@@ -8,7 +8,10 @@
'node_use_etw%': 'false',
'node_shared_v8%': 'false',
'node_shared_zlib%': 'false',
'node_shared_http_parser%': 'false',
'node_shared_cares%': 'false',
'node_use_openssl%': 'true',
'node_use_systemtap%': 'false',
'node_shared_openssl%': 'false',
'library_files': [
'src/node.js',
@@ -39,6 +42,11 @@
'lib/readline.js',
'lib/repl.js',
'lib/stream.js',
'lib/_stream_readable.js',
'lib/_stream_writable.js',
'lib/_stream_duplex.js',
'lib/_stream_transform.js',
'lib/_stream_passthrough.js',
'lib/string_decoder.js',
'lib/sys.js',
'lib/timers.js',
@@ -57,8 +65,6 @@
'type': 'executable',

'dependencies': [
'deps/cares/cares.gyp:cares',
'deps/http_parser/http_parser.gyp:http_parser',
'deps/uv/uv.gyp:libuv',
'node_js2c#host',
],
@@ -146,7 +152,6 @@
}, {
'defines': [ 'HAVE_OPENSSL=0' ]
}],

[ 'node_use_dtrace=="true"', {
'defines': [ 'HAVE_DTRACE=1' ],
'dependencies': [ 'node_dtrace_header' ],
@@ -168,6 +173,15 @@
}
] ],
} ],
[ 'node_use_systemtap=="true"', {
'defines': [ 'HAVE_SYSTEMTAP=1', 'STAP_SDT_V1=1' ],
'dependencies': [ 'node_systemtap_header' ],
'include_dirs': [ '<(SHARED_INTERMEDIATE_DIR)' ],
'sources': [
'src/node_dtrace.cc',
'<(SHARED_INTERMEDIATE_DIR)/node_systemtap.h',
],
} ],
[ 'node_use_etw=="true"', {
'defines': [ 'HAVE_ETW=1' ],
'dependencies': [ 'node_etw' ],
@@ -192,6 +206,14 @@
'dependencies': [ 'deps/zlib/zlib.gyp:zlib' ],
}],

[ 'node_shared_http_parser=="false"', {
'dependencies': [ 'deps/http_parser/http_parser.gyp:http_parser' ],
}],

[ 'node_shared_cares=="false"', {
'dependencies': [ 'deps/cares/cares.gyp:cares' ],
}],

[ 'OS=="win"', {
'sources': [
'src/res/node.rc',
@@ -211,6 +233,9 @@
'defines!': [
'PLATFORM="mac"',
],
'xcode_settings': {
'DEAD_CODE_STRIPPING': 'YES',
},
'defines': [
# we need to use node's preferred "darwin" rather than gyp's preferred "mac"
'PLATFORM="darwin"',
@@ -285,7 +310,10 @@
# action?

'conditions': [
[ 'node_use_dtrace=="true" or node_use_etw=="true"', {
[ 'node_use_dtrace=="true"'
' or node_use_etw=="true"'
' or node_use_systemtap=="true"',
{
'action': [
'python',
'tools/js2c.py',
@@ -322,6 +350,23 @@
} ]
]
},
{
'target_name': 'node_systemtap_header',
'type': 'none',
'conditions': [
[ 'node_use_systemtap=="true"', {
'actions': [
{
'action_name': 'node_systemtap_header',
'inputs': [ 'src/node_systemtap.d' ],
'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/node_systemtap.h' ],
'action': [ 'dtrace', '-h', '-C', '-s', '<@(_inputs)',
'-o', '<@(_outputs)' ]
}
]
} ]
]
},
{
'target_name': 'node_dtrace_provider',
'type': 'none',
@@ -813,6 +813,24 @@ void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) {
}


static Handle<Value> IsIP(const Arguments& args) {
HandleScope scope;

String::AsciiValue ip(args[0]);
char address_buffer[sizeof(struct in6_addr)];

if (uv_inet_pton(AF_INET, *ip, &address_buffer).code == UV_OK) {
return scope.Close(v8::Integer::New(4));
}

if (uv_inet_pton(AF_INET6, *ip, &address_buffer).code == UV_OK) {
return scope.Close(v8::Integer::New(6));
}

return scope.Close(v8::Integer::New(0));
}


static Handle<Value> GetAddrInfo(const Arguments& args) {
HandleScope scope;

@@ -886,6 +904,7 @@ static void Initialize(Handle<Object> target) {
NODE_SET_METHOD(target, "getHostByName", QueryWithFamily<GetHostByNameWrap>);

NODE_SET_METHOD(target, "getaddrinfo", GetAddrInfo);
NODE_SET_METHOD(target, "isIP", IsIP);

target->Set(String::NewSymbol("AF_INET"), Integer::New(AF_INET));
target->Set(String::NewSymbol("AF_INET6"), Integer::New(AF_INET6));
@@ -27,7 +27,7 @@
#include "uv.h"

#include "v8-debug.h"
#if defined HAVE_DTRACE || defined HAVE_ETW
#if defined HAVE_DTRACE || defined HAVE_ETW || defined HAVE_SYSTEMTAP
# include "node_dtrace.h"
#endif

@@ -72,6 +72,9 @@ typedef int mode_t;
#if HAVE_OPENSSL
# include "node_crypto.h"
#endif
#if HAVE_SYSTEMTAP
#include "node_systemtap.h"
#endif
#include "node_script.h"
#include "v8_typed_array.h"

@@ -2307,7 +2310,7 @@ void Load(Handle<Object> process_l) {
Local<Object> global = v8::Context::GetCurrent()->Global();
Local<Value> args[1] = { Local<Value>::New(process_l) };

#if defined HAVE_DTRACE || defined HAVE_ETW
#if defined HAVE_DTRACE || defined HAVE_ETW || defined HAVE_SYSTEMTAP
InitDTrace(global);
#endif

@@ -701,8 +701,8 @@

var nativeModule = new NativeModule(id);

nativeModule.compile();
nativeModule.cache();
nativeModule.compile();

return nativeModule.exports;
};
@@ -19,14 +19,23 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

#include "node_dtrace.h"
#include <string.h>

#ifdef HAVE_DTRACE
#include "node_dtrace.h"
#include <string.h>
#include "node_provider.h"
#elif HAVE_ETW
#include "node_dtrace.h"
#include <string.h>
#include "node_win32_etw_provider.h"
#include "node_win32_etw_provider-inl.h"
#elif HAVE_SYSTEMTAP
#include <string.h>
#include <node.h>
#include <v8.h>
#include <sys/sdt.h>
#include "node_systemtap.h"
#include "node_dtrace.h"
#else
#define NODE_HTTP_SERVER_REQUEST(arg0, arg1)
#define NODE_HTTP_SERVER_REQUEST_ENABLED() (0)
@@ -118,76 +127,97 @@ using namespace v8;


Handle<Value> DTRACE_NET_SERVER_CONNECTION(const Arguments& args) {
#ifndef HAVE_SYSTEMTAP
if (!NODE_NET_SERVER_CONNECTION_ENABLED())
return Undefined();
#endif

HandleScope scope;

SLURP_CONNECTION(args[0], conn);
#ifdef HAVE_SYSTEMTAP
NODE_NET_SERVER_CONNECTION(conn.fd, conn.remote, conn.port, \
conn.buffered);
#else
NODE_NET_SERVER_CONNECTION(&conn);
#endif

return Undefined();
}

Handle<Value> DTRACE_NET_STREAM_END(const Arguments& args) {
#ifndef HAVE_SYSTEMTAP
if (!NODE_NET_STREAM_END_ENABLED())
return Undefined();
#endif

HandleScope scope;

SLURP_CONNECTION(args[0], conn);
#ifdef HAVE_SYSTEMTAP
NODE_NET_STREAM_END(conn.fd, conn.remote, conn.port, conn.buffered);
#else
NODE_NET_STREAM_END(&conn);
#endif

return Undefined();
}

Handle<Value> DTRACE_NET_SOCKET_READ(const Arguments& args) {
#ifndef HAVE_SYSTEMTAP
if (!NODE_NET_SOCKET_READ_ENABLED())
return Undefined();
#endif

HandleScope scope;
int nbytes;

SLURP_CONNECTION(args[0], conn);

#ifdef HAVE_SYSTEMTAP
NODE_NET_SOCKET_READ(conn.fd, conn.remote, conn.port, conn.buffered);
#else
if (!args[1]->IsNumber()) {
return (ThrowException(Exception::Error(String::New("expected "
"argument 1 to be number of bytes"))));
}

nbytes = args[1]->Int32Value();

int nbytes = args[1]->Int32Value();
NODE_NET_SOCKET_READ(&conn, nbytes);
#endif

return Undefined();
}

Handle<Value> DTRACE_NET_SOCKET_WRITE(const Arguments& args) {
#ifndef HAVE_SYSTEMTAP
if (!NODE_NET_SOCKET_WRITE_ENABLED())
return Undefined();
#endif

HandleScope scope;
int nbytes;

SLURP_CONNECTION(args[0], conn);

#ifdef HAVE_SYSTEMTAP
NODE_NET_SOCKET_WRITE(conn.fd, conn.remote, conn.port, conn.buffered);
#else
if (!args[1]->IsNumber()) {
return (ThrowException(Exception::Error(String::New("expected "
"argument 1 to be number of bytes"))));
}

nbytes = args[1]->Int32Value();

int nbytes = args[1]->Int32Value();
NODE_NET_SOCKET_WRITE(&conn, nbytes);
#endif

return Undefined();
}

Handle<Value> DTRACE_HTTP_SERVER_REQUEST(const Arguments& args) {
node_dtrace_http_server_request_t req;

#ifndef HAVE_SYSTEMTAP
if (!NODE_HTTP_SERVER_REQUEST_ENABLED())
return Undefined();
#endif

HandleScope scope;

@@ -213,18 +243,29 @@ Handle<Value> DTRACE_HTTP_SERVER_REQUEST(const Arguments& args) {

SLURP_CONNECTION(args[1], conn);

#ifdef HAVE_SYSTEMTAP
NODE_HTTP_SERVER_REQUEST(&req, conn.fd, conn.remote, conn.port, \
conn.buffered);
#else
NODE_HTTP_SERVER_REQUEST(&req, &conn);
#endif
return Undefined();
}

Handle<Value> DTRACE_HTTP_SERVER_RESPONSE(const Arguments& args) {
#ifndef HAVE_SYSTEMTAP
if (!NODE_HTTP_SERVER_RESPONSE_ENABLED())
return Undefined();
#endif

HandleScope scope;

SLURP_CONNECTION(args[0], conn);
#ifdef HAVE_SYSTEMTAP
NODE_HTTP_SERVER_RESPONSE(conn.fd, conn.remote, conn.port, conn.buffered);
#else
NODE_HTTP_SERVER_RESPONSE(&conn);
#endif

return Undefined();
}
@@ -233,8 +274,10 @@ Handle<Value> DTRACE_HTTP_CLIENT_REQUEST(const Arguments& args) {
node_dtrace_http_client_request_t req;
char *header;

#ifndef HAVE_SYSTEMTAP
if (!NODE_HTTP_CLIENT_REQUEST_ENABLED())
return Undefined();
#endif

HandleScope scope;

@@ -263,26 +306,40 @@ Handle<Value> DTRACE_HTTP_CLIENT_REQUEST(const Arguments& args) {
*header = '\0';

SLURP_CONNECTION_HTTP_CLIENT(args[1], conn);
#ifdef HAVE_SYSTEMTAP
NODE_HTTP_CLIENT_REQUEST(&req, conn.fd, conn.remote, conn.port, \
conn.buffered);
#else
NODE_HTTP_CLIENT_REQUEST(&req, &conn);
#endif
return Undefined();
}

Handle<Value> DTRACE_HTTP_CLIENT_RESPONSE(const Arguments& args) {
#ifndef HAVE_SYSTEMTAP
if (!NODE_HTTP_CLIENT_RESPONSE_ENABLED())
return Undefined();

#endif
HandleScope scope;

SLURP_CONNECTION_HTTP_CLIENT_RESPONSE(args[0], args[1], conn);
#ifdef HAVE_SYSTEMTAP
NODE_HTTP_CLIENT_RESPONSE(conn.fd, conn.remote, conn.port, conn.buffered);
#else
NODE_HTTP_CLIENT_RESPONSE(&conn);
#endif

return Undefined();
}

#define NODE_PROBE(name) #name, name
#define NODE_PROBE(name) #name, name, Persistent<FunctionTemplate>()

static int dtrace_gc_start(GCType type, GCCallbackFlags flags) {
#ifdef HAVE_SYSTEMTAP
NODE_GC_START();
#else
NODE_GC_START(type, flags);
#endif
/*
* We avoid the tail-call elimination of the USDT probe (which screws up
* args) by forcing a return of 0.
@@ -291,7 +348,11 @@ static int dtrace_gc_start(GCType type, GCCallbackFlags flags) {
}

static int dtrace_gc_done(GCType type, GCCallbackFlags flags) {
#ifdef HAVE_SYSTEMTAP
NODE_GC_DONE();
#else
NODE_GC_DONE(type, flags);
#endif
return 0;
}

@@ -308,11 +369,10 @@ void InitDTrace(Handle<Object> target) {
{ NODE_PROBE(DTRACE_HTTP_SERVER_REQUEST) },
{ NODE_PROBE(DTRACE_HTTP_SERVER_RESPONSE) },
{ NODE_PROBE(DTRACE_HTTP_CLIENT_REQUEST) },
{ NODE_PROBE(DTRACE_HTTP_CLIENT_RESPONSE) },
{ NULL }
{ NODE_PROBE(DTRACE_HTTP_CLIENT_RESPONSE) }
};

for (int i = 0; tab[i].name != NULL; i++) {
for (unsigned int i = 0; i < ARRAY_SIZE(tab); i++) {
tab[i].templ = Persistent<FunctionTemplate>::New(
FunctionTemplate::New(tab[i].func));
target->Set(String::NewSymbol(tab[i].name), tab[i].templ->GetFunction());
@@ -322,7 +382,7 @@ void InitDTrace(Handle<Object> target) {
init_etw();
#endif

#if defined HAVE_DTRACE || defined HAVE_ETW
#if defined HAVE_DTRACE || defined HAVE_ETW || defined HAVE_SYSTEMTAP
v8::V8::AddGCPrologueCallback((GCPrologueCallback)dtrace_gc_start);
v8::V8::AddGCEpilogueCallback((GCEpilogueCallback)dtrace_gc_done);
#endif
@@ -41,6 +41,14 @@ namespace node {

using namespace v8;

static Handle<Value> GetEndianness(const Arguments& args) {
HandleScope scope;
int i = 1;
bool big = (*(char *)&i) == 0;
Local<String> endianness = String::New(big ? "BE" : "LE");
return scope.Close(endianness);
}

static Handle<Value> GetHostname(const Arguments& args) {
HandleScope scope;
char s[255];
@@ -242,6 +250,7 @@ static Handle<Value> GetInterfaceAddresses(const Arguments& args) {
void OS::Initialize(v8::Handle<v8::Object> target) {
HandleScope scope;

NODE_SET_METHOD(target, "getEndianness", GetEndianness);
NODE_SET_METHOD(target, "getHostname", GetHostname);
NODE_SET_METHOD(target, "getLoadAvg", GetLoadAvg);
NODE_SET_METHOD(target, "getUptime", GetUptime);
@@ -0,0 +1,51 @@
// 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.

// Hints:
// This .d defines compiled in probes
// probes are handles (untyped pointers)
// v8 forward declared objs (dtrace_connection_t) are defined
// in node_dtrace.cc which builds an InitDtrace object which
// gets populated with the probes
// The probes gather the following:
// PROBE_REQUEST(req, fd, remote, port, buffered)
// PROBE_OTHER(fd, remote, port, buffered)
// other notes:
// using any PROBE_ENABLED() macros in dtrace.cc sdt broke it
// can only pass strings/ints/primitives not dtrace_connection_t
// conn or other structs
// verify probe existence by using
// $ stap -l 'process("out/Release/node").mark("*")'
// TODO: write .stp scripts (node.stp, node_v8ustack.stp + ???)


provider node {
probe http__client__request(string, int, string, int, int);
probe http__client__response(int, string, int, int);
probe http__server__request(string, int, string, int, int);
probe http__server__response(int, string, int, int);
probe net__server__connection(int, string, int, int);
probe net__socket__read(int, string, int, int);
probe net__socket__write(int, string, int, int);
probe net__stream__end(int, string, int, int);
probe gc__done();
probe gc__start();
};
@@ -42,6 +42,20 @@ extern int events_enabled;
#define ETW_WRITE_INT32_DATA(data_descriptor, data) \
EventDataDescCreate(data_descriptor, data, sizeof(int32_t));

#define ETW_WRITE_INT64_DATA(data_descriptor, data) \
EventDataDescCreate(data_descriptor, data, sizeof(int64_t));

#define ETW_WRITE_ADDRESS_DATA(data_descriptor, data) \
EventDataDescCreate(data_descriptor, data, sizeof(intptr_t));

#define ETW_WRITE_INT16_DATA(data_descriptor, data) \
EventDataDescCreate(data_descriptor, data, sizeof(int16_t));

#define ETW_WRITE_WSTRING_DATA_LENGTH(data_descriptor, data, data_len_bytes) \
EventDataDescCreate(data_descriptor, \
data, \
data_len_bytes);

#define ETW_WRITE_NET_CONNECTION(descriptors, conn) \
ETW_WRITE_INT32_DATA(descriptors, &conn->fd); \
ETW_WRITE_INT32_DATA(descriptors + 1, &conn->port); \
@@ -61,6 +75,34 @@ extern int events_enabled;
ETW_WRITE_INT32_DATA(descriptors, &type); \
ETW_WRITE_INT32_DATA(descriptors + 1, &flags);

#define ETW_WRITE_V8ADDRESSCHANGE(descriptors, addr1, addr2) \
ETW_WRITE_ADDRESS_DATA(descriptors, &addr1); \
ETW_WRITE_ADDRESS_DATA(descriptors + 1, &addr2);

#define ETW_WRITE_JSMETHOD_LOADUNLOAD(descriptors, \
context, \
startAddr, \
size, \
id, \
flags, \
rangeId, \
sourceId, \
line, \
col, \
name, \
name_len_bytes) \
ETW_WRITE_ADDRESS_DATA(descriptors, &context); \
ETW_WRITE_ADDRESS_DATA(descriptors + 1, &startAddr); \
ETW_WRITE_INT64_DATA(descriptors + 2, &size); \
ETW_WRITE_INT32_DATA(descriptors + 3, &id); \
ETW_WRITE_INT16_DATA(descriptors + 4, &flags); \
ETW_WRITE_INT16_DATA(descriptors + 5, &rangeId); \
ETW_WRITE_INT64_DATA(descriptors + 6, &sourceId); \
ETW_WRITE_INT32_DATA(descriptors + 7, &line); \
ETW_WRITE_INT32_DATA(descriptors + 8, &col); \
ETW_WRITE_WSTRING_DATA_LENGTH(descriptors + 9, name, name_len_bytes);


#define ETW_WRITE_EVENT(eventDescriptor, dataDescriptors) \
DWORD status = event_write(node_provider, \
&eventDescriptor, \
@@ -133,6 +175,83 @@ void NODE_GC_DONE(GCType type, GCCallbackFlags flags) {
}


void NODE_V8SYMBOL_REMOVE(const void* addr1, const void* addr2) {
if (events_enabled > 0) {
EVENT_DATA_DESCRIPTOR descriptors[2];
ETW_WRITE_V8ADDRESSCHANGE(descriptors, addr1, addr2);
ETW_WRITE_EVENT(NODE_V8SYMBOL_REMOVE_EVENT, descriptors);
}
}


void NODE_V8SYMBOL_MOVE(const void* addr1, const void* addr2) {
if (events_enabled > 0) {
EVENT_DATA_DESCRIPTOR descriptors[2];
ETW_WRITE_V8ADDRESSCHANGE(descriptors, addr1, addr2);
ETW_WRITE_EVENT(NODE_V8SYMBOL_MOVE_EVENT, descriptors);
}
}


void NODE_V8SYMBOL_RESET() {
if (events_enabled > 0) {
int val = 0;
EVENT_DATA_DESCRIPTOR descriptors[1];
ETW_WRITE_INT32_DATA(descriptors, &val);
ETW_WRITE_EVENT(NODE_V8SYMBOL_RESET_EVENT, descriptors);
}
}

#define SETSYMBUF(s) \
wcscpy(symbuf, s); \
symbol_len = ARRAY_SIZE(s) - 1;

void NODE_V8SYMBOL_ADD(LPCSTR symbol,
int symbol_len,
const void* addr1,
int len) {
if (events_enabled > 0) {
wchar_t symbuf[128];
if (symbol == NULL) {
SETSYMBUF(L"NULL");
} else {
symbol_len = MultiByteToWideChar(CP_ACP, 0, symbol, symbol_len, symbuf, 128);
if (symbol_len == 0) {
SETSYMBUF(L"Invalid");
} else {
if (symbol_len > 127) {
symbol_len = 127;
}
symbuf[symbol_len] = L'\0';
}
}
void* context = NULL;
INT64 size = (INT64)len;
INT32 id = (INT32)addr1;
INT16 flags = 0;
INT16 rangeid = 1;
INT32 col = 1;
INT32 line = 1;
INT64 sourceid = 0;
EVENT_DATA_DESCRIPTOR descriptors[10];
ETW_WRITE_JSMETHOD_LOADUNLOAD(descriptors,
context,
addr1,
size,
id,
flags,
rangeid,
sourceid,
line,
col,
symbuf,
symbol_len * sizeof(symbuf[0]));
ETW_WRITE_EVENT(MethodLoad, descriptors);
}
}
#undef SETSYMBUF


bool NODE_HTTP_SERVER_REQUEST_ENABLED() { return events_enabled > 0; }
bool NODE_HTTP_SERVER_RESPONSE_ENABLED() { return events_enabled > 0; }
bool NODE_HTTP_CLIENT_REQUEST_ENABLED() { return events_enabled > 0; }
@@ -141,5 +260,6 @@ bool NODE_NET_SERVER_CONNECTION_ENABLED() { return events_enabled > 0; }
bool NODE_NET_STREAM_END_ENABLED() { return events_enabled > 0; }
bool NODE_NET_SOCKET_READ_ENABLED() { return events_enabled > 0; }
bool NODE_NET_SOCKET_WRITE_ENABLED() { return events_enabled > 0; }
bool NODE_V8SYMBOL_ENABLED() { return events_enabled > 0; }
}
#endif // SRC_ETW_INL_H_
@@ -22,6 +22,7 @@
#include "node_dtrace.h"
#include "node_win32_etw_provider.h"
#include "node_etw_provider.h"
#include "node_win32_etw_provider-inl.h"

namespace node {

@@ -33,10 +34,110 @@ EventRegisterFunc event_register;
EventUnregisterFunc event_unregister;
EventWriteFunc event_write;
int events_enabled;
static uv_async_t dispatch_etw_events_change_async;

struct v8tags {
char prefix[32 - sizeof(size_t)];
size_t prelen;
};

// The v8 CODE_ADDED event name has a prefix indicating the type of event.
// Many of these are internal to v8.
// The trace_codes array specifies which types are written.
struct v8tags trace_codes[] = {
#define MAKE_V8TAG(s) { s, sizeof(s) - 1 }
MAKE_V8TAG("LazyCompile:"),
MAKE_V8TAG("Script:"),
MAKE_V8TAG("Function:"),
MAKE_V8TAG("RegExp:"),
MAKE_V8TAG("Eval:")
#undef MAKE_V8TAG
};

/* Below are some code prefixes which are not being written.
* "Builtin:"
* "Stub:"
* "CallIC:"
* "LoadIC:"
* "KeyedLoadIC:"
* "StoreIC:"
* "KeyedStoreIC:"
* "CallPreMonomorphic:"
* "CallInitialize:"
* "CallMiss:"
* "CallMegamorphic:"
*/

// v8 sometimes puts a '*' or '~' in front of the name.
#define V8_MARKER1 '*'
#define V8_MARKER2 '~'


// If prefix is not in filtered list return -1,
// else return length of prefix and marker.
int FilterCodeEvents(const char* name, size_t len) {
for (int i = 0; i < ARRAY_SIZE(trace_codes); i++) {
size_t prelen = trace_codes[i].prelen;
if (prelen < len) {
if (strncmp(name, trace_codes[i].prefix, prelen) == 0) {
if (name[prelen] == V8_MARKER1 || name[prelen] == V8_MARKER2)
prelen++;
return prelen;
}
}
}
return -1;
}


// callback from V8 module passes symbol and address info for stack walk
void CodeAddressNotification(const JitCodeEvent* jevent) {
int pre_offset = 0;
if (NODE_V8SYMBOL_ENABLED()) {
switch (jevent->type) {
case JitCodeEvent::CODE_ADDED:
pre_offset = FilterCodeEvents(jevent->name.str, jevent->name.len);
if (pre_offset >= 0) {
// skip over prefix and marker
NODE_V8SYMBOL_ADD(jevent->name.str + pre_offset,
jevent->name.len - pre_offset,
jevent->code_start,
jevent->code_len);
}
break;
case JitCodeEvent::CODE_REMOVED:
NODE_V8SYMBOL_REMOVE(jevent->code_start, 0);
break;
case JitCodeEvent::CODE_MOVED:
NODE_V8SYMBOL_MOVE(jevent->code_start, jevent->new_code_start);
break;
default:
break;
}
}
}


// Call v8 to enable or disable code event callbacks.
// Must be on default thread to do this.
// Note: It is possible to call v8 from ETW thread, but then
// event callbacks are received in the same thread. Attempts
// to write ETW events in this thread will fail.
void etw_events_change_async(uv_async_t* handle, int status) {
if (events_enabled > 0) {
NODE_V8SYMBOL_RESET();
V8::SetJitCodeEventHandler(v8::kJitCodeEventEnumExisting,
CodeAddressNotification);
} else {
V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, NULL);
}
}


// This callback is called by ETW when consumers of our provider
// are enabled or disabled.
// The callback is dispatched on ETW thread.
// Before calling into V8 to enable code events, switch to default thread.
void NTAPI etw_events_enable_callback(
LPCGUID SourceId,
ULONG IsEnabled,
@@ -47,8 +148,14 @@ void NTAPI etw_events_enable_callback(
PVOID CallbackContext) {
if (IsEnabled) {
events_enabled++;
if (events_enabled == 1) {
uv_async_send(&dispatch_etw_events_change_async);
}
} else {
events_enabled--;
if (events_enabled == 0) {
uv_async_send(&dispatch_etw_events_change_async);
}
}
}

@@ -64,6 +171,12 @@ void init_etw() {
GetProcAddress(advapi, "EventUnregister");
event_write = (EventWriteFunc)GetProcAddress(advapi, "EventWrite");

// create async object used to invoke main thread from callback
uv_async_init(uv_default_loop(),
&dispatch_etw_events_change_async,
etw_events_change_async);
uv_unref((uv_handle_t*) &dispatch_etw_events_change_async);

if (event_register) {
DWORD status = event_register(&NODE_ETW_PROVIDER,
etw_events_enable_callback,
@@ -82,6 +195,7 @@ void shutdown_etw() {
}

events_enabled = 0;
V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, NULL);

if (advapi) {
FreeLibrary(advapi);
@@ -66,6 +66,13 @@ INLINE void NODE_NET_SERVER_CONNECTION(node_dtrace_connection_t* conn);
INLINE void NODE_NET_STREAM_END(node_dtrace_connection_t* conn);
INLINE void NODE_GC_START(GCType type, GCCallbackFlags flags);
INLINE void NODE_GC_DONE(GCType type, GCCallbackFlags flags);
INLINE void NODE_V8SYMBOL_REMOVE(const void* addr1, const void* addr2);
INLINE void NODE_V8SYMBOL_MOVE(const void* addr1, const void* addr2);
INLINE void NODE_V8SYMBOL_RESET();
INLINE void NODE_V8SYMBOL_ADD(LPCSTR symbol,
int symbol_len,
const void* addr1,
int len);

INLINE bool NODE_HTTP_SERVER_REQUEST_ENABLED();
INLINE bool NODE_HTTP_SERVER_RESPONSE_ENABLED();
@@ -75,6 +82,7 @@ INLINE bool NODE_NET_SERVER_CONNECTION_ENABLED();
INLINE bool NODE_NET_STREAM_END_ENABLED();
INLINE bool NODE_NET_SOCKET_READ_ENABLED();
INLINE bool NODE_NET_SOCKET_WRITE_ENABLED();
INLINE bool NODE_V8SYMBOL_ENABLED();

#define NODE_NET_SOCKET_READ(arg0, arg1)
#define NODE_NET_SOCKET_WRITE(arg0, arg1)
@@ -109,7 +109,19 @@ class ZCtx : public ObjectWrap {
assert(!ctx->write_in_progress_ && "write already in progress");
ctx->write_in_progress_ = true;

assert(!args[0]->IsUndefined() && "must provide flush value");

unsigned int flush = args[0]->Uint32Value();

if (flush != Z_NO_FLUSH &&
flush != Z_PARTIAL_FLUSH &&
flush != Z_SYNC_FLUSH &&
flush != Z_FULL_FLUSH &&
flush != Z_FINISH &&
flush != Z_BLOCK) {
assert(0 && "Invalid flush value");
}

Bytef *in;
Bytef *out;
size_t in_off, in_len, out_off, out_len;
@@ -481,6 +493,7 @@ void InitZlib(Handle<Object> target) {
callback_sym = NODE_PSYMBOL("callback");
onerror_sym = NODE_PSYMBOL("onerror");

// valid flush values.
NODE_DEFINE_CONSTANT(target, Z_NO_FLUSH);
NODE_DEFINE_CONSTANT(target, Z_PARTIAL_FLUSH);
NODE_DEFINE_CONSTANT(target, Z_SYNC_FLUSH);
@@ -1,15 +1,25 @@
<instrumentationManifest
xmlns="http://schemas.microsoft.com/win/2004/08/events"
xmlns="http://schemas.microsoft.com/win/2004/08/events"
xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<instrumentation>
<events>
<provider name="NodeJS-ETW-provider"
guid="{77754E9B-264B-4D8D-B981-E4135C1ECB0C}"
symbol="NODE_ETW_PROVIDER"
resourceFileName="node.exe"
<provider name="NodeJS-ETW-provider"
guid="{77754E9B-264B-4D8D-B981-E4135C1ECB0C}"
symbol="NODE_ETW_PROVIDER"
resourceFileName="node.exe"
messageFileName="node.exe">

<tasks>
<task name="MethodRuntime" value="1"
symbol="JSCRIPT_METHOD_RUNTIME_TASK">
<opcodes>
<opcode name="MethodLoad" value="10"
symbol="JSCRIPT_METHOD_METHODLOAD_OPCODE"/>
</opcodes>
</task>
</tasks>

<opcodes>
<opcode name="NODE_HTTP_SERVER_REQUEST" value="10"/>
<opcode name="NODE_HTTP_SERVER_RESPONSE" value="11"/>
@@ -19,6 +29,9 @@
<opcode name="NODE_NET_STREAM_END" value="15"/>
<opcode name="NODE_GC_START" value="16"/>
<opcode name="NODE_GC_DONE" value="17"/>
<opcode name="NODE_V8SYMBOL_REMOVE" value="21"/>
<opcode name="NODE_V8SYMBOL_MOVE" value="22"/>
<opcode name="NODE_V8SYMBOL_RESET" value="23"/>
</opcodes>

<templates>
@@ -52,49 +65,87 @@
<data name="gctype" inType="win:UInt32" />
<data name="gccallbackflags" inType="win:UInt32" />
</template>

<template tid="V8AddressChange">
<data name="addr1" inType="win:Pointer" outType="win:HexInt64"/>
<data name="addr2" inType="win:Pointer" outType="win:HexInt64"/>
</template>

<template tid="MethodLoadUnload">
<data name="ScriptContextID" inType="win:Pointer" outType="win:HexInt64"/>
<data name="MethodStartAddress" inType="win:Pointer" outType="win:HexInt64" />
<data name="MethodSize" inType="win:UInt64" />
<data name="MethodID" inType="win:UInt32" />
<data name="MethodFlags" inType="win:UInt16" />
<data name="MethodAddressRangeID" inType="win:UInt16" />
<data name="SourceID" inType="win:UInt64" />
<data name="Line" inType="win:UInt32" outType="xs:unsignedInt" />
<data name="Column" inType="win:UInt32" outType="xs:unsignedInt" />
<data name="MethodName" inType="win:UnicodeString" outType="xs:string" />
</template>
</templates>

<events>
<event value="1"
<event value="1"
opcode="NODE_HTTP_SERVER_REQUEST"
template="node_http_server_request"
symbol="NODE_HTTP_SERVER_REQUEST_EVENT"
level="win:Informational"/>
<event value="2"
<event value="2"
opcode="NODE_HTTP_SERVER_RESPONSE"
template="node_connection"
symbol="NODE_HTTP_SERVER_RESPONSE_EVENT"
level="win:Informational"/>
<event value="3"
<event value="3"
opcode="NODE_HTTP_CLIENT_REQUEST"
template="node_http_client_request"
symbol="NODE_HTTP_CLIENT_REQUEST_EVENT"
level="win:Informational"/>
<event value="4"
<event value="4"
opcode="NODE_HTTP_CLIENT_RESPONSE"
template="node_connection"
symbol="NODE_HTTP_CLIENT_RESPONSE_EVENT"
level="win:Informational"/>
<event value="5"
<event value="5"
opcode="NODE_NET_SERVER_CONNECTION"
template="node_connection"
symbol="NODE_NET_SERVER_CONNECTION_EVENT"
level="win:Informational"/>
<event value="6"
<event value="6"
opcode="NODE_NET_STREAM_END"
template="node_connection"
symbol="NODE_NET_STREAM_END_EVENT"
level="win:Informational"/>
<event value="7"
<event value="7"
opcode="NODE_GC_START"
template="node_gc"
symbol="NODE_GC_START_EVENT"
level="win:Informational"/>
<event value="8"
<event value="8"
opcode="NODE_GC_DONE"
template="node_gc"
symbol="NODE_GC_DONE_EVENT"
level="win:Informational"/>
<event value="9"
level="win:Informational"
opcode="MethodLoad"
symbol="MethodLoad"
task="MethodRuntime"
template="MethodLoadUnload"/>
<event value="21"
opcode="NODE_V8SYMBOL_REMOVE"
template="V8AddressChange"
symbol="NODE_V8SYMBOL_REMOVE_EVENT"
level="win:Informational" />
<event value="22"
opcode="NODE_V8SYMBOL_MOVE"
template="V8AddressChange"
symbol="NODE_V8SYMBOL_MOVE_EVENT"
level="win:Informational" />
<event value="23"
opcode="NODE_V8SYMBOL_RESET"
symbol="NODE_V8SYMBOL_RESET_EVENT"
level="win:Informational" />
</events>
</provider>
</events>
@@ -71,6 +71,13 @@ class ArrayBuffer {
v8::Local<v8::ObjectTemplate> instance = ft_cache->InstanceTemplate();
instance->SetInternalFieldCount(1); // Buffer.

v8::Local<v8::Signature> default_signature = v8::Signature::New(ft_cache);

instance->Set(v8::String::New("slice"),
v8::FunctionTemplate::New(&ArrayBuffer::slice,
v8::Handle<v8::Value>(),
default_signature));

return ft_cache;
}

@@ -139,6 +146,42 @@ class ArrayBuffer {

return args.This();
}

static v8::Handle<v8::Value> slice(const v8::Arguments& args) {
if (args.Length() < 1)
return ThrowError("Wrong number of arguments.");

unsigned int length =
args.This()->Get(v8::String::New("byteLength"))->Uint32Value();
int begin = args[0]->Int32Value();
int end = length;
if (args.Length() > 1)
end = args[1]->Int32Value();

if (begin < 0) begin = length + begin;
if (begin < 0) begin = 0;
if (static_cast<unsigned>(begin) > length) begin = length;

if (end < 0) end = length + end;
if (end < 0) end = 0;
if (static_cast<unsigned>(end) > length) end = length;

if (begin > end) begin = end;

unsigned int slice_length = end - begin;
v8::Local<v8::Value> argv[] = {
v8::Integer::New(slice_length)};
v8::Local<v8::Object> buffer = ArrayBuffer::GetTemplate()->
GetFunction()->NewInstance(1, argv);

if (buffer.IsEmpty()) return v8::Undefined(); // constructor failed

void* src = args.This()->GetPointerFromInternalField(0);
void* dest = buffer->GetPointerFromInternalField(0);
memcpy(dest, static_cast<char*>(src) + begin, slice_length);

return buffer;
}
};

static bool checkAlignment(size_t val, unsigned int bytes) {
@@ -0,0 +1 @@
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
@@ -0,0 +1,89 @@
// 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.

/*
* Tests to verify slice functionality of ArrayBuffer.
* (http://www.khronos.org/registry/typedarray/specs/latest/#5)
*/

var common = require('../common');
var assert = require('assert');

var buffer = new ArrayBuffer(8);
var sub;
var i;

for (var i = 0; i < 8; i++) {
buffer[i] = i;
}

// slice a copy of buffer
sub = buffer.slice(2, 6);

// make sure it copied correctly
assert.ok(sub instanceof ArrayBuffer);
assert.equal(sub.byteLength, 4);

for (i = 0; i < 4; i++) {
assert.equal(sub[i], i+2);
}

// modifications to the new slice shouldn't affect the original
sub[0] = 999;

for (i = 0; i < 8; i++) {
assert.equal(buffer[i], i);
}

// test optional end param
sub = buffer.slice(5);

assert.ok(sub instanceof ArrayBuffer);
assert.equal(sub.byteLength, 3);
for (i = 0; i < 3; i++) {
assert.equal(sub[i], i+5);
}

// test clamping
sub = buffer.slice(-3, -1);

assert.ok(sub instanceof ArrayBuffer);
assert.equal(sub.byteLength, 2);
for (i = 0; i < 2; i++) {
assert.equal(sub[i], i+5);
}

// test invalid clamping range
sub = buffer.slice(-1, -3);

assert.ok(sub instanceof ArrayBuffer);
assert.equal(sub.byteLength, 0);

// test wrong number of arguments
var gotError = false;

try {
sub = buffer.slice();
} catch (e) {
gotError = true;
}

assert.ok(gotError);
@@ -31,6 +31,8 @@ if (process.argv[2] === 'child') {

server.on('connection', function(socket) {

socket.resume();

process.on('disconnect', function() {
socket.end((process.connected).toString());
});
@@ -23,31 +23,59 @@ var assert = require('assert');
var common = require('../common');
var fork = require('child_process').fork;
var net = require('net');
var count = 12;

if (process.argv[2] === 'child') {

var endMe = null;
var needEnd = [];

process.on('message', function(m, socket) {
if (!socket) return;

console.error('got socket', m);

// will call .end('end') or .write('write');
socket[m](m);

socket.resume();

socket.on('data', function() {
console.error('%d socket.data', process.pid, m);
});

socket.on('end', function() {
console.error('%d socket.end', process.pid, m);
});

// store the unfinished socket
if (m === 'write') {
endMe = socket;
needEnd.push(socket);
}

socket.on('close', function() {
console.error('%d socket.close', process.pid, m);
});

socket.on('finish', function() {
console.error('%d socket finished', process.pid, m);
});
});

process.on('message', function(m) {
if (m !== 'close') return;
endMe.end('end');
endMe = null;
console.error('got close message');
needEnd.forEach(function(endMe, i) {
console.error('%d ending %d', process.pid, i);
endMe.end('end');
});
});

process.on('disconnect', function() {
endMe.end('end');
console.error('%d process disconnect, ending', process.pid);
needEnd.forEach(function(endMe, i) {
console.error('%d ending %d', process.pid, i);
endMe.end('end');
});
endMe = null;
});

@@ -61,7 +89,7 @@ if (process.argv[2] === 'child') {

var connected = 0;
server.on('connection', function(socket) {
switch (connected) {
switch (connected % 6) {
case 0:
child1.send('end', socket); break;
case 1:
@@ -77,25 +105,31 @@ if (process.argv[2] === 'child') {
}
connected += 1;

if (connected === 6) {
if (connected === count) {
closeServer();
}
});

var disconnected = 0;
server.on('listening', function() {

var j = 6, client;
var j = count, client;
while (j--) {
client = net.connect(common.PORT, '127.0.0.1');
client.on('close', function() {
console.error('CLIENT: close event in master');
disconnected += 1;
});
// XXX This resume() should be unnecessary.
// a stream high water mark should be enough to keep
// consuming the input.
client.resume();
}
});

var closeEmitted = false;
server.on('close', function() {
console.error('server close');
closeEmitted = true;

child1.kill();
@@ -107,23 +141,27 @@ if (process.argv[2] === 'child') {

var timeElasped = 0;
var closeServer = function() {
console.error('closeServer');
var startTime = Date.now();
server.on('close', function() {
console.error('emit(close)');
timeElasped = Date.now() - startTime;
});

console.error('calling server.close');
server.close();

setTimeout(function() {
console.error('sending close to children');
child1.send('close');
child2.send('close');
child3.disconnect();
}, 200);
};

process.on('exit', function() {
assert.equal(disconnected, 6);
assert.equal(connected, 6);
assert.equal(disconnected, count);
assert.equal(connected, count);
assert.ok(closeEmitted);
assert.ok(timeElasped >= 190 && timeElasped <= 1000,
'timeElasped was not between 190 and 1000 ms');
@@ -81,13 +81,15 @@ else if (cluster.isMaster) {
var check = function(type, result) {
checks[type].receive = true;
checks[type].correct = result;
console.error('check', checks);

var missing = false;
forEach(checks, function(type) {
if (type.receive === false) missing = true;
});

if (missing === false) {
console.error('end client');
client.end();
}
};
@@ -230,15 +230,20 @@ var rfc4231 = [

for (var i = 0, l = rfc4231.length; i < l; i++) {
for (var hash in rfc4231[i]['hmac']) {
var str = crypto.createHmac(hash, rfc4231[i].key);
str.end(rfc4231[i].data);
var strRes = str.read().toString('hex');
var result = crypto.createHmac(hash, rfc4231[i]['key'])
.update(rfc4231[i]['data'])
.digest('hex');
if (rfc4231[i]['truncate']) {
result = result.substr(0, 32); // first 128 bits == 32 hex chars
strRes = strRes.substr(0, 32);
}
assert.equal(rfc4231[i]['hmac'][hash],
result,
'Test HMAC-' + hash + ': Test case ' + (i + 1) + ' rfc 4231');
assert.equal(strRes, result, 'Should get same result from stream');
}
}

@@ -373,6 +378,18 @@ var a2 = crypto.createHash('sha256').update('Test123').digest('base64');
var a3 = crypto.createHash('sha512').update('Test123').digest(); // binary
var a4 = crypto.createHash('sha1').update('Test123').digest('buffer');

// stream interface
var a5 = crypto.createHash('sha512');
a5.end('Test123');
a5 = a5.read();

var a6 = crypto.createHash('sha512');
a6.write('Te');
a6.write('st');
a6.write('123');
a6.end();
a6 = a6.read();

assert.equal(a0, '8308651804facb7b9af8ffc53a33a22d6a1c8ac2', 'Test SHA1');
assert.equal(a1, 'h\u00ea\u00cb\u0097\u00d8o\fF!\u00fa+\u000e\u0017\u00ca' +
'\u00bd\u008c', 'Test MD5 as binary');
@@ -392,6 +409,10 @@ assert.deepEqual(a4,
new Buffer('8308651804facb7b9af8ffc53a33a22d6a1c8ac2', 'hex'),
'Test SHA1');

// stream interface should produce the same result.
assert.deepEqual(a5, a3, 'stream interface is consistent');
assert.deepEqual(a6, a3, 'stream interface is consistent');

// Test multiple updates to same hash
var h1 = crypto.createHash('sha1').update('Test123').digest('hex');
var h2 = crypto.createHash('sha1').update('Test').update('123').digest('hex');
@@ -419,6 +440,11 @@ assert.throws(function() {
var s1 = crypto.createSign('RSA-SHA1')
.update('Test123')
.sign(keyPem, 'base64');
var s1stream = crypto.createSign('RSA-SHA1');
s1stream.end('Test123');
s1stream = s1stream.sign(keyPem, 'base64');
assert.equal(s1, s1stream, 'Stream produces same output');

var verified = crypto.createVerify('RSA-SHA1')
.update('Test')
.update('123')
@@ -427,13 +453,25 @@ assert.strictEqual(verified, true, 'sign and verify (base 64)');

var s2 = crypto.createSign('RSA-SHA256')
.update('Test123')
.sign(keyPem); // binary
.sign(keyPem, 'binary');
var s2stream = crypto.createSign('RSA-SHA256');
s2stream.end('Test123');
s2stream = s2stream.sign(keyPem, 'binary');
assert.equal(s2, s2stream, 'Stream produces same output');

var verified = crypto.createVerify('RSA-SHA256')
.update('Test')
.update('123')
.verify(certPem, s2); // binary
.verify(certPem, s2, 'binary');
assert.strictEqual(verified, true, 'sign and verify (binary)');

var verStream = crypto.createVerify('RSA-SHA256');
verStream.write('Tes');
verStream.write('t12');
verStream.end('3');
verified = verStream.verify(certPem, s2, 'binary');
assert.strictEqual(verified, true, 'sign and verify (stream)');

var s3 = crypto.createSign('RSA-SHA1')
.update('Test123')
.sign(keyPem, 'buffer');
@@ -443,6 +481,13 @@ var verified = crypto.createVerify('RSA-SHA1')
.verify(certPem, s3);
assert.strictEqual(verified, true, 'sign and verify (buffer)');

var verStream = crypto.createVerify('RSA-SHA1');
verStream.write('Tes');
verStream.write('t12');
verStream.end('3');
verified = verStream.verify(certPem, s3);
assert.strictEqual(verified, true, 'sign and verify (stream)');


function testCipher1(key) {
// Test encryption and decryption
@@ -460,6 +505,20 @@ function testCipher1(key) {
txt += decipher.final('utf8');

assert.equal(txt, plaintext, 'encryption and decryption');

// streaming cipher interface
// NB: In real life, it's not guaranteed that you can get all of it
// in a single read() like this. But in this case, we know it's
// quite small, so there's no harm.
var cStream = crypto.createCipher('aes192', key);
cStream.end(plaintext);
ciph = cStream.read();

var dStream = crypto.createDecipher('aes192', key);
dStream.end(ciph);
txt = dStream.read().toString('utf8');

assert.equal(txt, plaintext, 'encryption and decryption with streams');
}


@@ -500,6 +559,20 @@ function testCipher3(key, iv) {
txt += decipher.final('utf8');

assert.equal(txt, plaintext, 'encryption and decryption with key and iv');

// streaming cipher interface
// NB: In real life, it's not guaranteed that you can get all of it
// in a single read() like this. But in this case, we know it's
// quite small, so there's no harm.
var cStream = crypto.createCipheriv('des-ede3-cbc', key, iv);
cStream.end(plaintext);
ciph = cStream.read();

var dStream = crypto.createDecipheriv('des-ede3-cbc', key, iv);
dStream.end(ciph);
txt = dStream.read().toString('utf8');

assert.equal(txt, plaintext, 'streaming cipher iv');
}


@@ -155,7 +155,8 @@ setTimeout(function() {
err = err + '. Expected: ' + expected[0].lines.shift();
}
quit();
child.kill('SIGKILL');
child.kill('SIGINT');
child.kill('SIGTERM');

// give the sigkill time to work.
setTimeout(function() {
@@ -192,7 +192,8 @@ setTimeout(function() {
err = err + '. Expected: ' + expected[0].lines.shift();
}
quit();
child.kill('SIGKILL');
child.kill('SIGINT');
child.kill('SIGTERM');

// give the sigkill time to work.
setTimeout(function() {
@@ -22,46 +22,50 @@
var common = require('../common');
var assert = require('assert');

var path = require('path'),
fs = require('fs'),
fn = path.join(common.tmpDir, 'write.txt'),
file = fs.createWriteStream(fn),
var path = require('path');
var fs = require('fs');
var fn = path.join(common.tmpDir, 'write.txt');
var file = fs.createWriteStream(fn, {
lowWaterMark: 0,
highWaterMark: 10
});

EXPECTED = '012345678910',
var EXPECTED = '012345678910';

callbacks = {
var callbacks = {
open: -1,
drain: -2,
close: -1,
endCb: -1
close: -1
};

file
.on('open', function(fd) {
console.error('open!');
callbacks.open++;
assert.equal('number', typeof fd);
})
.on('error', function(err) {
throw err;
console.error('error!', err.stack);
})
.on('drain', function() {
console.error('drain!', callbacks.drain);
callbacks.drain++;
if (callbacks.drain == -1) {
assert.equal(EXPECTED, fs.readFileSync(fn));
assert.equal(EXPECTED, fs.readFileSync(fn, 'utf8'));
file.write(EXPECTED);
} else if (callbacks.drain == 0) {
assert.equal(EXPECTED + EXPECTED, fs.readFileSync(fn));
file.end(function(err) {
assert.ok(!err);
callbacks.endCb++;
});
assert.equal(EXPECTED + EXPECTED, fs.readFileSync(fn, 'utf8'));
file.end();
}
})
.on('close', function() {
console.error('close!');
assert.strictEqual(file.bytesWritten, EXPECTED.length * 2);

callbacks.close++;
assert.throws(function() {
console.error('write after end should not be allowed');
file.write('should not work anymore');
});

@@ -70,12 +74,13 @@ file

for (var i = 0; i < 11; i++) {
(function(i) {
assert.strictEqual(false, file.write(i));
file.write('' + i);
})(i);
}

process.on('exit', function() {
for (var k in callbacks) {
assert.equal(0, callbacks[k], k + ' count off by ' + callbacks[k]);
}
console.log('ok');
});
@@ -22,18 +22,18 @@
var common = require('../common');
var assert = require('assert');

var path = require('path'),
fs = require('fs'),
util = require('util');
var path = require('path');
var fs = require('fs');
var util = require('util');


var filepath = path.join(common.tmpDir, 'write.txt'),
file;
var filepath = path.join(common.tmpDir, 'write.txt');
var file;

var EXPECTED = '012345678910';

var cb_expected = 'write open drain write drain close error ',
cb_occurred = '';
var cb_expected = 'write open drain write drain close error ';
var cb_occurred = '';

var countDrains = 0;

@@ -47,6 +47,8 @@ process.on('exit', function() {
assert.strictEqual(cb_occurred, cb_expected,
'events missing or out of order: "' +
cb_occurred + '" !== "' + cb_expected + '"');
} else {
console.log('ok');
}
});

@@ -59,22 +61,30 @@ function removeTestFile() {

removeTestFile();

file = fs.createWriteStream(filepath);
// drain at 0, return false at 10.
file = fs.createWriteStream(filepath, {
lowWaterMark: 0,
highWaterMark: 11
});

file.on('open', function(fd) {
console.error('open');
cb_occurred += 'open ';
assert.equal(typeof fd, 'number');
});

file.on('drain', function() {
console.error('drain');
cb_occurred += 'drain ';
++countDrains;
if (countDrains === 1) {
assert.equal(fs.readFileSync(filepath), EXPECTED);
file.write(EXPECTED);
console.error('drain=1, write again');
assert.equal(fs.readFileSync(filepath, 'utf8'), EXPECTED);
console.error('ondrain write ret=%j', file.write(EXPECTED));
cb_occurred += 'write ';
} else if (countDrains == 2) {
assert.equal(fs.readFileSync(filepath), EXPECTED + EXPECTED);
console.error('second drain, end');
assert.equal(fs.readFileSync(filepath, 'utf8'), EXPECTED + EXPECTED);
file.end();
}
});
@@ -88,11 +98,15 @@ file.on('close', function() {

file.on('error', function(err) {
cb_occurred += 'error ';
assert.ok(err.message.indexOf('not writable') >= 0);
assert.ok(err.message.indexOf('write after end') >= 0);
});


for (var i = 0; i < 11; i++) {
assert.strictEqual(file.write(i), false);
var ret = file.write(i + '');
console.error('%d %j', i, ret);

// return false when i hits 10
assert(ret === (i != 10));
}
cb_occurred += 'write ';
@@ -60,12 +60,10 @@ file.on('data', function(data) {

paused = true;
file.pause();
assert.ok(file.paused);

setTimeout(function() {
paused = false;
file.resume();
assert.ok(!file.paused);
}, 10);
});

@@ -77,7 +75,6 @@ file.on('end', function(chunk) {

file.on('close', function() {
callbacks.close++;
assert.ok(!file.readable);

//assert.equal(fs.readFileSync(fn), fileContent);
});
@@ -104,6 +101,7 @@ process.on('exit', function() {
assert.equal(2, callbacks.close);
assert.equal(30000, file.length);
assert.equal(10000, file3.length);
console.error('ok');
});

var file4 = fs.createReadStream(rangeFile, {bufferSize: 1, start: 1, end: 2});
@@ -31,7 +31,8 @@ var writeEndOk = false;
var file = path.join(common.tmpDir, 'write-end-test.txt');
var stream = fs.createWriteStream(file);

stream.end('a\n', 'utf8', function() {
stream.end('a\n', 'utf8');
stream.on('close', function() {
var content = fs.readFileSync(file, 'utf8');
assert.equal(content, 'a\n');
writeEndOk = true;
@@ -25,12 +25,14 @@ var net = require('net');
var closed = false;

var server = net.createServer(function(s) {
console.error('SERVER: got connection');
s.end();
});

server.listen(common.PORT, function() {
var c = net.createConnection(common.PORT);
c.on('close', function() {
console.error('connection closed');
assert.strictEqual(c._handle, null);
closed = true;
assert.doesNotThrow(function() {
@@ -41,12 +41,15 @@ for (var i = 255; i >= 0; i--) {

// safe constructor
var echoServer = net.Server(function(connection) {
// connection._readableState.lowWaterMark = 0;
console.error('SERVER got connection');
connection.setEncoding('binary');
connection.on('data', function(chunk) {
common.error('recved: ' + JSON.stringify(chunk));
common.error('SERVER recved: ' + JSON.stringify(chunk));
connection.write(chunk, 'binary');
});
connection.on('end', function() {
console.error('SERVER ending');
connection.end();
});
});
@@ -55,29 +58,44 @@ echoServer.listen(common.PORT);
var recv = '';

echoServer.on('listening', function() {
console.error('SERVER listening');
var j = 0;
var c = net.createConnection(common.PORT);
var c = net.createConnection({
port: common.PORT
});

// c._readableState.lowWaterMark = 0;

c.setEncoding('binary');
c.on('data', function(chunk) {
if (j < 256) {
common.error('write ' + j);
console.error('CLIENT data %j', chunk);
var n = j + chunk.length;
while (j < n && j < 256) {
common.error('CLIENT write ' + j);
c.write(String.fromCharCode(j), 'binary');
j++;
} else {
}
if (j === 256) {
console.error('CLIENT ending');
c.end();
}
recv += chunk;
});

c.on('connect', function() {
console.error('CLIENT connected, writing');
c.write(binaryString, 'binary');
});

c.on('close', function() {
console.error('CLIENT closed');
console.dir(recv);
echoServer.close();
});

c.on('finish', function() {
console.error('CLIENT finished');
});
});

process.on('exit', function() {
@@ -34,33 +34,40 @@ var count = 0;
var tcp = net.Server(function(s) {
console.log('tcp server connection');

// trigger old mode.
s.resume();

s.on('end', function() {
bytesRead += s.bytesRead;
console.log('tcp socket disconnect #' + count);
});
});

tcp.listen(common.PORT, function() {
tcp.listen(common.PORT, function doTest() {
console.error('listening');
var socket = net.createConnection(tcpPort);

socket.on('connect', function() {
count++;
console.log('tcp client connection #' + count);
console.error('CLIENT connect #%d', count);

socket.write('foo', function() {
console.error('CLIENT: write cb');
socket.end('bar');
});
});

socket.on('end', function() {
socket.on('finish', function() {
bytesWritten += socket.bytesWritten;
console.log('tcp client disconnect #' + count);
console.error('CLIENT end event #%d', count);
});

socket.on('close', function() {
console.error('CLIENT close event #%d', count);
console.log('Bytes read: ' + bytesRead);
console.log('Bytes written: ' + bytesWritten);
if (count < 2) {
console.error('RECONNECTING');
socket.connect(tcpPort);
} else {
tcp.close();
@@ -28,6 +28,8 @@ var timeoutCount = 0;
var server = net.createServer(function(stream) {
stream.setTimeout(100);

stream.resume();

stream.on('timeout', function() {
console.log('timeout');
// try to reset the timeout.
@@ -38,6 +38,7 @@ var tcp = net.Server(function(s) {
});

s.on('end', function() {
console.error('SERVER: end', buf.toString());
assert.equal(buf, "L'État, c'est moi");
console.log('tcp socket disconnect');
s.end();
@@ -50,7 +51,7 @@ var tcp = net.Server(function(s) {
});

tcp.listen(common.PORT, function() {
var socket = net.Stream();
var socket = net.Stream({ highWaterMark: 0 });

console.log('Connecting to socket ');

@@ -77,6 +78,7 @@ tcp.listen(common.PORT, function() {
{}
].forEach(function(v) {
function f() {
console.error('write', v);
socket.write(v);
}
assert.throws(f, TypeError);
@@ -90,12 +92,17 @@ tcp.listen(common.PORT, function() {
// We're still connecting at this point so the datagram is first pushed onto
// the connect queue. Make sure that it's not added to `bytesWritten` again
// when the actual write happens.
var r = socket.write(a, function() {
var r = socket.write(a, function(er) {
console.error('write cb');
dataWritten = true;
assert.ok(connectHappened);
assert.equal(socket.bytesWritten, Buffer(a + b).length);
console.error('socket.bytesWritten', socket.bytesWritten);
//assert.equal(socket.bytesWritten, Buffer(a + b).length);
console.error('data written');
});
console.error('socket.bytesWritten', socket.bytesWritten);
console.error('write returned', r);

assert.equal(socket.bytesWritten, Buffer(a).length);

assert.equal(false, r);
@@ -27,6 +27,7 @@ var serverGotEnd = false;
var clientGotEnd = false;

var server = net.createServer({allowHalfOpen: true}, function(socket) {
socket.resume();
socket.on('end', function() {
serverGotEnd = true;
});
@@ -39,6 +40,8 @@ server.listen(common.PORT, function() {
port: common.PORT,
allowHalfOpen: true
}, function() {
console.error('client connect cb');
client.resume();
client.on('end', function() {
clientGotEnd = true;
setTimeout(function() {
@@ -53,6 +56,7 @@ server.listen(common.PORT, function() {
});

process.on('exit', function() {
console.error('exit', serverGotEnd, clientGotEnd);
assert(serverGotEnd);
assert(clientGotEnd);
});
@@ -36,6 +36,15 @@ assert.equal(net.isIP('2001:dead::'), 6);
assert.equal(net.isIP('2001:dead:beef::'), 6);
assert.equal(net.isIP('2001:dead:beef:1::'), 6);
assert.equal(net.isIP('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'), 6);
assert.equal(net.isIP(':2001:252:0:1::2008:6:'), 0);
assert.equal(net.isIP(':2001:252:0:1::2008:6'), 0);
assert.equal(net.isIP('2001:252:0:1::2008:6:'), 0);
assert.equal(net.isIP('2001:252::1::2008:6'), 0);
assert.equal(net.isIP('::2001:252:1:2008:6'), 6);
assert.equal(net.isIP('::2001:252:1:1.1.1.1'), 6);
assert.equal(net.isIP('::2001:252:1:255.255.255.255'), 6);
assert.equal(net.isIP('::2001:252:1:255.255.255.255.76'), 0);
assert.equal(net.isIP('::anything'), 0);
assert.equal(net.isIP('::1'), 6);
assert.equal(net.isIP('::'), 6);
assert.equal(net.isIP('0000:0000:0000:0000:0000:0000:12345:0000'), 0);
@@ -49,4 +58,4 @@ assert.equal(net.isIPv4('2001:252:0:1::2008:6'), false);

assert.equal(net.isIPv6('127.0.0.1'), false);
assert.equal(net.isIPv6('example.com'), false);
assert.equal(net.isIPv6('2001:252:0:1::2008:6'), true);
assert.equal(net.isIPv6('2001:252:0:1::2008:6'), true);
@@ -60,6 +60,8 @@ function pingPongTest(port, host) {
});

socket.on('end', function() {
console.error(socket);
assert.equal(true, socket.allowHalfOpen);
assert.equal(true, socket.writable); // because allowHalfOpen
assert.equal(false, socket.readable);
socket.end();
@@ -129,10 +131,11 @@ function pingPongTest(port, host) {
}

/* All are run at once, so run on different ports */
console.log(common.PIPE);
pingPongTest(common.PIPE);
pingPongTest(20988);
pingPongTest(20989, 'localhost');
pingPongTest(20997, '::1');
pingPongTest(common.PORT);
pingPongTest(common.PORT + 1, 'localhost');
pingPongTest(common.PORT + 2, '::1');

process.on('exit', function() {
assert.equal(4, tests_run);
@@ -30,39 +30,49 @@ var client_recv_count = 0;
var disconnect_count = 0;

var server = net.createServer(function(socket) {
console.error('SERVER: got socket connection');
socket.resume();

socket.on('connect', function() {
console.error('SERVER connect, writing');
socket.write('hello\r\n');
});

socket.on('end', function() {
console.error('SERVER socket end, calling end()');
socket.end();
});

socket.on('close', function(had_error) {
//console.log('server had_error: ' + JSON.stringify(had_error));
console.log('SERVER had_error: ' + JSON.stringify(had_error));
assert.equal(false, had_error);
});
});

server.listen(common.PORT, function() {
console.log('listening');
console.log('SERVER listening');
var client = net.createConnection(common.PORT);

client.setEncoding('UTF8');

client.on('connect', function() {
console.log('client connected.');
console.error('CLIENT connected', client._writableState);
});

client.on('data', function(chunk) {
client_recv_count += 1;
console.log('client_recv_count ' + client_recv_count);
assert.equal('hello\r\n', chunk);
console.error('CLIENT: calling end', client._writableState);
client.end();
});

client.on('end', function() {
console.error('CLIENT end');
});

client.on('close', function(had_error) {
console.log('disconnect');
console.log('CLIENT disconnect');
assert.equal(false, had_error);
if (disconnect_count++ < N)
client.connect(common.PORT); // reconnect
@@ -34,6 +34,7 @@ var server = net.createServer(function(socket) {
socket.on('end', function() {
if (++conns_closed == 2) server.close();
});
socket.resume();
});

server.listen(common.PORT, 'localhost', function() {
@@ -32,12 +32,16 @@ process.on('exit', function() {
});

var server = net.createServer(function(socket) {
socket.resume();

socket.on('error', function(error) {
console.error('got error, closing server', error);
server.close();
gotError = true;
});

setTimeout(function() {
console.error('about to try to write');
socket.write('test', function(e) {
gotWriteCB = true;
});
@@ -39,6 +39,10 @@ assert.equal(os.tmpDir(), '/temp');
process.env.TEMP = '';
assert.equal(os.tmpDir(), t);

var endianness = os.endianness();
console.log('endianness = %s', endianness);
assert.ok(/[BL]E/.test(endianness));

var hostname = os.hostname();
console.log('hostname = %s', hostname);
assert.ok(hostname.length > 0);
@@ -31,6 +31,7 @@ var clientReqComplete = false;
var count = 0;

var server = http.createServer(function(req, res) {
console.error('SERVER request');
var timeoutId;
assert.equal('POST', req.method);
req.pause();
@@ -63,6 +64,8 @@ server.on('listening', function() {

cp.exec(cmd, function(err, stdout, stderr) {
if (err) throw err;
console.error('EXEC returned successfully stdout=%d stderr=%d',
stdout.length, stderr.length);
makeRequest();
});
});
@@ -75,16 +78,26 @@ function makeRequest() {
});

common.error('pipe!');

var s = fs.ReadStream(filename);
s.pipe(req);
s.on('data', function(chunk) {
console.error('FS data chunk=%d', chunk.length);
});
s.on('end', function() {
console.error('FS end');
});
s.on('close', function(err) {
if (err) throw err;
clientReqComplete = true;
common.error('client finished sending request');
});

req.on('response', function(res) {
console.error('RESPONSE', res.statusCode, res.headers);
res.resume();
res.on('end', function() {
console.error('RESPONSE end');
server.close();
});
});
@@ -125,6 +125,7 @@ function startClient() {
});
req.write(buffer);
req.end();
console.error('ended request', req);
}

process.on('exit', function() {
@@ -32,100 +32,104 @@ function FakeInput() {
inherits(FakeInput, EventEmitter);
FakeInput.prototype.resume = function() {};
FakeInput.prototype.pause = function() {};
FakeInput.prototype.write = function() {};
FakeInput.prototype.end = function() {};

var fi;
var rli;
var called;
[ true, false ].forEach(function(terminal) {
var fi;
var rli;
var called;

// sending a full line
fi = new FakeInput();
rli = new readline.Interface(fi, {});
called = false;
rli.on('line', function(line) {
called = true;
assert.equal(line, 'asdf\n');
});
fi.emit('data', 'asdf\n');
assert.ok(called);
// sending a full line
fi = new FakeInput();
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
called = false;
rli.on('line', function(line) {
called = true;
assert.equal(line, 'asdf');
});
fi.emit('data', 'asdf\n');
assert.ok(called);

// sending a blank line
fi = new FakeInput();
rli = new readline.Interface(fi, {});
called = false;
rli.on('line', function(line) {
called = true;
assert.equal(line, '\n');
});
fi.emit('data', '\n');
assert.ok(called);
// sending a blank line
fi = new FakeInput();
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
called = false;
rli.on('line', function(line) {
called = true;
assert.equal(line, '');
});
fi.emit('data', '\n');
assert.ok(called);

// sending a single character with no newline
fi = new FakeInput();
rli = new readline.Interface(fi, {});
called = false;
rli.on('line', function(line) {
called = true;
});
fi.emit('data', 'a');
assert.ok(!called);
rli.close();
// sending a single character with no newline
fi = new FakeInput();
rli = new readline.Interface(fi, {});
called = false;
rli.on('line', function(line) {
called = true;
});
fi.emit('data', 'a');
assert.ok(!called);
rli.close();

// sending a single character with no newline and then a newline
fi = new FakeInput();
rli = new readline.Interface(fi, {});
called = false;
rli.on('line', function(line) {
called = true;
assert.equal(line, 'a\n');
});
fi.emit('data', 'a');
assert.ok(!called);
fi.emit('data', '\n');
assert.ok(called);
rli.close();
// sending a single character with no newline and then a newline
fi = new FakeInput();
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
called = false;
rli.on('line', function(line) {
called = true;
assert.equal(line, 'a');
});
fi.emit('data', 'a');
assert.ok(!called);
fi.emit('data', '\n');
assert.ok(called);
rli.close();

// sending multiple newlines at once
fi = new FakeInput();
rli = new readline.Interface(fi, {});
var expectedLines = ['foo\n', 'bar\n', 'baz\n'];
var callCount = 0;
rli.on('line', function(line) {
assert.equal(line, expectedLines[callCount]);
callCount++;
});
fi.emit('data', expectedLines.join(''));
assert.equal(callCount, expectedLines.length);
rli.close();
// sending multiple newlines at once
fi = new FakeInput();
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
var expectedLines = ['foo', 'bar', 'baz'];
var callCount = 0;
rli.on('line', function(line) {
assert.equal(line, expectedLines[callCount]);
callCount++;
});
fi.emit('data', expectedLines.join('\n') + '\n');
assert.equal(callCount, expectedLines.length);
rli.close();

// sending multiple newlines at once that does not end with a new line
fi = new FakeInput();
rli = new readline.Interface(fi, {});
var expectedLines = ['foo\n', 'bar\n', 'baz\n', 'bat'];
var callCount = 0;
rli.on('line', function(line) {
assert.equal(line, expectedLines[callCount]);
callCount++;
});
fi.emit('data', expectedLines.join(''));
assert.equal(callCount, expectedLines.length - 1);
rli.close();
// sending multiple newlines at once that does not end with a new line
fi = new FakeInput();
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
expectedLines = ['foo', 'bar', 'baz', 'bat'];
callCount = 0;
rli.on('line', function(line) {
assert.equal(line, expectedLines[callCount]);
callCount++;
});
fi.emit('data', expectedLines.join('\n'));
assert.equal(callCount, expectedLines.length - 1);
rli.close();

// sending a multi-byte utf8 char over multiple writes
var buf = Buffer('☮', 'utf8');
fi = new FakeInput();
rli = new readline.Interface(fi, {});
callCount = 0;
rli.on('line', function(line) {
callCount++;
assert.equal(line, buf.toString('utf8') + '\n');
});
[].forEach.call(buf, function(i) {
fi.emit('data', Buffer([i]));
});
assert.equal(callCount, 0);
fi.emit('data', '\n');
assert.equal(callCount, 1);
rli.close();
// sending a multi-byte utf8 char over multiple writes
var buf = Buffer('☮', 'utf8');
fi = new FakeInput();
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
callCount = 0;
rli.on('line', function(line) {
callCount++;
assert.equal(line, buf.toString('utf8'));
});
[].forEach.call(buf, function(i) {
fi.emit('data', Buffer([i]));
});
assert.equal(callCount, 0);
fi.emit('data', '\n');
assert.equal(callCount, 1);
rli.close();

assert.deepEqual(fi.listeners('end'), []);
assert.deepEqual(fi.listeners('data'), []);
assert.deepEqual(fi.listeners('end'), []);
assert.deepEqual(fi.listeners(terminal ? 'keypress' : 'data'), []);
});
@@ -48,8 +48,9 @@ function test1(){
putIn.write = function (data) {
gotWrite = true;
if (data.length) {

// inspect output matches repl output
assert.equal(data, util.inspect(require('fs'), null, null, false) + '\n');
assert.equal(data, util.inspect(require('fs'), null, 2, false) + '\n');
// globally added lib matches required lib
assert.equal(global.fs, require('fs'));
test2();
@@ -119,6 +119,9 @@ function error_test() {
// You can recover with the .break command
{ client: client_unix, send: '.break',
expect: prompt_unix },
// Floating point numbers are not interpreted as REPL commands.
{ client: client_unix, send: '.1234',
expect: '0.1234' },
// Can parse valid JSON
{ client: client_unix, send: 'JSON.parse(\'{"valid": "json"}\');',
expect: '{ valid: \'json\' }'},
@@ -0,0 +1,312 @@
// 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.js');
var R = require('_stream_readable');
var assert = require('assert');

var util = require('util');
var EE = require('events').EventEmitter;

function TestReader(n) {
R.apply(this);
this._buffer = new Buffer(n || 100);
this._buffer.fill('x');
this._pos = 0;
this._bufs = 10;
}

util.inherits(TestReader, R);

TestReader.prototype.read = function(n) {
var max = this._buffer.length - this._pos;
n = n || max;
n = Math.max(n, 0);
var toRead = Math.min(n, max);
if (toRead === 0) {
// simulate the read buffer filling up with some more bytes some time
// in the future.
setTimeout(function() {
this._pos = 0;
this._bufs -= 1;
if (this._bufs <= 0) {
// read them all!
if (!this.ended) {
this.emit('end');
this.ended = true;
}
} else {
this.emit('readable');
}
}.bind(this), 10);
return null;
}

var ret = this._buffer.slice(this._pos, this._pos + toRead);
this._pos += toRead;
return ret;
};

/////

function TestWriter() {
EE.apply(this);
this.received = [];
this.flush = false;
}

util.inherits(TestWriter, EE);

TestWriter.prototype.write = function(c) {
this.received.push(c.toString());
this.emit('write', c);
return true;

// flip back and forth between immediate acceptance and not.
this.flush = !this.flush;
if (!this.flush) setTimeout(this.emit.bind(this, 'drain'), 10);
return this.flush;
};

TestWriter.prototype.end = function(c) {
if (c) this.write(c);
this.emit('end', this.received);
};

////////

// tiny node-tap lookalike.
var tests = [];
function test(name, fn) {
tests.push([name, fn]);
}

function run() {
var next = tests.shift();
if (!next)
return console.error('ok');

var name = next[0];
var fn = next[1];
console.log('# %s', name);
fn({
same: assert.deepEqual,
equal: assert.equal,
end: run
});
}

process.nextTick(run);


test('a most basic test', function(t) {
var r = new TestReader(20);

var reads = [];
var expect = [ 'x',
'xx',
'xxx',
'xxxx',
'xxxxx',
'xxxxx',
'xxxxxxxx',
'xxxxxxxxx',
'xxx',
'xxxxxxxxxxxx',
'xxxxxxxx',
'xxxxxxxxxxxxxxx',
'xxxxx',
'xxxxxxxxxxxxxxxxxx',
'xx',
'xxxxxxxxxxxxxxxxxxxx',
'xxxxxxxxxxxxxxxxxxxx',
'xxxxxxxxxxxxxxxxxxxx',
'xxxxxxxxxxxxxxxxxxxx',
'xxxxxxxxxxxxxxxxxxxx' ];

r.on('end', function() {
t.same(reads, expect);
t.end();
});

var readSize = 1;
function flow() {
var res;
while (null !== (res = r.read(readSize++))) {
reads.push(res.toString());
}
r.once('readable', flow);
}

flow();
});

test('pipe', function(t) {
var r = new TestReader(5);

var expect = [ 'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx' ]

var w = new TestWriter;
var flush = true;
w.on('end', function(received) {
t.same(received, expect);
t.end();
});

r.pipe(w);
});



[1,2,3,4,5,6,7,8,9].forEach(function(SPLIT) {
test('unpipe', function(t) {
var r = new TestReader(5);

// unpipe after 3 writes, then write to another stream instead.
var expect = [ 'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx' ];
expect = [ expect.slice(0, SPLIT), expect.slice(SPLIT) ];

var w = [ new TestWriter(), new TestWriter() ];

var writes = SPLIT;
w[0].on('write', function() {
if (--writes === 0) {
r.unpipe();
w[0].end();
r.pipe(w[1]);
}
});

var ended = 0;

w[0].on('end', function(results) {
ended++;
t.same(results, expect[0]);
});

w[1].on('end', function(results) {
ended++;
t.equal(ended, 2);
t.same(results, expect[1]);
t.end();
});

r.pipe(w[0]);
});
});


// both writers should get the same exact data.
test('multipipe', function(t) {
var r = new TestReader(5);
var w = [ new TestWriter, new TestWriter ];

var expect = [ 'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx' ];

var c = 2;
w[0].on('end', function(received) {
t.same(received, expect, 'first');
if (--c === 0) t.end();
});
w[1].on('end', function(received) {
t.same(received, expect, 'second');
if (--c === 0) t.end();
});

r.pipe(w[0]);
r.pipe(w[1]);
});


[1,2,3,4,5,6,7,8,9].forEach(function(SPLIT) {
test('multi-unpipe', function(t) {
var r = new TestReader(5);

// unpipe after 3 writes, then write to another stream instead.
var expect = [ 'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx' ];
expect = [ expect.slice(0, SPLIT), expect.slice(SPLIT) ];

var w = [ new TestWriter(), new TestWriter(), new TestWriter() ];

var writes = SPLIT;
w[0].on('write', function() {
if (--writes === 0) {
r.unpipe();
w[0].end();
r.pipe(w[1]);
}
});

var ended = 0;

w[0].on('end', function(results) {
ended++;
t.same(results, expect[0]);
});

w[1].on('end', function(results) {
ended++;
t.equal(ended, 2);
t.same(results, expect[1]);
t.end();
});

r.pipe(w[0]);
r.pipe(w[2]);
});
});
@@ -0,0 +1,76 @@
// 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.js');
var R = require('_stream_readable');
var assert = require('assert');

var fs = require('fs');
var FSReadable = fs.ReadStream;

var path = require('path');
var file = path.resolve(common.fixturesDir, 'x1024.txt');

var size = fs.statSync(file).size;

// expect to see chunks no more than 10 bytes each.
var expectLengths = [];
for (var i = size; i > 0; i -= 10) {
expectLengths.push(Math.min(i, 10));
}

var util = require('util');
var Stream = require('stream');

util.inherits(TestWriter, Stream);

function TestWriter() {
Stream.apply(this);
this.buffer = [];
this.length = 0;
}

TestWriter.prototype.write = function(c) {
this.buffer.push(c.toString());
this.length += c.length;
return true;
};

TestWriter.prototype.end = function(c) {
if (c) this.buffer.push(c.toString());
this.emit('results', this.buffer);
}

var r = new FSReadable(file, { bufferSize: 10 });
var w = new TestWriter();

w.on('results', function(res) {
console.error(res, w.length);
assert.equal(w.length, size);
var l = 0;
assert.deepEqual(res.map(function (c) {
return c.length;
}), expectLengths);
console.log('ok');
});

r.pipe(w, { chunkSize: 10 });
@@ -0,0 +1,109 @@
// 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 common = require('../common.js');
var fromList = require('_stream_readable')._fromList;

// tiny node-tap lookalike.
var tests = [];
function test(name, fn) {
tests.push([name, fn]);
}

function run() {
var next = tests.shift();
if (!next)
return console.error('ok');

var name = next[0];
var fn = next[1];
console.log('# %s', name);
fn({
same: assert.deepEqual,
equal: assert.equal,
end: run
});
}

process.nextTick(run);



test('buffers', function(t) {
// have a length
var len = 16;
var list = [ new Buffer('foog'),
new Buffer('bark'),
new Buffer('bazy'),
new Buffer('kuel') ];

// read more than the first element.
var ret = fromList(6, list, 16);
t.equal(ret.toString(), 'foogba');

// read exactly the first element.
ret = fromList(2, list, 10);
t.equal(ret.toString(), 'rk');

// read less than the first element.
ret = fromList(2, list, 8);
t.equal(ret.toString(), 'ba');

// read more than we have.
ret = fromList(100, list, 6);
t.equal(ret.toString(), 'zykuel');

// all consumed.
t.same(list, []);

t.end();
});

test('strings', function(t) {
// have a length
var len = 16;
var list = [ 'foog',
'bark',
'bazy',
'kuel' ];

// read more than the first element.
var ret = fromList(6, list, 16, true);
t.equal(ret, 'foogba');

// read exactly the first element.
ret = fromList(2, list, 10, true);
t.equal(ret, 'rk');

// read less than the first element.
ret = fromList(2, list, 8, true);
t.equal(ret, 'ba');

// read more than we have.
ret = fromList(100, list, 6, true);
t.equal(ret, 'zykuel');

// all consumed.
t.same(list, []);

t.end();
});