diff --git a/deps/http_parser/.gitignore b/deps/http_parser/.gitignore index 73fe6a4cef..04b7a1fee6 100644 --- a/deps/http_parser/.gitignore +++ b/deps/http_parser/.gitignore @@ -2,3 +2,4 @@ tags *.o test test_g +test_fast diff --git a/deps/http_parser/LICENSE-MIT b/deps/http_parser/LICENSE-MIT index f30a31de94..58010b3889 100644 --- a/deps/http_parser/LICENSE-MIT +++ b/deps/http_parser/LICENSE-MIT @@ -1,4 +1,8 @@ -Copyright 2009,2010 Ryan Dahl +http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright +Igor Sysoev. + +Additional changes are licensed under the same terms as NGINX and +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 diff --git a/deps/http_parser/Makefile b/deps/http_parser/Makefile index 2b945c16b0..4eceeaae75 100644 --- a/deps/http_parser/Makefile +++ b/deps/http_parser/Makefile @@ -1,11 +1,14 @@ -OPT_DEBUG=-O0 -g -Wall -Wextra -Werror -I. -OPT_FAST=-O3 -DHTTP_PARSER_STRICT=0 -I. +CPPFLAGS?=-Wall -Wextra -Werror -I. +OPT_DEBUG=$(CPPFLAGS) -O0 -g -DHTTP_PARSER_STRICT=1 +OPT_FAST=$(CPPFLAGS) -O3 -DHTTP_PARSER_STRICT=0 CC?=gcc +AR?=ar -test: test_g +test: test_g test_fast ./test_g + ./test_fast test_g: http_parser_g.o test_g.o $(CC) $(OPT_DEBUG) http_parser_g.o test_g.o -o $@ @@ -31,11 +34,13 @@ test_fast: http_parser.o test.c http_parser.h test-run-timed: test_fast while(true) do time ./test_fast > /dev/null; done +package: http_parser.o + $(AR) rcs libhttp_parser.a http_parser.o tags: http_parser.c http_parser.h test.c ctags $^ clean: - rm -f *.o test test_fast test_g http_parser.tar tags + rm -f *.o *.a test test_fast test_g http_parser.tar tags .PHONY: clean package test-run test-run-timed test-valgrind diff --git a/deps/http_parser/http_parser.c b/deps/http_parser/http_parser.c index 9c5640f6d4..1453d411b0 100644 --- a/deps/http_parser/http_parser.c +++ b/deps/http_parser/http_parser.c @@ -1,4 +1,7 @@ -/* Copyright 2009,2010 Ryan Dahl +/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev + * + * Additional changes are licensed under the same terms as NGINX and + * 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 @@ -97,6 +100,7 @@ static const char *method_strings[] = , "NOTIFY" , "SUBSCRIBE" , "UNSUBSCRIBE" + , "PATCH" }; @@ -186,7 +190,7 @@ static const uint8_t normal_url_char[256] = { /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ 1, 1, 1, 1, 1, 1, 1, 1, /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ - 1, 1, 1, 1, 1, 1, 1, 0 }; + 1, 1, 1, 1, 1, 1, 1, 0, }; enum state @@ -240,15 +244,17 @@ enum state , s_header_almost_done + , s_chunk_size_start + , s_chunk_size + , s_chunk_parameters + , s_chunk_size_almost_done + , s_headers_almost_done /* Important: 's_headers_almost_done' must be the last 'header' state. All * states beyond this must be 'body' states. It is used for overflow * checking. See the PARSING_HEADER() macro. */ - , s_chunk_size_start - , s_chunk_size - , s_chunk_size_almost_done - , s_chunk_parameters + , s_chunk_data , s_chunk_data_almost_done , s_chunk_data_done @@ -258,7 +264,7 @@ enum state }; -#define PARSING_HEADER(state) (state <= s_headers_almost_done && 0 == (parser->flags & F_TRAILING)) +#define PARSING_HEADER(state) (state <= s_headers_almost_done) enum header_states @@ -288,20 +294,24 @@ enum header_states }; -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 - }; +/* Macros for character classes; depends on strict-mode */ +#define CR '\r' +#define LF '\n' +#define LOWER(c) (unsigned char)(c | 0x20) +#define TOKEN(c) (tokens[(unsigned char)c]) +#define IS_ALPHA(c) ((c) >= 'a' && (c) <= 'z') +#define IS_NUM(c) ((c) >= '0' && (c) <= '9') +#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) - -#define CR '\r' -#define LF '\n' -#define LOWER(c) (unsigned char)(c | 0x20) -#define TOKEN(c) tokens[(unsigned char)c] +#if HTTP_PARSER_STRICT +#define IS_URL_CHAR(c) (normal_url_char[(unsigned char) (c)]) +#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') +#else +#define IS_URL_CHAR(c) \ + (normal_url_char[(unsigned char) (c)] || ((c) & 0x80)) +#define IS_HOST_CHAR(c) \ + (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') +#endif #define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) @@ -478,7 +488,7 @@ size_t http_parser_execute (http_parser *parser, break; } - if (ch < '0' || ch > '9') goto error; + if (!IS_NUM(ch)) goto error; parser->http_major *= 10; parser->http_major += ch - '0'; @@ -489,7 +499,7 @@ size_t http_parser_execute (http_parser *parser, /* first digit of minor HTTP version */ case s_res_first_http_minor: - if (ch < '0' || ch > '9') goto error; + if (!IS_NUM(ch)) goto error; parser->http_minor = ch - '0'; state = s_res_http_minor; break; @@ -502,7 +512,7 @@ size_t http_parser_execute (http_parser *parser, break; } - if (ch < '0' || ch > '9') goto error; + if (!IS_NUM(ch)) goto error; parser->http_minor *= 10; parser->http_minor += ch - '0'; @@ -513,7 +523,7 @@ size_t http_parser_execute (http_parser *parser, case s_res_first_status_code: { - if (ch < '0' || ch > '9') { + if (!IS_NUM(ch)) { if (ch == ' ') { break; } @@ -526,7 +536,7 @@ size_t http_parser_execute (http_parser *parser, case s_res_status_code: { - if (ch < '0' || ch > '9') { + if (!IS_NUM(ch)) { switch (ch) { case ' ': state = s_res_status; @@ -578,7 +588,7 @@ size_t http_parser_execute (http_parser *parser, CALLBACK2(message_begin); - if (ch < 'A' || 'Z' < ch) goto error; + if (!IS_ALPHA(LOWER(ch))) goto error; start_req_method_assign: parser->method = (enum http_method) 0; @@ -592,7 +602,9 @@ size_t http_parser_execute (http_parser *parser, case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break; case 'N': parser->method = HTTP_NOTIFY; break; case 'O': parser->method = HTTP_OPTIONS; break; - case 'P': parser->method = HTTP_POST; /* or PROPFIND or PROPPATCH or PUT */ break; + case 'P': parser->method = HTTP_POST; + /* or PROPFIND or PROPPATCH or PUT or PATCH */ + break; case 'R': parser->method = HTTP_REPORT; break; case 'S': parser->method = HTTP_SUBSCRIBE; break; case 'T': parser->method = HTTP_TRACE; break; @@ -633,6 +645,8 @@ size_t http_parser_execute (http_parser *parser, parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */ } else if (index == 1 && parser->method == HTTP_POST && ch == 'U') { parser->method = HTTP_PUT; + } else if (index == 1 && parser->method == HTTP_POST && ch == 'A') { + parser->method = HTTP_PATCH; } else if (index == 2 && parser->method == HTTP_UNLOCK && ch == 'S') { parser->method = HTTP_UNSUBSCRIBE; } else if (index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') { @@ -657,9 +671,13 @@ size_t http_parser_execute (http_parser *parser, c = LOWER(ch); - if (c >= 'a' && c <= 'z') { + /* Proxied requests are followed by scheme of an absolute URI (alpha). + * CONNECT is followed by a hostname, which begins with alphanum. + * All other methods are followed by '/' or '*' (handled above). + */ + if (IS_ALPHA(ch) || (parser->method == HTTP_CONNECT && IS_NUM(ch))) { MARK(url); - state = s_req_schema; + state = (parser->method == HTTP_CONNECT) ? s_req_host : s_req_schema; break; } @@ -670,17 +688,11 @@ size_t http_parser_execute (http_parser *parser, { c = LOWER(ch); - if (c >= 'a' && c <= 'z') break; + if (IS_ALPHA(c)) break; if (ch == ':') { state = s_req_schema_slash; break; - } else if (ch == '.') { - state = s_req_host; - break; - } else if ('0' <= ch && ch <= '9') { - state = s_req_host; - break; } goto error; @@ -699,8 +711,7 @@ size_t http_parser_execute (http_parser *parser, case s_req_host: { c = LOWER(ch); - if (c >= 'a' && c <= 'z') break; - if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') break; + if (IS_HOST_CHAR(ch)) break; switch (ch) { case ':': state = s_req_port; @@ -717,6 +728,9 @@ size_t http_parser_execute (http_parser *parser, CALLBACK(url); state = s_req_http_start; break; + case '?': + state = s_req_query_string_start; + break; default: goto error; } @@ -725,7 +739,7 @@ size_t http_parser_execute (http_parser *parser, case s_req_port: { - if (ch >= '0' && ch <= '9') break; + if (IS_NUM(ch)) break; switch (ch) { case '/': MARK(path); @@ -739,6 +753,9 @@ size_t http_parser_execute (http_parser *parser, CALLBACK(url); state = s_req_http_start; break; + case '?': + state = s_req_query_string_start; + break; default: goto error; } @@ -747,7 +764,7 @@ size_t http_parser_execute (http_parser *parser, case s_req_path: { - if (normal_url_char[(unsigned char)ch]) break; + if (IS_URL_CHAR(ch)) break; switch (ch) { case ' ': @@ -785,7 +802,7 @@ size_t http_parser_execute (http_parser *parser, case s_req_query_string_start: { - if (normal_url_char[(unsigned char)ch]) { + if (IS_URL_CHAR(ch)) { MARK(query_string); state = s_req_query_string; break; @@ -821,7 +838,7 @@ size_t http_parser_execute (http_parser *parser, case s_req_query_string: { - if (normal_url_char[(unsigned char)ch]) break; + if (IS_URL_CHAR(ch)) break; switch (ch) { case '?': @@ -858,7 +875,7 @@ size_t http_parser_execute (http_parser *parser, case s_req_fragment_start: { - if (normal_url_char[(unsigned char)ch]) { + if (IS_URL_CHAR(ch)) { MARK(fragment); state = s_req_fragment; break; @@ -895,7 +912,7 @@ size_t http_parser_execute (http_parser *parser, case s_req_fragment: { - if (normal_url_char[(unsigned char)ch]) break; + if (IS_URL_CHAR(ch)) break; switch (ch) { case ' ': @@ -973,7 +990,7 @@ size_t http_parser_execute (http_parser *parser, break; } - if (ch < '0' || ch > '9') goto error; + if (!IS_NUM(ch)) goto error; parser->http_major *= 10; parser->http_major += ch - '0'; @@ -984,7 +1001,7 @@ size_t http_parser_execute (http_parser *parser, /* first digit of minor HTTP version */ case s_req_first_http_minor: - if (ch < '0' || ch > '9') goto error; + if (!IS_NUM(ch)) goto error; parser->http_minor = ch - '0'; state = s_req_http_minor; break; @@ -1004,7 +1021,7 @@ size_t http_parser_execute (http_parser *parser, /* XXX allow spaces after digit? */ - if (ch < '0' || ch > '9') goto error; + if (!IS_NUM(ch)) goto error; parser->http_minor *= 10; parser->http_minor += ch - '0'; @@ -1237,7 +1254,7 @@ size_t http_parser_execute (http_parser *parser, break; case h_content_length: - if (ch < '0' || ch > '9') goto error; + if (!IS_NUM(ch)) goto error; parser->content_length = ch - '0'; break; @@ -1286,7 +1303,7 @@ size_t http_parser_execute (http_parser *parser, case h_content_length: if (ch == ' ') break; - if (ch < '0' || ch > '9') goto error; + if (!IS_NUM(ch)) goto error; parser->content_length *= 10; parser->content_length += ch - '0'; break; @@ -1458,6 +1475,7 @@ size_t http_parser_execute (http_parser *parser, case s_chunk_size_start: { + assert(nread == 1); assert(parser->flags & F_CHUNKED); c = unhex[(unsigned char)ch]; @@ -1507,6 +1525,8 @@ size_t http_parser_execute (http_parser *parser, assert(parser->flags & F_CHUNKED); STRICT_CHECK(ch != LF); + nread = 0; + if (parser->content_length == 0) { parser->flags |= F_TRAILING; state = s_header_field_start; diff --git a/deps/http_parser/http_parser.h b/deps/http_parser/http_parser.h index c03ec05783..6a54a2d635 100644 --- a/deps/http_parser/http_parser.h +++ b/deps/http_parser/http_parser.h @@ -1,4 +1,4 @@ -/* Copyright 2009,2010 Ryan Dahl +/* 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 @@ -24,6 +24,8 @@ extern "C" { #endif +#define HTTP_PARSER_VERSION_MAJOR 1 +#define HTTP_PARSER_VERSION_MINOR 0 #include #if defined(_WIN32) && !defined(__MINGW32__) @@ -47,8 +49,6 @@ typedef int ssize_t; */ #ifndef HTTP_PARSER_STRICT # define HTTP_PARSER_STRICT 1 -#else -# define HTTP_PARSER_STRICT 0 #endif @@ -106,16 +106,29 @@ enum http_method , HTTP_NOTIFY , HTTP_SUBSCRIBE , HTTP_UNSUBSCRIBE + /* RFC-5789 */ + , HTTP_PATCH }; enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; +/* Flag values for http_parser.flags field */ +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 + }; + + struct http_parser { /** PRIVATE **/ unsigned char type : 2; - unsigned char flags : 6; + unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */ unsigned char state; unsigned char header_state; unsigned char index; diff --git a/deps/http_parser/test.c b/deps/http_parser/test.c index 2d1d8bd5da..a4b80a2e60 100644 --- a/deps/http_parser/test.c +++ b/deps/http_parser/test.c @@ -1,4 +1,4 @@ -/* Copyright 2009,2010 Ryan Dahl +/* 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 @@ -498,7 +498,7 @@ const struct message requests[] = #define CONNECT_REQUEST 17 , {.name = "connect request" ,.type= HTTP_REQUEST - ,.raw= "CONNECT home0.netscape.com:443 HTTP/1.0\r\n" + ,.raw= "CONNECT 0-home0.netscape.com:443 HTTP/1.0\r\n" "User-agent: Mozilla/1.1N\r\n" "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n" "\r\n" @@ -510,7 +510,7 @@ const struct message requests[] = ,.query_string= "" ,.fragment= "" ,.request_path= "" - ,.request_url= "home0.netscape.com:443" + ,.request_url= "0-home0.netscape.com:443" ,.num_headers= 2 ,.upgrade=1 ,.headers= { { "User-agent", "Mozilla/1.1N" } @@ -557,7 +557,7 @@ const struct message requests[] = ,.body= "" } -#define MSEARCH_REQ 19 +#define MSEARCH_REQ 20 , {.name= "m-search request" ,.type= HTTP_REQUEST ,.raw= "M-SEARCH * HTTP/1.1\r\n" @@ -582,6 +582,139 @@ const struct message requests[] = ,.body= "" } +#define QUERY_TERMINATED_HOST 21 +, {.name= "host terminated by a query string" + ,.type= HTTP_REQUEST + ,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "hail=all" + ,.fragment= "" + ,.request_path= "" + ,.request_url= "http://hypnotoad.org?hail=all" + ,.num_headers= 0 + ,.headers= { } + ,.body= "" + } + +#define QUERY_TERMINATED_HOSTPORT 22 +, {.name= "host:port terminated by a query string" + ,.type= HTTP_REQUEST + ,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "hail=all" + ,.fragment= "" + ,.request_path= "" + ,.request_url= "http://hypnotoad.org:1234?hail=all" + ,.num_headers= 0 + ,.headers= { } + ,.body= "" + } + +#define SPACE_TERMINATED_HOSTPORT 23 +, {.name= "host:port terminated by a space" + ,.type= HTTP_REQUEST + ,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "" + ,.request_url= "http://hypnotoad.org:1234" + ,.num_headers= 0 + ,.headers= { } + ,.body= "" + } + +#if !HTTP_PARSER_STRICT +#define UTF8_PATH_REQ 24 +, {.name= "utf-8 path request" + ,.type= HTTP_REQUEST + ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n" + "Host: github.com\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "q=1" + ,.fragment= "narf" + ,.request_path= "/δ¶/δt/pope" + ,.request_url= "/δ¶/δt/pope?q=1#narf" + ,.num_headers= 1 + ,.headers= { {"Host", "github.com" } + } + ,.body= "" + } + +#define HOSTNAME_UNDERSCORE 25 +, {.name = "hostname underscore" + ,.type= HTTP_REQUEST + ,.raw= "CONNECT home_0.netscape.com:443 HTTP/1.0\r\n" + "User-agent: Mozilla/1.1N\r\n" + "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.method= HTTP_CONNECT + ,.query_string= "" + ,.fragment= "" + ,.request_path= "" + ,.request_url= "home_0.netscape.com:443" + ,.num_headers= 2 + ,.upgrade=1 + ,.headers= { { "User-agent", "Mozilla/1.1N" } + , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" } + } + ,.body= "" + } +#endif /* !HTTP_PARSER_STRICT */ + +#define PATCH_REQ 26 +, {.name = "PATCH request" + ,.type= HTTP_REQUEST + ,.raw= "PATCH /file.txt HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Content-Type: application/example\r\n" + "If-Match: \"e0023aa4e\"\r\n" + "Content-Length: 10\r\n" + "\r\n" + "cccccccccc" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_PATCH + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/file.txt" + ,.request_url= "/file.txt" + ,.num_headers= 4 + ,.upgrade=0 + ,.headers= { { "Host", "www.example.com" } + , { "Content-Type", "application/example" } + , { "If-Match", "\"e0023aa4e\"" } + , { "Content-Length", "10" } + } + ,.body= "cccccccccc" + } + , {.name= NULL } /* sentinel */ }; diff --git a/doc/api/assert.markdown b/doc/api/assert.markdown index a0607e2d3d..abced9d5de 100644 --- a/doc/api/assert.markdown +++ b/doc/api/assert.markdown @@ -5,7 +5,7 @@ access it with `require('assert')`. ### assert.fail(actual, expected, message, operator) -Tests if `actual` is equal to `expected` using the operator provided. +Throws an exception that displays the values for `actual` and `expected` separated by the provided operator. ### assert.ok(value, [message]) diff --git a/doc/api/crypto.markdown b/doc/api/crypto.markdown index 91fc3eab1a..0c0134c39a 100644 --- a/doc/api/crypto.markdown +++ b/doc/api/crypto.markdown @@ -93,7 +93,7 @@ Returns the enciphered contents, and can be called many times with new data as i ### cipher.final(output_encoding='binary') -Returns any remaining enciphered contents, with `output_encoding` being one of: `'binary'`, `'ascii'` or `'utf8'`. +Returns any remaining enciphered contents, with `output_encoding` being one of: `'binary'`, `'base64'` or `'hex'`. ### crypto.createDecipher(algorithm, key) @@ -108,7 +108,7 @@ The `output_decoding` specifies in what format to return the deciphered plaintex ### decipher.final(output_encoding='binary') Returns any remaining plaintext which is deciphered, -with `output_encoding' being one of: `'binary'`, `'ascii'` or `'utf8'`. +with `output_encoding` being one of: `'binary'`, `'ascii'` or `'utf8'`. ### crypto.createSign(algorithm) @@ -142,7 +142,7 @@ This can be called many times with new data as it is streamed. ### verifier.verify(cert, signature, signature_format='binary') Verifies the signed data by using the `cert` which is a string containing -the PEM encoded certificate, and `signature`, which is the previously calculates +the PEM encoded certificate, and `signature`, which is the previously calculated signature for the data, in the `signature_format` which can be `'binary'`, `'hex'` or `'base64'`. Returns true or false depending on the validity of the signature for the data and public key. diff --git a/doc/api/fs.markdown b/doc/api/fs.markdown index a8b9dd81a7..613c45efd3 100644 --- a/doc/api/fs.markdown +++ b/doc/api/fs.markdown @@ -148,14 +148,15 @@ See the [fs.Stats](#fs.Stats) section below for more information. ### fs.lstat(path, [callback]) Asynchronous lstat(2). The callback gets two arguments `(err, stats)` where -`stats` is a `fs.Stats` object. lstat() is identical to stat(), except that if -path is a symbolic link, then the link itself is stat-ed, not the file that it +`stats` is a `fs.Stats` object. `lstat()` is identical to `stat()`, except that if +`path` is a symbolic link, then the link itself is stat-ed, not the file that it refers to. ### fs.fstat(fd, [callback]) Asynchronous fstat(2). The callback gets two arguments `(err, stats)` where -`stats` is a `fs.Stats` object. +`stats` is a `fs.Stats` object. `fstat()` is identical to `stat()`, except that +the file to be stat-ed is specified by the file descriptor `fd`. ### fs.statSync(path) diff --git a/doc/api/globals.markdown b/doc/api/globals.markdown index 8aceceaed6..d2678fd8c8 100644 --- a/doc/api/globals.markdown +++ b/doc/api/globals.markdown @@ -32,6 +32,12 @@ To require modules. See the [Modules](modules.html#modules) section. Use the internal `require()` machinery to look up the location of a module, but rather than loading the module, just return the resolved filename. +### require.cache + +Modules are cached in this object when they are required. By deleting a key +value from this object, the next `require` will reload the module. + + ### require.paths An array of search paths for `require()`. This array can be modified to add diff --git a/doc/api/util.markdown b/doc/api/util.markdown index 0935860c8c..fdeb5b0c45 100644 --- a/doc/api/util.markdown +++ b/doc/api/util.markdown @@ -16,7 +16,7 @@ output `string` immediately to `stderr`. Output with timestamp on `stdout`. - require('util').log('Timestmaped message.'); + require('util').log('Timestamped message.'); ### util.inspect(object, showHidden=false, depth=2) diff --git a/lib/_debugger.js b/lib/_debugger.js index 2714fc52dc..9b898e3515 100644 --- a/lib/_debugger.js +++ b/lib/_debugger.js @@ -785,7 +785,7 @@ Interface.prototype.handleCommand = function(cmd) { self._lastCommand = null; self.tryQuit(); - } else if (/^r(un)?/.test(cmd)) { + } else if (/^r(un)?$/.test(cmd)) { self._lastCommand = null; if (self.child) { self.restartQuestion(function(yes) { @@ -805,7 +805,7 @@ Interface.prototype.handleCommand = function(cmd) { self.trySpawn(); } - } else if (/^help/.test(cmd)) { + } else if (/^help$/.test(cmd)) { console.log(helpMessage); term.prompt(); @@ -819,7 +819,7 @@ Interface.prototype.handleCommand = function(cmd) { term.prompt(); }); - } else if (/info +breakpoints/.test(cmd)) { + } else if (/^info(\s+breakpoint)?$/.test(cmd)) { if (!client) { self.printNotConnected(); return; @@ -867,7 +867,7 @@ Interface.prototype.handleCommand = function(cmd) { term.prompt(); }); - } else if (/^backtrace/.test(cmd) || /^bt/.test(cmd)) { + } else if (/^b(ack)?t(race)?$/.test(cmd)) { if (!client) { self.printNotConnected(); return; @@ -905,7 +905,7 @@ Interface.prototype.handleCommand = function(cmd) { self.printScripts(cmd.indexOf('full') > 0); term.prompt(); - } else if (/^c(ontinue)?/.test(cmd)) { + } else if (/^c(ontinue)?$/.test(cmd)) { if (!client) { self.printNotConnected(); return; @@ -916,7 +916,7 @@ Interface.prototype.handleCommand = function(cmd) { self.resume(); }); - } else if (/^k(ill)?/.test(cmd)) { + } else if (/^k(ill)?$/.test(cmd)) { if (!client) { self.printNotConnected(); return; @@ -934,7 +934,7 @@ Interface.prototype.handleCommand = function(cmd) { self.term.prompt(); } - } else if (/^next/.test(cmd) || /^n/.test(cmd)) { + } else if (/^n(ext)?$/.test(cmd)) { if (!client) { self.printNotConnected(); return; @@ -943,7 +943,7 @@ Interface.prototype.handleCommand = function(cmd) { // Wait for break point. (disable raw mode?) }); - } else if (/^step/.test(cmd) || /^s/.test(cmd)) { + } else if (/^s(tep)?$/.test(cmd)) { if (!client) { self.printNotConnected(); return; @@ -952,7 +952,7 @@ Interface.prototype.handleCommand = function(cmd) { // Wait for break point. (disable raw mode?) }); - } else if (/^print/.test(cmd) || /^p/.test(cmd)) { + } else if (/^p(rint)?$/.test(cmd)) { if (!client) { self.printNotConnected(); return; diff --git a/lib/stream.js b/lib/stream.js index a5b797482e..1ad08ec14b 100644 --- a/lib/stream.js +++ b/lib/stream.js @@ -95,7 +95,7 @@ Stream.prototype.pipe = function(dest, options) { // don't leave dangling pipes when there are errors. function onerror(er) { cleanup(); - if (this.listeners('error').length === 1) { + if (this.listeners('error').length === 0) { throw er; // Unhandled stream error in pipe. } } diff --git a/lib/url.js b/lib/url.js index 21d3acb348..8b01c8548f 100644 --- a/lib/url.js +++ b/lib/url.js @@ -54,9 +54,7 @@ var protocolPattern = /^([a-z0-9]+:)/i, // protocols that never have a hostname. hostlessProtocol = { 'javascript': true, - 'javascript:': true, - 'file': true, - 'file:': true + 'javascript:': true }, // protocols that always have a path component. pathedProtocol = { diff --git a/src/node_crypto.cc b/src/node_crypto.cc index c3b2932397..13749a83fe 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -99,11 +99,23 @@ Handle SecureContext::Init(const Arguments& args) { String::Utf8Value sslmethod(args[0]->ToString()); if (strcmp(*sslmethod, "SSLv2_method") == 0) { +#ifndef OPENSSL_NO_SSL2 method = SSLv2_method(); +#else + return ThrowException(Exception::Error(String::New("SSLv2 methods disabled"))); +#endif } else if (strcmp(*sslmethod, "SSLv2_server_method") == 0) { +#ifndef OPENSSL_NO_SSL2 method = SSLv2_server_method(); +#else + return ThrowException(Exception::Error(String::New("SSLv2 methods disabled"))); +#endif } else if (strcmp(*sslmethod, "SSLv2_client_method") == 0) { +#ifndef OPENSSL_NO_SSL2 method = SSLv2_client_method(); +#else + return ThrowException(Exception::Error(String::New("SSLv2 methods disabled"))); +#endif } else if (strcmp(*sslmethod, "SSLv3_method") == 0) { method = SSLv3_method(); } else if (strcmp(*sslmethod, "SSLv3_server_method") == 0) { @@ -1827,8 +1839,10 @@ class Cipher : public ObjectWrap { initialised_ = false; } - ~Cipher () - { + ~Cipher () { + if (initialised_) { + EVP_CIPHER_CTX_cleanup(&ctx); + } } private: @@ -2276,7 +2290,11 @@ class Decipher : public ObjectWrap { initialised_ = false; } - ~Decipher () { } + ~Decipher () { + if (initialised_) { + EVP_CIPHER_CTX_cleanup(&ctx); + } + } private: @@ -2466,7 +2484,11 @@ class Hmac : public ObjectWrap { initialised_ = false; } - ~Hmac () { } + ~Hmac () { + if (initialised_) { + HMAC_CTX_cleanup(&ctx); + } + } private: @@ -2622,7 +2644,11 @@ class Hash : public ObjectWrap { initialised_ = false; } - ~Hash () { } + ~Hash () { + if (initialised_) { + EVP_MD_CTX_cleanup(&mdctx); + } + } private: @@ -2827,7 +2853,11 @@ class Sign : public ObjectWrap { initialised_ = false; } - ~Sign () { } + ~Sign () { + if (initialised_) { + EVP_MD_CTX_cleanup(&mdctx); + } + } private: @@ -3043,7 +3073,11 @@ class Verify : public ObjectWrap { initialised_ = false; } - ~Verify () { } + ~Verify () { + if (initialised_) { + EVP_MD_CTX_cleanup(&mdctx); + } + } private: @@ -3576,16 +3610,12 @@ void InitCrypto(Handle target) { ERR_load_crypto_strings(); // Turn off compression. Saves memory - do it in userland. +#ifdef SSL_COMP_get_compression_methods + // Before OpenSSL 0.9.8 this was not possible. STACK_OF(SSL_COMP)* comp_methods = SSL_COMP_get_compression_methods(); -#if 0 - if (comp_methods && sk_SSL_COMP_num(comp_methods) > 0) { - default_compression_method = sk_SSL_COMP_pop(comp_methods); - fprintf(stderr, "SSL_COMP_get_name %s\n", - SSL_COMP_get_name(default_compression_method->method)); - } -#endif sk_SSL_COMP_zero(comp_methods); assert(sk_SSL_COMP_num(comp_methods) == 0); +#endif SecureContext::Initialize(target); Connection::Initialize(target); diff --git a/test/simple/test-stream-pipe-error-handling.js b/test/simple/test-stream-pipe-error-handling.js new file mode 100644 index 0000000000..df81573daa --- /dev/null +++ b/test/simple/test-stream-pipe-error-handling.js @@ -0,0 +1,58 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var Stream = require('stream').Stream; + +(function testErrorListenerCatches() { + var source = new Stream(); + var dest = new Stream(); + + source.pipe(dest); + + var gotErr = null; + source.on('error', function(err) { + gotErr = err; + }); + + var err = new Error('This stream turned into bacon.'); + source.emit('error', err); + assert.strictEqual(gotErr, err); +})(); + +(function testErrorWithoutListenerThrows() { + var source = new Stream(); + var dest = new Stream(); + + source.pipe(dest); + + var err = new Error('This stream turned into bacon.'); + + var gotErr = null; + try { + source.emit('error', err); + } catch (e) { + gotErr = e; + } + + assert.strictEqual(gotErr, err); +})(); diff --git a/test/simple/test-url.js b/test/simple/test-url.js index 954454ffa8..ea85bc967f 100644 --- a/test/simple/test-url.js +++ b/test/simple/test-url.js @@ -166,12 +166,38 @@ var parseTests = { 'file:///etc/passwd' : { 'href': 'file:///etc/passwd', 'protocol': 'file:', - 'pathname': '///etc/passwd' + 'pathname': '/etc/passwd', + 'hostname': '' + }, + 'file://localhost/etc/passwd' : { + 'href': 'file://localhost/etc/passwd', + 'protocol': 'file:', + 'pathname': '/etc/passwd', + 'hostname': 'localhost' + }, + 'file://foo/etc/passwd' : { + 'href': 'file://foo/etc/passwd', + 'protocol': 'file:', + 'pathname': '/etc/passwd', + 'hostname': 'foo' }, 'file:///etc/node/' : { 'href': 'file:///etc/node/', 'protocol': 'file:', - 'pathname': '///etc/node/' + 'pathname': '/etc/node/', + 'hostname': '' + }, + 'file://localhost/etc/node/' : { + 'href': 'file://localhost/etc/node/', + 'protocol': 'file:', + 'pathname': '/etc/node/', + 'hostname': 'localhost' + }, + 'file://foo/etc/node/' : { + 'href': 'file://foo/etc/node/', + 'protocol': 'file:', + 'pathname': '/etc/node/', + 'hostname': 'foo' }, 'http:/baz/../foo/bar' : { 'href': 'http:/baz/../foo/bar', diff --git a/wscript b/wscript index 1cc8dd1a12..a97dd65668 100644 --- a/wscript +++ b/wscript @@ -143,6 +143,13 @@ def set_options(opt): , dest='openssl_libpath' ) + opt.add_option( '--no-ssl2' + , action='store_true' + , default=False + , help="Disable OpenSSL v2" + , dest='openssl_nov2' + ) + opt.add_option( '--gdb' , action='store_true' , default=False @@ -257,6 +264,11 @@ def configure(conf): if not Options.options.without_ssl: # Don't override explicitly supplied openssl paths with pkg-config results. explicit_openssl = o.openssl_includes or o.openssl_libpath + + # Disable ssl v2 methods + if o.openssl_nov2: + conf.env.append_value("CPPFLAGS", "-DOPENSSL_NO_SSL2=1") + if not explicit_openssl and conf.check_cfg(package='openssl', args='--cflags --libs', uselib_store='OPENSSL'):