aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/http_client_async.zig24
-rw-r--r--src/url.zig36
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 => {},
}