diff --git a/CHANGELOG.md b/CHANGELOG.md index ef33a2de155550..1f417499eae2c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,9 +58,9 @@ https://iojs.org/api/console.html https://iojs.org/api/cluster.html -(**DETAILS TO BE ADDED HERE**) - - Updated `cluster` to use round-robin load balancing by default on non-Windows platforms. The scheduling policy is configurable however. +- `--debug` has been made cluster-aware. +- Many bug fixes. ### crypto diff --git a/GOVERNANCE.md b/GOVERNANCE.md index d4359ecd752a03..f9d4afe463f1c5 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -20,7 +20,7 @@ experience with the management of the io.js project. Membership is expected to evolve over time according to the needs of the project. For the current list of TC members, see the project -[README.md](./#current-project-team-members). +[README.md](./README.md#current-project-team-members). ## Collaborators @@ -56,7 +56,7 @@ request or issue. The TC should serve as the final arbiter where required. For the current list of Collaborators, see the project -[README.md](./#current-project-team-members). +[README.md](./README.md#current-project-team-members). A guide for Collaborators is maintained in [COLLABORATOR_GUIDE.md](./COLLABORATOR_GUIDE.md). diff --git a/LICENSE b/LICENSE index 1e05fee1c4630b..dd730277043756 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,6 @@ io.js is licensed for use as follows: -==== - +""" Copyright io.js contributors. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy @@ -21,12 +20,12 @@ 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. - -=== +""" This license applies to parts of io.js originating from the https://github.com/joyent/node repository: +""" Copyright Joyent, Inc. and other Node contributors. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -45,11 +44,12 @@ 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. +""" -==== +The io.js license applies to all parts of io.js that are not externally +maintained libraries. -This license applies to all parts of io.js that are not externally -maintained libraries. The externally maintained libraries used by io.js are: +The externally maintained libraries used by io.js are: - V8, located at deps/v8. V8's license follows: """ diff --git a/Makefile b/Makefile index 0072973e3da6c5..2d22a12293968e 100644 --- a/Makefile +++ b/Makefile @@ -188,11 +188,7 @@ apidoc_dirs = out/doc out/doc/api/ out/doc/api/assets apiassets = $(subst api_assets,api/assets,$(addprefix out/,$(wildcard doc/api_assets/*))) -website_files = \ - out/doc/sh_main.js \ - out/doc/sh_javascript.min.js - -doc: $(apidoc_dirs) $(website_files) $(apiassets) $(apidocs) tools/doc/ $(NODE_EXE) +doc: $(apidoc_dirs) $(apiassets) $(apidocs) tools/doc/ $(NODE_EXE) $(apidoc_dirs): mkdir -p $@ diff --git a/benchmark/README.md b/benchmark/README.md index 695f0eac7fab78..95ade9840d354a 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -1,7 +1,7 @@ -# Node.js core benchmark tests +# io.js core benchmark tests This folder contains benchmark tests to measure the performance for certain -Node.js APIs. +io.js APIs. ## How to run tests @@ -10,7 +10,7 @@ There are two ways to run benchmark tests: 1. Run all tests of a given type, for example, buffers ```sh -node benchmark/common.js buffers +iojs benchmark/common.js buffers ``` The above command will find all scripts under `buffers` directory and require @@ -70,7 +70,7 @@ buffers/buffer-read.js noAssert=false buffer=fast type=UInt16BE millions=1: 244. 2. Run an individual test, for example, buffer-slice.js ```sh -node benchmark/buffers/buffer-read.js +iojs benchmark/buffers/buffer-read.js ``` The output: ``` diff --git a/benchmark/common.js b/benchmark/common.js index 7d1730c373d8ae..825bfb43f080aa 100644 --- a/benchmark/common.js +++ b/benchmark/common.js @@ -8,7 +8,7 @@ exports.PORT = process.env.PORT || 12346; if (module === require.main) { var type = process.argv[2]; if (!type) { - console.error('usage:\n ./node benchmark/common.js '); + console.error('usage:\n ./iojs benchmark/common.js '); process.exit(1); } diff --git a/benchmark/http-flamegraph.sh b/benchmark/http-flamegraph.sh index 7df0c1575f511d..5dc41b541bdafb 100644 --- a/benchmark/http-flamegraph.sh +++ b/benchmark/http-flamegraph.sh @@ -1,7 +1,7 @@ #!/bin/bash cd "$(dirname "$(dirname $0)")" -node=${NODE:-./node} +node=${NODE:-./iojs} name=${NAME:-stacks} @@ -22,7 +22,7 @@ fi ulimit -n 100000 $node benchmark/http_simple.js & nodepid=$! -echo "node pid = $nodepid" +echo "iojs pid = $nodepid" sleep 1 # has to stay alive until dtrace exits @@ -62,7 +62,7 @@ echo 'Turn the stacks into a svg' stackvis dtrace flamegraph-svg < "$name".src > "$name".raw.svg echo 'Prune tiny stacks out of the graph' -node -e ' +iojs -e ' var infile = process.argv[1]; var outfile = process.argv[2]; var output = ""; diff --git a/benchmark/http.sh b/benchmark/http.sh index 9a844a900438fe..63fc02e973093f 100755 --- a/benchmark/http.sh +++ b/benchmark/http.sh @@ -24,7 +24,7 @@ if [ "$k" = "no" ]; then else k="-k" fi -node=${NODE:-./node} +node=${NODE:-./iojs} $node benchmark/http_simple.js & npid=$! diff --git a/benchmark/http_simple_bench.sh b/benchmark/http_simple_bench.sh index 6948227974908a..2066047b9e37fc 100755 --- a/benchmark/http_simple_bench.sh +++ b/benchmark/http_simple_bench.sh @@ -14,7 +14,7 @@ if [ ! -d benchmark/ ]; then fi if [ $SERVER == "127.0.0.1" ]; then - ./node benchmark/http_simple.js & + ./iojs benchmark/http_simple.js & node_pid=$! sleep 1 fi diff --git a/benchmark/plot.R b/benchmark/plot.R index 1f902eddfe6090..288e8bd5a48fc5 100755 --- a/benchmark/plot.R +++ b/benchmark/plot.R @@ -52,7 +52,7 @@ ab.load <- function (filename, name) { filename <- args[0:1] -data <- ab.load(filename, "node") +data <- ab.load(filename, "iojs") # histogram diff --git a/deps/http_parser/.gitignore b/deps/http_parser/.gitignore index 594f3047f4716e..32cb51b2d3f6f6 100644 --- a/deps/http_parser/.gitignore +++ b/deps/http_parser/.gitignore @@ -5,6 +5,7 @@ tags test test_g test_fast +bench url_parser parsertrace parsertrace_g diff --git a/deps/http_parser/.mailmap b/deps/http_parser/.mailmap index 75a187c5685746..278d1412637240 100644 --- a/deps/http_parser/.mailmap +++ b/deps/http_parser/.mailmap @@ -5,3 +5,4 @@ Salman Haq Simon Zimmermann Thomas LE ROUX LE ROUX Thomas Thomas LE ROUX Thomas LE ROUX +Fedor Indutny diff --git a/deps/http_parser/.travis.yml b/deps/http_parser/.travis.yml index ae85af020a8280..4b038e6e62d638 100644 --- a/deps/http_parser/.travis.yml +++ b/deps/http_parser/.travis.yml @@ -10,4 +10,4 @@ script: notifications: email: false irc: - - "irc.freenode.net#libuv" + - "irc.freenode.net#node-ci" diff --git a/deps/http_parser/AUTHORS b/deps/http_parser/AUTHORS index 51b53b12537210..29cdbb16d309af 100644 --- a/deps/http_parser/AUTHORS +++ b/deps/http_parser/AUTHORS @@ -39,12 +39,25 @@ BogDan Vatra Peter Faiman Corey Richardson Tóth Tamás -Patrik Stutz Cam Swords Chris Dickinson Uli Köhler Charlie Somerville +Patrik Stutz Fedor Indutny runner Alexis Campailla David Wragg +Vinnie Falco +Alex Butum +Rex Feng +Alex Kocharin +Mark Koopman +Helge Heß +Alexis La Goutte +George Miroshnykov +Maciej Małecki +Marc O'Morain +Jeff Pinner +Timothy J Fontaine +Akagi201 diff --git a/deps/http_parser/CONTRIBUTIONS b/deps/http_parser/CONTRIBUTIONS deleted file mode 100644 index 11ba31e4b990e3..00000000000000 --- a/deps/http_parser/CONTRIBUTIONS +++ /dev/null @@ -1,4 +0,0 @@ -Contributors must agree to the Contributor License Agreement before patches -can be accepted. - -http://spreadsheets2.google.com/viewform?hl=en&formkey=dDJXOGUwbzlYaWM4cHN1MERwQS1CSnc6MQ diff --git a/deps/http_parser/Makefile b/deps/http_parser/Makefile index 3ce463b88bf242..3600503bd082ba 100644 --- a/deps/http_parser/Makefile +++ b/deps/http_parser/Makefile @@ -19,20 +19,25 @@ # IN THE SOFTWARE. PLATFORM ?= $(shell sh -c 'uname -s | tr "[A-Z]" "[a-z]"') -SONAME ?= libhttp_parser.so.2.3 +SONAME ?= libhttp_parser.so.2.4.1 CC?=gcc AR?=ar +CPPFLAGS ?= +LDFLAGS ?= + CPPFLAGS += -I. CPPFLAGS_DEBUG = $(CPPFLAGS) -DHTTP_PARSER_STRICT=1 CPPFLAGS_DEBUG += $(CPPFLAGS_DEBUG_EXTRA) CPPFLAGS_FAST = $(CPPFLAGS) -DHTTP_PARSER_STRICT=0 CPPFLAGS_FAST += $(CPPFLAGS_FAST_EXTRA) +CPPFLAGS_BENCH = $(CPPFLAGS_FAST) CFLAGS += -Wall -Wextra -Werror CFLAGS_DEBUG = $(CFLAGS) -O0 -g $(CFLAGS_DEBUG_EXTRA) CFLAGS_FAST = $(CFLAGS) -O3 $(CFLAGS_FAST_EXTRA) +CFLAGS_BENCH = $(CFLAGS_FAST) -Wno-unused-parameter CFLAGS_LIB = $(CFLAGS_FAST) -fPIC LDFLAGS_LIB = $(LDFLAGS) -shared @@ -61,6 +66,12 @@ test_fast: http_parser.o test.o http_parser.h test.o: test.c http_parser.h Makefile $(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) -c test.c -o $@ +bench: http_parser.o bench.o + $(CC) $(CFLAGS_BENCH) $(LDFLAGS) http_parser.o bench.o -o $@ + +bench.o: bench.c http_parser.h Makefile + $(CC) $(CPPFLAGS_BENCH) $(CFLAGS_BENCH) -c bench.c -o $@ + http_parser.o: http_parser.c http_parser.h Makefile $(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) -c http_parser.c diff --git a/deps/http_parser/README.md b/deps/http_parser/README.md index 0bf5d359aca912..7c54dd42d087c3 100644 --- a/deps/http_parser/README.md +++ b/deps/http_parser/README.md @@ -61,7 +61,7 @@ if (recved < 0) { } /* Start up / continue the parser. - * Note we pass recved==0 to signal that EOF has been recieved. + * Note we pass recved==0 to signal that EOF has been received. */ nparsed = http_parser_execute(parser, &settings, buf, recved); @@ -75,7 +75,7 @@ if (parser->upgrade) { HTTP needs to know where the end of the stream is. For example, sometimes servers send responses without Content-Length and expect the client to consume input (for the body) until EOF. To tell http_parser about EOF, give -`0` as the forth parameter to `http_parser_execute()`. Callbacks and errors +`0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors can still be encountered during an EOF, so one must still be prepared to receive them. @@ -110,7 +110,7 @@ followed by non-HTTP data. information the Web Socket protocol.) To support this, the parser will treat this as a normal HTTP message without a -body. Issuing both on_headers_complete and on_message_complete callbacks. However +body, issuing both on_headers_complete and on_message_complete callbacks. However http_parser_execute() will stop parsing at the end of the headers and return. The user is expected to check if `parser->upgrade` has been set to 1 after @@ -131,7 +131,7 @@ There are two types of callbacks: * notification `typedef int (*http_cb) (http_parser*);` Callbacks: on_message_begin, on_headers_complete, on_message_complete. * data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);` - Callbacks: (requests only) on_uri, + Callbacks: (requests only) on_url, (common) on_header_field, on_header_value, on_body; Callbacks must return 0 on success. Returning a non-zero value indicates @@ -145,7 +145,7 @@ buffer to avoid copying memory around if this fits your application. Reading headers may be a tricky task if you read/parse headers partially. Basically, you need to remember whether last header callback was field or value -and apply following logic: +and apply the following logic: (on_header_field and on_header_value shortened to on_h_*) ------------------------ ------------ -------------------------------------------- diff --git a/deps/http_parser/bench.c b/deps/http_parser/bench.c new file mode 100644 index 00000000000000..5b452fa1cdb6e6 --- /dev/null +++ b/deps/http_parser/bench.c @@ -0,0 +1,111 @@ +/* Copyright Fedor Indutny. All rights reserved. + * + * 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. + */ +#include "http_parser.h" +#include +#include +#include +#include + +static const char data[] = + "POST /joyent/http-parser HTTP/1.1\r\n" + "Host: github.com\r\n" + "DNT: 1\r\n" + "Accept-Encoding: gzip, deflate, sdch\r\n" + "Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\r\n" + "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/39.0.2171.65 Safari/537.36\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9," + "image/webp,*/*;q=0.8\r\n" + "Referer: https://github.com/joyent/http-parser\r\n" + "Connection: keep-alive\r\n" + "Transfer-Encoding: chunked\r\n" + "Cache-Control: max-age=0\r\n\r\nb\r\nhello world\r\n0\r\n\r\n"; +static const size_t data_len = sizeof(data) - 1; + +static int on_info(http_parser* p) { + return 0; +} + + +static int on_data(http_parser* p, const char *at, size_t length) { + return 0; +} + +static http_parser_settings settings = { + .on_message_begin = on_info, + .on_headers_complete = on_info, + .on_message_complete = on_info, + .on_header_field = on_data, + .on_header_value = on_data, + .on_url = on_data, + .on_status = on_data, + .on_body = on_data +}; + +int bench(int iter_count, int silent) { + struct http_parser parser; + int i; + int err; + struct timeval start; + struct timeval end; + float rps; + + if (!silent) { + err = gettimeofday(&start, NULL); + assert(err == 0); + } + + for (i = 0; i < iter_count; i++) { + size_t parsed; + http_parser_init(&parser, HTTP_REQUEST); + + parsed = http_parser_execute(&parser, &settings, data, data_len); + assert(parsed == data_len); + } + + if (!silent) { + err = gettimeofday(&end, NULL); + assert(err == 0); + + fprintf(stdout, "Benchmark result:\n"); + + rps = (float) (end.tv_sec - start.tv_sec) + + (end.tv_usec - start.tv_usec) * 1e-6f; + fprintf(stdout, "Took %f seconds to run\n", rps); + + rps = (float) iter_count / rps; + fprintf(stdout, "%f req/sec\n", rps); + fflush(stdout); + } + + return 0; +} + +int main(int argc, char** argv) { + if (argc == 2 && strcmp(argv[1], "infinite") == 0) { + for (;;) + bench(5000000, 1); + return 0; + } else { + return bench(5000000, 0); + } +} diff --git a/deps/http_parser/contrib/parsertrace.c b/deps/http_parser/contrib/parsertrace.c index c9bc71ec017e7c..e7153680f467de 100644 --- a/deps/http_parser/contrib/parsertrace.c +++ b/deps/http_parser/contrib/parsertrace.c @@ -111,14 +111,14 @@ int main(int argc, char* argv[]) { FILE* file = fopen(filename, "r"); if (file == NULL) { perror("fopen"); - return EXIT_FAILURE; + goto fail; } fseek(file, 0, SEEK_END); long file_length = ftell(file); if (file_length == -1) { perror("ftell"); - return EXIT_FAILURE; + goto fail; } fseek(file, 0, SEEK_SET); @@ -126,7 +126,7 @@ int main(int argc, char* argv[]) { if (fread(data, 1, file_length, file) != (size_t)file_length) { fprintf(stderr, "couldn't read entire file\n"); free(data); - return EXIT_FAILURE; + goto fail; } http_parser_settings settings; @@ -149,8 +149,12 @@ int main(int argc, char* argv[]) { "Error: %s (%s)\n", http_errno_description(HTTP_PARSER_ERRNO(&parser)), http_errno_name(HTTP_PARSER_ERRNO(&parser))); - return EXIT_FAILURE; + goto fail; } return EXIT_SUCCESS; + +fail: + fclose(file); + return EXIT_FAILURE; } diff --git a/deps/http_parser/http_parser.c b/deps/http_parser/http_parser.c index 70cc9bd37b2da6..3e114c02ca14c4 100644 --- a/deps/http_parser/http_parser.c +++ b/deps/http_parser/http_parser.c @@ -56,19 +56,41 @@ do { \ parser->http_errno = (e); \ } while(0) +#define CURRENT_STATE() p_state +#define UPDATE_STATE(V) p_state = (enum state) (V); +#define RETURN(V) \ +do { \ + parser->state = CURRENT_STATE(); \ + return (V); \ +} while (0); +#define REEXECUTE() \ + --p; \ + break; + + +#ifdef __GNUC__ +# define LIKELY(X) __builtin_expect(!!(X), 1) +# define UNLIKELY(X) __builtin_expect(!!(X), 0) +#else +# define LIKELY(X) (X) +# define UNLIKELY(X) (X) +#endif + /* Run the notify callback FOR, returning ER if it fails */ #define CALLBACK_NOTIFY_(FOR, ER) \ do { \ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ \ - if (settings->on_##FOR) { \ - if (0 != settings->on_##FOR(parser)) { \ + if (LIKELY(settings->on_##FOR)) { \ + parser->state = CURRENT_STATE(); \ + if (UNLIKELY(0 != settings->on_##FOR(parser))) { \ SET_ERRNO(HPE_CB_##FOR); \ } \ + UPDATE_STATE(parser->state); \ \ /* We either errored above or got paused; get out */ \ - if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \ + if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ return (ER); \ } \ } \ @@ -86,13 +108,16 @@ do { \ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ \ if (FOR##_mark) { \ - if (settings->on_##FOR) { \ - if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) { \ + if (LIKELY(settings->on_##FOR)) { \ + parser->state = CURRENT_STATE(); \ + if (UNLIKELY(0 != \ + settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \ SET_ERRNO(HPE_CB_##FOR); \ } \ + UPDATE_STATE(parser->state); \ \ /* We either errored above or got paused; get out */ \ - if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \ + if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ return (ER); \ } \ } \ @@ -116,6 +141,26 @@ do { \ } \ } while (0) +/* Don't allow the total size of the HTTP headers (including the status + * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect + * embedders against denial-of-service attacks where the attacker feeds + * us a never-ending header that the embedder keeps buffering. + * + * This check is arguably the responsibility of embedders but we're doing + * it on the embedder's behalf because most won't bother and this way we + * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger + * than any reasonable request or response so this should never affect + * day-to-day operation. + */ +#define COUNT_HEADER_SIZE(V) \ +do { \ + parser->nread += (V); \ + if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \ + SET_ERRNO(HPE_HEADER_OVERFLOW); \ + goto error; \ + } \ +} while (0) + #define PROXY_CONNECTION "proxy-connection" #define CONNECTION "connection" @@ -334,12 +379,16 @@ enum header_states , h_upgrade , h_matching_transfer_encoding_chunked + , h_matching_connection_token_start , h_matching_connection_keep_alive , h_matching_connection_close + , h_matching_connection_upgrade + , h_matching_connection_token , h_transfer_encoding_chunked , h_connection_keep_alive , h_connection_close + , h_connection_upgrade }; enum http_host_state @@ -371,6 +420,8 @@ enum http_host_state (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ (c) == '$' || (c) == ',') +#define STRICT_TOKEN(c) (tokens[(unsigned char)c]) + #if HTTP_PARSER_STRICT #define TOKEN(c) (tokens[(unsigned char)c]) #define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) @@ -586,6 +637,7 @@ size_t http_parser_execute (http_parser *parser, const char *url_mark = 0; const char *body_mark = 0; const char *status_mark = 0; + enum state p_state = (enum state) parser->state; /* We're in an error state. Don't bother doing anything. */ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { @@ -593,7 +645,7 @@ size_t http_parser_execute (http_parser *parser, } if (len == 0) { - switch (parser->state) { + switch (CURRENT_STATE()) { case s_body_identity_eof: /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if * we got paused. @@ -614,11 +666,11 @@ size_t http_parser_execute (http_parser *parser, } - if (parser->state == s_header_field) + if (CURRENT_STATE() == s_header_field) header_field_mark = data; - if (parser->state == s_header_value) + if (CURRENT_STATE() == s_header_value) header_value_mark = data; - switch (parser->state) { + switch (CURRENT_STATE()) { case s_req_path: case s_req_schema: case s_req_schema_slash: @@ -635,38 +687,23 @@ size_t http_parser_execute (http_parser *parser, case s_res_status: status_mark = data; break; + default: + break; } for (p=data; p != data + len; p++) { ch = *p; - if (PARSING_HEADER(parser->state)) { - ++parser->nread; - /* Don't allow the total size of the HTTP headers (including the status - * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect - * embedders against denial-of-service attacks where the attacker feeds - * us a never-ending header that the embedder keeps buffering. - * - * This check is arguably the responsibility of embedders but we're doing - * it on the embedder's behalf because most won't bother and this way we - * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger - * than any reasonable request or response so this should never affect - * day-to-day operation. - */ - if (parser->nread > HTTP_MAX_HEADER_SIZE) { - SET_ERRNO(HPE_HEADER_OVERFLOW); - goto error; - } - } + if (PARSING_HEADER(CURRENT_STATE())) + COUNT_HEADER_SIZE(1); - reexecute_byte: - switch (parser->state) { + switch (CURRENT_STATE()) { case s_dead: /* this state is used after a 'Connection: close' message * the parser will error out if it reads another message */ - if (ch == CR || ch == LF) + if (LIKELY(ch == CR || ch == LF)) break; SET_ERRNO(HPE_CLOSED_CONNECTION); @@ -680,13 +717,13 @@ size_t http_parser_execute (http_parser *parser, parser->content_length = ULLONG_MAX; if (ch == 'H') { - parser->state = s_res_or_resp_H; + UPDATE_STATE(s_res_or_resp_H); CALLBACK_NOTIFY(message_begin); } else { parser->type = HTTP_REQUEST; - parser->state = s_start_req; - goto reexecute_byte; + UPDATE_STATE(s_start_req); + REEXECUTE(); } break; @@ -695,9 +732,9 @@ size_t http_parser_execute (http_parser *parser, case s_res_or_resp_H: if (ch == 'T') { parser->type = HTTP_RESPONSE; - parser->state = s_res_HT; + UPDATE_STATE(s_res_HT); } else { - if (ch != 'E') { + if (UNLIKELY(ch != 'E')) { SET_ERRNO(HPE_INVALID_CONSTANT); goto error; } @@ -705,7 +742,7 @@ size_t http_parser_execute (http_parser *parser, parser->type = HTTP_REQUEST; parser->method = HTTP_HEAD; parser->index = 2; - parser->state = s_req_method; + UPDATE_STATE(s_req_method); } break; @@ -716,7 +753,7 @@ size_t http_parser_execute (http_parser *parser, switch (ch) { case 'H': - parser->state = s_res_H; + UPDATE_STATE(s_res_H); break; case CR: @@ -734,39 +771,39 @@ size_t http_parser_execute (http_parser *parser, case s_res_H: STRICT_CHECK(ch != 'T'); - parser->state = s_res_HT; + UPDATE_STATE(s_res_HT); break; case s_res_HT: STRICT_CHECK(ch != 'T'); - parser->state = s_res_HTT; + UPDATE_STATE(s_res_HTT); break; case s_res_HTT: STRICT_CHECK(ch != 'P'); - parser->state = s_res_HTTP; + UPDATE_STATE(s_res_HTTP); break; case s_res_HTTP: STRICT_CHECK(ch != '/'); - parser->state = s_res_first_http_major; + UPDATE_STATE(s_res_first_http_major); break; case s_res_first_http_major: - if (ch < '0' || ch > '9') { + if (UNLIKELY(ch < '0' || ch > '9')) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_major = ch - '0'; - parser->state = s_res_http_major; + UPDATE_STATE(s_res_http_major); break; /* major HTTP version or dot */ case s_res_http_major: { if (ch == '.') { - parser->state = s_res_first_http_minor; + UPDATE_STATE(s_res_first_http_minor); break; } @@ -778,7 +815,7 @@ size_t http_parser_execute (http_parser *parser, parser->http_major *= 10; parser->http_major += ch - '0'; - if (parser->http_major > 999) { + if (UNLIKELY(parser->http_major > 999)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } @@ -788,24 +825,24 @@ size_t http_parser_execute (http_parser *parser, /* first digit of minor HTTP version */ case s_res_first_http_minor: - if (!IS_NUM(ch)) { + if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor = ch - '0'; - parser->state = s_res_http_minor; + UPDATE_STATE(s_res_http_minor); break; /* minor HTTP version or end of request line */ case s_res_http_minor: { if (ch == ' ') { - parser->state = s_res_first_status_code; + UPDATE_STATE(s_res_first_status_code); break; } - if (!IS_NUM(ch)) { + if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } @@ -813,7 +850,7 @@ size_t http_parser_execute (http_parser *parser, parser->http_minor *= 10; parser->http_minor += ch - '0'; - if (parser->http_minor > 999) { + if (UNLIKELY(parser->http_minor > 999)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } @@ -832,7 +869,7 @@ size_t http_parser_execute (http_parser *parser, goto error; } parser->status_code = ch - '0'; - parser->state = s_res_status_code; + UPDATE_STATE(s_res_status_code); break; } @@ -841,13 +878,13 @@ size_t http_parser_execute (http_parser *parser, if (!IS_NUM(ch)) { switch (ch) { case ' ': - parser->state = s_res_status_start; + UPDATE_STATE(s_res_status_start); break; case CR: - parser->state = s_res_line_almost_done; + UPDATE_STATE(s_res_line_almost_done); break; case LF: - parser->state = s_header_field_start; + UPDATE_STATE(s_header_field_start); break; default: SET_ERRNO(HPE_INVALID_STATUS); @@ -859,7 +896,7 @@ size_t http_parser_execute (http_parser *parser, parser->status_code *= 10; parser->status_code += ch - '0'; - if (parser->status_code > 999) { + if (UNLIKELY(parser->status_code > 999)) { SET_ERRNO(HPE_INVALID_STATUS); goto error; } @@ -870,30 +907,30 @@ size_t http_parser_execute (http_parser *parser, case s_res_status_start: { if (ch == CR) { - parser->state = s_res_line_almost_done; + UPDATE_STATE(s_res_line_almost_done); break; } if (ch == LF) { - parser->state = s_header_field_start; + UPDATE_STATE(s_header_field_start); break; } MARK(status); - parser->state = s_res_status; + UPDATE_STATE(s_res_status); parser->index = 0; break; } case s_res_status: if (ch == CR) { - parser->state = s_res_line_almost_done; + UPDATE_STATE(s_res_line_almost_done); CALLBACK_DATA(status); break; } if (ch == LF) { - parser->state = s_header_field_start; + UPDATE_STATE(s_header_field_start); CALLBACK_DATA(status); break; } @@ -902,7 +939,7 @@ size_t http_parser_execute (http_parser *parser, case s_res_line_almost_done: STRICT_CHECK(ch != LF); - parser->state = s_header_field_start; + UPDATE_STATE(s_header_field_start); break; case s_start_req: @@ -912,7 +949,7 @@ size_t http_parser_execute (http_parser *parser, parser->flags = 0; parser->content_length = ULLONG_MAX; - if (!IS_ALPHA(ch)) { + if (UNLIKELY(!IS_ALPHA(ch))) { SET_ERRNO(HPE_INVALID_METHOD); goto error; } @@ -925,7 +962,7 @@ size_t http_parser_execute (http_parser *parser, case 'G': parser->method = HTTP_GET; break; case 'H': parser->method = HTTP_HEAD; break; case 'L': parser->method = HTTP_LOCK; break; - case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break; + case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break; case 'N': parser->method = HTTP_NOTIFY; break; case 'O': parser->method = HTTP_OPTIONS; break; case 'P': parser->method = HTTP_POST; @@ -939,7 +976,7 @@ size_t http_parser_execute (http_parser *parser, SET_ERRNO(HPE_INVALID_METHOD); goto error; } - parser->state = s_req_method; + UPDATE_STATE(s_req_method); CALLBACK_NOTIFY(message_begin); @@ -949,14 +986,14 @@ size_t http_parser_execute (http_parser *parser, case s_req_method: { const char *matcher; - if (ch == '\0') { + if (UNLIKELY(ch == '\0')) { SET_ERRNO(HPE_INVALID_METHOD); goto error; } matcher = method_strings[parser->method]; if (ch == ' ' && matcher[parser->index] == '\0') { - parser->state = s_req_spaces_before_url; + UPDATE_STATE(s_req_spaces_before_url); } else if (ch == matcher[parser->index]) { ; /* nada */ } else if (parser->method == HTTP_CONNECT) { @@ -977,6 +1014,8 @@ size_t http_parser_execute (http_parser *parser, parser->method = HTTP_MSEARCH; } else if (parser->index == 2 && ch == 'A') { parser->method = HTTP_MKACTIVITY; + } else if (parser->index == 3 && ch == 'A') { + parser->method = HTTP_MKCALENDAR; } else { SET_ERRNO(HPE_INVALID_METHOD); goto error; @@ -1035,11 +1074,11 @@ size_t http_parser_execute (http_parser *parser, MARK(url); if (parser->method == HTTP_CONNECT) { - parser->state = s_req_server_start; + UPDATE_STATE(s_req_server_start); } - parser->state = parse_url_char((enum state)parser->state, ch); - if (parser->state == s_dead) { + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { SET_ERRNO(HPE_INVALID_URL); goto error; } @@ -1060,8 +1099,8 @@ size_t http_parser_execute (http_parser *parser, SET_ERRNO(HPE_INVALID_URL); goto error; default: - parser->state = parse_url_char((enum state)parser->state, ch); - if (parser->state == s_dead) { + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { SET_ERRNO(HPE_INVALID_URL); goto error; } @@ -1080,21 +1119,21 @@ size_t http_parser_execute (http_parser *parser, { switch (ch) { case ' ': - parser->state = s_req_http_start; + UPDATE_STATE(s_req_http_start); CALLBACK_DATA(url); break; case CR: case LF: parser->http_major = 0; parser->http_minor = 9; - parser->state = (ch == CR) ? + UPDATE_STATE((ch == CR) ? s_req_line_almost_done : - s_header_field_start; + s_header_field_start); CALLBACK_DATA(url); break; default: - parser->state = parse_url_char((enum state)parser->state, ch); - if (parser->state == s_dead) { + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { SET_ERRNO(HPE_INVALID_URL); goto error; } @@ -1105,7 +1144,7 @@ size_t http_parser_execute (http_parser *parser, case s_req_http_start: switch (ch) { case 'H': - parser->state = s_req_http_H; + UPDATE_STATE(s_req_http_H); break; case ' ': break; @@ -1117,44 +1156,44 @@ size_t http_parser_execute (http_parser *parser, case s_req_http_H: STRICT_CHECK(ch != 'T'); - parser->state = s_req_http_HT; + UPDATE_STATE(s_req_http_HT); break; case s_req_http_HT: STRICT_CHECK(ch != 'T'); - parser->state = s_req_http_HTT; + UPDATE_STATE(s_req_http_HTT); break; case s_req_http_HTT: STRICT_CHECK(ch != 'P'); - parser->state = s_req_http_HTTP; + UPDATE_STATE(s_req_http_HTTP); break; case s_req_http_HTTP: STRICT_CHECK(ch != '/'); - parser->state = s_req_first_http_major; + UPDATE_STATE(s_req_first_http_major); break; /* first digit of major HTTP version */ case s_req_first_http_major: - if (ch < '1' || ch > '9') { + if (UNLIKELY(ch < '1' || ch > '9')) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_major = ch - '0'; - parser->state = s_req_http_major; + UPDATE_STATE(s_req_http_major); break; /* major HTTP version or dot */ case s_req_http_major: { if (ch == '.') { - parser->state = s_req_first_http_minor; + UPDATE_STATE(s_req_first_http_minor); break; } - if (!IS_NUM(ch)) { + if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } @@ -1162,7 +1201,7 @@ size_t http_parser_execute (http_parser *parser, parser->http_major *= 10; parser->http_major += ch - '0'; - if (parser->http_major > 999) { + if (UNLIKELY(parser->http_major > 999)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } @@ -1172,31 +1211,31 @@ size_t http_parser_execute (http_parser *parser, /* first digit of minor HTTP version */ case s_req_first_http_minor: - if (!IS_NUM(ch)) { + if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor = ch - '0'; - parser->state = s_req_http_minor; + UPDATE_STATE(s_req_http_minor); break; /* minor HTTP version or end of request line */ case s_req_http_minor: { if (ch == CR) { - parser->state = s_req_line_almost_done; + UPDATE_STATE(s_req_line_almost_done); break; } if (ch == LF) { - parser->state = s_header_field_start; + UPDATE_STATE(s_header_field_start); break; } /* XXX allow spaces after digit? */ - if (!IS_NUM(ch)) { + if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } @@ -1204,7 +1243,7 @@ size_t http_parser_execute (http_parser *parser, parser->http_minor *= 10; parser->http_minor += ch - '0'; - if (parser->http_minor > 999) { + if (UNLIKELY(parser->http_minor > 999)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } @@ -1215,32 +1254,32 @@ size_t http_parser_execute (http_parser *parser, /* end of request line */ case s_req_line_almost_done: { - if (ch != LF) { + if (UNLIKELY(ch != LF)) { SET_ERRNO(HPE_LF_EXPECTED); goto error; } - parser->state = s_header_field_start; + UPDATE_STATE(s_header_field_start); break; } case s_header_field_start: { if (ch == CR) { - parser->state = s_headers_almost_done; + UPDATE_STATE(s_headers_almost_done); break; } if (ch == LF) { /* they might be just sending \n instead of \r\n so this would be * the second \n to denote the end of headers*/ - parser->state = s_headers_almost_done; - goto reexecute_byte; + UPDATE_STATE(s_headers_almost_done); + REEXECUTE(); } c = TOKEN(ch); - if (!c) { + if (UNLIKELY(!c)) { SET_ERRNO(HPE_INVALID_HEADER_TOKEN); goto error; } @@ -1248,7 +1287,7 @@ size_t http_parser_execute (http_parser *parser, MARK(header_field); parser->index = 0; - parser->state = s_header_field; + UPDATE_STATE(s_header_field); switch (c) { case 'c': @@ -1276,9 +1315,14 @@ size_t http_parser_execute (http_parser *parser, case s_header_field: { - c = TOKEN(ch); + const char* start = p; + for (; p != data + len; p++) { + ch = *p; + c = TOKEN(ch); + + if (!c) + break; - if (c) { switch (parser->header_state) { case h_general: break; @@ -1379,23 +1423,17 @@ size_t http_parser_execute (http_parser *parser, assert(0 && "Unknown header_state"); break; } - break; } - if (ch == ':') { - parser->state = s_header_value_discard_ws; - CALLBACK_DATA(header_field); - break; - } + COUNT_HEADER_SIZE(p - start); - if (ch == CR) { - parser->state = s_header_almost_done; - CALLBACK_DATA(header_field); + if (p == data + len) { + --p; break; } - if (ch == LF) { - parser->state = s_header_field_start; + if (ch == ':') { + UPDATE_STATE(s_header_value_discard_ws); CALLBACK_DATA(header_field); break; } @@ -1408,12 +1446,12 @@ size_t http_parser_execute (http_parser *parser, if (ch == ' ' || ch == '\t') break; if (ch == CR) { - parser->state = s_header_value_discard_ws_almost_done; + UPDATE_STATE(s_header_value_discard_ws_almost_done); break; } if (ch == LF) { - parser->state = s_header_value_discard_lws; + UPDATE_STATE(s_header_value_discard_lws); break; } @@ -1423,7 +1461,7 @@ size_t http_parser_execute (http_parser *parser, { MARK(header_value); - parser->state = s_header_value; + UPDATE_STATE(s_header_value); parser->index = 0; c = LOWER(ch); @@ -1444,7 +1482,7 @@ size_t http_parser_execute (http_parser *parser, break; case h_content_length: - if (!IS_NUM(ch)) { + if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); goto error; } @@ -1459,11 +1497,17 @@ size_t http_parser_execute (http_parser *parser, /* looking for 'Connection: close' */ } else if (c == 'c') { parser->header_state = h_matching_connection_close; + } else if (c == 'u') { + parser->header_state = h_matching_connection_upgrade; } else { - parser->header_state = h_general; + parser->header_state = h_matching_connection_token; } break; + /* Multi-value `Connection` header */ + case h_matching_connection_token_start: + break; + default: parser->header_state = h_general; break; @@ -1473,98 +1517,185 @@ size_t http_parser_execute (http_parser *parser, case s_header_value: { + const char* start = p; + enum header_states h_state = (enum header_states) parser->header_state; + for (; p != data + len; p++) { + ch = *p; + if (ch == CR) { + UPDATE_STATE(s_header_almost_done); + parser->header_state = h_state; + CALLBACK_DATA(header_value); + break; + } - if (ch == CR) { - parser->state = s_header_almost_done; - CALLBACK_DATA(header_value); - break; - } + if (ch == LF) { + UPDATE_STATE(s_header_almost_done); + COUNT_HEADER_SIZE(p - start); + parser->header_state = h_state; + CALLBACK_DATA_NOADVANCE(header_value); + REEXECUTE(); + } - if (ch == LF) { - parser->state = s_header_almost_done; - CALLBACK_DATA_NOADVANCE(header_value); - goto reexecute_byte; - } + c = LOWER(ch); - c = LOWER(ch); + switch (h_state) { + case h_general: + { + const char* p_cr; + const char* p_lf; + size_t limit = data + len - p; + + limit = MIN(limit, HTTP_MAX_HEADER_SIZE); + + p_cr = (const char*) memchr(p, CR, limit); + p_lf = (const char*) memchr(p, LF, limit); + if (p_cr != NULL) { + if (p_lf != NULL && p_cr >= p_lf) + p = p_lf; + else + p = p_cr; + } else if (UNLIKELY(p_lf != NULL)) { + p = p_lf; + } else { + p = data + len; + } + --p; - switch (parser->header_state) { - case h_general: - break; + break; + } - case h_connection: - case h_transfer_encoding: - assert(0 && "Shouldn't get here."); - break; + case h_connection: + case h_transfer_encoding: + assert(0 && "Shouldn't get here."); + break; - case h_content_length: - { - uint64_t t; + case h_content_length: + { + uint64_t t; - if (ch == ' ') break; + if (ch == ' ') break; - if (!IS_NUM(ch)) { - SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); - goto error; - } + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + } - t = parser->content_length; - t *= 10; - t += ch - '0'; + t = parser->content_length; + t *= 10; + t += ch - '0'; - /* Overflow? Test against a conservative limit for simplicity. */ - if ((ULLONG_MAX - 10) / 10 < parser->content_length) { - SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); - goto error; + /* Overflow? Test against a conservative limit for simplicity. */ + if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + } + + parser->content_length = t; + break; } - parser->content_length = t; - break; - } + /* Transfer-Encoding: chunked */ + case h_matching_transfer_encoding_chunked: + parser->index++; + if (parser->index > sizeof(CHUNKED)-1 + || c != CHUNKED[parser->index]) { + h_state = h_general; + } else if (parser->index == sizeof(CHUNKED)-2) { + h_state = h_transfer_encoding_chunked; + } + break; - /* Transfer-Encoding: chunked */ - case h_matching_transfer_encoding_chunked: - parser->index++; - if (parser->index > sizeof(CHUNKED)-1 - || c != CHUNKED[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(CHUNKED)-2) { - parser->header_state = h_transfer_encoding_chunked; - } - break; + case h_matching_connection_token_start: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') { + h_state = h_matching_connection_keep_alive; + /* looking for 'Connection: close' */ + } else if (c == 'c') { + h_state = h_matching_connection_close; + } else if (c == 'u') { + h_state = h_matching_connection_upgrade; + } else if (STRICT_TOKEN(c)) { + h_state = h_matching_connection_token; + } else { + h_state = h_general; + } + break; - /* looking for 'Connection: keep-alive' */ - case h_matching_connection_keep_alive: - parser->index++; - if (parser->index > sizeof(KEEP_ALIVE)-1 - || c != KEEP_ALIVE[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(KEEP_ALIVE)-2) { - parser->header_state = h_connection_keep_alive; - } - break; + /* looking for 'Connection: keep-alive' */ + case h_matching_connection_keep_alive: + parser->index++; + if (parser->index > sizeof(KEEP_ALIVE)-1 + || c != KEEP_ALIVE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(KEEP_ALIVE)-2) { + h_state = h_connection_keep_alive; + } + break; - /* looking for 'Connection: close' */ - case h_matching_connection_close: - parser->index++; - if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(CLOSE)-2) { - parser->header_state = h_connection_close; - } - break; + /* looking for 'Connection: close' */ + case h_matching_connection_close: + parser->index++; + if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(CLOSE)-2) { + h_state = h_connection_close; + } + break; - case h_transfer_encoding_chunked: - case h_connection_keep_alive: - case h_connection_close: - if (ch != ' ') parser->header_state = h_general; - break; + /* looking for 'Connection: upgrade' */ + case h_matching_connection_upgrade: + parser->index++; + if (parser->index > sizeof(UPGRADE) - 1 || + c != UPGRADE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(UPGRADE)-2) { + h_state = h_connection_upgrade; + } + break; - default: - parser->state = s_header_value; - parser->header_state = h_general; - break; + case h_matching_connection_token: + if (ch == ',') { + h_state = h_matching_connection_token_start; + parser->index = 0; + } + break; + + case h_transfer_encoding_chunked: + if (ch != ' ') h_state = h_general; + break; + + case h_connection_keep_alive: + case h_connection_close: + case h_connection_upgrade: + if (ch == ',') { + if (h_state == h_connection_keep_alive) { + parser->flags |= F_CONNECTION_KEEP_ALIVE; + } else if (h_state == h_connection_close) { + parser->flags |= F_CONNECTION_CLOSE; + } else if (h_state == h_connection_upgrade) { + parser->flags |= F_CONNECTION_UPGRADE; + } + h_state = h_matching_connection_token_start; + parser->index = 0; + } else if (ch != ' ') { + h_state = h_matching_connection_token; + } + break; + + default: + UPDATE_STATE(s_header_value); + h_state = h_general; + break; + } } + parser->header_state = h_state; + + COUNT_HEADER_SIZE(p - start); + + if (p == data + len) + --p; break; } @@ -1572,15 +1703,15 @@ size_t http_parser_execute (http_parser *parser, { STRICT_CHECK(ch != LF); - parser->state = s_header_value_lws; + UPDATE_STATE(s_header_value_lws); break; } case s_header_value_lws: { if (ch == ' ' || ch == '\t') { - parser->state = s_header_value_start; - goto reexecute_byte; + UPDATE_STATE(s_header_value_start); + REEXECUTE(); } /* finished the header */ @@ -1594,32 +1725,52 @@ size_t http_parser_execute (http_parser *parser, case h_transfer_encoding_chunked: parser->flags |= F_CHUNKED; break; + case h_connection_upgrade: + parser->flags |= F_CONNECTION_UPGRADE; + break; default: break; } - parser->state = s_header_field_start; - goto reexecute_byte; + UPDATE_STATE(s_header_field_start); + REEXECUTE(); } case s_header_value_discard_ws_almost_done: { STRICT_CHECK(ch != LF); - parser->state = s_header_value_discard_lws; + UPDATE_STATE(s_header_value_discard_lws); break; } case s_header_value_discard_lws: { if (ch == ' ' || ch == '\t') { - parser->state = s_header_value_discard_ws; + UPDATE_STATE(s_header_value_discard_ws); break; } else { + switch (parser->header_state) { + case h_connection_keep_alive: + parser->flags |= F_CONNECTION_KEEP_ALIVE; + break; + case h_connection_close: + parser->flags |= F_CONNECTION_CLOSE; + break; + case h_connection_upgrade: + parser->flags |= F_CONNECTION_UPGRADE; + break; + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; + default: + break; + } + /* header value was empty */ MARK(header_value); - parser->state = s_header_field_start; + UPDATE_STATE(s_header_field_start); CALLBACK_DATA_NOADVANCE(header_value); - goto reexecute_byte; + REEXECUTE(); } } @@ -1629,16 +1780,18 @@ size_t http_parser_execute (http_parser *parser, if (parser->flags & F_TRAILING) { /* End of a chunked request */ - parser->state = NEW_MESSAGE(); + UPDATE_STATE(NEW_MESSAGE()); CALLBACK_NOTIFY(message_complete); break; } - parser->state = s_headers_done; + UPDATE_STATE(s_headers_done); /* Set this here so that on_headers_complete() callbacks can see it */ parser->upgrade = - (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT); + ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) == + (F_UPGRADE | F_CONNECTION_UPGRADE) || + parser->method == HTTP_CONNECT); /* Here we call the headers_complete callback. This is somewhat * different than other callbacks because if the user returns 1, we @@ -1660,15 +1813,15 @@ size_t http_parser_execute (http_parser *parser, default: SET_ERRNO(HPE_CB_headers_complete); - return p - data; /* Error */ + RETURN(p - data); /* Error */ } } if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { - return p - data; + RETURN(p - data); } - goto reexecute_byte; + REEXECUTE(); } case s_headers_done: @@ -1679,34 +1832,34 @@ size_t http_parser_execute (http_parser *parser, /* Exit, the rest of the connect is in a different protocol. */ if (parser->upgrade) { - parser->state = NEW_MESSAGE(); + UPDATE_STATE(NEW_MESSAGE()); CALLBACK_NOTIFY(message_complete); - return (p - data) + 1; + RETURN((p - data) + 1); } if (parser->flags & F_SKIPBODY) { - parser->state = NEW_MESSAGE(); + UPDATE_STATE(NEW_MESSAGE()); CALLBACK_NOTIFY(message_complete); } else if (parser->flags & F_CHUNKED) { /* chunked encoding - ignore Content-Length header */ - parser->state = s_chunk_size_start; + UPDATE_STATE(s_chunk_size_start); } else { if (parser->content_length == 0) { /* Content-Length header given but zero: Content-Length: 0\r\n */ - parser->state = NEW_MESSAGE(); + UPDATE_STATE(NEW_MESSAGE()); CALLBACK_NOTIFY(message_complete); } else if (parser->content_length != ULLONG_MAX) { /* Content-Length header given and non-zero */ - parser->state = s_body_identity; + UPDATE_STATE(s_body_identity); } else { if (parser->type == HTTP_REQUEST || !http_message_needs_eof(parser)) { /* Assume content-length 0 - read the next */ - parser->state = NEW_MESSAGE(); + UPDATE_STATE(NEW_MESSAGE()); CALLBACK_NOTIFY(message_complete); } else { /* Read body until EOF */ - parser->state = s_body_identity_eof; + UPDATE_STATE(s_body_identity_eof); } } } @@ -1732,7 +1885,7 @@ size_t http_parser_execute (http_parser *parser, p += to_read - 1; if (parser->content_length == 0) { - parser->state = s_message_done; + UPDATE_STATE(s_message_done); /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. * @@ -1744,7 +1897,7 @@ size_t http_parser_execute (http_parser *parser, * important for applications, but let's keep it for now. */ CALLBACK_DATA_(body, p - body_mark + 1, p - data); - goto reexecute_byte; + REEXECUTE(); } break; @@ -1758,7 +1911,7 @@ size_t http_parser_execute (http_parser *parser, break; case s_message_done: - parser->state = NEW_MESSAGE(); + UPDATE_STATE(NEW_MESSAGE()); CALLBACK_NOTIFY(message_complete); break; @@ -1768,13 +1921,13 @@ size_t http_parser_execute (http_parser *parser, assert(parser->flags & F_CHUNKED); unhex_val = unhex[(unsigned char)ch]; - if (unhex_val == -1) { + if (UNLIKELY(unhex_val == -1)) { SET_ERRNO(HPE_INVALID_CHUNK_SIZE); goto error; } parser->content_length = unhex_val; - parser->state = s_chunk_size; + UPDATE_STATE(s_chunk_size); break; } @@ -1785,7 +1938,7 @@ size_t http_parser_execute (http_parser *parser, assert(parser->flags & F_CHUNKED); if (ch == CR) { - parser->state = s_chunk_size_almost_done; + UPDATE_STATE(s_chunk_size_almost_done); break; } @@ -1793,7 +1946,7 @@ size_t http_parser_execute (http_parser *parser, if (unhex_val == -1) { if (ch == ';' || ch == ' ') { - parser->state = s_chunk_parameters; + UPDATE_STATE(s_chunk_parameters); break; } @@ -1806,7 +1959,7 @@ size_t http_parser_execute (http_parser *parser, t += unhex_val; /* Overflow? Test against a conservative limit for simplicity. */ - if ((ULLONG_MAX - 16) / 16 < parser->content_length) { + if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); goto error; } @@ -1820,7 +1973,7 @@ size_t http_parser_execute (http_parser *parser, assert(parser->flags & F_CHUNKED); /* just ignore this shit. TODO check for overflow */ if (ch == CR) { - parser->state = s_chunk_size_almost_done; + UPDATE_STATE(s_chunk_size_almost_done); break; } break; @@ -1835,9 +1988,9 @@ size_t http_parser_execute (http_parser *parser, if (parser->content_length == 0) { parser->flags |= F_TRAILING; - parser->state = s_header_field_start; + UPDATE_STATE(s_header_field_start); } else { - parser->state = s_chunk_data; + UPDATE_STATE(s_chunk_data); } break; } @@ -1859,7 +2012,7 @@ size_t http_parser_execute (http_parser *parser, p += to_read - 1; if (parser->content_length == 0) { - parser->state = s_chunk_data_almost_done; + UPDATE_STATE(s_chunk_data_almost_done); } break; @@ -1869,7 +2022,7 @@ size_t http_parser_execute (http_parser *parser, assert(parser->flags & F_CHUNKED); assert(parser->content_length == 0); STRICT_CHECK(ch != CR); - parser->state = s_chunk_data_done; + UPDATE_STATE(s_chunk_data_done); CALLBACK_DATA(body); break; @@ -1877,7 +2030,7 @@ size_t http_parser_execute (http_parser *parser, assert(parser->flags & F_CHUNKED); STRICT_CHECK(ch != LF); parser->nread = 0; - parser->state = s_chunk_size_start; + UPDATE_STATE(s_chunk_size_start); break; default: @@ -1909,14 +2062,14 @@ size_t http_parser_execute (http_parser *parser, CALLBACK_DATA_NOADVANCE(body); CALLBACK_DATA_NOADVANCE(status); - return len; + RETURN(len); error: if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { SET_ERRNO(HPE_UNKNOWN); } - return (p - data); + RETURN(p - data); } @@ -2142,7 +2295,7 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect, u->port = u->field_set = 0; s = is_connect ? s_req_server_start : s_req_spaces_before_url; - uf = old_uf = UF_MAX; + old_uf = UF_MAX; for (p = buf; p < buf + buflen; p++) { s = parse_url_char(s, *p); diff --git a/deps/http_parser/http_parser.h b/deps/http_parser/http_parser.h index ec61a1287f37fc..640ffa0ac32e60 100644 --- a/deps/http_parser/http_parser.h +++ b/deps/http_parser/http_parser.h @@ -26,8 +26,8 @@ extern "C" { /* Also update SONAME in the Makefile whenever you change these. */ #define HTTP_PARSER_VERSION_MAJOR 2 -#define HTTP_PARSER_VERSION_MINOR 3 -#define HTTP_PARSER_VERSION_PATCH 0 +#define HTTP_PARSER_VERSION_MINOR 4 +#define HTTP_PARSER_VERSION_PATCH 1 #include #if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) @@ -52,9 +52,16 @@ typedef unsigned __int64 uint64_t; # define HTTP_PARSER_STRICT 1 #endif -/* Maximium header size allowed */ -#define HTTP_MAX_HEADER_SIZE (80*1024) - +/* Maximium header size allowed. If the macro is not defined + * before including this header then the default is used. To + * change the maximum header size, define the macro in the build + * environment (e.g. -DHTTP_MAX_HEADER_SIZE=). To remove + * the effective limit on the size of the header, define the macro + * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff) + */ +#ifndef HTTP_MAX_HEADER_SIZE +# define HTTP_MAX_HEADER_SIZE (80*1024) +#endif typedef struct http_parser http_parser; typedef struct http_parser_settings http_parser_settings; @@ -69,7 +76,7 @@ typedef struct http_parser_settings http_parser_settings; * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: * chunked' headers that indicate the presence of a body. * - * http_data_cb does not return data chunks. It will be call arbitrarally + * http_data_cb does not return data chunks. It will be called arbitrarily * many times for each string. E.G. you might get 10 callbacks for "on_url" * each providing just a few characters more data. */ @@ -110,6 +117,8 @@ typedef int (*http_cb) (http_parser*); /* RFC-5789 */ \ XX(24, PATCH, PATCH) \ XX(25, PURGE, PURGE) \ + /* CalDAV */ \ + XX(26, MKCALENDAR, MKCALENDAR) \ enum http_method { @@ -127,9 +136,10 @@ enum flags { F_CHUNKED = 1 << 0 , F_CONNECTION_KEEP_ALIVE = 1 << 1 , F_CONNECTION_CLOSE = 1 << 2 - , F_TRAILING = 1 << 3 - , F_UPGRADE = 1 << 4 - , F_SKIPBODY = 1 << 5 + , F_CONNECTION_UPGRADE = 1 << 3 + , F_TRAILING = 1 << 4 + , F_UPGRADE = 1 << 5 + , F_SKIPBODY = 1 << 6 }; @@ -271,13 +281,15 @@ struct http_parser_url { * unsigned major = (version >> 16) & 255; * unsigned minor = (version >> 8) & 255; * unsigned patch = version & 255; - * printf("http_parser v%u.%u.%u\n", major, minor, version); + * printf("http_parser v%u.%u.%u\n", major, minor, patch); */ unsigned long http_parser_version(void); void http_parser_init(http_parser *parser, enum http_parser_type type); +/* Executes the parser. Returns number of parsed bytes. Sets + * `parser->http_errno` on error. */ size_t http_parser_execute(http_parser *parser, const http_parser_settings *settings, const char *data, diff --git a/deps/http_parser/test.c b/deps/http_parser/test.c index 9799dc6d346dd7..6c45d59d0c494d 100644 --- a/deps/http_parser/test.c +++ b/deps/http_parser/test.c @@ -950,6 +950,42 @@ const struct message requests[] = ,.body= "" } +#define CONNECTION_MULTI 35 +, {.name = "multiple connection header values with folding" + ,.type= HTTP_REQUEST + ,.raw= "GET /demo HTTP/1.1\r\n" + "Host: example.com\r\n" + "Connection: Something,\r\n" + " Upgrade, ,Keep-Alive\r\n" + "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" + "Sec-WebSocket-Protocol: sample\r\n" + "Upgrade: WebSocket\r\n" + "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" + "Origin: http://example.com\r\n" + "\r\n" + "Hot diggity dogg" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/demo" + ,.request_url= "/demo" + ,.num_headers= 7 + ,.upgrade="Hot diggity dogg" + ,.headers= { { "Host", "example.com" } + , { "Connection", "Something, Upgrade, ,Keep-Alive" } + , { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" } + , { "Sec-WebSocket-Protocol", "sample" } + , { "Upgrade", "WebSocket" } + , { "Sec-WebSocket-Key1", "4 @1 46546xW%0l 1 5" } + , { "Origin", "http://example.com" } + } + ,.body= "" + } + , {.name= NULL } /* sentinel */ }; @@ -2207,7 +2243,6 @@ print_error (const char *raw, size_t error_location) break; case '\n': - char_len = 2; fprintf(stderr, "\\n\n"); if (this_line) goto print; @@ -2910,15 +2945,11 @@ test_simple (const char *buf, enum http_errno err_expected) { parser_init(HTTP_REQUEST); - size_t parsed; - int pass; enum http_errno err; - parsed = parse(buf, strlen(buf)); - pass = (parsed == strlen(buf)); + parse(buf, strlen(buf)); err = HTTP_PARSER_ERRNO(parser); - parsed = parse(NULL, 0); - pass &= (parsed == 0); + parse(NULL, 0); parser_free(); @@ -3476,6 +3507,13 @@ main (void) test_simple(buf, HPE_INVALID_METHOD); } + // illegal header field name line folding + test_simple("GET / HTTP/1.1\r\n" + "name\r\n" + " : value\r\n" + "\r\n", + HPE_INVALID_HEADER_TOKEN); + const char *dumbfuck2 = "GET / HTTP/1.1\r\n" "X-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n" diff --git a/deps/npm/node_modules/node-gyp/addon.gypi b/deps/npm/node_modules/node-gyp/addon.gypi index 0b81fab2027ff7..63fefe3d16c80b 100644 --- a/deps/npm/node_modules/node-gyp/addon.gypi +++ b/deps/npm/node_modules/node-gyp/addon.gypi @@ -42,7 +42,7 @@ '-luuid.lib', '-lodbc32.lib', '-lDelayImp.lib', - '-l"<(node_root_dir)/$(ConfigurationName)/node.lib"' + '-l"<(node_root_dir)/$(ConfigurationName)/iojs.lib"' ], # warning C4251: 'node::ObjectWrap::handle_' : class 'v8::Persistent' # needs to have dll-interface to be used by clients of class 'node::ObjectWrap' diff --git a/deps/npm/node_modules/node-gyp/lib/build.js b/deps/npm/node_modules/node-gyp/lib/build.js index f3605902e93c10..3d3c58785f1795 100644 --- a/deps/npm/node_modules/node-gyp/lib/build.js +++ b/deps/npm/node_modules/node-gyp/lib/build.js @@ -173,7 +173,7 @@ function build (gyp, argv, callback) { } /** - * Copies the node.lib file for the current target architecture into the + * Copies the iojs.lib file for the current target architecture into the * current proper dev dir location. */ @@ -181,15 +181,15 @@ function build (gyp, argv, callback) { if (!win || !copyDevLib) return doBuild() var buildDir = path.resolve(nodeDir, buildType) - , archNodeLibPath = path.resolve(nodeDir, arch, 'node.lib') - , buildNodeLibPath = path.resolve(buildDir, 'node.lib') + , archNodeLibPath = path.resolve(nodeDir, arch, 'iojs.lib') + , buildNodeLibPath = path.resolve(buildDir, 'iojs.lib') mkdirp(buildDir, function (err, isNew) { if (err) return callback(err) log.verbose('"' + buildType + '" dir needed to be created?', isNew) var rs = fs.createReadStream(archNodeLibPath) , ws = fs.createWriteStream(buildNodeLibPath) - log.verbose('copying "node.lib" for ' + arch, buildNodeLibPath) + log.verbose('copying "iojs.lib" for ' + arch, buildNodeLibPath) rs.pipe(ws) rs.on('error', callback) ws.on('error', callback) diff --git a/deps/npm/node_modules/node-gyp/lib/install.js b/deps/npm/node_modules/node-gyp/lib/install.js index ebc4e571a4e187..378fbfae4769b5 100644 --- a/deps/npm/node_modules/node-gyp/lib/install.js +++ b/deps/npm/node_modules/node-gyp/lib/install.js @@ -267,7 +267,7 @@ function install (gyp, argv, callback) { var async = 0 if (win) { - // need to download node.lib + // need to download iojs.lib async++ downloadNodeLib(deref) } @@ -295,7 +295,8 @@ function install (gyp, argv, callback) { // check content shasums for (var k in contentShasums) { log.verbose('validating download checksum for ' + k, '(%s == %s)', contentShasums[k], expectShasums[k]) - if (contentShasums[k] !== expectShasums[k]) { + // TODO(piscisaureus) re-enable checksum verification when the correct files are in place. + if (false || contentShasums[k] !== expectShasums[k]) { cb(new Error(k + ' local checksum ' + contentShasums[k] + ' not match remote ' + expectShasums[k])) return } @@ -343,36 +344,36 @@ function install (gyp, argv, callback) { } function downloadNodeLib (done) { - log.verbose('on Windows; need to download `node.lib`...') + log.verbose('on Windows; need to download `iojs.lib`...') var dir32 = path.resolve(devDir, 'ia32') , dir64 = path.resolve(devDir, 'x64') - , nodeLibPath32 = path.resolve(dir32, 'node.lib') - , nodeLibPath64 = path.resolve(dir64, 'node.lib') - , nodeLibUrl32 = distUrl + '/v' + version + '/node.lib' - , nodeLibUrl64 = distUrl + '/v' + version + '/x64/node.lib' + , nodeLibPath32 = path.resolve(dir32, 'iojs.lib') + , nodeLibPath64 = path.resolve(dir64, 'iojs.lib') + , nodeLibUrl32 = distUrl + '/v' + version + '/win-x86/iojs.lib' + , nodeLibUrl64 = distUrl + '/v' + version + '/win-x64/iojs.lib' - log.verbose('32-bit node.lib dir', dir32) - log.verbose('64-bit node.lib dir', dir64) - log.verbose('`node.lib` 32-bit url', nodeLibUrl32) - log.verbose('`node.lib` 64-bit url', nodeLibUrl64) + log.verbose('32-bit iojs.lib dir', dir32) + log.verbose('64-bit iojs.lib dir', dir64) + log.verbose('`iojs.lib` 32-bit url', nodeLibUrl32) + log.verbose('`iojs.lib` 64-bit url', nodeLibUrl64) var async = 2 mkdir(dir32, function (err) { if (err) return done(err) - log.verbose('streaming 32-bit node.lib to:', nodeLibPath32) + log.verbose('streaming 32-bit iojs.lib to:', nodeLibPath32) var req = download(nodeLibUrl32) if (!req) return req.on('error', done) req.on('response', function (res) { if (res.statusCode !== 200) { - done(new Error(res.statusCode + ' status code downloading 32-bit node.lib')) + done(new Error(res.statusCode + ' status code downloading 32-bit iojs.lib')) return } getContentSha(res, function (_, checksum) { - contentShasums['node.lib'] = checksum - log.verbose('content checksum', 'node.lib', checksum) + contentShasums['win-x86/iojs.lib'] = checksum + log.verbose('content checksum', 'win-x86/iojs.lib', checksum) }) var ws = fs.createWriteStream(nodeLibPath32) @@ -385,20 +386,20 @@ function install (gyp, argv, callback) { }) mkdir(dir64, function (err) { if (err) return done(err) - log.verbose('streaming 64-bit node.lib to:', nodeLibPath64) + log.verbose('streaming 64-bit iojs.lib to:', nodeLibPath64) var req = download(nodeLibUrl64) if (!req) return req.on('error', done) req.on('response', function (res) { if (res.statusCode !== 200) { - done(new Error(res.statusCode + ' status code downloading 64-bit node.lib')) + done(new Error(res.statusCode + ' status code downloading 64-bit iojs.lib')) return } getContentSha(res, function (_, checksum) { - contentShasums['x64/node.lib'] = checksum - log.verbose('content checksum', 'x64/node.lib', checksum) + contentShasums['win-x64/iojs.lib'] = checksum + log.verbose('content checksum', 'win-x64/iojs.lib', checksum) }) var ws = fs.createWriteStream(nodeLibPath64) diff --git a/deps/uv/AUTHORS b/deps/uv/AUTHORS index 7e455f6736ab98..aa860db57bc83d 100644 --- a/deps/uv/AUTHORS +++ b/deps/uv/AUTHORS @@ -177,3 +177,4 @@ Logan Rosen Kenneth Perry John Marino Alexey Melnichuk +Johan Bergström diff --git a/deps/uv/ChangeLog b/deps/uv/ChangeLog index 0f3fce81ebe184..a9d0829c917129 100644 --- a/deps/uv/ChangeLog +++ b/deps/uv/ChangeLog @@ -1,4 +1,32 @@ -2015.01.06, Version 1.2.0 (Stable) +2015.01.15, Version 1.2.1 (Stable), 4ca78e989062a1099dc4b9ad182a98e8374134b1 + +Changes since version 1.2.0: + +* unix: remove unused dtrace file (Saúl Ibarra Corretgé) + +* test: skip TTY select test if /dev/tty can't be opened (Saúl Ibarra Corretgé) + +* doc: clarify the behavior of uv_tty_init (Saúl Ibarra Corretgé) + +* doc: clarify how uv_async_send behaves (Saúl Ibarra Corretgé) + +* build: make dist now generates a full tarball (Johan Bergström) + +* freebsd: make uv_exepath more resilient (Saúl Ibarra Corretgé) + +* unix: make setting the tty mode to the same value a no-op (Saúl Ibarra + Corretgé) + +* win,tcp: support uv_try_write (Bert Belder) + +* test: enable test-tcp-try-write on windows (Bert Belder) + +* win,tty: support uv_try_write (Bert Belder) + +* unix: set non-block mode in uv_{pipe,tcp,udp}_open (Ben Noordhuis) + + +2015.01.06, Version 1.2.0 (Stable), 09f25b13cd149c7981108fc1a75611daf1277f83 Changes since version 1.1.0: diff --git a/deps/uv/Makefile.am b/deps/uv/Makefile.am index c5b8a1fa870092..e6af91d0726db8 100644 --- a/deps/uv/Makefile.am +++ b/deps/uv/Makefile.am @@ -108,7 +108,24 @@ libuv_la_SOURCES += src/unix/async.c \ endif # WINNT EXTRA_DIST = test/fixtures/empty_file \ - test/fixtures/load_error.node + test/fixtures/load_error.node \ + include \ + test \ + docs \ + img \ + samples \ + android-configure \ + CONTRIBUTING.md \ + LICENSE \ + README.md \ + checksparse.sh \ + vcbuild.bat \ + Makefile.mingw \ + common.gypi \ + gyp_uv.py \ + uv.gyp + + TESTS = test/run-tests check_PROGRAMS = test/run-tests diff --git a/deps/uv/configure.ac b/deps/uv/configure.ac index 56e97abf37880b..d765894e115aca 100644 --- a/deps/uv/configure.ac +++ b/deps/uv/configure.ac @@ -13,7 +13,7 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_PREREQ(2.57) -AC_INIT([libuv], [1.2.0], [https://github.com/libuv/libuv/issues]) +AC_INIT([libuv], [1.2.1], [https://github.com/libuv/libuv/issues]) AC_CONFIG_MACRO_DIR([m4]) m4_include([m4/libuv-extra-automake-flags.m4]) m4_include([m4/as_case.m4]) diff --git a/deps/uv/docs/src/async.rst b/deps/uv/docs/src/async.rst index 7afc92a71bcceb..5c400458244a86 100644 --- a/deps/uv/docs/src/async.rst +++ b/deps/uv/docs/src/async.rst @@ -48,9 +48,10 @@ API .. warning:: libuv will coalesce calls to :c:func:`uv_async_send`, that is, not every call to it will - yield an execution of the callback, the only guarantee is that it will be called at least - once. Thus, calling this function may not wakeup the event loop if it was already called - previously within a short period of time. + yield an execution of the callback. For example: if :c:func:`uv_async_send` is called 5 + times in a row before the callback is called, the callback will only be called once. If + :c:func:`uv_async_send` is called again after the callback was called, it will be called + again. .. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/deps/uv/docs/src/pipe.rst b/deps/uv/docs/src/pipe.rst index 614bb2e3b1ff03..9fbb1f6c20919a 100644 --- a/deps/uv/docs/src/pipe.rst +++ b/deps/uv/docs/src/pipe.rst @@ -38,8 +38,7 @@ API Open an existing file descriptor or HANDLE as a pipe. - .. note:: - The user is responsible for setting the file descriptor in non-blocking mode. + .. versionchanged:: 1.2.1 the file descriptor is set to non-blocking mode. .. c:function:: int uv_pipe_bind(uv_pipe_t* handle, const char* name) diff --git a/deps/uv/docs/src/tcp.rst b/deps/uv/docs/src/tcp.rst index 2c1001b531fde2..8baedde86c5c15 100644 --- a/deps/uv/docs/src/tcp.rst +++ b/deps/uv/docs/src/tcp.rst @@ -36,9 +36,7 @@ API Open an existing file descriptor or SOCKET as a TCP handle. - .. note:: - The user is responsible for setting the file descriptor in - non-blocking mode. + .. versionchanged:: 1.2.1 the file descriptor is set to non-blocking mode. .. c:function:: int uv_tcp_nodelay(uv_tcp_t* handle, int enable) diff --git a/deps/uv/docs/src/tty.rst b/deps/uv/docs/src/tty.rst index 74b485941c07ab..6c20c84bf75e58 100644 --- a/deps/uv/docs/src/tty.rst +++ b/deps/uv/docs/src/tty.rst @@ -58,8 +58,13 @@ API `readable`, specifies if you plan on calling :c:func:`uv_read_start` with this stream. stdin is readable, stdout is not. + On Unix this function will try to open ``/dev/tty`` and use it if the passed file + descriptor refers to a TTY. This lets libuv put the tty in non-blocking mode + without affecting other processes that share the tty. + .. note:: - TTY streams which are not readable have blocking writes. + If opening ``/dev/tty`` fails, libuv falls back to blocking writes for non-readable + TTY streams. .. c:function:: int uv_tty_set_mode(uv_tty_t*, uv_tty_mode_t mode) diff --git a/deps/uv/docs/src/udp.rst b/deps/uv/docs/src/udp.rst index 175ce07a2dbd19..9c4aa2102bdcfc 100644 --- a/deps/uv/docs/src/udp.rst +++ b/deps/uv/docs/src/udp.rst @@ -120,6 +120,8 @@ API In other words, other datagram-type sockets like raw sockets or netlink sockets can also be passed to this function. + .. versionchanged:: 1.2.1 the file descriptor is set to non-blocking mode. + .. c:function:: int uv_udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int flags) Bind the UDP handle to an IP address and port. diff --git a/deps/uv/include/uv-version.h b/deps/uv/include/uv-version.h index 85d74723af1727..398f0948c3468d 100644 --- a/deps/uv/include/uv-version.h +++ b/deps/uv/include/uv-version.h @@ -32,7 +32,7 @@ #define UV_VERSION_MAJOR 1 #define UV_VERSION_MINOR 2 -#define UV_VERSION_PATCH 0 +#define UV_VERSION_PATCH 1 #define UV_VERSION_IS_RELEASE 1 #define UV_VERSION_SUFFIX "" diff --git a/deps/uv/src/unix/freebsd.c b/deps/uv/src/unix/freebsd.c index 55492adc4801e6..d87b74b9bca028 100644 --- a/deps/uv/src/unix/freebsd.c +++ b/deps/uv/src/unix/freebsd.c @@ -75,8 +75,9 @@ uint64_t uv__hrtime(uv_clocktype_t type) { int uv_exepath(char* buffer, size_t* size) { + char abspath[PATH_MAX * 2 + 1]; int mib[4]; - size_t cb; + size_t abspath_size; if (buffer == NULL || size == NULL || *size == 0) return -EINVAL; @@ -93,10 +94,19 @@ int uv_exepath(char* buffer, size_t* size) { mib[3] = -1; #endif - cb = *size; - if (sysctl(mib, 4, buffer, &cb, NULL, 0)) + abspath_size = sizeof abspath;; + if (sysctl(mib, 4, abspath, &abspath_size, NULL, 0)) return -errno; - *size = strlen(buffer); + + assert(abspath_size > 0); + abspath_size -= 1; + *size -= 1; + + if (*size > abspath_size) + *size = abspath_size; + + memcpy(buffer, abspath, *size); + buffer[*size] = '\0'; return 0; } diff --git a/deps/uv/src/unix/pipe.c b/deps/uv/src/unix/pipe.c index ef47700b7a1635..ba833d3f54702c 100644 --- a/deps/uv/src/unix/pipe.c +++ b/deps/uv/src/unix/pipe.c @@ -125,9 +125,13 @@ void uv__pipe_close(uv_pipe_t* handle) { int uv_pipe_open(uv_pipe_t* handle, uv_file fd) { -#if defined(__APPLE__) int err; + err = uv__nonblock(fd, 1); + if (err) + return err; + +#if defined(__APPLE__) err = uv__stream_try_select((uv_stream_t*) handle, &fd); if (err) return err; diff --git a/deps/uv/src/unix/tcp.c b/deps/uv/src/unix/tcp.c index 8c19c1ab95606c..4060e7bd7094e8 100644 --- a/deps/uv/src/unix/tcp.c +++ b/deps/uv/src/unix/tcp.c @@ -156,6 +156,12 @@ int uv__tcp_connect(uv_connect_t* req, int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { + int err; + + err = uv__nonblock(sock, 1); + if (err) + return err; + return uv__stream_open((uv_stream_t*)handle, sock, UV_STREAM_READABLE | UV_STREAM_WRITABLE); diff --git a/deps/uv/src/unix/tty.c b/deps/uv/src/unix/tty.c index 068025eaf59cea..a1ea433f81792b 100644 --- a/deps/uv/src/unix/tty.c +++ b/deps/uv/src/unix/tty.c @@ -108,8 +108,10 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { struct termios tmp; int fd; - fd = uv__stream_fd(tty); + if (tty->mode == (int) mode) + return 0; + fd = uv__stream_fd(tty); if (tty->mode == UV_TTY_MODE_NORMAL && mode != UV_TTY_MODE_NORMAL) { if (tcgetattr(fd, &tty->orig_termios)) return -errno; diff --git a/deps/uv/src/unix/udp.c b/deps/uv/src/unix/udp.c index 2e1824c358ae91..941c0aec6e2f0b 100644 --- a/deps/uv/src/unix/udp.c +++ b/deps/uv/src/unix/udp.c @@ -565,6 +565,10 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { if (handle->io_watcher.fd != -1) return -EALREADY; /* FIXME(bnoordhuis) Should be -EBUSY. */ + err = uv__nonblock(sock, 1); + if (err) + return err; + err = uv__set_reuse(sock); if (err) return err; diff --git a/deps/uv/src/win/internal.h b/deps/uv/src/win/internal.h index 89290aea3277e0..04b2a548ca88cc 100644 --- a/deps/uv/src/win/internal.h +++ b/deps/uv/src/win/internal.h @@ -136,6 +136,8 @@ int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb); int uv_tcp_write(uv_loop_t* loop, uv_write_t* req, uv_tcp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb); +int uv__tcp_try_write(uv_tcp_t* handle, const uv_buf_t bufs[], + unsigned int nbufs); void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* req); void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, @@ -211,6 +213,8 @@ int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb, int uv_tty_read_stop(uv_tty_t* handle); int uv_tty_write(uv_loop_t* loop, uv_write_t* req, uv_tty_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb); +int uv__tty_try_write(uv_tty_t* handle, const uv_buf_t bufs[], + unsigned int nbufs); void uv_tty_close(uv_tty_t* handle); void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle, diff --git a/deps/uv/src/win/stream.c b/deps/uv/src/win/stream.c index 057f72ecad8408..36d88d00bd9893 100644 --- a/deps/uv/src/win/stream.c +++ b/deps/uv/src/win/stream.c @@ -184,8 +184,22 @@ int uv_write2(uv_write_t* req, int uv_try_write(uv_stream_t* stream, const uv_buf_t bufs[], unsigned int nbufs) { - /* NOTE: Won't work with overlapped writes */ - return UV_ENOSYS; + if (stream->flags & UV__HANDLE_CLOSING) + return UV_EBADF; + if (!(stream->flags & UV_HANDLE_WRITABLE)) + return UV_EPIPE; + + switch (stream->type) { + case UV_TCP: + return uv__tcp_try_write((uv_tcp_t*) stream, bufs, nbufs); + case UV_TTY: + return uv__tty_try_write((uv_tty_t*) stream, bufs, nbufs); + case UV_NAMED_PIPE: + return UV_EAGAIN; + default: + assert(0); + return UV_ENOSYS; + } } diff --git a/deps/uv/src/win/tcp.c b/deps/uv/src/win/tcp.c index cff2929e4cc697..c5ddbed08f75ae 100644 --- a/deps/uv/src/win/tcp.c +++ b/deps/uv/src/win/tcp.c @@ -876,6 +876,30 @@ int uv_tcp_write(uv_loop_t* loop, } +int uv__tcp_try_write(uv_tcp_t* handle, + const uv_buf_t bufs[], + unsigned int nbufs) { + int result; + DWORD bytes; + + if (handle->write_reqs_pending > 0) + return UV_EAGAIN; + + result = WSASend(handle->socket, + (WSABUF*) bufs, + nbufs, + &bytes, + 0, + NULL, + NULL); + + if (result == SOCKET_ERROR) + return uv_translate_sys_error(WSAGetLastError()); + else + return bytes; +} + + void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* req) { DWORD bytes, flags, err; diff --git a/deps/uv/src/win/tty.c b/deps/uv/src/win/tty.c index be4a8b81e40de2..603421045cac58 100644 --- a/deps/uv/src/win/tty.c +++ b/deps/uv/src/win/tty.c @@ -1878,6 +1878,21 @@ int uv_tty_write(uv_loop_t* loop, } +int uv__tty_try_write(uv_tty_t* handle, + const uv_buf_t bufs[], + unsigned int nbufs) { + DWORD error; + + if (handle->write_reqs_pending > 0) + return UV_EAGAIN; + + if (uv_tty_write_bufs(handle, bufs, nbufs, &error)) + return uv_translate_sys_error(error); + + return uv__count_bufs(bufs, nbufs); +} + + void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle, uv_write_t* req) { int err; diff --git a/deps/uv/test/test-close-fd.c b/deps/uv/test/test-close-fd.c index 0d17f07661506f..93a7bd7c021026 100644 --- a/deps/uv/test/test-close-fd.c +++ b/deps/uv/test/test-close-fd.c @@ -54,7 +54,6 @@ TEST_IMPL(close_fd) { int fd[2]; ASSERT(0 == pipe(fd)); - ASSERT(0 == fcntl(fd[0], F_SETFL, O_NONBLOCK)); ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0])); fd[0] = -1; /* uv_pipe_open() takes ownership of the file descriptor. */ diff --git a/deps/uv/test/test-osx-select.c b/deps/uv/test/test-osx-select.c index ef551eaf2fcc8d..6ccf603483488a 100644 --- a/deps/uv/test/test-osx-select.c +++ b/deps/uv/test/test-osx-select.c @@ -54,8 +54,10 @@ TEST_IMPL(osx_select) { uv_tty_t tty; fd = open("/dev/tty", O_RDONLY); - - ASSERT(fd >= 0); + if (fd < 0) { + LOGF("Cannot open /dev/tty as read-only: %s\n", strerror(errno)); + return TEST_SKIP; + } r = uv_tty_init(uv_default_loop(), &tty, fd, 1); ASSERT(r == 0); @@ -104,7 +106,10 @@ TEST_IMPL(osx_select_many_fds) { } fd = open("/dev/tty", O_RDONLY); - ASSERT(fd >= 0); + if (fd < 0) { + LOGF("Cannot open /dev/tty as read-only: %s\n", strerror(errno)); + return TEST_SKIP; + } r = uv_tty_init(uv_default_loop(), &tty, fd, 1); ASSERT(r == 0); diff --git a/deps/uv/test/test-spawn.c b/deps/uv/test/test-spawn.c index 5c25f81926b6e6..a229d225f1bb58 100644 --- a/deps/uv/test/test-spawn.c +++ b/deps/uv/test/test-spawn.c @@ -1243,7 +1243,6 @@ TEST_IMPL(closed_fd_events) { /* create a pipe and share it with a child process */ ASSERT(0 == pipe(fd)); - ASSERT(0 == fcntl(fd[0], F_SETFL, O_NONBLOCK)); /* spawn_helper4 blocks indefinitely. */ init_process_options("spawn_helper4", exit_cb); diff --git a/deps/uv/test/test-tcp-try-write.c b/deps/uv/test/test-tcp-try-write.c index baff6cf36cc009..8a60136b64e9aa 100644 --- a/deps/uv/test/test-tcp-try-write.c +++ b/deps/uv/test/test-tcp-try-write.c @@ -28,16 +28,6 @@ #define MAX_BYTES 1024 * 1024 -#ifdef _WIN32 - -TEST_IMPL(tcp_try_write) { - - MAKE_VALGRIND_HAPPY(); - return 0; -} - -#else /* !_WIN32 */ - static uv_tcp_t server; static uv_tcp_t client; static uv_tcp_t incoming; @@ -138,5 +128,3 @@ TEST_IMPL(tcp_try_write) { MAKE_VALGRIND_HAPPY(); return 0; } - -#endif /* !_WIN32 */ diff --git a/doc/api/http.markdown b/doc/api/http.markdown index 3d482b441c80d3..df76a25d66ad23 100644 --- a/doc/api/http.markdown +++ b/doc/api/http.markdown @@ -514,6 +514,9 @@ Example: res.on('data', function (chunk) { console.log('BODY: ' + chunk); }); + res.on('end', function() { + console.log('No more data in response.') + }) }); req.on('error', function(e) { diff --git a/doc/api/punycode.markdown b/doc/api/punycode.markdown index 2de93213be713a..12086f3e224be4 100644 --- a/doc/api/punycode.markdown +++ b/doc/api/punycode.markdown @@ -2,9 +2,9 @@ Stability: 2 - Unstable -[Punycode.js](http://mths.be/punycode) is bundled with Node.js v0.6.2+. Use -`require('punycode')` to access it. (To use it with other Node.js versions, -use npm to install the `punycode` module first.) +[Punycode.js](https://mths.be/punycode) is bundled with io.js v1.0.0+ and +Node.js v0.6.2+. Use `require('punycode')` to access it. (To use it with +other Node.js versions, use npm to install the `punycode` module first.) ## punycode.decode(string) diff --git a/doc/api/smalloc.markdown b/doc/api/smalloc.markdown index ff905714e5ed21..f72857ff870dc8 100644 --- a/doc/api/smalloc.markdown +++ b/doc/api/smalloc.markdown @@ -44,7 +44,7 @@ possible options are listed in `smalloc.Types`. Example usage: var doubleArr = smalloc.alloc(3, smalloc.Types.Double); for (var i = 0; i < 3; i++) - doubleArr = i / 10; + doubleArr[i] = i / 10; // { '0': 0, '1': 0.1, '2': 0.2 } diff --git a/doc/sh_javascript.min.js b/doc/api_assets/sh_javascript.min.js similarity index 100% rename from doc/sh_javascript.min.js rename to doc/api_assets/sh_javascript.min.js diff --git a/doc/sh_main.js b/doc/api_assets/sh_main.js similarity index 96% rename from doc/sh_main.js rename to doc/api_assets/sh_main.js index 27905696c8ef7e..fa9d8efd26fbf2 100644 --- a/doc/sh_main.js +++ b/doc/api_assets/sh_main.js @@ -1,553 +1,553 @@ -/* -SHJS - Syntax Highlighting in JavaScript -Copyright (C) 2007, 2008 gnombat@users.sourceforge.net -License: http://shjs.sourceforge.net/doc/gplv3.html -*/ - -if (! this.sh_languages) { - this.sh_languages = {}; -} -var sh_requests = {}; - -function sh_isEmailAddress(url) { - if (/^mailto:/.test(url)) { - return false; - } - return url.indexOf('@') !== -1; -} - -function sh_setHref(tags, numTags, inputString) { - var url = inputString.substring(tags[numTags - 2].pos, tags[numTags - 1].pos); - if (url.length >= 2 && url.charAt(0) === '<' && url.charAt(url.length - 1) === '>') { - url = url.substr(1, url.length - 2); - } - if (sh_isEmailAddress(url)) { - url = 'mailto:' + url; - } - tags[numTags - 2].node.href = url; -} - -/* -Konqueror has a bug where the regular expression /$/g will not match at the end -of a line more than once: - - var regex = /$/g; - var match; - - var line = '1234567890'; - regex.lastIndex = 10; - match = regex.exec(line); - - var line2 = 'abcde'; - regex.lastIndex = 5; - match = regex.exec(line2); // fails -*/ -function sh_konquerorExec(s) { - var result = ['']; - result.index = s.length; - result.input = s; - return result; -} - -/** -Highlights all elements containing source code in a text string. The return -value is an array of objects, each representing an HTML start or end tag. Each -object has a property named pos, which is an integer representing the text -offset of the tag. Every start tag also has a property named node, which is the -DOM element started by the tag. End tags do not have this property. -@param inputString a text string -@param language a language definition object -@return an array of tag objects -*/ -function sh_highlightString(inputString, language) { - if (/Konqueror/.test(navigator.userAgent)) { - if (! language.konquered) { - for (var s = 0; s < language.length; s++) { - for (var p = 0; p < language[s].length; p++) { - var r = language[s][p][0]; - if (r.source === '$') { - r.exec = sh_konquerorExec; - } - } - } - language.konquered = true; - } - } - - var a = document.createElement('a'); - var span = document.createElement('span'); - - // the result - var tags = []; - var numTags = 0; - - // each element is a pattern object from language - var patternStack = []; - - // the current position within inputString - var pos = 0; - - // the name of the current style, or null if there is no current style - var currentStyle = null; - - var output = function(s, style) { - var length = s.length; - // this is more than just an optimization - we don't want to output empty elements - if (length === 0) { - return; - } - if (! style) { - var stackLength = patternStack.length; - if (stackLength !== 0) { - var pattern = patternStack[stackLength - 1]; - // check whether this is a state or an environment - if (! pattern[3]) { - // it's not a state - it's an environment; use the style for this environment - style = pattern[1]; - } - } - } - if (currentStyle !== style) { - if (currentStyle) { - tags[numTags++] = {pos: pos}; - if (currentStyle === 'sh_url') { - sh_setHref(tags, numTags, inputString); - } - } - if (style) { - var clone; - if (style === 'sh_url') { - clone = a.cloneNode(false); - } - else { - clone = span.cloneNode(false); - } - clone.className = style; - tags[numTags++] = {node: clone, pos: pos}; - } - } - pos += length; - currentStyle = style; - }; - - var endOfLinePattern = /\r\n|\r|\n/g; - endOfLinePattern.lastIndex = 0; - var inputStringLength = inputString.length; - while (pos < inputStringLength) { - var start = pos; - var end; - var startOfNextLine; - var endOfLineMatch = endOfLinePattern.exec(inputString); - if (endOfLineMatch === null) { - end = inputStringLength; - startOfNextLine = inputStringLength; - } - else { - end = endOfLineMatch.index; - startOfNextLine = endOfLinePattern.lastIndex; - } - - var line = inputString.substring(start, end); - - var matchCache = []; - for (;;) { - var posWithinLine = pos - start; - - var stateIndex; - var stackLength = patternStack.length; - if (stackLength === 0) { - stateIndex = 0; - } - else { - // get the next state - stateIndex = patternStack[stackLength - 1][2]; - } - - var state = language[stateIndex]; - var numPatterns = state.length; - var mc = matchCache[stateIndex]; - if (! mc) { - mc = matchCache[stateIndex] = []; - } - var bestMatch = null; - var bestPatternIndex = -1; - for (var i = 0; i < numPatterns; i++) { - var match; - if (i < mc.length && (mc[i] === null || posWithinLine <= mc[i].index)) { - match = mc[i]; - } - else { - var regex = state[i][0]; - regex.lastIndex = posWithinLine; - match = regex.exec(line); - mc[i] = match; - } - if (match !== null && (bestMatch === null || match.index < bestMatch.index)) { - bestMatch = match; - bestPatternIndex = i; - if (match.index === posWithinLine) { - break; - } - } - } - - if (bestMatch === null) { - output(line.substring(posWithinLine), null); - break; - } - else { - // got a match - if (bestMatch.index > posWithinLine) { - output(line.substring(posWithinLine, bestMatch.index), null); - } - - var pattern = state[bestPatternIndex]; - - var newStyle = pattern[1]; - var matchedString; - if (newStyle instanceof Array) { - for (var subexpression = 0; subexpression < newStyle.length; subexpression++) { - matchedString = bestMatch[subexpression + 1]; - output(matchedString, newStyle[subexpression]); - } - } - else { - matchedString = bestMatch[0]; - output(matchedString, newStyle); - } - - switch (pattern[2]) { - case -1: - // do nothing - break; - case -2: - // exit - patternStack.pop(); - break; - case -3: - // exitall - patternStack.length = 0; - break; - default: - // this was the start of a delimited pattern or a state/environment - patternStack.push(pattern); - break; - } - } - } - - // end of the line - if (currentStyle) { - tags[numTags++] = {pos: pos}; - if (currentStyle === 'sh_url') { - sh_setHref(tags, numTags, inputString); - } - currentStyle = null; - } - pos = startOfNextLine; - } - - return tags; -} - -//////////////////////////////////////////////////////////////////////////////// -// DOM-dependent functions - -function sh_getClasses(element) { - var result = []; - var htmlClass = element.className; - if (htmlClass && htmlClass.length > 0) { - var htmlClasses = htmlClass.split(' '); - for (var i = 0; i < htmlClasses.length; i++) { - if (htmlClasses[i].length > 0) { - result.push(htmlClasses[i]); - } - } - } - return result; -} - -function sh_addClass(element, name) { - var htmlClasses = sh_getClasses(element); - for (var i = 0; i < htmlClasses.length; i++) { - if (name.toLowerCase() === htmlClasses[i].toLowerCase()) { - return; - } - } - htmlClasses.push(name); - element.className = htmlClasses.join(' '); -} - -/** -Extracts the tags from an HTML DOM NodeList. -@param nodeList a DOM NodeList -@param result an object with text, tags and pos properties -*/ -function sh_extractTagsFromNodeList(nodeList, result) { - var length = nodeList.length; - for (var i = 0; i < length; i++) { - var node = nodeList.item(i); - switch (node.nodeType) { - case 1: - if (node.nodeName.toLowerCase() === 'br') { - var terminator; - if (/MSIE/.test(navigator.userAgent)) { - terminator = '\r'; - } - else { - terminator = '\n'; - } - result.text.push(terminator); - result.pos++; - } - else { - result.tags.push({node: node.cloneNode(false), pos: result.pos}); - sh_extractTagsFromNodeList(node.childNodes, result); - result.tags.push({pos: result.pos}); - } - break; - case 3: - case 4: - result.text.push(node.data); - result.pos += node.length; - break; - } - } -} - -/** -Extracts the tags from the text of an HTML element. The extracted tags will be -returned as an array of tag objects. See sh_highlightString for the format of -the tag objects. -@param element a DOM element -@param tags an empty array; the extracted tag objects will be returned in it -@return the text of the element -@see sh_highlightString -*/ -function sh_extractTags(element, tags) { - var result = {}; - result.text = []; - result.tags = tags; - result.pos = 0; - sh_extractTagsFromNodeList(element.childNodes, result); - return result.text.join(''); -} - -/** -Merges the original tags from an element with the tags produced by highlighting. -@param originalTags an array containing the original tags -@param highlightTags an array containing the highlighting tags - these must not overlap -@result an array containing the merged tags -*/ -function sh_mergeTags(originalTags, highlightTags) { - var numOriginalTags = originalTags.length; - if (numOriginalTags === 0) { - return highlightTags; - } - - var numHighlightTags = highlightTags.length; - if (numHighlightTags === 0) { - return originalTags; - } - - var result = []; - var originalIndex = 0; - var highlightIndex = 0; - - while (originalIndex < numOriginalTags && highlightIndex < numHighlightTags) { - var originalTag = originalTags[originalIndex]; - var highlightTag = highlightTags[highlightIndex]; - - if (originalTag.pos <= highlightTag.pos) { - result.push(originalTag); - originalIndex++; - } - else { - result.push(highlightTag); - if (highlightTags[highlightIndex + 1].pos <= originalTag.pos) { - highlightIndex++; - result.push(highlightTags[highlightIndex]); - highlightIndex++; - } - else { - // new end tag - result.push({pos: originalTag.pos}); - - // new start tag - highlightTags[highlightIndex] = {node: highlightTag.node.cloneNode(false), pos: originalTag.pos}; - } - } - } - - while (originalIndex < numOriginalTags) { - result.push(originalTags[originalIndex]); - originalIndex++; - } - - while (highlightIndex < numHighlightTags) { - result.push(highlightTags[highlightIndex]); - highlightIndex++; - } - - return result; -} - -/** -Inserts tags into text. -@param tags an array of tag objects -@param text a string representing the text -@return a DOM DocumentFragment representing the resulting HTML -*/ -function sh_insertTags(tags, text) { - var doc = document; - - var result = document.createDocumentFragment(); - var tagIndex = 0; - var numTags = tags.length; - var textPos = 0; - var textLength = text.length; - var currentNode = result; - - // output one tag or text node every iteration - while (textPos < textLength || tagIndex < numTags) { - var tag; - var tagPos; - if (tagIndex < numTags) { - tag = tags[tagIndex]; - tagPos = tag.pos; - } - else { - tagPos = textLength; - } - - if (tagPos <= textPos) { - // output the tag - if (tag.node) { - // start tag - var newNode = tag.node; - currentNode.appendChild(newNode); - currentNode = newNode; - } - else { - // end tag - currentNode = currentNode.parentNode; - } - tagIndex++; - } - else { - // output text - currentNode.appendChild(doc.createTextNode(text.substring(textPos, tagPos))); - textPos = tagPos; - } - } - - return result; -} - -/** -Highlights an element containing source code. Upon completion of this function, -the element will have been placed in the "sh_sourceCode" class. -@param element a DOM
 element containing the source code to be highlighted
-@param  language  a language definition object
-*/
-function sh_highlightElement(element, language) {
-  sh_addClass(element, 'sh_sourceCode');
-  var originalTags = [];
-  var inputString = sh_extractTags(element, originalTags);
-  var highlightTags = sh_highlightString(inputString, language);
-  var tags = sh_mergeTags(originalTags, highlightTags);
-  var documentFragment = sh_insertTags(tags, inputString);
-  while (element.hasChildNodes()) {
-    element.removeChild(element.firstChild);
-  }
-  element.appendChild(documentFragment);
-}
-
-function sh_getXMLHttpRequest() {
-  if (window.ActiveXObject) {
-    return new ActiveXObject('Msxml2.XMLHTTP');
-  }
-  else if (window.XMLHttpRequest) {
-    return new XMLHttpRequest();
-  }
-  throw 'No XMLHttpRequest implementation available';
-}
-
-function sh_load(language, element, prefix, suffix) {
-  if (language in sh_requests) {
-    sh_requests[language].push(element);
-    return;
-  }
-  sh_requests[language] = [element];
-  var request = sh_getXMLHttpRequest();
-  var url = prefix + 'sh_' + language + suffix;
-  request.open('GET', url, true);
-  request.onreadystatechange = function () {
-    if (request.readyState === 4) {
-      try {
-        if (! request.status || request.status === 200) {
-          eval(request.responseText);
-          var elements = sh_requests[language];
-          for (var i = 0; i < elements.length; i++) {
-            sh_highlightElement(elements[i], sh_languages[language]);
-          }
-        }
-        else {
-          throw 'HTTP error: status ' + request.status;
-        }
-      }
-      finally {
-        request = null;
-      }
-    }
-  };
-  request.send(null);
-}
-
-/**
-Highlights all elements containing source code on the current page. Elements
-containing source code must be "pre" elements with a "class" attribute of
-"sh_LANGUAGE", where LANGUAGE is a valid language identifier; e.g., "sh_java"
-identifies the element as containing "java" language source code.
-*/
-function highlight(prefix, suffix, tag) {
-  var nodeList = document.getElementsByTagName(tag);
-  for (var i = 0; i < nodeList.length; i++) {
-    var element = nodeList.item(i);
-    var htmlClasses = sh_getClasses(element);
-    var highlighted = false;
-    var donthighlight = false;
-    for (var j = 0; j < htmlClasses.length; j++) {
-      var htmlClass = htmlClasses[j].toLowerCase();
-      if (htmlClass === 'sh_none') {
-        donthighlight = true
-        continue;
-      }
-      if (htmlClass.substr(0, 3) === 'sh_') {
-        var language = htmlClass.substring(3);
-        if (language in sh_languages) {
-          sh_highlightElement(element, sh_languages[language]);
-          highlighted = true;
-        }
-        else if (typeof(prefix) === 'string' && typeof(suffix) === 'string') {
-          sh_load(language, element, prefix, suffix);
-        }
-        else {
-          throw 'Found <' + tag + '> element with class="' + htmlClass + '", but no such language exists';
-        }
-        break;
-      }
-    }
-    if (highlighted === false && donthighlight == false) {
-      sh_highlightElement(element, sh_languages["javascript"]);
-    }
-  }
-}
-
-
-
-function sh_highlightDocument(prefix, suffix) {
-  highlight(prefix, suffix, 'tt');
-  highlight(prefix, suffix, 'code');
-  highlight(prefix, suffix, 'pre');
-}
+/*
+SHJS - Syntax Highlighting in JavaScript
+Copyright (C) 2007, 2008 gnombat@users.sourceforge.net
+License: http://shjs.sourceforge.net/doc/gplv3.html
+*/
+
+if (! this.sh_languages) {
+  this.sh_languages = {};
+}
+var sh_requests = {};
+
+function sh_isEmailAddress(url) {
+  if (/^mailto:/.test(url)) {
+    return false;
+  }
+  return url.indexOf('@') !== -1;
+}
+
+function sh_setHref(tags, numTags, inputString) {
+  var url = inputString.substring(tags[numTags - 2].pos, tags[numTags - 1].pos);
+  if (url.length >= 2 && url.charAt(0) === '<' && url.charAt(url.length - 1) === '>') {
+    url = url.substr(1, url.length - 2);
+  }
+  if (sh_isEmailAddress(url)) {
+    url = 'mailto:' + url;
+  }
+  tags[numTags - 2].node.href = url;
+}
+
+/*
+Konqueror has a bug where the regular expression /$/g will not match at the end
+of a line more than once:
+
+  var regex = /$/g;
+  var match;
+
+  var line = '1234567890';
+  regex.lastIndex = 10;
+  match = regex.exec(line);
+
+  var line2 = 'abcde';
+  regex.lastIndex = 5;
+  match = regex.exec(line2);  // fails
+*/
+function sh_konquerorExec(s) {
+  var result = [''];
+  result.index = s.length;
+  result.input = s;
+  return result;
+}
+
+/**
+Highlights all elements containing source code in a text string.  The return
+value is an array of objects, each representing an HTML start or end tag.  Each
+object has a property named pos, which is an integer representing the text
+offset of the tag. Every start tag also has a property named node, which is the
+DOM element started by the tag. End tags do not have this property.
+@param  inputString  a text string
+@param  language  a language definition object
+@return  an array of tag objects
+*/
+function sh_highlightString(inputString, language) {
+  if (/Konqueror/.test(navigator.userAgent)) {
+    if (! language.konquered) {
+      for (var s = 0; s < language.length; s++) {
+        for (var p = 0; p < language[s].length; p++) {
+          var r = language[s][p][0];
+          if (r.source === '$') {
+            r.exec = sh_konquerorExec;
+          }
+        }
+      }
+      language.konquered = true;
+    }
+  }
+
+  var a = document.createElement('a');
+  var span = document.createElement('span');
+
+  // the result
+  var tags = [];
+  var numTags = 0;
+
+  // each element is a pattern object from language
+  var patternStack = [];
+
+  // the current position within inputString
+  var pos = 0;
+
+  // the name of the current style, or null if there is no current style
+  var currentStyle = null;
+
+  var output = function(s, style) {
+    var length = s.length;
+    // this is more than just an optimization - we don't want to output empty  elements
+    if (length === 0) {
+      return;
+    }
+    if (! style) {
+      var stackLength = patternStack.length;
+      if (stackLength !== 0) {
+        var pattern = patternStack[stackLength - 1];
+        // check whether this is a state or an environment
+        if (! pattern[3]) {
+          // it's not a state - it's an environment; use the style for this environment
+          style = pattern[1];
+        }
+      }
+    }
+    if (currentStyle !== style) {
+      if (currentStyle) {
+        tags[numTags++] = {pos: pos};
+        if (currentStyle === 'sh_url') {
+          sh_setHref(tags, numTags, inputString);
+        }
+      }
+      if (style) {
+        var clone;
+        if (style === 'sh_url') {
+          clone = a.cloneNode(false);
+        }
+        else {
+          clone = span.cloneNode(false);
+        }
+        clone.className = style;
+        tags[numTags++] = {node: clone, pos: pos};
+      }
+    }
+    pos += length;
+    currentStyle = style;
+  };
+
+  var endOfLinePattern = /\r\n|\r|\n/g;
+  endOfLinePattern.lastIndex = 0;
+  var inputStringLength = inputString.length;
+  while (pos < inputStringLength) {
+    var start = pos;
+    var end;
+    var startOfNextLine;
+    var endOfLineMatch = endOfLinePattern.exec(inputString);
+    if (endOfLineMatch === null) {
+      end = inputStringLength;
+      startOfNextLine = inputStringLength;
+    }
+    else {
+      end = endOfLineMatch.index;
+      startOfNextLine = endOfLinePattern.lastIndex;
+    }
+
+    var line = inputString.substring(start, end);
+
+    var matchCache = [];
+    for (;;) {
+      var posWithinLine = pos - start;
+
+      var stateIndex;
+      var stackLength = patternStack.length;
+      if (stackLength === 0) {
+        stateIndex = 0;
+      }
+      else {
+        // get the next state
+        stateIndex = patternStack[stackLength - 1][2];
+      }
+
+      var state = language[stateIndex];
+      var numPatterns = state.length;
+      var mc = matchCache[stateIndex];
+      if (! mc) {
+        mc = matchCache[stateIndex] = [];
+      }
+      var bestMatch = null;
+      var bestPatternIndex = -1;
+      for (var i = 0; i < numPatterns; i++) {
+        var match;
+        if (i < mc.length && (mc[i] === null || posWithinLine <= mc[i].index)) {
+          match = mc[i];
+        }
+        else {
+          var regex = state[i][0];
+          regex.lastIndex = posWithinLine;
+          match = regex.exec(line);
+          mc[i] = match;
+        }
+        if (match !== null && (bestMatch === null || match.index < bestMatch.index)) {
+          bestMatch = match;
+          bestPatternIndex = i;
+          if (match.index === posWithinLine) {
+            break;
+          }
+        }
+      }
+
+      if (bestMatch === null) {
+        output(line.substring(posWithinLine), null);
+        break;
+      }
+      else {
+        // got a match
+        if (bestMatch.index > posWithinLine) {
+          output(line.substring(posWithinLine, bestMatch.index), null);
+        }
+
+        var pattern = state[bestPatternIndex];
+
+        var newStyle = pattern[1];
+        var matchedString;
+        if (newStyle instanceof Array) {
+          for (var subexpression = 0; subexpression < newStyle.length; subexpression++) {
+            matchedString = bestMatch[subexpression + 1];
+            output(matchedString, newStyle[subexpression]);
+          }
+        }
+        else {
+          matchedString = bestMatch[0];
+          output(matchedString, newStyle);
+        }
+
+        switch (pattern[2]) {
+        case -1:
+          // do nothing
+          break;
+        case -2:
+          // exit
+          patternStack.pop();
+          break;
+        case -3:
+          // exitall
+          patternStack.length = 0;
+          break;
+        default:
+          // this was the start of a delimited pattern or a state/environment
+          patternStack.push(pattern);
+          break;
+        }
+      }
+    }
+
+    // end of the line
+    if (currentStyle) {
+      tags[numTags++] = {pos: pos};
+      if (currentStyle === 'sh_url') {
+        sh_setHref(tags, numTags, inputString);
+      }
+      currentStyle = null;
+    }
+    pos = startOfNextLine;
+  }
+
+  return tags;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DOM-dependent functions
+
+function sh_getClasses(element) {
+  var result = [];
+  var htmlClass = element.className;
+  if (htmlClass && htmlClass.length > 0) {
+    var htmlClasses = htmlClass.split(' ');
+    for (var i = 0; i < htmlClasses.length; i++) {
+      if (htmlClasses[i].length > 0) {
+        result.push(htmlClasses[i]);
+      }
+    }
+  }
+  return result;
+}
+
+function sh_addClass(element, name) {
+  var htmlClasses = sh_getClasses(element);
+  for (var i = 0; i < htmlClasses.length; i++) {
+    if (name.toLowerCase() === htmlClasses[i].toLowerCase()) {
+      return;
+    }
+  }
+  htmlClasses.push(name);
+  element.className = htmlClasses.join(' ');
+}
+
+/**
+Extracts the tags from an HTML DOM NodeList.
+@param  nodeList  a DOM NodeList
+@param  result  an object with text, tags and pos properties
+*/
+function sh_extractTagsFromNodeList(nodeList, result) {
+  var length = nodeList.length;
+  for (var i = 0; i < length; i++) {
+    var node = nodeList.item(i);
+    switch (node.nodeType) {
+    case 1:
+      if (node.nodeName.toLowerCase() === 'br') {
+        var terminator;
+        if (/MSIE/.test(navigator.userAgent)) {
+          terminator = '\r';
+        }
+        else {
+          terminator = '\n';
+        }
+        result.text.push(terminator);
+        result.pos++;
+      }
+      else {
+        result.tags.push({node: node.cloneNode(false), pos: result.pos});
+        sh_extractTagsFromNodeList(node.childNodes, result);
+        result.tags.push({pos: result.pos});
+      }
+      break;
+    case 3:
+    case 4:
+      result.text.push(node.data);
+      result.pos += node.length;
+      break;
+    }
+  }
+}
+
+/**
+Extracts the tags from the text of an HTML element. The extracted tags will be
+returned as an array of tag objects. See sh_highlightString for the format of
+the tag objects.
+@param  element  a DOM element
+@param  tags  an empty array; the extracted tag objects will be returned in it
+@return  the text of the element
+@see  sh_highlightString
+*/
+function sh_extractTags(element, tags) {
+  var result = {};
+  result.text = [];
+  result.tags = tags;
+  result.pos = 0;
+  sh_extractTagsFromNodeList(element.childNodes, result);
+  return result.text.join('');
+}
+
+/**
+Merges the original tags from an element with the tags produced by highlighting.
+@param  originalTags  an array containing the original tags
+@param  highlightTags  an array containing the highlighting tags - these must not overlap
+@result  an array containing the merged tags
+*/
+function sh_mergeTags(originalTags, highlightTags) {
+  var numOriginalTags = originalTags.length;
+  if (numOriginalTags === 0) {
+    return highlightTags;
+  }
+
+  var numHighlightTags = highlightTags.length;
+  if (numHighlightTags === 0) {
+    return originalTags;
+  }
+
+  var result = [];
+  var originalIndex = 0;
+  var highlightIndex = 0;
+
+  while (originalIndex < numOriginalTags && highlightIndex < numHighlightTags) {
+    var originalTag = originalTags[originalIndex];
+    var highlightTag = highlightTags[highlightIndex];
+
+    if (originalTag.pos <= highlightTag.pos) {
+      result.push(originalTag);
+      originalIndex++;
+    }
+    else {
+      result.push(highlightTag);
+      if (highlightTags[highlightIndex + 1].pos <= originalTag.pos) {
+        highlightIndex++;
+        result.push(highlightTags[highlightIndex]);
+        highlightIndex++;
+      }
+      else {
+        // new end tag
+        result.push({pos: originalTag.pos});
+
+        // new start tag
+        highlightTags[highlightIndex] = {node: highlightTag.node.cloneNode(false), pos: originalTag.pos};
+      }
+    }
+  }
+
+  while (originalIndex < numOriginalTags) {
+    result.push(originalTags[originalIndex]);
+    originalIndex++;
+  }
+
+  while (highlightIndex < numHighlightTags) {
+    result.push(highlightTags[highlightIndex]);
+    highlightIndex++;
+  }
+
+  return result;
+}
+
+/**
+Inserts tags into text.
+@param  tags  an array of tag objects
+@param  text  a string representing the text
+@return  a DOM DocumentFragment representing the resulting HTML
+*/
+function sh_insertTags(tags, text) {
+  var doc = document;
+
+  var result = document.createDocumentFragment();
+  var tagIndex = 0;
+  var numTags = tags.length;
+  var textPos = 0;
+  var textLength = text.length;
+  var currentNode = result;
+
+  // output one tag or text node every iteration
+  while (textPos < textLength || tagIndex < numTags) {
+    var tag;
+    var tagPos;
+    if (tagIndex < numTags) {
+      tag = tags[tagIndex];
+      tagPos = tag.pos;
+    }
+    else {
+      tagPos = textLength;
+    }
+
+    if (tagPos <= textPos) {
+      // output the tag
+      if (tag.node) {
+        // start tag
+        var newNode = tag.node;
+        currentNode.appendChild(newNode);
+        currentNode = newNode;
+      }
+      else {
+        // end tag
+        currentNode = currentNode.parentNode;
+      }
+      tagIndex++;
+    }
+    else {
+      // output text
+      currentNode.appendChild(doc.createTextNode(text.substring(textPos, tagPos)));
+      textPos = tagPos;
+    }
+  }
+
+  return result;
+}
+
+/**
+Highlights an element containing source code.  Upon completion of this function,
+the element will have been placed in the "sh_sourceCode" class.
+@param  element  a DOM 
 element containing the source code to be highlighted
+@param  language  a language definition object
+*/
+function sh_highlightElement(element, language) {
+  sh_addClass(element, 'sh_sourceCode');
+  var originalTags = [];
+  var inputString = sh_extractTags(element, originalTags);
+  var highlightTags = sh_highlightString(inputString, language);
+  var tags = sh_mergeTags(originalTags, highlightTags);
+  var documentFragment = sh_insertTags(tags, inputString);
+  while (element.hasChildNodes()) {
+    element.removeChild(element.firstChild);
+  }
+  element.appendChild(documentFragment);
+}
+
+function sh_getXMLHttpRequest() {
+  if (window.ActiveXObject) {
+    return new ActiveXObject('Msxml2.XMLHTTP');
+  }
+  else if (window.XMLHttpRequest) {
+    return new XMLHttpRequest();
+  }
+  throw 'No XMLHttpRequest implementation available';
+}
+
+function sh_load(language, element, prefix, suffix) {
+  if (language in sh_requests) {
+    sh_requests[language].push(element);
+    return;
+  }
+  sh_requests[language] = [element];
+  var request = sh_getXMLHttpRequest();
+  var url = prefix + 'sh_' + language + suffix;
+  request.open('GET', url, true);
+  request.onreadystatechange = function () {
+    if (request.readyState === 4) {
+      try {
+        if (! request.status || request.status === 200) {
+          eval(request.responseText);
+          var elements = sh_requests[language];
+          for (var i = 0; i < elements.length; i++) {
+            sh_highlightElement(elements[i], sh_languages[language]);
+          }
+        }
+        else {
+          throw 'HTTP error: status ' + request.status;
+        }
+      }
+      finally {
+        request = null;
+      }
+    }
+  };
+  request.send(null);
+}
+
+/**
+Highlights all elements containing source code on the current page. Elements
+containing source code must be "pre" elements with a "class" attribute of
+"sh_LANGUAGE", where LANGUAGE is a valid language identifier; e.g., "sh_java"
+identifies the element as containing "java" language source code.
+*/
+function highlight(prefix, suffix, tag) {
+  var nodeList = document.getElementsByTagName(tag);
+  for (var i = 0; i < nodeList.length; i++) {
+    var element = nodeList.item(i);
+    var htmlClasses = sh_getClasses(element);
+    var highlighted = false;
+    var donthighlight = false;
+    for (var j = 0; j < htmlClasses.length; j++) {
+      var htmlClass = htmlClasses[j].toLowerCase();
+      if (htmlClass === 'sh_none') {
+        donthighlight = true
+        continue;
+      }
+      if (htmlClass.substr(0, 3) === 'sh_') {
+        var language = htmlClass.substring(3);
+        if (language in sh_languages) {
+          sh_highlightElement(element, sh_languages[language]);
+          highlighted = true;
+        }
+        else if (typeof(prefix) === 'string' && typeof(suffix) === 'string') {
+          sh_load(language, element, prefix, suffix);
+        }
+        else {
+          throw 'Found <' + tag + '> element with class="' + htmlClass + '", but no such language exists';
+        }
+        break;
+      }
+    }
+    if (highlighted === false && donthighlight == false) {
+      sh_highlightElement(element, sh_languages["javascript"]);
+    }
+  }
+}
+
+
+
+function sh_highlightDocument(prefix, suffix) {
+  highlight(prefix, suffix, 'tt');
+  highlight(prefix, suffix, 'code');
+  highlight(prefix, suffix, 'pre');
+}
diff --git a/doc/template.html b/doc/template.html
index 1ce0815ef25f7f..7d32a5b5968498 100644
--- a/doc/template.html
+++ b/doc/template.html
@@ -3,50 +3,50 @@
 
   
   __SECTION__ io.js __VERSION__ Manual & Documentation
-  
+  
   
   
-  
+  
 
 
-    
-
- - __GTOC__ -
+
+
+ + __GTOC__ +
-
-
-

io.js __VERSION__ Documentation

- -
-
+
+
+

io.js __VERSION__ Documentation

+ +
+
-
-

Table of Contents

- __TOC__ -
+
+

Table of Contents

+ __TOC__ +
-
- __CONTENT__ -
-
-
- +
+ - - + + diff --git a/lib/_debug_agent.js b/lib/_debug_agent.js index d0e6dff257a655..221b6728a6995f 100644 --- a/lib/_debug_agent.js +++ b/lib/_debug_agent.js @@ -83,8 +83,9 @@ Agent.prototype.notifyWait = function notifyWait() { }; function Client(agent, socket) { - Transform.call(this); - this._readableState.objectMode = true; + Transform.call(this, { + readableObjectMode: true + }); this.agent = agent; this.binding = this.agent.binding; diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index fe8e5031ee9737..f170f07e40957c 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -24,6 +24,9 @@ # include #endif +#if defined(__OpenBSD__) +# define AI_V4MAPPED 0 +#endif namespace node { namespace cares_wrap { diff --git a/src/node.cc b/src/node.cc index d470069018428b..cae9bc85db391d 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2911,7 +2911,7 @@ static void PrintHelp() { #endif #endif "\n" - "Documentation can be found at http://nodejs.org/\n"); + "Documentation can be found at https://iojs.org/\n"); } diff --git a/src/node_root_certs.h b/src/node_root_certs.h index 67bf765fe0bc94..6af5e9c97dfb8d 100644 --- a/src/node_root_certs.h +++ b/src/node_root_certs.h @@ -88,6 +88,21 @@ "2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4\n" "-----END CERTIFICATE-----\n", +/* Verisign Class 3 Public Primary Certification Authority */ +"-----BEGIN CERTIFICATE-----\n" +"MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMC\n" +"VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQ\n" +"cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgw\n" +"MTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYD\n" +"VQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGf\n" +"MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ\n" +"2RHP7gJYHyX3KqhEBarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaO\n" +"IG+YD/isI19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G\n" +"CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2ws\n" +"qFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzk\n" +"uxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k\n" +"-----END CERTIFICATE-----\n", + /* Verisign Class 3 Public Primary Certification Authority - G2 */ "-----BEGIN CERTIFICATE-----\n" "MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYT\n" diff --git a/tools/msvs/msi/WixUI_en-us.wxl b/tools/msvs/msi/WixUI_en-us.wxl index 76ca68a1ee14f5..519024c727780d 100644 --- a/tools/msvs/msi/WixUI_en-us.wxl +++ b/tools/msvs/msi/WixUI_en-us.wxl @@ -1,4 +1,5 @@ - Choose a custom location or click Next to install + The Setup Wizard will install [ProductName] on your computer. WARNING: if you're upgrading from io.js v1.0.0 or v1.0.1, you must first uninstall these versions manually. + Choose a custom location or click Next to install. diff --git a/tools/msvs/msi/product.wxs b/tools/msvs/msi/product.wxs index c3b6ca8191a42d..878999f309d205 100755 --- a/tools/msvs/msi/product.wxs +++ b/tools/msvs/msi/product.wxs @@ -16,7 +16,7 @@ Language="1033" Version="$(var.ProductVersion)" Manufacturer="$(var.ProductAuthor)" - UpgradeCode="1d60944c-b9ce-4a71-a7c0-0384eb884baa"> + UpgradeCode="47c07a3a-42ef-4213-a85d-8f5a59077c28">