mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
563 lines
27 KiB
563 lines
27 KiB
#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 "node_http2_state.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_CREDENTIALS, "access-control-allow-credentials") \
|
|
V(ACCESS_CONTROL_ALLOW_HEADERS, "access-control-allow-headers") \
|
|
V(ACCESS_CONTROL_ALLOW_METHODS, "access-control-allow-methods") \
|
|
V(ACCESS_CONTROL_ALLOW_ORIGIN, "access-control-allow-origin") \
|
|
V(ACCESS_CONTROL_EXPOSE_HEADERS, "access-control-expose-headers") \
|
|
V(ACCESS_CONTROL_MAX_AGE, "access-control-max-age") \
|
|
V(ACCESS_CONTROL_REQUEST_HEADERS, "access-control-request-headers") \
|
|
V(ACCESS_CONTROL_REQUEST_METHOD, "access-control-request-method") \
|
|
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(DNT, "dnt") \
|
|
V(ETAG, "etag") \
|
|
V(EXPECT, "expect") \
|
|
V(EXPIRES, "expires") \
|
|
V(FORWARDED, "forwarded") \
|
|
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(TRAILER, "trailer") \
|
|
V(TRANSFER_ENCODING, "transfer-encoding") \
|
|
V(TE, "te") \
|
|
V(TK, "tk") \
|
|
V(UPGRADE_INSECURE_REQUESTS, "upgrade-insecure-requests") \
|
|
V(UPGRADE, "upgrade") \
|
|
V(USER_AGENT, "user-agent") \
|
|
V(VARY, "vary") \
|
|
V(VIA, "via") \
|
|
V(WARNING, "warning") \
|
|
V(WWW_AUTHENTICATE, "www-authenticate") \
|
|
V(X_CONTENT_TYPE_OPTIONS, "x-content-type-options") \
|
|
V(X_FRAME_OPTIONS, "x-frame-options") \
|
|
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*() const {
|
|
return options_;
|
|
}
|
|
|
|
void SetPaddingStrategy(padding_strategy_type val) {
|
|
CHECK_LE(val, PADDING_STRATEGY_CALLBACK);
|
|
padding_strategy_ = static_cast<padding_strategy_type>(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);
|
|
}
|
|
|
|
~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,
|
|
const SubmitTrailers& submit_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 Destroying(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;
|
|
|
|
char stream_buf_[kAllocBufferSize];
|
|
};
|
|
|
|
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 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 String::Empty(env->isolate());
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
ExternalHeader* h_str = new ExternalHeader(buf);
|
|
MaybeLocal<String> str = String::NewExternalOneByte(env->isolate(), h_str);
|
|
if (str.IsEmpty())
|
|
delete h_str;
|
|
|
|
return str;
|
|
}
|
|
|
|
private:
|
|
nghttp2_rcbuf* buf_;
|
|
nghttp2_vec vec_;
|
|
};
|
|
|
|
class Headers {
|
|
public:
|
|
Headers(Isolate* isolate, Local<Context> context, Local<Array> headers);
|
|
~Headers() {}
|
|
|
|
nghttp2_nv* operator*() {
|
|
return reinterpret_cast<nghttp2_nv*>(*buf_);
|
|
}
|
|
|
|
size_t length() const {
|
|
return count_;
|
|
}
|
|
|
|
private:
|
|
size_t count_;
|
|
MaybeStackBuffer<char, 3000> buf_;
|
|
};
|
|
|
|
} // namespace http2
|
|
} // namespace node
|
|
|
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#endif // SRC_NODE_HTTP2_H_
|
|
|