From c3f176222a990387b030898592bea3bbd1c43dd3 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Sun, 12 Feb 2012 21:04:03 +0100 Subject: [PATCH 01/27] Fix vcbuild.bat, print error when an unrecognized option is encountered --- vcbuild.bat | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/vcbuild.bat b/vcbuild.bat index 71b46cbf1a..f6d0fefe37 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -26,26 +26,27 @@ set upload= :next-arg if "%1"=="" goto args-done -if /i "%1"=="debug" set config=Debug&goto arg-ok -if /i "%1"=="release" set config=Release&goto arg-ok -if /i "%1"=="clean" set target=Clean&goto arg-ok -if /i "%1"=="ia32" set target_arch=ia32&goto arg-ok -if /i "%1"=="x86" set target_arch=ia32&goto arg-ok -if /i "%1"=="x64" set target_arch=x64&goto arg-ok -if /i "%1"=="noprojgen" set noprojgen=1&goto arg-ok -if /i "%1"=="nobuild" set nobuild=1&goto arg-ok -if /i "%1"=="nosign" set nosign=1&goto arg-ok -if /i "%1"=="nosnapshot" set nosnapshot=1&goto arg-ok -if /i "%1"=="test-uv" set test=test-uv&goto arg-ok -if /i "%1"=="test-internet"set test=test-internet&goto arg-ok -if /i "%1"=="test-pummel" set test=test-pummel&goto arg-ok -if /i "%1"=="test-simple" set test=test-simple&goto arg-ok -if /i "%1"=="test-message" set test=test-message&goto arg-ok -if /i "%1"=="test-all" set test=test-all&goto arg-ok -if /i "%1"=="test" set test=test&goto arg-ok -if /i "%1"=="msi" set msi=1&goto arg-ok -if /i "%1"=="upload" set upload=1&goto arg-ok - +if /i "%1"=="debug" set config=Debug&goto arg-ok +if /i "%1"=="release" set config=Release&goto arg-ok +if /i "%1"=="clean" set target=Clean&goto arg-ok +if /i "%1"=="ia32" set target_arch=ia32&goto arg-ok +if /i "%1"=="x86" set target_arch=ia32&goto arg-ok +if /i "%1"=="x64" set target_arch=x64&goto arg-ok +if /i "%1"=="noprojgen" set noprojgen=1&goto arg-ok +if /i "%1"=="nobuild" set nobuild=1&goto arg-ok +if /i "%1"=="nosign" set nosign=1&goto arg-ok +if /i "%1"=="nosnapshot" set nosnapshot=1&goto arg-ok +if /i "%1"=="test-uv" set test=test-uv&goto arg-ok +if /i "%1"=="test-internet" set test=test-internet&goto arg-ok +if /i "%1"=="test-pummel" set test=test-pummel&goto arg-ok +if /i "%1"=="test-simple" set test=test-simple&goto arg-ok +if /i "%1"=="test-message" set test=test-message&goto arg-ok +if /i "%1"=="test-all" set test=test-all&goto arg-ok +if /i "%1"=="test" set test=test&goto arg-ok +if /i "%1"=="msi" set msi=1&goto arg-ok +if /i "%1"=="upload" set upload=1&goto arg-ok + +echo Warning: ignoring invalid command line option `%1`. :arg-ok shift From 2e6ad6204758042ca715ed0acfa88c2c292074ff Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Sun, 12 Feb 2012 21:07:31 +0100 Subject: [PATCH 02/27] Add libuv test that was omitted in last libuv upgrade --- deps/uv/test/test-udp-multicast-ttl.c | 89 +++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 deps/uv/test/test-udp-multicast-ttl.c diff --git a/deps/uv/test/test-udp-multicast-ttl.c b/deps/uv/test/test-udp-multicast-ttl.c new file mode 100644 index 0000000000..e54583c13f --- /dev/null +++ b/deps/uv/test/test-udp-multicast-ttl.c @@ -0,0 +1,89 @@ +/* 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 + * 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 "uv.h" +#include "task.h" + +#include +#include +#include + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + +static uv_udp_t server; +static uv_udp_t client; + +static int cl_recv_cb_called; + +static int sv_send_cb_called; + +static int close_cb_called; + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + close_cb_called++; +} + + +static void sv_send_cb(uv_udp_send_t* req, int status) { + ASSERT(req != NULL); + ASSERT(status == 0); + CHECK_HANDLE(req->handle); + + sv_send_cb_called++; + + uv_close((uv_handle_t*) req->handle, close_cb); +} + + +TEST_IMPL(udp_multicast_ttl) { + int r; + uv_udp_send_t req; + uv_buf_t buf; + struct sockaddr_in addr = uv_ip4_addr("239.255.0.1", TEST_PORT); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + r = uv_udp_bind(&server, uv_ip4_addr("0.0.0.0", 0), 0); + ASSERT(r == 0); + + r = uv_udp_set_multicast_ttl(&server, 32); + ASSERT(r == 0); + + /* server sends "PING" */ + buf = uv_buf_init("PING", 4); + r = uv_udp_send(&req, &server, &buf, 1, addr, sv_send_cb); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + ASSERT(sv_send_cb_called == 0); + + /* run the loop till all events are processed */ + uv_run(uv_default_loop()); + + ASSERT(sv_send_cb_called == 1); + ASSERT(close_cb_called == 1); + + return 0; +} From ef032cbe85b46584304c665b539b4f7561c4c26c Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Mon, 13 Feb 2012 20:55:29 +0100 Subject: [PATCH 03/27] Windows: support non-ansi command line arguments --- src/node.cc | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/node.cc b/src/node.cc index 9ac528a85d..f6ae19da92 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2045,12 +2045,40 @@ Handle SetupProcessObject(int argc, char *argv[]) { process->Set(String::NewSymbol("platform"), String::New(PLATFORM)); // process.argv +#ifdef _WIN32 + // Windows - use unicode + WCHAR* command_line = GetCommandLineW(); + if (command_line == NULL) { + winapi_perror("GetCommandLineW"); + exit(1); + } + + int wargc = 0; + WCHAR** wargv = CommandLineToArgvW(command_line, &wargc); + if (wargv == NULL || wargc <= 0) { + winapi_perror("CommandLineToArgvW"); + exit(1); + } + + assert(wargc == argc); + + Local arguments = Array::New(wargc - option_end_index + 1); + arguments->Set(Integer::New(0), String::New(reinterpret_cast(wargv[0]))); + for (j = 1, i = option_end_index; i < wargc; j++, i++) { + Local arg = String::New(reinterpret_cast(wargv[i])); + arguments->Set(Integer::New(j), arg); + } + + LocalFree(wargv); +#else + // Unix Local arguments = Array::New(argc - option_end_index + 1); arguments->Set(Integer::New(0), String::New(argv[0])); for (j = 1, i = option_end_index; i < argc; j++, i++) { Local arg = String::New(argv[i]); arguments->Set(Integer::New(j), arg); } +#endif // assign it process->Set(String::NewSymbol("argv"), arguments); From 1ce14eca44c7c3bf9812b462e1931de58d09edc3 Mon Sep 17 00:00:00 2001 From: Seth Fitzsimmons Date: Mon, 13 Feb 2012 19:30:09 -0800 Subject: [PATCH 04/27] dgram: handle close of dgram socket before DNS lookup completes --- lib/dgram.js | 2 +- test/simple/test-dgram-close.js | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 test/simple/test-dgram-close.js diff --git a/lib/dgram.js b/lib/dgram.js index b5b0473de8..8a88364bb5 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -175,7 +175,7 @@ Socket.prototype.send = function(buffer, if (callback) callback(err); self.emit('error', err); } - else { + else if (self._handle) { var req = self._handle.send(buffer, offset, length, port, ip); if (req) { req.oncomplete = afterSend; diff --git a/test/simple/test-dgram-close.js b/test/simple/test-dgram-close.js new file mode 100644 index 0000000000..58d7aa786c --- /dev/null +++ b/test/simple/test-dgram-close.js @@ -0,0 +1,35 @@ +// 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. + +// Ensure that if a dgram socket is closed before the DNS lookup completes, it +// won't crash. + +var assert = require('assert'), + common = require('../common'), + dgram = require('dgram'); + +var buf = new Buffer(1024); +buf.fill(42); + +var socket = dgram.createSocket('udp4'); + +socket.send(buf, 0, buf.length, common.port, 'localhost'); +socket.close(); From b19b8836c3bd9d8775103cddd68e7247a16e0a7f Mon Sep 17 00:00:00 2001 From: koichik Date: Mon, 9 Jan 2012 02:28:49 +0100 Subject: [PATCH 05/27] tls: Allow establishing secure connection on the existing socket --- doc/api/tls.markdown | 5 ++ lib/tls.js | 6 +- test/simple/test-tls-connect-given-socket.js | 59 ++++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 test/simple/test-tls-connect-given-socket.js diff --git a/doc/api/tls.markdown b/doc/api/tls.markdown index f0343b3a0a..40ab6275ed 100644 --- a/doc/api/tls.markdown +++ b/doc/api/tls.markdown @@ -130,6 +130,11 @@ defaults to `localhost`.) `options` should be an object which specifies - `servername`: Servername for SNI (Server Name Indication) TLS extension. + - `socket`: Establish secure connection on a given socket rather than + creating a new socket. If this option is specified, `host` and `port` + are ignored. This is intended FOR INTERNAL USE ONLY. As with all + undocumented APIs in Node, they should not be used. + The `secureConnectListener` parameter will be added as a listener for the ['secureConnect'](#event_secureConnect_) event. diff --git a/lib/tls.js b/lib/tls.js index 0dd1782350..9d11a42a3c 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -1030,7 +1030,7 @@ exports.connect = function(port /* host, options, cb */) { } } - var socket = new net.Stream(); + var socket = options.socket ? options.socket : new net.Stream(); var sslcontext = crypto.createCredentials(options); @@ -1050,7 +1050,9 @@ exports.connect = function(port /* host, options, cb */) { cleartext.on('secureConnect', cb); } - socket.connect(port, host); + if (!options.socket) { + socket.connect(port, host); + } pair.on('secure', function() { var verifyError = pair.ssl.verifyError(); diff --git a/test/simple/test-tls-connect-given-socket.js b/test/simple/test-tls-connect-given-socket.js new file mode 100644 index 0000000000..9a5f7f1c03 --- /dev/null +++ b/test/simple/test-tls-connect-given-socket.js @@ -0,0 +1,59 @@ +// 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 tls = require('tls'); +var net = require('net'); +var fs = require('fs'); +var path = require('path'); + +var serverConnected = false; +var clientConnected = false; + +var options = { + key: fs.readFileSync(path.join(common.fixturesDir, 'test_key.pem')), + cert: fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')) +}; + +var server = tls.createServer(options, function(socket) { + serverConnected = true; + socket.end('Hello'); +}).listen(common.PORT, function() { + var socket = net.connect(common.PORT, function() { + var client = tls.connect(0, {socket: socket}, function() { + clientConnected = true; + var data = ''; + client.on('data', function(chunk) { + data += chunk.toString(); + }); + client.on('end', function() { + assert.equal(data, 'Hello'); + server.close(); + }); + }); + }); +}); + +process.on('exit', function() { + assert(serverConnected); + assert(clientConnected); +}); From 14b20ffc3006eaebb08840601a1330e5204c290c Mon Sep 17 00:00:00 2001 From: Igor Zinkovsky Date: Tue, 14 Feb 2012 00:03:13 -0800 Subject: [PATCH 06/27] add tls-over-http-tunnel test --- test/simple/test-tls-over-http-tunnel.js | 156 +++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 test/simple/test-tls-over-http-tunnel.js diff --git a/test/simple/test-tls-over-http-tunnel.js b/test/simple/test-tls-over-http-tunnel.js new file mode 100644 index 0000000000..d2b8257e92 --- /dev/null +++ b/test/simple/test-tls-over-http-tunnel.js @@ -0,0 +1,156 @@ +// 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. + + + + +if (!process.versions.openssl) { + console.error('Skipping because node compiled without OpenSSL.'); + process.exit(0); +} + +var common = require('../common'); +var assert = require('assert'); + +var fs = require('fs'); +var net = require('net'); +var http = require('http'); +var https = require('https'); + +var proxyPort = common.PORT + 1; +var gotRequest = false; + +var key = fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'); +var cert = fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem'); + +var options = { + key: key, + cert: cert +}; + +var server = https.createServer(options, function(req, res) { + console.log('SERVER: got request'); + res.writeHead(200, { + 'content-type': 'text/plain', + }); + console.log('SERVER: sending response'); + res.end('hello world\n'); +}); + +var proxy = net.createServer(function(clientSocket) { + console.log('PROXY: got a client connection'); + + var serverSocket = null; + + clientSocket.on('data', function(chunk) { + if (!serverSocket) { + // Verify the CONNECT request + assert.equal('CONNECT localhost:' + common.PORT + ' HTTP/1.1\r\n' + + 'Proxy-Connections: keep-alive\r\nContent-Length:' + + ' 0\r\nHost: localhost:' + proxyPort + '\r\n\r\n', + chunk); + + console.log('PROXY: got CONNECT request'); + console.log('PROXY: creating a tunnel'); + + // create the tunnel + serverSocket = net.connect(common.PORT, function() { + console.log('PROXY: replying to client CONNECT request'); + + // Send the response + clientSocket.write('HTTP/1.1 200 OK\r\nProxy-Connections: keep' + + '-alive\r\nConnections: keep-alive\r\nVia: ' + + 'localhost:' + proxyPort + '\r\n\r\n'); + }); + + serverSocket.on('data', function(chunk) { + clientSocket.write(chunk); + }); + + serverSocket.on('end', function() { + clientSocket.destroy(); + }); + } else { + serverSocket.write(chunk); + } + }); + + clientSocket.on('end', function() { + serverSocket.destroy(); + }); +}); + +server.listen(common.PORT); + +proxy.listen(proxyPort, function() { + console.log('CLIENT: Making CONNECT request'); + + http.request({ + port: proxyPort, + method: 'CONNECT', + path: 'localhost:' + common.PORT, + headers: { + 'Proxy-Connections': 'keep-alive', + 'Content-Length': 0 + } + }, function(res) { + assert.equal(200, res.statusCode); + console.log('CLIENT: got CONNECT response'); + + // detach the socket + res.socket.emit('agentRemove'); + res.socket.removeAllListeners('data'); + res.socket.removeAllListeners('close'); + res.socket.removeAllListeners('error'); + res.socket.removeAllListeners('drain'); + res.socket.removeAllListeners('end'); + res.socket.ondata = null; + res.socket.onend = null; + res.socket.ondrain = null; + + console.log('CLIENT: Making HTTPS request'); + + https.get({ + path: '/foo', + key: key, + cert: cert, + socket: res.socket, // reuse the socket + agent: false, + }, function(res) { + assert.equal(200, res.statusCode); + + res.on('data', function(chunk) { + assert.equal('hello world\n', chunk); + console.log('CLIENT: got HTTPS response'); + gotRequest = true; + }); + + res.on('end', function() { + proxy.close(); + server.close(); + }); + }).end(); + }).end(); +}); + +process.on('exit', function() { + assert.ok(gotRequest); +}); From 0685707bc643250de297b59f4f58878d4c17292e Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 15 Feb 2012 16:45:02 +0100 Subject: [PATCH 07/27] tcp, pipe: don't assert on uv_accept() errors It's possible for a new connection to be closed in the window between the accept() syscall and the call to uv_accept(). Deal with it and move on, don't assert. --- src/pipe_wrap.cc | 5 +---- src/tcp_wrap.cc | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc index 6c3887d84f..c99fe47397 100644 --- a/src/pipe_wrap.cc +++ b/src/pipe_wrap.cc @@ -204,10 +204,7 @@ void PipeWrap::OnConnection(uv_stream_t* handle, int status) { PipeWrap* client_wrap = static_cast(client_obj->GetPointerFromInternalField(0)); - int r = uv_accept(handle, (uv_stream_t*)&client_wrap->handle_); - - // uv_accept should always work. - assert(r == 0); + if (uv_accept(handle, (uv_stream_t*)&client_wrap->handle_)) return; // Successful accept. Call the onconnection callback in JavaScript land. Local argv[1] = { client_obj }; diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index 688d7d7190..14e6d3ed66 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -366,10 +366,7 @@ void TCPWrap::OnConnection(uv_stream_t* handle, int status) { TCPWrap* client_wrap = static_cast(client_obj->GetPointerFromInternalField(0)); - int r = uv_accept(handle, (uv_stream_t*)&client_wrap->handle_); - - // uv_accept should always work. - assert(r == 0); + if (uv_accept(handle, (uv_stream_t*)&client_wrap->handle_)) return; // Successful accept. Call the onconnection callback in JavaScript land. argv[0] = client_obj; From 077f9d7293468ad5446b330999fe47bc40e47571 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 15 Feb 2012 23:34:18 +0100 Subject: [PATCH 08/27] Windows: use unicode environment --- src/node.cc | 130 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 94 insertions(+), 36 deletions(-) diff --git a/src/node.cc b/src/node.cc index f6ae19da92..d429a9c7f2 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1296,7 +1296,8 @@ static Handle CwdForDrive(const Arguments& args) { env_key[1] = (WCHAR) drive; DWORD len = GetEnvironmentVariableW(env_key, NULL, 0); - if (len == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { + if (len == 0 && (GetLastError() == ERROR_ENVVAR_NOT_FOUND || + GetLastError() == ERROR_SUCCESS)) { // There is no current directory for that drive. Default to drive + ":\". Local cwd = String::Concat(String::New(&drive, 1), String::New(":\\")); @@ -1317,7 +1318,7 @@ static Handle CwdForDrive(const Arguments& args) { } DWORD len2 = GetEnvironmentVariableW(env_key, buffer, len); - if (len2 == 0 || len2 >= len) { + if ((len2 == 0 && GetLastError() != ERROR_SUCCESS) || len2 >= len) { // Error delete[] buffer; Local exception = Exception::Error( @@ -1869,12 +1870,28 @@ static void ProcessTitleSetter(Local property, static Handle EnvGetter(Local property, const AccessorInfo& info) { + HandleScope scope; +#ifdef __POSIX__ String::Utf8Value key(property); const char* val = getenv(*key); if (val) { - HandleScope scope; return scope.Close(String::New(val)); } +#else // _WIN32 + String::Value key(property); + WCHAR buffer[32767]; // The maximum size allowed for environment variables. + DWORD result = GetEnvironmentVariableW(reinterpret_cast(*key), + buffer, + ARRAY_SIZE(buffer)); + // If result >= sizeof buffer the buffer was too small. That should never + // happen. If result == 0 and result != ERROR_SUCCESS the variable was not + // not found. + if ((result > 0 || GetLastError() == ERROR_SUCCESS) && + result < ARRAY_SIZE(buffer)) { + return scope.Close(String::New(reinterpret_cast(buffer), result)); + } +#endif + // Not found return Undefined(); } @@ -1883,66 +1900,82 @@ static Handle EnvSetter(Local property, Local value, const AccessorInfo& info) { HandleScope scope; +#ifdef __POSIX__ String::Utf8Value key(property); String::Utf8Value val(value); - -#ifdef __POSIX__ setenv(*key, *val, 1); -#else // __WIN32__ - int n = key.length() + val.length() + 2; - char* pair = new char[n]; - snprintf(pair, n, "%s=%s", *key, *val); - int r = _putenv(pair); - if (r) { - fprintf(stderr, "error putenv: '%s'\n", pair); +#else // _WIN32 + String::Value key(property); + String::Value val(value); + WCHAR* key_ptr = reinterpret_cast(*key); + // Environment variables that start with '=' are read-only. + if (key_ptr[0] != L'=') { + SetEnvironmentVariableW(key_ptr, reinterpret_cast(*val)); } - delete [] pair; #endif - - return value; + // Whether it worked or not, always return rval. + return scope.Close(value); } static Handle EnvQuery(Local property, const AccessorInfo& info) { + HandleScope scope; +#ifdef __POSIX__ String::Utf8Value key(property); if (getenv(*key)) { - HandleScope scope; return scope.Close(Integer::New(None)); } - return Handle(); +#else // _WIN32 + String::Value key(property); + WCHAR* key_ptr = reinterpret_cast(*key); + if (GetEnvironmentVariableW(key_ptr, NULL, 0) > 0 || + GetLastError() == ERROR_SUCCESS) { + if (key_ptr[0] == L'=') { + // Environment variables that start with '=' are hidden and read-only. + return scope.Close(Integer::New(v8::ReadOnly || + v8::DontDelete || + v8::DontEnum)); + } else { + return scope.Close(Integer::New(None)); + } + } +#endif + // Not found + return scope.Close(Handle()); } static Handle EnvDeleter(Local property, const AccessorInfo& info) { HandleScope scope; - - String::Utf8Value key(property); - - if (getenv(*key)) { #ifdef __POSIX__ - unsetenv(*key); // prototyped as `void unsetenv(const char*)` on some platforms + String::Utf8Value key(property); + // prototyped as `void unsetenv(const char*)` on some platforms + if (unsetenv(*key) < 0) { + // Deletion failed. Return true if the key wasn't there in the first place, + // false if it is still there. + return scope.Close(Boolean::New(getenv(*key) == NULL)); + }; #else - int n = key.length() + 2; - char* pair = new char[n]; - snprintf(pair, n, "%s=", *key); - int r = _putenv(pair); - if (r) { - fprintf(stderr, "error unsetenv: '%s'\n", pair); - } - delete [] pair; -#endif - return True(); + String::Value key(property); + WCHAR* key_ptr = reinterpret_cast(*key); + if (key_ptr[0] == L'=' || !SetEnvironmentVariableW(key_ptr, NULL)) { + // Deletion failed. Return true if the key wasn't there in the first place, + // false if it is still there. + bool rv = GetEnvironmentVariableW(key_ptr, NULL, NULL) == 0 && + GetLastError() != ERROR_SUCCESS; + return scope.Close(Boolean::New(rv)); } - - return False(); +#endif + // It worked + return v8::True(); } static Handle EnvEnumerator(const AccessorInfo& info) { HandleScope scope; - +#ifdef __POSIX__ int size = 0; while (environ[size]) size++; @@ -1954,7 +1987,32 @@ static Handle EnvEnumerator(const AccessorInfo& info) { const int length = s ? s - var : strlen(var); env->Set(i, String::New(var, length)); } - +#else // _WIN32 + WCHAR* environment = GetEnvironmentStringsW(); + if (environment == NULL) { + // This should not happen. + return scope.Close(Handle()); + } + Local env = Array::New(); + WCHAR* p = environment; + int i = 0; + while (*p != NULL) { + WCHAR *s; + if (*p == L'=') { + // If the key starts with '=' it is a hidden environment variable. + p += wcslen(p) + 1; + continue; + } else { + s = wcschr(p, L'='); + } + if (!s) { + s = p + wcslen(p); + } + env->Set(i++, String::New(reinterpret_cast(p), s - p)); + p = s + wcslen(s) + 1; + } + FreeEnvironmentStringsW(environment); +#endif return scope.Close(env); } From d91bc7cb0977b058750183199906590d95003fc0 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 15 Feb 2012 23:50:04 +0100 Subject: [PATCH 09/27] Windows: get rid of process._cwdForDrive() --- lib/path.js | 8 +++++- src/node.cc | 75 ----------------------------------------------------- 2 files changed, 7 insertions(+), 76 deletions(-) diff --git a/lib/path.js b/lib/path.js index 87451e9375..5d69d2270f 100644 --- a/lib/path.js +++ b/lib/path.js @@ -96,7 +96,13 @@ if (isWindows) { // directories. If we've resolved a drive letter but not yet an // absolute path, get cwd for that drive. We're sure the device is not // an unc path at this points, because unc paths are always absolute. - path = process._cwdForDrive(resolvedDevice[0]); + path = process.env['=' + resolvedDevice]; + // Verify that a drive-local cwd was found and that it actually points + // to our drive. If not, default to the drive's root. + if (!path || path.slice(0, 3).toLowerCase() !== + resolvedDevice.toLowerCase() + '\\') { + path = resolvedDevice + '\\'; + } } // Skip empty and invalid entries diff --git a/src/node.cc b/src/node.cc index d429a9c7f2..6cbfd02748 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1262,77 +1262,6 @@ static Handle Cwd(const Arguments& args) { } -#ifdef _WIN32 -static Handle CwdForDrive(const Arguments& args) { - HandleScope scope; - - if (args.Length() < 1) { - Local exception = Exception::Error( - String::New("process._cwdForDrive takes exactly 1 argument.")); - return ThrowException(exception); - } - - Local driveLetter = args[0]->ToString(); - if (driveLetter->Length() != 1) { - Local exception = Exception::Error( - String::New("Drive name should be 1 character.")); - return ThrowException(exception); - } - - char drive; - - driveLetter->WriteAscii(&drive, 0, 1, 0); - if (drive >= 'a' && drive <= 'z') { - // Convert to uppercase - drive += 'A' - 'a'; - } else if (drive < 'A' || drive > 'Z') { - // Not a letter - Local exception = Exception::Error( - String::New("Drive name should be a letter.")); - return ThrowException(exception); - } - - WCHAR env_key[] = L"=X:"; - env_key[1] = (WCHAR) drive; - - DWORD len = GetEnvironmentVariableW(env_key, NULL, 0); - if (len == 0 && (GetLastError() == ERROR_ENVVAR_NOT_FOUND || - GetLastError() == ERROR_SUCCESS)) { - // There is no current directory for that drive. Default to drive + ":\". - Local cwd = String::Concat(String::New(&drive, 1), - String::New(":\\")); - return scope.Close(cwd); - - } else if (len == 0) { - // Error - Local exception = Exception::Error( - String::New(winapi_strerror(GetLastError()))); - return ThrowException(exception); - } - - WCHAR* buffer = new WCHAR[len]; - if (buffer == NULL) { - Local exception = Exception::Error( - String::New("Out of memory.")); - return ThrowException(exception); - } - - DWORD len2 = GetEnvironmentVariableW(env_key, buffer, len); - if ((len2 == 0 && GetLastError() != ERROR_SUCCESS) || len2 >= len) { - // Error - delete[] buffer; - Local exception = Exception::Error( - String::New(winapi_strerror(GetLastError()))); - return ThrowException(exception); - } - - Local cwd = String::New(reinterpret_cast(buffer), len2); - delete[] buffer; - return scope.Close(cwd); -} -#endif - - static Handle Umask(const Arguments& args) { HandleScope scope; unsigned int old; @@ -2177,10 +2106,6 @@ Handle SetupProcessObject(int argc, char *argv[]) { NODE_SET_METHOD(process, "chdir", Chdir); NODE_SET_METHOD(process, "cwd", Cwd); -#ifdef _WIN32 - NODE_SET_METHOD(process, "_cwdForDrive", CwdForDrive); -#endif - NODE_SET_METHOD(process, "umask", Umask); #ifdef __POSIX__ From ef50bd2e541e3d62ed4ca91aa960b34c39516560 Mon Sep 17 00:00:00 2001 From: koichik Date: Fri, 17 Feb 2012 02:02:02 +0900 Subject: [PATCH 10/27] docs: removed unnecessary STARTTLS section --- doc/api/tls.markdown | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/doc/api/tls.markdown b/doc/api/tls.markdown index 40ab6275ed..a3cd4542f2 100644 --- a/doc/api/tls.markdown +++ b/doc/api/tls.markdown @@ -169,17 +169,6 @@ Here is an example of a client of echo server as described previously: }); -### STARTTLS - -In the v0.4 branch no function exists for starting a TLS session on an -already existing TCP connection. This is possible it just requires a bit of -work. The technique is to use `tls.createSecurePair()` which returns two -streams: an encrypted stream and a cleartext stream. The encrypted stream is -then piped to the socket, the cleartext stream is what the user interacts with -thereafter. - -[Here is some code that does it.](http://gist.github.com/848444) - ### NPN and SNI NPN (Next Protocol Negotiation) and SNI (Server Name Indication) are TLS From 3415427dbfbd590bbee3d777ffc73dde3c67790d Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 15 Feb 2012 19:26:43 +0100 Subject: [PATCH 11/27] tls: mitigate session renegotiation attacks The TLS protocol allows (and sometimes requires) clients to renegotiate the session. However, renegotiation requires a disproportional amount of server-side resources, particularly CPU time, which makes it a potential vector for denial-of-service attacks. To mitigate this issue, we keep track of and limit the number of renegotiation requests over time, emitting an error if the threshold is exceeded. --- doc/api/tls.markdown | 21 +++++ lib/tls.js | 52 ++++++++++++ src/node_crypto.cc | 16 ++++ src/node_crypto.h | 2 + test/pummel/test-tls-ci-reneg-attack.js | 100 ++++++++++++++++++++++++ 5 files changed, 191 insertions(+) create mode 100644 test/pummel/test-tls-ci-reneg-attack.js diff --git a/doc/api/tls.markdown b/doc/api/tls.markdown index a3cd4542f2..f3222170c3 100644 --- a/doc/api/tls.markdown +++ b/doc/api/tls.markdown @@ -26,6 +26,27 @@ Alternatively you can send the CSR to a Certificate Authority for signing. (TODO: docs on creating a CA, for now interested users should just look at `test/fixtures/keys/Makefile` in the Node source code) +### Client-initiated renegotiation attack mitigation + +The TLS protocol lets the client renegotiate certain aspects of the TLS session. +Unfortunately, session renegotiation requires a disproportional amount of +server-side resources, which makes it a potential vector for denial-of-service +attacks. + +To mitigate this, renegotiations are limited to three times every 10 minutes. An +error is emitted on the [CleartextStream](#tls.CleartextStream) instance when +the threshold is exceeded. The limits are configurable: + + - `tls.CLIENT_RENEG_LIMIT`: renegotiation limit, default is 3. + + - `tls.CLIENT_RENEG_WINDOW`: renegotiation window in seconds, default is + 10 minutes. + +Don't change the defaults unless you know what you are doing. + +To test your server, connect to it with `openssl s_client -connect address:port` +and tap `R` (that's the letter `R` followed by a carriage return) a few +times. #### tls.createServer(options, [secureConnectionListener]) diff --git a/lib/tls.js b/lib/tls.js index 9d11a42a3c..f9bb63c6a1 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -27,6 +27,14 @@ var stream = require('stream'); var END_OF_FILE = 42; var assert = require('assert').ok; +// Allow {CLIENT_RENEG_LIMIT} client-initiated session renegotiations +// every {CLIENT_RENEG_WINDOW} seconds. An error event is emitted if more +// renegotations are seen. The settings are applied to all remote client +// connections. +exports.CLIENT_RENEG_LIMIT = 3; +exports.CLIENT_RENEG_WINDOW = 600; + + var debug; if (process.env.NODE_DEBUG && /tls/.test(process.env.NODE_DEBUG)) { debug = function(a) { console.error('TLS:', a); }; @@ -542,6 +550,37 @@ EncryptedStream.prototype._pusher = function(pool, offset, length) { }; +function onhandshakestart() { + debug('onhandshakestart'); + + var self = this, ssl = this.ssl; + ssl.handshakes++; + + if (ssl.handshakes === 1) { + function timeout() { + ssl.handshakes = 0; + ssl.timer = null; + } + ssl.timer = setTimeout(timeout, exports.CLIENT_RENEG_WINDOW * 1000); + } + else if (ssl.handshakes >= exports.CLIENT_RENEG_LIMIT) { + // Defer the error event to the next tick. We're being called from OpenSSL's + // state machine and OpenSSL is not re-entrant. We cannot allow the user's + // callback to destroy the connection right now, it would crash and burn. + process.nextTick(function() { + var err = new Error('TLS session renegotiation attack detected.'); + if (self.cleartext) self.cleartext.emit('error', err); + }); + } +} + + +function onhandshakedone() { + // for future use + debug('onhandshakedone'); +} + + /** * Provides a pair of streams to do encrypted communication. */ @@ -588,6 +627,13 @@ function SecurePair(credentials, isServer, requestCert, rejectUnauthorized, this._isServer ? this._requestCert : options.servername, this._rejectUnauthorized); + if (this._isServer) { + this.ssl.onhandshakestart = onhandshakestart.bind(this); + this.ssl.onhandshakedone = onhandshakedone.bind(this); + this.ssl.handshakes = 0; + this.ssl.timer = null; + } + if (process.features.tls_sni) { if (this._isServer && options.SNICallback) { this.ssl.setSNICallback(options.SNICallback); @@ -727,6 +773,12 @@ SecurePair.prototype.destroy = function() { if (!this._doneFlag) { this._doneFlag = true; + + if (this.ssl.timer) { + clearTimeout(this.ssl.timer); + this.ssl.timer = null; + } + this.ssl.error = null; this.ssl.close(); this.ssl = null; diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 5274120e2c..cf43cc6504 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -929,6 +929,8 @@ Handle Connection::New(const Arguments& args) { SSL_set_app_data(p->ssl_, p); + if (is_server) SSL_set_info_callback(p->ssl_, SSLInfoCallback); + #ifdef OPENSSL_NPN_NEGOTIATED if (is_server) { // Server should advertise NPN protocols @@ -991,6 +993,20 @@ Handle Connection::New(const Arguments& args) { } +void Connection::SSLInfoCallback(const SSL *ssl, int where, int ret) { + if (where & SSL_CB_HANDSHAKE_START) { + HandleScope scope; + Connection* c = static_cast(SSL_get_app_data(ssl)); + MakeCallback(c->handle_, "onhandshakestart", 0, NULL); + } + if (where & SSL_CB_HANDSHAKE_DONE) { + HandleScope scope; + Connection* c = static_cast(SSL_get_app_data(ssl)); + MakeCallback(c->handle_, "onhandshakedone", 0, NULL); + } +} + + Handle Connection::EncIn(const Arguments& args) { HandleScope scope; diff --git a/src/node_crypto.h b/src/node_crypto.h index 4cde964da4..87a5340147 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -190,6 +190,8 @@ class Connection : ObjectWrap { } private: + static void SSLInfoCallback(const SSL *ssl, int where, int ret); + BIO *bio_read_; BIO *bio_write_; SSL *ssl_; diff --git a/test/pummel/test-tls-ci-reneg-attack.js b/test/pummel/test-tls-ci-reneg-attack.js new file mode 100644 index 0000000000..5d04a6b6f3 --- /dev/null +++ b/test/pummel/test-tls-ci-reneg-attack.js @@ -0,0 +1,100 @@ +// 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 spawn = require('child_process').spawn; +var tls = require('tls'); +var fs = require('fs'); + +// renegotiation limits to test +var LIMITS = [0, 1, 2, 3, 5, 10, 16]; + +if (process.platform === 'win32') { + console.log("Skipping test, you probably don't have openssl installed."); + process.exit(); +} + +(function() { + var n = 0; + function next() { + if (n >= LIMITS.length) return; + tls.CLIENT_RENEG_LIMIT = LIMITS[n++]; + test(next); + } + next(); +})(); + +function test(next) { + var options = { + cert: fs.readFileSync(common.fixturesDir + '/test_cert.pem'), + key: fs.readFileSync(common.fixturesDir + '/test_key.pem') + }; + + var server = tls.createServer(options, function(conn) { + conn.on('error', function(err) { + console.error('Caught exception: ' + err); + assert(/TLS session renegotiation attack/.test(err)); + conn.destroy(); + }); + conn.pipe(conn); + }); + + server.listen(common.PORT, function() { + var args = ('s_client -connect 127.0.0.1:' + common.PORT).split(' '); + var child = spawn('openssl', args); + + child.stdout.pipe(process.stdout); + child.stderr.pipe(process.stderr); + + // count handshakes, start the attack after the initial handshake is done + var handshakes = 0; + child.stderr.on('data', function(data) { + handshakes += (('' + data).match(/verify return:1/g) || []).length; + if (handshakes === 2) spam(); + }); + + child.on('exit', function() { + // with a renegotiation limit <= 1, we always see 4 handshake markers: + // two for the initial handshake and another two for the attempted + // renegotiation + assert.equal(handshakes, 2 * Math.max(2, tls.CLIENT_RENEG_LIMIT)); + server.close(); + process.nextTick(next); + }); + + var closed = false; + child.stdin.on('error', function(err) { + assert.equal(err.code, 'EPIPE'); + closed = true; + }); + child.stdin.on('close', function() { + closed = true; + }); + + // simulate renegotiation attack + function spam() { + if (closed) return; + child.stdin.write("R\n"); + setTimeout(spam, 250); + } + }); +} From 9364699be194cf9175d1bc421050b17ac9875447 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 16 Feb 2012 20:05:18 +0100 Subject: [PATCH 12/27] Revert "Windows: support non-ansi command line arguments" CommandLineToArgvW doesn't behave exactly the same as the crt, which makes it useless. This reverts commit ef032cbe85b46584304c665b539b4f7561c4c26c. --- src/node.cc | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/src/node.cc b/src/node.cc index 6cbfd02748..652e30a7ab 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2032,40 +2032,12 @@ Handle SetupProcessObject(int argc, char *argv[]) { process->Set(String::NewSymbol("platform"), String::New(PLATFORM)); // process.argv -#ifdef _WIN32 - // Windows - use unicode - WCHAR* command_line = GetCommandLineW(); - if (command_line == NULL) { - winapi_perror("GetCommandLineW"); - exit(1); - } - - int wargc = 0; - WCHAR** wargv = CommandLineToArgvW(command_line, &wargc); - if (wargv == NULL || wargc <= 0) { - winapi_perror("CommandLineToArgvW"); - exit(1); - } - - assert(wargc == argc); - - Local arguments = Array::New(wargc - option_end_index + 1); - arguments->Set(Integer::New(0), String::New(reinterpret_cast(wargv[0]))); - for (j = 1, i = option_end_index; i < wargc; j++, i++) { - Local arg = String::New(reinterpret_cast(wargv[i])); - arguments->Set(Integer::New(j), arg); - } - - LocalFree(wargv); -#else - // Unix Local arguments = Array::New(argc - option_end_index + 1); arguments->Set(Integer::New(0), String::New(argv[0])); for (j = 1, i = option_end_index; i < argc; j++, i++) { Local arg = String::New(argv[i]); arguments->Set(Integer::New(j), arg); } -#endif // assign it process->Set(String::NewSymbol("argv"), arguments); From d52f5020cecc16349cce902e9f869d33a9213752 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 16 Feb 2012 20:20:07 +0100 Subject: [PATCH 13/27] Windows: another attempt to support unicode argv --- node.gyp | 1 + src/node_main.cc | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/node.gyp b/node.gyp index 75cf4f20cb..766788bd6e 100644 --- a/node.gyp +++ b/node.gyp @@ -160,6 +160,7 @@ 'FD_SETSIZE=1024', # we need to use node's preferred "win32" rather than gyp's preferred "win" 'PLATFORM="win32"', + '_UNICODE=1', ], 'libraries': [ '-lpsapi.lib' ] },{ # POSIX diff --git a/src/node_main.cc b/src/node_main.cc index 3100149868..dba8b69203 100644 --- a/src/node_main.cc +++ b/src/node_main.cc @@ -21,6 +21,47 @@ #include +#ifdef _WIN32 +int wmain(int argc, wchar_t *wargv[]) { + // Convert argv to to UTF8 + char** argv = new char*[argc]; + for (int i = 0; i < argc; i++) { + // Compute the size of the required buffer + DWORD size = WideCharToMultiByte(CP_UTF8, + 0, + wargv[i], + -1, + NULL, + 0, + NULL, + NULL); + if (size == 0) { + // This should never happen. + fprintf(stderr, "Could not convert arguments to utf8."); + exit(1); + } + // Do the actual conversion + argv[i] = new char[size]; + DWORD result = WideCharToMultiByte(CP_UTF8, + 0, + wargv[i], + -1, + argv[i], + size, + NULL, + NULL); + if (result == 0) { + // This should never happen. + fprintf(stderr, "Could not convert arguments to utf8."); + exit(1); + } + } + // Now that conversion is done, we can finally start. + return node::Start(argc, argv); +} +#else +// UNIX int main(int argc, char *argv[]) { return node::Start(argc, argv); } +#endif From 8f2694bb5380a0ae80e7503cf2fb9507d83573b6 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 16 Feb 2012 22:19:48 +0100 Subject: [PATCH 14/27] Make win32 ansi api usage explicit Use widechar versions in a couple of places. Don't use C-style cast in C++ code. --- src/node.cc | 42 ++++++++++++++++--------------- src/platform_win32.cc | 57 ++++++++++++++++++++++++++----------------- 2 files changed, 57 insertions(+), 42 deletions(-) diff --git a/src/node.cc b/src/node.cc index 652e30a7ab..d7c26ffcd1 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2361,13 +2361,14 @@ DWORD WINAPI EnableDebugThreadProc(void* arg) { } -static int GetDebugSignalHandlerMappingName(DWORD pid, char* buf, size_t buf_len) { - return snprintf(buf, buf_len, "node-debug-handler-%u", pid); +static int GetDebugSignalHandlerMappingName(DWORD pid, wchar_t* buf, + size_t buf_len) { + return _snwprintf(buf, buf_len, L"node-debug-handler-%u", pid); } static int RegisterDebugSignalHandler() { - char mapping_name[32]; + wchar_t mapping_name[32]; HANDLE mapping_handle; DWORD pid; LPTHREAD_START_ROUTINE* handler; @@ -2376,11 +2377,11 @@ static int RegisterDebugSignalHandler() { if (GetDebugSignalHandlerMappingName(pid, mapping_name, - sizeof mapping_name) < 0) { + ARRAY_SIZE(mapping_name)) < 0) { return -1; } - mapping_handle = CreateFileMappingA(INVALID_HANDLE_VALUE, + mapping_handle = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, @@ -2390,11 +2391,12 @@ static int RegisterDebugSignalHandler() { return -1; } - handler = (LPTHREAD_START_ROUTINE*) MapViewOfFile(mapping_handle, - FILE_MAP_ALL_ACCESS, - 0, - 0, - sizeof *handler); + handler = reinterpret_cast( + MapViewOfFile(mapping_handle, + FILE_MAP_ALL_ACCESS, + 0, + 0, + sizeof *handler)); if (handler == NULL) { CloseHandle(mapping_handle); return -1; @@ -2415,7 +2417,7 @@ static Handle DebugProcess(const Arguments& args) { HANDLE process = NULL; HANDLE thread = NULL; HANDLE mapping = NULL; - char mapping_name[32]; + wchar_t mapping_name[32]; LPTHREAD_START_ROUTINE* handler = NULL; if (args.Length() != 1) { @@ -2437,22 +2439,24 @@ static Handle DebugProcess(const Arguments& args) { if (GetDebugSignalHandlerMappingName(pid, mapping_name, - sizeof mapping_name) < 0) { + ARRAY_SIZE(mapping_name)) < 0) { rv = ThrowException(ErrnoException(errno, "sprintf")); goto out; } - mapping = OpenFileMapping(FILE_MAP_READ, FALSE, mapping_name); + mapping = OpenFileMappingW(FILE_MAP_READ, FALSE, mapping_name); if (mapping == NULL) { - rv = ThrowException(WinapiErrnoException(GetLastError(), "sprintf")); + rv = ThrowException(WinapiErrnoException(GetLastError(), + "OpenFileMappingW")); goto out; } - handler = (LPTHREAD_START_ROUTINE*) MapViewOfFile(mapping, - FILE_MAP_READ, - 0, - 0, - sizeof *handler); + handler = reinterpret_cast( + MapViewOfFile(mapping, + FILE_MAP_READ, + 0, + 0, + sizeof *handler)); if (handler == NULL || *handler == NULL) { rv = ThrowException(WinapiErrnoException(GetLastError(), "MapViewOfFile")); goto out; diff --git a/src/platform_win32.cc b/src/platform_win32.cc index 65add1124a..c84131a701 100644 --- a/src/platform_win32.cc +++ b/src/platform_win32.cc @@ -48,9 +48,10 @@ double Platform::prog_start_time = Platform::GetUptime(); const char *winapi_strerror(const int errorno) { char *errmsg = NULL; - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errmsg, 0, NULL); + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&errmsg), 0, NULL); if (errmsg) { // Remove trailing newlines @@ -72,9 +73,10 @@ void winapi_perror(const char* prefix = NULL) { DWORD errorno = GetLastError(); const char *errmsg = NULL; - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errmsg, 0, NULL); + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&errmsg), 0, NULL); if (!errmsg) { errmsg = "Unknown error\n"; @@ -203,8 +205,7 @@ int Platform::GetMemory(size_t *rss) { HANDLE current_process = GetCurrentProcess(); PROCESS_MEMORY_COUNTERS pmc; - if ( !GetProcessMemoryInfo( current_process, &pmc, sizeof(pmc)) ) - { + if (!GetProcessMemoryInfo(current_process, &pmc, sizeof(pmc))) { winapi_perror("GetProcessMemoryInfo"); } @@ -220,40 +221,49 @@ int Platform::GetCPUInfo(Local *cpus) { for (int i = 0; i < 32; i++) { - char key[128] = "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\"; - char processor_number[32]; - itoa(i, processor_number, 10); - strncat(key, processor_number, 2); + wchar_t key[128] = L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\"; + wchar_t processor_number[32]; + _itow(i, processor_number, 10); + wcsncat(key, processor_number, 2); HKEY processor_key = NULL; DWORD cpu_speed = 0; DWORD cpu_speed_length = sizeof(cpu_speed); - char cpu_brand[256]; + wchar_t cpu_brand[256]; DWORD cpu_brand_length = sizeof(cpu_brand); - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_QUERY_VALUE, - &processor_key) != ERROR_SUCCESS) { + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, + key, + 0, + KEY_QUERY_VALUE, + &processor_key) != ERROR_SUCCESS) { if (i == 0) { - winapi_perror("RegOpenKeyEx"); + winapi_perror("RegOpenKeyExW"); return -1; } continue; } - if (RegQueryValueEx(processor_key, "~MHz", NULL, NULL, - (LPBYTE)&cpu_speed, &cpu_speed_length) - != ERROR_SUCCESS) { - winapi_perror("RegQueryValueEx"); + if (RegQueryValueExW(processor_key, + L"~MHz", + NULL, + NULL, + reinterpret_cast(&cpu_speed), + &cpu_speed_length) != ERROR_SUCCESS) { + winapi_perror("RegQueryValueExW"); return -1; } - if (RegQueryValueEx(processor_key, "ProcessorNameString", NULL, NULL, - (LPBYTE)&cpu_brand, &cpu_brand_length) - != ERROR_SUCCESS) { - winapi_perror("RegQueryValueEx"); + if (RegQueryValueExW(processor_key, + L"ProcessorNameString", + NULL, + NULL, + reinterpret_cast(&cpu_brand), + &cpu_brand_length) != ERROR_SUCCESS) { + winapi_perror("RegQueryValueExW"); return -1; } @@ -267,7 +277,8 @@ int Platform::GetCPUInfo(Local *cpus) { times_info->Set(String::New("irq"), Integer::New(0)); Local cpu_info = Object::New(); - cpu_info->Set(String::New("model"), String::New(cpu_brand)); + cpu_info->Set(String::New("model"), + String::New(reinterpret_cast(cpu_brand))); cpu_info->Set(String::New("speed"), Integer::New(cpu_speed)); cpu_info->Set(String::New("times"), times_info); (*cpus)->Set(i,cpu_info); From 2c07712860398f863670c5b90fb4f4559d299e19 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 15 Feb 2012 00:20:54 +0100 Subject: [PATCH 15/27] http: allow multiple WWW-Authenticate headers --- lib/http.js | 1 + test/simple/test-http-server-multiheaders.js | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/lib/http.js b/lib/http.js index 1d7eac32ab..fb92a38c19 100644 --- a/lib/http.js +++ b/lib/http.js @@ -294,6 +294,7 @@ IncomingMessage.prototype._addHeaderLine = function(field, value) { case 'cookie': case 'pragma': case 'link': + case 'www-authenticate': if (field in dest) { dest[field] += ', ' + value; } else { diff --git a/test/simple/test-http-server-multiheaders.js b/test/simple/test-http-server-multiheaders.js index a2930f1d55..9917dd2bba 100644 --- a/test/simple/test-http-server-multiheaders.js +++ b/test/simple/test-http-server-multiheaders.js @@ -30,6 +30,7 @@ var http = require('http'); var srv = http.createServer(function(req, res) { assert.equal(req.headers.accept, 'abc, def, ghijklmnopqrst'); assert.equal(req.headers.host, 'foo'); + assert.equal(req.headers['www-authenticate'], 'foo, bar, baz'); assert.equal(req.headers['x-foo'], 'bingo'); assert.equal(req.headers['x-bar'], 'banjo, bango'); @@ -51,6 +52,9 @@ srv.listen(common.PORT, function() { ['host', 'foo'], ['Host', 'bar'], ['hOst', 'baz'], + ['www-authenticate', 'foo'], + ['WWW-Authenticate', 'bar'], + ['WWW-AUTHENTICATE', 'baz'], ['x-foo', 'bingo'], ['x-bar', 'banjo'], ['x-bar', 'bango'] From 83fd1c1de51e6b0171101a725c8d92081c2fadbd Mon Sep 17 00:00:00 2001 From: einaros Date: Thu, 16 Feb 2012 10:42:13 +0100 Subject: [PATCH 16/27] Add WebSocket RFC6455 multiheader fields to the http parser. --- lib/http.js | 2 ++ test/simple/test-http-server-multiheaders.js | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/http.js b/lib/http.js index fb92a38c19..4f7aa0a00a 100644 --- a/lib/http.js +++ b/lib/http.js @@ -295,6 +295,8 @@ IncomingMessage.prototype._addHeaderLine = function(field, value) { case 'pragma': case 'link': case 'www-authenticate': + case 'sec-websocket-extensions': + case 'sec-websocket-protocol': if (field in dest) { dest[field] += ', ' + value; } else { diff --git a/test/simple/test-http-server-multiheaders.js b/test/simple/test-http-server-multiheaders.js index 9917dd2bba..a94ea27321 100644 --- a/test/simple/test-http-server-multiheaders.js +++ b/test/simple/test-http-server-multiheaders.js @@ -33,6 +33,8 @@ var srv = http.createServer(function(req, res) { assert.equal(req.headers['www-authenticate'], 'foo, bar, baz'); assert.equal(req.headers['x-foo'], 'bingo'); assert.equal(req.headers['x-bar'], 'banjo, bango'); + assert.equal(req.headers['sec-websocket-protocol'], 'chat, share'); + assert.equal(req.headers['sec-websocket-extensions'], 'foo; 1, bar; 2, baz'); res.writeHead(200, {'Content-Type' : 'text/plain'}); res.end('EOF'); @@ -57,7 +59,12 @@ srv.listen(common.PORT, function() { ['WWW-AUTHENTICATE', 'baz'], ['x-foo', 'bingo'], ['x-bar', 'banjo'], - ['x-bar', 'bango'] + ['x-bar', 'bango'], + ['sec-websocket-protocol', 'chat'], + ['sec-websocket-protocol', 'share'], + ['sec-websocket-extensions', 'foo; 1'], + ['sec-websocket-extensions', 'bar; 2'], + ['sec-websocket-extensions', 'baz'] ] }); }); From 30e462e91937ced3847af3fe9c393ebd32294b68 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 10 Feb 2012 20:26:56 +0100 Subject: [PATCH 17/27] cluster: propagate bind errors This commit fixes a bug where the cluster module failed to propagate EADDRINUSE errors. When a worker starts a (net, http) server, it requests the listen socket from its master who then creates and binds the socket. Now, OS X and Windows don't always signal EADDRINUSE from bind() but instead defer the error until a later syscall. libuv mimics this behaviour to provide consistent behaviour across platforms but that means the worker could end up with a socket that is not actually bound to the requested addresss. That's why the worker now checks if the socket is bound, raising EADDRINUSE if that's not the case. Fixes #2721. --- lib/net.js | 24 +++- ...twice.js => test-cluster-bind-twice-v1.js} | 0 test/simple/test-cluster-bind-twice-v2.js | 115 ++++++++++++++++++ 3 files changed, 133 insertions(+), 6 deletions(-) rename test/simple/{test-cluster-bind-twice.js => test-cluster-bind-twice-v1.js} (100%) create mode 100644 test/simple/test-cluster-bind-twice-v2.js diff --git a/lib/net.js b/lib/net.js index e41e96ae1d..a797ed6fed 100644 --- a/lib/net.js +++ b/lib/net.js @@ -756,14 +756,26 @@ Server.prototype._listen2 = function(address, port, addressType) { function listen(self, address, port, addressType) { - if (process.env.NODE_WORKER_ID) { - require('cluster')._getServer(address, port, addressType, function(handle) { - self._handle = handle; - self._listen2(address, port, addressType); - }); - } else { + if (!process.env.NODE_WORKER_ID) { self._listen2(address, port, addressType); + return; } + + require('cluster')._getServer(address, port, addressType, function(handle) { + // OS X doesn't necessarily signal EADDRINUSE from bind(), it may defer + // the error until later. libuv mimics this behaviour to provide + // consistent behaviour across platforms but that means we could very + // well have a socket that is not actually bound... that's why we do + // this ghetto port check and raise EADDRINUSE if the requested and the + // actual port differ except if port == 0 because that means "any port". + if (port && port != handle.getsockname().port) { + self.emit('error', errnoException('EADDRINUSE', 'bind')); + return; + } + + self._handle = handle; + self._listen2(address, port, addressType); + }); } diff --git a/test/simple/test-cluster-bind-twice.js b/test/simple/test-cluster-bind-twice-v1.js similarity index 100% rename from test/simple/test-cluster-bind-twice.js rename to test/simple/test-cluster-bind-twice-v1.js diff --git a/test/simple/test-cluster-bind-twice-v2.js b/test/simple/test-cluster-bind-twice-v2.js new file mode 100644 index 0000000000..1842cce260 --- /dev/null +++ b/test/simple/test-cluster-bind-twice-v2.js @@ -0,0 +1,115 @@ +// 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. + +// This test starts two clustered HTTP servers on the same port. It expects the +// first cluster to succeed and the second cluster to fail with EADDRINUSE. +// +// The test may seem complex but most of it is plumbing that routes messages +// from the child processes back to the "super" master. As a tree it looks +// something like this: +// +// +// / \ +// +// / \ +// +// +// The first worker starts a server on a fixed port and fires a ready message +// that is routed to the second worker. When it tries to bind, it expects to +// see an EADDRINUSE error. +// +// See https://github.com/joyent/node/issues/2721 for more details. + +var common = require('../common'); +var assert = require('assert'); +var cluster = require('cluster'); +var fork = require('child_process').fork; +var http = require('http'); + +var id = process.argv[2]; + +if (!id) { + var a = fork(__filename, ['one']); + var b = fork(__filename, ['two']); + + a.on('message', function(m) { + if (typeof m === 'object') return; + assert.equal(m, 'READY'); + b.send('START'); + }); + + var ok = false; + + b.on('message', function(m) { + if (typeof m === 'object') return; // ignore system messages + assert.equal(m, 'EADDRINUSE'); + a.kill(); + b.kill(); + ok = true; + }); + + process.on('exit', function() { + a.kill(); + b.kill(); + assert(ok); + }); +} +else if (id === 'one') { + if (cluster.isMaster) return startWorker(); + + http.createServer(assert.fail).listen(common.PORT, function() { + process.send('READY'); + }); +} +else if (id === 'two') { + if (cluster.isMaster) return startWorker(); + + var ok = false; + process.on('SIGTERM', process.exit); + process.on('exit', function() { + assert(ok); + }); + + process.on('message', function(m) { + if (typeof m === 'object') return; // ignore system messages + assert.equal(m, 'START'); + var server = http.createServer(assert.fail); + server.listen(common.PORT, assert.fail); + server.on('error', function(e) { + assert.equal(e.code, 'EADDRINUSE'); + process.send(e.code); + ok = true; + }); + }); +} +else { + assert(0); // bad command line argument +} + +function startWorker() { + var worker = cluster.fork(); + worker.on('message', process.send); + process.on('message', worker.send.bind(worker)); + process.on('SIGTERM', function() { + worker.kill(); + process.exit(); + }); +} From 7a1a62ec6e168876493688cfa45097567a332267 Mon Sep 17 00:00:00 2001 From: isaacs Date: Thu, 16 Feb 2012 14:52:03 -0800 Subject: [PATCH 18/27] Upgrade uv to 86ebe48660e --- deps/uv/include/uv-private/uv-win.h | 2 +- deps/uv/src/unix/tty.c | 4 +- deps/uv/src/win/error.c | 8 ++- deps/uv/src/win/fs.c | 83 +++++++++++++++-------------- deps/uv/test/test-fs.c | 2 +- 5 files changed, 53 insertions(+), 46 deletions(-) diff --git a/deps/uv/include/uv-private/uv-win.h b/deps/uv/include/uv-private/uv-win.h index ea770132dd..e620f8b084 100644 --- a/deps/uv/include/uv-private/uv-win.h +++ b/deps/uv/include/uv-private/uv-win.h @@ -388,7 +388,7 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); #define UV_FS_PRIVATE_FIELDS \ wchar_t* pathw; \ int flags; \ - int last_error; \ + DWORD sys_errno_; \ struct _stati64 stat; \ void* arg0; \ union { \ diff --git a/deps/uv/src/unix/tty.c b/deps/uv/src/unix/tty.c index 18a892168f..c1429660eb 100644 --- a/deps/uv/src/unix/tty.c +++ b/deps/uv/src/unix/tty.c @@ -76,8 +76,8 @@ int uv_tty_set_mode(uv_tty_t* tty, int mode) { raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; - /* Put terminal in raw mode after flushing */ - if (tcsetattr(fd, TCSAFLUSH, &raw)) { + /* Put terminal in raw mode after draining */ + if (tcsetattr(fd, TCSADRAIN, &raw)) { goto fatal; } diff --git a/deps/uv/src/win/error.c b/deps/uv/src/win/error.c index dd0018daa6..1922f2039b 100644 --- a/deps/uv/src/win/error.c +++ b/deps/uv/src/win/error.c @@ -84,6 +84,8 @@ uv_err_code uv_translate_sys_error(int sys_errno) { case WSAECONNREFUSED: return UV_ECONNREFUSED; case ERROR_NETNAME_DELETED: return UV_ECONNRESET; case WSAECONNRESET: return UV_ECONNRESET; + case ERROR_ALREADY_EXISTS: return UV_EEXIST; + case ERROR_FILE_EXISTS: return UV_EEXIST; case WSAEFAULT: return UV_EFAULT; case ERROR_HOST_UNREACHABLE: return UV_EHOSTUNREACH; case WSAEHOSTUNREACH: return UV_EHOSTUNREACH; @@ -93,6 +95,7 @@ uv_err_code uv_translate_sys_error(int sys_errno) { case ERROR_TOO_MANY_OPEN_FILES: return UV_EMFILE; case WSAEMFILE: return UV_EMFILE; case WSAEMSGSIZE: return UV_EMSGSIZE; + case ERROR_FILENAME_EXCED_RANGE: return UV_ENAMETOOLONG; case ERROR_NETWORK_UNREACHABLE: return UV_ENETUNREACH; case WSAENETUNREACH: return UV_ENETUNREACH; case WSAENOBUFS: return UV_ENOBUFS; @@ -105,9 +108,12 @@ uv_err_code uv_translate_sys_error(int sys_errno) { case ERROR_INVALID_PARAMETER: return UV_EINVAL; case ERROR_NO_UNICODE_TRANSLATION: return UV_ECHARSET; case ERROR_BROKEN_PIPE: return UV_EOF; + case ERROR_BAD_PIPE: return UV_EPIPE; + case ERROR_NO_DATA: return UV_EPIPE; + case ERROR_PIPE_NOT_CONNECTED: return UV_EPIPE; case ERROR_PIPE_BUSY: return UV_EBUSY; case ERROR_SEM_TIMEOUT: return UV_ETIMEDOUT; - case ERROR_ALREADY_EXISTS: return UV_EEXIST; + case WSAETIMEDOUT: return UV_ETIMEDOUT; case WSAHOST_NOT_FOUND: return UV_ENOENT; default: return UV_UNKNOWN; } diff --git a/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c index 12ad514c48..94da291930 100644 --- a/deps/uv/src/win/fs.c +++ b/deps/uv/src/win/fs.c @@ -38,7 +38,6 @@ #define UV_FS_FREE_ARG1 0x0004 #define UV_FS_FREE_PTR 0x0008 #define UV_FS_CLEANEDUP 0x0010 -#define UV_FS_LAST_ERROR_SET 0x0020 #define UTF8_TO_UTF16(s, t) \ @@ -88,33 +87,30 @@ uv_ref((loop)); #define SET_UV_LAST_ERROR_FROM_REQ(req) \ - if (req->flags & UV_FS_LAST_ERROR_SET) { \ - uv__set_sys_error(req->loop, req->last_error); \ - } else if (req->result == -1) { \ - uv__set_error(req->loop, (uv_err_code)req->errorno, req->last_error); \ - } - -#define SET_REQ_LAST_ERROR(req, error) \ - req->last_error = error; \ - req->flags |= UV_FS_LAST_ERROR_SET; + uv__set_error(req->loop, req->errorno, req->sys_errno_); #define SET_REQ_RESULT(req, result_value) \ req->result = (result_value); \ if (req->result == -1) { \ - req->last_error = _doserrno; \ - req->errorno = uv_translate_sys_error(req->last_error); \ + req->sys_errno_ = _doserrno; \ + req->errorno = uv_translate_sys_error(req->sys_errno_); \ } -#define SET_REQ_RESULT_WIN32_ERROR(req, sys_errno) \ +#define SET_REQ_WIN32_ERROR(req, sys_errno) \ + req->result = -1; \ + req->sys_errno_ = (sys_errno); \ + req->errorno = uv_translate_sys_error(req->sys_errno_); + +#define SET_REQ_UV_ERROR(req, uv_errno, sys_errno) \ req->result = -1; \ - req->errorno = uv_translate_sys_error(sys_errno); \ - SET_REQ_LAST_ERROR(req, sys_errno); + req->sys_errno_ = (sys_errno); \ + req->errorno = (uv_errno); #define VERIFY_UV_FILE(file, req) \ if (file == -1) { \ req->result = -1; \ req->errorno = UV_EBADF; \ - req->last_error = ERROR_SUCCESS; \ + req->sys_errno_ = ERROR_SUCCESS; \ return; \ } @@ -137,7 +133,7 @@ static void uv_fs_req_init_async(uv_loop_t* loop, uv_fs_t* req, req->path = path ? strdup(path) : NULL; req->pathw = (wchar_t*)pathw; req->errorno = 0; - req->last_error = 0; + req->sys_errno_ = 0; memset(&req->overlapped, 0, sizeof(req->overlapped)); } @@ -266,7 +262,15 @@ void fs__open(uv_fs_t* req, const wchar_t* path, int flags, int mode) { attributes, NULL); if (file == INVALID_HANDLE_VALUE) { - SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + DWORD error = GetLastError(); + if (error == ERROR_FILE_EXISTS && (flags & _O_CREAT) && + !(flags & _O_EXCL)) { + /* Special case: when ERROR_FILE_EXISTS happens and O_CREAT was */ + /* specified, it means the path referred to a directory. */ + SET_REQ_UV_ERROR(req, UV_EISDIR, error); + } else { + SET_REQ_WIN32_ERROR(req, GetLastError()); + } return; } result = _open_osfhandle((intptr_t)file, flags); @@ -300,7 +304,7 @@ void fs__read(uv_fs_t* req, uv_file file, void *buf, size_t length, } if (length > INT_MAX) { - SET_REQ_ERROR(req, ERROR_INSUFFICIENT_BUFFER); + SET_REQ_WIN32_ERROR(req, ERROR_INSUFFICIENT_BUFFER); return; } @@ -319,7 +323,7 @@ void fs__read(uv_fs_t* req, uv_file file, void *buf, size_t length, if (ReadFile(handle, buf, length, &bytes, overlapped_ptr)) { SET_REQ_RESULT(req, bytes); } else { - SET_REQ_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, GetLastError()); } } @@ -340,7 +344,7 @@ void fs__write(uv_fs_t* req, uv_file file, void *buf, size_t length, } if (length > INT_MAX) { - SET_REQ_ERROR(req, ERROR_INSUFFICIENT_BUFFER); + SET_REQ_WIN32_ERROR(req, ERROR_INSUFFICIENT_BUFFER); return; } @@ -359,7 +363,7 @@ void fs__write(uv_fs_t* req, uv_file file, void *buf, size_t length, if (WriteFile(handle, buf, length, &bytes, overlapped_ptr)) { SET_REQ_RESULT(req, bytes); } else { - SET_REQ_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, GetLastError()); } } @@ -398,7 +402,7 @@ void fs__readdir(uv_fs_t* req, const wchar_t* path, int flags) { if (!(GetFileAttributesW(path) & FILE_ATTRIBUTE_DIRECTORY)) { req->result = -1; req->errorno = UV_ENOTDIR; - req->last_error = ERROR_SUCCESS; + req->sys_errno_ = ERROR_SUCCESS; return; } @@ -416,7 +420,7 @@ void fs__readdir(uv_fs_t* req, const wchar_t* path, int flags) { free(path2); if(dir == INVALID_HANDLE_VALUE) { - SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, GetLastError()); return; } @@ -460,7 +464,7 @@ void fs__readdir(uv_fs_t* req, const wchar_t* path, int flags) { /* Convert result to UTF8. */ size = uv_utf16_to_utf8(buf, buf_char_len, NULL, 0); if (!size) { - SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, GetLastError()); return; } @@ -474,7 +478,7 @@ void fs__readdir(uv_fs_t* req, const wchar_t* path, int flags) { free(buf); free(req->ptr); req->ptr = NULL; - SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, GetLastError()); return; } free(buf); @@ -491,7 +495,6 @@ void fs__readdir(uv_fs_t* req, const wchar_t* path, int flags) { static void fs__stat(uv_fs_t* req, const wchar_t* path) { HANDLE handle; - int result; BY_HANDLE_FILE_INFORMATION info; req->ptr = NULL; @@ -504,12 +507,12 @@ static void fs__stat(uv_fs_t* req, const wchar_t* path) { FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle == INVALID_HANDLE_VALUE) { - SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, GetLastError()); return; } if (!GetFileInformationByHandle(handle, &info)) { - SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, GetLastError()); CloseHandle(handle); return; } @@ -565,7 +568,7 @@ void fs__fstat(uv_fs_t* req, uv_file file) { void fs__rename(uv_fs_t* req, const wchar_t* path, const wchar_t* new_path) { if (!MoveFileExW(path, new_path, MOVEFILE_REPLACE_EXISTING)) { - SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, GetLastError()); return; } @@ -580,7 +583,7 @@ void fs__fsync(uv_fs_t* req, uv_file file) { result = FlushFileBuffers((HANDLE)_get_osfhandle(file)) ? 0 : -1; if (result == -1) { - SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, GetLastError()); } else { SET_REQ_RESULT(req, result); } @@ -711,7 +714,7 @@ void fs__futime(uv_fs_t* req, uv_file file, double atime, double mtime) { void fs__link(uv_fs_t* req, const wchar_t* path, const wchar_t* new_path) { int result = CreateHardLinkW(new_path, path, NULL) ? 0 : -1; if (result == -1) { - SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, GetLastError()); } else { SET_REQ_RESULT(req, result); } @@ -726,13 +729,11 @@ void fs__symlink(uv_fs_t* req, const wchar_t* path, const wchar_t* new_path, path, flags & UV_FS_SYMLINK_DIR ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) ? 0 : -1; if (result == -1) { - SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, GetLastError()); return; } } else { - req->result = -1; - req->errorno = UV_ENOTSUP; - req->last_error = ERROR_SUCCESS; + SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED); return; } @@ -761,7 +762,7 @@ void fs__readlink(uv_fs_t* req, const wchar_t* path) { if (INVALID_HANDLE_VALUE == symlink) { result = -1; - SET_REQ_LAST_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, GetLastError()); goto done; } @@ -781,7 +782,7 @@ void fs__readlink(uv_fs_t* req, const wchar_t* path) { if (!rv) { result = -1; - SET_REQ_LAST_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, GetLastError()); goto done; } @@ -789,7 +790,7 @@ void fs__readlink(uv_fs_t* req, const wchar_t* path) { if (reparse_data->ReparseTag != IO_REPARSE_TAG_SYMLINK) { result = -1; /* something is seriously wrong */ - SET_REQ_LAST_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, GetLastError()); goto done; } @@ -810,7 +811,7 @@ void fs__readlink(uv_fs_t* req, const wchar_t* path) { 0); if (!utf8size) { result = -1; - SET_REQ_LAST_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, GetLastError()); goto done; } @@ -825,7 +826,7 @@ void fs__readlink(uv_fs_t* req, const wchar_t* path) { utf8size); if (!utf8size) { result = -1; - SET_REQ_LAST_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, GetLastError()); goto done; } diff --git a/deps/uv/test/test-fs.c b/deps/uv/test/test-fs.c index 006968829d..af2ebbf5bc 100644 --- a/deps/uv/test/test-fs.c +++ b/deps/uv/test/test-fs.c @@ -45,7 +45,7 @@ # define close _close #endif -#define TOO_LONG_NAME_LENGTH 8192 +#define TOO_LONG_NAME_LENGTH 65536 typedef struct { const char* path; From a118f2172880d338101f80647437b8832ac1f768 Mon Sep 17 00:00:00 2001 From: Nathan Rajlich Date: Thu, 16 Feb 2012 16:33:40 -0800 Subject: [PATCH 19/27] repl: make tab completion work on non-objects --- lib/repl.js | 7 ++++++- test/simple/test-repl-tab-complete.js | 9 +++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/repl.js b/lib/repl.js index 08697ab72e..20fa7aefc7 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -523,8 +523,13 @@ REPLServer.prototype.complete = function(line, callback) { } // works for non-objects try { - var p = Object.getPrototypeOf(obj); var sentinel = 5; + var p; + if (typeof obj == 'object') { + p = Object.getPrototypeOf(obj); + } else { + p = obj.constructor ? obj.constructor.prototype : null; + } while (p !== null) { memberGroups.push(Object.getOwnPropertyNames(p)); p = Object.getPrototypeOf(p); diff --git a/test/simple/test-repl-tab-complete.js b/test/simple/test-repl-tab-complete.js index ba511046b5..0bc43ab196 100644 --- a/test/simple/test-repl-tab-complete.js +++ b/test/simple/test-repl-tab-complete.js @@ -180,3 +180,12 @@ testMe.complete('inner.o', function(error, data) { assert.deepEqual(data, doesNotBreak); }); +putIn.run(['.clear']); + +// make sure tab completion works on non-Objects +putIn.run([ + 'var str = "test";' +]); +testMe.complete('str.len', function(error, data) { + assert.deepEqual(data, [ [ 'str.length' ], 'str.len' ]); +}); From 4ed7b035bd74a24c2a3d2cf15962a31b2ab204c8 Mon Sep 17 00:00:00 2001 From: Shannen Saez Date: Thu, 16 Feb 2012 17:38:05 +1100 Subject: [PATCH 20/27] docs: add lang="en" and remove redundant types --- doc/about/index.html | 2 +- doc/community/index.html | 6 +++--- doc/logos/index.html | 25 ++++++++++--------------- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/doc/about/index.html b/doc/about/index.html index e6d3fdb6ff..398b112d00 100644 --- a/doc/about/index.html +++ b/doc/about/index.html @@ -1,5 +1,5 @@ - + - - - + + - - + + - node.js @@ -88,10 +87,7 @@

Copyright Joyent, Inc., Node.js is a trademark of Joyent, Inc., View License

- - - - - - - + + From 9764bea97db0a615d0baf905ded2760868443d21 Mon Sep 17 00:00:00 2001 From: Shannen Saez Date: Thu, 16 Feb 2012 17:47:23 +1100 Subject: [PATCH 21/27] docs: remove unused javascript includes --- doc/logos/index.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/logos/index.html b/doc/logos/index.html index 41ba8914f6..7f93307d0d 100644 --- a/doc/logos/index.html +++ b/doc/logos/index.html @@ -8,8 +8,6 @@ margin: 0; } - - From 4672872ddde70e079670d2a12079cb303ef41dbb Mon Sep 17 00:00:00 2001 From: isaacs Date: Fri, 17 Feb 2012 10:08:40 -0800 Subject: [PATCH 22/27] Fix #2770 Compile the OS X pkg as ia32 --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 2a6b70b465..1609495749 100644 --- a/Makefile +++ b/Makefile @@ -173,8 +173,8 @@ pkg: $(PKG) $(PKG): -rm -rf $(PKGDIR) - $(WAF) configure --prefix=/usr/local --without-snapshot - DESTDIR=$(PKGDIR) $(WAF) install + $(WAF) configure --prefix=/usr/local --without-snapshot --dest-cpu=ia32 + CFLAGS=-m32 DESTDIR=$(PKGDIR) $(WAF) install $(packagemaker) \ --id "org.nodejs.NodeJS-$(VERSION)" \ --doc tools/osx-pkg.pmdoc \ From a2851b62345c59ab5d41d8e3d0da1bad8a9c59d3 Mon Sep 17 00:00:00 2001 From: isaacs Date: Fri, 17 Feb 2012 10:10:02 -0800 Subject: [PATCH 23/27] Revert "cluster: propagate bind errors" This reverts commit 30e462e91937ced3847af3fe9c393ebd32294b68. --- lib/net.js | 24 ++--- test/simple/test-cluster-bind-twice-v1.js | 77 --------------- test/simple/test-cluster-bind-twice-v2.js | 115 ---------------------- 3 files changed, 6 insertions(+), 210 deletions(-) delete mode 100644 test/simple/test-cluster-bind-twice-v1.js delete mode 100644 test/simple/test-cluster-bind-twice-v2.js diff --git a/lib/net.js b/lib/net.js index a797ed6fed..e41e96ae1d 100644 --- a/lib/net.js +++ b/lib/net.js @@ -756,26 +756,14 @@ Server.prototype._listen2 = function(address, port, addressType) { function listen(self, address, port, addressType) { - if (!process.env.NODE_WORKER_ID) { + if (process.env.NODE_WORKER_ID) { + require('cluster')._getServer(address, port, addressType, function(handle) { + self._handle = handle; + self._listen2(address, port, addressType); + }); + } else { self._listen2(address, port, addressType); - return; } - - require('cluster')._getServer(address, port, addressType, function(handle) { - // OS X doesn't necessarily signal EADDRINUSE from bind(), it may defer - // the error until later. libuv mimics this behaviour to provide - // consistent behaviour across platforms but that means we could very - // well have a socket that is not actually bound... that's why we do - // this ghetto port check and raise EADDRINUSE if the requested and the - // actual port differ except if port == 0 because that means "any port". - if (port && port != handle.getsockname().port) { - self.emit('error', errnoException('EADDRINUSE', 'bind')); - return; - } - - self._handle = handle; - self._listen2(address, port, addressType); - }); } diff --git a/test/simple/test-cluster-bind-twice-v1.js b/test/simple/test-cluster-bind-twice-v1.js deleted file mode 100644 index 068842fa53..0000000000 --- a/test/simple/test-cluster-bind-twice-v1.js +++ /dev/null @@ -1,77 +0,0 @@ -// 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. - -// This test starts two clustered HTTP servers on the same port. It expects the -// first cluster to succeed and the second cluster to fail with EADDRINUSE. - -var common = require('../common'); -var assert = require('assert'); -var cluster = require('cluster'); -var fork = require('child_process').fork; -var http = require('http'); - -var id = process.argv[2]; - -if (!id) { - var a = fork(__filename, ['one']); - var b = fork(__filename, ['two']); - - a.on('message', function(m) { - assert.equal(m, 'READY'); - b.send('START'); - }); - - var ok = false; - - b.on('message', function(m) { - assert.equal(m, 'EADDRINUSE'); - a.kill(); - b.kill(); - ok = true; - }); - - process.on('exit', function() { - a.kill(); - b.kill(); - assert(ok); - }); -} -else if (id === 'one') { - if (cluster.isMaster) cluster.fork(); - http.createServer(assert.fail).listen(common.PORT, function() { - process.send('READY'); - }); -} -else if (id === 'two') { - if (cluster.isMaster) cluster.fork(); - process.on('message', function(m) { - assert.equal(m, 'START'); - var server = http.createServer(assert.fail); - server.listen(common.PORT, assert.fail); - server.on('error', function(e) { - assert.equal(e.code, 'EADDRINUSE'); - process.send(e.code); - }); - }); -} -else { - assert(0); // bad command line argument -} diff --git a/test/simple/test-cluster-bind-twice-v2.js b/test/simple/test-cluster-bind-twice-v2.js deleted file mode 100644 index 1842cce260..0000000000 --- a/test/simple/test-cluster-bind-twice-v2.js +++ /dev/null @@ -1,115 +0,0 @@ -// 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. - -// This test starts two clustered HTTP servers on the same port. It expects the -// first cluster to succeed and the second cluster to fail with EADDRINUSE. -// -// The test may seem complex but most of it is plumbing that routes messages -// from the child processes back to the "super" master. As a tree it looks -// something like this: -// -// -// / \ -// -// / \ -// -// -// The first worker starts a server on a fixed port and fires a ready message -// that is routed to the second worker. When it tries to bind, it expects to -// see an EADDRINUSE error. -// -// See https://github.com/joyent/node/issues/2721 for more details. - -var common = require('../common'); -var assert = require('assert'); -var cluster = require('cluster'); -var fork = require('child_process').fork; -var http = require('http'); - -var id = process.argv[2]; - -if (!id) { - var a = fork(__filename, ['one']); - var b = fork(__filename, ['two']); - - a.on('message', function(m) { - if (typeof m === 'object') return; - assert.equal(m, 'READY'); - b.send('START'); - }); - - var ok = false; - - b.on('message', function(m) { - if (typeof m === 'object') return; // ignore system messages - assert.equal(m, 'EADDRINUSE'); - a.kill(); - b.kill(); - ok = true; - }); - - process.on('exit', function() { - a.kill(); - b.kill(); - assert(ok); - }); -} -else if (id === 'one') { - if (cluster.isMaster) return startWorker(); - - http.createServer(assert.fail).listen(common.PORT, function() { - process.send('READY'); - }); -} -else if (id === 'two') { - if (cluster.isMaster) return startWorker(); - - var ok = false; - process.on('SIGTERM', process.exit); - process.on('exit', function() { - assert(ok); - }); - - process.on('message', function(m) { - if (typeof m === 'object') return; // ignore system messages - assert.equal(m, 'START'); - var server = http.createServer(assert.fail); - server.listen(common.PORT, assert.fail); - server.on('error', function(e) { - assert.equal(e.code, 'EADDRINUSE'); - process.send(e.code); - ok = true; - }); - }); -} -else { - assert(0); // bad command line argument -} - -function startWorker() { - var worker = cluster.fork(); - worker.on('message', process.send); - process.on('message', worker.send.bind(worker)); - process.on('SIGTERM', function() { - worker.kill(); - process.exit(); - }); -} From 1eb1fe32250fc88cb5b0a97cddf3e02be02e3f4a Mon Sep 17 00:00:00 2001 From: isaacs Date: Thu, 16 Feb 2012 16:04:34 -0800 Subject: [PATCH 24/27] 2012.02.17 Version 0.6.11 (stable) * http: allow multiple WebSocket RFC6455 headers (Einar Otto Stangvik) * http: allow multiple WWW-Authenticate headers (Ben Noordhuis) * windows: support unicode argv and environment variables (Bert Belder) * tls: mitigate session renegotiation attacks (Ben Noordhuis) * tcp, pipe: don't assert on uv_accept() errors (Ben Noordhuis) * tls: Allow establishing secure connection on the existing socket (koichik) * dgram: handle close of dgram socket before DNS lookup completes (Seth Fitzsimmons) * windows: Support half-duplex pipes (Igor Zinkovsky) * build: disable omit-frame-pointer on solaris systems (Dave Pacheco) * debugger: fix --debug-brk (Ben Noordhuis) * net: fix large file downloads failing (koichik) * fs: fix ReadStream failure to read from existing fd (Christopher Jeffrey) * net: destroy socket on DNS error (Stefan Rusu) * dtrace: add missing translator (Dave Pacheco) * unix: don't flush tty on switch to raw mode (Ben Noordhuis) * windows: reset brightness when reverting to default text color (Bert Belder) * npm: update to 1.1.1 - Update which, fstream, mkdirp, request, and rimraf - Fix #2123 Set path properly for lifecycle scripts on windows - Mark the root as seen, so we don't recurse into it. Fixes #1838. (Martin Cooper) --- AUTHORS | 5 +++++ ChangeLog | 42 +++++++++++++++++++++++++++++++++++++++- doc/about/index.html | 2 +- doc/community/index.html | 2 +- doc/index.html | 18 ++++++++--------- doc/logos/index.html | 2 +- doc/template.html | 6 +++--- src/node_version.h | 2 +- 8 files changed, 62 insertions(+), 17 deletions(-) diff --git a/AUTHORS b/AUTHORS index 9c2969a614..3afd5f597b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -261,3 +261,8 @@ Brandon Benvie Nicolas LaCasse Dan VerWeire Matthew Fitzsimmons +Paddy Byers +Philip Tellis +Christopher Jeffrey +Seth Fitzsimmons +Einar Otto Stangvik diff --git a/ChangeLog b/ChangeLog index b778f6cb61..f4b5fc56f6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,44 @@ -2012.02.02, Version 0.6.10 (stable) +2012.02.17 Version 0.6.11 (stable) + +* http: allow multiple WebSocket RFC6455 headers (Einar Otto Stangvik) + +* http: allow multiple WWW-Authenticate headers (Ben Noordhuis) + +* windows: support unicode argv and environment variables (Bert Belder) + +* tls: mitigate session renegotiation attacks (Ben Noordhuis) + +* tcp, pipe: don't assert on uv_accept() errors (Ben Noordhuis) + +* tls: Allow establishing secure connection on the existing socket (koichik) + +* dgram: handle close of dgram socket before DNS lookup completes (Seth Fitzsimmons) + +* windows: Support half-duplex pipes (Igor Zinkovsky) + +* build: disable omit-frame-pointer on solaris systems (Dave Pacheco) + +* debugger: fix --debug-brk (Ben Noordhuis) + +* net: fix large file downloads failing (koichik) + +* fs: fix ReadStream failure to read from existing fd (Christopher Jeffrey) + +* net: destroy socket on DNS error (Stefan Rusu) + +* dtrace: add missing translator (Dave Pacheco) + +* unix: don't flush tty on switch to raw mode (Ben Noordhuis) + +* windows: reset brightness when reverting to default text color (Bert Belder) + +* npm: update to 1.1.1 + - Update which, fstream, mkdirp, request, and rimraf + - Fix #2123 Set path properly for lifecycle scripts on windows + - Mark the root as seen, so we don't recurse into it. Fixes #1838. (Martin Cooper) + + +2012.02.02, Version 0.6.10 (stable), 051908e023f87894fa68f5b64d0b99a19a7db01e * Update V8 to 3.6.6.20 diff --git a/doc/about/index.html b/doc/about/index.html index e6d3fdb6ff..9ec091f67e 100644 --- a/doc/about/index.html +++ b/doc/about/index.html @@ -130,7 +130,7 @@ console.log('Server running at http://127.0.0.1:1337/');
  • -

    Copyright 2010 Joyent, Inc, Node.js is a trademark of Joyent, Inc. View license.

    +

    Copyright 2010 Joyent, Inc, Node.js is a trademark of Joyent, Inc. View license.

    diff --git a/doc/community/index.html b/doc/community/index.html index bd15c12678..e410de25ca 100644 --- a/doc/community/index.html +++ b/doc/community/index.html @@ -180,7 +180,7 @@
  • -

    Copyright 2010 Joyent, Inc, Node.js is a trademark of Joyent, Inc. View license.

    +

    Copyright 2010 Joyent, Inc, Node.js is a trademark of Joyent, Inc. View license.

    diff --git a/src/node_version.h b/src/node_version.h index b1260d78a4..1cab68a50b 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -29,7 +29,7 @@ #define NODE_MAJOR_VERSION 0 #define NODE_MINOR_VERSION 6 #define NODE_PATCH_VERSION 11 -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n) From f73f07e1266cce36d41001297438b640cd6bb3b5 Mon Sep 17 00:00:00 2001 From: isaacs Date: Fri, 17 Feb 2012 13:33:58 -0800 Subject: [PATCH 25/27] Now working on 0.6.12 --- src/node_version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node_version.h b/src/node_version.h index 1cab68a50b..2b715b7aff 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -28,8 +28,8 @@ #define NODE_MAJOR_VERSION 0 #define NODE_MINOR_VERSION 6 -#define NODE_PATCH_VERSION 11 -#define NODE_VERSION_IS_RELEASE 1 +#define NODE_PATCH_VERSION 12 +#define NODE_VERSION_IS_RELEASE 0 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n) From 23c4278e0694c6c2cca3dc4f917f9ce74be64f40 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 17 Feb 2012 23:58:42 +0100 Subject: [PATCH 26/27] docs: fix tls markdown --- doc/api/tls.markdown | 62 +++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/doc/api/tls.markdown b/doc/api/tls.markdown index f3222170c3..023c34be64 100644 --- a/doc/api/tls.markdown +++ b/doc/api/tls.markdown @@ -48,7 +48,18 @@ To test your server, connect to it with `openssl s_client -connect address:port` and tap `R` (that's the letter `R` followed by a carriage return) a few times. -#### tls.createServer(options, [secureConnectionListener]) + +### NPN and SNI + +NPN (Next Protocol Negotiation) and SNI (Server Name Indication) are TLS +handshake extensions allowing you: + + * NPN - to use one TLS server for multiple protocols (HTTP, SPDY) + * SNI - to use one TLS server for multiple hostnames with different SSL + certificates. + + +## tls.createServer(options, [secureConnectionListener]) Creates a new [tls.Server](#tls.Server). The `connectionListener` argument is automatically set as a listener for the @@ -127,7 +138,7 @@ You can test this server by connecting to it with `openssl s_client`: openssl s_client -connect 127.0.0.1:8000 -#### tls.connect(port, [host], [options], [secureConnectListener]) +## tls.connect(port, [host], [options], [secureConnectListener]) Creates a new client connection to the given `port` and `host`. (If `host` defaults to `localhost`.) `options` should be an object which specifies @@ -190,16 +201,7 @@ Here is an example of a client of echo server as described previously: }); -### NPN and SNI - -NPN (Next Protocol Negotiation) and SNI (Server Name Indication) are TLS -handshake extensions allowing you: - - * NPN - to use one TLS server for multiple protocols (HTTP, SPDY) - * SNI - to use one TLS server for multiple hostnames with different SSL - certificates. - -### pair = tls.createSecurePair([credentials], [isServer], [requestCert], [rejectUnauthorized]) +## tls.createSecurePair([credentials], [isServer], [requestCert], [rejectUnauthorized]) Creates a new secure pair object with two streams, one of which reads/writes encrypted data, and one reads/writes cleartext data. @@ -221,7 +223,7 @@ and the cleartext one is used as a replacement for the initial encrypted stream. `tls.createSecurePair()` returns a SecurePair object with [cleartext](#tls.CleartextStream) and `encrypted` stream properties. -#### Event: 'secure' +### Event: 'secure' The event is emitted from the SecurePair once the pair has successfully established a secure connection. @@ -230,13 +232,13 @@ Similarly to the checking for the server 'secureConnection' event, pair.cleartext.authorized should be checked to confirm whether the certificate used properly authorized. -### tls.Server +## tls.Server This class is a subclass of `net.Server` and has the same methods on it. Instead of accepting just raw TCP connections, this accepts encrypted connections using TLS or SSL. -#### Event: 'secureConnection' +### Event: 'secureConnection' `function (cleartextStream) {}` @@ -256,7 +258,7 @@ server, you unauthorized connections may be accepted. SNI. -#### Event: 'clientError' +### Event: 'clientError' `function (exception) { }` @@ -264,7 +266,7 @@ When a client connection emits an 'error' event before secure connection is established - it will be forwarded here. -#### server.listen(port, [host], [callback]) +### server.listen(port, [host], [callback]) Begin accepting connections on the specified `port` and `host`. If the `host` is omitted, the server will accept connections directed to any @@ -276,35 +278,35 @@ when the server has been bound. See `net.Server` for more information. -#### server.close() +### server.close() Stops the server from accepting new connections. This function is asynchronous, the server is finally closed when the server emits a `'close'` event. -#### server.address() +### server.address() Returns the bound address and port of the server as reported by the operating system. See [net.Server.address()](net.html#server.address) for more information. -#### server.addContext(hostname, credentials) +### server.addContext(hostname, credentials) Add secure context that will be used if client request's SNI hostname is matching passed `hostname` (wildcards can be used). `credentials` can contain `key`, `cert` and `ca`. -#### server.maxConnections +### server.maxConnections Set this property to reject connections when the server's connection count gets high. -#### server.connections +### server.connections The number of concurrent connections on the server. -### tls.CleartextStream +## tls.CleartextStream This is a stream on top of the *Encrypted* stream that makes it possible to read/write an encrypted data as a cleartext data. @@ -312,7 +314,7 @@ read/write an encrypted data as a cleartext data. This instance implements a duplex [Stream](streams.html#streams) interfaces. It has all the common stream methods and events. -#### Event: 'secureConnect' +### Event: 'secureConnect' `function () {}` @@ -324,17 +326,17 @@ If `cleartextStream.authorized === false` then the error can be found in `cleartextStream.authorizationError`. Also if NPN was used - you can check `cleartextStream.npnProtocol` for negotiated protocol. -#### cleartextStream.authorized +### cleartextStream.authorized A boolean that is `true` if the peer certificate was signed by one of the specified CAs, otherwise `false` -#### cleartextStream.authorizationError +### cleartextStream.authorizationError The reason why the peer's certificate has not been verified. This property becomes available only when `cleartextStream.authorized === false`. -#### cleartextStream.getPeerCertificate() +### cleartextStream.getPeerCertificate() Returns an object representing the peer's certificate. The returned object has some properties corresponding to the field of the certificate. @@ -362,17 +364,17 @@ Example: If the peer does not provide a certificate, it returns `null` or an empty object. -#### cleartextStream.address() +### cleartextStream.address() Returns the bound address and port of the underlying socket as reported by the operating system. Returns an object with two properties, e.g. `{"address":"192.168.57.1", "port":62053}` -#### cleartextStream.remoteAddress +### cleartextStream.remoteAddress The string representation of the remote IP address. For example, `'74.125.127.100'` or `'2001:4860:a005::68'`. -#### cleartextStream.remotePort +### cleartextStream.remotePort The numeric representation of the remote port. For example, `443`. From c1f474010e8e58793408fc1d8303aeb1a01ba735 Mon Sep 17 00:00:00 2001 From: Paul Vorbach Date: Sat, 18 Feb 2012 01:40:26 +0100 Subject: [PATCH 27/27] docs: fix quotation style in the webserver example Replace " by ' --- doc/index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/index.html b/doc/index.html index 5db25391b1..06b04026a9 100644 --- a/doc/index.html +++ b/doc/index.html @@ -157,7 +157,7 @@ var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); -}).listen(1337, "127.0.0.1"); +}).listen(1337, '127.0.0.1'); console.log('Server running at http://127.0.0.1:1337/');

    To run the server, put the code into a file example.js and execute it with the node program:

    @@ -171,11 +171,11 @@ Server running at http://127.0.0.1:1337/ var net = require('net'); var server = net.createServer(function (socket) { - socket.write("Echo server\r\n"); + socket.write('Echo server\r\n'); socket.pipe(socket); }); -server.listen(1337, "127.0.0.1"); +server.listen(1337, '127.0.0.1');