From aeb0b0728df1bbdc7c0fc7c3552c56f23c9241fb Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sun, 15 Aug 2021 21:36:43 +0900 Subject: [PATCH] nghttpx: Add QUICConnectionHandler and HTTP3Upstream skeleton --- src/Makefile.am | 2 + src/shrpx_http3_upstream.cc | 132 ++++++++++++++++++++++ src/shrpx_http3_upstream.h | 93 ++++++++++++++++ src/shrpx_quic.cc | 7 ++ src/shrpx_quic.h | 11 +- src/shrpx_quic_connection_handler.cc | 157 +++++++++++++++++++++++++++ src/shrpx_quic_connection_handler.h | 64 +++++++++++ 7 files changed, 465 insertions(+), 1 deletion(-) create mode 100644 src/shrpx_http3_upstream.cc create mode 100644 src/shrpx_http3_upstream.h create mode 100644 src/shrpx_quic_connection_handler.cc create mode 100644 src/shrpx_quic_connection_handler.h diff --git a/src/Makefile.am b/src/Makefile.am index 497f7fa4..e7ed9db1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -155,6 +155,8 @@ NGHTTPX_SRCS = \ shrpx_dns_tracker.cc shrpx_dns_tracker.h \ shrpx_quic.cc shrpx_quic.h \ shrpx_quic_listener.cc shrpx_quic_listener.h \ + shrpx_quic_connection_handler.cc shrpx_quic_connection_handler.h \ + shrpx_http3_upstream.cc shrpx_http3_upstream.h \ buffer.h memchunk.h template.h allocator.h \ xsi_strerror.c xsi_strerror.h diff --git a/src/shrpx_http3_upstream.cc b/src/shrpx_http3_upstream.cc new file mode 100644 index 00000000..6c2c8541 --- /dev/null +++ b/src/shrpx_http3_upstream.cc @@ -0,0 +1,132 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2021 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_http3_upstream.h" +#include "shrpx_client_handler.h" +#include "shrpx_downstream.h" +#include "shrpx_downstream_connection.h" +#include "shrpx_log.h" + +namespace shrpx { + +Http3Upstream::Http3Upstream(ClientHandler *handler) : handler_{handler} {} + +Http3Upstream::~Http3Upstream() {} + +int Http3Upstream::on_read() { return 0; } + +int Http3Upstream::on_write() { return 0; } + +int Http3Upstream::on_timeout(Downstream *downstream) { return 0; } + +int Http3Upstream::on_downstream_abort_request(Downstream *downstream, + unsigned int status_code) { + return 0; +} + +int Http3Upstream::on_downstream_abort_request_with_https_redirect( + Downstream *downstream) { + return 0; +} + +int Http3Upstream::downstream_read(DownstreamConnection *dconn) { return 0; } + +int Http3Upstream::downstream_write(DownstreamConnection *dconn) { return 0; } + +int Http3Upstream::downstream_eof(DownstreamConnection *dconn) { return 0; } + +int Http3Upstream::downstream_error(DownstreamConnection *dconn, int events) { + return 0; +} + +ClientHandler *Http3Upstream::get_client_handler() const { return handler_; } + +int Http3Upstream::on_downstream_header_complete(Downstream *downstream) { + return 0; +} + +int Http3Upstream::on_downstream_body(Downstream *downstream, + const uint8_t *data, size_t len, + bool flush) { + return 0; +} + +int Http3Upstream::on_downstream_body_complete(Downstream *downstream) { + return 0; +} + +void Http3Upstream::on_handler_delete() {} + +int Http3Upstream::on_downstream_reset(Downstream *downstream, bool no_retry) { + return 0; +} + +void Http3Upstream::pause_read(IOCtrlReason reason) {} + +int Http3Upstream::resume_read(IOCtrlReason reason, Downstream *downstream, + size_t consumed) { + return 0; +} + +int Http3Upstream::send_reply(Downstream *downstream, const uint8_t *body, + size_t bodylen) { + return 0; +} + +int Http3Upstream::initiate_push(Downstream *downstream, const StringRef &uri) { + return 0; +} + +int Http3Upstream::response_riovec(struct iovec *iov, int iovcnt) const { + return 0; +} + +void Http3Upstream::response_drain(size_t n) {} + +bool Http3Upstream::response_empty() const { return false; } + +Downstream * +Http3Upstream::on_downstream_push_promise(Downstream *downstream, + int32_t promised_stream_id) { + return nullptr; +} + +int Http3Upstream::on_downstream_push_promise_complete( + Downstream *downstream, Downstream *promised_downstream) { + return 0; +} + +bool Http3Upstream::push_enabled() const { return false; } + +void Http3Upstream::cancel_premature_downstream( + Downstream *promised_downstream) {} + +int Http3Upstream::on_read(const UpstreamAddr *faddr, + const Address &remote_addr, + const Address &local_addr, const uint8_t *data, + size_t datalen) { + return 0; +} + +} // namespace shrpx diff --git a/src/shrpx_http3_upstream.h b/src/shrpx_http3_upstream.h new file mode 100644 index 00000000..318d9cc0 --- /dev/null +++ b/src/shrpx_http3_upstream.h @@ -0,0 +1,93 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2021 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_HTTP3_UPSTREAM_H +#define SHRPX_HTTP3_UPSTREAM_H + +#include "shrpx.h" +#include "shrpx_upstream.h" +#include "network.h" + +using namespace nghttp2; + +namespace shrpx { + +struct UpstreamAddr; + +class Http3Upstream : public Upstream { +public: + Http3Upstream(ClientHandler *handler); + virtual ~Http3Upstream(); + + virtual int on_read(); + virtual int on_write(); + virtual int on_timeout(Downstream *downstream); + virtual int on_downstream_abort_request(Downstream *downstream, + unsigned int status_code); + virtual int + on_downstream_abort_request_with_https_redirect(Downstream *downstream); + virtual int downstream_read(DownstreamConnection *dconn); + virtual int downstream_write(DownstreamConnection *dconn); + virtual int downstream_eof(DownstreamConnection *dconn); + virtual int downstream_error(DownstreamConnection *dconn, int events); + virtual ClientHandler *get_client_handler() const; + + virtual int on_downstream_header_complete(Downstream *downstream); + virtual int on_downstream_body(Downstream *downstream, const uint8_t *data, + size_t len, bool flush); + virtual int on_downstream_body_complete(Downstream *downstream); + + virtual void on_handler_delete(); + virtual int on_downstream_reset(Downstream *downstream, bool no_retry); + + virtual void pause_read(IOCtrlReason reason); + virtual int resume_read(IOCtrlReason reason, Downstream *downstream, + size_t consumed); + virtual int send_reply(Downstream *downstream, const uint8_t *body, + size_t bodylen); + + virtual int initiate_push(Downstream *downstream, const StringRef &uri); + + virtual int response_riovec(struct iovec *iov, int iovcnt) const; + virtual void response_drain(size_t n); + virtual bool response_empty() const; + + virtual Downstream *on_downstream_push_promise(Downstream *downstream, + int32_t promised_stream_id); + virtual int + on_downstream_push_promise_complete(Downstream *downstream, + Downstream *promised_downstream); + virtual bool push_enabled() const; + virtual void cancel_premature_downstream(Downstream *promised_downstream); + + int on_read(const UpstreamAddr *faddr, const Address &remote_addr, + const Address &local_addr, const uint8_t *data, size_t datalen); + +private: + ClientHandler *handler_; +}; + +} // namespace shrpx + +#endif // SHRPX_HTTP3_UPSTREAM_H diff --git a/src/shrpx_quic.cc b/src/shrpx_quic.cc index 48b9b94d..175ffaeb 100644 --- a/src/shrpx_quic.cc +++ b/src/shrpx_quic.cc @@ -185,4 +185,11 @@ int create_quic_server_socket(UpstreamAddr &faddr) { return 0; } +int quic_send_packet(const UpstreamAddr *addr, const sockaddr *remote_sa, + size_t remote_salen, const sockaddr *local_sa, + size_t local_salen, const uint8_t *data, size_t datalen, + size_t gso_size) { + return 0; +} + } // namespace shrpx diff --git a/src/shrpx_quic.h b/src/shrpx_quic.h index 59b273e2..a575fee4 100644 --- a/src/shrpx_quic.h +++ b/src/shrpx_quic.h @@ -27,12 +27,21 @@ #include "shrpx.h" -struct UpstreamAddr; +#include namespace shrpx { +struct UpstreamAddr; + +constexpr size_t SHRPX_QUIC_SCIDLEN = 20; + int create_quic_server_socket(UpstreamAddr &addr); +int quic_send_packet(const UpstreamAddr *addr, const sockaddr *remote_sa, + size_t remote_salen, const sockaddr *local_sa, + size_t local_salen, const uint8_t *data, size_t datalen, + size_t gso_size); + } // namespace shrpx #endif // SHRPX_QUIC_H diff --git a/src/shrpx_quic_connection_handler.cc b/src/shrpx_quic_connection_handler.cc new file mode 100644 index 00000000..913da6b2 --- /dev/null +++ b/src/shrpx_quic_connection_handler.cc @@ -0,0 +1,157 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2021 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_quic_connection_handler.h" + +#include + +#include "shrpx_worker.h" +#include "shrpx_client_handler.h" +#include "shrpx_log.h" +#include "shrpx_quic.h" +#include "shrpx_http3_upstream.h" + +namespace shrpx { + +QUICConnectionHandler::QUICConnectionHandler(Worker *worker) + : worker_{worker} {} + +QUICConnectionHandler::~QUICConnectionHandler() {} + +namespace { +std::string make_cid_key(const uint8_t *dcid, size_t dcidlen) { + return std::string{dcid, dcid + dcidlen}; +} +} // namespace + +int QUICConnectionHandler::handle_packet(const UpstreamAddr *faddr, + const Address &remote_addr, + const Address &local_addr, + const uint8_t *data, size_t datalen) { + int rv; + uint32_t version; + const uint8_t *dcid, *scid; + size_t dcidlen, scidlen; + + rv = ngtcp2_pkt_decode_version_cid(&version, &dcid, &dcidlen, &scid, &scidlen, + data, datalen, SHRPX_QUIC_SCIDLEN); + if (rv != 0) { + if (rv == 1) { + send_version_negotiation(faddr, version, scid, scidlen, dcid, dcidlen, + remote_addr, local_addr); + } + + return 0; + } + + auto dcid_key = make_cid_key(dcid, dcidlen); + + auto it = connections_.find(dcid_key); + if (it == std::end(connections_)) { + // new connection + + ngtcp2_pkt_hd hd; + + switch (ngtcp2_accept(&hd, data, datalen)) { + case 0: + break; + case NGTCP2_ERR_RETRY: + // TODO Send retry + return 0; + case NGTCP2_ERR_VERSION_NEGOTIATION: + send_version_negotiation(faddr, version, scid, scidlen, dcid, dcidlen, + remote_addr, local_addr); + return 0; + default: + return 0; + } + + return 0; + } + + auto h = (*it).second.get(); + auto upstream = static_cast(h->get_upstream()); + + // TODO Delete h on failure + upstream->on_read(faddr, remote_addr, local_addr, data, datalen); + + return 0; +} + +namespace { +uint32_t generate_reserved_version(const Address &addr, uint32_t version) { + uint32_t h = 0x811C9DC5u; + const uint8_t *p = reinterpret_cast(&addr.su.sa); + const uint8_t *ep = p + addr.len; + + for (; p != ep; ++p) { + h ^= *p; + h *= 0x01000193u; + } + + version = htonl(version); + p = (const uint8_t *)&version; + ep = p + sizeof(version); + + for (; p != ep; ++p) { + h ^= *p; + h *= 0x01000193u; + } + + h &= 0xf0f0f0f0u; + h |= 0x0a0a0a0au; + + return h; +} +} // namespace + +int QUICConnectionHandler::send_version_negotiation( + const UpstreamAddr *faddr, uint32_t version, const uint8_t *dcid, + size_t dcidlen, const uint8_t *scid, size_t scidlen, + const Address &remote_addr, const Address &local_addr) { + std::array sv; + + sv[0] = generate_reserved_version(remote_addr, version); + sv[1] = NGTCP2_PROTO_VER_V1; + + std::array buf; + + uint8_t rand_byte; + util::random_bytes(&rand_byte, &rand_byte + 1, worker_->get_randgen()); + + auto nwrite = ngtcp2_pkt_write_version_negotiation( + buf.data(), buf.size(), rand_byte, dcid, dcidlen, scid, scidlen, + sv.data(), sv.size()); + if (nwrite < 0) { + LOG(ERROR) << "ngtcp2_pkt_write_version_negotiation: " + << ngtcp2_strerror(nwrite); + return -1; + } + + return quic_send_packet(faddr, &remote_addr.su.sa, remote_addr.len, + &local_addr.su.sa, local_addr.len, buf.data(), nwrite, + 0); +} + +} // namespace shrpx diff --git a/src/shrpx_quic_connection_handler.h b/src/shrpx_quic_connection_handler.h new file mode 100644 index 00000000..b580d128 --- /dev/null +++ b/src/shrpx_quic_connection_handler.h @@ -0,0 +1,64 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2021 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_QUIC_CONNECTION_HANDLER_H +#define SHRPX_QUIC_CONNECTION_HANDLER_H + +#include "shrpx.h" + +#include +#include +#include + +#include "network.h" + +using namespace nghttp2; + +namespace shrpx { + +struct UpstreamAddr; +class ClientHandler; +class Worker; + +class QUICConnectionHandler { +public: + QUICConnectionHandler(Worker *worker); + ~QUICConnectionHandler(); + int handle_packet(const UpstreamAddr *faddr, const Address &remote_addr, + const Address &local_addr, const uint8_t *data, + size_t datalen); + int send_version_negotiation(const UpstreamAddr *faddr, uint32_t version, + const uint8_t *dcid, size_t dcidlen, + const uint8_t *scid, size_t scidlen, + const Address &remote_addr, + const Address &local_addr); + +private: + Worker *worker_; + std::unordered_map> connections_; +}; + +} // namespace shrpx + +#endif // SHRPX_QUIC_CONNECTION_HANDLER_H