diff options
Diffstat (limited to 'src/node_http2_core-inl.h')
-rw-r--r-- | src/node_http2_core-inl.h | 925 |
1 files changed, 0 insertions, 925 deletions
diff --git a/src/node_http2_core-inl.h b/src/node_http2_core-inl.h deleted file mode 100644 index de38a5df47..0000000000 --- a/src/node_http2_core-inl.h +++ /dev/null @@ -1,925 +0,0 @@ -#ifndef SRC_NODE_HTTP2_CORE_INL_H_ -#define SRC_NODE_HTTP2_CORE_INL_H_ - -#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#include "node_http2_core.h" -#include "node_internals.h" // arraysize -#include <algorithm> - -namespace node { -namespace http2 { - -#ifdef NODE_DEBUG_HTTP2 -inline int Nghttp2Session::OnNghttpError(nghttp2_session* session, - const char* message, - size_t len, - void* user_data) { - Nghttp2Session* handle = static_cast<Nghttp2Session*>(user_data); - DEBUG_HTTP2("Nghttp2Session %s: Error '%.*s'\n", - handle->TypeName(), len, message); - return 0; -} -#endif - -inline int32_t GetFrameID(const nghttp2_frame* frame) { - // If this is a push promise, we want to grab the id of the promised stream - return (frame->hd.type == NGHTTP2_PUSH_PROMISE) ? - frame->push_promise.promised_stream_id : - frame->hd.stream_id; -} - -// nghttp2 calls this at the beginning a new HEADERS or PUSH_PROMISE frame. -// We use it to ensure that an Nghttp2Stream instance is allocated to store -// the state. -inline int Nghttp2Session::OnBeginHeadersCallback(nghttp2_session* session, - const nghttp2_frame* frame, - void* user_data) { - Nghttp2Session* handle = static_cast<Nghttp2Session*>(user_data); - int32_t id = GetFrameID(frame); - DEBUG_HTTP2("Nghttp2Session %s: beginning headers for stream %d\n", - handle->TypeName(), id); - - Nghttp2Stream* stream = handle->FindStream(id); - if (stream == nullptr) { - new Nghttp2Stream(id, handle, frame->headers.cat); - } else { - stream->StartHeaders(frame->headers.cat); - } - return 0; -} - -inline size_t GetBufferLength(nghttp2_rcbuf* buf) { - return nghttp2_rcbuf_get_buf(buf).len; -} - -inline bool Nghttp2Stream::AddHeader(nghttp2_rcbuf* name, - nghttp2_rcbuf* value, - uint8_t flags) { - size_t length = GetBufferLength(name) + GetBufferLength(value) + 32; - if (current_headers_.size() == max_header_pairs_ || - current_headers_length_ + length > max_header_length_) { - return false; - } - nghttp2_header header; - header.name = name; - header.value = value; - header.flags = flags; - current_headers_.push_back(header); - nghttp2_rcbuf_incref(name); - nghttp2_rcbuf_incref(value); - current_headers_length_ += length; - return true; -} - -// nghttp2 calls this once for every header name-value pair in a HEADERS -// or PUSH_PROMISE block. CONTINUATION frames are handled automatically -// and transparently so we do not need to worry about those at all. -inline int Nghttp2Session::OnHeaderCallback(nghttp2_session* session, - const nghttp2_frame* frame, - nghttp2_rcbuf* name, - nghttp2_rcbuf* value, - uint8_t flags, - void* user_data) { - Nghttp2Session* handle = static_cast<Nghttp2Session*>(user_data); - int32_t id = GetFrameID(frame); - Nghttp2Stream* stream = handle->FindStream(id); - if (!stream->AddHeader(name, value, flags)) { - // This will only happen if the connected peer sends us more - // than the allowed number of header items at any given time - stream->SubmitRstStream(NGHTTP2_ENHANCE_YOUR_CALM); - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - return 0; -} - - -// When nghttp2 has completely processed a frame, it calls OnFrameReceive. -// It is our responsibility to delegate out from there. We can ignore most -// control frames since nghttp2 will handle those for us. -inline int Nghttp2Session::OnFrameReceive(nghttp2_session* session, - const nghttp2_frame* frame, - void* user_data) { - Nghttp2Session* handle = static_cast<Nghttp2Session*>(user_data); - DEBUG_HTTP2("Nghttp2Session %s: complete frame received: type: %d\n", - handle->TypeName(), frame->hd.type); - bool ack; - switch (frame->hd.type) { - case NGHTTP2_DATA: - handle->HandleDataFrame(frame); - break; - case NGHTTP2_PUSH_PROMISE: - // Intentional fall-through, handled just like headers frames - case NGHTTP2_HEADERS: - handle->HandleHeadersFrame(frame); - break; - case NGHTTP2_SETTINGS: - ack = (frame->hd.flags & NGHTTP2_FLAG_ACK) == NGHTTP2_FLAG_ACK; - handle->OnSettings(ack); - break; - case NGHTTP2_PRIORITY: - handle->HandlePriorityFrame(frame); - break; - case NGHTTP2_GOAWAY: - handle->HandleGoawayFrame(frame); - break; - default: - break; - } - return 0; -} - -// nghttp2 will call this if an error occurs attempting to send a frame. -// Unless the stream or session is closed, this really should not happen -// unless there is a serious flaw in our implementation. -inline int Nghttp2Session::OnFrameNotSent(nghttp2_session* session, - const nghttp2_frame* frame, - int error_code, - void* user_data) { - Nghttp2Session* handle = static_cast<Nghttp2Session*>(user_data); - DEBUG_HTTP2("Nghttp2Session %s: frame type %d was not sent, code: %d\n", - handle->TypeName(), frame->hd.type, error_code); - // Do not report if the frame was not sent due to the session closing - if (error_code != NGHTTP2_ERR_SESSION_CLOSING && - error_code != NGHTTP2_ERR_STREAM_CLOSED && - error_code != NGHTTP2_ERR_STREAM_CLOSING) { - handle->OnFrameError(frame->hd.stream_id, - frame->hd.type, - error_code); - } - return 0; -} - -inline int Nghttp2Session::OnInvalidHeader(nghttp2_session* session, - const nghttp2_frame* frame, - nghttp2_rcbuf* name, - nghttp2_rcbuf* value, - uint8_t flags, - void* user_data) { - // Ignore invalid header fields by default. - return 0; -} - -// Called when nghttp2 closes a stream, either in response to an RST_STREAM -// frame or the stream closing naturally on it's own -inline int Nghttp2Session::OnStreamClose(nghttp2_session* session, - int32_t id, - uint32_t code, - void* user_data) { - Nghttp2Session*handle = static_cast<Nghttp2Session*>(user_data); - DEBUG_HTTP2("Nghttp2Session %s: stream %d closed, code: %d\n", - handle->TypeName(), id, code); - Nghttp2Stream* stream = handle->FindStream(id); - // Intentionally ignore the callback if the stream does not exist - if (stream != nullptr) - stream->Close(code); - return 0; -} - -// Called by nghttp2 to collect the data while a file response is sent. -// The buf is the DATA frame buffer that needs to be filled with at most -// length bytes. flags is used to control what nghttp2 does next. -inline ssize_t Nghttp2Session::OnStreamReadFD(nghttp2_session* session, - int32_t id, - uint8_t* buf, - size_t length, - uint32_t* flags, - nghttp2_data_source* source, - void* user_data) { - Nghttp2Session* handle = static_cast<Nghttp2Session*>(user_data); - DEBUG_HTTP2("Nghttp2Session %s: reading outbound file data for stream %d\n", - handle->TypeName(), id); - Nghttp2Stream* stream = handle->FindStream(id); - - int fd = source->fd; - int64_t offset = stream->fd_offset_; - ssize_t numchars = 0; - - if (stream->fd_length_ >= 0 && - stream->fd_length_ < static_cast<int64_t>(length)) - length = stream->fd_length_; - - uv_buf_t data; - data.base = reinterpret_cast<char*>(buf); - data.len = length; - - uv_fs_t read_req; - - if (length > 0) { - // TODO(addaleax): Never use synchronous I/O on the main thread. - numchars = uv_fs_read(handle->event_loop(), - &read_req, - fd, &data, 1, - offset, nullptr); - uv_fs_req_cleanup(&read_req); - } - - // Close the stream with an error if reading fails - if (numchars < 0) - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - - // Update the read offset for the next read - stream->fd_offset_ += numchars; - stream->fd_length_ -= numchars; - - // if numchars < length, assume that we are done. - if (static_cast<size_t>(numchars) < length || length <= 0) { - DEBUG_HTTP2("Nghttp2Session %s: no more data for stream %d\n", - handle->TypeName(), id); - *flags |= NGHTTP2_DATA_FLAG_EOF; - GetTrailers(session, handle, stream, flags); - } - - return numchars; -} - -// Called by nghttp2 to collect the data to pack within a DATA frame. -// The buf is the DATA frame buffer that needs to be filled with at most -// length bytes. flags is used to control what nghttp2 does next. -inline ssize_t Nghttp2Session::OnStreamRead(nghttp2_session* session, - int32_t id, - uint8_t* buf, - size_t length, - uint32_t* flags, - nghttp2_data_source* source, - void* user_data) { - Nghttp2Session* handle = static_cast<Nghttp2Session*>(user_data); - DEBUG_HTTP2("Nghttp2Session %s: reading outbound data for stream %d\n", - handle->TypeName(), id); - Nghttp2Stream* stream = handle->FindStream(id); - size_t remaining = length; - size_t offset = 0; - - // While there is data in the queue, copy data into buf until it is full. - // There may be data left over, which will be sent the next time nghttp - // calls this callback. - while (!stream->queue_.empty()) { - DEBUG_HTTP2("Nghttp2Session %s: processing outbound data chunk\n", - handle->TypeName()); - nghttp2_stream_write* head = stream->queue_.front(); - while (stream->queue_index_ < head->nbufs) { - if (remaining == 0) - goto end; - - unsigned int n = stream->queue_index_; - // len is the number of bytes in head->bufs[n] that are yet to be written - size_t len = head->bufs[n].len - stream->queue_offset_; - size_t bytes_to_write = len < remaining ? len : remaining; - memcpy(buf + offset, - head->bufs[n].base + stream->queue_offset_, - bytes_to_write); - offset += bytes_to_write; - remaining -= bytes_to_write; - if (bytes_to_write < len) { - stream->queue_offset_ += bytes_to_write; - } else { - stream->queue_index_++; - stream->queue_offset_ = 0; - } - } - stream->queue_offset_ = 0; - stream->queue_index_ = 0; - head->cb(head->req, 0); - delete head; - stream->queue_.pop(); - } - -end: - // If we are no longer writable and there is no more data in the queue, - // then we need to set the NGHTTP2_DATA_FLAG_EOF flag. - // If we are still writable but there is not yet any data to send, set the - // NGHTTP2_ERR_DEFERRED flag. This will put the stream into a pending state - // that will wait for data to become available. - // If neither of these flags are set, then nghttp2 will call this callback - // again to get the data for the next DATA frame. - int writable = !stream->queue_.empty() || stream->IsWritable(); - if (offset == 0 && writable && stream->queue_.empty()) { - DEBUG_HTTP2("Nghttp2Session %s: deferring stream %d\n", - handle->TypeName(), id); - return NGHTTP2_ERR_DEFERRED; - } - if (!writable) { - DEBUG_HTTP2("Nghttp2Session %s: no more data for stream %d\n", - handle->TypeName(), id); - *flags |= NGHTTP2_DATA_FLAG_EOF; - - GetTrailers(session, handle, stream, flags); - } -#if defined(DEBUG) && DEBUG - CHECK(offset <= length); -#endif - return offset; -} - -// Called by nghttp2 when it needs to determine how much padding to apply -// to a DATA or HEADERS frame -inline ssize_t Nghttp2Session::OnSelectPadding(nghttp2_session* session, - const nghttp2_frame* frame, - size_t maxPayloadLen, - void* user_data) { - Nghttp2Session* handle = static_cast<Nghttp2Session*>(user_data); -#if defined(DEBUG) && DEBUG - CHECK(handle->HasGetPaddingCallback()); -#endif - ssize_t padding = handle->GetPadding(frame->hd.length, maxPayloadLen); - DEBUG_HTTP2("Nghttp2Session %s: using padding, size: %d\n", - handle->TypeName(), padding); - return padding; -} - -// While nghttp2 is processing a DATA frame, it will call the -// OnDataChunkReceived callback multiple times, passing along individual -// chunks of data from the DATA frame payload. These *must* be memcpy'd -// out because the pointer to the data will quickly become invalid. -inline int Nghttp2Session::OnDataChunkReceived(nghttp2_session* session, - uint8_t flags, - int32_t id, - const uint8_t* data, - size_t len, - void* user_data) { - Nghttp2Session* handle = static_cast<Nghttp2Session*>(user_data); - DEBUG_HTTP2("Nghttp2Session %s: buffering data chunk for stream %d, size: " - "%d, flags: %d\n", handle->TypeName(), - id, len, flags); - // We should never actually get a 0-length chunk so this check is - // only a precaution at this point. - if (len > 0) { - nghttp2_session_consume_connection(session, len); - Nghttp2Stream* stream = handle->FindStream(id); - char* buf = Malloc<char>(len); - memcpy(buf, data, len); - stream->data_chunks_.emplace(uv_buf_init(buf, len)); - } - return 0; -} - -// Only when we are done sending the last chunk of data do we check for -// any trailing headers that are to be sent. This is the only opportunity -// we have to make this check. If there are trailers, then the -// NGHTTP2_DATA_FLAG_NO_END_STREAM flag must be set. -inline void Nghttp2Session::GetTrailers(nghttp2_session* session, - Nghttp2Session* handle, - Nghttp2Stream* stream, - uint32_t* flags) { - if (stream->GetTrailers()) { - SubmitTrailers submit_trailers{handle, stream, flags}; - handle->OnTrailers(stream, submit_trailers); - } -} - -// Submits any trailing header fields that have been collected -inline void Nghttp2Session::SubmitTrailers::Submit(nghttp2_nv* trailers, - size_t length) const { - if (length == 0) - return; - DEBUG_HTTP2("Nghttp2Session %s: sending trailers for stream %d, " - "count: %d\n", handle_->TypeName(), - stream_->id(), length); - *flags_ |= NGHTTP2_DATA_FLAG_NO_END_STREAM; - nghttp2_submit_trailer(handle_->session_, - stream_->id(), - trailers, - length); -} - -// Submits a graceful shutdown notice to nghttp -// See: https://nghttp2.org/documentation/nghttp2_submit_shutdown_notice.html -inline void Nghttp2Session::SubmitShutdownNotice() { - DEBUG_HTTP2("Nghttp2Session %s: submitting shutdown notice\n", - TypeName()); - nghttp2_submit_shutdown_notice(session_); -} - -// Sends a SETTINGS frame on the current session -// Note that this *should* send a SETTINGS frame even if niv == 0 and there -// are no settings entries to send. -inline int Nghttp2Session::SubmitSettings(const nghttp2_settings_entry iv[], - size_t niv) { - DEBUG_HTTP2("Nghttp2Session %s: submitting settings, count: %d\n", - TypeName(), niv); - return nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv, niv); -} - -// Returns the Nghttp2Stream associated with the given id, or nullptr if none -inline Nghttp2Stream* Nghttp2Session::FindStream(int32_t id) { - auto s = streams_.find(id); - if (s != streams_.end()) { - DEBUG_HTTP2("Nghttp2Session %s: stream %d found\n", - TypeName(), id); - return s->second; - } else { - DEBUG_HTTP2("Nghttp2Session %s: stream %d not found\n", TypeName(), id); - return nullptr; - } -} - -// Flushes one buffered data chunk at a time. -inline void Nghttp2Stream::FlushDataChunks() { - if (!data_chunks_.empty()) { - uv_buf_t buf = data_chunks_.front(); - data_chunks_.pop(); - if (buf.len > 0) { - nghttp2_session_consume_stream(session_->session(), id_, buf.len); - session_->OnDataChunk(this, &buf); - } else { - session_->OnDataChunk(this, nullptr); - } - } -} - -// Called when a DATA frame has been completely processed. Will check to -// see if the END_STREAM flag is set, and will flush the queued data chunks -// to JS if the stream is flowing -inline void Nghttp2Session::HandleDataFrame(const nghttp2_frame* frame) { - int32_t id = GetFrameID(frame); - DEBUG_HTTP2("Nghttp2Session %s: handling data frame for stream %d\n", - TypeName(), id); - Nghttp2Stream* stream = this->FindStream(id); - // If the stream does not exist, something really bad happened -#if defined(DEBUG) && DEBUG - CHECK_NE(stream, nullptr); -#endif - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) - stream->data_chunks_.emplace(uv_buf_init(0, 0)); - if (stream->IsReading()) - stream->FlushDataChunks(); -} - -// Passes all of the collected headers for a HEADERS frame out to the JS layer. -// The headers are collected as the frame is being processed and sent out -// to the JS side only when the frame is fully processed. -inline void Nghttp2Session::HandleHeadersFrame(const nghttp2_frame* frame) { - int32_t id = GetFrameID(frame); - DEBUG_HTTP2("Nghttp2Session %s: handling headers frame for stream %d\n", - TypeName(), id); - Nghttp2Stream* stream = FindStream(id); - // If the stream does not exist, something really bad happened -#if defined(DEBUG) && DEBUG - CHECK_NE(stream, nullptr); -#endif - OnHeaders(stream, - stream->headers(), - stream->headers_count(), - stream->headers_category(), - frame->hd.flags); -} - -// Notifies the JS layer that a PRIORITY frame has been received -inline void Nghttp2Session::HandlePriorityFrame(const nghttp2_frame* frame) { - nghttp2_priority priority_frame = frame->priority; - int32_t id = GetFrameID(frame); - DEBUG_HTTP2("Nghttp2Session %s: handling priority frame for stream %d\n", - TypeName(), id); - - // Priority frame stream ID should never be <= 0. nghttp2 handles this - // as an error condition that terminates the session, so we should be - // good here - -#if defined(DEBUG) && DEBUG - CHECK_GT(id, 0); -#endif - - nghttp2_priority_spec spec = priority_frame.pri_spec; - OnPriority(id, spec.stream_id, spec.weight, spec.exclusive); -} - -// Notifies the JS layer that a GOAWAY frame has been received -inline void Nghttp2Session::HandleGoawayFrame(const nghttp2_frame* frame) { - nghttp2_goaway goaway_frame = frame->goaway; - DEBUG_HTTP2("Nghttp2Session %s: handling goaway frame\n", TypeName()); - - OnGoAway(goaway_frame.last_stream_id, - goaway_frame.error_code, - goaway_frame.opaque_data, - goaway_frame.opaque_data_len); -} - -// Prompts nghttp2 to flush the queue of pending data frames -inline void Nghttp2Session::SendPendingData() { - DEBUG_HTTP2("Nghttp2Session %s: Sending pending data\n", TypeName()); - // Do not attempt to send data on the socket if the destroying flag has - // been set. That means everything is shutting down and the socket - // will not be usable. - if (IsDestroying()) - return; - - WriteWrap* req = nullptr; - char* dest = nullptr; - size_t destRemaining = 0; - size_t destLength = 0; // amount of data stored in dest - size_t destOffset = 0; // current write offset of dest - - const uint8_t* src; // pointer to the serialized data - ssize_t srcLength = 0; // length of serialized data chunk - - // While srcLength is greater than zero - while ((srcLength = nghttp2_session_mem_send(session_, &src)) > 0) { - if (req == nullptr) { - req = AllocateSend(); - destRemaining = req->ExtraSize(); - dest = req->Extra(); - } - DEBUG_HTTP2("Nghttp2Session %s: nghttp2 has %d bytes to send\n", - TypeName(), srcLength); - size_t srcRemaining = srcLength; - size_t srcOffset = 0; - - // The amount of data we have to copy is greater than the space - // remaining. Copy what we can into the remaining space, send it, - // the proceed with the rest. - while (srcRemaining > destRemaining) { - DEBUG_HTTP2("Nghttp2Session %s: pushing %d bytes to the socket\n", - TypeName(), destLength + destRemaining); - memcpy(dest + destOffset, src + srcOffset, destRemaining); - destLength += destRemaining; - Send(req, dest, destLength); - destOffset = 0; - destLength = 0; - srcRemaining -= destRemaining; - srcOffset += destRemaining; - req = AllocateSend(); - destRemaining = req->ExtraSize(); - dest = req->Extra(); - } - - if (srcRemaining > 0) { - memcpy(dest + destOffset, src + srcOffset, srcRemaining); - destLength += srcRemaining; - destOffset += srcRemaining; - destRemaining -= srcRemaining; - srcRemaining = 0; - srcOffset = 0; - } - } - - if (destLength > 0) { - DEBUG_HTTP2("Nghttp2Session %s: pushing %d bytes to the socket\n", - TypeName(), destLength); - Send(req, dest, destLength); - } -} - -// Initialize the Nghttp2Session handle by creating and -// assigning the Nghttp2Session instance and associated -// uv_loop_t. -inline int Nghttp2Session::Init(const nghttp2_session_type type, - nghttp2_option* options, - nghttp2_mem* mem, - uint32_t maxHeaderPairs) { - session_type_ = type; - DEBUG_HTTP2("Nghttp2Session %s: initializing session\n", TypeName()); - destroying_ = false; - - max_header_pairs_ = maxHeaderPairs; - - nghttp2_session_callbacks* callbacks - = callback_struct_saved[HasGetPaddingCallback() ? 1 : 0].callbacks; - - CHECK_NE(options, nullptr); - - typedef int (*init_fn)(nghttp2_session** session, - const nghttp2_session_callbacks* callbacks, - void* user_data, - const nghttp2_option* options, - nghttp2_mem* mem); - init_fn fn = type == NGHTTP2_SESSION_SERVER ? - nghttp2_session_server_new3 : - nghttp2_session_client_new3; - - return fn(&session_, callbacks, this, options, mem); -} - -inline void Nghttp2Session::MarkDestroying() { - destroying_ = true; -} - -inline Nghttp2Session::~Nghttp2Session() { - Close(); -} - -inline void Nghttp2Session::Close() { - if (IsClosed()) - return; - DEBUG_HTTP2("Nghttp2Session %s: freeing session\n", TypeName()); - nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR); - nghttp2_session_del(session_); - session_ = nullptr; - DEBUG_HTTP2("Nghttp2Session %s: session freed\n", TypeName()); -} - -// Write data received from the socket to the underlying nghttp2_session. -inline ssize_t Nghttp2Session::Write(const uv_buf_t* bufs, unsigned int nbufs) { - size_t total = 0; - for (unsigned int n = 0; n < nbufs; n++) { - ssize_t ret = - nghttp2_session_mem_recv(session_, - reinterpret_cast<uint8_t*>(bufs[n].base), - bufs[n].len); - if (ret < 0) { - return ret; - } else { - total += ret; - } - } - SendPendingData(); - return total; -} - -inline void Nghttp2Session::AddStream(Nghttp2Stream* stream) { - streams_[stream->id()] = stream; -} - -// Removes a stream instance from this session -inline void Nghttp2Session::RemoveStream(int32_t id) { - streams_.erase(id); -} - -// Implementation for Nghttp2Stream functions - -Nghttp2Stream::Nghttp2Stream( - int32_t id, - Nghttp2Session* session, - nghttp2_headers_category category, - int options) : id_(id), - session_(session), - current_headers_category_(category) { - // Limit the number of header pairs - max_header_pairs_ = session->GetMaxHeaderPairs(); - if (max_header_pairs_ == 0) - max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS; - current_headers_.reserve(max_header_pairs_); - - // Limit the number of header octets - max_header_length_ = - std::min( - nghttp2_session_get_local_settings( - session->session(), - NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE), - MAX_MAX_HEADER_LIST_SIZE); - - getTrailers_ = options & STREAM_OPTION_GET_TRAILERS; - if (options & STREAM_OPTION_EMPTY_PAYLOAD) - Shutdown(); - session->AddStream(this); -} - - -inline void Nghttp2Stream::Destroy() { - DEBUG_HTTP2("Nghttp2Stream %d: destroying stream\n", id_); - // Do nothing if this stream instance is already destroyed - if (IsDestroyed()) - return; - flags_ |= NGHTTP2_STREAM_FLAG_DESTROYED; - Nghttp2Session* session = this->session_; - - if (session != nullptr) { - session_->RemoveStream(this->id()); - session_ = nullptr; - } - - // Free any remaining incoming data chunks. - while (!data_chunks_.empty()) { - uv_buf_t buf = data_chunks_.front(); - free(buf.base); - data_chunks_.pop(); - } - - // Free any remaining outgoing data chunks. - while (!queue_.empty()) { - nghttp2_stream_write* head = queue_.front(); - head->cb(head->req, UV_ECANCELED); - delete head; - queue_.pop(); - } - - delete this; -} - -// Submit informational headers for a stream. -inline int Nghttp2Stream::SubmitInfo(nghttp2_nv* nva, size_t len) { - DEBUG_HTTP2("Nghttp2Stream %d: sending informational headers, count: %d\n", - id_, len); - CHECK_GT(len, 0); - return nghttp2_submit_headers(session_->session(), - NGHTTP2_FLAG_NONE, - id_, nullptr, - nva, len, nullptr); -} - -inline int Nghttp2Stream::SubmitPriority(nghttp2_priority_spec* prispec, - bool silent) { - DEBUG_HTTP2("Nghttp2Stream %d: sending priority spec\n", id_); - return silent ? - nghttp2_session_change_stream_priority(session_->session(), - id_, prispec) : - nghttp2_submit_priority(session_->session(), - NGHTTP2_FLAG_NONE, - id_, prispec); -} - -// Submit an RST_STREAM frame -inline int Nghttp2Stream::SubmitRstStream(const uint32_t code) { - DEBUG_HTTP2("Nghttp2Stream %d: sending rst-stream, code: %d\n", id_, code); - session_->SendPendingData(); - return nghttp2_submit_rst_stream(session_->session(), - NGHTTP2_FLAG_NONE, - id_, - code); -} - -// Submit a push promise. -inline int32_t Nghttp2Stream::SubmitPushPromise( - nghttp2_nv* nva, - size_t len, - Nghttp2Stream** assigned, - int options) { -#if defined(DEBUG) && DEBUG - CHECK_GT(len, 0); -#endif - DEBUG_HTTP2("Nghttp2Stream %d: sending push promise\n", id_); - int32_t ret = nghttp2_submit_push_promise(session_->session(), - NGHTTP2_FLAG_NONE, - id_, nva, len, - nullptr); - if (ret > 0) { - auto stream = new Nghttp2Stream(ret, session_, - NGHTTP2_HCAT_HEADERS, - options); - if (assigned != nullptr) *assigned = stream; - } - return ret; -} - -// Initiate a response. If the nghttp2_stream is still writable by -// the time this is called, then an nghttp2_data_provider will be -// initialized, causing at least one (possibly empty) data frame to -// be sent. -inline int Nghttp2Stream::SubmitResponse(nghttp2_nv* nva, - size_t len, - int options) { -#if defined(DEBUG) && DEBUG - CHECK_GT(len, 0); -#endif - DEBUG_HTTP2("Nghttp2Stream %d: submitting response\n", id_); - getTrailers_ = options & STREAM_OPTION_GET_TRAILERS; - nghttp2_data_provider* provider = nullptr; - nghttp2_data_provider prov; - prov.source.ptr = this; - prov.read_callback = Nghttp2Session::OnStreamRead; - if (IsWritable() && !(options & STREAM_OPTION_EMPTY_PAYLOAD)) - provider = &prov; - - return nghttp2_submit_response(session_->session(), id_, - nva, len, provider); -} - -// Initiate a response that contains data read from a file descriptor. -inline int Nghttp2Stream::SubmitFile(int fd, - nghttp2_nv* nva, size_t len, - int64_t offset, - int64_t length, - int options) { -#if defined(DEBUG) && DEBUG - CHECK_GT(len, 0); - CHECK_GT(fd, 0); -#endif - DEBUG_HTTP2("Nghttp2Stream %d: submitting file\n", id_); - getTrailers_ = options & STREAM_OPTION_GET_TRAILERS; - nghttp2_data_provider prov; - prov.source.fd = fd; - prov.read_callback = Nghttp2Session::OnStreamReadFD; - - if (offset > 0) fd_offset_ = offset; - if (length > -1) fd_length_ = length; - - return nghttp2_submit_response(session_->session(), id_, - nva, len, &prov); -} - -// Initiate a request. If writable is true (the default), then -// an nghttp2_data_provider will be initialized, causing at -// least one (possibly empty) data frame to to be sent. -inline int32_t Nghttp2Session::SubmitRequest( - nghttp2_priority_spec* prispec, - nghttp2_nv* nva, - size_t len, - Nghttp2Stream** assigned, - int options) { -#if defined(DEBUG) && DEBUG - CHECK_GT(len, 0); -#endif - DEBUG_HTTP2("Nghttp2Session: submitting request\n"); - nghttp2_data_provider* provider = nullptr; - nghttp2_data_provider prov; - prov.source.ptr = this; - prov.read_callback = OnStreamRead; - if (!(options & STREAM_OPTION_EMPTY_PAYLOAD)) - provider = &prov; - int32_t ret = nghttp2_submit_request(session_, - prispec, nva, len, - provider, nullptr); - // Assign the Nghttp2Stream handle - if (ret > 0) { - auto stream = new Nghttp2Stream(ret, this, NGHTTP2_HCAT_HEADERS, options); - if (assigned != nullptr) *assigned = stream; - } - return ret; -} - -// Queue the given set of uv_but_t handles for writing to an -// nghttp2_stream. The callback will be invoked once the chunks -// of data have been flushed to the underlying nghttp2_session. -// Note that this does *not* mean that the data has been flushed -// to the socket yet. -inline int Nghttp2Stream::Write(nghttp2_stream_write_t* req, - const uv_buf_t bufs[], - unsigned int nbufs, - nghttp2_stream_write_cb cb) { - if (!IsWritable()) { - if (cb != nullptr) - cb(req, UV_EOF); - return 0; - } - DEBUG_HTTP2("Nghttp2Stream %d: queuing buffers to send, count: %d\n", - id_, nbufs); - nghttp2_stream_write* item = new nghttp2_stream_write; - item->cb = cb; - item->req = req; - item->nbufs = nbufs; - item->bufs.AllocateSufficientStorage(nbufs); - memcpy(*(item->bufs), bufs, nbufs * sizeof(*bufs)); - queue_.push(item); - nghttp2_session_resume_data(session_->session(), id_); - return 0; -} - -inline void Nghttp2Stream::ReadStart() { - if (IsReading()) - return; - DEBUG_HTTP2("Nghttp2Stream %d: start reading\n", id_); - flags_ |= NGHTTP2_STREAM_FLAG_READ_START; - flags_ &= ~NGHTTP2_STREAM_FLAG_READ_PAUSED; - - // Flush any queued data chunks immediately out to the JS layer - FlushDataChunks(); -} - -inline void Nghttp2Stream::ReadResume() { - DEBUG_HTTP2("Nghttp2Stream %d: resume reading\n", id_); - flags_ &= ~NGHTTP2_STREAM_FLAG_READ_PAUSED; - - // Flush any queued data chunks immediately out to the JS layer - FlushDataChunks(); -} - -inline void Nghttp2Stream::ReadStop() { - DEBUG_HTTP2("Nghttp2Stream %d: stop reading\n", id_); - if (!IsReading()) - return; - flags_ |= NGHTTP2_STREAM_FLAG_READ_PAUSED; -} - -Nghttp2Session::Callbacks::Callbacks(bool kHasGetPaddingCallback) { - nghttp2_session_callbacks_new(&callbacks); - nghttp2_session_callbacks_set_on_begin_headers_callback( - callbacks, OnBeginHeadersCallback); - nghttp2_session_callbacks_set_on_header_callback2( - callbacks, OnHeaderCallback); - nghttp2_session_callbacks_set_on_frame_recv_callback( - callbacks, OnFrameReceive); - nghttp2_session_callbacks_set_on_stream_close_callback( - callbacks, OnStreamClose); - nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - callbacks, OnDataChunkReceived); - nghttp2_session_callbacks_set_on_frame_not_send_callback( - callbacks, OnFrameNotSent); - nghttp2_session_callbacks_set_on_invalid_header_callback2( - callbacks, OnInvalidHeader); - -#ifdef NODE_DEBUG_HTTP2 - nghttp2_session_callbacks_set_error_callback( - callbacks, OnNghttpError); -#endif - - if (kHasGetPaddingCallback) { - nghttp2_session_callbacks_set_select_padding_callback( - callbacks, OnSelectPadding); - } -} - -Nghttp2Session::Callbacks::~Callbacks() { - nghttp2_session_callbacks_del(callbacks); -} - -Nghttp2Session::SubmitTrailers::SubmitTrailers( - Nghttp2Session* handle, - Nghttp2Stream* stream, - uint32_t* flags) - : handle_(handle), stream_(stream), flags_(flags) { } - -} // namespace http2 -} // namespace node - -#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS - -#endif // SRC_NODE_HTTP2_CORE_INL_H_ |