| author | |
| committer | |
| log | ef6d58ed3b03b2a5d08489daee0c232d5e113d55 |
| tree | 57026931ce164f75ece056989d224eaa58fc9901 |
| parent | 8250a925444254c6c0c84af3006d04b7102a3343 |
| signature |
3 files changed, 73 insertions(+), 30 deletions(-)
lib/std/http/Client.zig+19-9| ... | @@ -15,8 +15,6 @@ const proto = @import("protocol.zig"); | ... | @@ -15,8 +15,6 @@ const proto = @import("protocol.zig"); |
| 15 | pub const default_connection_pool_size = 32; | 15 | pub const default_connection_pool_size = 32; |
| 16 | pub const connection_pool_size = std.options.http_connection_pool_size; | 16 | pub const connection_pool_size = std.options.http_connection_pool_size; |
| 17 | 17 | ||
| 18 | /// Used for tcpConnectToHost and storing HTTP headers when an externally | ||
| 19 | /// managed buffer is not provided. | ||
| 20 | allocator: Allocator, | 18 | allocator: Allocator, |
| 21 | ca_bundle: std.crypto.Certificate.Bundle = .{}, | 19 | ca_bundle: std.crypto.Certificate.Bundle = .{}, |
| 22 | ca_bundle_mutex: std.Thread.Mutex = .{}, | 20 | ca_bundle_mutex: std.Thread.Mutex = .{}, |
| ... | @@ -24,8 +22,10 @@ ca_bundle_mutex: std.Thread.Mutex = .{}, | ... | @@ -24,8 +22,10 @@ ca_bundle_mutex: std.Thread.Mutex = .{}, |
| 24 | /// it will first rescan the system for root certificates. | 22 | /// it will first rescan the system for root certificates. |
| 25 | next_https_rescan_certs: bool = true, | 23 | next_https_rescan_certs: bool = true, |
| 26 | 24 | ||
| 25 | /// The pool of connections that can be reused (and currently in use). | ||
| 27 | connection_pool: ConnectionPool = .{}, | 26 | connection_pool: ConnectionPool = .{}, |
| 28 | 27 | ||
| 28 | /// The last error that occurred on this client. This is not threadsafe, do not expect it to be completely accurate. | ||
| 29 | last_error: ?ExtraError = null, | 29 | last_error: ?ExtraError = null, |
| 30 | 30 | ||
| 31 | pub const ExtraError = union(enum) { | 31 | pub const ExtraError = union(enum) { |
| ... | @@ -68,7 +68,9 @@ pub const ExtraError = union(enum) { | ... | @@ -68,7 +68,9 @@ pub const ExtraError = union(enum) { |
| 68 | decompress: DecompressError, // error.ReadFailed | 68 | decompress: DecompressError, // error.ReadFailed |
| 69 | }; | 69 | }; |
| 70 | 70 | ||
| 71 | /// A set of linked lists of connections that can be reused. | ||
| 71 | pub const ConnectionPool = struct { | 72 | pub const ConnectionPool = struct { |
| 73 | /// The criteria for a connection to be considered a match. | ||
| 72 | pub const Criteria = struct { | 74 | pub const Criteria = struct { |
| 73 | host: []const u8, | 75 | host: []const u8, |
| 74 | port: u16, | 76 | port: u16, |
| ... | @@ -92,7 +94,9 @@ pub const ConnectionPool = struct { | ... | @@ -92,7 +94,9 @@ pub const ConnectionPool = struct { |
| 92 | pub const Node = Queue.Node; | 94 | pub const Node = Queue.Node; |
| 93 | 95 | ||
| 94 | mutex: std.Thread.Mutex = .{}, | 96 | mutex: std.Thread.Mutex = .{}, |
| 97 | /// Open connections that are currently in use. | ||
| 95 | used: Queue = .{}, | 98 | used: Queue = .{}, |
| 99 | /// Open connections that are not currently in use. | ||
| 96 | free: Queue = .{}, | 100 | free: Queue = .{}, |
| 97 | free_len: usize = 0, | 101 | free_len: usize = 0, |
| 98 | free_size: usize = connection_pool_size, | 102 | free_size: usize = connection_pool_size, |
| ... | @@ -189,6 +193,7 @@ pub const ConnectionPool = struct { | ... | @@ -189,6 +193,7 @@ pub const ConnectionPool = struct { |
| 189 | } | 193 | } |
| 190 | }; | 194 | }; |
| 191 | 195 | ||
| 196 | /// An interface to either a plain or TLS connection. | ||
| 192 | pub const Connection = struct { | 197 | pub const Connection = struct { |
| 193 | stream: net.Stream, | 198 | stream: net.Stream, |
| 194 | /// undefined unless protocol is tls. | 199 | /// undefined unless protocol is tls. |
| ... | @@ -261,6 +266,7 @@ pub const Connection = struct { | ... | @@ -261,6 +266,7 @@ pub const Connection = struct { |
| 261 | } | 266 | } |
| 262 | }; | 267 | }; |
| 263 | 268 | ||
| 269 | /// A buffered (and peekable) Connection. | ||
| 264 | pub const BufferedConnection = struct { | 270 | pub const BufferedConnection = struct { |
| 265 | pub const buffer_size = 0x2000; | 271 | pub const buffer_size = 0x2000; |
| 266 | 272 | ||
| ... | @@ -344,12 +350,14 @@ pub const BufferedConnection = struct { | ... | @@ -344,12 +350,14 @@ pub const BufferedConnection = struct { |
| 344 | } | 350 | } |
| 345 | }; | 351 | }; |
| 346 | 352 | ||
| 353 | /// The mode of transport for requests. | ||
| 347 | pub const RequestTransfer = union(enum) { | 354 | pub const RequestTransfer = union(enum) { |
| 348 | content_length: u64, | 355 | content_length: u64, |
| 349 | chunked: void, | 356 | chunked: void, |
| 350 | none: void, | 357 | none: void, |
| 351 | }; | 358 | }; |
| 352 | 359 | ||
| 360 | /// The decompressor for response messages. | ||
| 353 | pub const Compression = union(enum) { | 361 | pub const Compression = union(enum) { |
| 354 | pub const DeflateDecompressor = std.compress.zlib.ZlibStream(Request.TransferReader); | 362 | pub const DeflateDecompressor = std.compress.zlib.ZlibStream(Request.TransferReader); |
| 355 | pub const GzipDecompressor = std.compress.gzip.Decompress(Request.TransferReader); | 363 | pub const GzipDecompressor = std.compress.gzip.Decompress(Request.TransferReader); |
| ... | @@ -361,6 +369,7 @@ pub const Compression = union(enum) { | ... | @@ -361,6 +369,7 @@ pub const Compression = union(enum) { |
| 361 | none: void, | 369 | none: void, |
| 362 | }; | 370 | }; |
| 363 | 371 | ||
| 372 | /// A HTTP response originating from a server. | ||
| 364 | pub const Response = struct { | 373 | pub const Response = struct { |
| 365 | pub const Headers = struct { | 374 | pub const Headers = struct { |
| 366 | status: http.Status, | 375 | status: http.Status, |
| ... | @@ -501,14 +510,9 @@ pub const Response = struct { | ... | @@ -501,14 +510,9 @@ pub const Response = struct { |
| 501 | skip: bool = false, | 510 | skip: bool = false, |
| 502 | }; | 511 | }; |
| 503 | 512 | ||
| 504 | /// A HTTP request. | 513 | /// A HTTP request that has been sent. |
| 505 | /// | 514 | /// |
| 506 | /// Order of operations: | 515 | /// Order of operations: request[ -> write -> finish] -> do -> read |
| 507 | /// - request | ||
| 508 | /// - write | ||
| 509 | /// - finish | ||
| 510 | /// - do | ||
| 511 | /// - read | ||
| 512 | pub const Request = struct { | 516 | pub const Request = struct { |
| 513 | pub const Headers = struct { | 517 | pub const Headers = struct { |
| 514 | version: http.Version = .@"HTTP/1.1", | 518 | version: http.Version = .@"HTTP/1.1", |
| ... | @@ -862,6 +866,8 @@ pub const Request = struct { | ... | @@ -862,6 +866,8 @@ pub const Request = struct { |
| 862 | } | 866 | } |
| 863 | }; | 867 | }; |
| 864 | 868 | ||
| 869 | /// Release all associated resources with the client. | ||
| 870 | /// TODO: currently leaks all request allocated data | ||
| 865 | pub fn deinit(client: *Client) void { | 871 | pub fn deinit(client: *Client) void { |
| 866 | client.connection_pool.deinit(client); | 872 | client.connection_pool.deinit(client); |
| 867 | 873 | ||
| ... | @@ -871,6 +877,8 @@ pub fn deinit(client: *Client) void { | ... | @@ -871,6 +877,8 @@ pub fn deinit(client: *Client) void { |
| 871 | 877 | ||
| 872 | pub const ConnectError = Allocator.Error || error{ ConnectionFailed, TlsInitializationFailed }; | 878 | pub const ConnectError = Allocator.Error || error{ ConnectionFailed, TlsInitializationFailed }; |
| 873 | 879 | ||
| 880 | /// Connect to `host:port` using the specified protocol. This will reuse a connection if one is already open. | ||
| 881 | /// This function is threadsafe. | ||
| 874 | pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) ConnectError!*ConnectionPool.Node { | 882 | pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) ConnectError!*ConnectionPool.Node { |
| 875 | if (client.connection_pool.findConnection(.{ | 883 | if (client.connection_pool.findConnection(.{ |
| 876 | .host = host, | 884 | .host = host, |
| ... | @@ -955,6 +963,8 @@ pub const protocol_map = std.ComptimeStringMap(Connection.Protocol, .{ | ... | @@ -955,6 +963,8 @@ pub const protocol_map = std.ComptimeStringMap(Connection.Protocol, .{ |
| 955 | .{ "wss", .tls }, | 963 | .{ "wss", .tls }, |
| 956 | }); | 964 | }); |
| 957 | 965 | ||
| 966 | /// Form and send a http request to a server. | ||
| 967 | /// This function is threadsafe. | ||
| 958 | pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Options) RequestError!Request { | 968 | pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Options) RequestError!Request { |
| 959 | const protocol = protocol_map.get(uri.scheme) orelse return error.UnsupportedUrlScheme; | 969 | const protocol = protocol_map.get(uri.scheme) orelse return error.UnsupportedUrlScheme; |
| 960 | 970 |
lib/std/http/Server.zig+22-7| ... | @@ -14,10 +14,7 @@ allocator: Allocator, | ... | @@ -14,10 +14,7 @@ allocator: Allocator, |
| 14 | 14 | ||
| 15 | socket: net.StreamServer, | 15 | socket: net.StreamServer, |
| 16 | 16 | ||
| 17 | pub const DeflateDecompressor = std.compress.zlib.ZlibStream(Response.TransferReader); | 17 | /// An interface to either a plain or TLS connection. |
| 18 | pub const GzipDecompressor = std.compress.gzip.Decompress(Response.TransferReader); | ||
| 19 | pub const ZstdDecompressor = std.compress.zstd.DecompressStream(Response.TransferReader, .{}); | ||
| 20 | |||
| 21 | pub const Connection = struct { | 18 | pub const Connection = struct { |
| 22 | stream: net.Stream, | 19 | stream: net.Stream, |
| 23 | protocol: Protocol, | 20 | protocol: Protocol, |
| ... | @@ -74,6 +71,7 @@ pub const Connection = struct { | ... | @@ -74,6 +71,7 @@ pub const Connection = struct { |
| 74 | } | 71 | } |
| 75 | }; | 72 | }; |
| 76 | 73 | ||
| 74 | /// A buffered (and peekable) Connection. | ||
| 77 | pub const BufferedConnection = struct { | 75 | pub const BufferedConnection = struct { |
| 78 | pub const buffer_size = 0x2000; | 76 | pub const buffer_size = 0x2000; |
| 79 | 77 | ||
| ... | @@ -157,6 +155,7 @@ pub const BufferedConnection = struct { | ... | @@ -157,6 +155,7 @@ pub const BufferedConnection = struct { |
| 157 | } | 155 | } |
| 158 | }; | 156 | }; |
| 159 | 157 | ||
| 158 | /// A HTTP request originating from a client. | ||
| 160 | pub const Request = struct { | 159 | pub const Request = struct { |
| 161 | pub const Headers = struct { | 160 | pub const Headers = struct { |
| 162 | method: http.Method, | 161 | method: http.Method, |
| ... | @@ -290,6 +289,11 @@ pub const Request = struct { | ... | @@ -290,6 +289,11 @@ pub const Request = struct { |
| 290 | compression: Compression = .none, | 289 | compression: Compression = .none, |
| 291 | }; | 290 | }; |
| 292 | 291 | ||
| 292 | /// A HTTP response waiting to be sent. | ||
| 293 | /// | ||
| 294 | /// [/ <----------------------------------- \] | ||
| 295 | /// Order of operations: accept -> wait -> do [ -> write -> finish][ -> reset /] | ||
| 296 | /// \ -> read / | ||
| 293 | pub const Response = struct { | 297 | pub const Response = struct { |
| 294 | pub const Headers = struct { | 298 | pub const Headers = struct { |
| 295 | version: http.Version = .@"HTTP/1.1", | 299 | version: http.Version = .@"HTTP/1.1", |
| ... | @@ -310,6 +314,7 @@ pub const Response = struct { | ... | @@ -310,6 +314,7 @@ pub const Response = struct { |
| 310 | headers: Headers = .{}, | 314 | headers: Headers = .{}, |
| 311 | request: Request, | 315 | request: Request, |
| 312 | 316 | ||
| 317 | /// Reset this response to its initial state. This must be called before handling a second request on the same connection. | ||
| 313 | pub fn reset(res: *Response) void { | 318 | pub fn reset(res: *Response) void { |
| 314 | switch (res.request.compression) { | 319 | switch (res.request.compression) { |
| 315 | .none => {}, | 320 | .none => {}, |
| ... | @@ -336,7 +341,8 @@ pub const Response = struct { | ... | @@ -336,7 +341,8 @@ pub const Response = struct { |
| 336 | } | 341 | } |
| 337 | } | 342 | } |
| 338 | 343 | ||
| 339 | pub fn sendResponseHead(res: *Response) !void { | 344 | /// Send the response headers. |
| 345 | pub fn do(res: *Response) !void { | ||
| 340 | var buffered = std.io.bufferedWriter(res.connection.writer()); | 346 | var buffered = std.io.bufferedWriter(res.connection.writer()); |
| 341 | const w = buffered.writer(); | 347 | const w = buffered.writer(); |
| 342 | 348 | ||
| ... | @@ -402,7 +408,8 @@ pub const Response = struct { | ... | @@ -402,7 +408,8 @@ pub const Response = struct { |
| 402 | 408 | ||
| 403 | pub const WaitForCompleteHeadError = BufferedConnection.ReadError || proto.HeadersParser.WaitForCompleteHeadError || Request.Headers.ParseError || error{ BadHeader, InvalidCompression, StreamTooLong, InvalidWindowSize } || error{CompressionNotSupported}; | 409 | pub const WaitForCompleteHeadError = BufferedConnection.ReadError || proto.HeadersParser.WaitForCompleteHeadError || Request.Headers.ParseError || error{ BadHeader, InvalidCompression, StreamTooLong, InvalidWindowSize } || error{CompressionNotSupported}; |
| 404 | 410 | ||
| 405 | pub fn waitForCompleteHead(res: *Response) !void { | 411 | /// Wait for the client to send a complete request head. |
| 412 | pub fn wait(res: *Response) !void { | ||
| 406 | while (true) { | 413 | while (true) { |
| 407 | try res.connection.fill(); | 414 | try res.connection.fill(); |
| 408 | 415 | ||
| ... | @@ -451,7 +458,7 @@ pub const Response = struct { | ... | @@ -451,7 +458,7 @@ pub const Response = struct { |
| 451 | } | 458 | } |
| 452 | } | 459 | } |
| 453 | 460 | ||
| 454 | pub const ReadError = DeflateDecompressor.Error || GzipDecompressor.Error || ZstdDecompressor.Error || WaitForCompleteHeadError; | 461 | pub const ReadError = Compression.DeflateDecompressor.Error || Compression.GzipDecompressor.Error || Compression.ZstdDecompressor.Error || WaitForCompleteHeadError; |
| 455 | 462 | ||
| 456 | pub const Reader = std.io.Reader(*Response, ReadError, read); | 463 | pub const Reader = std.io.Reader(*Response, ReadError, read); |
| 457 | 464 | ||
| ... | @@ -517,13 +524,19 @@ pub const Response = struct { | ... | @@ -517,13 +524,19 @@ pub const Response = struct { |
| 517 | } | 524 | } |
| 518 | }; | 525 | }; |
| 519 | 526 | ||
| 527 | /// The mode of transport for responses. | ||
| 520 | pub const RequestTransfer = union(enum) { | 528 | pub const RequestTransfer = union(enum) { |
| 521 | content_length: u64, | 529 | content_length: u64, |
| 522 | chunked: void, | 530 | chunked: void, |
| 523 | none: void, | 531 | none: void, |
| 524 | }; | 532 | }; |
| 525 | 533 | ||
| 534 | /// The decompressor for request messages. | ||
| 526 | pub const Compression = union(enum) { | 535 | pub const Compression = union(enum) { |
| 536 | pub const DeflateDecompressor = std.compress.zlib.ZlibStream(Response.TransferReader); | ||
| 537 | pub const GzipDecompressor = std.compress.gzip.Decompress(Response.TransferReader); | ||
| 538 | pub const ZstdDecompressor = std.compress.zstd.DecompressStream(Response.TransferReader, .{}); | ||
| 539 | |||
| 527 | deflate: DeflateDecompressor, | 540 | deflate: DeflateDecompressor, |
| 528 | gzip: GzipDecompressor, | 541 | gzip: GzipDecompressor, |
| 529 | zstd: ZstdDecompressor, | 542 | zstd: ZstdDecompressor, |
| ... | @@ -543,6 +556,7 @@ pub fn deinit(server: *Server) void { | ... | @@ -543,6 +556,7 @@ pub fn deinit(server: *Server) void { |
| 543 | 556 | ||
| 544 | pub const ListenError = std.os.SocketError || std.os.BindError || std.os.ListenError || std.os.SetSockOptError || std.os.GetSockNameError; | 557 | pub const ListenError = std.os.SocketError || std.os.BindError || std.os.ListenError || std.os.SetSockOptError || std.os.GetSockNameError; |
| 545 | 558 | ||
| 559 | /// Start the HTTP server listening on the given address. | ||
| 546 | pub fn listen(server: *Server, address: net.Address) !void { | 560 | pub fn listen(server: *Server, address: net.Address) !void { |
| 547 | try server.socket.listen(address); | 561 | try server.socket.listen(address); |
| 548 | } | 562 | } |
| ... | @@ -562,6 +576,7 @@ pub const HeaderStrategy = union(enum) { | ... | @@ -562,6 +576,7 @@ pub const HeaderStrategy = union(enum) { |
| 562 | static: []u8, | 576 | static: []u8, |
| 563 | }; | 577 | }; |
| 564 | 578 | ||
| 579 | /// Accept a new connection and allocate a Response for it. | ||
| 565 | pub fn accept(server: *Server, options: HeaderStrategy) AcceptError!*Response { | 580 | pub fn accept(server: *Server, options: HeaderStrategy) AcceptError!*Response { |
| 566 | const in = try server.socket.accept(); | 581 | const in = try server.socket.accept(); |
| 567 | 582 |
lib/std/http/protocol.zig+32-14| ... | @@ -21,6 +21,7 @@ pub const State = enum { | ... | @@ -21,6 +21,7 @@ pub const State = enum { |
| 21 | chunk_data_suffix, | 21 | chunk_data_suffix, |
| 22 | chunk_data_suffix_r, | 22 | chunk_data_suffix_r, |
| 23 | 23 | ||
| 24 | /// Returns true if the parser is in a content state (ie. not waiting for more headers). | ||
| 24 | pub fn isContent(self: State) bool { | 25 | pub fn isContent(self: State) bool { |
| 25 | return switch (self) { | 26 | return switch (self) { |
| 26 | .invalid, .start, .seen_n, .seen_r, .seen_rn, .seen_rnr => false, | 27 | .invalid, .start, .seen_n, .seen_r, .seen_rn, .seen_rnr => false, |
| ... | @@ -31,7 +32,7 @@ pub const State = enum { | ... | @@ -31,7 +32,7 @@ pub const State = enum { |
| 31 | 32 | ||
| 32 | pub const HeadersParser = struct { | 33 | pub const HeadersParser = struct { |
| 33 | state: State = .start, | 34 | state: State = .start, |
| 34 | /// Wether or not `header_bytes` is allocated or was provided as a fixed buffer. | 35 | /// Whether or not `header_bytes` is allocated or was provided as a fixed buffer. |
| 35 | header_bytes_owned: bool, | 36 | header_bytes_owned: bool, |
| 36 | /// Either a fixed buffer of len `max_header_bytes` or a dynamic buffer that can grow up to `max_header_bytes`. | 37 | /// Either a fixed buffer of len `max_header_bytes` or a dynamic buffer that can grow up to `max_header_bytes`. |
| 37 | /// Pointers into this buffer are not stable until after a message is complete. | 38 | /// Pointers into this buffer are not stable until after a message is complete. |
| ... | @@ -39,10 +40,11 @@ pub const HeadersParser = struct { | ... | @@ -39,10 +40,11 @@ pub const HeadersParser = struct { |
| 39 | /// The maximum allowed size of `header_bytes`. | 40 | /// The maximum allowed size of `header_bytes`. |
| 40 | max_header_bytes: usize, | 41 | max_header_bytes: usize, |
| 41 | next_chunk_length: u64 = 0, | 42 | next_chunk_length: u64 = 0, |
| 42 | /// Wether this parser is done parsing a complete message. | 43 | /// Whether this parser is done parsing a complete message. |
| 43 | /// A message is only done when the entire payload has been read | 44 | /// A message is only done when the entire payload has been read. |
| 44 | done: bool = false, | 45 | done: bool = false, |
| 45 | 46 | ||
| 47 | /// Initializes the parser with a dynamically growing header buffer of up to `max` bytes. | ||
| 46 | pub fn initDynamic(max: usize) HeadersParser { | 48 | pub fn initDynamic(max: usize) HeadersParser { |
| 47 | return .{ | 49 | return .{ |
| 48 | .header_bytes = .{}, | 50 | .header_bytes = .{}, |
| ... | @@ -51,6 +53,7 @@ pub const HeadersParser = struct { | ... | @@ -51,6 +53,7 @@ pub const HeadersParser = struct { |
| 51 | }; | 53 | }; |
| 52 | } | 54 | } |
| 53 | 55 | ||
| 56 | /// Initializes the parser with a provided buffer `buf`. | ||
| 54 | pub fn initStatic(buf: []u8) HeadersParser { | 57 | pub fn initStatic(buf: []u8) HeadersParser { |
| 55 | return .{ | 58 | return .{ |
| 56 | .header_bytes = .{ .items = buf[0..0], .capacity = buf.len }, | 59 | .header_bytes = .{ .items = buf[0..0], .capacity = buf.len }, |
| ... | @@ -59,7 +62,11 @@ pub const HeadersParser = struct { | ... | @@ -59,7 +62,11 @@ pub const HeadersParser = struct { |
| 59 | }; | 62 | }; |
| 60 | } | 63 | } |
| 61 | 64 | ||
| 65 | /// Completely resets the parser to it's initial state. | ||
| 66 | /// This must be called after a message is complete. | ||
| 62 | pub fn reset(r: *HeadersParser) void { | 67 | pub fn reset(r: *HeadersParser) void { |
| 68 | assert(r.done); // The message must be completely read before reset, otherwise the parser is in an invalid state. | ||
| 69 | |||
| 63 | r.header_bytes.clearRetainingCapacity(); | 70 | r.header_bytes.clearRetainingCapacity(); |
| 64 | 71 | ||
| 65 | r.* = .{ | 72 | r.* = .{ |
| ... | @@ -69,13 +76,14 @@ pub const HeadersParser = struct { | ... | @@ -69,13 +76,14 @@ pub const HeadersParser = struct { |
| 69 | }; | 76 | }; |
| 70 | } | 77 | } |
| 71 | 78 | ||
| 72 | /// Returns how many bytes are part of HTTP headers. Always less than or | 79 | /// Returns the number of bytes consumed by headers. This is always less than or equal to `bytes.len`. |
| 73 | /// equal to bytes.len. If the amount returned is less than bytes.len, it | 80 | /// You should check `r.state.isContent()` after this to check if the headers are done. |
| 74 | /// means the headers ended and the first byte after the double \r\n\r\n is | 81 | /// |
| 75 | /// located at `bytes[result]`. | 82 | /// If the amount returned is less than `bytes.len`, you may assume that the parser is in a content state and the |
| 83 | /// first byte of content is located at `bytes[result]`. | ||
| 76 | pub fn findHeadersEnd(r: *HeadersParser, bytes: []const u8) u32 { | 84 | pub fn findHeadersEnd(r: *HeadersParser, bytes: []const u8) u32 { |
| 77 | const vector_len = 16; | 85 | const vector_len: comptime_int = comptime std.simd.suggestVectorSize(u8) orelse 8; |
| 78 | const len = @truncate(u32, bytes.len); | 86 | const len = @intCast(u32, bytes.len); |
| 79 | var index: u32 = 0; | 87 | var index: u32 = 0; |
| 80 | 88 | ||
| 81 | while (true) { | 89 | while (true) { |
| ... | @@ -390,8 +398,13 @@ pub const HeadersParser = struct { | ... | @@ -390,8 +398,13 @@ pub const HeadersParser = struct { |
| 390 | } | 398 | } |
| 391 | } | 399 | } |
| 392 | 400 | ||
| 401 | /// Returns the number of bytes consumed by the chunk size. This is always less than or equal to `bytes.len`. | ||
| 402 | /// You should check `r.state == .chunk_data` after this to check if the chunk size has been fully parsed. | ||
| 403 | /// | ||
| 404 | /// If the amount returned is less than `bytes.len`, you may assume that the parser is in the `chunk_data` state | ||
| 405 | /// and that the first byte of the chunk is at `bytes[result]`. | ||
| 393 | pub fn findChunkedLen(r: *HeadersParser, bytes: []const u8) u32 { | 406 | pub fn findChunkedLen(r: *HeadersParser, bytes: []const u8) u32 { |
| 394 | const len = @truncate(u32, bytes.len); | 407 | const len = @intCast(u32, bytes.len); |
| 395 | 408 | ||
| 396 | for (bytes[0..], 0..) |c, i| { | 409 | for (bytes[0..], 0..) |c, i| { |
| 397 | const index = @intCast(u32, i); | 410 | const index = @intCast(u32, i); |
| ... | @@ -471,8 +484,10 @@ pub const HeadersParser = struct { | ... | @@ -471,8 +484,10 @@ pub const HeadersParser = struct { |
| 471 | 484 | ||
| 472 | pub const CheckCompleteHeadError = mem.Allocator.Error || error{HttpHeadersExceededSizeLimit}; | 485 | pub const CheckCompleteHeadError = mem.Allocator.Error || error{HttpHeadersExceededSizeLimit}; |
| 473 | 486 | ||
| 474 | /// Pumps `in` bytes into the parser. Returns the number of bytes consumed. This function will return 0 if the parser | 487 | /// Pushes `in` into the parser. Returns the number of bytes consumed by the header. Any header bytes are appended |
| 475 | /// is not in a state to parse more headers. | 488 | /// to the `header_bytes` buffer. |
| 489 | /// | ||
| 490 | /// This function only uses `allocator` if `r.header_bytes_owned` is true, and may be undefined otherwise. | ||
| 476 | pub fn checkCompleteHead(r: *HeadersParser, allocator: std.mem.Allocator, in: []const u8) CheckCompleteHeadError!u32 { | 491 | pub fn checkCompleteHead(r: *HeadersParser, allocator: std.mem.Allocator, in: []const u8) CheckCompleteHeadError!u32 { |
| 477 | if (r.state.isContent()) return 0; | 492 | if (r.state.isContent()) return 0; |
| 478 | 493 | ||
| ... | @@ -493,8 +508,11 @@ pub const HeadersParser = struct { | ... | @@ -493,8 +508,11 @@ pub const HeadersParser = struct { |
| 493 | HttpChunkInvalid, | 508 | HttpChunkInvalid, |
| 494 | }; | 509 | }; |
| 495 | 510 | ||
| 496 | /// Reads the body of the message into `buffer`. If `skip` is true, the buffer will be unused and the body will be | 511 | /// Reads the body of the message into `buffer`. Returns the number of bytes placed in the buffer. |
| 497 | /// skipped. Returns the number of bytes placed in the buffer. | 512 | /// |
| 513 | /// If `skip` is true, the buffer will be unused and the body will be skipped. | ||
| 514 | /// | ||
| 515 | /// See `std.http.Client.BufferedConnection for an example of `bconn`. | ||
| 498 | pub fn read(r: *HeadersParser, bconn: anytype, buffer: []u8, skip: bool) !usize { | 516 | pub fn read(r: *HeadersParser, bconn: anytype, buffer: []u8, skip: bool) !usize { |
| 499 | assert(r.state.isContent()); | 517 | assert(r.state.isContent()); |
| 500 | if (r.done) return 0; | 518 | if (r.done) return 0; |