Support *.localhost subdomains

In the new always-on-node service we'll want to test things locally with
domains that look like this: rudolfs.localhost:3080
This commit is contained in:
Rūdolfs Ošiņš
2026-01-13 12:49:18 -06:00
parent 0fae94fce4
commit 8f52b2bdfb
4 changed files with 115 additions and 3 deletions
+1 -1
View File
@@ -243,4 +243,4 @@ export function routeToPath(route: Route): string {
}
}
export const testExports = { urlToRoute, routeToPath };
export const testExports = { urlToRoute, routeToPath, extractBaseUrl };
+2 -2
View File
@@ -203,9 +203,9 @@ export function isMarkdownPath(path: string): boolean {
return /\.(md|mkd|markdown)$/i.test(path);
}
// Check whether the given address is a localhost address.
export function isLocal(addr: string): boolean {
return addr.startsWith("127.0.0.1") || addr.startsWith("localhost");
// Support *.localhost subdomains and 127.0.0.1.
return addr.endsWith("localhost") || addr.startsWith("127.0.0.1");
}
// Check whether the given domain name is an onion domain name.
+92
View File
@@ -269,3 +269,95 @@ describe("pathToRoute", () => {
).toEqual(route);
}
});
describe("extractBaseUrl", () => {
test("hostname with explicit port", () => {
const result = testExports.extractBaseUrl("example.com:9000");
expect(result).toEqual({
hostname: "example.com",
port: 9000,
scheme: config.nodes.defaultHttpdScheme,
});
});
test("hostname without port uses default port and scheme", () => {
const result = testExports.extractBaseUrl("example.com");
expect(result).toEqual({
hostname: "example.com",
port: config.nodes.defaultHttpdPort,
scheme: config.nodes.defaultHttpdScheme,
});
});
test("localhost without port uses local port and http scheme", () => {
const result = testExports.extractBaseUrl("localhost");
expect(result).toEqual({
hostname: "localhost",
port: config.nodes.defaultLocalHttpdPort,
scheme: "http",
});
});
test("localhost with explicit port uses http scheme", () => {
const result = testExports.extractBaseUrl("localhost:3000");
expect(result).toEqual({
hostname: "localhost",
port: 3000,
scheme: "http",
});
});
test("wildcard localhost domain without port uses local port and http scheme", () => {
const result = testExports.extractBaseUrl("app.localhost");
expect(result).toEqual({
hostname: "app.localhost",
port: config.nodes.defaultLocalHttpdPort,
scheme: "http",
});
});
test("127.0.0.1 without port uses local port and http scheme", () => {
const result = testExports.extractBaseUrl("127.0.0.1");
expect(result).toEqual({
hostname: "127.0.0.1",
port: config.nodes.defaultLocalHttpdPort,
scheme: "http",
});
});
test("127.0.0.1 with explicit port uses http scheme", () => {
const result = testExports.extractBaseUrl("127.0.0.1:8080");
expect(result).toEqual({
hostname: "127.0.0.1",
port: 8080,
scheme: "http",
});
});
test("onion domain without port uses default port and http scheme", () => {
const result = testExports.extractBaseUrl("example.onion");
expect(result).toEqual({
hostname: "example.onion",
port: config.nodes.defaultHttpdPort,
scheme: "http",
});
});
test("onion domain with explicit port uses http scheme", () => {
const result = testExports.extractBaseUrl("example.onion:9050");
expect(result).toEqual({
hostname: "example.onion",
port: 9050,
scheme: "http",
});
});
test("URL-encoded hostname is decoded", () => {
const result = testExports.extractBaseUrl("example.com%3A8000");
expect(result).toEqual({
hostname: "example.com",
port: 8000,
scheme: config.nodes.defaultHttpdScheme,
});
});
});
+20
View File
@@ -234,3 +234,23 @@ describe("Date Manipulation", () => {
);
});
});
describe("isLocal", () => {
test("returns true for localhost", () => {
expect(utils.isLocal("localhost")).toBe(true);
});
test("returns true for 127.0.0.1", () => {
expect(utils.isLocal("127.0.0.1")).toBe(true);
});
test("returns true for localhost subdomains", () => {
expect(utils.isLocal("test.localhost")).toBe(true);
expect(utils.isLocal("deeper.test.localhost")).toBe(true);
});
test("returns false for any other tld", () => {
expect(utils.isLocal("example.com")).toBe(false);
expect(utils.isLocal("radicle.garden")).toBe(false);
});
});