/src/trafficserver/include/proxy/hdrs/HTTP.h
Line | Count | Source |
1 | | /** @file |
2 | | |
3 | | A brief file description |
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 <cassert> |
27 | | #include <string_view> |
28 | | |
29 | | using namespace std::literals; |
30 | | |
31 | | #include "tscore/Arena.h" |
32 | | #include "tscore/CryptoHash.h" |
33 | | #include "tscore/HTTPVersion.h" |
34 | | #include "proxy/hdrs/MIME.h" |
35 | | #include "proxy/hdrs/URL.h" |
36 | | |
37 | | #include "tscore/ink_apidefs.h" |
38 | | |
39 | | class Http2HeaderTable; |
40 | | |
41 | | enum class HTTPStatus { |
42 | | NONE = 0, |
43 | | |
44 | | CONTINUE = 100, |
45 | | SWITCHING_PROTOCOL = 101, |
46 | | PROCESSING = 102, |
47 | | EARLY_HINTS = 103, |
48 | | |
49 | | OK = 200, |
50 | | CREATED = 201, |
51 | | ACCEPTED = 202, |
52 | | NON_AUTHORITATIVE_INFORMATION = 203, |
53 | | NO_CONTENT = 204, |
54 | | RESET_CONTENT = 205, |
55 | | PARTIAL_CONTENT = 206, |
56 | | |
57 | | MULTIPLE_CHOICES = 300, |
58 | | MOVED_PERMANENTLY = 301, |
59 | | MOVED_TEMPORARILY = 302, |
60 | | SEE_OTHER = 303, |
61 | | NOT_MODIFIED = 304, |
62 | | USE_PROXY = 305, |
63 | | TEMPORARY_REDIRECT = 307, |
64 | | PERMANENT_REDIRECT = 308, |
65 | | |
66 | | BAD_REQUEST = 400, |
67 | | UNAUTHORIZED = 401, |
68 | | PAYMENT_REQUIRED = 402, |
69 | | FORBIDDEN = 403, |
70 | | NOT_FOUND = 404, |
71 | | METHOD_NOT_ALLOWED = 405, |
72 | | NOT_ACCEPTABLE = 406, |
73 | | PROXY_AUTHENTICATION_REQUIRED = 407, |
74 | | REQUEST_TIMEOUT = 408, |
75 | | CONFLICT = 409, |
76 | | GONE = 410, |
77 | | LENGTH_REQUIRED = 411, |
78 | | PRECONDITION_FAILED = 412, |
79 | | REQUEST_ENTITY_TOO_LARGE = 413, |
80 | | REQUEST_URI_TOO_LONG = 414, |
81 | | UNSUPPORTED_MEDIA_TYPE = 415, |
82 | | RANGE_NOT_SATISFIABLE = 416, |
83 | | TOO_EARLY = 425, |
84 | | |
85 | | INTERNAL_SERVER_ERROR = 500, |
86 | | NOT_IMPLEMENTED = 501, |
87 | | BAD_GATEWAY = 502, |
88 | | SERVICE_UNAVAILABLE = 503, |
89 | | GATEWAY_TIMEOUT = 504, |
90 | | HTTPVER_NOT_SUPPORTED = 505 |
91 | | }; |
92 | | |
93 | | enum class HTTPKeepAlive { |
94 | | UNDEFINED = 0, |
95 | | NO_KEEPALIVE, |
96 | | KEEPALIVE, |
97 | | }; |
98 | | |
99 | | enum class HTTPWarningCode { |
100 | | NONE = 0, |
101 | | |
102 | | RESPONSE_STALE = 110, |
103 | | REVALIDATION_FAILED = 111, |
104 | | DISCONNECTED_OPERATION = 112, |
105 | | HERUISTIC_EXPIRATION = 113, |
106 | | TRANSFORMATION_APPLIED = 114, |
107 | | MISC_WARNING = 199 |
108 | | }; |
109 | | |
110 | | /* squid log codes |
111 | | There is code (e.g. logstats) that depends on these errors coming at the end of this enum */ |
112 | | enum class SquidLogCode { |
113 | | EMPTY = '0', |
114 | | TCP_HIT = '1', |
115 | | TCP_DISK_HIT = '2', |
116 | | TCP_MEM_HIT = '.', // Don't want to change others codes |
117 | | TCP_MISS = '3', |
118 | | TCP_EXPIRED_MISS = '4', |
119 | | TCP_REFRESH_HIT = '5', |
120 | | TCP_REF_FAIL_HIT = '6', |
121 | | TCP_REFRESH_MISS = '7', |
122 | | TCP_CLIENT_REFRESH = '8', |
123 | | TCP_IMS_HIT = '9', |
124 | | TCP_IMS_MISS = 'a', |
125 | | TCP_SWAPFAIL = 'b', |
126 | | TCP_DENIED = 'c', |
127 | | TCP_WEBFETCH_MISS = 'd', |
128 | | TCP_FUTURE_2 = 'f', |
129 | | TCP_HIT_REDIRECT = '[', // standard redirect |
130 | | TCP_MISS_REDIRECT = ']', // standard redirect |
131 | | TCP_HIT_X_REDIRECT = '<', // extended redirect |
132 | | TCP_MISS_X_REDIRECT = '>', // extended redirect |
133 | | UDP_HIT = 'g', |
134 | | UDP_WEAK_HIT = 'h', |
135 | | UDP_HIT_OBJ = 'i', |
136 | | UDP_MISS = 'j', |
137 | | UDP_DENIED = 'k', |
138 | | UDP_INVALID = 'l', |
139 | | UDP_RELOADING = 'm', |
140 | | UDP_FUTURE_1 = 'n', |
141 | | UDP_FUTURE_2 = 'o', |
142 | | ERR_READ_TIMEOUT = 'p', |
143 | | ERR_LIFETIME_EXP = 'q', |
144 | | ERR_POST_ENTITY_TOO_LARGE = 'L', |
145 | | ERR_NO_CLIENTS_BIG_OBJ = 'r', |
146 | | ERR_READ_ERROR = 's', |
147 | | ERR_CLIENT_ABORT = 't', // Client side abort logging |
148 | | ERR_CONNECT_FAIL = 'u', |
149 | | ERR_INVALID_REQ = 'v', |
150 | | ERR_UNSUP_REQ = 'w', |
151 | | ERR_INVALID_URL = 'x', |
152 | | ERR_NO_FDS = 'y', |
153 | | ERR_DNS_FAIL = 'z', |
154 | | ERR_NOT_IMPLEMENTED = 'A', |
155 | | ERR_CANNOT_FETCH = 'B', |
156 | | ERR_NO_RELAY = 'C', |
157 | | ERR_DISK_IO = 'D', |
158 | | ERR_ZERO_SIZE_OBJECT = 'E', |
159 | | TCP_CF_HIT = 'F', // Collapsed forwarding HIT also known as Read while write hit |
160 | | ERR_PROXY_DENIED = 'G', |
161 | | ERR_WEBFETCH_DETECTED = 'H', |
162 | | ERR_FUTURE_1 = 'I', |
163 | | ERR_CLIENT_READ_ERROR = 'J', // Client side abort logging |
164 | | ERR_LOOP_DETECTED = 'K', // Loop or cycle detected, request came back to this server |
165 | | ERR_TUN_ACTIVE_TIMEOUT = 'T', // Tunnel (CONNECT) active timeout |
166 | | ERR_UNKNOWN = 'Z' |
167 | | }; |
168 | | |
169 | | // squid log subcodes |
170 | | enum class SquidSubcode { |
171 | | EMPTY = '0', |
172 | | NUM_REDIRECTIONS_EXCEEDED = '1', |
173 | | }; |
174 | | |
175 | | /* squid hierarchy codes */ |
176 | | enum class SquidHierarchyCode { |
177 | | EMPTY = '0', |
178 | | NONE = '1', |
179 | | DIRECT = '2', |
180 | | SIBLING_HIT = '3', |
181 | | PARENT_HIT = '4', |
182 | | DEFAULT_PARENT = '5', |
183 | | SINGLE_PARENT = '6', |
184 | | FIRST_UP_PARENT = '7', |
185 | | NO_PARENT_DIRECT = '8', |
186 | | FIRST_PARENT_MISS = '9', |
187 | | LOCAL_IP_DIRECT = 'a', |
188 | | FIREWALL_IP_DIRECT = 'b', |
189 | | NO_DIRECT_FAIL = 'c', |
190 | | SOURCE_FASTEST = 'd', |
191 | | SIBLING_UDP_HIT_OBJ = 'e', |
192 | | PARENT_UDP_HIT_OBJ = 'f', |
193 | | PASSTHROUGH_PARENT = 'g', |
194 | | SSL_PARENT_MISS = 'h', |
195 | | INVALID_CODE = 'i', |
196 | | TIMEOUT_DIRECT = 'j', |
197 | | TIMEOUT_SIBLING_HIT = 'k', |
198 | | TIMEOUT_PARENT_HIT = 'l', |
199 | | TIMEOUT_DEFAULT_PARENT = 'm', |
200 | | TIMEOUT_SINGLE_PARENT = 'n', |
201 | | TIMEOUT_FIRST_UP_PARENT = 'o', |
202 | | TIMEOUT_NO_PARENT_DIRECT = 'p', |
203 | | TIMEOUT_FIRST_PARENT_MISS = 'q', |
204 | | TIMEOUT_LOCAL_IP_DIRECT = 'r', |
205 | | TIMEOUT_FIREWALL_IP_DIRECT = 's', |
206 | | TIMEOUT_NO_DIRECT_FAIL = 't', |
207 | | TIMEOUT_SOURCE_FASTEST = 'u', |
208 | | TIMEOUT_SIBLING_UDP_HIT_OBJ = 'v', |
209 | | TIMEOUT_PARENT_UDP_HIT_OBJ = 'w', |
210 | | TIMEOUT_PASSTHROUGH_PARENT = 'x', |
211 | | TIMEOUT_TIMEOUT_SSL_PARENT_MISS = 'y', |
212 | | INVALID_ASSIGNED_CODE = 'z' |
213 | | }; |
214 | | |
215 | | /* squid hit/miss codes */ |
216 | | enum SquidHitMissCode { |
217 | | SQUID_HIT_RESERVED = '0', // Kinda wonky that this is '0', so skipping 'A' for now |
218 | | SQUID_HIT_LEVEL_1 = 'B', |
219 | | SQUID_HIT_LEVEL_2 = 'C', |
220 | | SQUID_HIT_LEVEL_3 = 'D', |
221 | | SQUID_HIT_LEVEL_4 = 'E', |
222 | | SQUID_HIT_LEVEL_5 = 'F', |
223 | | SQUID_HIT_LEVEL_6 = 'G', |
224 | | SQUID_HIT_LEVEL_7 = 'H', |
225 | | SQUID_HIT_LEVEL_8 = 'I', |
226 | | SQUID_HIT_LEVEl_9 = 'J', |
227 | | SQUID_MISS_NONE = '1', |
228 | | SQUID_MISS_HTTP_NON_CACHE = '3', |
229 | | SQUID_MISS_HTTP_NO_DLE = '5', |
230 | | SQUID_MISS_HTTP_NO_LE = '6', |
231 | | SQUID_MISS_HTTP_CONTENT = '7', |
232 | | SQUID_MISS_PRAGMA_NOCACHE = '8', |
233 | | SQUID_MISS_PASS = '9', |
234 | | SQUID_MISS_PRE_EXPIRED = 'a', |
235 | | SQUID_MISS_ERROR = 'b', |
236 | | SQUID_MISS_CACHE_BYPASS = 'c', |
237 | | SQUID_HIT_MISS_INVALID_ASSIGNED_CODE = 'z', |
238 | | // These are pre-allocated with special semantics, added here for convenience |
239 | | SQUID_HIT_RAM = SQUID_HIT_LEVEL_1, |
240 | | SQUID_HIT_SSD = SQUID_HIT_LEVEL_2, |
241 | | SQUID_HIT_DISK = SQUID_HIT_LEVEL_3, |
242 | | SQUID_HIT_CLUSTER = SQUID_HIT_LEVEL_4, |
243 | | SQUID_HIT_NET = SQUID_HIT_LEVEL_5, |
244 | | SQUID_HIT_RWW = SQUID_HIT_LEVEL_6 |
245 | | }; |
246 | | |
247 | | constexpr std::string_view PSEUDO_HEADER_SCHEME = ":scheme"; |
248 | | constexpr std::string_view PSEUDO_HEADER_AUTHORITY = ":authority"; |
249 | | constexpr std::string_view PSEUDO_HEADER_PATH = ":path"; |
250 | | constexpr std::string_view PSEUDO_HEADER_METHOD = ":method"; |
251 | | constexpr std::string_view PSEUDO_HEADER_STATUS = ":status"; |
252 | | |
253 | | enum class HTTPType { |
254 | | UNKNOWN, |
255 | | REQUEST, |
256 | | RESPONSE, |
257 | | }; |
258 | | |
259 | | struct HTTPHdrImpl : public HdrHeapObjImpl { |
260 | | // HdrHeapObjImpl is 4 bytes |
261 | | HTTPType m_polarity; // request or response or unknown |
262 | | HTTPVersion m_version; // cooked version number |
263 | | // 12 bytes means 4 bytes padding here on 64-bit architectures |
264 | | union { |
265 | | struct { |
266 | | URLImpl *m_url_impl; |
267 | | const char *m_ptr_method; |
268 | | uint16_t m_len_method; |
269 | | int16_t m_method_wks_idx; |
270 | | } req; |
271 | | |
272 | | struct { |
273 | | const char *m_ptr_reason; |
274 | | uint16_t m_len_reason; |
275 | | int16_t m_status; |
276 | | } resp; |
277 | | } u; |
278 | | |
279 | | MIMEHdrImpl *m_fields_impl; |
280 | | |
281 | | // Marshaling Functions |
282 | | int marshal(MarshalXlate *ptr_xlate, int num_ptr, MarshalXlate *str_xlate, int num_str); |
283 | | void unmarshal(intptr_t offset); |
284 | | void move_strings(HdrStrHeap *new_heap); |
285 | | size_t strings_length(); |
286 | | |
287 | | // Sanity Check Functions |
288 | | void check_strings(HeapCheck *heaps, int num_heaps); |
289 | | }; |
290 | | |
291 | | struct HTTPValAccept { |
292 | | char *type; |
293 | | char *subtype; |
294 | | double qvalue; |
295 | | }; |
296 | | |
297 | | struct HTTPValAcceptCharset { |
298 | | char *charset; |
299 | | double qvalue; |
300 | | }; |
301 | | |
302 | | struct HTTPValAcceptEncoding { |
303 | | char *encoding; |
304 | | double qvalue; |
305 | | }; |
306 | | |
307 | | struct HTTPValAcceptLanguage { |
308 | | char *language; |
309 | | double qvalue; |
310 | | }; |
311 | | |
312 | | struct HTTPValFieldList { |
313 | | char *name; |
314 | | HTTPValFieldList *next; |
315 | | }; |
316 | | |
317 | | struct HTTPValCacheControl { |
318 | | const char *directive; |
319 | | |
320 | | union { |
321 | | int delta_seconds; |
322 | | HTTPValFieldList *field_names; |
323 | | } u; |
324 | | }; |
325 | | |
326 | | struct HTTPValRange { |
327 | | int start; |
328 | | int end; |
329 | | HTTPValRange *next; |
330 | | }; |
331 | | |
332 | | struct HTTPValTE { |
333 | | char *encoding; |
334 | | double qvalue; |
335 | | }; |
336 | | |
337 | | struct HTTPParser { |
338 | | bool m_parsing_http = false; |
339 | | MIMEParser m_mime_parser; |
340 | | }; |
341 | | |
342 | | extern c_str_view HTTP_METHOD_CONNECT; |
343 | | extern c_str_view HTTP_METHOD_DELETE; |
344 | | extern c_str_view HTTP_METHOD_GET; |
345 | | extern c_str_view HTTP_METHOD_HEAD; |
346 | | extern c_str_view HTTP_METHOD_OPTIONS; |
347 | | extern c_str_view HTTP_METHOD_POST; |
348 | | extern c_str_view HTTP_METHOD_PURGE; |
349 | | extern c_str_view HTTP_METHOD_PUT; |
350 | | extern c_str_view HTTP_METHOD_TRACE; |
351 | | extern c_str_view HTTP_METHOD_PUSH; |
352 | | |
353 | | extern int HTTP_WKSIDX_CONNECT; |
354 | | extern int HTTP_WKSIDX_DELETE; |
355 | | extern int HTTP_WKSIDX_GET; |
356 | | extern int HTTP_WKSIDX_HEAD; |
357 | | extern int HTTP_WKSIDX_OPTIONS; |
358 | | extern int HTTP_WKSIDX_POST; |
359 | | extern int HTTP_WKSIDX_PURGE; |
360 | | extern int HTTP_WKSIDX_PUT; |
361 | | extern int HTTP_WKSIDX_TRACE; |
362 | | extern int HTTP_WKSIDX_PUSH; |
363 | | extern int HTTP_WKSIDX_METHODS_CNT; |
364 | | |
365 | | extern c_str_view HTTP_VALUE_BYTES; |
366 | | extern c_str_view HTTP_VALUE_CHUNKED; |
367 | | extern c_str_view HTTP_VALUE_CLOSE; |
368 | | extern c_str_view HTTP_VALUE_COMPRESS; |
369 | | extern c_str_view HTTP_VALUE_DEFLATE; |
370 | | extern c_str_view HTTP_VALUE_GZIP; |
371 | | extern c_str_view HTTP_VALUE_BROTLI; |
372 | | extern c_str_view HTTP_VALUE_ZSTD; |
373 | | extern c_str_view HTTP_VALUE_IDENTITY; |
374 | | extern c_str_view HTTP_VALUE_KEEP_ALIVE; |
375 | | extern c_str_view HTTP_VALUE_MAX_AGE; |
376 | | extern c_str_view HTTP_VALUE_MAX_STALE; |
377 | | extern c_str_view HTTP_VALUE_MIN_FRESH; |
378 | | extern c_str_view HTTP_VALUE_MUST_REVALIDATE; |
379 | | extern c_str_view HTTP_VALUE_NONE; |
380 | | extern c_str_view HTTP_VALUE_NO_CACHE; |
381 | | extern c_str_view HTTP_VALUE_NO_STORE; |
382 | | extern c_str_view HTTP_VALUE_NO_TRANSFORM; |
383 | | extern c_str_view HTTP_VALUE_ONLY_IF_CACHED; |
384 | | extern c_str_view HTTP_VALUE_PRIVATE; |
385 | | extern c_str_view HTTP_VALUE_PROXY_REVALIDATE; |
386 | | extern c_str_view HTTP_VALUE_PUBLIC; |
387 | | extern c_str_view HTTP_VALUE_S_MAXAGE; |
388 | | extern c_str_view HTTP_VALUE_NEED_REVALIDATE_ONCE; |
389 | | extern c_str_view HTTP_VALUE_100_CONTINUE; |
390 | | |
391 | | /* Private */ |
392 | | void http_hdr_adjust(HTTPHdrImpl *hdrp, int32_t offset, int32_t length, int32_t delta); |
393 | | |
394 | | /* Public */ |
395 | | void http_init(); |
396 | | |
397 | | HTTPHdrImpl *http_hdr_create(HdrHeap *heap, HTTPType polarity, HTTPVersion version); |
398 | | void http_hdr_init(HdrHeap *heap, HTTPHdrImpl *hh, HTTPType polarity, HTTPVersion version); |
399 | | HTTPHdrImpl *http_hdr_clone(HTTPHdrImpl *s_hh, HdrHeap *s_heap, HdrHeap *d_heap); |
400 | | void http_hdr_copy_onto(HTTPHdrImpl *s_hh, HdrHeap *s_heap, HTTPHdrImpl *d_hh, HdrHeap *d_heap, bool inherit_strs); |
401 | | |
402 | | int http_hdr_print(HTTPHdrImpl const *hh, char *buf, int bufsize, int *bufindex, int *dumpoffset); |
403 | | |
404 | | void http_hdr_describe(HdrHeapObjImpl *obj, bool recurse = true); |
405 | | |
406 | | bool http_hdr_version_set(HTTPHdrImpl *hh, const HTTPVersion &ver); |
407 | | |
408 | | std::string_view http_hdr_method_get(HTTPHdrImpl *hh); |
409 | | void http_hdr_method_set(HdrHeap *heap, HTTPHdrImpl *hh, std::string_view method, int16_t method_wks_idx, bool must_copy); |
410 | | |
411 | | void http_hdr_url_set(HdrHeap *heap, HTTPHdrImpl *hh, URLImpl *url); |
412 | | |
413 | | // HTTPStatus http_hdr_status_get (HTTPHdrImpl *hh); |
414 | | void http_hdr_status_set(HTTPHdrImpl *hh, HTTPStatus status); |
415 | | std::string_view http_hdr_reason_get(HTTPHdrImpl *hh); |
416 | | void http_hdr_reason_set(HdrHeap *heap, HTTPHdrImpl *hh, std::string_view value, bool must_copy); |
417 | | const char *http_hdr_reason_lookup(HTTPStatus status); |
418 | | |
419 | | void http_parser_init(HTTPParser *parser); |
420 | | void http_parser_clear(HTTPParser *parser); |
421 | | ParseResult http_parser_parse_req(HTTPParser *parser, HdrHeap *heap, HTTPHdrImpl *hh, const char **start, const char *end, |
422 | | bool must_copy_strings, bool eof, int strict_uri_parsing, size_t max_request_line_size, |
423 | | size_t max_hdr_field_size); |
424 | | ParseResult validate_hdr_request_target(int method_wks_idx, URLImpl *url); |
425 | | |
426 | | // This calls http_parse_host_header internally to parse the Host field value |
427 | | // when present, so it enforces the same syntax rules and also validates the |
428 | | // port number when specified. |
429 | | ParseResult validate_hdr_host(HTTPHdrImpl *hh); |
430 | | |
431 | | // This parses and validates the Host field value. |
432 | | bool http_parse_host_header(std::string_view value, std::string_view &host, int &port, bool &has_port); |
433 | | |
434 | | ParseResult validate_hdr_content_length(HdrHeap *heap, HTTPHdrImpl *hh); |
435 | | ParseResult http_parser_parse_resp(HTTPParser *parser, HdrHeap *heap, HTTPHdrImpl *hh, const char **start, const char *end, |
436 | | bool must_copy_strings, bool eof); |
437 | | |
438 | | HTTPStatus http_parse_status(const char *start, const char *end); |
439 | | HTTPVersion http_parse_version(const char *start, const char *end); |
440 | | |
441 | | /* |
442 | | HTTPValAccept* http_parse_accept (const char *buf, Arena *arena); |
443 | | HTTPValAcceptCharset* http_parse_accept_charset (const char *buf, Arena *arena); |
444 | | HTTPValAcceptEncoding* http_parse_accept_encoding (const char *buf, Arena *arena); |
445 | | HTTPValAcceptLanguage* http_parse_accept_language (const char *buf, Arena *arena); |
446 | | HTTPValCacheControl* http_parse_cache_control (const char *buf, Arena *arena); |
447 | | const char* http_parse_cache_directive (const char **buf); |
448 | | HTTPValRange* http_parse_range (const char *buf, Arena *arena); |
449 | | */ |
450 | | HTTPValTE *http_parse_te(const char *buf, int len, Arena *arena); |
451 | | |
452 | | bool is_http1_hdr_version_supported(const HTTPVersion &http_version); |
453 | | |
454 | | class IOBufferReader; |
455 | | |
456 | | /** HTTP Header class. |
457 | | * |
458 | | * @warning Changing the size of this class (adding/removing fields) will change |
459 | | * the on-disk cache format and cause cache incompatibility. The HTTPCacheAlt |
460 | | * structure contains embedded HTTPHdr objects, and the cache marshalling code |
461 | | * uses sizeof(HTTPCacheAlt) to read/write cache entries. Any size change will |
462 | | * cause "vector inconsistency" errors when reading cache entries written by a |
463 | | * different version. |
464 | | */ |
465 | | class HTTPHdr : public MIMEHdr |
466 | | { |
467 | | public: |
468 | | HTTPHdrImpl *m_http = nullptr; |
469 | | mutable URL m_url_cached; |
470 | | mutable MIMEField *m_host_mime = nullptr; |
471 | | mutable int m_host_length = 0; ///< Length of hostname (parsed, excludes port). |
472 | | mutable int m_port = 0; ///< Target port. |
473 | | mutable bool m_target_cached = false; ///< Whether host name and port are cached. |
474 | | mutable bool m_target_in_url = false; ///< Whether host name and port are in the URL. |
475 | | mutable bool m_100_continue_sent = false; ///< Whether ATS sent a 100 Continue optimized response. |
476 | | mutable bool m_100_continue_required = false; ///< Whether 100_continue is in the Expect header. |
477 | | /// Set if the port was effectively specified in the header. |
478 | | /// @c true if the target (in the URL or the HOST field) also specified |
479 | | /// a port. That is, @c true if whatever source had the target host |
480 | | /// also had a port, @c false otherwise. |
481 | | mutable bool m_port_in_header = false; |
482 | | |
483 | | mutable bool early_data = false; |
484 | | |
485 | 0 | HTTPHdr() = default; // Force the creation of the default constructor |
486 | | |
487 | | int valid() const; |
488 | | |
489 | | void create(HTTPType polarity, HTTPVersion version = HTTP_INVALID, HdrHeap *heap = nullptr); |
490 | | void clear(); |
491 | | void reset(); |
492 | | void copy(const HTTPHdr *hdr); |
493 | | void copy_shallow(const HTTPHdr *hdr); |
494 | | |
495 | | int unmarshal(char *buf, int len, RefCountObj *block_ref); |
496 | | |
497 | | int print(char *buf, int bufsize, int *bufindex, int *dumpoffset) const; |
498 | | |
499 | | int length_get() const; |
500 | | |
501 | | HTTPType type_get() const; |
502 | | |
503 | | HTTPVersion version_get() const; |
504 | | void version_set(HTTPVersion version); |
505 | | |
506 | | std::string_view method_get(); |
507 | | int method_get_wksidx() const; |
508 | | void method_set(std::string_view value); |
509 | | |
510 | | URL *url_create(URL *url); |
511 | | |
512 | | URL *url_get() const; |
513 | | URL *url_get(URL *url); |
514 | | /** Get a string with the effective URL in it. |
515 | | If @a length is not @c NULL then the length of the string |
516 | | is stored in the int pointed to by @a length. |
517 | | |
518 | | Note that this can be different from getting the @c URL |
519 | | and invoking @c URL::string_get if the host is in a header |
520 | | field and not explicitly in the URL. |
521 | | */ |
522 | | char *url_string_get(Arena *arena = nullptr, ///< Arena to use, or @c malloc if NULL. |
523 | | int *length = nullptr ///< Store string length here. |
524 | | ); |
525 | | /** Get a string with the effective URL in it. |
526 | | This is automatically allocated if needed in the request heap. |
527 | | |
528 | | @see url_string_get |
529 | | */ |
530 | | char *url_string_get_ref(int *length = nullptr ///< Store string length here. |
531 | | ); |
532 | | |
533 | | /** Print the URL. |
534 | | Output is not null terminated. |
535 | | @return 0 on failure, non-zero on success. |
536 | | */ |
537 | | int url_print(char *buff, ///< Output buffer |
538 | | int length, ///< Length of @a buffer |
539 | | int *offset, ///< [in,out] ??? |
540 | | int *skip, ///< [in,out] ??? |
541 | | unsigned normalization_flags = URLNormalize::NONE ///< host/scheme normalized to lower case |
542 | | ); |
543 | | |
544 | | /** Return the length of the URL that url_print() will create. |
545 | | @return -1 on failure, non-negative on success. |
546 | | */ |
547 | | int url_printed_length(unsigned normalizaion_flags = URLNormalize::NONE); |
548 | | |
549 | | /** Get the URL path. |
550 | | This is a reference, not allocated. |
551 | | @return A string_view to the path or an empty string_view if there is no valid URL. |
552 | | */ |
553 | | std::string_view path_get(); |
554 | | |
555 | | /** Get the URL query. |
556 | | This is a reference, not allocated. |
557 | | @return A string_view to the query or an empty string_view if there is no valid URL. |
558 | | */ |
559 | | std::string_view query_get(); |
560 | | |
561 | | /** Get the URL fragment. |
562 | | This is a reference, not allocated. |
563 | | @return A string_view to the fragment or an empty string_view if there is no valid URL. |
564 | | */ |
565 | | std::string_view fragment_get(); |
566 | | |
567 | | /** Get the target host name. |
568 | | The length is returned in @a length if non-NULL. |
569 | | @note The results are cached so this is fast after the first call. |
570 | | @return A string_view to the host name. |
571 | | */ |
572 | | std::string_view host_get() const; |
573 | | |
574 | | /** Get the target port. |
575 | | If the target port is not found then it is adjusted to the |
576 | | default port for the URL type. |
577 | | @note The results are cached so this is fast after the first call. |
578 | | @return The canonicalized target port. |
579 | | */ |
580 | | int port_get(); |
581 | | |
582 | | /** Get the URL scheme. |
583 | | This is a reference, not allocated. |
584 | | @return A string_view to the scheme or an empty string_view if there is no valid URL. |
585 | | */ |
586 | | std::string_view scheme_get(); |
587 | | void url_set(URL *url); |
588 | | void url_set(std::string_view value); |
589 | | |
590 | | /// Check location of target host. |
591 | | /// @return @c true if the host was in the URL, @c false otherwise. |
592 | | /// @note This returns @c false if the host is missing. |
593 | | bool is_target_in_url() const; |
594 | | |
595 | | /// Check if a port was specified in the target. |
596 | | /// @return @c true if the port was part of the target. |
597 | | bool is_port_in_header() const; |
598 | | |
599 | | /// If the target is in the fields and not the URL, copy it to the @a url. |
600 | | /// If @a url is @c NULL the cached URL in this header is used. |
601 | | /// @note In the default case the copy is avoided if the cached URL already |
602 | | /// has the target. If @a url is non @c NULL the copy is always performed. |
603 | | void set_url_target_from_host_field(URL *url = nullptr); |
604 | | |
605 | | /// Mark the target cache as invalid. |
606 | | /// @internal Ugly but too many places currently that touch the |
607 | | /// header internals, they must be able to do this. |
608 | | void mark_target_dirty() const; |
609 | | |
610 | | HTTPStatus status_get() const; |
611 | | void status_set(HTTPStatus status); |
612 | | |
613 | | std::string_view reason_get(); |
614 | | void reason_set(std::string_view value); |
615 | | |
616 | | void mark_early_data(bool flag = true) const; |
617 | | bool is_early_data() const; |
618 | | |
619 | | ParseResult parse_req(HTTPParser *parser, const char **start, const char *end, bool eof, int strict_uri_parsing = 0, |
620 | | size_t max_request_line_size = UINT16_MAX, size_t max_hdr_field_size = 131070); |
621 | | ParseResult parse_resp(HTTPParser *parser, const char **start, const char *end, bool eof); |
622 | | |
623 | | ParseResult parse_req(HTTPParser *parser, IOBufferReader *r, int *bytes_used, bool eof, int strict_uri_parsing = 0, |
624 | | size_t max_request_line_size = UINT16_MAX, size_t max_hdr_field_size = UINT16_MAX); |
625 | | ParseResult parse_resp(HTTPParser *parser, IOBufferReader *r, int *bytes_used, bool eof); |
626 | | |
627 | | bool check_hdr_implements(); |
628 | | |
629 | | public: |
630 | | // Utility routines |
631 | | bool is_cache_control_set(const char *cc_directive_wks); |
632 | | bool is_pragma_no_cache_set(); |
633 | | bool is_keep_alive_set() const; |
634 | | bool expect_final_response() const; |
635 | | HTTPKeepAlive keep_alive_get() const; |
636 | | |
637 | | protected: |
638 | | /** Load the target cache. |
639 | | @see m_host, m_port, m_target_in_url |
640 | | */ |
641 | | void _fill_target_cache() const; |
642 | | /** Test the cache and fill it if necessary. |
643 | | @internal In contrast to @c _fill_target_cache, this method |
644 | | is inline and checks whether the cache is already filled. |
645 | | @ _fill_target_cache @b always does a cache fill. |
646 | | */ |
647 | | void _test_and_fill_target_cache() const; |
648 | | |
649 | | static Arena *const USE_HDR_HEAP_MAGIC; |
650 | | |
651 | | // No gratuitous copies! |
652 | | HTTPHdr(const HTTPHdr &m) = delete; |
653 | | HTTPHdr &operator=(const HTTPHdr &m) = delete; |
654 | | |
655 | | private: |
656 | | friend class UrlPrintHack; // don't ask. |
657 | | }; |
658 | | |
659 | | /*------------------------------------------------------------------------- |
660 | | -------------------------------------------------------------------------*/ |
661 | | |
662 | | inline int |
663 | | HTTPHdr::valid() const |
664 | 0 | { |
665 | 0 | return (m_http && m_mime && m_heap); |
666 | 0 | } |
667 | | |
668 | | /*------------------------------------------------------------------------- |
669 | | -------------------------------------------------------------------------*/ |
670 | | |
671 | | inline void |
672 | | HTTPHdr::create(HTTPType polarity, HTTPVersion version, HdrHeap *heap) |
673 | 0 | { |
674 | 0 | if (heap) { |
675 | 0 | m_heap = heap; |
676 | 0 | } else if (!m_heap) { |
677 | 0 | m_heap = new_HdrHeap(); |
678 | 0 | } |
679 | |
|
680 | 0 | m_http = http_hdr_create(m_heap, polarity, version); |
681 | 0 | m_mime = m_http->m_fields_impl; |
682 | 0 | } |
683 | | |
684 | | inline void |
685 | | HTTPHdr::clear() |
686 | 0 | { |
687 | 0 | if (m_http && m_http->m_polarity == HTTPType::REQUEST) { |
688 | 0 | m_url_cached.clear(); |
689 | 0 | } |
690 | 0 | this->HdrHeapSDKHandle::clear(); |
691 | 0 | m_http = nullptr; |
692 | 0 | m_mime = nullptr; |
693 | 0 | } |
694 | | |
695 | | inline void |
696 | | HTTPHdr::reset() |
697 | 0 | { |
698 | 0 | m_heap = nullptr; |
699 | 0 | m_http = nullptr; |
700 | 0 | m_mime = nullptr; |
701 | 0 | m_url_cached.reset(); |
702 | 0 | } |
703 | | |
704 | | /*------------------------------------------------------------------------- |
705 | | -------------------------------------------------------------------------*/ |
706 | | |
707 | | inline void |
708 | | HTTPHdr::copy(const HTTPHdr *hdr) |
709 | 0 | { |
710 | 0 | ink_assert(hdr->valid()); |
711 | |
|
712 | 0 | if (valid()) { |
713 | 0 | http_hdr_copy_onto(hdr->m_http, hdr->m_heap, m_http, m_heap, (m_heap != hdr->m_heap) ? true : false); |
714 | 0 | } else { |
715 | 0 | m_heap = new_HdrHeap(); |
716 | 0 | m_http = http_hdr_clone(hdr->m_http, hdr->m_heap, m_heap); |
717 | 0 | m_mime = m_http->m_fields_impl; |
718 | 0 | } |
719 | 0 | } |
720 | | |
721 | | /*------------------------------------------------------------------------- |
722 | | -------------------------------------------------------------------------*/ |
723 | | |
724 | | inline void |
725 | | HTTPHdr::copy_shallow(const HTTPHdr *hdr) |
726 | 0 | { |
727 | 0 | ink_assert(hdr->valid()); |
728 | 0 |
|
729 | 0 | m_heap = hdr->m_heap; |
730 | 0 | m_http = hdr->m_http; |
731 | 0 | m_mime = hdr->m_mime; |
732 | 0 |
|
733 | 0 | if (hdr->type_get() == HTTPType::REQUEST && m_url_cached.valid()) |
734 | 0 | m_url_cached.copy_shallow(&hdr->m_url_cached); |
735 | 0 | } |
736 | | |
737 | | /*------------------------------------------------------------------------- |
738 | | -------------------------------------------------------------------------*/ |
739 | | |
740 | | inline int |
741 | | HTTPHdr::print(char *buf, int bufsize, int *bufindex, int *dumpoffset) const |
742 | 0 | { |
743 | 0 | ink_assert(valid()); |
744 | 0 | return http_hdr_print(m_http, buf, bufsize, bufindex, dumpoffset); |
745 | 0 | } |
746 | | |
747 | | /*------------------------------------------------------------------------- |
748 | | -------------------------------------------------------------------------*/ |
749 | | |
750 | | inline void |
751 | | HTTPHdr::_test_and_fill_target_cache() const |
752 | 0 | { |
753 | 0 | if (!m_target_cached) { |
754 | 0 | this->_fill_target_cache(); |
755 | 0 | return; |
756 | 0 | } |
757 | | |
758 | | // If host came from the Host header (not URL), check for staleness by verifying |
759 | | // the current Host header value length matches what we expect from cached values. |
760 | 0 | if (!m_target_in_url && m_host_mime != nullptr) { |
761 | 0 | int expected_len = m_host_length; |
762 | 0 | if (m_port_in_header && m_port > 0) { |
763 | | // Account for ":port" suffix in the raw Host header value. |
764 | 0 | expected_len += 1; // colon |
765 | 0 | if (m_port < 10) { |
766 | 0 | expected_len += 1; |
767 | 0 | } else if (m_port < 100) { |
768 | 0 | expected_len += 2; |
769 | 0 | } else if (m_port < 1000) { |
770 | 0 | expected_len += 3; |
771 | 0 | } else if (m_port < 10000) { |
772 | 0 | expected_len += 4; |
773 | 0 | } else { |
774 | 0 | expected_len += 5; |
775 | 0 | } |
776 | 0 | } |
777 | 0 | if (m_host_mime->m_len_value != expected_len) { |
778 | 0 | this->_fill_target_cache(); |
779 | 0 | } |
780 | 0 | } |
781 | 0 | } |
782 | | |
783 | | /*------------------------------------------------------------------------- |
784 | | -------------------------------------------------------------------------*/ |
785 | | |
786 | | inline std::string_view |
787 | | HTTPHdr::host_get() const |
788 | 0 | { |
789 | 0 | this->_test_and_fill_target_cache(); |
790 | 0 | if (m_target_in_url) { |
791 | 0 | return url_get()->host_get(); |
792 | 0 | } else if (m_host_mime) { |
793 | 0 | return std::string_view{m_host_mime->m_ptr_value, static_cast<std::string_view::size_type>(m_host_length)}; |
794 | 0 | } |
795 | | |
796 | 0 | return std::string_view{}; |
797 | 0 | } |
798 | | |
799 | | /*------------------------------------------------------------------------- |
800 | | -------------------------------------------------------------------------*/ |
801 | | |
802 | | inline int |
803 | | HTTPHdr::port_get() |
804 | 0 | { |
805 | 0 | this->_test_and_fill_target_cache(); |
806 | 0 | return m_port; |
807 | 0 | } |
808 | | |
809 | | /*------------------------------------------------------------------------- |
810 | | -------------------------------------------------------------------------*/ |
811 | | |
812 | | inline bool |
813 | | HTTPHdr::is_target_in_url() const |
814 | 0 | { |
815 | 0 | this->_test_and_fill_target_cache(); |
816 | 0 | return m_target_in_url; |
817 | 0 | } |
818 | | |
819 | | /*------------------------------------------------------------------------- |
820 | | -------------------------------------------------------------------------*/ |
821 | | |
822 | | inline bool |
823 | | HTTPHdr::is_port_in_header() const |
824 | 0 | { |
825 | 0 | this->_test_and_fill_target_cache(); |
826 | 0 | return m_port_in_header; |
827 | 0 | } |
828 | | |
829 | | /*------------------------------------------------------------------------- |
830 | | -------------------------------------------------------------------------*/ |
831 | | |
832 | | inline void |
833 | | HTTPHdr::mark_target_dirty() const |
834 | 0 | { |
835 | 0 | m_target_cached = false; |
836 | 0 | } |
837 | | /*------------------------------------------------------------------------- |
838 | | -------------------------------------------------------------------------*/ |
839 | | |
840 | | inline HTTPType |
841 | | http_hdr_type_get(HTTPHdrImpl *hh) |
842 | 0 | { |
843 | 0 | return (hh->m_polarity); |
844 | 0 | } |
845 | | |
846 | | /*------------------------------------------------------------------------- |
847 | | -------------------------------------------------------------------------*/ |
848 | | |
849 | | inline HTTPType |
850 | | HTTPHdr::type_get() const |
851 | 0 | { |
852 | 0 | ink_assert(valid()); |
853 | 0 | return http_hdr_type_get(m_http); |
854 | 0 | } |
855 | | |
856 | | /*------------------------------------------------------------------------- |
857 | | -------------------------------------------------------------------------*/ |
858 | | |
859 | | inline HTTPVersion |
860 | | HTTPHdr::version_get() const |
861 | 0 | { |
862 | 0 | ink_assert(valid()); |
863 | 0 | return m_http->m_version; |
864 | 0 | } |
865 | | |
866 | | /*------------------------------------------------------------------------- |
867 | | -------------------------------------------------------------------------*/ |
868 | | |
869 | | inline static HTTPKeepAlive |
870 | | is_header_keep_alive(const HTTPVersion &http_version, const MIMEField *con_hdr) |
871 | 0 | { |
872 | 0 | enum class ConToken { |
873 | 0 | NONE = 0, |
874 | 0 | KEEP_ALIVE, |
875 | 0 | CLOSE, |
876 | 0 | }; |
877 | 0 |
|
878 | 0 | auto con_token = ConToken::NONE; |
879 | 0 | HTTPKeepAlive keep_alive = HTTPKeepAlive::NO_KEEPALIVE; |
880 | 0 | // *unknown_tokens = false; |
881 | 0 |
|
882 | 0 | if (con_hdr) { |
883 | 0 | if (con_hdr->value_get_index("keep-alive"sv) >= 0) |
884 | 0 | con_token = ConToken::KEEP_ALIVE; |
885 | 0 | else if (con_hdr->value_get_index("close"sv) >= 0) |
886 | 0 | con_token = ConToken::CLOSE; |
887 | 0 | } |
888 | 0 |
|
889 | 0 | if (HTTP_1_0 == http_version) { |
890 | 0 | keep_alive = (con_token == ConToken::KEEP_ALIVE) ? (HTTPKeepAlive::KEEPALIVE) : (HTTPKeepAlive::NO_KEEPALIVE); |
891 | 0 | } else if (HTTP_1_1 == http_version) { |
892 | 0 | // We deviate from the spec here. If the we got a response where |
893 | 0 | // where there is no Connection header and the request 1.0 was |
894 | 0 | // 1.0 don't treat this as keep-alive since Netscape-Enterprise/3.6 SP1 |
895 | 0 | // server doesn't |
896 | 0 | keep_alive = ((con_token == ConToken::KEEP_ALIVE) || (con_token == ConToken::NONE && HTTP_1_1 == http_version)) ? |
897 | 0 | (HTTPKeepAlive::KEEPALIVE) : |
898 | 0 | (HTTPKeepAlive::NO_KEEPALIVE); |
899 | 0 | } else { |
900 | 0 | keep_alive = HTTPKeepAlive::NO_KEEPALIVE; |
901 | 0 | } |
902 | 0 | return (keep_alive); |
903 | 0 | } Unexecuted instantiation: fuzz_hpack.cc:is_header_keep_alive(HTTPVersion const&, MIMEField const*) Unexecuted instantiation: HTTP2.cc:is_header_keep_alive(HTTPVersion const&, MIMEField const*) Unexecuted instantiation: Http2Frame.cc:is_header_keep_alive(HTTPVersion const&, MIMEField const*) Unexecuted instantiation: HPACK.cc:is_header_keep_alive(HTTPVersion const&, MIMEField const*) Unexecuted instantiation: HTTP.cc:is_header_keep_alive(HTTPVersion const&, MIMEField const*) Unexecuted instantiation: HdrHeap.cc:is_header_keep_alive(HTTPVersion const&, MIMEField const*) Unexecuted instantiation: HdrToken.cc:is_header_keep_alive(HTTPVersion const&, MIMEField const*) Unexecuted instantiation: URL.cc:is_header_keep_alive(HTTPVersion const&, MIMEField const*) Unexecuted instantiation: VersionConverter.cc:is_header_keep_alive(HTTPVersion const&, MIMEField const*) Unexecuted instantiation: HeaderValidator.cc:is_header_keep_alive(HTTPVersion const&, MIMEField const*) |
904 | | |
905 | | inline HTTPKeepAlive |
906 | | HTTPHdr::keep_alive_get() const |
907 | 0 | { |
908 | 0 | HTTPKeepAlive retval = HTTPKeepAlive::NO_KEEPALIVE; |
909 | 0 | const MIMEField *pc = this->field_find(static_cast<std::string_view>(MIME_FIELD_PROXY_CONNECTION)); |
910 | 0 | if (pc != nullptr) { |
911 | 0 | retval = is_header_keep_alive(this->version_get(), pc); |
912 | 0 | } else { |
913 | 0 | const MIMEField *c = this->field_find(static_cast<std::string_view>(MIME_FIELD_CONNECTION)); |
914 | 0 | retval = is_header_keep_alive(this->version_get(), c); |
915 | 0 | } |
916 | 0 | return retval; |
917 | 0 | } |
918 | | |
919 | | inline bool |
920 | | HTTPHdr::is_keep_alive_set() const |
921 | 0 | { |
922 | 0 | return this->keep_alive_get() == HTTPKeepAlive::KEEPALIVE; |
923 | 0 | } |
924 | | |
925 | | /** |
926 | | Check the status code is informational and expecting final response |
927 | | - e.g. "100 Continue", "103 Early Hints" |
928 | | |
929 | | Please note that "101 Switching Protocol" is not included. |
930 | | */ |
931 | | inline bool |
932 | | HTTPHdr::expect_final_response() const |
933 | 0 | { |
934 | 0 | switch (this->status_get()) { |
935 | 0 | case HTTPStatus::CONTINUE: |
936 | 0 | case HTTPStatus::EARLY_HINTS: |
937 | 0 | return true; |
938 | 0 | default: |
939 | 0 | return false; |
940 | 0 | } |
941 | 0 | } |
942 | | |
943 | | /*------------------------------------------------------------------------- |
944 | | -------------------------------------------------------------------------*/ |
945 | | |
946 | | inline void |
947 | | HTTPHdr::version_set(HTTPVersion version) |
948 | 0 | { |
949 | 0 | ink_assert(valid()); |
950 | 0 | http_hdr_version_set(m_http, version); |
951 | 0 | } |
952 | | |
953 | | /*------------------------------------------------------------------------- |
954 | | -------------------------------------------------------------------------*/ |
955 | | |
956 | | inline std::string_view |
957 | | HTTPHdr::method_get() |
958 | 0 | { |
959 | 0 | ink_assert(valid()); |
960 | 0 | ink_assert(m_http->m_polarity == HTTPType::REQUEST); |
961 | |
|
962 | 0 | return http_hdr_method_get(m_http); |
963 | 0 | } |
964 | | |
965 | | inline int |
966 | | HTTPHdr::method_get_wksidx() const |
967 | 0 | { |
968 | 0 | ink_assert(valid()); |
969 | 0 | ink_assert(m_http->m_polarity == HTTPType::REQUEST); |
970 | 0 |
|
971 | 0 | return (m_http->u.req.m_method_wks_idx); |
972 | 0 | } |
973 | | |
974 | | /*------------------------------------------------------------------------- |
975 | | -------------------------------------------------------------------------*/ |
976 | | |
977 | | inline void |
978 | | HTTPHdr::method_set(std::string_view value) |
979 | 0 | { |
980 | 0 | ink_assert(valid()); |
981 | 0 | ink_assert(m_http->m_polarity == HTTPType::REQUEST); |
982 | |
|
983 | 0 | int method_wks_idx = hdrtoken_tokenize(value.data(), static_cast<int>(value.length())); |
984 | 0 | http_hdr_method_set(m_heap, m_http, value, method_wks_idx, true); |
985 | 0 | } |
986 | | |
987 | | /*------------------------------------------------------------------------- |
988 | | -------------------------------------------------------------------------*/ |
989 | | |
990 | | inline URL * |
991 | | HTTPHdr::url_create(URL *u) |
992 | 0 | { |
993 | 0 | ink_assert(valid()); |
994 | 0 | ink_assert(m_http->m_polarity == HTTPType::REQUEST); |
995 | 0 |
|
996 | 0 | u->set(this); |
997 | 0 | u->create(m_heap); |
998 | 0 | return (u); |
999 | 0 | } |
1000 | | |
1001 | | /*------------------------------------------------------------------------- |
1002 | | -------------------------------------------------------------------------*/ |
1003 | | |
1004 | | inline URL * |
1005 | | HTTPHdr::url_get() const |
1006 | 0 | { |
1007 | 0 | ink_assert(valid()); |
1008 | 0 | ink_assert(m_http->m_polarity == HTTPType::REQUEST); |
1009 | | |
1010 | | // It's entirely possible that someone changed URL in our impl |
1011 | | // without updating the cached copy in the C++ layer. Check |
1012 | | // to see if this happened before handing back the url |
1013 | |
|
1014 | 0 | URLImpl *real_impl = m_http->u.req.m_url_impl; |
1015 | 0 | if (m_url_cached.m_url_impl != real_impl) { |
1016 | 0 | m_url_cached.set(this); |
1017 | 0 | m_url_cached.m_url_impl = real_impl; |
1018 | 0 | this->mark_target_dirty(); |
1019 | 0 | } |
1020 | 0 | return (&m_url_cached); |
1021 | 0 | } |
1022 | | |
1023 | | /*------------------------------------------------------------------------- |
1024 | | -------------------------------------------------------------------------*/ |
1025 | | |
1026 | | inline URL * |
1027 | | HTTPHdr::url_get(URL *url) |
1028 | 0 | { |
1029 | 0 | ink_assert(valid()); |
1030 | 0 | ink_assert(m_http->m_polarity == HTTPType::REQUEST); |
1031 | 0 |
|
1032 | 0 | url->set(this); // attach refcount |
1033 | 0 | url->m_url_impl = m_http->u.req.m_url_impl; |
1034 | 0 | return (url); |
1035 | 0 | } |
1036 | | |
1037 | | /*------------------------------------------------------------------------- |
1038 | | -------------------------------------------------------------------------*/ |
1039 | | |
1040 | | inline void |
1041 | | HTTPHdr::url_set(URL *url) |
1042 | 0 | { |
1043 | 0 | ink_assert(valid()); |
1044 | 0 | ink_assert(m_http->m_polarity == HTTPType::REQUEST); |
1045 | 0 |
|
1046 | 0 | URLImpl *url_impl = m_http->u.req.m_url_impl; |
1047 | 0 | ::url_copy_onto(url->m_url_impl, url->m_heap, url_impl, m_heap, true); |
1048 | 0 | } |
1049 | | |
1050 | | /*------------------------------------------------------------------------- |
1051 | | -------------------------------------------------------------------------*/ |
1052 | | |
1053 | | inline void |
1054 | | HTTPHdr::url_set(std::string_view value) |
1055 | 0 | { |
1056 | 0 | URLImpl *url_impl; |
1057 | 0 |
|
1058 | 0 | ink_assert(valid()); |
1059 | 0 | ink_assert(m_http->m_polarity == HTTPType::REQUEST); |
1060 | 0 |
|
1061 | 0 | url_impl = m_http->u.req.m_url_impl; |
1062 | 0 | ::url_clear(url_impl); |
1063 | 0 | const char *str{value.data()}; |
1064 | 0 | ::url_parse(m_heap, url_impl, &str, str + value.length(), true); |
1065 | 0 | } |
1066 | | |
1067 | | /*------------------------------------------------------------------------- |
1068 | | -------------------------------------------------------------------------*/ |
1069 | | |
1070 | | inline HTTPStatus |
1071 | | http_hdr_status_get(HTTPHdrImpl const *hh) |
1072 | 0 | { |
1073 | 0 | ink_assert(hh->m_polarity == HTTPType::RESPONSE); |
1074 | 0 | return (HTTPStatus)hh->u.resp.m_status; |
1075 | 0 | } |
1076 | | |
1077 | | /*------------------------------------------------------------------------- |
1078 | | -------------------------------------------------------------------------*/ |
1079 | | |
1080 | | inline HTTPStatus |
1081 | | HTTPHdr::status_get() const |
1082 | 0 | { |
1083 | 0 | ink_assert(valid()); |
1084 | |
|
1085 | 0 | if (m_http) { |
1086 | 0 | ink_assert(m_http->m_polarity == HTTPType::RESPONSE); |
1087 | 0 | return http_hdr_status_get(m_http); |
1088 | 0 | } |
1089 | | |
1090 | 0 | return HTTPStatus::NONE; |
1091 | 0 | } |
1092 | | |
1093 | | /*------------------------------------------------------------------------- |
1094 | | -------------------------------------------------------------------------*/ |
1095 | | |
1096 | | inline void |
1097 | | HTTPHdr::status_set(HTTPStatus status) |
1098 | 0 | { |
1099 | 0 | ink_assert(valid()); |
1100 | 0 | ink_assert(m_http->m_polarity == HTTPType::RESPONSE); |
1101 | |
|
1102 | 0 | http_hdr_status_set(m_http, status); |
1103 | 0 | } |
1104 | | |
1105 | | /*------------------------------------------------------------------------- |
1106 | | -------------------------------------------------------------------------*/ |
1107 | | |
1108 | | inline std::string_view |
1109 | | HTTPHdr::reason_get() |
1110 | 0 | { |
1111 | 0 | ink_assert(valid()); |
1112 | 0 | ink_assert(m_http->m_polarity == HTTPType::RESPONSE); |
1113 | 0 |
|
1114 | 0 | return http_hdr_reason_get(m_http); |
1115 | 0 | } |
1116 | | |
1117 | | /*------------------------------------------------------------------------- |
1118 | | -------------------------------------------------------------------------*/ |
1119 | | |
1120 | | inline void |
1121 | | HTTPHdr::reason_set(std::string_view value) |
1122 | 0 | { |
1123 | 0 | ink_assert(valid()); |
1124 | 0 | ink_assert(m_http->m_polarity == HTTPType::RESPONSE); |
1125 | 0 |
|
1126 | 0 | http_hdr_reason_set(m_heap, m_http, value, true); |
1127 | 0 | } |
1128 | | |
1129 | | /*------------------------------------------------------------------------- |
1130 | | -------------------------------------------------------------------------*/ |
1131 | | |
1132 | | inline void |
1133 | | HTTPHdr::mark_early_data(bool flag) const |
1134 | 0 | { |
1135 | 0 | ink_assert(valid()); |
1136 | 0 | early_data = flag; |
1137 | 0 | } |
1138 | | |
1139 | | /*------------------------------------------------------------------------- |
1140 | | -------------------------------------------------------------------------*/ |
1141 | | |
1142 | | inline bool |
1143 | | HTTPHdr::is_early_data() const |
1144 | 0 | { |
1145 | 0 | ink_assert(valid()); |
1146 | 0 | return early_data; |
1147 | 0 | } |
1148 | | |
1149 | | /*------------------------------------------------------------------------- |
1150 | | -------------------------------------------------------------------------*/ |
1151 | | |
1152 | | inline ParseResult |
1153 | | HTTPHdr::parse_req(HTTPParser *parser, const char **start, const char *end, bool eof, int strict_uri_parsing, |
1154 | | size_t max_request_line_size, size_t max_hdr_field_size) |
1155 | 0 | { |
1156 | 0 | ink_assert(valid()); |
1157 | 0 | ink_assert(m_http->m_polarity == HTTPType::REQUEST); |
1158 | 0 |
|
1159 | 0 | return http_parser_parse_req(parser, m_heap, m_http, start, end, true, eof, strict_uri_parsing, max_request_line_size, |
1160 | 0 | max_hdr_field_size); |
1161 | 0 | } |
1162 | | |
1163 | | /*------------------------------------------------------------------------- |
1164 | | -------------------------------------------------------------------------*/ |
1165 | | |
1166 | | inline ParseResult |
1167 | | HTTPHdr::parse_resp(HTTPParser *parser, const char **start, const char *end, bool eof) |
1168 | 0 | { |
1169 | 0 | ink_assert(valid()); |
1170 | 0 | ink_assert(m_http->m_polarity == HTTPType::RESPONSE); |
1171 | 0 |
|
1172 | 0 | return http_parser_parse_resp(parser, m_heap, m_http, start, end, true, eof); |
1173 | 0 | } |
1174 | | |
1175 | | /*------------------------------------------------------------------------- |
1176 | | -------------------------------------------------------------------------*/ |
1177 | | |
1178 | | inline bool |
1179 | | HTTPHdr::is_cache_control_set(const char *cc_directive_wks) |
1180 | 0 | { |
1181 | 0 | ink_assert(valid()); |
1182 | 0 | ink_assert(hdrtoken_is_wks(cc_directive_wks)); |
1183 | 0 |
|
1184 | 0 | const HdrTokenHeapPrefix *prefix = hdrtoken_wks_to_prefix(cc_directive_wks); |
1185 | 0 | ink_assert(prefix->wks_token_type == HdrTokenType::CACHE_CONTROL); |
1186 | 0 |
|
1187 | 0 | uint32_t cc_mask = prefix->wks_type_specific.u.cache_control.cc_mask; |
1188 | 0 | if (get_cooked_cc_mask() & cc_mask) |
1189 | 0 | return (true); |
1190 | 0 | else |
1191 | 0 | return (false); |
1192 | 0 | } |
1193 | | |
1194 | | /*------------------------------------------------------------------------- |
1195 | | -------------------------------------------------------------------------*/ |
1196 | | |
1197 | | inline bool |
1198 | | HTTPHdr::is_pragma_no_cache_set() |
1199 | 0 | { |
1200 | 0 | ink_assert(valid()); |
1201 | 0 | return (get_cooked_pragma_no_cache()); |
1202 | 0 | } |
1203 | | |
1204 | | inline char * |
1205 | | HTTPHdr::url_string_get_ref(int *length) |
1206 | 0 | { |
1207 | 0 | return this->url_string_get(USE_HDR_HEAP_MAGIC, length); |
1208 | 0 | } |
1209 | | |
1210 | | inline std::string_view |
1211 | | HTTPHdr::path_get() |
1212 | 0 | { |
1213 | 0 | URL *url = this->url_get(); |
1214 | 0 | if (url) { |
1215 | 0 | return url->path_get(); |
1216 | 0 | } |
1217 | 0 | return {}; |
1218 | 0 | } |
1219 | | |
1220 | | inline std::string_view |
1221 | | HTTPHdr::query_get() |
1222 | 0 | { |
1223 | 0 | URL *url = this->url_get(); |
1224 | 0 | if (url) { |
1225 | 0 | return url->query_get(); |
1226 | 0 | } |
1227 | 0 | return std::string_view{}; |
1228 | 0 | } |
1229 | | |
1230 | | inline std::string_view |
1231 | | HTTPHdr::fragment_get() |
1232 | 0 | { |
1233 | 0 | URL *url = this->url_get(); |
1234 | 0 | if (url) { |
1235 | 0 | return url->fragment_get(); |
1236 | 0 | } |
1237 | 0 | return std::string_view{}; |
1238 | 0 | } |
1239 | | |
1240 | | inline std::string_view |
1241 | | HTTPHdr::scheme_get() |
1242 | 0 | { |
1243 | 0 | URL *url = this->url_get(); |
1244 | 0 | if (url) { |
1245 | 0 | return url->scheme_get(); |
1246 | 0 | } |
1247 | 0 | return std::string_view{}; |
1248 | 0 | } |
1249 | | |
1250 | | /*------------------------------------------------------------------------- |
1251 | | -------------------------------------------------------------------------*/ |
1252 | | |
1253 | | enum class CacheAltMagic : uint32_t { |
1254 | | ALIVE = 0xabcddeed, |
1255 | | MARSHALED = 0xdcbadeed, |
1256 | | DEAD = 0xdeadeed, |
1257 | | }; |
1258 | | |
1259 | | // struct HTTPCacheAlt |
1260 | | struct HTTPCacheAlt { |
1261 | | HTTPCacheAlt(); |
1262 | | void copy(HTTPCacheAlt *to_copy); |
1263 | | void copy_frag_offsets_from(HTTPCacheAlt *src); |
1264 | | void destroy(); |
1265 | | |
1266 | | CacheAltMagic m_magic = CacheAltMagic::ALIVE; |
1267 | | |
1268 | | // Writeable is set to true is we reside |
1269 | | // in a buffer owned by this structure. |
1270 | | // INVARIANT: if own the buffer this HttpCacheAlt |
1271 | | // we also own the buffers for the request & |
1272 | | // response headers |
1273 | | int32_t m_writeable = 1; |
1274 | | int32_t m_unmarshal_len = -1; |
1275 | | |
1276 | | int32_t m_id = -1; |
1277 | | int32_t m_rid = -1; |
1278 | | |
1279 | | int32_t m_object_key[sizeof(CryptoHash) / sizeof(int32_t)]; |
1280 | | int32_t m_object_size[2]; |
1281 | | |
1282 | | HTTPHdr m_request_hdr; |
1283 | | HTTPHdr m_response_hdr; |
1284 | | |
1285 | | time_t m_request_sent_time = 0; |
1286 | | time_t m_response_received_time = 0; |
1287 | | |
1288 | | /// # of fragment offsets in this alternate. |
1289 | | /// @note This is one less than the number of fragments. |
1290 | | int m_frag_offset_count = 0; |
1291 | | /// Type of offset for a fragment. |
1292 | | using FragOffset = uint64_t; |
1293 | | /// Table of fragment offsets. |
1294 | | /// @note The offsets are forward looking so that frag[0] is the |
1295 | | /// first byte past the end of fragment 0 which is also the first |
1296 | | /// byte of fragment 1. For this reason there is no fragment offset |
1297 | | /// for the last fragment. |
1298 | | FragOffset *m_frag_offsets = nullptr; |
1299 | | /// # of fragment offsets built in to object. |
1300 | | static int constexpr N_INTEGRAL_FRAG_OFFSETS = 4; |
1301 | | /// Integral fragment offset table. |
1302 | | FragOffset m_integral_frag_offsets[N_INTEGRAL_FRAG_OFFSETS]; |
1303 | | |
1304 | | // With clustering, our alt may be in cluster |
1305 | | // incoming channel buffer, when we are |
1306 | | // destroyed we decrement the refcount |
1307 | | // on that buffer so that it gets destroyed |
1308 | | // We don't want to use a ref count ptr (Ptr<>) |
1309 | | // since our ownership model requires explicit |
1310 | | // destroys and ref count pointers defeat this |
1311 | | RefCountObj *m_ext_buffer = nullptr; |
1312 | | }; |
1313 | | |
1314 | | class HTTPInfo |
1315 | | { |
1316 | | public: |
1317 | | using FragOffset = HTTPCacheAlt::FragOffset; ///< Import type. |
1318 | | |
1319 | | HTTPCacheAlt *m_alt = nullptr; |
1320 | | |
1321 | 0 | HTTPInfo() {} |
1322 | 0 | ~HTTPInfo() { clear(); } |
1323 | | void |
1324 | | clear() |
1325 | 0 | { |
1326 | 0 | m_alt = nullptr; |
1327 | 0 | } |
1328 | | bool |
1329 | | valid() const |
1330 | 0 | { |
1331 | 0 | return m_alt != nullptr; |
1332 | 0 | } |
1333 | | |
1334 | | void create(); |
1335 | | void destroy(); |
1336 | | |
1337 | | void copy(HTTPInfo *to_copy); |
1338 | | void |
1339 | | copy_shallow(HTTPInfo *info) |
1340 | 0 | { |
1341 | 0 | m_alt = info->m_alt; |
1342 | 0 | } |
1343 | | void copy_frag_offsets_from(HTTPInfo *src); |
1344 | | HTTPInfo &operator=(const HTTPInfo &m); |
1345 | | |
1346 | | int marshal_length(); |
1347 | | int marshal(char *buf, int len); |
1348 | | static int unmarshal(char *buf, int len, RefCountObj *block_ref); |
1349 | | static int unmarshal_v24_1(char *buf, int len, RefCountObj *block_ref); |
1350 | | void set_buffer_reference(RefCountObj *block_ref); |
1351 | | int get_handle(char *buf, int len); |
1352 | | |
1353 | | int32_t |
1354 | | id_get() const |
1355 | 0 | { |
1356 | 0 | return m_alt->m_id; |
1357 | 0 | } |
1358 | | int32_t |
1359 | | rid_get() |
1360 | 0 | { |
1361 | 0 | return m_alt->m_rid; |
1362 | 0 | } |
1363 | | |
1364 | | void |
1365 | | id_set(int32_t id) |
1366 | 0 | { |
1367 | 0 | m_alt->m_id = id; |
1368 | 0 | } |
1369 | | void |
1370 | | rid_set(int32_t id) |
1371 | 0 | { |
1372 | 0 | m_alt->m_rid = id; |
1373 | 0 | } |
1374 | | |
1375 | | CryptoHash object_key_get(); |
1376 | | void object_key_get(CryptoHash *); |
1377 | | bool compare_object_key(const CryptoHash *); |
1378 | | int64_t object_size_get(); |
1379 | | |
1380 | | void |
1381 | | request_get(HTTPHdr *hdr) |
1382 | 0 | { |
1383 | 0 | hdr->copy_shallow(&m_alt->m_request_hdr); |
1384 | 0 | } |
1385 | | void |
1386 | | response_get(HTTPHdr *hdr) |
1387 | 0 | { |
1388 | 0 | hdr->copy_shallow(&m_alt->m_response_hdr); |
1389 | 0 | } |
1390 | | |
1391 | | HTTPHdr * |
1392 | | request_get() |
1393 | 0 | { |
1394 | 0 | return &m_alt->m_request_hdr; |
1395 | 0 | } |
1396 | | HTTPHdr * |
1397 | | response_get() |
1398 | 0 | { |
1399 | 0 | return &m_alt->m_response_hdr; |
1400 | 0 | } |
1401 | | |
1402 | | URL * |
1403 | | request_url_get(URL *url = nullptr) |
1404 | 0 | { |
1405 | 0 | return m_alt->m_request_hdr.url_get(url); |
1406 | 0 | } |
1407 | | |
1408 | | time_t |
1409 | | request_sent_time_get() |
1410 | 0 | { |
1411 | 0 | return m_alt->m_request_sent_time; |
1412 | 0 | } |
1413 | | time_t |
1414 | | response_received_time_get() |
1415 | 0 | { |
1416 | 0 | return m_alt->m_response_received_time; |
1417 | 0 | } |
1418 | | |
1419 | | void object_key_set(CryptoHash &hash); |
1420 | | void object_size_set(int64_t size); |
1421 | | |
1422 | | void |
1423 | | request_set(const HTTPHdr *req) |
1424 | 0 | { |
1425 | 0 | m_alt->m_request_hdr.copy(req); |
1426 | 0 | } |
1427 | | void |
1428 | | response_set(const HTTPHdr *resp) |
1429 | 0 | { |
1430 | 0 | m_alt->m_response_hdr.copy(resp); |
1431 | 0 | } |
1432 | | |
1433 | | void |
1434 | | request_sent_time_set(time_t t) |
1435 | 0 | { |
1436 | 0 | m_alt->m_request_sent_time = t; |
1437 | 0 | } |
1438 | | void |
1439 | | response_received_time_set(time_t t) |
1440 | 0 | { |
1441 | 0 | m_alt->m_response_received_time = t; |
1442 | 0 | } |
1443 | | |
1444 | | /// Get the fragment table. |
1445 | | FragOffset *get_frag_table(); |
1446 | | /// Get the # of fragment offsets |
1447 | | /// @note This is the size of the fragment offset table, and one less |
1448 | | /// than the actual # of fragments. |
1449 | | int get_frag_offset_count(); |
1450 | | /// Add an @a offset to the end of the fragment offset table. |
1451 | | void push_frag_offset(FragOffset offset); |
1452 | | |
1453 | | // Sanity check functions |
1454 | | static bool check_marshalled(char *buf, int len); |
1455 | | |
1456 | | private: |
1457 | | HTTPInfo(const HTTPInfo &h); |
1458 | | }; |
1459 | | |
1460 | | inline void |
1461 | | HTTPInfo::destroy() |
1462 | 0 | { |
1463 | 0 | if (m_alt) { |
1464 | 0 | if (m_alt->m_writeable) { |
1465 | 0 | m_alt->destroy(); |
1466 | 0 | } else if (m_alt->m_ext_buffer) { |
1467 | 0 | if (m_alt->m_ext_buffer->refcount_dec() == 0) { |
1468 | 0 | m_alt->m_ext_buffer->free(); |
1469 | 0 | } |
1470 | 0 | } |
1471 | 0 | } |
1472 | 0 | clear(); |
1473 | 0 | } |
1474 | | |
1475 | | inline HTTPInfo & |
1476 | | HTTPInfo::operator=(const HTTPInfo &m) |
1477 | 0 | { |
1478 | 0 | m_alt = m.m_alt; |
1479 | 0 | return *this; |
1480 | 0 | } |
1481 | | |
1482 | | inline CryptoHash |
1483 | | HTTPInfo::object_key_get() |
1484 | 0 | { |
1485 | 0 | CryptoHash val; |
1486 | 0 | int32_t *pi = reinterpret_cast<int32_t *>(&val); |
1487 | 0 |
|
1488 | 0 | memcpy(pi, m_alt->m_object_key, sizeof(CryptoHash)); |
1489 | 0 |
|
1490 | 0 | return val; |
1491 | 0 | } |
1492 | | |
1493 | | inline void |
1494 | | HTTPInfo::object_key_get(CryptoHash *hash) |
1495 | 0 | { |
1496 | 0 | int32_t *pi = reinterpret_cast<int32_t *>(hash); |
1497 | 0 | memcpy(pi, m_alt->m_object_key, CRYPTO_HASH_SIZE); |
1498 | 0 | } |
1499 | | |
1500 | | inline bool |
1501 | | HTTPInfo::compare_object_key(const CryptoHash *hash) |
1502 | 0 | { |
1503 | 0 | int32_t const *pi = reinterpret_cast<int32_t const *>(hash); |
1504 | 0 | return memcmp(pi, m_alt->m_object_key, CRYPTO_HASH_SIZE) == 0; |
1505 | 0 | } |
1506 | | |
1507 | | inline int64_t |
1508 | | HTTPInfo::object_size_get() |
1509 | 0 | { |
1510 | 0 | int64_t val = 0; // make gcc shut up. |
1511 | 0 | int32_t *pi = reinterpret_cast<int32_t *>(&val); |
1512 | 0 |
|
1513 | 0 | pi[0] = m_alt->m_object_size[0]; |
1514 | 0 | pi[1] = m_alt->m_object_size[1]; |
1515 | 0 | return val; |
1516 | 0 | } |
1517 | | |
1518 | | inline void |
1519 | | HTTPInfo::object_key_set(CryptoHash &hash) |
1520 | 0 | { |
1521 | 0 | int32_t *pi = reinterpret_cast<int32_t *>(&hash); |
1522 | 0 | memcpy(m_alt->m_object_key, pi, CRYPTO_HASH_SIZE); |
1523 | 0 | } |
1524 | | |
1525 | | inline void |
1526 | | HTTPInfo::object_size_set(int64_t size) |
1527 | 0 | { |
1528 | 0 | int32_t *pi = reinterpret_cast<int32_t *>(&size); |
1529 | 0 | m_alt->m_object_size[0] = pi[0]; |
1530 | 0 | m_alt->m_object_size[1] = pi[1]; |
1531 | 0 | } |
1532 | | |
1533 | | inline HTTPInfo::FragOffset * |
1534 | | HTTPInfo::get_frag_table() |
1535 | 0 | { |
1536 | 0 | return m_alt ? m_alt->m_frag_offsets : nullptr; |
1537 | 0 | } |
1538 | | |
1539 | | inline int |
1540 | | HTTPInfo::get_frag_offset_count() |
1541 | 0 | { |
1542 | 0 | return m_alt ? m_alt->m_frag_offset_count : 0; |
1543 | 0 | } |