mirror of https://github.com/lukechilds/node.git
Browse Source
Implements WHATWG URL support. Example: ``` var u = new url.URL('http://example.org'); ``` Currently passing all WHATWG url parsing tests and all but two of the setter tests. The two setter tests are intentionally skipped for now but will be revisited. PR-URL: https://github.com/nodejs/node/pull/7448 Reviewed-By: Ilkka Myller <ilkka.myller@nodefield.com>v7.x
12 changed files with 3965 additions and 72 deletions
@ -0,0 +1,57 @@ |
|||
'use strict'; |
|||
const common = require('../common.js'); |
|||
const url = require('url'); |
|||
const v8 = require('v8'); |
|||
|
|||
const bench = common.createBenchmark(main, { |
|||
type: 'one two three four five'.split(' '), |
|||
method: ['old', 'new'], |
|||
n: [25e4] |
|||
}); |
|||
|
|||
function useOld(n, input) { |
|||
// Force-optimize url.parse() so that the benchmark doesn't get
|
|||
// disrupted by the optimizer kicking in halfway through.
|
|||
url.parse(input); |
|||
v8.setFlagsFromString('--allow_natives_syntax'); |
|||
eval('%OptimizeFunctionOnNextCall(url.parse)'); |
|||
|
|||
bench.start(); |
|||
for (var i = 0; i < n; i += 1) |
|||
url.parse(input); |
|||
bench.end(n); |
|||
} |
|||
|
|||
function useNew(n, input) { |
|||
bench.start(); |
|||
for (var i = 0; i < n; i += 1) |
|||
new url.URL(input); |
|||
bench.end(n); |
|||
} |
|||
|
|||
function main(conf) { |
|||
const type = conf.type; |
|||
const n = conf.n | 0; |
|||
const method = conf.method; |
|||
|
|||
var inputs = { |
|||
one: 'http://nodejs.org/docs/latest/api/url.html#url_url_format_urlobj', |
|||
two: 'http://blog.nodejs.org/', |
|||
three: 'https://encrypted.google.com/search?q=url&q=site:npmjs.org&hl=en', |
|||
four: 'javascript:alert("node is awesome");', |
|||
//five: 'some.ran/dom/url.thing?oh=yes#whoo',
|
|||
five: 'https://user:pass@example.com/', |
|||
}; |
|||
var input = inputs[type] || ''; |
|||
|
|||
switch (method) { |
|||
case 'old': |
|||
useOld(n, input); |
|||
break; |
|||
case 'new': |
|||
useNew(n, input); |
|||
break; |
|||
default: |
|||
throw new Error('Unknown method'); |
|||
} |
|||
} |
@ -0,0 +1,629 @@ |
|||
'use strict'; |
|||
|
|||
function getPunycode() { |
|||
try { |
|||
return process.binding('icu'); |
|||
} catch (err) { |
|||
return require('punycode'); |
|||
} |
|||
} |
|||
const punycode = getPunycode(); |
|||
const binding = process.binding('url'); |
|||
const context = Symbol('context'); |
|||
const cannotBeBase = Symbol('cannot-be-base'); |
|||
const special = Symbol('special'); |
|||
const searchParams = Symbol('query'); |
|||
const querystring = require('querystring'); |
|||
|
|||
const kScheme = Symbol('scheme'); |
|||
const kHost = Symbol('host'); |
|||
const kPort = Symbol('port'); |
|||
const kDomain = Symbol('domain'); |
|||
|
|||
function StorageObject() {} |
|||
StorageObject.prototype = Object.create(null); |
|||
|
|||
class OpaqueOrigin { |
|||
toString() { |
|||
return 'null'; |
|||
} |
|||
|
|||
get effectiveDomain() { |
|||
return this; |
|||
} |
|||
} |
|||
|
|||
class TupleOrigin { |
|||
constructor(scheme, host, port, domain) { |
|||
this[kScheme] = scheme; |
|||
this[kHost] = host; |
|||
this[kPort] = port; |
|||
this[kDomain] = domain; |
|||
} |
|||
|
|||
get scheme() { |
|||
return this[kScheme]; |
|||
} |
|||
|
|||
get host() { |
|||
return this[kHost]; |
|||
} |
|||
|
|||
get port() { |
|||
return this[kPort]; |
|||
} |
|||
|
|||
get domain() { |
|||
return this[kDomain]; |
|||
} |
|||
|
|||
get effectiveDomain() { |
|||
return this[kDomain] || this[kHost]; |
|||
} |
|||
|
|||
toString(unicode = false) { |
|||
var result = this.scheme; |
|||
result += '://'; |
|||
result += unicode ? URL.domainToUnicode(this.host) : this.host; |
|||
if (this.port !== undefined && this.port !== null) |
|||
result += `:${this.port}`; |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
class URL { |
|||
constructor(input, base) { |
|||
if (base !== undefined && !(base instanceof URL)) |
|||
base = new URL(String(base)); |
|||
input = String(input); |
|||
const base_context = base ? base[context] : undefined; |
|||
this[context] = new StorageObject(); |
|||
binding.parse(input.trim(), -1, base_context, undefined, |
|||
(flags, protocol, username, password, |
|||
host, port, path, query, fragment) => { |
|||
if (flags & binding.URL_FLAGS_FAILED) |
|||
throw new TypeError('Invalid URL'); |
|||
this[context].flags = flags; |
|||
this[context].scheme = protocol; |
|||
this[context].username = username; |
|||
this[context].password = password; |
|||
this[context].port = port; |
|||
this[context].path = path; |
|||
this[context].query = query; |
|||
this[context].fragment = fragment; |
|||
this[context].host = host; |
|||
this[searchParams] = new URLSearchParams(this); |
|||
}); |
|||
} |
|||
|
|||
get origin() { |
|||
return URL.originFor(this).toString(true); |
|||
} |
|||
|
|||
get [special]() { |
|||
return (this[context].flags & binding.URL_FLAGS_SPECIAL) != 0; |
|||
} |
|||
|
|||
get [cannotBeBase]() { |
|||
return (this[context].flags & binding.URL_FLAGS_CANNOT_BE_BASE) != 0; |
|||
} |
|||
|
|||
get protocol() { |
|||
return this[context].scheme; |
|||
} |
|||
|
|||
get searchParams() { |
|||
return this[searchParams]; |
|||
} |
|||
|
|||
set protocol(scheme) { |
|||
scheme = String(scheme); |
|||
if (scheme.length === 0) |
|||
return; |
|||
binding.parse(scheme, |
|||
binding.kSchemeStart, |
|||
null, |
|||
this[context], |
|||
(flags, protocol, username, password, |
|||
host, port, path, query, fragment) => { |
|||
if (flags & binding.URL_FLAGS_FAILED) |
|||
return; |
|||
const newIsSpecial = (flags & binding.URL_FLAGS_SPECIAL) != 0; |
|||
if ((this[special] && !newIsSpecial) || |
|||
(!this[special] && newIsSpecial) || |
|||
(newIsSpecial && !this[special] && |
|||
this[context].host === undefined)) { |
|||
return; |
|||
} |
|||
if (newIsSpecial) { |
|||
this[context].flags |= binding.URL_FLAGS_SPECIAL; |
|||
} else { |
|||
this[context].flags &= ~binding.URL_FLAGS_SPECIAL; |
|||
} |
|||
if (protocol) { |
|||
this[context].scheme = protocol; |
|||
this[context].flags |= binding.URL_FLAGS_HAS_SCHEME; |
|||
} else { |
|||
this[context].flags &= ~binding.URL_FLAGS_HAS_SCHEME; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
get username() { |
|||
return this[context].username || ''; |
|||
} |
|||
|
|||
set username(username) { |
|||
username = String(username); |
|||
if (!this.hostname) |
|||
return; |
|||
if (!username) { |
|||
this[context].username = null; |
|||
this[context].flags &= ~binding.URL_FLAGS_HAS_USERNAME; |
|||
return; |
|||
} |
|||
this[context].username = binding.encodeAuth(username); |
|||
this[context].flags |= binding.URL_FLAGS_HAS_USERNAME; |
|||
} |
|||
|
|||
get password() { |
|||
return this[context].password || ''; |
|||
} |
|||
|
|||
set password(password) { |
|||
password = String(password); |
|||
if (!this.hostname) |
|||
return; |
|||
if (!password) { |
|||
this[context].password = null; |
|||
this[context].flags &= ~binding.URL_FLAGS_HAS_PASSWORD; |
|||
return; |
|||
} |
|||
this[context].password = binding.encodeAuth(password); |
|||
this[context].flags |= binding.URL_FLAGS_HAS_PASSWORD; |
|||
} |
|||
|
|||
get host() { |
|||
var ret = this[context].host || ''; |
|||
if (this[context].port !== undefined) |
|||
ret += `:${this[context].port}`; |
|||
return ret; |
|||
} |
|||
|
|||
set host(host) { |
|||
host = String(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) { |
|||
this[context].host = null; |
|||
this[context].flags &= ~binding.URL_FLAGS_HAS_HOST; |
|||
return; |
|||
} |
|||
binding.parse(host, binding.kHost, null, this[context], |
|||
(flags, protocol, username, password, |
|||
host, port, path, query, fragment) => { |
|||
if (flags & binding.URL_FLAGS_FAILED) |
|||
return; |
|||
if (host) { |
|||
this[context].host = host; |
|||
this[context].flags |= binding.URL_FLAGS_HAS_HOST; |
|||
} else { |
|||
this[context].flags &= ~binding.URL_FLAGS_HAS_HOST; |
|||
} |
|||
if (port !== undefined) |
|||
this[context].port = port; |
|||
}); |
|||
} |
|||
|
|||
get hostname() { |
|||
return this[context].host || ''; |
|||
} |
|||
|
|||
set hostname(host) { |
|||
host = String(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) { |
|||
this[context].host = null; |
|||
this[context].flags &= ~binding.URL_FLAGS_HAS_HOST; |
|||
return; |
|||
} |
|||
binding.parse(host, |
|||
binding.kHostname, |
|||
null, |
|||
this[context], |
|||
(flags, protocol, username, password, |
|||
host, port, path, query, fragment) => { |
|||
if (flags & binding.URL_FLAGS_FAILED) |
|||
return; |
|||
if (host) { |
|||
this[context].host = host; |
|||
this[context].flags |= binding.URL_FLAGS_HAS_HOST; |
|||
} else { |
|||
this[context].flags &= ~binding.URL_FLAGS_HAS_HOST; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
get port() { |
|||
const port = this[context].port; |
|||
return port === undefined ? '' : String(port); |
|||
} |
|||
|
|||
set port(port) { |
|||
if (!this[context].host || this[cannotBeBase] || this.protocol === 'file:') |
|||
return; |
|||
port = String(port); |
|||
if (port === '') { |
|||
// Currently, if port number is empty, left unchanged.
|
|||
// TODO(jasnell): This might be changing in the spec
|
|||
return; |
|||
} |
|||
binding.parse(port, binding.kPort, null, this[context], |
|||
(flags, protocol, username, password, |
|||
host, port, path, query, fragment) => { |
|||
if (flags & binding.URL_FLAGS_FAILED) |
|||
return; |
|||
this[context].port = port; |
|||
}); |
|||
} |
|||
|
|||
get pathname() { |
|||
if (this[cannotBeBase]) |
|||
return this[context].path[0]; |
|||
return this[context].path !== undefined ? |
|||
`/${this[context].path.join('/')}` : ''; |
|||
} |
|||
|
|||
set pathname(path) { |
|||
if (this[cannotBeBase]) |
|||
return; |
|||
path = String(path); |
|||
binding.parse(path, |
|||
binding.kPathStart, |
|||
null, |
|||
this[context], |
|||
(flags, protocol, username, password, |
|||
host, port, path, query, fragment) => { |
|||
if (flags & binding.URL_FLAGS_FAILED) |
|||
return; |
|||
if (path) { |
|||
this[context].path = path; |
|||
this[context].flags |= binding.URL_FLAGS_HAS_PATH; |
|||
} else { |
|||
this[context].flags &= ~binding.URL_FLAGS_HAS_PATH; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
get search() { |
|||
return !this[context].query ? '' : `?${this[context].query}`; |
|||
} |
|||
|
|||
set search(search) { |
|||
update(this, search); |
|||
this[searchParams][searchParams] = querystring.parse(this.search); |
|||
} |
|||
|
|||
get hash() { |
|||
return !this[context].fragment ? '' : `#${this[context].fragment}`; |
|||
} |
|||
|
|||
set hash(hash) { |
|||
hash = String(hash); |
|||
if (this.protocol === 'javascript:') |
|||
return; |
|||
if (!hash) { |
|||
this[context].fragment = null; |
|||
this[context].flags &= ~binding.URL_FLAGS_HAS_FRAGMENT; |
|||
return; |
|||
} |
|||
if (hash[0] === '#') hash = hash.slice(1); |
|||
this[context].fragment = ''; |
|||
binding.parse(hash, |
|||
binding.kFragment, |
|||
null, |
|||
this[context], |
|||
(flags, protocol, username, password, |
|||
host, port, path, query, fragment) => { |
|||
if (flags & binding.URL_FLAGS_FAILED) |
|||
return; |
|||
if (fragment) { |
|||
this[context].fragment = fragment; |
|||
this[context].flags |= binding.URL_FLAGS_HAS_FRAGMENT; |
|||
} else { |
|||
this[context].flags &= ~binding.URL_FLAGS_HAS_FRAGMENT; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
get href() { |
|||
return this.toString(); |
|||
} |
|||
|
|||
toString(options) { |
|||
options = options || {}; |
|||
const fragment = |
|||
options.fragment !== undefined ? |
|||
!!options.fragment : true; |
|||
const unicode = !!options.unicode; |
|||
var ret; |
|||
if (this.protocol) |
|||
ret = this.protocol; |
|||
if (this[context].host !== undefined) { |
|||
ret += '//'; |
|||
const has_username = typeof this[context].username === 'string'; |
|||
const has_password = typeof this[context].password === 'string'; |
|||
if (has_username || has_password) { |
|||
if (has_username) |
|||
ret += this[context].username; |
|||
if (has_password) |
|||
ret += `:${this[context].password}`; |
|||
ret += '@'; |
|||
} |
|||
if (unicode) { |
|||
ret += punycode.toUnicode(this.hostname); |
|||
if (this.port !== undefined) |
|||
ret += `:${this.port}`; |
|||
} else { |
|||
ret += this.host; |
|||
} |
|||
} else if (this[context].scheme === 'file:') { |
|||
ret += '//'; |
|||
} |
|||
if (this.pathname) |
|||
ret += this.pathname; |
|||
if (typeof this[context].query === 'string') |
|||
ret += `?${this[context].query}`; |
|||
if (fragment & typeof this[context].fragment === 'string') |
|||
ret += `#${this[context].fragment}`; |
|||
return ret; |
|||
} |
|||
|
|||
inspect(depth, opts) { |
|||
var ret = 'URL {\n'; |
|||
ret += ` href: ${this.href}\n`; |
|||
if (this[context].scheme !== undefined) |
|||
ret += ` protocol: ${this.protocol}\n`; |
|||
if (this[context].username !== undefined) |
|||
ret += ` username: ${this.username}\n`; |
|||
if (this[context].password !== undefined) { |
|||
const pwd = opts.showHidden ? this[context].password : '--------'; |
|||
ret += ` password: ${pwd}\n`; |
|||
} |
|||
if (this[context].host !== undefined) |
|||
ret += ` hostname: ${this.hostname}\n`; |
|||
if (this[context].port !== undefined) |
|||
ret += ` port: ${this.port}\n`; |
|||
if (this[context].path !== undefined) |
|||
ret += ` pathname: ${this.pathname}\n`; |
|||
if (this[context].query !== undefined) |
|||
ret += ` search: ${this.search}\n`; |
|||
if (this[context].fragment !== undefined) |
|||
ret += ` hash: ${this.hash}\n`; |
|||
if (opts.showHidden) { |
|||
ret += ` cannot-be-base: ${this[cannotBeBase]}\n`; |
|||
ret += ` special: ${this[special]}\n;`; |
|||
} |
|||
ret += '}'; |
|||
return ret; |
|||
} |
|||
} |
|||
|
|||
var hexTable = new Array(256); |
|||
for (var i = 0; i < 256; ++i) |
|||
hexTable[i] = '%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase(); |
|||
function encodeAuth(str) { |
|||
// faster encodeURIComponent alternative for encoding auth uri components
|
|||
var out = ''; |
|||
var lastPos = 0; |
|||
for (var i = 0; i < str.length; ++i) { |
|||
var c = str.charCodeAt(i); |
|||
|
|||
// These characters do not need escaping:
|
|||
// ! - . _ ~
|
|||
// ' ( ) * :
|
|||
// digits
|
|||
// alpha (uppercase)
|
|||
// alpha (lowercase)
|
|||
if (c === 0x21 || c === 0x2D || c === 0x2E || c === 0x5F || c === 0x7E || |
|||
(c >= 0x27 && c <= 0x2A) || |
|||
(c >= 0x30 && c <= 0x3A) || |
|||
(c >= 0x41 && c <= 0x5A) || |
|||
(c >= 0x61 && c <= 0x7A)) { |
|||
continue; |
|||
} |
|||
|
|||
if (i - lastPos > 0) |
|||
out += str.slice(lastPos, i); |
|||
|
|||
lastPos = i + 1; |
|||
|
|||
// Other ASCII characters
|
|||
if (c < 0x80) { |
|||
out += hexTable[c]; |
|||
continue; |
|||
} |
|||
|
|||
// Multi-byte characters ...
|
|||
if (c < 0x800) { |
|||
out += hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)]; |
|||
continue; |
|||
} |
|||
if (c < 0xD800 || c >= 0xE000) { |
|||
out += hexTable[0xE0 | (c >> 12)] + |
|||
hexTable[0x80 | ((c >> 6) & 0x3F)] + |
|||
hexTable[0x80 | (c & 0x3F)]; |
|||
continue; |
|||
} |
|||
// Surrogate pair
|
|||
++i; |
|||
var c2; |
|||
if (i < str.length) |
|||
c2 = str.charCodeAt(i) & 0x3FF; |
|||
else |
|||
c2 = 0; |
|||
c = 0x10000 + (((c & 0x3FF) << 10) | c2); |
|||
out += hexTable[0xF0 | (c >> 18)] + |
|||
hexTable[0x80 | ((c >> 12) & 0x3F)] + |
|||
hexTable[0x80 | ((c >> 6) & 0x3F)] + |
|||
hexTable[0x80 | (c & 0x3F)]; |
|||
} |
|||
if (lastPos === 0) |
|||
return str; |
|||
if (lastPos < str.length) |
|||
return out + str.slice(lastPos); |
|||
return out; |
|||
} |
|||
|
|||
function update(url, search) { |
|||
search = String(search); |
|||
if (!search) { |
|||
url[context].query = null; |
|||
url[context].flags &= ~binding.URL_FLAGS_HAS_QUERY; |
|||
return; |
|||
} |
|||
if (search[0] === '?') search = search.slice(1); |
|||
url[context].query = ''; |
|||
binding.parse(search, |
|||
binding.kQuery, |
|||
null, |
|||
url[context], |
|||
(flags, protocol, username, password, |
|||
host, port, path, query, fragment) => { |
|||
if (flags & binding.URL_FLAGS_FAILED) |
|||
return; |
|||
if (query) { |
|||
url[context].query = query; |
|||
url[context].flags |= binding.URL_FLAGS_HAS_QUERY; |
|||
} else { |
|||
url[context].flags &= ~binding.URL_FLAGS_HAS_QUERY; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
class URLSearchParams { |
|||
constructor(url) { |
|||
this[context] = url; |
|||
this[searchParams] = querystring.parse(url[context].search || ''); |
|||
} |
|||
|
|||
append(name, value) { |
|||
const obj = this[searchParams]; |
|||
name = String(name); |
|||
value = String(value); |
|||
var existing = obj[name]; |
|||
if (!existing) { |
|||
obj[name] = value; |
|||
} else if (Array.isArray(existing)) { |
|||
existing.push(value); |
|||
} else { |
|||
obj[name] = [existing, value]; |
|||
} |
|||
update(this[context], querystring.stringify(obj)); |
|||
} |
|||
|
|||
delete(name) { |
|||
const obj = this[searchParams]; |
|||
name = String(name); |
|||
delete obj[name]; |
|||
update(this[context], querystring.stringify(obj)); |
|||
} |
|||
|
|||
set(name, value) { |
|||
const obj = this[searchParams]; |
|||
name = String(name); |
|||
value = String(value); |
|||
obj[name] = value; |
|||
update(this[context], querystring.stringify(obj)); |
|||
} |
|||
|
|||
get(name) { |
|||
const obj = this[searchParams]; |
|||
name = String(name); |
|||
var value = obj[name]; |
|||
return Array.isArray(value) ? value[0] : value; |
|||
} |
|||
|
|||
getAll(name) { |
|||
const obj = this[searchParams]; |
|||
name = String(name); |
|||
var value = obj[name]; |
|||
return value === undefined ? [] : Array.isArray(value) ? value : [value]; |
|||
} |
|||
|
|||
has(name) { |
|||
const obj = this[searchParams]; |
|||
name = String(name); |
|||
return name in obj; |
|||
} |
|||
|
|||
*[Symbol.iterator]() { |
|||
const obj = this[searchParams]; |
|||
for (const name in obj) { |
|||
const value = obj[name]; |
|||
if (Array.isArray(value)) { |
|||
for (const item of value) |
|||
yield [name, item]; |
|||
} else { |
|||
yield [name, value]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
toString() { |
|||
return querystring.stringify(this[searchParams]); |
|||
} |
|||
} |
|||
|
|||
URL.originFor = function(url) { |
|||
if (!(url instanceof URL)) |
|||
url = new URL(url); |
|||
var origin; |
|||
const protocol = url.protocol; |
|||
switch (protocol) { |
|||
case 'blob:': |
|||
if (url[context].path && url[context].path.length > 0) { |
|||
try { |
|||
return (new URL(url[context].path[0])).origin; |
|||
} catch (err) { |
|||
// fall through... do nothing
|
|||
} |
|||
} |
|||
origin = new OpaqueOrigin(); |
|||
break; |
|||
case 'ftp:': |
|||
case 'gopher:': |
|||
case 'http:': |
|||
case 'https:': |
|||
case 'ws:': |
|||
case 'wss:': |
|||
case 'file': |
|||
origin = new TupleOrigin(protocol.slice(0, -1), |
|||
url[context].host, |
|||
url[context].port, |
|||
null); |
|||
break; |
|||
default: |
|||
origin = new OpaqueOrigin(); |
|||
} |
|||
return origin; |
|||
}; |
|||
|
|||
URL.domainToASCII = function(domain) { |
|||
return binding.domainToASCII(String(domain)); |
|||
}; |
|||
URL.domainToUnicode = function(domain) { |
|||
return binding.domainToUnicode(String(domain)); |
|||
}; |
|||
|
|||
exports.URL = URL; |
|||
exports.encodeAuth = encodeAuth; |
File diff suppressed because it is too large
@ -0,0 +1,538 @@ |
|||
#ifndef SRC_NODE_URL_H_ |
|||
#define SRC_NODE_URL_H_ |
|||
|
|||
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
|||
|
|||
#include "node.h" |
|||
#include <string> |
|||
|
|||
namespace node { |
|||
namespace url { |
|||
|
|||
#define BIT_AT(a, i) \ |
|||
(!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ |
|||
(1 << ((unsigned int) (i) & 7)))) |
|||
#define TAB_AND_NEWLINE(ch) \ |
|||
(ch == 0x09 || ch == 0x0a || ch == 0x0d) |
|||
#define ASCII_DIGIT(ch) \ |
|||
(ch >= 0x30 && ch <= 0x39) |
|||
#define ASCII_HEX_DIGIT(ch) \ |
|||
(ASCII_DIGIT(ch) || (ch >= 0x41 && ch <= 0x46) || (ch >= 0x61 && ch <= 0x66)) |
|||
#define ASCII_ALPHA(ch) \ |
|||
((ch >= 0x41 && ch <= 0x5a) || (ch >= 0x61 && ch <= 0x7a)) |
|||
#define ASCII_ALPHANUMERIC(ch) \ |
|||
(ASCII_DIGIT(ch) || ASCII_ALPHA(ch)) |
|||
#define TO_LOWER(ch) \ |
|||
(ASCII_ALPHA(ch) ? (ch | 0x20) : ch) |
|||
#define SCHEME_CHAR(ch) \ |
|||
(ASCII_ALPHANUMERIC(ch) || ch == '+' || ch == '-' || ch == '.') |
|||
#define WINDOWS_DRIVE_LETTER(ch, next) \ |
|||
(ASCII_ALPHA(ch) && (next == ':' || next == '|')) |
|||
#define NORMALIZED_WINDOWS_DRIVE_LETTER(str) \ |
|||
(str.length() == 2 && \ |
|||
ASCII_ALPHA(str[0]) && \ |
|||
str[1] == ':') |
|||
|
|||
static const char* hex[256] = { |
|||
"%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", |
|||
"%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F", |
|||
"%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17", |
|||
"%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F", |
|||
"%20", "%21", "%22", "%23", "%24", "%25", "%26", "%27", |
|||
"%28", "%29", "%2A", "%2B", "%2C", "%2D", "%2E", "%2F", |
|||
"%30", "%31", "%32", "%33", "%34", "%35", "%36", "%37", |
|||
"%38", "%39", "%3A", "%3B", "%3C", "%3D", "%3E", "%3F", |
|||
"%40", "%41", "%42", "%43", "%44", "%45", "%46", "%47", |
|||
"%48", "%49", "%4A", "%4B", "%4C", "%4D", "%4E", "%4F", |
|||
"%50", "%51", "%52", "%53", "%54", "%55", "%56", "%57", |
|||
"%58", "%59", "%5A", "%5B", "%5C", "%5D", "%5E", "%5F", |
|||
"%60", "%61", "%62", "%63", "%64", "%65", "%66", "%67", |
|||
"%68", "%69", "%6A", "%6B", "%6C", "%6D", "%6E", "%6F", |
|||
"%70", "%71", "%72", "%73", "%74", "%75", "%76", "%77", |
|||
"%78", "%79", "%7A", "%7B", "%7C", "%7D", "%7E", "%7F", |
|||
"%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87", |
|||
"%88", "%89", "%8A", "%8B", "%8C", "%8D", "%8E", "%8F", |
|||
"%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97", |
|||
"%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E", "%9F", |
|||
"%A0", "%A1", "%A2", "%A3", "%A4", "%A5", "%A6", "%A7", |
|||
"%A8", "%A9", "%AA", "%AB", "%AC", "%AD", "%AE", "%AF", |
|||
"%B0", "%B1", "%B2", "%B3", "%B4", "%B5", "%B6", "%B7", |
|||
"%B8", "%B9", "%BA", "%BB", "%BC", "%BD", "%BE", "%BF", |
|||
"%C0", "%C1", "%C2", "%C3", "%C4", "%C5", "%C6", "%C7", |
|||
"%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE", "%CF", |
|||
"%D0", "%D1", "%D2", "%D3", "%D4", "%D5", "%D6", "%D7", |
|||
"%D8", "%D9", "%DA", "%DB", "%DC", "%DD", "%DE", "%DF", |
|||
"%E0", "%E1", "%E2", "%E3", "%E4", "%E5", "%E6", "%E7", |
|||
"%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE", "%EF", |
|||
"%F0", "%F1", "%F2", "%F3", "%F4", "%F5", "%F6", "%F7", |
|||
"%F8", "%F9", "%FA", "%FB", "%FC", "%FD", "%FE", "%FF" |
|||
}; |
|||
|
|||
static const uint8_t SIMPLE_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
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 10 11 12 13 14 15 16 17
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 18 19 1A 1B 1C 1D 1E 1F
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 20 21 22 23 24 25 26 27
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 28 29 2A 2B 2C 2D 2E 2F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 30 31 32 33 34 35 36 37
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 38 39 3A 3B 3C 3D 3E 3F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 40 41 42 43 44 45 46 47
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 48 49 4A 4B 4C 4D 4E 4F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 50 51 52 53 54 55 56 57
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 58 59 5A 5B 5C 5D 5E 5F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 60 61 62 63 64 65 66 67
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 68 69 6A 6B 6C 6D 6E 6F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 70 71 72 73 74 75 76 77
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 78 79 7A 7B 7C 7D 7E 7F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80, |
|||
// 80 81 82 83 84 85 86 87
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 88 89 8A 8B 8C 8D 8E 8F
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 90 91 92 93 94 95 96 97
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 98 99 9A 9B 9C 9D 9E 9F
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// A0 A1 A2 A3 A4 A5 A6 A7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// A8 A9 AA AB AC AD AE AF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// B0 B1 B2 B3 B4 B5 B6 B7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// B8 B9 BA BB BC BD BE BF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// C0 C1 C2 C3 C4 C5 C6 C7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// C8 C9 CA CB CC CD CE CF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// D0 D1 D2 D3 D4 D5 D6 D7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// D8 D9 DA DB DC DD DE DF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// E0 E1 E2 E3 E4 E5 E6 E7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// E8 E9 EA EB EC ED EE EF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// F0 F1 F2 F3 F4 F5 F6 F7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// F8 F9 FA FB FC FD FE FF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80 |
|||
}; |
|||
|
|||
static const uint8_t DEFAULT_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
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 10 11 12 13 14 15 16 17
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 18 19 1A 1B 1C 1D 1E 1F
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 20 21 22 23 24 25 26 27
|
|||
0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 28 29 2A 2B 2C 2D 2E 2F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 30 31 32 33 34 35 36 37
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 38 39 3A 3B 3C 3D 3E 3F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x80, |
|||
// 40 41 42 43 44 45 46 47
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 48 49 4A 4B 4C 4D 4E 4F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 50 51 52 53 54 55 56 57
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 58 59 5A 5B 5C 5D 5E 5F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 60 61 62 63 64 65 66 67
|
|||
0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 68 69 6A 6B 6C 6D 6E 6F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 70 71 72 73 74 75 76 77
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 78 79 7A 7B 7C 7D 7E 7F
|
|||
0x00 | 0x00 | 0x00 | 0x08 | 0x00 | 0x20 | 0x00 | 0x80, |
|||
// 80 81 82 83 84 85 86 87
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 88 89 8A 8B 8C 8D 8E 8F
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 90 91 92 93 94 95 96 97
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 98 99 9A 9B 9C 9D 9E 9F
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// A0 A1 A2 A3 A4 A5 A6 A7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// A8 A9 AA AB AC AD AE AF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// B0 B1 B2 B3 B4 B5 B6 B7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// B8 B9 BA BB BC BD BE BF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// C0 C1 C2 C3 C4 C5 C6 C7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// C8 C9 CA CB CC CD CE CF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// D0 D1 D2 D3 D4 D5 D6 D7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// D8 D9 DA DB DC DD DE DF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// E0 E1 E2 E3 E4 E5 E6 E7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// E8 E9 EA EB EC ED EE EF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// F0 F1 F2 F3 F4 F5 F6 F7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// F8 F9 FA FB FC FD FE FF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80 |
|||
}; |
|||
|
|||
static const uint8_t USERINFO_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
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 10 11 12 13 14 15 16 17
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 18 19 1A 1B 1C 1D 1E 1F
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 20 21 22 23 24 25 26 27
|
|||
0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 28 29 2A 2B 2C 2D 2E 2F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80, |
|||
// 30 31 32 33 34 35 36 37
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 38 39 3A 3B 3C 3D 3E 3F
|
|||
0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 40 41 42 43 44 45 46 47
|
|||
0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 48 49 4A 4B 4C 4D 4E 4F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 50 51 52 53 54 55 56 57
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 58 59 5A 5B 5C 5D 5E 5F
|
|||
0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00, |
|||
// 60 61 62 63 64 65 66 67
|
|||
0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 68 69 6A 6B 6C 6D 6E 6F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 70 71 72 73 74 75 76 77
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 78 79 7A 7B 7C 7D 7E 7F
|
|||
0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x00 | 0x80, |
|||
// 80 81 82 83 84 85 86 87
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 88 89 8A 8B 8C 8D 8E 8F
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 90 91 92 93 94 95 96 97
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 98 99 9A 9B 9C 9D 9E 9F
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// A0 A1 A2 A3 A4 A5 A6 A7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// A8 A9 AA AB AC AD AE AF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// B0 B1 B2 B3 B4 B5 B6 B7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// B8 B9 BA BB BC BD BE BF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// C0 C1 C2 C3 C4 C5 C6 C7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// C8 C9 CA CB CC CD CE CF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// D0 D1 D2 D3 D4 D5 D6 D7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// D8 D9 DA DB DC DD DE DF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// E0 E1 E2 E3 E4 E5 E6 E7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// E8 E9 EA EB EC ED EE EF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// F0 F1 F2 F3 F4 F5 F6 F7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// F8 F9 FA FB FC FD FE FF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80 |
|||
}; |
|||
|
|||
static const uint8_t QUERY_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
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 10 11 12 13 14 15 16 17
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 18 19 1A 1B 1C 1D 1E 1F
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 20 21 22 23 24 25 26 27
|
|||
0x01 | 0x00 | 0x04 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 28 29 2A 2B 2C 2D 2E 2F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 30 31 32 33 34 35 36 37
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 38 39 3A 3B 3C 3D 3E 3F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x10 | 0x00 | 0x40 | 0x00, |
|||
// 40 41 42 43 44 45 46 47
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 48 49 4A 4B 4C 4D 4E 4F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 50 51 52 53 54 55 56 57
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 58 59 5A 5B 5C 5D 5E 5F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 60 61 62 63 64 65 66 67
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 68 69 6A 6B 6C 6D 6E 6F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 70 71 72 73 74 75 76 77
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00, |
|||
// 78 79 7A 7B 7C 7D 7E 7F
|
|||
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x80, |
|||
// 80 81 82 83 84 85 86 87
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 88 89 8A 8B 8C 8D 8E 8F
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 90 91 92 93 94 95 96 97
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// 98 99 9A 9B 9C 9D 9E 9F
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// A0 A1 A2 A3 A4 A5 A6 A7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// A8 A9 AA AB AC AD AE AF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// B0 B1 B2 B3 B4 B5 B6 B7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// B8 B9 BA BB BC BD BE BF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// C0 C1 C2 C3 C4 C5 C6 C7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// C8 C9 CA CB CC CD CE CF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// D0 D1 D2 D3 D4 D5 D6 D7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// D8 D9 DA DB DC DD DE DF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// E0 E1 E2 E3 E4 E5 E6 E7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// E8 E9 EA EB EC ED EE EF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// F0 F1 F2 F3 F4 F5 F6 F7
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, |
|||
// F8 F9 FA FB FC FD FE FF
|
|||
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80 |
|||
}; |
|||
|
|||
// Must return true if the character is to be percent-encoded
|
|||
typedef bool (*must_escape_cb)(const unsigned char ch); |
|||
|
|||
// Appends ch to str. If test(ch) returns true, the ch will
|
|||
// be percent-encoded then appended.
|
|||
static inline void AppendOrEscape(std::string* str, |
|||
const unsigned char ch, |
|||
must_escape_cb test) { |
|||
if (test(ch)) |
|||
*str += hex[ch]; |
|||
else |
|||
*str += ch; |
|||
} |
|||
|
|||
static inline bool SimpleEncodeSet(const unsigned char ch) { |
|||
return BIT_AT(SIMPLE_ENCODE_SET, ch); |
|||
} |
|||
|
|||
static inline bool DefaultEncodeSet(const unsigned char ch) { |
|||
return BIT_AT(DEFAULT_ENCODE_SET, ch); |
|||
} |
|||
|
|||
static inline bool UserinfoEncodeSet(const unsigned char ch) { |
|||
return BIT_AT(USERINFO_ENCODE_SET, ch); |
|||
} |
|||
|
|||
static inline bool QueryEncodeSet(const unsigned char ch) { |
|||
return BIT_AT(QUERY_ENCODE_SET, ch); |
|||
} |
|||
|
|||
static inline unsigned hex2bin(const char ch) { |
|||
if (ch >= '0' && ch <= '9') |
|||
return ch - '0'; |
|||
if (ch >= 'A' && ch <= 'F') |
|||
return 10 + (ch - 'A'); |
|||
if (ch >= 'a' && ch <= 'f') |
|||
return 10 + (ch - 'a'); |
|||
return static_cast<unsigned>(-1); |
|||
} |
|||
|
|||
static inline int PercentDecode(const char* input, |
|||
size_t len, |
|||
std::string* dest) { |
|||
if (len == 0) |
|||
return 0; |
|||
dest->reserve(len); |
|||
const char* pointer = input; |
|||
const char* end = input + len; |
|||
size_t remaining = pointer - end - 1; |
|||
while (pointer < end) { |
|||
const char ch = pointer[0]; |
|||
remaining = (end - pointer) + 1; |
|||
if (ch != '%' || remaining < 2 || |
|||
(ch == '%' && |
|||
(!ASCII_HEX_DIGIT(pointer[1]) || |
|||
!ASCII_HEX_DIGIT(pointer[2])))) { |
|||
*dest += ch; |
|||
pointer++; |
|||
continue; |
|||
} else { |
|||
unsigned a = hex2bin(pointer[1]); |
|||
unsigned b = hex2bin(pointer[2]); |
|||
char c = static_cast<char>(a * 16 + b); |
|||
*dest += static_cast<char>(c); |
|||
pointer += 3; |
|||
} |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
#define SPECIALS(XX) \ |
|||
XX("ftp:", 21) \ |
|||
XX("file:", -1) \ |
|||
XX("gopher:", 70) \ |
|||
XX("http:", 80) \ |
|||
XX("https:", 443) \ |
|||
XX("ws:", 80) \ |
|||
XX("wss:", 443) |
|||
|
|||
#define PARSESTATES(XX) \ |
|||
XX(kSchemeStart) \ |
|||
XX(kScheme) \ |
|||
XX(kNoScheme) \ |
|||
XX(kSpecialRelativeOrAuthority) \ |
|||
XX(kPathOrAuthority) \ |
|||
XX(kRelative) \ |
|||
XX(kRelativeSlash) \ |
|||
XX(kSpecialAuthoritySlashes) \ |
|||
XX(kSpecialAuthorityIgnoreSlashes) \ |
|||
XX(kAuthority) \ |
|||
XX(kHost) \ |
|||
XX(kHostname) \ |
|||
XX(kPort) \ |
|||
XX(kFile) \ |
|||
XX(kFileSlash) \ |
|||
XX(kFileHost) \ |
|||
XX(kPathStart) \ |
|||
XX(kPath) \ |
|||
XX(kCannotBeBase) \ |
|||
XX(kQuery) \ |
|||
XX(kFragment) |
|||
|
|||
#define FLAGS(XX) \ |
|||
XX(URL_FLAGS_NONE, 0) \ |
|||
XX(URL_FLAGS_FAILED, 0x01) \ |
|||
XX(URL_FLAGS_CANNOT_BE_BASE, 0x02) \ |
|||
XX(URL_FLAGS_INVALID_PARSE_STATE, 0x04) \ |
|||
XX(URL_FLAGS_TERMINATED, 0x08) \ |
|||
XX(URL_FLAGS_SPECIAL, 0x10) \ |
|||
XX(URL_FLAGS_HAS_SCHEME, 0x20) \ |
|||
XX(URL_FLAGS_HAS_USERNAME, 0x40) \ |
|||
XX(URL_FLAGS_HAS_PASSWORD, 0x80) \ |
|||
XX(URL_FLAGS_HAS_HOST, 0x100) \ |
|||
XX(URL_FLAGS_HAS_PATH, 0x200) \ |
|||
XX(URL_FLAGS_HAS_QUERY, 0x400) \ |
|||
XX(URL_FLAGS_HAS_FRAGMENT, 0x800) |
|||
|
|||
#define ARGS(XX) \ |
|||
XX(ARG_FLAGS) \ |
|||
XX(ARG_PROTOCOL) \ |
|||
XX(ARG_USERNAME) \ |
|||
XX(ARG_PASSWORD) \ |
|||
XX(ARG_HOST) \ |
|||
XX(ARG_PORT) \ |
|||
XX(ARG_PATH) \ |
|||
XX(ARG_QUERY) \ |
|||
XX(ARG_FRAGMENT) |
|||
|
|||
static const char kEOL = -1; |
|||
|
|||
enum url_parse_state { |
|||
kUnknownState = -1, |
|||
#define XX(name) name, |
|||
PARSESTATES(XX) |
|||
#undef XX |
|||
} url_parse_state; |
|||
|
|||
enum url_flags { |
|||
#define XX(name, val) name = val, |
|||
FLAGS(XX) |
|||
#undef XX |
|||
} url_flags; |
|||
|
|||
enum url_cb_args { |
|||
#define XX(name) name, |
|||
ARGS(XX) |
|||
#undef XX |
|||
} url_cb_args; |
|||
|
|||
static inline bool IsSpecial(std::string scheme) { |
|||
#define XX(name, _) if (scheme == name) return true; |
|||
SPECIALS(XX); |
|||
#undef XX |
|||
return false; |
|||
} |
|||
|
|||
static inline int NormalizePort(std::string scheme, int p) { |
|||
#define XX(name, port) if (scheme == name && p == port) return -1; |
|||
SPECIALS(XX); |
|||
#undef XX |
|||
return p; |
|||
} |
|||
|
|||
struct url_data { |
|||
int32_t flags = URL_FLAGS_NONE; |
|||
int port = -1; |
|||
std::string scheme; |
|||
std::string username; |
|||
std::string password; |
|||
std::string host; |
|||
std::string query; |
|||
std::string fragment; |
|||
std::vector<std::string> path; |
|||
}; |
|||
|
|||
union url_host_value { |
|||
std::string domain; |
|||
uint32_t ipv4; |
|||
uint16_t ipv6[8]; |
|||
~url_host_value() {} |
|||
}; |
|||
|
|||
enum url_host_type { |
|||
HOST_TYPE_FAILED = -1, |
|||
HOST_TYPE_DOMAIN = 0, |
|||
HOST_TYPE_IPV4 = 1, |
|||
HOST_TYPE_IPV6 = 2 |
|||
}; |
|||
|
|||
struct url_host { |
|||
url_host_value value; |
|||
enum url_host_type type; |
|||
}; |
|||
} // namespace url
|
|||
|
|||
} // namespace node
|
|||
|
|||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|||
|
|||
#endif // SRC_NODE_URL_H_
|
File diff suppressed because it is too large
@ -0,0 +1,122 @@ |
|||
'use strict'; |
|||
|
|||
const common = require('../common'); |
|||
const URL = require('url').URL; |
|||
const path = require('path'); |
|||
const assert = require('assert'); |
|||
const tests = require(path.join(common.fixturesDir, 'url-tests.json')); |
|||
|
|||
for (const test of tests) { |
|||
if (typeof test === 'string') |
|||
continue; |
|||
|
|||
if (test.failure) { |
|||
assert.throws(() => new URL(test.input, test.base), /Invalid URL/); |
|||
} else { |
|||
assert.doesNotThrow(() => { |
|||
const url = new URL(test.input, test.base); |
|||
assert.strictEqual(url.href, test.href); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
const additional_tests = [ |
|||
{ |
|||
'url': 'tftp://foobar.com/someconfig;mode=netascii', |
|||
'protocol': 'tftp:', |
|||
'hostname': 'foobar.com', |
|||
'pathname': '/someconfig;mode=netascii' |
|||
}, |
|||
{ |
|||
'url': 'telnet://user:pass@foobar.com:23/', |
|||
'protocol': 'telnet:', |
|||
'username': 'user', |
|||
'password': 'pass', |
|||
'hostname': 'foobar.com', |
|||
'port': '23', |
|||
'pathname': '/' |
|||
}, |
|||
{ |
|||
'url': 'ut2004://10.10.10.10:7777/Index.ut2', |
|||
'protocol': 'ut2004:', |
|||
'hostname': '10.10.10.10', |
|||
'port': '7777', |
|||
'pathname': '/Index.ut2' |
|||
}, |
|||
{ |
|||
'url': 'redis://foo:bar@somehost:6379/0?baz=bam&qux=baz', |
|||
'protocol': 'redis:', |
|||
'username': 'foo', |
|||
'password': 'bar', |
|||
'hostname': 'somehost', |
|||
'port': '6379', |
|||
'pathname': '/0', |
|||
'search': '?baz=bam&qux=baz' |
|||
}, |
|||
{ |
|||
'url': 'rsync://foo@host:911/sup', |
|||
'protocol': 'rsync:', |
|||
'username': 'foo', |
|||
'hostname': 'host', |
|||
'port': '911', |
|||
'pathname': '/sup' |
|||
}, |
|||
{ |
|||
'url': 'git://github.com/foo/bar.git', |
|||
'protocol': 'git:', |
|||
'hostname': 'github.com', |
|||
'pathname': '/foo/bar.git' |
|||
}, |
|||
{ |
|||
'url': 'irc://myserver.com:6999/channel?passwd', |
|||
'protocol': 'irc:', |
|||
'hostname': 'myserver.com', |
|||
'port': '6999', |
|||
'pathname': '/channel', |
|||
'search': '?passwd' |
|||
}, |
|||
{ |
|||
'url': 'dns://fw.example.org:9999/foo.bar.org?type=TXT', |
|||
'protocol': 'dns:', |
|||
'hostname': 'fw.example.org', |
|||
'port': '9999', |
|||
'pathname': '/foo.bar.org', |
|||
'search': '?type=TXT' |
|||
}, |
|||
{ |
|||
'url': 'ldap://localhost:389/ou=People,o=JNDITutorial', |
|||
'protocol': 'ldap:', |
|||
'hostname': 'localhost', |
|||
'port': '389', |
|||
'pathname': '/ou=People,o=JNDITutorial' |
|||
}, |
|||
{ |
|||
'url': 'git+https://github.com/foo/bar', |
|||
'protocol': 'git+https:', |
|||
'hostname': 'github.com', |
|||
'pathname': '/foo/bar' |
|||
}, |
|||
{ |
|||
'url': 'urn:ietf:rfc:2648', |
|||
'protocol': 'urn:', |
|||
'pathname': 'ietf:rfc:2648' |
|||
}, |
|||
{ |
|||
'url': 'tag:joe@example.org,2001:foo/bar', |
|||
'protocol': 'tag:', |
|||
'pathname': 'joe@example.org,2001:foo/bar' |
|||
} |
|||
]; |
|||
|
|||
additional_tests.forEach((test) => { |
|||
const u = new URL(test.url); |
|||
if (test.protocol) assert.strictEqual(test.protocol, u.protocol); |
|||
if (test.username) assert.strictEqual(test.username, u.username); |
|||
if (test.password) assert.strictEqual(test.password, u.password); |
|||
if (test.hostname) assert.strictEqual(test.hostname, u.hostname); |
|||
if (test.host) assert.strictEqual(test.host, u.host); |
|||
if (test.port !== undefined) assert.strictEqual(test.port, u.port); |
|||
if (test.pathname) assert.strictEqual(test.pathname, u.pathname); |
|||
if (test.search) assert.strictEqual(test.search, u.search); |
|||
if (test.hash) assert.strictEqual(test.hash, u.hash); |
|||
}); |
@ -0,0 +1,36 @@ |
|||
'use strict'; |
|||
|
|||
require('../common'); |
|||
const assert = require('assert'); |
|||
const URL = require('url').URL; |
|||
|
|||
const serialized = 'a=a&a=1&a=true&a=undefined&a=null&a=%5Bobject%20Object%5D'; |
|||
const values = ['a', 1, true, undefined, null, {}]; |
|||
|
|||
const m = new URL('http://example.org'); |
|||
const sp = m.searchParams; |
|||
|
|||
assert(sp); |
|||
assert.strictEqual(sp.toString(), ''); |
|||
assert.strictEqual(m.search, ''); |
|||
|
|||
assert(!sp.has('a')); |
|||
values.forEach((i) => sp.set('a', i)); |
|||
assert(sp.has('a')); |
|||
assert.strictEqual(sp.get('a'), '[object Object]'); |
|||
sp.delete('a'); |
|||
assert(!sp.has('a')); |
|||
values.forEach((i) => sp.append('a', i)); |
|||
assert(sp.has('a')); |
|||
assert.strictEqual(sp.getAll('a').length, 6); |
|||
assert.strictEqual(sp.get('a'), 'a'); |
|||
|
|||
assert.strictEqual(sp.toString(), serialized); |
|||
|
|||
assert.strictEqual(m.search, `?${serialized}`); |
|||
|
|||
var key, val, n = 0; |
|||
for ([key, val] of sp) { |
|||
assert.strictEqual(key, 'a'); |
|||
assert.strictEqual(val, String(values[n++])); |
|||
} |
@ -0,0 +1,24 @@ |
|||
'use strict'; |
|||
|
|||
const common = require('../common'); |
|||
const path = require('path'); |
|||
const URL = require('url').URL; |
|||
const assert = require('assert'); |
|||
const attrs = require(path.join(common.fixturesDir, 'url-setter-tests.json')); |
|||
|
|||
for (const attr in attrs) { |
|||
if (attr === 'comment') |
|||
continue; |
|||
const tests = attrs[attr]; |
|||
var n = 0; |
|||
for (const test of tests) { |
|||
if (test.skip) continue; |
|||
n++; |
|||
const url = new URL(test.href); |
|||
url[attr] = test.new_value; |
|||
for (const test_attr in test.expected) { |
|||
assert.equal(test.expected[test_attr], url[test_attr], |
|||
`${n} ${attr} ${test_attr} ${test.href} ${test.comment}`); |
|||
} |
|||
} |
|||
} |
Loading…
Reference in new issue