Skip to content

Commit 5759c88

Browse files
committed
Remove the http/Client.zig header_callback.
The callback which was called on a per-header basis is removed. Only XHR was using this, and it was created before the HeaderIterator existed (because I didn't know we could iterate through the response headers in curl after the fact). The header_done_callback remains, but is now called header_callback (a bit confusing in the short term). The only difficulty was with fulfilled requests, which do not have an easy handle for our HeaderIterator. The existing code would segfault if transfer.responseHeaderIterator() was called on a fulfilled requests. The HeaderIterator is now a tagged union that abstracts whether the source of the response header is a curl easy, or just an injected list from the fulfilled requests.
1 parent bade412 commit 5759c88

File tree

4 files changed

+70
-39
lines changed

4 files changed

+70
-39
lines changed

src/browser/ScriptManager.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ pub fn addFromElement(self: *ScriptManager, element: *parser.Element) !void {
240240
.cookie_jar = page.cookie_jar,
241241
.resource_type = .script,
242242
.start_callback = if (log.enabled(.http, .debug)) startCallback else null,
243-
.header_done_callback = headerCallback,
243+
.header_callback = headerCallback,
244244
.data_callback = dataCallback,
245245
.done_callback = doneCallback,
246246
.error_callback = errorCallback,
@@ -309,7 +309,7 @@ pub fn blockingGet(self: *ScriptManager, url: [:0]const u8) !BlockingResult {
309309
.ctx = &blocking,
310310
.resource_type = .script,
311311
.start_callback = if (log.enabled(.http, .debug)) Blocking.startCallback else null,
312-
.header_done_callback = Blocking.headerCallback,
312+
.header_callback = Blocking.headerCallback,
313313
.data_callback = Blocking.dataCallback,
314314
.done_callback = Blocking.doneCallback,
315315
.error_callback = Blocking.errorCallback,

src/browser/page.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ pub const Page = struct {
520520
.body = opts.body,
521521
.cookie_jar = self.cookie_jar,
522522
.resource_type = .document,
523-
.header_done_callback = pageHeaderDoneCallback,
523+
.header_callback = pageHeaderDoneCallback,
524524
.data_callback = pageDataCallback,
525525
.done_callback = pageDoneCallback,
526526
.error_callback = pageErrorCallback,

src/browser/xhr/xhr.zig

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,8 +385,7 @@ pub const XMLHttpRequest = struct {
385385
.cookie_jar = page.cookie_jar,
386386
.resource_type = .xhr,
387387
.start_callback = httpStartCallback,
388-
.header_callback = httpHeaderCallback,
389-
.header_done_callback = httpHeaderDoneCallback,
388+
.header_callback = httpHeaderDoneCallback,
390389
.data_callback = httpDataCallback,
391390
.done_callback = httpDoneCallback,
392391
.error_callback = httpErrorCallback,
@@ -422,6 +421,12 @@ pub const XMLHttpRequest = struct {
422421
};
423422
}
424423

424+
var it = transfer.responseHeaderIterator();
425+
while (it.next()) |hdr| {
426+
const joined = try std.fmt.allocPrint(self.arena, "{s}: {s}", .{ hdr.name, hdr.value });
427+
try self.response_headers.append(self.arena, joined);
428+
}
429+
425430
// TODO handle override mime type
426431
self.state = .headers_received;
427432
self.dispatchEvt("readystatechange");

src/http/Client.zig

Lines changed: 60 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -532,8 +532,7 @@ pub const Request = struct {
532532
ctx: *anyopaque = undefined,
533533

534534
start_callback: ?*const fn (transfer: *Transfer) anyerror!void = null,
535-
header_callback: ?*const fn (transfer: *Transfer, header: Http.Header) anyerror!void = null,
536-
header_done_callback: *const fn (transfer: *Transfer) anyerror!void,
535+
header_callback: *const fn (transfer: *Transfer) anyerror!void,
537536
data_callback: *const fn (transfer: *Transfer, data: []const u8) anyerror!void,
538537
done_callback: *const fn (ctx: *anyopaque) anyerror!void,
539538
error_callback: *const fn (ctx: *anyopaque, err: anyerror) void,
@@ -739,8 +738,8 @@ pub const Transfer = struct {
739738
if (i >= ct.?.amount) break;
740739
}
741740

742-
transfer.req.header_done_callback(transfer) catch |err| {
743-
log.err(.http, "header_done_callback", .{ .err = err, .req = transfer });
741+
transfer.req.header_callback(transfer) catch |err| {
742+
log.err(.http, "header_callback", .{ .err = err, .req = transfer });
744743
// returning < buf_len terminates the request
745744
return 0;
746745
};
@@ -750,15 +749,6 @@ pub const Transfer = struct {
750749
.transfer = transfer,
751750
});
752751
}
753-
} else {
754-
if (transfer.req.header_callback) |cb| {
755-
if (Http.Headers.parseHeader(header)) |hdr| {
756-
cb(transfer, hdr) catch |err| {
757-
log.err(.http, "header_callback", .{ .err = err, .req = transfer });
758-
return 0;
759-
};
760-
}
761-
}
762752
}
763753
return buf_len;
764754
}
@@ -784,10 +774,17 @@ pub const Transfer = struct {
784774
return chunk_len;
785775
}
786776

787-
// we assume that the caller is smart and only calling this after being
788-
// told that the header was ready.
789777
pub fn responseHeaderIterator(self: *Transfer) HeaderIterator {
790-
return .{ .easy = self._handle.?.conn.easy };
778+
if (self._handle) |handle| {
779+
// If we have a handle, than this is a real curl request and we
780+
// iterate through the header that curl maintains.
781+
return .{ .curl = .{ .easy = handle.conn.easy } };
782+
}
783+
// If there's no handle, it either means this is being called before
784+
// the request is even being made (which would be a bug in the code)
785+
// or when a response was injected via transfer.fulfill. The injected
786+
// header should be iterated, since there is no handle/easy.
787+
return .{ .list = .{ .list = self.response_header.?._injected_headers } };
791788
}
792789

793790
// pub because Page.printWaitAnalysis uses it
@@ -817,15 +814,10 @@ pub const Transfer = struct {
817814
try cb(transfer);
818815
}
819816

820-
if (req.header_callback) |cb| {
821-
for (headers) |hdr| {
822-
try cb(transfer, hdr);
823-
}
824-
}
825-
826817
transfer.response_header = .{
827818
.status = status,
828819
.url = req.url,
820+
._injected_headers = headers,
829821
};
830822
for (headers) |hdr| {
831823
if (std.ascii.eqlIgnoreCase(hdr.name, "content-type")) {
@@ -835,7 +827,7 @@ pub const Transfer = struct {
835827
}
836828
}
837829

838-
try req.header_done_callback(transfer);
830+
try req.header_callback(transfer);
839831

840832
if (body) |b| {
841833
try req.data_callback(transfer, b);
@@ -852,6 +844,11 @@ pub const ResponseHeader = struct {
852844
url: [*c]const u8,
853845
_content_type_len: usize = 0,
854846
_content_type: [MAX_CONTENT_TYPE_LEN]u8 = undefined,
847+
// this is normally an empty list, but if the response is being injected
848+
// than it'll be populated. It isn't meant to be used directly, but should
849+
// be used through the transfer.responseHeaderIterator() which abstracts
850+
// whether the headers are from a live curl easy handle, or injected.
851+
_injected_headers: []const Http.Header = &.{},
855852

856853
pub fn contentType(self: *ResponseHeader) ?[]u8 {
857854
if (self._content_type_len == 0) {
@@ -861,20 +858,49 @@ pub const ResponseHeader = struct {
861858
}
862859
};
863860

864-
const HeaderIterator = struct {
865-
easy: *c.CURL,
866-
prev: ?*c.curl_header = null,
861+
// In normal cases, the header iterator comes from the curl linked list.
862+
// But it's also possible to inject a response, via `transfer.fulfill`. In that
863+
// case, the resposne headers are a list, []const Http.Header.
864+
// This union, is an iterator that exposes the same API for either case.
865+
const HeaderIterator = union(enum) {
866+
curl: CurlHeaderIterator,
867+
list: ListHeaderIterator,
867868

868869
pub fn next(self: *HeaderIterator) ?Http.Header {
869-
const h = c.curl_easy_nextheader(self.easy, c.CURLH_HEADER, -1, self.prev) orelse return null;
870-
self.prev = h;
871-
872-
const header = h.*;
873-
return .{
874-
.name = std.mem.span(header.name),
875-
.value = std.mem.span(header.value),
876-
};
870+
switch (self.*) {
871+
inline else => |*it| return it.next(),
872+
}
877873
}
874+
875+
const CurlHeaderIterator = struct {
876+
easy: *c.CURL,
877+
prev: ?*c.curl_header = null,
878+
879+
pub fn next(self: *CurlHeaderIterator) ?Http.Header {
880+
const h = c.curl_easy_nextheader(self.easy, c.CURLH_HEADER, -1, self.prev) orelse return null;
881+
self.prev = h;
882+
883+
const header = h.*;
884+
return .{
885+
.name = std.mem.span(header.name),
886+
.value = std.mem.span(header.value),
887+
};
888+
}
889+
};
890+
891+
const ListHeaderIterator = struct {
892+
index: usize = 0,
893+
list: []const Http.Header,
894+
895+
pub fn next(self: *ListHeaderIterator) ?Http.Header {
896+
const index = self.index;
897+
if (index == self.list.len) {
898+
return null;
899+
}
900+
self.index = index + 1;
901+
return self.list[index];
902+
}
903+
};
878904
};
879905

880906
const CurlHeaderValue = struct {

0 commit comments

Comments
 (0)