Skip to content

Commit a6ac7d9

Browse files
committed
Delay setting the requests' keepalive flag until the request is fully processed
We currently set request._keepalive prematurely. There are [error cases] where the request could be abandoned before being fully drained. While we do try to drain in some cases, it isn't always possible. For this reason, request.keepalive is only set at the end of the request lifecycle, at which point we know the connection is ready to be re-used.
1 parent 9b35736 commit a6ac7d9

File tree

1 file changed

+15
-17
lines changed

1 file changed

+15
-17
lines changed

src/http/client.zig

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ pub const Request = struct {
716716
}
717717

718718
fn newReader(self: *Request) Reader {
719-
return Reader.init(self._state, &self._keepalive);
719+
return Reader.init(self._state);
720720
}
721721

722722
// Does additional setup of the request for the firsts (i.e. non-redirect) call.
@@ -879,12 +879,14 @@ pub const Request = struct {
879879
});
880880
}
881881

882-
fn requestCompleted(self: *Request, response: ResponseHeader) void {
882+
fn requestCompleted(self: *Request, response: ResponseHeader, can_keepalive: bool) void {
883883
const notification = self.notification orelse return;
884884
if (self._notified_complete) {
885885
return;
886886
}
887+
887888
self._notified_complete = true;
889+
self._keepalive = can_keepalive;
888890
notification.dispatch(.http_request_complete, &.{
889891
.id = self.id,
890892
.url = self.request_uri,
@@ -1111,14 +1113,14 @@ fn AsyncHandler(comptime H: type, comptime L: type) type {
11111113
.handler_error => {
11121114
// handler should never have been called if we're redirecting
11131115
std.debug.assert(self.redirect == null);
1114-
self.request.requestCompleted(self.reader.response);
1116+
self.request.requestCompleted(self.reader.response, self.reader.keepalive);
11151117
self.deinit();
11161118
return;
11171119
},
11181120
.done => {
11191121
const redirect = self.redirect orelse {
11201122
var handler = self.handler;
1121-
self.request.requestCompleted(self.reader.response);
1123+
self.request.requestCompleted(self.reader.response, self.reader.keepalive);
11221124
self.deinit();
11231125

11241126
// Emit the done chunk. We expect the caller to do
@@ -1560,8 +1562,6 @@ const SyncHandler = struct {
15601562
var decompressor = std.compress.gzip.decompressor(compress_reader.reader());
15611563
try decompressor.decompress(body.writer(request.arena));
15621564

1563-
self.request.requestCompleted(reader.response);
1564-
15651565
return .{
15661566
.header = reader.response,
15671567
._done = true,
@@ -1781,8 +1781,8 @@ const SyncHandler = struct {
17811781

17821782
// Used for reading the response (both the header and the body)
17831783
const Reader = struct {
1784-
// ref request.keepalive
1785-
keepalive: *bool,
1784+
// Wether, from the reader's point of view, this connection could be kept-alive
1785+
keepalive: bool,
17861786

17871787
// always references state.header_buf
17881788
header_buf: []u8,
@@ -1802,13 +1802,13 @@ const Reader = struct {
18021802
// Whether or not the current header has to be skipped [because it's too long].
18031803
skip_current_header: bool,
18041804

1805-
fn init(state: *State, keepalive: *bool) Reader {
1805+
fn init(state: *State) Reader {
18061806
return .{
18071807
.pos = 0,
18081808
.response = .{},
18091809
.body_reader = null,
18101810
.header_done = false,
1811-
.keepalive = keepalive,
1811+
.keepalive = false,
18121812
.skip_current_header = false,
18131813
.header_buf = state.header_buf,
18141814
.arena = state.arena.allocator(),
@@ -1835,7 +1835,6 @@ const Reader = struct {
18351835
// us to emit whatever data we have, but it isn't safe to keep
18361836
// the connection alive.
18371837
std.debug.assert(result.done == true);
1838-
self.keepalive.* = false;
18391838
}
18401839
return result;
18411840
}
@@ -1930,7 +1929,7 @@ const Reader = struct {
19301929
// We think we're done reading the body, but we still have data
19311930
// We'll return what we have as-is, but close the connection
19321931
// because we don't know what state it's in.
1933-
self.keepalive.* = false;
1932+
self.keepalive = false;
19341933
} else {
19351934
result.unprocessed = unprocessed;
19361935
}
@@ -1945,7 +1944,7 @@ const Reader = struct {
19451944

19461945
if (response.get("connection")) |connection| {
19471946
if (std.ascii.eqlIgnoreCase(connection, "close")) {
1948-
self.keepalive.* = false;
1947+
self.keepalive = false;
19491948
}
19501949
}
19511950

@@ -2005,7 +2004,7 @@ const Reader = struct {
20052004
}
20062005
const protocol = data[0..9];
20072006
if (std.mem.eql(u8, protocol, "HTTP/1.1 ")) {
2008-
self.keepalive.* = true;
2007+
self.keepalive = true;
20092008
} else if (std.mem.eql(u8, protocol, "HTTP/1.0 ") == false) {
20102009
return error.InvalidStatusLine;
20112010
}
@@ -2387,7 +2386,7 @@ pub const Response = struct {
23872386
return data;
23882387
}
23892388
if (self._done) {
2390-
self._request.requestCompleted(self.header);
2389+
self._request.requestCompleted(self.header, self._reader.keepalive);
23912390
return null;
23922391
}
23932392

@@ -3396,8 +3395,7 @@ const CaptureHandler = struct {
33963395

33973396
fn testReader(state: *State, res: *TestResponse, data: []const u8) !void {
33983397
var status: u16 = 0;
3399-
var keepalive = false;
3400-
var r = Reader.init(state, &keepalive);
3398+
var r = Reader.init(state);
34013399

34023400
// dupe it so that we have a mutable copy
34033401
const owned = try testing.allocator.dupe(u8, data);

0 commit comments

Comments
 (0)