1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
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
|