diff --git a/ChangeLog b/ChangeLog index f309a062f1f..07fd8f35e88 100644 --- a/ChangeLog +++ b/ChangeLog @@ -59,6 +59,29 @@ * Bug fixes +2012.02.02, Version 0.6.10 (stable) + +* Update V8 to 3.6.6.20 + +* Add npm msysgit bash shim to msi installer (isaacs) + +* buffers: fix intermittent out of bounds error (Ben Noordhuis) + +* buffers: honor length argument in base64 decoder (Ben Noordhuis) + +* windows: Fix path.exists regression (Bert Belder) + +* Make QueryString.parse run faster (Philip Tellis) + +* http: avoid freeing http-parser objects too early (koichik) + +* timers: add v0.4 compatibility hack (Ben Noordhuis) + +* Proper EPERM error code support (Igor Zinkovsky, Brandon Philips) + +* dgram: Implement udp multicast methods on windows (Bert Belder) + + 2012.01.27, Version 0.6.9 (stable), f19e20d33f57c4d2853aaea7d2724d44f3b0012f * dgram: Bring back missing functionality for Unix (Dan VerWeire, Roman Shtylman, Ben Noordhuis) diff --git a/deps/uv/src/win/error.c b/deps/uv/src/win/error.c index 59c9ea83461..779061031f3 100644 --- a/deps/uv/src/win/error.c +++ b/deps/uv/src/win/error.c @@ -69,7 +69,7 @@ uv_err_code uv_translate_sys_error(int sys_errno) { case ERROR_SUCCESS: return UV_OK; case ERROR_FILE_NOT_FOUND: return UV_ENOENT; case ERROR_PATH_NOT_FOUND: return UV_ENOENT; - case ERROR_ACCESS_DENIED: return UV_EACCES; + case ERROR_ACCESS_DENIED: return UV_EPERM; case ERROR_NOACCESS: return UV_EACCES; case WSAEACCES: return UV_EACCES; case ERROR_ADDRESS_ALREADY_ASSOCIATED: return UV_EADDRINUSE; diff --git a/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c index 5248ac2a99e..12ad514c485 100644 --- a/deps/uv/src/win/fs.c +++ b/deps/uv/src/win/fs.c @@ -489,191 +489,61 @@ void fs__readdir(uv_fs_t* req, const wchar_t* path, int flags) { } -#define IS_SLASH(c) \ - ((wchar_t) c == L'/' || (wchar_t) c == L'\\') -#define IS_COLON(c) \ - ((wchar_t) c == L':') -#define IS_LETTER(c) \ - ((((wchar_t) c >= L'a') && ((wchar_t) c <= L'z')) || \ - (((wchar_t) c >= L'A') && ((wchar_t) c <= L'Z'))) -#define IS_QUESTION(c) \ - ((wchar_t) c == L'?') - - -static int uv__count_slash_separated_words(const wchar_t* pos, - const wchar_t* end, - int limit) { - char last_was_slash = 1, count = 0; - - for (; pos < end; pos++) { - if (IS_SLASH(*pos)) { - /* Don't accept double slashes */ - if (last_was_slash) { - return 0; - } else { - last_was_slash = 1; - } - } else { - if (last_was_slash) { - /* Found a new word */ - count++; - if (count > limit) { - return -1; - } - last_was_slash = 0; - } - } - } - - return count; -} - -/* - * Returns true if the given path is a root directory. The following patterns - * are recognized: - * \ - * c:\ (must have trailing slash) - * \\server\share (trailing slash optional) - * \\?\c: (trailing slash optional) - * \\?\UNC\server\share (trailing slash optional) - */ -static int uv__is_root(const wchar_t* path) { - size_t len = wcslen(path); - - /* Test for \ */ - if (len == 1 && IS_SLASH(path[0])) { - return 1; - } - - if (len < 3) { - return 0; - } - - /* Test for c:\ */ - if (IS_LETTER(path[0]) && IS_COLON(path[1]) && IS_SLASH(path[2])) { - return 1; - } - - if (!IS_SLASH(path[0]) || !IS_SLASH(path[1])) { - return 0; - } - - /* Test for \\server\share */ - if (!IS_QUESTION(path[2])) { - return uv__count_slash_separated_words(path + 2, path + len, 2) == 2; - } - - if (!IS_SLASH(path[3])) { - return 0; - } - - if ((len == 6 || len == 7) && - IS_LETTER(path[4]) && IS_COLON(path[5]) && - (len == 6 || IS_SLASH(path[6]))) { - return 1; - } - - /* Test for \\?\UNC\server\share */ - if (len >= 8 && - (path[4] == L'u' || path[4] == L'U') && - (path[5] == L'n' || path[5] == L'N') && - (path[6] == L'c' || path[6] == L'C') && - IS_SLASH(path[7])) { - return uv__count_slash_separated_words(path + 8, path + len, 2) == 2; - } - - return 0; -} - - -void fs__stat(uv_fs_t* req, const wchar_t* path) { - HANDLE file; - WIN32_FIND_DATAW ent; +static void fs__stat(uv_fs_t* req, const wchar_t* path) { + HANDLE handle; int result; + BY_HANDLE_FILE_INFORMATION info; req->ptr = NULL; - if (uv__is_root(path)) { - /* We can't stat root directories like c:\. _wstati64 can't either, but */ - /* it will make up something reasonable. */ - DWORD drive_type = GetDriveTypeW(path); - if (drive_type == DRIVE_UNKNOWN || drive_type == DRIVE_NO_ROOT_DIR) { - req->last_error = ERROR_PATH_NOT_FOUND; - req->errorno = UV_ENOENT; - req->result = -1; - return; - } - - memset(&req->stat, 0, sizeof req->stat); - - req->stat.st_nlink = 1; - req->stat.st_mode = ((_S_IREAD|_S_IWRITE) + ((_S_IREAD|_S_IWRITE) >> 3) + - ((_S_IREAD|_S_IWRITE) >> 6)) | S_IFDIR; - - req->last_error = ERROR_SUCCESS; - req->errorno = UV_OK; - req->result = 0; - req->ptr = &req->stat; - return; - } - - file = FindFirstFileExW(path, FindExInfoStandard, &ent, - FindExSearchNameMatch, NULL, 0); - - if (file == INVALID_HANDLE_VALUE) { + handle = CreateFileW(path, + FILE_READ_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); + if (handle == INVALID_HANDLE_VALUE) { SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); return; } - FindClose(file); - - if (ent.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && - ent.dwReserved0 == IO_REPARSE_TAG_SYMLINK) { - fs__open(req, path, _O_RDONLY, 0); - if (req->result != -1) { - result = _fstati64(req->result, &req->stat); - _close(req->result); - - if (result != -1) { - req->ptr = &req->stat; - } - - SET_REQ_RESULT(req, result); - } - + if (!GetFileInformationByHandle(handle, &info)) { + SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + CloseHandle(handle); return; } - req->stat.st_ino = 0; - req->stat.st_uid = 0; - req->stat.st_gid = 0; - req->stat.st_mode = 0; - req->stat.st_rdev = 0; - req->stat.st_dev = 0; - req->stat.st_nlink = 1; + memset(&req->stat, 0, sizeof req->stat); - if (ent.dwFileAttributes & FILE_ATTRIBUTE_READONLY ) { + /* TODO: set st_dev and st_ino? */ + + if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { req->stat.st_mode |= (_S_IREAD + (_S_IREAD >> 3) + (_S_IREAD >> 6)); } else { req->stat.st_mode |= ((_S_IREAD|_S_IWRITE) + ((_S_IREAD|_S_IWRITE) >> 3) + ((_S_IREAD|_S_IWRITE) >> 6)); } - if (ent.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { req->stat.st_mode |= _S_IFDIR; } else { req->stat.st_mode |= _S_IFREG; } - uv_filetime_to_time_t(&ent.ftLastWriteTime, &(req->stat.st_mtime)); - uv_filetime_to_time_t(&ent.ftLastAccessTime, &(req->stat.st_atime)); - uv_filetime_to_time_t(&ent.ftCreationTime, &(req->stat.st_ctime)); + uv_filetime_to_time_t(&info.ftLastWriteTime, &(req->stat.st_mtime)); + uv_filetime_to_time_t(&info.ftLastAccessTime, &(req->stat.st_atime)); + uv_filetime_to_time_t(&info.ftCreationTime, &(req->stat.st_ctime)); - req->stat.st_size = ((int64_t)ent.nFileSizeHigh << 32) + - (int64_t)ent.nFileSizeLow; + req->stat.st_size = ((int64_t) info.nFileSizeHigh << 32) + + (int64_t) info.nFileSizeLow; + + req->stat.st_nlink = info.nNumberOfLinks; req->ptr = &req->stat; req->result = 0; + + CloseHandle(handle); } diff --git a/deps/uv/test/test-fs.c b/deps/uv/test/test-fs.c index dbeb2a19c17..109ae014b40 100644 --- a/deps/uv/test/test-fs.c +++ b/deps/uv/test/test-fs.c @@ -1309,6 +1309,19 @@ TEST_IMPL(fs_stat_root) { r = uv_fs_stat(loop, &stat_req, "\\", NULL); ASSERT(r == 0); + r = uv_fs_stat(loop, &stat_req, "..\\..\\..\\..\\..\\..\\..", NULL); + ASSERT(r == 0); + + r = uv_fs_stat(loop, &stat_req, "..", NULL); + ASSERT(r == 0); + + r = uv_fs_stat(loop, &stat_req, "..\\", NULL); + ASSERT(r == 0); + + /* stats the current directory on c: */ + r = uv_fs_stat(loop, &stat_req, "c:", NULL); + ASSERT(r == 0); + r = uv_fs_stat(loop, &stat_req, "c:\\", NULL); ASSERT(r == 0); diff --git a/doc/api/http.markdown b/doc/api/http.markdown index 365f2006044..327eb3ba2d8 100644 --- a/doc/api/http.markdown +++ b/doc/api/http.markdown @@ -154,7 +154,8 @@ no limit will be applied. This object is created internally by a HTTP server -- not by the user -- and passed as the first argument to a `'request'` listener. -This is an `EventEmitter` with the following events: +The request implements the [Readable Stream](streams.html#readable_Stream) +interface. This is an `EventEmitter` with the following events: ### Event: 'data' @@ -167,6 +168,9 @@ argument. The transfer-encoding has been decoded. The body chunk is a string. The body encoding is set with `request.setEncoding()`. +Note that the __data will be lost__ if there is no listener when a +`ServerRequest` emits a `'data'` event. + ### Event: 'end' `function () { }` @@ -271,7 +275,10 @@ authentication details. ## http.ServerResponse This object is created internally by a HTTP server--not by the user. It is -passed as the second parameter to the `'request'` event. It is a `Writable Stream`. +passed as the second parameter to the `'request'` event. + +The response implements the [Writable Stream](streams.html#writable_Stream) +interface. This is an `EventEmitter` with the following events: ### Event: 'close' @@ -601,11 +608,11 @@ event, the entire body will be caught. }, 10); }); -This is a `Writable Stream`. Note: Node does not check whether Content-Length and the length of the body which has been transmitted are equal or not. -This is an `EventEmitter` with the following events: +The request implements the [Writable Stream](streams.html#writable_Stream) +interface. This is an `EventEmitter` with the following events: ### Event 'response' @@ -797,7 +804,9 @@ will be called. This object is created when making a request with `http.request()`. It is passed to the `'response'` event of the request object. -The response implements the `Readable Stream` interface. +The response implements the [Readable Stream](streams.html#readable_Stream) +interface. This is an `EventEmitter` with the following events: + ### Event: 'data' @@ -805,6 +814,9 @@ The response implements the `Readable Stream` interface. Emitted when a piece of the message body is received. +Note that the __data will be lost__ if there is no listener when a +`ClientResponse` emits a `'data'` event. + ### Event: 'end' diff --git a/doc/api/net.markdown b/doc/api/net.markdown index ad078a52e01..90568676651 100644 --- a/doc/api/net.markdown +++ b/doc/api/net.markdown @@ -415,6 +415,9 @@ Emitted when data is received. The argument `data` will be a `Buffer` or (See the [Readable Stream](streams.html#readable_Stream) section for more information.) +Note that the __data will be lost__ if there is no listener when a `Socket` +emits a `'data'` event. + #### Event: 'end' `function () { }` diff --git a/doc/api/streams.markdown b/doc/api/streams.markdown index 4c353212e36..e11f2dade76 100644 --- a/doc/api/streams.markdown +++ b/doc/api/streams.markdown @@ -15,6 +15,9 @@ A `Readable Stream` has the following methods, members, and events. The `'data'` event emits either a `Buffer` (by default) or a string if `setEncoding()` was used. +Note that the __data will be lost__ if there is no listener when a +`Readable Stream` emits a `'data'` event. + ### Event: 'end' `function () { }` diff --git a/doc/index.html b/doc/index.html index ab73c06a96c..513881213c7 100644 --- a/doc/index.html +++ b/doc/index.html @@ -78,6 +78,7 @@ X + +<<<<<<< HEAD

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.

+>>>>>>> ry/v0.6 diff --git a/lib/fs.js b/lib/fs.js index 1c8f975504c..09a3dcb8aea 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1130,6 +1130,7 @@ var ReadStream = fs.ReadStream = function(path, options) { } if (this.fd !== null) { + this._read(); return; } diff --git a/lib/net.js b/lib/net.js index 006036aaf29..e03ae0c1f5d 100644 --- a/lib/net.js +++ b/lib/net.js @@ -511,7 +511,7 @@ function afterWrite(status, handle, req, buffer) { return; } - timers.active(this); + timers.active(self); self._pendingWriteReqs--; @@ -604,6 +604,7 @@ Socket.prototype.connect = function(options, cb) { // error event to the next tick. process.nextTick(function() { self.emit('error', err); + self.destroy(); }); } else { timers.active(self); diff --git a/lib/path.js b/lib/path.js index 14805e05b8a..ff04cc67623 100644 --- a/lib/path.js +++ b/lib/path.js @@ -416,6 +416,11 @@ module.deprecate('existsSync', 'It is now called `fs.existsSync`.'); exports._makeLong = isWindows ? function(path) { + path = "" + path; + if (!path) { + return ""; + } + var resolvedPath = exports.resolve(path); if (resolvedPath.match(/^[a-zA-Z]\:\\/)) { diff --git a/lib/querystring.js b/lib/querystring.js index 47e259a320b..467eabdea7d 100644 --- a/lib/querystring.js +++ b/lib/querystring.js @@ -183,9 +183,17 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) { } qs.forEach(function(kvp) { - var x = kvp.split(eq); - var k = QueryString.unescape(x[0], true); - var v = QueryString.unescape(x.slice(1).join(eq), true); + var x = kvp.split(eq), k, v, useQS = false; + try { + if (kvp.match(/\+/)) { // decodeURIComponent does not decode + to space + throw 'has +'; + } + k = decodeURIComponent(x[0]); + v = decodeURIComponent(x.slice(1).join(eq) || ""); + } catch(e) { + k = QueryString.unescape(x[0], true); + v = QueryString.unescape(x.slice(1).join(eq), true); + } if (!hasOwnProperty(obj, k)) { obj[k] = v; diff --git a/src/node.cc b/src/node.cc index ab755d743bc..89b930aa39d 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2446,7 +2446,7 @@ static Handle DebugProcess(const Arguments& args) { HandleScope scope; Handle rv = Undefined(); DWORD pid; - HANDLE process_l = NULL; + HANDLE process = NULL; HANDLE thread = NULL; HANDLE mapping = NULL; char mapping_name[32]; @@ -2459,12 +2459,12 @@ static Handle DebugProcess(const Arguments& args) { pid = (DWORD) args[0]->IntegerValue(); - process_l = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | + process = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, pid); - if (process_l == NULL) { + if (process == NULL) { rv = ThrowException(WinapiErrnoException(GetLastError(), "OpenProcess")); goto out; } @@ -2492,7 +2492,7 @@ static Handle DebugProcess(const Arguments& args) { goto out; } - thread = CreateRemoteThread(process_l, + thread = CreateRemoteThread(process, NULL, 0, *handler, @@ -2513,8 +2513,8 @@ static Handle DebugProcess(const Arguments& args) { } out: - if (process_l != NULL) { - CloseHandle(process_l); + if (process != NULL) { + CloseHandle(process); } if (thread != NULL) { CloseHandle(thread); diff --git a/src/node.d b/src/node.d index c666127fffc..fae2378437e 100644 --- a/src/node.d +++ b/src/node.d @@ -64,26 +64,63 @@ translator node_connection_t { &((node_dtrace_connection64_t *)nc)->buffered, sizeof (int32_t)); }; -typedef struct { - uint32_t version; -} node_dtrace_http_request_t; - +/* + * 32-bit and 64-bit structures received from node for HTTP client request + * probe. + */ typedef struct { uint32_t url; uint32_t method; -} node_dtrace_http_request_v0_t; +} node_dtrace_http_client_request_t; + +typedef struct { + uint64_t url; + uint64_t method; +} node_dtrace_http_client_request64_t; + +/* + * The following structures are never used directly, but must exist to bind the + * types specified in the provider to the translators defined here. + * Ultimately, they always get cast to a more specific type inside the + * translator. To add to the confusion, the DTrace compiler does not allow + * declaring two translators with the same destination type if the source types + * are structures with the same size (because libctf says they're compatible, + * so dtrace considers them equivalent). Since we must define translators from + * node_dtrace_http_client_request_t (above), node_dtrace_http_request_t, and + * node_dtrace_http_server_request_t (both below), each of these three structs + * must be declared with a different size. + */ +typedef struct { + uint32_t version; + uint64_t dummy1; +} node_dtrace_http_request_t; + +typedef struct { + uint32_t version; + uint64_t dummy2; + uint64_t dummy3; +} node_dtrace_http_server_request_t; + +/* + * Actual 32-bit and 64-bit, v0 and v1 structures received from node for the + * HTTP server request probe. + */ +typedef struct { + uint32_t url; + uint32_t method; +} node_dtrace_http_server_request_v0_t; typedef struct { uint32_t version; uint32_t url; uint32_t method; uint32_t forwardedFor; -} node_dtrace_http_request_v1_t; +} node_dtrace_http_server_request_v1_t; typedef struct { uint64_t url; uint64_t method; -} node_dtrace_http_request64_v0_t; +} node_dtrace_http_server_request64_v0_t; typedef struct { uint32_t version; @@ -91,8 +128,13 @@ typedef struct { uint64_t url; uint64_t method; uint64_t forwardedFor; -} node_dtrace_http_request64_v1_t; +} node_dtrace_http_server_request64_v1_t; +/* + * In the end, both client and server request probes from both old and new + * binaries translate their arguments to node_http_request_t, which is what the + * user's D script ultimately sees. + */ typedef struct { string url; string method; @@ -100,60 +142,174 @@ typedef struct { } node_http_request_t; /* - * This translator is even filthier than usual owing to our attempts to - * maintain backwards compatibility. Previous versions of node used an - * http_request struct that had fields for "url" and "method". The current - * version also provides a "forwardedFor" field. To distinguish the binary - * representations of these structs, the new version also prepends a "version" - * member (where the old one has a "url" pointer). So each field that we're - * translating below first switches on the value of this "version" field: if - * it's larger than 4096, we know we must be looking at the "url" pointer of - * the older structure version. Otherwise, we must be looking at the new - * version. Besides this, we have the usual switch based on the userland - * process data model. This would all be simpler with macros, but those aren't - * available in delivered D library files since that would make DTrace - * dependent on cpp, which isn't always available. + * The following translators are particularly filthy for reasons of backwards + * compatibility. Stable versions of node prior to 0.6 used a single + * http_request struct with fields for "url" and "method" for both client and + * server probes. 0.6 added a "forwardedFor" field intended for the server + * probe only, and the http_request struct passed by the application was split + * first into client_http_request and server_http_request and the latter was + * again split for v0 (the old struct) and v1. + * + * To distinguish the binary representations of the two versions of these + * structs, the new version prepends a "version" member (where the old one has + * a "url" pointer). Each field that we're translating below first switches on + * the value of this "version" field: if it's larger than 4096, we know we must + * be looking at the "url" pointer of the older structure version. Otherwise, + * we must be looking at the new version. Besides this, we have the usual + * switch based on the userland process data model. This would all be simpler + * with macros, but those aren't available in D library files since we cannot + * rely on cpp being present at runtime. + * + * In retrospect, the versioning bit might have been unnecessary since the type + * of the object passed in should allow DTrace to select which translator to + * use. However, DTrace does sometimes use translators whose source types + * don't quite match, and since we know this versioning logic works, we just + * leave it alone. Each of the translators below is functionally identical + * (except that the client -> client translator doesn't bother translating + * forwardedFor) and should actually work with any version of any of the client + * or server structs transmitted by the application up to this point. */ -translator node_http_request_t { - url = (*(uint32_t *)copyin((uintptr_t)&nd->version, sizeof (uint32_t))) >= 4096 ? + +/* + * Translate from node_dtrace_http_server_request_t (received from node 0.6 and + * later versions) to node_http_request_t. + */ +translator node_http_request_t { + url = (*(uint32_t *)copyin((uintptr_t)(uint32_t *)nd, + sizeof (uint32_t))) >= 4096 ? (curpsinfo->pr_dmodel == PR_MODEL_ILP32 ? copyinstr(*(uint32_t *)copyin((uintptr_t) - &((node_dtrace_http_request_v0_t *)nd)->url, + &((node_dtrace_http_server_request_v0_t *)nd)->url, sizeof (uint32_t))) : copyinstr(*(uint64_t *)copyin((uintptr_t) - &((node_dtrace_http_request64_v0_t *)nd)->url, + &((node_dtrace_http_server_request64_v0_t *)nd)->url, sizeof (uint64_t)))) : (curpsinfo->pr_dmodel == PR_MODEL_ILP32 ? copyinstr(*(uint32_t *)copyin((uintptr_t) - &((node_dtrace_http_request_v1_t *)nd)->url, + &((node_dtrace_http_server_request_v1_t *)nd)->url, sizeof (uint32_t))) : copyinstr(*(uint64_t *)copyin((uintptr_t) - &((node_dtrace_http_request64_v1_t *)nd)->url, + &((node_dtrace_http_server_request64_v1_t *)nd)->url, sizeof (uint64_t)))); - method = (*(uint32_t *)copyin((uintptr_t)&nd->version, sizeof (uint32_t))) >= 4096 ? + method = (*(uint32_t *)copyin((uintptr_t)(uint32_t *)nd, + sizeof (uint32_t))) >= 4096 ? (curpsinfo->pr_dmodel == PR_MODEL_ILP32 ? copyinstr(*(uint32_t *)copyin((uintptr_t) - &((node_dtrace_http_request_v0_t *)nd)->method, + &((node_dtrace_http_server_request_v0_t *)nd)->method, sizeof (uint32_t))) : copyinstr(*(uint64_t *)copyin((uintptr_t) - &((node_dtrace_http_request64_v0_t *)nd)->method, + &((node_dtrace_http_server_request64_v0_t *)nd)->method, sizeof (uint64_t)))) : (curpsinfo->pr_dmodel == PR_MODEL_ILP32 ? copyinstr(*(uint32_t *)copyin((uintptr_t) - &((node_dtrace_http_request_v1_t *)nd)->method, + &((node_dtrace_http_server_request_v1_t *)nd)->method, sizeof (uint32_t))) : copyinstr(*(uint64_t *)copyin((uintptr_t) - &((node_dtrace_http_request64_v1_t *)nd)->method, + &((node_dtrace_http_server_request64_v1_t *)nd)->method, sizeof (uint64_t)))); - - forwardedFor = (*(uint32_t *) - copyin((uintptr_t)&nd->version, sizeof (uint32_t))) >= 4096 ? "" : + + forwardedFor = (*(uint32_t *)copyin((uintptr_t)(uint32_t *)nd, + sizeof (uint32_t))) >= 4096 ? "" : (curpsinfo->pr_dmodel == PR_MODEL_ILP32 ? copyinstr(*(uint32_t *)copyin((uintptr_t) - &((node_dtrace_http_request_v1_t *)nd)->forwardedFor, + &((node_dtrace_http_server_request_v1_t *)nd)->forwardedFor, sizeof (uint32_t))) : copyinstr(*(uint64_t *)copyin((uintptr_t) - &((node_dtrace_http_request64_v1_t *)nd)->forwardedFor, - sizeof (uint64_t)))); + &((node_dtrace_http_server_request64_v1_t *)nd)-> + forwardedFor, sizeof (uint64_t)))); +}; + +/* + * Translate from node_dtrace_http_client_request_t (received from node 0.6 and + * later versions) to node_http_request_t. + */ +translator node_http_request_t { + url = (*(uint32_t *)copyin((uintptr_t)(uint32_t *)nd, + sizeof (uint32_t))) >= 4096 ? + (curpsinfo->pr_dmodel == PR_MODEL_ILP32 ? + copyinstr(*(uint32_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request_v0_t *)nd)->url, + sizeof (uint32_t))) : + copyinstr(*(uint64_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request64_v0_t *)nd)->url, + sizeof (uint64_t)))) : + (curpsinfo->pr_dmodel == PR_MODEL_ILP32 ? + copyinstr(*(uint32_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request_v1_t *)nd)->url, + sizeof (uint32_t))) : + copyinstr(*(uint64_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request64_v1_t *)nd)->url, + sizeof (uint64_t)))); + + method = (*(uint32_t *)copyin((uintptr_t)(uint32_t *)nd, + sizeof (uint32_t))) >= 4096 ? + (curpsinfo->pr_dmodel == PR_MODEL_ILP32 ? + copyinstr(*(uint32_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request_v0_t *)nd)->method, + sizeof (uint32_t))) : + copyinstr(*(uint64_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request64_v0_t *)nd)->method, + sizeof (uint64_t)))) : + (curpsinfo->pr_dmodel == PR_MODEL_ILP32 ? + copyinstr(*(uint32_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request_v1_t *)nd)->method, + sizeof (uint32_t))) : + copyinstr(*(uint64_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request64_v1_t *)nd)->method, + sizeof (uint64_t)))); + + forwardedFor = ""; +}; + +/* + * Translate from node_dtrace_http_request_t (received from versions of node + * prior to 0.6) to node_http_request_t. This is used for both the server and + * client probes since these versions of node didn't distinguish between the + * types used in these probes. + */ +translator node_http_request_t { + url = (*(uint32_t *)copyin((uintptr_t)(uint32_t *)nd, + sizeof (uint32_t))) >= 4096 ? + (curpsinfo->pr_dmodel == PR_MODEL_ILP32 ? + copyinstr(*(uint32_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request_v0_t *)nd)->url, + sizeof (uint32_t))) : + copyinstr(*(uint64_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request64_v0_t *)nd)->url, + sizeof (uint64_t)))) : + (curpsinfo->pr_dmodel == PR_MODEL_ILP32 ? + copyinstr(*(uint32_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request_v1_t *)nd)->url, + sizeof (uint32_t))) : + copyinstr(*(uint64_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request64_v1_t *)nd)->url, + sizeof (uint64_t)))); + + method = (*(uint32_t *)copyin((uintptr_t)(uint32_t *)nd, + sizeof (uint32_t))) >= 4096 ? + (curpsinfo->pr_dmodel == PR_MODEL_ILP32 ? + copyinstr(*(uint32_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request_v0_t *)nd)->method, + sizeof (uint32_t))) : + copyinstr(*(uint64_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request64_v0_t *)nd)->method, + sizeof (uint64_t)))) : + (curpsinfo->pr_dmodel == PR_MODEL_ILP32 ? + copyinstr(*(uint32_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request_v1_t *)nd)->method, + sizeof (uint32_t))) : + copyinstr(*(uint64_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request64_v1_t *)nd)->method, + sizeof (uint64_t)))); + + forwardedFor = (*(uint32_t *) copyin((uintptr_t)(uint32_t *)nd, + sizeof (uint32_t))) >= 4096 ? "" : + (curpsinfo->pr_dmodel == PR_MODEL_ILP32 ? + copyinstr(*(uint32_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request_v1_t *)nd)->forwardedFor, + sizeof (uint32_t))) : + copyinstr(*(uint64_t *)copyin((uintptr_t) + &((node_dtrace_http_server_request64_v1_t *)nd)-> + forwardedFor, sizeof (uint64_t)))); }; diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 924eff3b8f7..87b5bd4378a 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -580,17 +580,6 @@ Handle Buffer::AsciiWrite(const Arguments &args) { Handle Buffer::Base64Write(const Arguments &args) { HandleScope scope; - assert(unbase64('/') == 63); - assert(unbase64('+') == 62); - assert(unbase64('T') == 19); - assert(unbase64('Z') == 25); - assert(unbase64('t') == 45); - assert(unbase64('z') == 51); - - assert(unbase64(' ') == -2); - assert(unbase64('\n') == -2); - assert(unbase64('\r') == -2); - Buffer *buffer = ObjectWrap::Unwrap(args.This()); if (!args[0]->IsString()) { @@ -600,67 +589,52 @@ Handle Buffer::Base64Write(const Arguments &args) { String::AsciiValue s(args[0]->ToString()); size_t offset = args[1]->Int32Value(); + size_t max_length = args[2]->IsUndefined() ? buffer->length_ - offset + : args[2]->Uint32Value(); + max_length = MIN(s.length(), MIN(buffer->length_ - offset, max_length)); - // handle zero-length buffers graciously - if (offset == 0 && buffer->length_ == 0) { - return scope.Close(Integer::New(0)); - } - - if (offset >= buffer->length_) { + if (max_length && offset >= buffer->length_) { return ThrowException(Exception::TypeError(String::New( "Offset is out of bounds"))); } - const size_t size = base64_decoded_size(*s, s.length()); - if (size > buffer->length_ - offset) { - // throw exception, don't silently truncate - return ThrowException(Exception::TypeError(String::New( - "Buffer too small"))); - } - char a, b, c, d; char* start = buffer->data_ + offset; char* dst = start; - const char *src = *s; - const char *const srcEnd = src + s.length(); + char* const dstEnd = dst + max_length; + const char* src = *s; + const char* const srcEnd = src + s.length(); - while (src < srcEnd) { + while (src < srcEnd && dst < dstEnd) { int remaining = srcEnd - src; - while (unbase64(*src) < 0 && src < srcEnd) { - src++; - remaining--; - } + while (unbase64(*src) < 0 && src < srcEnd) src++, remaining--; if (remaining == 0 || *src == '=') break; a = unbase64(*src++); - while (unbase64(*src) < 0 && src < srcEnd) { - src++; - remaining--; - } + while (unbase64(*src) < 0 && src < srcEnd) src++, remaining--; if (remaining <= 1 || *src == '=') break; b = unbase64(*src++); - *dst++ = (a << 2) | ((b & 0x30) >> 4); - while (unbase64(*src) < 0 && src < srcEnd) { - src++; - remaining--; - } + *dst++ = (a << 2) | ((b & 0x30) >> 4); + if (dst == dstEnd) break; + + while (unbase64(*src) < 0 && src < srcEnd) src++, remaining--; if (remaining <= 2 || *src == '=') break; c = unbase64(*src++); - *dst++ = ((b & 0x0F) << 4) | ((c & 0x3C) >> 2); - while (unbase64(*src) < 0 && src < srcEnd) { - src++; - remaining--; - } + *dst++ = ((b & 0x0F) << 4) | ((c & 0x3C) >> 2); + if (dst == dstEnd) break; + + while (unbase64(*src) < 0 && src < srcEnd) src++, remaining--; if (remaining <= 3 || *src == '=') break; d = unbase64(*src++); + *dst++ = ((c & 0x03) << 6) | (d & 0x3F); } constructor_template->GetFunction()->Set(chars_written_sym, - Integer::New(s.length())); + Integer::New(dst - start)); return scope.Close(Integer::New(dst - start)); } @@ -755,6 +729,17 @@ bool Buffer::HasInstance(v8::Handle val) { void Buffer::Initialize(Handle target) { HandleScope scope; + // sanity checks + assert(unbase64('/') == 63); + assert(unbase64('+') == 62); + assert(unbase64('T') == 19); + assert(unbase64('Z') == 25); + assert(unbase64('t') == 45); + assert(unbase64('z') == 51); + assert(unbase64(' ') == -2); + assert(unbase64('\n') == -2); + assert(unbase64('\r') == -2); + length_symbol = Persistent::New(String::NewSymbol("length")); chars_written_sym = Persistent::New(String::NewSymbol("_charsWritten")); diff --git a/test/simple/test-buffer-regress-GH-2659.js b/test/simple/test-buffer-regress-GH-2659.js new file mode 100644 index 00000000000..19a1362fd77 --- /dev/null +++ b/test/simple/test-buffer-regress-GH-2659.js @@ -0,0 +1,30 @@ +// 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'); + +// This is not a great test. It depends on a Node internal, namely the slab +// size. Maybe we should expose that in some way. Then again, maybe not... +for (var n = 1; n <= 8192; ++n) { + Buffer(n); + Buffer(0).write('', 'base64'); +} diff --git a/test/simple/test-buffer.js b/test/simple/test-buffer.js index b9f6ee042df..fd41ba3f8f2 100644 --- a/test/simple/test-buffer.js +++ b/test/simple/test-buffer.js @@ -687,7 +687,7 @@ assert.equal(Buffer._charsWritten, 9); buf.write('0123456789', 'binary'); assert.equal(Buffer._charsWritten, 9); buf.write('123456', 'base64'); -assert.equal(Buffer._charsWritten, 6); +assert.equal(Buffer._charsWritten, 4); buf.write('00010203040506070809', 'hex'); assert.equal(Buffer._charsWritten, 18); @@ -703,3 +703,14 @@ assert.equal(Buffer({length: 'BAM'}).length, 0); // Make sure that strings are not coerced to numbers. assert.equal(Buffer('99').length, 2); assert.equal(Buffer('13.37').length, 5); + +// Ensure that the length argument is respected. +'ascii utf8 hex base64 binary'.split(' ').forEach(function(enc) { + assert.equal(Buffer(1).write('aaaaaa', 0, 1, enc), 1); +}); + +// Regression test, guard against buffer overrun in the base64 decoder. +var a = Buffer(3); +var b = Buffer('xxx'); +a.write('aaaaaaaa', 'base64'); +assert.equal(b.toString(), 'xxx'); diff --git a/test/simple/test-fs-read-stream-fd.js b/test/simple/test-fs-read-stream-fd.js new file mode 100644 index 00000000000..835ed97271d --- /dev/null +++ b/test/simple/test-fs-read-stream-fd.js @@ -0,0 +1,44 @@ +// 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 fs = require('fs'); +var assert = require('assert'); +var path = require('path'); + +var common = require('../common'); + +var file = path.join(common.tmpDir, '/read_stream_fd_test.txt'); +var input = 'hello world'; +var output = ''; +var fd, stream; + +fs.writeFileSync(file, input); +fd = fs.openSync(file, 'r'); + +stream = fs.createReadStream(null, { fd: fd, encoding: 'utf8' }); +stream.on('data', function(data) { + output += data; +}); + +process.on('exit', function() { + fs.unlinkSync(file); + assert.equal(output, input); +}); diff --git a/test/simple/test-http-dns-error.js b/test/simple/test-http-dns-error.js index 7abdbcf1346..fc6d6b89e11 100644 --- a/test/simple/test-http-dns-error.js +++ b/test/simple/test-http-dns-error.js @@ -59,10 +59,7 @@ function test(mod) { req.end(); } -// FIXME This doesn't work for https because the tls module won't emit errors -// until a secure channel has been established - and that is never going to -// happen because the host name is invalid. -//test(https); +test(https); test(http); process.on('exit', function() { diff --git a/test/simple/test-net-dns-error.js b/test/simple/test-net-dns-error.js new file mode 100644 index 00000000000..76b51497567 --- /dev/null +++ b/test/simple/test-net-dns-error.js @@ -0,0 +1,48 @@ +// 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 assert = require('assert'); + +var net = require('net'); + +var expected_bad_connections = 1; +var actual_bad_connections = 0; + +var host = '********'; +host += host; +host += host; +host += host; +host += host; +host += host; + +function do_not_call() { + throw new Error('This function should not have been called.'); +} + +var socket = net.connect(42, host, do_not_call); +socket.on('error', function (err) { + assert.equal(err.code, 'ENOTFOUND'); + actual_bad_connections++; +}); + +process.on('exit', function() { + assert.equal(actual_bad_connections, expected_bad_connections); +}); diff --git a/test/simple/test-net-write-slow.js b/test/simple/test-net-write-slow.js new file mode 100644 index 00000000000..ddb35c081d4 --- /dev/null +++ b/test/simple/test-net-write-slow.js @@ -0,0 +1,60 @@ +// 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 net = require('net'); + +var SIZE = 1E5; +var N = 10; +var received = 0; +var buf = new Buffer(SIZE); +buf.fill(0x61); // 'a' + +var server = net.createServer(function(socket) { + socket.setNoDelay(); + socket.setTimeout(500); + socket.on('timeout', function() { + assert.fail(); + }); + + for (var i = 0; i < N; ++i) { + socket.write(buf); + } + socket.end(); + +}).listen(common.PORT, function() { + var conn = net.connect(common.PORT); + conn.on('data', function(buf) { + received += buf.length; + conn.pause(); + setTimeout(function() { + conn.resume(); + }, 50); + }); + conn.on('end', function() { + server.close(); + }); +}); + +process.on('exit', function() { + assert.equal(received, SIZE * N); +}); diff --git a/tools/msvs/msi/product.wxs b/tools/msvs/msi/product.wxs old mode 100644 new mode 100755 index 1d445d272a9..889fd37f1d5 --- a/tools/msvs/msi/product.wxs +++ b/tools/msvs/msi/product.wxs @@ -46,6 +46,9 @@ + + + @@ -69,6 +72,7 @@ +