Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 39 additions & 10 deletions src/browser/browser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const Location = @import("../html/location.zig").Location;

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

const HttpClient = @import("../http/client.zig").Client;
const http = @import("../http/client.zig");
const UserContext = @import("../user_context.zig").UserContext;

const polyfill = @import("../polyfill/polyfill.zig");
Expand All @@ -60,7 +60,7 @@ pub const Browser = struct {
app: *App,
session: ?*Session,
allocator: Allocator,
http_client: *HttpClient,
http_client: *http.Client,
session_pool: SessionPool,
page_arena: std.heap.ArenaAllocator,

Expand Down Expand Up @@ -130,10 +130,12 @@ pub const Session = struct {

window: Window,

// TODO move the shed to the browser?
// TODO move the shed/jar to the browser?
storage_shed: storage.Shed,
cookie_jar: storage.CookieJar,

page: ?Page = null,
http_client: *HttpClient,
http_client: *http.Client,

jstypes: [Types.len]usize = undefined,

Expand All @@ -148,6 +150,7 @@ pub const Session = struct {
.http_client = browser.http_client,
.storage_shed = storage.Shed.init(allocator),
.arena = std.heap.ArenaAllocator.init(allocator),
.cookie_jar = storage.CookieJar.init(allocator),
.window = Window.create(null, .{ .agent = user_agent }),
};

Expand Down Expand Up @@ -183,6 +186,7 @@ pub const Session = struct {
}
self.env.deinit();
self.arena.deinit();
self.cookie_jar.deinit();
self.storage_shed.deinit();
}

Expand Down Expand Up @@ -371,14 +375,16 @@ pub const Page = struct {
} });

// load the data
var request = try self.session.http_client.request(.GET, self.uri);
var request = try self.newHTTPRequest(.GET, self.uri, .{ .navigation = true });
defer request.deinit();
var response = try request.sendSync(.{});

var response = try request.sendSync(.{});
const header = response.header;
try self.session.cookie_jar.populateFromResponse(self.uri, &header);

log.info("GET {any} {d}", .{ self.uri, header.status });

const ct = response.header.get("content-type") orelse {
const ct = header.get("content-type") orelse {
// no content type in HTTP headers.
// TODO try to sniff mime type from the body.
log.info("no content-type HTTP header", .{});
Expand Down Expand Up @@ -439,7 +445,9 @@ pub const Page = struct {

// replace the user context document with the new one.
try session.env.setUserContext(.{
.uri = self.uri,
.document = html_doc,
.cookie_jar = @ptrCast(&self.session.cookie_jar),
.http_client = @ptrCast(self.session.http_client),
});

Expand Down Expand Up @@ -614,13 +622,19 @@ pub const Page = struct {
}
const u = try std.Uri.resolve_inplace(self.uri, res_src, &b);

var request = try self.session.http_client.request(.GET, u);
var request = try self.newHTTPRequest(.GET, u, .{
.origin_uri = self.uri,
.navigation = false,
});
defer request.deinit();

var response = try request.sendSync(.{});
var header = response.header;
try self.session.cookie_jar.populateFromResponse(u, &header);

log.info("fetch {any}: {d}", .{ u, response.header.status });
log.info("fetch {any}: {d}", .{ u, header.status });

if (response.header.status != 200) {
if (header.status != 200) {
return FetchError.BadStatusCode;
}

Expand All @@ -645,6 +659,21 @@ pub const Page = struct {
try s.eval(arena, &self.session.env, body);
}

fn newHTTPRequest(self: *const Page, method: http.Request.Method, uri: std.Uri, opts: storage.cookie.LookupOpts) !http.Request {
const session = self.session;
var request = try session.http_client.request(method, uri);
errdefer request.deinit();

var arr: std.ArrayListUnmanaged(u8) = .{};
try session.cookie_jar.forRequest(uri, arr.writer(self.arena), opts);

if (arr.items.len > 0) {
try request.addHeader("Cookie", arr.items, .{});
}

return request;
}

const Script = struct {
element: *parser.Element,
kind: Kind,
Expand Down
73 changes: 73 additions & 0 deletions src/http/client.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1443,6 +1443,33 @@ pub const ResponseHeader = struct {
pub fn count(self: *const ResponseHeader) usize {
return self.headers.items.len;
}

pub fn iterate(self: *const ResponseHeader, name: []const u8) HeaderIterator {
return .{
.index = 0,
.name = name,
.headers = self.headers,
};
}
};

const HeaderIterator = struct {
index: usize,
name: []const u8,
headers: HeaderList,

pub fn next(self: *HeaderIterator) ?[]const u8 {
const name = self.name;
const index = self.index;
for (self.headers.items[index..], index..) |h, i| {
if (std.mem.eql(u8, name, h.name)) {
self.index = i + 1;
return h.value;
}
}
self.index = self.headers.items.len;
return null;
}
};

// What we emit from the AsyncHandler
Expand Down Expand Up @@ -2044,6 +2071,52 @@ test "HttpClient: async redirect plaintext to TLS" {
}
}

test "HttpClient: HeaderIterator" {
var header = ResponseHeader{};
defer header.headers.deinit(testing.allocator);

{
var it = header.iterate("nope");
try testing.expectEqual(null, it.next());
try testing.expectEqual(null, it.next());
}

try header.headers.append(testing.allocator, .{ .name = "h1", .value = "value1" });
try header.headers.append(testing.allocator, .{ .name = "h2", .value = "value2" });
try header.headers.append(testing.allocator, .{ .name = "h3", .value = "value3" });
try header.headers.append(testing.allocator, .{ .name = "h1", .value = "value4" });
try header.headers.append(testing.allocator, .{ .name = "h1", .value = "value5" });

{
var it = header.iterate("nope");
try testing.expectEqual(null, it.next());
try testing.expectEqual(null, it.next());
}

{
var it = header.iterate("h2");
try testing.expectEqual("value2", it.next());
try testing.expectEqual(null, it.next());
try testing.expectEqual(null, it.next());
}

{
var it = header.iterate("h3");
try testing.expectEqual("value3", it.next());
try testing.expectEqual(null, it.next());
try testing.expectEqual(null, it.next());
}

{
var it = header.iterate("h1");
try testing.expectEqual("value1", it.next());
try testing.expectEqual("value4", it.next());
try testing.expectEqual("value5", it.next());
try testing.expectEqual(null, it.next());
try testing.expectEqual(null, it.next());
}
}

const TestResponse = struct {
status: u16,
keepalive: ?bool,
Expand Down
22 changes: 14 additions & 8 deletions src/main_tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ const apiweb = @import("apiweb.zig");
const Window = @import("html/window.zig").Window;
const xhr = @import("xhr/xhr.zig");
const storage = @import("storage/storage.zig");
const url = @import("url/url.zig");
const URL = url.URL;
const URL = @import("url/url.zig").URL;
const urlquery = @import("url/query.zig");
const Location = @import("html/location.zig").Location;

Expand All @@ -54,7 +53,7 @@ const EventTestExecFn = @import("events/event.zig").testExecFn;
const XHRTestExecFn = xhr.testExecFn;
const ProgressEventTestExecFn = @import("xhr/progress_event.zig").testExecFn;
const StorageTestExecFn = storage.testExecFn;
const URLTestExecFn = url.testExecFn;
const URLTestExecFn = @import("url/url.zig").testExecFn;
const HTMLElementTestExecFn = @import("html/elements.zig").testExecFn;
const MutationObserverTestExecFn = @import("dom/mutation_observer.zig").testExecFn;

Expand Down Expand Up @@ -91,16 +90,23 @@ fn testExecFn(
var http_client = try @import("http/client.zig").Client.init(alloc, 5, .{});
defer http_client.deinit();

// alias global as self and window
var window = Window.create(null, null);

const url = "https://lightpanda.io/opensource-browser/";
var u = try URL.constructor(alloc, url, null);
defer u.deinit(alloc);

var cookie_jar = storage.CookieJar.init(alloc);
defer cookie_jar.deinit();

try js_env.setUserContext(.{
.uri = try std.Uri.parse(url),
.document = doc,
.cookie_jar = &cookie_jar,
.http_client = &http_client,
});

// alias global as self and window
var window = Window.create(null, null);

var u = try URL.constructor(alloc, "https://lightpanda.io/opensource-browser/", null);
defer u.deinit(alloc);
var location = Location{ .url = &u };
try window.replaceLocation(&location);

Expand Down
Loading