@@ -1133,7 +1133,8 @@ fn initHttpResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer:
11331133 const status = response .head .status ;
11341134
11351135 if (@intFromEnum (status ) >= 500 or status == .too_many_requests or status == .not_found ) {
1136- if (parseRetryAfter (response ) catch null ) | delay_sec | {
1136+ var iter = response .head .iterateHeaders ();
1137+ if (parseRetryAfter (& iter ) catch null ) | delay_sec | {
11371138 // Set max by dividing and multiplying again, because Retry-After
11381139 // header value needs to be u32, and could be obsurdly large, and
11391140 // we do not want to multiply that large number by 1000 in case of
@@ -1162,9 +1163,8 @@ fn initHttpResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer:
11621163///
11631164/// For more information, see the MDN documentation:
11641165/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Retry-After
1165- fn parseRetryAfter (response : * const std.http.Client.Response ) ! ? u32 {
1166- var iter = response .head .iterateHeaders ();
1167- const retry_after : []const u8 = while (iter .next ()) | header | {
1166+ fn parseRetryAfter (header_iter : * std.http.HeaderIterator ) ! ? u32 {
1167+ const retry_after : []const u8 = while (header_iter .next ()) | header | {
11681168 if (ascii .eqlIgnoreCase (header .name , "retry-after" )) {
11691169 break header .value ;
11701170 }
@@ -2428,12 +2428,60 @@ test "retries the correct number of times" {
24282428 try std .testing .expectEqual (retry .max_retries , retry .cur_retries );
24292429}
24302430
2431+ test "parse Retry-After header" {
2432+ var arena = std .heap .ArenaAllocator .init (std .testing .allocator );
2433+ const alloc = arena .allocator ();
2434+ defer arena .deinit ();
2435+ {
2436+ var iter = try mockRetryAfterHeaderFactory (alloc , "6" );
2437+ const result = try parseRetryAfter (& iter );
2438+ try std .testing .expectEqual (6 , result .? );
2439+ }
2440+ {
2441+ var iter = try mockRetryAfterHeaderFactory (alloc , "12345" );
2442+ const result = try parseRetryAfter (& iter );
2443+ try std .testing .expectEqual (12345 , result .? );
2444+ }
2445+ {
2446+ // anything in the past should return `null`
2447+ var iter = try mockRetryAfterHeaderFactory (alloc , "Wed, 21 Oct 1970 07:28:00 GMT" );
2448+ const result = try parseRetryAfter (& iter );
2449+ try std .testing .expectEqual (null , result );
2450+ }
2451+ {
2452+ // anything in the future should return `null` (this will fail in 100 years)
2453+ const future_time = "Wed, 21 Oct 2125 16:19:10 GMT" ;
2454+ const future_epoc_seconds = 4916737150 ;
2455+ const seconds_from_now = future_epoc_seconds - std .time .timestamp ();
2456+ var iter = try mockRetryAfterHeaderFactory (alloc , future_time );
2457+ const result = try parseRetryAfter (& iter );
2458+ // The time between us calling `std.time.timestamp()` here in this test and when it's called in the
2459+ // `parseRetryAfter` function could be significant enough that they don't match. So we want approx values here.
2460+ try std .testing .expect (result .? + 5 > seconds_from_now );
2461+ try std .testing .expect (result .? - 5 < seconds_from_now );
2462+ }
2463+ {
2464+ // returns error on invalid header
2465+ var iter = try mockRetryAfterHeaderFactory (alloc , "Not a timestamp" );
2466+ try std .testing .expectError (error .InvalidHeaderValueLength , parseRetryAfter (& iter ));
2467+ }
2468+ {
2469+ // returns null on missing header
2470+ var iter = std .http .HeaderIterator .init ("HTTP/1.1 599 NOT-OK\r \n \r \n " );
2471+ try std .testing .expectEqual (null , try parseRetryAfter (& iter ));
2472+ }
2473+ }
2474+
24312475fn testFnToCallWithRetries (r : * Retry , is_spurious_error : bool ) ! void {
24322476 // set to 1 ms so the unit test doesn't take forever
24332477 r .retry_delay_override_ms = 1 ;
24342478 return if (is_spurious_error ) error .MaybeSpurious else error .NonSpurious ;
24352479}
24362480
2481+ fn mockRetryAfterHeaderFactory (alloc : std.mem.Allocator , time : []const u8 ) ! std.http.HeaderIterator {
2482+ return std .http .HeaderIterator .init (try std .fmt .allocPrint (alloc , "HTTP/1.1 599 NOT-OK\r \n Retry-After:{s}\r \n " , .{time }));
2483+ }
2484+
24372485fn saveEmbedFile (comptime tarball_name : []const u8 , dir : fs.Dir ) ! void {
24382486 //const tarball_name = "duplicate_paths_excluded.tar.gz";
24392487 const tarball_content = @embedFile ("Fetch/testdata/" ++ tarball_name );
0 commit comments