diff options
author | James M Snell <jasnell@gmail.com> | 2017-07-17 10:17:16 -0700 |
---|---|---|
committer | James M Snell <jasnell@gmail.com> | 2017-08-04 12:55:44 -0700 |
commit | e71e71b5138c3dfee080f4215dd957dc7a6cbdaf (patch) | |
tree | a46b77ae1bd423c0db3cf0f65ea370a721b67c24 /src/node_http2.h | |
parent | 71a1876f6c3f2a7131c7019d63fea5c8fa740276 (diff) | |
download | android-node-v8-e71e71b5138c3dfee080f4215dd957dc7a6cbdaf.tar.gz android-node-v8-e71e71b5138c3dfee080f4215dd957dc7a6cbdaf.tar.bz2 android-node-v8-e71e71b5138c3dfee080f4215dd957dc7a6cbdaf.zip |
http2: introducing HTTP/2
At long last: The initial *experimental* implementation of HTTP/2.
This is an accumulation of the work that has been done in the nodejs/http2
repository, squashed down to a couple of commits. The original commit
history has been preserved in the nodejs/http2 repository.
This PR introduces the nghttp2 C library as a new dependency. This library
provides the majority of the HTTP/2 protocol implementation, with the rest
of the code here providing the mapping of the library into a usable JS API.
Within src, a handful of new node_http2_*.c and node_http2_*.h files are
introduced. These provide the internal mechanisms that interface with nghttp
and define the `process.binding('http2')` interface.
The JS API is defined within `internal/http2/*.js`.
There are two APIs provided: Core and Compat.
The Core API is HTTP/2 specific and is designed to be as minimal and as
efficient as possible.
The Compat API is intended to be as close to the existing HTTP/1 API as
possible, with some exceptions.
Tests, documentation and initial benchmarks are included.
The `http2` module is gated by a new `--expose-http2` command line flag.
When used, `require('http2')` will be exposed to users. Note that there
is an existing `http2` module on npm that would be impacted by the introduction
of this module, which is the main reason for gating this behind a flag.
When using `require('http2')` the first time, a process warning will be
emitted indicating that an experimental feature is being used.
To run the benchmarks, the `h2load` tool (part of the nghttp project) is
required: `./node benchmarks/http2/simple.js benchmarker=h2load`. Only
two benchmarks are currently available.
Additional configuration options to enable verbose debugging are provided:
```
$ ./configure --debug-http2 --debug-nghttp2
$ NODE_DEBUG=http2 ./node
```
The `--debug-http2` configuration option enables verbose debug statements
from the `src/node_http2_*` files. The `--debug-nghttp2` enables the nghttp
library's own verbose debug output. The `NODE_DEBUG=http2` enables JS-level
debug output.
The following illustrates as simple HTTP/2 server and client interaction:
(The HTTP/2 client and server support both plain text and TLS connections)
```jt client = http2.connect('http://localhost:80');
const req = client.request({ ':path': '/some/path' });
req.on('data', (chunk) => { /* do something with the data */ });
req.on('end', () => {
client.destroy();
});
// Plain text (non-TLS server)
const server = http2.createServer();
server.on('stream', (stream, requestHeaders) => {
stream.respond({ ':status': 200 });
stream.write('hello ');
stream.end('world');
});
server.listen(80);
```
```js
const http2 = require('http2');
const client = http2.connect('http://localhost');
```
Author: Anna Henningsen <anna@addaleax.net>
Author: Colin Ihrig <cjihrig@gmail.com>
Author: Daniel Bevenius <daniel.bevenius@gmail.com>
Author: James M Snell <jasnell@gmail.com>
Author: Jun Mukai
Author: Kelvin Jin
Author: Matteo Collina <matteo.collina@gmail.com>
Author: Robert Kowalski <rok@kowalski.gd>
Author: Santiago Gimeno <santiago.gimeno@gmail.com>
Author: Sebastiaan Deckers <sebdeckers83@gmail.com>
Author: Yosuke Furukawa <yosuke.furukawa@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/14239
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Diffstat (limited to 'src/node_http2.h')
-rw-r--r-- | src/node_http2.h | 572 |
1 files changed, 572 insertions, 0 deletions
diff --git a/src/node_http2.h b/src/node_http2.h new file mode 100644 index 0000000000..f6ccad2984 --- /dev/null +++ b/src/node_http2.h @@ -0,0 +1,572 @@ +#ifndef SRC_NODE_HTTP2_H_ +#define SRC_NODE_HTTP2_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "node_http2_core-inl.h" +#include "stream_base-inl.h" +#include "string_bytes.h" + +namespace node { +namespace http2 { + +using v8::Array; +using v8::Context; +using v8::EscapableHandleScope; +using v8::Isolate; +using v8::MaybeLocal; + +#define HTTP_KNOWN_METHODS(V) \ + V(ACL, "ACL") \ + V(BASELINE_CONTROL, "BASELINE-CONTROL") \ + V(BIND, "BIND") \ + V(CHECKIN, "CHECKIN") \ + V(CHECKOUT, "CHECKOUT") \ + V(CONNECT, "CONNECT") \ + V(COPY, "COPY") \ + V(DELETE, "DELETE") \ + V(GET, "GET") \ + V(HEAD, "HEAD") \ + V(LABEL, "LABEL") \ + V(LINK, "LINK") \ + V(LOCK, "LOCK") \ + V(MERGE, "MERGE") \ + V(MKACTIVITY, "MKACTIVITY") \ + V(MKCALENDAR, "MKCALENDAR") \ + V(MKCOL, "MKCOL") \ + V(MKREDIRECTREF, "MKREDIRECTREF") \ + V(MKWORKSPACE, "MKWORKSPACE") \ + V(MOVE, "MOVE") \ + V(OPTIONS, "OPTIONS") \ + V(ORDERPATCH, "ORDERPATCH") \ + V(PATCH, "PATCH") \ + V(POST, "POST") \ + V(PRI, "PRI") \ + V(PROPFIND, "PROPFIND") \ + V(PROPPATCH, "PROPPATCH") \ + V(PUT, "PUT") \ + V(REBIND, "REBIND") \ + V(REPORT, "REPORT") \ + V(SEARCH, "SEARCH") \ + V(TRACE, "TRACE") \ + V(UNBIND, "UNBIND") \ + V(UNCHECKOUT, "UNCHECKOUT") \ + V(UNLINK, "UNLINK") \ + V(UNLOCK, "UNLOCK") \ + V(UPDATE, "UPDATE") \ + V(UPDATEREDIRECTREF, "UPDATEREDIRECTREF") \ + V(VERSION_CONTROL, "VERSION-CONTROL") + +#define HTTP_KNOWN_HEADERS(V) \ + V(STATUS, ":status") \ + V(METHOD, ":method") \ + V(AUTHORITY, ":authority") \ + V(SCHEME, ":scheme") \ + V(PATH, ":path") \ + V(ACCEPT_CHARSET, "accept-charset") \ + V(ACCEPT_ENCODING, "accept-encoding") \ + V(ACCEPT_LANGUAGE, "accept-language") \ + V(ACCEPT_RANGES, "accept-ranges") \ + V(ACCEPT, "accept") \ + V(ACCESS_CONTROL_ALLOW_ORIGIN, "access-control-allow-origin") \ + V(AGE, "age") \ + V(ALLOW, "allow") \ + V(AUTHORIZATION, "authorization") \ + V(CACHE_CONTROL, "cache-control") \ + V(CONNECTION, "connection") \ + V(CONTENT_DISPOSITION, "content-disposition") \ + V(CONTENT_ENCODING, "content-encoding") \ + V(CONTENT_LANGUAGE, "content-language") \ + V(CONTENT_LENGTH, "content-length") \ + V(CONTENT_LOCATION, "content-location") \ + V(CONTENT_MD5, "content-md5") \ + V(CONTENT_RANGE, "content-range") \ + V(CONTENT_TYPE, "content-type") \ + V(COOKIE, "cookie") \ + V(DATE, "date") \ + V(ETAG, "etag") \ + V(EXPECT, "expect") \ + V(EXPIRES, "expires") \ + V(FROM, "from") \ + V(HOST, "host") \ + V(IF_MATCH, "if-match") \ + V(IF_MODIFIED_SINCE, "if-modified-since") \ + V(IF_NONE_MATCH, "if-none-match") \ + V(IF_RANGE, "if-range") \ + V(IF_UNMODIFIED_SINCE, "if-unmodified-since") \ + V(LAST_MODIFIED, "last-modified") \ + V(LINK, "link") \ + V(LOCATION, "location") \ + V(MAX_FORWARDS, "max-forwards") \ + V(PREFER, "prefer") \ + V(PROXY_AUTHENTICATE, "proxy-authenticate") \ + V(PROXY_AUTHORIZATION, "proxy-authorization") \ + V(RANGE, "range") \ + V(REFERER, "referer") \ + V(REFRESH, "refresh") \ + V(RETRY_AFTER, "retry-after") \ + V(SERVER, "server") \ + V(SET_COOKIE, "set-cookie") \ + V(STRICT_TRANSPORT_SECURITY, "strict-transport-security") \ + V(TRANSFER_ENCODING, "transfer-encoding") \ + V(TE, "te") \ + V(UPGRADE, "upgrade") \ + V(USER_AGENT, "user-agent") \ + V(VARY, "vary") \ + V(VIA, "via") \ + V(WWW_AUTHENTICATE, "www-authenticate") \ + V(HTTP2_SETTINGS, "http2-settings") \ + V(KEEP_ALIVE, "keep-alive") \ + V(PROXY_CONNECTION, "proxy-connection") + +enum http_known_headers { +HTTP_KNOWN_HEADER_MIN, +#define V(name, value) HTTP_HEADER_##name, +HTTP_KNOWN_HEADERS(V) +#undef V +HTTP_KNOWN_HEADER_MAX +}; + +#define HTTP_STATUS_CODES(V) \ + V(CONTINUE, 100) \ + V(SWITCHING_PROTOCOLS, 101) \ + V(PROCESSING, 102) \ + V(OK, 200) \ + V(CREATED, 201) \ + V(ACCEPTED, 202) \ + V(NON_AUTHORITATIVE_INFORMATION, 203) \ + V(NO_CONTENT, 204) \ + V(RESET_CONTENT, 205) \ + V(PARTIAL_CONTENT, 206) \ + V(MULTI_STATUS, 207) \ + V(ALREADY_REPORTED, 208) \ + V(IM_USED, 226) \ + V(MULTIPLE_CHOICES, 300) \ + V(MOVED_PERMANENTLY, 301) \ + V(FOUND, 302) \ + V(SEE_OTHER, 303) \ + V(NOT_MODIFIED, 304) \ + V(USE_PROXY, 305) \ + V(TEMPORARY_REDIRECT, 307) \ + V(PERMANENT_REDIRECT, 308) \ + V(BAD_REQUEST, 400) \ + V(UNAUTHORIZED, 401) \ + V(PAYMENT_REQUIRED, 402) \ + V(FORBIDDEN, 403) \ + V(NOT_FOUND, 404) \ + V(METHOD_NOT_ALLOWED, 405) \ + V(NOT_ACCEPTABLE, 406) \ + V(PROXY_AUTHENTICATION_REQUIRED, 407) \ + V(REQUEST_TIMEOUT, 408) \ + V(CONFLICT, 409) \ + V(GONE, 410) \ + V(LENGTH_REQUIRED, 411) \ + V(PRECONDITION_FAILED, 412) \ + V(PAYLOAD_TOO_LARGE, 413) \ + V(URI_TOO_LONG, 414) \ + V(UNSUPPORTED_MEDIA_TYPE, 415) \ + V(RANGE_NOT_SATISFIABLE, 416) \ + V(EXPECTATION_FAILED, 417) \ + V(TEAPOT, 418) \ + V(MISDIRECTED_REQUEST, 421) \ + V(UNPROCESSABLE_ENTITY, 422) \ + V(LOCKED, 423) \ + V(FAILED_DEPENDENCY, 424) \ + V(UNORDERED_COLLECTION, 425) \ + V(UPGRADE_REQUIRED, 426) \ + V(PRECONDITION_REQUIRED, 428) \ + V(TOO_MANY_REQUESTS, 429) \ + V(REQUEST_HEADER_FIELDS_TOO_LARGE, 431) \ + V(UNAVAILABLE_FOR_LEGAL_REASONS, 451) \ + V(INTERNAL_SERVER_ERROR, 500) \ + V(NOT_IMPLEMENTED, 501) \ + V(BAD_GATEWAY, 502) \ + V(SERVICE_UNAVAILABLE, 503) \ + V(GATEWAY_TIMEOUT, 504) \ + V(HTTP_VERSION_NOT_SUPPORTED, 505) \ + V(VARIANT_ALSO_NEGOTIATES, 506) \ + V(INSUFFICIENT_STORAGE, 507) \ + V(LOOP_DETECTED, 508) \ + V(BANDWIDTH_LIMIT_EXCEEDED, 509) \ + V(NOT_EXTENDED, 510) \ + V(NETWORK_AUTHENTICATION_REQUIRED, 511) + +enum http_status_codes { +#define V(name, code) HTTP_STATUS_##name = code, +HTTP_STATUS_CODES(V) +#undef V +}; + +enum padding_strategy_type { + // No padding strategy + PADDING_STRATEGY_NONE, + // Padding will ensure all data frames are maxFrameSize + PADDING_STRATEGY_MAX, + // Padding will be determined via JS callback + PADDING_STRATEGY_CALLBACK +}; + +#define NGHTTP2_ERROR_CODES(V) \ + V(NGHTTP2_ERR_INVALID_ARGUMENT) \ + V(NGHTTP2_ERR_BUFFER_ERROR) \ + V(NGHTTP2_ERR_UNSUPPORTED_VERSION) \ + V(NGHTTP2_ERR_WOULDBLOCK) \ + V(NGHTTP2_ERR_PROTO) \ + V(NGHTTP2_ERR_INVALID_FRAME) \ + V(NGHTTP2_ERR_EOF) \ + V(NGHTTP2_ERR_DEFERRED) \ + V(NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE) \ + V(NGHTTP2_ERR_STREAM_CLOSED) \ + V(NGHTTP2_ERR_STREAM_CLOSING) \ + V(NGHTTP2_ERR_STREAM_SHUT_WR) \ + V(NGHTTP2_ERR_INVALID_STREAM_ID) \ + V(NGHTTP2_ERR_INVALID_STREAM_STATE) \ + V(NGHTTP2_ERR_DEFERRED_DATA_EXIST) \ + V(NGHTTP2_ERR_START_STREAM_NOT_ALLOWED) \ + V(NGHTTP2_ERR_GOAWAY_ALREADY_SENT) \ + V(NGHTTP2_ERR_INVALID_HEADER_BLOCK) \ + V(NGHTTP2_ERR_INVALID_STATE) \ + V(NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) \ + V(NGHTTP2_ERR_FRAME_SIZE_ERROR) \ + V(NGHTTP2_ERR_HEADER_COMP) \ + V(NGHTTP2_ERR_FLOW_CONTROL) \ + V(NGHTTP2_ERR_INSUFF_BUFSIZE) \ + V(NGHTTP2_ERR_PAUSE) \ + V(NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS) \ + V(NGHTTP2_ERR_PUSH_DISABLED) \ + V(NGHTTP2_ERR_DATA_EXIST) \ + V(NGHTTP2_ERR_SESSION_CLOSING) \ + V(NGHTTP2_ERR_HTTP_HEADER) \ + V(NGHTTP2_ERR_HTTP_MESSAGING) \ + V(NGHTTP2_ERR_REFUSED_STREAM) \ + V(NGHTTP2_ERR_INTERNAL) \ + V(NGHTTP2_ERR_CANCEL) \ + V(NGHTTP2_ERR_FATAL) \ + V(NGHTTP2_ERR_NOMEM) \ + V(NGHTTP2_ERR_CALLBACK_FAILURE) \ + V(NGHTTP2_ERR_BAD_CLIENT_MAGIC) \ + V(NGHTTP2_ERR_FLOODED) + +const char* nghttp2_errname(int rv) { + switch (rv) { +#define V(code) case code: return #code; + NGHTTP2_ERROR_CODES(V) +#undef V + default: + return "NGHTTP2_UNKNOWN_ERROR"; + } +} + +#define DEFAULT_SETTINGS_HEADER_TABLE_SIZE 4096 +#define DEFAULT_SETTINGS_ENABLE_PUSH 1 +#define DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE 65535 +#define DEFAULT_SETTINGS_MAX_FRAME_SIZE 16384 +#define MAX_MAX_FRAME_SIZE 16777215 +#define MIN_MAX_FRAME_SIZE DEFAULT_SETTINGS_MAX_FRAME_SIZE +#define MAX_INITIAL_WINDOW_SIZE 2147483647 + +class Http2Options { + public: + explicit Http2Options(Environment* env); + + ~Http2Options() { + nghttp2_option_del(options_); + } + + nghttp2_option* operator*() { + return options_; + } + + void SetPaddingStrategy(uint32_t val) { + CHECK_LE(val, PADDING_STRATEGY_CALLBACK); + padding_strategy_ = static_cast<padding_strategy_type>(val); + } + + void SetMaxDeflateDynamicTableSize(size_t val) { + nghttp2_option_set_max_deflate_dynamic_table_size(options_, val); + } + + void SetMaxReservedRemoteStreams(uint32_t val) { + nghttp2_option_set_max_reserved_remote_streams(options_, val); + } + + void SetMaxSendHeaderBlockLength(size_t val) { + nghttp2_option_set_max_send_header_block_length(options_, val); + } + + void SetPeerMaxConcurrentStreams(uint32_t val) { + nghttp2_option_set_peer_max_concurrent_streams(options_, val); + } + + padding_strategy_type GetPaddingStrategy() { + return padding_strategy_; + } + + private: + nghttp2_option* options_; + padding_strategy_type padding_strategy_ = PADDING_STRATEGY_NONE; +}; + +static const size_t kAllocBufferSize = 64 * 1024; + +//// +typedef uint32_t(*get_setting)(nghttp2_session* session, + nghttp2_settings_id id); + +class Http2Session : public AsyncWrap, + public StreamBase, + public Nghttp2Session { + public: + Http2Session(Environment* env, + Local<Object> wrap, + nghttp2_session_type type) : + AsyncWrap(env, wrap, AsyncWrap::PROVIDER_HTTP2SESSION), + StreamBase(env) { + Wrap(object(), this); + + Http2Options opts(env); + + padding_strategy_ = opts.GetPaddingStrategy(); + + Init(env->event_loop(), type, *opts); + stream_buf_.AllocateSufficientStorage(kAllocBufferSize); + } + + ~Http2Session() override { + CHECK_EQ(false, persistent().IsEmpty()); + ClearWrap(object()); + persistent().Reset(); + CHECK_EQ(true, persistent().IsEmpty()); + } + + static void OnStreamAllocImpl(size_t suggested_size, + uv_buf_t* buf, + void* ctx); + static void OnStreamReadImpl(ssize_t nread, + const uv_buf_t* bufs, + uv_handle_type pending, + void* ctx); + protected: + void OnFreeSession() override; + + ssize_t OnMaxFrameSizePadding(size_t frameLength, + size_t maxPayloadLen); + + ssize_t OnCallbackPadding(size_t frame, + size_t maxPayloadLen); + + bool HasGetPaddingCallback() override { + return padding_strategy_ == PADDING_STRATEGY_MAX || + padding_strategy_ == PADDING_STRATEGY_CALLBACK; + } + + ssize_t GetPadding(size_t frameLength, size_t maxPayloadLen) override { + if (padding_strategy_ == PADDING_STRATEGY_MAX) { + return OnMaxFrameSizePadding(frameLength, maxPayloadLen); + } + + CHECK_EQ(padding_strategy_, PADDING_STRATEGY_CALLBACK); + + return OnCallbackPadding(frameLength, maxPayloadLen); + } + + void OnHeaders(Nghttp2Stream* stream, + nghttp2_header_list* headers, + nghttp2_headers_category cat, + uint8_t flags) override; + void OnStreamClose(int32_t id, uint32_t code) override; + void Send(uv_buf_t* bufs, size_t total) override; + void OnDataChunk(Nghttp2Stream* stream, nghttp2_data_chunk_t* chunk) override; + void OnSettings(bool ack) override; + void OnPriority(int32_t stream, + int32_t parent, + int32_t weight, + int8_t exclusive) override; + void OnGoAway(int32_t lastStreamID, + uint32_t errorCode, + uint8_t* data, + size_t length) override; + void OnFrameError(int32_t id, uint8_t type, int error_code) override; + void OnTrailers(Nghttp2Stream* stream, + MaybeStackBuffer<nghttp2_nv>* trailers) override; + void AllocateSend(size_t recommended, uv_buf_t* buf) override; + + int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count, + uv_stream_t* send_handle) override; + + AsyncWrap* GetAsyncWrap() override { + return static_cast<AsyncWrap*>(this); + } + + void* Cast() override { + return reinterpret_cast<void*>(this); + } + + // Required for StreamBase + bool IsAlive() override { + return true; + } + + // Required for StreamBase + bool IsClosing() override { + return false; + } + + // Required for StreamBase + int ReadStart() override { return 0; } + + // Required for StreamBase + int ReadStop() override { return 0; } + + // Required for StreamBase + int DoShutdown(ShutdownWrap* req_wrap) override { + return 0; + } + + public: + void Consume(Local<External> external); + void Unconsume(); + + static void New(const FunctionCallbackInfo<Value>& args); + static void Consume(const FunctionCallbackInfo<Value>& args); + static void Unconsume(const FunctionCallbackInfo<Value>& args); + static void Destroy(const FunctionCallbackInfo<Value>& args); + static void SubmitSettings(const FunctionCallbackInfo<Value>& args); + static void SubmitRstStream(const FunctionCallbackInfo<Value>& args); + static void SubmitResponse(const FunctionCallbackInfo<Value>& args); + static void SubmitFile(const FunctionCallbackInfo<Value>& args); + static void SubmitRequest(const FunctionCallbackInfo<Value>& args); + static void SubmitPushPromise(const FunctionCallbackInfo<Value>& args); + static void SubmitPriority(const FunctionCallbackInfo<Value>& args); + static void SendHeaders(const FunctionCallbackInfo<Value>& args); + static void ShutdownStream(const FunctionCallbackInfo<Value>& args); + static void StreamWrite(const FunctionCallbackInfo<Value>& args); + static void StreamReadStart(const FunctionCallbackInfo<Value>& args); + static void StreamReadStop(const FunctionCallbackInfo<Value>& args); + static void SetNextStreamID(const FunctionCallbackInfo<Value>& args); + static void SendShutdownNotice(const FunctionCallbackInfo<Value>& args); + static void SubmitGoaway(const FunctionCallbackInfo<Value>& args); + static void DestroyStream(const FunctionCallbackInfo<Value>& args); + + template <get_setting fn> + static void GetSettings(const FunctionCallbackInfo<Value>& args); + + size_t self_size() const override { + return sizeof(*this); + } + + char* stream_alloc() { + return *stream_buf_; + } + + private: + StreamBase* stream_; + StreamResource::Callback<StreamResource::AllocCb> prev_alloc_cb_; + StreamResource::Callback<StreamResource::ReadCb> prev_read_cb_; + padding_strategy_type padding_strategy_ = PADDING_STRATEGY_NONE; + MaybeStackBuffer<char, kAllocBufferSize> stream_buf_; +}; + +class ExternalHeader : + public String::ExternalOneByteStringResource { + public: + explicit ExternalHeader(nghttp2_rcbuf* buf) + : buf_(buf), vec_(nghttp2_rcbuf_get_buf(buf)) { + } + + ~ExternalHeader() override { + nghttp2_rcbuf_decref(buf_); + buf_ = nullptr; + } + + const char* data() const override { + return const_cast<const char*>(reinterpret_cast<char*>(vec_.base)); + } + + size_t length() const override { + return vec_.len; + } + + static Local<String> New(Isolate* isolate, nghttp2_rcbuf* buf) { + EscapableHandleScope scope(isolate); + nghttp2_vec vec = nghttp2_rcbuf_get_buf(buf); + if (vec.len == 0) { + nghttp2_rcbuf_decref(buf); + return scope.Escape(String::Empty(isolate)); + } + + ExternalHeader* h_str = new ExternalHeader(buf); + MaybeLocal<String> str = String::NewExternalOneByte(isolate, h_str); + isolate->AdjustAmountOfExternalAllocatedMemory(vec.len); + + if (str.IsEmpty()) { + delete h_str; + return scope.Escape(String::Empty(isolate)); + } + + return scope.Escape(str.ToLocalChecked()); + } + + private: + nghttp2_rcbuf* buf_; + nghttp2_vec vec_; +}; + +class Headers { + public: + Headers(Isolate* isolate, Local<Context> context, Local<Array> headers) { + headers_.AllocateSufficientStorage(headers->Length()); + Local<Value> item; + Local<Array> header; + + for (size_t n = 0; n < headers->Length(); n++) { + item = headers->Get(context, n).ToLocalChecked(); + CHECK(item->IsArray()); + header = item.As<Array>(); + Local<Value> key = header->Get(context, 0).ToLocalChecked(); + Local<Value> value = header->Get(context, 1).ToLocalChecked(); + CHECK(key->IsString()); + CHECK(value->IsString()); + size_t keylen = StringBytes::StorageSize(isolate, key, ASCII); + size_t valuelen = StringBytes::StorageSize(isolate, value, ASCII); + headers_[n].flags = NGHTTP2_NV_FLAG_NONE; + Local<Value> flag = header->Get(context, 2).ToLocalChecked(); + if (flag->BooleanValue(context).ToChecked()) + headers_[n].flags |= NGHTTP2_NV_FLAG_NO_INDEX; + uint8_t* buf = Malloc<uint8_t>(keylen + valuelen); + headers_[n].name = buf; + headers_[n].value = buf + keylen; + headers_[n].namelen = + StringBytes::Write(isolate, + reinterpret_cast<char*>(headers_[n].name), + keylen, key, ASCII); + headers_[n].valuelen = + StringBytes::Write(isolate, + reinterpret_cast<char*>(headers_[n].value), + valuelen, value, ASCII); + } + } + + ~Headers() { + for (size_t n = 0; n < headers_.length(); n++) + free(headers_[n].name); + } + + nghttp2_nv* operator*() { + return *headers_; + } + + size_t length() const { + return headers_.length(); + } + + private: + MaybeStackBuffer<nghttp2_nv> headers_; +}; + +} // namespace http2 +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_HTTP2_H_ |