streams: introduce StreamWrap and JSStream
Introduce a way to wrap plain-js `stream.Duplex` streams into C++ StreamBase's child class. With such method at hand it is now possible to pass `stream.Duplex` instance as a `socket` parameter to `tls.connect()`. PR-URL: https://github.com/iojs/io.js/pull/926 Reviewed-By: Chris Dickinson <christopher.s.dickinson@gmail.com>
This commit is contained in:
parent
e00c938d24
commit
1738c77835
118
lib/_stream_wrap.js
Normal file
118
lib/_stream_wrap.js
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
const util = require('util');
|
||||||
|
const Socket = require('net').Socket;
|
||||||
|
const JSStream = process.binding('js_stream').JSStream;
|
||||||
|
const uv = process.binding('uv');
|
||||||
|
|
||||||
|
function StreamWrap(stream) {
|
||||||
|
var handle = new JSStream();
|
||||||
|
|
||||||
|
this.stream = stream;
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
handle.close = function(cb) {
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
handle.isAlive = function() {
|
||||||
|
return self.isAlive();
|
||||||
|
};
|
||||||
|
handle.isClosing = function() {
|
||||||
|
return self.isClosing();
|
||||||
|
};
|
||||||
|
handle.onreadstart = function() {
|
||||||
|
return self.readStart();
|
||||||
|
};
|
||||||
|
handle.onreadstop = function() {
|
||||||
|
return self.readStop();
|
||||||
|
};
|
||||||
|
handle.onshutdown = function(req) {
|
||||||
|
return self.shutdown(req);
|
||||||
|
};
|
||||||
|
handle.onwrite = function(req, bufs) {
|
||||||
|
return self.write(req, bufs);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.stream.pause();
|
||||||
|
this.stream.on('data', function(chunk) {
|
||||||
|
self._handle.readBuffer(chunk);
|
||||||
|
});
|
||||||
|
this.stream.once('end', function() {
|
||||||
|
self._handle.emitEOF();
|
||||||
|
});
|
||||||
|
this.stream.on('error', function(err) {
|
||||||
|
self.emit('error', err);
|
||||||
|
});
|
||||||
|
|
||||||
|
Socket.call(this, {
|
||||||
|
handle: handle
|
||||||
|
});
|
||||||
|
}
|
||||||
|
util.inherits(StreamWrap, Socket);
|
||||||
|
module.exports = StreamWrap;
|
||||||
|
|
||||||
|
// require('_stream_wrap').StreamWrap
|
||||||
|
StreamWrap.StreamWrap = StreamWrap;
|
||||||
|
|
||||||
|
StreamWrap.prototype.isAlive = function isAlive() {
|
||||||
|
return this.readable && this.writable;
|
||||||
|
};
|
||||||
|
|
||||||
|
StreamWrap.prototype.isClosing = function isClosing() {
|
||||||
|
return !this.isAlive();
|
||||||
|
};
|
||||||
|
|
||||||
|
StreamWrap.prototype.readStart = function readStart() {
|
||||||
|
this.stream.resume();
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
StreamWrap.prototype.readStop = function readStop() {
|
||||||
|
this.stream.pause();
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
StreamWrap.prototype.shutdown = function shutdown(req) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.stream.end(function() {
|
||||||
|
// Ensure that write was dispatched
|
||||||
|
setImmediate(function() {
|
||||||
|
self._handle.finishShutdown(req, 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
StreamWrap.prototype.write = function write(req, bufs) {
|
||||||
|
var pending = bufs.length;
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.stream.cork();
|
||||||
|
bufs.forEach(function(buf) {
|
||||||
|
self.stream.write(buf, done);
|
||||||
|
});
|
||||||
|
self.stream.uncork();
|
||||||
|
|
||||||
|
function done(err) {
|
||||||
|
if (!err && --pending !== 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Ensure that this is called once in case of error
|
||||||
|
pending = 0;
|
||||||
|
|
||||||
|
// Ensure that write was dispatched
|
||||||
|
setImmediate(function() {
|
||||||
|
var errCode = 0;
|
||||||
|
if (err) {
|
||||||
|
if (err.code && uv['UV_' + err.code])
|
||||||
|
errCode = uv['UV_' + err.code];
|
||||||
|
else
|
||||||
|
errCode = uv.UV_EPIPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
self._handle.doAfterWrite(req);
|
||||||
|
self._handle.finishWrite(req, errCode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
@ -7,6 +7,8 @@ const tls = require('tls');
|
|||||||
const util = require('util');
|
const util = require('util');
|
||||||
const listenerCount = require('events').listenerCount;
|
const listenerCount = require('events').listenerCount;
|
||||||
const common = require('_tls_common');
|
const common = require('_tls_common');
|
||||||
|
const StreamWrap = require('_stream_wrap').StreamWrap;
|
||||||
|
const Duplex = require('stream').Duplex;
|
||||||
const debug = util.debuglog('tls');
|
const debug = util.debuglog('tls');
|
||||||
const Timer = process.binding('timer_wrap').Timer;
|
const Timer = process.binding('timer_wrap').Timer;
|
||||||
const tls_wrap = process.binding('tls_wrap');
|
const tls_wrap = process.binding('tls_wrap');
|
||||||
@ -224,6 +226,10 @@ function TLSSocket(socket, options) {
|
|||||||
this.authorized = false;
|
this.authorized = false;
|
||||||
this.authorizationError = null;
|
this.authorizationError = null;
|
||||||
|
|
||||||
|
// Wrap plain JS Stream into StreamWrap
|
||||||
|
if (!(socket instanceof net.Socket) && socket instanceof Duplex)
|
||||||
|
socket = new StreamWrap(socket);
|
||||||
|
|
||||||
// Just a documented property to make secure sockets
|
// Just a documented property to make secure sockets
|
||||||
// distinguishable from regular ones.
|
// distinguishable from regular ones.
|
||||||
this.encrypted = true;
|
this.encrypted = true;
|
||||||
@ -280,7 +286,8 @@ TLSSocket.prototype._wrapHandle = function(handle) {
|
|||||||
// Proxy HandleWrap, PipeWrap and TCPWrap methods
|
// Proxy HandleWrap, PipeWrap and TCPWrap methods
|
||||||
proxiedMethods.forEach(function(name) {
|
proxiedMethods.forEach(function(name) {
|
||||||
res[name] = function methodProxy() {
|
res[name] = function methodProxy() {
|
||||||
return handle[name].apply(handle, arguments);
|
if (handle[name])
|
||||||
|
return handle[name].apply(handle, arguments);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -373,7 +380,7 @@ TLSSocket.prototype._init = function(socket) {
|
|||||||
this.setTimeout(options.handshakeTimeout, this._handleTimeout);
|
this.setTimeout(options.handshakeTimeout, this._handleTimeout);
|
||||||
|
|
||||||
// Socket already has some buffered data - emulate receiving it
|
// Socket already has some buffered data - emulate receiving it
|
||||||
if (socket && socket._readableState.length) {
|
if (socket && socket._readableState && socket._readableState.length) {
|
||||||
var buf;
|
var buf;
|
||||||
while ((buf = socket.read()) !== null)
|
while ((buf = socket.read()) !== null)
|
||||||
ssl.receive(buf);
|
ssl.receive(buf);
|
||||||
@ -388,6 +395,10 @@ TLSSocket.prototype._init = function(socket) {
|
|||||||
self._connecting = false;
|
self._connecting = false;
|
||||||
self.emit('connect');
|
self.emit('connect');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on('error', function(err) {
|
||||||
|
self._tlsError(err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assume `tls.connect()`
|
// Assume `tls.connect()`
|
||||||
|
3
node.gyp
3
node.gyp
@ -56,6 +56,7 @@
|
|||||||
'lib/_stream_duplex.js',
|
'lib/_stream_duplex.js',
|
||||||
'lib/_stream_transform.js',
|
'lib/_stream_transform.js',
|
||||||
'lib/_stream_passthrough.js',
|
'lib/_stream_passthrough.js',
|
||||||
|
'lib/_stream_wrap.js',
|
||||||
'lib/string_decoder.js',
|
'lib/string_decoder.js',
|
||||||
'lib/sys.js',
|
'lib/sys.js',
|
||||||
'lib/timers.js',
|
'lib/timers.js',
|
||||||
@ -95,6 +96,7 @@
|
|||||||
'src/fs_event_wrap.cc',
|
'src/fs_event_wrap.cc',
|
||||||
'src/cares_wrap.cc',
|
'src/cares_wrap.cc',
|
||||||
'src/handle_wrap.cc',
|
'src/handle_wrap.cc',
|
||||||
|
'src/js_stream.cc',
|
||||||
'src/node.cc',
|
'src/node.cc',
|
||||||
'src/node_buffer.cc',
|
'src/node_buffer.cc',
|
||||||
'src/node_constants.cc',
|
'src/node_constants.cc',
|
||||||
@ -132,6 +134,7 @@
|
|||||||
'src/env.h',
|
'src/env.h',
|
||||||
'src/env-inl.h',
|
'src/env-inl.h',
|
||||||
'src/handle_wrap.h',
|
'src/handle_wrap.h',
|
||||||
|
'src/js_stream.h',
|
||||||
'src/node.h',
|
'src/node.h',
|
||||||
'src/node_buffer.h',
|
'src/node_buffer.h',
|
||||||
'src/node_constants.h',
|
'src/node_constants.h',
|
||||||
|
@ -17,6 +17,7 @@ namespace node {
|
|||||||
V(FSREQWRAP) \
|
V(FSREQWRAP) \
|
||||||
V(GETADDRINFOREQWRAP) \
|
V(GETADDRINFOREQWRAP) \
|
||||||
V(GETNAMEINFOREQWRAP) \
|
V(GETNAMEINFOREQWRAP) \
|
||||||
|
V(JSSTREAM) \
|
||||||
V(PIPEWRAP) \
|
V(PIPEWRAP) \
|
||||||
V(PROCESSWRAP) \
|
V(PROCESSWRAP) \
|
||||||
V(QUERYWRAP) \
|
V(QUERYWRAP) \
|
||||||
|
@ -107,6 +107,8 @@ namespace node {
|
|||||||
V(ipv4_string, "IPv4") \
|
V(ipv4_string, "IPv4") \
|
||||||
V(ipv6_lc_string, "ipv6") \
|
V(ipv6_lc_string, "ipv6") \
|
||||||
V(ipv6_string, "IPv6") \
|
V(ipv6_string, "IPv6") \
|
||||||
|
V(isalive_string, "isAlive") \
|
||||||
|
V(isclosing_string, "isClosing") \
|
||||||
V(issuer_string, "issuer") \
|
V(issuer_string, "issuer") \
|
||||||
V(issuercert_string, "issuerCertificate") \
|
V(issuercert_string, "issuerCertificate") \
|
||||||
V(kill_signal_string, "killSignal") \
|
V(kill_signal_string, "killSignal") \
|
||||||
@ -141,9 +143,13 @@ namespace node {
|
|||||||
V(onnewsessiondone_string, "onnewsessiondone") \
|
V(onnewsessiondone_string, "onnewsessiondone") \
|
||||||
V(onocspresponse_string, "onocspresponse") \
|
V(onocspresponse_string, "onocspresponse") \
|
||||||
V(onread_string, "onread") \
|
V(onread_string, "onread") \
|
||||||
|
V(onreadstart_string, "onreadstart") \
|
||||||
|
V(onreadstop_string, "onreadstop") \
|
||||||
V(onselect_string, "onselect") \
|
V(onselect_string, "onselect") \
|
||||||
|
V(onshutdown_string, "onshutdown") \
|
||||||
V(onsignal_string, "onsignal") \
|
V(onsignal_string, "onsignal") \
|
||||||
V(onstop_string, "onstop") \
|
V(onstop_string, "onstop") \
|
||||||
|
V(onwrite_string, "onwrite") \
|
||||||
V(output_string, "output") \
|
V(output_string, "output") \
|
||||||
V(order_string, "order") \
|
V(order_string, "order") \
|
||||||
V(owner_string, "owner") \
|
V(owner_string, "owner") \
|
||||||
@ -225,6 +231,7 @@ namespace node {
|
|||||||
V(context, v8::Context) \
|
V(context, v8::Context) \
|
||||||
V(domain_array, v8::Array) \
|
V(domain_array, v8::Array) \
|
||||||
V(fs_stats_constructor_function, v8::Function) \
|
V(fs_stats_constructor_function, v8::Function) \
|
||||||
|
V(jsstream_constructor_template, v8::FunctionTemplate) \
|
||||||
V(module_load_list_array, v8::Array) \
|
V(module_load_list_array, v8::Array) \
|
||||||
V(pipe_constructor_template, v8::FunctionTemplate) \
|
V(pipe_constructor_template, v8::FunctionTemplate) \
|
||||||
V(process_object, v8::Object) \
|
V(process_object, v8::Object) \
|
||||||
|
199
src/js_stream.cc
199
src/js_stream.cc
@ -3,19 +3,218 @@
|
|||||||
#include "async-wrap.h"
|
#include "async-wrap.h"
|
||||||
#include "env.h"
|
#include "env.h"
|
||||||
#include "env-inl.h"
|
#include "env-inl.h"
|
||||||
|
#include "node_buffer.h"
|
||||||
#include "stream_base.h"
|
#include "stream_base.h"
|
||||||
#include "v8.h"
|
#include "v8.h"
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
|
|
||||||
|
using v8::Array;
|
||||||
using v8::Context;
|
using v8::Context;
|
||||||
|
using v8::External;
|
||||||
|
using v8::FunctionCallbackInfo;
|
||||||
|
using v8::FunctionTemplate;
|
||||||
using v8::Handle;
|
using v8::Handle;
|
||||||
|
using v8::HandleScope;
|
||||||
|
using v8::Local;
|
||||||
using v8::Object;
|
using v8::Object;
|
||||||
using v8::Value;
|
using v8::Value;
|
||||||
|
|
||||||
|
|
||||||
|
JSStream::JSStream(Environment* env, Handle<Object> obj, AsyncWrap* parent)
|
||||||
|
: StreamBase(env),
|
||||||
|
AsyncWrap(env, obj, AsyncWrap::PROVIDER_JSSTREAM, parent) {
|
||||||
|
node::Wrap(obj, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSStream::~JSStream() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void* JSStream::Cast() {
|
||||||
|
return static_cast<void*>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AsyncWrap* JSStream::GetAsyncWrap() {
|
||||||
|
return static_cast<AsyncWrap*>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool JSStream::IsAlive() {
|
||||||
|
return MakeCallback(env()->isalive_string(), 0, nullptr)->IsTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool JSStream::IsClosing() {
|
||||||
|
return MakeCallback(env()->isclosing_string(), 0, nullptr)->IsTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int JSStream::ReadStart() {
|
||||||
|
return MakeCallback(env()->onreadstart_string(), 0, nullptr)->Int32Value();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int JSStream::ReadStop() {
|
||||||
|
return MakeCallback(env()->onreadstop_string(), 0, nullptr)->Int32Value();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int JSStream::DoShutdown(ShutdownWrap* req_wrap) {
|
||||||
|
HandleScope scope(env()->isolate());
|
||||||
|
|
||||||
|
Local<Value> argv[] = {
|
||||||
|
req_wrap->object()
|
||||||
|
};
|
||||||
|
|
||||||
|
Local<Value> res =
|
||||||
|
MakeCallback(env()->onshutdown_string(), ARRAY_SIZE(argv), argv);
|
||||||
|
|
||||||
|
return res->Int32Value();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int JSStream::DoWrite(WriteWrap* w,
|
||||||
|
uv_buf_t* bufs,
|
||||||
|
size_t count,
|
||||||
|
uv_stream_t* send_handle) {
|
||||||
|
CHECK_EQ(send_handle, nullptr);
|
||||||
|
|
||||||
|
HandleScope scope(env()->isolate());
|
||||||
|
|
||||||
|
Local<Array> bufs_arr = Array::New(env()->isolate(), count);
|
||||||
|
for (size_t i = 0; i < count; i++)
|
||||||
|
bufs_arr->Set(i, Buffer::New(env(), bufs[0].base, bufs[0].len));
|
||||||
|
|
||||||
|
Local<Value> argv[] = {
|
||||||
|
w->object(),
|
||||||
|
bufs_arr
|
||||||
|
};
|
||||||
|
|
||||||
|
Local<Value> res =
|
||||||
|
MakeCallback(env()->onwrite_string(), ARRAY_SIZE(argv), argv);
|
||||||
|
|
||||||
|
return res->Int32Value();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void JSStream::New(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
// This constructor should not be exposed to public javascript.
|
||||||
|
// Therefore we assert that we are not trying to call this as a
|
||||||
|
// normal function.
|
||||||
|
CHECK(args.IsConstructCall());
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
JSStream* wrap;
|
||||||
|
|
||||||
|
if (args.Length() == 0) {
|
||||||
|
wrap = new JSStream(env, args.This(), nullptr);
|
||||||
|
} else if (args[0]->IsExternal()) {
|
||||||
|
void* ptr = args[0].As<External>()->Value();
|
||||||
|
wrap = new JSStream(env, args.This(), static_cast<AsyncWrap*>(ptr));
|
||||||
|
} else {
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
CHECK(wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void FreeCallback(char* data, void* hint) {
|
||||||
|
// Intentional no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void JSStream::DoAlloc(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
JSStream* wrap = Unwrap<JSStream>(args.Holder());
|
||||||
|
|
||||||
|
uv_buf_t buf;
|
||||||
|
wrap->OnAlloc(args[0]->Int32Value(), &buf);
|
||||||
|
args.GetReturnValue().Set(Buffer::New(wrap->env(),
|
||||||
|
buf.base,
|
||||||
|
buf.len,
|
||||||
|
FreeCallback,
|
||||||
|
nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void JSStream::DoRead(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
JSStream* wrap = Unwrap<JSStream>(args.Holder());
|
||||||
|
|
||||||
|
CHECK(Buffer::HasInstance(args[1]));
|
||||||
|
uv_buf_t buf = uv_buf_init(Buffer::Data(args[1]), Buffer::Length(args[1]));
|
||||||
|
wrap->OnRead(args[0]->Int32Value(), &buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void JSStream::DoAfterWrite(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
JSStream* wrap = Unwrap<JSStream>(args.Holder());
|
||||||
|
WriteWrap* w = Unwrap<WriteWrap>(args[0].As<Object>());
|
||||||
|
|
||||||
|
wrap->OnAfterWrite(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class Wrap>
|
||||||
|
void JSStream::Finish(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Wrap* w = Unwrap<Wrap>(args[0].As<Object>());
|
||||||
|
|
||||||
|
w->Done(args[0]->Int32Value());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void JSStream::ReadBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
JSStream* wrap = Unwrap<JSStream>(args.Holder());
|
||||||
|
|
||||||
|
CHECK(Buffer::HasInstance(args[0]));
|
||||||
|
char* data = Buffer::Data(args[0]);
|
||||||
|
int len = Buffer::Length(args[0]);
|
||||||
|
|
||||||
|
do {
|
||||||
|
uv_buf_t buf;
|
||||||
|
ssize_t avail = len;
|
||||||
|
wrap->OnAlloc(len, &buf);
|
||||||
|
if (static_cast<ssize_t>(buf.len) < avail)
|
||||||
|
avail = buf.len;
|
||||||
|
|
||||||
|
memcpy(buf.base, data, avail);
|
||||||
|
data += avail;
|
||||||
|
len -= avail;
|
||||||
|
wrap->OnRead(avail, &buf);
|
||||||
|
} while (len != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void JSStream::EmitEOF(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
JSStream* wrap = Unwrap<JSStream>(args.Holder());
|
||||||
|
|
||||||
|
wrap->OnRead(UV_EOF, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void JSStream::Initialize(Handle<Object> target,
|
void JSStream::Initialize(Handle<Object> target,
|
||||||
Handle<Value> unused,
|
Handle<Value> unused,
|
||||||
Handle<Context> context) {
|
Handle<Context> context) {
|
||||||
|
Environment* env = Environment::GetCurrent(context);
|
||||||
|
|
||||||
|
Local<FunctionTemplate> t = env->NewFunctionTemplate(New);
|
||||||
|
t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "JSStream"));
|
||||||
|
t->InstanceTemplate()->SetInternalFieldCount(1);
|
||||||
|
|
||||||
|
env->SetProtoMethod(t, "doAlloc", DoAlloc);
|
||||||
|
env->SetProtoMethod(t, "doRead", DoRead);
|
||||||
|
env->SetProtoMethod(t, "doAfterWrite", DoAfterWrite);
|
||||||
|
env->SetProtoMethod(t, "finishWrite", Finish<WriteWrap>);
|
||||||
|
env->SetProtoMethod(t, "finishShutdown", Finish<ShutdownWrap>);
|
||||||
|
env->SetProtoMethod(t, "readBuffer", ReadBuffer);
|
||||||
|
env->SetProtoMethod(t, "emitEOF", EmitEOF);
|
||||||
|
|
||||||
|
StreamBase::AddMethods<JSStream>(env, t);
|
||||||
|
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "JSStream"),
|
||||||
|
t->GetFunction());
|
||||||
|
env->set_jsstream_constructor_template(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
|
||||||
|
NODE_MODULE_CONTEXT_AWARE_BUILTIN(js_stream, node::JSStream::Initialize)
|
||||||
|
@ -8,11 +8,39 @@
|
|||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
|
|
||||||
class JSStream : public StreamBase {
|
class JSStream : public StreamBase, public AsyncWrap {
|
||||||
public:
|
public:
|
||||||
static void Initialize(v8::Handle<v8::Object> target,
|
static void Initialize(v8::Handle<v8::Object> target,
|
||||||
v8::Handle<v8::Value> unused,
|
v8::Handle<v8::Value> unused,
|
||||||
v8::Handle<v8::Context> context);
|
v8::Handle<v8::Context> context);
|
||||||
|
|
||||||
|
void* Cast() override;
|
||||||
|
bool IsAlive() override;
|
||||||
|
bool IsClosing() override;
|
||||||
|
int ReadStart() override;
|
||||||
|
int ReadStop() override;
|
||||||
|
|
||||||
|
int DoShutdown(ShutdownWrap* req_wrap) override;
|
||||||
|
int DoWrite(WriteWrap* w,
|
||||||
|
uv_buf_t* bufs,
|
||||||
|
size_t count,
|
||||||
|
uv_stream_t* send_handle) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
JSStream(Environment* env, v8::Handle<v8::Object> obj, AsyncWrap* parent);
|
||||||
|
~JSStream();
|
||||||
|
|
||||||
|
AsyncWrap* GetAsyncWrap() override;
|
||||||
|
|
||||||
|
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
static void DoAlloc(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
static void DoRead(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
static void DoAfterWrite(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
static void ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
static void EmitEOF(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
|
||||||
|
template <class Wrap>
|
||||||
|
static void Finish(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "env.h"
|
#include "env.h"
|
||||||
#include "env-inl.h"
|
#include "env-inl.h"
|
||||||
|
#include "js_stream.h"
|
||||||
#include "pipe_wrap.h"
|
#include "pipe_wrap.h"
|
||||||
#include "tcp_wrap.h"
|
#include "tcp_wrap.h"
|
||||||
#include "tty_wrap.h"
|
#include "tty_wrap.h"
|
||||||
@ -40,6 +41,10 @@ namespace node {
|
|||||||
env->tls_wrap_constructor_template()->HasInstance(obj)) { \
|
env->tls_wrap_constructor_template()->HasInstance(obj)) { \
|
||||||
TLSWrap* const wrap = Unwrap<TLSWrap>(obj); \
|
TLSWrap* const wrap = Unwrap<TLSWrap>(obj); \
|
||||||
BODY \
|
BODY \
|
||||||
|
} else if (env->jsstream_constructor_template().IsEmpty() == false && \
|
||||||
|
env->jsstream_constructor_template()->HasInstance(obj)) { \
|
||||||
|
JSStream* const wrap = Unwrap<JSStream>(obj); \
|
||||||
|
BODY \
|
||||||
} \
|
} \
|
||||||
}); \
|
}); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "node_buffer.h"
|
#include "node_buffer.h"
|
||||||
#include "env.h"
|
#include "env.h"
|
||||||
#include "env-inl.h"
|
#include "env-inl.h"
|
||||||
|
#include "js_stream.h"
|
||||||
#include "string_bytes.h"
|
#include "string_bytes.h"
|
||||||
#include "tls_wrap.h"
|
#include "tls_wrap.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@ -34,6 +35,8 @@ template void StreamBase::AddMethods<StreamWrap>(Environment* env,
|
|||||||
Handle<FunctionTemplate> t);
|
Handle<FunctionTemplate> t);
|
||||||
template void StreamBase::AddMethods<TLSWrap>(Environment* env,
|
template void StreamBase::AddMethods<TLSWrap>(Environment* env,
|
||||||
Handle<FunctionTemplate> t);
|
Handle<FunctionTemplate> t);
|
||||||
|
template void StreamBase::AddMethods<JSStream>(Environment* env,
|
||||||
|
Handle<FunctionTemplate> t);
|
||||||
|
|
||||||
|
|
||||||
template <class Base>
|
template <class Base>
|
||||||
@ -488,8 +491,29 @@ void StreamBase::EmitData(ssize_t nread,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AsyncWrap* StreamBase::GetAsyncWrap() {
|
bool StreamBase::IsIPCPipe() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int StreamBase::GetFD() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int StreamResource::DoTryWrite(uv_buf_t** bufs, size_t* count) {
|
||||||
|
// No TryWrite by default
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char* StreamResource::Error() const {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void StreamResource::ClearError() {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
@ -106,13 +106,13 @@ class StreamResource {
|
|||||||
virtual ~StreamResource() = default;
|
virtual ~StreamResource() = default;
|
||||||
|
|
||||||
virtual int DoShutdown(ShutdownWrap* req_wrap) = 0;
|
virtual int DoShutdown(ShutdownWrap* req_wrap) = 0;
|
||||||
virtual int DoTryWrite(uv_buf_t** bufs, size_t* count) = 0;
|
virtual int DoTryWrite(uv_buf_t** bufs, size_t* count);
|
||||||
virtual int DoWrite(WriteWrap* w,
|
virtual int DoWrite(WriteWrap* w,
|
||||||
uv_buf_t* bufs,
|
uv_buf_t* bufs,
|
||||||
size_t count,
|
size_t count,
|
||||||
uv_stream_t* send_handle) = 0;
|
uv_stream_t* send_handle) = 0;
|
||||||
virtual const char* Error() const = 0;
|
virtual const char* Error() const;
|
||||||
virtual void ClearError() = 0;
|
virtual void ClearError();
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
inline void OnAfterWrite(WriteWrap* w) {
|
inline void OnAfterWrite(WriteWrap* w) {
|
||||||
@ -127,7 +127,7 @@ class StreamResource {
|
|||||||
|
|
||||||
inline void OnRead(size_t nread,
|
inline void OnRead(size_t nread,
|
||||||
const uv_buf_t* buf,
|
const uv_buf_t* buf,
|
||||||
uv_handle_type pending) {
|
uv_handle_type pending = UV_UNKNOWN_HANDLE) {
|
||||||
if (read_cb_ != nullptr)
|
if (read_cb_ != nullptr)
|
||||||
read_cb_(nread, buf, pending, read_ctx_);
|
read_cb_(nread, buf, pending, read_ctx_);
|
||||||
}
|
}
|
||||||
@ -163,10 +163,10 @@ class StreamBase : public StreamResource {
|
|||||||
v8::Handle<v8::FunctionTemplate> target);
|
v8::Handle<v8::FunctionTemplate> target);
|
||||||
|
|
||||||
virtual void* Cast() = 0;
|
virtual void* Cast() = 0;
|
||||||
virtual bool IsAlive() const = 0;
|
virtual bool IsAlive() = 0;
|
||||||
virtual bool IsClosing() const = 0;
|
virtual bool IsClosing() = 0;
|
||||||
virtual bool IsIPCPipe() const = 0;
|
virtual bool IsIPCPipe();
|
||||||
virtual int GetFD() const = 0;
|
virtual int GetFD();
|
||||||
|
|
||||||
virtual int ReadStart() = 0;
|
virtual int ReadStart() = 0;
|
||||||
virtual int ReadStop() = 0;
|
virtual int ReadStop() = 0;
|
||||||
|
@ -84,7 +84,7 @@ void StreamWrap::AddMethods(Environment* env,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int StreamWrap::GetFD() const {
|
int StreamWrap::GetFD() {
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
if (stream() != nullptr)
|
if (stream() != nullptr)
|
||||||
@ -94,12 +94,12 @@ int StreamWrap::GetFD() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool StreamWrap::IsAlive() const {
|
bool StreamWrap::IsAlive() {
|
||||||
return HandleWrap::IsAlive(this);
|
return HandleWrap::IsAlive(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool StreamWrap::IsClosing() const {
|
bool StreamWrap::IsClosing() {
|
||||||
return uv_is_closing(reinterpret_cast<uv_handle_t*>(stream()));
|
return uv_is_closing(reinterpret_cast<uv_handle_t*>(stream()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ AsyncWrap* StreamWrap::GetAsyncWrap() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool StreamWrap::IsIPCPipe() const {
|
bool StreamWrap::IsIPCPipe() {
|
||||||
return is_named_pipe_ipc();
|
return is_named_pipe_ipc();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,16 +359,6 @@ void StreamWrap::OnAfterWriteImpl(WriteWrap* w, void* ctx) {
|
|||||||
wrap->UpdateWriteQueueSize();
|
wrap->UpdateWriteQueueSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const char* StreamWrap::Error() const {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void StreamWrap::ClearError() {
|
|
||||||
// No-op
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
|
||||||
NODE_MODULE_CONTEXT_AWARE_BUILTIN(stream_wrap, node::StreamWrap::Initialize)
|
NODE_MODULE_CONTEXT_AWARE_BUILTIN(stream_wrap, node::StreamWrap::Initialize)
|
||||||
|
@ -19,11 +19,11 @@ class StreamWrap : public HandleWrap, public StreamBase {
|
|||||||
v8::Handle<v8::Value> unused,
|
v8::Handle<v8::Value> unused,
|
||||||
v8::Handle<v8::Context> context);
|
v8::Handle<v8::Context> context);
|
||||||
|
|
||||||
int GetFD() const override;
|
int GetFD() override;
|
||||||
void* Cast() override;
|
void* Cast() override;
|
||||||
bool IsAlive() const override;
|
bool IsAlive() override;
|
||||||
bool IsClosing() const override;
|
bool IsClosing() override;
|
||||||
bool IsIPCPipe() const override;
|
bool IsIPCPipe() override;
|
||||||
|
|
||||||
// JavaScript functions
|
// JavaScript functions
|
||||||
int ReadStart() override;
|
int ReadStart() override;
|
||||||
@ -36,8 +36,6 @@ class StreamWrap : public HandleWrap, public StreamBase {
|
|||||||
uv_buf_t* bufs,
|
uv_buf_t* bufs,
|
||||||
size_t count,
|
size_t count,
|
||||||
uv_stream_t* send_handle) override;
|
uv_stream_t* send_handle) override;
|
||||||
const char* Error() const override;
|
|
||||||
void ClearError() override;
|
|
||||||
|
|
||||||
inline uv_stream_t* stream() const {
|
inline uv_stream_t* stream() const {
|
||||||
return stream_;
|
return stream_;
|
||||||
|
@ -216,7 +216,7 @@ void TLSWrap::Receive(const FunctionCallbackInfo<Value>& args) {
|
|||||||
size_t copy = buf.len > len ? len : buf.len;
|
size_t copy = buf.len > len ? len : buf.len;
|
||||||
memcpy(buf.base, data, copy);
|
memcpy(buf.base, data, copy);
|
||||||
buf.len = copy;
|
buf.len = copy;
|
||||||
wrap->stream_->OnRead(buf.len, &buf, UV_UNKNOWN_HANDLE);
|
wrap->stream_->OnRead(buf.len, &buf);
|
||||||
|
|
||||||
data += copy;
|
data += copy;
|
||||||
len -= copy;
|
len -= copy;
|
||||||
@ -414,7 +414,7 @@ void TLSWrap::ClearOut() {
|
|||||||
if (static_cast<int>(buf.len) < avail)
|
if (static_cast<int>(buf.len) < avail)
|
||||||
avail = buf.len;
|
avail = buf.len;
|
||||||
memcpy(buf.base, out, avail);
|
memcpy(buf.base, out, avail);
|
||||||
OnRead(avail, &buf, UV_UNKNOWN_HANDLE);
|
OnRead(avail, &buf);
|
||||||
|
|
||||||
read -= avail;
|
read -= avail;
|
||||||
}
|
}
|
||||||
@ -423,7 +423,7 @@ void TLSWrap::ClearOut() {
|
|||||||
int flags = SSL_get_shutdown(ssl_);
|
int flags = SSL_get_shutdown(ssl_);
|
||||||
if (!eof_ && flags & SSL_RECEIVED_SHUTDOWN) {
|
if (!eof_ && flags & SSL_RECEIVED_SHUTDOWN) {
|
||||||
eof_ = true;
|
eof_ = true;
|
||||||
OnRead(UV_EOF, nullptr, UV_UNKNOWN_HANDLE);
|
OnRead(UV_EOF, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read == -1) {
|
if (read == -1) {
|
||||||
@ -495,22 +495,22 @@ AsyncWrap* TLSWrap::GetAsyncWrap() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool TLSWrap::IsIPCPipe() const {
|
bool TLSWrap::IsIPCPipe() {
|
||||||
return stream_->IsIPCPipe();
|
return stream_->IsIPCPipe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int TLSWrap::GetFD() const {
|
int TLSWrap::GetFD() {
|
||||||
return stream_->GetFD();
|
return stream_->GetFD();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool TLSWrap::IsAlive() const {
|
bool TLSWrap::IsAlive() {
|
||||||
return stream_->IsAlive();
|
return stream_->IsAlive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool TLSWrap::IsClosing() const {
|
bool TLSWrap::IsClosing() {
|
||||||
return stream_->IsClosing();
|
return stream_->IsClosing();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,12 +536,6 @@ void TLSWrap::ClearError() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int TLSWrap::DoTryWrite(uv_buf_t** bufs, size_t* count) {
|
|
||||||
// TODO(indutny): Support it
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int TLSWrap::DoWrite(WriteWrap* w,
|
int TLSWrap::DoWrite(WriteWrap* w,
|
||||||
uv_buf_t* bufs,
|
uv_buf_t* bufs,
|
||||||
size_t count,
|
size_t count,
|
||||||
@ -668,7 +662,7 @@ void TLSWrap::DoRead(ssize_t nread,
|
|||||||
|
|
||||||
HandleScope handle_scope(env()->isolate());
|
HandleScope handle_scope(env()->isolate());
|
||||||
Context::Scope context_scope(env()->context());
|
Context::Scope context_scope(env()->context());
|
||||||
OnRead(nread, nullptr, UV_UNKNOWN_HANDLE);
|
OnRead(nread, nullptr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,16 +32,15 @@ class TLSWrap : public crypto::SSLWrap<TLSWrap>,
|
|||||||
v8::Handle<v8::Context> context);
|
v8::Handle<v8::Context> context);
|
||||||
|
|
||||||
void* Cast() override;
|
void* Cast() override;
|
||||||
int GetFD() const override;
|
int GetFD() override;
|
||||||
bool IsAlive() const override;
|
bool IsAlive() override;
|
||||||
bool IsClosing() const override;
|
bool IsClosing() override;
|
||||||
|
|
||||||
// JavaScript functions
|
// JavaScript functions
|
||||||
int ReadStart() override;
|
int ReadStart() override;
|
||||||
int ReadStop() override;
|
int ReadStop() override;
|
||||||
|
|
||||||
int DoShutdown(ShutdownWrap* req_wrap) override;
|
int DoShutdown(ShutdownWrap* req_wrap) override;
|
||||||
int DoTryWrite(uv_buf_t** bufs, size_t* count) override;
|
|
||||||
int DoWrite(WriteWrap* w,
|
int DoWrite(WriteWrap* w,
|
||||||
uv_buf_t* bufs,
|
uv_buf_t* bufs,
|
||||||
size_t count,
|
size_t count,
|
||||||
@ -78,7 +77,7 @@ class TLSWrap : public crypto::SSLWrap<TLSWrap>,
|
|||||||
|
|
||||||
TLSWrap(Environment* env,
|
TLSWrap(Environment* env,
|
||||||
Kind kind,
|
Kind kind,
|
||||||
StreamBase* steram,
|
StreamBase* stream,
|
||||||
v8::Handle<v8::Object> stream_obj,
|
v8::Handle<v8::Object> stream_obj,
|
||||||
v8::Handle<v8::Object> sc);
|
v8::Handle<v8::Object> sc);
|
||||||
|
|
||||||
@ -104,7 +103,7 @@ class TLSWrap : public crypto::SSLWrap<TLSWrap>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
AsyncWrap* GetAsyncWrap() override;
|
AsyncWrap* GetAsyncWrap() override;
|
||||||
bool IsIPCPipe() const override;
|
bool IsIPCPipe() override;
|
||||||
|
|
||||||
// Resource implementation
|
// Resource implementation
|
||||||
static void OnAfterWriteImpl(WriteWrap* w, void* ctx);
|
static void OnAfterWriteImpl(WriteWrap* w, void* ctx);
|
||||||
|
69
test/parallel/test-tls-js-stream.js
Normal file
69
test/parallel/test-tls-js-stream.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
var assert = require('assert');
|
||||||
|
var stream = require('stream');
|
||||||
|
var tls = require('tls');
|
||||||
|
var fs = require('fs');
|
||||||
|
var net = require('net');
|
||||||
|
|
||||||
|
var common = require('../common');
|
||||||
|
|
||||||
|
var connected = {
|
||||||
|
client: 0,
|
||||||
|
server: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
var server = tls.createServer({
|
||||||
|
key: fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'),
|
||||||
|
cert: fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem')
|
||||||
|
}, function(c) {
|
||||||
|
console.log('new client');
|
||||||
|
connected.server++;
|
||||||
|
c.end('ohai');
|
||||||
|
}).listen(common.PORT, function() {
|
||||||
|
var raw = net.connect(common.PORT);
|
||||||
|
|
||||||
|
var pending = false;
|
||||||
|
raw.on('readable', function() {
|
||||||
|
if (pending)
|
||||||
|
p._read();
|
||||||
|
});
|
||||||
|
|
||||||
|
var p = new stream.Duplex({
|
||||||
|
read: function read() {
|
||||||
|
pending = false;
|
||||||
|
|
||||||
|
var chunk = raw.read();
|
||||||
|
if (chunk) {
|
||||||
|
console.log('read', chunk);
|
||||||
|
this.push(chunk);
|
||||||
|
} else {
|
||||||
|
pending = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
write: function write(data, enc, cb) {
|
||||||
|
console.log('write', data, enc);
|
||||||
|
raw.write(data, enc, cb);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var socket = tls.connect({
|
||||||
|
socket: p,
|
||||||
|
rejectUnauthorized: false
|
||||||
|
}, function() {
|
||||||
|
console.log('client secure');
|
||||||
|
|
||||||
|
connected.client++;
|
||||||
|
|
||||||
|
socket.end('hello');
|
||||||
|
socket.resume();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.once('close', function() {
|
||||||
|
console.log('client close');
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
process.once('exit', function() {
|
||||||
|
assert.equal(connected.client, 1);
|
||||||
|
assert.equal(connected.server, 1);
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user