| 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 | 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 | 591 | inline fn int64(array: *const [8]u8) u64 { |
| 555 | 592 | return @bitCast(array.*); |
| 556 | 593 | } |
| ... | ... | @@ -575,6 +612,67 @@ pub const Response = struct { |
| 575 | 612 | pub fn iterateHeaders(r: Response) http.HeaderIterator { |
| 576 | 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 | 678 | /// A HTTP request that has been sent. |
lib/std/http/Server.zig+84| ... | ... | @@ -271,6 +271,29 @@ pub const Request = struct { |
| 271 | 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 | 297 | inline fn int64(array: *const [8]u8) u64 { |
| 275 | 298 | return @bitCast(array.*); |
| 276 | 299 | } |
| ... | ... | @@ -280,6 +303,66 @@ pub const Request = struct { |
| 280 | 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 | 366 | pub const RespondOptions = struct { |
| 284 | 367 | version: http.Version = .@"HTTP/1.1", |
| 285 | 368 | status: http.Status = .ok, |
| ... | ... | @@ -1060,5 +1143,6 @@ const mem = std.mem; |
| 1060 | 1143 | const net = std.net; |
| 1061 | 1144 | const Uri = std.Uri; |
| 1062 | 1145 | const assert = std.debug.assert; |
| 1146 | const testing = std.testing; | |
| 1063 | 1147 | |
| 1064 | 1148 | const Server = @This(); |
lib/std/http/test.zig+2-2| ... | ... | @@ -328,8 +328,8 @@ test "receiving arbitrary http headers from the client" { |
| 328 | 328 | defer test_server.destroy(); |
| 329 | 329 | |
| 330 | 330 | const request_bytes = "GET /bar HTTP/1.1\r\n" ++ |
| 331 | "CoNneCtIoN: close\r\n" ++ | |
| 332 | "aoeu: asdf\r\n" ++ | |
| 331 | "CoNneCtIoN:close\r\n" ++ | |
| 332 | "aoeu: asdf \r\n" ++ | |
| 333 | 333 | "\r\n"; |
| 334 | 334 | const gpa = std.testing.allocator; |
| 335 | 335 | const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port()); |