blob d803e3cd (5510B) - Raw
1 const builtin = @import("builtin"); 2 const std = @import("std"); 3 const testing = std.testing; 4 5 test "trailers" { 6 if (builtin.single_threaded) return error.SkipZigTest; 7 if (builtin.os.tag == .wasi) return error.SkipZigTest; 8 9 const gpa = testing.allocator; 10 11 const address = try std.net.Address.parseIp("127.0.0.1", 0); 12 var http_server = try address.listen(.{ 13 .reuse_address = true, 14 }); 15 16 const port = http_server.listen_address.in.getPort(); 17 18 const server_thread = try std.Thread.spawn(.{}, serverThread, .{&http_server}); 19 defer server_thread.join(); 20 21 var client: std.http.Client = .{ .allocator = gpa }; 22 defer client.deinit(); 23 24 const location = try std.fmt.allocPrint(gpa, "http://127.0.0.1:{d}/trailer", .{port}); 25 defer gpa.free(location); 26 const uri = try std.Uri.parse(location); 27 28 { 29 var server_header_buffer: [1024]u8 = undefined; 30 var req = try client.open(.GET, uri, .{ 31 .server_header_buffer = &server_header_buffer, 32 }); 33 defer req.deinit(); 34 35 try req.send(.{}); 36 try req.wait(); 37 38 const body = try req.reader().readAllAlloc(gpa, 8192); 39 defer gpa.free(body); 40 41 try testing.expectEqualStrings("Hello, World!\n", body); 42 43 var it = req.response.iterateHeaders(); 44 { 45 const header = it.next().?; 46 try testing.expect(!it.is_trailer); 47 try testing.expectEqualStrings("connection", header.name); 48 try testing.expectEqualStrings("keep-alive", header.value); 49 } 50 { 51 const header = it.next().?; 52 try testing.expect(!it.is_trailer); 53 try testing.expectEqualStrings("transfer-encoding", header.name); 54 try testing.expectEqualStrings("chunked", header.value); 55 } 56 { 57 const header = it.next().?; 58 try testing.expect(it.is_trailer); 59 try testing.expectEqualStrings("X-Checksum", header.name); 60 try testing.expectEqualStrings("aaaa", header.value); 61 } 62 try testing.expectEqual(null, it.next()); 63 } 64 65 // connection has been kept alive 66 try testing.expect(client.connection_pool.free_len == 1); 67 } 68 69 fn serverThread(http_server: *std.net.Server) anyerror!void { 70 var header_buffer: [1024]u8 = undefined; 71 var remaining: usize = 1; 72 while (remaining != 0) : (remaining -= 1) { 73 const conn = try http_server.accept(); 74 defer conn.stream.close(); 75 76 var server = std.http.Server.init(conn, &header_buffer); 77 78 try testing.expectEqual(.ready, server.state); 79 var request = try server.receiveHead(); 80 try serve(&request); 81 try testing.expectEqual(.ready, server.state); 82 } 83 } 84 85 fn serve(request: *std.http.Server.Request) !void { 86 try testing.expectEqualStrings(request.head.target, "/trailer"); 87 88 var send_buffer: [1024]u8 = undefined; 89 var response = request.respondStreaming(.{ 90 .send_buffer = &send_buffer, 91 }); 92 try response.writeAll("Hello, "); 93 try response.flush(); 94 try response.writeAll("World!\n"); 95 try response.flush(); 96 try response.endChunked(.{ 97 .trailers = &.{ 98 .{ .name = "X-Checksum", .value = "aaaa" }, 99 }, 100 }); 101 } 102 103 test "HTTP server handles a chunked transfer coding request" { 104 // This test requires spawning threads. 105 if (builtin.single_threaded) { 106 return error.SkipZigTest; 107 } 108 109 const native_endian = comptime builtin.cpu.arch.endian(); 110 if (builtin.zig_backend == .stage2_llvm and native_endian == .big) { 111 // https://github.com/ziglang/zig/issues/13782 112 return error.SkipZigTest; 113 } 114 115 if (builtin.os.tag == .wasi) return error.SkipZigTest; 116 117 const allocator = std.testing.allocator; 118 const expect = std.testing.expect; 119 120 const max_header_size = 8192; 121 122 const address = try std.net.Address.parseIp("127.0.0.1", 0); 123 var socket_server = try address.listen(.{ .reuse_address = true }); 124 defer socket_server.deinit(); 125 const server_port = socket_server.listen_address.in.getPort(); 126 127 const server_thread = try std.Thread.spawn(.{}, (struct { 128 fn apply(net_server: *std.net.Server) !void { 129 var header_buffer: [max_header_size]u8 = undefined; 130 const conn = try net_server.accept(); 131 defer conn.stream.close(); 132 133 var server = std.http.Server.init(conn, &header_buffer); 134 var request = try server.receiveHead(); 135 136 try expect(request.head.transfer_encoding == .chunked); 137 138 var buf: [128]u8 = undefined; 139 const n = try request.reader().readAll(&buf); 140 try expect(std.mem.eql(u8, buf[0..n], "ABCD")); 141 142 try request.respond("message from server!\n", .{ 143 .extra_headers = &.{ 144 .{ .name = "content-type", .value = "text/plain" }, 145 }, 146 .keep_alive = false, 147 }); 148 } 149 }).apply, .{&socket_server}); 150 151 const request_bytes = 152 "POST / HTTP/1.1\r\n" ++ 153 "Content-Type: text/plain\r\n" ++ 154 "Transfer-Encoding: chunked\r\n" ++ 155 "\r\n" ++ 156 "1\r\n" ++ 157 "A\r\n" ++ 158 "1\r\n" ++ 159 "B\r\n" ++ 160 "2\r\n" ++ 161 "CD\r\n" ++ 162 "0\r\n" ++ 163 "\r\n"; 164 165 const stream = try std.net.tcpConnectToHost(allocator, "127.0.0.1", server_port); 166 defer stream.close(); 167 try stream.writeAll(request_bytes); 168 169 server_thread.join(); 170 }