/src/trafficserver/include/proxy/http2/HTTP2.h
Line | Count | Source |
1 | | /** @file |
2 | | * |
3 | | * Fundamental HTTP/2 protocol definitions and parsers. |
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 "tscore/ink_defs.h" |
27 | | #include "tscore/ink_memory.h" |
28 | | #include "proxy/http2/HPACK.h" |
29 | | #include "proxy/hdrs/MIME.h" |
30 | | #include "records/RecDefs.h" |
31 | | |
32 | | #include "tsutil/Metrics.h" |
33 | | |
34 | | using ts::Metrics; |
35 | | |
36 | | class HTTPHdr; |
37 | | |
38 | | // [RFC 9113] 5.1.1 Stream identifiers. |
39 | | using Http2StreamId = uint32_t; |
40 | | |
41 | | constexpr Http2StreamId HTTP2_CONNECTION_CONTROL_STREAM = 0; |
42 | | constexpr uint8_t HTTP2_FRAME_NO_FLAG = 0; |
43 | | |
44 | | // [RFC 7540] 6.9.2. Initial Flow Control Window Size |
45 | | // the flow control window can be come negative so we need to track it with a signed type. |
46 | | using Http2WindowSize = int32_t; |
47 | | |
48 | | extern const char *const HTTP2_CONNECTION_PREFACE; |
49 | | const size_t HTTP2_CONNECTION_PREFACE_LEN = 24; |
50 | | |
51 | | const size_t HTTP2_FRAME_HEADER_LEN = 9; |
52 | | const size_t HTTP2_DATA_PADLEN_LEN = 1; |
53 | | const size_t HTTP2_HEADERS_PADLEN_LEN = 1; |
54 | | const size_t HTTP2_PRIORITY_LEN = 5; |
55 | | const size_t HTTP2_RST_STREAM_LEN = 4; |
56 | | const size_t HTTP2_PING_LEN = 8; |
57 | | const size_t HTTP2_GOAWAY_LEN = 8; |
58 | | const size_t HTTP2_WINDOW_UPDATE_LEN = 4; |
59 | | const size_t HTTP2_SETTINGS_PARAMETER_LEN = 6; |
60 | | |
61 | | // SETTINGS initial values. NOTE: These should not be modified |
62 | | // unless the protocol changes! Do not change this thinking you |
63 | | // are changing server defaults. that is done via RecordsConfig.cc |
64 | | const uint32_t HTTP2_ENABLE_PUSH = 1; |
65 | | const uint32_t HTTP2_MAX_CONCURRENT_STREAMS = UINT_MAX; |
66 | | const uint32_t HTTP2_INITIAL_WINDOW_SIZE = 65535; |
67 | | const uint32_t HTTP2_MAX_FRAME_SIZE = 16384; |
68 | | const uint32_t HTTP2_HEADER_TABLE_SIZE = 4096; |
69 | | const uint32_t HTTP2_MAX_HEADER_LIST_SIZE = UINT_MAX; |
70 | | const uint32_t HTTP2_MAX_BUFFER_USAGE = 524288; |
71 | | |
72 | | // [RFC 7540] 5.3.5 Default Priorities |
73 | | // The RFC says weight value is 1 to 256, but the value in TS is between 0 to 255 |
74 | | // to use uint8_t. So the default weight is 16 minus 1. |
75 | | const uint32_t HTTP2_PRIORITY_DEFAULT_STREAM_DEPENDENCY = 0; |
76 | | const uint8_t HTTP2_PRIORITY_DEFAULT_WEIGHT = 15; |
77 | | |
78 | | // Statistics |
79 | | struct Http2StatsBlock { |
80 | | Metrics::Gauge::AtomicType *current_client_session_count; |
81 | | Metrics::Gauge::AtomicType *current_server_session_count; |
82 | | Metrics::Gauge::AtomicType *current_active_client_connection_count; |
83 | | Metrics::Gauge::AtomicType *current_active_server_connection_count; |
84 | | Metrics::Gauge::AtomicType *current_client_stream_count; |
85 | | Metrics::Gauge::AtomicType *current_server_stream_count; |
86 | | Metrics::Counter::AtomicType *total_client_stream_count; |
87 | | Metrics::Counter::AtomicType *total_server_stream_count; |
88 | | Metrics::Counter::AtomicType *total_transactions_time; |
89 | | Metrics::Counter::AtomicType *total_client_connection_count; |
90 | | Metrics::Counter::AtomicType *total_server_connection_count; |
91 | | Metrics::Counter::AtomicType *stream_errors_count; |
92 | | Metrics::Counter::AtomicType *connection_errors_count; |
93 | | Metrics::Counter::AtomicType *session_die_default; |
94 | | Metrics::Counter::AtomicType *session_die_other; |
95 | | Metrics::Counter::AtomicType *session_die_active; |
96 | | Metrics::Counter::AtomicType *session_die_inactive; |
97 | | Metrics::Counter::AtomicType *session_die_eos; |
98 | | Metrics::Counter::AtomicType *session_die_error; |
99 | | Metrics::Counter::AtomicType *session_die_high_error_rate; |
100 | | Metrics::Counter::AtomicType *max_settings_per_frame_exceeded; |
101 | | Metrics::Counter::AtomicType *max_settings_per_minute_exceeded; |
102 | | Metrics::Counter::AtomicType *max_settings_frames_per_minute_exceeded; |
103 | | Metrics::Counter::AtomicType *max_ping_frames_per_minute_exceeded; |
104 | | Metrics::Counter::AtomicType *max_priority_frames_per_minute_exceeded; |
105 | | Metrics::Counter::AtomicType *max_rst_stream_frames_per_minute_exceeded; |
106 | | Metrics::Counter::AtomicType *max_continuation_frames_per_minute_exceeded; |
107 | | Metrics::Counter::AtomicType *max_empty_frames_per_minute_exceeded; |
108 | | Metrics::Counter::AtomicType *insufficient_avg_window_update; |
109 | | Metrics::Counter::AtomicType *max_concurrent_streams_exceeded_in; |
110 | | Metrics::Counter::AtomicType *max_concurrent_streams_exceeded_out; |
111 | | Metrics::Counter::AtomicType *data_frames_in; |
112 | | Metrics::Counter::AtomicType *headers_frames_in; |
113 | | Metrics::Counter::AtomicType *priority_frames_in; |
114 | | Metrics::Counter::AtomicType *rst_stream_frames_in; |
115 | | Metrics::Counter::AtomicType *settings_frames_in; |
116 | | Metrics::Counter::AtomicType *push_promise_frames_in; |
117 | | Metrics::Counter::AtomicType *ping_frames_in; |
118 | | Metrics::Counter::AtomicType *goaway_frames_in; |
119 | | Metrics::Counter::AtomicType *window_update_frames_in; |
120 | | Metrics::Counter::AtomicType *continuation_frames_in; |
121 | | Metrics::Counter::AtomicType *unknown_frames_in; |
122 | | }; |
123 | | |
124 | | extern Http2StatsBlock http2_rsb; |
125 | | |
126 | | // [RFC 7540] 6.9.1. The Flow Control Window |
127 | | static const Http2WindowSize HTTP2_MAX_WINDOW_SIZE = 0x7FFFFFFF; |
128 | | |
129 | | // [RFC 7540] 5.4. Error Handling |
130 | | enum class Http2ErrorClass { |
131 | | HTTP2_ERROR_CLASS_NONE, |
132 | | HTTP2_ERROR_CLASS_CONNECTION, |
133 | | HTTP2_ERROR_CLASS_STREAM, |
134 | | }; |
135 | | |
136 | | // [RFC 7540] 7. Error Codes |
137 | | enum class Http2ErrorCode { |
138 | | HTTP2_ERROR_NO_ERROR = 0, |
139 | | HTTP2_ERROR_PROTOCOL_ERROR = 1, |
140 | | HTTP2_ERROR_INTERNAL_ERROR = 2, |
141 | | HTTP2_ERROR_FLOW_CONTROL_ERROR = 3, |
142 | | HTTP2_ERROR_SETTINGS_TIMEOUT = 4, |
143 | | HTTP2_ERROR_STREAM_CLOSED = 5, |
144 | | HTTP2_ERROR_FRAME_SIZE_ERROR = 6, |
145 | | HTTP2_ERROR_REFUSED_STREAM = 7, |
146 | | HTTP2_ERROR_CANCEL = 8, |
147 | | HTTP2_ERROR_COMPRESSION_ERROR = 9, |
148 | | HTTP2_ERROR_CONNECT_ERROR = 10, |
149 | | HTTP2_ERROR_ENHANCE_YOUR_CALM = 11, |
150 | | HTTP2_ERROR_INADEQUATE_SECURITY = 12, |
151 | | HTTP2_ERROR_HTTP_1_1_REQUIRED = 13, |
152 | | |
153 | | HTTP2_ERROR_MAX, |
154 | | }; |
155 | | |
156 | | // [RFC 7540] 5.1. Stream States |
157 | | enum class Http2StreamState { |
158 | | HTTP2_STREAM_STATE_IDLE, |
159 | | HTTP2_STREAM_STATE_RESERVED_LOCAL, |
160 | | HTTP2_STREAM_STATE_RESERVED_REMOTE, |
161 | | HTTP2_STREAM_STATE_OPEN, |
162 | | HTTP2_STREAM_STATE_HALF_CLOSED_LOCAL, |
163 | | HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE, |
164 | | HTTP2_STREAM_STATE_CLOSED |
165 | | }; |
166 | | |
167 | | enum Http2FrameType { |
168 | | HTTP2_FRAME_TYPE_DATA = 0, |
169 | | HTTP2_FRAME_TYPE_HEADERS = 1, |
170 | | HTTP2_FRAME_TYPE_PRIORITY = 2, |
171 | | HTTP2_FRAME_TYPE_RST_STREAM = 3, |
172 | | HTTP2_FRAME_TYPE_SETTINGS = 4, |
173 | | HTTP2_FRAME_TYPE_PUSH_PROMISE = 5, |
174 | | HTTP2_FRAME_TYPE_PING = 6, |
175 | | HTTP2_FRAME_TYPE_GOAWAY = 7, |
176 | | HTTP2_FRAME_TYPE_WINDOW_UPDATE = 8, |
177 | | HTTP2_FRAME_TYPE_CONTINUATION = 9, |
178 | | |
179 | | HTTP2_FRAME_TYPE_MAX, |
180 | | }; |
181 | | |
182 | | extern Metrics::Counter::AtomicType *http2_frame_metrics_in[HTTP2_FRAME_TYPE_MAX + 1]; |
183 | | |
184 | | // [RFC 7540] 6.1. Data |
185 | | enum Http2FrameFlagsData { |
186 | | HTTP2_FLAGS_DATA_END_STREAM = 0x01, |
187 | | HTTP2_FLAGS_DATA_PADDED = 0x08, |
188 | | |
189 | | HTTP2_FLAGS_DATA_MASK = 0x09, |
190 | | }; |
191 | | |
192 | | // [RFC 7540] 6.2. Headers |
193 | | enum Http2FrameFlagsHeaders { |
194 | | HTTP2_FLAGS_HEADERS_END_STREAM = 0x01, |
195 | | HTTP2_FLAGS_HEADERS_END_HEADERS = 0x04, |
196 | | HTTP2_FLAGS_HEADERS_PADDED = 0x08, |
197 | | HTTP2_FLAGS_HEADERS_PRIORITY = 0x20, |
198 | | |
199 | | HTTP2_FLAGS_HEADERS_MASK = 0x2D, |
200 | | }; |
201 | | |
202 | | // [RFC 7540] 6.3. Priority |
203 | | enum Http2FrameFlagsPriority { |
204 | | HTTP2_FLAGS_PRIORITY_MASK = 0x00, |
205 | | }; |
206 | | |
207 | | // [RFC 7540] 6.4. Rst Stream |
208 | | enum Http2FrameFlagsRstStream { |
209 | | HTTP2_FLAGS_RST_STREAM_MASK = 0x00, |
210 | | }; |
211 | | |
212 | | // [RFC 7540] 6.5. Settings |
213 | | enum Http2FrameFlagsSettings { |
214 | | HTTP2_FLAGS_SETTINGS_ACK = 0x01, |
215 | | |
216 | | HTTP2_FLAGS_SETTINGS_MASK = 0x01 |
217 | | }; |
218 | | |
219 | | // [RFC 7540] 6.6. Push Promise |
220 | | enum Http2FrameFlagsPushPromise { |
221 | | HTTP2_FLAGS_PUSH_PROMISE_END_HEADERS = 0x04, |
222 | | HTTP2_FLAGS_PUSH_PROMISE_PADDED = 0x08, |
223 | | |
224 | | HTTP2_FLAGS_PUSH_PROMISE_MASK = 0x0C, |
225 | | }; |
226 | | |
227 | | // [RFC 7540] 6.7. Ping |
228 | | enum Http2FrameFlagsPing { |
229 | | HTTP2_FLAGS_PING_ACK = 0x01, |
230 | | |
231 | | HTTP2_FLAGS_PING_MASK = 0x01 |
232 | | }; |
233 | | |
234 | | // [RFC 7540] 6.8. Goaway |
235 | | enum Http2FrameFlagsGoaway { |
236 | | HTTP2_FLAGS_GOAWAY_MASK = 0x00, |
237 | | }; |
238 | | |
239 | | // [RFC 7540] 6.9. Window Update |
240 | | enum Http2FrameFlagsWindowUpdate { |
241 | | HTTP2_FLAGS_WINDOW_UPDATE_MASK = 0x00, |
242 | | }; |
243 | | |
244 | | // [RFC 7540] 6.10. Continuation |
245 | | enum Http2FrameFlagsContinuation { |
246 | | HTTP2_FLAGS_CONTINUATION_END_HEADERS = 0x04, |
247 | | |
248 | | HTTP2_FLAGS_CONTINUATION_MASK = 0x04, |
249 | | }; |
250 | | |
251 | | // [RFC 7540] 6.5.2. Defined SETTINGS Parameters |
252 | | enum Http2SettingsIdentifier { |
253 | | HTTP2_SETTINGS_HEADER_TABLE_SIZE = 1, |
254 | | HTTP2_SETTINGS_ENABLE_PUSH = 2, |
255 | | HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 3, |
256 | | HTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 4, |
257 | | HTTP2_SETTINGS_MAX_FRAME_SIZE = 5, |
258 | | HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 6, |
259 | | HTTP2_SETTINGS_MAX, // Really just the max of the "densely numbered" core id's |
260 | | }; |
261 | | |
262 | | // [RFC 7540] 4.1. Frame Format |
263 | | struct Http2FrameHeader { |
264 | | uint32_t length; |
265 | | uint8_t type; |
266 | | uint8_t flags; |
267 | | Http2StreamId streamid; |
268 | | }; |
269 | | |
270 | | // [RFC 7540] 5.4. Error Handling |
271 | | struct Http2Error { |
272 | | Http2Error(const Http2ErrorClass error_class = Http2ErrorClass::HTTP2_ERROR_CLASS_NONE, |
273 | | const Http2ErrorCode error_code = Http2ErrorCode::HTTP2_ERROR_NO_ERROR, const char *err_msg = "") |
274 | 0 | { |
275 | 0 | cls = error_class; |
276 | 0 | code = error_code; |
277 | 0 | msg = err_msg; |
278 | 0 | }; |
279 | | |
280 | | Http2ErrorClass cls; |
281 | | Http2ErrorCode code; |
282 | | const char *msg; |
283 | | }; |
284 | | |
285 | | // [RFC 7540] 6.5.1. SETTINGS Format |
286 | | struct Http2SettingsParameter { |
287 | | uint16_t id; |
288 | | uint32_t value; |
289 | | }; |
290 | | |
291 | | // [RFC 7540] 6.3 PRIORITY Format |
292 | | struct Http2Priority { |
293 | 0 | Http2Priority() : weight(HTTP2_PRIORITY_DEFAULT_WEIGHT), stream_dependency(HTTP2_PRIORITY_DEFAULT_STREAM_DEPENDENCY) {} |
294 | | |
295 | | bool exclusive_flag = false; |
296 | | uint8_t weight; |
297 | | uint32_t stream_dependency; |
298 | | }; |
299 | | |
300 | | // [RFC 7540] 6.2 HEADERS Format |
301 | | struct Http2HeadersParameter { |
302 | 0 | Http2HeadersParameter() {} |
303 | | uint8_t pad_length = 0; |
304 | | Http2Priority priority; |
305 | | }; |
306 | | |
307 | | // [RFC 7540] 6.8 GOAWAY Format |
308 | | struct Http2Goaway { |
309 | 0 | Http2Goaway() {} |
310 | | Http2StreamId last_streamid = 0; |
311 | | Http2ErrorCode error_code = Http2ErrorCode::HTTP2_ERROR_NO_ERROR; |
312 | | |
313 | | // NOTE: we don't (de)serialize the variable length debug data at this layer |
314 | | // because there's |
315 | | // really nothing we can do with it without some out of band agreement. Trying |
316 | | // to deal with it |
317 | | // just complicates memory management. |
318 | | }; |
319 | | |
320 | | // [RFC 7540] 6.4 RST_STREAM Format |
321 | | struct Http2RstStream { |
322 | | uint32_t error_code; |
323 | | }; |
324 | | |
325 | | // [RFC 7540] 6.6 PUSH_PROMISE Format |
326 | | struct Http2PushPromise { |
327 | | uint8_t pad_length = 0; |
328 | | Http2StreamId promised_streamid = 0; |
329 | | }; |
330 | | |
331 | | static inline bool |
332 | | http2_is_client_streamid(Http2StreamId streamid) |
333 | 0 | { |
334 | 0 | return (streamid & 0x1u) == 0x1u; |
335 | 0 | } Unexecuted instantiation: fuzz_hpack.cc:http2_is_client_streamid(unsigned int) Unexecuted instantiation: HTTP2.cc:http2_is_client_streamid(unsigned int) Unexecuted instantiation: Http2Frame.cc:http2_is_client_streamid(unsigned int) |
336 | | |
337 | | static inline bool |
338 | | http2_is_server_streamid(Http2StreamId streamid) |
339 | 0 | { |
340 | 0 | return (streamid & 0x1u) == 0x0u && streamid != 0x0u; |
341 | 0 | } Unexecuted instantiation: fuzz_hpack.cc:http2_is_server_streamid(unsigned int) Unexecuted instantiation: HTTP2.cc:http2_is_server_streamid(unsigned int) Unexecuted instantiation: Http2Frame.cc:http2_is_server_streamid(unsigned int) |
342 | | |
343 | | bool http2_parse_frame_header(IOVec, Http2FrameHeader &); |
344 | | |
345 | | bool http2_write_frame_header(const Http2FrameHeader &, IOVec); |
346 | | |
347 | | bool http2_write_rst_stream(uint32_t, IOVec); |
348 | | |
349 | | bool http2_write_settings(const Http2SettingsParameter &, const IOVec &); |
350 | | |
351 | | bool http2_write_ping(const uint8_t *, IOVec); |
352 | | |
353 | | bool http2_write_goaway(const Http2Goaway &, IOVec); |
354 | | |
355 | | bool http2_write_window_update(const uint32_t new_size, const IOVec &); |
356 | | |
357 | | bool http2_write_push_promise(const Http2PushPromise &push_promise, const uint8_t *src, size_t length, const IOVec &iov); |
358 | | |
359 | | bool http2_frame_header_is_valid(const Http2FrameHeader &, unsigned); |
360 | | |
361 | | bool http2_settings_parameter_is_valid(const Http2SettingsParameter &); |
362 | | |
363 | | bool http2_parse_headers_parameter(IOVec, Http2HeadersParameter &); |
364 | | |
365 | | bool http2_parse_priority_parameter(IOVec, Http2Priority &); |
366 | | |
367 | | bool http2_parse_rst_stream(IOVec, Http2RstStream &); |
368 | | |
369 | | bool http2_parse_settings_parameter(IOVec, Http2SettingsParameter &); |
370 | | |
371 | | bool http2_parse_goaway(IOVec, Http2Goaway &); |
372 | | |
373 | | bool http2_parse_window_update(IOVec, uint32_t &); |
374 | | |
375 | | Http2ErrorCode http2_decode_header_blocks(HTTPHdr *, const uint8_t *, const uint32_t, uint32_t *, HpackHandle &, bool, uint32_t, |
376 | | bool is_outbound = false); |
377 | | |
378 | | Http2ErrorCode http2_encode_header_blocks(HTTPHdr *, uint8_t *, uint32_t, uint32_t *, HpackHandle &, int32_t); |
379 | | |
380 | | ParseResult http2_convert_header_from_2_to_1_1(HTTPHdr *); |
381 | | ParseResult http2_convert_header_from_1_1_to_2(HTTPHdr *); |
382 | | void http2_init(); |
383 | | |
384 | | /** Each of these values correspond to the flow control policy described in or |
385 | | * records.yaml documentation for proxy.config.http2.flow_control.policy_in. |
386 | | */ |
387 | | enum class Http2FlowControlPolicy { |
388 | | STATIC_SESSION_AND_STATIC_STREAM, |
389 | | LARGE_SESSION_AND_STATIC_STREAM, |
390 | | LARGE_SESSION_AND_DYNAMIC_STREAM, |
391 | | }; |
392 | | |
393 | | // Not sure where else to put this, but figure this is as good of a start as |
394 | | // anything else. |
395 | | // Right now, only the static init() is available, which sets up some basic |
396 | | // librecords |
397 | | // dependencies. |
398 | | class Http2 |
399 | | { |
400 | | public: |
401 | | static uint32_t max_concurrent_streams_in; |
402 | | static uint32_t min_concurrent_streams_in; |
403 | | static uint32_t max_active_streams_in; |
404 | | static bool throttling; |
405 | | static uint32_t stream_priority_enabled; |
406 | | static uint32_t initial_window_size_in; |
407 | | static Http2FlowControlPolicy flow_control_policy_in; |
408 | | static uint32_t max_frame_size; |
409 | | static uint32_t header_table_size; |
410 | | static uint32_t max_header_list_size; |
411 | | static uint32_t accept_no_activity_timeout; |
412 | | static uint32_t no_activity_timeout_in; |
413 | | static uint32_t active_timeout_in; |
414 | | static uint32_t incomplete_header_timeout_in; |
415 | | static uint32_t push_diary_size; |
416 | | static uint32_t zombie_timeout_in; |
417 | | |
418 | | static uint32_t max_concurrent_streams_out; |
419 | | static uint32_t min_concurrent_streams_out; |
420 | | static uint32_t max_active_streams_out; |
421 | | static uint32_t no_activity_timeout_out; |
422 | | static uint32_t initial_window_size_out; |
423 | | static Http2FlowControlPolicy flow_control_policy_out; |
424 | | |
425 | | static float stream_error_rate_threshold; |
426 | | static uint32_t stream_error_sampling_threshold; |
427 | | static int32_t max_settings_per_frame; |
428 | | static int32_t max_settings_per_minute; |
429 | | static int32_t max_settings_frames_per_minute; |
430 | | static int32_t max_ping_frames_per_minute; |
431 | | static int32_t max_priority_frames_per_minute; |
432 | | static int32_t max_rst_stream_frames_per_minute; |
433 | | static int32_t max_continuation_frames_per_minute; |
434 | | static int32_t max_empty_frames_per_minute; |
435 | | static float min_avg_window_update; |
436 | | static uint32_t con_slow_log_threshold; |
437 | | static uint32_t stream_slow_log_threshold; |
438 | | static uint32_t header_table_size_limit; |
439 | | static uint32_t write_buffer_block_size; |
440 | | static int64_t write_buffer_block_size_index; |
441 | | static float write_size_threshold; |
442 | | static uint32_t write_time_threshold; |
443 | | static uint32_t buffer_water_mark; |
444 | | |
445 | | static void init(); |
446 | | }; |