whistle
Version:
HTTP, HTTP2, HTTPS, Websocket debugging proxy
212 lines (178 loc) • 4.57 kB
JavaScript
var parseUrl = require('url').parse;
var CONTROL_RE = /^[\n\r\t\x00-\x20\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]+/;
var PROTOCOL_RE = /^([a-z][a-z0-9.+-]*:)?(\/\/)?([\\/]+)?([\S\s]*)/i;
var WIN_DRIVE_RE = /^[a-zA-Z]:/;
var PORT_RE = /:(\d*)$/;
function formatHost(host) {
if (host[0] === '[') {
var end = host.length - 1;
if (host[end] === ']') {
host = host.substring(1, end);
}
}
return host;
}
function needPort(port, protocol) {
protocol = protocol.split(':')[0];
port = +port;
if (!port) {
return false;
}
switch (protocol) {
case 'http':
case 'ws':
return port !== 80;
case 'https':
case 'wss':
return port !== 443;
case 'ftp':
return port !== 21;
case 'gopher':
return port !== 70;
case 'file':
return false;
}
return port !== 0;
}
function getProtocol(addr) {
var match = PROTOCOL_RE.exec(addr);
var protocol = match[1] ? match[1].toLowerCase() : '';
var forwardSlashes = match[2] || '';
var otherSlashes = match[3] || '';
var slashesCount = forwardSlashes.length + otherSlashes.length;
var rest = forwardSlashes + otherSlashes + match[4];
if (protocol === 'file:') {
if (slashesCount >= 2) {
rest = rest.slice(2);
}
} else if (protocol && forwardSlashes) {
rest = rest.slice(2);
}
return {
protocol: protocol,
slashes: !!forwardSlashes,
slashesCount: slashesCount,
rest: rest
};
}
function encode(str) {
try {
return encodeURIComponent(decodeURIComponent(str));
} catch (e) {
return '';
}
}
function formatPath(url) {
if (url.pathname.charAt(0) !== '/') {
url.pathname = '/' + url.pathname;
}
}
function formatPort(url) {
if (!needPort(url.port, url.protocol)) {
url.host = url.hostname;
url.port = '';
}
}
function parseAuth(url) {
url.username = url.password = '';
if (url.auth) {
var index = url.auth.indexOf(':');
if (index !== -1) {
url.username = url.auth.slice(0, index);
url.username = encode(url.username);
url.password = url.auth.slice(index + 1);
url.password = encode(url.password);
} else {
url.username = encode(url.auth);
}
url.auth = url.password ? url.username + ':' + url.password : url.username;
}
}
function formatOrigin(url) {
var protocol = url.protocol;
if (protocol !== 'file:' && url.host) {
url.origin = protocol +'//'+ url.host;
} else {
url.origin = 'null';
}
}
function formatUrl(url) {
var host = url.host;
var protocol = url.protocol;
var result = protocol + (protocol && url.slashes ? '//' : '');
if (url.username) {
result += url.username;
if (url.password) {
result += ':' + url.password;
}
result += '@';
} else if (url.password) {
result += ':' + url.password + '@';
}
result += host + url.pathname;
if (url.search) {
result += url.search;
}
if (url.hash) {
result += url.hash;
}
url.href = result;
}
module.exports = function(addr) {
try {
return parseUrl(addr);
} catch (e) {}
addr = String(addr || '').replace(CONTROL_RE, '');
var url = {};
var result = getProtocol(addr);
var protocol = result.protocol;
url.slashes = result.slashes;
url.protocol = protocol;
addr = result.rest;
var index = addr.indexOf('#');
if (index !== -1) {
url.hash = addr.substring(index);
addr = addr.substring(0, index);
}
index = addr.indexOf('?');
if (index !== -1) {
url.search = addr.substring(index);
url.query = addr.substring(index + 1);
addr = addr.substring(0, index);
}
if (
(protocol === 'file:' && (result.slashesCount !== 2 || WIN_DRIVE_RE.test(addr))) ||
(!result.slashes && (protocol || result.slashesCount < 2))
) {
url.pathname = addr;
url.host = url.hostname = '';
} else {
index = addr.indexOf('/');
if (index !== -1) {
url.pathname = addr.substring(index);
addr = addr.substring(0, index);
} else {
url.pathname = '/';
}
url.path = url.pathname + (url.search || '');
index = addr.lastIndexOf('@');
if (index !== -1) {
url.auth = addr.substring(0, index);
addr = addr.substring(index + 1);
}
url.host = addr;
index = PORT_RE.exec(addr);
if (index) {
url.port = index[1];
addr = addr.substring(0, index.index);
}
url.hostname = formatHost(addr);
}
formatPath(url);
formatPort(url);
parseAuth(url);
formatOrigin(url);
formatUrl(url);
return url;
};
module.exports.formatHost = formatHost;