| author | |
| committer | |
| log | e62b0773cca027e720db1b18ce3905b7fe24ca96 |
| tree | 18e55b90c5c45a5191080dd7407d733c48b6e383 |
| parent | 81713f20a6635c6905e81daa0a061e43c85db277 |
| signature |
3 files changed, 184 insertions(+), 2 deletions(-)
lib/std/http/Client.zig+98| ... | @@ -551,6 +551,43 @@ pub const Response = struct { | ... | @@ -551,6 +551,43 @@ pub const Response = struct { |
| 551 | return error.HttpHeadersInvalid; // missing empty line | 551 | return error.HttpHeadersInvalid; // missing empty line |
| 552 | } | 552 | } |
| 553 | 553 | ||
| 554 | test parse { | ||
| 555 | const response_bytes = "HTTP/1.1 200 OK\r\n" ++ | ||
| 556 | "LOcation:url\r\n" ++ | ||
| 557 | "content-tYpe: text/plain\r\n" ++ | ||
| 558 | "content-disposition:attachment; filename=example.txt \r\n" ++ | ||
| 559 | "content-Length:10\r\n" ++ | ||
| 560 | "TRansfer-encoding:\tdeflate, chunked \r\n" ++ | ||
| 561 | "connectioN:\t keep-alive \r\n\r\n"; | ||
| 562 | |||
| 563 | var header_buffer: [1024]u8 = undefined; | ||
| 564 | var res = Response{ | ||
| 565 | .status = undefined, | ||
| 566 | .reason = undefined, | ||
| 567 | .version = undefined, | ||
| 568 | .keep_alive = false, | ||
| 569 | .parser = proto.HeadersParser.init(&header_buffer), | ||
| 570 | }; | ||
| 571 | |||
| 572 | @memcpy(header_buffer[0..response_bytes.len], response_bytes); | ||
| 573 | res.parser.header_bytes_len = response_bytes.len; | ||
| 574 | |||
| 575 | try res.parse(response_bytes); | ||
| 576 | |||
| 577 | try testing.expectEqual(.@"HTTP/1.1", res.version); | ||
| 578 | try testing.expectEqualStrings("OK", res.reason); | ||
| 579 | try testing.expectEqual(.ok, res.status); | ||
| 580 | |||
| 581 | try testing.expectEqualStrings("url", res.location.?); | ||
| 582 | try testing.expectEqualStrings("text/plain", res.content_type.?); | ||
| 583 | try testing.expectEqualStrings("attachment; filename=example.txt", res.content_disposition.?); | ||
| 584 | |||
| 585 | try testing.expectEqual(true, res.keep_alive); | ||
| 586 | try testing.expectEqual(10, res.content_length.?); | ||
| 587 | try testing.expectEqual(.chunked, res.transfer_encoding); | ||
| 588 | try testing.expectEqual(.deflate, res.transfer_compression); | ||
| 589 | } | ||
| 590 | |||
| 554 | inline fn int64(array: *const [8]u8) u64 { | 591 | inline fn int64(array: *const [8]u8) u64 { |
| 555 | return @bitCast(array.*); | 592 | return @bitCast(array.*); |
| 556 | } | 593 | } |
| ... | @@ -575,6 +612,67 @@ pub const Response = struct { | ... | @@ -575,6 +612,67 @@ pub const Response = struct { |
| 575 | pub fn iterateHeaders(r: Response) http.HeaderIterator { | 612 | pub fn iterateHeaders(r: Response) http.HeaderIterator { |
| 576 | return http.HeaderIterator.init(r.parser.get()); | 613 | return http.HeaderIterator.init(r.parser.get()); |
| 577 | } | 614 | } |
| 615 | |||
| 616 | test iterateHeaders { | ||
| 617 | const response_bytes = "HTTP/1.1 200 OK\r\n" ++ | ||
| 618 | "LOcation:url\r\n" ++ | ||
| 619 | "content-tYpe: text/plain\r\n" ++ | ||
| 620 | "content-disposition:attachment; filename=example.txt \r\n" ++ | ||
| 621 | "content-Length:10\r\n" ++ | ||
| 622 | "TRansfer-encoding:\tdeflate, chunked \r\n" ++ | ||
| 623 | "connectioN:\t keep-alive \r\n\r\n"; | ||
| 624 | |||
| 625 | var header_buffer: [1024]u8 = undefined; | ||
| 626 | var res = Response{ | ||
| 627 | .status = undefined, | ||
| 628 | .reason = undefined, | ||
| 629 | .version = undefined, | ||
| 630 | .keep_alive = false, | ||
| 631 | .parser = proto.HeadersParser.init(&header_buffer), | ||
| 632 | }; | ||
| 633 | |||
| 634 | @memcpy(header_buffer[0..response_bytes.len], response_bytes); | ||
| 635 | res.parser.header_bytes_len = response_bytes.len; | ||
| 636 | |||
| 637 | var it = res.iterateHeaders(); | ||
| 638 | { | ||
| 639 | const header = it.next().?; | ||
| 640 | try testing.expectEqualStrings("LOcation", header.name); | ||
| 641 | try testing.expectEqualStrings("url", header.value); | ||
| 642 | try testing.expect(!it.is_trailer); | ||
| 643 | } | ||
| 644 | { | ||
| 645 | const header = it.next().?; | ||
| 646 | try testing.expectEqualStrings("content-tYpe", header.name); | ||
| 647 | try testing.expectEqualStrings("text/plain", header.value); | ||
| 648 | try testing.expect(!it.is_trailer); | ||
| 649 | } | ||
| 650 | { | ||
| 651 | const header = it.next().?; | ||
| 652 | try testing.expectEqualStrings("content-disposition", header.name); | ||
| 653 | try testing.expectEqualStrings("attachment; filename=example.txt", header.value); | ||
| 654 | try testing.expect(!it.is_trailer); | ||
| 655 | } | ||
| 656 | { | ||
| 657 | const header = it.next().?; | ||
| 658 | try testing.expectEqualStrings("content-Length", header.name); | ||
| 659 | try testing.expectEqualStrings("10", header.value); | ||
| 660 | try testing.expect(!it.is_trailer); | ||
| 661 | } | ||
| 662 | { | ||
| 663 | const header = it.next().?; | ||
| 664 | try testing.expectEqualStrings("TRansfer-encoding", header.name); | ||
| 665 | try testing.expectEqualStrings("deflate, chunked", header.value); | ||
| 666 | try testing.expect(!it.is_trailer); | ||
| 667 | } | ||
| 668 | { | ||
| 669 | const header = it.next().?; | ||
| 670 | try testing.expectEqualStrings("connectioN", header.name); | ||
| 671 | try testing.expectEqualStrings("keep-alive", header.value); | ||
| 672 | try testing.expect(!it.is_trailer); | ||
| 673 | } | ||
| 674 | try testing.expectEqual(null, it.next()); | ||
| 675 | } | ||
| 578 | }; | 676 | }; |
| 579 | 677 | ||
| 580 | /// A HTTP request that has been sent. | 678 | /// A HTTP request that has been sent. |
lib/std/http/Server.zig+84| ... | @@ -271,6 +271,29 @@ pub const Request = struct { | ... | @@ -271,6 +271,29 @@ pub const Request = struct { |
| 271 | return error.MissingFinalNewline; | 271 | return error.MissingFinalNewline; |
| 272 | } | 272 | } |
| 273 | 273 | ||
| 274 | test parse { | ||
| 275 | const request_bytes = "GET /hi HTTP/1.0\r\n" ++ | ||
| 276 | "content-tYpe: text/plain\r\n" ++ | ||
| 277 | "content-Length:10\r\n" ++ | ||
| 278 | "expeCt: 100-continue \r\n" ++ | ||
| 279 | "TRansfer-encoding:\tdeflate, chunked \r\n" ++ | ||
| 280 | "connectioN:\t keep-alive \r\n\r\n"; | ||
| 281 | |||
| 282 | const req = try parse(request_bytes); | ||
| 283 | |||
| 284 | try testing.expectEqual(.GET, req.method); | ||
| 285 | try testing.expectEqual(.@"HTTP/1.0", req.version); | ||
| 286 | try testing.expectEqualStrings("/hi", req.target); | ||
| 287 | |||
| 288 | try testing.expectEqualStrings("text/plain", req.content_type.?); | ||
| 289 | try testing.expectEqualStrings("100-continue", req.expect.?); | ||
| 290 | |||
| 291 | try testing.expectEqual(true, req.keep_alive); | ||
| 292 | try testing.expectEqual(10, req.content_length.?); | ||
| 293 | try testing.expectEqual(.chunked, req.transfer_encoding); | ||
| 294 | try testing.expectEqual(.deflate, req.transfer_compression); | ||
| 295 | } | ||
| 296 | |||
| 274 | inline fn int64(array: *const [8]u8) u64 { | 297 | inline fn int64(array: *const [8]u8) u64 { |
| 275 | return @bitCast(array.*); | 298 | return @bitCast(array.*); |
| 276 | } | 299 | } |
| ... | @@ -280,6 +303,66 @@ pub const Request = struct { | ... | @@ -280,6 +303,66 @@ pub const Request = struct { |
| 280 | return http.HeaderIterator.init(r.server.read_buffer[0..r.head_end]); | 303 | return http.HeaderIterator.init(r.server.read_buffer[0..r.head_end]); |
| 281 | } | 304 | } |
| 282 | 305 | ||
| 306 | test iterateHeaders { | ||
| 307 | const request_bytes = "GET /hi HTTP/1.0\r\n" ++ | ||
| 308 | "content-tYpe: text/plain\r\n" ++ | ||
| 309 | "content-Length:10\r\n" ++ | ||
| 310 | "expeCt: 100-continue \r\n" ++ | ||
| 311 | "TRansfer-encoding:\tdeflate, chunked \r\n" ++ | ||
| 312 | "connectioN:\t keep-alive \r\n\r\n"; | ||
| 313 | |||
| 314 | var read_buffer: [500]u8 = undefined; | ||
| 315 | @memcpy(read_buffer[0..request_bytes.len], request_bytes); | ||
| 316 | |||
| 317 | var server: Server = .{ | ||
| 318 | .connection = undefined, | ||
| 319 | .state = .ready, | ||
| 320 | .read_buffer = &read_buffer, | ||
| 321 | .read_buffer_len = request_bytes.len, | ||
| 322 | .next_request_start = 0, | ||
| 323 | }; | ||
| 324 | |||
| 325 | var request: Request = .{ | ||
| 326 | .server = &server, | ||
| 327 | .head_end = request_bytes.len, | ||
| 328 | .head = undefined, | ||
| 329 | .reader_state = undefined, | ||
| 330 | }; | ||
| 331 | |||
| 332 | var it = request.iterateHeaders(); | ||
| 333 | { | ||
| 334 | const header = it.next().?; | ||
| 335 | try testing.expectEqualStrings("content-tYpe", header.name); | ||
| 336 | try testing.expectEqualStrings("text/plain", header.value); | ||
| 337 | try testing.expect(!it.is_trailer); | ||
| 338 | } | ||
| 339 | { | ||
| 340 | const header = it.next().?; | ||
| 341 | try testing.expectEqualStrings("content-Length", header.name); | ||
| 342 | try testing.expectEqualStrings("10", header.value); | ||
| 343 | try testing.expect(!it.is_trailer); | ||
| 344 | } | ||
| 345 | { | ||
| 346 | const header = it.next().?; | ||
| 347 | try testing.expectEqualStrings("expeCt", header.name); | ||
| 348 | try testing.expectEqualStrings("100-continue", header.value); | ||
| 349 | try testing.expect(!it.is_trailer); | ||
| 350 | } | ||
| 351 | { | ||
| 352 | const header = it.next().?; | ||
| 353 | try testing.expectEqualStrings("TRansfer-encoding", header.name); | ||
| 354 | try testing.expectEqualStrings("deflate, chunked", header.value); | ||
| 355 | try testing.expect(!it.is_trailer); | ||
| 356 | } | ||
| 357 | { | ||
| 358 | const header = it.next().?; | ||
| 359 | try testing.expectEqualStrings("connectioN", header.name); | ||
| 360 | try testing.expectEqualStrings("keep-alive", header.value); | ||
| 361 | try testing.expect(!it.is_trailer); | ||
| 362 | } | ||
| 363 | try testing.expectEqual(null, it.next()); | ||
| 364 | } | ||
| 365 | |||
| 283 | pub const RespondOptions = struct { | 366 | pub const RespondOptions = struct { |
| 284 | version: http.Version = .@"HTTP/1.1", | 367 | version: http.Version = .@"HTTP/1.1", |
| 285 | status: http.Status = .ok, | 368 | status: http.Status = .ok, |
| ... | @@ -1060,5 +1143,6 @@ const mem = std.mem; | ... | @@ -1060,5 +1143,6 @@ const mem = std.mem; |
| 1060 | const net = std.net; | 1143 | const net = std.net; |
| 1061 | const Uri = std.Uri; | 1144 | const Uri = std.Uri; |
| 1062 | const assert = std.debug.assert; | 1145 | const assert = std.debug.assert; |
| 1146 | const testing = std.testing; | ||
| 1063 | 1147 | ||
| 1064 | const Server = @This(); | 1148 | const Server = @This(); |
lib/std/http/test.zig+2-2| ... | @@ -328,8 +328,8 @@ test "receiving arbitrary http headers from the client" { | ... | @@ -328,8 +328,8 @@ test "receiving arbitrary http headers from the client" { |
| 328 | defer test_server.destroy(); | 328 | defer test_server.destroy(); |
| 329 | 329 | ||
| 330 | const request_bytes = "GET /bar HTTP/1.1\r\n" ++ | 330 | const request_bytes = "GET /bar HTTP/1.1\r\n" ++ |
| 331 | "CoNneCtIoN: close\r\n" ++ | 331 | "CoNneCtIoN:close\r\n" ++ |
| 332 | "aoeu: asdf\r\n" ++ | 332 | "aoeu: asdf \r\n" ++ |
| 333 | "\r\n"; | 333 | "\r\n"; |
| 334 | const gpa = std.testing.allocator; | 334 | const gpa = std.testing.allocator; |
| 335 | const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port()); | 335 | const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port()); |