Skip to content

Commit

Permalink
http2: minor refactor of passing headers to JS
Browse files Browse the repository at this point in the history
- Make `ExternalString::New` return a `MaybeLocal`. Failing is
  left up to the caller.
- Allow creating internalized strings for short header names
  to reduce memory consumption and increase performance.
- Use persistent storage for statically allocated header names.

PR-URL: #14808
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Timothy Gu <[email protected]>
  • Loading branch information
addaleax authored and MylesBorins committed Sep 12, 2017
1 parent 56bb199 commit 1b57c37
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 14 deletions.
13 changes: 11 additions & 2 deletions benchmark/http2/headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const PORT = common.PORT;

var bench = common.createBenchmark(main, {
n: [1e3],
nheaders: [100, 1000],
nheaders: [0, 10, 100, 1000],
}, { flags: ['--expose-http2', '--no-warnings'] });

function main(conf) {
Expand All @@ -14,7 +14,16 @@ function main(conf) {
const http2 = require('http2');
const server = http2.createServer();

const headersObject = { ':path': '/' };
const headersObject = {
':path': '/',
':scheme': 'http',
'accept-encoding': 'gzip, deflate',
'accept-language': 'en',
'content-type': 'text/plain',
'referer': 'https://example.org/',
'user-agent': 'SuperBenchmarker 3000'
};

for (var i = 0; i < nheaders; i++) {
headersObject[`foo${i}`] = `some header value ${i}`;
}
Expand Down
5 changes: 5 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
#include <stdint.h>
#include <vector>
#include <stack>
#include <unordered_map>

struct nghttp2_rcbuf;

namespace node {

Expand Down Expand Up @@ -341,6 +344,8 @@ class IsolateData {
#undef VS
#undef VP

std::unordered_map<nghttp2_rcbuf*, v8::Eternal<v8::String>> http2_static_strs;

private:
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
Expand Down
6 changes: 4 additions & 2 deletions src/node_http2.cc
Original file line number Diff line number Diff line change
Expand Up @@ -901,8 +901,10 @@ void Http2Session::OnHeaders(Nghttp2Stream* stream,
while (headers != nullptr && j < arraysize(argv) / 2) {
nghttp2_header_list* item = headers;
// The header name and value are passed as external one-byte strings
name_str = ExternalHeader::New(isolate, item->name);
value_str = ExternalHeader::New(isolate, item->value);
name_str =
ExternalHeader::New<true>(env(), item->name).ToLocalChecked();
value_str =
ExternalHeader::New<false>(env(), item->value).ToLocalChecked();
argv[j * 2] = name_str;
argv[j * 2 + 1] = value_str;
headers = item->next;
Expand Down
44 changes: 34 additions & 10 deletions src/node_http2.h
Original file line number Diff line number Diff line change
Expand Up @@ -472,24 +472,48 @@ class ExternalHeader :
return vec_.len;
}

static Local<String> New(Isolate* isolate, nghttp2_rcbuf* buf) {
EscapableHandleScope scope(isolate);
static inline
MaybeLocal<String> GetInternalizedString(Environment* env,
const nghttp2_vec& vec) {
return String::NewFromOneByte(env->isolate(),
vec.base,
v8::NewStringType::kInternalized,
vec.len);
}

template<bool may_internalize>
static MaybeLocal<String> New(Environment* env, nghttp2_rcbuf* buf) {
if (nghttp2_rcbuf_is_static(buf)) {
auto& static_str_map = env->isolate_data()->http2_static_strs;
v8::Eternal<v8::String>& eternal = static_str_map[buf];
if (eternal.IsEmpty()) {
Local<String> str =
GetInternalizedString(env, nghttp2_rcbuf_get_buf(buf))
.ToLocalChecked();
eternal.Set(env->isolate(), str);
return str;
}
return eternal.Get(env->isolate());
}

nghttp2_vec vec = nghttp2_rcbuf_get_buf(buf);
if (vec.len == 0) {
nghttp2_rcbuf_decref(buf);
return scope.Escape(String::Empty(isolate));
return String::Empty(env->isolate());
}

ExternalHeader* h_str = new ExternalHeader(buf);
MaybeLocal<String> str = String::NewExternalOneByte(isolate, h_str);
isolate->AdjustAmountOfExternalAllocatedMemory(vec.len);
if (may_internalize && vec.len < 64) {
// This is a short header name, so there is a good chance V8 already has
// it internalized.
return GetInternalizedString(env, vec);
}

if (str.IsEmpty()) {
ExternalHeader* h_str = new ExternalHeader(buf);
MaybeLocal<String> str = String::NewExternalOneByte(env->isolate(), h_str);
if (str.IsEmpty())
delete h_str;
return scope.Escape(String::Empty(isolate));
}

return scope.Escape(str.ToLocalChecked());
return str;
}

private:
Expand Down

0 comments on commit 1b57c37

Please sign in to comment.