Skip to content

Commit 15a42dd

Browse files
committed
Add command line options to control HTTP client
http_timeout_ms http_connect_timeout_ms http_max_host_open http_max_concurrent
1 parent dc4d8bb commit 15a42dd

File tree

4 files changed

+131
-16
lines changed

4 files changed

+131
-16
lines changed

src/app.zig

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ pub const App = struct {
3535
tls_verify_host: bool = true,
3636
http_proxy: ?[:0]const u8 = null,
3737
proxy_bearer_token: ?[:0]const u8 = null,
38+
http_timeout_ms: ?u31 = null,
39+
http_connect_timeout_ms: ?u31 = null,
40+
http_max_host_open: ?u8 = null,
41+
http_max_concurrent: ?u8 = null,
3842
};
3943

4044
pub fn init(allocator: Allocator, config: Config) !*App {
@@ -51,7 +55,10 @@ pub const App = struct {
5155
errdefer notification.deinit();
5256

5357
var http = try Http.init(allocator, .{
54-
.max_concurrent_transfers = 10,
58+
.max_host_open = config.http_max_host_open orelse 4,
59+
.max_concurrent = config.http_max_concurrent orelse 10,
60+
.timeout_ms = config.http_timeout_ms orelse 5000,
61+
.connect_timeout_ms = config.http_connect_timeout_ms orelse 0,
5562
.http_proxy = config.http_proxy,
5663
.tls_verify_host = config.tls_verify_host,
5764
.proxy_bearer_token = config.proxy_bearer_token,

src/http/Client.zig

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ pub fn init(allocator: Allocator, ca_blob: ?c.curl_blob, opts: Http.Opts) !*Clie
102102
const multi = c.curl_multi_init() orelse return error.FailedToInitializeMulti;
103103
errdefer _ = c.curl_multi_cleanup(multi);
104104

105+
try errorMCheck(c.curl_multi_setopt(multi, c.CURLMOPT_MAX_HOST_CONNECTIONS, @as(c_long, opts.max_host_open)));
106+
105107
var handles = try Handles.init(allocator, client, ca_blob, &opts);
106108
errdefer handles.deinit(allocator);
107109

@@ -346,8 +348,7 @@ const Handles = struct {
346348

347349
// pointer to opts is not stable, don't hold a reference to it!
348350
fn init(allocator: Allocator, client: *Client, ca_blob: ?c.curl_blob, opts: *const Http.Opts) !Handles {
349-
const count = opts.max_concurrent_transfers;
350-
std.debug.assert(count > 0);
351+
const count = if (opts.max_concurrent == 0) 1 else opts.max_concurrent;
351352

352353
const handles = try allocator.alloc(Handle, count);
353354
errdefer allocator.free(handles);

src/http/Http.zig

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,12 @@ pub fn errorMCheck(code: c.CURLMcode) errors.Multi!void {
230230
}
231231

232232
pub const Opts = struct {
233-
timeout_ms: u31 = 0,
233+
timeout_ms: u31,
234+
max_host_open: u8,
235+
max_concurrent: u8,
236+
connect_timeout_ms: u31,
234237
max_redirects: u8 = 10,
235238
tls_verify_host: bool = true,
236-
connect_timeout_ms: u31 = 5000,
237-
max_concurrent_transfers: u8 = 5,
238239
http_proxy: ?[:0]const u8 = null,
239240
proxy_bearer_token: ?[:0]const u8 = null,
240241
};

src/main.zig

Lines changed: 116 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ fn run(alloc: Allocator) !void {
8787
.http_proxy = args.httpProxy(),
8888
.proxy_bearer_token = args.proxyBearerToken(),
8989
.tls_verify_host = args.tlsVerifyHost(),
90+
.http_timeout_ms = args.httpTimeout(),
91+
.http_connect_timeout_ms = args.httpConnectTiemout(),
92+
.http_max_host_open = args.httpMaxHostOpen(),
93+
.http_max_concurrent = args.httpMaxConcurrent(),
9094
});
9195
defer app.deinit();
9296
app.telemetry.record(.{ .run = {} });
@@ -165,6 +169,34 @@ const Command = struct {
165169
};
166170
}
167171

172+
fn httpMaxConcurrent(self: *const Command) ?u8 {
173+
return switch (self.mode) {
174+
inline .serve, .fetch => |opts| opts.common.http_max_concurrent,
175+
else => unreachable,
176+
};
177+
}
178+
179+
fn httpMaxHostOpen(self: *const Command) ?u8 {
180+
return switch (self.mode) {
181+
inline .serve, .fetch => |opts| opts.common.http_max_host_open,
182+
else => unreachable,
183+
};
184+
}
185+
186+
fn httpConnectTiemout(self: *const Command) ?u31 {
187+
return switch (self.mode) {
188+
inline .serve, .fetch => |opts| opts.common.http_connect_timeout,
189+
else => unreachable,
190+
};
191+
}
192+
193+
fn httpTimeout(self: *const Command) ?u31 {
194+
return switch (self.mode) {
195+
inline .serve, .fetch => |opts| opts.common.http_timeout,
196+
else => unreachable,
197+
};
198+
}
199+
168200
fn logLevel(self: *const Command) ?log.Level {
169201
return switch (self.mode) {
170202
inline .serve, .fetch => |opts| opts.common.log_level,
@@ -208,31 +240,52 @@ const Command = struct {
208240
};
209241

210242
const Common = struct {
211-
http_proxy: ?[:0]const u8 = null,
212243
proxy_bearer_token: ?[:0]const u8 = null,
244+
http_proxy: ?[:0]const u8 = null,
245+
http_max_concurrent: ?u8 = null,
246+
http_max_host_open: ?u8 = null,
247+
http_timeout: ?u31 = null,
248+
http_connect_timeout: ?u31 = null,
213249
tls_verify_host: bool = true,
214250
log_level: ?log.Level = null,
215251
log_format: ?log.Format = null,
216252
log_filter_scopes: ?[]log.Scope = null,
217253
};
218254

219255
fn printUsageAndExit(self: *const Command, success: bool) void {
256+
// MAX_HELP_LEN|
220257
const common_options =
221258
\\
222259
\\--insecure_disable_tls_host_verification
223-
\\ Disables host verification on all HTTP requests.
224-
\\ This is an advanced option which should only be
225-
\\ set if you understand and accept the risk of
226-
\\ disabling host verification.
260+
\\ Disables host verification on all HTTP requests. This is an
261+
\\ advanced option which should only be set if you understand
262+
\\ and accept the risk of disabling host verification.
227263
\\
228264
\\--http_proxy The HTTP proxy to use for all HTTP requests.
229-
\\ A username:password can be included to use basic
230-
\\ authentication.
265+
\\ A username:password can be included for basic authentication.
231266
\\ Defaults to none.
232267
\\
233268
\\--proxy_bearer_token
234-
\\ The <token> to send for bearer authentication with the proxy
235-
\\ Proxy-Authorization: Bearer <token>
269+
\\ The <token> to send for bearer authentication with the proxy
270+
\\ Proxy-Authorization: Bearer <token>
271+
\\
272+
\\--http_max_concurrent
273+
\\ The maximum number of concurrent HTTP requests.
274+
\\ Defaults to 10.
275+
\\
276+
\\--http_max_host_open
277+
\\ The maximum number of open connection to a given host:port.
278+
\\ Defaults to 4.
279+
\\
280+
\\--http_connect_timeout
281+
\\ The time, in milliseconds, for establishing an HTTP connection
282+
\\ before timing out. 0 means it never times out.
283+
\\ Defaults to 0.
284+
\\
285+
\\--http_timeout
286+
\\ The maximum time, in milliseconds, the transfer is allowed
287+
\\ to complete. 0 means it never times out.
288+
\\ Defaults to 10000.
236289
\\
237290
\\--log_level The log level: debug, info, warn, error or fatal.
238291
\\ Defaults to
@@ -243,9 +296,9 @@ const Command = struct {
243296
\\ Defaults to
244297
++ (if (builtin.mode == .Debug) " pretty." else " logfmt.") ++
245298
\\
246-
\\
247299
;
248300

301+
// MAX_HELP_LEN|
249302
const usage =
250303
\\usage: {s} command [options] [URL]
251304
\\
@@ -497,6 +550,58 @@ fn parseCommonArg(
497550
return true;
498551
}
499552

553+
if (std.mem.eql(u8, "--http_max_concurrent", opt)) {
554+
const str = args.next() orelse {
555+
log.fatal(.app, "missing argument value", .{ .arg = "--http_max_concurrent" });
556+
return error.InvalidArgument;
557+
};
558+
559+
common.http_max_concurrent = std.fmt.parseInt(u8, str, 10) catch |err| {
560+
log.fatal(.app, "invalid argument value", .{ .arg = "--http_max_concurrent", .err = err });
561+
return error.InvalidArgument;
562+
};
563+
return true;
564+
}
565+
566+
if (std.mem.eql(u8, "--http_max_host_open", opt)) {
567+
const str = args.next() orelse {
568+
log.fatal(.app, "missing argument value", .{ .arg = "--http_max_host_open" });
569+
return error.InvalidArgument;
570+
};
571+
572+
common.http_max_host_open = std.fmt.parseInt(u8, str, 10) catch |err| {
573+
log.fatal(.app, "invalid argument value", .{ .arg = "--http_max_host_open", .err = err });
574+
return error.InvalidArgument;
575+
};
576+
return true;
577+
}
578+
579+
if (std.mem.eql(u8, "--http_connect_timeout", opt)) {
580+
const str = args.next() orelse {
581+
log.fatal(.app, "missing argument value", .{ .arg = "--http_connect_timeout" });
582+
return error.InvalidArgument;
583+
};
584+
585+
common.http_connect_timeout = std.fmt.parseInt(u31, str, 10) catch |err| {
586+
log.fatal(.app, "invalid argument value", .{ .arg = "--http_connect_timeout", .err = err });
587+
return error.InvalidArgument;
588+
};
589+
return true;
590+
}
591+
592+
if (std.mem.eql(u8, "--http_timeout", opt)) {
593+
const str = args.next() orelse {
594+
log.fatal(.app, "missing argument value", .{ .arg = "--http_timeout" });
595+
return error.InvalidArgument;
596+
};
597+
598+
common.http_timeout = std.fmt.parseInt(u31, str, 10) catch |err| {
599+
log.fatal(.app, "invalid argument value", .{ .arg = "--http_timeout", .err = err });
600+
return error.InvalidArgument;
601+
};
602+
return true;
603+
}
604+
500605
if (std.mem.eql(u8, "--log_level", opt)) {
501606
const str = args.next() orelse {
502607
log.fatal(.app, "missing argument value", .{ .arg = "--log_level" });
@@ -644,6 +749,7 @@ fn serveCDP(address: std.net.Address, platform: *const Platform) !void {
644749
.run_mode = .serve,
645750
.tls_verify_host = false,
646751
.platform = platform,
752+
.max_concurrent_transfers = 2,
647753
});
648754
defer app.deinit();
649755

0 commit comments

Comments
 (0)