Browse Source

http2: minor refactor of passing headers to JS

- 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: https://github.com/nodejs/node/pull/14808
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Timothy Gu <timothygu99@gmail.com>
canary-base
Anna Henningsen 7 years ago
parent
commit
4766fcb96d
No known key found for this signature in database GPG Key ID: D8B9F5AEAE84E4CF
  1. 13
      benchmark/http2/headers.js
  2. 5
      src/env.h
  3. 6
      src/node_http2.cc
  4. 44
      src/node_http2.h

13
benchmark/http2/headers.js

@ -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) {
@ -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}`;
}

5
src/env.h

@ -39,6 +39,9 @@
#include <stdint.h>
#include <vector>
#include <stack>
#include <unordered_map>
struct nghttp2_rcbuf;
namespace node {
@ -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)

6
src/node_http2.cc

@ -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;

44
src/node_http2.h

@ -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:

Loading…
Cancel
Save