diff --git a/src/Address.ts b/src/Address.ts index 8b506a7..5c85f04 100644 --- a/src/Address.ts +++ b/src/Address.ts @@ -5,18 +5,34 @@ import * as path from "path"; import * as url from "url"; export function sanitizeUrl(urlRemote: string) { - var urlParts = url.parse(urlRemote, false, true); - var origin = urlParts.host || ""; + try { + const urlObj = new URL(urlRemote); + var origin = urlObj.host || ""; - if ((urlParts.pathname || "").charAt(0) != "/") origin += "/"; + if ((urlObj.pathname || "").charAt(0) != "/") origin += "/"; - origin += urlParts.pathname; - return [ - urlParts.protocol || "http:", - "//", - url.resolve("", origin), - urlParts.search || "" - ].join(""); + origin += urlObj.pathname; + return [ + urlObj.protocol || "http:", + "//", + url.resolve("", origin), + urlObj.search || "" + ].join(""); + } catch (e) { + // If URL is invalid, try to construct it with a base + const urlObj = new URL(urlRemote, "http://dummy"); + var origin = urlObj.host || ""; + + if ((urlObj.pathname || "").charAt(0) != "/") origin += "/"; + + origin += urlObj.pathname; + return [ + urlObj.protocol || "http:", + "//", + url.resolve("", origin), + urlObj.search || "" + ].join(""); + } } /** Last line of defence to filter malicious paths. */ diff --git a/src/Cache.ts b/src/Cache.ts index 16400fe..f7e47e4 100644 --- a/src/Cache.ts +++ b/src/Cache.ts @@ -4,7 +4,6 @@ import * as fs from "fs"; import * as path from "path"; -import * as url from "url"; import * as http from "http"; import * as stream from "stream"; import got from "got"; @@ -565,27 +564,29 @@ export class Cache { private static forceRedirect(urlRemote: string, options: FetchOptions) { if (!options.forceHost && !options.forcePort) return urlRemote; - var urlParts = url.parse(urlRemote); + const urlObj = new URL(urlRemote); var changed = false; - if (!urlParts.hostname) return urlRemote; + if (!urlObj.hostname) return urlRemote; - if (options.forceHost && urlParts.hostname != options.forceHost) { - urlParts.hostname = options.forceHost; + if (options.forceHost && urlObj.hostname != options.forceHost) { + urlObj.hostname = options.forceHost; changed = true; } - if (options.forcePort && urlParts.port != "" + options.forcePort) { - urlParts.port = "" + options.forcePort; + if (options.forcePort && urlObj.port != "" + options.forcePort) { + urlObj.port = "" + options.forcePort; changed = true; } if (!changed) return urlRemote; - urlParts.search = "?host=" + encodeURIComponent(urlParts.host || ""); - urlParts.host = null as any; + const originalHost = urlObj.host; + urlObj.search = "?host=" + encodeURIComponent(originalHost || ""); + // Remove the host to use the modified hostname/port + urlObj.host = urlObj.hostname + (urlObj.port ? ":" + urlObj.port : ""); - return url.format(urlParts); + return urlObj.href; } /** Queue for limiting parallel downloads. */ diff --git a/test/serve.ts b/test/serve.ts index def09a7..678e6ff 100644 --- a/test/serve.ts +++ b/test/serve.ts @@ -20,7 +20,6 @@ console.log( import { assignIn } from "lodash"; import * as fs from "fs"; import { fsa } from "../dist/mkdirp"; -import * as url from "url"; import * as http from "http"; import { Address, Cache } from "../dist/cget"; @@ -86,8 +85,8 @@ function checkRemoteLink(cachePath: string) { var app = http.createServer( (req: http.IncomingMessage, res: http.ServerResponse) => { - var urlParts = url.parse(req.url); - var args = parseArgs(urlParts.query); + const urlObj = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`); + var args = parseArgs(urlObj.search.substring(1)); var host = args["host"]; // TODO does localhost not count as a host? @@ -105,13 +104,12 @@ var app = http.createServer( return; } - urlParts.protocol = "http"; - urlParts.search = null; - urlParts.query = null; - urlParts.host = host; + urlObj.protocol = "http"; + urlObj.search = ""; + urlObj.host = host; cache - .getCachePath(new Address(url.format(urlParts))) + .getCachePath(new Address(urlObj.href)) .then((cachePath: string) => checkRemoteLink(cachePath) .then((urlRemote: string) => {