1 /** @file
2 
3   Http2ConnectionState.
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 #pragma once
25 
26 #include <atomic>
27 
28 #include "NetTimeout.h"
29 
30 #include "HTTP2.h"
31 #include "HPACK.h"
32 #include "Http2Stream.h"
33 #include "Http2DependencyTree.h"
34 #include "Http2FrequencyCounter.h"
35 
36 class Http2ClientSession;
37 
38 enum class Http2SendDataFrameResult {
39   NO_ERROR = 0,
40   NO_WINDOW,
41   NO_PAYLOAD,
42   NOT_WRITE_AVAIL,
43   ERROR,
44   DONE,
45 };
46 
47 enum Http2ShutdownState { HTTP2_SHUTDOWN_NONE, HTTP2_SHUTDOWN_NOT_INITIATED, HTTP2_SHUTDOWN_INITIATED, HTTP2_SHUTDOWN_IN_PROGRESS };
48 
49 class Http2ConnectionSettings
50 {
51 public:
Http2ConnectionSettings()52   Http2ConnectionSettings()
53   {
54     // 6.5.2.  Defined SETTINGS Parameters. These should generally not be
55     // modified,
56     // only if the protocol changes should these change.
57     settings[indexof(HTTP2_SETTINGS_ENABLE_PUSH)]            = HTTP2_ENABLE_PUSH;
58     settings[indexof(HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS)] = HTTP2_MAX_CONCURRENT_STREAMS;
59     settings[indexof(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE)]    = HTTP2_INITIAL_WINDOW_SIZE;
60     settings[indexof(HTTP2_SETTINGS_MAX_FRAME_SIZE)]         = HTTP2_MAX_FRAME_SIZE;
61     settings[indexof(HTTP2_SETTINGS_HEADER_TABLE_SIZE)]      = HTTP2_HEADER_TABLE_SIZE;
62     settings[indexof(HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE)]   = HTTP2_MAX_HEADER_LIST_SIZE;
63   }
64 
65   void
settings_from_configs()66   settings_from_configs()
67   {
68     settings[indexof(HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS)] = Http2::max_concurrent_streams_in;
69     settings[indexof(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE)]    = Http2::initial_window_size;
70     settings[indexof(HTTP2_SETTINGS_MAX_FRAME_SIZE)]         = Http2::max_frame_size;
71     settings[indexof(HTTP2_SETTINGS_HEADER_TABLE_SIZE)]      = Http2::header_table_size;
72     settings[indexof(HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE)]   = Http2::max_header_list_size;
73   }
74 
75   unsigned
get(Http2SettingsIdentifier id)76   get(Http2SettingsIdentifier id) const
77   {
78     if (0 < id && id < HTTP2_SETTINGS_MAX) {
79       return this->settings[indexof(id)];
80     } else {
81       ink_assert(!"Bad Settings Identifier");
82     }
83 
84     return 0;
85   }
86 
87   unsigned
set(Http2SettingsIdentifier id,unsigned value)88   set(Http2SettingsIdentifier id, unsigned value)
89   {
90     if (0 < id && id < HTTP2_SETTINGS_MAX) {
91       return this->settings[indexof(id)] = value;
92     } else {
93       // Do nothing - 6.5.2 Unsupported parameters MUST be ignored
94     }
95 
96     return 0;
97   }
98 
99 private:
100   // Settings ID is 1-based, so convert it to a 0-based index.
101   static unsigned
indexof(Http2SettingsIdentifier id)102   indexof(Http2SettingsIdentifier id)
103   {
104     ink_assert(0 < id && id < HTTP2_SETTINGS_MAX);
105 
106     return id - 1;
107   }
108 
109   unsigned settings[HTTP2_SETTINGS_MAX - 1];
110 };
111 
112 // Http2ConnectionState
113 //
114 // Capture the semantics of a HTTP/2 connection. The client session captures the
115 // frame layer, and the
116 // connection state captures the connection-wide state.
117 
118 class Http2ConnectionState : public Continuation
119 {
120 public:
Http2ConnectionState()121   Http2ConnectionState() : stream_list() { SET_HANDLER(&Http2ConnectionState::main_event_handler); }
122 
123   ProxyError rx_error_code;
124   ProxyError tx_error_code;
125   Http2ClientSession *ua_session   = nullptr;
126   HpackHandle *local_hpack_handle  = nullptr;
127   HpackHandle *remote_hpack_handle = nullptr;
128   DependencyTree *dependency_tree  = nullptr;
129   ActivityCop<Http2Stream> _cop;
130 
131   // Settings.
132   Http2ConnectionSettings server_settings;
133   Http2ConnectionSettings client_settings;
134 
135   void
init()136   init()
137   {
138     this->_server_rwnd = Http2::initial_window_size;
139 
140     local_hpack_handle  = new HpackHandle(HTTP2_HEADER_TABLE_SIZE);
141     remote_hpack_handle = new HpackHandle(HTTP2_HEADER_TABLE_SIZE);
142     if (Http2::stream_priority_enabled) {
143       dependency_tree = new DependencyTree(Http2::max_concurrent_streams_in);
144     }
145 
146     _cop = ActivityCop<Http2Stream>(this->mutex, &stream_list, 1);
147     _cop.start();
148   }
149 
150   void
destroy()151   destroy()
152   {
153     if (in_destroy) {
154       schedule_zombie_event();
155       return;
156     }
157     in_destroy = true;
158 
159     _cop.stop();
160 
161     if (shutdown_cont_event) {
162       shutdown_cont_event->cancel();
163       shutdown_cont_event = nullptr;
164     }
165     cleanup_streams();
166 
167     delete local_hpack_handle;
168     local_hpack_handle = nullptr;
169     delete remote_hpack_handle;
170     remote_hpack_handle = nullptr;
171     delete dependency_tree;
172     dependency_tree  = nullptr;
173     this->ua_session = nullptr;
174 
175     if (fini_event) {
176       fini_event->cancel();
177     }
178     if (zombie_event) {
179       zombie_event->cancel();
180     }
181     // release the mutex after the events are cancelled and sessions are destroyed.
182     mutex = nullptr; // magic happens - assigning to nullptr frees the ProxyMutex
183   }
184 
185   // Event handlers
186   int main_event_handler(int, void *);
187   int state_closed(int, void *);
188 
189   // Stream control interfaces
190   Http2Stream *create_stream(Http2StreamId new_id, Http2Error &error);
191   Http2Stream *find_stream(Http2StreamId id) const;
192   void restart_streams();
193   bool delete_stream(Http2Stream *stream);
194   void release_stream();
195   void cleanup_streams();
196   void restart_receiving(Http2Stream *stream);
197   void update_initial_rwnd(Http2WindowSize new_size);
198 
199   Http2StreamId
get_latest_stream_id_in()200   get_latest_stream_id_in() const
201   {
202     return latest_streamid_in;
203   }
204 
205   Http2StreamId
get_latest_stream_id_out()206   get_latest_stream_id_out() const
207   {
208     return latest_streamid_out;
209   }
210 
211   int
get_stream_requests()212   get_stream_requests() const
213   {
214     return stream_requests;
215   }
216 
217   void
increment_stream_requests()218   increment_stream_requests()
219   {
220     stream_requests++;
221   }
222 
223   // Continuated header decoding
224   Http2StreamId
get_continued_stream_id()225   get_continued_stream_id() const
226   {
227     return continued_stream_id;
228   }
229   void
set_continued_stream_id(Http2StreamId stream_id)230   set_continued_stream_id(Http2StreamId stream_id)
231   {
232     continued_stream_id = stream_id;
233   }
234   void
clear_continued_stream_id()235   clear_continued_stream_id()
236   {
237     continued_stream_id = 0;
238   }
239 
240   uint32_t
get_client_stream_count()241   get_client_stream_count() const
242   {
243     return client_streams_in_count;
244   }
245 
246   void
decrement_stream_count()247   decrement_stream_count()
248   {
249     --total_client_streams_count;
250   }
251 
252   double
get_stream_error_rate()253   get_stream_error_rate() const
254   {
255     int total = get_stream_requests();
256 
257     if (total >= (1 / Http2::stream_error_rate_threshold)) {
258       return (double)stream_error_count / (double)total;
259     } else {
260       return 0;
261     }
262   }
263 
264   Http2ErrorCode
get_shutdown_reason()265   get_shutdown_reason() const
266   {
267     return shutdown_reason;
268   }
269 
270   // HTTP/2 frame sender
271   void schedule_stream(Http2Stream *stream);
272   void send_data_frames_depends_on_priority();
273   void send_data_frames(Http2Stream *stream);
274   Http2SendDataFrameResult send_a_data_frame(Http2Stream *stream, size_t &payload_length);
275   void send_headers_frame(Http2Stream *stream);
276   bool send_push_promise_frame(Http2Stream *stream, URL &url, const MIMEField *accept_encoding);
277   void send_rst_stream_frame(Http2StreamId id, Http2ErrorCode ec);
278   void send_settings_frame(const Http2ConnectionSettings &new_settings);
279   void send_ping_frame(Http2StreamId id, uint8_t flag, const uint8_t *opaque_data);
280   void send_goaway_frame(Http2StreamId id, Http2ErrorCode ec);
281   void send_window_update_frame(Http2StreamId id, uint32_t size);
282 
283   bool
is_state_closed()284   is_state_closed() const
285   {
286     return ua_session == nullptr || fini_received;
287   }
288 
289   bool
is_recursing()290   is_recursing() const
291   {
292     return recursion > 0;
293   }
294 
295   bool
is_valid_streamid(Http2StreamId id)296   is_valid_streamid(Http2StreamId id) const
297   {
298     if (http2_is_client_streamid(id)) {
299       return id <= get_latest_stream_id_in();
300     } else {
301       return id <= get_latest_stream_id_out();
302     }
303   }
304 
305   Http2ShutdownState
get_shutdown_state()306   get_shutdown_state() const
307   {
308     return shutdown_state;
309   }
310 
311   void
312   set_shutdown_state(Http2ShutdownState state, Http2ErrorCode reason = Http2ErrorCode::HTTP2_ERROR_NO_ERROR)
313   {
314     shutdown_state  = state;
315     shutdown_reason = reason;
316   }
317 
318   // noncopyable
319   Http2ConnectionState(const Http2ConnectionState &) = delete;
320   Http2ConnectionState &operator=(const Http2ConnectionState &) = delete;
321 
322   Event *
get_zombie_event()323   get_zombie_event()
324   {
325     return zombie_event;
326   }
327 
328   void
schedule_zombie_event()329   schedule_zombie_event()
330   {
331     if (Http2::zombie_timeout_in) { // If we have zombie debugging enabled
332       if (zombie_event) {
333         zombie_event->cancel();
334       }
335       zombie_event = this_ethread()->schedule_in(this, HRTIME_SECONDS(Http2::zombie_timeout_in));
336     }
337   }
338 
339   void increment_received_settings_count(uint32_t count);
340   uint32_t get_received_settings_count();
341   void increment_received_settings_frame_count();
342   uint32_t get_received_settings_frame_count();
343   void increment_received_ping_frame_count();
344   uint32_t get_received_ping_frame_count();
345   void increment_received_priority_frame_count();
346   uint32_t get_received_priority_frame_count();
347 
348   ssize_t client_rwnd() const;
349   Http2ErrorCode increment_client_rwnd(size_t amount);
350   Http2ErrorCode decrement_client_rwnd(size_t amount);
351   ssize_t server_rwnd() const;
352   Http2ErrorCode increment_server_rwnd(size_t amount);
353   Http2ErrorCode decrement_server_rwnd(size_t amount);
354 
355 private:
356   unsigned _adjust_concurrent_stream();
357 
358   // NOTE: 'stream_list' has only active streams.
359   //   If given Stream Identifier is not found in stream_list and it is less
360   //   than or equal to latest_streamid_in, the state of Stream
361   //   is CLOSED.
362   //   If given Stream Identifier is not found in stream_list and it is greater
363   //   than latest_streamid_in, the state of Stream is IDLE.
364   Queue<Http2Stream> stream_list;
365   Http2StreamId latest_streamid_in  = 0;
366   Http2StreamId latest_streamid_out = 0;
367   std::atomic<int> stream_requests  = 0;
368 
369   // Counter for current active streams which is started by client
370   std::atomic<uint32_t> client_streams_in_count = 0;
371 
372   // Counter for current active streams which is started by server
373   std::atomic<uint32_t> client_streams_out_count = 0;
374 
375   // Counter for current active streams and streams in the process of shutting down
376   std::atomic<uint32_t> total_client_streams_count = 0;
377 
378   // Counter for stream errors ATS sent
379   uint32_t stream_error_count = 0;
380 
381   // Connection level window size
382   ssize_t _client_rwnd = HTTP2_INITIAL_WINDOW_SIZE;
383   ssize_t _server_rwnd = 0;
384 
385   std::vector<size_t> _recent_rwnd_increment = {SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX};
386   int _recent_rwnd_increment_index           = 0;
387 
388   Http2FrequencyCounter _received_settings_counter;
389   Http2FrequencyCounter _received_settings_frame_counter;
390   Http2FrequencyCounter _received_ping_frame_counter;
391   Http2FrequencyCounter _received_priority_frame_counter;
392 
393   // NOTE: Id of stream which MUST receive CONTINUATION frame.
394   //   - [RFC 7540] 6.2 HEADERS
395   //     "A HEADERS frame without the END_HEADERS flag set MUST be followed by a
396   //     CONTINUATION frame for the same stream."
397   //   - [RFC 7540] 6.10 CONTINUATION
398   //     "If the END_HEADERS bit is not set, this frame MUST be followed by
399   //     another CONTINUATION frame."
400   Http2StreamId continued_stream_id = 0;
401   bool _scheduled                   = false;
402   bool fini_received                = false;
403   bool in_destroy                   = false;
404   int recursion                     = 0;
405   Http2ShutdownState shutdown_state = HTTP2_SHUTDOWN_NONE;
406   Http2ErrorCode shutdown_reason    = Http2ErrorCode::HTTP2_ERROR_MAX;
407   Event *shutdown_cont_event        = nullptr;
408   Event *fini_event                 = nullptr;
409   Event *zombie_event               = nullptr;
410 };
411