diff options
Diffstat (limited to '')
-rw-r--r-- | src/shrpx_http2_session.h | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/src/shrpx_http2_session.h b/src/shrpx_http2_session.h new file mode 100644 index 0000000..31b2545 --- /dev/null +++ b/src/shrpx_http2_session.h @@ -0,0 +1,296 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 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_HTTP2_SESSION_H +#define SHRPX_HTTP2_SESSION_H + +#include "shrpx.h" + +#include <unordered_set> +#include <memory> + +#include <openssl/ssl.h> + +#include <ev.h> + +#include <nghttp2/nghttp2.h> + +#include "llhttp.h" + +#include "shrpx_connection.h" +#include "buffer.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +class Http2DownstreamConnection; +class Worker; +class Downstream; +struct DownstreamAddrGroup; +struct DownstreamAddr; +struct DNSQuery; + +struct StreamData { + StreamData *dlnext, *dlprev; + Http2DownstreamConnection *dconn; +}; + +enum class FreelistZone { + // Http2Session object is not linked in any freelist. + NONE, + // Http2Session object is linked in address scope + // http2_extra_freelist. + EXTRA, + // Http2Session object is about to be deleted, and it does not + // belong to any linked list. + GONE +}; + +enum class Http2SessionState { + // Disconnected + DISCONNECTED, + // Connecting proxy and making CONNECT request + PROXY_CONNECTING, + // Tunnel is established with proxy + PROXY_CONNECTED, + // Establishing tunnel is failed + PROXY_FAILED, + // Connecting to downstream and/or performing SSL/TLS handshake + CONNECTING, + // Connected to downstream + CONNECTED, + // Connection is started to fail + CONNECT_FAILING, + // Resolving host name + RESOLVING_NAME, +}; + +enum class ConnectionCheck { + // Connection checking is not required + NONE, + // Connection checking is required + REQUIRED, + // Connection checking has been started + STARTED, +}; + +class Http2Session { +public: + Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker, + const std::shared_ptr<DownstreamAddrGroup> &group, + DownstreamAddr *addr); + ~Http2Session(); + + // If hard is true, all pending requests are abandoned and + // associated ClientHandlers will be deleted. + int disconnect(bool hard = false); + int initiate_connection(); + int resolve_name(); + + void add_downstream_connection(Http2DownstreamConnection *dconn); + void remove_downstream_connection(Http2DownstreamConnection *dconn); + + void remove_stream_data(StreamData *sd); + + int submit_request(Http2DownstreamConnection *dconn, const nghttp2_nv *nva, + size_t nvlen, const nghttp2_data_provider *data_prd); + + int submit_rst_stream(int32_t stream_id, uint32_t error_code); + + int terminate_session(uint32_t error_code); + + nghttp2_session *get_session() const; + + int resume_data(Http2DownstreamConnection *dconn); + + int connection_made(); + + int do_read(); + int do_write(); + + int on_read(const uint8_t *data, size_t datalen); + int on_write(); + + int connected(); + int read_clear(); + int write_clear(); + int tls_handshake(); + int read_tls(); + int write_tls(); + // This is a special write function which just stop write event + // watcher. + int write_void(); + + int downstream_read_proxy(const uint8_t *data, size_t datalen); + int downstream_connect_proxy(); + + int downstream_read(const uint8_t *data, size_t datalen); + int downstream_write(); + + int noop(); + int read_noop(const uint8_t *data, size_t datalen); + int write_noop(); + + void signal_write(); + + struct ev_loop *get_loop() const; + + ev_io *get_wev(); + + Http2SessionState get_state() const; + void set_state(Http2SessionState state); + + void start_settings_timer(); + void stop_settings_timer(); + + SSL *get_ssl() const; + + int consume(int32_t stream_id, size_t len); + + // Returns true if request can be issued on downstream connection. + bool can_push_request(const Downstream *downstream) const; + // Initiates the connection checking if downstream connection has + // been established and connection checking is required. + void start_checking_connection(); + // Resets connection check timer to timeout |t|. After timeout, we + // require connection checking. If connection checking is already + // enabled, this timeout is for PING ACK timeout. + void reset_connection_check_timer(ev_tstamp t); + void reset_connection_check_timer_if_not_checking(); + // Signals that connection is alive. Internally + // reset_connection_check_timer() is called. + void connection_alive(); + // Change connection check state. + void set_connection_check_state(ConnectionCheck state); + ConnectionCheck get_connection_check_state() const; + + bool should_hard_fail() const; + + void submit_pending_requests(); + + DownstreamAddr *get_addr() const; + + const std::shared_ptr<DownstreamAddrGroup> &get_downstream_addr_group() const; + + int handle_downstream_push_promise(Downstream *downstream, + int32_t promised_stream_id); + int handle_downstream_push_promise_complete(Downstream *downstream, + Downstream *promised_downstream); + + // Returns number of downstream connections, including pushed + // streams. + size_t get_num_dconns() const; + + // Adds to group scope http2_avail_freelist. + void add_to_avail_freelist(); + // Adds to address scope http2_extra_freelist. + void add_to_extra_freelist(); + + // Removes this object from any freelist. If this object is not + // linked from any freelist, this function does nothing. + void remove_from_freelist(); + + // Removes this object form any freelist, and marks this object as + // not schedulable. + void exclude_from_scheduling(); + + // Returns true if the maximum concurrency is reached. In other + // words, the number of currently participated streams in this + // session is equal or greater than the max concurrent streams limit + // advertised by server. If |extra| is nonzero, it is added to the + // number of current concurrent streams when comparing against + // server initiated concurrency limit. + bool max_concurrency_reached(size_t extra = 0) const; + + DefaultMemchunks *get_request_buf(); + + void on_timeout(); + + // This is called periodically using ev_prepare watcher, and if + // group_ is retired (backend has been replaced), send GOAWAY to + // shutdown the connection. + void check_retire(); + + // Returns address used to connect to backend. Could be nullptr. + const Address *get_raddr() const; + + // This is called when SETTINGS frame without ACK flag set is + // received. + void on_settings_received(const nghttp2_frame *frame); + + bool get_allow_connect_proto() const; + + using ReadBuf = Buffer<8_k>; + + Http2Session *dlnext, *dlprev; + +private: + Connection conn_; + DefaultMemchunks wb_; + ev_timer settings_timer_; + // This timer has 2 purpose: when it first timeout, set + // connection_check_state_ = ConnectionCheck::REQUIRED. After + // connection check has started, this timer is started again and + // traps PING ACK timeout. + ev_timer connchk_timer_; + // timer to initiate connection. usually, this fires immediately. + ev_timer initiate_connection_timer_; + ev_prepare prep_; + DList<Http2DownstreamConnection> dconns_; + DList<StreamData> streams_; + std::function<int(Http2Session &)> read_, write_; + std::function<int(Http2Session &, const uint8_t *, size_t)> on_read_; + std::function<int(Http2Session &)> on_write_; + // Used to parse the response from HTTP proxy + std::unique_ptr<llhttp_t> proxy_htp_; + Worker *worker_; + // NULL if no TLS is configured + SSL_CTX *ssl_ctx_; + std::shared_ptr<DownstreamAddrGroup> group_; + // Address of remote endpoint + DownstreamAddr *addr_; + nghttp2_session *session_; + // Actual remote address used to contact backend. This is initially + // nullptr, and may point to either &addr_->addr, + // resolved_addr_.get(), or HTTP proxy's address structure. + const Address *raddr_; + // Resolved IP address if dns parameter is used + std::unique_ptr<Address> resolved_addr_; + std::unique_ptr<DNSQuery> dns_query_; + Http2SessionState state_; + ConnectionCheck connection_check_state_; + FreelistZone freelist_zone_; + // true if SETTINGS without ACK is received from peer. + bool settings_recved_; + // true if peer enables RFC 8441 CONNECT protocol. + bool allow_connect_proto_; +}; + +nghttp2_session_callbacks *create_http2_downstream_callbacks(); + +} // namespace shrpx + +#endif // SHRPX_HTTP2_SESSION_H |