{"id":21725731,"url":"https://github.com/laytan/odin-http","last_synced_at":"2026-01-27T01:32:09.377Z","repository":{"id":163117216,"uuid":"637533282","full_name":"laytan/odin-http","owner":"laytan","description":"A HTTP/1.1 client/server implementation for Odin.","archived":false,"fork":false,"pushed_at":"2026-01-17T13:25:39.000Z","size":66101,"stargazers_count":323,"open_issues_count":17,"forks_count":36,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-01-17T23:54:25.238Z","etag":null,"topics":["http","http-client","http-server","odin-lib"],"latest_commit_sha":null,"homepage":"https://odin-http.laytan.dev/","language":"Odin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/laytan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-05-07T20:56:41.000Z","updated_at":"2026-01-17T13:25:43.000Z","dependencies_parsed_at":"2023-12-21T14:50:55.045Z","dependency_job_id":"534b61e0-d5fa-442a-859b-faf511f8a9cf","html_url":"https://github.com/laytan/odin-http","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/laytan/odin-http","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laytan%2Fodin-http","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laytan%2Fodin-http/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laytan%2Fodin-http/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laytan%2Fodin-http/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/laytan","download_url":"https://codeload.github.com/laytan/odin-http/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laytan%2Fodin-http/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28795468,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-27T01:07:07.743Z","status":"ssl_error","status_checked_at":"2026-01-27T01:07:06.974Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["http","http-client","http-server","odin-lib"],"created_at":"2024-11-26T03:19:57.720Z","updated_at":"2026-01-27T01:32:09.348Z","avatar_url":"https://github.com/laytan.png","language":"Odin","readme":"# Odin HTTP\n\nA HTTP/1.1 implementation for Odin purely written in Odin (besides SSL).\n\nSee generated package documentation at [odin-http.laytan.dev](https://odin-http.laytan.dev).\n\nSee below examples or the examples directory.\n\n## Compatibility\n\nThis is beta software, confirmed to work in my own use cases but can certainly contain edge cases and bugs that I did not catch.\nPlease file issues for any bug or suggestion you encounter/have.\n\nI am usually on a recent master version of Odin and commits will be made with new features if applicable, backwards compatibility or even\nstable version compatibility is not currently a thing.\n\nBecause this is still heavily in development, I do not hesitate to push API changes at the moment, so beware.\n\nThe package has been tested to work with Ubuntu Linux (other \"normal\" distros should work), MacOS (m1 and intel), and Windows 64 bit.\nAny other distributions or versions have not been tested and might not work.\n\n## Dependencies\n\nThe *client* package depends on OpenSSL for making HTTPS requests.\n\nThis repository contains a copy of these libraries for ease of use on Windows.\n\nFor Linux, most distros come with OpenSSL, if not you can install it with a package manager, usually under `libssl3`.\n\n## Performance\n\nSome small benchmarks have been done in the comparisons directory.\n\nMy main priority in terms of performance is currently Linux (because most servers end up there in production).\n\nOther targets are still made to be performant, but benchmarking etc. is mostly done on Linux.\n\n## IO implementations\n\nAlthough these implementation details are not exposed when using the package, these are the underlying kernel API's that are used.\n\n- Windows: [IOCP (IO Completion Ports)](https://en.wikipedia.org/wiki/Input/output_completion_port)\n- Linux:   [io_uring](https://en.wikipedia.org/wiki/Io_uring)\n- Darwin:  [KQueue](https://en.wikipedia.org/wiki/Kqueue)\n\nThe IO part of this package can be used on its own for other types of applications, see the nbio directory for the documentation on that.\nIt has APIs for reading, writing, opening, closing, seeking files and accepting, connecting, sending, receiving and closing sockets, both UDP and TCP, fully cross-platform.\n\n## Server example\n\n```odin\npackage main\n\nimport \"core:fmt\"\nimport \"core:log\"\nimport \"core:net\"\nimport \"core:time\"\n\nimport http \"../..\" // Change to path of package.\n\nmain :: proc() {\n\tcontext.logger = log.create_console_logger(.Info)\n\n\ts: http.Server\n\t// Register a graceful shutdown when the program receives a SIGINT signal.\n\thttp.server_shutdown_on_interrupt(\u0026s)\n\n\t// Set up routing\n\trouter: http.Router\n\thttp.router_init(\u0026router)\n\tdefer http.router_destroy(\u0026router)\n\n\t// Routes are tried in order.\n\t// Route matching is implemented using an implementation of Lua patterns, see the docs on them here:\n\t// https://www.lua.org/pil/20.2.html\n\t// They are very similar to regex patterns but a bit more limited, which makes them much easier to implement since Odin does not have a regex implementation.\n\n\t// Matches /users followed by any word (alphanumeric) followed by /comments and then / with any number.\n\t// The word is available as req.url_params[0], and the number as req.url_params[1].\n\thttp.route_get(\u0026router, \"/users/(%w+)/comments/(%d+)\", http.handler(proc(req: ^http.Request, res: ^http.Response) {\n\t\thttp.respond_plain(res, fmt.tprintf(\"user %s, comment: %s\", req.url_params[0], req.url_params[1]))\n\t}))\n\thttp.route_get(\u0026router, \"/cookies\", http.handler(cookies))\n\thttp.route_get(\u0026router, \"/api\", http.handler(api))\n\thttp.route_get(\u0026router, \"/ping\", http.handler(ping))\n\thttp.route_get(\u0026router, \"/index\", http.handler(index))\n\n\t// Matches every get request that did not match another route.\n\thttp.route_get(\u0026router, \"(.*)\", http.handler(static))\n\n\thttp.route_post(\u0026router, \"/ping\", http.handler(post_ping))\n\n\trouted := http.router_handler(\u0026router)\n\n\tlog.info(\"Listening on http://localhost:6969\")\n\n\terr := http.listen_and_serve(\u0026s, routed, net.Endpoint{address = net.IP4_Loopback, port = 6969})\n\tfmt.assertf(err == nil, \"server stopped with error: %v\", err)\n}\n\ncookies :: proc(req: ^http.Request, res: ^http.Response) {\n\tappend(\n\t\t\u0026res.cookies,\n\t\thttp.Cookie{\n\t\t\tname         = \"Session\",\n\t\t\tvalue        = \"123\",\n\t\t\texpires_gmt  = time.now(),\n\t\t\tmax_age_secs = 10,\n\t\t\thttp_only    = true,\n\t\t\tsame_site    = .Lax,\n\t\t},\n\t)\n\thttp.respond_plain(res, \"Yo!\")\n}\n\napi :: proc(req: ^http.Request, res: ^http.Response) {\n\tif err := http.respond_json(res, req.line); err != nil {\n\t\tlog.errorf(\"could not respond with JSON: %s\", err)\n\t}\n}\n\nping :: proc(req: ^http.Request, res: ^http.Response) {\n\thttp.respond_plain(res, \"pong\")\n}\n\nindex :: proc(req: ^http.Request, res: ^http.Response) {\n\thttp.respond_file(res, \"examples/complete/static/index.html\")\n}\n\nstatic :: proc(req: ^http.Request, res: ^http.Response) {\n\thttp.respond_dir(res, \"/\", \"examples/complete/static\", req.url_params[0])\n}\n\npost_ping :: proc(req: ^http.Request, res: ^http.Response) {\n\thttp.body(req, len(\"ping\"), res, proc(res: rawptr, body: http.Body, err: http.Body_Error) {\n\t\tres := cast(^http.Response)res\n\n\t\tif err != nil {\n\t\t\thttp.respond(res, http.body_error_status(err))\n\t\t\treturn\n\t\t}\n\n\t\tif body != \"ping\" {\n\t\t\thttp.respond(res, http.Status.Unprocessable_Content)\n\t\t\treturn\n\t\t}\n\n\t\thttp.respond_plain(res, \"pong\")\n\t})\n}\n```\n\n## Client example\n\n```odin\npackage main\n\nimport \"core:fmt\"\n\nimport \"../../client\"\n\nmain :: proc() {\n\tget()\n\tpost()\n}\n\n// basic get request.\nget :: proc() {\n\tres, err := client.get(\"https://www.google.com/\")\n\tif err != nil {\n\t\tfmt.printf(\"Request failed: %s\", err)\n\t\treturn\n\t}\n\tdefer client.response_destroy(\u0026res)\n\n\tfmt.printf(\"Status: %s\\n\", res.status)\n\tfmt.printf(\"Headers: %v\\n\", res.headers)\n\tfmt.printf(\"Cookies: %v\\n\", res.cookies)\n\tbody, allocation, berr := client.response_body(\u0026res)\n\tif berr != nil {\n\t\tfmt.printf(\"Error retrieving response body: %s\", berr)\n\t\treturn\n\t}\n\tdefer client.body_destroy(body, allocation)\n\n\tfmt.println(body)\n}\n\nPost_Body :: struct {\n\tname:    string,\n\tmessage: string,\n}\n\n// POST request with JSON.\npost :: proc() {\n\treq: client.Request\n\tclient.request_init(\u0026req, .Post)\n\tdefer client.request_destroy(\u0026req)\n\n\tpbody := Post_Body{\"Laytan\", \"Hello, World!\"}\n\tif err := client.with_json(\u0026req, pbody); err != nil {\n\t\tfmt.printf(\"JSON error: %s\", err)\n\t\treturn\n\t}\n\n\tres, err := client.request(\u0026req, \"https://webhook.site/YOUR-ID-HERE\")\n\tif err != nil {\n\t\tfmt.printf(\"Request failed: %s\", err)\n\t\treturn\n\t}\n\tdefer client.response_destroy(\u0026res)\n\n\tfmt.printf(\"Status: %s\\n\", res.status)\n\tfmt.printf(\"Headers: %v\\n\", res.headers)\n\tfmt.printf(\"Cookies: %v\\n\", res.cookies)\n\n\tbody, allocation, berr := client.response_body(\u0026res)\n\tif berr != nil {\n\t\tfmt.printf(\"Error retrieving response body: %s\", berr)\n\t\treturn\n\t}\n\tdefer client.body_destroy(body, allocation)\n\n\tfmt.println(body)\n}\n```\n","funding_links":[],"categories":["Libraries","Odin"],"sub_categories":["Networking"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaytan%2Fodin-http","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flaytan%2Fodin-http","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaytan%2Fodin-http/lists"}