Skip to content

Commit 7da7111

Browse files
committed
replace zig-async-io and std.http.Client with a custom HTTP client
1 parent afb46c6 commit 7da7111

File tree

19 files changed

+1560
-2149
lines changed

19 files changed

+1560
-2149
lines changed

.gitmodules

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,3 @@
2323
[submodule "vendor/mimalloc"]
2424
path = vendor/mimalloc
2525
url = https://github.com/microsoft/mimalloc.git/
26-
[submodule "vendor/tls.zig"]
27-
path = vendor/tls.zig
28-
url = https://github.com/ianic/tls.zig.git/
29-
[submodule "vendor/zig-async-io"]
30-
path = vendor/zig-async-io
31-
url = https://github.com/lightpanda-io/zig-async-io.git/
32-
branch = zig-0.14

LICENSING.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ The default license for this project is [AGPL-3.0-only](LICENSE).
1010
The following files are licensed under MIT:
1111

1212
```
13-
src/http/Client.zig
1413
src/polyfill/fetch.js
1514
```
1615

build.zig

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ fn common(
177177
options: jsruntime.Options,
178178
) !void {
179179
const target = step.root_module.resolved_target.?;
180+
const optimize = step.root_module.optimize.?;
181+
const dep_opts = .{ .target = target, .optimize = optimize };
182+
180183
const jsruntimemod = try jsruntime_pkgs.module(
181184
b,
182185
options,
@@ -189,15 +192,7 @@ fn common(
189192
netsurf.addImport("jsruntime", jsruntimemod);
190193
step.root_module.addImport("netsurf", netsurf);
191194

192-
const asyncio = b.addModule("asyncio", .{
193-
.root_source_file = b.path("vendor/zig-async-io/src/lib.zig"),
194-
});
195-
step.root_module.addImport("asyncio", asyncio);
196-
197-
const tlsmod = b.addModule("tls", .{
198-
.root_source_file = b.path("vendor/tls.zig/src/root.zig"),
199-
});
200-
step.root_module.addImport("tls", tlsmod);
195+
step.root_module.addImport("tls", b.dependency("tls", dep_opts).module("tls"));
201196
}
202197

203198
fn moduleNetSurf(b: *std.Build, target: std.Build.ResolvedTarget) !*std.Build.Module {

build.zig.zon

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.{
2+
.name = .browser,
3+
.paths = .{""},
4+
.version = "0.0.0",
5+
.fingerprint = 0xda130f3af836cea0,
6+
.dependencies = .{
7+
.tls = .{
8+
.url = "https://github.com/karlseguin/tls.zig/archive/e39d40150f10464992da11352fb3955b3345272f.tar.gz",
9+
.hash = "122039cd3abe387b69d23930bf12154c2c84fc894874e10129a1fc5e8ac75ca0ddc0"
10+
},
11+
},
12+
}

src/browser/browser.zig

Lines changed: 33 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ const Allocator = std.mem.Allocator;
2424
const Types = @import("root").Types;
2525

2626
const parser = @import("netsurf");
27-
const Loader = @import("loader.zig").Loader;
2827
const Dump = @import("dump.zig");
2928
const Mime = @import("mime.zig").Mime;
3029

@@ -44,10 +43,8 @@ const Location = @import("../html/location.zig").Location;
4443

4544
const storage = @import("../storage/storage.zig");
4645

47-
const FetchResult = @import("../http/Client.zig").Client.FetchResult;
48-
46+
const http = @import("../http/client.zig");
4947
const UserContext = @import("../user_context.zig").UserContext;
50-
const HttpClient = @import("asyncio").Client;
5148

5249
const polyfill = @import("../polyfill/polyfill.zig");
5350

@@ -63,20 +60,20 @@ pub const Browser = struct {
6360
app: *App,
6461
session: ?*Session,
6562
allocator: Allocator,
66-
http_client: HttpClient,
63+
http_client: http.Client,
6764
session_pool: SessionPool,
6865
page_arena: std.heap.ArenaAllocator,
6966

7067
const SessionPool = std.heap.MemoryPool(Session);
7168

72-
pub fn init(app: *App) Browser {
69+
pub fn init(app: *App) !Browser {
7370
const allocator = app.allocator;
7471
return .{
7572
.app = app,
7673
.session = null,
7774
.allocator = allocator,
78-
.http_client = .{ .allocator = allocator },
7975
.session_pool = SessionPool.init(allocator),
76+
.http_client = try http.Client.init(allocator, 5),
8077
.page_arena = std.heap.ArenaAllocator.init(allocator),
8178
};
8279
}
@@ -122,9 +119,6 @@ pub const Session = struct {
122119
// all others Session deps use directly self.alloc and not the arena.
123120
arena: std.heap.ArenaAllocator,
124121

125-
// TODO handle proxy
126-
loader: Loader,
127-
128122
env: Env,
129123
inspector: jsruntime.Inspector,
130124

@@ -133,6 +127,7 @@ pub const Session = struct {
133127
// TODO move the shed to the browser?
134128
storage_shed: storage.Shed,
135129
page: ?Page = null,
130+
http_client: *http.Client,
136131

137132
jstypes: [Types.len]usize = undefined,
138133

@@ -144,7 +139,7 @@ pub const Session = struct {
144139
.env = undefined,
145140
.browser = browser,
146141
.inspector = undefined,
147-
.loader = Loader.init(allocator),
142+
.http_client = &browser.http_client,
148143
.storage_shed = storage.Shed.init(allocator),
149144
.arena = std.heap.ArenaAllocator.init(allocator),
150145
.window = Window.create(null, .{ .agent = user_agent }),
@@ -182,7 +177,6 @@ pub const Session = struct {
182177
}
183178
self.env.deinit();
184179
self.arena.deinit();
185-
self.loader.deinit();
186180
self.storage_shed.deinit();
187181
}
188182

@@ -371,32 +365,14 @@ pub const Page = struct {
371365
} });
372366

373367
// load the data
374-
var resp = try self.session.loader.get(arena, self.uri);
375-
defer resp.deinit();
368+
var request = try self.session.http_client.request(.GET, self.uri);
369+
defer request.deinit();
370+
var response = try request.sendSync(.{});
376371

377-
const req = resp.req;
372+
const header = response.header;
373+
log.info("GET {any} {d}", .{ self.uri, header.status });
378374

379-
log.info("GET {any} {d}", .{ self.uri, @intFromEnum(req.response.status) });
380-
381-
// TODO handle redirection
382-
log.debug("{?} {d} {s}", .{
383-
req.response.version,
384-
@intFromEnum(req.response.status),
385-
req.response.reason,
386-
// TODO log headers
387-
});
388-
389-
// TODO handle charset
390-
// https://html.spec.whatwg.org/#content-type
391-
var it = req.response.iterateHeaders();
392-
var ct_: ?[]const u8 = null;
393-
while (true) {
394-
const h = it.next() orelse break;
395-
if (std.ascii.eqlIgnoreCase(h.name, "Content-Type")) {
396-
ct_ = try arena.dupe(u8, h.value);
397-
}
398-
}
399-
const ct = ct_ orelse {
375+
const ct = response.header.get("content-type") orelse {
400376
// no content type in HTTP headers.
401377
// TODO try to sniff mime type from the body.
402378
log.info("no content-type HTTP header", .{});
@@ -405,14 +381,18 @@ pub const Page = struct {
405381

406382
log.debug("header content-type: {s}", .{ct});
407383
var mime = try Mime.parse(arena, ct);
384+
defer mime.deinit();
408385

409386
if (mime.isHTML()) {
410-
try self.loadHTMLDoc(req.reader(), mime.charset orelse "utf-8", aux_data);
387+
try self.loadHTMLDoc(&response, mime.charset orelse "utf-8", aux_data);
411388
} else {
412389
log.info("non-HTML document: {s}", .{ct});
413-
390+
var arr: std.ArrayListUnmanaged(u8) = .{};
391+
while (try response.next()) |data| {
392+
try arr.appendSlice(arena, try arena.dupe(u8, data));
393+
}
414394
// save the body into the page.
415-
self.raw_data = try req.reader().readAllAlloc(arena, 16 * 1024 * 1024);
395+
self.raw_data = arr.items;
416396
}
417397
}
418398

@@ -454,7 +434,7 @@ pub const Page = struct {
454434
// replace the user context document with the new one.
455435
try session.env.setUserContext(.{
456436
.document = html_doc,
457-
.httpClient = &self.session.browser.http_client,
437+
.http_client = @ptrCast(&self.session.http_client),
458438
});
459439

460440
// browse the DOM tree to retrieve scripts
@@ -629,30 +609,32 @@ pub const Page = struct {
629609

630610
const u = try std.Uri.resolve_inplace(self.uri, res_src, &b);
631611

632-
var fetchres = try self.session.loader.get(arena, u);
633-
defer fetchres.deinit();
634-
635-
const resp = fetchres.req.response;
612+
var request = try self.session.http_client.request(.GET, u);
613+
defer request.deinit();
614+
var response = try request.sendSync(.{});
636615

637-
log.info("fetch {any}: {d}", .{ u, resp.status });
616+
log.info("fetch {any}: {d}", .{ u, response.header.status });
638617

639-
if (resp.status != .ok) {
618+
if (response.header.status != 200) {
640619
return FetchError.BadStatusCode;
641620
}
642621

622+
var arr: std.ArrayListUnmanaged(u8) = .{};
623+
while (try response.next()) |data| {
624+
try arr.appendSlice(arena, try arena.dupe(u8, data));
625+
}
626+
643627
// TODO check content-type
644-
const body = try fetchres.req.reader().readAllAlloc(arena, 16 * 1024 * 1024);
645628

646629
// check no body
647-
if (body.len == 0) {
630+
if (arr.items.len == 0) {
648631
return FetchError.NoBody;
649632
}
650633

651-
return body;
634+
return arr.items;
652635
}
653636

654-
// fetchScript senf a GET request to the src and execute the script
655-
// received.
637+
656638
fn fetchScript(self: *const Page, s: *const Script) !void {
657639
const arena = self.arena;
658640
const body = try self.fetchData(arena, s.src, null);

src/browser/loader.zig

Lines changed: 0 additions & 97 deletions
This file was deleted.

src/cdp/cdp.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,13 @@ pub fn CDPT(comptime TypeProvider: type) type {
7070
pub const Browser = TypeProvider.Browser;
7171
pub const Session = TypeProvider.Session;
7272

73-
pub fn init(app: *App, client: TypeProvider.Client) Self {
73+
pub fn init(app: *App, client: TypeProvider.Client) !Self {
7474
const allocator = app.allocator;
7575
return .{
7676
.client = client,
7777
.allocator = allocator,
7878
.browser_context = null,
79-
.browser = Browser.init(app),
79+
.browser = try Browser.init(app),
8080
.message_arena = std.heap.ArenaAllocator.init(allocator),
8181
.browser_context_pool = std.heap.MemoryPool(BrowserContext(Self)).init(allocator),
8282
};

src/cdp/testing.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const Browser = struct {
1717
session: ?*Session = null,
1818
arena: std.heap.ArenaAllocator,
1919

20-
pub fn init(app: *App) Browser {
20+
pub fn init(app: *App) !Browser {
2121
return .{
2222
.arena = std.heap.ArenaAllocator.init(app.allocator),
2323
};
@@ -136,7 +136,7 @@ const TestContext = struct {
136136
self.client = Client.init(self.arena.allocator());
137137
// Don't use the arena here. We want to detect leaks in CDP.
138138
// The arena is only for test-specific stuff
139-
self.cdp_ = TestCDP.init(&self.app, &self.client.?);
139+
self.cdp_ = try TestCDP.init(&self.app, &self.client.?);
140140
}
141141
return &self.cdp_.?;
142142
}

0 commit comments

Comments
 (0)