Skip to content

Inch4Tk/odinhttp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Odin Http Client

This library provides a lightweight http client written in Odin. The underlying socket is from SDL2.net provided by Odin/Vendor.

Installation

// inside odin/shared
// git clone -b v0.1.3 https://github.com/Inch4Tk/odinhttp
// then in project:
import http "shared:odinhttp/http"

// alternatively to run tests, inside odinhttp folder:
// for some reason the builtin odin (dev-2022-04) test command does not work
odin run test

Dependency Notes:

For Windows (find and add the following dlls to path or directory):

  • SDL2.dll (from odin/vendor/sdl2)
  • SDL2_net.dll (from odin/vendor/sdl2/net)

For Unix:

  • sudo apt-get install libsdl2-2.0

Important: Then follow the instructions in the SSL section to either install OpenSSL oder compile without SSL support.

Example

For a more complete example check example/example.odin

// Before you send requests always make a session, keep this session around
// for the time you want to make requests.
// Right now you can (should) only have a single session and it is not threadsafe.
session := http.make_session() or_return
defer http.delete_session(session)

// Example: Simple get request
req := http.request_prepare(.Get, "https://xyz.com/404") or_return
fmt.printf("Request:\n%s\n", req.buffer)

res := http.request(session, req) or_return
fmt.printf("Response in %d milliseconds:\n%s\n", res.elapsed_ms, res.buffer)

defer http.request_delete(req)
defer http.response_delete(res)

List of supported behavior

  • Http 1.1
  • SSL/TLS (compile with SSL Support see below)
  • All methods (except CONNECT)
  • Headers
  • Params
  • Body as binary/bytestring data
  • Cookies (as Odin map)
  • content-encoding: deflate and gzip (automatically set to accept both)
  • Timeout

List of unsupported behavior

This is an incomplete list of unsupported behavior

  • Http2
  • Redirects
  • "Expect: 100 Continue" requests/responses
  • Http tunnels (Method = CONNECT)
  • Proxies
  • CookieJar (only supports cookies that are supplied as Odin map)
  • Ipv6 (no SDL2.net support)
  • Parallel requests (library is not threadsafe)
  • Charset is assumed to always be utf8
  • transfer-encoding: compress, gzip, deflate (this is unlikely to be used when content-encoding is set, which is the preferred way to do compression anyways)
  • Chunked requests (only responses can be chunked)
  • Chunk extensions
  • Timeouts will not work reliably when servers misbehave and send incomplete messages while keeping the connection open
    • If a server does not send crlf as last line of header
    • If any chunk was not sent completely. This is partly a limitation in SDL, since there is no timeout on read.
    • Probably others
  • No support for brotli encoding (if someone makes bindings for brotli, I will happily add this)

Authorization

Add the authorization headers of your choice yourself. Examples:

// Bearer:
token := "...."
headers := make(map[string]string)
defer delete(headers)
headers["authorization"] = fmt.tprintf("Bearer %s", token)
req, err := request_prepare(.Get, "https://xyz.com/404", headers=headers)

// Basic
// expects username/password in base64 encoding separated with ":"
import "core:mem"
import base64 "core:encoding/base64"
username := "...."
password := "...."
headers := make(map[string]string)
defer delete(headers)
concat := fmt.tprintf("%s:%s", username, password)
// have to do some acrobatics to get []u8 from string without copy, is there a better way?
bytecrobatics : []u8 = mem.slice_ptr(strings.ptr_from_string(concat), len(concat))
headers["authorization"] = fmt.tprintf("Basic %s", base64.encode(bytecrobatics))
req, err := request_prepare(.Get, "https://xyz.com/404", headers=headers)

Content-Types

Before sending a request, convert any content-types and input them as byte array. The same goes for content-types received. The response will only contain a byte buffer of the body. Conversion of the content-type has to happen in the calling code.

// Form data
headers := make(map[string]string)
defer delete(headers)
headers["content-type"] = "application/x-www-form-urlencoded"
body_data := "parameter1=value&anykeyname=anothervalue"
req, err := request_prepare(.Post, "https://xyz.com/404", headers=headers, body_data=body_data)

For building bodies with other complex types refer to here:

Content-Length header will be set automatically based on the body size

SSL Support