From d22259426c900f0083513d8b43c250792a5ca7f7 Mon Sep 17 00:00:00 2001 From: Brian White Date: Mon, 23 May 2011 22:40:09 -0700 Subject: [PATCH 01/11] Fix incorrect documentation for assert.fail() Fixes #1100. --- doc/api/assert.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/assert.markdown b/doc/api/assert.markdown index a0607e2d3dc..abced9d5de3 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]) From 2b91256c6185fbcaaa5fc49a3a541e5ed0ec0e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Geisendo=CC=88rfer?= Date: Mon, 23 May 2011 11:02:53 +0200 Subject: [PATCH 02/11] Fix error handling bug in stream.pipe() Problem: Since stream.pipe() is registering it's own error handlers on the source and destination stream, it needs to replicate the EventEmitter 'error' emitting semantics of throwing an error if there are no other listeners. However, there was a off-by-one error because the check for remaining listeners was done after cleanup() which means the pipe's own listener was no longer included. This would cause 'error' events on either the dest or the source to throw if there was one other error listener, and while swallowing the 'error' event if there was no other listener. Solution: I added a test demonstrating the two issues and fixed the problem by correcting the off-by-one error. Fixes #1095. --- lib/stream.js | 2 +- .../simple/test-stream-pipe-error-handling.js | 58 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 test/simple/test-stream-pipe-error-handling.js diff --git a/lib/stream.js b/lib/stream.js index a5b797482ee..1ad08ec14b0 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/test/simple/test-stream-pipe-error-handling.js b/test/simple/test-stream-pipe-error-handling.js new file mode 100644 index 00000000000..df81573daa0 --- /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); +})(); From 9b3472637e37da3d5e93356a4ea0de6a53800226 Mon Sep 17 00:00:00 2001 From: Brian White Date: Tue, 24 May 2011 21:26:57 -0700 Subject: [PATCH 03/11] Crypto documentation fixes Fixes #1104. --- doc/api/crypto.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/crypto.markdown b/doc/api/crypto.markdown index 78284b35ac1..cefbd7b3a4a 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) @@ -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. From eb4c9ed881cb93bc19401eb4270d113c4ba52d6b Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 23 May 2011 13:10:00 +0200 Subject: [PATCH 04/11] Fix resource leaks in node_crypto.cc Fixes #1097. --- src/node_crypto.cc | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index b1369d694e4..784dbf87b16 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -1677,8 +1677,10 @@ class Cipher : public ObjectWrap { initialised_ = false; } - ~Cipher () - { + ~Cipher () { + if (initialised_) { + EVP_CIPHER_CTX_cleanup(&ctx); + } } private: @@ -2126,7 +2128,11 @@ class Decipher : public ObjectWrap { initialised_ = false; } - ~Decipher () { } + ~Decipher () { + if (initialised_) { + EVP_CIPHER_CTX_cleanup(&ctx); + } + } private: @@ -2316,7 +2322,11 @@ class Hmac : public ObjectWrap { initialised_ = false; } - ~Hmac () { } + ~Hmac () { + if (initialised_) { + HMAC_CTX_cleanup(&ctx); + } + } private: @@ -2472,7 +2482,11 @@ class Hash : public ObjectWrap { initialised_ = false; } - ~Hash () { } + ~Hash () { + if (initialised_) { + EVP_MD_CTX_cleanup(&mdctx); + } + } private: @@ -2677,7 +2691,11 @@ class Sign : public ObjectWrap { initialised_ = false; } - ~Sign () { } + ~Sign () { + if (initialised_) { + EVP_MD_CTX_cleanup(&mdctx); + } + } private: @@ -2893,7 +2911,11 @@ class Verify : public ObjectWrap { initialised_ = false; } - ~Verify () { } + ~Verify () { + if (initialised_) { + EVP_MD_CTX_cleanup(&mdctx); + } + } private: From 58a1d7ec30a5e26625f42c97c3cbe9134368605d Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Fri, 20 May 2011 00:50:35 -0400 Subject: [PATCH 05/11] Close #562 Close #1078 Parse file:// urls properly The file:// protocol *always* has a hostname; it's frequently abbreviated as an empty string, which represents 'localhost' implicitly. According to RFC 1738 (http://tools.ietf.org/html/rfc1738): A file URL takes the form: file:/// where is the fully qualified domain name of the system on which the is accessible... As a special case, can be the string "localhost" or the empty string; this is interpreted as 'the machine from which the URL is being interpreted'. --- lib/url.js | 4 +--- test/simple/test-url.js | 30 ++++++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/url.js b/lib/url.js index 21d3acb348e..8b01c8548f5 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/test/simple/test-url.js b/test/simple/test-url.js index 954454ffa8a..ea85bc967fe 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', From b96ae6674d6b499c5cf1ed4825520adabb22c943 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 3 Jun 2011 08:14:35 +0200 Subject: [PATCH 06/11] document require.cache --- doc/api/globals.markdown | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/api/globals.markdown b/doc/api/globals.markdown index 8aceceaed6a..d2678fd8c8e 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 From f23c45f7f46143c225696b57422234d746698b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Lal?= Date: Fri, 3 Jun 2011 08:35:11 +0200 Subject: [PATCH 07/11] Option to disable SSL v2 Fixes #880 --- src/node_crypto.cc | 12 ++++++++++++ wscript | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 784dbf87b16..c0bdea6f24e 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -98,11 +98,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) { diff --git a/wscript b/wscript index e99a05d88f4..09d4b5af885 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 @@ -279,6 +286,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'): From 0cb4484d433ce0dc27015f18159c2e9d0c601081 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 3 Jun 2011 13:19:06 +0200 Subject: [PATCH 08/11] Doc improvements Fixes #1147. Fixes #1139. Fixes #1126. Thanks ctide, kext, disfated. --- doc/api/crypto.markdown | 2 +- doc/api/fs.markdown | 7 ++++--- doc/api/util.markdown | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/api/crypto.markdown b/doc/api/crypto.markdown index cefbd7b3a4a..b47808ba982 100644 --- a/doc/api/crypto.markdown +++ b/doc/api/crypto.markdown @@ -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) diff --git a/doc/api/fs.markdown b/doc/api/fs.markdown index 506883bbfe5..dd4f1a98d98 100644 --- a/doc/api/fs.markdown +++ b/doc/api/fs.markdown @@ -103,14 +103,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/util.markdown b/doc/api/util.markdown index 0935860c8c5..fdeb5b0c452 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) From 4956e3c0a25992bfa454a43c377ac120e0ff7727 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 3 Jun 2011 14:12:14 +0200 Subject: [PATCH 09/11] Upgrade http-parser to eee60127c0df551be085cc8e7983e36d7700d885 --- deps/http_parser/.gitignore | 1 + deps/http_parser/LICENSE-MIT | 6 +- deps/http_parser/Makefile | 13 ++- deps/http_parser/http_parser.c | 118 +++++++++++++++------------ deps/http_parser/http_parser.h | 21 ++++- deps/http_parser/test.c | 141 ++++++++++++++++++++++++++++++++- 6 files changed, 238 insertions(+), 62 deletions(-) diff --git a/deps/http_parser/.gitignore b/deps/http_parser/.gitignore index 73fe6a4cef5..04b7a1fee64 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 f30a31de946..58010b38894 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 2b945c16b04..4eceeaae757 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 9c5640f6d4e..1453d411b0c 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 c03ec057837..6a54a2d6357 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 2d1d8bd5daa..a4b80a2e606 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 */ }; From 1d7a46a58884de2cb566e4ae54e39b4895355527 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 3 Jun 2011 14:38:55 +0200 Subject: [PATCH 10/11] Disabling SSL compression is dependent on OpenSSL version 0.9.8 Fixes #1087. --- src/node_crypto.cc | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index c0bdea6f24e..81fb95e1baa 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -2948,16 +2948,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); From 37d529f818aacd1146b1d927b3251bbefcea389f Mon Sep 17 00:00:00 2001 From: Siddharth Mahendraker Date: Thu, 2 Jun 2011 23:08:18 +0300 Subject: [PATCH 11/11] debugger: don't allow users to input non-valid commands Fixes #1144. --- lib/_debugger.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/_debugger.js b/lib/_debugger.js index 2714fc52dc8..9b898e3515a 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;