zig

fork of https://codeberg.org/ziglang/zig
Log | Tree | Refs | README | LICENSE

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 }