deps: introduce llhttp
llhttp is modern, written in human-readable TypeScript, verifiable, and is very easy to maintain. See: https://github.com/indutny/llhttp PR-URL: https://github.com/nodejs/node/pull/24059 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rod Vagg <rod@vagg.org> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Ujjwal Sharma <usharma1998@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
This commit is contained in:
parent
d3f02d0da3
commit
d4654d89be
26
LICENSE
26
LICENSE
@ -606,6 +606,32 @@ The externally maintained libraries used by Node.js are:
|
|||||||
n° 289016). Three clause BSD license.
|
n° 289016). Three clause BSD license.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
- llhttp, located at deps/llhttp, is licensed as follows:
|
||||||
|
"""
|
||||||
|
This software is licensed under the MIT License.
|
||||||
|
|
||||||
|
Copyright Fedor Indutny, 2018.
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
|
||||||
- OpenSSL, located at deps/openssl, is licensed as follows:
|
- OpenSSL, located at deps/openssl, is licensed as follows:
|
||||||
"""
|
"""
|
||||||
Copyright (c) 1998-2018 The OpenSSL Project. All rights reserved.
|
Copyright (c) 1998-2018 The OpenSSL Project. All rights reserved.
|
||||||
|
@ -182,6 +182,11 @@ parser.add_option('--openssl-system-ca-path',
|
|||||||
help='Use the specified path to system CA (PEM format) in addition to '
|
help='Use the specified path to system CA (PEM format) in addition to '
|
||||||
'the OpenSSL supplied CA store or compiled-in Mozilla CA copy.')
|
'the OpenSSL supplied CA store or compiled-in Mozilla CA copy.')
|
||||||
|
|
||||||
|
parser.add_option('--experimental-http-parser',
|
||||||
|
action='store_true',
|
||||||
|
dest='experimental_http_parser',
|
||||||
|
help='use llhttp instead of http_parser')
|
||||||
|
|
||||||
shared_optgroup.add_option('--shared-http-parser',
|
shared_optgroup.add_option('--shared-http-parser',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
dest='shared_http_parser',
|
dest='shared_http_parser',
|
||||||
@ -1106,6 +1111,9 @@ def configure_node(o):
|
|||||||
else:
|
else:
|
||||||
o['variables']['node_target_type'] = 'executable'
|
o['variables']['node_target_type'] = 'executable'
|
||||||
|
|
||||||
|
o['variables']['node_experimental_http_parser'] = \
|
||||||
|
b(options.experimental_http_parser)
|
||||||
|
|
||||||
def configure_library(lib, output):
|
def configure_library(lib, output):
|
||||||
shared_lib = 'shared_' + lib
|
shared_lib = 'shared_' + lib
|
||||||
output['variables']['node_' + shared_lib] = b(getattr(options, shared_lib))
|
output['variables']['node_' + shared_lib] = b(getattr(options, shared_lib))
|
||||||
|
22
deps/llhttp/LICENSE-MIT
vendored
Normal file
22
deps/llhttp/LICENSE-MIT
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
This software is licensed under the MIT License.
|
||||||
|
|
||||||
|
Copyright Fedor Indutny, 2018.
|
||||||
|
|
||||||
|
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.
|
129
deps/llhttp/README.md
vendored
Normal file
129
deps/llhttp/README.md
vendored
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
# llhttp
|
||||||
|
[](http://travis-ci.org/indutny/llhttp)
|
||||||
|
|
||||||
|
Port of [http_parser][0] to [llparse][1].
|
||||||
|
|
||||||
|
## Why?
|
||||||
|
|
||||||
|
Let's face it, [http_parser][0] is practically unmaintainable. Even
|
||||||
|
introduction of a single new method results in a significant code churn.
|
||||||
|
|
||||||
|
This project aims to:
|
||||||
|
|
||||||
|
* Make it maintainable
|
||||||
|
* Verifiable
|
||||||
|
* Improving benchmarks where possible
|
||||||
|
|
||||||
|
## How?
|
||||||
|
|
||||||
|
Over time, different approaches for improving [http_parser][0]'s code base
|
||||||
|
were tried. However, all of them failed due to resulting significant performance
|
||||||
|
degradation.
|
||||||
|
|
||||||
|
This project is a port of [http_parser][0] to TypeScript. [llparse][1] is used
|
||||||
|
to generate the output C and/or bitcode artifacts, which could be compiled and
|
||||||
|
linked with the embedder's program (like [Node.js][7]).
|
||||||
|
|
||||||
|
## Peformance
|
||||||
|
|
||||||
|
So far llhttp outperforms http_parser:
|
||||||
|
|
||||||
|
| | input size | bandwidth | reqs/sec | time |
|
||||||
|
|:----------------|-----------:|-------------:|-----------:|--------:|
|
||||||
|
| **llhttp** _(C)_ | 8192.00 mb | 1497.88 mb/s | 3020458.87 ops/sec | 5.47 s |
|
||||||
|
| **llhttp** _(bitcode)_ | 8192.00 mb | 1131.75 mb/s | 2282171.24 ops/sec | 7.24 s |
|
||||||
|
| **http_parser** | 8192.00 mb | 694.66 mb/s | 1406180.33 req/sec | 11.79 s |
|
||||||
|
|
||||||
|
llhttp is faster by approximately **116%**.
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
llhttp project has about 1400 lines of TypeScript code describing the parser
|
||||||
|
itself and around 450 lines of C code and headers providing the helper methods.
|
||||||
|
The whole [http_parser][0] is implemented in approximately 2500 lines of C, and
|
||||||
|
436 lines of headers.
|
||||||
|
|
||||||
|
All optimizations and multi-character matching in llhttp are generated
|
||||||
|
automatically, and thus doesn't add any extra maintenance cost. On the contrary,
|
||||||
|
most of http_parser's code is hand-optimized and unrolled. Instead describing
|
||||||
|
"how" it should parse the HTTP requests/responses, a maintainer should
|
||||||
|
implement the new features in [http_parser][0] cautiously, considering
|
||||||
|
possible performance degradation and manually optimizing the new code.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
The state machine graph is encoded explicitly in llhttp. The [llparse][1]
|
||||||
|
automatically checks the graph for absence of loops and correct reporting of the
|
||||||
|
input ranges (spans) like header names and values. In the future, additional
|
||||||
|
checks could be performed to get even stricter verification of the llhttp.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```C
|
||||||
|
#include "llhttp.h"
|
||||||
|
|
||||||
|
llhttp_t parser;
|
||||||
|
llhttp_settings_t settings;
|
||||||
|
|
||||||
|
/* Initialize user callbacks and settings */
|
||||||
|
llhttp_settings_init(&settings);
|
||||||
|
|
||||||
|
/* Set user callback */
|
||||||
|
settings.on_message_complete = handle_on_message_complete;
|
||||||
|
|
||||||
|
/* Initialize the parser in HTTP_BOTH mode, meaning that it will select between
|
||||||
|
* HTTP_REQUEST and HTTP_RESPONSE parsing automatically while reading the first
|
||||||
|
* input.
|
||||||
|
*/
|
||||||
|
llhttp_init(&parser, HTTP_BOTH, &settings);
|
||||||
|
|
||||||
|
/* Use `llhttp_set_type(&parser, HTTP_REQUEST);` to override the mode */
|
||||||
|
|
||||||
|
/* Parse request! */
|
||||||
|
const char* request = "GET / HTTP/1.1\r\n\r\n";
|
||||||
|
int request_len = strlen(request);
|
||||||
|
|
||||||
|
enum llhttp_errno err = llhttp_execute(&parser, request, request_len);
|
||||||
|
if (err == HPE_OK) {
|
||||||
|
/* Successfully parsed! */
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err),
|
||||||
|
parser.reason);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### LICENSE
|
||||||
|
|
||||||
|
This software is licensed under the MIT License.
|
||||||
|
|
||||||
|
Copyright Fedor Indutny, 2018.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
[0]: https://github.com/nodejs/http-parser
|
||||||
|
[1]: https://github.com/indutny/llparse
|
||||||
|
[2]: https://en.wikipedia.org/wiki/Register_allocation#Spilling
|
||||||
|
[3]: https://en.wikipedia.org/wiki/Tail_call
|
||||||
|
[4]: https://llvm.org/docs/LangRef.html
|
||||||
|
[5]: https://llvm.org/docs/LangRef.html#call-instruction
|
||||||
|
[6]: https://clang.llvm.org/
|
||||||
|
[7]: https://github.com/nodejs/node
|
46
deps/llhttp/common.gypi
vendored
Normal file
46
deps/llhttp/common.gypi
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
'target_defaults': {
|
||||||
|
'default_configuration': 'Debug',
|
||||||
|
'configurations': {
|
||||||
|
# TODO: hoist these out and put them somewhere common, because
|
||||||
|
# RuntimeLibrary MUST MATCH across the entire project
|
||||||
|
'Debug': {
|
||||||
|
'defines': [ 'DEBUG', '_DEBUG' ],
|
||||||
|
'cflags': [ '-Wall', '-Wextra', '-O0', '-g', '-ftrapv' ],
|
||||||
|
'msvs_settings': {
|
||||||
|
'VCCLCompilerTool': {
|
||||||
|
'RuntimeLibrary': 1, # static debug
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'Release': {
|
||||||
|
'defines': [ 'NDEBUG' ],
|
||||||
|
'cflags': [ '-Wall', '-Wextra', '-O3' ],
|
||||||
|
'msvs_settings': {
|
||||||
|
'VCCLCompilerTool': {
|
||||||
|
'RuntimeLibrary': 0, # static release
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'msvs_settings': {
|
||||||
|
'VCCLCompilerTool': {
|
||||||
|
# Compile as C++. llhttp.c is actually C99, but C++ is
|
||||||
|
# close enough in this case.
|
||||||
|
'CompileAs': 2,
|
||||||
|
},
|
||||||
|
'VCLibrarianTool': {
|
||||||
|
},
|
||||||
|
'VCLinkerTool': {
|
||||||
|
'GenerateDebugInformation': 'true',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'conditions': [
|
||||||
|
['OS == "win"', {
|
||||||
|
'defines': [
|
||||||
|
'WIN32'
|
||||||
|
],
|
||||||
|
}]
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
353
deps/llhttp/include/llhttp.h
vendored
Normal file
353
deps/llhttp/include/llhttp.h
vendored
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
#ifndef INCLUDE_LLHTTP_H_
|
||||||
|
#define INCLUDE_LLHTTP_H_
|
||||||
|
|
||||||
|
#define LLHTTP_VERSION_MAJOR 1
|
||||||
|
#define LLHTTP_VERSION_MINOR 0
|
||||||
|
#define LLHTTP_VERSION_PATCH 0
|
||||||
|
|
||||||
|
#ifndef INCLUDE_LLHTTP_ITSELF_H_
|
||||||
|
#define INCLUDE_LLHTTP_ITSELF_H_
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct llhttp__internal_s llhttp__internal_t;
|
||||||
|
struct llhttp__internal_s {
|
||||||
|
int32_t _index;
|
||||||
|
void* _span_pos0;
|
||||||
|
void* _span_cb0;
|
||||||
|
int32_t error;
|
||||||
|
const char* reason;
|
||||||
|
const char* error_pos;
|
||||||
|
void* data;
|
||||||
|
void* _current;
|
||||||
|
uint64_t content_length;
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t method;
|
||||||
|
uint8_t http_major;
|
||||||
|
uint8_t http_minor;
|
||||||
|
uint8_t header_state;
|
||||||
|
uint8_t flags;
|
||||||
|
uint8_t upgrade;
|
||||||
|
uint16_t status_code;
|
||||||
|
uint8_t finish;
|
||||||
|
void* settings;
|
||||||
|
};
|
||||||
|
|
||||||
|
int llhttp__internal_init(llhttp__internal_t* s);
|
||||||
|
int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
#endif /* INCLUDE_LLHTTP_ITSELF_H_ */
|
||||||
|
|
||||||
|
#ifndef LLLLHTTP_C_HEADERS_
|
||||||
|
#define LLLLHTTP_C_HEADERS_
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum llhttp_errno {
|
||||||
|
HPE_OK = 0,
|
||||||
|
HPE_INTERNAL = 1,
|
||||||
|
HPE_STRICT = 2,
|
||||||
|
HPE_LF_EXPECTED = 3,
|
||||||
|
HPE_UNEXPECTED_CONTENT_LENGTH = 4,
|
||||||
|
HPE_CLOSED_CONNECTION = 5,
|
||||||
|
HPE_INVALID_METHOD = 6,
|
||||||
|
HPE_INVALID_URL = 7,
|
||||||
|
HPE_INVALID_CONSTANT = 8,
|
||||||
|
HPE_INVALID_VERSION = 9,
|
||||||
|
HPE_INVALID_HEADER_TOKEN = 10,
|
||||||
|
HPE_INVALID_CONTENT_LENGTH = 11,
|
||||||
|
HPE_INVALID_CHUNK_SIZE = 12,
|
||||||
|
HPE_INVALID_STATUS = 13,
|
||||||
|
HPE_INVALID_EOF_STATE = 14,
|
||||||
|
HPE_CB_MESSAGE_BEGIN = 15,
|
||||||
|
HPE_CB_HEADERS_COMPLETE = 16,
|
||||||
|
HPE_CB_MESSAGE_COMPLETE = 17,
|
||||||
|
HPE_CB_CHUNK_HEADER = 18,
|
||||||
|
HPE_CB_CHUNK_COMPLETE = 19,
|
||||||
|
HPE_PAUSED = 20,
|
||||||
|
HPE_PAUSED_UPGRADE = 21,
|
||||||
|
HPE_USER = 22
|
||||||
|
};
|
||||||
|
typedef enum llhttp_errno llhttp_errno_t;
|
||||||
|
|
||||||
|
enum llhttp_flags {
|
||||||
|
F_CONNECTION_KEEP_ALIVE = 0x1,
|
||||||
|
F_CONNECTION_CLOSE = 0x2,
|
||||||
|
F_CONNECTION_UPGRADE = 0x4,
|
||||||
|
F_CHUNKED = 0x8,
|
||||||
|
F_UPGRADE = 0x10,
|
||||||
|
F_CONTENT_LENGTH = 0x20,
|
||||||
|
F_SKIPBODY = 0x40,
|
||||||
|
F_TRAILING = 0x80
|
||||||
|
};
|
||||||
|
typedef enum llhttp_flags llhttp_flags_t;
|
||||||
|
|
||||||
|
enum llhttp_type {
|
||||||
|
HTTP_BOTH = 0,
|
||||||
|
HTTP_REQUEST = 1,
|
||||||
|
HTTP_RESPONSE = 2
|
||||||
|
};
|
||||||
|
typedef enum llhttp_type llhttp_type_t;
|
||||||
|
|
||||||
|
enum llhttp_finish {
|
||||||
|
HTTP_FINISH_SAFE = 0,
|
||||||
|
HTTP_FINISH_SAFE_WITH_CB = 1,
|
||||||
|
HTTP_FINISH_UNSAFE = 2
|
||||||
|
};
|
||||||
|
typedef enum llhttp_finish llhttp_finish_t;
|
||||||
|
|
||||||
|
enum llhttp_method {
|
||||||
|
HTTP_DELETE = 0,
|
||||||
|
HTTP_GET = 1,
|
||||||
|
HTTP_HEAD = 2,
|
||||||
|
HTTP_POST = 3,
|
||||||
|
HTTP_PUT = 4,
|
||||||
|
HTTP_CONNECT = 5,
|
||||||
|
HTTP_OPTIONS = 6,
|
||||||
|
HTTP_TRACE = 7,
|
||||||
|
HTTP_COPY = 8,
|
||||||
|
HTTP_LOCK = 9,
|
||||||
|
HTTP_MKCOL = 10,
|
||||||
|
HTTP_MOVE = 11,
|
||||||
|
HTTP_PROPFIND = 12,
|
||||||
|
HTTP_PROPPATCH = 13,
|
||||||
|
HTTP_SEARCH = 14,
|
||||||
|
HTTP_UNLOCK = 15,
|
||||||
|
HTTP_BIND = 16,
|
||||||
|
HTTP_REBIND = 17,
|
||||||
|
HTTP_UNBIND = 18,
|
||||||
|
HTTP_ACL = 19,
|
||||||
|
HTTP_REPORT = 20,
|
||||||
|
HTTP_MKACTIVITY = 21,
|
||||||
|
HTTP_CHECKOUT = 22,
|
||||||
|
HTTP_MERGE = 23,
|
||||||
|
HTTP_MSEARCH = 24,
|
||||||
|
HTTP_NOTIFY = 25,
|
||||||
|
HTTP_SUBSCRIBE = 26,
|
||||||
|
HTTP_UNSUBSCRIBE = 27,
|
||||||
|
HTTP_PATCH = 28,
|
||||||
|
HTTP_PURGE = 29,
|
||||||
|
HTTP_MKCALENDAR = 30,
|
||||||
|
HTTP_LINK = 31,
|
||||||
|
HTTP_UNLINK = 32,
|
||||||
|
HTTP_SOURCE = 33
|
||||||
|
};
|
||||||
|
typedef enum llhttp_method llhttp_method_t;
|
||||||
|
|
||||||
|
#define HTTP_ERRNO_MAP(XX) \
|
||||||
|
XX(0, OK, OK) \
|
||||||
|
XX(1, INTERNAL, INTERNAL) \
|
||||||
|
XX(2, STRICT, STRICT) \
|
||||||
|
XX(3, LF_EXPECTED, LF_EXPECTED) \
|
||||||
|
XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \
|
||||||
|
XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \
|
||||||
|
XX(6, INVALID_METHOD, INVALID_METHOD) \
|
||||||
|
XX(7, INVALID_URL, INVALID_URL) \
|
||||||
|
XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \
|
||||||
|
XX(9, INVALID_VERSION, INVALID_VERSION) \
|
||||||
|
XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \
|
||||||
|
XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \
|
||||||
|
XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \
|
||||||
|
XX(13, INVALID_STATUS, INVALID_STATUS) \
|
||||||
|
XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \
|
||||||
|
XX(15, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \
|
||||||
|
XX(16, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \
|
||||||
|
XX(17, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \
|
||||||
|
XX(18, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \
|
||||||
|
XX(19, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \
|
||||||
|
XX(20, PAUSED, PAUSED) \
|
||||||
|
XX(21, PAUSED_UPGRADE, PAUSED_UPGRADE) \
|
||||||
|
XX(22, USER, USER) \
|
||||||
|
|
||||||
|
|
||||||
|
#define HTTP_METHOD_MAP(XX) \
|
||||||
|
XX(0, DELETE, DELETE) \
|
||||||
|
XX(1, GET, GET) \
|
||||||
|
XX(2, HEAD, HEAD) \
|
||||||
|
XX(3, POST, POST) \
|
||||||
|
XX(4, PUT, PUT) \
|
||||||
|
XX(5, CONNECT, CONNECT) \
|
||||||
|
XX(6, OPTIONS, OPTIONS) \
|
||||||
|
XX(7, TRACE, TRACE) \
|
||||||
|
XX(8, COPY, COPY) \
|
||||||
|
XX(9, LOCK, LOCK) \
|
||||||
|
XX(10, MKCOL, MKCOL) \
|
||||||
|
XX(11, MOVE, MOVE) \
|
||||||
|
XX(12, PROPFIND, PROPFIND) \
|
||||||
|
XX(13, PROPPATCH, PROPPATCH) \
|
||||||
|
XX(14, SEARCH, SEARCH) \
|
||||||
|
XX(15, UNLOCK, UNLOCK) \
|
||||||
|
XX(16, BIND, BIND) \
|
||||||
|
XX(17, REBIND, REBIND) \
|
||||||
|
XX(18, UNBIND, UNBIND) \
|
||||||
|
XX(19, ACL, ACL) \
|
||||||
|
XX(20, REPORT, REPORT) \
|
||||||
|
XX(21, MKACTIVITY, MKACTIVITY) \
|
||||||
|
XX(22, CHECKOUT, CHECKOUT) \
|
||||||
|
XX(23, MERGE, MERGE) \
|
||||||
|
XX(24, MSEARCH, M-SEARCH) \
|
||||||
|
XX(25, NOTIFY, NOTIFY) \
|
||||||
|
XX(26, SUBSCRIBE, SUBSCRIBE) \
|
||||||
|
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
|
||||||
|
XX(28, PATCH, PATCH) \
|
||||||
|
XX(29, PURGE, PURGE) \
|
||||||
|
XX(30, MKCALENDAR, MKCALENDAR) \
|
||||||
|
XX(31, LINK, LINK) \
|
||||||
|
XX(32, UNLINK, UNLINK) \
|
||||||
|
XX(33, SOURCE, SOURCE) \
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
#endif /* LLLLHTTP_C_HEADERS_ */
|
||||||
|
|
||||||
|
#ifndef INCLUDE_LLHTTP_API_H_
|
||||||
|
#define INCLUDE_LLHTTP_API_H_
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef llhttp__internal_t llhttp_t;
|
||||||
|
typedef struct llhttp_settings_s llhttp_settings_t;
|
||||||
|
|
||||||
|
typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length);
|
||||||
|
typedef int (*llhttp_cb)(llhttp_t*);
|
||||||
|
|
||||||
|
struct llhttp_settings_s {
|
||||||
|
/* Possible return values 0, -1, `HPE_PAUSED` */
|
||||||
|
llhttp_cb on_message_begin;
|
||||||
|
|
||||||
|
llhttp_data_cb on_url;
|
||||||
|
llhttp_data_cb on_status;
|
||||||
|
llhttp_data_cb on_header_field;
|
||||||
|
llhttp_data_cb on_header_value;
|
||||||
|
|
||||||
|
/* Possible return values:
|
||||||
|
* 0 - Proceed normally
|
||||||
|
* 1 - Assume that request/response has no body, and proceed to parsing the
|
||||||
|
* next message
|
||||||
|
* 2 - Assume absence of body (as above) and make `llhttp_execute()` return
|
||||||
|
* `HPE_PAUSED_UPGRADE`
|
||||||
|
* -1 - Error
|
||||||
|
* `HPE_PAUSED`
|
||||||
|
*/
|
||||||
|
llhttp_cb on_headers_complete;
|
||||||
|
|
||||||
|
llhttp_data_cb on_body;
|
||||||
|
|
||||||
|
/* Possible return values 0, -1, `HPE_PAUSED` */
|
||||||
|
llhttp_cb on_message_complete;
|
||||||
|
|
||||||
|
/* When on_chunk_header is called, the current chunk length is stored
|
||||||
|
* in parser->content_length.
|
||||||
|
* Possible return values 0, -1, `HPE_PAUSED`
|
||||||
|
*/
|
||||||
|
llhttp_cb on_chunk_header;
|
||||||
|
llhttp_cb on_chunk_complete;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Initialize the parser with specific type and user settings */
|
||||||
|
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
|
||||||
|
const llhttp_settings_t* settings);
|
||||||
|
|
||||||
|
/* Initialize the settings object */
|
||||||
|
void llhttp_settings_init(llhttp_settings_t* settings);
|
||||||
|
|
||||||
|
/* Parse full or partial request/response, invoking user callbacks along the
|
||||||
|
* way.
|
||||||
|
*
|
||||||
|
* If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing
|
||||||
|
* interrupts, and such errno is returned from `llhttp_execute()`. If
|
||||||
|
* `HPE_PAUSED` was used as a errno, the execution can be resumed with
|
||||||
|
* `llhttp_resume()` call.
|
||||||
|
*
|
||||||
|
* In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE`
|
||||||
|
* is returned after fully parsing the request/response. If the user wishes to
|
||||||
|
* continue parsing, they need to invoke `llhttp_resume_after_upgrade()`.
|
||||||
|
*/
|
||||||
|
llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len);
|
||||||
|
|
||||||
|
/* This method should be called when the other side has no further bytes to
|
||||||
|
* send (e.g. shutdown of readable side of the TCP connection.)
|
||||||
|
*
|
||||||
|
* Requests without `Content-Length` and other messages might require treating
|
||||||
|
* all incoming bytes as the part of the body, up to the last byte of the
|
||||||
|
* connection. This method will invoke `on_message_complete()` callback if the
|
||||||
|
* request was terminated safely. Otherwise a error code would be returned.
|
||||||
|
*/
|
||||||
|
llhttp_errno_t llhttp_finish(llhttp_t* parser);
|
||||||
|
|
||||||
|
/* Returns `1` if the incoming message is parsed until the last byte, and has
|
||||||
|
* to be completed by calling `llhttp_finish()` on EOF
|
||||||
|
*/
|
||||||
|
int llhttp_message_needs_eof(const llhttp_t* parser);
|
||||||
|
|
||||||
|
/* Returns `1` if there might be any other messages following the last that was
|
||||||
|
* successfuly parsed.
|
||||||
|
*/
|
||||||
|
int llhttp_should_keep_alive(const llhttp_t* parser);
|
||||||
|
|
||||||
|
/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set
|
||||||
|
* appropriate error reason.
|
||||||
|
*
|
||||||
|
* Important: do not call this from user callbacks! User callbacks must return
|
||||||
|
* `HPE_PAUSED` if pausing is required.
|
||||||
|
*/
|
||||||
|
void llhttp_pause(llhttp_t* parser);
|
||||||
|
|
||||||
|
/* Might be called to resume the execution after the pause in user's callback.
|
||||||
|
* See `llhttp_execute()` above for details.
|
||||||
|
*
|
||||||
|
* Call this only if `llhttp_execute()` returns `HPE_PAUSED`.
|
||||||
|
*/
|
||||||
|
void llhttp_resume(llhttp_t* parser);
|
||||||
|
|
||||||
|
/* Might be called to resume the execution after the pause in user's callback.
|
||||||
|
* See `llhttp_execute()` above for details.
|
||||||
|
*
|
||||||
|
* Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE`
|
||||||
|
*/
|
||||||
|
void llhttp_resume_after_upgrade(llhttp_t* parser);
|
||||||
|
|
||||||
|
/* Returns the latest return error */
|
||||||
|
llhttp_errno_t llhttp_get_errno(const llhttp_t* parser);
|
||||||
|
|
||||||
|
/* Returns the verbal explanation of the latest returned error.
|
||||||
|
*
|
||||||
|
* Note: User callback should set error reason when returning the error. See
|
||||||
|
* `llhttp_set_error_reason()` for details.
|
||||||
|
*/
|
||||||
|
const char* llhttp_get_error_reason(const llhttp_t* parser);
|
||||||
|
|
||||||
|
/* Assign verbal description to the returned error. Must be called in user
|
||||||
|
* callbacks right before returning the errno.
|
||||||
|
*
|
||||||
|
* Note: `HPE_USER` error code might be useful in user callbacks.
|
||||||
|
*/
|
||||||
|
void llhttp_set_error_reason(llhttp_t* parser, const char* reason);
|
||||||
|
|
||||||
|
/* Returns the pointer to the last parsed byte before the returned error. The
|
||||||
|
* pointer is relative to the `data` argument of `llhttp_execute()`.
|
||||||
|
*
|
||||||
|
* Note: this method might be useful for counting the number of parsed bytes.
|
||||||
|
*/
|
||||||
|
const char* llhttp_get_error_pos(const llhttp_t* parser);
|
||||||
|
|
||||||
|
/* Returns textual name of error code */
|
||||||
|
const char* llhttp_errno_name(llhttp_errno_t err);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
#endif /* INCLUDE_LLHTTP_API_H_ */
|
||||||
|
|
||||||
|
#endif /* INCLUDE_LLHTTP_H_ */
|
13
deps/llhttp/llhttp.gyp
vendored
Normal file
13
deps/llhttp/llhttp.gyp
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
'targets': [
|
||||||
|
{
|
||||||
|
'target_name': 'llhttp',
|
||||||
|
'type': 'static_library',
|
||||||
|
'include_dirs': [ '.', 'include' ],
|
||||||
|
'direct_dependent_settings': {
|
||||||
|
'include_dirs': [ 'include' ],
|
||||||
|
},
|
||||||
|
'sources': [ 'src/llhttp.c', 'src/api.c', 'src/http.c' ],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
205
deps/llhttp/src/api.c
vendored
Normal file
205
deps/llhttp/src/api.c
vendored
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "llhttp.h"
|
||||||
|
|
||||||
|
#define CALLBACK_MAYBE(PARSER, NAME, ...) \
|
||||||
|
do { \
|
||||||
|
llhttp_settings_t* settings; \
|
||||||
|
settings = (llhttp_settings_t*) (PARSER)->settings; \
|
||||||
|
if (settings == NULL || settings->NAME == NULL) { \
|
||||||
|
err = 0; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
err = settings->NAME(__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
|
||||||
|
const llhttp_settings_t* settings) {
|
||||||
|
llhttp__internal_init(parser);
|
||||||
|
|
||||||
|
parser->type = type;
|
||||||
|
parser->settings = (void*) settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) {
|
||||||
|
return llhttp__internal_execute(parser, data, data + len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void llhttp_settings_init(llhttp_settings_t* settings) {
|
||||||
|
memset(settings, 0, sizeof(*settings));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
llhttp_errno_t llhttp_finish(llhttp_t* parser) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* We're in an error state. Don't bother doing anything. */
|
||||||
|
if (parser->error != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (parser->finish) {
|
||||||
|
case HTTP_FINISH_SAFE_WITH_CB:
|
||||||
|
CALLBACK_MAYBE(parser, on_message_complete, parser);
|
||||||
|
if (err != HPE_OK) return err;
|
||||||
|
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
case HTTP_FINISH_SAFE:
|
||||||
|
return HPE_OK;
|
||||||
|
case HTTP_FINISH_UNSAFE:
|
||||||
|
parser->reason = "Invalid EOF state";
|
||||||
|
return HPE_INVALID_EOF_STATE;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void llhttp_pause(llhttp_t* parser) {
|
||||||
|
if (parser->error != HPE_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parser->error = HPE_PAUSED;
|
||||||
|
parser->reason = "Paused";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void llhttp_resume(llhttp_t* parser) {
|
||||||
|
if (parser->error != HPE_PAUSED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parser->error = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void llhttp_resume_after_upgrade(llhttp_t* parser) {
|
||||||
|
if (parser->error != HPE_PAUSED_UPGRADE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parser->error = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) {
|
||||||
|
return parser->error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char* llhttp_get_error_reason(const llhttp_t* parser) {
|
||||||
|
return parser->reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void llhttp_set_error_reason(llhttp_t* parser, const char* reason) {
|
||||||
|
parser->reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char* llhttp_get_error_pos(const llhttp_t* parser) {
|
||||||
|
return parser->error_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char* llhttp_errno_name(llhttp_errno_t err) {
|
||||||
|
#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME;
|
||||||
|
switch (err) {
|
||||||
|
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
|
||||||
|
default: abort();
|
||||||
|
}
|
||||||
|
#undef HTTP_ERRNO_GEN
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Callbacks */
|
||||||
|
|
||||||
|
|
||||||
|
int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) {
|
||||||
|
int err;
|
||||||
|
CALLBACK_MAYBE(s, on_message_begin, s);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) {
|
||||||
|
int err;
|
||||||
|
CALLBACK_MAYBE(s, on_url, s, p, endp - p);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) {
|
||||||
|
int err;
|
||||||
|
CALLBACK_MAYBE(s, on_status, s, p, endp - p);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) {
|
||||||
|
int err;
|
||||||
|
CALLBACK_MAYBE(s, on_header_field, s, p, endp - p);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) {
|
||||||
|
int err;
|
||||||
|
CALLBACK_MAYBE(s, on_header_value, s, p, endp - p);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||||
|
int err;
|
||||||
|
CALLBACK_MAYBE(s, on_headers_complete, s);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||||
|
int err;
|
||||||
|
CALLBACK_MAYBE(s, on_message_complete, s);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) {
|
||||||
|
int err;
|
||||||
|
CALLBACK_MAYBE(s, on_body, s, p, endp - p);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) {
|
||||||
|
int err;
|
||||||
|
CALLBACK_MAYBE(s, on_chunk_header, s);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||||
|
int err;
|
||||||
|
CALLBACK_MAYBE(s, on_chunk_complete, s);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Private */
|
||||||
|
|
||||||
|
|
||||||
|
void llhttp__debug(llhttp_t* s, const char* p, const char* endp,
|
||||||
|
const char* msg) {
|
||||||
|
if (p == endp) {
|
||||||
|
fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type,
|
||||||
|
s->flags, msg);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s,
|
||||||
|
s->type, s->flags, *p, msg);
|
||||||
|
}
|
||||||
|
}
|
120
deps/llhttp/src/http.c
vendored
Normal file
120
deps/llhttp/src/http.c
vendored
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#ifndef LLHTTP__TEST
|
||||||
|
# include "llhttp.h"
|
||||||
|
#else
|
||||||
|
# define llhttp_t llparse_t
|
||||||
|
#endif /* */
|
||||||
|
|
||||||
|
int llhttp_message_needs_eof(const llhttp_t* parser);
|
||||||
|
int llhttp_should_keep_alive(const llhttp_t* parser);
|
||||||
|
|
||||||
|
int llhttp__before_headers_complete(llhttp_t* parser, const char* p,
|
||||||
|
const char* endp) {
|
||||||
|
/* Set this here so that on_headers_complete() callbacks can see it */
|
||||||
|
if ((parser->flags & F_UPGRADE) &&
|
||||||
|
(parser->flags & F_CONNECTION_UPGRADE)) {
|
||||||
|
/* For responses, "Upgrade: foo" and "Connection: upgrade" are
|
||||||
|
* mandatory only when it is a 101 Switching Protocols response,
|
||||||
|
* otherwise it is purely informational, to announce support.
|
||||||
|
*/
|
||||||
|
parser->upgrade =
|
||||||
|
(parser->type == HTTP_REQUEST || parser->status_code == 101);
|
||||||
|
} else {
|
||||||
|
parser->upgrade = (parser->method == HTTP_CONNECT);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return values:
|
||||||
|
* 0 - No body, `restart`, message_complete
|
||||||
|
* 1 - CONNECT request, `restart`, message_complete, and pause
|
||||||
|
* 2 - chunk_size_start
|
||||||
|
* 3 - body_identity
|
||||||
|
* 4 - body_identity_eof
|
||||||
|
*/
|
||||||
|
int llhttp__after_headers_complete(llhttp_t* parser, const char* p,
|
||||||
|
const char* endp) {
|
||||||
|
int hasBody;
|
||||||
|
|
||||||
|
hasBody = parser->flags & F_CHUNKED || parser->content_length > 0;
|
||||||
|
if (parser->upgrade && (parser->method == HTTP_CONNECT ||
|
||||||
|
(parser->flags & F_SKIPBODY) || !hasBody)) {
|
||||||
|
/* Exit, the rest of the message is in a different protocol. */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser->flags & F_SKIPBODY) {
|
||||||
|
return 0;
|
||||||
|
} else if (parser->flags & F_CHUNKED) {
|
||||||
|
/* chunked encoding - ignore Content-Length header */
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
if (!(parser->flags & F_CONTENT_LENGTH)) {
|
||||||
|
if (!llhttp_message_needs_eof(parser)) {
|
||||||
|
/* Assume content-length 0 - read the next */
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
/* Read body until EOF */
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
} else if (parser->content_length == 0) {
|
||||||
|
/* Content-Length header given but zero: Content-Length: 0\r\n */
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
/* Content-Length header given and non-zero */
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int llhttp__after_message_complete(llhttp_t* parser, const char* p,
|
||||||
|
const char* endp) {
|
||||||
|
int should_keep_alive;
|
||||||
|
|
||||||
|
should_keep_alive = llhttp_should_keep_alive(parser);
|
||||||
|
parser->flags = 0;
|
||||||
|
parser->finish = HTTP_FINISH_SAFE;
|
||||||
|
|
||||||
|
/* NOTE: this is ignored in loose parsing mode */
|
||||||
|
return should_keep_alive;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int llhttp_message_needs_eof(const llhttp_t* parser) {
|
||||||
|
if (parser->type == HTTP_REQUEST) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See RFC 2616 section 4.4 */
|
||||||
|
if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
|
||||||
|
parser->status_code == 204 || /* No Content */
|
||||||
|
parser->status_code == 304 || /* Not Modified */
|
||||||
|
(parser->flags & F_SKIPBODY)) { /* response to a HEAD request */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int llhttp_should_keep_alive(const llhttp_t* parser) {
|
||||||
|
if (parser->http_major > 0 && parser->http_minor > 0) {
|
||||||
|
/* HTTP/1.1 */
|
||||||
|
if (parser->flags & F_CONNECTION_CLOSE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* HTTP/1.0 or earlier */
|
||||||
|
if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !llhttp_message_needs_eof(parser);
|
||||||
|
}
|
6044
deps/llhttp/src/llhttp.c
vendored
Normal file
6044
deps/llhttp/src/llhttp.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node.gyp
1
node.gyp
@ -12,6 +12,7 @@
|
|||||||
'force_dynamic_crt%': 0,
|
'force_dynamic_crt%': 0,
|
||||||
'node_module_version%': '',
|
'node_module_version%': '',
|
||||||
'node_shared_zlib%': 'false',
|
'node_shared_zlib%': 'false',
|
||||||
|
'node_experimental_http_parser%': 'false',
|
||||||
'node_shared_http_parser%': 'false',
|
'node_shared_http_parser%': 'false',
|
||||||
'node_shared_cares%': 'false',
|
'node_shared_cares%': 'false',
|
||||||
'node_shared_libuv%': 'false',
|
'node_shared_libuv%': 'false',
|
||||||
|
11
node.gypi
11
node.gypi
@ -163,9 +163,14 @@
|
|||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
|
|
||||||
[ 'node_shared_http_parser=="false"', {
|
[ 'node_experimental_http_parser=="true"', {
|
||||||
'dependencies': [ 'deps/http_parser/http_parser.gyp:http_parser' ],
|
'defines': [ 'NODE_EXPERIMENTAL_HTTP' ],
|
||||||
}],
|
'dependencies': [ 'deps/llhttp/llhttp.gyp:llhttp' ],
|
||||||
|
}, {
|
||||||
|
'conditions': [ [ 'node_shared_http_parser=="false"', {
|
||||||
|
'dependencies': [ 'deps/http_parser/http_parser.gyp:http_parser' ],
|
||||||
|
} ] ],
|
||||||
|
} ],
|
||||||
|
|
||||||
[ 'node_shared_cares=="false"', {
|
[ 'node_shared_cares=="false"', {
|
||||||
'dependencies': [ 'deps/cares/cares.gyp:cares' ],
|
'dependencies': [ 'deps/cares/cares.gyp:cares' ],
|
||||||
|
@ -265,6 +265,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
|
|||||||
V(raw_string, "raw") \
|
V(raw_string, "raw") \
|
||||||
V(read_host_object_string, "_readHostObject") \
|
V(read_host_object_string, "_readHostObject") \
|
||||||
V(readable_string, "readable") \
|
V(readable_string, "readable") \
|
||||||
|
V(reason_string, "reason") \
|
||||||
V(refresh_string, "refresh") \
|
V(refresh_string, "refresh") \
|
||||||
V(regexp_string, "regexp") \
|
V(regexp_string, "regexp") \
|
||||||
V(rename_string, "rename") \
|
V(rename_string, "rename") \
|
||||||
|
24
src/http_parser_adaptor.h
Normal file
24
src/http_parser_adaptor.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef SRC_HTTP_PARSER_ADAPTOR_H_
|
||||||
|
#define SRC_HTTP_PARSER_ADAPTOR_H_
|
||||||
|
|
||||||
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
# include "llhttp.h"
|
||||||
|
|
||||||
|
typedef llhttp_type_t parser_type_t;
|
||||||
|
typedef llhttp_errno_t parser_errno_t;
|
||||||
|
typedef llhttp_settings_t parser_settings_t;
|
||||||
|
typedef llhttp_t parser_t;
|
||||||
|
|
||||||
|
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||||
|
# include "http_parser.h"
|
||||||
|
|
||||||
|
typedef enum http_parser_type parser_type_t;
|
||||||
|
typedef enum http_errno parser_errno_t;
|
||||||
|
typedef http_parser_settings parser_settings_t;
|
||||||
|
typedef http_parser parser_t;
|
||||||
|
|
||||||
|
#define HPE_USER HPE_UNKNOWN
|
||||||
|
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
|
|
||||||
|
#endif /* SRC_HTTP_PARSER_ADAPTOR_H_ */
|
@ -1,6 +1,6 @@
|
|||||||
#include "inspector_socket.h"
|
#include "inspector_socket.h"
|
||||||
|
|
||||||
#include "http_parser.h"
|
#include "http_parser_adaptor.h"
|
||||||
#include "util-inl.h"
|
#include "util-inl.h"
|
||||||
|
|
||||||
#define NODE_WANT_INTERNALS 1
|
#define NODE_WANT_INTERNALS 1
|
||||||
@ -433,8 +433,13 @@ class HttpHandler : public ProtocolHandler {
|
|||||||
explicit HttpHandler(InspectorSocket* inspector, TcpHolder::Pointer tcp)
|
explicit HttpHandler(InspectorSocket* inspector, TcpHolder::Pointer tcp)
|
||||||
: ProtocolHandler(inspector, std::move(tcp)),
|
: ProtocolHandler(inspector, std::move(tcp)),
|
||||||
parsing_value_(false) {
|
parsing_value_(false) {
|
||||||
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
llhttp_init(&parser_, HTTP_REQUEST, &parser_settings);
|
||||||
|
llhttp_settings_init(&parser_settings);
|
||||||
|
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||||
http_parser_init(&parser_, HTTP_REQUEST);
|
http_parser_init(&parser_, HTTP_REQUEST);
|
||||||
http_parser_settings_init(&parser_settings);
|
http_parser_settings_init(&parser_settings);
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
parser_settings.on_header_field = OnHeaderField;
|
parser_settings.on_header_field = OnHeaderField;
|
||||||
parser_settings.on_header_value = OnHeaderValue;
|
parser_settings.on_header_value = OnHeaderValue;
|
||||||
parser_settings.on_message_complete = OnMessageComplete;
|
parser_settings.on_message_complete = OnMessageComplete;
|
||||||
@ -478,9 +483,20 @@ class HttpHandler : public ProtocolHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OnData(std::vector<char>* data) override {
|
void OnData(std::vector<char>* data) override {
|
||||||
|
parser_errno_t err;
|
||||||
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
err = llhttp_execute(&parser_, data->data(), data->size());
|
||||||
|
|
||||||
|
if (err == HPE_PAUSED_UPGRADE) {
|
||||||
|
err = HPE_OK;
|
||||||
|
llhttp_resume_after_upgrade(&parser_);
|
||||||
|
}
|
||||||
|
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||||
http_parser_execute(&parser_, &parser_settings, data->data(), data->size());
|
http_parser_execute(&parser_, &parser_settings, data->data(), data->size());
|
||||||
|
err = HTTP_PARSER_ERRNO(&parser_);
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
data->clear();
|
data->clear();
|
||||||
if (parser_.http_errno != HPE_OK) {
|
if (err != HPE_OK) {
|
||||||
CancelHandshake();
|
CancelHandshake();
|
||||||
}
|
}
|
||||||
// Event handling may delete *this
|
// Event handling may delete *this
|
||||||
@ -517,14 +533,14 @@ class HttpHandler : public ProtocolHandler {
|
|||||||
handler->inspector()->SwitchProtocol(nullptr);
|
handler->inspector()->SwitchProtocol(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int OnHeaderValue(http_parser* parser, const char* at, size_t length) {
|
static int OnHeaderValue(parser_t* parser, const char* at, size_t length) {
|
||||||
HttpHandler* handler = From(parser);
|
HttpHandler* handler = From(parser);
|
||||||
handler->parsing_value_ = true;
|
handler->parsing_value_ = true;
|
||||||
handler->headers_[handler->current_header_].append(at, length);
|
handler->headers_[handler->current_header_].append(at, length);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int OnHeaderField(http_parser* parser, const char* at, size_t length) {
|
static int OnHeaderField(parser_t* parser, const char* at, size_t length) {
|
||||||
HttpHandler* handler = From(parser);
|
HttpHandler* handler = From(parser);
|
||||||
if (handler->parsing_value_) {
|
if (handler->parsing_value_) {
|
||||||
handler->parsing_value_ = false;
|
handler->parsing_value_ = false;
|
||||||
@ -534,17 +550,17 @@ class HttpHandler : public ProtocolHandler {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int OnPath(http_parser* parser, const char* at, size_t length) {
|
static int OnPath(parser_t* parser, const char* at, size_t length) {
|
||||||
HttpHandler* handler = From(parser);
|
HttpHandler* handler = From(parser);
|
||||||
handler->path_.append(at, length);
|
handler->path_.append(at, length);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HttpHandler* From(http_parser* parser) {
|
static HttpHandler* From(parser_t* parser) {
|
||||||
return node::ContainerOf(&HttpHandler::parser_, parser);
|
return node::ContainerOf(&HttpHandler::parser_, parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int OnMessageComplete(http_parser* parser) {
|
static int OnMessageComplete(parser_t* parser) {
|
||||||
// Event needs to be fired after the parser is done.
|
// Event needs to be fired after the parser is done.
|
||||||
HttpHandler* handler = From(parser);
|
HttpHandler* handler = From(parser);
|
||||||
handler->events_.push_back(
|
handler->events_.push_back(
|
||||||
@ -581,8 +597,8 @@ class HttpHandler : public ProtocolHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool parsing_value_;
|
bool parsing_value_;
|
||||||
http_parser parser_;
|
parser_t parser_;
|
||||||
http_parser_settings parser_settings;
|
parser_settings_t parser_settings;
|
||||||
std::vector<HttpEvent> events_;
|
std::vector<HttpEvent> events_;
|
||||||
std::string current_header_;
|
std::string current_header_;
|
||||||
std::map<std::string, std::string> headers_;
|
std::map<std::string, std::string> headers_;
|
||||||
|
46
src/node.cc
46
src/node.cc
@ -53,7 +53,11 @@
|
|||||||
#include "async_wrap-inl.h"
|
#include "async_wrap-inl.h"
|
||||||
#include "env-inl.h"
|
#include "env-inl.h"
|
||||||
#include "handle_wrap.h"
|
#include "handle_wrap.h"
|
||||||
#include "http_parser.h"
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
# include "llhttp.h"
|
||||||
|
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||||
|
# include "http_parser.h"
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
#include "nghttp2/nghttp2ver.h"
|
#include "nghttp2/nghttp2ver.h"
|
||||||
#include "req_wrap-inl.h"
|
#include "req_wrap-inl.h"
|
||||||
#include "string_bytes.h"
|
#include "string_bytes.h"
|
||||||
@ -179,6 +183,22 @@ static node_module* modlist_internal;
|
|||||||
static node_module* modlist_linked;
|
static node_module* modlist_linked;
|
||||||
static node_module* modlist_addon;
|
static node_module* modlist_addon;
|
||||||
|
|
||||||
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
static const char llhttp_version[] =
|
||||||
|
NODE_STRINGIFY(LLHTTP_VERSION_MAJOR)
|
||||||
|
"."
|
||||||
|
NODE_STRINGIFY(LLHTTP_VERSION_MINOR)
|
||||||
|
"."
|
||||||
|
NODE_STRINGIFY(LLHTTP_VERSION_PATCH);
|
||||||
|
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||||
|
static const char http_parser_version[] =
|
||||||
|
NODE_STRINGIFY(HTTP_PARSER_VERSION_MAJOR)
|
||||||
|
"."
|
||||||
|
NODE_STRINGIFY(HTTP_PARSER_VERSION_MINOR)
|
||||||
|
"."
|
||||||
|
NODE_STRINGIFY(HTTP_PARSER_VERSION_PATCH);
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
|
|
||||||
// Bit flag used to track security reverts (see node_revert.h)
|
// Bit flag used to track security reverts (see node_revert.h)
|
||||||
unsigned int reverted = 0;
|
unsigned int reverted = 0;
|
||||||
|
|
||||||
@ -217,17 +237,15 @@ class NodeTraceStateObserver :
|
|||||||
auto trace_process = tracing::TracedValue::Create();
|
auto trace_process = tracing::TracedValue::Create();
|
||||||
trace_process->BeginDictionary("versions");
|
trace_process->BeginDictionary("versions");
|
||||||
|
|
||||||
const char http_parser_version[] =
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
NODE_STRINGIFY(HTTP_PARSER_VERSION_MAJOR)
|
trace_process->SetString("llhttp", llhttp_version);
|
||||||
"."
|
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||||
NODE_STRINGIFY(HTTP_PARSER_VERSION_MINOR)
|
trace_process->SetString("http_parser", http_parser_version);
|
||||||
"."
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
NODE_STRINGIFY(HTTP_PARSER_VERSION_PATCH);
|
|
||||||
|
|
||||||
const char node_napi_version[] = NODE_STRINGIFY(NAPI_VERSION);
|
const char node_napi_version[] = NODE_STRINGIFY(NAPI_VERSION);
|
||||||
const char node_modules_version[] = NODE_STRINGIFY(NODE_MODULE_VERSION);
|
const char node_modules_version[] = NODE_STRINGIFY(NODE_MODULE_VERSION);
|
||||||
|
|
||||||
trace_process->SetString("http_parser", http_parser_version);
|
|
||||||
trace_process->SetString("node", NODE_VERSION_STRING);
|
trace_process->SetString("node", NODE_VERSION_STRING);
|
||||||
trace_process->SetString("v8", V8::GetVersion());
|
trace_process->SetString("v8", V8::GetVersion());
|
||||||
trace_process->SetString("uv", uv_version_string());
|
trace_process->SetString("uv", uv_version_string());
|
||||||
@ -1344,14 +1362,16 @@ void SetupProcessObject(Environment* env,
|
|||||||
Local<Object> versions = Object::New(env->isolate());
|
Local<Object> versions = Object::New(env->isolate());
|
||||||
READONLY_PROPERTY(process, "versions", versions);
|
READONLY_PROPERTY(process, "versions", versions);
|
||||||
|
|
||||||
const char http_parser_version[] = NODE_STRINGIFY(HTTP_PARSER_VERSION_MAJOR)
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
"."
|
READONLY_PROPERTY(versions,
|
||||||
NODE_STRINGIFY(HTTP_PARSER_VERSION_MINOR)
|
"llhttp",
|
||||||
"."
|
FIXED_ONE_BYTE_STRING(env->isolate(), llhttp_version));
|
||||||
NODE_STRINGIFY(HTTP_PARSER_VERSION_PATCH);
|
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||||
READONLY_PROPERTY(versions,
|
READONLY_PROPERTY(versions,
|
||||||
"http_parser",
|
"http_parser",
|
||||||
FIXED_ONE_BYTE_STRING(env->isolate(), http_parser_version));
|
FIXED_ONE_BYTE_STRING(env->isolate(), http_parser_version));
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
|
|
||||||
// +1 to get rid of the leading 'v'
|
// +1 to get rid of the leading 'v'
|
||||||
READONLY_PROPERTY(versions,
|
READONLY_PROPERTY(versions,
|
||||||
"node",
|
"node",
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
#include "async_wrap-inl.h"
|
#include "async_wrap-inl.h"
|
||||||
#include "env-inl.h"
|
#include "env-inl.h"
|
||||||
#include "http_parser.h"
|
|
||||||
#include "stream_base-inl.h"
|
#include "stream_base-inl.h"
|
||||||
#include "util-inl.h"
|
#include "util-inl.h"
|
||||||
#include "v8.h"
|
#include "v8.h"
|
||||||
@ -33,6 +32,8 @@
|
|||||||
#include <stdlib.h> // free()
|
#include <stdlib.h> // free()
|
||||||
#include <string.h> // strdup()
|
#include <string.h> // strdup()
|
||||||
|
|
||||||
|
#include "http_parser_adaptor.h"
|
||||||
|
|
||||||
// This is a binding to http_parser (https://github.com/nodejs/http-parser)
|
// This is a binding to http_parser (https://github.com/nodejs/http-parser)
|
||||||
// The goal is to decouple sockets from parsing for more javascript-level
|
// The goal is to decouple sockets from parsing for more javascript-level
|
||||||
// agility. A Buffer is read from a socket and passed to parser.execute().
|
// agility. A Buffer is read from a socket and passed to parser.execute().
|
||||||
@ -148,7 +149,7 @@ struct StringPtr {
|
|||||||
|
|
||||||
class Parser : public AsyncWrap, public StreamListener {
|
class Parser : public AsyncWrap, public StreamListener {
|
||||||
public:
|
public:
|
||||||
Parser(Environment* env, Local<Object> wrap, enum http_parser_type type)
|
Parser(Environment* env, Local<Object> wrap, parser_type_t type)
|
||||||
: AsyncWrap(env, wrap, AsyncWrap::PROVIDER_HTTPPARSER),
|
: AsyncWrap(env, wrap, AsyncWrap::PROVIDER_HTTPPARSER),
|
||||||
current_buffer_len_(0),
|
current_buffer_len_(0),
|
||||||
current_buffer_data_(nullptr) {
|
current_buffer_data_(nullptr) {
|
||||||
@ -172,18 +173,33 @@ class Parser : public AsyncWrap, public StreamListener {
|
|||||||
|
|
||||||
|
|
||||||
int on_url(const char* at, size_t length) {
|
int on_url(const char* at, size_t length) {
|
||||||
|
int rv = TrackHeader(length);
|
||||||
|
if (rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
url_.Update(at, length);
|
url_.Update(at, length);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int on_status(const char* at, size_t length) {
|
int on_status(const char* at, size_t length) {
|
||||||
|
int rv = TrackHeader(length);
|
||||||
|
if (rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
status_message_.Update(at, length);
|
status_message_.Update(at, length);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int on_header_field(const char* at, size_t length) {
|
int on_header_field(const char* at, size_t length) {
|
||||||
|
int rv = TrackHeader(length);
|
||||||
|
if (rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
if (num_fields_ == num_values_) {
|
if (num_fields_ == num_values_) {
|
||||||
// start of new field name
|
// start of new field name
|
||||||
num_fields_++;
|
num_fields_++;
|
||||||
@ -206,6 +222,11 @@ class Parser : public AsyncWrap, public StreamListener {
|
|||||||
|
|
||||||
|
|
||||||
int on_header_value(const char* at, size_t length) {
|
int on_header_value(const char* at, size_t length) {
|
||||||
|
int rv = TrackHeader(length);
|
||||||
|
if (rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
if (num_values_ != num_fields_) {
|
if (num_values_ != num_fields_) {
|
||||||
// start of new header value
|
// start of new header value
|
||||||
num_values_++;
|
num_values_++;
|
||||||
@ -222,6 +243,10 @@ class Parser : public AsyncWrap, public StreamListener {
|
|||||||
|
|
||||||
|
|
||||||
int on_headers_complete() {
|
int on_headers_complete() {
|
||||||
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
header_nread_ = 0;
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
|
|
||||||
// Arguments for the on-headers-complete javascript callback. This
|
// Arguments for the on-headers-complete javascript callback. This
|
||||||
// list needs to be kept in sync with the actual argument list for
|
// list needs to be kept in sync with the actual argument list for
|
||||||
// `parserOnHeadersComplete` in lib/_http_common.js.
|
// `parserOnHeadersComplete` in lib/_http_common.js.
|
||||||
@ -279,8 +304,15 @@ class Parser : public AsyncWrap, public StreamListener {
|
|||||||
argv[A_VERSION_MAJOR] = Integer::New(env()->isolate(), parser_.http_major);
|
argv[A_VERSION_MAJOR] = Integer::New(env()->isolate(), parser_.http_major);
|
||||||
argv[A_VERSION_MINOR] = Integer::New(env()->isolate(), parser_.http_minor);
|
argv[A_VERSION_MINOR] = Integer::New(env()->isolate(), parser_.http_minor);
|
||||||
|
|
||||||
|
bool should_keep_alive;
|
||||||
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
should_keep_alive = llhttp_should_keep_alive(&parser_);
|
||||||
|
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||||
|
should_keep_alive = http_should_keep_alive(&parser_);
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
|
|
||||||
argv[A_SHOULD_KEEP_ALIVE] =
|
argv[A_SHOULD_KEEP_ALIVE] =
|
||||||
Boolean::New(env()->isolate(), http_should_keep_alive(&parser_));
|
Boolean::New(env()->isolate(), should_keep_alive);
|
||||||
|
|
||||||
argv[A_UPGRADE] = Boolean::New(env()->isolate(), parser_.upgrade);
|
argv[A_UPGRADE] = Boolean::New(env()->isolate(), parser_.upgrade);
|
||||||
|
|
||||||
@ -332,7 +364,10 @@ class Parser : public AsyncWrap, public StreamListener {
|
|||||||
|
|
||||||
if (r.IsEmpty()) {
|
if (r.IsEmpty()) {
|
||||||
got_exception_ = true;
|
got_exception_ = true;
|
||||||
return -1;
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
llhttp_set_error_reason(&parser_, "JS Exception");
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
|
return HPE_USER;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -357,18 +392,33 @@ class Parser : public AsyncWrap, public StreamListener {
|
|||||||
|
|
||||||
if (r.IsEmpty()) {
|
if (r.IsEmpty()) {
|
||||||
got_exception_ = true;
|
got_exception_ = true;
|
||||||
return -1;
|
return HPE_USER;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
// Reset nread for the next chunk
|
||||||
|
int on_chunk_header() {
|
||||||
|
header_nread_ = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Reset nread for the next chunk
|
||||||
|
int on_chunk_complete() {
|
||||||
|
header_nread_ = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
|
|
||||||
|
|
||||||
static void New(const FunctionCallbackInfo<Value>& args) {
|
static void New(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
CHECK(args[0]->IsInt32());
|
CHECK(args[0]->IsInt32());
|
||||||
http_parser_type type =
|
parser_type_t type =
|
||||||
static_cast<http_parser_type>(args[0].As<Int32>()->Value());
|
static_cast<parser_type_t>(args[0].As<Int32>()->Value());
|
||||||
CHECK(type == HTTP_REQUEST || type == HTTP_RESPONSE);
|
CHECK(type == HTTP_REQUEST || type == HTTP_RESPONSE);
|
||||||
new Parser(env, args.This(), type);
|
new Parser(env, args.This(), type);
|
||||||
}
|
}
|
||||||
@ -434,30 +484,11 @@ class Parser : public AsyncWrap, public StreamListener {
|
|||||||
|
|
||||||
|
|
||||||
static void Finish(const FunctionCallbackInfo<Value>& args) {
|
static void Finish(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
|
||||||
|
|
||||||
Parser* parser;
|
Parser* parser;
|
||||||
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder());
|
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder());
|
||||||
|
|
||||||
CHECK(parser->current_buffer_.IsEmpty());
|
CHECK(parser->current_buffer_.IsEmpty());
|
||||||
parser->got_exception_ = false;
|
parser->Execute(nullptr, 0);
|
||||||
|
|
||||||
int rv = http_parser_execute(&(parser->parser_), &settings, nullptr, 0);
|
|
||||||
|
|
||||||
if (parser->got_exception_)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (rv != 0) {
|
|
||||||
enum http_errno err = HTTP_PARSER_ERRNO(&parser->parser_);
|
|
||||||
|
|
||||||
Local<Value> e = Exception::Error(env->parse_error_string());
|
|
||||||
Local<Object> obj = e.As<Object>();
|
|
||||||
obj->Set(env->bytes_parsed_string(), Integer::New(env->isolate(), 0));
|
|
||||||
obj->Set(env->code_string(),
|
|
||||||
OneByteString(env->isolate(), http_errno_name(err)));
|
|
||||||
|
|
||||||
args.GetReturnValue().Set(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -467,8 +498,8 @@ class Parser : public AsyncWrap, public StreamListener {
|
|||||||
CHECK(args[0]->IsInt32());
|
CHECK(args[0]->IsInt32());
|
||||||
CHECK(args[1]->IsBoolean());
|
CHECK(args[1]->IsBoolean());
|
||||||
bool isReused = args[1]->IsTrue();
|
bool isReused = args[1]->IsTrue();
|
||||||
http_parser_type type =
|
parser_type_t type =
|
||||||
static_cast<http_parser_type>(args[0].As<Int32>()->Value());
|
static_cast<parser_type_t>(args[0].As<Int32>()->Value());
|
||||||
|
|
||||||
CHECK(type == HTTP_REQUEST || type == HTTP_RESPONSE);
|
CHECK(type == HTTP_REQUEST || type == HTTP_RESPONSE);
|
||||||
Parser* parser;
|
Parser* parser;
|
||||||
@ -492,7 +523,21 @@ class Parser : public AsyncWrap, public StreamListener {
|
|||||||
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder());
|
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder());
|
||||||
// Should always be called from the same context.
|
// Should always be called from the same context.
|
||||||
CHECK_EQ(env, parser->env());
|
CHECK_EQ(env, parser->env());
|
||||||
|
|
||||||
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
if (parser->execute_depth_) {
|
||||||
|
parser->pending_pause_ = should_pause;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_pause) {
|
||||||
|
llhttp_pause(&parser->parser_);
|
||||||
|
} else {
|
||||||
|
llhttp_resume(&parser->parser_);
|
||||||
|
}
|
||||||
|
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||||
http_parser_pause(&parser->parser_, should_pause);
|
http_parser_pause(&parser->parser_, should_pause);
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -602,10 +647,46 @@ class Parser : public AsyncWrap, public StreamListener {
|
|||||||
current_buffer_data_ = data;
|
current_buffer_data_ = data;
|
||||||
got_exception_ = false;
|
got_exception_ = false;
|
||||||
|
|
||||||
size_t nparsed =
|
parser_errno_t err;
|
||||||
http_parser_execute(&parser_, &settings, data, len);
|
|
||||||
|
|
||||||
Save();
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
// Do not allow re-entering `http_parser_execute()`
|
||||||
|
CHECK_EQ(execute_depth_, 0);
|
||||||
|
|
||||||
|
execute_depth_++;
|
||||||
|
if (data == nullptr) {
|
||||||
|
err = llhttp_finish(&parser_);
|
||||||
|
} else {
|
||||||
|
err = llhttp_execute(&parser_, data, len);
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
execute_depth_--;
|
||||||
|
|
||||||
|
// Calculate bytes read and resume after Upgrade/CONNECT pause
|
||||||
|
size_t nread = len;
|
||||||
|
if (err != HPE_OK) {
|
||||||
|
nread = llhttp_get_error_pos(&parser_) - data;
|
||||||
|
|
||||||
|
// This isn't a real pause, just a way to stop parsing early.
|
||||||
|
if (err == HPE_PAUSED_UPGRADE) {
|
||||||
|
err = HPE_OK;
|
||||||
|
llhttp_resume_after_upgrade(&parser_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply pending pause
|
||||||
|
if (pending_pause_) {
|
||||||
|
pending_pause_ = false;
|
||||||
|
llhttp_pause(&parser_);
|
||||||
|
}
|
||||||
|
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||||
|
size_t nread = http_parser_execute(&parser_, &settings, data, len);
|
||||||
|
if (data != nullptr) {
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
err = HTTP_PARSER_ERRNO(&parser_);
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
|
|
||||||
// Unassign the 'buffer_' variable
|
// Unassign the 'buffer_' variable
|
||||||
current_buffer_.Clear();
|
current_buffer_.Clear();
|
||||||
@ -616,22 +697,29 @@ class Parser : public AsyncWrap, public StreamListener {
|
|||||||
if (got_exception_)
|
if (got_exception_)
|
||||||
return scope.Escape(Local<Value>());
|
return scope.Escape(Local<Value>());
|
||||||
|
|
||||||
Local<Integer> nparsed_obj = Integer::New(env()->isolate(), nparsed);
|
Local<Integer> nread_obj = Integer::New(env()->isolate(), nread);
|
||||||
|
|
||||||
// If there was a parse error in one of the callbacks
|
// If there was a parse error in one of the callbacks
|
||||||
// TODO(bnoordhuis) What if there is an error on EOF?
|
// TODO(bnoordhuis) What if there is an error on EOF?
|
||||||
if (!parser_.upgrade && nparsed != len) {
|
if (!parser_.upgrade && err != HPE_OK) {
|
||||||
enum http_errno err = HTTP_PARSER_ERRNO(&parser_);
|
|
||||||
|
|
||||||
Local<Value> e = Exception::Error(env()->parse_error_string());
|
Local<Value> e = Exception::Error(env()->parse_error_string());
|
||||||
Local<Object> obj = e->ToObject(env()->isolate()->GetCurrentContext())
|
Local<Object> obj = e->ToObject(env()->isolate()->GetCurrentContext())
|
||||||
.ToLocalChecked();
|
.ToLocalChecked();
|
||||||
obj->Set(env()->bytes_parsed_string(), nparsed_obj);
|
obj->Set(env()->bytes_parsed_string(), nread_obj);
|
||||||
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
obj->Set(env()->code_string(),
|
||||||
|
OneByteString(env()->isolate(), llhttp_errno_name(err)));
|
||||||
|
obj->Set(env()->reason_string(),
|
||||||
|
OneByteString(env()->isolate(), parser_.reason));
|
||||||
|
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||||
obj->Set(env()->code_string(),
|
obj->Set(env()->code_string(),
|
||||||
OneByteString(env()->isolate(), http_errno_name(err)));
|
OneByteString(env()->isolate(), http_errno_name(err)));
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
|
|
||||||
return scope.Escape(e);
|
return scope.Escape(e);
|
||||||
}
|
}
|
||||||
return scope.Escape(nparsed_obj);
|
|
||||||
|
return scope.Escape(nread_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
Local<Array> CreateHeaders() {
|
Local<Array> CreateHeaders() {
|
||||||
@ -684,8 +772,12 @@ class Parser : public AsyncWrap, public StreamListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Init(enum http_parser_type type) {
|
void Init(parser_type_t type) {
|
||||||
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
llhttp_init(&parser_, type, &settings);
|
||||||
|
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||||
http_parser_init(&parser_, type);
|
http_parser_init(&parser_, type);
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
url_.Reset();
|
url_.Reset();
|
||||||
status_message_.Reset();
|
status_message_.Reset();
|
||||||
num_fields_ = 0;
|
num_fields_ = 0;
|
||||||
@ -695,7 +787,35 @@ class Parser : public AsyncWrap, public StreamListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
http_parser parser_;
|
int TrackHeader(size_t len) {
|
||||||
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
header_nread_ += len;
|
||||||
|
if (header_nread_ >= kMaxHeaderSize) {
|
||||||
|
llhttp_set_error_reason(&parser_, "Headers overflow");
|
||||||
|
return HPE_USER;
|
||||||
|
}
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int MaybePause() {
|
||||||
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
CHECK_NE(execute_depth_, 0);
|
||||||
|
|
||||||
|
if (!pending_pause_) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pending_pause_ = false;
|
||||||
|
llhttp_set_error_reason(&parser_, "Paused in callback");
|
||||||
|
return HPE_PAUSED;
|
||||||
|
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||||
|
return 0;
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
|
}
|
||||||
|
|
||||||
|
parser_t parser_;
|
||||||
StringPtr fields_[32]; // header fields
|
StringPtr fields_[32]; // header fields
|
||||||
StringPtr values_[32]; // header values
|
StringPtr values_[32]; // header values
|
||||||
StringPtr url_;
|
StringPtr url_;
|
||||||
@ -707,25 +827,37 @@ class Parser : public AsyncWrap, public StreamListener {
|
|||||||
Local<Object> current_buffer_;
|
Local<Object> current_buffer_;
|
||||||
size_t current_buffer_len_;
|
size_t current_buffer_len_;
|
||||||
char* current_buffer_data_;
|
char* current_buffer_data_;
|
||||||
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
unsigned int execute_depth_ = 0;
|
||||||
|
bool pending_pause_ = false;
|
||||||
|
uint64_t header_nread_ = 0;
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
|
|
||||||
// These are helper functions for filling `http_parser_settings`, which turn
|
// These are helper functions for filling `http_parser_settings`, which turn
|
||||||
// a member function of Parser into a C-style HTTP parser callback.
|
// a member function of Parser into a C-style HTTP parser callback.
|
||||||
template <typename Parser, Parser> struct Proxy;
|
template <typename Parser, Parser> struct Proxy;
|
||||||
template <typename Parser, typename ...Args, int (Parser::*Member)(Args...)>
|
template <typename Parser, typename ...Args, int (Parser::*Member)(Args...)>
|
||||||
struct Proxy<int (Parser::*)(Args...), Member> {
|
struct Proxy<int (Parser::*)(Args...), Member> {
|
||||||
static int Raw(http_parser* p, Args ... args) {
|
static int Raw(parser_t* p, Args ... args) {
|
||||||
Parser* parser = ContainerOf(&Parser::parser_, p);
|
Parser* parser = ContainerOf(&Parser::parser_, p);
|
||||||
return (parser->*Member)(std::forward<Args>(args)...);
|
int rv = (parser->*Member)(std::forward<Args>(args)...);
|
||||||
|
if (rv == 0) {
|
||||||
|
rv = parser->MaybePause();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef int (Parser::*Call)();
|
typedef int (Parser::*Call)();
|
||||||
typedef int (Parser::*DataCall)(const char* at, size_t length);
|
typedef int (Parser::*DataCall)(const char* at, size_t length);
|
||||||
|
|
||||||
static const struct http_parser_settings settings;
|
static const parser_settings_t settings;
|
||||||
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
|
static const uint64_t kMaxHeaderSize = 80 * 1024;
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct http_parser_settings Parser::settings = {
|
const parser_settings_t Parser::settings = {
|
||||||
Proxy<Call, &Parser::on_message_begin>::Raw,
|
Proxy<Call, &Parser::on_message_begin>::Raw,
|
||||||
Proxy<DataCall, &Parser::on_url>::Raw,
|
Proxy<DataCall, &Parser::on_url>::Raw,
|
||||||
Proxy<DataCall, &Parser::on_status>::Raw,
|
Proxy<DataCall, &Parser::on_status>::Raw,
|
||||||
@ -734,8 +866,13 @@ const struct http_parser_settings Parser::settings = {
|
|||||||
Proxy<Call, &Parser::on_headers_complete>::Raw,
|
Proxy<Call, &Parser::on_headers_complete>::Raw,
|
||||||
Proxy<DataCall, &Parser::on_body>::Raw,
|
Proxy<DataCall, &Parser::on_body>::Raw,
|
||||||
Proxy<Call, &Parser::on_message_complete>::Raw,
|
Proxy<Call, &Parser::on_message_complete>::Raw,
|
||||||
nullptr, // on_chunk_header
|
#ifdef NODE_EXPERIMENTAL_HTTP
|
||||||
nullptr // on_chunk_complete
|
Proxy<Call, &Parser::on_chunk_header>::Raw,
|
||||||
|
Proxy<Call, &Parser::on_chunk_complete>::Raw,
|
||||||
|
#else /* !NODE_EXPERIMENTAL_HTTP */
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
#endif /* NODE_EXPERIMENTAL_HTTP */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ function flushPool() {
|
|||||||
function demoBug(part1, part2) {
|
function demoBug(part1, part2) {
|
||||||
flushPool();
|
flushPool();
|
||||||
|
|
||||||
const parser = new HTTPParser(0);
|
const parser = new HTTPParser(HTTPParser.REQUEST);
|
||||||
|
|
||||||
parser.headers = [];
|
parser.headers = [];
|
||||||
parser.url = '';
|
parser.url = '';
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
|
||||||
const expected_keys = ['ares', 'http_parser', 'modules', 'node',
|
const expected_keys = ['ares', 'modules', 'node',
|
||||||
'uv', 'v8', 'zlib', 'nghttp2', 'napi'];
|
'uv', 'v8', 'zlib', 'nghttp2', 'napi'];
|
||||||
|
|
||||||
if (common.hasCrypto) {
|
if (common.hasCrypto) {
|
||||||
@ -16,6 +16,9 @@ if (common.hasIntl) {
|
|||||||
expected_keys.push('unicode');
|
expected_keys.push('unicode');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expected_keys.push(
|
||||||
|
process.versions.llhttp === undefined ? 'http_parser' : 'llhttp');
|
||||||
|
|
||||||
expected_keys.sort();
|
expected_keys.sort();
|
||||||
const actual_keys = Object.keys(process.versions).sort();
|
const actual_keys = Object.keys(process.versions).sort();
|
||||||
|
|
||||||
@ -24,7 +27,8 @@ assert.deepStrictEqual(actual_keys, expected_keys);
|
|||||||
const commonTemplate = /^\d+\.\d+\.\d+(?:-.*)?$/;
|
const commonTemplate = /^\d+\.\d+\.\d+(?:-.*)?$/;
|
||||||
|
|
||||||
assert(commonTemplate.test(process.versions.ares));
|
assert(commonTemplate.test(process.versions.ares));
|
||||||
assert(commonTemplate.test(process.versions.http_parser));
|
assert(commonTemplate.test(process.versions.llhttp === undefined ?
|
||||||
|
process.versions.http_parser : process.versions.llhttp));
|
||||||
assert(commonTemplate.test(process.versions.node));
|
assert(commonTemplate.test(process.versions.node));
|
||||||
assert(commonTemplate.test(process.versions.uv));
|
assert(commonTemplate.test(process.versions.uv));
|
||||||
assert(commonTemplate.test(process.versions.zlib));
|
assert(commonTemplate.test(process.versions.zlib));
|
||||||
|
@ -36,8 +36,9 @@ proc.once('exit', common.mustCall(() => {
|
|||||||
|
|
||||||
assert(traces.some((trace) =>
|
assert(traces.some((trace) =>
|
||||||
trace.name === 'node' &&
|
trace.name === 'node' &&
|
||||||
trace.args.process.versions.http_parser ===
|
(trace.args.process.versions.http_parser ===
|
||||||
process.versions.http_parser &&
|
process.versions.http_parser ||
|
||||||
|
trace.args.process.versions.llhttp === process.versions.llhttp) &&
|
||||||
trace.args.process.versions.node ===
|
trace.args.process.versions.node ===
|
||||||
process.versions.node &&
|
process.versions.node &&
|
||||||
trace.args.process.versions.v8 ===
|
trace.args.process.versions.v8 ===
|
||||||
|
@ -149,7 +149,7 @@ if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check
|
|||||||
|
|
||||||
{
|
{
|
||||||
const { HTTPParser } = internalBinding('http_parser');
|
const { HTTPParser } = internalBinding('http_parser');
|
||||||
testInitialized(new HTTPParser(0), 'HTTPParser');
|
testInitialized(new HTTPParser(HTTPParser.REQUEST), 'HTTPParser');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
addlicense "libuv" "deps/uv" "$(cat ${rootdir}/deps/uv/LICENSE)"
|
addlicense "libuv" "deps/uv" "$(cat ${rootdir}/deps/uv/LICENSE)"
|
||||||
|
addlicense "llhttp" "deps/llhttp" "$(cat deps/llhttp/LICENSE-MIT)"
|
||||||
addlicense "OpenSSL" "deps/openssl" \
|
addlicense "OpenSSL" "deps/openssl" \
|
||||||
"$(sed -e '/^ \*\/$/,$d' -e '/^ [^*].*$/d' -e '/\/\*.*$/d' -e '/^$/d' -e 's/^[/ ]\* *//' ${rootdir}/deps/openssl/openssl/LICENSE)"
|
"$(sed -e '/^ \*\/$/,$d' -e '/^ [^*].*$/d' -e '/\/\*.*$/d' -e '/^$/d' -e 's/^[/ ]\* *//' ${rootdir}/deps/openssl/openssl/LICENSE)"
|
||||||
addlicense "Punycode.js" "lib/punycode.js" \
|
addlicense "Punycode.js" "lib/punycode.js" \
|
||||||
|
Loading…
x
Reference in New Issue
Block a user