diff options
| author | Nameless <truemedian@gmail.com> | 2023-12-14 15:52:39 -0600 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2024-01-13 18:51:38 -0800 |
| commit | b723296e1fa65b73a43b0790bdddcbfcea7d656d (patch) | |
| tree | 33173696d644f655374e134b73df3d694e4a1a28 /lib/std/http/Server.zig | |
| parent | 832f6d8f7f7f7b10b86b109a8b26bb5eacc8d13e (diff) | |
| download | zig-b723296e1fa65b73a43b0790bdddcbfcea7d656d.tar.gz zig-b723296e1fa65b73a43b0790bdddcbfcea7d656d.zip | |
std.http: add missing documentation and a few examples
Diffstat (limited to 'lib/std/http/Server.zig')
| -rw-r--r-- | lib/std/http/Server.zig | 86 |
1 files changed, 78 insertions, 8 deletions
diff --git a/lib/std/http/Server.zig b/lib/std/http/Server.zig index 6928606b1b..48c4e2cbfb 100644 --- a/lib/std/http/Server.zig +++ b/lib/std/http/Server.zig @@ -1,3 +1,44 @@ +//! HTTP Server implementation. +//! +//! This server assumes *all* clients are well behaved and standard compliant; it can and will deadlock if a client holds a connection open without sending a request. +//! +//! Example usage: +//! +//! ```zig +//! var server = Server.init(.{ .reuse_address = true }); +//! defer server.deinit(); +//! +//! try server.listen(bind_addr); +//! +//! while (true) { +//! var res = try server.accept(.{ .allocator = gpa }); +//! defer res.deinit(); +//! +//! while (res.reset() != .closing) { +//! res.wait() catch |err| switch (err) { +//! error.HttpHeadersInvalid => break, +//! error.HttpHeadersExceededSizeLimit => { +//! res.status = .request_header_fields_too_large; +//! res.send() catch break; +//! break; +//! }, +//! else => { +//! res.status = .bad_request; +//! res.send() catch break; +//! break; +//! }, +//! } +//! +//! res.status = .ok; +//! res.transfer_encoding = .chunked; +//! +//! try res.send(); +//! try res.writeAll("Hello, World!\n"); +//! try res.finish(); +//! } +//! } +//! ``` + const std = @import("../std.zig"); const testing = std.testing; const http = std.http; @@ -10,8 +51,7 @@ const assert = std.debug.assert; const Server = @This(); const proto = @import("protocol.zig"); -allocator: Allocator, - +/// The underlying server socket. socket: net.StreamServer, /// An interface to a plain connection. @@ -269,8 +309,13 @@ pub const Request = struct { return @as(u64, @bitCast(array.*)); } + /// The HTTP request method. method: http.Method, + + /// The HTTP request target. target: []const u8, + + /// The HTTP version of this request. version: http.Version, /// The length of the request body, if known. @@ -282,16 +327,21 @@ pub const Request = struct { /// The compression of the request body, or .identity (no compression) if not present. transfer_compression: http.ContentEncoding = .identity, + /// The list of HTTP request headers headers: http.Headers, + parser: proto.HeadersParser, compression: Compression = .none, }; /// A HTTP response waiting to be sent. /// -/// [/ <----------------------------------- \] -/// Order of operations: accept -> wait -> send [ -> write -> finish][ -> reset /] -/// \ -> read / +/// Order of operations: +/// ``` +/// [/ <--------------------------------------- \] +/// accept -> wait -> send [ -> write -> finish][ -> reset /] +/// \ -> read / +/// ``` pub const Response = struct { version: http.Version = .@"HTTP/1.1", status: http.Status = .ok, @@ -299,11 +349,21 @@ pub const Response = struct { transfer_encoding: ResponseTransfer = .none, + /// The allocator responsible for allocating memory for this response. allocator: Allocator, + + /// The peer's address address: net.Address, + + /// The underlying connection for this response. connection: Connection, + /// The HTTP response headers headers: http.Headers, + + /// The HTTP request that this response is responding to. + /// + /// This field is only valid after calling `wait`. request: Request, state: State = .first, @@ -495,6 +555,17 @@ pub const Response = struct { pub const WaitError = Connection.ReadError || proto.HeadersParser.CheckCompleteHeadError || Request.ParseError || error{ CompressionInitializationFailed, CompressionNotSupported }; /// Wait for the client to send a complete request head. + /// + /// For correct behavior, the following rules must be followed: + /// + /// * If this returns any error in `Connection.ReadError`, you MUST immediately close the connection by calling `deinit`. + /// * If this returns `error.HttpHeadersInvalid`, you MAY immediately close the connection by calling `deinit`. + /// * If this returns `error.HttpHeadersExceededSizeLimit`, you MUST respond with a 431 status code and then call `deinit`. + /// * If this returns any error in `Request.ParseError`, you MUST respond with a 400 status code and then call `deinit`. + /// * If this returns any other error, you MUST respond with a 400 status code and then call `deinit`. + /// * If the request has an Expect header containing 100-continue, you MUST either: + /// * Respond with a 100 status code, then call `wait` again. + /// * Respond with a 417 status code. pub fn wait(res: *Response) WaitError!void { switch (res.state) { .first, .start => res.state = .waited, @@ -664,9 +735,8 @@ pub const Response = struct { }; /// Create a new HTTP server. -pub fn init(allocator: Allocator, options: net.StreamServer.Options) Server { +pub fn init(options: net.StreamServer.Options) Server { return .{ - .allocator = allocator, .socket = net.StreamServer.init(options), }; } @@ -748,7 +818,7 @@ test "HTTP server handles a chunked transfer coding request" { const expect = std.testing.expect; const max_header_size = 8192; - var server = std.http.Server.init(allocator, .{ .reuse_address = true }); + var server = std.http.Server.init(.{ .reuse_address = true }); defer server.deinit(); const address = try std.net.Address.parseIp("127.0.0.1", 0); |
