Skip to content

Commit

Permalink
src: move HTTP/2 state out of Environment
Browse files Browse the repository at this point in the history
Moves state that is specific to HTTP/2 into the HTTP/2 implementation
as a cleanup.

PR-URL: #32538
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
addaleax committed Apr 6, 2020
1 parent 6576b9b commit f54b5b2
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 109 deletions.
10 changes: 0 additions & 10 deletions src/env-inl.h
Expand Up @@ -630,16 +630,6 @@ inline void Environment::set_http_parser_buffer_in_use(bool in_use) {
http_parser_buffer_in_use_ = in_use;
}

inline http2::Http2State* Environment::http2_state() const {
return http2_state_.get();
}

inline void Environment::set_http2_state(
std::unique_ptr<http2::Http2State> buffer) {
CHECK(!http2_state_); // Should be set only once.
http2_state_ = std::move(buffer);
}

inline AliasedFloat64Array* Environment::fs_stats_field_array() {
return &fs_stats_field_array_;
}
Expand Down
10 changes: 2 additions & 8 deletions src/env.h
Expand Up @@ -33,7 +33,6 @@
#include "handle_wrap.h"
#include "node.h"
#include "node_binding.h"
#include "node_http2_state.h"
#include "node_main_instance.h"
#include "node_options.h"
#include "req_wrap.h"
Expand All @@ -50,8 +49,6 @@
#include <unordered_set>
#include <vector>

struct nghttp2_rcbuf;

namespace node {

namespace contextify {
Expand Down Expand Up @@ -515,7 +512,8 @@ class IsolateData : public MemoryRetainer {
#undef VP
inline v8::Local<v8::String> async_wrap_provider(int index) const;

std::unordered_map<const char*, v8::Eternal<v8::String>> http_static_strs;
std::unordered_map<const char*, v8::Eternal<v8::String>> static_str_map;

inline v8::Isolate* isolate() const;
IsolateData(const IsolateData&) = delete;
IsolateData& operator=(const IsolateData&) = delete;
Expand Down Expand Up @@ -1026,9 +1024,6 @@ class Environment : public MemoryRetainer {
inline bool http_parser_buffer_in_use() const;
inline void set_http_parser_buffer_in_use(bool in_use);

inline http2::Http2State* http2_state() const;
inline void set_http2_state(std::unique_ptr<http2::Http2State> state);

EnabledDebugList* enabled_debug_list() { return &enabled_debug_list_; }

inline AliasedFloat64Array* fs_stats_field_array();
Expand Down Expand Up @@ -1391,7 +1386,6 @@ class Environment : public MemoryRetainer {

char* http_parser_buffer_ = nullptr;
bool http_parser_buffer_in_use_ = false;
std::unique_ptr<http2::Http2State> http2_state_;

EnabledDebugList enabled_debug_list_;
AliasedFloat64Array fs_stats_field_array_;
Expand Down
89 changes: 48 additions & 41 deletions src/node_http2.cc
Expand Up @@ -4,7 +4,6 @@
#include "node.h"
#include "node_buffer.h"
#include "node_http2.h"
#include "node_http2_state.h"
#include "node_http_common-inl.h"
#include "node_mem-inl.h"
#include "node_perf.h"
Expand Down Expand Up @@ -113,7 +112,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, nghttp2_session_type type) {
Http2Options::Http2Options(Http2State* http2_state, nghttp2_session_type type) {
nghttp2_option* option;
CHECK_EQ(nghttp2_option_new(&option), 0);
CHECK_NOT_NULL(option);
Expand All @@ -138,7 +137,7 @@ Http2Options::Http2Options(Environment* env, nghttp2_session_type type) {
nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ORIGIN);
}

AliasedUint32Array& buffer = env->http2_state()->options_buffer;
AliasedUint32Array& buffer = http2_state->options_buffer;
uint32_t flags = buffer[IDX_OPTIONS_FLAGS];

if (flags & (1 << IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE)) {
Expand Down Expand Up @@ -213,8 +212,8 @@ Http2Options::Http2Options(Environment* env, nghttp2_session_type type) {
SetMaxSessionMemory(buffer[IDX_OPTIONS_MAX_SESSION_MEMORY] * 1000000);
}

void Http2Session::Http2Settings::Init() {
AliasedUint32Array& buffer = env()->http2_state()->settings_buffer;
void Http2Session::Http2Settings::Init(Http2State* http2_state) {
AliasedUint32Array& buffer = http2_state->settings_buffer;
uint32_t flags = buffer[IDX_SETTINGS_COUNT];

size_t n = 0;
Expand Down Expand Up @@ -244,14 +243,14 @@ void Http2Session::Http2Settings::Init() {
// The Http2Settings class is used to configure a SETTINGS frame that is
// to be sent to the connected peer. The settings are set using a TypedArray
// that is shared with the JavaScript side.
Http2Session::Http2Settings::Http2Settings(Environment* env,
Http2Session::Http2Settings::Http2Settings(Http2State* http2_state,
Http2Session* session,
Local<Object> obj,
uint64_t start_time)
: AsyncWrap(env, obj, PROVIDER_HTTP2SETTINGS),
: AsyncWrap(http2_state->env(), obj, PROVIDER_HTTP2SETTINGS),
session_(session),
startTime_(start_time) {
Init();
Init(http2_state);
}

// Generates a Buffer that contains the serialized payload of a SETTINGS
Expand All @@ -272,10 +271,9 @@ Local<Value> Http2Session::Http2Settings::Pack() {

// Updates the shared TypedArray with the current remote or local settings for
// the session.
void Http2Session::Http2Settings::Update(Environment* env,
Http2Session* session,
void Http2Session::Http2Settings::Update(Http2Session* session,
get_setting fn) {
AliasedUint32Array& buffer = env->http2_state()->settings_buffer;
AliasedUint32Array& buffer = session->http2_state()->settings_buffer;
buffer[IDX_SETTINGS_HEADER_TABLE_SIZE] =
fn(**session, NGHTTP2_SETTINGS_HEADER_TABLE_SIZE);
buffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS] =
Expand All @@ -293,8 +291,8 @@ void Http2Session::Http2Settings::Update(Environment* env,
}

// Initializes the shared TypedArray with the default settings values.
void Http2Session::Http2Settings::RefreshDefaults(Environment* env) {
AliasedUint32Array& buffer = env->http2_state()->settings_buffer;
void Http2Session::Http2Settings::RefreshDefaults(Http2State* http2_state) {
AliasedUint32Array& buffer = http2_state->settings_buffer;

buffer[IDX_SETTINGS_HEADER_TABLE_SIZE] =
DEFAULT_SETTINGS_HEADER_TABLE_SIZE;
Expand Down Expand Up @@ -469,16 +467,17 @@ void Http2Session::DecreaseAllocatedSize(size_t size) {
current_nghttp2_memory_ -= size;
}

Http2Session::Http2Session(Environment* env,
Http2Session::Http2Session(Http2State* http2_state,
Local<Object> wrap,
nghttp2_session_type type)
: AsyncWrap(env, wrap, AsyncWrap::PROVIDER_HTTP2SESSION),
session_type_(type) {
: AsyncWrap(http2_state->env(), wrap, AsyncWrap::PROVIDER_HTTP2SESSION),
session_type_(type),
http2_state_(http2_state) {
MakeWeak();
statistics_.start_time = uv_hrtime();

// Capture the configuration options for this session
Http2Options opts(env, type);
Http2Options opts(http2_state, type);

max_session_memory_ = opts.GetMaxSessionMemory();

Expand Down Expand Up @@ -521,13 +520,13 @@ Http2Session::Http2Session(Environment* env,

// Make the js_fields_ property accessible to JS land.
js_fields_store_ =
ArrayBuffer::NewBackingStore(env->isolate(), sizeof(SessionJSFields));
ArrayBuffer::NewBackingStore(env()->isolate(), sizeof(SessionJSFields));
js_fields_ = new(js_fields_store_->Data()) SessionJSFields;

Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), js_fields_store_);
Local<ArrayBuffer> ab = ArrayBuffer::New(env()->isolate(), js_fields_store_);
Local<Uint8Array> uint8_arr =
Uint8Array::New(ab, 0, kSessionUint8FieldCount);
USE(wrap->Set(env->context(), env->fields_string(), uint8_arr));
USE(wrap->Set(env()->context(), env()->fields_string(), uint8_arr));
}

Http2Session::~Http2Session() {
Expand All @@ -551,15 +550,17 @@ inline bool HasHttp2Observer(Environment* env) {
}

void Http2Stream::EmitStatistics() {
CHECK_NOT_NULL(session());
if (!HasHttp2Observer(env()))
return;
auto entry =
std::make_unique<Http2StreamPerformanceEntry>(env(), id_, statistics_);
std::make_unique<Http2StreamPerformanceEntry>(
session()->http2_state(), id_, statistics_);
env()->SetImmediate([entry = move(entry)](Environment* env) {
if (!HasHttp2Observer(env))
return;
HandleScope handle_scope(env->isolate());
AliasedFloat64Array& buffer = env->http2_state()->stream_stats_buffer;
AliasedFloat64Array& buffer = entry->http2_state()->stream_stats_buffer;
buffer[IDX_STREAM_STATS_ID] = entry->id();
if (entry->first_byte() != 0) {
buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTE] =
Expand Down Expand Up @@ -592,12 +593,12 @@ void Http2Session::EmitStatistics() {
if (!HasHttp2Observer(env()))
return;
auto entry = std::make_unique<Http2SessionPerformanceEntry>(
env(), statistics_, session_type_);
http2_state(), statistics_, session_type_);
env()->SetImmediate([entry = std::move(entry)](Environment* env) {
if (!HasHttp2Observer(env))
return;
HandleScope handle_scope(env->isolate());
AliasedFloat64Array& buffer = env->http2_state()->session_stats_buffer;
AliasedFloat64Array& buffer = entry->http2_state()->session_stats_buffer;
buffer[IDX_SESSION_STATS_TYPE] = entry->type();
buffer[IDX_SESSION_STATS_PINGRTT] = entry->ping_rtt() / 1e6;
buffer[IDX_SESSION_STATS_FRAMESRECEIVED] = entry->frame_count();
Expand Down Expand Up @@ -2334,24 +2335,25 @@ void HttpErrorString(const FunctionCallbackInfo<Value>& args) {
// would be suitable, for instance, for creating the Base64
// output for an HTTP2-Settings header field.
void PackSettings(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Http2State* state = Unwrap<Http2State>(args.Data());
Environment* env = state->env();
// TODO(addaleax): We should not be creating a full AsyncWrap for this.
Local<Object> obj;
if (!env->http2settings_constructor_template()
->NewInstance(env->context())
.ToLocal(&obj)) {
return;
}
Http2Session::Http2Settings settings(env, nullptr, obj);
Http2Session::Http2Settings settings(state, nullptr, obj);
args.GetReturnValue().Set(settings.Pack());
}

// A TypedArray instance is shared between C++ and JS land to contain the
// default SETTINGS. RefreshDefaultSettings updates that TypedArray with the
// default values.
void RefreshDefaultSettings(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Http2Session::Http2Settings::RefreshDefaults(env);
Http2State* state = Unwrap<Http2State>(args.Data());
Http2Session::Http2Settings::RefreshDefaults(state);
}

// Sets the next stream ID the Http2Session. If successful, returns true.
Expand All @@ -2373,23 +2375,21 @@ void Http2Session::SetNextStreamID(const FunctionCallbackInfo<Value>& args) {
// values established for each of the settings so those can be read in JS land.
template <get_setting fn>
void Http2Session::RefreshSettings(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Http2Session* session;
ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
Http2Settings::Update(env, session, fn);
Http2Settings::Update(session, fn);
Debug(session, "settings refreshed for session");
}

// A TypedArray instance is shared between C++ and JS land to contain state
// information of the current Http2Session. This updates the values in the
// TypedArray so those can be read in JS land.
void Http2Session::RefreshState(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Http2Session* session;
ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
Debug(session, "refreshing state");

AliasedFloat64Array& buffer = env->http2_state()->session_state_buffer;
AliasedFloat64Array& buffer = session->http2_state()->session_state_buffer;

nghttp2_session* s = **session;

Expand All @@ -2416,11 +2416,12 @@ void Http2Session::RefreshState(const FunctionCallbackInfo<Value>& args) {

// Constructor for new Http2Session instances.
void Http2Session::New(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Http2State* state = Unwrap<Http2State>(args.Data());
Environment* env = state->env();
CHECK(args.IsConstructCall());
int32_t val = args[0]->Int32Value(env->context()).ToChecked();
nghttp2_session_type type = static_cast<nghttp2_session_type>(val);
Http2Session* session = new Http2Session(env, args.This(), type);
Http2Session* session = new Http2Session(state, args.This(), type);
session->get_async_id(); // avoid compiler warning
Debug(session, "session created");
}
Expand Down Expand Up @@ -2645,13 +2646,14 @@ void Http2Stream::Priority(const FunctionCallbackInfo<Value>& args) {
// information about the Http2Stream. This updates the values in that
// TypedArray so that the state can be read by JS.
void Http2Stream::RefreshState(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Http2Stream* stream;
ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder());

Debug(stream, "refreshing state");

AliasedFloat64Array& buffer = env->http2_state()->stream_state_buffer;
CHECK_NOT_NULL(stream->session());
AliasedFloat64Array& buffer =
stream->session()->http2_state()->stream_state_buffer;

nghttp2_stream* str = **stream;
nghttp2_session* s = **(stream->session());
Expand Down Expand Up @@ -2797,7 +2799,8 @@ void Http2Session::Settings(const FunctionCallbackInfo<Value>& args) {
return;

Http2Settings* settings = session->AddSettings(
MakeDetachedBaseObject<Http2Settings>(session->env(), session, obj, 0));
MakeDetachedBaseObject<Http2Settings>(
session->http2_state(), session, obj, 0));
if (settings == nullptr) return args.GetReturnValue().Set(false);

settings->Send();
Expand Down Expand Up @@ -2921,16 +2924,22 @@ void SetCallbackFunctions(const FunctionCallbackInfo<Value>& args) {
#undef SET_FUNCTION
}

void Http2State::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackField("root_buffer", root_buffer);
}

// Set up the process.binding('http2') binding.
void Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv) {
Environment* env = Environment::GetCurrent(context);
Isolate* isolate = env->isolate();
HandleScope scope(isolate);
HandleScope handle_scope(isolate);

std::unique_ptr<Http2State> state(new Http2State(isolate));
Environment::BindingScope<Http2State> binding_scope(env);
if (!binding_scope) return;
Http2State* state = binding_scope.data;

#define SET_STATE_TYPEDARRAY(name, field) \
target->Set(context, \
Expand All @@ -2953,8 +2962,6 @@ void Initialize(Local<Object> target,
"sessionStats", state->session_stats_buffer.GetJSArray());
#undef SET_STATE_TYPEDARRAY

env->set_http2_state(std::move(state));

NODE_DEFINE_CONSTANT(target, kBitfield);
NODE_DEFINE_CONSTANT(target, kSessionPriorityListenerCount);
NODE_DEFINE_CONSTANT(target, kSessionFrameErrorListenerCount);
Expand Down

0 comments on commit f54b5b2

Please sign in to comment.