diff options
-rw-r--r-- | src/http_client_async.zig | 24 | ||||
-rw-r--r-- | src/url.zig | 36 |
2 files changed, 46 insertions, 14 deletions
diff --git a/src/http_client_async.zig b/src/http_client_async.zig index 743be4558..3c18b9552 100644 --- a/src/http_client_async.zig +++ b/src/http_client_async.zig @@ -1998,6 +1998,30 @@ pub fn handleResponseMetadata( this.url = URL.parse(url_buf.data[0..url_buf_len]); this.redirect = url_buf; + } else if (strings.hasPrefixComptime(location, "//")) { + var url_buf = URLBufferPool.get(default_allocator); + + const protocol_name = this.url.displayProtocol(); + + if (protocol_name.len + 1 + location.len > url_buf.data.len) { + return error.RedirectURLTooLong; + } + + deferred_redirect.* = this.redirect; + var url_buf_len = location.len; + + if (strings.eqlComptime(protocol_name, "http")) { + url_buf.data[0.."http:".len].* = "http:".*; + std.mem.copy(u8, url_buf.data["http:".len..], location); + url_buf_len += "http:".len; + } else { + url_buf.data[0.."https:".len].* = "https:".*; + std.mem.copy(u8, url_buf.data["https:".len..], location); + url_buf_len += "https:".len; + } + + this.url = URL.parse(url_buf.data[0..url_buf_len]); + this.redirect = url_buf; } else { var url_buf = URLBufferPool.get(default_allocator); const original_url = this.url; diff --git a/src/url.zig b/src/url.zig index a4e0946fb..f51c8cc55 100644 --- a/src/url.zig +++ b/src/url.zig @@ -220,22 +220,30 @@ pub const URL = struct { offset += url.parsePassword(base[offset..]) orelse 0; offset += url.parseHost(base[offset..]) orelse 0; }, - 'a'...'z', 'A'...'Z', '0'...'9', '-', '_', ':' => { - offset += url.parseProtocol(base[offset..]) orelse 0; - - // if there's no protocol or @, it's ambiguous whether the colon is a port or a username. - if (offset > 0) { - // see https://github.com/oven-sh/bun/issues/1390 - const first_at = strings.indexOfChar(base[offset..], '@') orelse 0; - const first_colon = strings.indexOfChar(base[offset..], ':') orelse 0; - - if (first_at > first_colon and first_at < (strings.indexOfChar(base[offset..], '/') orelse std.math.maxInt(u32))) { - offset += url.parseUsername(base[offset..]) orelse 0; - offset += url.parsePassword(base[offset..]) orelse 0; - } + '/', 'a'...'z', 'A'...'Z', '0'...'9', '-', '_', ':' => { + const is_protocol_relative = base.len > 1 and base[1] == '/'; + if (is_protocol_relative) { + offset += 1; + } else { + offset += url.parseProtocol(base[offset..]) orelse 0; } - offset += url.parseHost(base[offset..]) orelse 0; + if (!(!is_protocol_relative and base[0] == '/')) { + + // if there's no protocol or @, it's ambiguous whether the colon is a port or a username. + if (offset > 0) { + // see https://github.com/oven-sh/bun/issues/1390 + const first_at = strings.indexOfChar(base[offset..], '@') orelse 0; + const first_colon = strings.indexOfChar(base[offset..], ':') orelse 0; + + if (first_at > first_colon and first_at < (strings.indexOfChar(base[offset..], '/') orelse std.math.maxInt(u32))) { + offset += url.parseUsername(base[offset..]) orelse 0; + offset += url.parsePassword(base[offset..]) orelse 0; + } + } + + offset += url.parseHost(base[offset..]) orelse 0; + } }, else => {}, } |