From 544882ca0ecd2203d4be3c4a6e20743b642eca3b Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Mon, 16 Aug 2021 22:45:36 +0900 Subject: [PATCH] nghttpx: Add HTTP3 skeleton and minor SSL_CTX fix --- src/shrpx_connection_handler.cc | 3 +- src/shrpx_http3_upstream.cc | 164 +++++++++++++++++++++++++++++++- src/shrpx_http3_upstream.h | 4 + src/shrpx_tls.cc | 7 +- 4 files changed, 174 insertions(+), 4 deletions(-) diff --git a/src/shrpx_connection_handler.cc b/src/shrpx_connection_handler.cc index ddeb51ec..03f74594 100644 --- a/src/shrpx_connection_handler.cc +++ b/src/shrpx_connection_handler.cc @@ -345,7 +345,8 @@ int ConnectionHandler::create_worker_thread(size_t num) { return -1; } # endif // HAVE_MRUBY - if (worker->setup_quic_server_socket() != 0) { + if ((!apiconf.enabled || i != 0) && + worker->setup_quic_server_socket() != 0) { return -1; } diff --git a/src/shrpx_http3_upstream.cc b/src/shrpx_http3_upstream.cc index aafe45e1..33e62a3e 100644 --- a/src/shrpx_http3_upstream.cc +++ b/src/shrpx_http3_upstream.cc @@ -72,7 +72,7 @@ fail: } // namespace Http3Upstream::Http3Upstream(ClientHandler *handler) - : handler_{handler}, conn_{nullptr}, tls_alert_{0} { + : handler_{handler}, conn_{nullptr}, tls_alert_{0}, httpconn_{nullptr} { ev_timer_init(&timer_, timeoutcb, 0., 0.); timer_.data = this; @@ -89,6 +89,8 @@ Http3Upstream::~Http3Upstream() { ev_timer_stop(loop, &idle_timer_); ev_timer_stop(loop, &timer_); + nghttp3_conn_del(httpconn_); + if (conn_) { auto worker = handler_->get_worker(); auto quic_client_handler = worker->get_quic_connection_handler(); @@ -594,6 +596,10 @@ int Http3Upstream::on_tx_secret(ngtcp2_crypto_level level, return -1; } + if (level == NGTCP2_CRYPTO_LEVEL_APPLICATION && setup_httpconn() != 0) { + return -1; + } + return 0; } @@ -648,4 +654,160 @@ void Http3Upstream::reset_timer() { ev_timer_again(handler_->get_loop(), &timer_); } +namespace { +int http_deferred_consume(nghttp3_conn *conn, int64_t stream_id, + size_t nsoncumed, void *user_data, + void *stream_user_data) { + return 0; +} +} // namespace + +namespace { +int http_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, + size_t datalen, void *user_data, + void *stream_user_data) { + return 0; +} +} // namespace + +namespace { +int http_begin_request_headers(nghttp3_conn *conn, int64_t stream_id, + void *user_data, void *stream_user_data) { + return 0; +} +} // namespace + +namespace { +int http_recv_request_header(nghttp3_conn *conn, int64_t stream_id, + int32_t token, nghttp3_rcbuf *name, + nghttp3_rcbuf *value, uint8_t flags, + void *user_data, void *stream_user_data) { + return 0; +} +} // namespace + +namespace { +int http_end_request_headers(nghttp3_conn *conn, int64_t stream_id, + void *user_data, void *stream_user_data) { + return 0; +} +} // namespace + +namespace { +int http_recv_data(nghttp3_conn *conn, int64_t stream_id, const uint8_t *data, + size_t datalen, void *user_data, void *stream_user_data) { + return 0; +} +} // namespace + +namespace { +int http_end_stream(nghttp3_conn *conn, int64_t stream_id, void *user_data, + void *stream_user_data) { + return 0; +} +} // namespace + +namespace { +int http_stream_close(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *conn_user_data, + void *stream_user_data) { + return 0; +} +} // namespace + +namespace { +int http_send_stop_sending(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) { + return 0; +} +} // namespace + +namespace { +int http_reset_stream(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) { + return 0; +} +} // namespace + +int Http3Upstream::setup_httpconn() { + int rv; + + if (ngtcp2_conn_get_max_local_streams_uni(conn_) < 3) { + return -1; + } + + nghttp3_callbacks callbacks{ + http_acked_stream_data, + http_stream_close, + http_recv_data, + http_deferred_consume, + http_begin_request_headers, + http_recv_request_header, + http_end_request_headers, + nullptr, // begin_trailers + nullptr, // recv_trailer + nullptr, // end_trailers + http_send_stop_sending, + http_end_stream, + http_reset_stream, + }; + + nghttp3_settings settings; + nghttp3_settings_default(&settings); + settings.qpack_max_table_capacity = 4_k; + + auto mem = nghttp3_mem_default(); + + rv = nghttp3_conn_server_new(&httpconn_, &callbacks, &settings, mem, this); + if (rv != 0) { + LOG(ERROR) << "nghttp3_conn_server_new: " << nghttp3_strerror(rv); + return -1; + } + + ngtcp2_transport_params params; + ngtcp2_conn_get_local_transport_params(conn_, ¶ms); + + nghttp3_conn_set_max_client_streams_bidi(httpconn_, + params.initial_max_streams_bidi); + + int64_t ctrl_stream_id; + + rv = ngtcp2_conn_open_uni_stream(conn_, &ctrl_stream_id, nullptr); + if (rv != 0) { + LOG(ERROR) << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv); + return -1; + } + + rv = nghttp3_conn_bind_control_stream(httpconn_, ctrl_stream_id); + if (rv != 0) { + LOG(ERROR) << "nghttp3_conn_bind_control_stream: " << nghttp3_strerror(rv); + return -1; + } + + int64_t qpack_enc_stream_id, qpack_dec_stream_id; + + rv = ngtcp2_conn_open_uni_stream(conn_, &qpack_enc_stream_id, nullptr); + if (rv != 0) { + LOG(ERROR) << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv); + return -1; + } + + rv = ngtcp2_conn_open_uni_stream(conn_, &qpack_dec_stream_id, nullptr); + if (rv != 0) { + LOG(ERROR) << "ngtcp2_conn_open_uni_stream: " << ngtcp2_strerror(rv); + return -1; + } + + rv = nghttp3_conn_bind_qpack_streams(httpconn_, qpack_enc_stream_id, + qpack_dec_stream_id); + if (rv != 0) { + LOG(ERROR) << "nghttp3_conn_bind_qpack_streams: " << nghttp3_strerror(rv); + return -1; + } + + return 0; +} + } // namespace shrpx diff --git a/src/shrpx_http3_upstream.h b/src/shrpx_http3_upstream.h index 5e94bf65..cf8fa9b4 100644 --- a/src/shrpx_http3_upstream.h +++ b/src/shrpx_http3_upstream.h @@ -28,6 +28,7 @@ #include "shrpx.h" #include +#include #include "shrpx_upstream.h" #include "quic.h" @@ -109,6 +110,8 @@ public: void reset_idle_timer(); void reset_timer(); + int setup_httpconn(); + private: ClientHandler *handler_; ev_timer timer_; @@ -117,6 +120,7 @@ private: ngtcp2_conn *conn_; quic::Error last_error_; uint8_t tls_alert_; + nghttp3_conn *httpconn_; }; } // namespace shrpx diff --git a/src/shrpx_tls.cc b/src/shrpx_tls.cc index e6d7fabf..79104d54 100644 --- a/src/shrpx_tls.cc +++ b/src/shrpx_tls.cc @@ -185,7 +185,8 @@ int servername_callback(SSL *ssl, int *al, void *arg) { auto hostname = StringRef{std::begin(buf), end_buf}; - auto cert_tree = worker->get_cert_lookup_tree(); + auto cert_tree = SSL_is_quic(ssl) ? worker->get_quic_cert_lookup_tree() + : worker->get_cert_lookup_tree(); auto idx = cert_tree->lookup(hostname); if (idx == -1) { @@ -196,7 +197,9 @@ int servername_callback(SSL *ssl, int *al, void *arg) { auto conn_handler = worker->get_connection_handler(); - const auto &ssl_ctx_list = conn_handler->get_indexed_ssl_ctx(idx); + const auto &ssl_ctx_list = SSL_is_quic(ssl) + ? conn_handler->get_quic_indexed_ssl_ctx(idx) + : conn_handler->get_indexed_ssl_ctx(idx); assert(!ssl_ctx_list.empty()); #if !defined(OPENSSL_IS_BORINGSSL) && !LIBRESSL_IN_USE && \