Skip to content

Commit a5d87ab

Browse files
committed
Reduce duration of the main request
We currently keep the main request open during loadHTMLDoc and processHTMLDoc. It _has_ to be open during loadHTMLDoc, since that streams the body. But it does not have to be open during processHTMLDoc, which can be log and itself could make use of that same connection if it was released. Reorganized the navigate flow to limit the scope of the request. Also, just like we track pending_write and pending_read, we now also track pending_connect and only shutdown when all are not pending.
1 parent 48c25c3 commit a5d87ab

File tree

4 files changed

+75
-46
lines changed

4 files changed

+75
-46
lines changed

src/browser/html/window.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ pub const Window = struct {
164164
}
165165

166166
pub fn _requestAnimationFrame(self: *Window, cbk: Function, page: *Page) !u32 {
167-
return self.createTimeout(cbk, 5, page, .{.animation_frame = true});
167+
return self.createTimeout(cbk, 5, page, .{ .animation_frame = true });
168168
}
169169

170170
pub fn _cancelAnimationFrame(self: *Window, id: u32, page: *Page) !void {
@@ -179,7 +179,7 @@ pub const Window = struct {
179179

180180
// TODO handle callback arguments.
181181
pub fn _setInterval(self: *Window, cbk: Function, delay: ?u32, page: *Page) !u32 {
182-
return self.createTimeout(cbk, delay, page, .{.repeat = true});
182+
return self.createTimeout(cbk, delay, page, .{ .repeat = true });
183183
}
184184

185185
pub fn _clearTimeout(self: *Window, id: u32, page: *Page) !void {

src/browser/page.zig

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -207,58 +207,63 @@ pub const Page = struct {
207207
// redirect)
208208
self.url = request_url;
209209

210-
// load the data
211-
var request = try self.newHTTPRequest(opts.method, &self.url, .{ .navigation = true });
212-
defer request.deinit();
213-
request.body = opts.body;
214-
request.notification = notification;
210+
{
211+
// block exists to limit the lifetime of the request, which holds
212+
// onto a connection
213+
var request = try self.newHTTPRequest(opts.method, &self.url, .{ .navigation = true });
214+
defer request.deinit();
215215

216-
notification.dispatch(.page_navigate, &.{
217-
.opts = opts,
218-
.url = &self.url,
219-
.timestamp = timestamp(),
220-
});
216+
request.body = opts.body;
217+
request.notification = notification;
221218

222-
var response = try request.sendSync(.{});
219+
notification.dispatch(.page_navigate, &.{
220+
.opts = opts,
221+
.url = &self.url,
222+
.timestamp = timestamp(),
223+
});
223224

224-
// would be different than self.url in the case of a redirect
225-
self.url = try URL.fromURI(arena, request.request_uri);
225+
var response = try request.sendSync(.{});
226226

227-
const header = response.header;
228-
try session.cookie_jar.populateFromResponse(&self.url.uri, &header);
227+
// would be different than self.url in the case of a redirect
228+
self.url = try URL.fromURI(arena, request.request_uri);
229229

230-
// TODO handle fragment in url.
231-
try self.window.replaceLocation(.{ .url = try self.url.toWebApi(arena) });
230+
const header = response.header;
231+
try session.cookie_jar.populateFromResponse(&self.url.uri, &header);
232232

233-
const content_type = header.get("content-type");
233+
// TODO handle fragment in url.
234+
try self.window.replaceLocation(.{ .url = try self.url.toWebApi(arena) });
234235

235-
const mime: Mime = blk: {
236-
if (content_type) |ct| {
237-
break :blk try Mime.parse(arena, ct);
238-
}
239-
break :blk Mime.sniff(try response.peek());
240-
} orelse .unknown;
236+
const content_type = header.get("content-type");
241237

242-
log.info(.http, "navigation", .{
243-
.status = header.status,
244-
.content_type = content_type,
245-
.charset = mime.charset,
246-
.url = request_url,
247-
});
238+
const mime: Mime = blk: {
239+
if (content_type) |ct| {
240+
break :blk try Mime.parse(arena, ct);
241+
}
242+
break :blk Mime.sniff(try response.peek());
243+
} orelse .unknown;
244+
245+
log.info(.http, "navigation", .{
246+
.status = header.status,
247+
.content_type = content_type,
248+
.charset = mime.charset,
249+
.url = request_url,
250+
});
251+
252+
if (!mime.isHTML()) {
253+
var arr: std.ArrayListUnmanaged(u8) = .{};
254+
while (try response.next()) |data| {
255+
try arr.appendSlice(arena, try arena.dupe(u8, data));
256+
}
257+
// save the body into the page.
258+
self.raw_data = arr.items;
259+
return;
260+
}
248261

249-
if (mime.isHTML()) {
250-
self.raw_data = null;
251262
try self.loadHTMLDoc(&response, mime.charset orelse "utf-8");
252-
try self.processHTMLDoc();
253-
} else {
254-
var arr: std.ArrayListUnmanaged(u8) = .{};
255-
while (try response.next()) |data| {
256-
try arr.appendSlice(arena, try arena.dupe(u8, data));
257-
}
258-
// save the body into the page.
259-
self.raw_data = arr.items;
260263
}
261264

265+
try self.processHTMLDoc();
266+
262267
notification.dispatch(.page_navigated, &.{
263268
.url = &self.url,
264269
.timestamp = timestamp(),

src/browser/xhr/xhr.zig

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,14 @@ pub const XMLHttpRequest = struct {
430430
self.request_body = try self.arena.dupe(u8, b);
431431
}
432432

433-
try page.request_factory.initAsync(page.arena, self.method, &self.url.?.uri, self, onHttpRequestReady, self.loop,);
433+
try page.request_factory.initAsync(
434+
page.arena,
435+
self.method,
436+
&self.url.?.uri,
437+
self,
438+
onHttpRequestReady,
439+
self.loop,
440+
);
434441
}
435442

436443
fn onHttpRequestReady(ctx: *anyopaque, request: *http.Request) !void {

src/http/client.zig

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,7 @@ pub const Request = struct {
677677

678678
if (self._connection_from_keepalive) {
679679
// we're already connected
680+
async_handler.pending_connect = false;
680681
return async_handler.conn.connected();
681682
}
682683

@@ -915,6 +916,7 @@ fn AsyncHandler(comptime H: type, comptime L: type) type {
915916
shutdown: bool = false,
916917
pending_write: bool = false,
917918
pending_receive: bool = false,
919+
pending_connect: bool = true,
918920

919921
const Self = @This();
920922
const SendQueue = std.DoublyLinkedList([]const u8);
@@ -939,10 +941,15 @@ fn AsyncHandler(comptime H: type, comptime L: type) type {
939941
fn abort(ctx: *anyopaque) void {
940942
var self: *Self = @alignCast(@ptrCast(ctx));
941943
self.shutdown = true;
944+
posix.shutdown(self.request._connection.?.socket, .both) catch {};
942945
self.maybeShutdown();
943946
}
944947

945948
fn connected(self: *Self, _: *IO.Completion, result: IO.ConnectError!void) void {
949+
self.pending_connect = false;
950+
if (self.shutdown) {
951+
return self.maybeShutdown();
952+
}
946953
result catch |err| return self.handleError("Connection failed", err);
947954
self.conn.connected() catch |err| {
948955
self.handleError("connected handler error", err);
@@ -1109,7 +1116,7 @@ fn AsyncHandler(comptime H: type, comptime L: type) type {
11091116

11101117
fn maybeShutdown(self: *Self) void {
11111118
std.debug.assert(self.shutdown);
1112-
if (self.pending_write or self.pending_receive) {
1119+
if (self.pending_write or self.pending_receive or self.pending_connect) {
11131120
return;
11141121
}
11151122

@@ -2538,12 +2545,14 @@ const StatePool = struct {
25382545

25392546
pub fn release(self: *StatePool, state: *State) void {
25402547
state.reset();
2541-
self.mutex.lock();
25422548
var states = self.states;
2549+
2550+
self.mutex.lock();
25432551
const available = self.available;
25442552
states[available] = state;
25452553
self.available = available + 1;
25462554
self.mutex.unlock();
2555+
25472556
self.cond.signal();
25482557
}
25492558
};
@@ -2980,7 +2989,15 @@ test "HttpClient: async connect error" {
29802989
};
29812990

29822991
const uri = try Uri.parse("HTTP://127.0.0.1:9920");
2983-
try client.initAsync(testing.arena_allocator, .GET, &uri, &handler, Handler.requestReady, &loop, .{}, );
2992+
try client.initAsync(
2993+
testing.arena_allocator,
2994+
.GET,
2995+
&uri,
2996+
&handler,
2997+
Handler.requestReady,
2998+
&loop,
2999+
.{},
3000+
);
29843001

29853002
try loop.io.run_for_ns(std.time.ns_per_ms);
29863003
try reset.timedWait(std.time.ns_per_s);

0 commit comments

Comments
 (0)