mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 15:13:28 +00:00
connect proxy
This commit is contained in:
@@ -33,9 +33,8 @@ pub const App = struct {
|
|||||||
run_mode: RunMode,
|
run_mode: RunMode,
|
||||||
platform: ?*const Platform = null,
|
platform: ?*const Platform = null,
|
||||||
tls_verify_host: bool = true,
|
tls_verify_host: bool = true,
|
||||||
http_proxy: ?std.Uri = null,
|
http_proxy: ?[:0]const u8 = null,
|
||||||
proxy_type: ?Http.ProxyType = null,
|
proxy_bearer_token: ?[:0]const u8 = null,
|
||||||
proxy_auth: ?Http.ProxyAuth = null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, config: Config) !*App {
|
pub fn init(allocator: Allocator, config: Config) !*App {
|
||||||
@@ -53,7 +52,9 @@ pub const App = struct {
|
|||||||
|
|
||||||
var http = try Http.init(allocator, .{
|
var http = try Http.init(allocator, .{
|
||||||
.max_concurrent_transfers = 3,
|
.max_concurrent_transfers = 3,
|
||||||
|
.http_proxy = config.http_proxy,
|
||||||
.tls_verify_host = config.tls_verify_host,
|
.tls_verify_host = config.tls_verify_host,
|
||||||
|
.proxy_bearer_token = config.proxy_bearer_token,
|
||||||
});
|
});
|
||||||
errdefer http.deinit();
|
errdefer http.deinit();
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ blocking_active: if (builtin.mode == .Debug) bool else void = if (builtin.mode =
|
|||||||
|
|
||||||
const RequestQueue = std.DoublyLinkedList(Request);
|
const RequestQueue = std.DoublyLinkedList(Request);
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, ca_blob: c.curl_blob, opts: Http.Opts) !*Client {
|
pub fn init(allocator: Allocator, ca_blob: ?c.curl_blob, opts: Http.Opts) !*Client {
|
||||||
var transfer_pool = std.heap.MemoryPool(Transfer).init(allocator);
|
var transfer_pool = std.heap.MemoryPool(Transfer).init(allocator);
|
||||||
errdefer transfer_pool.deinit();
|
errdefer transfer_pool.deinit();
|
||||||
|
|
||||||
@@ -70,10 +70,10 @@ pub fn init(allocator: Allocator, ca_blob: c.curl_blob, opts: Http.Opts) !*Clien
|
|||||||
const multi = c.curl_multi_init() orelse return error.FailedToInitializeMulti;
|
const multi = c.curl_multi_init() orelse return error.FailedToInitializeMulti;
|
||||||
errdefer _ = c.curl_multi_cleanup(multi);
|
errdefer _ = c.curl_multi_cleanup(multi);
|
||||||
|
|
||||||
var handles = try Handles.init(allocator, client, ca_blob, opts);
|
var handles = try Handles.init(allocator, client, ca_blob, &opts);
|
||||||
errdefer handles.deinit(allocator);
|
errdefer handles.deinit(allocator);
|
||||||
|
|
||||||
var blocking = try Handle.init(client, ca_blob, opts);
|
var blocking = try Handle.init(client, ca_blob, &opts);
|
||||||
errdefer blocking.deinit();
|
errdefer blocking.deinit();
|
||||||
|
|
||||||
client.* = .{
|
client.* = .{
|
||||||
@@ -104,7 +104,7 @@ pub fn deinit(self: *Client) void {
|
|||||||
|
|
||||||
pub fn abort(self: *Client) void {
|
pub fn abort(self: *Client) void {
|
||||||
while (self.handles.in_use.first) |node| {
|
while (self.handles.in_use.first) |node| {
|
||||||
var transfer = Transfer.fromEasy(node.data.easy) catch |err| {
|
var transfer = Transfer.fromEasy(node.data.conn.easy) catch |err| {
|
||||||
log.err(.http, "get private info", .{ .err = err, .source = "abort" });
|
log.err(.http, "get private info", .{ .err = err, .source = "abort" });
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
@@ -173,19 +173,19 @@ pub fn blockingRequest(self: *Client, req: Request) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn makeRequest(self: *Client, handle: *Handle, req: Request) !void {
|
fn makeRequest(self: *Client, handle: *Handle, req: Request) !void {
|
||||||
const easy = handle.easy;
|
const conn = handle.conn;
|
||||||
|
const easy = conn.easy;
|
||||||
|
|
||||||
const header_list = blk: {
|
const header_list = blk: {
|
||||||
errdefer self.handles.release(handle);
|
errdefer self.handles.release(handle);
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_URL, req.url.ptr));
|
try conn.setMethod(req.method);
|
||||||
|
try conn.setURL(req.url);
|
||||||
|
|
||||||
try Http.setMethod(easy, req.method);
|
|
||||||
if (req.body) |b| {
|
if (req.body) |b| {
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_POSTFIELDS, b.ptr));
|
try conn.setBody(b);
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_POSTFIELDSIZE, @as(c_long, @intCast(b.len))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var header_list = c.curl_slist_append(null, "User-Agent: Lightpanda/1.0");
|
var header_list = conn.commonHeaders();
|
||||||
errdefer c.curl_slist_free_all(header_list);
|
errdefer c.curl_slist_free_all(header_list);
|
||||||
|
|
||||||
if (req.content_type) |ct| {
|
if (req.content_type) |ct| {
|
||||||
@@ -269,7 +269,7 @@ fn endTransfer(self: *Client, transfer: *Transfer) void {
|
|||||||
transfer.deinit();
|
transfer.deinit();
|
||||||
self.transfer_pool.destroy(transfer);
|
self.transfer_pool.destroy(transfer);
|
||||||
|
|
||||||
errorMCheck(c.curl_multi_remove_handle(self.multi, handle.easy)) catch |err| {
|
errorMCheck(c.curl_multi_remove_handle(self.multi, handle.conn.easy)) catch |err| {
|
||||||
log.fatal(.http, "Failed to abort", .{ .err = err });
|
log.fatal(.http, "Failed to abort", .{ .err = err });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -284,7 +284,8 @@ const Handles = struct {
|
|||||||
|
|
||||||
const HandleList = std.DoublyLinkedList(*Handle);
|
const HandleList = std.DoublyLinkedList(*Handle);
|
||||||
|
|
||||||
fn init(allocator: Allocator, client: *Client, ca_blob: c.curl_blob, opts: Http.Opts) !Handles {
|
// pointer to opts is not stable, don't hold a reference to it!
|
||||||
|
fn init(allocator: Allocator, client: *Client, ca_blob: ?c.curl_blob, opts: *const Http.Opts) !Handles {
|
||||||
const count = opts.max_concurrent_transfers;
|
const count = opts.max_concurrent_transfers;
|
||||||
std.debug.assert(count > 0);
|
std.debug.assert(count > 0);
|
||||||
|
|
||||||
@@ -340,22 +341,16 @@ const Handles = struct {
|
|||||||
|
|
||||||
// wraps a c.CURL (an easy handle)
|
// wraps a c.CURL (an easy handle)
|
||||||
const Handle = struct {
|
const Handle = struct {
|
||||||
easy: *c.CURL,
|
|
||||||
client: *Client,
|
client: *Client,
|
||||||
|
conn: Http.Connection,
|
||||||
node: ?Handles.HandleList.Node,
|
node: ?Handles.HandleList.Node,
|
||||||
|
|
||||||
fn init(client: *Client, ca_blob: c.curl_blob, opts: Http.Opts) !Handle {
|
// pointer to opts is not stable, don't hold a reference to it!
|
||||||
const easy = c.curl_easy_init() orelse return error.FailedToInitializeEasy;
|
fn init(client: *Client, ca_blob: ?c.curl_blob, opts: *const Http.Opts) !Handle {
|
||||||
errdefer _ = c.curl_easy_cleanup(easy);
|
const conn = try Http.Connection.init(ca_blob, opts);
|
||||||
|
errdefer conn.deinit();
|
||||||
|
|
||||||
// timeouts
|
const easy = conn.easy;
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_TIMEOUT_MS, @as(c_long, @intCast(opts.timeout_ms))));
|
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CONNECTTIMEOUT_MS, @as(c_long, @intCast(opts.connect_timeout_ms))));
|
|
||||||
|
|
||||||
// redirect behavior
|
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_MAXREDIRS, @as(c_long, @intCast(opts.max_redirects))));
|
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_FOLLOWLOCATION, @as(c_long, 2)));
|
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_REDIR_PROTOCOLS_STR, "HTTP,HTTPS")); // remove FTP and FTPS from the default
|
|
||||||
|
|
||||||
// callbacks
|
// callbacks
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HEADERDATA, easy));
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HEADERDATA, easy));
|
||||||
@@ -363,30 +358,15 @@ const Handle = struct {
|
|||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_WRITEDATA, easy));
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_WRITEDATA, easy));
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_WRITEFUNCTION, Transfer.dataCallback));
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_WRITEFUNCTION, Transfer.dataCallback));
|
||||||
|
|
||||||
// tls
|
|
||||||
if (opts.tls_verify_host) {
|
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CAINFO_BLOB, ca_blob));
|
|
||||||
} else {
|
|
||||||
// Verify peer checks that the cert is signed by a CA, verify host makes sure the
|
|
||||||
// cert contains the server name.
|
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_SSL_VERIFYPEER, @as(c_long, 0)));
|
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_SSL_VERIFYHOST, @as(c_long, 0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// debug
|
|
||||||
if (comptime Http.ENABLE_DEBUG) {
|
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_VERBOSE, @as(c_long, 1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.easy = easy,
|
.conn = conn,
|
||||||
.node = null,
|
.node = null,
|
||||||
.client = client,
|
.client = client,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: *const Handle) void {
|
fn deinit(self: *const Handle) void {
|
||||||
_ = c.curl_easy_cleanup(self.easy);
|
self.conn.deinit();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ const Http = @This();
|
|||||||
|
|
||||||
opts: Opts,
|
opts: Opts,
|
||||||
client: *Client,
|
client: *Client,
|
||||||
ca_blob: c.curl_blob,
|
ca_blob: ?c.curl_blob,
|
||||||
cert_arena: ArenaAllocator,
|
arena: ArenaAllocator,
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, opts: Opts) !Http {
|
pub fn init(allocator: Allocator, opts: Opts) !Http {
|
||||||
try errorCheck(c.curl_global_init(c.CURL_GLOBAL_SSL));
|
try errorCheck(c.curl_global_init(c.CURL_GLOBAL_SSL));
|
||||||
@@ -49,41 +49,58 @@ pub fn init(allocator: Allocator, opts: Opts) !Http {
|
|||||||
std.debug.print("curl version: {s}\n\n", .{c.curl_version()});
|
std.debug.print("curl version: {s}\n\n", .{c.curl_version()});
|
||||||
}
|
}
|
||||||
|
|
||||||
var cert_arena = ArenaAllocator.init(allocator);
|
var arena = ArenaAllocator.init(allocator);
|
||||||
errdefer cert_arena.deinit();
|
errdefer arena.deinit();
|
||||||
const ca_blob = try loadCerts(allocator, cert_arena.allocator());
|
|
||||||
|
|
||||||
var client = try Client.init(allocator, ca_blob, opts);
|
var adjusted_opts = opts;
|
||||||
|
if (opts.proxy_bearer_token) |bt| {
|
||||||
|
adjusted_opts.proxy_bearer_token = try std.fmt.allocPrintZ(
|
||||||
|
arena.allocator(),
|
||||||
|
"Proxy-Authorization: Bearer {s}",
|
||||||
|
.{bt},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ca_blob: ?c.curl_blob = null;
|
||||||
|
if (opts.tls_verify_host) {
|
||||||
|
ca_blob = try loadCerts(allocator, arena.allocator());
|
||||||
|
}
|
||||||
|
|
||||||
|
var client = try Client.init(allocator, ca_blob, adjusted_opts);
|
||||||
errdefer client.deinit();
|
errdefer client.deinit();
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.opts = opts,
|
.arena = arena,
|
||||||
.client = client,
|
.client = client,
|
||||||
.ca_blob = ca_blob,
|
.ca_blob = ca_blob,
|
||||||
.cert_arena = cert_arena,
|
.opts = adjusted_opts,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Http) void {
|
pub fn deinit(self: *Http) void {
|
||||||
self.client.deinit();
|
self.client.deinit();
|
||||||
c.curl_global_cleanup();
|
c.curl_global_cleanup();
|
||||||
self.cert_arena.deinit();
|
self.arena.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn newConnection(self: *Http) !Connection {
|
pub fn newConnection(self: *Http) !Connection {
|
||||||
return Connection.init(self.ca_blob, self.opts);
|
return Connection.init(self.ca_blob, &self.opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Connection = struct {
|
pub const Connection = struct {
|
||||||
easy: *c.CURL,
|
easy: *c.CURL,
|
||||||
|
opts: Connection.Opts,
|
||||||
|
|
||||||
// Is called by Handles when already partially initialized. Done like this
|
const Opts = struct {
|
||||||
// so that we have a stable pointer to error_buffer.
|
proxy_bearer_token: ?[:0]const u8,
|
||||||
pub fn init(ca_blob: c.curl_blob, opts: Opts) !Connection {
|
};
|
||||||
|
|
||||||
|
// pointer to opts is not stable, don't hold a reference to it!
|
||||||
|
pub fn init(ca_blob_: ?c.curl_blob, opts: *const Http.Opts) !Connection {
|
||||||
const easy = c.curl_easy_init() orelse return error.FailedToInitializeEasy;
|
const easy = c.curl_easy_init() orelse return error.FailedToInitializeEasy;
|
||||||
errdefer _ = c.curl_easy_cleanup(easy);
|
errdefer _ = c.curl_easy_cleanup(easy);
|
||||||
|
|
||||||
// timeouts
|
// timeouts
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_TIMEOUT_MS, @as(c_long, @intCast(opts.timeout_ms))));
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_TIMEOUT_MS, @as(c_long, @intCast(opts.timeout_ms))));
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CONNECTTIMEOUT_MS, @as(c_long, @intCast(opts.connect_timeout_ms))));
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CONNECTTIMEOUT_MS, @as(c_long, @intCast(opts.connect_timeout_ms))));
|
||||||
|
|
||||||
@@ -92,10 +109,36 @@ pub const Connection = struct {
|
|||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_FOLLOWLOCATION, @as(c_long, 2)));
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_FOLLOWLOCATION, @as(c_long, 2)));
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_REDIR_PROTOCOLS_STR, "HTTP,HTTPS")); // remove FTP and FTPS from the default
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_REDIR_PROTOCOLS_STR, "HTTP,HTTPS")); // remove FTP and FTPS from the default
|
||||||
|
|
||||||
|
// proxy
|
||||||
|
if (opts.http_proxy) |proxy| {
|
||||||
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_PROXY, proxy.ptr));
|
||||||
|
}
|
||||||
|
|
||||||
// tls
|
// tls
|
||||||
// try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_SSL_VERIFYHOST, @as(c_long, 0)));
|
if (ca_blob_) |ca_blob| {
|
||||||
// try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_SSL_VERIFYPEER, @as(c_long, 0)));
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CAINFO_BLOB, ca_blob));
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CAINFO_BLOB, ca_blob));
|
if (opts.http_proxy != null) {
|
||||||
|
// Note, this can be difference for the proxy and for the main
|
||||||
|
// request. Might be something worth exposting as command
|
||||||
|
// line arguments at some point.
|
||||||
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_PROXY_CAINFO_BLOB , ca_blob));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std.debug.assert(opts.tls_verify_host == false);
|
||||||
|
|
||||||
|
// Verify peer checks that the cert is signed by a CA, verify host makes sure the
|
||||||
|
// cert contains the server name.
|
||||||
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_SSL_VERIFYHOST, @as(c_long, 0)));
|
||||||
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_SSL_VERIFYPEER, @as(c_long, 0)));
|
||||||
|
|
||||||
|
if (opts.http_proxy != null) {
|
||||||
|
// Note, this can be difference for the proxy and for the main
|
||||||
|
// request. Might be something worth exposting as command
|
||||||
|
// line arguments at some point.
|
||||||
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_PROXY_SSL_VERIFYHOST, @as(c_long, 0)));
|
||||||
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_PROXY_SSL_VERIFYPEER , @as(c_long, 0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// debug
|
// debug
|
||||||
if (comptime Http.ENABLE_DEBUG) {
|
if (comptime Http.ENABLE_DEBUG) {
|
||||||
@@ -104,6 +147,9 @@ pub const Connection = struct {
|
|||||||
|
|
||||||
return .{
|
return .{
|
||||||
.easy = easy,
|
.easy = easy,
|
||||||
|
.opts = .{
|
||||||
|
.proxy_bearer_token = opts.proxy_bearer_token,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +162,15 @@ pub const Connection = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn setMethod(self: *const Connection, method: Method) !void {
|
pub fn setMethod(self: *const Connection, method: Method) !void {
|
||||||
try Http.setMethod(self.easy, method);
|
const easy = self.easy;
|
||||||
|
switch (method) {
|
||||||
|
.GET => try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HTTPGET, @as(c_long, 1))),
|
||||||
|
.POST => try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HTTPPOST, @as(c_long, 1))),
|
||||||
|
.PUT => try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CUSTOMREQUEST, "put")),
|
||||||
|
.DELETE => try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CUSTOMREQUEST, "delete")),
|
||||||
|
.HEAD => try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CUSTOMREQUEST, "head")),
|
||||||
|
.OPTIONS => try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CUSTOMREQUEST, "options")),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setBody(self: *const Connection, body: []const u8) !void {
|
pub fn setBody(self: *const Connection, body: []const u8) !void {
|
||||||
@@ -125,10 +179,24 @@ pub const Connection = struct {
|
|||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_POSTFIELDS, body.ptr));
|
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");
|
||||||
|
if (self.opts.proxy_bearer_token) |hdr| {
|
||||||
|
header_list = c.curl_slist_append(header_list, hdr);
|
||||||
|
}
|
||||||
|
return header_list;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn request(self: *const Connection) !u16 {
|
pub fn request(self: *const Connection) !u16 {
|
||||||
try errorCheck(c.curl_easy_perform(self.easy));
|
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));
|
||||||
|
|
||||||
|
try errorCheck(c.curl_easy_perform(easy));
|
||||||
var http_code: c_long = undefined;
|
var http_code: c_long = undefined;
|
||||||
try errorCheck(c.curl_easy_getinfo(self.easy, c.CURLINFO_RESPONSE_CODE, &http_code));
|
try errorCheck(c.curl_easy_getinfo(easy, c.CURLINFO_RESPONSE_CODE, &http_code));
|
||||||
if (http_code < 0 or http_code > std.math.maxInt(u16)) {
|
if (http_code < 0 or http_code > std.math.maxInt(u16)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -136,17 +204,6 @@ pub const Connection = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// used by Connection and Handle
|
|
||||||
pub fn setMethod(easy: *c.CURL, method: Method) !void {
|
|
||||||
switch (method) {
|
|
||||||
.GET => try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HTTPGET, @as(c_long, 1))),
|
|
||||||
.POST => try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HTTPPOST, @as(c_long, 1))),
|
|
||||||
.PUT => try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CUSTOMREQUEST, "put")),
|
|
||||||
.DELETE => try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CUSTOMREQUEST, "delete")),
|
|
||||||
.HEAD => try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CUSTOMREQUEST, "head")),
|
|
||||||
.OPTIONS => try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CUSTOMREQUEST, "options")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn errorCheck(code: c.CURLcode) errors.Error!void {
|
pub fn errorCheck(code: c.CURLcode) errors.Error!void {
|
||||||
if (code == c.CURLE_OK) {
|
if (code == c.CURLE_OK) {
|
||||||
@@ -173,6 +230,8 @@ pub const Opts = struct {
|
|||||||
tls_verify_host: bool = true,
|
tls_verify_host: bool = true,
|
||||||
connect_timeout_ms: u31 = 5000,
|
connect_timeout_ms: u31 = 5000,
|
||||||
max_concurrent_transfers: u8 = 5,
|
max_concurrent_transfers: u8 = 5,
|
||||||
|
http_proxy: ?[:0]const u8 = null,
|
||||||
|
proxy_bearer_token: ?[:0]const u8 = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Method = enum {
|
pub const Method = enum {
|
||||||
@@ -184,16 +243,6 @@ pub const Method = enum {
|
|||||||
OPTIONS,
|
OPTIONS,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ProxyType = enum {
|
|
||||||
forward,
|
|
||||||
connect,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const ProxyAuth = union(enum) {
|
|
||||||
basic: struct { user_pass: []const u8 },
|
|
||||||
bearer: struct { token: []const u8 },
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: on BSD / Linux, we could just read the PEM file directly.
|
// TODO: on BSD / Linux, we could just read the PEM file directly.
|
||||||
// This whole rescan + decode is really just needed for MacOS. On Linux
|
// This whole rescan + decode is really just needed for MacOS. On Linux
|
||||||
// bundle.rescan does find the .pem file(s) which could be in a few different
|
// bundle.rescan does find the .pem file(s) which could be in a few different
|
||||||
|
|||||||
70
src/main.zig
70
src/main.zig
@@ -85,8 +85,7 @@ fn run(alloc: Allocator) !void {
|
|||||||
.run_mode = args.mode,
|
.run_mode = args.mode,
|
||||||
.platform = &platform,
|
.platform = &platform,
|
||||||
.http_proxy = args.httpProxy(),
|
.http_proxy = args.httpProxy(),
|
||||||
.proxy_type = args.proxyType(),
|
.proxy_bearer_token = args.proxyBearerToken(),
|
||||||
.proxy_auth = args.proxyAuth(),
|
|
||||||
.tls_verify_host = args.tlsVerifyHost(),
|
.tls_verify_host = args.tlsVerifyHost(),
|
||||||
});
|
});
|
||||||
defer app.deinit();
|
defer app.deinit();
|
||||||
@@ -156,23 +155,16 @@ const Command = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn httpProxy(self: *const Command) ?std.Uri {
|
fn httpProxy(self: *const Command) ?[:0]const u8 {
|
||||||
return switch (self.mode) {
|
return switch (self.mode) {
|
||||||
inline .serve, .fetch => |opts| opts.common.http_proxy,
|
inline .serve, .fetch => |opts| opts.common.http_proxy,
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn proxyType(self: *const Command) ?Http.ProxyType {
|
fn proxyBearerToken(self: *const Command) ?[:0]const u8 {
|
||||||
return switch (self.mode) {
|
return switch (self.mode) {
|
||||||
inline .serve, .fetch => |opts| opts.common.proxy_type,
|
inline .serve, .fetch => |opts| opts.common.proxy_bearer_token,
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn proxyAuth(self: *const Command) ?Http.ProxyAuth {
|
|
||||||
return switch (self.mode) {
|
|
||||||
inline .serve, .fetch => |opts| opts.common.proxy_auth,
|
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -221,9 +213,8 @@ const Command = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Common = struct {
|
const Common = struct {
|
||||||
http_proxy: ?std.Uri = null,
|
http_proxy: ?[:0]const u8 = null,
|
||||||
proxy_type: ?Http.ProxyType = null,
|
proxy_bearer_token: ?[:0]const u8 = null,
|
||||||
proxy_auth: ?Http.ProxyAuth = null,
|
|
||||||
tls_verify_host: bool = true,
|
tls_verify_host: bool = true,
|
||||||
log_level: ?log.Level = null,
|
log_level: ?log.Level = null,
|
||||||
log_format: ?log.Format = null,
|
log_format: ?log.Format = null,
|
||||||
@@ -242,21 +233,10 @@ const Command = struct {
|
|||||||
\\--http_proxy The HTTP proxy to use for all HTTP requests.
|
\\--http_proxy The HTTP proxy to use for all HTTP requests.
|
||||||
\\ Defaults to none.
|
\\ Defaults to none.
|
||||||
\\
|
\\
|
||||||
\\--proxy_type The type of proxy: connect, forward.
|
|
||||||
\\ 'connect' creates a tunnel through the proxy via
|
|
||||||
\\ and initial CONNECT request.
|
|
||||||
\\ 'forward' sends the full URL in the request target
|
|
||||||
\\ and expects the proxy to MITM the request.
|
|
||||||
\\ Defaults to connect when --http_proxy is set.
|
|
||||||
\\
|
|
||||||
\\--proxy_bearer_token
|
\\--proxy_bearer_token
|
||||||
\\ The token to send for bearer authentication with the proxy
|
\\ The <token> to send for bearer authentication with the proxy
|
||||||
\\ Proxy-Authorization: Bearer <token>
|
\\ Proxy-Authorization: Bearer <token>
|
||||||
\\
|
\\
|
||||||
\\--proxy_basic_auth
|
|
||||||
\\ The user:password to send for basic authentication with the proxy
|
|
||||||
\\ Proxy-Authorization: Basic <base64(user:password)>
|
|
||||||
\\
|
|
||||||
\\--log_level The log level: debug, info, warn, error or fatal.
|
\\--log_level The log level: debug, info, warn, error or fatal.
|
||||||
\\ Defaults to
|
\\ Defaults to
|
||||||
++ (if (builtin.mode == .Debug) " info." else "warn.") ++
|
++ (if (builtin.mode == .Debug) " info." else "warn.") ++
|
||||||
@@ -521,48 +501,16 @@ fn parseCommonArg(
|
|||||||
log.fatal(.app, "missing argument value", .{ .arg = "--http_proxy" });
|
log.fatal(.app, "missing argument value", .{ .arg = "--http_proxy" });
|
||||||
return error.InvalidArgument;
|
return error.InvalidArgument;
|
||||||
};
|
};
|
||||||
common.http_proxy = try std.Uri.parse(try allocator.dupe(u8, str));
|
common.http_proxy = try allocator.dupeZ(u8, str);
|
||||||
if (common.http_proxy.?.host == null) {
|
|
||||||
log.fatal(.app, "invalid http proxy", .{ .arg = "--http_proxy", .hint = "missing scheme?" });
|
|
||||||
return error.InvalidArgument;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std.mem.eql(u8, "--proxy_type", opt)) {
|
|
||||||
const str = args.next() orelse {
|
|
||||||
log.fatal(.app, "missing argument value", .{ .arg = "--proxy_type" });
|
|
||||||
return error.InvalidArgument;
|
|
||||||
};
|
|
||||||
common.proxy_type = std.meta.stringToEnum(Http.ProxyType, str) orelse {
|
|
||||||
log.fatal(.app, "invalid option choice", .{ .arg = "--proxy_type", .value = str });
|
|
||||||
return error.InvalidArgument;
|
|
||||||
};
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std.mem.eql(u8, "--proxy_bearer_token", opt)) {
|
if (std.mem.eql(u8, "--proxy_bearer_token", opt)) {
|
||||||
if (common.proxy_auth != null) {
|
|
||||||
log.fatal(.app, "proxy auth already set", .{ .arg = "--proxy_bearer_token" });
|
|
||||||
return error.InvalidArgument;
|
|
||||||
}
|
|
||||||
const str = args.next() orelse {
|
const str = args.next() orelse {
|
||||||
log.fatal(.app, "missing argument value", .{ .arg = "--proxy_bearer_token" });
|
log.fatal(.app, "missing argument value", .{ .arg = "--proxy_bearer_token" });
|
||||||
return error.InvalidArgument;
|
return error.InvalidArgument;
|
||||||
};
|
};
|
||||||
common.proxy_auth = .{ .bearer = .{ .token = str } };
|
common.proxy_bearer_token = try allocator.dupeZ(u8, str);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (std.mem.eql(u8, "--proxy_basic_auth", opt)) {
|
|
||||||
if (common.proxy_auth != null) {
|
|
||||||
log.fatal(.app, "proxy auth already set", .{ .arg = "--proxy_basic_auth" });
|
|
||||||
return error.InvalidArgument;
|
|
||||||
}
|
|
||||||
const str = args.next() orelse {
|
|
||||||
log.fatal(.app, "missing argument value", .{ .arg = "--proxy_basic_auth" });
|
|
||||||
return error.InvalidArgument;
|
|
||||||
};
|
|
||||||
common.proxy_auth = .{ .basic = .{ .user_pass = str } };
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user