%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/quic/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/quic/http3.cc |
#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC #include "http3.h" #include <async_wrap-inl.h> #include <base_object-inl.h> #include <debug_utils-inl.h> #include <env-inl.h> #include <memory_tracker-inl.h> #include <nghttp3/nghttp3.h> #include <ngtcp2/ngtcp2.h> #include <node_http_common-inl.h> #include <node_sockaddr-inl.h> #include <util-inl.h> #include "application.h" #include "bindingdata.h" #include "defs.h" #include "session.h" #include "sessionticket.h" namespace node { namespace quic { namespace { struct Http3HeadersTraits { typedef nghttp3_nv nv_t; }; struct Http3RcBufferPointerTraits { typedef nghttp3_rcbuf rcbuf_t; typedef nghttp3_vec vector_t; static void inc(rcbuf_t* buf) { CHECK_NOT_NULL(buf); nghttp3_rcbuf_incref(buf); } static void dec(rcbuf_t* buf) { CHECK_NOT_NULL(buf); nghttp3_rcbuf_decref(buf); } static vector_t get_vec(rcbuf_t* buf) { CHECK_NOT_NULL(buf); return nghttp3_rcbuf_get_buf(buf); } static bool is_static(const rcbuf_t* buf) { CHECK_NOT_NULL(buf); return nghttp3_rcbuf_is_static(buf); } }; using Http3ConnectionPointer = DeleteFnPtr<nghttp3_conn, nghttp3_conn_del>; using Http3Headers = NgHeaders<Http3HeadersTraits>; using Http3RcBufferPointer = NgRcBufPointer<Http3RcBufferPointerTraits>; struct Http3HeaderTraits { typedef Http3RcBufferPointer rcbufferpointer_t; typedef BindingData allocator_t; static const char* ToHttpHeaderName(int32_t token) { switch (token) { case -1: return nullptr; #define V(key, name) \ case NGHTTP3_QPACK_TOKEN__##key: \ return name; HTTP_SPECIAL_HEADERS(V) #undef V #define V(key, name) \ case NGHTTP3_QPACK_TOKEN_##key: \ return name; HTTP_REGULAR_HEADERS(V) #undef V } return nullptr; } }; using Http3Header = NgHeader<Http3HeaderTraits>; // Implements the low-level HTTP/3 Application semantics. class Http3Application final : public Session::Application { public: Http3Application(Session* session, const Session::Application_Options& options) : Application(session, options), allocator_(BindingData::Get(env())), options_(options), conn_(InitializeConnection()) { session->set_priority_supported(); } bool Start() override { CHECK(!started_); started_ = true; Debug(&session(), "Starting HTTP/3 application."); auto params = ngtcp2_conn_get_remote_transport_params(session()); if (params == nullptr) { // The params are not available yet. Cannot start. Debug(&session(), "Cannot start HTTP/3 application yet. No remote transport params"); return false; } if (params->initial_max_streams_uni < 3) { // If the initial max unidirectional stream limit is not at least three, // we cannot actually use it since we need to create the control streams. Debug(&session(), "Cannot start HTTP/3 application. Initial max " "unidirectional streams is too low"); return false; } if (session().is_server()) { nghttp3_conn_set_max_client_streams_bidi( *this, params->initial_max_streams_bidi); } return CreateAndBindControlStreams(); } bool ReceiveStreamData(Stream* stream, const uint8_t* data, size_t datalen, Stream::ReceiveDataFlags flags) override { Debug(&session(), "HTTP/3 application received %zu bytes of data", datalen); ssize_t nread = nghttp3_conn_read_stream( *this, stream->id(), data, datalen, flags.fin ? 1 : 0); if (nread < 0) { Debug(&session(), "HTTP/3 application failed to read stream data: %s", nghttp3_strerror(nread)); return false; } Debug(&session(), "Extending stream and connection offset by %zd bytes", nread); session().ExtendStreamOffset(stream->id(), nread); session().ExtendOffset(nread); return true; } void AcknowledgeStreamData(Stream* stream, size_t datalen) override { Debug(&session(), "HTTP/3 application received acknowledgement for %zu bytes of data", datalen); CHECK_EQ(nghttp3_conn_add_ack_offset(*this, stream->id(), datalen), 0); } bool CanAddHeader(size_t current_count, size_t current_headers_length, size_t this_header_length) override { // We cannot add the header if we've either reached // * the max number of header pairs or // * the max number of header bytes bool answer = (current_count < options_.max_header_pairs) && (current_headers_length + this_header_length) <= options_.max_header_length; IF_QUIC_DEBUG(env()) { if (answer) { Debug(&session(), "HTTP/3 application can add header"); } else { Debug(&session(), "HTTP/3 application cannot add header"); } } return answer; } void BlockStream(int64_t id) override { nghttp3_conn_block_stream(*this, id); Application::BlockStream(id); } void ResumeStream(int64_t id) override { nghttp3_conn_resume_stream(*this, id); Application::ResumeStream(id); } void ExtendMaxStreams(EndpointLabel label, Direction direction, uint64_t max_streams) override { switch (label) { case EndpointLabel::LOCAL: return; case EndpointLabel::REMOTE: { switch (direction) { case Direction::BIDIRECTIONAL: { Debug(&session(), "HTTP/3 application extending max bidi streams to %" PRIu64, max_streams); ngtcp2_conn_extend_max_streams_bidi( session(), static_cast<size_t>(max_streams)); break; } case Direction::UNIDIRECTIONAL: { Debug(&session(), "HTTP/3 application extending max uni streams to %" PRIu64, max_streams); ngtcp2_conn_extend_max_streams_uni( session(), static_cast<size_t>(max_streams)); break; } } } } } void ExtendMaxStreamData(Stream* stream, uint64_t max_data) override { Debug(&session(), "HTTP/3 application extending max stream data to %" PRIu64, max_data); nghttp3_conn_unblock_stream(*this, stream->id()); } void CollectSessionTicketAppData( SessionTicket::AppData* app_data) const override { // TODO(@jasnell): There's currently nothing to store but there may be // later. } SessionTicket::AppData::Status ExtractSessionTicketAppData( const SessionTicket::AppData& app_data, SessionTicket::AppData::Source::Flag flag) override { // There's currently nothing stored here but we might do so later. return flag == SessionTicket::AppData::Source::Flag::STATUS_RENEW ? SessionTicket::AppData::Status::TICKET_USE_RENEW : SessionTicket::AppData::Status::TICKET_USE; } void StreamClose(Stream* stream, QuicError error = QuicError()) override { Debug( &session(), "HTTP/3 application closing stream %" PRIi64, stream->id()); uint64_t code = NGHTTP3_H3_NO_ERROR; if (error) { CHECK_EQ(error.type(), QuicError::Type::APPLICATION); code = error.code(); } int rv = nghttp3_conn_close_stream(*this, stream->id(), code); // If the call is successful, Http3Application::OnStreamClose callback will // be invoked when the stream is ready to be closed. We'll handle destroying // the actual Stream object there. if (rv == 0) return; if (rv == NGHTTP3_ERR_STREAM_NOT_FOUND) { ExtendMaxStreams(EndpointLabel::REMOTE, stream->direction(), 1); return; } session().SetLastError( QuicError::ForApplication(nghttp3_err_infer_quic_app_error_code(rv))); session().Close(); } void StreamReset(Stream* stream, uint64_t final_size, QuicError error) override { // We are shutting down the readable side of the local stream here. Debug(&session(), "HTTP/3 application resetting stream %" PRIi64, stream->id()); int rv = nghttp3_conn_shutdown_stream_read(*this, stream->id()); if (rv == 0) { stream->ReceiveStreamReset(final_size, error); return; } session().SetLastError( QuicError::ForApplication(nghttp3_err_infer_quic_app_error_code(rv))); session().Close(); } void StreamStopSending(Stream* stream, QuicError error) override { Application::StreamStopSending(stream, error); } bool SendHeaders(const Stream& stream, HeadersKind kind, const v8::Local<v8::Array>& headers, HeadersFlags flags = HeadersFlags::NONE) override { Session::SendPendingDataScope send_scope(&session()); Http3Headers nva(env(), headers); switch (kind) { case HeadersKind::HINTS: { if (!session().is_server()) { // Client side cannot send hints return false; } Debug(&session(), "Submitting early hints for stream " PRIi64, stream.id()); return nghttp3_conn_submit_info( *this, stream.id(), nva.data(), nva.length()) == 0; break; } case HeadersKind::INITIAL: { static constexpr nghttp3_data_reader reader = {on_read_data_callback}; const nghttp3_data_reader* reader_ptr = nullptr; // If the terminal flag is set, that means that we know we're only // sending headers and no body and the stream writable side should be // closed immediately because there is no nghttp3_data_reader provided. if (flags != HeadersFlags::TERMINAL) reader_ptr = &reader; if (session().is_server()) { // If this is a server, we're submitting a response... Debug(&session(), "Submitting response headers for stream " PRIi64, stream.id()); return nghttp3_conn_submit_response( *this, stream.id(), nva.data(), nva.length(), reader_ptr); } else { // Otherwise we're submitting a request... Debug(&session(), "Submitting request headers for stream " PRIi64, stream.id()); return nghttp3_conn_submit_request(*this, stream.id(), nva.data(), nva.length(), reader_ptr, const_cast<Stream*>(&stream)) == 0; } break; } case HeadersKind::TRAILING: { return nghttp3_conn_submit_trailers( *this, stream.id(), nva.data(), nva.length()) == 0; break; } } return false; } StreamPriority GetStreamPriority(const Stream& stream) override { nghttp3_pri pri; if (nghttp3_conn_get_stream_priority(*this, &pri, stream.id()) == 0) { // TODO(@jasnell): Support the incremental flag switch (pri.urgency) { case NGHTTP3_URGENCY_HIGH: return StreamPriority::HIGH; case NGHTTP3_URGENCY_LOW: return StreamPriority::LOW; default: return StreamPriority::DEFAULT; } } return StreamPriority::DEFAULT; } int GetStreamData(StreamData* data) override { ssize_t ret = 0; Debug(&session(), "HTTP/3 application getting stream data"); if (conn_ && session().max_data_left()) { nghttp3_vec vec = *data; ret = nghttp3_conn_writev_stream( *this, &data->id, &data->fin, &vec, data->count); if (ret < 0) { return static_cast<int>(ret); } else { data->remaining = data->count = static_cast<size_t>(ret); } } return 0; } bool StreamCommit(StreamData* data, size_t datalen) override { Debug(&session(), "HTTP/3 application committing stream %" PRIi64 " data %zu", data->id, datalen); int err = nghttp3_conn_add_write_offset(*this, data->id, datalen); if (err != 0) { session().SetLastError(QuicError::ForApplication( nghttp3_err_infer_quic_app_error_code(err))); return false; } return true; } bool ShouldSetFin(const StreamData& data) override { return data.id > -1 && !is_control_stream(data.id) && data.fin == 1; } SET_NO_MEMORY_INFO() SET_MEMORY_INFO_NAME(Http3Application) SET_SELF_SIZE(Http3Application) private: inline operator nghttp3_conn*() const { DCHECK_NOT_NULL(conn_.get()); return conn_.get(); } bool CreateAndBindControlStreams() { Debug(&session(), "Creating and binding HTTP/3 control streams"); auto stream = session().OpenStream(Direction::UNIDIRECTIONAL); if (!stream) return false; if (nghttp3_conn_bind_control_stream(*this, stream->id()) != 0) { return false; } auto enc_stream = session().OpenStream(Direction::UNIDIRECTIONAL); if (!enc_stream) return false; auto dec_stream = session().OpenStream(Direction::UNIDIRECTIONAL); if (!dec_stream) return false; bool bound = nghttp3_conn_bind_qpack_streams( *this, enc_stream->id(), dec_stream->id()) == 0; control_stream_id_ = stream->id(); qpack_enc_stream_id_ = enc_stream->id(); qpack_dec_stream_id_ = dec_stream->id(); return bound; } inline bool is_control_stream(int64_t id) const { return id == control_stream_id_ || id == qpack_dec_stream_id_ || id == qpack_enc_stream_id_; } bool is_destroyed() const { return session().is_destroyed(); } Http3ConnectionPointer InitializeConnection() { nghttp3_conn* conn = nullptr; nghttp3_settings settings = options_; if (session().is_server()) { CHECK_EQ(nghttp3_conn_server_new( &conn, &kCallbacks, &settings, &allocator_, this), 0); } else { CHECK_EQ(nghttp3_conn_client_new( &conn, &kCallbacks, &settings, &allocator_, this), 0); } return Http3ConnectionPointer(conn); } void OnStreamClose(Stream* stream, uint64_t app_error_code) { if (stream->is_destroyed()) return; Debug(&session(), "HTTP/3 application received stream close for stream %" PRIi64, stream->id()); auto direction = stream->direction(); stream->Destroy(QuicError::ForApplication(app_error_code)); ExtendMaxStreams(EndpointLabel::REMOTE, direction, 1); } void OnReceiveData(Stream* stream, const nghttp3_vec& vec) { if (stream->is_destroyed()) return; Debug(&session(), "HTTP/3 application received %zu bytes of data", vec.len); stream->ReceiveData(vec.base, vec.len, Stream::ReceiveDataFlags{}); } void OnDeferredConsume(Stream* stream, size_t consumed) { auto& sess = session(); Debug( &session(), "HTTP/3 application deferred consume %zu bytes", consumed); if (!stream->is_destroyed()) { sess.ExtendStreamOffset(stream->id(), consumed); } sess.ExtendOffset(consumed); } void OnBeginHeaders(Stream* stream) { if (stream->is_destroyed()) return; Debug(&session(), "HTTP/3 application beginning initial block of headers for stream " "%" PRIi64, stream->id()); stream->BeginHeaders(HeadersKind::INITIAL); } void OnReceiveHeader(Stream* stream, Http3Header&& header) { if (stream->is_destroyed()) return; if (header.name() == ":status") { if (header.value()[0] == '1') { Debug( &session(), "HTTP/3 application switching to hints headers for stream %" PRIi64, stream->id()); stream->set_headers_kind(HeadersKind::HINTS); } } stream->AddHeader(std::move(header)); } void OnEndHeaders(Stream* stream, int fin) { Debug(&session(), "HTTP/3 application received end of headers for stream %" PRIi64, stream->id()); stream->EmitHeaders(); if (fin != 0) { // The stream is done. There's no more data to receive! Debug(&session(), "Headers are final for stream %" PRIi64, stream->id()); OnEndStream(stream); } } void OnBeginTrailers(Stream* stream) { if (stream->is_destroyed()) return; Debug(&session(), "HTTP/3 application beginning block of trailers for stream %" PRIi64, stream->id()); stream->BeginHeaders(HeadersKind::TRAILING); } void OnReceiveTrailer(Stream* stream, Http3Header&& header) { stream->AddHeader(header); } void OnEndTrailers(Stream* stream, int fin) { if (stream->is_destroyed()) return; Debug(&session(), "HTTP/3 application received end of trailers for stream %" PRIi64, stream->id()); stream->EmitHeaders(); if (fin != 0) { Debug(&session(), "Trailers are final for stream %" PRIi64, stream->id()); // The stream is done. There's no more data to receive! stream->ReceiveData(nullptr, 0, Stream::ReceiveDataFlags{/* .fin = */ true, /* .early = */ false}); } } void OnEndStream(Stream* stream) { if (stream->is_destroyed()) return; Debug(&session(), "HTTP/3 application received end of stream for stream %" PRIi64, stream->id()); stream->ReceiveData(nullptr, 0, Stream::ReceiveDataFlags{/* .fin = */ true, /* .early = */ false}); } void OnStopSending(Stream* stream, uint64_t app_error_code) { if (stream->is_destroyed()) return; Debug(&session(), "HTTP/3 application received stop sending for stream %" PRIi64, stream->id()); stream->ReceiveStopSending(QuicError::ForApplication(app_error_code)); } void OnResetStream(Stream* stream, uint64_t app_error_code) { if (stream->is_destroyed()) return; Debug(&session(), "HTTP/3 application received reset stream for stream %" PRIi64, stream->id()); stream->ReceiveStreamReset(0, QuicError::ForApplication(app_error_code)); } void OnShutdown() { // This callback is invoked when we receive a request to gracefully shutdown // the http3 connection. For client, the id is the stream id of a client // initiated stream. For server, the id is the stream id of a server // initiated stream. Once received, the other side is guaranteed not to // process any more data. // On the client side, if id is equal to NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID, // or on the server if the id is equal to NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID, // then this is a request to begin a graceful shutdown. // This can be called multiple times but the id can only stay the same or // *decrease*. // TODO(@jasnell): Need to determine exactly how to handle. Debug(&session(), "HTTP/3 application received shutdown notice"); } void OnReceiveSettings(const nghttp3_settings* settings) { options_.enable_connect_protocol = settings->enable_connect_protocol; options_.enable_datagrams = settings->h3_datagram; options_.max_field_section_size = settings->max_field_section_size; options_.qpack_blocked_streams = settings->qpack_blocked_streams; options_.qpack_encoder_max_dtable_capacity = settings->qpack_encoder_max_dtable_capacity; options_.qpack_max_dtable_capacity = settings->qpack_max_dtable_capacity; Debug( &session(), "HTTP/3 application received updated settings ", options_); } bool started_ = false; nghttp3_mem allocator_; Session::Application_Options options_; Http3ConnectionPointer conn_; int64_t control_stream_id_ = -1; int64_t qpack_dec_stream_id_ = -1; int64_t qpack_enc_stream_id_ = -1; // ========================================================================== // Static callbacks static Http3Application* From(nghttp3_conn* conn, void* user_data) { DCHECK_NOT_NULL(user_data); auto app = static_cast<Http3Application*>(user_data); DCHECK_EQ(conn, app->conn_.get()); return app; } static Stream* From(int64_t stream_id, void* stream_user_data) { DCHECK_NOT_NULL(stream_user_data); auto stream = static_cast<Stream*>(stream_user_data); DCHECK_EQ(stream_id, stream->id()); return stream; } #define NGHTTP3_CALLBACK_SCOPE(name) \ auto name = From(conn, conn_user_data); \ if (UNLIKELY(name->is_destroyed())) return NGHTTP3_ERR_CALLBACK_FAILURE; \ NgHttp3CallbackScope scope(name->env()); static nghttp3_ssize on_read_data_callback(nghttp3_conn* conn, int64_t stream_id, nghttp3_vec* vec, size_t veccnt, uint32_t* pflags, void* conn_user_data, void* stream_user_data) { return 0; } static int on_acked_stream_data(nghttp3_conn* conn, int64_t stream_id, uint64_t datalen, void* conn_user_data, void* stream_user_data) { NGHTTP3_CALLBACK_SCOPE(app); auto stream = From(stream_id, stream_user_data); if (stream == nullptr) return NGHTTP3_ERR_CALLBACK_FAILURE; app->AcknowledgeStreamData(stream, static_cast<size_t>(datalen)); return NGTCP2_SUCCESS; } static int on_stream_close(nghttp3_conn* conn, int64_t stream_id, uint64_t app_error_code, void* conn_user_data, void* stream_user_data) { NGHTTP3_CALLBACK_SCOPE(app); auto stream = From(stream_id, stream_user_data); if (stream == nullptr) return NGHTTP3_ERR_CALLBACK_FAILURE; app->OnStreamClose(stream, app_error_code); return NGTCP2_SUCCESS; } static int on_receive_data(nghttp3_conn* conn, int64_t stream_id, const uint8_t* data, size_t datalen, void* conn_user_data, void* stream_user_data) { NGHTTP3_CALLBACK_SCOPE(app); auto stream = From(stream_id, stream_user_data); if (stream == nullptr) return NGHTTP3_ERR_CALLBACK_FAILURE; app->OnReceiveData(stream, nghttp3_vec{const_cast<uint8_t*>(data), datalen}); return NGTCP2_SUCCESS; } static int on_deferred_consume(nghttp3_conn* conn, int64_t stream_id, size_t consumed, void* conn_user_data, void* stream_user_data) { NGHTTP3_CALLBACK_SCOPE(app); auto stream = From(stream_id, stream_user_data); if (stream == nullptr) return NGHTTP3_ERR_CALLBACK_FAILURE; app->OnDeferredConsume(stream, consumed); return NGTCP2_SUCCESS; } static int on_begin_headers(nghttp3_conn* conn, int64_t stream_id, void* conn_user_data, void* stream_user_data) { NGHTTP3_CALLBACK_SCOPE(app); auto stream = From(stream_id, stream_user_data); if (stream == nullptr) return NGHTTP3_ERR_CALLBACK_FAILURE; app->OnBeginHeaders(stream); return NGTCP2_SUCCESS; } static int on_receive_header(nghttp3_conn* conn, int64_t stream_id, int32_t token, nghttp3_rcbuf* name, nghttp3_rcbuf* value, uint8_t flags, void* conn_user_data, void* stream_user_data) { NGHTTP3_CALLBACK_SCOPE(app); auto stream = From(stream_id, stream_user_data); if (stream == nullptr) return NGHTTP3_ERR_CALLBACK_FAILURE; if (Http3Header::IsZeroLength(token, name, value)) return NGTCP2_SUCCESS; app->OnReceiveHeader(stream, Http3Header(app->env(), token, name, value, flags)); return NGTCP2_SUCCESS; } static int on_end_headers(nghttp3_conn* conn, int64_t stream_id, int fin, void* conn_user_data, void* stream_user_data) { NGHTTP3_CALLBACK_SCOPE(app); auto stream = From(stream_id, stream_user_data); if (stream == nullptr) return NGHTTP3_ERR_CALLBACK_FAILURE; app->OnEndHeaders(stream, fin); return NGTCP2_SUCCESS; } static int on_begin_trailers(nghttp3_conn* conn, int64_t stream_id, void* conn_user_data, void* stream_user_data) { NGHTTP3_CALLBACK_SCOPE(app); auto stream = From(stream_id, stream_user_data); if (stream == nullptr) return NGHTTP3_ERR_CALLBACK_FAILURE; app->OnBeginTrailers(stream); return NGTCP2_SUCCESS; } static int on_receive_trailer(nghttp3_conn* conn, int64_t stream_id, int32_t token, nghttp3_rcbuf* name, nghttp3_rcbuf* value, uint8_t flags, void* conn_user_data, void* stream_user_data) { NGHTTP3_CALLBACK_SCOPE(app); auto stream = From(stream_id, stream_user_data); if (stream == nullptr) return NGHTTP3_ERR_CALLBACK_FAILURE; if (Http3Header::IsZeroLength(token, name, value)) return NGTCP2_SUCCESS; app->OnReceiveTrailer(stream, Http3Header(app->env(), token, name, value, flags)); return NGTCP2_SUCCESS; } static int on_end_trailers(nghttp3_conn* conn, int64_t stream_id, int fin, void* conn_user_data, void* stream_user_data) { NGHTTP3_CALLBACK_SCOPE(app); auto stream = From(stream_id, stream_user_data); if (stream == nullptr) return NGHTTP3_ERR_CALLBACK_FAILURE; app->OnEndTrailers(stream, fin); return NGTCP2_SUCCESS; } static int on_end_stream(nghttp3_conn* conn, int64_t stream_id, void* conn_user_data, void* stream_user_data) { NGHTTP3_CALLBACK_SCOPE(app); auto stream = From(stream_id, stream_user_data); if (stream == nullptr) return NGHTTP3_ERR_CALLBACK_FAILURE; app->OnEndStream(stream); return NGTCP2_SUCCESS; } static int on_stop_sending(nghttp3_conn* conn, int64_t stream_id, uint64_t app_error_code, void* conn_user_data, void* stream_user_data) { NGHTTP3_CALLBACK_SCOPE(app); auto stream = From(stream_id, stream_user_data); if (stream == nullptr) return NGHTTP3_ERR_CALLBACK_FAILURE; app->OnStopSending(stream, app_error_code); return NGTCP2_SUCCESS; } static int on_reset_stream(nghttp3_conn* conn, int64_t stream_id, uint64_t app_error_code, void* conn_user_data, void* stream_user_data) { NGHTTP3_CALLBACK_SCOPE(app); auto stream = From(stream_id, stream_user_data); if (stream == nullptr) return NGHTTP3_ERR_CALLBACK_FAILURE; app->OnResetStream(stream, app_error_code); return NGTCP2_SUCCESS; } static int on_shutdown(nghttp3_conn* conn, int64_t id, void* conn_user_data) { NGHTTP3_CALLBACK_SCOPE(app); app->OnShutdown(); return NGTCP2_SUCCESS; } static int on_receive_settings(nghttp3_conn* conn, const nghttp3_settings* settings, void* conn_user_data) { NGHTTP3_CALLBACK_SCOPE(app); app->OnReceiveSettings(settings); return NGTCP2_SUCCESS; } static constexpr nghttp3_callbacks kCallbacks = {on_acked_stream_data, on_stream_close, on_receive_data, on_deferred_consume, on_begin_headers, on_receive_header, on_end_headers, on_begin_trailers, on_receive_trailer, on_end_trailers, on_stop_sending, on_end_stream, on_reset_stream, on_shutdown, on_receive_settings}; }; } // namespace std::unique_ptr<Session::Application> createHttp3Application( Session* session, const Session::Application_Options& options) { return std::make_unique<Http3Application>(session, options); } } // namespace quic } // namespace node #endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC