| ... | ... | @@ -23,21 +23,33 @@ pub const Connection = struct { |
| 23 | 23 | |
| 24 | 24 | pub const Protocol = enum { plain }; |
| 25 | 25 | |
| 26 | | pub fn read(conn: *Connection, buffer: []u8) !usize { |
| 27 | | switch (conn.protocol) { |
| 28 | | .plain => return conn.stream.read(buffer), |
| 26 | pub fn read(conn: *Connection, buffer: []u8) ReadError!usize { |
| 27 | return switch (conn.protocol) { |
| 28 | .plain => conn.stream.read(buffer), |
| 29 | 29 | // .tls => return conn.tls_client.read(conn.stream, buffer), |
| 30 | | } |
| 30 | } catch |err| switch (err) { |
| 31 | error.ConnectionTimedOut => return error.ConnectionTimedOut, |
| 32 | error.ConnectionResetByPeer, error.BrokenPipe => return error.ConnectionResetByPeer, |
| 33 | else => return error.UnexpectedReadFailure, |
| 34 | }; |
| 31 | 35 | } |
| 32 | 36 | |
| 33 | | pub fn readAtLeast(conn: *Connection, buffer: []u8, len: usize) !usize { |
| 34 | | switch (conn.protocol) { |
| 35 | | .plain => return conn.stream.readAtLeast(buffer, len), |
| 37 | pub fn readAtLeast(conn: *Connection, buffer: []u8, len: usize) ReadError!usize { |
| 38 | return switch (conn.protocol) { |
| 39 | .plain => conn.stream.readAtLeast(buffer, len), |
| 36 | 40 | // .tls => return conn.tls_client.readAtLeast(conn.stream, buffer, len), |
| 37 | | } |
| 41 | } catch |err| switch (err) { |
| 42 | error.ConnectionTimedOut => return error.ConnectionTimedOut, |
| 43 | error.ConnectionResetByPeer, error.BrokenPipe => return error.ConnectionResetByPeer, |
| 44 | else => return error.UnexpectedReadFailure, |
| 45 | }; |
| 38 | 46 | } |
| 39 | 47 | |
| 40 | | pub const ReadError = net.Stream.ReadError; |
| 48 | pub const ReadError = error{ |
| 49 | ConnectionTimedOut, |
| 50 | ConnectionResetByPeer, |
| 51 | UnexpectedReadFailure, |
| 52 | }; |
| 41 | 53 | |
| 42 | 54 | pub const Reader = std.io.Reader(*Connection, ReadError, read); |
| 43 | 55 | |
| ... | ... | @@ -45,21 +57,31 @@ pub const Connection = struct { |
| 45 | 57 | return Reader{ .context = conn }; |
| 46 | 58 | } |
| 47 | 59 | |
| 48 | | pub fn writeAll(conn: *Connection, buffer: []const u8) !void { |
| 49 | | switch (conn.protocol) { |
| 50 | | .plain => return conn.stream.writeAll(buffer), |
| 60 | pub fn writeAll(conn: *Connection, buffer: []const u8) WriteError!void { |
| 61 | return switch (conn.protocol) { |
| 62 | .plain => conn.stream.writeAll(buffer), |
| 51 | 63 | // .tls => return conn.tls_client.writeAll(conn.stream, buffer), |
| 52 | | } |
| 64 | } catch |err| switch (err) { |
| 65 | error.BrokenPipe, error.ConnectionResetByPeer => return error.ConnectionResetByPeer, |
| 66 | else => return error.UnexpectedWriteFailure, |
| 67 | }; |
| 53 | 68 | } |
| 54 | 69 | |
| 55 | | pub fn write(conn: *Connection, buffer: []const u8) !usize { |
| 56 | | switch (conn.protocol) { |
| 57 | | .plain => return conn.stream.write(buffer), |
| 70 | pub fn write(conn: *Connection, buffer: []const u8) WriteError!usize { |
| 71 | return switch (conn.protocol) { |
| 72 | .plain => conn.stream.write(buffer), |
| 58 | 73 | // .tls => return conn.tls_client.write(conn.stream, buffer), |
| 59 | | } |
| 74 | } catch |err| switch (err) { |
| 75 | error.BrokenPipe, error.ConnectionResetByPeer => return error.ConnectionResetByPeer, |
| 76 | else => return error.UnexpectedWriteFailure, |
| 77 | }; |
| 60 | 78 | } |
| 61 | 79 | |
| 62 | | pub const WriteError = net.Stream.WriteError || error{}; |
| 80 | pub const WriteError = error{ |
| 81 | ConnectionResetByPeer, |
| 82 | UnexpectedWriteFailure, |
| 83 | }; |
| 84 | |
| 63 | 85 | pub const Writer = std.io.Writer(*Connection, WriteError, write); |
| 64 | 86 | |
| 65 | 87 | pub fn writer(conn: *Connection) Writer { |
| ... | ... | @@ -155,6 +177,25 @@ pub const BufferedConnection = struct { |
| 155 | 177 | } |
| 156 | 178 | }; |
| 157 | 179 | |
| 180 | /// The mode of transport for responses. |
| 181 | pub const ResponseTransfer = union(enum) { |
| 182 | content_length: u64, |
| 183 | chunked: void, |
| 184 | none: void, |
| 185 | }; |
| 186 | |
| 187 | /// The decompressor for request messages. |
| 188 | pub const Compression = union(enum) { |
| 189 | pub const DeflateDecompressor = std.compress.zlib.ZlibStream(Response.TransferReader); |
| 190 | pub const GzipDecompressor = std.compress.gzip.Decompress(Response.TransferReader); |
| 191 | pub const ZstdDecompressor = std.compress.zstd.DecompressStream(Response.TransferReader, .{}); |
| 192 | |
| 193 | deflate: DeflateDecompressor, |
| 194 | gzip: GzipDecompressor, |
| 195 | zstd: ZstdDecompressor, |
| 196 | none: void, |
| 197 | }; |
| 198 | |
| 158 | 199 | /// A HTTP request originating from a client. |
| 159 | 200 | pub const Request = struct { |
| 160 | 201 | pub const ParseError = Allocator.Error || error{ |
| ... | ... | @@ -165,10 +206,11 @@ pub const Request = struct { |
| 165 | 206 | HttpHeaderContinuationsUnsupported, |
| 166 | 207 | HttpTransferEncodingUnsupported, |
| 167 | 208 | HttpConnectionHeaderUnsupported, |
| 168 | | InvalidCharacter, |
| 209 | InvalidContentLength, |
| 210 | CompressionNotSupported, |
| 169 | 211 | }; |
| 170 | 212 | |
| 171 | | pub fn parse(req: *Request, bytes: []const u8) !void { |
| 213 | pub fn parse(req: *Request, bytes: []const u8) ParseError!void { |
| 172 | 214 | var it = mem.tokenize(u8, bytes[0 .. bytes.len - 4], "\r\n"); |
| 173 | 215 | |
| 174 | 216 | const first_line = it.next() orelse return error.HttpHeadersInvalid; |
| ... | ... | @@ -211,7 +253,7 @@ pub const Request = struct { |
| 211 | 253 | |
| 212 | 254 | if (std.ascii.eqlIgnoreCase(header_name, "content-length")) { |
| 213 | 255 | if (req.content_length != null) return error.HttpHeadersInvalid; |
| 214 | | req.content_length = try std.fmt.parseInt(u64, header_value, 10); |
| 256 | req.content_length = std.fmt.parseInt(u64, header_value, 10) catch return error.InvalidContentLength; |
| 215 | 257 | } else if (std.ascii.eqlIgnoreCase(header_name, "transfer-encoding")) { |
| 216 | 258 | // Transfer-Encoding: second, first |
| 217 | 259 | // Transfer-Encoding: deflate, chunked |
| ... | ... | @@ -321,6 +363,8 @@ pub const Response = struct { |
| 321 | 363 | } |
| 322 | 364 | } |
| 323 | 365 | |
| 366 | pub const DoError = BufferedConnection.WriteError || error{ UnsupportedTransferEncoding, InvalidContentLength }; |
| 367 | |
| 324 | 368 | /// Send the response headers. |
| 325 | 369 | pub fn do(res: *Response) !void { |
| 326 | 370 | var buffered = std.io.bufferedWriter(res.connection.writer()); |
| ... | ... | @@ -356,7 +400,7 @@ pub const Response = struct { |
| 356 | 400 | } |
| 357 | 401 | } else { |
| 358 | 402 | if (has_content_length) { |
| 359 | | const content_length = try std.fmt.parseInt(u64, res.headers.getFirstValue("content-length").?, 10); |
| 403 | const content_length = std.fmt.parseInt(u64, res.headers.getFirstValue("content-length").?, 10) catch return error.InvalidContentLength; |
| 360 | 404 | |
| 361 | 405 | res.transfer_encoding = .{ .content_length = content_length }; |
| 362 | 406 | } else if (has_transfer_encoding) { |
| ... | ... | @@ -386,23 +430,23 @@ pub const Response = struct { |
| 386 | 430 | return .{ .context = res }; |
| 387 | 431 | } |
| 388 | 432 | |
| 389 | | pub fn transferRead(res: *Response, buf: []u8) TransferReadError!usize { |
| 390 | | if (res.request.parser.isComplete()) return 0; |
| 433 | fn transferRead(res: *Response, buf: []u8) TransferReadError!usize { |
| 434 | if (res.request.parser.done) return 0; |
| 391 | 435 | |
| 392 | 436 | var index: usize = 0; |
| 393 | 437 | while (index == 0) { |
| 394 | 438 | const amt = try res.request.parser.read(&res.connection, buf[index..], false); |
| 395 | | if (amt == 0 and res.request.parser.isComplete()) break; |
| 439 | if (amt == 0 and res.request.parser.done) break; |
| 396 | 440 | index += amt; |
| 397 | 441 | } |
| 398 | 442 | |
| 399 | 443 | return index; |
| 400 | 444 | } |
| 401 | 445 | |
| 402 | | pub const WaitForCompleteHeadError = BufferedConnection.ReadError || proto.HeadersParser.WaitForCompleteHeadError || Request.Headers.ParseError || error{ BadHeader, InvalidCompression, StreamTooLong, InvalidWindowSize } || error{CompressionNotSupported}; |
| 446 | pub const WaitError = BufferedConnection.ReadError || proto.HeadersParser.CheckCompleteHeadError || Request.ParseError || error{ CompressionInitializationFailed, CompressionNotSupported }; |
| 403 | 447 | |
| 404 | 448 | /// Wait for the client to send a complete request head. |
| 405 | | pub fn wait(res: *Response) !void { |
| 449 | pub fn wait(res: *Response) WaitError!void { |
| 406 | 450 | while (true) { |
| 407 | 451 | try res.connection.fill(); |
| 408 | 452 | |
| ... | ... | @@ -445,10 +489,10 @@ pub const Response = struct { |
| 445 | 489 | if (res.request.transfer_compression) |tc| switch (tc) { |
| 446 | 490 | .compress => return error.CompressionNotSupported, |
| 447 | 491 | .deflate => res.request.compression = .{ |
| 448 | | .deflate = try std.compress.zlib.zlibStream(res.server.allocator, res.transferReader()), |
| 492 | .deflate = std.compress.zlib.zlibStream(res.server.allocator, res.transferReader()) catch return error.CompressionInitializationFailed, |
| 449 | 493 | }, |
| 450 | 494 | .gzip => res.request.compression = .{ |
| 451 | | .gzip = try std.compress.gzip.decompress(res.server.allocator, res.transferReader()), |
| 495 | .gzip = std.compress.gzip.decompress(res.server.allocator, res.transferReader()) catch return error.CompressionInitializationFailed, |
| 452 | 496 | }, |
| 453 | 497 | .zstd => res.request.compression = .{ |
| 454 | 498 | .zstd = std.compress.zstd.decompressStream(res.server.allocator, res.transferReader()), |
| ... | ... | @@ -457,7 +501,7 @@ pub const Response = struct { |
| 457 | 501 | } |
| 458 | 502 | } |
| 459 | 503 | |
| 460 | | pub const ReadError = Compression.DeflateDecompressor.Error || Compression.GzipDecompressor.Error || Compression.ZstdDecompressor.Error || WaitForCompleteHeadError; |
| 504 | pub const ReadError = TransferReadError || proto.HeadersParser.CheckCompleteHeadError || error{DecompressionFailure}; |
| 461 | 505 | |
| 462 | 506 | pub const Reader = std.io.Reader(*Response, ReadError, read); |
| 463 | 507 | |
| ... | ... | @@ -466,12 +510,23 @@ pub const Response = struct { |
| 466 | 510 | } |
| 467 | 511 | |
| 468 | 512 | pub fn read(res: *Response, buffer: []u8) ReadError!usize { |
| 469 | | return switch (res.request.compression) { |
| 470 | | .deflate => |*deflate| try deflate.read(buffer), |
| 471 | | .gzip => |*gzip| try gzip.read(buffer), |
| 472 | | .zstd => |*zstd| try zstd.read(buffer), |
| 513 | const out_index = switch (res.request.compression) { |
| 514 | .deflate => |*deflate| deflate.read(buffer) catch return error.DecompressionFailure, |
| 515 | .gzip => |*gzip| gzip.read(buffer) catch return error.DecompressionFailure, |
| 516 | .zstd => |*zstd| zstd.read(buffer) catch return error.DecompressionFailure, |
| 473 | 517 | else => try res.transferRead(buffer), |
| 474 | 518 | }; |
| 519 | |
| 520 | if (out_index == 0) { |
| 521 | while (!res.request.parser.state.isContent()) { // read trailing headers |
| 522 | try res.connection.fill(); |
| 523 | |
| 524 | const nchecked = try res.request.parser.checkCompleteHead(res.server.allocator, res.connection.peek()); |
| 525 | res.connection.clear(@intCast(u16, nchecked)); |
| 526 | } |
| 527 | } |
| 528 | |
| 529 | return out_index; |
| 475 | 530 | } |
| 476 | 531 | |
| 477 | 532 | pub fn readAll(res: *Response, buffer: []u8) !usize { |
| ... | ... | @@ -513,9 +568,18 @@ pub const Response = struct { |
| 513 | 568 | } |
| 514 | 569 | } |
| 515 | 570 | |
| 571 | pub fn writeAll(req: *Request, bytes: []const u8) WriteError!void { |
| 572 | var index: usize = 0; |
| 573 | while (index < bytes.len) { |
| 574 | index += try write(req, bytes[index..]); |
| 575 | } |
| 576 | } |
| 577 | |
| 578 | pub const FinishError = WriteError || error{MessageNotCompleted}; |
| 579 | |
| 516 | 580 | /// Finish the body of a request. This notifies the server that you have no more data to send. |
| 517 | | pub fn finish(res: *Response) !void { |
| 518 | | switch (res.headers.transfer_encoding) { |
| 581 | pub fn finish(res: *Response) FinishError!void { |
| 582 | switch (res.transfer_encoding) { |
| 519 | 583 | .chunked => try res.connection.writeAll("0\r\n\r\n"), |
| 520 | 584 | .content_length => |len| if (len != 0) return error.MessageNotCompleted, |
| 521 | 585 | .none => {}, |
| ... | ... | @@ -523,25 +587,6 @@ pub const Response = struct { |
| 523 | 587 | } |
| 524 | 588 | }; |
| 525 | 589 | |
| 526 | | /// The mode of transport for responses. |
| 527 | | pub const ResponseTransfer = union(enum) { |
| 528 | | content_length: u64, |
| 529 | | chunked: void, |
| 530 | | none: void, |
| 531 | | }; |
| 532 | | |
| 533 | | /// The decompressor for request messages. |
| 534 | | pub const Compression = union(enum) { |
| 535 | | pub const DeflateDecompressor = std.compress.zlib.ZlibStream(Response.TransferReader); |
| 536 | | pub const GzipDecompressor = std.compress.gzip.Decompress(Response.TransferReader); |
| 537 | | pub const ZstdDecompressor = std.compress.zstd.DecompressStream(Response.TransferReader, .{}); |
| 538 | | |
| 539 | | deflate: DeflateDecompressor, |
| 540 | | gzip: GzipDecompressor, |
| 541 | | zstd: ZstdDecompressor, |
| 542 | | none: void, |
| 543 | | }; |
| 544 | | |
| 545 | 590 | pub fn init(allocator: Allocator, options: net.StreamServer.Options) Server { |
| 546 | 591 | return .{ |
| 547 | 592 | .allocator = allocator, |