mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge pull request #14248 from meteor/node/url-parse
refactor: replace Node.js 'url' module with the native URL API
This commit is contained in:
@@ -327,16 +327,15 @@ Object.assign(StreamServer.prototype, {
|
||||
// Store arguments for use within the closure below
|
||||
var args = arguments;
|
||||
|
||||
// TODO replace with url package
|
||||
var url = Npm.require('url');
|
||||
|
||||
// Rewrite /websocket and /websocket/ urls to /sockjs/websocket while
|
||||
// preserving query string.
|
||||
var parsedUrl = url.parse(request.url);
|
||||
var parsedUrl = new URL(request.url, 'http://localhost');
|
||||
if (parsedUrl.pathname === pathPrefix + '/websocket' ||
|
||||
parsedUrl.pathname === pathPrefix + '/websocket/') {
|
||||
parsedUrl.pathname = self.prefix + '/websocket';
|
||||
request.url = url.format(parsedUrl);
|
||||
request.url = parsedUrl.search
|
||||
? parsedUrl.pathname + parsedUrl.search
|
||||
: parsedUrl.pathname;
|
||||
}
|
||||
oldHttpServerListeners.forEach(function(oldListener) {
|
||||
oldListener.apply(httpServer, args);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
var url = Npm.require("url");
|
||||
import { isLocalConnection, isSslConnection } from 'meteor/force-ssl-common';
|
||||
|
||||
// Unfortunately we can't use a connect middleware here since
|
||||
@@ -23,7 +22,7 @@ httpServer.addListener('request', function (req, res) {
|
||||
if (!isLocalConnection(req) && !isSslConnection(req)) {
|
||||
// connection is not cool. send a 302 redirect!
|
||||
|
||||
var host = url.parse(Meteor.absoluteUrl()).hostname;
|
||||
var host = new URL(Meteor.absoluteUrl()).hostname;
|
||||
|
||||
// strip off the port number. If we went to a URL with a custom
|
||||
// port, we don't know what the custom SSL port is anyway.
|
||||
|
||||
@@ -2,7 +2,7 @@ if (process.env.ROOT_URL &&
|
||||
typeof __meteor_runtime_config__ === "object") {
|
||||
__meteor_runtime_config__.ROOT_URL = process.env.ROOT_URL;
|
||||
if (__meteor_runtime_config__.ROOT_URL) {
|
||||
var parsedUrl = Npm.require('url').parse(__meteor_runtime_config__.ROOT_URL);
|
||||
var parsedUrl = new URL(__meteor_runtime_config__.ROOT_URL);
|
||||
// Sometimes users try to pass, eg, ROOT_URL=mydomain.com.
|
||||
if (!parsedUrl.host || ['http:', 'https:'].indexOf(parsedUrl.protocol) === -1) {
|
||||
throw Error("$ROOT_URL, if specified, must be an URL");
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import path from 'path';
|
||||
import url from 'url';
|
||||
import postcss from 'postcss';
|
||||
import cssnano from 'cssnano';
|
||||
|
||||
@@ -236,25 +235,29 @@ const rewriteRules = (rules, mergedCssPath) => {
|
||||
while (parts = cssUrlRegex.exec(value)) {
|
||||
const oldCssUrl = parts[0];
|
||||
const quote = parts[1];
|
||||
const resource = url.parse(parts[2]);
|
||||
const cssUrlValue = parts[2];
|
||||
|
||||
// We don't rewrite URLs starting with a protocol definition such as
|
||||
// http, https, or data, or those with network-path references
|
||||
// i.e. //img.domain.com/cat.gif
|
||||
if (resource.protocol !== null ||
|
||||
resource.href.startsWith('//') ||
|
||||
resource.href.startsWith('#')) {
|
||||
if (/^[a-zA-Z][a-zA-Z0-9+\-.]*:/.test(cssUrlValue) ||
|
||||
cssUrlValue.startsWith('//') ||
|
||||
cssUrlValue.startsWith('#')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Rewrite relative paths (that refers to the internal application tree)
|
||||
// to absolute paths (addressable from the public build).
|
||||
let absolutePath = isRelative(resource.path)
|
||||
? pathJoin(basePath, resource.path)
|
||||
: resource.path;
|
||||
const hashIndex = cssUrlValue.indexOf('#');
|
||||
const hash = hashIndex >= 0 ? cssUrlValue.slice(hashIndex) : '';
|
||||
const pathPart = hashIndex >= 0 ? cssUrlValue.slice(0, hashIndex) : cssUrlValue;
|
||||
|
||||
if (resource.hash) {
|
||||
absolutePath += resource.hash;
|
||||
let absolutePath = isRelative(pathPart)
|
||||
? pathJoin(basePath, pathPart)
|
||||
: pathPart;
|
||||
|
||||
if (hash) {
|
||||
absolutePath += hash;
|
||||
}
|
||||
|
||||
// We used to finish the rewriting process at the absolute path step
|
||||
|
||||
@@ -18,7 +18,6 @@ OAuth._redirectUri = (serviceName, config, params, absoluteUrlOptions) => {
|
||||
}
|
||||
|
||||
if (Meteor.isServer && isCordova) {
|
||||
const url = Npm.require('url');
|
||||
let rootUrl = process.env.MOBILE_ROOT_URL ||
|
||||
__meteor_runtime_config__.ROOT_URL;
|
||||
|
||||
@@ -28,12 +27,11 @@ OAuth._redirectUri = (serviceName, config, params, absoluteUrlOptions) => {
|
||||
// XXX Maybe we should put this in a separate package or something
|
||||
// that is used here and by boilerplate-generator? Or maybe
|
||||
// `Meteor.absoluteUrl` should know how to do this?
|
||||
const parsedRootUrl = url.parse(rootUrl);
|
||||
const parsedRootUrl = new URL(rootUrl);
|
||||
if (parsedRootUrl.hostname === "localhost") {
|
||||
parsedRootUrl.hostname = "10.0.2.2";
|
||||
delete parsedRootUrl.host;
|
||||
}
|
||||
rootUrl = url.format(parsedRootUrl);
|
||||
rootUrl = parsedRootUrl.toString();
|
||||
}
|
||||
|
||||
absoluteUrlOptions = {
|
||||
|
||||
@@ -1,27 +1,17 @@
|
||||
import url from 'url';
|
||||
import { OAuth1Binding } from './oauth1_binding';
|
||||
|
||||
OAuth._queryParamsWithAuthTokenUrl = (authUrl, oauthBinding, params = {}, whitelistedQueryParams = []) => {
|
||||
const redirectUrlObj = url.parse(authUrl, true);
|
||||
const redirectUrl = new URL(authUrl);
|
||||
|
||||
Object.assign(
|
||||
redirectUrlObj.query,
|
||||
whitelistedQueryParams.reduce((prev, param) =>
|
||||
params.query[param] ? { ...prev, param: params.query[param] } : prev,
|
||||
{}
|
||||
),
|
||||
{
|
||||
oauth_token: oauthBinding.requestToken,
|
||||
whitelistedQueryParams.forEach(param => {
|
||||
if (params.query && params.query[param]) {
|
||||
redirectUrl.searchParams.set(param, params.query[param]);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Clear the `search` so it is rebuilt by Node's `url` from the `query` above.
|
||||
// Using previous versions of the Node `url` module, this was just set to ""
|
||||
// However, Node 6 docs seem to indicate that this should be `undefined`.
|
||||
delete redirectUrlObj.search;
|
||||
redirectUrl.searchParams.set('oauth_token', oauthBinding.requestToken);
|
||||
|
||||
// Reconstruct the URL back with provided query parameters merged with oauth_token
|
||||
return url.format(redirectUrlObj);
|
||||
return redirectUrl.toString();
|
||||
};
|
||||
|
||||
// connect middleware
|
||||
|
||||
@@ -169,6 +169,38 @@ Tinytest.add("oauth1 - headers are encoded correctly", test => {
|
||||
);
|
||||
});
|
||||
|
||||
Tinytest.add("oauth1 - _queryParamsWithAuthTokenUrl adds oauth_token to URL", test => {
|
||||
const mockBinding = { requestToken: 'my-token' };
|
||||
const result = OAuth._queryParamsWithAuthTokenUrl(
|
||||
'https://provider.com/oauth?existing=1', mockBinding, {}, []
|
||||
);
|
||||
const parsed = new URL(result);
|
||||
test.equal(parsed.searchParams.get('oauth_token'), 'my-token');
|
||||
test.equal(parsed.searchParams.get('existing'), '1');
|
||||
});
|
||||
|
||||
Tinytest.add("oauth1 - _queryParamsWithAuthTokenUrl merges whitelisted query params", test => {
|
||||
const mockBinding = { requestToken: 'tok' };
|
||||
const result = OAuth._queryParamsWithAuthTokenUrl(
|
||||
'https://provider.com/oauth', mockBinding,
|
||||
{ query: { screen_name: 'user1', secret: 'x' } },
|
||||
['screen_name']
|
||||
);
|
||||
const parsed = new URL(result);
|
||||
test.equal(parsed.searchParams.get('screen_name'), 'user1');
|
||||
test.isNull(parsed.searchParams.get('secret'));
|
||||
});
|
||||
|
||||
Tinytest.add("oauth1 - _queryParamsWithAuthTokenUrl excludes non-whitelisted params", test => {
|
||||
const mockBinding = { requestToken: 'tok' };
|
||||
const result = OAuth._queryParamsWithAuthTokenUrl(
|
||||
'https://provider.com/oauth', mockBinding,
|
||||
{ query: { not_allowed: 'val' } }, []
|
||||
);
|
||||
const parsed = new URL(result);
|
||||
test.isNull(parsed.searchParams.get('not_allowed'));
|
||||
});
|
||||
|
||||
Tinytest.add("oauth1 - auth header string is built correctly", test => {
|
||||
const binding = new OAuth1Binding();
|
||||
const headers = {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
var url_util = require('url');
|
||||
var common = require("./url_common.js");
|
||||
var { URL } = Npm.require('url');
|
||||
|
||||
exports._constructUrl = function (url, query, params) {
|
||||
var url_parts = url_util.parse(url);
|
||||
exports._constructUrl = function (urlString, query, params) {
|
||||
var url_parts = new URL(urlString);
|
||||
return common.buildUrl(
|
||||
url_parts.protocol + "//" + url_parts.host + url_parts.pathname,
|
||||
url_parts.search,
|
||||
|
||||
@@ -3,7 +3,6 @@ import { readFileSync, chmodSync, chownSync } from 'fs';
|
||||
import { createServer } from 'http';
|
||||
import { userInfo } from 'os';
|
||||
import { join as pathJoin, dirname as pathDirname } from 'path';
|
||||
import { parse as parseUrl } from 'url';
|
||||
import { createHash } from 'crypto';
|
||||
import express from 'express';
|
||||
import compress from 'compression';
|
||||
@@ -162,7 +161,7 @@ WebApp.categorizeRequest = function(req) {
|
||||
modern,
|
||||
path,
|
||||
arch: WebApp.defaultArch,
|
||||
url: parseUrl(req.url, true),
|
||||
url: { query: Object.fromEntries(new URL(req.url, 'http://localhost').searchParams) },
|
||||
dynamicHead: req.dynamicHead,
|
||||
dynamicBody: req.dynamicBody,
|
||||
headers: req.headers,
|
||||
@@ -810,7 +809,7 @@ async function runWebAppServer() {
|
||||
var syncQueue = new Meteor._AsynchronousQueue();
|
||||
|
||||
var getItemPathname = function(itemUrl) {
|
||||
return decodeURIComponent(parseUrl(itemUrl).pathname);
|
||||
return decodeURIComponent(new URL(itemUrl, 'http://localhost').pathname);
|
||||
};
|
||||
|
||||
WebAppInternals.reloadClientPrograms = async function() {
|
||||
@@ -1098,7 +1097,7 @@ async function runWebAppServer() {
|
||||
// Strip off the path prefix, if it exists.
|
||||
app.use(function(request, response, next) {
|
||||
const pathPrefix = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX;
|
||||
const { pathname, search } = parseUrl(request.url);
|
||||
const { pathname, search } = new URL(request.url, 'http://localhost');
|
||||
|
||||
// check if the path in the url starts with the path prefix
|
||||
if (pathPrefix) {
|
||||
|
||||
3
tools/cordova/builder.js
vendored
3
tools/cordova/builder.js
vendored
@@ -1,5 +1,4 @@
|
||||
import _ from 'underscore';
|
||||
import url from 'url';
|
||||
import { Console } from '../console/console.js';
|
||||
import buildmessage from '../utils/buildmessage.js';
|
||||
import files from '../fs/files';
|
||||
@@ -548,7 +547,7 @@ export class CordovaBuilder {
|
||||
|
||||
const mobileServerUrl = this.options.mobileServerUrl;
|
||||
|
||||
const parsedUrl = url.parse(mobileServerUrl);
|
||||
const parsedUrl = new URL(mobileServerUrl);
|
||||
|
||||
const runtimeConfig = {
|
||||
meteorRelease: meteorRelease,
|
||||
|
||||
@@ -5,7 +5,6 @@ var config = require('./config.js');
|
||||
var httpHelpers = require('../utils/http-helpers.js');
|
||||
var fiberHelpers = require('../utils/fiber-helpers.js');
|
||||
var querystring = require('querystring');
|
||||
var url = require('url');
|
||||
var Console = require('../console/console.js').Console;
|
||||
|
||||
var auth = exports;
|
||||
@@ -377,8 +376,8 @@ var sendAuthorizeRequest = async function (clientId, redirectUri, state) {
|
||||
throw new Error('access-denied');
|
||||
}
|
||||
|
||||
if (url.parse(response.headers.location).hostname !==
|
||||
url.parse(redirectUri).hostname) {
|
||||
if (new URL(response.headers.location).hostname !==
|
||||
new URL(redirectUri).hostname) {
|
||||
// If we didn't get an immediate redirect to the redirectUri then
|
||||
// presumably the oauth server is trying to interact with us (make
|
||||
// us log in, authorize the client, or something like that). We're
|
||||
|
||||
@@ -50,7 +50,8 @@ export const normalizeModernConfig = (r = false) => Object.fromEntries(
|
||||
* @returns {Object} - The initialized Meteor configuration object.
|
||||
*/
|
||||
export function initMeteorConfig(appDir = process.cwd()) {
|
||||
const modernForced = JSON.parse(process.env.METEOR_MODERN || "false");
|
||||
const rawModern = process.env.METEOR_MODERN;
|
||||
const modernForced = JSON.parse(rawModern && rawModern !== 'undefined' ? rawModern : 'false');
|
||||
let packageJson;
|
||||
if (appDir) {
|
||||
const packageJsonPath = files.pathJoin(appDir, 'package.json');
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
var _ = require('underscore');
|
||||
var semver = require('semver');
|
||||
var os = require('os');
|
||||
var url = require('url');
|
||||
|
||||
var archinfo = require('./archinfo');
|
||||
var buildmessage = require('./buildmessage.js');
|
||||
var files = require('../fs/files');
|
||||
@@ -33,19 +31,31 @@ exports.parseUrl = function (str, defaults) {
|
||||
protocol: defaultProtocol };
|
||||
}
|
||||
|
||||
// Capture any IPv6 address in brackets before new URL() normalizes it
|
||||
// (e.g. "0000:...0001" gets compressed to "::1" by the WHATWG parser).
|
||||
var ipv6Match = str.match(/\[([^\]]+)\]/);
|
||||
var rawIPv6 = ipv6Match ? ipv6Match[1] : null;
|
||||
|
||||
var hasScheme = exports.hasScheme(str);
|
||||
if (! hasScheme) {
|
||||
str = "http://" + str;
|
||||
}
|
||||
|
||||
var parsed = url.parse(str);
|
||||
var parsed = new URL(str);
|
||||
|
||||
// for consistency remove colon at the end of protocol
|
||||
parsed.protocol = parsed.protocol.replace(/\:$/, '');
|
||||
var parsedProtocol = parsed.protocol.replace(/\:$/, '');
|
||||
|
||||
// WHATWG URL wraps IPv6 in brackets and normalizes the address; use the
|
||||
// raw value extracted above to preserve the original formatting.
|
||||
var hostname = parsed.hostname || defaultHostname;
|
||||
if (hostname && hostname.startsWith('[') && hostname.endsWith(']')) {
|
||||
hostname = rawIPv6 || hostname.slice(1, -1);
|
||||
}
|
||||
|
||||
var ret = {
|
||||
protocol: hasScheme ? parsed.protocol : defaultProtocol,
|
||||
hostname: parsed.hostname || defaultHostname,
|
||||
protocol: hasScheme ? parsedProtocol : defaultProtocol,
|
||||
hostname: hostname,
|
||||
port: parsed.port || defaultPort
|
||||
};
|
||||
if (parsed.pathname !== '/' && parsed.pathname) {
|
||||
@@ -57,12 +67,12 @@ exports.parseUrl = function (str, defaults) {
|
||||
// 'options' is an object with 'hostname', 'port', and 'protocol' keys, such as
|
||||
// the return value of parseUrl.
|
||||
exports.formatUrl = function (options) {
|
||||
// For consistency with `Meteor.absoluteUrl`, add a trailing slash to make
|
||||
// this a valid URL
|
||||
if (!options.pathname)
|
||||
options.pathname = "/";
|
||||
|
||||
return url.format(options);
|
||||
const u = new URL('http://placeholder');
|
||||
u.protocol = options.protocol + ':';
|
||||
u.hostname = options.hostname;
|
||||
if (options.port) u.port = options.port;
|
||||
u.pathname = options.pathname || '/';
|
||||
return u.toString();
|
||||
};
|
||||
|
||||
exports.ipAddress = function () {
|
||||
|
||||
@@ -45,6 +45,7 @@ describe('parseUrl', () => {
|
||||
['localhost', {}, { hostname: 'localhost' }],
|
||||
['localhost:3000', {}, { hostname: 'localhost', port: '3000', protocol: undefined }],
|
||||
['https://ex.com:8080/path', {}, { protocol: 'https', hostname: 'ex.com', port: '8080', pathname: '/path' }],
|
||||
['http://ex.com/path?q=1', {}, { protocol: 'http', hostname: 'ex.com', pathname: '/path' }],
|
||||
['ex.com:3000', { protocol: 'https' }, { protocol: 'https', hostname: 'ex.com', port: '3000' }],
|
||||
['http://ex.com', { protocol: 'https' }, { protocol: 'http', hostname: 'ex.com' }],
|
||||
['http://ex.com', { port: '9999' }, { protocol: 'http', hostname: 'ex.com', port: '9999' }],
|
||||
@@ -58,6 +59,21 @@ describe('parseUrl', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatUrl', () => {
|
||||
test('constructs URL from hostname, protocol and port', () => {
|
||||
expect(utils.formatUrl({ hostname: 'example.com', protocol: 'https', port: '8080' }))
|
||||
.toBe('https://example.com:8080/');
|
||||
});
|
||||
test('includes pathname when provided', () => {
|
||||
expect(utils.formatUrl({ hostname: 'example.com', protocol: 'http', pathname: '/app' }))
|
||||
.toBe('http://example.com/app');
|
||||
});
|
||||
test('defaults to root path when no pathname given', () => {
|
||||
expect(utils.formatUrl({ hostname: 'h.com', protocol: 'http' }))
|
||||
.toBe('http://h.com/');
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasScheme', () => {
|
||||
test.each([
|
||||
['http://x', true], ['https://x', true], ['git+ssh://x', true],
|
||||
|
||||
Reference in New Issue
Block a user