mirror of
https://github.com/quizhizhe/LiteLoaderBDS-1.16.40.git
synced 2025-06-07 12:18:11 +00:00
1146 lines
38 KiB
C++
1146 lines
38 KiB
C++
#ifndef SRC_NODE_HTTP2_H_
|
|
#define SRC_NODE_HTTP2_H_
|
|
|
|
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
// FIXME(joyeecheung): nghttp2.h needs stdint.h to compile on Windows
|
|
#include <cstdint>
|
|
#include "nghttp2/nghttp2.h"
|
|
|
|
#include "env.h"
|
|
#include "aliased_struct.h"
|
|
#include "node_http2_state.h"
|
|
#include "node_http_common.h"
|
|
#include "node_mem.h"
|
|
#include "node_perf.h"
|
|
#include "stream_base.h"
|
|
#include "string_bytes.h"
|
|
|
|
#include <algorithm>
|
|
#include <queue>
|
|
|
|
namespace node {
|
|
namespace http2 {
|
|
|
|
// Constants in all caps are exported as user-facing constants
|
|
// in JavaScript. Constants using the kName pattern are internal
|
|
// only.
|
|
|
|
// We strictly limit the number of outstanding unacknowledged PINGS a user
|
|
// may send in order to prevent abuse. The current default cap is 10. The
|
|
// user may set a different limit using a per Http2Session configuration
|
|
// option.
|
|
constexpr size_t kDefaultMaxPings = 10;
|
|
|
|
// Also strictly limit the number of outstanding SETTINGS frames a user sends
|
|
constexpr size_t kDefaultMaxSettings = 10;
|
|
|
|
// Default maximum total memory cap for Http2Session.
|
|
constexpr uint64_t kDefaultMaxSessionMemory = 10000000;
|
|
|
|
// These are the standard HTTP/2 defaults as specified by the RFC
|
|
constexpr uint32_t DEFAULT_SETTINGS_HEADER_TABLE_SIZE = 4096;
|
|
constexpr uint32_t DEFAULT_SETTINGS_ENABLE_PUSH = 1;
|
|
constexpr uint32_t DEFAULT_SETTINGS_MAX_CONCURRENT_STREAMS = 0xffffffffu;
|
|
constexpr uint32_t DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE = 65535;
|
|
constexpr uint32_t DEFAULT_SETTINGS_MAX_FRAME_SIZE = 16384;
|
|
constexpr uint32_t DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE = 65535;
|
|
constexpr uint32_t DEFAULT_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0;
|
|
constexpr uint32_t MAX_MAX_FRAME_SIZE = 16777215;
|
|
constexpr uint32_t MIN_MAX_FRAME_SIZE = DEFAULT_SETTINGS_MAX_FRAME_SIZE;
|
|
constexpr uint32_t MAX_INITIAL_WINDOW_SIZE = 2147483647;
|
|
|
|
// Stream is not going to have any DATA frames
|
|
constexpr int STREAM_OPTION_EMPTY_PAYLOAD = 0x1;
|
|
|
|
// Stream might have trailing headers
|
|
constexpr int STREAM_OPTION_GET_TRAILERS = 0x2;
|
|
|
|
// Http2Stream internal states
|
|
constexpr int kStreamStateNone = 0x0;
|
|
constexpr int kStreamStateShut = 0x1;
|
|
constexpr int kStreamStateReadStart = 0x2;
|
|
constexpr int kStreamStateReadPaused = 0x4;
|
|
constexpr int kStreamStateClosed = 0x8;
|
|
constexpr int kStreamStateDestroyed = 0x10;
|
|
constexpr int kStreamStateTrailers = 0x20;
|
|
|
|
// Http2Session internal states
|
|
constexpr int kSessionStateNone = 0x0;
|
|
constexpr int kSessionStateHasScope = 0x1;
|
|
constexpr int kSessionStateWriteScheduled = 0x2;
|
|
constexpr int kSessionStateClosed = 0x4;
|
|
constexpr int kSessionStateClosing = 0x8;
|
|
constexpr int kSessionStateSending = 0x10;
|
|
constexpr int kSessionStateWriteInProgress = 0x20;
|
|
constexpr int kSessionStateReadingStopped = 0x40;
|
|
constexpr int kSessionStateReceivePaused = 0x80;
|
|
|
|
// The Padding Strategy determines the method by which extra padding is
|
|
// selected for HEADERS and DATA frames. These are configurable via the
|
|
// options passed in to a Http2Session object.
|
|
enum PaddingStrategy {
|
|
// No padding strategy. This is the default.
|
|
PADDING_STRATEGY_NONE,
|
|
// Attempts to ensure that the frame is 8-byte aligned
|
|
PADDING_STRATEGY_ALIGNED,
|
|
// Padding will ensure all data frames are maxFrameSize
|
|
PADDING_STRATEGY_MAX,
|
|
// Removed and turned into an alias because it is unreasonably expensive for
|
|
// very little benefit.
|
|
PADDING_STRATEGY_CALLBACK = PADDING_STRATEGY_ALIGNED
|
|
};
|
|
|
|
enum SessionType {
|
|
NGHTTP2_SESSION_SERVER,
|
|
NGHTTP2_SESSION_CLIENT
|
|
};
|
|
|
|
template <typename T, void(*fn)(T*)>
|
|
struct Nghttp2Deleter {
|
|
void operator()(T* ptr) const noexcept { fn(ptr); }
|
|
};
|
|
|
|
using Nghttp2OptionPointer =
|
|
std::unique_ptr<nghttp2_option,
|
|
Nghttp2Deleter<nghttp2_option, nghttp2_option_del>>;
|
|
|
|
using Nghttp2SessionPointer =
|
|
std::unique_ptr<nghttp2_session,
|
|
Nghttp2Deleter<nghttp2_session, nghttp2_session_del>>;
|
|
|
|
using Nghttp2SessionCallbacksPointer =
|
|
std::unique_ptr<nghttp2_session_callbacks,
|
|
Nghttp2Deleter<nghttp2_session_callbacks,
|
|
nghttp2_session_callbacks_del>>;
|
|
|
|
struct Http2HeadersTraits {
|
|
typedef nghttp2_nv nv_t;
|
|
};
|
|
|
|
struct Http2RcBufferPointerTraits {
|
|
typedef nghttp2_rcbuf rcbuf_t;
|
|
typedef nghttp2_vec vector_t;
|
|
|
|
static void inc(rcbuf_t* buf) {
|
|
CHECK_NOT_NULL(buf);
|
|
nghttp2_rcbuf_incref(buf);
|
|
}
|
|
static void dec(rcbuf_t* buf) {
|
|
CHECK_NOT_NULL(buf);
|
|
nghttp2_rcbuf_decref(buf);
|
|
}
|
|
static vector_t get_vec(rcbuf_t* buf) {
|
|
CHECK_NOT_NULL(buf);
|
|
return nghttp2_rcbuf_get_buf(buf);
|
|
}
|
|
static bool is_static(const rcbuf_t* buf) {
|
|
CHECK_NOT_NULL(buf);
|
|
return nghttp2_rcbuf_is_static(buf);
|
|
}
|
|
};
|
|
|
|
using Http2Headers = NgHeaders<Http2HeadersTraits>;
|
|
using Http2RcBufferPointer = NgRcBufPointer<Http2RcBufferPointerTraits>;
|
|
|
|
struct NgHttp2StreamWrite : public MemoryRetainer {
|
|
BaseObjectPtr<AsyncWrap> req_wrap;
|
|
uv_buf_t buf;
|
|
|
|
inline explicit NgHttp2StreamWrite(uv_buf_t buf_) : buf(buf_) {}
|
|
inline NgHttp2StreamWrite(BaseObjectPtr<AsyncWrap> req_wrap, uv_buf_t buf_) :
|
|
req_wrap(std::move(req_wrap)), buf(buf_) {}
|
|
|
|
void MemoryInfo(MemoryTracker* tracker) const override;
|
|
SET_MEMORY_INFO_NAME(NgHttp2StreamWrite)
|
|
SET_SELF_SIZE(NgHttp2StreamWrite)
|
|
};
|
|
|
|
typedef uint32_t(*get_setting)(nghttp2_session* session,
|
|
nghttp2_settings_id id);
|
|
|
|
class Http2Ping;
|
|
class Http2Session;
|
|
class Http2Settings;
|
|
class Http2Stream;
|
|
class Origins;
|
|
|
|
// This scope should be present when any call into nghttp2 that may schedule
|
|
// data to be written to the underlying transport is made, and schedules
|
|
// such a write automatically once the scope is exited.
|
|
class Http2Scope {
|
|
public:
|
|
explicit Http2Scope(Http2Stream* stream);
|
|
explicit Http2Scope(Http2Session* session);
|
|
~Http2Scope();
|
|
|
|
private:
|
|
BaseObjectPtr<Http2Session> session_;
|
|
};
|
|
|
|
// The Http2Options class is used to parse the options object passed in to
|
|
// a Http2Session object and convert those into an appropriate nghttp2_option
|
|
// struct. This is the primary mechanism by which the Http2Session object is
|
|
// configured.
|
|
class Http2Options {
|
|
public:
|
|
Http2Options(Http2State* http2_state,
|
|
SessionType type);
|
|
|
|
~Http2Options() = default;
|
|
|
|
nghttp2_option* operator*() const {
|
|
return options_.get();
|
|
}
|
|
|
|
void set_max_header_pairs(uint32_t max) {
|
|
max_header_pairs_ = max;
|
|
}
|
|
|
|
uint32_t max_header_pairs() const {
|
|
return max_header_pairs_;
|
|
}
|
|
|
|
void set_padding_strategy(PaddingStrategy val) {
|
|
padding_strategy_ = val;
|
|
}
|
|
|
|
PaddingStrategy padding_strategy() const {
|
|
return padding_strategy_;
|
|
}
|
|
|
|
void set_max_outstanding_pings(size_t max) {
|
|
max_outstanding_pings_ = max;
|
|
}
|
|
|
|
size_t max_outstanding_pings() const {
|
|
return max_outstanding_pings_;
|
|
}
|
|
|
|
void set_max_outstanding_settings(size_t max) {
|
|
max_outstanding_settings_ = max;
|
|
}
|
|
|
|
size_t max_outstanding_settings() const {
|
|
return max_outstanding_settings_;
|
|
}
|
|
|
|
void set_max_session_memory(uint64_t max) {
|
|
max_session_memory_ = max;
|
|
}
|
|
|
|
uint64_t max_session_memory() const {
|
|
return max_session_memory_;
|
|
}
|
|
|
|
private:
|
|
Nghttp2OptionPointer options_;
|
|
uint64_t max_session_memory_ = kDefaultMaxSessionMemory;
|
|
uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS;
|
|
PaddingStrategy padding_strategy_ = PADDING_STRATEGY_NONE;
|
|
size_t max_outstanding_pings_ = kDefaultMaxPings;
|
|
size_t max_outstanding_settings_ = kDefaultMaxSettings;
|
|
};
|
|
|
|
struct Http2Priority : public nghttp2_priority_spec {
|
|
Http2Priority(Environment* env,
|
|
v8::Local<v8::Value> parent,
|
|
v8::Local<v8::Value> weight,
|
|
v8::Local<v8::Value> exclusive);
|
|
};
|
|
|
|
class Http2StreamListener : public StreamListener {
|
|
public:
|
|
uv_buf_t OnStreamAlloc(size_t suggested_size) override;
|
|
void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override;
|
|
};
|
|
|
|
struct Http2HeaderTraits {
|
|
typedef Http2RcBufferPointer rcbufferpointer_t;
|
|
typedef Http2Session allocator_t;
|
|
|
|
// HTTP/2 does not support identifying header names by token id.
|
|
// HTTP/3 will, however, so we prepare for that now.
|
|
static const char* ToHttpHeaderName(int32_t token) { return nullptr; }
|
|
};
|
|
|
|
using Http2Header = NgHeader<Http2HeaderTraits>;
|
|
|
|
class Http2Stream : public AsyncWrap,
|
|
public StreamBase {
|
|
public:
|
|
static Http2Stream* New(
|
|
Http2Session* session,
|
|
int32_t id,
|
|
nghttp2_headers_category category = NGHTTP2_HCAT_HEADERS,
|
|
int options = 0);
|
|
~Http2Stream() override;
|
|
|
|
nghttp2_stream* operator*() const;
|
|
|
|
nghttp2_stream* stream() const;
|
|
|
|
Http2Session* session() { return session_.get(); }
|
|
const Http2Session* session() const { return session_.get(); }
|
|
|
|
// Required for StreamBase
|
|
int ReadStart() override;
|
|
|
|
// Required for StreamBase
|
|
int ReadStop() override;
|
|
|
|
// Required for StreamBase
|
|
ShutdownWrap* CreateShutdownWrap(v8::Local<v8::Object> object) override;
|
|
int DoShutdown(ShutdownWrap* req_wrap) override;
|
|
|
|
bool HasWantsWrite() const override { return true; }
|
|
|
|
// Initiate a response on this stream.
|
|
int SubmitResponse(const Http2Headers& headers, int options);
|
|
|
|
// Submit informational headers for this stream
|
|
int SubmitInfo(const Http2Headers& headers);
|
|
|
|
// Submit trailing headers for this stream
|
|
int SubmitTrailers(const Http2Headers& headers);
|
|
void OnTrailers();
|
|
|
|
// Submit a PRIORITY frame for this stream
|
|
int SubmitPriority(const Http2Priority& priority, bool silent = false);
|
|
|
|
// Submits an RST_STREAM frame using the given code
|
|
void SubmitRstStream(const uint32_t code);
|
|
|
|
void FlushRstStream();
|
|
|
|
// Submits a PUSH_PROMISE frame with this stream as the parent.
|
|
Http2Stream* SubmitPushPromise(
|
|
const Http2Headers& headers,
|
|
int32_t* ret,
|
|
int options = 0);
|
|
|
|
|
|
void Close(int32_t code);
|
|
|
|
// Destroy this stream instance and free all held memory.
|
|
void Destroy();
|
|
|
|
bool is_destroyed() const {
|
|
return flags_ & kStreamStateDestroyed;
|
|
}
|
|
|
|
bool is_writable() const {
|
|
return !(flags_ & kStreamStateShut);
|
|
}
|
|
|
|
bool is_paused() const {
|
|
return flags_ & kStreamStateReadPaused;
|
|
}
|
|
|
|
bool is_closed() const {
|
|
return flags_ & kStreamStateClosed;
|
|
}
|
|
|
|
bool has_trailers() const {
|
|
return flags_ & kStreamStateTrailers;
|
|
}
|
|
|
|
void set_has_trailers(bool on = true) {
|
|
if (on)
|
|
flags_ |= kStreamStateTrailers;
|
|
else
|
|
flags_ &= ~kStreamStateTrailers;
|
|
}
|
|
|
|
void set_closed() {
|
|
flags_ |= kStreamStateClosed;
|
|
}
|
|
|
|
void set_destroyed() {
|
|
flags_ |= kStreamStateDestroyed;
|
|
}
|
|
|
|
void set_not_writable() {
|
|
flags_ |= kStreamStateShut;
|
|
}
|
|
|
|
void set_reading(bool on = true) {
|
|
if (on) {
|
|
flags_ |= kStreamStateReadStart;
|
|
set_paused(false);
|
|
} else {}
|
|
}
|
|
|
|
void set_paused(bool on = true) {
|
|
if (on)
|
|
flags_ |= kStreamStateReadPaused;
|
|
else
|
|
flags_ &= ~kStreamStateReadPaused;
|
|
}
|
|
|
|
// Returns true if this stream is in the reading state, which occurs when
|
|
// the kStreamStateReadStart flag has been set and the
|
|
// kStreamStateReadPaused flag is *not* set.
|
|
bool is_reading() const {
|
|
return flags_ & kStreamStateReadStart && !is_paused();
|
|
}
|
|
|
|
// Returns the RST_STREAM code used to close this stream
|
|
int32_t code() const { return code_; }
|
|
|
|
// Returns the stream identifier for this stream
|
|
int32_t id() const { return id_; }
|
|
|
|
void IncrementAvailableOutboundLength(size_t amount);
|
|
void DecrementAvailableOutboundLength(size_t amount);
|
|
|
|
bool AddHeader(nghttp2_rcbuf* name, nghttp2_rcbuf* value, uint8_t flags);
|
|
|
|
template <typename Fn>
|
|
void TransferHeaders(Fn&& fn) {
|
|
size_t i = 0;
|
|
for (const auto& header : current_headers_ )
|
|
fn(header, i++);
|
|
ClearHeaders();
|
|
}
|
|
|
|
void ClearHeaders() {
|
|
current_headers_.clear();
|
|
}
|
|
|
|
size_t headers_count() const {
|
|
return current_headers_.size();
|
|
}
|
|
|
|
nghttp2_headers_category headers_category() const {
|
|
return current_headers_category_;
|
|
}
|
|
|
|
void StartHeaders(nghttp2_headers_category category);
|
|
|
|
// Required for StreamBase
|
|
bool IsAlive() override {
|
|
return true;
|
|
}
|
|
|
|
// Required for StreamBase
|
|
bool IsClosing() override {
|
|
return false;
|
|
}
|
|
|
|
AsyncWrap* GetAsyncWrap() override { return this; }
|
|
|
|
int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count,
|
|
uv_stream_t* send_handle) override;
|
|
|
|
void MemoryInfo(MemoryTracker* tracker) const override;
|
|
SET_MEMORY_INFO_NAME(Http2Stream)
|
|
SET_SELF_SIZE(Http2Stream)
|
|
|
|
std::string diagnostic_name() const override;
|
|
|
|
// JavaScript API
|
|
static void GetID(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void Destroy(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void Priority(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void PushPromise(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void RefreshState(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void Info(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void Trailers(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void Respond(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void RstStream(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
|
|
class Provider;
|
|
|
|
struct Statistics {
|
|
uint64_t start_time;
|
|
uint64_t end_time;
|
|
uint64_t first_header; // Time first header was received
|
|
uint64_t first_byte; // Time first DATA frame byte was received
|
|
uint64_t first_byte_sent; // Time first DATA frame byte was sent
|
|
uint64_t sent_bytes;
|
|
uint64_t received_bytes;
|
|
uint64_t id;
|
|
};
|
|
|
|
Statistics statistics_ = {};
|
|
|
|
private:
|
|
Http2Stream(Http2Session* session,
|
|
v8::Local<v8::Object> obj,
|
|
int32_t id,
|
|
nghttp2_headers_category category,
|
|
int options);
|
|
|
|
void EmitStatistics();
|
|
|
|
BaseObjectWeakPtr<Http2Session> session_; // The Parent HTTP/2 Session
|
|
int32_t id_ = 0; // The Stream Identifier
|
|
int32_t code_ = NGHTTP2_NO_ERROR; // The RST_STREAM code (if any)
|
|
int flags_ = kStreamStateNone; // Internal state flags
|
|
|
|
uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS;
|
|
uint32_t max_header_length_ = DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE;
|
|
|
|
// The Current Headers block... As headers are received for this stream,
|
|
// they are temporarily stored here until the OnFrameReceived is called
|
|
// signalling the end of the HEADERS frame
|
|
nghttp2_headers_category current_headers_category_ = NGHTTP2_HCAT_HEADERS;
|
|
uint32_t current_headers_length_ = 0; // total number of octets
|
|
std::vector<Http2Header> current_headers_;
|
|
|
|
// This keeps track of the amount of data read from the socket while the
|
|
// socket was in paused mode. When `ReadStart()` is called (and not before
|
|
// then), we tell nghttp2 that we consumed that data to get proper
|
|
// backpressure handling.
|
|
size_t inbound_consumed_data_while_paused_ = 0;
|
|
|
|
// Outbound Data... This is the data written by the JS layer that is
|
|
// waiting to be written out to the socket.
|
|
std::queue<NgHttp2StreamWrite> queue_;
|
|
size_t available_outbound_length_ = 0;
|
|
|
|
Http2StreamListener stream_listener_;
|
|
|
|
friend class Http2Session;
|
|
};
|
|
|
|
class Http2Stream::Provider {
|
|
public:
|
|
Provider(Http2Stream* stream, int options);
|
|
explicit Provider(int options);
|
|
virtual ~Provider();
|
|
|
|
nghttp2_data_provider* operator*() {
|
|
return !empty_ ? &provider_ : nullptr;
|
|
}
|
|
|
|
class FD;
|
|
class Stream;
|
|
protected:
|
|
nghttp2_data_provider provider_;
|
|
|
|
private:
|
|
bool empty_ = false;
|
|
};
|
|
|
|
class Http2Stream::Provider::Stream : public Http2Stream::Provider {
|
|
public:
|
|
Stream(Http2Stream* stream, int options);
|
|
explicit Stream(int options);
|
|
|
|
static ssize_t OnRead(nghttp2_session* session,
|
|
int32_t id,
|
|
uint8_t* buf,
|
|
size_t length,
|
|
uint32_t* flags,
|
|
nghttp2_data_source* source,
|
|
void* user_data);
|
|
};
|
|
|
|
struct SessionJSFields {
|
|
uint8_t bitfield;
|
|
uint8_t priority_listener_count;
|
|
uint8_t frame_error_listener_count;
|
|
uint32_t max_invalid_frames = 1000;
|
|
uint32_t max_rejected_streams = 100;
|
|
};
|
|
|
|
// Indices for js_fields_, which serves as a way to communicate data with JS
|
|
// land fast. In particular, we store information about the number/presence
|
|
// of certain event listeners in JS, and skip calls from C++ into JS if they
|
|
// are missing.
|
|
enum SessionUint8Fields {
|
|
kBitfield = offsetof(SessionJSFields, bitfield), // See below
|
|
kSessionPriorityListenerCount =
|
|
offsetof(SessionJSFields, priority_listener_count),
|
|
kSessionFrameErrorListenerCount =
|
|
offsetof(SessionJSFields, frame_error_listener_count),
|
|
kSessionMaxInvalidFrames = offsetof(SessionJSFields, max_invalid_frames),
|
|
kSessionMaxRejectedStreams = offsetof(SessionJSFields, max_rejected_streams),
|
|
kSessionUint8FieldCount = sizeof(SessionJSFields)
|
|
};
|
|
|
|
enum SessionBitfieldFlags {
|
|
kSessionHasRemoteSettingsListeners,
|
|
kSessionRemoteSettingsIsUpToDate,
|
|
kSessionHasPingListeners,
|
|
kSessionHasAltsvcListeners
|
|
};
|
|
|
|
class Http2Session : public AsyncWrap,
|
|
public StreamListener,
|
|
public mem::NgLibMemoryManager<Http2Session, nghttp2_mem> {
|
|
public:
|
|
Http2Session(Http2State* http2_state,
|
|
v8::Local<v8::Object> wrap,
|
|
SessionType type = NGHTTP2_SESSION_SERVER);
|
|
~Http2Session() override;
|
|
|
|
StreamBase* underlying_stream() {
|
|
return static_cast<StreamBase*>(stream_);
|
|
}
|
|
|
|
void Close(uint32_t code = NGHTTP2_NO_ERROR,
|
|
bool socket_closed = false);
|
|
|
|
void Consume(v8::Local<v8::Object> stream);
|
|
|
|
void Goaway(uint32_t code, int32_t lastStreamID,
|
|
const uint8_t* data, size_t len);
|
|
|
|
void AltSvc(int32_t id,
|
|
uint8_t* origin,
|
|
size_t origin_len,
|
|
uint8_t* value,
|
|
size_t value_len);
|
|
|
|
void Origin(const Origins& origins);
|
|
|
|
uint8_t SendPendingData();
|
|
|
|
// Submits a new request. If the request is a success, assigned
|
|
// will be a pointer to the Http2Stream instance assigned.
|
|
// This only works if the session is a client session.
|
|
Http2Stream* SubmitRequest(
|
|
const Http2Priority& priority,
|
|
const Http2Headers& headers,
|
|
int32_t* ret,
|
|
int options = 0);
|
|
|
|
SessionType type() const { return session_type_; }
|
|
|
|
nghttp2_session* session() const { return session_.get(); }
|
|
|
|
nghttp2_session* operator*() { return session_.get(); }
|
|
|
|
uint32_t max_header_pairs() const { return max_header_pairs_; }
|
|
|
|
const char* TypeName() const;
|
|
|
|
bool is_destroyed() {
|
|
return (flags_ & kSessionStateClosed) || session_ == nullptr;
|
|
}
|
|
|
|
void set_destroyed() {
|
|
flags_ |= kSessionStateClosed;
|
|
}
|
|
|
|
#define IS_FLAG(name, flag) \
|
|
bool is_##name() const { return flags_ & flag; } \
|
|
void set_##name(bool on = true) { \
|
|
if (on) \
|
|
flags_ |= flag; \
|
|
else \
|
|
flags_ &= ~flag; \
|
|
}
|
|
|
|
IS_FLAG(in_scope, kSessionStateHasScope)
|
|
IS_FLAG(write_scheduled, kSessionStateWriteScheduled)
|
|
IS_FLAG(closing, kSessionStateClosing)
|
|
IS_FLAG(sending, kSessionStateSending)
|
|
IS_FLAG(write_in_progress, kSessionStateWriteInProgress)
|
|
IS_FLAG(reading_stopped, kSessionStateReadingStopped)
|
|
IS_FLAG(receive_paused, kSessionStateReceivePaused)
|
|
|
|
#undef IS_FLAG
|
|
|
|
// Schedule a write if nghttp2 indicates it wants to write to the socket.
|
|
void MaybeScheduleWrite();
|
|
|
|
// Stop reading if nghttp2 doesn't want to anymore.
|
|
void MaybeStopReading();
|
|
|
|
// Returns pointer to the stream, or nullptr if stream does not exist
|
|
BaseObjectPtr<Http2Stream> FindStream(int32_t id);
|
|
|
|
bool CanAddStream();
|
|
|
|
// Adds a stream instance to this session
|
|
void AddStream(Http2Stream* stream);
|
|
|
|
// Removes a stream instance from this session
|
|
BaseObjectPtr<Http2Stream> RemoveStream(int32_t id);
|
|
|
|
// Indicates whether there currently exist outgoing buffers for this stream.
|
|
bool HasWritesOnSocketForStream(Http2Stream* stream);
|
|
|
|
// Write data from stream_buf_ to the session.
|
|
// This will call the error callback if an error occurs.
|
|
void ConsumeHTTP2Data();
|
|
|
|
void MemoryInfo(MemoryTracker* tracker) const override;
|
|
SET_MEMORY_INFO_NAME(Http2Session)
|
|
SET_SELF_SIZE(Http2Session)
|
|
|
|
std::string diagnostic_name() const override;
|
|
|
|
// Schedule an RstStream for after the current write finishes.
|
|
void AddPendingRstStream(int32_t stream_id) {
|
|
pending_rst_streams_.emplace_back(stream_id);
|
|
}
|
|
|
|
bool has_pending_rststream(int32_t stream_id) {
|
|
return pending_rst_streams_.end() !=
|
|
std::find(pending_rst_streams_.begin(),
|
|
pending_rst_streams_.end(),
|
|
stream_id);
|
|
}
|
|
|
|
// Handle reads/writes from the underlying network transport.
|
|
uv_buf_t OnStreamAlloc(size_t suggested_size) override;
|
|
void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override;
|
|
void OnStreamAfterWrite(WriteWrap* w, int status) override;
|
|
|
|
// Implementation for mem::NgLibMemoryManager
|
|
void CheckAllocatedSize(size_t previous_size) const;
|
|
void IncreaseAllocatedSize(size_t size);
|
|
void DecreaseAllocatedSize(size_t size);
|
|
|
|
// The JavaScript API
|
|
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void Consume(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void Receive(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void Destroy(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void Settings(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void Request(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void SetNextStreamID(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void SetLocalWindowSize(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void Goaway(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void UpdateChunksSent(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void RefreshState(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void Ping(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void AltSvc(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void Origin(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
|
|
template <get_setting fn>
|
|
static void RefreshSettings(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
|
|
uv_loop_t* event_loop() const {
|
|
return env()->event_loop();
|
|
}
|
|
|
|
Http2State* http2_state() const { return http2_state_.get(); }
|
|
|
|
BaseObjectPtr<Http2Ping> PopPing();
|
|
bool AddPing(const uint8_t* data, v8::Local<v8::Function> callback);
|
|
|
|
BaseObjectPtr<Http2Settings> PopSettings();
|
|
bool AddSettings(v8::Local<v8::Function> callback);
|
|
|
|
void IncrementCurrentSessionMemory(uint64_t amount) {
|
|
current_session_memory_ += amount;
|
|
}
|
|
|
|
void DecrementCurrentSessionMemory(uint64_t amount) {
|
|
DCHECK_LE(amount, current_session_memory_);
|
|
current_session_memory_ -= amount;
|
|
}
|
|
|
|
// Tell our custom memory allocator that this rcbuf is independent of
|
|
// this session now, and may outlive it.
|
|
void StopTrackingRcbuf(nghttp2_rcbuf* buf);
|
|
|
|
// Returns the current session memory including memory allocated by nghttp2,
|
|
// the current outbound storage queue, and pending writes.
|
|
uint64_t current_session_memory() const {
|
|
uint64_t total = current_session_memory_ + sizeof(Http2Session);
|
|
total += current_nghttp2_memory_;
|
|
total += outgoing_storage_.size();
|
|
return total;
|
|
}
|
|
|
|
// Return true if current_session_memory + amount is less than the max
|
|
bool has_available_session_memory(uint64_t amount) const {
|
|
return current_session_memory() + amount <= max_session_memory_;
|
|
}
|
|
|
|
struct Statistics {
|
|
uint64_t start_time;
|
|
uint64_t end_time;
|
|
uint64_t ping_rtt;
|
|
uint64_t data_sent;
|
|
uint64_t data_received;
|
|
uint32_t frame_count;
|
|
uint32_t frame_sent;
|
|
int32_t stream_count;
|
|
size_t max_concurrent_streams;
|
|
double stream_average_duration;
|
|
SessionType session_type;
|
|
};
|
|
|
|
Statistics statistics_ = {};
|
|
|
|
private:
|
|
void EmitStatistics();
|
|
|
|
// Frame Padding Strategies
|
|
ssize_t OnDWordAlignedPadding(size_t frameLength,
|
|
size_t maxPayloadLen);
|
|
ssize_t OnMaxFrameSizePadding(size_t frameLength,
|
|
size_t maxPayloadLen);
|
|
|
|
// Frame Handler
|
|
int HandleDataFrame(const nghttp2_frame* frame);
|
|
void HandleGoawayFrame(const nghttp2_frame* frame);
|
|
void HandleHeadersFrame(const nghttp2_frame* frame);
|
|
void HandlePriorityFrame(const nghttp2_frame* frame);
|
|
void HandleSettingsFrame(const nghttp2_frame* frame);
|
|
void HandlePingFrame(const nghttp2_frame* frame);
|
|
void HandleAltSvcFrame(const nghttp2_frame* frame);
|
|
void HandleOriginFrame(const nghttp2_frame* frame);
|
|
|
|
void DecrefHeaders(const nghttp2_frame* frame);
|
|
|
|
// nghttp2 callbacks
|
|
static int OnBeginHeadersCallback(
|
|
nghttp2_session* session,
|
|
const nghttp2_frame* frame,
|
|
void* user_data);
|
|
static int OnHeaderCallback(
|
|
nghttp2_session* session,
|
|
const nghttp2_frame* frame,
|
|
nghttp2_rcbuf* name,
|
|
nghttp2_rcbuf* value,
|
|
uint8_t flags,
|
|
void* user_data);
|
|
static int OnFrameReceive(
|
|
nghttp2_session* session,
|
|
const nghttp2_frame* frame,
|
|
void* user_data);
|
|
static int OnFrameNotSent(
|
|
nghttp2_session* session,
|
|
const nghttp2_frame* frame,
|
|
int error_code,
|
|
void* user_data);
|
|
static int OnFrameSent(
|
|
nghttp2_session* session,
|
|
const nghttp2_frame* frame,
|
|
void* user_data);
|
|
static int OnStreamClose(
|
|
nghttp2_session* session,
|
|
int32_t id,
|
|
uint32_t code,
|
|
void* user_data);
|
|
static int OnInvalidHeader(
|
|
nghttp2_session* session,
|
|
const nghttp2_frame* frame,
|
|
nghttp2_rcbuf* name,
|
|
nghttp2_rcbuf* value,
|
|
uint8_t flags,
|
|
void* user_data);
|
|
static int OnDataChunkReceived(
|
|
nghttp2_session* session,
|
|
uint8_t flags,
|
|
int32_t id,
|
|
const uint8_t* data,
|
|
size_t len,
|
|
void* user_data);
|
|
static ssize_t OnSelectPadding(
|
|
nghttp2_session* session,
|
|
const nghttp2_frame* frame,
|
|
size_t maxPayloadLen,
|
|
void* user_data);
|
|
static int OnNghttpError(
|
|
nghttp2_session* session,
|
|
const char* message,
|
|
size_t len,
|
|
void* user_data);
|
|
static int OnSendData(
|
|
nghttp2_session* session,
|
|
nghttp2_frame* frame,
|
|
const uint8_t* framehd,
|
|
size_t length,
|
|
nghttp2_data_source* source,
|
|
void* user_data);
|
|
static int OnInvalidFrame(
|
|
nghttp2_session* session,
|
|
const nghttp2_frame* frame,
|
|
int lib_error_code,
|
|
void* user_data);
|
|
|
|
struct Callbacks {
|
|
explicit Callbacks(bool kHasGetPaddingCallback);
|
|
|
|
Nghttp2SessionCallbacksPointer callbacks;
|
|
};
|
|
|
|
/* Use callback_struct_saved[kHasGetPaddingCallback ? 1 : 0] */
|
|
static const Callbacks callback_struct_saved[2];
|
|
|
|
// The underlying nghttp2_session handle
|
|
Nghttp2SessionPointer session_;
|
|
|
|
// JS-accessible numeric fields, as indexed by SessionUint8Fields.
|
|
AliasedStruct<SessionJSFields> js_fields_;
|
|
|
|
// The session type: client or server
|
|
SessionType session_type_;
|
|
|
|
// The maximum number of header pairs permitted for streams on this session
|
|
uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS;
|
|
|
|
// The maximum amount of memory allocated for this session
|
|
uint64_t max_session_memory_ = kDefaultMaxSessionMemory;
|
|
uint64_t current_session_memory_ = 0;
|
|
// The amount of memory allocated by nghttp2 internals
|
|
uint64_t current_nghttp2_memory_ = 0;
|
|
|
|
// The collection of active Http2Streams associated with this session
|
|
std::unordered_map<int32_t, BaseObjectPtr<Http2Stream>> streams_;
|
|
|
|
int flags_ = kSessionStateNone;
|
|
|
|
// The StreamBase instance being used for i/o
|
|
PaddingStrategy padding_strategy_ = PADDING_STRATEGY_NONE;
|
|
|
|
// use this to allow timeout tracking during long-lasting writes
|
|
uint32_t chunks_sent_since_last_write_ = 0;
|
|
|
|
uv_buf_t stream_buf_ = uv_buf_init(nullptr, 0);
|
|
// When processing input data, either stream_buf_ab_ or stream_buf_allocation_
|
|
// will be set. stream_buf_ab_ is lazily created from stream_buf_allocation_.
|
|
v8::Global<v8::ArrayBuffer> stream_buf_ab_;
|
|
std::unique_ptr<v8::BackingStore> stream_buf_allocation_;
|
|
size_t stream_buf_offset_ = 0;
|
|
// Custom error code for errors that originated inside one of the callbacks
|
|
// called by nghttp2_session_mem_recv.
|
|
const char* custom_recv_error_code_ = nullptr;
|
|
|
|
size_t max_outstanding_pings_ = kDefaultMaxPings;
|
|
std::queue<BaseObjectPtr<Http2Ping>> outstanding_pings_;
|
|
|
|
size_t max_outstanding_settings_ = kDefaultMaxSettings;
|
|
std::queue<BaseObjectPtr<Http2Settings>> outstanding_settings_;
|
|
|
|
std::vector<NgHttp2StreamWrite> outgoing_buffers_;
|
|
std::vector<uint8_t> outgoing_storage_;
|
|
size_t outgoing_length_ = 0;
|
|
std::vector<int32_t> pending_rst_streams_;
|
|
// Count streams that have been rejected while being opened. Exceeding a fixed
|
|
// limit will result in the session being destroyed, as an indication of a
|
|
// misbehaving peer. This counter is reset once new streams are being
|
|
// accepted again.
|
|
uint32_t rejected_stream_count_ = 0;
|
|
// Also use the invalid frame count as a measure for rejecting input frames.
|
|
uint32_t invalid_frame_count_ = 0;
|
|
|
|
void PushOutgoingBuffer(NgHttp2StreamWrite&& write);
|
|
|
|
BaseObjectPtr<Http2State> http2_state_;
|
|
|
|
void CopyDataIntoOutgoing(const uint8_t* src, size_t src_length);
|
|
void ClearOutgoing(int status);
|
|
|
|
friend class Http2Scope;
|
|
friend class Http2StreamListener;
|
|
};
|
|
|
|
struct Http2SessionPerformanceEntryTraits {
|
|
static constexpr performance::PerformanceEntryType kType =
|
|
performance::NODE_PERFORMANCE_ENTRY_TYPE_HTTP2;
|
|
|
|
using Details = Http2Session::Statistics;
|
|
|
|
static v8::MaybeLocal<v8::Object> GetDetails(
|
|
Environment* env,
|
|
const performance::PerformanceEntry<Http2SessionPerformanceEntryTraits>&
|
|
entry);
|
|
};
|
|
|
|
struct Http2StreamPerformanceEntryTraits {
|
|
static constexpr performance::PerformanceEntryType kType =
|
|
performance::NODE_PERFORMANCE_ENTRY_TYPE_HTTP2;
|
|
|
|
using Details = Http2Stream::Statistics;
|
|
|
|
static v8::MaybeLocal<v8::Object> GetDetails(
|
|
Environment* env,
|
|
const performance::PerformanceEntry<Http2StreamPerformanceEntryTraits>&
|
|
entry);
|
|
};
|
|
|
|
using Http2SessionPerformanceEntry =
|
|
performance::PerformanceEntry<Http2SessionPerformanceEntryTraits>;
|
|
using Http2StreamPerformanceEntry =
|
|
performance::PerformanceEntry<Http2StreamPerformanceEntryTraits>;
|
|
|
|
class Http2Ping : public AsyncWrap {
|
|
public:
|
|
explicit Http2Ping(
|
|
Http2Session* session,
|
|
v8::Local<v8::Object> obj,
|
|
v8::Local<v8::Function> callback);
|
|
|
|
void MemoryInfo(MemoryTracker* tracker) const override;
|
|
SET_MEMORY_INFO_NAME(Http2Ping)
|
|
SET_SELF_SIZE(Http2Ping)
|
|
|
|
void Send(const uint8_t* payload);
|
|
void Done(bool ack, const uint8_t* payload = nullptr);
|
|
void DetachFromSession();
|
|
|
|
v8::Local<v8::Function> callback() const;
|
|
|
|
private:
|
|
BaseObjectWeakPtr<Http2Session> session_;
|
|
v8::Global<v8::Function> callback_;
|
|
uint64_t startTime_;
|
|
};
|
|
|
|
// The Http2Settings class is used to parse the settings passed in for
|
|
// an Http2Session, converting those into an array of nghttp2_settings_entry
|
|
// structs.
|
|
class Http2Settings : public AsyncWrap {
|
|
public:
|
|
Http2Settings(Http2Session* session,
|
|
v8::Local<v8::Object> obj,
|
|
v8::Local<v8::Function> callback,
|
|
uint64_t start_time = uv_hrtime());
|
|
|
|
void MemoryInfo(MemoryTracker* tracker) const override;
|
|
SET_MEMORY_INFO_NAME(Http2Settings)
|
|
SET_SELF_SIZE(Http2Settings)
|
|
|
|
void Send();
|
|
void Done(bool ack);
|
|
|
|
v8::Local<v8::Function> callback() const;
|
|
|
|
// Returns a Buffer instance with the serialized SETTINGS payload
|
|
v8::Local<v8::Value> Pack();
|
|
|
|
static v8::Local<v8::Value> Pack(Http2State* state);
|
|
|
|
// Resets the default values in the settings buffer
|
|
static void RefreshDefaults(Http2State* http2_state);
|
|
|
|
// Update the local or remote settings for the given session
|
|
static void Update(Http2Session* session,
|
|
get_setting fn);
|
|
|
|
private:
|
|
static size_t Init(
|
|
Http2State* http2_state,
|
|
nghttp2_settings_entry* entries);
|
|
|
|
static v8::Local<v8::Value> Pack(
|
|
Environment* env,
|
|
size_t count,
|
|
const nghttp2_settings_entry* entries);
|
|
|
|
BaseObjectWeakPtr<Http2Session> session_;
|
|
v8::Global<v8::Function> callback_;
|
|
uint64_t startTime_;
|
|
size_t count_ = 0;
|
|
nghttp2_settings_entry entries_[IDX_SETTINGS_COUNT];
|
|
};
|
|
|
|
class Origins {
|
|
public:
|
|
Origins(Environment* env,
|
|
v8::Local<v8::String> origin_string,
|
|
size_t origin_count);
|
|
~Origins() = default;
|
|
|
|
const nghttp2_origin_entry* operator*() const {
|
|
return static_cast<const nghttp2_origin_entry*>(bs_->Data());
|
|
}
|
|
|
|
size_t length() const {
|
|
return count_;
|
|
}
|
|
|
|
private:
|
|
size_t count_;
|
|
std::unique_ptr<v8::BackingStore> bs_;
|
|
};
|
|
|
|
#define HTTP2_HIDDEN_CONSTANTS(V) \
|
|
V(NGHTTP2_HCAT_REQUEST) \
|
|
V(NGHTTP2_HCAT_RESPONSE) \
|
|
V(NGHTTP2_HCAT_PUSH_RESPONSE) \
|
|
V(NGHTTP2_HCAT_HEADERS) \
|
|
V(NGHTTP2_NV_FLAG_NONE) \
|
|
V(NGHTTP2_NV_FLAG_NO_INDEX) \
|
|
V(NGHTTP2_ERR_DEFERRED) \
|
|
V(NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE) \
|
|
V(NGHTTP2_ERR_INVALID_ARGUMENT) \
|
|
V(NGHTTP2_ERR_STREAM_CLOSED) \
|
|
V(NGHTTP2_ERR_NOMEM) \
|
|
V(STREAM_OPTION_EMPTY_PAYLOAD) \
|
|
V(STREAM_OPTION_GET_TRAILERS)
|
|
|
|
#define HTTP2_ERROR_CODES(V) \
|
|
V(NGHTTP2_NO_ERROR) \
|
|
V(NGHTTP2_PROTOCOL_ERROR) \
|
|
V(NGHTTP2_INTERNAL_ERROR) \
|
|
V(NGHTTP2_FLOW_CONTROL_ERROR) \
|
|
V(NGHTTP2_SETTINGS_TIMEOUT) \
|
|
V(NGHTTP2_STREAM_CLOSED) \
|
|
V(NGHTTP2_FRAME_SIZE_ERROR) \
|
|
V(NGHTTP2_REFUSED_STREAM) \
|
|
V(NGHTTP2_CANCEL) \
|
|
V(NGHTTP2_COMPRESSION_ERROR) \
|
|
V(NGHTTP2_CONNECT_ERROR) \
|
|
V(NGHTTP2_ENHANCE_YOUR_CALM) \
|
|
V(NGHTTP2_INADEQUATE_SECURITY) \
|
|
V(NGHTTP2_HTTP_1_1_REQUIRED) \
|
|
|
|
#define HTTP2_CONSTANTS(V) \
|
|
V(NGHTTP2_ERR_FRAME_SIZE_ERROR) \
|
|
V(NGHTTP2_SESSION_SERVER) \
|
|
V(NGHTTP2_SESSION_CLIENT) \
|
|
V(NGHTTP2_STREAM_STATE_IDLE) \
|
|
V(NGHTTP2_STREAM_STATE_OPEN) \
|
|
V(NGHTTP2_STREAM_STATE_RESERVED_LOCAL) \
|
|
V(NGHTTP2_STREAM_STATE_RESERVED_REMOTE) \
|
|
V(NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL) \
|
|
V(NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE) \
|
|
V(NGHTTP2_STREAM_STATE_CLOSED) \
|
|
V(NGHTTP2_FLAG_NONE) \
|
|
V(NGHTTP2_FLAG_END_STREAM) \
|
|
V(NGHTTP2_FLAG_END_HEADERS) \
|
|
V(NGHTTP2_FLAG_ACK) \
|
|
V(NGHTTP2_FLAG_PADDED) \
|
|
V(NGHTTP2_FLAG_PRIORITY) \
|
|
V(DEFAULT_SETTINGS_HEADER_TABLE_SIZE) \
|
|
V(DEFAULT_SETTINGS_ENABLE_PUSH) \
|
|
V(DEFAULT_SETTINGS_MAX_CONCURRENT_STREAMS) \
|
|
V(DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE) \
|
|
V(DEFAULT_SETTINGS_MAX_FRAME_SIZE) \
|
|
V(DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE) \
|
|
V(DEFAULT_SETTINGS_ENABLE_CONNECT_PROTOCOL) \
|
|
V(MAX_MAX_FRAME_SIZE) \
|
|
V(MIN_MAX_FRAME_SIZE) \
|
|
V(MAX_INITIAL_WINDOW_SIZE) \
|
|
V(NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) \
|
|
V(NGHTTP2_SETTINGS_ENABLE_PUSH) \
|
|
V(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) \
|
|
V(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE) \
|
|
V(NGHTTP2_SETTINGS_MAX_FRAME_SIZE) \
|
|
V(NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE) \
|
|
V(NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL) \
|
|
V(PADDING_STRATEGY_NONE) \
|
|
V(PADDING_STRATEGY_ALIGNED) \
|
|
V(PADDING_STRATEGY_MAX) \
|
|
V(PADDING_STRATEGY_CALLBACK) \
|
|
HTTP2_ERROR_CODES(V)
|
|
|
|
#define HTTP2_SETTINGS(V) \
|
|
V(HEADER_TABLE_SIZE) \
|
|
V(ENABLE_PUSH) \
|
|
V(MAX_CONCURRENT_STREAMS) \
|
|
V(INITIAL_WINDOW_SIZE) \
|
|
V(MAX_FRAME_SIZE) \
|
|
V(MAX_HEADER_LIST_SIZE) \
|
|
V(ENABLE_CONNECT_PROTOCOL) \
|
|
|
|
} // namespace http2
|
|
} // namespace node
|
|
|
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#endif // SRC_NODE_HTTP2_H_
|