diff options
author | James M Snell <jasnell@gmail.com> | 2018-09-16 19:13:11 -0700 |
---|---|---|
committer | James M Snell <jasnell@gmail.com> | 2018-09-21 13:23:08 -0700 |
commit | b92ce5165f91eec1f312bb9f762357e673f83501 (patch) | |
tree | 26b2416d55c157aba43e76953f3abebb8e65343f /src | |
parent | c55ebd8502352c6405bfd8c3b356285ccdd242fd (diff) | |
download | android-node-v8-b92ce5165f91eec1f312bb9f762357e673f83501.tar.gz android-node-v8-b92ce5165f91eec1f312bb9f762357e673f83501.tar.bz2 android-node-v8-b92ce5165f91eec1f312bb9f762357e673f83501.zip |
http2: add origin frame support
PR-URL: https://github.com/nodejs/node/pull/22956
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/env.h | 1 | ||||
-rw-r--r-- | src/node_http2.cc | 126 | ||||
-rw-r--r-- | src/node_http2.h | 27 |
3 files changed, 147 insertions, 7 deletions
@@ -224,6 +224,7 @@ struct PackageConfig { V(onnewsession_string, "onnewsession") \ V(onocspresponse_string, "onocspresponse") \ V(ongoawaydata_string, "ongoawaydata") \ + V(onorigin_string, "onorigin") \ V(onpriority_string, "onpriority") \ V(onread_string, "onread") \ V(onreadstart_string, "onreadstart") \ diff --git a/src/node_http2.cc b/src/node_http2.cc index 4d47291ace..837d58c81c 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -95,7 +95,7 @@ Http2Scope::~Http2Scope() { // instances to configure an appropriate nghttp2_options struct. The class // uses a single TypedArray instance that is shared with the JavaScript side // to more efficiently pass values back and forth. -Http2Options::Http2Options(Environment* env) { +Http2Options::Http2Options(Environment* env, nghttp2_session_type type) { nghttp2_option_new(&options_); // We manually handle flow control within a session in order to @@ -106,10 +106,12 @@ Http2Options::Http2Options(Environment* env) { // are required to buffer. nghttp2_option_set_no_auto_window_update(options_, 1); - // Enable built in support for ALTSVC frames. Once we add support for - // other non-built in extension frames, this will need to be handled - // a bit differently. For now, let's let nghttp2 take care of it. - nghttp2_option_set_builtin_recv_extension_type(options_, NGHTTP2_ALTSVC); + // Enable built in support for receiving ALTSVC and ORIGIN frames (but + // only on client side sessions + if (type == NGHTTP2_SESSION_CLIENT) { + nghttp2_option_set_builtin_recv_extension_type(options_, NGHTTP2_ALTSVC); + nghttp2_option_set_builtin_recv_extension_type(options_, NGHTTP2_ORIGIN); + } AliasedBuffer<uint32_t, Uint32Array>& buffer = env->http2_state()->options_buffer; @@ -413,6 +415,56 @@ Headers::Headers(Isolate* isolate, } } +Origins::Origins(Isolate* isolate, + Local<Context> context, + Local<String> origin_string, + size_t origin_count) : count_(origin_count) { + int origin_string_len = origin_string->Length(); + if (count_ == 0) { + CHECK_EQ(origin_string_len, 0); + return; + } + + // Allocate a single buffer with count_ nghttp2_nv structs, followed + // by the raw header data as passed from JS. This looks like: + // | possible padding | nghttp2_nv | nghttp2_nv | ... | header contents | + buf_.AllocateSufficientStorage((alignof(nghttp2_origin_entry) - 1) + + count_ * sizeof(nghttp2_origin_entry) + + origin_string_len); + + // Make sure the start address is aligned appropriately for an nghttp2_nv*. + char* start = reinterpret_cast<char*>( + ROUND_UP(reinterpret_cast<uintptr_t>(*buf_), + alignof(nghttp2_origin_entry))); + char* origin_contents = start + (count_ * sizeof(nghttp2_origin_entry)); + nghttp2_origin_entry* const nva = + reinterpret_cast<nghttp2_origin_entry*>(start); + + CHECK_LE(origin_contents + origin_string_len, *buf_ + buf_.length()); + CHECK_EQ(origin_string->WriteOneByte( + isolate, + reinterpret_cast<uint8_t*>(origin_contents), + 0, + origin_string_len, + String::NO_NULL_TERMINATION), + origin_string_len); + + size_t n = 0; + char* p; + for (p = origin_contents; p < origin_contents + origin_string_len; n++) { + if (n >= count_) { + static uint8_t zero = '\0'; + nva[0].origin = &zero; + nva[0].origin_len = 1; + count_ = 1; + return; + } + + nva[n].origin = reinterpret_cast<uint8_t*>(p); + nva[n].origin_len = strlen(p); + p += nva[n].origin_len + 1; + } +} // Sets the various callback functions that nghttp2 will use to notify us // about significant events while processing http2 stuff. @@ -548,7 +600,7 @@ Http2Session::Http2Session(Environment* env, statistics_.start_time = uv_hrtime(); // Capture the configuration options for this session - Http2Options opts(env); + Http2Options opts(env, type); max_session_memory_ = opts.GetMaxSessionMemory(); @@ -933,6 +985,9 @@ int Http2Session::OnFrameReceive(nghttp2_session* handle, case NGHTTP2_ALTSVC: session->HandleAltSvcFrame(frame); break; + case NGHTTP2_ORIGIN: + session->HandleOriginFrame(frame); + break; default: break; } @@ -1365,6 +1420,41 @@ void Http2Session::HandleAltSvcFrame(const nghttp2_frame* frame) { MakeCallback(env()->onaltsvc_string(), arraysize(argv), argv); } +void Http2Session::HandleOriginFrame(const nghttp2_frame* frame) { + Isolate* isolate = env()->isolate(); + HandleScope scope(isolate); + Local<Context> context = env()->context(); + Context::Scope context_scope(context); + + Debug(this, "handling origin frame"); + + nghttp2_extension ext = frame->ext; + nghttp2_ext_origin* origin = static_cast<nghttp2_ext_origin*>(ext.payload); + + Local<Array> holder = Array::New(isolate); + Local<Function> fn = env()->push_values_to_array_function(); + Local<Value> argv[NODE_PUSH_VAL_TO_ARRAY_MAX]; + + size_t n = 0; + while (n < origin->nov) { + size_t j = 0; + while (n < origin->nov && j < arraysize(argv)) { + auto entry = origin->ov[n++]; + argv[j++] = + String::NewFromOneByte(isolate, + entry.origin, + v8::NewStringType::kNormal, + entry.origin_len).ToLocalChecked(); + } + if (j > 0) + fn->Call(context, holder, j, argv).ToLocalChecked(); + } + + Local<Value> args[1] = { holder }; + + MakeCallback(env()->onorigin_string(), arraysize(args), args); +} + // Called by OnFrameReceived when a complete PING frame has been received. void Http2Session::HandlePingFrame(const nghttp2_frame* frame) { bool ack = frame->hd.flags & NGHTTP2_FLAG_ACK; @@ -2613,6 +2703,11 @@ void Http2Session::AltSvc(int32_t id, origin, origin_len, value, value_len), 0); } +void Http2Session::Origin(nghttp2_origin_entry* ov, size_t count) { + Http2Scope h2scope(this); + CHECK_EQ(nghttp2_submit_origin(session_, NGHTTP2_FLAG_NONE, ov, count), 0); +} + // Submits an AltSvc frame to be sent to the connected peer. void Http2Session::AltSvc(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); @@ -2641,6 +2736,24 @@ void Http2Session::AltSvc(const FunctionCallbackInfo<Value>& args) { session->AltSvc(id, *origin, origin_len, *value, value_len); } +void Http2Session::Origin(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args); + Local<Context> context = env->context(); + Http2Session* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + + Local<String> origin_string = args[0].As<String>(); + int count = args[1]->IntegerValue(context).ToChecked(); + + + Origins origins(env->isolate(), + env->context(), + origin_string, + count); + + session->Origin(*origins, origins.length()); +} + // Submits a PING frame to be sent to the connected peer. void Http2Session::Ping(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); @@ -2874,6 +2987,7 @@ void Initialize(Local<Object> target, session->SetClassName(http2SessionClassName); session->InstanceTemplate()->SetInternalFieldCount(1); AsyncWrap::AddWrapMethods(env, session); + env->SetProtoMethod(session, "origin", Http2Session::Origin); env->SetProtoMethod(session, "altsvc", Http2Session::AltSvc); env->SetProtoMethod(session, "ping", Http2Session::Ping); env->SetProtoMethod(session, "consume", Http2Session::Consume); diff --git a/src/node_http2.h b/src/node_http2.h index d7f8d9acae..7fa230979a 100644 --- a/src/node_http2.h +++ b/src/node_http2.h @@ -364,7 +364,7 @@ class Http2Scope { // configured. class Http2Options { public: - explicit Http2Options(Environment* env); + Http2Options(Environment* env, nghttp2_session_type type); ~Http2Options() { nghttp2_option_del(options_); @@ -700,6 +700,8 @@ class Http2Session : public AsyncWrap, public StreamListener { size_t origin_len, uint8_t* value, size_t value_len); + void Origin(nghttp2_origin_entry* ov, size_t count); + bool Ping(v8::Local<v8::Function> function); @@ -796,6 +798,7 @@ class Http2Session : public AsyncWrap, public StreamListener { static void RefreshState(const FunctionCallbackInfo<Value>& args); static void Ping(const FunctionCallbackInfo<Value>& args); static void AltSvc(const FunctionCallbackInfo<Value>& args); + static void Origin(const FunctionCallbackInfo<Value>& args); template <get_setting fn> static void RefreshSettings(const FunctionCallbackInfo<Value>& args); @@ -871,6 +874,7 @@ class Http2Session : public AsyncWrap, public StreamListener { void HandleSettingsFrame(const nghttp2_frame* frame); void HandlePingFrame(const nghttp2_frame* frame); void HandleAltSvcFrame(const nghttp2_frame* frame); + void HandleOriginFrame(const nghttp2_frame* frame); // nghttp2 callbacks static int OnBeginHeadersCallback( @@ -1224,6 +1228,27 @@ class Headers { MaybeStackBuffer<char, 3000> buf_; }; +class Origins { + public: + Origins(Isolate* isolate, + Local<Context> context, + Local<v8::String> origin_string, + size_t origin_count); + ~Origins() {} + + nghttp2_origin_entry* operator*() { + return reinterpret_cast<nghttp2_origin_entry*>(*buf_); + } + + size_t length() const { + return count_; + } + + private: + size_t count_; + MaybeStackBuffer<char, 512> buf_; +}; + } // namespace http2 } // namespace node |