authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-02-26 17:04:28+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-26 20:11:43-08:00
log4e2570baafb587c679ee0fc5e113ddeb36522a5d
treee74f607290693900c9e863d427e9a78ee5f03826
parent8775d8bbcef347640c6ae7e73da02ca6eff1d669

http: fix fetching a github release

* Support different keep alive defaults with different http versions. * Fix incorrect usage of `copyBackwards`, which copies in a backwards direction allowing data to be moved forward in a buffer, not backwards in a buffer.

4 files changed, 32 insertions(+), 27 deletions(-)

lib/std/Uri.zig+1-1
......@@ -367,7 +367,7 @@ pub const ResolveInplaceError = ParseError || error{OutOfMemory};
367367/// If a merge needs to take place, the newly constructed path will be stored
368368/// in `aux_buf` just after the copied `new`.
369369pub fn resolve_inplace(base: Uri, new: []const u8, aux_buf: []u8) ResolveInplaceError!Uri {
370 std.mem.copyBackwards(u8, aux_buf, new);
370 std.mem.copyForwards(u8, aux_buf, new);
371371 // At this point, new is an invalid pointer.
372372 const new_mut = aux_buf[0..new.len];
373373
lib/std/http/Client.zig+9-3
......@@ -430,7 +430,7 @@ pub const Response = struct {
430430 /// Points into the user-provided `server_header_buffer`.
431431 content_disposition: ?[]const u8 = null,
432432
433 keep_alive: bool = false,
433 keep_alive: bool,
434434
435435 /// If present, the number of bytes in the response body.
436436 content_length: ?u64 = null,
......@@ -477,6 +477,10 @@ pub const Response = struct {
477477 res.version = version;
478478 res.status = status;
479479 res.reason = reason;
480 res.keep_alive = switch (version) {
481 .@"HTTP/1.0" => false,
482 .@"HTTP/1.1" => true,
483 };
480484
481485 while (it.next()) |line| {
482486 if (line.len == 0) return;
......@@ -684,9 +688,10 @@ pub const Request = struct {
684688 req.response.parser.reset();
685689
686690 req.response = .{
691 .version = undefined,
687692 .status = undefined,
688693 .reason = undefined,
689 .version = undefined,
694 .keep_alive = undefined,
690695 .parser = req.response.parser,
691696 };
692697 }
......@@ -1564,9 +1569,10 @@ pub fn open(
15641569 .redirect_behavior = options.redirect_behavior,
15651570 .handle_continue = options.handle_continue,
15661571 .response = .{
1572 .version = undefined,
15671573 .status = undefined,
15681574 .reason = undefined,
1569 .version = undefined,
1575 .keep_alive = undefined,
15701576 .parser = proto.HeadersParser.init(options.server_header_buffer),
15711577 },
15721578 .headers = options.headers,
lib/std/http/Server.zig+17-7
......@@ -25,7 +25,7 @@ pub const State = enum {
2525 /// The client is uploading something to this Server.
2626 receiving_body,
2727 /// The connection is eligible for another HTTP request, however the client
28 /// and server did not negotiate connection: keep-alive.
28 /// and server did not negotiate a persistent connection.
2929 closing,
3030};
3131
......@@ -197,7 +197,10 @@ pub const Request = struct {
197197 .content_length = null,
198198 .transfer_encoding = .none,
199199 .transfer_compression = .identity,
200 .keep_alive = false,
200 .keep_alive = switch (version) {
201 .@"HTTP/1.0" => false,
202 .@"HTTP/1.1" => true,
203 },
201204 .compression = .none,
202205 };
203206
......@@ -330,7 +333,7 @@ pub const Request = struct {
330333 // reader() and hence discardBody() above sets expect to null if it
331334 // is handled. So the fact that it is not null here means unhandled.
332335 h.appendSliceAssumeCapacity("HTTP/1.1 417 Expectation Failed\r\n");
333 if (keep_alive) h.appendSliceAssumeCapacity("connection: keep-alive\r\n");
336 if (!keep_alive) h.appendSliceAssumeCapacity("connection: close\r\n");
334337 h.appendSliceAssumeCapacity("content-length: 0\r\n\r\n");
335338 try request.server.connection.stream.writeAll(h.items);
336339 return;
......@@ -339,7 +342,10 @@ pub const Request = struct {
339342 @tagName(options.version), @intFromEnum(options.status), phrase,
340343 }) catch unreachable;
341344
342 if (keep_alive) h.appendSliceAssumeCapacity("connection: keep-alive\r\n");
345 switch (options.version) {
346 .@"HTTP/1.0" => if (keep_alive) h.appendSliceAssumeCapacity("connection: keep-alive\r\n"),
347 .@"HTTP/1.1" => if (!keep_alive) h.appendSliceAssumeCapacity("connection: close\r\n"),
348 }
343349
344350 if (options.transfer_encoding) |transfer_encoding| switch (transfer_encoding) {
345351 .none => {},
......@@ -480,14 +486,18 @@ pub const Request = struct {
480486 // reader() and hence discardBody() above sets expect to null if it
481487 // is handled. So the fact that it is not null here means unhandled.
482488 h.appendSliceAssumeCapacity("HTTP/1.1 417 Expectation Failed\r\n");
483 if (keep_alive) h.appendSliceAssumeCapacity("connection: keep-alive\r\n");
489 if (!keep_alive) h.appendSliceAssumeCapacity("connection: close\r\n");
484490 h.appendSliceAssumeCapacity("content-length: 0\r\n\r\n");
485491 break :eb true;
486492 } else eb: {
487493 h.fixedWriter().print("{s} {d} {s}\r\n", .{
488494 @tagName(o.version), @intFromEnum(o.status), phrase,
489495 }) catch unreachable;
490 if (keep_alive) h.appendSliceAssumeCapacity("connection: keep-alive\r\n");
496
497 switch (o.version) {
498 .@"HTTP/1.0" => if (keep_alive) h.appendSliceAssumeCapacity("connection: keep-alive\r\n"),
499 .@"HTTP/1.1" => if (!keep_alive) h.appendSliceAssumeCapacity("connection: close\r\n"),
500 }
491501
492502 if (o.transfer_encoding) |transfer_encoding| switch (transfer_encoding) {
493503 .chunked => h.appendSliceAssumeCapacity("transfer-encoding: chunked\r\n"),
......@@ -694,7 +704,7 @@ pub const Request = struct {
694704 }
695705 }
696706
697 /// Returns whether the connection: keep-alive header should be sent to the client.
707 /// Returns whether the connection should remain persistent.
698708 /// If it would fail, it instead sets the Server state to `receiving_body`
699709 /// and returns false.
700710 fn discardBody(request: *Request, keep_alive: bool) bool {
lib/std/http/test.zig+5-16
......@@ -73,12 +73,6 @@ test "trailers" {
7373 try expectEqualStrings("Hello, World!\n", body);
7474
7575 var it = req.response.iterateHeaders();
76 {
77 const header = it.next().?;
78 try expect(!it.is_trailer);
79 try expectEqualStrings("connection", header.name);
80 try expectEqualStrings("keep-alive", header.value);
81 }
8276 {
8377 const header = it.next().?;
8478 try expect(!it.is_trailer);
......@@ -143,15 +137,15 @@ test "HTTP server handles a chunked transfer coding request" {
143137 defer stream.close();
144138 try stream.writeAll(request_bytes);
145139
146 const response = try stream.reader().readAllAlloc(gpa, 100);
147 defer gpa.free(response);
148
149140 const expected_response =
150141 "HTTP/1.1 200 OK\r\n" ++
142 "connection: close\r\n" ++
151143 "content-length: 21\r\n" ++
152144 "content-type: text/plain\r\n" ++
153145 "\r\n" ++
154146 "message from server!\n";
147 const response = try stream.reader().readAllAlloc(gpa, expected_response.len);
148 defer gpa.free(response);
155149 try expectEqualStrings(expected_response, response);
156150}
157151
......@@ -285,7 +279,7 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {
285279 var expected_response = std.ArrayList(u8).init(gpa);
286280 defer expected_response.deinit();
287281
288 try expected_response.appendSlice("HTTP/1.1 200 OK\r\n\r\n");
282 try expected_response.appendSlice("HTTP/1.1 200 OK\r\nconnection: close\r\n\r\n");
289283
290284 {
291285 var total: usize = 0;
......@@ -349,6 +343,7 @@ test "receiving arbitrary http headers from the client" {
349343 defer expected_response.deinit();
350344
351345 try expected_response.appendSlice("HTTP/1.1 200 OK\r\n");
346 try expected_response.appendSlice("connection: close\r\n");
352347 try expected_response.appendSlice("content-length: 0\r\n\r\n");
353348 try expectEqualStrings(expected_response.items, response);
354349}
......@@ -700,12 +695,6 @@ test "general client/server API coverage" {
700695 try expectEqualStrings("", body);
701696
702697 var it = req.response.iterateHeaders();
703 {
704 const header = it.next().?;
705 try expect(!it.is_trailer);
706 try expectEqualStrings("connection", header.name);
707 try expectEqualStrings("keep-alive", header.value);
708 }
709698 {
710699 const header = it.next().?;
711700 try expect(!it.is_trailer);