authorgravatar for emil@lerch.orgEmil Lerch <emil@lerch.org> 2023-09-28 11:16:39+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-09-28 14:16:39+03:00
logfcca3cd1a3f5430dd9d813d80a0f0512c99e371f
tree3ee2c1cafab28aa845348fc75f9d46f89f3eb1f8
parent18f1db134cf7fbc10d5aaf0156048c77df003ae4
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

std.http: introduce options to http client to allow for raw uris

Addresses #17015 by introducing a new startWithOptions. The only option is currently is a flag to use the provided URI as is, without modification when passed to the server. Normally, this is not needed nor desired. However, some REST APIs may have requirements that cannot be satisfied with the default handling.

4 files changed, 56 insertions(+), 28 deletions(-)

lib/std/Uri.zig+16-3
...@@ -216,6 +216,7 @@ pub fn format(...@@ -216,6 +216,7 @@ pub fn format(
216216
217 const needs_absolute = comptime std.mem.indexOf(u8, fmt, "+") != null;217 const needs_absolute = comptime std.mem.indexOf(u8, fmt, "+") != null;
218 const needs_path = comptime std.mem.indexOf(u8, fmt, "/") != null or fmt.len == 0;218 const needs_path = comptime std.mem.indexOf(u8, fmt, "/") != null or fmt.len == 0;
219 const raw_uri = comptime std.mem.indexOf(u8, fmt, "r") != null;
219 const needs_fragment = comptime std.mem.indexOf(u8, fmt, "#") != null;220 const needs_fragment = comptime std.mem.indexOf(u8, fmt, "#") != null;
220221
221 if (needs_absolute) {222 if (needs_absolute) {
...@@ -246,18 +247,30 @@ pub fn format(...@@ -246,18 +247,30 @@ pub fn format(
246 if (uri.path.len == 0) {247 if (uri.path.len == 0) {
247 try writer.writeAll("/");248 try writer.writeAll("/");
248 } else {249 } else {
249 try Uri.writeEscapedPath(writer, uri.path);250 if (raw_uri) {
251 try writer.writeAll(uri.path);
252 } else {
253 try Uri.writeEscapedPath(writer, uri.path);
254 }
250 }255 }
251256
252 if (uri.query) |q| {257 if (uri.query) |q| {
253 try writer.writeAll("?");258 try writer.writeAll("?");
254 try Uri.writeEscapedQuery(writer, q);259 if (raw_uri) {
260 try writer.writeAll(q);
261 } else {
262 try Uri.writeEscapedQuery(writer, q);
263 }
255 }264 }
256265
257 if (needs_fragment) {266 if (needs_fragment) {
258 if (uri.fragment) |f| {267 if (uri.fragment) |f| {
259 try writer.writeAll("#");268 try writer.writeAll("#");
260 try Uri.writeEscapedQuery(writer, f);269 if (raw_uri) {
270 try writer.writeAll(f);
271 } else {
272 try Uri.writeEscapedQuery(writer, f);
273 }
261 }274 }
262 }275 }
263 }276 }
lib/std/http/Client.zig+23-8
...@@ -538,8 +538,13 @@ pub const Request = struct {...@@ -538,8 +538,13 @@ pub const Request = struct {
538538
539 pub const StartError = Connection.WriteError || error{ InvalidContentLength, UnsupportedTransferEncoding };539 pub const StartError = Connection.WriteError || error{ InvalidContentLength, UnsupportedTransferEncoding };
540540
541 pub const StartOptions = struct {
542 /// Specifies that the uri should be used as is
543 raw_uri: bool = false,
544 };
545
541 /// Send the request to the server.546 /// Send the request to the server.
542 pub fn start(req: *Request) StartError!void {547 pub fn start(req: *Request, options: StartOptions) StartError!void {
543 if (!req.method.requestHasBody() and req.transfer_encoding != .none) return error.UnsupportedTransferEncoding;548 if (!req.method.requestHasBody() and req.transfer_encoding != .none) return error.UnsupportedTransferEncoding;
544549
545 var buffered = std.io.bufferedWriter(req.connection.?.data.writer());550 var buffered = std.io.bufferedWriter(req.connection.?.data.writer());
...@@ -552,13 +557,22 @@ pub const Request = struct {...@@ -552,13 +557,22 @@ pub const Request = struct {
552 try w.writeAll(req.uri.host.?);557 try w.writeAll(req.uri.host.?);
553 try w.writeByte(':');558 try w.writeByte(':');
554 try w.print("{}", .{req.uri.port.?});559 try w.print("{}", .{req.uri.port.?});
555 } else if (req.connection.?.data.proxied) {
556 // proxied connections require the full uri
557 try w.print("{+/}", .{req.uri});
558 } else {560 } else {
559 try w.print("{/}", .{req.uri});561 if (req.connection.?.data.proxied) {
562 // proxied connections require the full uri
563 if (options.raw_uri) {
564 try w.print("{+/r}", .{req.uri});
565 } else {
566 try w.print("{+/}", .{req.uri});
567 }
568 } else {
569 if (options.raw_uri) {
570 try w.print("{/r}", .{req.uri});
571 } else {
572 try w.print("{/}", .{req.uri});
573 }
574 }
560 }575 }
561
562 try w.writeByte(' ');576 try w.writeByte(' ');
563 try w.writeAll(@tagName(req.version));577 try w.writeAll(@tagName(req.version));
564 try w.writeAll("\r\n");578 try w.writeAll("\r\n");
...@@ -757,7 +771,7 @@ pub const Request = struct {...@@ -757,7 +771,7 @@ pub const Request = struct {
757771
758 try req.redirect(resolved_url);772 try req.redirect(resolved_url);
759773
760 try req.start();774 try req.start(.{});
761 } else {775 } else {
762 req.response.skip = false;776 req.response.skip = false;
763 if (!req.response.parser.done) {777 if (!req.response.parser.done) {
...@@ -1141,6 +1155,7 @@ pub const FetchOptions = struct {...@@ -1141,6 +1155,7 @@ pub const FetchOptions = struct {
1141 method: http.Method = .GET,1155 method: http.Method = .GET,
1142 headers: http.Headers = http.Headers{ .allocator = std.heap.page_allocator, .owned = false },1156 headers: http.Headers = http.Headers{ .allocator = std.heap.page_allocator, .owned = false },
1143 payload: Payload = .none,1157 payload: Payload = .none,
1158 raw_uri: bool = false,
1144};1159};
11451160
1146pub const FetchResult = struct {1161pub const FetchResult = struct {
...@@ -1188,7 +1203,7 @@ pub fn fetch(client: *Client, allocator: Allocator, options: FetchOptions) !Fetc...@@ -1188,7 +1203,7 @@ pub fn fetch(client: *Client, allocator: Allocator, options: FetchOptions) !Fetc
1188 .none => {},1203 .none => {},
1189 }1204 }
11901205
1191 try req.start();1206 try req.start(.{ .raw_uri = options.raw_uri });
11921207
1193 switch (options.payload) {1208 switch (options.payload) {
1194 .string => |str| try req.writeAll(str),1209 .string => |str| try req.writeAll(str),
src/Package.zig+1-1
...@@ -653,7 +653,7 @@ fn fetchAndUnpack(...@@ -653,7 +653,7 @@ fn fetchAndUnpack(
653 var req = try http_client.request(.GET, uri, h, .{});653 var req = try http_client.request(.GET, uri, h, .{});
654 defer req.deinit();654 defer req.deinit();
655655
656 try req.start();656 try req.start(.{});
657 try req.wait();657 try req.wait();
658658
659 if (req.response.status != .ok) {659 if (req.response.status != .ok) {
test/standalone/http.zig+16-16
...@@ -240,7 +240,7 @@ pub fn main() !void {...@@ -240,7 +240,7 @@ pub fn main() !void {
240 var req = try client.request(.GET, uri, h, .{});240 var req = try client.request(.GET, uri, h, .{});
241 defer req.deinit();241 defer req.deinit();
242242
243 try req.start();243 try req.start(.{});
244 try req.wait();244 try req.wait();
245245
246 const body = try req.reader().readAllAlloc(calloc, 8192);246 const body = try req.reader().readAllAlloc(calloc, 8192);
...@@ -265,7 +265,7 @@ pub fn main() !void {...@@ -265,7 +265,7 @@ pub fn main() !void {
265 var req = try client.request(.GET, uri, h, .{});265 var req = try client.request(.GET, uri, h, .{});
266 defer req.deinit();266 defer req.deinit();
267267
268 try req.start();268 try req.start(.{});
269 try req.wait();269 try req.wait();
270270
271 const body = try req.reader().readAllAlloc(calloc, 8192 * 1024);271 const body = try req.reader().readAllAlloc(calloc, 8192 * 1024);
...@@ -289,7 +289,7 @@ pub fn main() !void {...@@ -289,7 +289,7 @@ pub fn main() !void {
289 var req = try client.request(.HEAD, uri, h, .{});289 var req = try client.request(.HEAD, uri, h, .{});
290 defer req.deinit();290 defer req.deinit();
291291
292 try req.start();292 try req.start(.{});
293 try req.wait();293 try req.wait();
294294
295 const body = try req.reader().readAllAlloc(calloc, 8192);295 const body = try req.reader().readAllAlloc(calloc, 8192);
...@@ -315,7 +315,7 @@ pub fn main() !void {...@@ -315,7 +315,7 @@ pub fn main() !void {
315 var req = try client.request(.GET, uri, h, .{});315 var req = try client.request(.GET, uri, h, .{});
316 defer req.deinit();316 defer req.deinit();
317317
318 try req.start();318 try req.start(.{});
319 try req.wait();319 try req.wait();
320320
321 const body = try req.reader().readAllAlloc(calloc, 8192);321 const body = try req.reader().readAllAlloc(calloc, 8192);
...@@ -340,7 +340,7 @@ pub fn main() !void {...@@ -340,7 +340,7 @@ pub fn main() !void {
340 var req = try client.request(.HEAD, uri, h, .{});340 var req = try client.request(.HEAD, uri, h, .{});
341 defer req.deinit();341 defer req.deinit();
342342
343 try req.start();343 try req.start(.{});
344 try req.wait();344 try req.wait();
345345
346 const body = try req.reader().readAllAlloc(calloc, 8192);346 const body = try req.reader().readAllAlloc(calloc, 8192);
...@@ -366,7 +366,7 @@ pub fn main() !void {...@@ -366,7 +366,7 @@ pub fn main() !void {
366 var req = try client.request(.GET, uri, h, .{});366 var req = try client.request(.GET, uri, h, .{});
367 defer req.deinit();367 defer req.deinit();
368368
369 try req.start();369 try req.start(.{});
370 try req.wait();370 try req.wait();
371371
372 const body = try req.reader().readAllAlloc(calloc, 8192);372 const body = try req.reader().readAllAlloc(calloc, 8192);
...@@ -395,7 +395,7 @@ pub fn main() !void {...@@ -395,7 +395,7 @@ pub fn main() !void {
395395
396 req.transfer_encoding = .{ .content_length = 14 };396 req.transfer_encoding = .{ .content_length = 14 };
397397
398 try req.start();398 try req.start(.{});
399 try req.writeAll("Hello, ");399 try req.writeAll("Hello, ");
400 try req.writeAll("World!\n");400 try req.writeAll("World!\n");
401 try req.finish();401 try req.finish();
...@@ -425,7 +425,7 @@ pub fn main() !void {...@@ -425,7 +425,7 @@ pub fn main() !void {
425 var req = try client.request(.GET, uri, h, .{});425 var req = try client.request(.GET, uri, h, .{});
426 defer req.deinit();426 defer req.deinit();
427427
428 try req.start();428 try req.start(.{});
429 try req.wait();429 try req.wait();
430430
431 const body = try req.reader().readAllAlloc(calloc, 8192);431 const body = try req.reader().readAllAlloc(calloc, 8192);
...@@ -454,7 +454,7 @@ pub fn main() !void {...@@ -454,7 +454,7 @@ pub fn main() !void {
454454
455 req.transfer_encoding = .chunked;455 req.transfer_encoding = .chunked;
456456
457 try req.start();457 try req.start(.{});
458 try req.writeAll("Hello, ");458 try req.writeAll("Hello, ");
459 try req.writeAll("World!\n");459 try req.writeAll("World!\n");
460 try req.finish();460 try req.finish();
...@@ -482,7 +482,7 @@ pub fn main() !void {...@@ -482,7 +482,7 @@ pub fn main() !void {
482 var req = try client.request(.GET, uri, h, .{});482 var req = try client.request(.GET, uri, h, .{});
483 defer req.deinit();483 defer req.deinit();
484484
485 try req.start();485 try req.start(.{});
486 try req.wait();486 try req.wait();
487487
488 const body = try req.reader().readAllAlloc(calloc, 8192);488 const body = try req.reader().readAllAlloc(calloc, 8192);
...@@ -506,7 +506,7 @@ pub fn main() !void {...@@ -506,7 +506,7 @@ pub fn main() !void {
506 var req = try client.request(.GET, uri, h, .{});506 var req = try client.request(.GET, uri, h, .{});
507 defer req.deinit();507 defer req.deinit();
508508
509 try req.start();509 try req.start(.{});
510 try req.wait();510 try req.wait();
511511
512 const body = try req.reader().readAllAlloc(calloc, 8192);512 const body = try req.reader().readAllAlloc(calloc, 8192);
...@@ -530,7 +530,7 @@ pub fn main() !void {...@@ -530,7 +530,7 @@ pub fn main() !void {
530 var req = try client.request(.GET, uri, h, .{});530 var req = try client.request(.GET, uri, h, .{});
531 defer req.deinit();531 defer req.deinit();
532532
533 try req.start();533 try req.start(.{});
534 try req.wait();534 try req.wait();
535535
536 const body = try req.reader().readAllAlloc(calloc, 8192);536 const body = try req.reader().readAllAlloc(calloc, 8192);
...@@ -554,7 +554,7 @@ pub fn main() !void {...@@ -554,7 +554,7 @@ pub fn main() !void {
554 var req = try client.request(.GET, uri, h, .{});554 var req = try client.request(.GET, uri, h, .{});
555 defer req.deinit();555 defer req.deinit();
556556
557 try req.start();557 try req.start(.{});
558 req.wait() catch |err| switch (err) {558 req.wait() catch |err| switch (err) {
559 error.TooManyHttpRedirects => {},559 error.TooManyHttpRedirects => {},
560 else => return err,560 else => return err,
...@@ -576,7 +576,7 @@ pub fn main() !void {...@@ -576,7 +576,7 @@ pub fn main() !void {
576 var req = try client.request(.GET, uri, h, .{});576 var req = try client.request(.GET, uri, h, .{});
577 defer req.deinit();577 defer req.deinit();
578578
579 try req.start();579 try req.start(.{});
580 const result = req.wait();580 const result = req.wait();
581581
582 try testing.expectError(error.ConnectionRefused, result); // expects not segfault but the regular error582 try testing.expectError(error.ConnectionRefused, result); // expects not segfault but the regular error
...@@ -623,7 +623,7 @@ pub fn main() !void {...@@ -623,7 +623,7 @@ pub fn main() !void {
623623
624 req.transfer_encoding = .chunked;624 req.transfer_encoding = .chunked;
625625
626 try req.start();626 try req.start(.{});
627 try req.wait();627 try req.wait();
628 try testing.expectEqual(http.Status.@"continue", req.response.status);628 try testing.expectEqual(http.Status.@"continue", req.response.status);
629629
...@@ -657,7 +657,7 @@ pub fn main() !void {...@@ -657,7 +657,7 @@ pub fn main() !void {
657657
658 req.transfer_encoding = .chunked;658 req.transfer_encoding = .chunked;
659659
660 try req.start();660 try req.start(.{});
661 try req.wait();661 try req.wait();
662 try testing.expectEqual(http.Status.expectation_failed, req.response.status);662 try testing.expectEqual(http.Status.expectation_failed, req.response.status);
663 }663 }