Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/bun-types/bun.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2404,6 +2404,8 @@ declare module "bun" {
*/
readonly pendingWebSockets: number;

readonly url: URL;

readonly port: number;
/**
* The hostname the server is listening on. Does not include the port
Expand Down
4 changes: 4 additions & 0 deletions src/bun.js/api/server.classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ function generate(name) {
getter: "getAddress",
cache: true,
},
url: {
getter: "getURL",
cache: true,
},
protocol: {
getter: "getProtocol",
},
Expand Down
36 changes: 34 additions & 2 deletions src/bun.js/api/server.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5266,8 +5266,9 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
this: *ThisServer,
_: *JSC.JSGlobalObject,
) callconv(.C) JSC.JSValue {
if (this.config.address != .tcp) {
return JSValue.undefined;
switch (this.config.address) {
.unix => return .undefined,
else => {},
}

var listener = this.listener orelse return JSC.JSValue.jsNumber(this.config.address.tcp.port);
Expand Down Expand Up @@ -5328,7 +5329,38 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
}
}

pub fn getURL(this: *ThisServer, globalThis: *JSGlobalObject) callconv(.C) JSC.JSValue {
const fmt = switch (this.config.address) {
.unix => |unix| strings.URLFormatter{
.proto = .unix,
.hostname = bun.sliceTo(@constCast(unix), 0),
},
.tcp => |tcp| blk: {
var port: u16 = tcp.port;
if (this.listener) |listener| {
port = @intCast(listener.getLocalPort());
}
break :blk strings.URLFormatter{
.proto = if (comptime ssl_enabled_) .https else .http,
.hostname = if (tcp.hostname) |hostname| bun.sliceTo(@constCast(hostname), 0) else null,
.port = port,
};
},
};

var buf = std.fmt.allocPrint(default_allocator, "{any}", .{fmt}) catch @panic("Out of memory");
defer default_allocator.free(buf);

var value = bun.String.create(buf);
return value.toJSDOMURL(globalThis);
}

pub fn getHostname(this: *ThisServer, globalThis: *JSGlobalObject) callconv(.C) JSC.JSValue {
switch (this.config.address) {
.unix => return .undefined,
else => {},
}

if (this.cached_hostname.isEmpty()) {
if (this.listener) |listener| {
var buf: [1024]u8 = [_]u8{0} ** 1024;
Expand Down
14 changes: 14 additions & 0 deletions src/bun.js/bindings/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1419,6 +1419,20 @@ extern "C" JSC__JSValue ZigString__toJSONObject(const ZigString* strPtr, JSC::JS
return JSValue::encode(result);
}

// TODO: Move this to BunString.cpp
extern "C" JSC__JSValue BunString__toJSDOMURL(JSC::JSGlobalObject* lexicalGlobalObject, BunString* bunString)
{
auto& globalObject = *reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
auto& vm = globalObject.vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);

auto str = Bun::toWTFString(*bunString);

auto object = WebCore::DOMURL::create(str, String());
auto jsValue = WebCore::toJSNewlyCreated<IDLInterface<DOMURL>>(*lexicalGlobalObject, globalObject, throwScope, WTFMove(object));
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(jsValue));
}

JSC__JSValue SystemError__toErrorInstance(const SystemError* arg0,
JSC__JSGlobalObject* globalObject)
{
Expand Down
8 changes: 8 additions & 0 deletions src/bun.js/bindings/bindings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -236,11 +236,19 @@ pub const ZigString = extern struct {
}

extern fn ZigString__toJSONObject(this: *const ZigString, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue;

pub fn toJSONObject(this: ZigString, globalThis: *JSC.JSGlobalObject) JSValue {
JSC.markBinding(@src());
return ZigString__toJSONObject(&this, globalThis);
}

extern fn BunString__toURL(this: *const ZigString, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue;

pub fn toURL(this: ZigString, globalThis: *JSC.JSGlobalObject) JSValue {
JSC.markBinding(@src());
return BunString__toURL(&this, globalThis);
}

pub fn hasPrefixChar(this: ZigString, char: u8) bool {
if (this.len == 0)
return false;
Expand Down
4 changes: 2 additions & 2 deletions src/bun.js/webcore/request.zig
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ pub const Request = struct {
const req_url = req.url();
if (req_url.len > 0 and req_url[0] == '/') {
if (req.header("host")) |host| {
const fmt = ZigURL.HostFormatter{
const fmt = strings.HostFormatter{
.is_https = this.https,
.host = host,
};
Expand All @@ -368,7 +368,7 @@ pub const Request = struct {
const req_url = req.url();
if (req_url.len > 0 and req_url[0] == '/') {
if (req.header("host")) |host| {
const fmt = ZigURL.HostFormatter{
const fmt = strings.HostFormatter{
.is_https = this.https,
.host = host,
};
Expand Down
2 changes: 1 addition & 1 deletion src/http/websocket_http_client.zig
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ fn buildRequestBody(
host_.deinit();
}

const host_fmt = ZigURL.HostFormatter{
const host_fmt = strings.HostFormatter{
.is_https = is_https,
.host = host_.slice(),
.port = port,
Expand Down
7 changes: 7 additions & 0 deletions src/string.zig
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,12 @@ pub const String = extern struct {
return BunString__toJSWithLength(globalObject, this, len);
}

pub fn toJSDOMURL(this: *String, globalObject: *bun.JSC.JSGlobalObject) JSC.JSValue {
JSC.markBinding(@src());

return BunString__toJSDOMURL(globalObject, this);
}

pub fn toJSConst(this: *const String, globalObject: *bun.JSC.JSGlobalObject) JSC.JSValue {
JSC.markBinding(@src());
var a = this.*;
Expand Down Expand Up @@ -693,6 +699,7 @@ pub const String = extern struct {
extern fn BunString__fromJS(globalObject: *JSC.JSGlobalObject, value: bun.JSC.JSValue, out: *String) bool;
extern fn BunString__toJS(globalObject: *JSC.JSGlobalObject, in: *String) JSC.JSValue;
extern fn BunString__toJSWithLength(globalObject: *JSC.JSGlobalObject, in: *String, usize) JSC.JSValue;
extern fn BunString__toJSDOMURL(globalObject: *JSC.JSGlobalObject, in: *String) JSC.JSValue;
extern fn BunString__toWTFString(this: *String) void;

pub fn ref(this: String) void {
Expand Down
65 changes: 65 additions & 0 deletions src/string_immutable.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4961,3 +4961,68 @@ pub fn concatIfNeeded(
}
std.debug.assert(remain.len == 0);
}

pub const HostFormatter = struct {
host: string,
port: ?u16 = null,
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.port == null or (formatter.is_https and formatter.port == 443) or
(!formatter.is_https and formatter.port == 80);
if (!is_port_optional) {
try writer.print(":{d}", .{formatter.port.?});
return;
}
}
};

const Proto = enum {
http,
https,
unix,
};

pub const URLFormatter = struct {
proto: Proto = .http,
hostname: ?string = null,
port: ?u16 = null,

pub fn format(this: URLFormatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
try writer.print("{s}://", .{switch (this.proto) {
.http => "http",
.https => "https",
.unix => "unix",
}});

if (this.hostname) |hostname| {
const needs_brackets = hostname[0] != '[' and strings.isIPV6Address(hostname);
if (needs_brackets) {
try writer.print("[{s}]", .{hostname});
} else {
try writer.writeAll(hostname);
}
} else {
try writer.writeAll("localhost");
}

if (this.proto == .unix) {
return;
}

const is_port_optional = this.port == null or (this.proto == .https and this.port == 443) or
(this.proto == .http and this.port == 80);
if (is_port_optional) {
try writer.writeAll("/");
} else {
try writer.print(":{d}/", .{this.port.?});
}
}
};
25 changes: 2 additions & 23 deletions src/url.zig
Original file line number Diff line number Diff line change
Expand Up @@ -101,29 +101,8 @@ pub const URL = struct {
return "localhost";
}

pub const HostFormatter = struct {
host: string,
port: ?u16 = null,
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.port == null or (formatter.is_https and formatter.port == 443) or
(!formatter.is_https and formatter.port == 80);
if (!is_port_optional) {
try writer.print(":{d}", .{formatter.port.?});
return;
}
}
};
pub fn displayHost(this: *const URL) HostFormatter {
return HostFormatter{
pub fn displayHost(this: *const URL) strings.HostFormatter {
return strings.HostFormatter{
.host = if (this.host.len > 0) this.host else this.displayHostname(),
.port = if (this.port.len > 0) this.getPort() else null,
.is_https = this.isHTTPS(),
Expand Down
28 changes: 28 additions & 0 deletions test/js/bun/http/fixtures/cert.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCIzOJskt6VkEJY
XKSJv/Gdil3XYkjk3NVc/+m+kzqnkTRbPtT9w+IGWgmJhuf9DJPLCwHFAEFarVwV
x16Q0PbU4ajXaLRHEYGhrH10oTMjQnJ24xVm26mxRXPQa5vaLpWJqNyIdNLIQLe+
UXUOzSGGsFTRMAjvYrkzjBe4ZUnaZV+aFY/ug0jfzeA1dJjzKZs6+yTJRbsuWUEb
8MsDmT4v+kBZDKdaDn7AFDWRVqx/38BnqsRzkM0CxpnyT2kRzw5zQajIE13gdTJo
1EHvYSUkkxrY5m30Rl9BuBBZBjhMzOHq0fYVVooHO+sf4XHPgvFTTxJum85u7J1J
oEUjrLKtAgMBAAECggEACInVNhaiqu4infZGVMy0rXMV8VwSlapM7O2SLtFsr0nK
XUmaLK6dvGzBPKK9dxdiYCFzPlMKQTkhzsAvYFWSmm3tRmikG+11TFyCRhXLpc8/
ark4vD9Io6ZkmKUmyKLwtXNjNGcqQtJ7RXc7Ga3nAkueN6JKZHqieZusXVeBGQ70
YH1LKyVNBeJggbj+g9rqaksPyNJQ8EWiNTJkTRQPazZ0o1VX/fzDFyr/a5npFtHl
4BHfafv9o1Xyr70Kie8CYYRJNViOCN+ylFs7Gd3XRaAkSkgMT/7DzrHdEM2zrrHK
yNg2gyDVX9UeEJG2X5UtU0o9BVW7WBshz/2hqIUHoQKBgQC8zsRFvC7u/rGr5vRR
mhZZG+Wvg03/xBSuIgOrzm+Qie6mAzOdVmfSL/pNV9EFitXt1yd2ROo31AbS7Evy
Bm/QVKr2mBlmLgov3B7O/e6ABteooOL7769qV/v+yo8VdEg0biHmsfGIIXDe3Lwl
OT0XwF9r/SeZLbw1zfkSsUVG/QKBgQC5fANM3Dc9LEek+6PHv5+eC1cKkyioEjUl
/y1VUD00aABI1TUcdLF3BtFN2t/S6HW0hrP3KwbcUfqC25k+GDLh1nM6ZK/gI3Yn
IGtCHxtE3S6jKhE9QcK/H+PzGVKWge9SezeYRP0GHJYDrTVTA8Kt9HgoZPPeReJl
+Ss9c8ThcQKBgECX6HQHFnNzNSufXtSQB7dCoQizvjqTRZPxVRoxDOABIGExVTYt
umUhPtu5AGyJ+/hblEeU+iBRbGg6qRzK8PPwE3E7xey8MYYAI5YjL7YjISKysBUL
AhM6uJ6Jg/wOBSnSx8xZ8kzlS+0izUda1rjKeprCSArSp8IsjlrDxPStAoGAEcPr
+P+altRX5Fhpvmb/Hb8OTif8G+TqjEIdkG9H/W38oP0ywg/3M2RGxcMx7txu8aR5
NjI7zPxZFxF7YvQkY3cLwEsGgVxEI8k6HLIoBXd90Qjlb82NnoqqZY1GWL4HMwo0
L/Rjm6M/Rwje852Hluu0WoIYzXA6F/Q+jPs6nzECgYAxx4IbDiGXuenkwSF1SUyj
NwJXhx4HDh7U6EO/FiPZE5BHE3BoTrFu3o1lzverNk7G3m+j+m1IguEAalHlukYl
rip9iUISlKYqbYZdLBoLwHAfHhszdrjqn8/v6oqbB5yR3HXjPFUWJo0WJ2pqJp56
ZshgmQQ/5Khoj6x0/dMPSg==
-----END PRIVATE KEY-----
23 changes: 23 additions & 0 deletions test/js/bun/http/fixtures/cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIID5jCCAs6gAwIBAgIUN7coIsdMcLo9amZfkwogu0YkeLEwDQYJKoZIhvcNAQEL
BQAwfjELMAkGA1UEBhMCU0UxDjAMBgNVBAgMBVN0YXRlMREwDwYDVQQHDAhMb2Nh
dGlvbjEaMBgGA1UECgwRT3JnYW5pemF0aW9uIE5hbWUxHDAaBgNVBAsME09yZ2Fu
aXphdGlvbmFsIFVuaXQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yMzA5MjExNDE2
MjNaFw0yNDA5MjAxNDE2MjNaMH4xCzAJBgNVBAYTAlNFMQ4wDAYDVQQIDAVTdGF0
ZTERMA8GA1UEBwwITG9jYXRpb24xGjAYBgNVBAoMEU9yZ2FuaXphdGlvbiBOYW1l
MRwwGgYDVQQLDBNPcmdhbml6YXRpb25hbCBVbml0MRIwEAYDVQQDDAlsb2NhbGhv
c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCIzOJskt6VkEJYXKSJ
v/Gdil3XYkjk3NVc/+m+kzqnkTRbPtT9w+IGWgmJhuf9DJPLCwHFAEFarVwVx16Q
0PbU4ajXaLRHEYGhrH10oTMjQnJ24xVm26mxRXPQa5vaLpWJqNyIdNLIQLe+UXUO
zSGGsFTRMAjvYrkzjBe4ZUnaZV+aFY/ug0jfzeA1dJjzKZs6+yTJRbsuWUEb8MsD
mT4v+kBZDKdaDn7AFDWRVqx/38BnqsRzkM0CxpnyT2kRzw5zQajIE13gdTJo1EHv
YSUkkxrY5m30Rl9BuBBZBjhMzOHq0fYVVooHO+sf4XHPgvFTTxJum85u7J1JoEUj
rLKtAgMBAAGjXDBaMA4GA1UdDwEB/wQEAwIDiDATBgNVHSUEDDAKBggrBgEFBQcD
ATAUBgNVHREEDTALgglsb2NhbGhvc3QwHQYDVR0OBBYEFNzx4Rfs9m8XR5ML0WsI
sorKmB4PMA0GCSqGSIb3DQEBCwUAA4IBAQB87iQy8R0fiOky9WTcyzVeMaavS3MX
iTe1BRn1OCyDq+UiwwoNz7zdzZJFEmRtFBwPNFOe4HzLu6E+7yLFR552eYRHlqIi
/fiLb5JiZfPtokUHeqwELWBsoXtU8vKxViPiLZ09jkWOPZWo7b/xXd6QYykBfV91
usUXLzyTD2orMagpqNksLDGS3p3ggHEJBZtRZA8R7kPEw98xZHznOQpr26iv8kYz
ZWdLFoFdwgFBSfxePKax5rfo+FbwdrcTX0MhbORyiu2XsBAghf8s2vKDkHg2UQE8
haonxFYMFaASfaZ/5vWKYDTCJkJ67m/BtkpRafFEO+ad1i1S61OjfxH4
-----END CERTIFICATE-----
Loading