[bun.js] Add baseURI support to HTTP server

This commit is contained in:
Jarred Sumner
2022-04-07 06:22:42 -07:00
parent b97e81b27f
commit 4e05fd0541
8 changed files with 118 additions and 15 deletions

View File

@@ -3,6 +3,6 @@
"lib": ["ESNext"],
"module": "esnext",
"target": "esnext",
"typeRoots": ["~/.bun/types"]
"typeRoots": ["../types"]
}
}

Binary file not shown.

View File

@@ -8,9 +8,7 @@
"build": "esbuild --define:process.env.NODE_ENV=\"'production'\" --minify index.tsx bun-error.css --bundle --outdir=dist --platform=browser --format=esm"
},
"dependencies": {
"esbuild": "^0.14.10",
"preact": "^10.5.14",
"preact-compat": "^3.19.0",
"esbuild": "latest",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},

View File

@@ -1,6 +1,6 @@
{
"name": "bun-framework-next",
"version": "12.1.0",
"version": "12.1.1",
"main": "empty.js",
"module": "empty.js",
"description": "bun compatibility layer for Next.js v12.x.x",
@@ -16,12 +16,12 @@
"next": "^12"
},
"devDependencies": {
"@types/react": "^17.0.34",
"@types/react-dom": "^17.0.11",
"@types/react": "^18",
"@types/react-dom": "^18",
"next": "^12.1.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"typescript": "^4.4.4"
"react": "^18",
"react-dom": "^18",
"typescript": "^4"
},
"framework": {
"displayName": "Next.js",

View File

@@ -95,6 +95,11 @@ const linux = std.os.linux;
pub const ServerConfig = struct {
port: u16 = 0,
hostname: [*:0]const u8 = "0.0.0.0",
// TODO: use webkit URL parser instead of bun's
base_url: URL = URL{},
base_uri: string = "",
ssl_config: ?SSLConfig = null,
max_request_body_size: usize = 1024 * 1024 * 128,
development: bool = false,
@@ -238,7 +243,7 @@ pub const ServerConfig = struct {
.hostname = "0.0.0.0",
.development = true,
};
var has_hostname = false;
if (strings.eqlComptime(env.get("NODE_ENV") orelse "", "production")) {
args.development = false;
}
@@ -261,6 +266,10 @@ pub const ServerConfig = struct {
args.port = port;
}
if (VirtualMachine.vm.bundler.options.transform_options.origin) |origin| {
args.base_uri = origin;
}
if (arguments.next()) |arg| {
if (arg.isUndefinedOrNull() or !arg.isObject()) {
JSC.throwInvalidArguments("Bun.serve expects an object", .{}, global.ref(), exception);
@@ -271,6 +280,15 @@ pub const ServerConfig = struct {
args.port = @intCast(u16, @minimum(@maximum(0, port_.toInt32()), std.math.maxInt(u16)));
}
if (arg.getTruthy(global, "baseURI")) |baseURI| {
var sliced = baseURI.toSlice(global, bun.default_allocator);
if (sliced.len > 0) {
defer sliced.deinit();
args.base_uri = bun.default_allocator.dupe(u8, sliced.slice()) catch unreachable;
}
}
if (arg.getTruthy(global, "hostname") orelse arg.getTruthy(global, "host")) |host| {
const host_str = host.toSlice(
global,
@@ -278,6 +296,7 @@ pub const ServerConfig = struct {
);
if (host_str.len > 0) {
args.hostname = bun.default_allocator.dupeZ(u8, host_str.slice()) catch unreachable;
has_hostname = true;
}
}
@@ -329,6 +348,80 @@ pub const ServerConfig = struct {
JSC.throwInvalidArguments("Invalid port: must be > 0", .{}, global.ref(), exception);
}
if (args.base_uri.len > 0) {
args.base_url = URL.parse(args.base_uri);
if (args.base_url.hostname.len == 0) {
JSC.throwInvalidArguments("baseURI must have a hostname", .{}, global.ref(), exception);
bun.default_allocator.free(bun.constStrToU8(args.base_uri));
args.base_uri = "";
return args;
}
if (!strings.isAllASCII(args.base_uri)) {
JSC.throwInvalidArguments("Unicode baseURI must already be encoded for now.\nnew URL(baseuRI).toString() should do the trick.", .{}, global.ref(), exception);
bun.default_allocator.free(bun.constStrToU8(args.base_uri));
args.base_uri = "";
return args;
}
if (args.base_url.protocol.len == 0) {
const protocol: string = if (args.ssl_config != null) "https" else "http";
args.base_uri = (if ((args.port == 80 and args.ssl_config == null) or (args.port == 443 and args.ssl_config != null))
std.fmt.allocPrint(bun.default_allocator, "{s}://{s}/{s}", .{
protocol,
args.base_url.hostname,
strings.trimLeadingChar(args.base_url.pathname, '/'),
})
else
std.fmt.allocPrint(bun.default_allocator, "{s}://{s}:{d}/{s}", .{
protocol,
args.base_url.hostname,
args.port,
strings.trimLeadingChar(args.base_url.pathname, '/'),
})) catch unreachable;
args.base_url = URL.parse(args.base_uri);
}
} else {
const hostname: string =
if (has_hostname and std.mem.span(args.hostname).len > 0) std.mem.span(args.hostname) else "localhost";
const protocol: string = if (args.ssl_config != null) "https" else "http";
args.base_uri = (if ((args.port == 80 and args.ssl_config == null) or (args.port == 443 and args.ssl_config != null))
std.fmt.allocPrint(bun.default_allocator, "{s}://{s}/", .{
protocol,
hostname,
})
else
std.fmt.allocPrint(bun.default_allocator, "{s}://{s}:{d}/", .{ protocol, hostname, args.port })) catch unreachable;
if (!strings.isAllASCII(hostname)) {
JSC.throwInvalidArguments("Unicode hostnames must already be encoded for now.\nnew URL(input).hostname should do the trick.", .{}, global.ref(), exception);
bun.default_allocator.free(bun.constStrToU8(args.base_uri));
args.base_uri = "";
return args;
}
args.base_url = URL.parse(args.base_uri);
}
// I don't think there's a case where this can happen
// but let's check anyway, just in case
if (args.base_url.hostname.len == 0) {
JSC.throwInvalidArguments("baseURI must have a hostname", .{}, global.ref(), exception);
bun.default_allocator.free(bun.constStrToU8(args.base_uri));
args.base_uri = "";
return args;
}
if (args.base_url.username.len > 0 or args.base_url.password.len > 0) {
JSC.throwInvalidArguments("baseURI can't have a username or password", .{}, global.ref(), exception);
bun.default_allocator.free(bun.constStrToU8(args.base_uri));
args.base_uri = "";
return args;
}
return args;
}
};
@@ -499,7 +592,9 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
this.* = .{
.resp = resp,
.req = req,
.url = req.url(),
// this memory is owned by the Request object
.url = strings.append(bun.default_allocator, server.base_url_string_for_joining, req.url()) catch
@panic("Out of memory while joining the URL path?"),
.method = HTTP.Method.which(req.method()) orelse .GET,
.server = server,
};
@@ -1030,7 +1125,7 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type {
app: *App = undefined,
globalThis: *JSGlobalObject,
base_url_string_for_joining: string = "",
response_objects_pool: JSC.WebCore.Response.Pool = JSC.WebCore.Response.Pool{},
config: ServerConfig = ServerConfig{},
request_pool_allocator: std.mem.Allocator = undefined,
@@ -1040,6 +1135,7 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type {
server.* = .{
.globalThis = globalThis,
.config = config,
.base_url_string_for_joining = strings.trim(config.base_url.href, "/"),
};
RequestContext.pool = bun.default_allocator.create(RequestContext.RequestContextStackAllocator) catch @panic("Out of memory!");
server.request_pool_allocator = RequestContext.pool.get();
@@ -1190,6 +1286,7 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type {
},
},
};
request_object.url.mark();
// We keep the Request object alive for the duration of the request so that we can remove the pointer to the UWS request object.
var args = [_]JSC.C.JSValueRef{JSC.WebCore.Request.Class.make(this.globalThis.ref(), request_object)};
ctx.request_js_object = args[0];

View File

@@ -1085,7 +1085,7 @@ JSC::JSObject* GlobalObject::moduleLoaderCreateImportMetaProperties(JSGlobalObje
metaProperties->putDirect(vm, clientData->builtinNames().resolvePublicName(),
JSC::JSFunction::create(vm, JSC::jsCast<JSC::JSGlobalObject*>(globalObject), 0,
WTF::String("resolve"), functionImportMeta__resolve),
WTF::String("resolve"_s), functionImportMeta__resolve),
0);
}

View File

@@ -2780,6 +2780,10 @@ pub const JSValue = enum(u64) {
return @intToPtr(C_API.JSValueRef, @intCast(usize, @enumToInt(this)));
}
pub inline fn c(this: C_API.JSValueRef) JSValue {
return @intToEnum(JSValue, @ptrToInt(this));
}
pub inline fn fromRef(this: C_API.JSValueRef) JSValue {
return @intToEnum(JSValue, @ptrToInt(this));
}

View File

@@ -231,7 +231,7 @@ pub const Response = struct {
_: js.ExceptionRef,
) js.JSValueRef {
// https://developer.mozilla.org/en-US/docs/Web/API/Response/url
return ZigString.init(this.url).withEncoding().toValueGC(ctx.ptr()).asObjectRef();
return ZigString.init(this.url).toValueGC(ctx.ptr()).asObjectRef();
}
pub fn getResponseType(
@@ -4352,6 +4352,10 @@ pub const Request = struct {
this.headers = null;
}
if (this.url.isGloballyAllocated()) {
bun.default_allocator.free(bun.constStrToU8(this.url.slice()));
}
bun.default_allocator.destroy(this);
}