Skip to content

Commit f1fd46d

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

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

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

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

46-
const FetchResult = @import("../http/Client.zig").Client.FetchResult;
47-
45+
const http = @import("../http/client.zig");
4846
const UserContext = @import("../user_context.zig").UserContext;
49-
const HttpClient = @import("asyncio").Client;
5047

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

@@ -62,19 +59,19 @@ pub const Browser = struct {
6259
loop: *Loop,
6360
session: ?*Session,
6461
allocator: Allocator,
65-
http_client: HttpClient,
62+
http_client: http.Client,
6663
session_pool: SessionPool,
6764
page_arena: std.heap.ArenaAllocator,
6865

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

71-
pub fn init(allocator: Allocator, loop: *Loop) Browser {
68+
pub fn init(allocator: Allocator, loop: *Loop) !Browser {
7269
return .{
7370
.loop = loop,
7471
.session = null,
7572
.allocator = allocator,
76-
.http_client = .{ .allocator = allocator },
7773
.session_pool = SessionPool.init(allocator),
74+
.http_client = try http.Client.init(allocator, 5),
7875
.page_arena = std.heap.ArenaAllocator.init(allocator),
7976
};
8077
}
@@ -118,9 +115,6 @@ pub const Session = struct {
118115
// all others Session deps use directly self.alloc and not the arena.
119116
arena: std.heap.ArenaAllocator,
120117

121-
// TODO handle proxy
122-
loader: Loader,
123-
124118
env: Env,
125119
inspector: jsruntime.Inspector,
126120

@@ -129,6 +123,7 @@ pub const Session = struct {
129123
// TODO move the shed to the browser?
130124
storage_shed: storage.Shed,
131125
page: ?Page = null,
126+
http_client: *http.Client,
132127

133128
jstypes: [Types.len]usize = undefined,
134129

@@ -138,7 +133,7 @@ pub const Session = struct {
138133
.env = undefined,
139134
.browser = browser,
140135
.inspector = undefined,
141-
.loader = Loader.init(allocator),
136+
.http_client = &browser.http_client,
142137
.storage_shed = storage.Shed.init(allocator),
143138
.arena = std.heap.ArenaAllocator.init(allocator),
144139
.window = Window.create(null, .{ .agent = user_agent }),
@@ -176,7 +171,6 @@ pub const Session = struct {
176171
}
177172
self.env.deinit();
178173
self.arena.deinit();
179-
self.loader.deinit();
180174
self.storage_shed.deinit();
181175
}
182176

@@ -360,32 +354,14 @@ pub const Page = struct {
360354
// TODO handle fragment in url.
361355

362356
// load the data
363-
var resp = try self.session.loader.get(arena, self.uri);
364-
defer resp.deinit();
357+
var request = try self.session.http_client.request(.GET, self.uri);
358+
defer request.deinit();
359+
var response = try request.sendSync(.{});
365360

366-
const req = resp.req;
361+
const header = response.header;
362+
log.info("GET {any} {d}", .{ self.uri, header.status });
367363

368-
log.info("GET {any} {d}", .{ self.uri, @intFromEnum(req.response.status) });
369-
370-
// TODO handle redirection
371-
log.debug("{?} {d} {s}", .{
372-
req.response.version,
373-
@intFromEnum(req.response.status),
374-
req.response.reason,
375-
// TODO log headers
376-
});
377-
378-
// TODO handle charset
379-
// https://html.spec.whatwg.org/#content-type
380-
var it = req.response.iterateHeaders();
381-
var ct_: ?[]const u8 = null;
382-
while (true) {
383-
const h = it.next() orelse break;
384-
if (std.ascii.eqlIgnoreCase(h.name, "Content-Type")) {
385-
ct_ = try arena.dupe(u8, h.value);
386-
}
387-
}
388-
const ct = ct_ orelse {
364+
const ct = response.header.get("content-type") orelse {
389365
// no content type in HTTP headers.
390366
// TODO try to sniff mime type from the body.
391367
log.info("no content-type HTTP header", .{});
@@ -394,14 +370,18 @@ pub const Page = struct {
394370

395371
log.debug("header content-type: {s}", .{ct});
396372
var mime = try Mime.parse(arena, ct);
373+
defer mime.deinit();
397374

398375
if (mime.isHTML()) {
399-
try self.loadHTMLDoc(req.reader(), mime.charset orelse "utf-8", aux_data);
376+
try self.loadHTMLDoc(&response, mime.charset orelse "utf-8", aux_data);
400377
} else {
401378
log.info("non-HTML document: {s}", .{ct});
402-
379+
var arr: std.ArrayListUnmanaged(u8) = .{};
380+
while (try response.next()) |data| {
381+
try arr.appendSlice(arena, try arena.dupe(u8, data));
382+
}
403383
// save the body into the page.
404-
self.raw_data = try req.reader().readAllAlloc(arena, 16 * 1024 * 1024);
384+
self.raw_data = arr.items;
405385
}
406386
}
407387

@@ -443,7 +423,7 @@ pub const Page = struct {
443423
// replace the user context document with the new one.
444424
try session.env.setUserContext(.{
445425
.document = html_doc,
446-
.httpClient = &self.session.browser.http_client,
426+
.http_client = @ptrCast(&self.session.http_client),
447427
});
448428

449429
// browse the DOM tree to retrieve scripts
@@ -620,30 +600,32 @@ pub const Page = struct {
620600

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

623-
var fetchres = try self.session.loader.get(arena, u);
624-
defer fetchres.deinit();
625-
626-
const resp = fetchres.req.response;
603+
var request = try self.session.http_client.request(.GET, u);
604+
defer request.deinit();
605+
var response = try request.sendSync(.{});
627606

628-
log.info("fetch {any}: {d}", .{ u, resp.status });
607+
log.info("fetch {any}: {d}", .{ u, response.header.status });
629608

630-
if (resp.status != .ok) {
609+
if (response.header.status != 200) {
631610
return FetchError.BadStatusCode;
632611
}
633612

613+
var arr: std.ArrayListUnmanaged(u8) = .{};
614+
while (try response.next()) |data| {
615+
try arr.appendSlice(arena, try arena.dupe(u8, data));
616+
}
617+
634618
// TODO check content-type
635-
const body = try fetchres.req.reader().readAllAlloc(arena, 16 * 1024 * 1024);
636619

637620
// check no body
638-
if (body.len == 0) {
621+
if (arr.items.len == 0) {
639622
return FetchError.NoBody;
640623
}
641624

642-
return body;
625+
return arr.items;
643626
}
644627

645-
// fetchScript senf a GET request to the src and execute the script
646-
// received.
628+
647629
fn fetchScript(self: *const Page, s: *const Script) !void {
648630
const arena = self.arena;
649631

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
@@ -73,13 +73,13 @@ pub fn CDPT(comptime TypeProvider: type) type {
7373
pub const Browser = TypeProvider.Browser;
7474
pub const Session = TypeProvider.Session;
7575

76-
pub fn init(allocator: Allocator, client: TypeProvider.Client, loop: TypeProvider.Loop) Self {
76+
pub fn init(allocator: Allocator, client: TypeProvider.Client, loop: TypeProvider.Loop) !Self {
7777
return .{
7878
.loop = loop,
7979
.client = client,
8080
.allocator = allocator,
8181
.browser_context = null,
82-
.browser = Browser.init(allocator, loop),
82+
.browser = try Browser.init(allocator, loop),
8383
.message_arena = std.heap.ArenaAllocator.init(allocator),
8484
.browser_context_pool = std.heap.MemoryPool(BrowserContext(Self)).init(allocator),
8585
};

src/cdp/testing.zig

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

19-
pub fn init(allocator: Allocator, loop: anytype) Browser {
19+
pub fn init(allocator: Allocator, loop: anytype) !Browser {
2020
_ = loop;
2121
return .{
2222
.arena = std.heap.ArenaAllocator.init(allocator),
@@ -135,7 +135,7 @@ const TestContext = struct {
135135
self.client = Client.init(self.arena.allocator());
136136
// Don't use the arena here. We want to detect leaks in CDP.
137137
// The arena is only for test-specific stuff
138-
self.cdp_ = TestCDP.init(std.testing.allocator, &self.client.?, {});
138+
self.cdp_ = try TestCDP.init(std.testing.allocator, &self.client.?, {});
139139
}
140140
return &self.cdp_.?;
141141
}

0 commit comments

Comments
 (0)