mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-16 08:18:59 +00:00
3# This is a combination of 3 commits.
intercept continue and abort feedback First version of headers, no cookies yet
This commit is contained in:
@@ -20,6 +20,8 @@ const std = @import("std");
|
||||
const log = @import("../log.zig");
|
||||
const builtin = @import("builtin");
|
||||
const Http = @import("Http.zig");
|
||||
pub const Headers = Http.Headers;
|
||||
const Notification = @import("../notification.zig").Notification;
|
||||
|
||||
const c = Http.c;
|
||||
|
||||
@@ -57,6 +59,9 @@ multi: *c.CURLM,
|
||||
// of easys.
|
||||
handles: Handles,
|
||||
|
||||
// Use to generate the next request ID
|
||||
next_request_id: u64 = 0,
|
||||
|
||||
// When handles has no more available easys, requests get queued.
|
||||
queue: RequestQueue,
|
||||
|
||||
@@ -74,6 +79,9 @@ transfer_pool: std.heap.MemoryPool(Transfer),
|
||||
// see ScriptManager.blockingGet
|
||||
blocking: Handle,
|
||||
|
||||
// To notify registered subscribers of events, the browser sets/nulls this for us.
|
||||
notification: ?*Notification = null,
|
||||
|
||||
// The only place this is meant to be used is in `makeRequest` BEFORE `perform`
|
||||
// is called. It is used to generate our Cookie header. It can be used for other
|
||||
// purposes, but keep in mind that, while single-threaded, calls like makeRequest
|
||||
@@ -184,12 +192,26 @@ pub fn tick(self: *Client, timeout_ms: usize) !void {
|
||||
}
|
||||
|
||||
pub fn request(self: *Client, req: Request) !void {
|
||||
var req_copy = req; // We need it mutable
|
||||
|
||||
if (req_copy.id == null) { // If the ID has already been set that means the request was previously intercepted
|
||||
req_copy.id = self.next_request_id;
|
||||
self.next_request_id += 1;
|
||||
if (self.notification) |notification| {
|
||||
notification.dispatch(.http_request_start, &.{ .request = &req_copy });
|
||||
|
||||
var wait_for_interception = false;
|
||||
notification.dispatch(.http_request_intercept, &.{ .request = &req_copy, .wait_for_interception = &wait_for_interception });
|
||||
if (wait_for_interception) return; // The user is send an invitation to intercept this request.
|
||||
}
|
||||
}
|
||||
|
||||
if (self.handles.getFreeHandle()) |handle| {
|
||||
return self.makeRequest(handle, req);
|
||||
return self.makeRequest(handle, req_copy);
|
||||
}
|
||||
|
||||
const node = try self.queue_node_pool.create();
|
||||
node.data = req;
|
||||
node.data = req_copy;
|
||||
self.queue.append(node);
|
||||
}
|
||||
|
||||
@@ -239,7 +261,8 @@ fn makeRequest(self: *Client, handle: *Handle, req: Request) !void {
|
||||
return;
|
||||
};
|
||||
|
||||
const header_list = blk: {
|
||||
var header_list = req.headers;
|
||||
{
|
||||
errdefer self.handles.release(handle);
|
||||
try conn.setMethod(req.method);
|
||||
try conn.setURL(req.url);
|
||||
@@ -248,31 +271,23 @@ fn makeRequest(self: *Client, handle: *Handle, req: Request) !void {
|
||||
try conn.setBody(b);
|
||||
}
|
||||
|
||||
var header_list = conn.commonHeaders();
|
||||
errdefer c.curl_slist_free_all(header_list);
|
||||
// { // TODO move up to `fn request()`
|
||||
// const aa = self.arena.allocator();
|
||||
// var arr: std.ArrayListUnmanaged(u8) = .{};
|
||||
// try req.cookie.forRequest(&uri, arr.writer(aa));
|
||||
|
||||
if (req.header) |hdr| {
|
||||
header_list = c.curl_slist_append(header_list, hdr);
|
||||
}
|
||||
// if (arr.items.len > 0) {
|
||||
// try arr.append(aa, 0); //null terminate
|
||||
|
||||
{
|
||||
const aa = self.arena.allocator();
|
||||
var arr: std.ArrayListUnmanaged(u8) = .{};
|
||||
try req.cookie.forRequest(&uri, arr.writer(aa));
|
||||
// // copies the value
|
||||
// header_list = c.curl_slist_append(header_list, @ptrCast(arr.items.ptr));
|
||||
// defer _ = self.arena.reset(.{ .retain_with_limit = 2048 });
|
||||
// }
|
||||
// }
|
||||
|
||||
if (arr.items.len > 0) {
|
||||
try arr.append(aa, 0); //null terminate
|
||||
|
||||
// copies the value
|
||||
header_list = c.curl_slist_append(header_list, @ptrCast(arr.items.ptr));
|
||||
defer _ = self.arena.reset(.{ .retain_with_limit = 2048 });
|
||||
}
|
||||
}
|
||||
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HTTPHEADER, header_list));
|
||||
|
||||
break :blk header_list;
|
||||
};
|
||||
try conn.secretHeaders(&header_list); // Add headers that must be hidden from intercepts
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HTTPHEADER, header_list.headers));
|
||||
}
|
||||
|
||||
{
|
||||
errdefer self.handles.release(handle);
|
||||
@@ -284,7 +299,7 @@ fn makeRequest(self: *Client, handle: *Handle, req: Request) !void {
|
||||
.req = req,
|
||||
.ctx = req.ctx,
|
||||
.handle = handle,
|
||||
._request_header_list = header_list,
|
||||
._request_header_list = header_list.headers,
|
||||
};
|
||||
errdefer self.transfer_pool.destroy(transfer);
|
||||
|
||||
@@ -471,10 +486,11 @@ pub const RequestCookie = struct {
|
||||
};
|
||||
|
||||
pub const Request = struct {
|
||||
id: ?u64 = null,
|
||||
method: Method,
|
||||
url: [:0]const u8,
|
||||
headers: Headers,
|
||||
body: ?[]const u8 = null,
|
||||
header: ?[:0]const u8 = null,
|
||||
cookie: RequestCookie,
|
||||
|
||||
// arbitrary data that can be associated with this request
|
||||
|
||||
@@ -185,20 +185,20 @@ pub const Connection = struct {
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_POSTFIELDS, body.ptr));
|
||||
}
|
||||
|
||||
pub fn commonHeaders(self: *const Connection) *c.curl_slist {
|
||||
var header_list = c.curl_slist_append(null, "User-Agent: Lightpanda/1.0");
|
||||
// These are headers that may not be send to the users for inteception.
|
||||
pub fn secretHeaders(self: *const Connection, headers: *Headers) !void {
|
||||
if (self.opts.proxy_bearer_token) |hdr| {
|
||||
header_list = c.curl_slist_append(header_list, hdr);
|
||||
try headers.add(hdr);
|
||||
}
|
||||
return header_list;
|
||||
}
|
||||
|
||||
pub fn request(self: *const Connection) !u16 {
|
||||
const easy = self.easy;
|
||||
|
||||
const header_list = self.commonHeaders();
|
||||
defer c.curl_slist_free_all(header_list);
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HTTPHEADER, header_list));
|
||||
const header_list = try Headers.init();
|
||||
defer header_list.deinit();
|
||||
try self.secretHeaders(&header_list);
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HTTPHEADER, header_list.headers));
|
||||
|
||||
try errorCheck(c.curl_easy_perform(easy));
|
||||
var http_code: c_long = undefined;
|
||||
@@ -210,6 +210,59 @@ pub const Connection = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const Headers = struct {
|
||||
headers: *c.curl_slist,
|
||||
|
||||
pub fn init() !Headers {
|
||||
const header_list = c.curl_slist_append(null, "User-Agent: Lightpanda/1.0");
|
||||
if (header_list == null) return error.OutOfMemory;
|
||||
return .{ .headers = header_list };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Headers) void {
|
||||
c.curl_slist_free_all(self.headers);
|
||||
}
|
||||
|
||||
pub fn add(self: *Headers, header: [*c]const u8) !void {
|
||||
const updated_headers = c.curl_slist_append(self.headers, header);
|
||||
if (updated_headers == null) return error.OutOfMemory;
|
||||
self.headers = updated_headers;
|
||||
}
|
||||
|
||||
pub fn asHashMap(self: *const Headers, allocator: Allocator) !std.StringArrayHashMapUnmanaged([]const u8) {
|
||||
var list: std.StringArrayHashMapUnmanaged([]const u8) = .empty;
|
||||
try list.ensureTotalCapacity(allocator, self.count());
|
||||
|
||||
var current: [*c]c.curl_slist = self.headers;
|
||||
while (current) |node| {
|
||||
const str = std.mem.span(@as([*:0]const u8, @ptrCast(node.*.data)));
|
||||
const header = parseHeader(str) orelse return error.InvalidHeader;
|
||||
list.putAssumeCapacity(header.name, header.value);
|
||||
current = node.*.next;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
fn parseHeader(header_str: []const u8) ?struct { name: []const u8, value: []const u8 } {
|
||||
const colon_pos = std.mem.indexOf(u8, header_str, ":") orelse return null;
|
||||
|
||||
const name = std.mem.trim(u8, header_str[0..colon_pos], " \t");
|
||||
const value = std.mem.trim(u8, header_str[colon_pos + 1 ..], " \t");
|
||||
|
||||
return .{ .name = name, .value = value };
|
||||
}
|
||||
|
||||
pub fn count(self: *const Headers) usize {
|
||||
var current: [*c]c.curl_slist = self.headers;
|
||||
var num: usize = 0;
|
||||
while (current) |node| {
|
||||
num += 1;
|
||||
current = node.*.next;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn errorCheck(code: c.CURLcode) errors.Error!void {
|
||||
if (code == c.CURLE_OK) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user