| ... | ... | @@ -313,11 +313,20 @@ pub const Request = struct { |
| 313 | 313 | |
| 314 | 314 | var first_buffer: [500]u8 = undefined; |
| 315 | 315 | var h = std.ArrayListUnmanaged(u8).initBuffer(&first_buffer); |
| 316 | if (request.head.expect != null) { |
| 317 | // reader() and hence discardBody() above sets expect to null if it |
| 318 | // is handled. So the fact that it is not null here means unhandled. |
| 319 | h.appendSliceAssumeCapacity("HTTP/1.1 417 Expectation Failed\r\n"); |
| 320 | if (keep_alive) h.appendSliceAssumeCapacity("connection: keep-alive\r\n"); |
| 321 | h.appendSliceAssumeCapacity("content-length: 0\r\n\r\n"); |
| 322 | try request.server.connection.stream.writeAll(h.items); |
| 323 | return; |
| 324 | } |
| 316 | 325 | h.fixedWriter().print("{s} {d} {s}\r\n", .{ |
| 317 | 326 | @tagName(options.version), @intFromEnum(options.status), phrase, |
| 318 | 327 | }) catch unreachable; |
| 319 | | if (keep_alive) |
| 320 | | h.appendSliceAssumeCapacity("connection: keep-alive\r\n"); |
| 328 | |
| 329 | if (keep_alive) h.appendSliceAssumeCapacity("connection: keep-alive\r\n"); |
| 321 | 330 | |
| 322 | 331 | if (options.transfer_encoding) |transfer_encoding| switch (transfer_encoding) { |
| 323 | 332 | .none => {}, |
| ... | ... | @@ -452,25 +461,35 @@ pub const Request = struct { |
| 452 | 461 | |
| 453 | 462 | var h = std.ArrayListUnmanaged(u8).initBuffer(options.send_buffer); |
| 454 | 463 | |
| 455 | | h.fixedWriter().print("{s} {d} {s}\r\n", .{ |
| 456 | | @tagName(o.version), @intFromEnum(o.status), phrase, |
| 457 | | }) catch unreachable; |
| 458 | | if (keep_alive) h.appendSliceAssumeCapacity("connection: keep-alive\r\n"); |
| 464 | const elide_body = if (request.head.expect != null) eb: { |
| 465 | // reader() and hence discardBody() above sets expect to null if it |
| 466 | // is handled. So the fact that it is not null here means unhandled. |
| 467 | h.appendSliceAssumeCapacity("HTTP/1.1 417 Expectation Failed\r\n"); |
| 468 | if (keep_alive) h.appendSliceAssumeCapacity("connection: keep-alive\r\n"); |
| 469 | h.appendSliceAssumeCapacity("content-length: 0\r\n\r\n"); |
| 470 | break :eb true; |
| 471 | } else eb: { |
| 472 | h.fixedWriter().print("{s} {d} {s}\r\n", .{ |
| 473 | @tagName(o.version), @intFromEnum(o.status), phrase, |
| 474 | }) catch unreachable; |
| 475 | if (keep_alive) h.appendSliceAssumeCapacity("connection: keep-alive\r\n"); |
| 476 | |
| 477 | if (options.content_length) |len| { |
| 478 | h.fixedWriter().print("content-length: {d}\r\n", .{len}) catch unreachable; |
| 479 | } else { |
| 480 | h.appendSliceAssumeCapacity("transfer-encoding: chunked\r\n"); |
| 481 | } |
| 459 | 482 | |
| 460 | | if (options.content_length) |len| { |
| 461 | | h.fixedWriter().print("content-length: {d}\r\n", .{len}) catch unreachable; |
| 462 | | } else { |
| 463 | | h.appendSliceAssumeCapacity("transfer-encoding: chunked\r\n"); |
| 464 | | } |
| 483 | for (o.extra_headers) |header| { |
| 484 | h.appendSliceAssumeCapacity(header.name); |
| 485 | h.appendSliceAssumeCapacity(": "); |
| 486 | h.appendSliceAssumeCapacity(header.value); |
| 487 | h.appendSliceAssumeCapacity("\r\n"); |
| 488 | } |
| 465 | 489 | |
| 466 | | for (o.extra_headers) |header| { |
| 467 | | h.appendSliceAssumeCapacity(header.name); |
| 468 | | h.appendSliceAssumeCapacity(": "); |
| 469 | | h.appendSliceAssumeCapacity(header.value); |
| 470 | 490 | h.appendSliceAssumeCapacity("\r\n"); |
| 471 | | } |
| 472 | | |
| 473 | | h.appendSliceAssumeCapacity("\r\n"); |
| 491 | break :eb request.head.method == .HEAD; |
| 492 | }; |
| 474 | 493 | |
| 475 | 494 | return .{ |
| 476 | 495 | .stream = request.server.connection.stream, |
| ... | ... | @@ -478,16 +497,20 @@ pub const Request = struct { |
| 478 | 497 | .send_buffer_start = 0, |
| 479 | 498 | .send_buffer_end = h.items.len, |
| 480 | 499 | .content_length = options.content_length, |
| 481 | | .elide_body = request.head.method == .HEAD, |
| 500 | .elide_body = elide_body, |
| 482 | 501 | .chunk_len = 0, |
| 483 | 502 | }; |
| 484 | 503 | } |
| 485 | 504 | |
| 486 | | pub const ReadError = net.Stream.ReadError || error{ HttpChunkInvalid, HttpHeadersOversize }; |
| 505 | pub const ReadError = net.Stream.ReadError || error{ |
| 506 | HttpChunkInvalid, |
| 507 | HttpHeadersOversize, |
| 508 | }; |
| 487 | 509 | |
| 488 | 510 | fn read_cl(context: *const anyopaque, buffer: []u8) ReadError!usize { |
| 489 | 511 | const request: *Request = @constCast(@alignCast(@ptrCast(context))); |
| 490 | 512 | const s = request.server; |
| 513 | |
| 491 | 514 | const remaining_content_length = &request.reader_state.remaining_content_length; |
| 492 | 515 | if (remaining_content_length.* == 0) { |
| 493 | 516 | s.state = .ready; |
| ... | ... | @@ -515,6 +538,7 @@ pub const Request = struct { |
| 515 | 538 | fn read_chunked(context: *const anyopaque, buffer: []u8) ReadError!usize { |
| 516 | 539 | const request: *Request = @constCast(@alignCast(@ptrCast(context))); |
| 517 | 540 | const s = request.server; |
| 541 | |
| 518 | 542 | const cp = &request.reader_state.chunk_parser; |
| 519 | 543 | const head_end = request.head_end; |
| 520 | 544 | |
| ... | ... | @@ -599,11 +623,33 @@ pub const Request = struct { |
| 599 | 623 | return out_end; |
| 600 | 624 | } |
| 601 | 625 | |
| 602 | | pub fn reader(request: *Request) std.io.AnyReader { |
| 626 | pub const ReaderError = Response.WriteError || error{ |
| 627 | /// The client sent an expect HTTP header value other than |
| 628 | /// "100-continue". |
| 629 | HttpExpectationFailed, |
| 630 | }; |
| 631 | |
| 632 | /// In the case that the request contains "expect: 100-continue", this |
| 633 | /// function writes the continuation header, which means it can fail with a |
| 634 | /// write error. After sending the continuation header, it sets the |
| 635 | /// request's expect field to `null`. |
| 636 | /// |
| 637 | /// Asserts that this function is only called once. |
| 638 | pub fn reader(request: *Request) ReaderError!std.io.AnyReader { |
| 603 | 639 | const s = request.server; |
| 604 | 640 | assert(s.state == .received_head); |
| 605 | 641 | s.state = .receiving_body; |
| 606 | 642 | s.next_request_start = request.head_end; |
| 643 | |
| 644 | if (request.head.expect) |expect| { |
| 645 | if (mem.eql(u8, expect, "100-continue")) { |
| 646 | try request.server.connection.stream.writeAll("HTTP/1.1 100 Continue\r\n\r\n"); |
| 647 | request.head.expect = null; |
| 648 | } else { |
| 649 | return error.HttpExpectationFailed; |
| 650 | } |
| 651 | } |
| 652 | |
| 607 | 653 | switch (request.head.transfer_encoding) { |
| 608 | 654 | .chunked => { |
| 609 | 655 | request.reader_state = .{ .chunk_parser = http.ChunkParser.init }; |
| ... | ... | @@ -639,7 +685,8 @@ pub const Request = struct { |
| 639 | 685 | const s = request.server; |
| 640 | 686 | if (keep_alive and request.head.keep_alive) switch (s.state) { |
| 641 | 687 | .received_head => { |
| 642 | | _ = request.reader().discard() catch return false; |
| 688 | const r = request.reader() catch return false; |
| 689 | _ = r.discard() catch return false; |
| 643 | 690 | assert(s.state == .ready); |
| 644 | 691 | return true; |
| 645 | 692 | }, |