aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bundler.zig15
-rw-r--r--src/css_scanner.zig10
-rw-r--r--src/http.zig151
-rw-r--r--src/javascript/jsc/api/router.zig3
-rw-r--r--src/javascript/jsc/javascript.zig48
-rw-r--r--src/javascript/jsc/webcore/response.zig2
-rw-r--r--src/linker.zig46
-rw-r--r--src/query_string_map.zig34
8 files changed, 246 insertions, 63 deletions
diff --git a/src/bundler.zig b/src/bundler.zig
index b726996a1..068d8cd2b 100644
--- a/src/bundler.zig
+++ b/src/bundler.zig
@@ -10,7 +10,6 @@ const default_allocator = _global.default_allocator;
const StoredFileDescriptorType = _global.StoredFileDescriptorType;
const FeatureFlags = _global.FeatureFlags;
const C = _global.C;
-
const std = @import("std");
const lex = @import("js_lexer.zig");
const logger = @import("logger.zig");
@@ -49,6 +48,7 @@ const NewBunQueue = @import("./bun_queue.zig").NewBunQueue;
const NodeFallbackModules = @import("./node_fallbacks.zig");
const CacheEntry = @import("./cache.zig").FsCacheEntry;
const Analytics = @import("./analytics/analytics_thread.zig");
+const URL = @import("./query_string_map.zig").URL;
const Linker = linker.Linker;
const Resolver = _resolver.Resolver;
@@ -2166,6 +2166,7 @@ pub const Bundler = struct {
comptime WatcherType: type,
watcher: *WatcherType,
client_entry_point: ?*ClientEntryPoint,
+ origin: URL,
) !BuildResolveResultPair {
if (resolve_result.is_external) {
return BuildResolveResultPair{
@@ -2230,6 +2231,7 @@ pub const Bundler = struct {
allocator,
bundler.log,
&bundler.linker,
+ origin,
)).written;
} else {
break :brk (try CSSBundler.bundle(
@@ -2243,6 +2245,7 @@ pub const Bundler = struct {
allocator,
bundler.log,
&bundler.linker,
+ origin,
)).written;
}
},
@@ -2274,7 +2277,7 @@ pub const Bundler = struct {
return BuildResolveResultPair{ .written = 0, .input_fd = result.input_fd, .empty = true };
}
- try bundler.linker.link(file_path, &result, import_path_format, false);
+ try bundler.linker.link(file_path, &result, origin, import_path_format, false);
if (bundler.options.platform.isBun()) {
return BuildResolveResultPair{
@@ -2382,6 +2385,7 @@ pub const Bundler = struct {
try bundler.linker.link(
file_path,
&result,
+ bundler.options.origin,
import_path_format,
false,
);
@@ -2411,11 +2415,15 @@ pub const Bundler = struct {
output_file.value = .{ .move = file_op };
},
.css => {
+ const CSSBuildContext = struct {
+ origin: URL,
+ };
+ var build_ctx = CSSBuildContext{ .origin = bundler.options.origin };
const CSSWriter = Css.NewWriter(
std.fs.File,
@TypeOf(&bundler.linker),
import_path_format,
- void,
+ CSSBuildContext,
);
const entry = bundler.resolver.caches.fs.readFile(
bundler.fs,
@@ -2434,6 +2442,7 @@ pub const Bundler = struct {
&bundler.linker,
bundler.log,
);
+ css_writer.buildCtx = build_ctx;
var did_warn = false;
try css_writer.run(bundler.log, bundler.allocator, &did_warn);
output_file.size = css_writer.written;
diff --git a/src/css_scanner.zig b/src/css_scanner.zig
index 10f065058..55b3298d2 100644
--- a/src/css_scanner.zig
+++ b/src/css_scanner.zig
@@ -19,7 +19,7 @@ const logger = @import("./logger.zig");
const Options = options;
const resolver = @import("./resolver/resolver.zig");
const _linker = @import("./linker.zig");
-
+const URL = @import("./query_string_map.zig").URL;
const replacementCharacter: CodePoint = 0xFFFD;
pub const Chunk = struct {
@@ -1020,6 +1020,7 @@ pub fn NewWriter(
import.text.utf8,
chunk.range,
import_record.ImportKind.at,
+ writer.buildCtx.origin,
Options.BundleOptions.ImportPathFormat.absolute_path,
true,
) catch |err| {
@@ -1066,6 +1067,7 @@ pub fn NewWriter(
url.utf8,
chunk.range,
import_record.ImportKind.url,
+ writer.buildCtx.origin,
import_path_format,
true,
);
@@ -1078,6 +1080,7 @@ pub fn NewWriter(
import.text.utf8,
chunk.range,
import_record.ImportKind.at,
+ writer.buildCtx.origin,
import_path_format,
false,
);
@@ -1155,6 +1158,8 @@ pub fn NewBundler(
fs_reader: FileReader,
fs: FSType,
allocator: std.mem.Allocator,
+ origin: URL = URL{},
+
pub fn bundle(
absolute_path: string,
fs: FSType,
@@ -1166,6 +1171,7 @@ pub fn NewBundler(
allocator: std.mem.Allocator,
log: *logger.Log,
linker: Linker,
+ origin: URL,
) !CodeCount {
if (!has_set_global_queue) {
global_queued = QueuedList.init(default_allocator);
@@ -1186,7 +1192,7 @@ pub fn NewBundler(
.writer = writer,
.fs_reader = fs_reader,
.fs = fs,
-
+ .origin = origin,
.allocator = allocator,
.watcher = watcher,
};
diff --git a/src/http.zig b/src/http.zig
index 0e0db02f3..bccfcce3e 100644
--- a/src/http.zig
+++ b/src/http.zig
@@ -94,6 +94,7 @@ pub const RequestContext = struct {
watcher: *Watcher,
timer: std.time.Timer,
matched_route: ?Router.Match = null,
+ origin: ZigURL,
full_url: [:0]const u8 = "",
res_headers_count: usize = 0,
@@ -101,10 +102,92 @@ pub const RequestContext = struct {
/// --disable-bun.js propagates here
pub var fallback_only = false;
+ fn parseOrigin(this: *RequestContext) void {
+ var protocol: ?string = null;
+ var host: ?string = null;
+
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
+ if (this.header("Forwarded")) |forwarded| {
+ if (strings.indexOf(forwarded, "host=")) |host_start| {
+ const host_i = host_start + "host=".len;
+ const host_ = forwarded[host_i..][0 .. strings.indexOfChar(forwarded[host_i..], ';') orelse forwarded[host_i..].len];
+ if (host_.len > 0) {
+ host = host_;
+ }
+ }
+
+ if (strings.indexOf(forwarded, "proto=")) |protocol_start| {
+ const protocol_i = protocol_start + "proto=".len;
+ if (strings.eqlComptime(forwarded[protocol_i..][0 .. strings.indexOfChar(forwarded[protocol_i..], ';') orelse forwarded[protocol_i..].len], "https")) {
+ protocol = "https";
+ } else {
+ protocol = "http";
+ }
+ }
+ }
+
+ if (protocol == null) {
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto
+ determine_protocol: {
+ if (this.header("X-Forwarded-Proto")) |proto| {
+ if (strings.eqlComptime(proto, "https")) {
+ protocol = "https";
+ break :determine_protocol;
+ }
+ }
+
+ // Microsoft IIS
+ if (this.header("Front-End-Https")) |proto| {
+ if (strings.eqlComptime(proto, "on")) {
+ protocol = "https";
+ break :determine_protocol;
+ }
+ }
+ }
+ }
+
+ if (host == null) {
+ determine_host: {
+ if (this.header("X-Forwarded-Host")) |_host| {
+ host = _host;
+ break :determine_host;
+ }
+ }
+
+ if (this.header("Origin")) |origin| {
+ this.origin = ZigURL.parse(origin);
+ return;
+ }
+ }
+
+ if (host != null or protocol != null) {
+ // Proxies like Caddy might only send X-Forwarded-Proto if the host matches
+ // In that case,
+ const display_protocol = protocol orelse @as(string, "http");
+ var display_host = host orelse
+ (if (protocol != null) this.header("Host") else null) orelse
+ @as(string, this.origin.host);
+
+ var display_port = if (this.origin.port.len > 0) this.origin.port else @as(string, "3000");
+
+ if (strings.indexOfChar(display_host, ':')) |colon| {
+ display_port = display_host[colon + 1 .. display_host.len];
+ display_host = display_host[0..colon];
+ } else if (this.bundler.options.origin.port_was_automatically_set and protocol != null) {
+ if (strings.eqlComptime(display_protocol, "https")) {
+ display_port = "443";
+ } else {
+ display_port = "80";
+ }
+ }
+ this.origin = ZigURL.parse(std.fmt.allocPrint(this.allocator, "{s}://{s}:{s}/", .{ display_protocol, display_host, display_port }) catch unreachable);
+ }
+ }
+
pub fn getFullURL(this: *RequestContext) [:0]const u8 {
if (this.full_url.len == 0) {
- if (this.bundler.options.origin.isAbsolute()) {
- this.full_url = std.fmt.allocPrintZ(this.allocator, "{s}{s}", .{ this.bundler.options.origin.origin, this.request.path }) catch unreachable;
+ if (this.origin.isAbsolute()) {
+ this.full_url = std.fmt.allocPrintZ(this.allocator, "{s}{s}", .{ this.origin.origin, this.request.path }) catch unreachable;
} else {
this.full_url = this.allocator.dupeZ(u8, this.request.path) catch unreachable;
}
@@ -120,7 +203,11 @@ pub const RequestContext = struct {
try this.flushHeaders();
}
- pub fn header(ctx: *RequestContext, comptime name: anytype) ?Header {
+ pub fn header(ctx: *RequestContext, comptime name: anytype) ?[]const u8 {
+ return (ctx.headerEntry(name) orelse return null).value;
+ }
+
+ pub fn headerEntry(ctx: *RequestContext, comptime name: anytype) ?Header {
for (ctx.request.headers) |head| {
if (strings.eqlCaseInsensitiveASCII(head.name, name, true)) {
return head;
@@ -130,6 +217,18 @@ pub const RequestContext = struct {
return null;
}
+ pub fn headerEntryFirst(ctx: *RequestContext, comptime name: []const string) ?Header {
+ for (ctx.request.headers) |head| {
+ inline for (name) |match| {
+ if (strings.eqlCaseInsensitiveASCII(head.name, match, true)) {
+ return head;
+ }
+ }
+ }
+
+ return null;
+ }
+
pub fn renderFallback(
this: *RequestContext,
allocator: std.mem.Allocator,
@@ -175,7 +274,7 @@ pub const RequestContext = struct {
bundler_parse_options,
@as(?*bundler.FallbackEntryPoint, &fallback_entry_point),
)) |*result| {
- try bundler_.linker.link(fallback_entry_point.source.path, result, .absolute_url, false);
+ try bundler_.linker.link(fallback_entry_point.source.path, result, this.origin, .absolute_url, false);
var buffer_writer = try js_printer.BufferWriter.init(default_allocator);
var writer = js_printer.BufferPrinter.init(buffer_writer);
_ = try bundler_.print(
@@ -535,6 +634,7 @@ pub const RequestContext = struct {
.method = Method.which(req.method) orelse return error.InvalidMethod,
.watcher = watcher_,
.timer = timer,
+ .origin = bundler_.options.origin,
};
return ctx;
@@ -542,7 +642,7 @@ pub const RequestContext = struct {
pub inline fn isBrowserNavigation(req: *RequestContext) bool {
if (req.header("Sec-Fetch-Mode")) |mode| {
- return strings.eqlComptime(mode.value, "navigate");
+ return strings.eqlComptime(mode, "navigate");
}
return false;
@@ -621,7 +721,7 @@ pub const RequestContext = struct {
ctx.appendHeader("Cache-Control", "immutable, max-age=99999");
if (ctx.header("If-None-Match")) |etag_header| {
- if (std.mem.eql(u8, node_modules_bundle.bundle.etag, etag_header.value)) {
+ if (strings.eqlLong(node_modules_bundle.bundle.etag, etag_header, true)) {
try ctx.sendNotModified();
return;
}
@@ -680,6 +780,7 @@ pub const RequestContext = struct {
printer: js_printer.BufferPrinter,
timer: std.time.Timer,
count: usize = 0,
+ origin: ZigURL,
pub const WatchBuildResult = struct {
value: Value,
id: u32,
@@ -779,6 +880,7 @@ pub const RequestContext = struct {
this.bundler.linker.link(
Fs.Path.init(file_path_str),
&parse_result,
+ this.origin,
.absolute_url,
false,
) catch return WatchBuildResult{
@@ -865,6 +967,7 @@ pub const RequestContext = struct {
this.allocator,
&log,
&this.bundler.linker,
+ this.origin,
);
} else {
break :brk CSSBundler.bundle(
@@ -878,6 +981,7 @@ pub const RequestContext = struct {
this.allocator,
&log,
&this.bundler.linker,
+ this.origin,
);
}
} catch {
@@ -1275,9 +1379,10 @@ pub const RequestContext = struct {
var handler: *JavaScriptHandler = try channel.readItem();
JavaScript.VirtualMachine.vm.tick();
-
JavaScript.VirtualMachine.vm.preflush();
-
+ const original_origin = vm.origin;
+ vm.origin = handler.ctx.origin;
+ defer vm.origin = original_origin;
JavaScript.EventListenerMixin.emitFetchEvent(
vm,
&handler.ctx,
@@ -1388,6 +1493,7 @@ pub const RequestContext = struct {
clone.message_buffer = try MutableString.init(server.allocator, 0);
clone.ctx.conn = &clone.conn;
clone.ctx.log = logger.Log.init(server.allocator);
+ clone.ctx.origin = ZigURL.parse(server.allocator.dupe(u8, ctx.origin.href) catch unreachable);
var printer_writer = try js_printer.BufferWriter.init(server.allocator);
clone.builder = WatchBuilder{
@@ -1396,6 +1502,7 @@ pub const RequestContext = struct {
.printer = js_printer.BufferPrinter.init(printer_writer),
.timer = ctx.timer,
.watcher = ctx.watcher,
+ .origin = clone.ctx.origin,
};
clone.websocket = Websocket.Websocket.create(&clone.conn, SOCKET_FLAGS);
@@ -1763,14 +1870,14 @@ pub const RequestContext = struct {
var request: *RequestContext = &self.ctx;
const upgrade_header = request.header("Upgrade") orelse return error.BadRequest;
- if (!strings.eqlComptime(upgrade_header.value, "websocket")) {
+ if (!strings.eqlComptime(upgrade_header, "websocket")) {
return error.BadRequest; // Can only upgrade to websocket
}
// Some proxies/load balancers will mess with the connection header
// and browsers also send multiple values here
const connection_header = request.header("Connection") orelse return error.BadRequest;
- var it = std.mem.split(u8, connection_header.value, ",");
+ var it = std.mem.split(u8, connection_header, ",");
while (it.next()) |part| {
const conn = std.mem.trim(u8, part, " ");
if (strings.eqlCaseInsensitiveASCII(conn, "upgrade", true)) {
@@ -1788,8 +1895,8 @@ pub const RequestContext = struct {
Output.prettyErrorln("HMR WebSocket error: missing Sec-WebSocket-Version header", .{});
return error.BadRequest;
};
- return std.fmt.parseInt(u8, v.value, 10) catch {
- Output.prettyErrorln("HMR WebSocket error: Sec-WebSocket-Version is invalid {s}", .{v.value});
+ return std.fmt.parseInt(u8, v, 10) catch {
+ Output.prettyErrorln("HMR WebSocket error: Sec-WebSocket-Version is invalid {s}", .{v});
return error.BadRequest;
};
}
@@ -1798,7 +1905,7 @@ pub const RequestContext = struct {
self: *WebsocketHandler,
) ![]const u8 {
var request: *RequestContext = &self.ctx;
- const key = (request.header("Sec-WebSocket-Key") orelse return error.BadRequest).value;
+ const key = (request.header("Sec-WebSocket-Key") orelse return error.BadRequest);
if (key.len < 8) {
Output.prettyErrorln("HMR WebSocket error: Sec-WebSocket-Key is less than 8 characters long: {s}", .{key});
return error.BadRequest;
@@ -1822,7 +1929,7 @@ pub const RequestContext = struct {
this.appendHeader("ETag", etag_content_slice);
if (this.header("If-None-Match")) |etag_header| {
- if (std.mem.eql(u8, etag_content_slice, etag_header.value)) {
+ if (strings.eqlLong(etag_content_slice, etag_header, true)) {
try this.sendNotModified();
return true;
}
@@ -1951,7 +2058,7 @@ pub const RequestContext = struct {
chunky.rctx.appendHeader("ETag", etag_content_slice);
if (chunky.rctx.header("If-None-Match")) |etag_header| {
- if (std.mem.eql(u8, etag_content_slice, etag_header.value)) {
+ if (std.mem.eql(u8, etag_content_slice, etag_header)) {
try chunky.rctx.sendNotModified();
return;
}
@@ -2012,6 +2119,7 @@ pub const RequestContext = struct {
Watcher,
ctx.watcher,
client_entry_point_,
+ ctx.origin,
) catch |err| {
ctx.sendInternalError(err) catch {};
return;
@@ -2052,7 +2160,7 @@ pub const RequestContext = struct {
ctx.appendHeader("ETag", etag_content_slice);
if (ctx.header("If-None-Match")) |etag_header| {
- if (std.mem.eql(u8, etag_content_slice, etag_header.value)) {
+ if (std.mem.eql(u8, etag_content_slice, etag_header)) {
try ctx.sendNotModified();
return;
}
@@ -2120,7 +2228,7 @@ pub const RequestContext = struct {
ctx.appendHeader("ETag", complete_weak_etag);
if (ctx.header("If-None-Match")) |etag_header| {
- if (strings.eql(complete_weak_etag, etag_header.value)) {
+ if (strings.eql(complete_weak_etag, etag_header)) {
try ctx.sendNotModified();
return;
}
@@ -2227,7 +2335,7 @@ pub const RequestContext = struct {
if (strings.eqlComptime(path, "_api.hmr")) {
if (ctx.header("Upgrade")) |upgrade| {
- if (strings.eqlCaseInsensitiveASCII(upgrade.value, "websocket", true)) {
+ if (strings.eqlCaseInsensitiveASCII(upgrade, "websocket", true)) {
try ctx.handleWebsocket(server);
return;
}
@@ -2296,8 +2404,8 @@ pub const RequestContext = struct {
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Dest
pub fn isScriptOrStyleRequest(ctx: *RequestContext) bool {
const header_ = ctx.header("Sec-Fetch-Dest") orelse return false;
- return strings.eqlComptime(header_.value, "script") or
- strings.eqlComptime(header_.value, "style");
+ return strings.eqlComptime(header_, "script") or
+ strings.eqlComptime(header_, "style");
}
fn handleSrcURL(ctx: *RequestContext, _: *Server) !void {
@@ -2944,6 +3052,7 @@ pub const Server = struct {
const is_navigation_request = req_ctx_.isBrowserNavigation();
defer if (is_navigation_request) Analytics.enqueue(Analytics.EventName.http_build);
+ req_ctx.parseOrigin();
if (req_ctx.url.needs_redirect) {
req_ctx.handleRedirect(req_ctx.url.path) catch |err| {
@@ -3028,7 +3137,7 @@ pub const Server = struct {
if (comptime FeatureFlags.keep_alive) {
if (req_ctx.header("Connection")) |connection| {
- req_ctx.keep_alive = strings.eqlInsensitive(connection.value, "keep-alive");
+ req_ctx.keep_alive = strings.eqlInsensitive(connection, "keep-alive");
}
conn.client.setKeepAlive(req_ctx.keep_alive) catch {};
diff --git a/src/javascript/jsc/api/router.zig b/src/javascript/jsc/api/router.zig
index 66aca8349..2e2588e5a 100644
--- a/src/javascript/jsc/api/router.zig
+++ b/src/javascript/jsc/api/router.zig
@@ -390,11 +390,12 @@ pub fn getScriptSrcString(
&entry_point_tempbuf,
Fs.PathName.init(file_path),
),
+ VirtualMachine.vm.origin,
ScriptSrcStream.Writer,
writer,
);
} else {
- JavaScript.Bun.getPublicPath(file_path, ScriptSrcStream.Writer, writer);
+ JavaScript.Bun.getPublicPath(file_path, VirtualMachine.vm.origin, ScriptSrcStream.Writer, writer);
}
}
diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig
index 844ffea92..043bc7aea 100644
--- a/src/javascript/jsc/javascript.zig
+++ b/src/javascript/jsc/javascript.zig
@@ -75,7 +75,7 @@ const ErrorableZigString = @import("javascript_core").ErrorableZigString;
const ZigGlobalObject = @import("javascript_core").ZigGlobalObject;
const VM = @import("javascript_core").VM;
const Config = @import("./config.zig");
-
+const URL = @import("../../query_string_map.zig").URL;
pub const GlobalClasses = [_]type{
Request.Class,
Response.Class,
@@ -105,7 +105,7 @@ pub const Bun = struct {
pub fn onImportCSS(
resolve_result: *const Resolver.Result,
import_record: *ImportRecord,
- _: string,
+ origin: URL,
) void {
if (!css_imports_buf_loaded) {
css_imports_buf = std.ArrayList(u8).initCapacity(
@@ -121,7 +121,7 @@ pub const Bun = struct {
.offset = @truncate(u32, offset),
.length = 0,
};
- getPublicPath(resolve_result.path_pair.primary.text, @TypeOf(writer), writer);
+ getPublicPath(resolve_result.path_pair.primary.text, origin, @TypeOf(writer), writer);
const length = css_imports_buf.items.len - offset;
css_imports_list[css_imports_list_tail].length = @truncate(u32, length);
css_imports_list_tail += 1;
@@ -198,7 +198,7 @@ pub const Bun = struct {
_: js.JSStringRef,
_: js.ExceptionRef,
) js.JSValueRef {
- return ZigString.init(VirtualMachine.vm.bundler.options.origin.origin).toValue(VirtualMachine.vm.global).asRef();
+ return ZigString.init(VirtualMachine.vm.origin.origin).toValue(VirtualMachine.vm.global).asRef();
}
pub fn enableANSIColors(
@@ -509,10 +509,10 @@ pub const Bun = struct {
return result;
}
- pub fn getPublicPath(to: string, comptime Writer: type, writer: Writer) void {
+ pub fn getPublicPath(to: string, origin: URL, comptime Writer: type, writer: Writer) void {
const relative_path = VirtualMachine.vm.bundler.fs.relativeTo(to);
- if (VirtualMachine.vm.bundler.options.origin.isAbsolute()) {
- VirtualMachine.vm.bundler.options.origin.joinWrite(
+ if (origin.isAbsolute()) {
+ origin.joinWrite(
Writer,
writer,
VirtualMachine.vm.bundler.options.routes.asset_prefix_path,
@@ -558,7 +558,7 @@ pub const Bun = struct {
var stream = std.io.fixedBufferStream(&public_path_temp_str);
var writer = stream.writer();
- getPublicPath(to, @TypeOf(&writer), &writer);
+ getPublicPath(to, VirtualMachine.vm.origin, @TypeOf(&writer), &writer);
return ZigString.init(stream.buffer[0..stream.pos]).toValueGC(VirtualMachine.vm.global).asRef();
}
@@ -820,6 +820,7 @@ pub const VirtualMachine = struct {
blobs: *Blob.Group = undefined,
flush_list: std.ArrayList(string),
entry_point: ServerEntryPoint = undefined,
+ origin: URL = URL{},
arena: *std.heap.ArenaAllocator = undefined,
has_loaded: bool = false,
@@ -941,6 +942,7 @@ pub const VirtualMachine = struct {
.log = log,
.flush_list = std.ArrayList(string).init(allocator),
.blobs = try Blob.Group.init(allocator),
+ .origin = bundler.options.origin,
.macros = MacroMap.init(allocator),
.macro_entry_points = @TypeOf(VirtualMachine.vm.macro_entry_points).init(allocator),
@@ -1078,6 +1080,7 @@ pub const VirtualMachine = struct {
try bundler.linker.link(
file_path,
&parse_result,
+ vm.origin,
.absolute_path,
false,
);
@@ -1205,6 +1208,7 @@ pub const VirtualMachine = struct {
try vm.bundler.linker.link(
path,
&parse_result,
+ vm.origin,
.absolute_path,
false,
);
@@ -1384,19 +1388,33 @@ pub const VirtualMachine = struct {
pub fn normalizeSpecifier(slice_: string) string {
var slice = slice_;
if (slice.len == 0) return slice;
+ var was_http = false;
+ if (strings.hasPrefix(slice, "https://")) {
+ slice = slice["https://".len..];
+ was_http = true;
+ }
- if (strings.startsWith(slice, VirtualMachine.vm.bundler.options.origin.host)) {
- slice = slice[VirtualMachine.vm.bundler.options.origin.host.len..];
+ if (strings.hasPrefix(slice, "http://")) {
+ slice = slice["http://".len..];
+ was_http = true;
+ }
+
+ if (strings.hasPrefix(slice, VirtualMachine.vm.origin.host)) {
+ slice = slice[VirtualMachine.vm.origin.host.len..];
+ } else if (was_http) {
+ if (strings.indexOfChar(slice, '/')) |i| {
+ slice = slice[i..];
+ }
}
- if (VirtualMachine.vm.bundler.options.origin.path.len > 1) {
- if (strings.startsWith(slice, VirtualMachine.vm.bundler.options.origin.path)) {
- slice = slice[VirtualMachine.vm.bundler.options.origin.path.len..];
+ if (VirtualMachine.vm.origin.path.len > 1) {
+ if (strings.hasPrefix(slice, VirtualMachine.vm.origin.path)) {
+ slice = slice[VirtualMachine.vm.origin.path.len..];
}
}
if (VirtualMachine.vm.bundler.options.routes.asset_prefix_path.len > 0) {
- if (strings.startsWith(slice, VirtualMachine.vm.bundler.options.routes.asset_prefix_path)) {
+ if (strings.hasPrefix(slice, VirtualMachine.vm.bundler.options.routes.asset_prefix_path)) {
slice = slice[VirtualMachine.vm.bundler.options.routes.asset_prefix_path.len..];
}
}
@@ -1742,7 +1760,7 @@ pub const VirtualMachine = struct {
),
frame.sourceURLFormatter(
vm.bundler.fs.top_level_dir,
- &vm.bundler.options.origin,
+ &vm.origin,
allow_ansi_colors,
),
},
diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig
index 455c36e8a..cb87f2e98 100644
--- a/src/javascript/jsc/webcore/response.zig
+++ b/src/javascript/jsc/webcore/response.zig
@@ -1663,7 +1663,7 @@ pub const Request = struct {
_: js.ExceptionRef,
) js.JSValueRef {
if (this.request_context.header("Referrer")) |referrer| {
- return ZigString.init(referrer.value).toValueGC(VirtualMachine.vm.global).asRef();
+ return ZigString.init(referrer).toValueGC(VirtualMachine.vm.global).asRef();
} else {
return ZigString.init("").toValueGC(VirtualMachine.vm.global).asRef();
}
diff --git a/src/linker.zig b/src/linker.zig
index c652ec9b8..3255d770e 100644
--- a/src/linker.zig
+++ b/src/linker.zig
@@ -35,10 +35,10 @@ const Bundler = _bundler.Bundler;
const ResolveQueue = _bundler.ResolveQueue;
const ResolverType = Resolver.Resolver;
const Runtime = @import("./runtime.zig").Runtime;
-
+const URL = @import("query_string_map.zig").URL;
pub const CSSResolveError = error{ResolveError};
-pub const OnImportCallback = fn (resolve_result: *const Resolver.Result, import_record: *ImportRecord, source_dir: string) void;
+pub const OnImportCallback = fn (resolve_result: *const Resolver.Result, import_record: *ImportRecord, origin: URL) void;
pub const Linker = struct {
const HashedFileNameMap = std.AutoHashMap(u64, string);
@@ -117,6 +117,7 @@ pub const Linker = struct {
url: string,
range: logger.Range,
kind: ImportKind,
+ origin: URL,
comptime import_path_format: Options.BundleOptions.ImportPathFormat,
comptime resolve_only: bool,
) !string {
@@ -133,7 +134,7 @@ pub const Linker = struct {
const loader = this.options.loaders.get(resolve_result.path_pair.primary.name.ext) orelse .file;
- this.processImportRecord(loader, dir, &resolve_result, &import_record, import_path_format) catch unreachable;
+ this.processImportRecord(loader, dir, &resolve_result, &import_record, origin, import_path_format) catch unreachable;
return import_record.path.text;
},
.at_conditional => {
@@ -145,7 +146,7 @@ pub const Linker = struct {
var import_record = ImportRecord{ .range = range, .path = resolve_result.path_pair.primary, .kind = kind };
const loader = this.options.loaders.get(resolve_result.path_pair.primary.name.ext) orelse .file;
- this.processImportRecord(loader, dir, &resolve_result, &import_record, import_path_format) catch unreachable;
+ this.processImportRecord(loader, dir, &resolve_result, &import_record, origin, import_path_format) catch unreachable;
return import_record.path.text;
},
.url => {
@@ -157,7 +158,7 @@ pub const Linker = struct {
var import_record = ImportRecord{ .range = range, .path = resolve_result.path_pair.primary, .kind = kind };
const loader = this.options.loaders.get(resolve_result.path_pair.primary.name.ext) orelse .file;
- this.processImportRecord(loader, dir, &resolve_result, &import_record, import_path_format) catch unreachable;
+ this.processImportRecord(loader, dir, &resolve_result, &import_record, origin, import_path_format) catch unreachable;
return import_record.path.text;
},
else => unreachable,
@@ -165,13 +166,10 @@ pub const Linker = struct {
unreachable;
}
- pub inline fn nodeModuleBundleImportPath(this: *const ThisLinker) string {
+ pub inline fn nodeModuleBundleImportPath(this: *const ThisLinker, origin: URL) string {
if (this.options.platform.isBun()) return "/node_modules.server.bun";
- return if (this.options.node_modules_bundle_url.len > 0)
- this.options.node_modules_bundle_url
- else
- this.options.node_modules_bundle.?.bundle.import_from_name;
+ return std.fmt.allocPrint(this.allocator, "{s}://{}{s}", .{ origin.displayProtocol(), origin.displayHost(), this.options.node_modules_bundle.?.bundle.import_from_name }) catch unreachable;
}
// pub const Scratch = struct {
@@ -193,6 +191,7 @@ pub const Linker = struct {
linker: *ThisLinker,
file_path: Fs.Path,
result: *_bundler.ParseResult,
+ origin: URL,
comptime import_path_format: Options.BundleOptions.ImportPathFormat,
comptime ignore_runtime: bool,
) !void {
@@ -201,6 +200,7 @@ pub const Linker = struct {
var needs_bundle = false;
var had_resolve_errors = false;
var needs_require = false;
+ var node_module_bundle_import_path: ?string = null;
// Step 1. Resolve imports & requires
switch (result.loader) {
@@ -213,15 +213,17 @@ pub const Linker = struct {
if (strings.eqlComptime(import_record.path.text, Runtime.Imports.Name)) {
// runtime is included in the bundle, so we don't need to dynamically import it
if (linker.options.node_modules_bundle != null) {
- import_record.path.text = linker.nodeModuleBundleImportPath();
+ node_module_bundle_import_path = node_module_bundle_import_path orelse
+ linker.nodeModuleBundleImportPath(origin);
+ import_record.path.text = node_module_bundle_import_path.?;
result.ast.runtime_import_record_id = record_index;
} else {
import_record.path = try linker.generateImportPath(
source_dir,
linker.runtime_source_path,
- Runtime.version(),
false,
"bun",
+ origin,
import_path_format,
);
result.ast.runtime_import_record_id = record_index;
@@ -287,7 +289,9 @@ pub const Linker = struct {
}
import_record.is_bundled = true;
- import_record.path.text = linker.nodeModuleBundleImportPath();
+ node_module_bundle_import_path = node_module_bundle_import_path orelse
+ linker.nodeModuleBundleImportPath(origin);
+ import_record.path.text = node_module_bundle_import_path.?;
import_record.module_id = found_module.id;
needs_bundle = true;
continue;
@@ -303,6 +307,7 @@ pub const Linker = struct {
source_dir,
resolved_import,
import_record,
+ origin,
import_path_format,
) catch continue;
@@ -407,14 +412,14 @@ pub const Linker = struct {
import_records[import_records.len - 1] = ImportRecord{
.kind = .stmt,
.path = if (linker.options.node_modules_bundle != null)
- Fs.Path.init(linker.nodeModuleBundleImportPath())
+ Fs.Path.init(node_module_bundle_import_path orelse linker.nodeModuleBundleImportPath(origin))
else
try linker.generateImportPath(
source_dir,
linker.runtime_source_path,
- Runtime.version(),
false,
"bun",
+ origin,
import_path_format,
),
.range = logger.Range{ .loc = logger.Loc{ .start = 0 }, .len = 0 },
@@ -460,9 +465,9 @@ pub const Linker = struct {
linker: *ThisLinker,
source_dir: string,
source_path: string,
- _: ?string,
use_hashed_name: bool,
namespace: string,
+ origin: URL,
comptime import_path_format: Options.BundleOptions.ImportPathFormat,
) !Fs.Path {
switch (import_path_format) {
@@ -532,7 +537,7 @@ pub const Linker = struct {
// assumption: already starts with "node:"
"{s}/{s}",
.{
- linker.options.origin.origin,
+ origin,
source_path,
},
));
@@ -559,7 +564,7 @@ pub const Linker = struct {
basename = try linker.getHashedFilename(basepath, null);
}
- return Fs.Path.init(try linker.options.origin.joinAlloc(
+ return Fs.Path.init(try origin.joinAlloc(
linker.allocator,
linker.options.routes.asset_prefix_path,
dirname,
@@ -580,6 +585,7 @@ pub const Linker = struct {
source_dir: string,
resolve_result: *const Resolver.Result,
import_record: *ImportRecord,
+ origin: URL,
comptime import_path_format: Options.BundleOptions.ImportPathFormat,
) !void {
linker.import_counter += 1;
@@ -594,16 +600,16 @@ pub const Linker = struct {
import_record.path = try linker.generateImportPath(
source_dir,
if (path.is_symlink and import_path_format == .absolute_url and linker.options.platform.isNotBun()) path.pretty else path.text,
- if (resolve_result.package_json) |package_json| package_json.version else "",
Bundler.isCacheEnabled and loader == .file,
path.namespace,
+ origin,
import_path_format,
);
switch (loader) {
.css => {
if (linker.onImportCSS) |callback| {
- callback(resolve_result, import_record, source_dir);
+ callback(resolve_result, import_record, origin);
}
// This saves us a less reliable string check
import_record.print_mode = .css;
diff --git a/src/query_string_map.zig b/src/query_string_map.zig
index 0f43e9671..16a3c83c7 100644
--- a/src/query_string_map.zig
+++ b/src/query_string_map.zig
@@ -15,7 +15,11 @@ const C = _global.C;
// This is close to WHATWG URL, but we don't want the validation errors
pub const URL = struct {
hash: string = "",
+ /// hostname, but with a port
+ /// `localhost:3000`
host: string = "",
+ /// hostname does not have a port
+ /// `localhost`
hostname: string = "",
href: string = "",
origin: string = "",
@@ -86,6 +90,36 @@ pub const URL = struct {
return "localhost";
}
+ pub const HostFormatter = struct {
+ host: string,
+ port: string,
+ is_https: bool = false,
+
+ pub fn format(formatter: HostFormatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
+ if (strings.indexOfChar(formatter.host, ':') != null) {
+ try writer.writeAll(formatter.host);
+ return;
+ }
+
+ try writer.writeAll(formatter.host);
+
+ const is_port_optional = (formatter.is_https and (formatter.port.len == 0 or strings.eqlComptime(formatter.port, "443"))) or
+ (!formatter.is_https and (formatter.port.len == 0 or strings.eqlComptime(formatter.port, "80")));
+ if (!is_port_optional) {
+ try writer.writeAll(":");
+ try writer.writeAll(formatter.port);
+ return;
+ }
+ }
+ };
+ pub fn displayHost(this: *const URL) HostFormatter {
+ return HostFormatter{
+ .host = if (this.host.len > 0) this.host else this.displayHostname(),
+ .port = this.port,
+ .is_https = this.isHTTPS(),
+ };
+ }
+
pub fn hasHTTPLikeProtocol(this: *const URL) bool {
return strings.eqlComptime(this.protocol, "http") or strings.eqlComptime(this.protocol, "https");
}