diff --git a/.gitmodules b/.gitmodules index 3a63130..14a014f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "deps/uriparser"] - path = deps/uriparser - url = git://uriparser.git.sourceforge.net/gitroot/uriparser/uriparser +[submodule "deps/ngx_url_parser"] + path = deps/ngx_url_parser + url = https://github.com/DreamLab/ngx_url_parser diff --git a/deps/ngx_url_parser b/deps/ngx_url_parser new file mode 160000 index 0000000..9d2bd75 --- /dev/null +++ b/deps/ngx_url_parser @@ -0,0 +1 @@ +Subproject commit 9d2bd753ba7aa2d4510e79ad429d0e4ecb139905 diff --git a/deps/uriparser b/deps/uriparser deleted file mode 160000 index c87f53f..0000000 --- a/deps/uriparser +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c87f53f727ed138739e0fd1b9345015e5b70bb3b diff --git a/examples/benchmark1-NGX.js b/examples/benchmark1-NGX.js new file mode 100644 index 0000000..242d126 --- /dev/null +++ b/examples/benchmark1-NGX.js @@ -0,0 +1,28 @@ +/* + * Copyright (C) DreamLab Onet.pl Sp. z o. o. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +var uriparser = require('../build/default/uriparser'), url = "http://10.177.51.76:1337//example/dir/hi", matches; + +for (var i = 0; i < 2000000; i++) { + matches = uriparser.parse(url, uriparser.Uri.PROTOCOL, uriparser.Engines.NGINX); +} + diff --git a/examples/example.js b/examples/example.js new file mode 100644 index 0000000..405b575 --- /dev/null +++ b/examples/example.js @@ -0,0 +1,27 @@ +/* + * Copyright (C) DreamLab Onet.pl Sp. z o. o. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +var uriparser = require('../bin/uriparser'), url = "http://10.177.51.76:1337//example/dir/hi", matches; + +matches = uriparser.parse(url); +console.info(matches); + diff --git a/package.json b/package.json index 379b627..d486f24 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,12 @@ { "name": "uriparser", - "version": "1.2.8", + "version": "1.2.16", "description": "Fastest uriparser, written in C", "keywords": ["uriparser", "uri", "parser", "url"], - "author": "Jakub Lekstan ", + "contributors": [ + "Jakub Lekstan ", + "Marcin Kaciuba " + ], "dependencies": {}, "main": "./lib/bindings.js", "repository": { @@ -14,6 +17,6 @@ "preinstall": "node-waf clean || true; node-waf configure build" }, "engines": { - "node": ">= 0.4.0" + "node": ">= 0.4.0 < 0.8.0" } } diff --git a/src/node-uriparser.cc b/src/node-uriparser.cc index 1dfaaae..a5ffd03 100644 --- a/src/node-uriparser.cc +++ b/src/node-uriparser.cc @@ -22,11 +22,16 @@ #include #include -#include +#include +#include +#include +#include +#include -#define THROW_IF_NULL(var) if (var.afterLast == NULL) { \ - return v8::ThrowException(v8::Exception::SyntaxError(v8::String::New("Bad string given"))); \ - } +#include "parsers.hpp" + +#define ENCODED_BRACKETS "%5B%5D" +#define BRACKETS "[]" enum parseOptions { kProtocol = 1, @@ -39,19 +44,22 @@ enum parseOptions { kAll = kProtocol | kAuth | kHost | kPort | kQuery | kFragment | kPath }; + static v8::Persistent protocol_symbol = NODE_PSYMBOL("protocol"); static v8::Persistent auth_symbol = NODE_PSYMBOL("auth"); static v8::Persistent host_symbol = NODE_PSYMBOL("host"); static v8::Persistent port_symbol = NODE_PSYMBOL("port"); static v8::Persistent query_symbol = NODE_PSYMBOL("query"); +static v8::Persistent query_arr_suffix = NODE_PSYMBOL("queryArraySuffix"); +static v8::Persistent query_separator = NODE_PSYMBOL("querySeparator"); static v8::Persistent fragment_symbol = NODE_PSYMBOL("fragment"); static v8::Persistent path_symbol = NODE_PSYMBOL("path"); static v8::Persistent user_symbol = NODE_PSYMBOL("user"); +static v8::Persistent search_symbol = NODE_PSYMBOL("search"); static v8::Persistent password_symbol = NODE_PSYMBOL("password"); static v8::Handle parse(const v8::Arguments& args){ v8::HandleScope scope; - parseOptions opts = kAll; if (args.Length() == 0 || !args[0]->IsString()) { @@ -62,149 +70,188 @@ static v8::Handle parse(const v8::Arguments& args){ opts = static_cast(args[1]->Int32Value()); } - v8::String::AsciiValue url (args[0]->ToString()); + v8::String::Utf8Value url(args[0]->ToString()); if (url.length() == 0) { return v8::ThrowException(v8::Exception::TypeError(v8::String::New("String mustn't be empty"))); } - UriParserStateA state; - UriUriA uri; + v8::PropertyAttribute attrib = (v8::PropertyAttribute) (v8::ReadOnly | v8::DontDelete); + v8::Local data = v8::Object::New(); - state.uri = &uri; + Url uri; + NgxParser parser(*url); - if (uriParseUriA(&state, *url) != URI_SUCCESS) { + if (parser.status != Parser::OK) { return v8::ThrowException(v8::Exception::Error(v8::String::New("Unable to parse given url"))); } + uri = parser.url; - v8::PropertyAttribute attrib = (v8::PropertyAttribute) (v8::ReadOnly | v8::DontDelete); - v8::Local data = v8::Object::New(); - - if (uri.scheme.first && opts & kProtocol) { - THROW_IF_NULL(uri.scheme) - // +1 here because we need : after protocol - data->Set(protocol_symbol, v8::String::New(uri.scheme.first, (uri.scheme.afterLast - uri.scheme.first) + 1), attrib); + if (uri.scheme.start && (opts & kProtocol)) { + // +1 becasue we need ":" in scheme/protocol + data->Set(protocol_symbol, v8::String::New(uri.scheme.start, uri.scheme.len + 1), attrib); } - if (uri.userInfo.first && opts & kAuth) { - THROW_IF_NULL(uri.userInfo) - char *auth = (char *) uri.userInfo.first; - char *authPtr, *authUser, *authPassword; + if (uri.auth.start && (opts & kAuth)) { const char *delim = ":"; - auth[uri.userInfo.afterLast - uri.userInfo.first] = '\0'; + char *authPtr, *authUser, *authPassword; + + char * auth = new char[uri.auth.len + 1]; + std::strncpy(auth, uri.auth.start, uri.auth.len); + auth[uri.auth.len] = '\0'; authUser = strtok_r(auth, delim, &authPtr); authPassword = strtok_r(NULL, delim, &authPtr); if (authUser != NULL && authPassword != NULL) { v8::Local authData = v8::Object::New(); - authData->Set(user_symbol, v8::String::New(authUser)), attrib; + authData->Set(user_symbol, v8::String::New(authUser), attrib); authData->Set(password_symbol, v8::String::New(authPassword), attrib); data->Set(auth_symbol, authData, attrib); } + + delete[] auth; } - if (uri.hostText.first && opts & kHost) { - THROW_IF_NULL(uri.hostText) - data->Set(host_symbol, v8::String::New(uri.hostText.first, uri.hostText.afterLast - uri.hostText.first), attrib); + if (uri.host.start && (opts & kHost)) { + data->Set(host_symbol, v8::String::New(uri.host.start, uri.host.len), attrib); } - if (uri.portText.first && opts & kPort) { - THROW_IF_NULL(uri.portText) - data->Set(port_symbol, v8::String::New(uri.portText.first, uri.portText.afterLast - uri.portText.first), attrib); + if (uri.port.start && (opts & kPort)) { + data->Set(port_symbol, v8::String::New(uri.port.start, uri.port.len), attrib); } - if (uri.query.first && opts & kQuery) { - THROW_IF_NULL(uri.query) - char *query = (char *) uri.query.first; - const char *amp = "&", *sum = "="; + if (uri.query.start && (opts & kQuery)) { + std::map > paramsMap; + std::vector paramsOrder; + paramsOrder.reserve(uri.query.len / 2); + char *query = new char[uri.query.len + 2]; + std::strncpy(query, uri.query.start - 1, uri.query.len + 1); + query[uri.query.len + 1] = '\0'; + + const char *amp = "&", *sum = "=", *semicolon = ";", *separator = amp; char *queryParamPairPtr, *queryParam, *queryParamKey, *queryParamValue, *queryParamPtr; bool empty = true; - - query[uri.query.afterLast - uri.query.first] = '\0'; - queryParam = strtok_r(query, amp, &queryParamPairPtr); + v8::Local qsSuffix = v8::Object::New(); + data->Set(search_symbol, v8::String::New(query), attrib); + query++; + + // find qs delimeter & or ; + for (size_t i = 0; i < uri.query.len; ++i) { + if (query[i] == *amp) { + separator = amp; + break; + } else if (query[i] == *semicolon) { + separator = semicolon; + break; + } + } - v8::Local queryData = v8::Object::New(); + data->Set(query_separator, v8::String::New(separator), attrib); + queryParam = strtok_r(query, separator, &queryParamPairPtr); + + v8::Local queryData = v8::Object::New(); + bool arrayBrackets = false; while (queryParam) { if (*queryParam != *sum) { empty = false; + uint16_t len, queryLen; + queryLen = strlen(queryParam); queryParamKey = strtok_r(queryParam, sum, &queryParamPtr); - queryParamValue = strtok_r(NULL, sum, &queryParamPtr); - queryData->Set(v8::String::New(queryParamKey), v8::String::New(queryParamValue ? queryParamValue : ""), attrib); + len = strlen(queryParamKey); + if (len > (sizeof(ENCODED_BRACKETS) - 1) && + !strncmp(queryParamKey + len - (sizeof(ENCODED_BRACKETS) - 1), + ENCODED_BRACKETS, + sizeof(ENCODED_BRACKETS) - 1)) { + arrayBrackets = true; + queryParamKey[len - (sizeof(ENCODED_BRACKETS) - 1)] = '\0'; + qsSuffix->Set(v8::String::New(queryParamKey), v8::String::New(ENCODED_BRACKETS)); + } else if (len > (sizeof(BRACKETS) - 1) && + !strncmp(queryParamKey + len - (sizeof(BRACKETS) - 1), + BRACKETS, + sizeof(BRACKETS) - 1)) { + arrayBrackets = true; + queryParamKey[len - (sizeof(BRACKETS) - 1)] = '\0'; + qsSuffix->Set(v8::String::New(queryParamKey), v8::String::New(BRACKETS)); + } + + queryParamValue = strtok_r(NULL, separator, &queryParamPtr); + if (paramsMap.find(queryParamKey) == paramsMap.end()) { + paramsOrder.push_back(queryParamKey); + } + if (queryLen - len > 0) { + paramsMap[queryParamKey].push_back(queryParamValue ? queryParamValue: ""); + } else { + paramsMap[queryParamKey].push_back(NULL); + } + } + queryParam = strtok_r(NULL, separator, &queryParamPairPtr); + } + + for (std::vector::iterator it=paramsOrder.begin(); it!=paramsOrder.end(); ++it) { + v8::Local key = v8::String::New(*it); + std::vector &vals = paramsMap[*it]; + int arrSize = vals.size(); + if (arrSize > 1 || qsSuffix->Has(key)) { + v8::Local arrVal = v8::Array::New(arrSize); + + int i = 0; + for (std::vector::iterator it2 = vals.begin(); it2 != vals.end(); ++it2) { + if (*it2 != NULL) { + arrVal->Set(i, v8::String::New(*it2)); + } else { + arrVal->Set(i, v8::Null()); + } + i++; + } + queryData->Set(key, arrVal); + } else { + const char * tmp = vals.front(); + if (tmp) { + queryData->Set(key, v8::String::New(vals.front())); + } else { + queryData->Set(key, v8::Null()); + } } - queryParam = strtok_r(NULL, amp, &queryParamPairPtr); } //no need for empty object if the query string is going to be wrong if (!empty) { data->Set(query_symbol, queryData, attrib); + if (arrayBrackets) { + data->Set(query_arr_suffix, qsSuffix, attrib); + } } - //parsing the path will be easier + query--; - *query = '\0'; + delete[] query; } - if (uri.fragment.first && opts & kFragment) { - THROW_IF_NULL(uri.fragment); - data->Set(fragment_symbol, v8::String::New(uri.fragment.first, uri.fragment.afterLast - uri.fragment.first), attrib); + if (uri.fragment.start && (opts & kFragment)) { + data->Set(fragment_symbol, v8::String::New(uri.fragment.start, uri.fragment.len), attrib); } - if (uri.pathHead && uri.pathHead->text.first && opts & kPath) { - int len = 0; - - - UriPathSegmentA *pathHead = uri.pathHead; - do { - THROW_IF_NULL(pathHead->text) - if (pathHead->text.first == pathHead->text.afterLast) { - len++; - } else { - //len +1 because we need to add slash - len += pathHead->text.afterLast - pathHead->text.first + 1; - } - pathHead = pathHead->next; - } while (pathHead); - - char *path, *p, *tmp; - path = p = tmp = (char *) malloc(len + 1); - path[len] = '\0'; - - pathHead = uri.pathHead; - do { - //adding slash no matter what - *path = '/'; - path++; - //copying text only in case when path segment is not empty - if (pathHead->text.first != pathHead->text.afterLast) { - int len = pathHead->text.afterLast - pathHead->text.first; - memcpy(path, pathHead->text.first, len); - path += len; - } - pathHead = pathHead->next; - } while (pathHead); - - //path is always prefixed with / in case of relative url we move pointer further - if (!uri.absolutePath && !uri.hostText.first) { - p++; + if (opts & kPath) { + if (uri.path.start) { + data->Set(path_symbol, v8::String::New(uri.path.start, uri.path.len), attrib); + } else { + data->Set(path_symbol, v8::String::New("/"), attrib); } - - data->Set(path_symbol, v8::String::New(p), attrib); - free(tmp); - } else if (opts & kPath) { - data->Set(path_symbol, v8::String::New("/"), attrib); } - uriFreeUriMembersA(&uri); return scope.Close(data); } + extern "C" void init (v8::Handle target){ v8::HandleScope scope; NODE_SET_METHOD(target, "parse", parse); + + // Old properties NODE_DEFINE_CONSTANT(target, kProtocol); NODE_DEFINE_CONSTANT(target, kAuth); NODE_DEFINE_CONSTANT(target, kHost); @@ -213,4 +260,17 @@ extern "C" void init (v8::Handle target){ NODE_DEFINE_CONSTANT(target, kFragment); NODE_DEFINE_CONSTANT(target, kPath); NODE_DEFINE_CONSTANT(target, kAll); + + v8::Handle uri = v8::Object::New(); + uri->Set(v8::String::New("PROTOCOL"), v8::Integer::New(kProtocol)); + uri->Set(v8::String::New("AUTH"), v8::Integer::New(kAuth)); + uri->Set(v8::String::New("HOST"), v8::Integer::New(kHost)); + uri->Set(v8::String::New("PORT"), v8::Integer::New(kPort)); + uri->Set(v8::String::New("QUERY"), v8::Integer::New(kQuery)); + uri->Set(v8::String::New("FRAGMENT"), v8::Integer::New(kFragment)); + uri->Set(v8::String::New("ALL"), v8::Integer::New(kAll)); + target->Set(v8::String::New("Uri"), uri); + } + +NODE_MODULE(uriparser, init) diff --git a/src/parsers.hpp b/src/parsers.hpp new file mode 100644 index 0000000..2f0538f --- /dev/null +++ b/src/parsers.hpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) DreamLab Onet.pl Sp. z o. o. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +extern "C" { + #include +} + +// base structure for url properties +typedef struct { + const char * start; + size_t len; +} Element; + +typedef struct { + Element scheme; + Element host; + Element path; + Element query; + Element fragment; + Element port; + Element auth; +} Url; + + +class Parser { + public: + Parser(const char * str): m_str(str) { + url.scheme.start = NULL; + url.host.start = NULL; + url.path.start = NULL; + url.query.start = NULL; + url.fragment.start = NULL; + url.port.start = NULL; + url.auth.start = NULL; + } + + virtual ~Parser() { + } + + enum Status { + ERROR = -1, + OK, + }; + + Status status; + Url url; + protected: + const char * m_str; +}; + +class NgxParser: public Parser { + public: + NgxParser(const char * str): Parser(str), url_meta() { + if (ngx_url_parser_meta(&url_meta, str) == NGX_URL_OK) { + status = Parser::OK; + } else { + status = Parser::ERROR; + return; + } + + url.scheme.start = url_meta.scheme_start; + url.scheme.len = url_meta.scheme_end - url_meta.scheme_start; + url.host.start = url_meta.host_start; + url.host.len = url_meta.host_end - url_meta.host_start; + url.path.start = url_meta.uri_start; + url.path.len = url_meta.uri_end - url_meta.uri_start; + url.query.start = url_meta.args_start; + url.query.len = url_meta.args_end - url_meta.args_start; + url.fragment.start = url_meta.fragment_start; + url.fragment.len = url_meta.fragment_end - url_meta.fragment_start; + url.port.start = url_meta.port_start; + url.port.len = url_meta.port_end - url_meta.port_start; + url.auth.start = url_meta.auth_start; + url.auth.len = url_meta.auth_end - url_meta.auth_start; + } + + virtual ~NgxParser() { + } + + protected: + ngx_http_url_meta url_meta; +}; diff --git a/tests/exceptionSpec.js b/tests/exceptionSpec.js index 2f84b48..c2812fd 100644 --- a/tests/exceptionSpec.js +++ b/tests/exceptionSpec.js @@ -35,9 +35,9 @@ describe("uriparser - exceptions", function () { }).toThrow(); }); - it("wrong url given", function () { + it("incorrect url - no auth", function () { expect(function () { - uriparser.parse("http://www.dreamlab.pl/a[]=1"); + uriparser.parse("http://:www.dreamlab.pl"); }).toThrow(); }); }); diff --git a/tests/parseSpec.js b/tests/parseSpec.js index 3edd8a9..760a53d 100644 --- a/tests/parseSpec.js +++ b/tests/parseSpec.js @@ -2,7 +2,7 @@ * Copyright (C) DreamLab Onet.pl Sp. z o. o. * * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal + * of this software and associated documentation files (the 'Software'), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is @@ -11,7 +11,7 @@ * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER @@ -19,142 +19,447 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -var uriparser = require("../lib/bindings.js"); +var uriparser = require('../lib/bindings.js'); - -describe("uriparser - parse", function () { - it("http://dreamlab.pl", function () { - var url = uriparser.parse("http://dreamlab.pl"); +describe('uriparser - parse', function () { + it('http://dreamlab.pl', function () { + var url = uriparser.parse('http://dreamlab.pl'); expect(Object.keys(url).length).toEqual(3); - expect(url.protocol).toEqual("http:"); - expect(url.host).toEqual("dreamlab.pl"); - expect(url.path).toEqual("/"); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('dreamlab.pl'); + expect(url.path).toBe('/'); }); - it("http://dreamlab.pl/", function () { - var url = uriparser.parse("http://dreamlab.pl/"); + it('http://dreamlab.pl/', function () { + var url = uriparser.parse('http://dreamlab.pl/'); expect(Object.keys(url).length).toEqual(3); - expect(url.protocol).toEqual("http:"); - expect(url.host).toEqual("dreamlab.pl"); - expect(url.path).toEqual("/"); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('dreamlab.pl'); + expect(url.path).toBe('/'); }); - it("/1.txt?test1=okok", function () { - var url = uriparser.parse("/1.txt?test1=okok"); + it('1.txt?test1=okok', function () { + var url = uriparser.parse('1.txt?test1=okok'); - expect(Object.keys(url).length).toEqual(2); - expect(url.path).toEqual("/1.txt"); - expect(url.query).toEqual({"test1": "okok"}); + expect(Object.keys(url).length).toEqual(4); + expect(url.path).toBe('1.txt'); + expect(url.query).toEqual({'test1': 'okok'}); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?test1=okok'); }); - it("1.txt?test1=okok", function () { - var url = uriparser.parse("1.txt?test1=okok"); + it('/1.txt?test1=okok', function () { + var url = uriparser.parse('/1.txt?test1=okok'); - expect(Object.keys(url).length).toEqual(2); - expect(url.path).toEqual("1.txt"); - expect(url.query).toEqual({"test1": "okok"}); + expect(Object.keys(url).length).toEqual(4); + expect(url.path).toBe('/1.txt'); + expect(url.query).toEqual({'test1': 'okok'}); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?test1=okok'); }); - it("http://dreamlab.pl/X?X", function () { - var url = uriparser.parse("http://dreamlab.pl/X?X"); + it('http://dreamlab.pl/X?X', function () { + var url = uriparser.parse('http://dreamlab.pl/X?X'); - expect(Object.keys(url).length).toEqual(4); - expect(url.protocol).toEqual("http:"); - expect(url.host).toEqual("dreamlab.pl"); - expect(url.path).toEqual("/X"); - expect(url.query).toEqual({"X": ""}); + expect(Object.keys(url).length).toEqual(6); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('dreamlab.pl'); + expect(url.path).toBe('/X'); + expect(url.query).toEqual({'X': null}); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?X'); }); - it("http://dreamlab.pl/X", function () { - var url = uriparser.parse("http://dreamlab.pl/X"); + it('http://dreamlab.pl/X', function () { + var url = uriparser.parse('http://dreamlab.pl/X'); expect(Object.keys(url).length).toEqual(3); - expect(url.protocol).toEqual("http:"); - expect(url.host).toEqual("dreamlab.pl"); - expect(url.path).toEqual("/X"); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('dreamlab.pl'); + expect(url.path).toBe('/X'); }); - it("http://dreamlab.pl/hejka/", function () { - var url = uriparser.parse("http://dreamlab.pl/hejka/"); + it('http://dreamlab.pl/hejka/', function () { + var url = uriparser.parse('http://dreamlab.pl/hejka/'); expect(Object.keys(url).length).toEqual(3); - expect(url.protocol).toEqual("http:"); - expect(url.host).toEqual("dreamlab.pl"); - expect(url.path).toEqual("/hejka/"); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('dreamlab.pl'); + expect(url.path).toBe('/hejka/'); }); - it("http://dreamlab.pl/hejka/?hejka=123", function () { - var url = uriparser.parse("http://dreamlab.pl/hejka/?hejka=123"); + it('http://dreamlab.pl/hejka/?hejka=123', function () { + var url = uriparser.parse('http://dreamlab.pl/hejka/?hejka=123'); - expect(Object.keys(url).length).toEqual(4); - expect(url.protocol).toEqual("http:"); - expect(url.host).toEqual("dreamlab.pl"); - expect(url.path).toEqual("/hejka/"); - expect(url.query).toEqual({"hejka": "123"}) + expect(Object.keys(url).length).toEqual(6); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('dreamlab.pl'); + expect(url.path).toBe('/hejka/'); + expect(url.query).toEqual({'hejka': "123"}) + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?hejka=123'); }); - it("http://:@www.dreamlab.pl/?", function () { - var url = uriparser.parse("http://:@www.dreamlab.pl/?"); + it('http://:@www.dreamlab.pl/?', function () { + var url = uriparser.parse('http://:@www.dreamlab.pl/?'); - expect(Object.keys(url).length).toEqual(3); - expect(url.protocol).toEqual("http:"); - expect(url.host).toEqual("www.dreamlab.pl"); - expect(url.path).toEqual("/"); + expect(Object.keys(url).length).toEqual(5); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.path).toBe('/'); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?'); + }); + + it('http://user:@www.dreamlab.pl/?', function () { + var url = uriparser.parse('http://user:@www.dreamlab.pl/?'); + + expect(Object.keys(url).length).toEqual(5); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.path).toBe('/'); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?'); }); - it("http://user:@www.dreamlab.pl/?", function () { - var url = uriparser.parse("http://user:@www.dreamlab.pl/?"); + it('http://:pw@www.dreamlab.pl/?', function () { + var url = uriparser.parse('http://:pw@www.dreamlab.pl/?'); + + expect(Object.keys(url).length).toEqual(5); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.path).toBe('/'); + expect(url.search).toBe('?'); + }); + + it('http://user:pw@www.dreamlab.pl/?', function () { + var url = uriparser.parse('http://user:pw@www.dreamlab.pl/?'); + + expect(Object.keys(url).length).toEqual(6); + expect(url.protocol).toBe('http:'); + expect(url.auth).toEqual({'user': "user", "password": "pw"}); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.path).toBe('/'); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?'); + }); + + it('http://www.dreamlab.pl/?=&=', function () { + var url = uriparser.parse('http://www.dreamlab.pl/?=&='); + + expect(Object.keys(url).length).toEqual(5); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.path).toBe('/'); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?=&='); + }); + + it('http://www.dreamlab.pl//test/123', function () { + var url = uriparser.parse('http://www.dreamlab.pl//test/123'); expect(Object.keys(url).length).toEqual(3); - expect(url.protocol).toEqual("http:"); - expect(url.host).toEqual("www.dreamlab.pl"); - expect(url.path).toEqual("/"); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.path).toBe('//test/123'); }); - it("http://:pw@www.dreamlab.pl/?", function () { - var url = uriparser.parse("http://:pw@www.dreamlab.pl/?"); + it('http://www.dreamlab.pl/test//123', function () { + var url = uriparser.parse('http://www.dreamlab.pl/test//123'); expect(Object.keys(url).length).toEqual(3); - expect(url.protocol).toEqual("http:"); - expect(url.host).toEqual("www.dreamlab.pl"); - expect(url.path).toEqual("/"); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.path).toBe('/test//123'); + }); + + it('http://www.dreamlab.pl/test/?a=1&b=1&a=2&c=3', function () { + var url = uriparser.parse('http://www.dreamlab.pl/test/?a=1&b=4&a=2&c=3'); + + expect(Object.keys(url).length).toEqual(6); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.query).toEqual({a: ['1', '2'], b: '4', c: '3'}); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?a=1&b=4&a=2&c=3'); }); - it("http://user:pw@www.dreamlab.pl/?", function () { - var url = uriparser.parse("http://user:pw@www.dreamlab.pl/?"); + it('http://www.dreamlab.pl/test/?b=1&c=3&a%5B%5D=1&a%5B%5D=1', function () { + var url = uriparser.parse('http://www.dreamlab.pl/test/?b=1&c=3&a%5B%5D=1&a%5B%5D=2'); + + expect(Object.keys(url).length).toEqual(7); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.query).toEqual({b: '1', c: '3', a: ['1', '2']}); + expect(url.queryArraySuffix).toEqual({a: '%5B%5D'}); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?b=1&c=3&a%5B%5D=1&a%5B%5D=2'); + }); + + it('http://www.dreamlab.pl/test/test1/a[]=1&a[]=2', function () { + var url = uriparser.parse('http://www.dreamlab.pl/test/test1/?a[]=1&a[]=2'); + + expect(Object.keys(url).length).toEqual(7); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.path).toBe('/test/test1/'); + expect(url.query).toEqual({a: ['1', '2']}); + expect(url.queryArraySuffix).toEqual({a: '[]'}); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?a[]=1&a[]=2'); + }); + + it('http://www.dreamlab.pl/test/test1/a[name]=1&a[name]=2', function () { + var url = uriparser.parse('http://www.dreamlab.pl/test/test1/?a[name]=1&a[name]=2'); + + expect(Object.keys(url).length).toEqual(6); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.path).toBe('/test/test1/'); + expect(url.query).toEqual({'a[name]': ['1', '2']}); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?a[name]=1&a[name]=2'); + }); + + it('http://user:pw@www.dreamlab.pl/test/test1/a[name]=1&a[name]=2#fragment', function () { + var url = uriparser.parse('http://user:pw@www.dreamlab.pl/test/test1/?a[name]=1&a[name]=2#fragment'); + + expect(Object.keys(url).length).toEqual(8); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.path).toBe('/test/test1/'); + expect(url.query).toEqual({'a[name]': ['1', '2']}); + expect(url.auth).toEqual({'user': 'user', 'password': 'pw'}); + expect(url.fragment).toBe('fragment'); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?a[name]=1&a[name]=2'); + }); + + it('http://www.dreamlab.pl/test/test1/?b=3&a=1&a=2', function () { + var url = uriparser.parse('http://www.dreamlab.pl/test/test1/?b=3&a=1&a=2'); + + expect(Object.keys(url).length).toEqual(6); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.path).toBe('/test/test1/'); + expect(url.query).toEqual({b:'3', a: ['1', '2']}); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?b=3&a=1&a=2'); + }); + + it('http://dreamlab.pl:/', function () { + var url = uriparser.parse('http://www.dreamlab.pl:/'); expect(Object.keys(url).length).toEqual(4); - expect(url.protocol).toEqual("http:"); - expect(url.auth).toEqual({"user": "user", "password": "pw"}); - expect(url.host).toEqual("www.dreamlab.pl"); - expect(url.path).toEqual("/"); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.port).toBe(''); }); - it("http://www.dreamlab.pl/?=&=", function () { - var url = uriparser.parse("http://www.dreamlab.pl/?=&="); + it('http://dreamlab.pl:/ - only protocol', function () { + var url = uriparser.parse('http://www.dreamlab.pl:/', uriparser.Uri.PROTOCOL); + expect(Object.keys(url).length).toEqual(1); + expect(url.protocol).toBe('http:'); + }); - expect(Object.keys(url).length).toEqual(3); - expect(url.protocol).toEqual("http:"); - expect(url.host).toEqual("www.dreamlab.pl"); - expect(url.path).toEqual("/"); + it('http://dreamlab.pl:', function () { + var url = uriparser.parse('http://www.dreamlab.pl:'); + + expect(Object.keys(url).length).toEqual(4); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.port).toBe(''); + }); + + it('http://dreamlab.pl:80/path/index.html?arg1=5', function () { + var url = uriparser.parse('http://www.dreamlab.pl:80/path/index.html?arg1=5'); + + expect(Object.keys(url).length).toEqual(7); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.port).toBe('80'); + expect(url.path).toBe('/path/index.html'); + expect(url.query).toEqual({arg1: '5'}); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?arg1=5'); }); - it("http://www.dreamlab.pl//test/123", function () { - var url = uriparser.parse("http://www.dreamlab.pl//test/123"); + it('http:////', function () { + var url = uriparser.parse('http:////'); expect(Object.keys(url).length).toEqual(3); - expect(url.protocol).toEqual("http:"); - expect(url.host).toEqual("www.dreamlab.pl"); - expect(url.path).toEqual("//test/123"); + expect(url.protocol).toBe('http:'); + expect(url.path).toBe('//'); + expect(url.host).toBe(''); + }); + + it('somestring', function () { + var url = uriparser.parse('somestring'); + + expect(Object.keys(url).length).toEqual(1); + expect(url.path).toBe('somestring'); }); - it("http://www.dreamlab.pl/test//123", function () { - var url = uriparser.parse("http://www.dreamlab.pl/test//123"); + it('#fragmen/t', function () { + var url = uriparser.parse('#fragmen/t'); + + expect(Object.keys(url).length).toEqual(2); + expect(url.fragment).toBe('fragmen/t'); + }); + + it('?query', function () { + var url = uriparser.parse('?query'); + + expect(Object.keys(url).length).toEqual(4); + expect(url.query).toEqual({'query': null}); + expect(url.search).toBe('?query'); + expect(url.querySeparator).toBe('&'); + }); + + it('https://user_usr:password@www.dreamlab.pl:81/path/index.html?arg1=1&arg2=2', function () { + var url = uriparser.parse('https://user_usr:password@www.dreamlab.pl:81/path/index.html?arg1=1&arg2=2'); + + expect(Object.keys(url).length).toEqual(8); + expect(url.protocol).toBe('https:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.port).toBe('81'); + expect(url.path).toBe('/path/index.html'); + expect(url.auth).toEqual({user: 'user_usr', password: 'password'}); + expect(url.query).toEqual({arg1: '1', arg2: '2'}); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?arg1=1&arg2=2'); + }); + + it('https://zs.usernanme_cms.test.api.pl/', function () { + var url = uriparser.parse('https://zs.usernanme_cms.test.api.pl/'); expect(Object.keys(url).length).toEqual(3); - expect(url.protocol).toEqual("http:"); - expect(url.host).toEqual("www.dreamlab.pl"); - expect(url.path).toEqual("/test//123"); + expect(url.protocol).toBe('https:'); + expect(url.host).toBe('zs.usernanme_cms.test.api.pl'); + expect(url.path).toBe('/'); + }); + + it('http://www.dreamlab.pl/test/test1/?a=3;b=1;a=2', function () { + var url = uriparser.parse('http://www.dreamlab.pl/test/test1/?a=3;b=1;a=2'); + + expect(Object.keys(url).length).toEqual(6); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.path).toBe('/test/test1/'); + expect(url.query).toEqual({ a: ['3', '2'], b: '1'}); + expect(url.querySeparator).toBe(';'); + expect(url.search).toBe('?a=3;b=1;a=2'); + }); + + it('http://www.dreamlab.pl/test/test1/?a=3;b;a=2', function () { + var url = uriparser.parse('http://www.dreamlab.pl/test/test1/?a=3;b;a=2'); + + expect(Object.keys(url).length).toEqual(6); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.path).toBe('/test/test1/'); + expect(url.query).toEqual({ a: ['3', '2'], b: null}); + expect(url.querySeparator).toBe(';'); + expect(url.search).toBe('?a=3;b;a=2'); + }); + + it('http://www.dreamlab.pl/test/test1/?a=', function () { + var url = uriparser.parse('http://www.dreamlab.pl/test/test1/?a='); + + expect(Object.keys(url).length).toEqual(6); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.path).toBe('/test/test1/'); + expect(url.query).toEqual({ a: ''}); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?a='); + }); + + it('http://www.dreamlab.pl/test/test1/?a', function () { + var url = uriparser.parse('http://www.dreamlab.pl/test/test1/?a'); + + expect(Object.keys(url).length).toEqual(6); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.path).toBe('/test/test1/'); + expect(url.query).toEqual({ a: null}); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?a'); + }); + + it('//www.dreamlab.pl/test/test1/?a', function () { + var url = uriparser.parse('//www.dreamlab.pl/test/test1/?a'); + + expect(Object.keys(url).length).toEqual(5); + expect(url.host).toBe('www.dreamlab.pl'); + expect(url.path).toBe('/test/test1/'); + expect(url.query).toEqual({ a: null}); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?a'); + }); + + it('/?a', function () { + var url = uriparser.parse('/?a'); + + expect(Object.keys(url).length).toEqual(4); + expect(url.path).toBe('/'); + expect(url.query).toEqual({ a: null}); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?a'); + }); + + it('/?a=', function () { + var url = uriparser.parse('/?a='); + + expect(Object.keys(url).length).toEqual(4); + expect(url.path).toBe('/'); + expect(url.query).toEqual({ a: ''}); + expect(url.querySeparator).toBe('&'); + expect(url.search).toBe('?a='); + }); + + it('/.!@$%^&*()_-feed', function () { + var url = uriparser.parse('/.!@$%^&*()_-feed'); + + expect(Object.keys(url).length).toEqual(1); + expect(url.path).toBe('/.!@$%^&*()_-feed'); }); + + it('.!@$%^&*()_-feed', function () { + var url = uriparser.parse('.!@$%^&*()_-feed'); + + expect(Object.keys(url).length).toEqual(1); + expect(url.path).toBe('.!@$%^&*()_-feed'); + }); + + it('/adclick?a=1&d2dest=https://ad.doubleclick.net/ddm/trackclk/N100601.127443ONET.PL/B11524173.200789083;dc_trk_aid=400712373;dc_trk_cid=83040526;dc_lat=;dc_rdid=;tag_for_child_directed_treatment=', function () { + var url = uriparser.parse('/adclick?a=1&d2dest=https://ad.doubleclick.net/ddm/trackclk/N100601.127443ONET.PL/B11524173.200789083;dc_trk_aid=400712373;dc_trk_cid=83040526;dc_lat=;dc_rdid=;tag_for_child_directed_treatment='); + + expect(Object.keys(url).length).toEqual(4); + expect(url.path).toBe('/adclick'); + expect(url.search).toBe('?a=1&d2dest=https://ad.doubleclick.net/ddm/trackclk/N100601.127443ONET.PL/B11524173.200789083;dc_trk_aid=400712373;dc_trk_cid=83040526;dc_lat=;dc_rdid=;tag_for_child_directed_treatment='); + expect(url.query).toEqual({ a : '1', + d2dest : 'https://ad.doubleclick.net/ddm/trackclk/N100601.127443ONET.PL/B11524173.200789083;dc_trk_aid=400712373;dc_trk_cid=83040526;dc_lat=;dc_rdid=;tag_for_child_directed_treatment=' + }); + }); + + it('http://dreamlab.pl/?attach_external_tab&180508352&4&0&0&0&0&iexplore', function () { + + var url = uriparser.parse('http://dreamlab.pl/?attach_external_tab&180508352&4&0&0&0&0&iexplore'); + + expect(Object.keys(url).length).toEqual(6); + expect(url.protocol).toBe('http:'); + expect(url.host).toBe('dreamlab.pl'); + expect(url.search).toBe('?attach_external_tab&180508352&4&0&0&0&0&iexplore'); + expect(url.query).toEqual({ + '0': [ null, null, null, null ], + '4': null, + '180508352': null, + attach_external_tab: null, + iexplore: null + }); + expect(url.path).toBe('/'); + }); + }); diff --git a/wscript b/wscript index 030bf14..4771afa 100644 --- a/wscript +++ b/wscript @@ -7,7 +7,7 @@ bldir = "build" version = "1.0" def set_options(opt): - opt.add_option('-b', '--build-uri-parser', action='store_false', default=True, dest='buildUri', help='build with bundled uriparser'); + opt.add_option('-b', '--build-uri-parser', action='store_false', default=True, dest='buildUri', help='build with bundled deps'); opt.tool_options("compiler_cxx") def configure(conf): @@ -18,18 +18,18 @@ def do_clean(): for arg in sys.argv: if arg == 'clean': return True - + return False def build(bld): if Options.options.buildUri == True and do_clean() == False: - bld.exec_command("mkdir -p uriparser; cd ../deps/uriparser && ./autogen.sh && ./configure --with-pic --disable-test --disable-doc --disable-shared --prefix=%s && make clean install" % (bld.bdir + "/uriparser")) + bld.exec_command("mkdir -p ngx_url_parser; cd ../deps/ngx_url_parser && ./autogen.sh && ./configure --with-pic --disable-shared --prefix=%s && make clean install" %(bld.bdir + "/ngx_url_parser")) obj = bld.new_task_gen("cxx", "shlib", "node_addon") obj.target = "uriparser" obj.source = "src/node-uriparser.cc" - obj.lib = ["uriparser"] - obj.includes = [bld.bdir + "/uriparser/include/uriparser"] - obj.libpath = [bld.bdir + "/uriparser/lib"] - obj.cxxflags = ["-g"] - obj.cflags = ["-g"] + obj.lib = [ "ngx_url_parser"] + obj.includes = [bld.bdir + "/ngx_url_parser/include/ngx_url_parser"] + obj.libpath = [bld.bdir + '/ngx_url_parser/lib'] + obj.cxxflags = ["-O3"] + obj.cflags = ["-O3"]