odin-http clientx/serverx
  • Odin 92.8%
  • Shell 6.5%
  • Dockerfile 0.4%
  • Go 0.2%
Find a file
Be Dangerous c3cb23aaa2
Some checks failed
Deploy docs to GitHub pages / docs (push) Waiting to run
CI / check (ubuntu-latest) (push) Failing after 4s
CI / unit (ubuntu-latest) (push) Failing after 3s
CI / fuzz (push) Failing after 4s
CI / h2spec (push) Failing after 4s
CI / interop (push) Failing after 4s
CI / adversity (push) Failing after 3s
CI / soak (push) Has been skipped
CI / check (macos-15-intel) (push) Has been cancelled
CI / check (macos-latest) (push) Has been cancelled
CI / check (windows-latest) (push) Has been cancelled
CI / unit (macos-latest) (push) Has been cancelled
readiness R4: plain HTTP always Runtime demux; retire Nbio_H1_Host
Listen_IO.Nbio only selects the nbio poll_once backend; accept/drive use
Tcp_Conn on Runtime like Demux. Multi-protocol with Nbio readiness is one
Runtime (no dual-engine Nbio_H1). listen_and_serve_nbio is a thin alias.
2026-07-11 20:19:47 -05:00
.github/workflows clientx-stack: sessions, runtime, tests, interop runner 2026-07-10 00:35:26 -05:00
boringssl boringssl: split bindings into per-area files; tls: backend switch at repo root 2026-06-11 13:27:39 -05:00
client Rename packages: legacy for old APIs; clientx/serverx → client/server 2026-07-11 00:45:37 -05:00
comparisons retire in-tree http.Server; slim package http for server hosts 2026-07-11 19:25:55 -05:00
docs readiness R4: plain HTTP always Runtime demux; retire Nbio_H1_Host 2026-07-11 20:19:47 -05:00
examples readiness R4: plain HTTP always Runtime demux; retire Nbio_H1_Host 2026-07-11 20:19:47 -05:00
hpack clientx-stack: sessions, runtime, tests, interop runner 2026-07-10 00:35:26 -05:00
http2 Rename packages: legacy for old APIs; clientx/serverx → client/server 2026-07-11 00:45:37 -05:00
http3 Rename packages: legacy for old APIs; clientx/serverx → client/server 2026-07-11 00:45:37 -05:00
huffman clientx-stack: sessions, runtime, tests, interop runner 2026-07-10 00:35:26 -05:00
openssl serverx: Tls_Conn nonblocking drive (Package B) 2026-07-10 20:22:24 -05:00
qpack clientx-stack: sessions, runtime, tests, interop runner 2026-07-10 00:35:26 -05:00
quic clientx-stack: sessions, runtime, tests, interop runner 2026-07-10 00:35:26 -05:00
server readiness R4: plain HTTP always Runtime demux; retire Nbio_H1_Host 2026-07-11 20:19:47 -05:00
tests readiness R4: plain HTTP always Runtime demux; retire Nbio_H1_Host 2026-07-11 20:19:47 -05:00
tls_server tls_server: default_provider uses BoringSSL static 2026-07-10 23:40:08 -05:00
vendor naming: drop cleartext; laytan-shaped listen_and_serve surface 2026-07-11 17:32:08 -05:00
.dockerignore clientx-stack: sessions, runtime, tests, interop runner 2026-07-10 00:35:26 -05:00
.editorconfig refactor: routing now takes a Handler, so specific routes can be wrapped with middleware individually 2023-05-12 10:30:13 +02:00
.gitignore gitignore: only ignore root example binaries, not package dirs 2026-07-11 18:05:06 -05:00
body.odin retire in-tree http.Server; slim package http for server hosts 2026-07-11 19:25:55 -05:00
cookie.odin fix client cookies 2025-12-15 16:45:31 +03:00
handlers.odin fix: fixes for deprecations and changes in Odin 2025-11-06 20:42:06 +01:00
headers.odin fix headers combining 2025-12-14 02:35:28 +03:00
http.odin fix headers combining 2025-12-14 02:35:28 +03:00
LICENSE feat: add a license 2023-05-19 22:57:08 +02:00
mimes.odin fix: SVG mime type (#46) 2024-05-14 09:39:05 +02:00
mod.pkg make ssl work on requests to domains that use SNI for certificates (cloudflare universal ssl for example) 2023-08-11 14:27:46 +02:00
odinfmt.json Non-Blocking IO (#1) 2023-08-06 23:17:09 +02:00
README.md retire in-tree http.Server; slim package http for server hosts 2026-07-11 19:25:55 -05:00
request.odin retire in-tree http.Server; slim package http for server hosts 2026-07-11 19:25:55 -05:00
response.odin retire in-tree http.Server; slim package http for server hosts 2026-07-11 19:25:55 -05:00
responses.odin retire in-tree http.Server; slim package http for server hosts 2026-07-11 19:25:55 -05:00
status.odin fix: fixes for deprecations and changes in Odin 2025-11-06 20:42:06 +01:00
types.odin Rename packages: legacy for old APIs; clientx/serverx → client/server 2026-07-11 00:45:37 -05:00
url.odin retire in-tree http.Server; slim package http for server hosts 2026-07-11 19:25:55 -05:00

Odin HTTP

A HTTP/1.1 implementation for Odin purely written in Odin (besides SSL).

See generated package documentation at odin-http.laytan.dev.

See below examples or the examples directory.

Compatibility

This is beta software, confirmed to work in my own use cases but can certainly contain edge cases and bugs that I did not catch. Please file issues for any bug or suggestion you encounter/have.

I am usually on a recent master version of Odin and commits will be made with new features if applicable, backwards compatibility or even stable version compatibility is not currently a thing.

Because this is still heavily in development, I do not hesitate to push API changes at the moment, so beware.

The package has been tested to work with Ubuntu Linux (other "normal" distros should work), MacOS (m1 and intel), and Windows 64 bit. Any other distributions or versions have not been tested and might not work.

Dependencies

HTTPS clients and TLS servers depend on OpenSSL and/or BoringSSL (see openssl/, boringssl/, tls_server/). Windows may use bundled static libs; Linux/macOS typically use system libraries (libssl).

Performance

Some small benchmarks have been done in the comparisons directory.

My main priority in terms of performance is currently Linux (because most servers end up there in production).

Other targets are still made to be performant, but benchmarking etc. is mostly done on Linux.

IO implementations

Although these implementation details are not exposed when using the package, these are the underlying kernel API's that are used.

I/O lives in package server (plain HTTP/1.1, TLS ALPN h2/h1, H3 via listen_and_serve_config). Shared types and respond_* are package http (no socket server). Routing: server.Router. Upstream nbio baseline: vendor/laytan/odin-http. See docs/MIGRATION.md and docs/MERGE_MAIN.md.

Server example

package main

import "core:fmt"
import "core:log"
import "core:net"
import "core:time"

import http "../.." // Change to path of package.
import "../server"  // package server host + Router

main :: proc() {
	context.logger = log.create_console_logger(.Info)

	// Router: same shape as laytan http.Router (init → route_* → router_handler).
	// Patterns use path segments: {name} params and trailing * wildcards
	// (not Lua patterns). Routes are tried by specificity, not registration order.
	router: server.Router
	server.router_init(&router)

	// /users/{user}/comments/{id}  — was: /users/(%w+)/comments/(%d+)
	// Params: server.url_param(req, "user")  — was: req.url_params[0]
	server.route_get(&router, "/users/{user}/comments/{id}", http.handler(proc(req: ^http.Request, res: ^http.Response) {
		user, _ := server.url_param(req, "user")
		id, _ := server.url_param(req, "id")
		http.respond_plain(res, fmt.tprintf("user %s, comment: %s", user, id))
	}))
	server.route_get(&router, "/cookies", http.handler(cookies))
	server.route_get(&router, "/api", http.handler(api))
	server.route_get(&router, "/ping", http.handler(ping))
	server.route_get(&router, "/index", http.handler(index))
	// Catch-all remainder: /*  — was: (.*)
	server.route_get(&router, "/*", http.handler(static))
	server.route_post(&router, "/ping", http.handler(post_ping))

	s: server.Server
	server.server_shutdown_on_interrupt(&s)

	log.info("Listening on http://localhost:6969")
	err := server.listen_and_serve(
		&s,
		server.router_handler(&router),
		net.Endpoint{address = net.IP4_Loopback, port = 6969},
	)
	fmt.assertf(err == .None, "server stopped with error: %v", err)
}

cookies :: proc(req: ^http.Request, res: ^http.Response) {
	append(
		&res.cookies,
		http.Cookie{
			name         = "Session",
			value        = "123",
			expires_gmt  = time.now(),
			max_age_secs = 10,
			http_only    = true,
			same_site    = .Lax,
		},
	)
	http.respond_plain(res, "Yo!")
}

api :: proc(req: ^http.Request, res: ^http.Response) {
	if err := http.respond_json(res, req.line); err != nil {
		log.errorf("could not respond with JSON: %s", err)
	}
}

ping :: proc(req: ^http.Request, res: ^http.Response) {
	http.respond_plain(res, "pong")
}

INDEX_HTML :: #load("examples/complete/static/index.html")

index :: proc(req: ^http.Request, res: ^http.Response) {
	// Prefer embed; disk respond_file/dir are deprecated (docs/MIGRATION.md § Static files).
	http.respond_file_content(res, "index.html", INDEX_HTML)
}

static :: proc(req: ^http.Request, res: ^http.Response) {
	rest, _ := server.url_param(req, "*")
	if rest == "" || rest == "index.html" {
		http.respond_file_content(res, "index.html", INDEX_HTML)
		return
	}
	http.respond_plain(res, fmt.tprintf("not found: %q", rest), .Not_Found)
}

post_ping :: proc(req: ^http.Request, res: ^http.Response) {
	http.body(req, len("ping"), res, proc(res: rawptr, body: http.Body, err: http.Body_Error) {
		res := cast(^http.Response)res

		if err != nil {
			http.respond(res, http.body_error_status(err))
			return
		}

		if body != "ping" {
			http.respond(res, http.Status.Unprocessable_Content)
			return
		}

		http.respond_plain(res, "pong")
	})
}

Router at a glance

Laytan / in-tree http.Router This fork server.Router
Setup http.router_init / route_get / router_handler server.router_init / route_get / router_handler
Patterns Lua e.g. /users/(%w+) Segments e.g. /users/{user}
Catch-all (.*) /* or /static/*
Params req.url_params[0] server.url_param(req, "user")

Full port table: docs/MIGRATION.md § Router. Example: examples/routing.

Client example

package main

import "core:fmt"

import "../../client"

main :: proc() {
	res, err := client.get("https://www.google.com/")
	if err != .None {
		fmt.printf("Request failed: %v\n", err)
		return
	}
	defer client.response_destroy(&res)

	fmt.printf("Status: %v  version: %v  body: %d bytes\n", res.status, res.version, len(res.body))
}

See also examples/client_get (h1/h2/h3 flags), examples/server_serve, docs/LIBRARY.md, and docs/MIGRATION.md.

Upstream nbio-only baseline (for benchmarks): vendor/laytan/odin-http.