···11# An HTTP/1.1 server for Zig.
2233## Zig Version
44-This is for Zig 0.16.0. Use the [zig-0.15.2](https://github.com/karlseguin/http.zig/tree/zig-0.15) branch for Zig 0.15 or the [dev](https://github.com/karlseguin/http.zig/tree/dev) which may or may not be up to date with zig dev.
44+This is for Zig 0.16.0. Use the [zig-0.15](https://github.com/karlseguin/http.zig/tree/zig-0.15) branch for Zig 0.15 or the [dev](https://github.com/karlseguin/http.zig/tree/dev) branch which may or may not be up to date with zig dev.
5566This ZIG 0.16 version is not well tested. Like Zig 0.16 itself, consider it experimental!
77···1313pub fn main(init: std.process.Init) !void {
1414 const allocator = init.gpa;
15151616- // More advance cases will use a custom "Handler" instead of "void".
1717- // The last parameter is our handler instance, since we have a "void"
1616+ // More advanced cases will use a custom "Handler" instead of "void".
1717+ // The last parameter is our handler instance; since we have a "void"
1818 // handler, we passed a void ({}) value.
1919 var server = try httpz.Server(void).init(init.io, allocator, .{
2020 // use .all(5882) to bind to all interfaces, i.e. 0.0.0.0
2121 .address = .localhost(5882),
2222 }, {});
2323 defer {
2424- // clean shutdown, finishes serving any live request
2424+ // clean shutdown, finishes serving any live requests
2525 server.stop();
2626 server.deinit();
2727 }
···64646565# Versions
66666767-The `master` branch targets the latest stable of Zig (0.15.1). The `dev` branch targets the latest version of Zig. If you're looking for an older version, look for an zig-X.YZ branches.
6767+The `master` branch targets the latest **stable** release of Zig; the `dev` branch targets the latest version of Zig. If you're looking for an older version, look for `zig-X.YY` branches.
68686969# Examples
7070···7878# Installation
797980801. Add http.zig as a dependency in your `build.zig.zon`:
8181-8282-```bash
8383-zig fetch --save "git+https://github.com/karlseguin/http.zig#master"
8484-```
8181+ ```bash
8282+ zig fetch --save "git+https://github.com/karlseguin/http.zig#master"
8383+ ```
8484+ The library will track Zig master. If you're using a specific version of Zig, use the appropriate branch.
858586862. In your `build.zig`, add the `httpz` module as a dependency to your program:
8787-8888-```zig
8989-const httpz = b.dependency("httpz", .{
9090- .target = target,
9191- .optimize = optimize,
9292-});
8787+ ```zig
8888+ const httpz = b.dependency("httpz", .{
8989+ .target = target,
9090+ .optimize = optimize,
9191+ });
9292+9393+ // the executable from your call to b.addExecutable(...)
9494+ exe.root_module.addImport("httpz", httpz.module("httpz"));
9595+ ```
93969494-// the executable from your call to b.addExecutable(...)
9595-exe.root_module.addImport("httpz", httpz.module("httpz"));
9696-```
9797-9898-The library tracks Zig master. If you're using a specific version of Zig, use the appropriate branch.
999710098# Alternatives
10199···236234237235## Error Handler
238236239239-If your handler has a public `uncaughtError` method, it will be called whenever there's an unhandled error. This could be due to some internal httpz bug, or because your action return an error.
237237+If your handler has a public `uncaughtError` method, it will be called whenever there's an unhandled error. This could be due to some internal httpz bug, or because your action returns an error.
240238241239```zig
242240const App = struct {
···252250253251## Takeover
254252255255-For the most control, you can define a `handle` method. This circumvents most of Httpz's dispatching, including routing. Frameworks like JetZig hook use `handle` in order to provide their own routing and dispatching. When you define a `handle` method, then any `dispatch`, `notFound` and `uncaughtError` methods are ignored by httpz.
253253+For the most control, you can define a `handle` method. This circumvents most of Httpz's dispatching, including routing. Frameworks like JetZig hook `handle` in order to provide their own routing and dispatching. When you define a `handle` method, then any `dispatch`, `notFound` and `uncaughtError` methods are ignored by httpz.
256254257255```zig
258256const App = struct {
···262260};
263261```
264262265265-The behavior `httpz.Server(H)` is controlled by
266263The library supports both simple and complex use cases. A simple use case is shown below. It's initiated by the call to `httpz.Server()`:
267264268265```zig
···337334338335Alternatively, you can explicitly call `res.write()`. Once `res.write()` returns, the response is sent and your action can cleanup/release any resources.
339336340340-`res.arena` is actually a configurable-sized thread-local buffer that fallsback to an `std.heap.ArenaAllocator`. In other words, it's fast so it should be your first option for data that needs to live only until your action exits.
337337+`res.arena` is actually a configurable-sized thread-local buffer that falls back to an `std.heap.ArenaAllocator`. In other words, it's fast so it should be your first option for data that needs to live only until your action exits.
341338342342-To align the `Response.writer()` with the new `*std.Io.Writer` interface (Zig 0.15) , a buffer must be provided. For the most part, you should pass in an empty buffer (`&.{}`) as httpz does its own buffer (as I expect most network applications would do). Still, it appears that some parts of std require a writer buffer. This seems like a really bad design to me, but if you find yourself using code that requires a writer buffer, then, of course, you'll have to provide one when creating the `response.writer(...)`.
339339+To align the `Response.writer()` with the new `*std.Io.Writer` interface (Zig 0.15), a buffer must be provided. For the most part, you should pass in an empty buffer (`&.{}`) as httpz does its own buffering (as I expect most network applications would do). Still, it appears that some parts of std require a writer buffer. This seems like a really bad design to me, but if you find yourself using code that requires a writer buffer, then, of course, you'll have to provide one when creating the `response.writer(...)`.
343340344341# httpz.Request
345342···347344348345- `method` - httpz.Method enum
349346- `method_string` - Only set if `method == .OTHER`, else empty. Used when using custom methods.
350350-- `arena` - A fast thread-local buffer that fallsback to an ArenaAllocator, same as `res.arena`.
347347+- `arena` - A fast thread-local buffer that falls back to an ArenaAllocator, same as `res.arena`.
351348- `url.path` - the path of the request (`[]const u8`)
352349- `address` - the std.net.Address of the client
353350···418415};
419416```
420417421421-On first call, the `query` function attempts to parse the querystring. This requires memory allocations to unescape encoded values. The parsed value is internally cached, so subsequent calls to `query()` are fast and cannot fail.
418418+On first call, the `query` function attempts to parse the query string. This requires memory allocations to unescape encoded values. The parsed value is internally cached, so subsequent calls to `query()` are fast and cannot fail.
422419423420The original casing of both the key and the name are preserved.
424421···472469473470### Form Data
474471475475-The body of the request, if any, can be parsed as a "x-www-form-urlencoded "value using `req.formData()`. The `request.max_form_count` configuration value must be set to the maximum number of form fields to support. This defaults to 0.
472472+The body of the request, if any, can be parsed as a `x-www-form-urlencoded` value using `req.formData()`. The `request.max_form_count` configuration value must be set to the maximum number of form fields to support. This defaults to 0.
476473477474This behaves similarly to `query()`.
478475···494491495492### Multi Part Form Data
496493497497-Similar to the above, `req.multiFormData()` can be called to parse requests with a "multipart/form-data" content type. The `request.max_multiform_count` configuration value must be set to the maximum number of form fields to support. This defaults to 0.
494494+Similar to the above, `req.multiFormData()` can be called to parse requests with a `multipart/form-data` content type. The `request.max_multiform_count` configuration value must be set to the maximum number of form fields to support. This defaults to 0.
498495499496This is a different API than `formData` because the return type is different. Rather than a simple string=>value type, the multi part form data value consists of a `value: []const u8` and a `filename: ?[]const u8`.
500497···561558562559- `status` - set the status code, by default, each response starts off with a 200 status code
563560- `content_type` - an httpz.ContentType enum value. This is a convenience and optimization over using the `res.header` function.
564564-- `arena` - A fast thread-local buffer that fallsback to an ArenaAllocator, same as `req.arena`.
561561+- `arena` - A fast thread-local buffer that falls back to an ArenaAllocator, same as `req.arena`.
565562566563The `status` field is a `u16`. You can alternatively use `res.setStatus(.ok)` if you prefer to use the `std.http.Status` enum.
567564···647644648645## Writing
649646650650-By default, httpz will automatically flush your response. In more advance cases, you can use `res.write()` to explicitly flush it. This is useful in cases where you have resources that need to be freed/released only after the response is written. For example, my [LRU cache](https://github.com/karlseguin/cache.zig) uses atomic referencing counting to safely allow concurrent access to cached data. This requires callers to "release" the cached entry:
647647+By default, httpz will automatically flush your response. In more advanced cases, you can use `res.write()` to explicitly flush it. This is useful in cases where you have resources that need to be freed/released only after the response is written. For example, my [LRU cache](https://github.com/karlseguin/cache.zig) uses atomic referencing counting to safely allow concurrent access to cached data. This requires callers to "release" the cached entry:
651648652649```zig
653650pub fn info(app: *MyApp, _: *httpz.Request, res: *httpz.Response) !void {
···679676router.tryGet("/", index, .{});
680677```
681678682682-The 3rd parameter is a route configuration. It allows you to speficy a different `handler` and/or `dispatch` method and/or `middleware`.
679679+The 3rd parameter is a route configuration. It allows you to specify a different `handler` and/or `dispatch` method and/or `middleware`.
683680684681```zig
685682// this can panic if it fails to create the route
···775772router.method("TEA", "/", teaList, .{});
776773```
777774778778-In such cases, `request.method` will be `.OTHER` and you can use the `reqeust.method_string` for the string value. The method name, `TEA` above, is cloned by the router and does not need to exist beyond the function call. The method name should only be uppercase ASCII letters.
775775+In such cases, `request.method` will be `.OTHER` and you can use the `request.method_string` for the string value. The method name, `TEA` above, is cloned by the router and does not need to exist beyond the function call. The method name should only be uppercase ASCII letters.
779776780777The `router.all` method **does not** route to custom methods.
781778···817814// explicitly opts out)
818815var router = try server.router(.{.middlewares = &.{cors}});
819816820820-// or we could add middleware on a route-per-route bassis
817817+// or we could add middleware on a route-per-route basis
821818router.get("/v1/users", user, .{.middlewares = &.{cors}});
822819823820// by default, middlewares on a route are appended to the global middlewares
···918915919916`request.buffer_size` must be large enough to fit the request header. Any extra space might be used to read the body. However, there can be up to `workers.count * workers.max_conn` pending requests, so a large `request.buffer_size` can take up a lot of memory. Instead, consider keeping `request.buffer_size` only large enough for the header (plus a bit of overhead for decoding URL-escape values) and set `workers.large_buffer_size` to a reasonable size for your incoming request bodies. This will take `workers.count * workers.large_buffer_count * workers.large_buffer_size` memory.
920917921921-Buffers for request bodies larger than `workers.large_buffer_size` but smaller than `request.max_body_size` will be dynamic allocated.
918918+Buffers for request bodies larger than `workers.large_buffer_size` but smaller than `request.max_body_size` will be dynamically allocated.
922919923920In addition to a bit of overhead, at a minimum, httpz will use:
924921···978975 // configures the threadpool which processes requests. The threadpool is
979976 // where your application code runs.
980977 .thread_pool = .{
981981- // Number threads. If you're handlers are doing a lot of i/o, a higher
978978+ // Number threads. If your handlers are doing a lot of i/o, a higher
982979 // number might provide better throughput
983980 // (blocking mode: handled differently)
984981 .count = 32,
985982986983 // The maximum number of pending requests that the thread pool will accept
987987- // This applies back pressure to the above workers and ensures that, under load
984984+ // This applies back pressure to the above workers and ensures that, under load,
988985 // pending requests get precedence over processing new requests.
989986 .backlog = 500,
990987···1175117211761173## Building the test Request
1177117411781178-The testing structure returns from <code>httpz.testing.init</code> exposes helper functions to set param, query and query values as well as the body:
11751175+The testing structure returned from <code>httpz.testing.init</code> exposes helper functions to set param, query and header values as well as the body:
1179117611801177```zig
11811178var web_test = ht.init(.{});
···12791276 pub const WebsocketHandler = struct {
12801277 conn: *websocket.Conn,
1281127812821282- // ctx is arbitrary data you passs to httpz.upgradeWebsocket
12791279+ // ctx is arbitrary data you pass to httpz.upgradeWebsocket
12831280 pub fn init(conn: *websocket.Conn, _: WebsocketContext) {
12841281 return .{
12851282 .conn = conn,