text: #ifndef SRC_NODE_HTTP2_H_
  #define SRC_NODE_HTTP2_H_
  
  #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
  
  #include "nghttp2/nghttp2.h"
  #include "node_http2_state.h"
  #include "node_perf.h"
  #include "stream_base-inl.h"
  #include "string_bytes.h"
  
  #include <queue>
  
  namespace node {
  namespace http2 {
  
  using v8::Array;
  using v8::Context;
  using v8::EscapableHandleScope;
  using v8::Isolate;
  using v8::MaybeLocal;
  
  using performance::PerformanceEntry;
  
  #ifdef NODE_DEBUG_HTTP2
  
  // Adapted from nghttp2 own debug printer
  static inline void _debug_vfprintf(const char* fmt, va_list args) {
    vfprintf(stderr, fmt, args);
  }
  
  void inline debug_vfprintf(const char* format, ...) {
    va_list args;
    va_start(args, format);
    _debug_vfprintf(format, args);
    va_end(args);
  }
  
  #define DEBUG_HTTP2(...) debug_vfprintf(__VA_ARGS__);
  #define DEBUG_HTTP2SESSION(session, message)                                  \
    do {                                                                        \
      if (session != nullptr) {                                                 \
        DEBUG_HTTP2("Http2Session %s (%.0lf) " message "\n",                    \
                    session->TypeName(),                                        \
                    session->get_async_id());                                   \
       }                                                                        \
    } while (0)
  #define DEBUG_HTTP2SESSION2(session, message, ...)                            \
    do {                                                                        \
      if (session != nullptr) {                                                 \
        DEBUG_HTTP2("Http2Session %s (%.0lf) " message "\n",                    \
                    session->TypeName(),                                        \
                    session->get_async_id(),                                    \
                    __VA_ARGS__);                                               \
      }                                                                         \
    } while (0)
  #define DEBUG_HTTP2STREAM(stream, message)                                    \
    do {                                                                        \
      DEBUG_HTTP2("Http2Stream %d (%.0lf) [Http2Session %s (%.0lf)] " message   \
                  "\n", stream->id(), stream->get_async_id(),                   \
                  stream->session()->TypeName(),                                \
                  stream->session()->get_async_id());                           \
    } while (0)
  #define DEBUG_HTTP2STREAM2(stream, message, ...)                              \
    do {                                                                        \
      DEBUG_HTTP2("Http2Stream %d (%.0lf) [Http2Session %s (%.0lf)] " message   \
                  "\n", stream->id(), stream->get_async_id(),                   \
                  stream->session()->TypeName(),                                \
                  stream->session()->get_async_id(),                            \
                  __VA_ARGS__);                                                 \
    } while (0)
  #else
  #define DEBUG_HTTP2(...) do {} while (0)
  #define DEBUG_HTTP2SESSION(...) do {} while (0)
  #define DEBUG_HTTP2SESSION2(...) do {} while (0)
  #define DEBUG_HTTP2STREAM(...) do {} while (0)
  #define DEBUG_HTTP2STREAM2(...) do {} while (0)
  #endif
  
  // 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.
  #define DEFAULT_MAX_PINGS 10
  
  // Also strictly limit the number of outstanding SETTINGS frames a user sends
  #define DEFAULT_MAX_SETTINGS 10
  
  // Default maximum total memory cap for Http2Session.
  #define DEFAULT_MAX_SESSION_MEMORY 1e7;
  
  // These are the standard HTTP/2 defaults as specified by the RFC
  #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 DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE 65535
  #define MAX_MAX_FRAME_SIZE 16777215
  #define MIN_MAX_FRAME_SIZE DEFAULT_SETTINGS_MAX_FRAME_SIZE
  #define MAX_INITIAL_WINDOW_SIZE 2147483647
  
  #define MAX_MAX_HEADER_LIST_SIZE 16777215u
  #define DEFAULT_MAX_HEADER_LIST_PAIRS 128u
  
  #define MAX_BUFFER_COUNT 16
  
  enum nghttp2_session_type {
    NGHTTP2_SESSION_SERVER,
    NGHTTP2_SESSION_CLIENT
  };
  
  enum nghttp2_shutdown_flags {
    NGHTTP2_SHUTDOWN_FLAG_GRACEFUL
  };
  
  enum nghttp2_stream_flags {
    NGHTTP2_STREAM_FLAG_NONE = 0x0,
    // Writable side has ended
    NGHTTP2_STREAM_FLAG_SHUT = 0x1,
    // Reading has started
    NGHTTP2_STREAM_FLAG_READ_START = 0x2,
    // Reading is paused
    NGHTTP2_STREAM_FLAG_READ_PAUSED = 0x4,
    // Stream is closed
    NGHTTP2_STREAM_FLAG_CLOSED = 0x8,
    // Stream is destroyed
    NGHTTP2_STREAM_FLAG_DESTROYED = 0x10,
    // Stream has trailers
    NGHTTP2_STREAM_FLAG_TRAILERS = 0x20,
    // Stream has received all the data it can
    NGHTTP2_STREAM_FLAG_EOS = 0x40
  };
  
  enum nghttp2_stream_options {
    // Stream is not going to have any DATA frames
    STREAM_OPTION_EMPTY_PAYLOAD = 0x1,
    // Stream might have trailing headers
    STREAM_OPTION_GET_TRAILERS = 0x2,
  };
  
  struct nghttp2_stream_write {
    WriteWrap* req_wrap = nullptr;
    uv_buf_t buf;
  
    inline explicit nghttp2_stream_write(uv_buf_t buf_) : buf(buf_) {}
    inline nghttp2_stream_write(WriteWrap* req, uv_buf_t buf_) :
        req_wrap(req), buf(buf_) {}
  };
  
  struct nghttp2_header {
    nghttp2_rcbuf* name = nullptr;
    nghttp2_rcbuf* value = nullptr;
    uint8_t flags = 0;
  };
  
  
  // Unlike the HTTP/1 implementation, the HTTP/2 implementation is not limited
  // to a fixed number of known supported HTTP methods. These constants, therefore
  // are provided strictly as a convenience to users and are exposed via the
  // require('http2').constants object.
  #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")
  
  // These are provided strictly as a convenience to users and are exposed via the
  // require('http2').constants objects
  #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
  };
  
  // While some of these codes are used within the HTTP/2 implementation in
  // core, they are provided strictly as a convenience to users and are exposed
  // via the require('http2').constants object.
  #define HTTP_STATUS_CODES(V)                                                  \
    V(CONTINUE, 100)                                                            \
    V(SWITCHING_PROTOCOLS, 101)                                                 \
    V(PROCESSING, 102)                                                          \
    V(EARLY_HINTS, 103)                                                         \
    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
  };
  
  // 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 padding_strategy_type {
    // 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,
    // Padding will be determined via a JS callback. Note that this can be
    // expensive because the callback is called once for every DATA and
    // HEADERS frame. For performance reasons, this strategy should be
    // avoided.
    PADDING_STRATEGY_CALLBACK
  };
  
  // These are the error codes provided by the underlying nghttp2 implementation.
  #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";
    }
  }
  
  enum session_state_flags {
    SESSION_STATE_NONE = 0x0,
    SESSION_STATE_HAS_SCOPE = 0x1,
    SESSION_STATE_WRITE_SCHEDULED = 0x2,
    SESSION_STATE_CLOSED = 0x4,
    SESSION_STATE_SENDING = 0x8,
  };
  
  // This allows for 4 default-sized frames with their frame headers
  static const size_t kAllocBufferSize = 4 * (16384 + 9);
  
  typedef uint32_t(*get_setting)(nghttp2_session* session,
                                 nghttp2_settings_id id);
  
  class Http2Session;
  class Http2Stream;
  
  // 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:
    Http2Session* session_ = nullptr;
    Local<Object> session_handle_;
  };
  
  // 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:
    explicit Http2Options(Environment* env);
  
    ~Http2Options() {
      nghttp2_option_del(options_);
    }
  
    nghttp2_option* operator*() const {
      return options_;
    }
  
    void SetMaxHeaderPairs(uint32_t max) {
      max_header_pairs_ = max;
    }
  
    uint32_t GetMaxHeaderPairs() const {
      return max_header_pairs_;
    }
  
    void SetPaddingStrategy(padding_strategy_type val) {
      padding_strategy_ = static_cast<padding_strategy_type>(val);
    }
  
    padding_strategy_type GetPaddingStrategy() const {
      return padding_strategy_;
    }
  
    void SetMaxOutstandingPings(size_t max) {
      max_outstanding_pings_ = max;
    }
  
    size_t GetMaxOutstandingPings() {
      return max_outstanding_pings_;
    }
  
    void SetMaxOutstandingSettings(size_t max) {
      max_outstanding_settings_ = max;
    }
  
    size_t GetMaxOutstandingSettings() {
      return max_outstanding_settings_;
    }
  
    void SetMaxSessionMemory(uint64_t max) {
      max_session_memory_ = max;
    }
  
    uint64_t GetMaxSessionMemory() {
      return max_session_memory_;
    }
  
   private:
    nghttp2_option* options_;
    uint64_t max_session_memory_ = DEFAULT_MAX_SESSION_MEMORY;
    uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS;
    padding_strategy_type padding_strategy_ = PADDING_STRATEGY_NONE;
    size_t max_outstanding_pings_ = DEFAULT_MAX_PINGS;
    size_t max_outstanding_settings_ = DEFAULT_MAX_SETTINGS;
  };
  
  class Http2Priority {
   public:
    Http2Priority(Environment* env,
                  Local<Value> parent,
                  Local<Value> weight,
                  Local<Value> exclusive);
  
    nghttp2_priority_spec* operator*() {
      return &spec;
    }
   private:
    nghttp2_priority_spec spec;
  };
  
  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;
  };
  
  class Http2Stream : public AsyncWrap,
                      public StreamBase {
   public:
    Http2Stream(Http2Session* session,
                int32_t id,
                nghttp2_headers_category category = NGHTTP2_HCAT_HEADERS,
                int options = 0);
    ~Http2Stream() override;
  
    nghttp2_stream* operator*();
  
    Http2Session* session() { return session_; }
  
    void EmitStatistics();
  
    // Process a Data Chunk
    void OnDataChunk(uv_buf_t* chunk);
  
  
    // Required for StreamBase
    int ReadStart() override;
  
    // Required for StreamBase
    int ReadStop() override;
  
    // Required for StreamBase
    int DoShutdown(ShutdownWrap* req_wrap) override;
  
    // Initiate a response on this stream.
    int SubmitResponse(nghttp2_nv* nva, size_t len, int options);
  
    // Send data read from a file descriptor as the response on this stream.
    inline int SubmitFile(int fd,
                          nghttp2_nv* nva, size_t len,
                          int64_t offset,
                          int64_t length,
                          int options);
  
    // Submit informational headers for this stream
    int SubmitInfo(nghttp2_nv* nva, size_t len);
  
    // Submit a PRIORITY frame for this stream
    int SubmitPriority(nghttp2_priority_spec* prispec, bool silent = false);
  
    // Submits an RST_STREAM frame using the given code
    void SubmitRstStream(const uint32_t code);
  
    // Submits a PUSH_PROMISE frame with this stream as the parent.
    Http2Stream* SubmitPushPromise(
        nghttp2_nv* nva,
        size_t len,
        int32_t* ret,
        int options = 0);
  
  
    void Close(int32_t code);
  
    // Destroy this stream instance and free all held memory.
    void Destroy();
  
    inline bool IsDestroyed() const {
      return flags_ & NGHTTP2_STREAM_FLAG_DESTROYED;
    }
  
    inline bool IsWritable() const {
      return !(flags_ & NGHTTP2_STREAM_FLAG_SHUT);
    }
  
    inline bool IsPaused() const {
      return flags_ & NGHTTP2_STREAM_FLAG_READ_PAUSED;
    }
  
    inline bool IsClosed() const {
      return flags_ & NGHTTP2_STREAM_FLAG_CLOSED;
      }
  
    inline bool HasTrailers() const {
      return flags_ & NGHTTP2_STREAM_FLAG_TRAILERS;
    }
  
    // Returns true if this stream is in the reading state, which occurs when
    // the NGHTTP2_STREAM_FLAG_READ_START flag has been set and the
    // NGHTTP2_STREAM_FLAG_READ_PAUSED flag is *not* set.
    inline bool IsReading() const {
      return flags_ & NGHTTP2_STREAM_FLAG_READ_START &&
             !(flags_ & NGHTTP2_STREAM_FLAG_READ_PAUSED);
    }
  
    // Returns the RST_STREAM code used to close this stream
    inline int32_t code() const { return code_; }
  
    // Returns the stream identifier for this stream
    inline int32_t id() const { return id_; }
  
    inline void IncrementAvailableOutboundLength(size_t amount);
    inline void DecrementAvailableOutboundLength(size_t amount);
  
    bool AddHeader(nghttp2_rcbuf* name, nghttp2_rcbuf* value, uint8_t flags);
  
    inline nghttp2_header* headers() {
      return current_headers_.data();
    }
  
    inline nghttp2_headers_category headers_category() const {
      return current_headers_category_;
    }
  
    inline size_t headers_count() const {
      return current_headers_.size();
    }
  
    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;
  
    size_t self_size() const override { return sizeof(*this); }
  
    // Handling Trailer Headers
    class SubmitTrailers {
     public:
      void Submit(nghttp2_nv* trailers, size_t length) const;
  
      SubmitTrailers(Http2Session* sesion,
                     Http2Stream* stream,
                     uint32_t* flags);
  
     private:
      Http2Session* const session_;
      Http2Stream* const stream_;
      uint32_t* const flags_;
  
      friend class Http2Stream;
    };
  
    void OnTrailers(const SubmitTrailers& submit_trailers);
  
    // JavaScript API
    static void GetID(const FunctionCallbackInfo<Value>& args);
    static void Destroy(const FunctionCallbackInfo<Value>& args);
    static void FlushData(const FunctionCallbackInfo<Value>& args);
    static void Priority(const FunctionCallbackInfo<Value>& args);
    static void PushPromise(const FunctionCallbackInfo<Value>& args);
    static void RefreshState(const FunctionCallbackInfo<Value>& args);
    static void Info(const FunctionCallbackInfo<Value>& args);
    static void RespondFD(const FunctionCallbackInfo<Value>& args);
    static void Respond(const FunctionCallbackInfo<Value>& args);
    static void RstStream(const FunctionCallbackInfo<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;
    };
  
    Statistics statistics_ = {};
  
   private:
    Http2Session* session_;                       // The Parent HTTP/2 Session
    int32_t id_;                                  // The Stream Identifier
    int32_t code_ = NGHTTP2_NO_ERROR;             // The RST_STREAM code (if any)
    int flags_ = NGHTTP2_STREAM_FLAG_NONE;        // 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<nghttp2_header> 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<nghttp2_stream_write> queue_;
    size_t available_outbound_length_ = 0;
    int64_t fd_offset_ = 0;
    int64_t fd_length_ = -1;
  
    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::FD : public Http2Stream::Provider {
   public:
    FD(int options, int fd);
    FD(Http2Stream* stream, int options, int fd);
  
    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);
  };
  
  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);
  };
  
  
  class Http2Session : public AsyncWrap, public StreamListener {
   public:
    Http2Session(Environment* env,
                 Local<Object> wrap,
                 nghttp2_session_type type = NGHTTP2_SESSION_SERVER);
    ~Http2Session() override;
  
    class Http2Ping;
    class Http2Settings;
  
    void EmitStatistics();
  
    inline StreamBase* underlying_stream() {
      return static_cast<StreamBase*>(stream_);
    }
  
    void Start();
    void Stop();
    void Close(uint32_t code = NGHTTP2_NO_ERROR,
               bool socket_closed = false);
    void Consume(Local<External> external);
    void Unconsume();
    void Goaway(uint32_t code, int32_t lastStreamID, 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);
  
    bool Ping(v8::Local<v8::Function> function);
  
    void 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(
        nghttp2_priority_spec* prispec,
        nghttp2_nv* nva,
        size_t len,
        int32_t* ret,
        int options = 0);
  
    inline nghttp2_session_type type() const { return session_type_; }
  
    inline nghttp2_session* session() const { return session_; }
  
    inline nghttp2_session* operator*() { return session_; }
  
    inline uint32_t GetMaxHeaderPairs() const { return max_header_pairs_; }
  
    inline const char* TypeName();
  
    inline bool IsDestroyed() {
      return (flags_ & SESSION_STATE_CLOSED) || session_ == nullptr;
    }
  
    // Schedule a write if nghttp2 indicates it wants to write to the socket.
    void MaybeScheduleWrite();
  
    // Returns pointer to the stream, or nullptr if stream does not exist
    inline Http2Stream* FindStream(int32_t id);
  
    inline bool CanAddStream();
  
    // Adds a stream instance to this session
    inline void AddStream(Http2Stream* stream);
  
    // Removes a stream instance from this session
    inline void RemoveStream(Http2Stream* stream);
  
    // Indicates whether there currently exist outgoing buffers for this stream.
    bool HasWritesOnSocketForStream(Http2Stream* stream);
  
    // Write data to the session
    ssize_t Write(const uv_buf_t* bufs, size_t nbufs);
  
    size_t self_size() const override { return sizeof(*this); }
  
    void GetTrailers(Http2Stream* stream, uint32_t* flags);
  
    // Handle reads/writes from the underlying network transport.
    void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override;
    void OnStreamAfterWrite(WriteWrap* w, int status) override;
  
    // The JavaScript API
    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 Settings(const FunctionCallbackInfo<Value>& args);
    static void Request(const FunctionCallbackInfo<Value>& args);
    static void SetNextStreamID(const FunctionCallbackInfo<Value>& args);
    static void Goaway(const FunctionCallbackInfo<Value>& args);
    static void UpdateChunksSent(const FunctionCallbackInfo<Value>& args);
    static void RefreshState(const FunctionCallbackInfo<Value>& args);
    static void Ping(const FunctionCallbackInfo<Value>& args);
    static void AltSvc(const FunctionCallbackInfo<Value>& args);
  
    template <get_setting fn>
    static void RefreshSettings(const FunctionCallbackInfo<Value>& args);
  
    template <get_setting fn>
    static void GetSettings(const FunctionCallbackInfo<Value>& args);
  
    uv_loop_t* event_loop() const {
      return env()->event_loop();
    }
  
    Http2Ping* PopPing();
    bool AddPing(Http2Ping* ping);
  
    Http2Settings* PopSettings();
    bool AddSettings(Http2Settings* settings);
  
    void IncrementCurrentSessionMemory(uint64_t amount) {
      current_session_memory_ += amount;
    }
  
    void DecrementCurrentSessionMemory(uint64_t amount) {
      current_session_memory_ -= amount;
    }
  
    // Returns the current session memory including the current size of both
    // the inflate and deflate hpack headers, the current outbound storage
    // queue, and pending writes.
    uint64_t GetCurrentSessionMemory() {
      uint64_t total = current_session_memory_ + sizeof(Http2Session);
      total += nghttp2_session_get_hd_deflate_dynamic_table_size(session_);
      total += nghttp2_session_get_hd_inflate_dynamic_table_size(session_);
      total += outgoing_storage_.size();
      return total;
    }
  
    // Return true if current_session_memory + amount is less than the max
    bool IsAvailableSessionMemory(uint64_t amount) {
      return GetCurrentSessionMemory() + 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;
    };
  
    Statistics statistics_ = {};
  
   private:
    // Frame Padding Strategies
    ssize_t OnDWordAlignedPadding(size_t frameLength,
                                  size_t maxPayloadLen);
    ssize_t OnMaxFrameSizePadding(size_t frameLength,
                                  size_t maxPayloadLen);
    ssize_t OnCallbackPadding(size_t frameLength,
                              size_t maxPayloadLen);
  
    // Frame Handler
    void 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);
  
    // 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 {
      inline explicit Callbacks(bool kHasGetPaddingCallback);
      inline ~Callbacks();
  
      nghttp2_session_callbacks* callbacks;
    };
  
    /* Use callback_struct_saved[kHasGetPaddingCallback ? 1 : 0] */
    static const Callbacks callback_struct_saved[2];
  
    // The underlying nghttp2_session handle
    nghttp2_session* session_;
  
    // The session type: client or server
    nghttp2_session_type 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_ = DEFAULT_MAX_SESSION_MEMORY;
    uint64_t current_session_memory_ = 0;
  
    // The collection of active Http2Streams associated with this session
    std::unordered_map<int32_t, Http2Stream*> streams_;
  
    int flags_ = SESSION_STATE_NONE;
  
    // The StreamBase instance being used for i/o
    padding_strategy_type 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);
    v8::Local<v8::ArrayBuffer> stream_buf_ab_;
  
    size_t max_outstanding_pings_ = DEFAULT_MAX_PINGS;
    std::queue<Http2Ping*> outstanding_pings_;
  
    size_t max_outstanding_settings_ = DEFAULT_MAX_SETTINGS;
    std::queue<Http2Settings*> outstanding_settings_;
  
    std::vector<nghttp2_stream_write> outgoing_buffers_;
    std::vector<uint8_t> outgoing_storage_;
  
    void CopyDataIntoOutgoing(const uint8_t* src, size_t src_length);
    void ClearOutgoing(int status);
  
    friend class Http2Scope;
    friend class Http2StreamListener;
  };
  
  class Http2SessionPerformanceEntry : public PerformanceEntry {
   public:
    Http2SessionPerformanceEntry(
        Environment* env,
        const Http2Session::Statistics& stats,
        nghttp2_session_type type) :
            PerformanceEntry(env, "Http2Session", "http2",
                             stats.start_time,
                             stats.end_time),
            ping_rtt_(stats.ping_rtt),
            data_sent_(stats.data_sent),
            data_received_(stats.data_received),
            frame_count_(stats.frame_count),
            frame_sent_(stats.frame_sent),
            stream_count_(stats.stream_count),
            max_concurrent_streams_(stats.max_concurrent_streams),
            stream_average_duration_(stats.stream_average_duration),
            session_type_(type) { }
  
    uint64_t ping_rtt() const { return ping_rtt_; }
    uint64_t data_sent() const { return data_sent_; }
    uint64_t data_received() const { return data_received_; }
    uint32_t frame_count() const { return frame_count_; }
    uint32_t frame_sent() const { return frame_sent_; }
    int32_t stream_count() const { return stream_count_; }
    size_t max_concurrent_streams() const { return max_concurrent_streams_; }
    double stream_average_duration() const { return stream_average_duration_; }
    nghttp2_session_type type() const { return session_type_; }
  
    void Notify(Local<Value> obj) {
      PerformanceEntry::Notify(env(), kind(), obj);
    }
  
   private:
    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_;
    nghttp2_session_type session_type_;
  };
  
  class Http2StreamPerformanceEntry : public PerformanceEntry {
   public:
    Http2StreamPerformanceEntry(
        Environment* env,
        int32_t id,
        const Http2Stream::Statistics& stats) :
            PerformanceEntry(env, "Http2Stream", "http2",
                             stats.start_time,
                             stats.end_time),
            id_(id),
            first_header_(stats.first_header),
            first_byte_(stats.first_byte),
            first_byte_sent_(stats.first_byte_sent),
            sent_bytes_(stats.sent_bytes),
            received_bytes_(stats.received_bytes) { }
  
    int32_t id() const { return id_; }
    uint64_t first_header() const { return first_header_; }
    uint64_t first_byte() const { return first_byte_; }
    uint64_t first_byte_sent() const { return first_byte_sent_; }
    uint64_t sent_bytes() const { return sent_bytes_; }
    uint64_t received_bytes() const { return received_bytes_; }
  
    void Notify(Local<Value> obj) {
      PerformanceEntry::Notify(env(), kind(), obj);
    }
  
   private:
    int32_t id_;
    uint64_t first_header_;
    uint64_t first_byte_;
    uint64_t first_byte_sent_;
    uint64_t sent_bytes_;
    uint64_t received_bytes_;
  };
  
  class Http2Session::Http2Ping : public AsyncWrap {
   public:
    explicit Http2Ping(Http2Session* session);
    ~Http2Ping();
  
    size_t self_size() const override { return sizeof(*this); }
  
    void Send(uint8_t* payload);
    void Done(bool ack, const uint8_t* payload = nullptr);
  
   private:
    Http2Session* session_;
    uint64_t startTime_;
  
    friend class Http2Session;
  };
  
  // 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 Http2Session::Http2Settings : public AsyncWrap {
   public:
    explicit Http2Settings(Environment* env);
    explicit Http2Settings(Http2Session* session);
    ~Http2Settings();
  
    size_t self_size() const override { return sizeof(*this); }
  
    void Send();
    void Done(bool ack);
  
    // Returns a Buffer instance with the serialized SETTINGS payload
    Local<Value> Pack();
  
    // Resets the default values in the settings buffer
    static void RefreshDefaults(Environment* env);
  
    // Update the local or remote settings for the given session
    static void Update(Environment* env,
                       Http2Session* session,
                       get_setting fn);
  
   private:
    void Init();
    Http2Session* session_;
    uint64_t startTime_;
    size_t count_ = 0;
    nghttp2_settings_entry entries_[IDX_SETTINGS_COUNT];
  };
  
  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_
  