Add fast parse and remove parseurl

master
Jonatan Nilsson 2019-10-11 19:41:53 +00:00
parent bd05c21456
commit 6b27b844ff
4 changed files with 112 additions and 6 deletions

57
lib/fastparse.js Normal file
View File

@ -0,0 +1,57 @@
const url = require('url');
/**
* Parse the `str` url with fast-path short-cut.
*
* @param {string} str
* @return {Object}
* @private
*/
module.exports = function fastparse(str) {
if (typeof str !== 'string' || str.charCodeAt(0) !== 0x2f /* / */) {
return url.parse(str);
}
let pathname = str;
let query = null;
let search = null;
// This takes the regexp from https://github.com/joyent/node/pull/7878
// Which is /^(\/[^?#\s]*)(\?[^#\s]*)?$/
// And unrolls it into a for loop
for (let i = 1; i < str.length; i++) {
switch (str.charCodeAt(i)) {
case 0x3f: /* ? */
if (search === null) {
pathname = str.substring(0, i);
query = str.substring(i + 1);
search = str.substring(i);
}
break;
case 0x09: /* \t */
case 0x0a: /* \n */
case 0x0c: /* \f */
case 0x0d: /* \r */
case 0x20: /* */
case 0x23: /* # */
case 0xa0:
case 0xfeff:
return url.parse(str);
}
}
let parsed = new url.Url();
parsed.path = str;
parsed.href = str;
parsed.pathname = pathname;
if (search !== null) {
parsed.query = query;
parsed.search = search;
}
parsed.__raw = str;
return parsed;
};

View File

@ -8,7 +8,7 @@
const URL = require('url').URL;
const net = require('net');
const stringify = require('url').format;
const parse = require('parseurl');
const fastparse = require('./fastparse');
const qs = require('querystring');
const typeis = require('type-is');
const fresh = require('fresh');
@ -139,7 +139,14 @@ module.exports = {
*/
get path() {
return parse(this.req).pathname;
return this.urlParsed.pathname;
},
get urlParsed() {
if (!this.req.__url || this.req.__url.__raw !== this.req.url) {
this.req.__url = fastparse(this.req.url);
}
return this.req.__url;
},
/**
@ -150,7 +157,7 @@ module.exports = {
*/
set path(path) {
const url = parse(this.req);
const url = this.urlParsed;
if (url.pathname === path) return;
url.pathname = path;
@ -192,7 +199,7 @@ module.exports = {
get querystring() {
if (!this.req) return '';
return parse(this.req).query || '';
return this.urlParsed.query || '';
},
/**
@ -203,7 +210,7 @@ module.exports = {
*/
set querystring(str) {
const url = parse(this.req);
const url = this.urlParsed;
if (url.search === `?${str}`) return;
url.search = str;

View File

@ -33,7 +33,6 @@
"koa-compose": "^4.1.0",
"koa-is-json": "^1.0.0",
"on-finished": "^2.3.0",
"parseurl": "^1.3.2",
"type-is": "^1.6.16",
"vary": "^1.1.2"
},

View File

@ -0,0 +1,43 @@
const assert = require('assert');
const fastparse = require('../../lib/fastparse');
const URL_EMPTY_VALUE = null;
describe('fastparse(url)', () => {
it('should parse the requrst URL', () => {
let url = fastparse('/foo/bar');
assert.strictEqual(url.host, URL_EMPTY_VALUE);
assert.strictEqual(url.hostname, URL_EMPTY_VALUE);
assert.strictEqual(url.href, '/foo/bar');
assert.strictEqual(url.pathname, '/foo/bar');
assert.strictEqual(url.port, URL_EMPTY_VALUE);
assert.strictEqual(url.query, URL_EMPTY_VALUE);
assert.strictEqual(url.search, URL_EMPTY_VALUE);
});
it('should parse with query string', () => {
let url = fastparse('/foo/bar?fizz=buzz');
assert.strictEqual(url.host, URL_EMPTY_VALUE);
assert.strictEqual(url.hostname, URL_EMPTY_VALUE);
assert.strictEqual(url.href, '/foo/bar?fizz=buzz');
assert.strictEqual(url.pathname, '/foo/bar');
assert.strictEqual(url.port, URL_EMPTY_VALUE);
assert.strictEqual(url.query, 'fizz=buzz');
assert.strictEqual(url.search, '?fizz=buzz');
});
it('should parse a full URL', () => {
let url = fastparse('http://localhost:8888/foo/bar');
assert.strictEqual(url.host, 'localhost:8888');
assert.strictEqual(url.hostname, 'localhost');
assert.strictEqual(url.href, 'http://localhost:8888/foo/bar');
assert.strictEqual(url.pathname, '/foo/bar');
assert.strictEqual(url.port, '8888');
assert.strictEqual(url.query, URL_EMPTY_VALUE);
assert.strictEqual(url.search, URL_EMPTY_VALUE);
});
it('should not choke on auth-looking URL', () => {
assert.strictEqual(fastparse('//todo@txt').pathname, '//todo@txt');
});
});