fix: address PR #149 review comments
This commit is contained in:
@@ -37,6 +37,22 @@ describe("validateGatewayUrl", () => {
|
|||||||
expect(validateGatewayUrl(" wss://host:443 ")).toBeNull();
|
expect(validateGatewayUrl(" wss://host:443 ")).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("accepts IPv6 URLs with explicit non-default port", () => {
|
||||||
|
expect(validateGatewayUrl("wss://[::1]:8080")).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("accepts IPv6 URLs with explicit default port", () => {
|
||||||
|
expect(validateGatewayUrl("wss://[2001:db8::1]:443")).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("accepts userinfo URLs with explicit port", () => {
|
||||||
|
expect(validateGatewayUrl("ws://user:pass@gateway.example.com:8080")).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("accepts userinfo URLs with IPv6 host and explicit port", () => {
|
||||||
|
expect(validateGatewayUrl("wss://user@[::1]:443")).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
it("rejects empty string", () => {
|
it("rejects empty string", () => {
|
||||||
expect(validateGatewayUrl("")).toBe("Gateway URL is required.");
|
expect(validateGatewayUrl("")).toBe("Gateway URL is required.");
|
||||||
});
|
});
|
||||||
@@ -71,6 +87,18 @@ describe("validateGatewayUrl", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("rejects out-of-range ports", () => {
|
||||||
|
expect(validateGatewayUrl("wss://gateway.example.com:65536")).toBe(
|
||||||
|
"Enter a valid gateway URL including port.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects userinfo URLs with no explicit port", () => {
|
||||||
|
expect(validateGatewayUrl("ws://user:pass@gateway.example.com")).toBe(
|
||||||
|
"Gateway URL must include an explicit port.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("rejects URL with only whitespace", () => {
|
it("rejects URL with only whitespace", () => {
|
||||||
expect(validateGatewayUrl(" ")).toBe("Gateway URL is required.");
|
expect(validateGatewayUrl(" ")).toBe("Gateway URL is required.");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,16 +13,42 @@ export type GatewayCheckStatus = "idle" | "checking" | "success" | "error";
|
|||||||
*/
|
*/
|
||||||
function hasExplicitPort(urlString: string): boolean {
|
function hasExplicitPort(urlString: string): boolean {
|
||||||
try {
|
try {
|
||||||
const { hostname } = new URL(urlString);
|
|
||||||
// Extract the authority portion (between // and the first / ? or #)
|
// Extract the authority portion (between // and the first / ? or #)
|
||||||
const withoutScheme = urlString.slice(urlString.indexOf("//") + 2);
|
const withoutScheme = urlString.slice(urlString.indexOf("//") + 2);
|
||||||
const authority = withoutScheme.split(/[/?#]/)[0];
|
const authority = withoutScheme.split(/[/?#]/)[0];
|
||||||
// authority is either "host", "host:port", or "[ipv6]:port"
|
if (!authority) {
|
||||||
// Remove a leading IPv6 bracket group before checking for ":"
|
return false;
|
||||||
const withoutIPv6 = authority.startsWith("[")
|
}
|
||||||
? authority.slice(authority.indexOf("]") + 1)
|
|
||||||
: authority.slice(hostname.length);
|
// authority may be:
|
||||||
return withoutIPv6.startsWith(":") && /^:\d+$/.test(withoutIPv6);
|
// - host[:port]
|
||||||
|
// - [ipv6][:port]
|
||||||
|
// - userinfo@host[:port]
|
||||||
|
// - userinfo@[ipv6][:port]
|
||||||
|
const atIndex = authority.lastIndexOf("@");
|
||||||
|
const hostPort = atIndex === -1 ? authority : authority.slice(atIndex + 1);
|
||||||
|
|
||||||
|
let portSegment = "";
|
||||||
|
if (hostPort.startsWith("[")) {
|
||||||
|
const closingBracketIndex = hostPort.indexOf("]");
|
||||||
|
if (closingBracketIndex === -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
portSegment = hostPort.slice(closingBracketIndex + 1);
|
||||||
|
} else {
|
||||||
|
const lastColonIndex = hostPort.lastIndexOf(":");
|
||||||
|
if (lastColonIndex === -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
portSegment = hostPort.slice(lastColonIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!portSegment.startsWith(":") || !/^:\d+$/.test(portSegment)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const port = Number.parseInt(portSegment.slice(1), 10);
|
||||||
|
return Number.isInteger(port) && port >= 0 && port <= 65535;
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user