@@ -24,15 +24,14 @@ const Client = @import("../../browser/HttpClient.zig").Client;
2424const Request = @import ("../../browser/HttpClient.zig" ).Request ;
2525const Response = @import ("../../browser/HttpClient.zig" ).Response ;
2626const Layer = @import ("../../browser/HttpClient.zig" ).Layer ;
27+ const StableResponse = @import ("../../browser/HttpClient.zig" ).StableResponse ;
2728const Forward = @import ("Forward.zig" );
2829
2930const DeferringLayer = @This ();
3031
3132allocator : std.mem.Allocator ,
3233next : 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.
3635deferred : std.DoublyLinkedList = .{},
3736
3837pub fn layer (self : * DeferringLayer ) Layer {
@@ -49,10 +48,13 @@ pub fn deinit(self: *DeferringLayer) void {
4948fn 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.
9797pub 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
120120pub 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
136135const 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