| ... | @@ -1,28 +1,57 @@ | ... | @@ -1,28 +1,57 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("std"); | 2 | const std = @import("std"); |
| 3 | const testing = std.testing; | | |
| 4 | const native_endian = builtin.cpu.arch.endian(); | 3 | const native_endian = builtin.cpu.arch.endian(); |
| | 4 | const expect = std.testing.expect; |
| | 5 | const expectEqual = std.testing.expectEqual; |
| | 6 | const expectEqualStrings = std.testing.expectEqualStrings; |
| | 7 | const expectError = std.testing.expectError; |
| 5 | | 8 | |
| 6 | test "trailers" { | 9 | test "trailers" { |
| 7 | if (builtin.single_threaded) return error.SkipZigTest; | 10 | const test_server = try createTestServer(struct { |
| 8 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | 11 | fn run(net_server: *std.net.Server) anyerror!void { |
| | 12 | var header_buffer: [1024]u8 = undefined; |
| | 13 | var remaining: usize = 1; |
| | 14 | while (remaining != 0) : (remaining -= 1) { |
| | 15 | const conn = try net_server.accept(); |
| | 16 | defer conn.stream.close(); |
| 9 | | 17 | |
| 10 | const gpa = testing.allocator; | 18 | var server = std.http.Server.init(conn, &header_buffer); |
| 11 | | 19 | |
| 12 | const address = try std.net.Address.parseIp("127.0.0.1", 0); | 20 | try expectEqual(.ready, server.state); |
| 13 | var http_server = try address.listen(.{ | 21 | var request = try server.receiveHead(); |
| 14 | .reuse_address = true, | 22 | try serve(&request); |
| 15 | }); | 23 | try expectEqual(.ready, server.state); |
| | 24 | } |
| | 25 | } |
| 16 | | 26 | |
| 17 | const port = http_server.listen_address.in.getPort(); | 27 | fn serve(request: *std.http.Server.Request) !void { |
| | 28 | try expectEqualStrings(request.head.target, "/trailer"); |
| 18 | | 29 | |
| 19 | const server_thread = try std.Thread.spawn(.{}, serverThread, .{&http_server}); | 30 | var send_buffer: [1024]u8 = undefined; |
| 20 | defer server_thread.join(); | 31 | var response = request.respondStreaming(.{ |
| | 32 | .send_buffer = &send_buffer, |
| | 33 | }); |
| | 34 | try response.writeAll("Hello, "); |
| | 35 | try response.flush(); |
| | 36 | try response.writeAll("World!\n"); |
| | 37 | try response.flush(); |
| | 38 | try response.endChunked(.{ |
| | 39 | .trailers = &.{ |
| | 40 | .{ .name = "X-Checksum", .value = "aaaa" }, |
| | 41 | }, |
| | 42 | }); |
| | 43 | } |
| | 44 | }); |
| | 45 | defer test_server.destroy(); |
| | 46 | |
| | 47 | const gpa = std.testing.allocator; |
| 21 | | 48 | |
| 22 | var client: std.http.Client = .{ .allocator = gpa }; | 49 | var client: std.http.Client = .{ .allocator = gpa }; |
| 23 | defer client.deinit(); | 50 | defer client.deinit(); |
| 24 | | 51 | |
| 25 | const location = try std.fmt.allocPrint(gpa, "http://127.0.0.1:{d}/trailer", .{port}); | 52 | const location = try std.fmt.allocPrint(gpa, "http://127.0.0.1:{d}/trailer", .{ |
| | 53 | test_server.port(), |
| | 54 | }); |
| 26 | defer gpa.free(location); | 55 | defer gpa.free(location); |
| 27 | const uri = try std.Uri.parse(location); | 56 | const uri = try std.Uri.parse(location); |
| 28 | | 57 | |
| ... | @@ -39,94 +68,38 @@ test "trailers" { | ... | @@ -39,94 +68,38 @@ test "trailers" { |
| 39 | const body = try req.reader().readAllAlloc(gpa, 8192); | 68 | const body = try req.reader().readAllAlloc(gpa, 8192); |
| 40 | defer gpa.free(body); | 69 | defer gpa.free(body); |
| 41 | | 70 | |
| 42 | try testing.expectEqualStrings("Hello, World!\n", body); | 71 | try expectEqualStrings("Hello, World!\n", body); |
| 43 | | 72 | |
| 44 | var it = req.response.iterateHeaders(); | 73 | var it = req.response.iterateHeaders(); |
| 45 | { | 74 | { |
| 46 | const header = it.next().?; | 75 | const header = it.next().?; |
| 47 | try testing.expect(!it.is_trailer); | 76 | try expect(!it.is_trailer); |
| 48 | try testing.expectEqualStrings("connection", header.name); | 77 | try expectEqualStrings("connection", header.name); |
| 49 | try testing.expectEqualStrings("keep-alive", header.value); | 78 | try expectEqualStrings("keep-alive", header.value); |
| 50 | } | 79 | } |
| 51 | { | 80 | { |
| 52 | const header = it.next().?; | 81 | const header = it.next().?; |
| 53 | try testing.expect(!it.is_trailer); | 82 | try expect(!it.is_trailer); |
| 54 | try testing.expectEqualStrings("transfer-encoding", header.name); | 83 | try expectEqualStrings("transfer-encoding", header.name); |
| 55 | try testing.expectEqualStrings("chunked", header.value); | 84 | try expectEqualStrings("chunked", header.value); |
| 56 | } | 85 | } |
| 57 | { | 86 | { |
| 58 | const header = it.next().?; | 87 | const header = it.next().?; |
| 59 | try testing.expect(it.is_trailer); | 88 | try expect(it.is_trailer); |
| 60 | try testing.expectEqualStrings("X-Checksum", header.name); | 89 | try expectEqualStrings("X-Checksum", header.name); |
| 61 | try testing.expectEqualStrings("aaaa", header.value); | 90 | try expectEqualStrings("aaaa", header.value); |
| 62 | } | 91 | } |
| 63 | try testing.expectEqual(null, it.next()); | 92 | try expectEqual(null, it.next()); |
| 64 | } | 93 | } |
| 65 | | 94 | |
| 66 | // connection has been kept alive | 95 | // connection has been kept alive |
| 67 | try testing.expect(client.connection_pool.free_len == 1); | 96 | try expect(client.connection_pool.free_len == 1); |
| 68 | } | | |
| 69 | | | |
| 70 | fn serverThread(http_server: *std.net.Server) anyerror!void { | | |
| 71 | var header_buffer: [1024]u8 = undefined; | | |
| 72 | var remaining: usize = 1; | | |
| 73 | while (remaining != 0) : (remaining -= 1) { | | |
| 74 | const conn = try http_server.accept(); | | |
| 75 | defer conn.stream.close(); | | |
| 76 | | | |
| 77 | var server = std.http.Server.init(conn, &header_buffer); | | |
| 78 | | | |
| 79 | try testing.expectEqual(.ready, server.state); | | |
| 80 | var request = try server.receiveHead(); | | |
| 81 | try serve(&request); | | |
| 82 | try testing.expectEqual(.ready, server.state); | | |
| 83 | } | | |
| 84 | } | | |
| 85 | | | |
| 86 | fn serve(request: *std.http.Server.Request) !void { | | |
| 87 | try testing.expectEqualStrings(request.head.target, "/trailer"); | | |
| 88 | | | |
| 89 | var send_buffer: [1024]u8 = undefined; | | |
| 90 | var response = request.respondStreaming(.{ | | |
| 91 | .send_buffer = &send_buffer, | | |
| 92 | }); | | |
| 93 | try response.writeAll("Hello, "); | | |
| 94 | try response.flush(); | | |
| 95 | try response.writeAll("World!\n"); | | |
| 96 | try response.flush(); | | |
| 97 | try response.endChunked(.{ | | |
| 98 | .trailers = &.{ | | |
| 99 | .{ .name = "X-Checksum", .value = "aaaa" }, | | |
| 100 | }, | | |
| 101 | }); | | |
| 102 | } | 97 | } |
| 103 | | 98 | |
| 104 | test "HTTP server handles a chunked transfer coding request" { | 99 | test "HTTP server handles a chunked transfer coding request" { |
| 105 | // This test requires spawning threads. | 100 | const test_server = try createTestServer(struct { |
| 106 | if (builtin.single_threaded) { | 101 | fn run(net_server: *std.net.Server) !void { |
| 107 | return error.SkipZigTest; | 102 | var header_buffer: [8192]u8 = undefined; |
| 108 | } | | |
| 109 | | | |
| 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(); | 103 | const conn = try net_server.accept(); |
| 131 | defer conn.stream.close(); | 104 | defer conn.stream.close(); |
| 132 | | 105 | |
| ... | @@ -146,7 +119,8 @@ test "HTTP server handles a chunked transfer coding request" { | ... | @@ -146,7 +119,8 @@ test "HTTP server handles a chunked transfer coding request" { |
| 146 | .keep_alive = false, | 119 | .keep_alive = false, |
| 147 | }); | 120 | }); |
| 148 | } | 121 | } |
| 149 | }).apply, .{&socket_server}); | 122 | }); |
| | 123 | defer test_server.destroy(); |
| 150 | | 124 | |
| 151 | const request_bytes = | 125 | const request_bytes = |
| 152 | "POST / HTTP/1.1\r\n" ++ | 126 | "POST / HTTP/1.1\r\n" ++ |
| ... | @@ -162,30 +136,48 @@ test "HTTP server handles a chunked transfer coding request" { | ... | @@ -162,30 +136,48 @@ test "HTTP server handles a chunked transfer coding request" { |
| 162 | "0\r\n" ++ | 136 | "0\r\n" ++ |
| 163 | "\r\n"; | 137 | "\r\n"; |
| 164 | | 138 | |
| 165 | const stream = try std.net.tcpConnectToHost(allocator, "127.0.0.1", server_port); | 139 | const gpa = std.testing.allocator; |
| | 140 | const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port()); |
| 166 | defer stream.close(); | 141 | defer stream.close(); |
| 167 | try stream.writeAll(request_bytes); | 142 | try stream.writeAll(request_bytes); |
| 168 | | | |
| 169 | server_thread.join(); | | |
| 170 | } | 143 | } |
| 171 | | 144 | |
| 172 | test "echo content server" { | 145 | test "echo content server" { |
| 173 | if (builtin.single_threaded) return error.SkipZigTest; | 146 | const test_server = try createTestServer(struct { |
| 174 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | 147 | fn run(net_server: *std.net.Server) anyerror!void { |
| | 148 | var read_buffer: [1024]u8 = undefined; |
| 175 | | 149 | |
| 176 | if (builtin.zig_backend == .stage2_llvm and native_endian == .big) { | 150 | accept: while (true) { |
| 177 | // https://github.com/ziglang/zig/issues/13782 | 151 | const conn = try net_server.accept(); |
| 178 | return error.SkipZigTest; | 152 | defer conn.stream.close(); |
| 179 | } | | |
| 180 | | 153 | |
| 181 | const gpa = std.testing.allocator; | 154 | var http_server = std.http.Server.init(conn, &read_buffer); |
| 182 | | 155 | |
| 183 | const address = try std.net.Address.parseIp("127.0.0.1", 0); | 156 | while (http_server.state == .ready) { |
| 184 | var socket_server = try address.listen(.{ .reuse_address = true }); | 157 | var request = http_server.receiveHead() catch |err| switch (err) { |
| 185 | defer socket_server.deinit(); | 158 | error.HttpConnectionClosing => continue :accept, |
| 186 | const port = socket_server.listen_address.in.getPort(); | 159 | else => |e| return e, |
| | 160 | }; |
| | 161 | if (std.mem.eql(u8, request.head.target, "/end")) { |
| | 162 | return request.respond("", .{ .keep_alive = false }); |
| | 163 | } |
| | 164 | if (request.head.expect) |expect_header_value| { |
| | 165 | if (std.mem.eql(u8, expect_header_value, "garbage")) { |
| | 166 | try expectError(error.HttpExpectationFailed, request.reader()); |
| | 167 | try request.respond("", .{ .keep_alive = false }); |
| | 168 | continue; |
| | 169 | } |
| | 170 | } |
| | 171 | handleRequest(&request) catch |err| { |
| | 172 | // This message helps the person troubleshooting determine whether |
| | 173 | // output comes from the server thread or the client thread. |
| | 174 | std.debug.print("handleRequest failed with '{s}'\n", .{@errorName(err)}); |
| | 175 | return err; |
| | 176 | }; |
| | 177 | } |
| | 178 | } |
| | 179 | } |
| 187 | | 180 | |
| 188 | const server_thread = try std.Thread.spawn(.{}, (struct { | | |
| 189 | fn handleRequest(request: *std.http.Server.Request) !void { | 181 | fn handleRequest(request: *std.http.Server.Request) !void { |
| 190 | //std.debug.print("server received {s} {s} {s}\n", .{ | 182 | //std.debug.print("server received {s} {s} {s}\n", .{ |
| 191 | // @tagName(request.head.method), | 183 | // @tagName(request.head.method), |
| ... | @@ -196,9 +188,9 @@ test "echo content server" { | ... | @@ -196,9 +188,9 @@ test "echo content server" { |
| 196 | const body = try (try request.reader()).readAllAlloc(std.testing.allocator, 8192); | 188 | const body = try (try request.reader()).readAllAlloc(std.testing.allocator, 8192); |
| 197 | defer std.testing.allocator.free(body); | 189 | defer std.testing.allocator.free(body); |
| 198 | | 190 | |
| 199 | try testing.expect(std.mem.startsWith(u8, request.head.target, "/echo-content")); | 191 | try expect(std.mem.startsWith(u8, request.head.target, "/echo-content")); |
| 200 | try testing.expectEqualStrings("Hello, World!\n", body); | 192 | try expectEqualStrings("Hello, World!\n", body); |
| 201 | try testing.expectEqualStrings("text/plain", request.head.content_type.?); | 193 | try expectEqualStrings("text/plain", request.head.content_type.?); |
| 202 | | 194 | |
| 203 | var send_buffer: [100]u8 = undefined; | 195 | var send_buffer: [100]u8 = undefined; |
| 204 | var response = request.respondStreaming(.{ | 196 | var response = request.respondStreaming(.{ |
| ... | @@ -206,7 +198,7 @@ test "echo content server" { | ... | @@ -206,7 +198,7 @@ test "echo content server" { |
| 206 | .content_length = switch (request.head.transfer_encoding) { | 198 | .content_length = switch (request.head.transfer_encoding) { |
| 207 | .chunked => null, | 199 | .chunked => null, |
| 208 | .none => len: { | 200 | .none => len: { |
| 209 | try testing.expectEqual(14, request.head.content_length.?); | 201 | try expectEqual(14, request.head.content_length.?); |
| 210 | break :len 14; | 202 | break :len 14; |
| 211 | }, | 203 | }, |
| 212 | }, | 204 | }, |
| ... | @@ -219,54 +211,19 @@ test "echo content server" { | ... | @@ -219,54 +211,19 @@ test "echo content server" { |
| 219 | try response.end(); | 211 | try response.end(); |
| 220 | //std.debug.print(" server finished responding\n", .{}); | 212 | //std.debug.print(" server finished responding\n", .{}); |
| 221 | } | 213 | } |
| 222 | | 214 | }); |
| 223 | fn run(net_server: *std.net.Server) anyerror!void { | 215 | defer test_server.destroy(); |
| 224 | var read_buffer: [1024]u8 = undefined; | | |
| 225 | | | |
| 226 | accept: while (true) { | | |
| 227 | const conn = try net_server.accept(); | | |
| 228 | defer conn.stream.close(); | | |
| 229 | | | |
| 230 | var http_server = std.http.Server.init(conn, &read_buffer); | | |
| 231 | | | |
| 232 | while (http_server.state == .ready) { | | |
| 233 | var request = http_server.receiveHead() catch |err| switch (err) { | | |
| 234 | error.HttpConnectionClosing => continue :accept, | | |
| 235 | else => |e| return e, | | |
| 236 | }; | | |
| 237 | if (std.mem.eql(u8, request.head.target, "/end")) { | | |
| 238 | return request.respond("", .{ .keep_alive = false }); | | |
| 239 | } | | |
| 240 | if (request.head.expect) |expect| { | | |
| 241 | if (std.mem.eql(u8, expect, "garbage")) { | | |
| 242 | try testing.expectError(error.HttpExpectationFailed, request.reader()); | | |
| 243 | try request.respond("", .{ .keep_alive = false }); | | |
| 244 | continue; | | |
| 245 | } | | |
| 246 | } | | |
| 247 | handleRequest(&request) catch |err| { | | |
| 248 | // This message helps the person troubleshooting determine whether | | |
| 249 | // output comes from the server thread or the client thread. | | |
| 250 | std.debug.print("handleRequest failed with '{s}'\n", .{@errorName(err)}); | | |
| 251 | return err; | | |
| 252 | }; | | |
| 253 | } | | |
| 254 | } | | |
| 255 | } | | |
| 256 | }).run, .{&socket_server}); | | |
| 257 | | | |
| 258 | defer server_thread.join(); | | |
| 259 | | 216 | |
| 260 | { | 217 | { |
| 261 | var client: std.http.Client = .{ .allocator = gpa }; | 218 | var client: std.http.Client = .{ .allocator = std.testing.allocator }; |
| 262 | defer client.deinit(); | 219 | defer client.deinit(); |
| 263 | | 220 | |
| 264 | try echoTests(&client, port); | 221 | try echoTests(&client, test_server.port()); |
| 265 | } | 222 | } |
| 266 | } | 223 | } |
| 267 | | 224 | |
| 268 | fn echoTests(client: *std.http.Client, port: u16) !void { | 225 | fn echoTests(client: *std.http.Client, port: u16) !void { |
| 269 | const gpa = testing.allocator; | 226 | const gpa = std.testing.allocator; |
| 270 | var location_buffer: [100]u8 = undefined; | 227 | var location_buffer: [100]u8 = undefined; |
| 271 | | 228 | |
| 272 | { // send content-length request | 229 | { // send content-length request |
| ... | @@ -295,11 +252,11 @@ fn echoTests(client: *std.http.Client, port: u16) !void { | ... | @@ -295,11 +252,11 @@ fn echoTests(client: *std.http.Client, port: u16) !void { |
| 295 | const body = try req.reader().readAllAlloc(gpa, 8192); | 252 | const body = try req.reader().readAllAlloc(gpa, 8192); |
| 296 | defer gpa.free(body); | 253 | defer gpa.free(body); |
| 297 | | 254 | |
| 298 | try testing.expectEqualStrings("Hello, World!\n", body); | 255 | try expectEqualStrings("Hello, World!\n", body); |
| 299 | } | 256 | } |
| 300 | | 257 | |
| 301 | // connection has been kept alive | 258 | // connection has been kept alive |
| 302 | try testing.expect(client.http_proxy != null or client.connection_pool.free_len == 1); | 259 | try expect(client.http_proxy != null or client.connection_pool.free_len == 1); |
| 303 | | 260 | |
| 304 | { // send chunked request | 261 | { // send chunked request |
| 305 | const uri = try std.Uri.parse(try std.fmt.bufPrint( | 262 | const uri = try std.Uri.parse(try std.fmt.bufPrint( |
| ... | @@ -329,11 +286,11 @@ fn echoTests(client: *std.http.Client, port: u16) !void { | ... | @@ -329,11 +286,11 @@ fn echoTests(client: *std.http.Client, port: u16) !void { |
| 329 | const body = try req.reader().readAllAlloc(gpa, 8192); | 286 | const body = try req.reader().readAllAlloc(gpa, 8192); |
| 330 | defer gpa.free(body); | 287 | defer gpa.free(body); |
| 331 | | 288 | |
| 332 | try testing.expectEqualStrings("Hello, World!\n", body); | 289 | try expectEqualStrings("Hello, World!\n", body); |
| 333 | } | 290 | } |
| 334 | | 291 | |
| 335 | // connection has been kept alive | 292 | // connection has been kept alive |
| 336 | try testing.expect(client.http_proxy != null or client.connection_pool.free_len == 1); | 293 | try expect(client.http_proxy != null or client.connection_pool.free_len == 1); |
| 337 | | 294 | |
| 338 | { // Client.fetch() | 295 | { // Client.fetch() |
| 339 | | 296 | |
| ... | @@ -352,8 +309,8 @@ fn echoTests(client: *std.http.Client, port: u16) !void { | ... | @@ -352,8 +309,8 @@ fn echoTests(client: *std.http.Client, port: u16) !void { |
| 352 | }, | 309 | }, |
| 353 | .response_storage = .{ .dynamic = &body }, | 310 | .response_storage = .{ .dynamic = &body }, |
| 354 | }); | 311 | }); |
| 355 | try testing.expectEqual(.ok, res.status); | 312 | try expectEqual(.ok, res.status); |
| 356 | try testing.expectEqualStrings("Hello, World!\n", body.items); | 313 | try expectEqualStrings("Hello, World!\n", body.items); |
| 357 | } | 314 | } |
| 358 | | 315 | |
| 359 | { // expect: 100-continue | 316 | { // expect: 100-continue |
| ... | @@ -379,12 +336,12 @@ fn echoTests(client: *std.http.Client, port: u16) !void { | ... | @@ -379,12 +336,12 @@ fn echoTests(client: *std.http.Client, port: u16) !void { |
| 379 | try req.finish(); | 336 | try req.finish(); |
| 380 | | 337 | |
| 381 | try req.wait(); | 338 | try req.wait(); |
| 382 | try testing.expectEqual(.ok, req.response.status); | 339 | try expectEqual(.ok, req.response.status); |
| 383 | | 340 | |
| 384 | const body = try req.reader().readAllAlloc(gpa, 8192); | 341 | const body = try req.reader().readAllAlloc(gpa, 8192); |
| 385 | defer gpa.free(body); | 342 | defer gpa.free(body); |
| 386 | | 343 | |
| 387 | try testing.expectEqualStrings("Hello, World!\n", body); | 344 | try expectEqualStrings("Hello, World!\n", body); |
| 388 | } | 345 | } |
| 389 | | 346 | |
| 390 | { // expect: garbage | 347 | { // expect: garbage |
| ... | @@ -406,7 +363,7 @@ fn echoTests(client: *std.http.Client, port: u16) !void { | ... | @@ -406,7 +363,7 @@ fn echoTests(client: *std.http.Client, port: u16) !void { |
| 406 | | 363 | |
| 407 | try req.send(.{}); | 364 | try req.send(.{}); |
| 408 | try req.wait(); | 365 | try req.wait(); |
| 409 | try testing.expectEqual(.expectation_failed, req.response.status); | 366 | try expectEqual(.expectation_failed, req.response.status); |
| 410 | } | 367 | } |
| 411 | | 368 | |
| 412 | _ = try client.fetch(.{ | 369 | _ = try client.fetch(.{ |
| ... | @@ -415,3 +372,32 @@ fn echoTests(client: *std.http.Client, port: u16) !void { | ... | @@ -415,3 +372,32 @@ fn echoTests(client: *std.http.Client, port: u16) !void { |
| 415 | }, | 372 | }, |
| 416 | }); | 373 | }); |
| 417 | } | 374 | } |
| | 375 | |
| | 376 | const TestServer = struct { |
| | 377 | server_thread: std.Thread, |
| | 378 | net_server: std.net.Server, |
| | 379 | |
| | 380 | fn destroy(self: *@This()) void { |
| | 381 | self.server_thread.join(); |
| | 382 | std.testing.allocator.destroy(self); |
| | 383 | } |
| | 384 | |
| | 385 | fn port(self: @This()) u16 { |
| | 386 | return self.net_server.listen_address.in.getPort(); |
| | 387 | } |
| | 388 | }; |
| | 389 | |
| | 390 | fn createTestServer(S: type) !*TestServer { |
| | 391 | if (builtin.single_threaded) return error.SkipZigTest; |
| | 392 | if (builtin.os.tag == .wasi) return error.SkipZigTest; |
| | 393 | if (builtin.zig_backend == .stage2_llvm and native_endian == .big) { |
| | 394 | // https://github.com/ziglang/zig/issues/13782 |
| | 395 | return error.SkipZigTest; |
| | 396 | } |
| | 397 | |
| | 398 | const address = try std.net.Address.parseIp("127.0.0.1", 0); |
| | 399 | const test_server = try std.testing.allocator.create(TestServer); |
| | 400 | test_server.net_server = try address.listen(.{ .reuse_address = true }); |
| | 401 | test_server.server_thread = try std.Thread.spawn(.{}, S.run, .{&test_server.net_server}); |
| | 402 | return test_server; |
| | 403 | } |