Skip to content

Commit 0e473d5

Browse files
committed
defer callback handling, not http req
1 parent c80a0e7 commit 0e473d5

3 files changed

Lines changed: 169 additions & 43 deletions

File tree

src/browser/Frame.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -983,7 +983,7 @@ fn frameHeaderDoneCallback(response: HttpClient.Response) !bool {
983983
try self._session.commitPendingPage();
984984
switch (response.inner) {
985985
.transfer => |t| t.req.params.protect_from_abort = false,
986-
.fulfilled, .cached => {},
986+
.fulfilled, .cached, .stable => {},
987987
}
988988
}
989989

src/browser/HttpClient.zig

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -993,12 +993,28 @@ pub const FulfilledResponse = struct {
993993
}
994994
};
995995

996+
pub const StableResponse = struct {
997+
ctx: *anyopaque,
998+
status: u16,
999+
url: [:0]const u8,
1000+
headers: []const http.Header,
1001+
body: ?[]const u8,
1002+
1003+
pub fn contentType(self: *const StableResponse) ?[]const u8 {
1004+
for (self.headers) |hdr| {
1005+
if (std.ascii.eqlIgnoreCase(hdr.name, "content-type")) return hdr.value;
1006+
}
1007+
return null;
1008+
}
1009+
};
1010+
9961011
pub const Response = struct {
9971012
ctx: *anyopaque,
9981013
inner: union(enum) {
9991014
transfer: *Transfer,
10001015
cached: *const CachedResponse,
10011016
fulfilled: *const FulfilledResponse,
1017+
stable: *const StableResponse,
10021018
},
10031019

10041020
pub fn fromTransfer(transfer: *Transfer) Response {
@@ -1013,11 +1029,16 @@ pub const Response = struct {
10131029
return .{ .ctx = ctx, .inner = .{ .fulfilled = fulfilled } };
10141030
}
10151031

1032+
pub fn fromStable(stable: *const StableResponse) Response {
1033+
return .{ .ctx = stable.ctx, .inner = .{ .stable = stable } };
1034+
}
1035+
10161036
pub fn status(self: Response) ?u16 {
10171037
return switch (self.inner) {
10181038
.transfer => |t| if (t.response_header) |rh| rh.status else null,
10191039
.cached => |c| c.metadata.status,
10201040
.fulfilled => |f| f.status,
1041+
.stable => |s| s.status,
10211042
};
10221043
}
10231044

@@ -1026,6 +1047,7 @@ pub const Response = struct {
10261047
.transfer => |t| if (t.response_header) |*rh| rh.contentType() else null,
10271048
.cached => |c| c.metadata.content_type,
10281049
.fulfilled => |f| f.contentType(),
1050+
.stable => |s| s.contentType(),
10291051
};
10301052
}
10311053

@@ -1037,13 +1059,14 @@ pub const Response = struct {
10371059
.file => |f| @intCast(f.len),
10381060
},
10391061
.fulfilled => |f| if (f.body) |b| @intCast(b.len) else null,
1062+
.stable => |s| if (s.body) |b| @intCast(b.len) else null,
10401063
};
10411064
}
10421065

10431066
pub fn redirectCount(self: Response) ?u32 {
10441067
return switch (self.inner) {
10451068
.transfer => |t| if (t.response_header) |rh| rh.redirect_count else null,
1046-
.cached, .fulfilled => 0,
1069+
.cached, .fulfilled, .stable => 0,
10471070
};
10481071
}
10491072

@@ -1052,6 +1075,7 @@ pub const Response = struct {
10521075
.transfer => |t| t.url,
10531076
.cached => |c| c.metadata.url,
10541077
.fulfilled => |f| f.url,
1078+
.stable => |s| s.url,
10551079
};
10561080
}
10571081

@@ -1060,13 +1084,14 @@ pub const Response = struct {
10601084
.transfer => |t| t.responseHeaderIterator(),
10611085
.cached => |c| HeaderIterator{ .list = .{ .list = c.metadata.headers } },
10621086
.fulfilled => |f| HeaderIterator{ .list = .{ .list = f.headers } },
1087+
.stable => |s| HeaderIterator{ .list = .{ .list = s.headers } },
10631088
};
10641089
}
10651090

10661091
pub fn abort(self: Response, err: anyerror) void {
10671092
switch (self.inner) {
10681093
.transfer => |t| t.abort(err),
1069-
.cached, .fulfilled => {},
1094+
.cached, .fulfilled, .stable => {},
10701095
}
10711096
}
10721097

@@ -1075,6 +1100,28 @@ pub const Response = struct {
10751100
.transfer => |t| try t.format(writer),
10761101
.cached => |c| try c.format(writer),
10771102
.fulfilled => |f| try writer.print("fulfilled {s}", .{f.url}),
1103+
.stable => |s| writer.print("stable {s}", .{s.url}),
1104+
};
1105+
}
1106+
1107+
pub fn toStable(self: Response, arena: std.mem.Allocator) !StableResponse {
1108+
const new_url = try arena.dupeZ(u8, self.url());
1109+
1110+
var headers: std.ArrayListUnmanaged(http.Header) = .{};
1111+
var it = self.headerIterator();
1112+
while (it.next()) |hdr| {
1113+
try headers.append(arena, .{
1114+
.name = try arena.dupe(u8, hdr.name),
1115+
.value = try arena.dupe(u8, hdr.value),
1116+
});
1117+
}
1118+
1119+
return .{
1120+
.ctx = self.ctx,
1121+
.status = self.status() orelse 0,
1122+
.url = new_url,
1123+
.headers = headers.items,
1124+
.body = null,
10781125
};
10791126
}
10801127
};

src/network/layer/DeferringLayer.zig

Lines changed: 119 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,14 @@ const Client = @import("../../browser/HttpClient.zig").Client;
2424
const Request = @import("../../browser/HttpClient.zig").Request;
2525
const Response = @import("../../browser/HttpClient.zig").Response;
2626
const Layer = @import("../../browser/HttpClient.zig").Layer;
27+
const StableResponse = @import("../../browser/HttpClient.zig").StableResponse;
2728
const Forward = @import("Forward.zig");
2829

2930
const DeferringLayer = @This();
3031

3132
allocator: std.mem.Allocator,
3233
next: Layer = undefined,
3334

34-
// Requests whose callbacks should be deferred until flush() is called.
35-
// Each node is a DeferredRequest allocated in the request's arena.
3635
deferred: std.DoublyLinkedList = .{},
3736

3837
pub fn layer(self: *DeferringLayer) Layer {
@@ -49,10 +48,13 @@ pub fn deinit(self: *DeferringLayer) void {
4948
fn request(ptr: *anyopaque, client: *Client, in_req: Request) anyerror!void {
5049
const self: *DeferringLayer = @ptrCast(@alignCast(ptr));
5150

52-
const deferred_req = try self.allocator.create(DeferredContext);
53-
errdefer self.allocator.destroy(deferred_req);
51+
const arena = try client.network.app.arena_pool.acquire(.small, "DeferringContext");
52+
errdefer client.network.app.arena_pool.release(arena);
53+
54+
const deferred_req = try arena.create(DeferredContext);
5455

5556
deferred_req.* = .{
57+
.arena = arena,
5658
.layer = self,
5759
.client = client,
5860
.forward = Forward.fromRequest(in_req),
@@ -92,8 +94,6 @@ pub fn flushUnblocked(
9294
}
9395
}
9496

95-
// Flush all deferred requests on the frame, firing their done/error callbacks.
96-
// This happens to requests ONLY on the given frame.
9797
pub fn flushFrame(self: *DeferringLayer, frame_id: u32) void {
9898
var node = self.deferred.first;
9999
while (node) |n| {
@@ -119,7 +119,6 @@ pub fn drainAll(self: *DeferringLayer) void {
119119

120120
pub fn drainFrame(self: *DeferringLayer, frame_id: u32, opts: Client.AbortOpts) void {
121121
var node = self.deferred.first;
122-
123122
while (node) |n| {
124123
node = n.next;
125124
const deferred: *DeferredContext = @fieldParentPtr("node", n);
@@ -134,92 +133,172 @@ pub fn drainFrame(self: *DeferringLayer, frame_id: u32, opts: Client.AbortOpts)
134133
}
135134

136135
const DeferredContext = struct {
136+
arena: std.mem.Allocator,
137137
layer: *DeferringLayer,
138138
client: *Client,
139139
forward: Forward,
140140
request: Request,
141141
node: std.DoublyLinkedList.Node,
142142

143-
outcome: union(enum) {
144-
none,
143+
buffered: std.ArrayListUnmanaged(BufferedEvent) = .{},
144+
deferring: bool = false,
145+
stable_resp: ?StableResponse = null,
146+
147+
const BufferedEvent = union(enum) {
148+
start,
149+
header,
150+
data: []const u8,
145151
done,
146152
err: anyerror,
147153
shutdown,
148-
} = .none,
154+
};
149155

150156
fn deinit(self: *DeferredContext) void {
151-
self.layer.allocator.destroy(self);
157+
self.client.network.app.arena_pool.release(self.arena);
152158
}
153159

154160
fn shouldDefer(self: *DeferredContext) bool {
155161
const blocking_id = self.client.blocking_requests.get(self.request.params.frame_id) orelse return false;
156162
return self.request.params.request_id != blocking_id;
157163
}
158164

165+
fn beginDefer(self: *DeferredContext) !void {
166+
if (self.deferring) return;
167+
self.deferring = true;
168+
self.layer.deferred.append(&self.node);
169+
}
170+
159171
fn startCallback(response: Response) anyerror!void {
160172
const self: *DeferredContext = @ptrCast(@alignCast(response.ctx));
161-
return self.forward.forwardStart(response);
173+
174+
if (!self.deferring and !self.shouldDefer()) {
175+
return self.forward.forwardStart(response);
176+
}
177+
178+
log.debug(.http, "deferring start callback", .{ .url = self.request.params.url });
179+
self.stable_resp = try response.toStable(self.arena);
180+
try self.beginDefer();
181+
try self.buffered.append(self.arena, .start);
162182
}
163183

164184
fn headerCallback(response: Response) anyerror!bool {
165185
const self: *DeferredContext = @ptrCast(@alignCast(response.ctx));
166-
return self.forward.forwardHeader(response);
186+
187+
if (!self.deferring and !self.shouldDefer()) {
188+
return self.forward.forwardHeader(response);
189+
}
190+
191+
log.debug(.http, "deferring header callback", .{ .url = self.request.params.url });
192+
self.stable_resp = try response.toStable(self.arena);
193+
try self.beginDefer();
194+
try self.buffered.append(self.arena, .header);
195+
return true;
167196
}
168197

169198
fn dataCallback(response: Response, chunk: []const u8) anyerror!void {
170199
const self: *DeferredContext = @ptrCast(@alignCast(response.ctx));
171-
return self.forward.forwardData(response, chunk);
200+
201+
if (!self.deferring and !self.shouldDefer()) {
202+
return self.forward.forwardData(response, chunk);
203+
}
204+
205+
log.debug(.http, "deferring data callback", .{ .url = self.request.params.url });
206+
try self.beginDefer();
207+
try self.buffered.append(self.arena, .{ .data = try self.arena.dupe(u8, chunk) });
172208
}
173209

174210
fn doneCallback(ctx: *anyopaque) anyerror!void {
175211
const self: *DeferredContext = @ptrCast(@alignCast(ctx));
176-
if (self.shouldDefer()) {
177-
log.debug(.http, "deferring done callback", .{ .url = self.request.params.url });
178-
self.outcome = .done;
179-
self.layer.deferred.append(&self.node);
180-
return;
212+
213+
if (!self.deferring and !self.shouldDefer()) {
214+
defer self.deinit();
215+
return self.forward.forwardDone();
181216
}
182217

183-
defer self.deinit();
184-
return self.forward.forwardDone();
218+
log.debug(.http, "deferring done callback", .{ .url = self.request.params.url });
219+
try self.beginDefer();
220+
try self.buffered.append(self.arena, .done);
185221
}
186222

187223
fn errorCallback(ctx: *anyopaque, err: anyerror) void {
188224
const self: *DeferredContext = @ptrCast(@alignCast(ctx));
189-
if (self.shouldDefer()) {
190-
log.debug(.http, "deferring error callback", .{ .url = self.request.params.url, .err = err });
191-
self.outcome = .{ .err = err };
192-
self.layer.deferred.append(&self.node);
225+
226+
if (!self.deferring and !self.shouldDefer()) {
227+
defer self.deinit();
228+
self.forward.forwardErr(err);
193229
return;
194230
}
195231

196-
defer self.deinit();
197-
self.forward.forwardErr(err);
232+
log.debug(.http, "deferring error callback", .{ .url = self.request.params.url, .err = err });
233+
self.beginDefer() catch {};
234+
self.buffered.append(self.arena, .{ .err = err }) catch {};
198235
}
199236

200237
fn shutdownCallback(ctx: *anyopaque) void {
201238
const self: *DeferredContext = @ptrCast(@alignCast(ctx));
202-
if (self.shouldDefer()) {
203-
self.outcome = .shutdown;
204-
self.layer.deferred.append(&self.node);
239+
240+
if (!self.deferring and !self.shouldDefer()) {
241+
defer self.deinit();
242+
self.forward.forwardShutdown();
205243
return;
206244
}
207245

208-
defer self.deinit();
209-
self.forward.forwardShutdown();
246+
log.debug(.http, "deferring shutdown callback", .{ .url = self.request.params.url });
247+
self.beginDefer() catch {};
248+
self.buffered.append(self.arena, .shutdown) catch {};
210249
}
211250

251+
// Replay all buffered events in order, then clean up.
212252
fn fire(self: *DeferredContext) void {
213253
defer self.deinit();
214254

215-
switch (self.outcome) {
216-
.none => unreachable,
217-
.done => self.forward.forwardDone() catch |err| {
218-
log.err(.http, "deferred done callback", .{ .err = err, .url = self.request.params.url });
219-
self.forward.forwardErr(err);
220-
},
221-
.err => |err| self.forward.forwardErr(err),
222-
.shutdown => self.forward.forwardShutdown(),
255+
const stable_response = self.stable_resp.?;
256+
const response = Response.fromStable(&stable_response);
257+
258+
for (self.buffered.items) |event| {
259+
switch (event) {
260+
.start => {
261+
self.forward.forwardStart(response) catch |err| {
262+
log.err(.http, "deferred start callback", .{ .err = err, .url = self.request.params.url });
263+
self.forward.forwardErr(err);
264+
return;
265+
};
266+
},
267+
.header => {
268+
const proceed = self.forward.forwardHeader(response) catch |err| {
269+
log.err(.http, "deferred header callback", .{ .err = err, .url = self.request.params.url });
270+
self.forward.forwardErr(err);
271+
return;
272+
};
273+
274+
if (!proceed) {
275+
self.forward.forwardErr(error.Abort);
276+
}
277+
},
278+
.data => |chunk| {
279+
self.forward.forwardData(response, chunk) catch |err| {
280+
log.err(.http, "deferred data callback", .{ .err = err, .url = self.request.params.url });
281+
self.forward.forwardErr(err);
282+
return;
283+
};
284+
},
285+
.done => {
286+
self.forward.forwardDone() catch |err| {
287+
log.err(.http, "deferred done callback", .{ .err = err, .url = self.request.params.url });
288+
self.forward.forwardErr(err);
289+
};
290+
291+
return;
292+
},
293+
.err => |err| {
294+
self.forward.forwardErr(err);
295+
return;
296+
},
297+
.shutdown => {
298+
self.forward.forwardShutdown();
299+
return;
300+
},
301+
}
223302
}
224303
}
225304
};

0 commit comments

Comments
 (0)