Skip to content
This repository has been archived by the owner on Aug 11, 2020. It is now read-only.

Commit

Permalink
[WIP] Testing QUIC without real UDP handle
Browse files Browse the repository at this point in the history
  • Loading branch information
addaleax committed Oct 14, 2019
1 parent 7830b8c commit 70aa77d
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 1 deletion.
9 changes: 8 additions & 1 deletion lib/internal/quic/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ const kSetSocket = Symbol('kSetSocket');
const kStreamClose = Symbol('kStreamClose');
const kStreamReset = Symbol('kStreamReset');
const kTrackWriteState = Symbol('kTrackWriteState');
const kUDPHandleForTesting = Symbol('kUDPHandleForTesting');
const kVersionNegotiation = Symbol('kVersionNegotiation');
const kWriteGeneric = Symbol('kWriteGeneric');

Expand Down Expand Up @@ -677,6 +678,11 @@ class QuicSocket extends EventEmitter {
(validateAddress ? QUICSOCKET_OPTIONS_VALIDATE_ADDRESS : 0) |
(validateAddressLRU ? QUICSOCKET_OPTIONS_VALIDATE_ADDRESS_LRU : 0);
this.#udpSocket = dgram.createSocket(type === AF_INET6 ? 'udp6' : 'udp4');
if (typeof options[kUDPHandleForTesting] === 'object') {
this.#udpSocket.bind(options[kUDPHandleForTesting]);
this.#state = kSocketBound;
process.nextTick(() => this[kReady](undefined));
}
const udpHandle = this.#udpSocket[internalDgram.kStateSymbol].handle;
const handle =
new QuicSocketHandle(
Expand Down Expand Up @@ -2384,7 +2390,8 @@ function createSocket(options = {}) {
}

module.exports = {
createSocket
createSocket,
kUDPHandleForTesting
};

/* eslint-enable no-use-before-define */
Expand Down
1 change: 1 addition & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,7 @@
'src/js_native_api_v8.h',
'src/js_native_api_v8_internals.h',
'src/js_stream.cc',
'src/js_udp_wrap.cc',
'src/module_wrap.cc',
'src/node.cc',
'src/node_api.cc',
Expand Down
1 change: 1 addition & 0 deletions src/async_wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ namespace node {
V(HTTPINCOMINGMESSAGE) \
V(HTTPCLIENTREQUEST) \
V(JSSTREAM) \
V(JSUDPWRAP) \
V(MESSAGEPORT) \
V(PIPECONNECTWRAP) \
V(PIPESERVERWRAP) \
Expand Down
223 changes: 223 additions & 0 deletions src/js_udp_wrap.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
#include "udp_wrap.h"
#include "async_wrap-inl.h"
#include "node_errors.h"

namespace node {

using errors::TryCatchScope;
using v8::Array;
using v8::Context;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::HandleScope;
using v8::Int32;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;

class JSUDPWrap final : public UDPWrapBase, public AsyncWrap {
public:
JSUDPWrap(Environment* env, Local<Object> obj);

int RecvStart() override;
int RecvStop() override;
ssize_t Send(uv_buf_t* bufs,
size_t nbufs,
const sockaddr* addr) override;
int GetPeerName(sockaddr* name, int* namelen) override;
int GetSockName(sockaddr* name, int* namelen) override;
int GetSockaddr(sockaddr* name, int* namelen, bool peer);
AsyncWrap* GetAsyncWrap() override { return this; }

static void New(const FunctionCallbackInfo<Value>& args);
static void EmitReceived(const FunctionCallbackInfo<Value>& args);
static void OnSendDone(const FunctionCallbackInfo<Value>& args);
static void OnAfterBind(const FunctionCallbackInfo<Value>& args);

static void Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv);
SET_NO_MEMORY_INFO()
SET_MEMORY_INFO_NAME(JSUDPWrap)
SET_SELF_SIZE(JSUDPWrap)
};

JSUDPWrap::JSUDPWrap(Environment* env, Local<Object> obj)
: AsyncWrap(env, obj, PROVIDER_JSUDPWRAP) {
MakeWeak();

obj->SetAlignedPointerInInternalField(
kUDPWrapBaseField, static_cast<UDPWrapBase*>(this));
}

int JSUDPWrap::RecvStart() {
HandleScope scope(env()->isolate());
Context::Scope context_scope(env()->context());
TryCatchScope try_catch(env());
Local<Value> value;
int32_t value_int = UV_EPROTO;
if (!MakeCallback(env()->onreadstart_string(), 0, nullptr).ToLocal(&value) ||
!value->Int32Value(env()->context()).To(&value_int)) {
if (try_catch.HasCaught() && !try_catch.HasTerminated())
errors::TriggerUncaughtException(env()->isolate(), try_catch);
}
return value_int;
}

int JSUDPWrap::RecvStop() {
HandleScope scope(env()->isolate());
Context::Scope context_scope(env()->context());
TryCatchScope try_catch(env());
Local<Value> value;
int32_t value_int = UV_EPROTO;
if (!MakeCallback(env()->onreadstop_string(), 0, nullptr).ToLocal(&value) ||
!value->Int32Value(env()->context()).To(&value_int)) {
if (try_catch.HasCaught() && !try_catch.HasTerminated())
errors::TriggerUncaughtException(env()->isolate(), try_catch);
}
return value_int;
}

ssize_t JSUDPWrap::Send(uv_buf_t* bufs,
size_t nbufs,
const sockaddr* addr) {
HandleScope scope(env()->isolate());
Context::Scope context_scope(env()->context());
TryCatchScope try_catch(env());
Local<Value> value;
int64_t value_int = UV_EPROTO;
size_t total_len = 0;

MaybeStackBuffer<Local<Value>, 16> buffers(nbufs);
for (size_t i = 0; i < nbufs; i++) {
buffers[i] = Buffer::Copy(env(), bufs[i].base, bufs[i].len)
.ToLocalChecked();
total_len += bufs[i].len;
}

Local<Value> args[] = {
listener()->CreateSendWrap(total_len)->object(),
Array::New(env()->isolate(), buffers.out(), nbufs),
AddressToJS(env(), addr)
};

if (!MakeCallback(env()->onwrite_string(), arraysize(args), args)
.ToLocal(&value) ||
!value->IntegerValue(env()->context()).To(&value_int)) {
if (try_catch.HasCaught() && !try_catch.HasTerminated())
errors::TriggerUncaughtException(env()->isolate(), try_catch);
}
return value_int;
}

int JSUDPWrap::GetPeerName(sockaddr* name, int* namelen) {
return GetSockaddr(name, namelen, true);
}

int JSUDPWrap::GetSockName(sockaddr* name, int* namelen) {
return GetSockaddr(name, namelen, false);
}

int JSUDPWrap::GetSockaddr(sockaddr* name, int* namelen, bool peer) {
// TODO(addaleax): Maybe turn this into a real JS-based method.
sockaddr_in addr_in;
CHECK_EQ(uv_ip4_addr("127.0.0.1", 1337, &addr_in), 0);
memcpy(name, &addr_in,
std::min(static_cast<size_t>(*namelen), sizeof(addr_in)));
*namelen = sizeof(addr_in);
return 0;
}

void JSUDPWrap::New(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
CHECK(args.IsConstructCall());
new JSUDPWrap(env, args.Holder());
}

void JSUDPWrap::EmitReceived(const FunctionCallbackInfo<Value>& args) {
JSUDPWrap* wrap;
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
Environment* env = wrap->env();

ArrayBufferViewContents<char> buffer(args[0]);
const char* data = buffer.data();
int len = buffer.length();

CHECK(args[1]->IsInt32()); // family
CHECK(args[2]->IsString()); // address
CHECK(args[3]->IsInt32()); // port
CHECK(args[4]->IsInt32()); // flags
int family = args[1].As<Int32>()->Value() == 4 ? AF_INET : AF_INET6;
Utf8Value address(env->isolate(), args[2]);
int port = args[3].As<Int32>()->Value();
int flags = args[3].As<Int32>()->Value();

sockaddr_storage addr;
CHECK_EQ(sockaddr_for_family(family, *address, port, &addr), 0);

// Repeatedly ask the stream's owner for memory, copy the data that we
// just read from JS into those buffers and emit them as reads.
while (len != 0) {
uv_buf_t buf = wrap->listener()->OnAlloc(len);
ssize_t avail = len;
if (static_cast<ssize_t>(buf.len) < avail)
avail = buf.len;

memcpy(buf.base, data, avail);
data += avail;
len -= avail;
wrap->listener()->OnRecv(
avail, buf, reinterpret_cast<sockaddr*>(&addr), flags);
}
}

void JSUDPWrap::OnSendDone(const FunctionCallbackInfo<Value>& args) {
JSUDPWrap* wrap;
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());

CHECK(args[0]->IsObject());
CHECK(args[1]->IsInt32());
ReqWrap<uv_udp_send_t>* req_wrap;
ASSIGN_OR_RETURN_UNWRAP(&wrap, args[0].As<Object>());
int status = args[1].As<Int32>()->Value();

wrap->listener()->OnSendDone(req_wrap, status);
}

void JSUDPWrap::OnAfterBind(const FunctionCallbackInfo<Value>& args) {
JSUDPWrap* wrap;
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());

wrap->listener()->OnAfterBind();
}

void JSUDPWrap::Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv) {
Environment* env = Environment::GetCurrent(context);

Local<FunctionTemplate> t = env->NewFunctionTemplate(New);
Local<String> js_udp_wrap_string =
FIXED_ONE_BYTE_STRING(env->isolate(), "JSUDPWrap");
t->SetClassName(js_udp_wrap_string);
t->InstanceTemplate()
->SetInternalFieldCount(UDPWrapBase::kUDPWrapBaseField + 1);
t->Inherit(AsyncWrap::GetConstructorTemplate(env));

UDPWrapBase::AddMethods(env, t);
env->SetProtoMethod(t, "emitReceived", EmitReceived);
env->SetProtoMethod(t, "onSendDone", OnSendDone);
env->SetProtoMethod(t, "onAfterBind", OnAfterBind);

target->Set(env->context(),
js_udp_wrap_string,
t->GetFunction(context).ToLocalChecked()).Check();
}


} // namespace node

NODE_MODULE_CONTEXT_AWARE_INTERNAL(js_udp_wrap, node::JSUDPWrap::Initialize)
1 change: 1 addition & 0 deletions src/node_binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
V(http_parser) \
V(inspector) \
V(js_stream) \
V(js_udp_wrap) \
V(messaging) \
V(module_wrap) \
V(native_module) \
Expand Down
5 changes: 5 additions & 0 deletions src/udp_wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@ class UDPWrap final : public HandleWrap,
v8::Local<v8::Object> current_send_req_wrap_;
};

int sockaddr_for_family(int address_family,
const char* address,
const unsigned short port,
sockaddr_storage* addr);

} // namespace node

#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
Expand Down

0 comments on commit 70aa77d

Please sign in to comment.