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(
216216
217217 const needs_absolute = comptime std.mem.indexOf(u8, fmt, "+") != null;
218218 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;
219220 const needs_fragment = comptime std.mem.indexOf(u8, fmt, "#") != null;
220221
221222 if (needs_absolute) {
......@@ -246,18 +247,30 @@ pub fn format(
246247 if (uri.path.len == 0) {
247248 try writer.writeAll("/");
248249 } 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 }
250255 }
251256
252257 if (uri.query) |q| {
253258 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 }
255264 }
256265
257266 if (needs_fragment) {
258267 if (uri.fragment) |f| {
259268 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 }
261274 }
262275 }
263276 }
lib/std/http/Client.zig+23-8
......@@ -538,8 +538,13 @@ pub const Request = struct {
538538
539539 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
541546 /// Send the request to the server.
542 pub fn start(req: *Request) StartError!void {
547 pub fn start(req: *Request, options: StartOptions) StartError!void {
543548 if (!req.method.requestHasBody() and req.transfer_encoding != .none) return error.UnsupportedTransferEncoding;
544549
545550 var buffered = std.io.bufferedWriter(req.connection.?.data.writer());
......@@ -552,13 +557,22 @@ pub const Request = struct {
552557 try w.writeAll(req.uri.host.?);
553558 try w.writeByte(':');
554559 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});
558560 } 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 }
560575 }
561
562576 try w.writeByte(' ');
563577 try w.writeAll(@tagName(req.version));
564578 try w.writeAll("\r\n");
......@@ -757,7 +771,7 @@ pub const Request = struct {
757771
758772 try req.redirect(resolved_url);
759773
760 try req.start();
774 try req.start(.{});
761775 } else {
762776 req.response.skip = false;
763777 if (!req.response.parser.done) {
......@@ -1141,6 +1155,7 @@ pub const FetchOptions = struct {
11411155 method: http.Method = .GET,
11421156 headers: http.Headers = http.Headers{ .allocator = std.heap.page_allocator, .owned = false },
11431157 payload: Payload = .none,
1158 raw_uri: bool = false,
11441159};
11451160
11461161pub const FetchResult = struct {
......@@ -1188,7 +1203,7 @@ pub fn fetch(client: *Client, allocator: Allocator, options: FetchOptions) !Fetc
11881203 .none => {},
11891204 }
11901205
1191 try req.start();
1206 try req.start(.{ .raw_uri = options.raw_uri });
11921207
11931208 switch (options.payload) {
11941209 .string => |str| try req.writeAll(str),
src/Package.zig+1-1
......@@ -653,7 +653,7 @@ fn fetchAndUnpack(
653653 var req = try http_client.request(.GET, uri, h, .{});
654654 defer req.deinit();
655655
656 try req.start();
656 try req.start(.{});
657657 try req.wait();
658658
659659 if (req.response.status != .ok) {
test/standalone/http.zig+16-16
......@@ -240,7 +240,7 @@ pub fn main() !void {
240240 var req = try client.request(.GET, uri, h, .{});
241241 defer req.deinit();
242242
243 try req.start();
243 try req.start(.{});
244244 try req.wait();
245245
246246 const body = try req.reader().readAllAlloc(calloc, 8192);
......@@ -265,7 +265,7 @@ pub fn main() !void {
265265 var req = try client.request(.GET, uri, h, .{});
266266 defer req.deinit();
267267
268 try req.start();
268 try req.start(.{});
269269 try req.wait();
270270
271271 const body = try req.reader().readAllAlloc(calloc, 8192 * 1024);
......@@ -289,7 +289,7 @@ pub fn main() !void {
289289 var req = try client.request(.HEAD, uri, h, .{});
290290 defer req.deinit();
291291
292 try req.start();
292 try req.start(.{});
293293 try req.wait();
294294
295295 const body = try req.reader().readAllAlloc(calloc, 8192);
......@@ -315,7 +315,7 @@ pub fn main() !void {
315315 var req = try client.request(.GET, uri, h, .{});
316316 defer req.deinit();
317317
318 try req.start();
318 try req.start(.{});
319319 try req.wait();
320320
321321 const body = try req.reader().readAllAlloc(calloc, 8192);
......@@ -340,7 +340,7 @@ pub fn main() !void {
340340 var req = try client.request(.HEAD, uri, h, .{});
341341 defer req.deinit();
342342
343 try req.start();
343 try req.start(.{});
344344 try req.wait();
345345
346346 const body = try req.reader().readAllAlloc(calloc, 8192);
......@@ -366,7 +366,7 @@ pub fn main() !void {
366366 var req = try client.request(.GET, uri, h, .{});
367367 defer req.deinit();
368368
369 try req.start();
369 try req.start(.{});
370370 try req.wait();
371371
372372 const body = try req.reader().readAllAlloc(calloc, 8192);
......@@ -395,7 +395,7 @@ pub fn main() !void {
395395
396396 req.transfer_encoding = .{ .content_length = 14 };
397397
398 try req.start();
398 try req.start(.{});
399399 try req.writeAll("Hello, ");
400400 try req.writeAll("World!\n");
401401 try req.finish();
......@@ -425,7 +425,7 @@ pub fn main() !void {
425425 var req = try client.request(.GET, uri, h, .{});
426426 defer req.deinit();
427427
428 try req.start();
428 try req.start(.{});
429429 try req.wait();
430430
431431 const body = try req.reader().readAllAlloc(calloc, 8192);
......@@ -454,7 +454,7 @@ pub fn main() !void {
454454
455455 req.transfer_encoding = .chunked;
456456
457 try req.start();
457 try req.start(.{});
458458 try req.writeAll("Hello, ");
459459 try req.writeAll("World!\n");
460460 try req.finish();
......@@ -482,7 +482,7 @@ pub fn main() !void {
482482 var req = try client.request(.GET, uri, h, .{});
483483 defer req.deinit();
484484
485 try req.start();
485 try req.start(.{});
486486 try req.wait();
487487
488488 const body = try req.reader().readAllAlloc(calloc, 8192);
......@@ -506,7 +506,7 @@ pub fn main() !void {
506506 var req = try client.request(.GET, uri, h, .{});
507507 defer req.deinit();
508508
509 try req.start();
509 try req.start(.{});
510510 try req.wait();
511511
512512 const body = try req.reader().readAllAlloc(calloc, 8192);
......@@ -530,7 +530,7 @@ pub fn main() !void {
530530 var req = try client.request(.GET, uri, h, .{});
531531 defer req.deinit();
532532
533 try req.start();
533 try req.start(.{});
534534 try req.wait();
535535
536536 const body = try req.reader().readAllAlloc(calloc, 8192);
......@@ -554,7 +554,7 @@ pub fn main() !void {
554554 var req = try client.request(.GET, uri, h, .{});
555555 defer req.deinit();
556556
557 try req.start();
557 try req.start(.{});
558558 req.wait() catch |err| switch (err) {
559559 error.TooManyHttpRedirects => {},
560560 else => return err,
......@@ -576,7 +576,7 @@ pub fn main() !void {
576576 var req = try client.request(.GET, uri, h, .{});
577577 defer req.deinit();
578578
579 try req.start();
579 try req.start(.{});
580580 const result = req.wait();
581581
582582 try testing.expectError(error.ConnectionRefused, result); // expects not segfault but the regular error
......@@ -623,7 +623,7 @@ pub fn main() !void {
623623
624624 req.transfer_encoding = .chunked;
625625
626 try req.start();
626 try req.start(.{});
627627 try req.wait();
628628 try testing.expectEqual(http.Status.@"continue", req.response.status);
629629
......@@ -657,7 +657,7 @@ pub fn main() !void {
657657
658658 req.transfer_encoding = .chunked;
659659
660 try req.start();
660 try req.start(.{});
661661 try req.wait();
662662 try testing.expectEqual(http.Status.expectation_failed, req.response.status);
663663 }