• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2012 Tatsuhiro Tsujikawa
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 #ifndef SHRPX_HTTP2_SESSION_H
26 #define SHRPX_HTTP2_SESSION_H
27 
28 #include "shrpx.h"
29 
30 #include <unordered_set>
31 #include <memory>
32 
33 #include "ssl_compat.h"
34 
35 #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL
36 #  include <wolfssl/options.h>
37 #  include <wolfssl/openssl/ssl.h>
38 #else // !NGHTTP2_OPENSSL_IS_WOLFSSL
39 #  include <openssl/ssl.h>
40 #endif // !NGHTTP2_OPENSSL_IS_WOLFSSL
41 
42 #include <ev.h>
43 
44 #include <nghttp2/nghttp2.h>
45 
46 #include "llhttp.h"
47 
48 #include "shrpx_connection.h"
49 #include "buffer.h"
50 #include "template.h"
51 
52 using namespace nghttp2;
53 
54 namespace shrpx {
55 
56 class Http2DownstreamConnection;
57 class Worker;
58 class Downstream;
59 struct DownstreamAddrGroup;
60 struct DownstreamAddr;
61 struct DNSQuery;
62 
63 struct StreamData {
64   StreamData *dlnext, *dlprev;
65   Http2DownstreamConnection *dconn;
66 };
67 
68 enum class FreelistZone {
69   // Http2Session object is not linked in any freelist.
70   NONE,
71   // Http2Session object is linked in address scope
72   // http2_extra_freelist.
73   EXTRA,
74   // Http2Session object is about to be deleted, and it does not
75   // belong to any linked list.
76   GONE
77 };
78 
79 enum class Http2SessionState {
80   // Disconnected
81   DISCONNECTED,
82   // Connecting proxy and making CONNECT request
83   PROXY_CONNECTING,
84   // Tunnel is established with proxy
85   PROXY_CONNECTED,
86   // Establishing tunnel is failed
87   PROXY_FAILED,
88   // Connecting to downstream and/or performing SSL/TLS handshake
89   CONNECTING,
90   // Connected to downstream
91   CONNECTED,
92   // Connection is started to fail
93   CONNECT_FAILING,
94   // Resolving host name
95   RESOLVING_NAME,
96 };
97 
98 enum class ConnectionCheck {
99   // Connection checking is not required
100   NONE,
101   // Connection checking is required
102   REQUIRED,
103   // Connection checking has been started
104   STARTED,
105 };
106 
107 class Http2Session {
108 public:
109   Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker,
110                const std::shared_ptr<DownstreamAddrGroup> &group,
111                DownstreamAddr *addr);
112   ~Http2Session();
113 
114   // If hard is true, all pending requests are abandoned and
115   // associated ClientHandlers will be deleted.
116   int disconnect(bool hard = false);
117   int initiate_connection();
118   int resolve_name();
119 
120   void add_downstream_connection(Http2DownstreamConnection *dconn);
121   void remove_downstream_connection(Http2DownstreamConnection *dconn);
122 
123   void remove_stream_data(StreamData *sd);
124 
125   int submit_request(Http2DownstreamConnection *dconn, const nghttp2_nv *nva,
126                      size_t nvlen, const nghttp2_data_provider2 *data_prd);
127 
128   int submit_rst_stream(int32_t stream_id, uint32_t error_code);
129 
130   int terminate_session(uint32_t error_code);
131 
132   nghttp2_session *get_session() const;
133 
134   int resume_data(Http2DownstreamConnection *dconn);
135 
136   int connection_made();
137 
138   int do_read();
139   int do_write();
140 
141   int on_read(const uint8_t *data, size_t datalen);
142   int on_write();
143 
144   int connected();
145   int read_clear();
146   int write_clear();
147   int tls_handshake();
148   int read_tls();
149   int write_tls();
150   // This is a special write function which just stop write event
151   // watcher.
152   int write_void();
153 
154   int downstream_read_proxy(const uint8_t *data, size_t datalen);
155   int downstream_connect_proxy();
156 
157   int downstream_read(const uint8_t *data, size_t datalen);
158   int downstream_write();
159 
160   int noop();
161   int read_noop(const uint8_t *data, size_t datalen);
162   int write_noop();
163 
164   void signal_write();
165 
166   struct ev_loop *get_loop() const;
167 
168   ev_io *get_wev();
169 
170   Http2SessionState get_state() const;
171   void set_state(Http2SessionState state);
172 
173   void start_settings_timer();
174   void stop_settings_timer();
175 
176   SSL *get_ssl() const;
177 
178   int consume(int32_t stream_id, size_t len);
179 
180   // Returns true if request can be issued on downstream connection.
181   bool can_push_request(const Downstream *downstream) const;
182   // Initiates the connection checking if downstream connection has
183   // been established and connection checking is required.
184   void start_checking_connection();
185   // Resets connection check timer to timeout |t|.  After timeout, we
186   // require connection checking.  If connection checking is already
187   // enabled, this timeout is for PING ACK timeout.
188   void reset_connection_check_timer(ev_tstamp t);
189   void reset_connection_check_timer_if_not_checking();
190   // Signals that connection is alive.  Internally
191   // reset_connection_check_timer() is called.
192   void connection_alive();
193   // Change connection check state.
194   void set_connection_check_state(ConnectionCheck state);
195   ConnectionCheck get_connection_check_state() const;
196 
197   bool should_hard_fail() const;
198 
199   void submit_pending_requests();
200 
201   DownstreamAddr *get_addr() const;
202 
203   const std::shared_ptr<DownstreamAddrGroup> &get_downstream_addr_group() const;
204 
205   int handle_downstream_push_promise(Downstream *downstream,
206                                      int32_t promised_stream_id);
207   int handle_downstream_push_promise_complete(Downstream *downstream,
208                                               Downstream *promised_downstream);
209 
210   // Returns number of downstream connections, including pushed
211   // streams.
212   size_t get_num_dconns() const;
213 
214   // Adds to group scope http2_avail_freelist.
215   void add_to_avail_freelist();
216   // Adds to address scope http2_extra_freelist.
217   void add_to_extra_freelist();
218 
219   // Removes this object from any freelist.  If this object is not
220   // linked from any freelist, this function does nothing.
221   void remove_from_freelist();
222 
223   // Removes this object form any freelist, and marks this object as
224   // not schedulable.
225   void exclude_from_scheduling();
226 
227   // Returns true if the maximum concurrency is reached.  In other
228   // words, the number of currently participated streams in this
229   // session is equal or greater than the max concurrent streams limit
230   // advertised by server.  If |extra| is nonzero, it is added to the
231   // number of current concurrent streams when comparing against
232   // server initiated concurrency limit.
233   bool max_concurrency_reached(size_t extra = 0) const;
234 
235   DefaultMemchunks *get_request_buf();
236 
237   void on_timeout();
238 
239   // This is called periodically using ev_prepare watcher, and if
240   // group_ is retired (backend has been replaced), send GOAWAY to
241   // shutdown the connection.
242   void check_retire();
243 
244   // Returns address used to connect to backend.  Could be nullptr.
245   const Address *get_raddr() const;
246 
247   // This is called when SETTINGS frame without ACK flag set is
248   // received.
249   void on_settings_received(const nghttp2_frame *frame);
250 
251   bool get_allow_connect_proto() const;
252 
253   using ReadBuf = Buffer<8_k>;
254 
255   Http2Session *dlnext, *dlprev;
256 
257 private:
258   Connection conn_;
259   DefaultMemchunks wb_;
260   ev_timer settings_timer_;
261   // This timer has 2 purpose: when it first timeout, set
262   // connection_check_state_ = ConnectionCheck::REQUIRED.  After
263   // connection check has started, this timer is started again and
264   // traps PING ACK timeout.
265   ev_timer connchk_timer_;
266   // timer to initiate connection.  usually, this fires immediately.
267   ev_timer initiate_connection_timer_;
268   ev_prepare prep_;
269   DList<Http2DownstreamConnection> dconns_;
270   DList<StreamData> streams_;
271   std::function<int(Http2Session &)> read_, write_;
272   std::function<int(Http2Session &, const uint8_t *, size_t)> on_read_;
273   std::function<int(Http2Session &)> on_write_;
274   // Used to parse the response from HTTP proxy
275   std::unique_ptr<llhttp_t> proxy_htp_;
276   Worker *worker_;
277   // NULL if no TLS is configured
278   SSL_CTX *ssl_ctx_;
279   std::shared_ptr<DownstreamAddrGroup> group_;
280   // Address of remote endpoint
281   DownstreamAddr *addr_;
282   nghttp2_session *session_;
283   // Actual remote address used to contact backend.  This is initially
284   // nullptr, and may point to either &addr_->addr,
285   // resolved_addr_.get(), or HTTP proxy's address structure.
286   const Address *raddr_;
287   // Resolved IP address if dns parameter is used
288   std::unique_ptr<Address> resolved_addr_;
289   std::unique_ptr<DNSQuery> dns_query_;
290   Http2SessionState state_;
291   ConnectionCheck connection_check_state_;
292   FreelistZone freelist_zone_;
293   // true if SETTINGS without ACK is received from peer.
294   bool settings_recved_;
295   // true if peer enables RFC 8441 CONNECT protocol.
296   bool allow_connect_proto_;
297 };
298 
299 nghttp2_session_callbacks *create_http2_downstream_callbacks();
300 
301 } // namespace shrpx
302 
303 #endif // SHRPX_HTTP2_SESSION_H
304