url: update WHATWG URL API to latest spec
- Update to spec - Add opaque hosts - File state did not correctly deal with lack of base URL - Cleanup API for file and non-special URLs - Allow % and IPv6 addresses in non-special URL hosts - Use specific names for percent-encode sets - Add empty host concept for file and non-special URLs - Clarify IPv6 serializer - Fix existing mistakes - Add missing ':' to forbidden host code point list. - Correct IPv4 parser empty label behavior - Maintain type equivalence in URLContext with spec - scheme, username, and password should always be strings - host, port, query, and fragment may be strings or null - Align scheme state more closely with the spec - Make sure the `special` variable is always synced with URL_FLAG_SPECIAL. PR-URL: https://github.com/nodejs/node/pull/12523 Fixes: https://github.com/nodejs/node/issues/10608 Fixes: https://github.com/nodejs/node/issues/10634 Refs: https://github.com/whatwg/url/pull/185 Refs: https://github.com/whatwg/url/pull/225 Refs: https://github.com/whatwg/url/pull/224 Refs: https://github.com/whatwg/url/pull/218 Refs: https://github.com/whatwg/url/pull/243 Refs: https://github.com/whatwg/url/pull/260 Refs: https://github.com/whatwg/url/pull/268 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Daijiro Wachi <daijiro.wachi@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
This commit is contained in:
parent
75bfdad037
commit
b2870a4f8c
@ -1053,23 +1053,25 @@ located within the structure of the URL. The WHATWG URL Standard uses a more
|
||||
selective and fine grained approach to selecting encoded characters than that
|
||||
used by the older [`url.parse()`][] and [`url.format()`][] methods.
|
||||
|
||||
The WHATWG algorithm defines three "encoding sets" that describe ranges of
|
||||
characters that must be percent-encoded:
|
||||
The WHATWG algorithm defines three "percent-encode sets" that describe ranges
|
||||
of characters that must be percent-encoded:
|
||||
|
||||
* The *simple encode set* includes code points in range U+0000 to U+001F
|
||||
(inclusive) and all code points greater than U+007E.
|
||||
* The *C0 control percent-encode set* includes code points in range U+0000 to
|
||||
U+001F (inclusive) and all code points greater than U+007E.
|
||||
|
||||
* The *default encode set* includes the *simple encode set* and code points
|
||||
U+0020, U+0022, U+0023, U+003C, U+003E, U+003F, U+0060, U+007B, and U+007D.
|
||||
* The *path percent-encode set* includes the *C0 control percent-encode set*
|
||||
and code points U+0020, U+0022, U+0023, U+003C, U+003E, U+003F, U+0060,
|
||||
U+007B, and U+007D.
|
||||
|
||||
* The *userinfo encode set* includes the *default encode set* and code points
|
||||
U+002F, U+003A, U+003B, U+003D, U+0040, U+005B, U+005C, U+005D, U+005E, and
|
||||
U+007C.
|
||||
* The *userinfo encode set* includes the *path percent-encode set* and code
|
||||
points U+002F, U+003A, U+003B, U+003D, U+0040, U+005B, U+005C, U+005D,
|
||||
U+005E, and U+007C.
|
||||
|
||||
The *simple encode set* is used primary for URL fragments and certain specific
|
||||
conditions for the path. The *userinfo encode set* is used specifically for
|
||||
username and passwords encoded within the URL. The *default encode set* is used
|
||||
for all other cases.
|
||||
The *userinfo percent-encode set* is used exclusively for username and
|
||||
passwords encoded within the URL. The *path percent-encode set* is used for the
|
||||
path of most URLs. The *C0 control percent-encode set* is used for all
|
||||
other cases, including URL fragments in particular, but also host and path
|
||||
under certain specific conditions.
|
||||
|
||||
When non-ASCII characters appear within a hostname, the hostname is encoded
|
||||
using the [Punycode][] algorithm. Note, however, that a hostname *may* contain
|
||||
|
@ -8,6 +8,8 @@ const {
|
||||
const binding = process.binding('url');
|
||||
const context = Symbol('context');
|
||||
const cannotBeBase = Symbol('cannot-be-base');
|
||||
const cannotHaveUsernamePasswordPort =
|
||||
Symbol('cannot-have-username-password-port');
|
||||
const special = Symbol('special');
|
||||
const searchParams = Symbol('query');
|
||||
const querystring = require('querystring');
|
||||
@ -42,7 +44,7 @@ const kOpaqueOrigin = 'null';
|
||||
// - https://html.spec.whatwg.org/multipage/browsers.html#ascii-serialisation-of-an-origin
|
||||
function serializeTupleOrigin(scheme, host, port, unicode = true) {
|
||||
const unicodeHost = unicode ? domainToUnicode(host) : host;
|
||||
return `${scheme}//${unicodeHost}${port == null ? '' : `:${port}`}`;
|
||||
return `${scheme}//${unicodeHost}${port === null ? '' : `:${port}`}`;
|
||||
}
|
||||
|
||||
// This class provides the internal state of a URL object. An instance of this
|
||||
@ -54,14 +56,14 @@ function serializeTupleOrigin(scheme, host, port, unicode = true) {
|
||||
class URLContext {
|
||||
constructor() {
|
||||
this.flags = 0;
|
||||
this.scheme = undefined;
|
||||
this.username = undefined;
|
||||
this.password = undefined;
|
||||
this.host = undefined;
|
||||
this.port = undefined;
|
||||
this.scheme = ':';
|
||||
this.username = '';
|
||||
this.password = '';
|
||||
this.host = null;
|
||||
this.port = null;
|
||||
this.path = [];
|
||||
this.query = undefined;
|
||||
this.fragment = undefined;
|
||||
this.query = null;
|
||||
this.fragment = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,10 +72,10 @@ function onParseComplete(flags, protocol, username, password,
|
||||
var ctx = this[context];
|
||||
ctx.flags = flags;
|
||||
ctx.scheme = protocol;
|
||||
ctx.username = username;
|
||||
ctx.password = password;
|
||||
ctx.username = (flags & binding.URL_FLAGS_HAS_USERNAME) !== 0 ? username : '';
|
||||
ctx.password = (flags & binding.URL_FLAGS_HAS_PASSWORD) !== 0 ? password : '';
|
||||
ctx.port = port;
|
||||
ctx.path = path;
|
||||
ctx.path = (flags & binding.URL_FLAGS_HAS_PATH) !== 0 ? path : [];
|
||||
ctx.query = query;
|
||||
ctx.fragment = fragment;
|
||||
ctx.host = host;
|
||||
@ -101,52 +103,37 @@ function parse(url, input, base) {
|
||||
|
||||
function onParseProtocolComplete(flags, protocol, username, password,
|
||||
host, port, path, query, fragment) {
|
||||
const newIsSpecial = (flags & binding.URL_FLAGS_SPECIAL) !== 0;
|
||||
const s = this[special];
|
||||
const ctx = this[context];
|
||||
if ((s && !newIsSpecial) || (!s && newIsSpecial)) {
|
||||
return;
|
||||
}
|
||||
if (protocol === 'file:' &&
|
||||
(ctx.username || ctx.password || ctx.port !== undefined)) {
|
||||
return;
|
||||
}
|
||||
if (ctx.scheme === 'file:' && !ctx.host) {
|
||||
return;
|
||||
}
|
||||
if (newIsSpecial) {
|
||||
if ((flags & binding.URL_FLAGS_SPECIAL) !== 0) {
|
||||
ctx.flags |= binding.URL_FLAGS_SPECIAL;
|
||||
} else {
|
||||
ctx.flags &= ~binding.URL_FLAGS_SPECIAL;
|
||||
}
|
||||
if (protocol) {
|
||||
ctx.scheme = protocol;
|
||||
ctx.flags |= binding.URL_FLAGS_HAS_SCHEME;
|
||||
} else {
|
||||
ctx.flags &= ~binding.URL_FLAGS_HAS_SCHEME;
|
||||
}
|
||||
ctx.scheme = protocol;
|
||||
}
|
||||
|
||||
function onParseHostComplete(flags, protocol, username, password,
|
||||
host, port, path, query, fragment) {
|
||||
const ctx = this[context];
|
||||
if (host) {
|
||||
if ((flags & binding.URL_FLAGS_HAS_HOST) !== 0) {
|
||||
ctx.host = host;
|
||||
ctx.flags |= binding.URL_FLAGS_HAS_HOST;
|
||||
} else {
|
||||
ctx.host = null;
|
||||
ctx.flags &= ~binding.URL_FLAGS_HAS_HOST;
|
||||
}
|
||||
if (port !== undefined)
|
||||
if (port !== null)
|
||||
ctx.port = port;
|
||||
}
|
||||
|
||||
function onParseHostnameComplete(flags, protocol, username, password,
|
||||
host, port, path, query, fragment) {
|
||||
const ctx = this[context];
|
||||
if (host) {
|
||||
if ((flags & binding.URL_FLAGS_HAS_HOST) !== 0) {
|
||||
ctx.host = host;
|
||||
ctx.flags |= binding.URL_FLAGS_HAS_HOST;
|
||||
} else {
|
||||
ctx.host = null;
|
||||
ctx.flags &= ~binding.URL_FLAGS_HAS_HOST;
|
||||
}
|
||||
}
|
||||
@ -159,29 +146,29 @@ function onParsePortComplete(flags, protocol, username, password,
|
||||
function onParsePathComplete(flags, protocol, username, password,
|
||||
host, port, path, query, fragment) {
|
||||
const ctx = this[context];
|
||||
if (path) {
|
||||
if ((flags & binding.URL_FLAGS_HAS_PATH) !== 0) {
|
||||
ctx.path = path;
|
||||
ctx.flags |= binding.URL_FLAGS_HAS_PATH;
|
||||
} else {
|
||||
ctx.path = [];
|
||||
ctx.flags &= ~binding.URL_FLAGS_HAS_PATH;
|
||||
}
|
||||
|
||||
// The C++ binding may set host to empty string.
|
||||
if ((flags & binding.URL_FLAGS_HAS_HOST) !== 0) {
|
||||
ctx.host = host;
|
||||
ctx.flags |= binding.URL_FLAGS_HAS_HOST;
|
||||
}
|
||||
}
|
||||
|
||||
function onParseSearchComplete(flags, protocol, username, password,
|
||||
host, port, path, query, fragment) {
|
||||
const ctx = this[context];
|
||||
ctx.query = query;
|
||||
this[context].query = query;
|
||||
}
|
||||
|
||||
function onParseHashComplete(flags, protocol, username, password,
|
||||
host, port, path, query, fragment) {
|
||||
const ctx = this[context];
|
||||
if (fragment) {
|
||||
ctx.fragment = fragment;
|
||||
ctx.flags |= binding.URL_FLAGS_HAS_FRAGMENT;
|
||||
} else {
|
||||
ctx.flags &= ~binding.URL_FLAGS_HAS_FRAGMENT;
|
||||
}
|
||||
this[context].fragment = fragment;
|
||||
}
|
||||
|
||||
function getEligibleConstructor(obj) {
|
||||
@ -214,6 +201,14 @@ class URL {
|
||||
return (this[context].flags & binding.URL_FLAGS_CANNOT_BE_BASE) !== 0;
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#cannot-have-a-username-password-port
|
||||
get [cannotHaveUsernamePasswordPort]() {
|
||||
const { host, scheme } = this[context];
|
||||
return ((host == null || host === '') ||
|
||||
this[cannotBeBase] ||
|
||||
scheme === 'file:');
|
||||
}
|
||||
|
||||
[util.inspect.custom](depth, opts) {
|
||||
if (this == null ||
|
||||
Object.getPrototypeOf(this[context]) !== URLContext.prototype) {
|
||||
@ -235,7 +230,7 @@ class URL {
|
||||
obj.origin = this.origin;
|
||||
obj.protocol = this.protocol;
|
||||
obj.username = this.username;
|
||||
obj.password = (opts.showHidden || ctx.password == null) ?
|
||||
obj.password = (opts.showHidden || ctx.password === '') ?
|
||||
this.password : '--------';
|
||||
obj.host = this.host;
|
||||
obj.hostname = this.hostname;
|
||||
@ -270,14 +265,11 @@ Object.defineProperties(URL.prototype, {
|
||||
auth: true
|
||||
}, options);
|
||||
const ctx = this[context];
|
||||
var ret;
|
||||
if (this.protocol)
|
||||
ret = this.protocol;
|
||||
if (ctx.host !== undefined) {
|
||||
var ret = ctx.scheme;
|
||||
if (ctx.host !== null) {
|
||||
ret += '//';
|
||||
const has_username = typeof ctx.username === 'string';
|
||||
const has_password = typeof ctx.password === 'string' &&
|
||||
ctx.password !== '';
|
||||
const has_username = ctx.username !== '';
|
||||
const has_password = ctx.password !== '';
|
||||
if (options.auth && (has_username || has_password)) {
|
||||
if (has_username)
|
||||
ret += ctx.username;
|
||||
@ -292,9 +284,9 @@ Object.defineProperties(URL.prototype, {
|
||||
}
|
||||
if (this.pathname)
|
||||
ret += this.pathname;
|
||||
if (options.search && typeof ctx.query === 'string')
|
||||
if (options.search && ctx.query !== null)
|
||||
ret += `?${ctx.query}`;
|
||||
if (options.fragment && typeof ctx.fragment === 'string')
|
||||
if (options.fragment && ctx.fragment !== null)
|
||||
ret += `#${ctx.fragment}`;
|
||||
return ret;
|
||||
}
|
||||
@ -363,7 +355,12 @@ Object.defineProperties(URL.prototype, {
|
||||
scheme = `${scheme}`;
|
||||
if (scheme.length === 0)
|
||||
return;
|
||||
binding.parse(scheme, binding.kSchemeStart, null, this[context],
|
||||
const ctx = this[context];
|
||||
if (ctx.scheme === 'file:' &&
|
||||
(ctx.host === '' || ctx.host === null)) {
|
||||
return;
|
||||
}
|
||||
binding.parse(scheme, binding.kSchemeStart, null, ctx,
|
||||
onParseProtocolComplete.bind(this));
|
||||
}
|
||||
},
|
||||
@ -371,16 +368,16 @@ Object.defineProperties(URL.prototype, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get() {
|
||||
return this[context].username || '';
|
||||
return this[context].username;
|
||||
},
|
||||
set(username) {
|
||||
// toUSVString is not needed.
|
||||
username = `${username}`;
|
||||
if (!this.hostname)
|
||||
if (this[cannotHaveUsernamePasswordPort])
|
||||
return;
|
||||
const ctx = this[context];
|
||||
if (!username) {
|
||||
ctx.username = null;
|
||||
if (username === '') {
|
||||
ctx.username = '';
|
||||
ctx.flags &= ~binding.URL_FLAGS_HAS_USERNAME;
|
||||
return;
|
||||
}
|
||||
@ -392,16 +389,16 @@ Object.defineProperties(URL.prototype, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get() {
|
||||
return this[context].password || '';
|
||||
return this[context].password;
|
||||
},
|
||||
set(password) {
|
||||
// toUSVString is not needed.
|
||||
password = `${password}`;
|
||||
if (!this.hostname)
|
||||
if (this[cannotHaveUsernamePasswordPort])
|
||||
return;
|
||||
const ctx = this[context];
|
||||
if (!password) {
|
||||
ctx.password = null;
|
||||
if (password === '') {
|
||||
ctx.password = '';
|
||||
ctx.flags &= ~binding.URL_FLAGS_HAS_PASSWORD;
|
||||
return;
|
||||
}
|
||||
@ -415,7 +412,7 @@ Object.defineProperties(URL.prototype, {
|
||||
get() {
|
||||
const ctx = this[context];
|
||||
var ret = ctx.host || '';
|
||||
if (ctx.port !== undefined)
|
||||
if (ctx.port !== null)
|
||||
ret += `:${ctx.port}`;
|
||||
return ret;
|
||||
},
|
||||
@ -423,15 +420,8 @@ Object.defineProperties(URL.prototype, {
|
||||
const ctx = this[context];
|
||||
// toUSVString is not needed.
|
||||
host = `${host}`;
|
||||
if (this[cannotBeBase] ||
|
||||
(this[special] && host.length === 0)) {
|
||||
// Cannot set the host if cannot-be-base is set or
|
||||
// scheme is special and host length is zero
|
||||
return;
|
||||
}
|
||||
if (!host) {
|
||||
ctx.host = null;
|
||||
ctx.flags &= ~binding.URL_FLAGS_HAS_HOST;
|
||||
if (this[cannotBeBase]) {
|
||||
// Cannot set the host if cannot-be-base is set
|
||||
return;
|
||||
}
|
||||
binding.parse(host, binding.kHost, null, ctx,
|
||||
@ -448,15 +438,8 @@ Object.defineProperties(URL.prototype, {
|
||||
const ctx = this[context];
|
||||
// toUSVString is not needed.
|
||||
host = `${host}`;
|
||||
if (this[cannotBeBase] ||
|
||||
(this[special] && host.length === 0)) {
|
||||
// Cannot set the host if cannot-be-base is set or
|
||||
// scheme is special and host length is zero
|
||||
return;
|
||||
}
|
||||
if (!host) {
|
||||
ctx.host = null;
|
||||
ctx.flags &= ~binding.URL_FLAGS_HAS_HOST;
|
||||
if (this[cannotBeBase]) {
|
||||
// Cannot set the host if cannot-be-base is set
|
||||
return;
|
||||
}
|
||||
binding.parse(host, binding.kHostname, null, ctx,
|
||||
@ -468,17 +451,16 @@ Object.defineProperties(URL.prototype, {
|
||||
configurable: true,
|
||||
get() {
|
||||
const port = this[context].port;
|
||||
return port === undefined ? '' : String(port);
|
||||
return port === null ? '' : String(port);
|
||||
},
|
||||
set(port) {
|
||||
// toUSVString is not needed.
|
||||
port = `${port}`;
|
||||
const ctx = this[context];
|
||||
if (!ctx.host || this[cannotBeBase] ||
|
||||
this.protocol === 'file:')
|
||||
if (this[cannotHaveUsernamePasswordPort])
|
||||
return;
|
||||
const ctx = this[context];
|
||||
if (port === '') {
|
||||
ctx.port = undefined;
|
||||
ctx.port = null;
|
||||
return;
|
||||
}
|
||||
binding.parse(port, binding.kPort, null, ctx,
|
||||
@ -492,7 +474,9 @@ Object.defineProperties(URL.prototype, {
|
||||
const ctx = this[context];
|
||||
if (this[cannotBeBase])
|
||||
return ctx.path[0];
|
||||
return ctx.path !== undefined ? `/${ctx.path.join('/')}` : '';
|
||||
if (ctx.path.length === 0)
|
||||
return '';
|
||||
return `/${ctx.path.join('/')}`;
|
||||
},
|
||||
set(path) {
|
||||
// toUSVString is not needed.
|
||||
@ -507,13 +491,15 @@ Object.defineProperties(URL.prototype, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get() {
|
||||
const ctx = this[context];
|
||||
return !ctx.query ? '' : `?${ctx.query}`;
|
||||
const { query } = this[context];
|
||||
if (query === null || query === '')
|
||||
return '';
|
||||
return `?${query}`;
|
||||
},
|
||||
set(search) {
|
||||
const ctx = this[context];
|
||||
search = toUSVString(search);
|
||||
if (!search) {
|
||||
if (search === '') {
|
||||
ctx.query = null;
|
||||
ctx.flags &= ~binding.URL_FLAGS_HAS_QUERY;
|
||||
} else {
|
||||
@ -539,8 +525,10 @@ Object.defineProperties(URL.prototype, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get() {
|
||||
const ctx = this[context];
|
||||
return !ctx.fragment ? '' : `#${ctx.fragment}`;
|
||||
const { fragment } = this[context];
|
||||
if (fragment === null || fragment === '')
|
||||
return '';
|
||||
return `#${fragment}`;
|
||||
},
|
||||
set(hash) {
|
||||
const ctx = this[context];
|
||||
@ -553,6 +541,7 @@ Object.defineProperties(URL.prototype, {
|
||||
}
|
||||
if (hash[0] === '#') hash = hash.slice(1);
|
||||
ctx.fragment = '';
|
||||
ctx.flags |= binding.URL_FLAGS_HAS_FRAGMENT;
|
||||
binding.parse(hash, binding.kFragment, null, ctx,
|
||||
onParseHashComplete.bind(this));
|
||||
}
|
||||
@ -1384,10 +1373,10 @@ function constructUrl(flags, protocol, username, password,
|
||||
var ctx = new URLContext();
|
||||
ctx.flags = flags;
|
||||
ctx.scheme = protocol;
|
||||
ctx.username = username;
|
||||
ctx.password = password;
|
||||
ctx.username = (flags & binding.URL_FLAGS_HAS_USERNAME) !== 0 ? username : '';
|
||||
ctx.password = (flags & binding.URL_FLAGS_HAS_PASSWORD) !== 0 ? password : '';
|
||||
ctx.port = port;
|
||||
ctx.path = path;
|
||||
ctx.path = (flags & binding.URL_FLAGS_HAS_PATH) !== 0 ? path : [];
|
||||
ctx.query = query;
|
||||
ctx.fragment = fragment;
|
||||
ctx.host = host;
|
||||
|
301
src/node_url.cc
301
src/node_url.cc
@ -59,10 +59,12 @@ static const char kEOL = -1;
|
||||
// Used in ToUSVString().
|
||||
static const char16_t kUnicodeReplacementCharacter = 0xFFFD;
|
||||
|
||||
// https://url.spec.whatwg.org/#concept-host
|
||||
union url_host_value {
|
||||
std::string domain;
|
||||
uint32_t ipv4;
|
||||
uint16_t ipv6[8];
|
||||
std::string opaque;
|
||||
~url_host_value() {}
|
||||
};
|
||||
|
||||
@ -70,7 +72,8 @@ enum url_host_type {
|
||||
HOST_TYPE_FAILED = -1,
|
||||
HOST_TYPE_DOMAIN = 0,
|
||||
HOST_TYPE_IPV4 = 1,
|
||||
HOST_TYPE_IPV6 = 2
|
||||
HOST_TYPE_IPV6 = 2,
|
||||
HOST_TYPE_OPAQUE = 3,
|
||||
};
|
||||
|
||||
struct url_host {
|
||||
@ -151,6 +154,13 @@ static inline T ASCIILowercase(T ch) {
|
||||
return IsASCIIAlpha(ch) ? (ch | 0x20) : ch;
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#forbidden-host-code-point
|
||||
CHAR_TEST(8, IsForbiddenHostCodePoint,
|
||||
ch == '\0' || ch == '\t' || ch == '\n' || ch == '\r' ||
|
||||
ch == ' ' || ch == '#' || ch == '%' || ch == '/' ||
|
||||
ch == ':' || ch == '?' || ch == '@' || ch == '[' ||
|
||||
ch == '\\' || ch == ']')
|
||||
|
||||
// https://url.spec.whatwg.org/#windows-drive-letter
|
||||
TWO_CHAR_STRING_TEST(8, IsWindowsDriveLetter,
|
||||
(IsASCIIAlpha(ch1) && (ch2 == ':' || ch2 == '|')))
|
||||
@ -206,7 +216,7 @@ static const char* hex[256] = {
|
||||
"%F8", "%F9", "%FA", "%FB", "%FC", "%FD", "%FE", "%FF"
|
||||
};
|
||||
|
||||
static const uint8_t SIMPLE_ENCODE_SET[32] = {
|
||||
static const uint8_t C0_CONTROL_ENCODE_SET[32] = {
|
||||
// 00 01 02 03 04 05 06 07
|
||||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
||||
// 08 09 0A 0B 0C 0D 0E 0F
|
||||
@ -273,7 +283,7 @@ static const uint8_t SIMPLE_ENCODE_SET[32] = {
|
||||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80
|
||||
};
|
||||
|
||||
static const uint8_t DEFAULT_ENCODE_SET[32] = {
|
||||
static const uint8_t PATH_ENCODE_SET[32] = {
|
||||
// 00 01 02 03 04 05 06 07
|
||||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
||||
// 08 09 0A 0B 0C 0D 0E 0F
|
||||
@ -756,8 +766,8 @@ static url_host_type ParseIPv4Host(url_host* host,
|
||||
if (ch == '.' || ch == kEOL) {
|
||||
if (++parts > 4)
|
||||
goto end;
|
||||
if (pointer - mark == 0)
|
||||
break;
|
||||
if (pointer == mark)
|
||||
goto end;
|
||||
int64_t n = ParseNumber(mark, pointer);
|
||||
if (n < 0)
|
||||
goto end;
|
||||
@ -797,9 +807,32 @@ static url_host_type ParseIPv4Host(url_host* host,
|
||||
return type;
|
||||
}
|
||||
|
||||
static url_host_type ParseOpaqueHost(url_host* host,
|
||||
const char* input,
|
||||
size_t length) {
|
||||
url_host_type type = HOST_TYPE_OPAQUE;
|
||||
std::string output;
|
||||
output.reserve(length * 3);
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
const char ch = input[i];
|
||||
if (ch != '%' && IsForbiddenHostCodePoint(ch)) {
|
||||
type = HOST_TYPE_FAILED;
|
||||
goto end;
|
||||
} else {
|
||||
AppendOrEscape(&output, ch, C0_CONTROL_ENCODE_SET);
|
||||
}
|
||||
}
|
||||
|
||||
host->value.opaque = output;
|
||||
end:
|
||||
host->type = type;
|
||||
return type;
|
||||
}
|
||||
|
||||
static url_host_type ParseHost(url_host* host,
|
||||
const char* input,
|
||||
size_t length,
|
||||
bool is_special,
|
||||
bool unicode = false) {
|
||||
url_host_type type = HOST_TYPE_FAILED;
|
||||
const char* pointer = input;
|
||||
@ -814,6 +847,9 @@ static url_host_type ParseHost(url_host* host,
|
||||
return ParseIPv6Host(host, ++pointer, length - 2);
|
||||
}
|
||||
|
||||
if (!is_special)
|
||||
return ParseOpaqueHost(host, input, length);
|
||||
|
||||
// First, we have to percent decode
|
||||
PercentDecode(input, length, &decoded);
|
||||
|
||||
@ -824,10 +860,7 @@ static url_host_type ParseHost(url_host* host,
|
||||
// If any of the following characters are still present, we have to fail
|
||||
for (size_t n = 0; n < decoded.size(); n++) {
|
||||
const char ch = decoded[n];
|
||||
if (ch == 0x00 || ch == 0x09 || ch == 0x0a || ch == 0x0d ||
|
||||
ch == 0x20 || ch == '#' || ch == '%' || ch == '/' ||
|
||||
ch == '?' || ch == '@' || ch == '[' || ch == '\\' ||
|
||||
ch == ']') {
|
||||
if (IsForbiddenHostCodePoint(ch)) {
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
@ -907,14 +940,17 @@ static url_host_type WriteHost(url_host* host, std::string* dest) {
|
||||
uint16_t* start = &host->value.ipv6[0];
|
||||
uint16_t* compress_pointer =
|
||||
FindLongestZeroSequence(start, 8);
|
||||
bool ignore0 = false;
|
||||
for (int n = 0; n <= 7; n++) {
|
||||
uint16_t* piece = &host->value.ipv6[n];
|
||||
if (ignore0 && *piece == 0)
|
||||
continue;
|
||||
else if (ignore0)
|
||||
ignore0 = false;
|
||||
if (compress_pointer == piece) {
|
||||
*dest += n == 0 ? "::" : ":";
|
||||
while (*piece == 0 && ++n < 8)
|
||||
piece = &host->value.ipv6[n];
|
||||
if (n == 8)
|
||||
break;
|
||||
ignore0 = true;
|
||||
continue;
|
||||
}
|
||||
char buf[5];
|
||||
char* buffer = buf;
|
||||
@ -926,6 +962,9 @@ static url_host_type WriteHost(url_host* host, std::string* dest) {
|
||||
*dest += ']';
|
||||
break;
|
||||
}
|
||||
case HOST_TYPE_OPAQUE:
|
||||
*dest = host->value.opaque;
|
||||
break;
|
||||
case HOST_TYPE_FAILED:
|
||||
break;
|
||||
}
|
||||
@ -934,11 +973,14 @@ static url_host_type WriteHost(url_host* host, std::string* dest) {
|
||||
|
||||
static bool ParseHost(std::string* input,
|
||||
std::string* output,
|
||||
bool is_special,
|
||||
bool unicode = false) {
|
||||
if (input->length() == 0)
|
||||
if (input->length() == 0) {
|
||||
output->clear();
|
||||
return true;
|
||||
}
|
||||
url_host host{{""}, HOST_TYPE_DOMAIN};
|
||||
ParseHost(&host, input->c_str(), input->length(), unicode);
|
||||
ParseHost(&host, input->c_str(), input->length(), is_special, unicode);
|
||||
if (host.type == HOST_TYPE_FAILED)
|
||||
return false;
|
||||
WriteHost(&host, output);
|
||||
@ -1006,6 +1048,12 @@ static inline void HarvestContext(Environment* env,
|
||||
context->flags |= URL_FLAGS_SPECIAL;
|
||||
if (_flags & URL_FLAGS_CANNOT_BE_BASE)
|
||||
context->flags |= URL_FLAGS_CANNOT_BE_BASE;
|
||||
if (_flags & URL_FLAGS_HAS_USERNAME)
|
||||
context->flags |= URL_FLAGS_HAS_USERNAME;
|
||||
if (_flags & URL_FLAGS_HAS_PASSWORD)
|
||||
context->flags |= URL_FLAGS_HAS_PASSWORD;
|
||||
if (_flags & URL_FLAGS_HAS_HOST)
|
||||
context->flags |= URL_FLAGS_HAS_HOST;
|
||||
}
|
||||
Local<Value> scheme = GET(env, context_obj, "scheme");
|
||||
if (scheme->IsString()) {
|
||||
@ -1015,6 +1063,23 @@ static inline void HarvestContext(Environment* env,
|
||||
Local<Value> port = GET(env, context_obj, "port");
|
||||
if (port->IsInt32())
|
||||
context->port = port->Int32Value(env->context()).FromJust();
|
||||
if (context->flags & URL_FLAGS_HAS_USERNAME) {
|
||||
Local<Value> username = GET(env, context_obj, "username");
|
||||
CHECK(username->IsString());
|
||||
Utf8Value value(env->isolate(), username);
|
||||
context->username.assign(*value, value.length());
|
||||
}
|
||||
if (context->flags & URL_FLAGS_HAS_PASSWORD) {
|
||||
Local<Value> password = GET(env, context_obj, "password");
|
||||
CHECK(password->IsString());
|
||||
Utf8Value value(env->isolate(), password);
|
||||
context->password.assign(*value, value.length());
|
||||
}
|
||||
Local<Value> host = GET(env, context_obj, "host");
|
||||
if (host->IsString()) {
|
||||
Utf8Value value(env->isolate(), host);
|
||||
context->host.assign(*value, value.length());
|
||||
}
|
||||
}
|
||||
|
||||
// Single dot segment can be ".", "%2e", or "%2E"
|
||||
@ -1077,7 +1142,6 @@ void URL::Parse(const char* input,
|
||||
bool atflag = false;
|
||||
bool sbflag = false;
|
||||
bool uflag = false;
|
||||
bool base_is_file = false;
|
||||
int wskip = 0;
|
||||
|
||||
std::string buffer;
|
||||
@ -1137,25 +1201,40 @@ void URL::Parse(const char* input,
|
||||
case kScheme:
|
||||
if (IsASCIIAlphanumeric(ch) || ch == '+' || ch == '-' || ch == '.') {
|
||||
buffer += ASCIILowercase(ch);
|
||||
p++;
|
||||
continue;
|
||||
} else if (ch == ':' || (has_state_override && ch == kEOL)) {
|
||||
if (buffer.size() > 0) {
|
||||
buffer += ':';
|
||||
url->scheme = buffer;
|
||||
} else if (has_state_override) {
|
||||
if (has_state_override && buffer.size() == 0) {
|
||||
url->flags |= URL_FLAGS_TERMINATED;
|
||||
return;
|
||||
}
|
||||
if (IsSpecial(url->scheme)) {
|
||||
buffer += ':';
|
||||
|
||||
bool new_is_special = IsSpecial(buffer);
|
||||
|
||||
if (has_state_override) {
|
||||
if ((special != new_is_special) ||
|
||||
((buffer == "file:") &&
|
||||
((url->flags & URL_FLAGS_HAS_USERNAME) ||
|
||||
(url->flags & URL_FLAGS_HAS_PASSWORD) ||
|
||||
(url->port != -1)))) {
|
||||
url->flags |= URL_FLAGS_TERMINATED;
|
||||
return;
|
||||
}
|
||||
|
||||
// File scheme && (host == empty or null) check left to JS-land
|
||||
// as it can be done before even entering C++ binding.
|
||||
}
|
||||
|
||||
url->scheme = buffer;
|
||||
if (new_is_special) {
|
||||
url->flags |= URL_FLAGS_SPECIAL;
|
||||
special = true;
|
||||
} else {
|
||||
url->flags &= ~URL_FLAGS_SPECIAL;
|
||||
special = false;
|
||||
}
|
||||
buffer.clear();
|
||||
if (has_state_override)
|
||||
return;
|
||||
buffer.clear();
|
||||
if (url->scheme == "file:") {
|
||||
state = kFile;
|
||||
} else if (special &&
|
||||
@ -1195,6 +1274,7 @@ void URL::Parse(const char* input,
|
||||
special = true;
|
||||
} else {
|
||||
url->flags &= ~URL_FLAGS_SPECIAL;
|
||||
special = false;
|
||||
}
|
||||
if (base->flags & URL_FLAGS_HAS_PATH) {
|
||||
url->flags |= URL_FLAGS_HAS_PATH;
|
||||
@ -1246,6 +1326,7 @@ void URL::Parse(const char* input,
|
||||
special = true;
|
||||
} else {
|
||||
url->flags &= ~URL_FLAGS_SPECIAL;
|
||||
special = false;
|
||||
}
|
||||
switch (ch) {
|
||||
case kEOL:
|
||||
@ -1414,6 +1495,10 @@ void URL::Parse(const char* input,
|
||||
ch == '?' ||
|
||||
ch == '#' ||
|
||||
special_back_slash) {
|
||||
if (atflag && buffer.size() == 0) {
|
||||
url->flags |= URL_FLAGS_FAILED;
|
||||
return;
|
||||
}
|
||||
p -= buffer.size() + 1 + wskip;
|
||||
buffer.clear();
|
||||
state = kHost;
|
||||
@ -1423,13 +1508,16 @@ void URL::Parse(const char* input,
|
||||
break;
|
||||
case kHost:
|
||||
case kHostname:
|
||||
if (ch == ':' && !sbflag) {
|
||||
if (special && buffer.size() == 0) {
|
||||
if (has_state_override && url->scheme == "file:") {
|
||||
state = kFileHost;
|
||||
continue;
|
||||
} else if (ch == ':' && !sbflag) {
|
||||
if (buffer.size() == 0) {
|
||||
url->flags |= URL_FLAGS_FAILED;
|
||||
return;
|
||||
}
|
||||
url->flags |= URL_FLAGS_HAS_HOST;
|
||||
if (!ParseHost(&buffer, &url->host)) {
|
||||
if (!ParseHost(&buffer, &url->host, special)) {
|
||||
url->flags |= URL_FLAGS_FAILED;
|
||||
return;
|
||||
}
|
||||
@ -1448,8 +1536,15 @@ void URL::Parse(const char* input,
|
||||
url->flags |= URL_FLAGS_FAILED;
|
||||
return;
|
||||
}
|
||||
if (has_state_override &&
|
||||
buffer.size() == 0 &&
|
||||
((url->username.size() > 0 || url->password.size() > 0) ||
|
||||
url->port != -1)) {
|
||||
url->flags |= URL_FLAGS_TERMINATED;
|
||||
return;
|
||||
}
|
||||
url->flags |= URL_FLAGS_HAS_HOST;
|
||||
if (!ParseHost(&buffer, &url->host)) {
|
||||
if (!ParseHost(&buffer, &url->host, special)) {
|
||||
url->flags |= URL_FLAGS_FAILED;
|
||||
return;
|
||||
}
|
||||
@ -1463,7 +1558,7 @@ void URL::Parse(const char* input,
|
||||
sbflag = true;
|
||||
if (ch == ']')
|
||||
sbflag = false;
|
||||
buffer += ASCIILowercase(ch);
|
||||
buffer += ch;
|
||||
}
|
||||
break;
|
||||
case kPort:
|
||||
@ -1508,12 +1603,12 @@ void URL::Parse(const char* input,
|
||||
}
|
||||
break;
|
||||
case kFile:
|
||||
base_is_file = (
|
||||
has_base &&
|
||||
base->scheme == "file:");
|
||||
switch (ch) {
|
||||
case kEOL:
|
||||
if (base_is_file) {
|
||||
url->scheme = "file:";
|
||||
if (ch == '/' || ch == '\\') {
|
||||
state = kFileSlash;
|
||||
} else if (has_base && base->scheme == "file:") {
|
||||
switch (ch) {
|
||||
case kEOL:
|
||||
if (base->flags & URL_FLAGS_HAS_HOST) {
|
||||
url->flags |= URL_FLAGS_HAS_HOST;
|
||||
url->host = base->host;
|
||||
@ -1527,15 +1622,7 @@ void URL::Parse(const char* input,
|
||||
url->query = base->query;
|
||||
}
|
||||
break;
|
||||
}
|
||||
state = kPath;
|
||||
continue;
|
||||
case '\\':
|
||||
case '/':
|
||||
state = kFileSlash;
|
||||
break;
|
||||
case '?':
|
||||
if (base_is_file) {
|
||||
case '?':
|
||||
if (base->flags & URL_FLAGS_HAS_HOST) {
|
||||
url->flags |= URL_FLAGS_HAS_HOST;
|
||||
url->host = base->host;
|
||||
@ -1545,11 +1632,10 @@ void URL::Parse(const char* input,
|
||||
url->path = base->path;
|
||||
}
|
||||
url->flags |= URL_FLAGS_HAS_QUERY;
|
||||
url->query.clear();
|
||||
state = kQuery;
|
||||
break;
|
||||
}
|
||||
case '#':
|
||||
if (base_is_file) {
|
||||
case '#':
|
||||
if (base->flags & URL_FLAGS_HAS_HOST) {
|
||||
url->flags |= URL_FLAGS_HAS_HOST;
|
||||
url->host = base->host;
|
||||
@ -1562,29 +1648,33 @@ void URL::Parse(const char* input,
|
||||
url->flags |= URL_FLAGS_HAS_QUERY;
|
||||
url->query = base->query;
|
||||
}
|
||||
url->flags |= URL_FLAGS_HAS_FRAGMENT;
|
||||
url->fragment.clear();
|
||||
state = kFragment;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (base_is_file &&
|
||||
(!IsWindowsDriveLetter(ch, p[1]) ||
|
||||
end - p == 1 ||
|
||||
(p[2] != '/' &&
|
||||
p[2] != '\\' &&
|
||||
p[2] != '?' &&
|
||||
p[2] != '#'))) {
|
||||
if (base->flags & URL_FLAGS_HAS_HOST) {
|
||||
url->flags |= URL_FLAGS_HAS_HOST;
|
||||
url->host = base->host;
|
||||
default:
|
||||
if ((!IsWindowsDriveLetter(ch, p[1]) ||
|
||||
end - p == 1 ||
|
||||
(p[2] != '/' &&
|
||||
p[2] != '\\' &&
|
||||
p[2] != '?' &&
|
||||
p[2] != '#'))) {
|
||||
if (base->flags & URL_FLAGS_HAS_HOST) {
|
||||
url->flags |= URL_FLAGS_HAS_HOST;
|
||||
url->host = base->host;
|
||||
}
|
||||
if (base->flags & URL_FLAGS_HAS_PATH) {
|
||||
url->flags |= URL_FLAGS_HAS_PATH;
|
||||
url->path = base->path;
|
||||
}
|
||||
ShortenUrlPath(url);
|
||||
}
|
||||
if (base->flags & URL_FLAGS_HAS_PATH) {
|
||||
url->flags |= URL_FLAGS_HAS_PATH;
|
||||
url->path = base->path;
|
||||
}
|
||||
ShortenUrlPath(url);
|
||||
}
|
||||
state = kPath;
|
||||
continue;
|
||||
state = kPath;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
state = kPath;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case kFileSlash:
|
||||
@ -1597,8 +1687,13 @@ void URL::Parse(const char* input,
|
||||
url->flags |= URL_FLAGS_HAS_PATH;
|
||||
url->path.push_back(base->path[0]);
|
||||
} else {
|
||||
url->flags |= URL_FLAGS_HAS_HOST;
|
||||
url->host = base->host;
|
||||
if (base->flags & URL_FLAGS_HAS_HOST) {
|
||||
url->flags |= URL_FLAGS_HAS_HOST;
|
||||
url->host = base->host;
|
||||
} else {
|
||||
url->flags &= ~URL_FLAGS_HAS_HOST;
|
||||
url->host.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
state = kPath;
|
||||
@ -1611,19 +1706,28 @@ void URL::Parse(const char* input,
|
||||
ch == '\\' ||
|
||||
ch == '?' ||
|
||||
ch == '#') {
|
||||
if (buffer.size() == 2 &&
|
||||
if (!has_state_override &&
|
||||
buffer.size() == 2 &&
|
||||
IsWindowsDriveLetter(buffer)) {
|
||||
state = kPath;
|
||||
} else if (buffer.size() == 0) {
|
||||
url->flags |= URL_FLAGS_HAS_HOST;
|
||||
url->host.clear();
|
||||
if (has_state_override)
|
||||
return;
|
||||
state = kPathStart;
|
||||
} else {
|
||||
if (buffer != "localhost") {
|
||||
url->flags |= URL_FLAGS_HAS_HOST;
|
||||
if (!ParseHost(&buffer, &url->host)) {
|
||||
url->flags |= URL_FLAGS_FAILED;
|
||||
return;
|
||||
}
|
||||
std::string host;
|
||||
if (!ParseHost(&buffer, &host, special)) {
|
||||
url->flags |= URL_FLAGS_FAILED;
|
||||
return;
|
||||
}
|
||||
if (host == "localhost")
|
||||
host.clear();
|
||||
url->flags |= URL_FLAGS_HAS_HOST;
|
||||
url->host = host;
|
||||
if (has_state_override)
|
||||
return;
|
||||
buffer.clear();
|
||||
state = kPathStart;
|
||||
}
|
||||
@ -1664,17 +1768,20 @@ void URL::Parse(const char* input,
|
||||
url->flags |= URL_FLAGS_HAS_PATH;
|
||||
url->path.push_back("");
|
||||
}
|
||||
} else if (IsSingleDotSegment(buffer)) {
|
||||
if (ch != '/' && !special_back_slash) {
|
||||
url->flags |= URL_FLAGS_HAS_PATH;
|
||||
url->path.push_back("");
|
||||
}
|
||||
} else if (IsSingleDotSegment(buffer) &&
|
||||
ch != '/' && !special_back_slash) {
|
||||
url->flags |= URL_FLAGS_HAS_PATH;
|
||||
url->path.push_back("");
|
||||
} else if (!IsSingleDotSegment(buffer)) {
|
||||
if (url->scheme == "file:" &&
|
||||
url->path.empty() &&
|
||||
buffer.size() == 2 &&
|
||||
IsWindowsDriveLetter(buffer)) {
|
||||
url->flags &= ~URL_FLAGS_HAS_HOST;
|
||||
if ((url->flags & URL_FLAGS_HAS_HOST) &&
|
||||
!url->host.empty()) {
|
||||
url->host.clear();
|
||||
url->flags |= URL_FLAGS_HAS_HOST;
|
||||
}
|
||||
buffer[1] = ':';
|
||||
}
|
||||
url->flags |= URL_FLAGS_HAS_PATH;
|
||||
@ -1697,7 +1804,7 @@ void URL::Parse(const char* input,
|
||||
state = kFragment;
|
||||
}
|
||||
} else {
|
||||
AppendOrEscape(&buffer, ch, DEFAULT_ENCODE_SET);
|
||||
AppendOrEscape(&buffer, ch, PATH_ENCODE_SET);
|
||||
}
|
||||
break;
|
||||
case kCannotBeBase:
|
||||
@ -1712,7 +1819,7 @@ void URL::Parse(const char* input,
|
||||
if (url->path.size() == 0)
|
||||
url->path.push_back("");
|
||||
if (url->path.size() > 0 && ch != kEOL)
|
||||
AppendOrEscape(&url->path[0], ch, SIMPLE_ENCODE_SET);
|
||||
AppendOrEscape(&url->path[0], ch, C0_CONTROL_ENCODE_SET);
|
||||
}
|
||||
break;
|
||||
case kQuery:
|
||||
@ -1735,7 +1842,7 @@ void URL::Parse(const char* input,
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
AppendOrEscape(&buffer, ch, SIMPLE_ENCODE_SET);
|
||||
AppendOrEscape(&buffer, ch, C0_CONTROL_ENCODE_SET);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -1800,17 +1907,18 @@ static void Parse(Environment* env,
|
||||
|
||||
// Define the return value placeholders
|
||||
const Local<Value> undef = Undefined(isolate);
|
||||
const Local<Value> null = Null(isolate);
|
||||
if (!(url.flags & URL_FLAGS_FAILED)) {
|
||||
Local<Value> argv[9] = {
|
||||
undef,
|
||||
undef,
|
||||
undef,
|
||||
undef,
|
||||
null, // host defaults to null
|
||||
null, // port defaults to null
|
||||
undef,
|
||||
undef,
|
||||
undef,
|
||||
undef,
|
||||
undef,
|
||||
null, // query defaults to null
|
||||
null, // fragment defaults to null
|
||||
};
|
||||
SetArgs(env, argv, &url);
|
||||
(void)cb->Call(context, recv, arraysize(argv), argv);
|
||||
@ -1914,7 +2022,8 @@ static void DomainToASCII(const FunctionCallbackInfo<Value>& args) {
|
||||
Utf8Value value(env->isolate(), args[0]);
|
||||
|
||||
url_host host{{""}, HOST_TYPE_DOMAIN};
|
||||
ParseHost(&host, *value, value.length());
|
||||
// Assuming the host is used for a special scheme.
|
||||
ParseHost(&host, *value, value.length(), true);
|
||||
if (host.type == HOST_TYPE_FAILED) {
|
||||
args.GetReturnValue().Set(FIXED_ONE_BYTE_STRING(env->isolate(), ""));
|
||||
return;
|
||||
@ -1934,7 +2043,8 @@ static void DomainToUnicode(const FunctionCallbackInfo<Value>& args) {
|
||||
Utf8Value value(env->isolate(), args[0]);
|
||||
|
||||
url_host host{{""}, HOST_TYPE_DOMAIN};
|
||||
ParseHost(&host, *value, value.length(), true);
|
||||
// Assuming the host is used for a special scheme.
|
||||
ParseHost(&host, *value, value.length(), true, true);
|
||||
if (host.type == HOST_TYPE_FAILED) {
|
||||
args.GetReturnValue().Set(FIXED_ONE_BYTE_STRING(env->isolate(), ""));
|
||||
return;
|
||||
@ -1957,6 +2067,7 @@ const Local<Value> URL::ToObject(Environment* env) const {
|
||||
Context::Scope context_scope(context);
|
||||
|
||||
const Local<Value> undef = Undefined(isolate);
|
||||
const Local<Value> null = Null(isolate);
|
||||
|
||||
if (context_.flags & URL_FLAGS_FAILED)
|
||||
return Local<Value>();
|
||||
@ -1966,11 +2077,11 @@ const Local<Value> URL::ToObject(Environment* env) const {
|
||||
undef,
|
||||
undef,
|
||||
undef,
|
||||
null, // host defaults to null
|
||||
null, // port defaults to null
|
||||
undef,
|
||||
undef,
|
||||
undef,
|
||||
undef,
|
||||
undef,
|
||||
null, // query defaults to null
|
||||
null, // fragment defaults to null
|
||||
};
|
||||
SetArgs(env, argv, &context_);
|
||||
|
||||
|
466
test/fixtures/url-setter-tests.js
vendored
466
test/fixtures/url-setter-tests.js
vendored
@ -370,14 +370,14 @@ module.exports =
|
||||
"username": "wario"
|
||||
}
|
||||
},
|
||||
// {
|
||||
// "href": "file://test/",
|
||||
// "new_value": "test",
|
||||
// "expected": {
|
||||
// "href": "file://test/",
|
||||
// "username": ""
|
||||
// }
|
||||
// }
|
||||
{
|
||||
"href": "file://test/",
|
||||
"new_value": "test",
|
||||
"expected": {
|
||||
"href": "file://test/",
|
||||
"username": ""
|
||||
}
|
||||
}
|
||||
],
|
||||
"password": [
|
||||
{
|
||||
@ -473,14 +473,14 @@ module.exports =
|
||||
"password": "bowser"
|
||||
}
|
||||
},
|
||||
// {
|
||||
// "href": "file://test/",
|
||||
// "new_value": "test",
|
||||
// "expected": {
|
||||
// "href": "file://test/",
|
||||
// "password": ""
|
||||
// }
|
||||
// }
|
||||
{
|
||||
"href": "file://test/",
|
||||
"new_value": "test",
|
||||
"expected": {
|
||||
"href": "file://test/",
|
||||
"password": ""
|
||||
}
|
||||
}
|
||||
],
|
||||
"host": [
|
||||
{
|
||||
@ -493,33 +493,33 @@ module.exports =
|
||||
"hostname": "x"
|
||||
}
|
||||
},
|
||||
// {
|
||||
// "href": "sc://x/",
|
||||
// "new_value": "\u0009",
|
||||
// "expected": {
|
||||
// "href": "sc:///",
|
||||
// "host": "",
|
||||
// "hostname": ""
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "href": "sc://x/",
|
||||
// "new_value": "\u000A",
|
||||
// "expected": {
|
||||
// "href": "sc:///",
|
||||
// "host": "",
|
||||
// "hostname": ""
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "href": "sc://x/",
|
||||
// "new_value": "\u000D",
|
||||
// "expected": {
|
||||
// "href": "sc:///",
|
||||
// "host": "",
|
||||
// "hostname": ""
|
||||
// }
|
||||
// },
|
||||
{
|
||||
"href": "sc://x/",
|
||||
"new_value": "\u0009",
|
||||
"expected": {
|
||||
"href": "sc:///",
|
||||
"host": "",
|
||||
"hostname": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "sc://x/",
|
||||
"new_value": "\u000A",
|
||||
"expected": {
|
||||
"href": "sc:///",
|
||||
"host": "",
|
||||
"hostname": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "sc://x/",
|
||||
"new_value": "\u000D",
|
||||
"expected": {
|
||||
"href": "sc:///",
|
||||
"host": "",
|
||||
"hostname": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "sc://x/",
|
||||
"new_value": " ",
|
||||
@ -529,33 +529,33 @@ module.exports =
|
||||
"hostname": "x"
|
||||
}
|
||||
},
|
||||
// {
|
||||
// "href": "sc://x/",
|
||||
// "new_value": "#",
|
||||
// "expected": {
|
||||
// "href": "sc:///",
|
||||
// "host": "",
|
||||
// "hostname": ""
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "href": "sc://x/",
|
||||
// "new_value": "/",
|
||||
// "expected": {
|
||||
// "href": "sc:///",
|
||||
// "host": "",
|
||||
// "hostname": ""
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "href": "sc://x/",
|
||||
// "new_value": "?",
|
||||
// "expected": {
|
||||
// "href": "sc:///",
|
||||
// "host": "",
|
||||
// "hostname": ""
|
||||
// }
|
||||
// },
|
||||
{
|
||||
"href": "sc://x/",
|
||||
"new_value": "#",
|
||||
"expected": {
|
||||
"href": "sc:///",
|
||||
"host": "",
|
||||
"hostname": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "sc://x/",
|
||||
"new_value": "/",
|
||||
"expected": {
|
||||
"href": "sc:///",
|
||||
"host": "",
|
||||
"hostname": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "sc://x/",
|
||||
"new_value": "?",
|
||||
"expected": {
|
||||
"href": "sc:///",
|
||||
"host": "",
|
||||
"hostname": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "sc://x/",
|
||||
"new_value": "@",
|
||||
@ -565,15 +565,15 @@ module.exports =
|
||||
"hostname": "x"
|
||||
}
|
||||
},
|
||||
// {
|
||||
// "href": "sc://x/",
|
||||
// "new_value": "ß",
|
||||
// "expected": {
|
||||
// "href": "sc://%C3%9F/",
|
||||
// "host": "%C3%9F",
|
||||
// "hostname": "%C3%9F"
|
||||
// }
|
||||
// },
|
||||
{
|
||||
"href": "sc://x/",
|
||||
"new_value": "ß",
|
||||
"expected": {
|
||||
"href": "sc://%C3%9F/",
|
||||
"host": "%C3%9F",
|
||||
"hostname": "%C3%9F"
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "IDNA Nontransitional_Processing",
|
||||
"href": "https://x/",
|
||||
@ -916,56 +916,56 @@ module.exports =
|
||||
"hostname": "example.net"
|
||||
}
|
||||
},
|
||||
// {
|
||||
// "href": "file://y/",
|
||||
// "new_value": "x:123",
|
||||
// "expected": {
|
||||
// "href": "file://y/",
|
||||
// "host": "y",
|
||||
// "hostname": "y",
|
||||
// "port": ""
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "href": "file://y/",
|
||||
// "new_value": "loc%41lhost",
|
||||
// "expected": {
|
||||
// "href": "file:///",
|
||||
// "host": "",
|
||||
// "hostname": "",
|
||||
// "port": ""
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "href": "file://hi/x",
|
||||
// "new_value": "",
|
||||
// "expected": {
|
||||
// "href": "file:///x",
|
||||
// "host": "",
|
||||
// "hostname": "",
|
||||
// "port": ""
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "href": "sc://test@test/",
|
||||
// "new_value": "",
|
||||
// "expected": {
|
||||
// "href": "sc://test@test/",
|
||||
// "host": "test",
|
||||
// "hostname": "test",
|
||||
// "username": "test"
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "href": "sc://test:12/",
|
||||
// "new_value": "",
|
||||
// "expected": {
|
||||
// "href": "sc://test:12/",
|
||||
// "host": "test:12",
|
||||
// "hostname": "test",
|
||||
// "port": "12"
|
||||
// }
|
||||
// }
|
||||
{
|
||||
"href": "file://y/",
|
||||
"new_value": "x:123",
|
||||
"expected": {
|
||||
"href": "file://y/",
|
||||
"host": "y",
|
||||
"hostname": "y",
|
||||
"port": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "file://y/",
|
||||
"new_value": "loc%41lhost",
|
||||
"expected": {
|
||||
"href": "file:///",
|
||||
"host": "",
|
||||
"hostname": "",
|
||||
"port": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "file://hi/x",
|
||||
"new_value": "",
|
||||
"expected": {
|
||||
"href": "file:///x",
|
||||
"host": "",
|
||||
"hostname": "",
|
||||
"port": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "sc://test@test/",
|
||||
"new_value": "",
|
||||
"expected": {
|
||||
"href": "sc://test@test/",
|
||||
"host": "test",
|
||||
"hostname": "test",
|
||||
"username": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "sc://test:12/",
|
||||
"new_value": "",
|
||||
"expected": {
|
||||
"href": "sc://test:12/",
|
||||
"host": "test:12",
|
||||
"hostname": "test",
|
||||
"port": "12"
|
||||
}
|
||||
}
|
||||
],
|
||||
"hostname": [
|
||||
{
|
||||
@ -978,33 +978,33 @@ module.exports =
|
||||
"hostname": "x"
|
||||
}
|
||||
},
|
||||
// {
|
||||
// "href": "sc://x/",
|
||||
// "new_value": "\u0009",
|
||||
// "expected": {
|
||||
// "href": "sc:///",
|
||||
// "host": "",
|
||||
// "hostname": ""
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "href": "sc://x/",
|
||||
// "new_value": "\u000A",
|
||||
// "expected": {
|
||||
// "href": "sc:///",
|
||||
// "host": "",
|
||||
// "hostname": ""
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "href": "sc://x/",
|
||||
// "new_value": "\u000D",
|
||||
// "expected": {
|
||||
// "href": "sc:///",
|
||||
// "host": "",
|
||||
// "hostname": ""
|
||||
// }
|
||||
// },
|
||||
{
|
||||
"href": "sc://x/",
|
||||
"new_value": "\u0009",
|
||||
"expected": {
|
||||
"href": "sc:///",
|
||||
"host": "",
|
||||
"hostname": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "sc://x/",
|
||||
"new_value": "\u000A",
|
||||
"expected": {
|
||||
"href": "sc:///",
|
||||
"host": "",
|
||||
"hostname": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "sc://x/",
|
||||
"new_value": "\u000D",
|
||||
"expected": {
|
||||
"href": "sc:///",
|
||||
"host": "",
|
||||
"hostname": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "sc://x/",
|
||||
"new_value": " ",
|
||||
@ -1014,33 +1014,33 @@ module.exports =
|
||||
"hostname": "x"
|
||||
}
|
||||
},
|
||||
// {
|
||||
// "href": "sc://x/",
|
||||
// "new_value": "#",
|
||||
// "expected": {
|
||||
// "href": "sc:///",
|
||||
// "host": "",
|
||||
// "hostname": ""
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "href": "sc://x/",
|
||||
// "new_value": "/",
|
||||
// "expected": {
|
||||
// "href": "sc:///",
|
||||
// "host": "",
|
||||
// "hostname": ""
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "href": "sc://x/",
|
||||
// "new_value": "?",
|
||||
// "expected": {
|
||||
// "href": "sc:///",
|
||||
// "host": "",
|
||||
// "hostname": ""
|
||||
// }
|
||||
// },
|
||||
{
|
||||
"href": "sc://x/",
|
||||
"new_value": "#",
|
||||
"expected": {
|
||||
"href": "sc:///",
|
||||
"host": "",
|
||||
"hostname": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "sc://x/",
|
||||
"new_value": "/",
|
||||
"expected": {
|
||||
"href": "sc:///",
|
||||
"host": "",
|
||||
"hostname": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "sc://x/",
|
||||
"new_value": "?",
|
||||
"expected": {
|
||||
"href": "sc:///",
|
||||
"host": "",
|
||||
"hostname": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "sc://x/",
|
||||
"new_value": "@",
|
||||
@ -1250,56 +1250,56 @@ module.exports =
|
||||
"hostname": "example.net"
|
||||
}
|
||||
},
|
||||
// {
|
||||
// "href": "file://y/",
|
||||
// "new_value": "x:123",
|
||||
// "expected": {
|
||||
// "href": "file://y/",
|
||||
// "host": "y",
|
||||
// "hostname": "y",
|
||||
// "port": ""
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "href": "file://y/",
|
||||
// "new_value": "loc%41lhost",
|
||||
// "expected": {
|
||||
// "href": "file:///",
|
||||
// "host": "",
|
||||
// "hostname": "",
|
||||
// "port": ""
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "href": "file://hi/x",
|
||||
// "new_value": "",
|
||||
// "expected": {
|
||||
// "href": "file:///x",
|
||||
// "host": "",
|
||||
// "hostname": "",
|
||||
// "port": ""
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "href": "sc://test@test/",
|
||||
// "new_value": "",
|
||||
// "expected": {
|
||||
// "href": "sc://test@test/",
|
||||
// "host": "test",
|
||||
// "hostname": "test",
|
||||
// "username": "test"
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "href": "sc://test:12/",
|
||||
// "new_value": "",
|
||||
// "expected": {
|
||||
// "href": "sc://test:12/",
|
||||
// "host": "test:12",
|
||||
// "hostname": "test",
|
||||
// "port": "12"
|
||||
// }
|
||||
// }
|
||||
{
|
||||
"href": "file://y/",
|
||||
"new_value": "x:123",
|
||||
"expected": {
|
||||
"href": "file://y/",
|
||||
"host": "y",
|
||||
"hostname": "y",
|
||||
"port": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "file://y/",
|
||||
"new_value": "loc%41lhost",
|
||||
"expected": {
|
||||
"href": "file:///",
|
||||
"host": "",
|
||||
"hostname": "",
|
||||
"port": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "file://hi/x",
|
||||
"new_value": "",
|
||||
"expected": {
|
||||
"href": "file:///x",
|
||||
"host": "",
|
||||
"hostname": "",
|
||||
"port": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "sc://test@test/",
|
||||
"new_value": "",
|
||||
"expected": {
|
||||
"href": "sc://test@test/",
|
||||
"host": "test",
|
||||
"hostname": "test",
|
||||
"username": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"href": "sc://test:12/",
|
||||
"new_value": "",
|
||||
"expected": {
|
||||
"href": "sc://test:12/",
|
||||
"host": "test:12",
|
||||
"hostname": "test",
|
||||
"port": "12"
|
||||
}
|
||||
}
|
||||
],
|
||||
"port": [
|
||||
{
|
||||
|
584
test/fixtures/url-tests.js
vendored
584
test/fixtures/url-tests.js
vendored
@ -1045,16 +1045,16 @@ module.exports =
|
||||
"search": "",
|
||||
"hash": ""
|
||||
},
|
||||
// {
|
||||
// "input": "file://example:1/",
|
||||
// "base": "about:blank",
|
||||
// "failure": true
|
||||
// },
|
||||
// {
|
||||
// "input": "file://example:test/",
|
||||
// "base": "about:blank",
|
||||
// "failure": true
|
||||
// },
|
||||
{
|
||||
"input": "file://example:1/",
|
||||
"base": "about:blank",
|
||||
"failure": true
|
||||
},
|
||||
{
|
||||
"input": "file://example:test/",
|
||||
"base": "about:blank",
|
||||
"failure": true
|
||||
},
|
||||
{
|
||||
"input": "file://example%/",
|
||||
"base": "about:blank",
|
||||
@ -3674,35 +3674,35 @@ module.exports =
|
||||
"search": "",
|
||||
"hash": ""
|
||||
},
|
||||
// {
|
||||
// "input": "https://faß.ExAmPlE/",
|
||||
// "base": "about:blank",
|
||||
// "href": "https://xn--fa-hia.example/",
|
||||
// "origin": "https://faß.example",
|
||||
// "protocol": "https:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "xn--fa-hia.example",
|
||||
// "hostname": "xn--fa-hia.example",
|
||||
// "port": "",
|
||||
// "pathname": "/",
|
||||
// "search": "",
|
||||
// "hash": ""
|
||||
// },
|
||||
// {
|
||||
// "input": "sc://faß.ExAmPlE/",
|
||||
// "base": "about:blank",
|
||||
// "href": "sc://fa%C3%9F.ExAmPlE/",
|
||||
// "protocol": "sc:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "fa%C3%9F.ExAmPlE",
|
||||
// "hostname": "fa%C3%9F.ExAmPlE",
|
||||
// "port": "",
|
||||
// "pathname": "/",
|
||||
// "search": "",
|
||||
// "hash": ""
|
||||
// },
|
||||
{
|
||||
"input": "https://faß.ExAmPlE/",
|
||||
"base": "about:blank",
|
||||
"href": "https://xn--fa-hia.example/",
|
||||
"origin": "https://faß.example",
|
||||
"protocol": "https:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "xn--fa-hia.example",
|
||||
"hostname": "xn--fa-hia.example",
|
||||
"port": "",
|
||||
"pathname": "/",
|
||||
"search": "",
|
||||
"hash": ""
|
||||
},
|
||||
{
|
||||
"input": "sc://faß.ExAmPlE/",
|
||||
"base": "about:blank",
|
||||
"href": "sc://fa%C3%9F.ExAmPlE/",
|
||||
"protocol": "sc:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "fa%C3%9F.ExAmPlE",
|
||||
"hostname": "fa%C3%9F.ExAmPlE",
|
||||
"port": "",
|
||||
"pathname": "/",
|
||||
"search": "",
|
||||
"hash": ""
|
||||
},
|
||||
"Invalid escaped characters should fail and the percents should be escaped. https://www.w3.org/Bugs/Public/show_bug.cgi?id=24191",
|
||||
{
|
||||
"input": "http://%zz%66%a.com",
|
||||
@ -4362,37 +4362,37 @@ module.exports =
|
||||
"search": "",
|
||||
"hash": ""
|
||||
},
|
||||
// "# unknown schemes and their hosts",
|
||||
// {
|
||||
// "input": "sc://ñ.test/",
|
||||
// "base": "about:blank",
|
||||
// "href": "sc://%C3%B1.test/",
|
||||
// "origin": "null",
|
||||
// "protocol": "sc:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "%C3%B1.test",
|
||||
// "hostname": "%C3%B1.test",
|
||||
// "port": "",
|
||||
// "pathname": "/",
|
||||
// "search": "",
|
||||
// "hash": ""
|
||||
// },
|
||||
// {
|
||||
// "input": "sc://\u001F!\"$&'()*+,-.;<=>^_`{|}~/",
|
||||
// "base": "about:blank",
|
||||
// "href": "sc://%1F!\"$&'()*+,-.;<=>^_`{|}~/",
|
||||
// "origin": "null",
|
||||
// "protocol": "sc:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "%1F!\"$&'()*+,-.;<=>^_`{|}~",
|
||||
// "hostname": "%1F!\"$&'()*+,-.;<=>^_`{|}~",
|
||||
// "port": "",
|
||||
// "pathname": "/",
|
||||
// "search": "",
|
||||
// "hash": ""
|
||||
// },
|
||||
"# unknown schemes and their hosts",
|
||||
{
|
||||
"input": "sc://ñ.test/",
|
||||
"base": "about:blank",
|
||||
"href": "sc://%C3%B1.test/",
|
||||
"origin": "null",
|
||||
"protocol": "sc:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "%C3%B1.test",
|
||||
"hostname": "%C3%B1.test",
|
||||
"port": "",
|
||||
"pathname": "/",
|
||||
"search": "",
|
||||
"hash": ""
|
||||
},
|
||||
{
|
||||
"input": "sc://\u001F!\"$&'()*+,-.;<=>^_`{|}~/",
|
||||
"base": "about:blank",
|
||||
"href": "sc://%1F!\"$&'()*+,-.;<=>^_`{|}~/",
|
||||
"origin": "null",
|
||||
"protocol": "sc:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "%1F!\"$&'()*+,-.;<=>^_`{|}~",
|
||||
"hostname": "%1F!\"$&'()*+,-.;<=>^_`{|}~",
|
||||
"port": "",
|
||||
"pathname": "/",
|
||||
"search": "",
|
||||
"hash": ""
|
||||
},
|
||||
{
|
||||
"input": "sc://\u0000/",
|
||||
"base": "about:blank",
|
||||
@ -4403,40 +4403,40 @@ module.exports =
|
||||
"base": "about:blank",
|
||||
"failure": true
|
||||
},
|
||||
// {
|
||||
// "input": "sc://%/",
|
||||
// "base": "about:blank",
|
||||
// "href": "sc://%/",
|
||||
// "protocol": "sc:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "%",
|
||||
// "hostname": "%",
|
||||
// "port": "",
|
||||
// "pathname": "/",
|
||||
// "search": "",
|
||||
// "hash": ""
|
||||
// },
|
||||
// {
|
||||
// "input": "sc://@/",
|
||||
// "base": "about:blank",
|
||||
// "failure": true
|
||||
// },
|
||||
// {
|
||||
// "input": "sc://te@s:t@/",
|
||||
// "base": "about:blank",
|
||||
// "failure": true
|
||||
// },
|
||||
// {
|
||||
// "input": "sc://:/",
|
||||
// "base": "about:blank",
|
||||
// "failure": true
|
||||
// },
|
||||
// {
|
||||
// "input": "sc://:12/",
|
||||
// "base": "about:blank",
|
||||
// "failure": true
|
||||
// },
|
||||
{
|
||||
"input": "sc://%/",
|
||||
"base": "about:blank",
|
||||
"href": "sc://%/",
|
||||
"protocol": "sc:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "%",
|
||||
"hostname": "%",
|
||||
"port": "",
|
||||
"pathname": "/",
|
||||
"search": "",
|
||||
"hash": ""
|
||||
},
|
||||
{
|
||||
"input": "sc://@/",
|
||||
"base": "about:blank",
|
||||
"failure": true
|
||||
},
|
||||
{
|
||||
"input": "sc://te@s:t@/",
|
||||
"base": "about:blank",
|
||||
"failure": true
|
||||
},
|
||||
{
|
||||
"input": "sc://:/",
|
||||
"base": "about:blank",
|
||||
"failure": true
|
||||
},
|
||||
{
|
||||
"input": "sc://:12/",
|
||||
"base": "about:blank",
|
||||
"failure": true
|
||||
},
|
||||
{
|
||||
"input": "sc://[/",
|
||||
"base": "about:blank",
|
||||
@ -4452,21 +4452,21 @@ module.exports =
|
||||
"base": "about:blank",
|
||||
"failure": true
|
||||
},
|
||||
// {
|
||||
// "input": "x",
|
||||
// "base": "sc://ñ",
|
||||
// "href": "sc://%C3%B1/x",
|
||||
// "origin": "null",
|
||||
// "protocol": "sc:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "%C3%B1",
|
||||
// "hostname": "%C3%B1",
|
||||
// "port": "",
|
||||
// "pathname": "/x",
|
||||
// "search": "",
|
||||
// "hash": ""
|
||||
// },
|
||||
{
|
||||
"input": "x",
|
||||
"base": "sc://ñ",
|
||||
"href": "sc://%C3%B1/x",
|
||||
"origin": "null",
|
||||
"protocol": "sc:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "%C3%B1",
|
||||
"hostname": "%C3%B1",
|
||||
"port": "",
|
||||
"pathname": "/x",
|
||||
"search": "",
|
||||
"hash": ""
|
||||
},
|
||||
"# unknown schemes and backslashes",
|
||||
{
|
||||
"input": "sc:\\../",
|
||||
@ -5327,20 +5327,20 @@ module.exports =
|
||||
"search": "",
|
||||
"hash": ""
|
||||
},
|
||||
// {
|
||||
// "input": "/..//localhost//pig",
|
||||
// "base": "file://lion/",
|
||||
// "href": "file://lion/localhost//pig",
|
||||
// "protocol": "file:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "lion",
|
||||
// "hostname": "lion",
|
||||
// "port": "",
|
||||
// "pathname": "/localhost//pig",
|
||||
// "search": "",
|
||||
// "hash": ""
|
||||
// },
|
||||
{
|
||||
"input": "/..//localhost//pig",
|
||||
"base": "file://lion/",
|
||||
"href": "file://lion/localhost//pig",
|
||||
"protocol": "file:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "lion",
|
||||
"hostname": "lion",
|
||||
"port": "",
|
||||
"pathname": "/localhost//pig",
|
||||
"search": "",
|
||||
"hash": ""
|
||||
},
|
||||
{
|
||||
"input": "file://",
|
||||
"base": "file://ape/",
|
||||
@ -5356,34 +5356,34 @@ module.exports =
|
||||
"hash": ""
|
||||
},
|
||||
"# File URLs with non-empty hosts",
|
||||
// {
|
||||
// "input": "/rooibos",
|
||||
// "base": "file://tea/",
|
||||
// "href": "file://tea/rooibos",
|
||||
// "protocol": "file:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "tea",
|
||||
// "hostname": "tea",
|
||||
// "port": "",
|
||||
// "pathname": "/rooibos",
|
||||
// "search": "",
|
||||
// "hash": ""
|
||||
// },
|
||||
// {
|
||||
// "input": "/?chai",
|
||||
// "base": "file://tea/",
|
||||
// "href": "file://tea/?chai",
|
||||
// "protocol": "file:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "tea",
|
||||
// "hostname": "tea",
|
||||
// "port": "",
|
||||
// "pathname": "/",
|
||||
// "search": "?chai",
|
||||
// "hash": ""
|
||||
// },
|
||||
{
|
||||
"input": "/rooibos",
|
||||
"base": "file://tea/",
|
||||
"href": "file://tea/rooibos",
|
||||
"protocol": "file:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "tea",
|
||||
"hostname": "tea",
|
||||
"port": "",
|
||||
"pathname": "/rooibos",
|
||||
"search": "",
|
||||
"hash": ""
|
||||
},
|
||||
{
|
||||
"input": "/?chai",
|
||||
"base": "file://tea/",
|
||||
"href": "file://tea/?chai",
|
||||
"protocol": "file:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "tea",
|
||||
"hostname": "tea",
|
||||
"port": "",
|
||||
"pathname": "/",
|
||||
"search": "?chai",
|
||||
"hash": ""
|
||||
},
|
||||
"# Windows drive letter quirk with not empty host",
|
||||
{
|
||||
"input": "file://example.net/C:/",
|
||||
@ -5567,109 +5567,109 @@ module.exports =
|
||||
"failure": true
|
||||
},
|
||||
"# Non-special-URL path tests",
|
||||
// {
|
||||
// "input": "sc://ñ",
|
||||
// "base": "about:blank",
|
||||
// "href": "sc://%C3%B1",
|
||||
// "origin": "null",
|
||||
// "protocol": "sc:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "%C3%B1",
|
||||
// "hostname": "%C3%B1",
|
||||
// "port": "",
|
||||
// "pathname": "",
|
||||
// "search": "",
|
||||
// "hash": ""
|
||||
// },
|
||||
// {
|
||||
// "input": "sc://ñ?x",
|
||||
// "base": "about:blank",
|
||||
// "href": "sc://%C3%B1?x",
|
||||
// "origin": "null",
|
||||
// "protocol": "sc:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "%C3%B1",
|
||||
// "hostname": "%C3%B1",
|
||||
// "port": "",
|
||||
// "pathname": "",
|
||||
// "search": "?x",
|
||||
// "hash": ""
|
||||
// },
|
||||
// {
|
||||
// "input": "sc://ñ#x",
|
||||
// "base": "about:blank",
|
||||
// "href": "sc://%C3%B1#x",
|
||||
// "origin": "null",
|
||||
// "protocol": "sc:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "%C3%B1",
|
||||
// "hostname": "%C3%B1",
|
||||
// "port": "",
|
||||
// "pathname": "",
|
||||
// "search": "",
|
||||
// "hash": "#x"
|
||||
// },
|
||||
// {
|
||||
// "input": "#x",
|
||||
// "base": "sc://ñ",
|
||||
// "href": "sc://%C3%B1#x",
|
||||
// "origin": "null",
|
||||
// "protocol": "sc:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "%C3%B1",
|
||||
// "hostname": "%C3%B1",
|
||||
// "port": "",
|
||||
// "pathname": "",
|
||||
// "search": "",
|
||||
// "hash": "#x"
|
||||
// },
|
||||
// {
|
||||
// "input": "?x",
|
||||
// "base": "sc://ñ",
|
||||
// "href": "sc://%C3%B1?x",
|
||||
// "origin": "null",
|
||||
// "protocol": "sc:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "%C3%B1",
|
||||
// "hostname": "%C3%B1",
|
||||
// "port": "",
|
||||
// "pathname": "",
|
||||
// "search": "?x",
|
||||
// "hash": ""
|
||||
// },
|
||||
// {
|
||||
// "input": "sc://?",
|
||||
// "base": "about:blank",
|
||||
// "href": "sc://?",
|
||||
// "protocol": "sc:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "",
|
||||
// "hostname": "",
|
||||
// "port": "",
|
||||
// "pathname": "",
|
||||
// "search": "",
|
||||
// "hash": ""
|
||||
// },
|
||||
// {
|
||||
// "input": "sc://#",
|
||||
// "base": "about:blank",
|
||||
// "href": "sc://#",
|
||||
// "protocol": "sc:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "",
|
||||
// "hostname": "",
|
||||
// "port": "",
|
||||
// "pathname": "",
|
||||
// "search": "",
|
||||
// "hash": ""
|
||||
// },
|
||||
{
|
||||
"input": "sc://ñ",
|
||||
"base": "about:blank",
|
||||
"href": "sc://%C3%B1",
|
||||
"origin": "null",
|
||||
"protocol": "sc:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "%C3%B1",
|
||||
"hostname": "%C3%B1",
|
||||
"port": "",
|
||||
"pathname": "",
|
||||
"search": "",
|
||||
"hash": ""
|
||||
},
|
||||
{
|
||||
"input": "sc://ñ?x",
|
||||
"base": "about:blank",
|
||||
"href": "sc://%C3%B1?x",
|
||||
"origin": "null",
|
||||
"protocol": "sc:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "%C3%B1",
|
||||
"hostname": "%C3%B1",
|
||||
"port": "",
|
||||
"pathname": "",
|
||||
"search": "?x",
|
||||
"hash": ""
|
||||
},
|
||||
{
|
||||
"input": "sc://ñ#x",
|
||||
"base": "about:blank",
|
||||
"href": "sc://%C3%B1#x",
|
||||
"origin": "null",
|
||||
"protocol": "sc:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "%C3%B1",
|
||||
"hostname": "%C3%B1",
|
||||
"port": "",
|
||||
"pathname": "",
|
||||
"search": "",
|
||||
"hash": "#x"
|
||||
},
|
||||
{
|
||||
"input": "#x",
|
||||
"base": "sc://ñ",
|
||||
"href": "sc://%C3%B1#x",
|
||||
"origin": "null",
|
||||
"protocol": "sc:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "%C3%B1",
|
||||
"hostname": "%C3%B1",
|
||||
"port": "",
|
||||
"pathname": "",
|
||||
"search": "",
|
||||
"hash": "#x"
|
||||
},
|
||||
{
|
||||
"input": "?x",
|
||||
"base": "sc://ñ",
|
||||
"href": "sc://%C3%B1?x",
|
||||
"origin": "null",
|
||||
"protocol": "sc:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "%C3%B1",
|
||||
"hostname": "%C3%B1",
|
||||
"port": "",
|
||||
"pathname": "",
|
||||
"search": "?x",
|
||||
"hash": ""
|
||||
},
|
||||
{
|
||||
"input": "sc://?",
|
||||
"base": "about:blank",
|
||||
"href": "sc://?",
|
||||
"protocol": "sc:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "",
|
||||
"hostname": "",
|
||||
"port": "",
|
||||
"pathname": "",
|
||||
"search": "",
|
||||
"hash": ""
|
||||
},
|
||||
{
|
||||
"input": "sc://#",
|
||||
"base": "about:blank",
|
||||
"href": "sc://#",
|
||||
"protocol": "sc:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "",
|
||||
"hostname": "",
|
||||
"port": "",
|
||||
"pathname": "",
|
||||
"search": "",
|
||||
"hash": ""
|
||||
},
|
||||
{
|
||||
"input": "///",
|
||||
"base": "sc://x/",
|
||||
@ -5893,34 +5893,34 @@ module.exports =
|
||||
"hash": ""
|
||||
},
|
||||
"# percent encoded hosts in non-special-URLs",
|
||||
// {
|
||||
// "input": "non-special://%E2%80%A0/",
|
||||
// "base": "about:blank",
|
||||
// "href": "non-special://%E2%80%A0/",
|
||||
// "protocol": "non-special:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "%E2%80%A0",
|
||||
// "hostname": "%E2%80%A0",
|
||||
// "port": "",
|
||||
// "pathname": "/",
|
||||
// "search": "",
|
||||
// "hash": ""
|
||||
// },
|
||||
// {
|
||||
// "input": "non-special://H%4fSt/path",
|
||||
// "base": "about:blank",
|
||||
// "href": "non-special://H%4fSt/path",
|
||||
// "protocol": "non-special:",
|
||||
// "username": "",
|
||||
// "password": "",
|
||||
// "host": "H%4fSt",
|
||||
// "hostname": "H%4fSt",
|
||||
// "port": "",
|
||||
// "pathname": "/path",
|
||||
// "search": "",
|
||||
// "hash": ""
|
||||
// },
|
||||
{
|
||||
"input": "non-special://%E2%80%A0/",
|
||||
"base": "about:blank",
|
||||
"href": "non-special://%E2%80%A0/",
|
||||
"protocol": "non-special:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "%E2%80%A0",
|
||||
"hostname": "%E2%80%A0",
|
||||
"port": "",
|
||||
"pathname": "/",
|
||||
"search": "",
|
||||
"hash": ""
|
||||
},
|
||||
{
|
||||
"input": "non-special://H%4fSt/path",
|
||||
"base": "about:blank",
|
||||
"href": "non-special://H%4fSt/path",
|
||||
"protocol": "non-special:",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"host": "H%4fSt",
|
||||
"hostname": "H%4fSt",
|
||||
"port": "",
|
||||
"pathname": "/path",
|
||||
"search": "",
|
||||
"hash": ""
|
||||
},
|
||||
"# IPv6 in non-special-URLs",
|
||||
{
|
||||
"input": "non-special://[1:2:0:0:5:0:0:0]/",
|
||||
|
Loading…
x
Reference in New Issue
Block a user