/src/trafficserver/src/proxy/hdrs/MIME.cc
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 | | #include "tscore/ink_defs.h" |
25 | | #include "tscore/ink_platform.h" |
26 | | #include "tscore/ink_memory.h" |
27 | | #include <cassert> |
28 | | #include <cctype> |
29 | | #include <cstdio> |
30 | | #include <cstring> |
31 | | #include <cctype> |
32 | | #include <algorithm> |
33 | | #include <string_view> |
34 | | #include "proxy/hdrs/MIME.h" |
35 | | #include "proxy/hdrs/HdrHeap.h" |
36 | | #include "proxy/hdrs/HdrToken.h" |
37 | | #include "proxy/hdrs/HdrUtils.h" |
38 | | #include "proxy/hdrs/HttpCompat.h" |
39 | | |
40 | | using swoc::TextView; |
41 | | |
42 | | /*********************************************************************** |
43 | | * * |
44 | | * C O M P I L E O P T I O N S * |
45 | | * * |
46 | | ***********************************************************************/ |
47 | | #define TRACK_FIELD_FIND_CALLS 0 |
48 | | #define TRACK_COOKING 0 |
49 | | #define MIME_FORMAT_DATE_USE_LOOKUP_TABLE 1 |
50 | | |
51 | | /*********************************************************************** |
52 | | * * |
53 | | * C O N S T A N T S * |
54 | | * * |
55 | | ***********************************************************************/ |
56 | | |
57 | | namespace |
58 | | { |
59 | | constexpr std::array<std::string_view, 7> day_names = { |
60 | | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", |
61 | | }; |
62 | | |
63 | | constexpr std::array<std::string_view, 12> month_names = { |
64 | | "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", |
65 | | }; |
66 | | |
67 | | template <size_t count> |
68 | | consteval std::array<uint32_t, count> |
69 | | make_packed(const std::array<std::string_view, count> &names) |
70 | | { |
71 | | std::array<uint32_t, count> packed{}; |
72 | | |
73 | | auto tl = [](char c) -> char { return (c >= 'A' && c <= 'Z') ? (c + 32) : c; }; |
74 | | |
75 | | for (size_t i = 0; i < count; ++i) { |
76 | | const auto &sv = names[i]; |
77 | | const uint32_t c0 = tl(static_cast<unsigned char>(sv[0])); |
78 | | const uint32_t c1 = tl(static_cast<unsigned char>(sv[1])); |
79 | | const uint32_t c2 = tl(static_cast<unsigned char>(sv[2])); |
80 | | packed[i] = (c0 << 16) | (c1 << 8) | c2; |
81 | | } |
82 | | return packed; |
83 | | } |
84 | | |
85 | | constexpr std::array<uint32_t, day_names.size()> day_names_packed = make_packed(day_names); |
86 | | constexpr std::array<uint32_t, month_names.size()> month_names_packed = make_packed(month_names); |
87 | | |
88 | | // Case-insensitive match of first 3 characters of input string against array of names |
89 | | // Longer strings will match if their first 3 characters match - this is intentional for |
90 | | // matching non-standard day/month names like "Thursday" or "September". |
91 | | template <size_t count> |
92 | | __attribute__((always_inline)) constexpr int |
93 | | match_3char_ci(const std::string_view s, const std::array<uint32_t, count> &names_packed) |
94 | 0 | { |
95 | 0 | if (s.size() < 3) { |
96 | 0 | return -1; |
97 | 0 | } |
98 | | |
99 | 0 | auto tl = [](char c) -> char { return (c >= 'A' && c <= 'Z') ? (c + 32) : c; };Unexecuted instantiation: MIME.cc:(anonymous namespace)::match_3char_ci<7ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::array<unsigned int, 7ul> const&)::{lambda(char)#1}::operator()(char) constUnexecuted instantiation: MIME.cc:(anonymous namespace)::match_3char_ci<12ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::array<unsigned int, 12ul> const&)::{lambda(char)#1}::operator()(char) const |
100 | 0 | const uint32_t packed = (tl(s[0]) << 16) | (tl(s[1]) << 8) | tl(s[2]); |
101 | |
|
102 | 0 | for (size_t i = 0; i < count; i++) { |
103 | 0 | if (packed == names_packed[i]) { |
104 | 0 | return i; |
105 | 0 | } |
106 | 0 | } |
107 | 0 | return -1; |
108 | 0 | } Unexecuted instantiation: MIME.cc:int (anonymous namespace)::match_3char_ci<7ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::array<unsigned int, 7ul> const&) Unexecuted instantiation: MIME.cc:int (anonymous namespace)::match_3char_ci<12ul>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::array<unsigned int, 12ul> const&) |
109 | | } // namespace |
110 | | |
111 | | struct MDY { |
112 | | uint8_t m; |
113 | | uint8_t d; |
114 | | uint16_t y; |
115 | | }; |
116 | | |
117 | | static MDY *_days_to_mdy_fast_lookup_table = nullptr; |
118 | | static time_t _days_to_mdy_fast_lookup_table_first_day; |
119 | | static time_t _days_to_mdy_fast_lookup_table_last_day; |
120 | | |
121 | | /*********************************************************************** |
122 | | * * |
123 | | * G L O B A L S * |
124 | | * * |
125 | | ***********************************************************************/ |
126 | | c_str_view MIME_FIELD_ACCEPT; |
127 | | c_str_view MIME_FIELD_ACCEPT_CHARSET; |
128 | | c_str_view MIME_FIELD_ACCEPT_ENCODING; |
129 | | c_str_view MIME_FIELD_ACCEPT_LANGUAGE; |
130 | | c_str_view MIME_FIELD_ACCEPT_RANGES; |
131 | | c_str_view MIME_FIELD_AGE; |
132 | | c_str_view MIME_FIELD_ALLOW; |
133 | | c_str_view MIME_FIELD_APPROVED; |
134 | | c_str_view MIME_FIELD_AUTHORIZATION; |
135 | | c_str_view MIME_FIELD_BYTES; |
136 | | c_str_view MIME_FIELD_CACHE_CONTROL; |
137 | | c_str_view MIME_FIELD_CLIENT_IP; |
138 | | c_str_view MIME_FIELD_CONNECTION; |
139 | | c_str_view MIME_FIELD_CONTENT_BASE; |
140 | | c_str_view MIME_FIELD_CONTENT_ENCODING; |
141 | | c_str_view MIME_FIELD_CONTENT_LANGUAGE; |
142 | | c_str_view MIME_FIELD_CONTENT_LENGTH; |
143 | | c_str_view MIME_FIELD_CONTENT_LOCATION; |
144 | | c_str_view MIME_FIELD_CONTENT_MD5; |
145 | | c_str_view MIME_FIELD_CONTENT_RANGE; |
146 | | c_str_view MIME_FIELD_CONTENT_TYPE; |
147 | | c_str_view MIME_FIELD_CONTROL; |
148 | | c_str_view MIME_FIELD_COOKIE; |
149 | | c_str_view MIME_FIELD_DATE; |
150 | | c_str_view MIME_FIELD_DISTRIBUTION; |
151 | | c_str_view MIME_FIELD_ETAG; |
152 | | c_str_view MIME_FIELD_EXPECT; |
153 | | c_str_view MIME_FIELD_EXPIRES; |
154 | | c_str_view MIME_FIELD_FOLLOWUP_TO; |
155 | | c_str_view MIME_FIELD_FROM; |
156 | | c_str_view MIME_FIELD_HOST; |
157 | | c_str_view MIME_FIELD_IF_MATCH; |
158 | | c_str_view MIME_FIELD_IF_MODIFIED_SINCE; |
159 | | c_str_view MIME_FIELD_IF_NONE_MATCH; |
160 | | c_str_view MIME_FIELD_IF_RANGE; |
161 | | c_str_view MIME_FIELD_IF_UNMODIFIED_SINCE; |
162 | | c_str_view MIME_FIELD_KEEP_ALIVE; |
163 | | c_str_view MIME_FIELD_KEYWORDS; |
164 | | c_str_view MIME_FIELD_LAST_MODIFIED; |
165 | | c_str_view MIME_FIELD_LINES; |
166 | | c_str_view MIME_FIELD_LOCATION; |
167 | | c_str_view MIME_FIELD_MAX_FORWARDS; |
168 | | c_str_view MIME_FIELD_MESSAGE_ID; |
169 | | c_str_view MIME_FIELD_NEWSGROUPS; |
170 | | c_str_view MIME_FIELD_ORGANIZATION; |
171 | | c_str_view MIME_FIELD_PATH; |
172 | | c_str_view MIME_FIELD_PRAGMA; |
173 | | c_str_view MIME_FIELD_PROXY_AUTHENTICATE; |
174 | | c_str_view MIME_FIELD_PROXY_AUTHORIZATION; |
175 | | c_str_view MIME_FIELD_PROXY_CONNECTION; |
176 | | c_str_view MIME_FIELD_PUBLIC; |
177 | | c_str_view MIME_FIELD_RANGE; |
178 | | c_str_view MIME_FIELD_REFERENCES; |
179 | | c_str_view MIME_FIELD_REFERER; |
180 | | c_str_view MIME_FIELD_REPLY_TO; |
181 | | c_str_view MIME_FIELD_RETRY_AFTER; |
182 | | c_str_view MIME_FIELD_SENDER; |
183 | | c_str_view MIME_FIELD_SERVER; |
184 | | c_str_view MIME_FIELD_SET_COOKIE; |
185 | | c_str_view MIME_FIELD_STRICT_TRANSPORT_SECURITY; |
186 | | c_str_view MIME_FIELD_SUBJECT; |
187 | | c_str_view MIME_FIELD_SUMMARY; |
188 | | c_str_view MIME_FIELD_TE; |
189 | | c_str_view MIME_FIELD_TRANSFER_ENCODING; |
190 | | c_str_view MIME_FIELD_UPGRADE; |
191 | | c_str_view MIME_FIELD_USER_AGENT; |
192 | | c_str_view MIME_FIELD_VARY; |
193 | | c_str_view MIME_FIELD_VIA; |
194 | | c_str_view MIME_FIELD_WARNING; |
195 | | c_str_view MIME_FIELD_WWW_AUTHENTICATE; |
196 | | c_str_view MIME_FIELD_XREF; |
197 | | c_str_view MIME_FIELD_ATS_INTERNAL; |
198 | | c_str_view MIME_FIELD_X_ID; |
199 | | c_str_view MIME_FIELD_X_FORWARDED_FOR; |
200 | | c_str_view MIME_FIELD_FORWARDED; |
201 | | c_str_view MIME_FIELD_SEC_WEBSOCKET_KEY; |
202 | | c_str_view MIME_FIELD_SEC_WEBSOCKET_VERSION; |
203 | | c_str_view MIME_FIELD_HTTP2_SETTINGS; |
204 | | c_str_view MIME_FIELD_EARLY_DATA; |
205 | | |
206 | | c_str_view MIME_VALUE_BYTES; |
207 | | c_str_view MIME_VALUE_CHUNKED; |
208 | | c_str_view MIME_VALUE_CLOSE; |
209 | | c_str_view MIME_VALUE_COMPRESS; |
210 | | c_str_view MIME_VALUE_DEFLATE; |
211 | | c_str_view MIME_VALUE_GZIP; |
212 | | c_str_view MIME_VALUE_BROTLI; |
213 | | c_str_view MIME_VALUE_ZSTD; |
214 | | c_str_view MIME_VALUE_IDENTITY; |
215 | | c_str_view MIME_VALUE_KEEP_ALIVE; |
216 | | c_str_view MIME_VALUE_MAX_AGE; |
217 | | c_str_view MIME_VALUE_MAX_STALE; |
218 | | c_str_view MIME_VALUE_MIN_FRESH; |
219 | | c_str_view MIME_VALUE_MUST_REVALIDATE; |
220 | | c_str_view MIME_VALUE_NONE; |
221 | | c_str_view MIME_VALUE_NO_CACHE; |
222 | | c_str_view MIME_VALUE_NO_STORE; |
223 | | c_str_view MIME_VALUE_NO_TRANSFORM; |
224 | | c_str_view MIME_VALUE_ONLY_IF_CACHED; |
225 | | c_str_view MIME_VALUE_PRIVATE; |
226 | | c_str_view MIME_VALUE_PROXY_REVALIDATE; |
227 | | c_str_view MIME_VALUE_PUBLIC; |
228 | | c_str_view MIME_VALUE_S_MAXAGE; |
229 | | c_str_view MIME_VALUE_NEED_REVALIDATE_ONCE; |
230 | | c_str_view MIME_VALUE_WEBSOCKET; |
231 | | c_str_view MIME_VALUE_H2C; |
232 | | |
233 | | // Cache-control: extension "need-revalidate-once" is used internally by T.S. |
234 | | // to invalidate a document, and it is not returned/forwarded. |
235 | | // If a cached document has this extension set (ie, is invalidated), |
236 | | // then the T.S. needs to revalidate the document once before returning it. |
237 | | // After a successful revalidation, the extension will be removed by T.S. |
238 | | // To set or unset this directive should be done via the following two |
239 | | // function: |
240 | | // set_cooked_cc_need_revalidate_once() |
241 | | // unset_cooked_cc_need_revalidate_once() |
242 | | // To test, use regular Cache-control testing functions, eg, |
243 | | // is_cache_control_set(HTTP_VALUE_NEED_REVALIDATE_ONCE) |
244 | | |
245 | | int MIME_WKSIDX_ACCEPT; |
246 | | int MIME_WKSIDX_ACCEPT_CHARSET; |
247 | | int MIME_WKSIDX_ACCEPT_ENCODING; |
248 | | int MIME_WKSIDX_ACCEPT_LANGUAGE; |
249 | | int MIME_WKSIDX_ACCEPT_RANGES; |
250 | | int MIME_WKSIDX_AGE; |
251 | | int MIME_WKSIDX_ALLOW; |
252 | | int MIME_WKSIDX_APPROVED; |
253 | | int MIME_WKSIDX_AUTHORIZATION; |
254 | | int MIME_WKSIDX_BYTES; |
255 | | int MIME_WKSIDX_CACHE_CONTROL; |
256 | | int MIME_WKSIDX_CLIENT_IP; |
257 | | int MIME_WKSIDX_CONNECTION; |
258 | | int MIME_WKSIDX_CONTENT_BASE; |
259 | | int MIME_WKSIDX_CONTENT_ENCODING; |
260 | | int MIME_WKSIDX_CONTENT_LANGUAGE; |
261 | | int MIME_WKSIDX_CONTENT_LENGTH; |
262 | | int MIME_WKSIDX_CONTENT_LOCATION; |
263 | | int MIME_WKSIDX_CONTENT_MD5; |
264 | | int MIME_WKSIDX_CONTENT_RANGE; |
265 | | int MIME_WKSIDX_CONTENT_TYPE; |
266 | | int MIME_WKSIDX_CONTROL; |
267 | | int MIME_WKSIDX_COOKIE; |
268 | | int MIME_WKSIDX_DATE; |
269 | | int MIME_WKSIDX_DISTRIBUTION; |
270 | | int MIME_WKSIDX_ETAG; |
271 | | int MIME_WKSIDX_EXPECT; |
272 | | int MIME_WKSIDX_EXPIRES; |
273 | | int MIME_WKSIDX_FOLLOWUP_TO; |
274 | | int MIME_WKSIDX_FROM; |
275 | | int MIME_WKSIDX_HOST; |
276 | | int MIME_WKSIDX_IF_MATCH; |
277 | | int MIME_WKSIDX_IF_MODIFIED_SINCE; |
278 | | int MIME_WKSIDX_IF_NONE_MATCH; |
279 | | int MIME_WKSIDX_IF_RANGE; |
280 | | int MIME_WKSIDX_IF_UNMODIFIED_SINCE; |
281 | | int MIME_WKSIDX_KEEP_ALIVE; |
282 | | int MIME_WKSIDX_KEYWORDS; |
283 | | int MIME_WKSIDX_LAST_MODIFIED; |
284 | | int MIME_WKSIDX_LINES; |
285 | | int MIME_WKSIDX_LOCATION; |
286 | | int MIME_WKSIDX_MAX_FORWARDS; |
287 | | int MIME_WKSIDX_MESSAGE_ID; |
288 | | int MIME_WKSIDX_NEWSGROUPS; |
289 | | int MIME_WKSIDX_ORGANIZATION; |
290 | | int MIME_WKSIDX_PATH; |
291 | | int MIME_WKSIDX_PRAGMA; |
292 | | int MIME_WKSIDX_PROXY_AUTHENTICATE; |
293 | | int MIME_WKSIDX_PROXY_AUTHORIZATION; |
294 | | int MIME_WKSIDX_PROXY_CONNECTION; |
295 | | int MIME_WKSIDX_PUBLIC; |
296 | | int MIME_WKSIDX_RANGE; |
297 | | int MIME_WKSIDX_REFERENCES; |
298 | | int MIME_WKSIDX_REFERER; |
299 | | int MIME_WKSIDX_REPLY_TO; |
300 | | int MIME_WKSIDX_RETRY_AFTER; |
301 | | int MIME_WKSIDX_SENDER; |
302 | | int MIME_WKSIDX_SERVER; |
303 | | int MIME_WKSIDX_SET_COOKIE; |
304 | | int MIME_WKSIDX_STRICT_TRANSPORT_SECURITY; |
305 | | int MIME_WKSIDX_SUBJECT; |
306 | | int MIME_WKSIDX_SUMMARY; |
307 | | int MIME_WKSIDX_TE; |
308 | | int MIME_WKSIDX_TRANSFER_ENCODING; |
309 | | int MIME_WKSIDX_UPGRADE; |
310 | | int MIME_WKSIDX_USER_AGENT; |
311 | | int MIME_WKSIDX_VARY; |
312 | | int MIME_WKSIDX_VIA; |
313 | | int MIME_WKSIDX_WARNING; |
314 | | int MIME_WKSIDX_WWW_AUTHENTICATE; |
315 | | int MIME_WKSIDX_XREF; |
316 | | int MIME_WKSIDX_ATS_INTERNAL; |
317 | | int MIME_WKSIDX_X_ID; |
318 | | int MIME_WKSIDX_X_FORWARDED_FOR; |
319 | | int MIME_WKSIDX_FORWARDED; |
320 | | int MIME_WKSIDX_SEC_WEBSOCKET_KEY; |
321 | | int MIME_WKSIDX_SEC_WEBSOCKET_VERSION; |
322 | | int MIME_WKSIDX_HTTP2_SETTINGS; |
323 | | int MIME_WKSIDX_EARLY_DATA; |
324 | | |
325 | | namespace |
326 | | { |
327 | | DbgCtl dbg_ctl_http{"http"}; |
328 | | |
329 | | } // end anonymous namespace |
330 | | |
331 | | /*********************************************************************** |
332 | | * * |
333 | | * U T I L I T Y R O U T I N E S * |
334 | | * * |
335 | | ***********************************************************************/ |
336 | | inline static int |
337 | | is_digit(char c) |
338 | 0 | { |
339 | 0 | return ((c <= '9') && (c >= '0')); |
340 | 0 | } |
341 | | |
342 | | inline static int |
343 | | is_ws(char c) |
344 | 20.0k | { |
345 | 20.0k | return ((c == ParseRules::CHAR_SP) || (c == ParseRules::CHAR_HT)); |
346 | 20.0k | } |
347 | | |
348 | | /*********************************************************************** |
349 | | * * |
350 | | * P R E S E N C E B I T S * |
351 | | * * |
352 | | ***********************************************************************/ |
353 | | uint64_t |
354 | | mime_field_presence_mask(const char *well_known_str) |
355 | 2.67k | { |
356 | 2.67k | return hdrtoken_wks_to_mask(well_known_str); |
357 | 2.67k | } |
358 | | |
359 | | uint64_t |
360 | | mime_field_presence_mask(int well_known_str_index) |
361 | 0 | { |
362 | 0 | return hdrtoken_index_to_mask(well_known_str_index); |
363 | 0 | } |
364 | | |
365 | | int |
366 | | mime_field_presence_get(MIMEHdrImpl *h, const char *well_known_str) |
367 | 0 | { |
368 | 0 | uint64_t mask = mime_field_presence_mask(well_known_str); |
369 | 0 | return ((mask == 0) ? 1 : ((h->m_presence_bits & mask) == 0 ? 0 : 1)); |
370 | 0 | } |
371 | | |
372 | | int |
373 | | mime_field_presence_get(MIMEHdrImpl *h, int well_known_str_index) |
374 | 0 | { |
375 | 0 | const char *wks = hdrtoken_index_to_wks(well_known_str_index); |
376 | 0 | return mime_field_presence_get(h, wks); |
377 | 0 | } |
378 | | |
379 | | void |
380 | | mime_hdr_presence_set(MIMEHdrImpl *h, const char *well_known_str) |
381 | 2.56k | { |
382 | 2.56k | uint64_t mask = mime_field_presence_mask(well_known_str); |
383 | 2.56k | if (mask != 0) { |
384 | 2.44k | h->m_presence_bits |= mask; |
385 | 2.44k | } |
386 | 2.56k | } |
387 | | |
388 | | void |
389 | | mime_hdr_presence_set(MIMEHdrImpl *h, int well_known_str_index) |
390 | 2.56k | { |
391 | 2.56k | const char *wks = hdrtoken_index_to_wks(well_known_str_index); |
392 | 2.56k | mime_hdr_presence_set(h, wks); |
393 | 2.56k | } |
394 | | |
395 | | void |
396 | | mime_hdr_presence_unset(MIMEHdrImpl *h, const char *well_known_str) |
397 | 106 | { |
398 | 106 | uint64_t mask = mime_field_presence_mask(well_known_str); |
399 | 106 | if (mask != 0) { |
400 | 106 | h->m_presence_bits &= (~mask); |
401 | 106 | } |
402 | 106 | } |
403 | | |
404 | | void |
405 | | mime_hdr_presence_unset(MIMEHdrImpl *h, int well_known_str_index) |
406 | 106 | { |
407 | 106 | const char *wks = hdrtoken_index_to_wks(well_known_str_index); |
408 | 106 | mime_hdr_presence_unset(h, wks); |
409 | 106 | } |
410 | | |
411 | | /*********************************************************************** |
412 | | * * |
413 | | * S L O T A C C E L E R A T O R S * |
414 | | * * |
415 | | ***********************************************************************/ |
416 | | inline void |
417 | | mime_hdr_init_accelerators_and_presence_bits(MIMEHdrImpl *mh) |
418 | 18.7k | { |
419 | 18.7k | mh->m_presence_bits = 0; |
420 | 18.7k | mh->m_slot_accelerators[0] = 0xFFFFFFFF; |
421 | 18.7k | mh->m_slot_accelerators[1] = 0xFFFFFFFF; |
422 | 18.7k | mh->m_slot_accelerators[2] = 0xFFFFFFFF; |
423 | 18.7k | mh->m_slot_accelerators[3] = 0xFFFFFFFF; |
424 | 18.7k | } |
425 | | |
426 | | inline uint32_t |
427 | | mime_hdr_get_accelerator_slotnum(MIMEHdrImpl *mh, int32_t slot_id) |
428 | 4.08k | { |
429 | 4.08k | ink_assert((slot_id != MIME_SLOTID_NONE) && (slot_id < 32)); |
430 | | |
431 | 4.08k | uint32_t word_index = slot_id / 8; // 4 words of 8 slots |
432 | 4.08k | uint32_t word = mh->m_slot_accelerators[word_index]; // 8 slots of 4 bits each |
433 | 4.08k | uint32_t nybble = slot_id % 8; // which of the 8 nybbles? |
434 | 4.08k | uint32_t slot = ((word >> (nybble * 4)) & 15); // grab the 4 bit slotnum |
435 | 4.08k | return slot; |
436 | 4.08k | } |
437 | | |
438 | | inline void |
439 | | mime_hdr_set_accelerator_slotnum(MIMEHdrImpl *mh, int32_t slot_id, uint32_t slot_num) |
440 | 2.15k | { |
441 | 2.15k | ink_assert((slot_id != MIME_SLOTID_NONE) && (slot_id < 32)); |
442 | 2.15k | ink_assert(slot_num < 16); |
443 | | |
444 | 2.15k | uint32_t word_index = slot_id / 8; // 4 words of 8 slots |
445 | 2.15k | uint32_t word = mh->m_slot_accelerators[word_index]; // 8 slots of 4 bits each |
446 | 2.15k | uint32_t nybble = slot_id % 8; // which of the 8 nybbles? |
447 | 2.15k | uint32_t shift = nybble * 4; // shift in chunks of 4 bits |
448 | 2.15k | uint32_t mask = ~(MIME_FIELD_SLOTNUM_MASK << shift); // mask to zero out old slot |
449 | 2.15k | uint32_t graft = (slot_num << shift); // plug to insert into slot |
450 | 2.15k | uint32_t new_word = (word & mask) | graft; // new value |
451 | | |
452 | 2.15k | mh->m_slot_accelerators[word_index] = new_word; |
453 | 2.15k | } |
454 | | |
455 | | inline void |
456 | | mime_hdr_set_accelerators_and_presence_bits(MIMEHdrImpl *mh, MIMEField *field) |
457 | 36.6k | { |
458 | 36.6k | int slot_id; |
459 | 36.6k | ptrdiff_t slot_num; |
460 | 36.6k | if (field->m_wks_idx < 0) { |
461 | 34.0k | return; |
462 | 34.0k | } |
463 | | |
464 | 2.56k | ink_assert(mh); |
465 | | |
466 | 2.56k | mime_hdr_presence_set(mh, field->m_wks_idx); |
467 | | |
468 | 2.56k | slot_id = hdrtoken_index_to_slotid(field->m_wks_idx); |
469 | 2.56k | if (slot_id != MIME_SLOTID_NONE) { |
470 | 2.04k | if (mh->m_first_fblock.contains(field)) { |
471 | 1.25k | slot_num = (field - &(mh->m_first_fblock.m_field_slots[0])); |
472 | | // constains() assure that the field is in the block, and the calculated |
473 | | // slot_num will be between 0 and 15, which seem valid. |
474 | | // However, strangely, this function regards slot number 14 and 15 as |
475 | | // unknown for some reason that is not clear. It might be a bug. |
476 | | // The block below is left to keep the original behavior. See also TS-4316. |
477 | 1.25k | if (slot_num >= MIME_FIELD_SLOTNUM_UNKNOWN) { |
478 | 82 | slot_num = MIME_FIELD_SLOTNUM_UNKNOWN; |
479 | 82 | } |
480 | 1.25k | } else { |
481 | 788 | slot_num = MIME_FIELD_SLOTNUM_UNKNOWN; |
482 | 788 | } |
483 | 2.04k | mime_hdr_set_accelerator_slotnum(mh, slot_id, slot_num); |
484 | 2.04k | } |
485 | 2.56k | } |
486 | | |
487 | | inline void |
488 | | mime_hdr_unset_accelerators_and_presence_bits(MIMEHdrImpl *mh, MIMEField *field) |
489 | 106 | { |
490 | 106 | int slot_id; |
491 | 106 | if (field->m_wks_idx < 0) { |
492 | 0 | return; |
493 | 0 | } |
494 | | |
495 | 106 | mime_hdr_presence_unset(mh, field->m_wks_idx); |
496 | | |
497 | 106 | slot_id = hdrtoken_index_to_slotid(field->m_wks_idx); |
498 | 106 | if (slot_id != MIME_SLOTID_NONE) { |
499 | 106 | mime_hdr_set_accelerator_slotnum(mh, slot_id, MIME_FIELD_SLOTNUM_MAX); |
500 | 106 | } |
501 | 106 | } |
502 | | |
503 | | /// Reset data in the header. |
504 | | /// Clear all the presence bits and accelerators. |
505 | | /// Update all the m_wks_idx values, presence bits and accelerators. |
506 | | inline void |
507 | | mime_hdr_reset_accelerators_and_presence_bits(MIMEHdrImpl *mh) |
508 | 0 | { |
509 | 0 | mime_hdr_init_accelerators_and_presence_bits(mh); |
510 | |
|
511 | 0 | for (MIMEFieldBlockImpl *fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
512 | 0 | for (MIMEField *field = fblock->m_field_slots, *limit = field + fblock->m_freetop; field < limit; ++field) { |
513 | 0 | if (field->is_live()) { |
514 | 0 | field->m_wks_idx = hdrtoken_tokenize(field->m_ptr_name, field->m_len_name); |
515 | 0 | if (field->is_dup_head()) { |
516 | 0 | mime_hdr_set_accelerators_and_presence_bits(mh, field); |
517 | 0 | } |
518 | 0 | } |
519 | 0 | } |
520 | 0 | } |
521 | 0 | } |
522 | | |
523 | | int |
524 | | checksum_block(const char *s, int len) |
525 | 0 | { |
526 | 0 | int sum = 0; |
527 | 0 | while (len--) { |
528 | 0 | sum ^= *s++; |
529 | 0 | } |
530 | 0 | return sum; |
531 | 0 | } |
532 | | |
533 | | #ifdef ENABLE_MIME_SANITY_CHECK |
534 | | void |
535 | | mime_hdr_sanity_check(MIMEHdrImpl *mh) |
536 | | { |
537 | | MIMEFieldBlockImpl *fblock, *blk, *last_fblock; |
538 | | MIMEField *field, *next_dup; |
539 | | uint32_t slot_index, index; |
540 | | uint64_t masksum; |
541 | | |
542 | | ink_assert(mh != nullptr); |
543 | | |
544 | | masksum = 0; |
545 | | slot_index = 0; |
546 | | last_fblock = nullptr; |
547 | | |
548 | | for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
549 | | for (index = 0; index < fblock->m_freetop; index++) { |
550 | | field = &(fblock->m_field_slots[index]); |
551 | | |
552 | | if (field->is_live()) { |
553 | | // dummy operations just to make sure deref doesn't crash |
554 | | checksum_block(field->m_ptr_name, field->m_len_name); |
555 | | if (field->m_ptr_value) { |
556 | | checksum_block(field->m_ptr_value, field->m_len_value); |
557 | | } |
558 | | |
559 | | if (field->m_n_v_raw_printable) { |
560 | | int total_len = field->m_len_name + field->m_len_value + field->m_n_v_raw_printable_pad; |
561 | | checksum_block(field->m_ptr_name, total_len); |
562 | | } |
563 | | // walk the dup list, quickly checking each cell |
564 | | if (field->m_next_dup != nullptr) { |
565 | | int field_slotnum = mime_hdr_field_slotnum(mh, field); |
566 | | |
567 | | for (next_dup = field->m_next_dup; next_dup; next_dup = next_dup->m_next_dup) { |
568 | | int next_slotnum = mime_hdr_field_slotnum(mh, next_dup); |
569 | | ink_release_assert((next_dup->m_flags & MIME_FIELD_SLOT_FLAGS_DUP_HEAD) == 0); |
570 | | ink_release_assert((next_dup->m_readiness == MIME_FIELD_SLOT_READINESS_LIVE)); |
571 | | ink_release_assert(next_dup->m_wks_idx == field->m_wks_idx); |
572 | | ink_release_assert(next_dup->m_len_name == field->m_len_name); |
573 | | ink_release_assert(strncasecmp(field->m_ptr_name, next_dup->m_ptr_name, field->m_len_name) == 0); |
574 | | ink_release_assert(next_slotnum > field_slotnum); |
575 | | } |
576 | | } |
577 | | // if this is a well known string, check presence bits & slot accelerators |
578 | | if (field->m_wks_idx >= 0) { |
579 | | const char *wks = hdrtoken_index_to_wks(field->m_wks_idx); |
580 | | int len = hdrtoken_index_to_length(field->m_wks_idx); |
581 | | |
582 | | if (field->m_len_name != len || strncasecmp(field->m_ptr_name, wks, field->m_len_name) != 0) { |
583 | | Warning("Encountered WKS hash collision on '%.*s'", field->m_len_name, field->m_ptr_name); |
584 | | } |
585 | | |
586 | | uint64_t mask = mime_field_presence_mask(field->m_wks_idx); |
587 | | masksum |= mask; |
588 | | |
589 | | int32_t slot_id = hdrtoken_index_to_slotid(field->m_wks_idx); |
590 | | if ((slot_id != MIME_SLOTID_NONE) && (slot_index < MIME_FIELD_SLOTNUM_UNKNOWN) && |
591 | | (field->m_flags & MIME_FIELD_SLOT_FLAGS_DUP_HEAD)) { |
592 | | uint32_t slot_num = mime_hdr_get_accelerator_slotnum(mh, slot_id); |
593 | | if (slot_num <= 14) { |
594 | | ink_release_assert(slot_num == slot_index); |
595 | | } |
596 | | } |
597 | | } else { |
598 | | int idx = hdrtoken_tokenize(field->m_ptr_name, field->m_len_name, nullptr); |
599 | | ink_release_assert(idx < 0); |
600 | | } |
601 | | |
602 | | // verify that the next dup pointer points to a block in this list |
603 | | if (field->m_next_dup) { |
604 | | bool found = false; |
605 | | for (blk = &(mh->m_first_fblock); blk != nullptr; blk = blk->m_next) { |
606 | | const char *addr = reinterpret_cast<const char *>(field->m_next_dup); |
607 | | if ((addr >= reinterpret_cast<const char *>(blk)) && |
608 | | (addr < reinterpret_cast<const char *>(blk) + sizeof(MIMEFieldBlockImpl))) { |
609 | | found = true; |
610 | | break; |
611 | | } |
612 | | } |
613 | | ink_release_assert(found); |
614 | | } |
615 | | // re-find the field --- should always find the head dup |
616 | | MIMEField *mf = mime_hdr_field_find(mh, field->m_ptr_name, field->m_len_name); |
617 | | ink_release_assert(mf != nullptr); |
618 | | if (mf == field) { |
619 | | ink_release_assert((field->m_flags & MIME_FIELD_SLOT_FLAGS_DUP_HEAD) != 0); |
620 | | } else { |
621 | | ink_release_assert((field->m_flags & MIME_FIELD_SLOT_FLAGS_DUP_HEAD) == 0); |
622 | | } |
623 | | } |
624 | | |
625 | | ++slot_index; |
626 | | } |
627 | | last_fblock = fblock; |
628 | | } |
629 | | |
630 | | ink_release_assert(last_fblock == mh->m_fblock_list_tail); |
631 | | ink_release_assert(masksum == mh->m_presence_bits); |
632 | | } |
633 | | #endif |
634 | | |
635 | | void |
636 | | mime_init() |
637 | 1 | { |
638 | 1 | static int init = 1; |
639 | | |
640 | 1 | if (init) { |
641 | 1 | init = 0; |
642 | | |
643 | 1 | hdrtoken_init(); |
644 | | |
645 | 1 | MIME_FIELD_ACCEPT = hdrtoken_string_to_wks_sv("Accept"); |
646 | 1 | MIME_FIELD_ACCEPT_CHARSET = hdrtoken_string_to_wks_sv("Accept-Charset"); |
647 | 1 | MIME_FIELD_ACCEPT_ENCODING = hdrtoken_string_to_wks_sv("Accept-Encoding"); |
648 | 1 | MIME_FIELD_ACCEPT_LANGUAGE = hdrtoken_string_to_wks_sv("Accept-Language"); |
649 | 1 | MIME_FIELD_ACCEPT_RANGES = hdrtoken_string_to_wks_sv("Accept-Ranges"); |
650 | 1 | MIME_FIELD_AGE = hdrtoken_string_to_wks_sv("Age"); |
651 | 1 | MIME_FIELD_ALLOW = hdrtoken_string_to_wks_sv("Allow"); |
652 | 1 | MIME_FIELD_APPROVED = hdrtoken_string_to_wks_sv("Approved"); |
653 | 1 | MIME_FIELD_AUTHORIZATION = hdrtoken_string_to_wks_sv("Authorization"); |
654 | 1 | MIME_FIELD_BYTES = hdrtoken_string_to_wks_sv("Bytes"); |
655 | 1 | MIME_FIELD_CACHE_CONTROL = hdrtoken_string_to_wks_sv("Cache-Control"); |
656 | 1 | MIME_FIELD_CLIENT_IP = hdrtoken_string_to_wks_sv("Client-ip"); |
657 | 1 | MIME_FIELD_CONNECTION = hdrtoken_string_to_wks_sv("Connection"); |
658 | 1 | MIME_FIELD_CONTENT_BASE = hdrtoken_string_to_wks_sv("Content-Base"); |
659 | 1 | MIME_FIELD_CONTENT_ENCODING = hdrtoken_string_to_wks_sv("Content-Encoding"); |
660 | 1 | MIME_FIELD_CONTENT_LANGUAGE = hdrtoken_string_to_wks_sv("Content-Language"); |
661 | 1 | MIME_FIELD_CONTENT_LENGTH = hdrtoken_string_to_wks_sv("Content-Length"); |
662 | 1 | MIME_FIELD_CONTENT_LOCATION = hdrtoken_string_to_wks_sv("Content-Location"); |
663 | 1 | MIME_FIELD_CONTENT_MD5 = hdrtoken_string_to_wks_sv("Content-MD5"); |
664 | 1 | MIME_FIELD_CONTENT_RANGE = hdrtoken_string_to_wks_sv("Content-Range"); |
665 | 1 | MIME_FIELD_CONTENT_TYPE = hdrtoken_string_to_wks_sv("Content-Type"); |
666 | 1 | MIME_FIELD_CONTROL = hdrtoken_string_to_wks_sv("Control"); |
667 | 1 | MIME_FIELD_COOKIE = hdrtoken_string_to_wks_sv("Cookie"); |
668 | 1 | MIME_FIELD_DATE = hdrtoken_string_to_wks_sv("Date"); |
669 | 1 | MIME_FIELD_DISTRIBUTION = hdrtoken_string_to_wks_sv("Distribution"); |
670 | 1 | MIME_FIELD_ETAG = hdrtoken_string_to_wks_sv("Etag"); |
671 | 1 | MIME_FIELD_EXPECT = hdrtoken_string_to_wks_sv("Expect"); |
672 | 1 | MIME_FIELD_EXPIRES = hdrtoken_string_to_wks_sv("Expires"); |
673 | 1 | MIME_FIELD_FOLLOWUP_TO = hdrtoken_string_to_wks_sv("Followup-To"); |
674 | 1 | MIME_FIELD_FROM = hdrtoken_string_to_wks_sv("From"); |
675 | 1 | MIME_FIELD_HOST = hdrtoken_string_to_wks_sv("Host"); |
676 | 1 | MIME_FIELD_IF_MATCH = hdrtoken_string_to_wks_sv("If-Match"); |
677 | 1 | MIME_FIELD_IF_MODIFIED_SINCE = hdrtoken_string_to_wks_sv("If-Modified-Since"); |
678 | 1 | MIME_FIELD_IF_NONE_MATCH = hdrtoken_string_to_wks_sv("If-None-Match"); |
679 | 1 | MIME_FIELD_IF_RANGE = hdrtoken_string_to_wks_sv("If-Range"); |
680 | 1 | MIME_FIELD_IF_UNMODIFIED_SINCE = hdrtoken_string_to_wks_sv("If-Unmodified-Since"); |
681 | 1 | MIME_FIELD_KEEP_ALIVE = hdrtoken_string_to_wks_sv("Keep-Alive"); |
682 | 1 | MIME_FIELD_KEYWORDS = hdrtoken_string_to_wks_sv("Keywords"); |
683 | 1 | MIME_FIELD_LAST_MODIFIED = hdrtoken_string_to_wks_sv("Last-Modified"); |
684 | 1 | MIME_FIELD_LINES = hdrtoken_string_to_wks_sv("Lines"); |
685 | 1 | MIME_FIELD_LOCATION = hdrtoken_string_to_wks_sv("Location"); |
686 | 1 | MIME_FIELD_MAX_FORWARDS = hdrtoken_string_to_wks_sv("Max-Forwards"); |
687 | 1 | MIME_FIELD_MESSAGE_ID = hdrtoken_string_to_wks_sv("Message-ID"); |
688 | 1 | MIME_FIELD_NEWSGROUPS = hdrtoken_string_to_wks_sv("Newsgroups"); |
689 | 1 | MIME_FIELD_ORGANIZATION = hdrtoken_string_to_wks_sv("Organization"); |
690 | 1 | MIME_FIELD_PATH = hdrtoken_string_to_wks_sv("Path"); |
691 | 1 | MIME_FIELD_PRAGMA = hdrtoken_string_to_wks_sv("Pragma"); |
692 | 1 | MIME_FIELD_PROXY_AUTHENTICATE = hdrtoken_string_to_wks_sv("Proxy-Authenticate"); |
693 | 1 | MIME_FIELD_PROXY_AUTHORIZATION = hdrtoken_string_to_wks_sv("Proxy-Authorization"); |
694 | 1 | MIME_FIELD_PROXY_CONNECTION = hdrtoken_string_to_wks_sv("Proxy-Connection"); |
695 | 1 | MIME_FIELD_PUBLIC = hdrtoken_string_to_wks_sv("Public"); |
696 | 1 | MIME_FIELD_RANGE = hdrtoken_string_to_wks_sv("Range"); |
697 | 1 | MIME_FIELD_REFERENCES = hdrtoken_string_to_wks_sv("References"); |
698 | 1 | MIME_FIELD_REFERER = hdrtoken_string_to_wks_sv("Referer"); |
699 | 1 | MIME_FIELD_REPLY_TO = hdrtoken_string_to_wks_sv("Reply-To"); |
700 | 1 | MIME_FIELD_RETRY_AFTER = hdrtoken_string_to_wks_sv("Retry-After"); |
701 | 1 | MIME_FIELD_SENDER = hdrtoken_string_to_wks_sv("Sender"); |
702 | 1 | MIME_FIELD_SERVER = hdrtoken_string_to_wks_sv("Server"); |
703 | 1 | MIME_FIELD_SET_COOKIE = hdrtoken_string_to_wks_sv("Set-Cookie"); |
704 | 1 | MIME_FIELD_STRICT_TRANSPORT_SECURITY = hdrtoken_string_to_wks_sv("Strict-Transport-Security"); |
705 | 1 | MIME_FIELD_SUBJECT = hdrtoken_string_to_wks_sv("Subject"); |
706 | 1 | MIME_FIELD_SUMMARY = hdrtoken_string_to_wks_sv("Summary"); |
707 | 1 | MIME_FIELD_TE = hdrtoken_string_to_wks_sv("TE"); |
708 | 1 | MIME_FIELD_TRANSFER_ENCODING = hdrtoken_string_to_wks_sv("Transfer-Encoding"); |
709 | 1 | MIME_FIELD_UPGRADE = hdrtoken_string_to_wks_sv("Upgrade"); |
710 | 1 | MIME_FIELD_USER_AGENT = hdrtoken_string_to_wks_sv("User-Agent"); |
711 | 1 | MIME_FIELD_VARY = hdrtoken_string_to_wks_sv("Vary"); |
712 | 1 | MIME_FIELD_VIA = hdrtoken_string_to_wks_sv("Via"); |
713 | 1 | MIME_FIELD_WARNING = hdrtoken_string_to_wks_sv("Warning"); |
714 | 1 | MIME_FIELD_WWW_AUTHENTICATE = hdrtoken_string_to_wks_sv("Www-Authenticate"); |
715 | 1 | MIME_FIELD_XREF = hdrtoken_string_to_wks_sv("Xref"); |
716 | 1 | MIME_FIELD_ATS_INTERNAL = hdrtoken_string_to_wks_sv("@Ats-Internal"); |
717 | 1 | MIME_FIELD_X_ID = hdrtoken_string_to_wks_sv("X-ID"); |
718 | 1 | MIME_FIELD_X_FORWARDED_FOR = hdrtoken_string_to_wks_sv("X-Forwarded-For"); |
719 | 1 | MIME_FIELD_FORWARDED = hdrtoken_string_to_wks_sv("Forwarded"); |
720 | 1 | MIME_FIELD_SEC_WEBSOCKET_KEY = hdrtoken_string_to_wks_sv("Sec-WebSocket-Key"); |
721 | 1 | MIME_FIELD_SEC_WEBSOCKET_VERSION = hdrtoken_string_to_wks_sv("Sec-WebSocket-Version"); |
722 | 1 | MIME_FIELD_HTTP2_SETTINGS = hdrtoken_string_to_wks_sv("HTTP2-Settings"); |
723 | 1 | MIME_FIELD_EARLY_DATA = hdrtoken_string_to_wks_sv("Early-Data"); |
724 | | |
725 | 1 | MIME_WKSIDX_ACCEPT = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT.c_str()); |
726 | 1 | MIME_WKSIDX_ACCEPT_CHARSET = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT_CHARSET.c_str()); |
727 | 1 | MIME_WKSIDX_ACCEPT_ENCODING = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT_ENCODING.c_str()); |
728 | 1 | MIME_WKSIDX_ACCEPT_LANGUAGE = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT_LANGUAGE.c_str()); |
729 | 1 | MIME_WKSIDX_ACCEPT_RANGES = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT_RANGES.c_str()); |
730 | 1 | MIME_WKSIDX_AGE = hdrtoken_wks_to_index(MIME_FIELD_AGE.c_str()); |
731 | 1 | MIME_WKSIDX_ALLOW = hdrtoken_wks_to_index(MIME_FIELD_ALLOW.c_str()); |
732 | 1 | MIME_WKSIDX_APPROVED = hdrtoken_wks_to_index(MIME_FIELD_APPROVED.c_str()); |
733 | 1 | MIME_WKSIDX_AUTHORIZATION = hdrtoken_wks_to_index(MIME_FIELD_AUTHORIZATION.c_str()); |
734 | 1 | MIME_WKSIDX_BYTES = hdrtoken_wks_to_index(MIME_FIELD_BYTES.c_str()); |
735 | 1 | MIME_WKSIDX_CACHE_CONTROL = hdrtoken_wks_to_index(MIME_FIELD_CACHE_CONTROL.c_str()); |
736 | 1 | MIME_WKSIDX_CLIENT_IP = hdrtoken_wks_to_index(MIME_FIELD_CLIENT_IP.c_str()); |
737 | 1 | MIME_WKSIDX_CONNECTION = hdrtoken_wks_to_index(MIME_FIELD_CONNECTION.c_str()); |
738 | 1 | MIME_WKSIDX_CONTENT_BASE = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_BASE.c_str()); |
739 | 1 | MIME_WKSIDX_CONTENT_ENCODING = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_ENCODING.c_str()); |
740 | 1 | MIME_WKSIDX_CONTENT_LANGUAGE = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_LANGUAGE.c_str()); |
741 | 1 | MIME_WKSIDX_CONTENT_LENGTH = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_LENGTH.c_str()); |
742 | 1 | MIME_WKSIDX_CONTENT_LOCATION = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_LOCATION.c_str()); |
743 | 1 | MIME_WKSIDX_CONTENT_MD5 = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_MD5.c_str()); |
744 | 1 | MIME_WKSIDX_CONTENT_RANGE = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_RANGE.c_str()); |
745 | 1 | MIME_WKSIDX_CONTENT_TYPE = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_TYPE.c_str()); |
746 | 1 | MIME_WKSIDX_CONTROL = hdrtoken_wks_to_index(MIME_FIELD_CONTROL.c_str()); |
747 | 1 | MIME_WKSIDX_COOKIE = hdrtoken_wks_to_index(MIME_FIELD_COOKIE.c_str()); |
748 | 1 | MIME_WKSIDX_DATE = hdrtoken_wks_to_index(MIME_FIELD_DATE.c_str()); |
749 | 1 | MIME_WKSIDX_DISTRIBUTION = hdrtoken_wks_to_index(MIME_FIELD_DISTRIBUTION.c_str()); |
750 | 1 | MIME_WKSIDX_ETAG = hdrtoken_wks_to_index(MIME_FIELD_ETAG.c_str()); |
751 | 1 | MIME_WKSIDX_EXPECT = hdrtoken_wks_to_index(MIME_FIELD_EXPECT.c_str()); |
752 | 1 | MIME_WKSIDX_EXPIRES = hdrtoken_wks_to_index(MIME_FIELD_EXPIRES.c_str()); |
753 | 1 | MIME_WKSIDX_FOLLOWUP_TO = hdrtoken_wks_to_index(MIME_FIELD_FOLLOWUP_TO.c_str()); |
754 | 1 | MIME_WKSIDX_FROM = hdrtoken_wks_to_index(MIME_FIELD_FROM.c_str()); |
755 | 1 | MIME_WKSIDX_HOST = hdrtoken_wks_to_index(MIME_FIELD_HOST.c_str()); |
756 | 1 | MIME_WKSIDX_IF_MATCH = hdrtoken_wks_to_index(MIME_FIELD_IF_MATCH.c_str()); |
757 | 1 | MIME_WKSIDX_IF_MODIFIED_SINCE = hdrtoken_wks_to_index(MIME_FIELD_IF_MODIFIED_SINCE.c_str()); |
758 | 1 | MIME_WKSIDX_IF_NONE_MATCH = hdrtoken_wks_to_index(MIME_FIELD_IF_NONE_MATCH.c_str()); |
759 | 1 | MIME_WKSIDX_IF_RANGE = hdrtoken_wks_to_index(MIME_FIELD_IF_RANGE.c_str()); |
760 | 1 | MIME_WKSIDX_IF_UNMODIFIED_SINCE = hdrtoken_wks_to_index(MIME_FIELD_IF_UNMODIFIED_SINCE.c_str()); |
761 | 1 | MIME_WKSIDX_KEEP_ALIVE = hdrtoken_wks_to_index(MIME_FIELD_KEEP_ALIVE.c_str()); |
762 | 1 | MIME_WKSIDX_KEYWORDS = hdrtoken_wks_to_index(MIME_FIELD_KEYWORDS.c_str()); |
763 | 1 | MIME_WKSIDX_LAST_MODIFIED = hdrtoken_wks_to_index(MIME_FIELD_LAST_MODIFIED.c_str()); |
764 | 1 | MIME_WKSIDX_LINES = hdrtoken_wks_to_index(MIME_FIELD_LINES.c_str()); |
765 | 1 | MIME_WKSIDX_LOCATION = hdrtoken_wks_to_index(MIME_FIELD_LOCATION.c_str()); |
766 | 1 | MIME_WKSIDX_MAX_FORWARDS = hdrtoken_wks_to_index(MIME_FIELD_MAX_FORWARDS.c_str()); |
767 | 1 | MIME_WKSIDX_MESSAGE_ID = hdrtoken_wks_to_index(MIME_FIELD_MESSAGE_ID.c_str()); |
768 | 1 | MIME_WKSIDX_NEWSGROUPS = hdrtoken_wks_to_index(MIME_FIELD_NEWSGROUPS.c_str()); |
769 | 1 | MIME_WKSIDX_ORGANIZATION = hdrtoken_wks_to_index(MIME_FIELD_ORGANIZATION.c_str()); |
770 | 1 | MIME_WKSIDX_PATH = hdrtoken_wks_to_index(MIME_FIELD_PATH.c_str()); |
771 | 1 | MIME_WKSIDX_PRAGMA = hdrtoken_wks_to_index(MIME_FIELD_PRAGMA.c_str()); |
772 | 1 | MIME_WKSIDX_PROXY_AUTHENTICATE = hdrtoken_wks_to_index(MIME_FIELD_PROXY_AUTHENTICATE.c_str()); |
773 | 1 | MIME_WKSIDX_PROXY_AUTHORIZATION = hdrtoken_wks_to_index(MIME_FIELD_PROXY_AUTHORIZATION.c_str()); |
774 | 1 | MIME_WKSIDX_PROXY_CONNECTION = hdrtoken_wks_to_index(MIME_FIELD_PROXY_CONNECTION.c_str()); |
775 | 1 | MIME_WKSIDX_PUBLIC = hdrtoken_wks_to_index(MIME_FIELD_PUBLIC.c_str()); |
776 | 1 | MIME_WKSIDX_RANGE = hdrtoken_wks_to_index(MIME_FIELD_RANGE.c_str()); |
777 | 1 | MIME_WKSIDX_REFERENCES = hdrtoken_wks_to_index(MIME_FIELD_REFERENCES.c_str()); |
778 | 1 | MIME_WKSIDX_REFERER = hdrtoken_wks_to_index(MIME_FIELD_REFERER.c_str()); |
779 | 1 | MIME_WKSIDX_REPLY_TO = hdrtoken_wks_to_index(MIME_FIELD_REPLY_TO.c_str()); |
780 | 1 | MIME_WKSIDX_RETRY_AFTER = hdrtoken_wks_to_index(MIME_FIELD_RETRY_AFTER.c_str()); |
781 | 1 | MIME_WKSIDX_SENDER = hdrtoken_wks_to_index(MIME_FIELD_SENDER.c_str()); |
782 | 1 | MIME_WKSIDX_SERVER = hdrtoken_wks_to_index(MIME_FIELD_SERVER.c_str()); |
783 | 1 | MIME_WKSIDX_SET_COOKIE = hdrtoken_wks_to_index(MIME_FIELD_SET_COOKIE.c_str()); |
784 | 1 | MIME_WKSIDX_STRICT_TRANSPORT_SECURITY = hdrtoken_wks_to_index(MIME_FIELD_STRICT_TRANSPORT_SECURITY.c_str()); |
785 | 1 | MIME_WKSIDX_SUBJECT = hdrtoken_wks_to_index(MIME_FIELD_SUBJECT.c_str()); |
786 | 1 | MIME_WKSIDX_SUMMARY = hdrtoken_wks_to_index(MIME_FIELD_SUMMARY.c_str()); |
787 | 1 | MIME_WKSIDX_TE = hdrtoken_wks_to_index(MIME_FIELD_TE.c_str()); |
788 | 1 | MIME_WKSIDX_TRANSFER_ENCODING = hdrtoken_wks_to_index(MIME_FIELD_TRANSFER_ENCODING.c_str()); |
789 | 1 | MIME_WKSIDX_UPGRADE = hdrtoken_wks_to_index(MIME_FIELD_UPGRADE.c_str()); |
790 | 1 | MIME_WKSIDX_USER_AGENT = hdrtoken_wks_to_index(MIME_FIELD_USER_AGENT.c_str()); |
791 | 1 | MIME_WKSIDX_VARY = hdrtoken_wks_to_index(MIME_FIELD_VARY.c_str()); |
792 | 1 | MIME_WKSIDX_VIA = hdrtoken_wks_to_index(MIME_FIELD_VIA.c_str()); |
793 | 1 | MIME_WKSIDX_WARNING = hdrtoken_wks_to_index(MIME_FIELD_WARNING.c_str()); |
794 | 1 | MIME_WKSIDX_WWW_AUTHENTICATE = hdrtoken_wks_to_index(MIME_FIELD_WWW_AUTHENTICATE.c_str()); |
795 | 1 | MIME_WKSIDX_XREF = hdrtoken_wks_to_index(MIME_FIELD_XREF.c_str()); |
796 | 1 | MIME_WKSIDX_X_ID = hdrtoken_wks_to_index(MIME_FIELD_X_ID.c_str()); |
797 | 1 | MIME_WKSIDX_X_FORWARDED_FOR = hdrtoken_wks_to_index(MIME_FIELD_X_FORWARDED_FOR.c_str()); |
798 | 1 | MIME_WKSIDX_FORWARDED = hdrtoken_wks_to_index(MIME_FIELD_FORWARDED.c_str()); |
799 | 1 | MIME_WKSIDX_SEC_WEBSOCKET_KEY = hdrtoken_wks_to_index(MIME_FIELD_SEC_WEBSOCKET_KEY.c_str()); |
800 | 1 | MIME_WKSIDX_SEC_WEBSOCKET_VERSION = hdrtoken_wks_to_index(MIME_FIELD_SEC_WEBSOCKET_VERSION.c_str()); |
801 | 1 | MIME_WKSIDX_HTTP2_SETTINGS = hdrtoken_wks_to_index(MIME_FIELD_HTTP2_SETTINGS.c_str()); |
802 | 1 | MIME_WKSIDX_EARLY_DATA = hdrtoken_wks_to_index(MIME_FIELD_EARLY_DATA.c_str()); |
803 | | |
804 | 1 | MIME_VALUE_BYTES = hdrtoken_string_to_wks_sv("bytes"); |
805 | 1 | MIME_VALUE_CHUNKED = hdrtoken_string_to_wks_sv("chunked"); |
806 | 1 | MIME_VALUE_CLOSE = hdrtoken_string_to_wks_sv("close"); |
807 | 1 | MIME_VALUE_COMPRESS = hdrtoken_string_to_wks_sv("compress"); |
808 | 1 | MIME_VALUE_DEFLATE = hdrtoken_string_to_wks_sv("deflate"); |
809 | 1 | MIME_VALUE_GZIP = hdrtoken_string_to_wks_sv("gzip"); |
810 | 1 | MIME_VALUE_BROTLI = hdrtoken_string_to_wks_sv("br"); |
811 | 1 | MIME_VALUE_ZSTD = hdrtoken_string_to_wks_sv("zstd"); |
812 | 1 | MIME_VALUE_IDENTITY = hdrtoken_string_to_wks_sv("identity"); |
813 | 1 | MIME_VALUE_KEEP_ALIVE = hdrtoken_string_to_wks_sv("keep-alive"); |
814 | 1 | MIME_VALUE_MAX_AGE = hdrtoken_string_to_wks_sv("max-age"); |
815 | 1 | MIME_VALUE_MAX_STALE = hdrtoken_string_to_wks_sv("max-stale"); |
816 | 1 | MIME_VALUE_MIN_FRESH = hdrtoken_string_to_wks_sv("min-fresh"); |
817 | 1 | MIME_VALUE_MUST_REVALIDATE = hdrtoken_string_to_wks_sv("must-revalidate"); |
818 | 1 | MIME_VALUE_NONE = hdrtoken_string_to_wks_sv("none"); |
819 | 1 | MIME_VALUE_NO_CACHE = hdrtoken_string_to_wks_sv("no-cache"); |
820 | 1 | MIME_VALUE_NO_STORE = hdrtoken_string_to_wks_sv("no-store"); |
821 | 1 | MIME_VALUE_NO_TRANSFORM = hdrtoken_string_to_wks_sv("no-transform"); |
822 | 1 | MIME_VALUE_ONLY_IF_CACHED = hdrtoken_string_to_wks_sv("only-if-cached"); |
823 | 1 | MIME_VALUE_PRIVATE = hdrtoken_string_to_wks_sv("private"); |
824 | 1 | MIME_VALUE_PROXY_REVALIDATE = hdrtoken_string_to_wks_sv("proxy-revalidate"); |
825 | 1 | MIME_VALUE_PUBLIC = hdrtoken_string_to_wks_sv("public"); |
826 | 1 | MIME_VALUE_S_MAXAGE = hdrtoken_string_to_wks_sv("s-maxage"); |
827 | 1 | MIME_VALUE_NEED_REVALIDATE_ONCE = hdrtoken_string_to_wks_sv("need-revalidate-once"); |
828 | 1 | MIME_VALUE_WEBSOCKET = hdrtoken_string_to_wks_sv("websocket"); |
829 | 1 | MIME_VALUE_H2C = hdrtoken_string_to_wks_sv(MIME_UPGRADE_H2C_TOKEN); |
830 | | |
831 | 1 | mime_init_date_format_table(); |
832 | 1 | mime_init_cache_control_cooking_masks(); |
833 | 1 | } |
834 | 1 | } |
835 | | |
836 | | void |
837 | | mime_init_cache_control_cooking_masks() |
838 | 1 | { |
839 | 1 | static struct { |
840 | 1 | const char *name; |
841 | 1 | uint32_t mask; |
842 | 1 | } cc_mask_table[] = { |
843 | 1 | {"max-age", MIME_COOKED_MASK_CC_MAX_AGE }, |
844 | 1 | {"no-cache", MIME_COOKED_MASK_CC_NO_CACHE }, |
845 | 1 | {"no-store", MIME_COOKED_MASK_CC_NO_STORE }, |
846 | 1 | {"no-transform", MIME_COOKED_MASK_CC_NO_TRANSFORM }, |
847 | 1 | {"max-stale", MIME_COOKED_MASK_CC_MAX_STALE }, |
848 | 1 | {"min-fresh", MIME_COOKED_MASK_CC_MIN_FRESH }, |
849 | 1 | {"only-if-cached", MIME_COOKED_MASK_CC_ONLY_IF_CACHED }, |
850 | 1 | {"public", MIME_COOKED_MASK_CC_PUBLIC }, |
851 | 1 | {"private", MIME_COOKED_MASK_CC_PRIVATE }, |
852 | 1 | {"must-revalidate", MIME_COOKED_MASK_CC_MUST_REVALIDATE }, |
853 | 1 | {"proxy-revalidate", MIME_COOKED_MASK_CC_PROXY_REVALIDATE }, |
854 | 1 | {"s-maxage", MIME_COOKED_MASK_CC_S_MAXAGE }, |
855 | 1 | {"need-revalidate-once", MIME_COOKED_MASK_CC_NEED_REVALIDATE_ONCE}, |
856 | 1 | {nullptr, 0 } |
857 | 1 | }; |
858 | | |
859 | 14 | for (int i = 0; cc_mask_table[i].name != nullptr; i++) { |
860 | 13 | const char *wks = hdrtoken_string_to_wks(cc_mask_table[i].name); |
861 | 13 | HdrTokenHeapPrefix *p = hdrtoken_wks_to_prefix(wks); |
862 | 13 | p->wks_type_specific.u.cache_control.cc_mask = cc_mask_table[i].mask; |
863 | 13 | } |
864 | 1 | } |
865 | | |
866 | | void |
867 | | mime_init_date_format_table() |
868 | 1 | { |
869 | | //////////////////////////////////////////////////////////////// |
870 | | // to speed up the days_since_epoch to m/d/y conversion, we // |
871 | | // use a pre-computed lookup table to support the common case // |
872 | | // of dates that are +/- one year from today --- this code // |
873 | | // builds the lookup table during the first call. // |
874 | | //////////////////////////////////////////////////////////////// |
875 | | |
876 | 1 | time_t now_secs; |
877 | 1 | time_t i, now_days, first_days, last_days, num_days; |
878 | 1 | int m = 0, d = 0, y = 0; |
879 | | |
880 | 1 | time(&now_secs); |
881 | 1 | now_days = static_cast<time_t>(now_secs / (60 * 60 * 24)); |
882 | 1 | first_days = now_days - 366; |
883 | 1 | last_days = now_days + 366; |
884 | 1 | num_days = last_days - first_days + 1; |
885 | | |
886 | 1 | _days_to_mdy_fast_lookup_table = static_cast<MDY *>(ats_malloc(num_days * sizeof(MDY))); |
887 | 1 | _days_to_mdy_fast_lookup_table_first_day = first_days; |
888 | 1 | _days_to_mdy_fast_lookup_table_last_day = last_days; |
889 | | |
890 | 734 | for (i = 0; i < num_days; i++) { |
891 | 733 | mime_days_since_epoch_to_mdy_slowcase(first_days + i, &m, &d, &y); |
892 | 733 | _days_to_mdy_fast_lookup_table[i].m = m; |
893 | 733 | _days_to_mdy_fast_lookup_table[i].d = d; |
894 | 733 | _days_to_mdy_fast_lookup_table[i].y = y; |
895 | 733 | } |
896 | 1 | } |
897 | | |
898 | | MIMEHdrImpl * |
899 | | mime_hdr_create(HdrHeap *heap) |
900 | 18.7k | { |
901 | 18.7k | MIMEHdrImpl *mh; |
902 | | |
903 | 18.7k | mh = (MIMEHdrImpl *)heap->allocate_obj(sizeof(MIMEHdrImpl), HdrHeapObjType::MIME_HEADER); |
904 | 18.7k | mime_hdr_init(mh); |
905 | 18.7k | return mh; |
906 | 18.7k | } |
907 | | |
908 | | void |
909 | | _mime_hdr_field_block_init(MIMEFieldBlockImpl *fblock) |
910 | 19.6k | { |
911 | 19.6k | fblock->m_freetop = 0; |
912 | 19.6k | fblock->m_next = nullptr; |
913 | | |
914 | | #ifdef BLOCK_INIT_PARANOIA |
915 | | int i; |
916 | | |
917 | | // FIX: Could eliminate this initialization loop if we assumed |
918 | | // every slot above the freetop of the block was garbage; |
919 | | // but to be safe, and help debugging, for now we are eating |
920 | | // the cost of initializing all slots in a block. |
921 | | |
922 | | for (i = 0; i < MIME_FIELD_BLOCK_SLOTS; i++) { |
923 | | MIMEField *field = &(fblock->m_field_slots[i]); |
924 | | field->m_readiness = MIME_FIELD_SLOT_READINESS_EMPTY; |
925 | | } |
926 | | #endif |
927 | 19.6k | } |
928 | | |
929 | | void |
930 | | mime_hdr_cooked_stuff_init(MIMEHdrImpl *mh, MIMEField *changing_field_or_null) |
931 | 20.1k | { |
932 | | // to be safe, reinitialize unless you know this call is for other cooked field |
933 | 20.1k | if ((changing_field_or_null == nullptr) || (changing_field_or_null->m_wks_idx != MIME_WKSIDX_PRAGMA)) { |
934 | 19.1k | mh->m_cooked_stuff.m_cache_control.m_mask = 0; |
935 | 19.1k | mh->m_cooked_stuff.m_cache_control.m_secs_max_age = 0; |
936 | 19.1k | mh->m_cooked_stuff.m_cache_control.m_secs_s_maxage = 0; |
937 | 19.1k | mh->m_cooked_stuff.m_cache_control.m_secs_max_stale = 0; |
938 | 19.1k | mh->m_cooked_stuff.m_cache_control.m_secs_min_fresh = 0; |
939 | 19.1k | } |
940 | 20.1k | if ((changing_field_or_null == nullptr) || (changing_field_or_null->m_wks_idx != MIME_WKSIDX_CACHE_CONTROL)) { |
941 | 19.7k | mh->m_cooked_stuff.m_pragma.m_no_cache = false; |
942 | 19.7k | } |
943 | 20.1k | } |
944 | | |
945 | | void |
946 | | mime_hdr_init(MIMEHdrImpl *mh) |
947 | 18.7k | { |
948 | 18.7k | mime_hdr_init_accelerators_and_presence_bits(mh); |
949 | | |
950 | 18.7k | mime_hdr_cooked_stuff_init(mh, nullptr); |
951 | | |
952 | | // first header is inline: fake an object header for uniformity |
953 | 18.7k | obj_init_header((HdrHeapObjImpl *)&(mh->m_first_fblock), HdrHeapObjType::FIELD_BLOCK, sizeof(MIMEFieldBlockImpl), 0); |
954 | | |
955 | 18.7k | _mime_hdr_field_block_init(&(mh->m_first_fblock)); |
956 | 18.7k | mh->m_fblock_list_tail = &(mh->m_first_fblock); |
957 | | |
958 | 18.7k | MIME_HDR_SANITY_CHECK(mh); |
959 | 18.7k | } |
960 | | |
961 | | MIMEFieldBlockImpl * |
962 | | _mime_field_block_copy(MIMEFieldBlockImpl *s_fblock, HdrHeap * /* s_heap ATS_UNUSED */, HdrHeap *d_heap) |
963 | 0 | { |
964 | 0 | MIMEFieldBlockImpl *d_fblock; |
965 | |
|
966 | 0 | d_fblock = (MIMEFieldBlockImpl *)d_heap->allocate_obj(sizeof(MIMEFieldBlockImpl), HdrHeapObjType::FIELD_BLOCK); |
967 | 0 | memcpy(d_fblock, s_fblock, sizeof(MIMEFieldBlockImpl)); |
968 | 0 | return d_fblock; |
969 | 0 | } |
970 | | |
971 | | void |
972 | | _mime_field_block_destroy(HdrHeap *heap, MIMEFieldBlockImpl *fblock) |
973 | 24 | { |
974 | 24 | heap->deallocate_obj(fblock); |
975 | 24 | } |
976 | | |
977 | | void |
978 | | mime_hdr_destroy_field_block_list(HdrHeap *heap, MIMEFieldBlockImpl *head) |
979 | 0 | { |
980 | 0 | MIMEFieldBlockImpl *next; |
981 | |
|
982 | 0 | while (head != nullptr) { |
983 | 0 | next = head->m_next; |
984 | 0 | _mime_field_block_destroy(heap, head); |
985 | 0 | head = next; |
986 | 0 | } |
987 | 0 | } |
988 | | |
989 | | void |
990 | | mime_hdr_destroy(HdrHeap *heap, MIMEHdrImpl *mh) |
991 | 0 | { |
992 | 0 | mime_hdr_destroy_field_block_list(heap, mh->m_first_fblock.m_next); |
993 | | |
994 | | // INKqa11458: if we deallocate mh here and call TSMLocRelease |
995 | | // again, the plugin fails in assert. We leave deallocating to |
996 | | // the plugin using TSMLocRelease |
997 | | |
998 | | // heap->deallocate_obj(mh); |
999 | 0 | } |
1000 | | |
1001 | | void |
1002 | | mime_hdr_copy_onto(MIMEHdrImpl *s_mh, HdrHeap *s_heap, MIMEHdrImpl *d_mh, HdrHeap *d_heap, bool inherit_strs) |
1003 | 0 | { |
1004 | 0 | int block_count; |
1005 | 0 | MIMEFieldBlockImpl *s_fblock, *d_fblock, *prev_d_fblock; |
1006 | | |
1007 | | // If there are chained field blocks beyond the first one, we're just going to |
1008 | | // destroy them. Ideally, we'd use them if the copied in header needed |
1009 | | // extra blocks. It's too late in the Tomcat code cycle to implement |
1010 | | // reuse. |
1011 | 0 | if (d_mh->m_first_fblock.m_next) { |
1012 | 0 | mime_hdr_destroy_field_block_list(d_heap, d_mh->m_first_fblock.m_next); |
1013 | 0 | } |
1014 | |
|
1015 | 0 | ink_assert(((char *)&(s_mh->m_first_fblock.m_field_slots[MIME_FIELD_BLOCK_SLOTS]) - (char *)s_mh) == sizeof(struct MIMEHdrImpl)); |
1016 | |
|
1017 | 0 | int top = s_mh->m_first_fblock.m_freetop; |
1018 | 0 | char *end = reinterpret_cast<char *>(&(s_mh->m_first_fblock.m_field_slots[top])); |
1019 | 0 | int bytes_below_top = end - reinterpret_cast<char *>(s_mh); |
1020 | | |
1021 | | // copies useful part of enclosed first block too |
1022 | 0 | memcpy(d_mh, s_mh, bytes_below_top); |
1023 | |
|
1024 | 0 | if (d_mh->m_first_fblock.m_next == nullptr) // common case: no other block |
1025 | 0 | { |
1026 | 0 | d_mh->m_fblock_list_tail = &(d_mh->m_first_fblock); |
1027 | 0 | block_count = 1; |
1028 | 0 | } else // uncommon case: block list exists |
1029 | 0 | { |
1030 | 0 | prev_d_fblock = &(d_mh->m_first_fblock); |
1031 | 0 | block_count = 1; |
1032 | 0 | for (s_fblock = s_mh->m_first_fblock.m_next; s_fblock != nullptr; s_fblock = s_fblock->m_next) { |
1033 | 0 | ++block_count; |
1034 | 0 | d_fblock = _mime_field_block_copy(s_fblock, s_heap, d_heap); |
1035 | 0 | prev_d_fblock->m_next = d_fblock; |
1036 | 0 | prev_d_fblock = d_fblock; |
1037 | 0 | } |
1038 | 0 | d_mh->m_fblock_list_tail = prev_d_fblock; |
1039 | 0 | } |
1040 | |
|
1041 | 0 | if (inherit_strs) { |
1042 | 0 | d_heap->inherit_string_heaps(s_heap); |
1043 | 0 | } |
1044 | |
|
1045 | 0 | mime_hdr_field_block_list_adjust(block_count, &(s_mh->m_first_fblock), &(d_mh->m_first_fblock)); |
1046 | |
|
1047 | 0 | MIME_HDR_SANITY_CHECK(s_mh); |
1048 | 0 | MIME_HDR_SANITY_CHECK(d_mh); |
1049 | 0 | } |
1050 | | |
1051 | | MIMEHdrImpl * |
1052 | | mime_hdr_clone(MIMEHdrImpl *s_mh, HdrHeap *s_heap, HdrHeap *d_heap, bool inherit_strs) |
1053 | 0 | { |
1054 | 0 | MIMEHdrImpl *d_mh; |
1055 | |
|
1056 | 0 | d_mh = mime_hdr_create(d_heap); |
1057 | 0 | mime_hdr_copy_onto(s_mh, s_heap, d_mh, d_heap, inherit_strs); |
1058 | 0 | return d_mh; |
1059 | 0 | } |
1060 | | |
1061 | | /** Move a pointer from one list to another, keeping the relative offset. |
1062 | | * @return A pointer that has the same relative offset to @a dest_base as |
1063 | | * @a dest_ptr does to @a src_base. |
1064 | | */ |
1065 | | static inline MIMEField * |
1066 | | rebase(MIMEField *dest_ptr, ///< Original pointer into @src_base memory. |
1067 | | void *dest_base, ///< New base pointer. |
1068 | | void *src_base ///< Original base pointer. |
1069 | | ) |
1070 | 0 | { |
1071 | 0 | return reinterpret_cast<MIMEField *>(reinterpret_cast<char *>(dest_ptr) + |
1072 | 0 | (static_cast<char *>(dest_base) - static_cast<char *>(src_base))); |
1073 | 0 | } |
1074 | | |
1075 | | static inline void |
1076 | | relocate(MIMEField *field, MIMEFieldBlockImpl *dest_block, MIMEFieldBlockImpl *src_block) |
1077 | 0 | { |
1078 | 0 | for (; src_block; src_block = src_block->m_next, dest_block = dest_block->m_next) { |
1079 | 0 | ink_release_assert(dest_block); |
1080 | | |
1081 | 0 | if (field->m_next_dup >= src_block->m_field_slots && field->m_next_dup < src_block->m_field_slots + src_block->m_freetop) { |
1082 | 0 | field->m_next_dup = rebase(field->m_next_dup, dest_block->m_field_slots, src_block->m_field_slots); |
1083 | 0 | return; |
1084 | 0 | } |
1085 | 0 | } |
1086 | 0 | } |
1087 | | |
1088 | | void |
1089 | | mime_hdr_field_block_list_adjust(int /* block_count ATS_UNUSED */, MIMEFieldBlockImpl *old_list, MIMEFieldBlockImpl *new_list) |
1090 | 0 | { |
1091 | 0 | for (MIMEFieldBlockImpl *new_blk = new_list; new_blk; new_blk = new_blk->m_next) { |
1092 | 0 | for (MIMEField *field = new_blk->m_field_slots, *end = field + new_blk->m_freetop; field != end; ++field) { |
1093 | 0 | if (field->is_live() && field->m_next_dup) { |
1094 | 0 | relocate(field, new_list, old_list); |
1095 | 0 | } |
1096 | 0 | } |
1097 | 0 | } |
1098 | 0 | } |
1099 | | |
1100 | | int |
1101 | | mime_hdr_length_get(MIMEHdrImpl *mh) |
1102 | 0 | { |
1103 | 0 | unsigned int length, index; |
1104 | 0 | MIMEFieldBlockImpl *fblock; |
1105 | 0 | MIMEField *field; |
1106 | |
|
1107 | 0 | length = 2; |
1108 | |
|
1109 | 0 | for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
1110 | 0 | for (index = 0; index < fblock->m_freetop; index++) { |
1111 | 0 | field = &(fblock->m_field_slots[index]); |
1112 | 0 | if (field->is_live()) { |
1113 | 0 | length += mime_field_length_get(field); |
1114 | 0 | } |
1115 | 0 | } |
1116 | 0 | } |
1117 | |
|
1118 | 0 | return length; |
1119 | 0 | } |
1120 | | |
1121 | | void |
1122 | | mime_hdr_fields_clear(HdrHeap *heap, MIMEHdrImpl *mh) |
1123 | 0 | { |
1124 | 0 | mime_hdr_destroy_field_block_list(heap, mh->m_first_fblock.m_next); |
1125 | 0 | mime_hdr_init(mh); |
1126 | 0 | } |
1127 | | |
1128 | | MIMEField * |
1129 | | _mime_hdr_field_list_search_by_wks(MIMEHdrImpl *mh, int wks_idx) |
1130 | 1.72k | { |
1131 | 1.72k | MIMEFieldBlockImpl *fblock; |
1132 | 1.72k | MIMEField *field, *too_far_field; |
1133 | | |
1134 | 1.72k | ink_assert(hdrtoken_is_valid_wks_idx(wks_idx)); |
1135 | | |
1136 | 2.98k | for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
1137 | 2.86k | field = &(fblock->m_field_slots[0]); |
1138 | | |
1139 | 2.86k | too_far_field = &(fblock->m_field_slots[fblock->m_freetop]); |
1140 | 28.6k | while (field < too_far_field) { |
1141 | 27.3k | if (field->is_live() && (field->m_wks_idx == wks_idx)) { |
1142 | 1.60k | return field; |
1143 | 1.60k | } |
1144 | 25.7k | ++field; |
1145 | 25.7k | } |
1146 | 2.86k | } |
1147 | | |
1148 | 122 | return nullptr; |
1149 | 1.72k | } |
1150 | | |
1151 | | MIMEField * |
1152 | | _mime_hdr_field_list_search_by_string(MIMEHdrImpl *mh, std::string_view field_name) |
1153 | 15.7k | { |
1154 | 15.7k | MIMEFieldBlockImpl *fblock; |
1155 | 15.7k | MIMEField *field, *too_far_field; |
1156 | | |
1157 | 15.7k | ink_assert(mh); |
1158 | 26.8k | for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
1159 | 24.0k | field = &(fblock->m_field_slots[0]); |
1160 | | |
1161 | 24.0k | too_far_field = &(fblock->m_field_slots[fblock->m_freetop]); |
1162 | 244k | while (field < too_far_field) { |
1163 | 233k | if (field->is_live() && |
1164 | 230k | strcasecmp(std::string_view{field->m_ptr_name, static_cast<std::string_view::size_type>(field->m_len_name)}, |
1165 | 230k | field_name) == 0) { |
1166 | 12.9k | return field; |
1167 | 12.9k | } |
1168 | 220k | ++field; |
1169 | 220k | } |
1170 | 24.0k | } |
1171 | | |
1172 | 2.84k | return nullptr; |
1173 | 15.7k | } |
1174 | | |
1175 | | MIMEField * |
1176 | | _mime_hdr_field_list_search_by_slotnum(MIMEHdrImpl *mh, int slotnum) |
1177 | 3.18k | { |
1178 | 3.18k | unsigned int block_num, block_index; |
1179 | 3.18k | MIMEFieldBlockImpl *fblock; |
1180 | | |
1181 | 3.18k | if (slotnum < MIME_FIELD_BLOCK_SLOTS) { |
1182 | 3.18k | fblock = &(mh->m_first_fblock); |
1183 | 3.18k | block_index = slotnum; |
1184 | 3.18k | if (block_index >= fblock->m_freetop) { |
1185 | 0 | return nullptr; |
1186 | 3.18k | } else { |
1187 | 3.18k | return &(fblock->m_field_slots[block_index]); |
1188 | 3.18k | } |
1189 | 3.18k | } else { |
1190 | 0 | block_num = slotnum / MIME_FIELD_BLOCK_SLOTS; |
1191 | 0 | block_index = slotnum % MIME_FIELD_BLOCK_SLOTS; |
1192 | |
|
1193 | 0 | fblock = &(mh->m_first_fblock); |
1194 | 0 | while (block_num-- && fblock) { |
1195 | 0 | fblock = fblock->m_next; |
1196 | 0 | } |
1197 | 0 | if ((fblock == nullptr) || (block_index >= fblock->m_freetop)) { |
1198 | 0 | return nullptr; |
1199 | 0 | } else { |
1200 | 0 | return &(fblock->m_field_slots[block_index]); |
1201 | 0 | } |
1202 | 0 | } |
1203 | 3.18k | } |
1204 | | |
1205 | | MIMEField * |
1206 | | mime_hdr_field_find(MIMEHdrImpl *mh, std::string_view field_name) |
1207 | 31.1k | { |
1208 | 31.1k | HdrTokenHeapPrefix *token_info; |
1209 | 31.1k | const bool is_wks = hdrtoken_is_wks(field_name.data()); |
1210 | | |
1211 | 31.1k | ink_assert(!field_name.empty()); |
1212 | | |
1213 | | //////////////////////////////////////////// |
1214 | | // do presence check and slot accelerator // |
1215 | | //////////////////////////////////////////// |
1216 | | |
1217 | | #if TRACK_FIELD_FIND_CALLS |
1218 | | Dbg(dbg_ctl_http, "mime_hdr_field_find(hdr 0x%X, field %.*s): is_wks = %d", mh, static_cast<int>(field_name.length()), |
1219 | | field_name.data(), is_wks); |
1220 | | #endif |
1221 | | |
1222 | 31.1k | if (is_wks) { |
1223 | 15.3k | token_info = hdrtoken_wks_to_prefix(field_name.data()); |
1224 | 15.3k | if ((token_info->wks_info.mask) && ((mh->m_presence_bits & token_info->wks_info.mask) == 0)) { |
1225 | | #if TRACK_FIELD_FIND_CALLS |
1226 | | Dbg(dbg_ctl_http, "mime_hdr_field_find(hdr 0x%X, field %.*s): MISS (due to presence bits)", mh, field_name_len, |
1227 | | field_name_str); |
1228 | | #endif |
1229 | 10.4k | return nullptr; |
1230 | 10.4k | } |
1231 | | |
1232 | 4.91k | int32_t slot_id = token_info->wks_info.slotid; |
1233 | | |
1234 | 4.91k | if (slot_id != MIME_SLOTID_NONE) { |
1235 | 4.08k | uint32_t slotnum = mime_hdr_get_accelerator_slotnum(mh, slot_id); |
1236 | | |
1237 | 4.08k | if (slotnum != MIME_FIELD_SLOTNUM_UNKNOWN) { |
1238 | 3.18k | MIMEField *f = _mime_hdr_field_list_search_by_slotnum(mh, slotnum); |
1239 | 3.18k | ink_assert((f == nullptr) || f->is_live()); |
1240 | | #if TRACK_FIELD_FIND_CALLS |
1241 | | Dbg(dbg_ctl_http, "mime_hdr_field_find(hdr 0x%X, field %.*s): %s (due to slot accelerators)", mh, |
1242 | | static_cast<int>(field_name.length()), field_name.data(), (f ? "HIT" : "MISS")); |
1243 | | #endif |
1244 | 3.18k | return f; |
1245 | 3.18k | } else { |
1246 | | #if TRACK_FIELD_FIND_CALLS |
1247 | | Dbg(dbg_ctl_http, "mime_hdr_field_find(hdr 0x%X, field %.*s): UNKNOWN (slot too big)", mh, |
1248 | | static_cast<int>(field_name.length()), field_name.data()); |
1249 | | #endif |
1250 | 895 | } |
1251 | 4.08k | } |
1252 | | |
1253 | | /////////////////////////////////////////////////////////////////////////// |
1254 | | // search by well-known string index or by case-insensitive string match // |
1255 | | /////////////////////////////////////////////////////////////////////////// |
1256 | | |
1257 | 1.72k | MIMEField *f = _mime_hdr_field_list_search_by_wks(mh, token_info->wks_idx); |
1258 | 1.72k | ink_assert((f == nullptr) || f->is_live()); |
1259 | | #if TRACK_FIELD_FIND_CALLS |
1260 | | Dbg(dbg_ctl_http, "mime_hdr_field_find(hdr 0x%X, field %.*s): %s (due to WKS list walk)", mh, |
1261 | | static_cast<int>(field_name.length()), field_name.data(), (f ? "HIT" : "MISS")); |
1262 | | #endif |
1263 | 1.72k | return f; |
1264 | 15.7k | } else { |
1265 | 15.7k | MIMEField *f = _mime_hdr_field_list_search_by_string(mh, field_name); |
1266 | | |
1267 | 15.7k | ink_assert((f == nullptr) || f->is_live()); |
1268 | | #if TRACK_FIELD_FIND_CALLS |
1269 | | Dbg(dbg_ctl_http, "mime_hdr_field_find(hdr 0x%X, field %.*s): %s (due to strcmp list walk)", mh, |
1270 | | static_cast<int>(field_name.length()), field_name.data(), (f ? "HIT" : "MISS")); |
1271 | | #endif |
1272 | 15.7k | return f; |
1273 | 15.7k | } |
1274 | 31.1k | } |
1275 | | |
1276 | | MIMEField * |
1277 | | mime_hdr_field_get(MIMEHdrImpl *mh, int idx) |
1278 | 0 | { |
1279 | 0 | unsigned int index; |
1280 | 0 | MIMEFieldBlockImpl *fblock; |
1281 | 0 | MIMEField *field; |
1282 | 0 | int got_idx; |
1283 | |
|
1284 | 0 | got_idx = -1; |
1285 | |
|
1286 | 0 | for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
1287 | 0 | for (index = 0; index < fblock->m_freetop; index++) { |
1288 | 0 | field = &(fblock->m_field_slots[index]); |
1289 | 0 | if (field->is_live()) { |
1290 | 0 | ++got_idx; |
1291 | 0 | } |
1292 | 0 | if (got_idx == idx) { |
1293 | 0 | return field; |
1294 | 0 | } |
1295 | 0 | } |
1296 | 0 | } |
1297 | | |
1298 | 0 | return nullptr; |
1299 | 0 | } |
1300 | | |
1301 | | MIMEField * |
1302 | | mime_hdr_field_get_slotnum(MIMEHdrImpl *mh, int slotnum) |
1303 | 0 | { |
1304 | 0 | return _mime_hdr_field_list_search_by_slotnum(mh, slotnum); |
1305 | 0 | } |
1306 | | |
1307 | | int |
1308 | | mime_hdr_fields_count(MIMEHdrImpl *mh) |
1309 | 0 | { |
1310 | 0 | unsigned int index; |
1311 | 0 | MIMEFieldBlockImpl *fblock; |
1312 | 0 | MIMEField *field; |
1313 | 0 | int count; |
1314 | |
|
1315 | 0 | count = 0; |
1316 | |
|
1317 | 0 | for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
1318 | 0 | for (index = 0; index < fblock->m_freetop; index++) { |
1319 | 0 | field = &(fblock->m_field_slots[index]); |
1320 | 0 | if (field->is_live()) { |
1321 | 0 | ++count; |
1322 | 0 | } |
1323 | 0 | } |
1324 | 0 | } |
1325 | |
|
1326 | 0 | return count; |
1327 | 0 | } |
1328 | | |
1329 | | void |
1330 | | mime_field_init(MIMEField *field) |
1331 | 51.1k | { |
1332 | 51.1k | memset(field, 0, sizeof(MIMEField)); |
1333 | 51.1k | field->m_readiness = MIME_FIELD_SLOT_READINESS_DETACHED; |
1334 | 51.1k | field->m_wks_idx = -1; |
1335 | 51.1k | } |
1336 | | |
1337 | | MIMEField * |
1338 | | mime_field_create(HdrHeap *heap, MIMEHdrImpl *mh) |
1339 | 51.1k | { |
1340 | 51.1k | MIMEField *field; |
1341 | 51.1k | MIMEFieldBlockImpl *tail_fblock, *new_fblock; |
1342 | | |
1343 | 51.1k | tail_fblock = mh->m_fblock_list_tail; |
1344 | 51.1k | if (tail_fblock->m_freetop >= MIME_FIELD_BLOCK_SLOTS) { |
1345 | 969 | new_fblock = (MIMEFieldBlockImpl *)heap->allocate_obj(sizeof(MIMEFieldBlockImpl), HdrHeapObjType::FIELD_BLOCK); |
1346 | 969 | _mime_hdr_field_block_init(new_fblock); |
1347 | 969 | tail_fblock->m_next = new_fblock; |
1348 | 969 | tail_fblock = new_fblock; |
1349 | 969 | mh->m_fblock_list_tail = new_fblock; |
1350 | 969 | } |
1351 | | |
1352 | 51.1k | field = &(tail_fblock->m_field_slots[tail_fblock->m_freetop]); |
1353 | 51.1k | ++tail_fblock->m_freetop; |
1354 | | |
1355 | 51.1k | mime_field_init(field); |
1356 | | |
1357 | 51.1k | return field; |
1358 | 51.1k | } |
1359 | | |
1360 | | MIMEField * |
1361 | | mime_field_create_named(HdrHeap *heap, MIMEHdrImpl *mh, std::string_view name) |
1362 | 31.2k | { |
1363 | 31.2k | MIMEField *field = mime_field_create(heap, mh); |
1364 | 31.2k | int field_name_wks_idx = hdrtoken_tokenize(name.data(), static_cast<int>(name.length())); |
1365 | 31.2k | mime_field_name_set(heap, mh, field, field_name_wks_idx, name, true); |
1366 | 31.2k | return field; |
1367 | 31.2k | } |
1368 | | |
1369 | | void |
1370 | | mime_hdr_field_attach(MIMEHdrImpl *mh, MIMEField *field, int check_for_dups, MIMEField *prev_dup) |
1371 | 51.1k | { |
1372 | 51.1k | MIME_HDR_SANITY_CHECK(mh); |
1373 | | |
1374 | 51.1k | if (!field->is_detached()) { |
1375 | 0 | return; |
1376 | 0 | } |
1377 | | |
1378 | 51.1k | ink_assert(field->m_ptr_name != nullptr); |
1379 | | |
1380 | | ////////////////////////////////////////////////// |
1381 | | // if we don't know the head dup, or are given // |
1382 | | // a non-head dup, then search for the head dup // |
1383 | | ////////////////////////////////////////////////// |
1384 | | |
1385 | 51.1k | if (check_for_dups || (prev_dup && (!prev_dup->is_dup_head()))) { |
1386 | 19.9k | std::string_view name{field->name_get()}; |
1387 | 19.9k | prev_dup = mime_hdr_field_find(mh, name); |
1388 | 19.9k | ink_assert((prev_dup == nullptr) || (prev_dup->is_dup_head())); |
1389 | 19.9k | } |
1390 | | |
1391 | 51.1k | field->m_readiness = MIME_FIELD_SLOT_READINESS_LIVE; |
1392 | | |
1393 | | //////////////////////////////////////////////////////////////////// |
1394 | | // now, attach the new field --- if there are dups, make sure the // |
1395 | | // field is patched into the dup list in increasing slot order to // |
1396 | | // maintain the invariant that dups are chained in slot order // |
1397 | | //////////////////////////////////////////////////////////////////// |
1398 | | |
1399 | 51.1k | if (prev_dup) { |
1400 | 15.3k | MIMEField *next_dup; |
1401 | 15.3k | int field_slotnum, prev_slotnum, next_slotnum; |
1402 | | |
1403 | | ///////////////////////////////////////////////////////////////// |
1404 | | // walk down dup list looking for the last dup in slot-order // |
1405 | | // before this field object --- meaning a dup before the field // |
1406 | | // in slot order who either has no next dup, or whose next dup // |
1407 | | // is numerically after the field in slot order. // |
1408 | | ///////////////////////////////////////////////////////////////// |
1409 | | |
1410 | 15.3k | field_slotnum = mime_hdr_field_slotnum(mh, field); |
1411 | 15.3k | prev_slotnum = mime_hdr_field_slotnum(mh, prev_dup); |
1412 | 15.3k | next_dup = prev_dup->m_next_dup; |
1413 | 15.3k | next_slotnum = (next_dup ? mime_hdr_field_slotnum(mh, next_dup) : -1); |
1414 | | |
1415 | 15.3k | ink_assert(field_slotnum != prev_slotnum); |
1416 | | |
1417 | 600k | while (prev_slotnum < field_slotnum) // break if prev after field |
1418 | 600k | { |
1419 | 600k | if (next_dup == nullptr) { |
1420 | 15.3k | break; // no next dup, we're done |
1421 | 15.3k | } |
1422 | 585k | if (next_slotnum > field_slotnum) { |
1423 | 0 | break; // next dup is after us, we're done |
1424 | 0 | } |
1425 | 585k | prev_dup = next_dup; |
1426 | 585k | prev_slotnum = next_slotnum; |
1427 | 585k | next_dup = prev_dup->m_next_dup; |
1428 | 585k | } |
1429 | | |
1430 | | ///////////////////////////////////////////////////// |
1431 | | // we get here if the prev_slotnum > field_slotnum // |
1432 | | // (meaning we're now the first dup in the list), // |
1433 | | // or when we've found the correct prev and next // |
1434 | | ///////////////////////////////////////////////////// |
1435 | | |
1436 | 15.3k | if (prev_slotnum > field_slotnum) // we are now the head |
1437 | 0 | { |
1438 | | ///////////////////////////////////////////////////////////// |
1439 | | // here, it turns out that "prev_dup" is actually after // |
1440 | | // "field" in the list of fields --- so, prev_dup is a bit // |
1441 | | // of a misnomer, it is actually, the NEXT field! // |
1442 | | ///////////////////////////////////////////////////////////// |
1443 | |
|
1444 | 0 | field->m_flags = (field->m_flags | MIME_FIELD_SLOT_FLAGS_DUP_HEAD); |
1445 | 0 | field->m_next_dup = prev_dup; |
1446 | 0 | prev_dup->m_flags = (prev_dup->m_flags & ~MIME_FIELD_SLOT_FLAGS_DUP_HEAD); |
1447 | 0 | mime_hdr_set_accelerators_and_presence_bits(mh, field); |
1448 | 0 | } else // patch us after prev, and before next |
1449 | 15.3k | { |
1450 | 15.3k | ink_assert(prev_slotnum < field_slotnum); |
1451 | 15.3k | ink_assert((next_dup == nullptr) || (next_slotnum > field_slotnum)); |
1452 | 15.3k | field->m_flags = (field->m_flags & ~MIME_FIELD_SLOT_FLAGS_DUP_HEAD); |
1453 | 15.3k | ink_assert((next_dup == nullptr) || next_dup->is_live()); |
1454 | 15.3k | prev_dup->m_next_dup = field; |
1455 | 15.3k | field->m_next_dup = next_dup; |
1456 | 15.3k | } |
1457 | 35.7k | } else { |
1458 | 35.7k | field->m_flags = (field->m_flags | MIME_FIELD_SLOT_FLAGS_DUP_HEAD); |
1459 | 35.7k | mime_hdr_set_accelerators_and_presence_bits(mh, field); |
1460 | 35.7k | } |
1461 | | |
1462 | | // Now keep the cooked cache consistent |
1463 | 51.1k | ink_assert(field->is_live()); |
1464 | 51.1k | if (field->m_ptr_value && field->is_cooked()) { |
1465 | 1.45k | mh->recompute_cooked_stuff(field); |
1466 | 1.45k | } |
1467 | | |
1468 | 51.1k | MIME_HDR_SANITY_CHECK(mh); |
1469 | 51.1k | } |
1470 | | |
1471 | | void |
1472 | | mime_hdr_field_detach(MIMEHdrImpl *mh, MIMEField *field, bool detach_all_dups) |
1473 | 1.09k | { |
1474 | 1.09k | ink_assert(mh); |
1475 | 1.09k | MIMEField *next_dup = field->m_next_dup; |
1476 | | |
1477 | | // If this field is already detached, there's nothing to do. There must |
1478 | | // not be a dup list if we detached correctly. |
1479 | 1.09k | if (field->is_detached()) { |
1480 | 0 | ink_assert(next_dup == nullptr); |
1481 | 0 | return; |
1482 | 0 | } |
1483 | | |
1484 | 1.09k | ink_assert(field->is_live()); |
1485 | 1.09k | MIME_HDR_SANITY_CHECK(mh); |
1486 | | |
1487 | | // Normally, this function is called with the current dup list head, |
1488 | | // so, we need to update the accelerators after the patch out. But, if |
1489 | | // this function is ever called in the middle of a dup list, we need |
1490 | | // to walk the list to find the previous dup in the list to patch out |
1491 | | // the dup being detached. |
1492 | | |
1493 | 1.09k | if (field->m_flags & MIME_FIELD_SLOT_FLAGS_DUP_HEAD) // head of list? |
1494 | 974 | { |
1495 | 974 | if (!next_dup) // only child |
1496 | 106 | { |
1497 | 106 | mime_hdr_unset_accelerators_and_presence_bits(mh, field); |
1498 | 106 | } else // next guy is dup head |
1499 | 868 | { |
1500 | 868 | next_dup->m_flags |= MIME_FIELD_SLOT_FLAGS_DUP_HEAD; |
1501 | 868 | mime_hdr_set_accelerators_and_presence_bits(mh, next_dup); |
1502 | 868 | } |
1503 | 974 | } else // need to walk list to find and patch out from predecessor |
1504 | 122 | { |
1505 | 122 | std::string_view name{field->name_get()}; |
1506 | 122 | MIMEField *prev = mime_hdr_field_find(mh, name); |
1507 | | |
1508 | 122 | while (prev && (prev->m_next_dup != field)) { |
1509 | 0 | prev = prev->m_next_dup; |
1510 | 0 | } |
1511 | 122 | ink_assert(prev != nullptr); |
1512 | | |
1513 | 122 | if (prev->m_next_dup == field) { |
1514 | 122 | prev->m_next_dup = next_dup; |
1515 | 122 | } |
1516 | 122 | } |
1517 | | |
1518 | | // Field is now detached and alone |
1519 | 1.09k | field->m_readiness = MIME_FIELD_SLOT_READINESS_DETACHED; |
1520 | 1.09k | field->m_next_dup = nullptr; |
1521 | | |
1522 | | // Because we changed the values through detaching,update the cooked cache |
1523 | 1.09k | if (field->is_cooked()) { |
1524 | 0 | mh->recompute_cooked_stuff(field); |
1525 | 0 | } |
1526 | | |
1527 | 1.09k | MIME_HDR_SANITY_CHECK(mh); |
1528 | | |
1529 | | // At this point, the list should be back to a valid state, either the |
1530 | | // next dup detached and the accelerators set to the next dup (if any), |
1531 | | // or an interior dup detached and patched around. If we are requested |
1532 | | // to delete the whole dup list, we tail-recurse to delete it. |
1533 | | |
1534 | 1.09k | if (detach_all_dups && next_dup) { |
1535 | 0 | mime_hdr_field_detach(mh, next_dup, detach_all_dups); |
1536 | 0 | } |
1537 | 1.09k | } |
1538 | | |
1539 | | void |
1540 | | mime_hdr_field_delete(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, bool delete_all_dups) |
1541 | 1.20k | { |
1542 | 1.20k | if (delete_all_dups) { |
1543 | 1.08k | while (field) { |
1544 | 974 | MIMEField *next = field->m_next_dup; |
1545 | 974 | mime_hdr_field_delete(heap, mh, field, false); |
1546 | 974 | field = next; |
1547 | 974 | } |
1548 | 1.09k | } else { |
1549 | 1.09k | heap->free_string(field->m_ptr_name, field->m_len_name); |
1550 | 1.09k | heap->free_string(field->m_ptr_value, field->m_len_value); |
1551 | | |
1552 | 1.09k | MIME_HDR_SANITY_CHECK(mh); |
1553 | 1.09k | mime_hdr_field_detach(mh, field, false); |
1554 | | |
1555 | 1.09k | MIME_HDR_SANITY_CHECK(mh); |
1556 | 1.09k | mime_field_destroy(mh, field); |
1557 | | |
1558 | 1.09k | MIMEFieldBlockImpl *prev_block = nullptr; |
1559 | 1.09k | bool can_destroy_block = true; |
1560 | 3.46k | for (auto fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
1561 | 2.95k | if (prev_block != nullptr) { |
1562 | 1.86k | if (fblock->m_freetop == MIME_FIELD_BLOCK_SLOTS && fblock->contains(field)) { |
1563 | | // Check if fields in all slots are deleted |
1564 | 4.59k | for (auto &m_field_slot : fblock->m_field_slots) { |
1565 | 4.59k | if (m_field_slot.m_readiness != MIME_FIELD_SLOT_READINESS_DELETED) { |
1566 | 568 | can_destroy_block = false; |
1567 | 568 | break; |
1568 | 568 | } |
1569 | 4.59k | } |
1570 | | // Destroy a block and maintain the chain |
1571 | 592 | if (can_destroy_block) { |
1572 | 24 | prev_block->m_next = fblock->m_next; |
1573 | 24 | _mime_field_block_destroy(heap, fblock); |
1574 | 24 | if (prev_block->m_next == nullptr) { |
1575 | 10 | mh->m_fblock_list_tail = prev_block; |
1576 | 10 | } |
1577 | 24 | } |
1578 | 592 | break; |
1579 | 592 | } |
1580 | 1.86k | } |
1581 | 2.36k | prev_block = fblock; |
1582 | 2.36k | } |
1583 | 1.09k | } |
1584 | | |
1585 | 1.20k | MIME_HDR_SANITY_CHECK(mh); |
1586 | 1.20k | } |
1587 | | |
1588 | | auto |
1589 | | MIMEHdrImpl::find(MIMEField const *field) -> iterator |
1590 | 0 | { |
1591 | 0 | for (MIMEFieldBlockImpl *fblock = &m_first_fblock; fblock != nullptr; fblock = fblock->m_next) { |
1592 | 0 | if (fblock->contains(field)) { |
1593 | 0 | return {fblock, unsigned(field - fblock->m_field_slots)}; |
1594 | 0 | } |
1595 | 0 | } |
1596 | 0 | return {}; |
1597 | 0 | } |
1598 | | |
1599 | | // This function needs to be removed - use of it indicates poorly implemented code. |
1600 | | // That code should be updated to use the field iterators, which are much more performant. |
1601 | | int |
1602 | | mime_hdr_field_slotnum(MIMEHdrImpl *mh, MIMEField *field) |
1603 | 44.5k | { |
1604 | 44.5k | int slots_so_far; |
1605 | 44.5k | MIMEFieldBlockImpl *fblock; |
1606 | | |
1607 | 44.5k | slots_so_far = 0; |
1608 | 119k | for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
1609 | 119k | if (fblock->contains(field)) { |
1610 | 44.5k | MIMEField *first = &(fblock->m_field_slots[0]); |
1611 | 44.5k | ptrdiff_t block_slot = field - first; // in units of MIMEField |
1612 | 44.5k | return slots_so_far + block_slot; |
1613 | 44.5k | } |
1614 | 74.4k | slots_so_far += MIME_FIELD_BLOCK_SLOTS; |
1615 | 74.4k | } |
1616 | 0 | return -1; |
1617 | 44.5k | } |
1618 | | |
1619 | | MIMEField * |
1620 | | mime_hdr_prepare_for_value_set(HdrHeap *heap, MIMEHdrImpl *mh, std::string_view name) |
1621 | 0 | { |
1622 | 0 | int wks_idx; |
1623 | 0 | MIMEField *field; |
1624 | |
|
1625 | 0 | field = mime_hdr_field_find(mh, name); |
1626 | | |
1627 | | ////////////////////////////////////////////////////////////////////// |
1628 | | // this function returns with exactly one attached field created, // |
1629 | | // ready to have its value set. // |
1630 | | // // |
1631 | | // on return from field_find, there are 3 possibilities: // |
1632 | | // no field found: create attached, named field // |
1633 | | // field found w/dups: delete list, create attached, named field // |
1634 | | // dupless field found: return the field for mutation // |
1635 | | ////////////////////////////////////////////////////////////////////// |
1636 | |
|
1637 | 0 | if (field == nullptr) // no fields of this name |
1638 | 0 | { |
1639 | 0 | wks_idx = hdrtoken_tokenize(name.data(), static_cast<int>(name.length())); |
1640 | 0 | field = mime_field_create(heap, mh); |
1641 | 0 | mime_field_name_set(heap, mh, field, wks_idx, name, true); |
1642 | 0 | mime_hdr_field_attach(mh, field, 0, nullptr); |
1643 | |
|
1644 | 0 | } else if (field->m_next_dup) // list of more than 1 field |
1645 | 0 | { |
1646 | 0 | wks_idx = field->m_wks_idx; |
1647 | 0 | mime_hdr_field_delete(heap, mh, field, true); |
1648 | 0 | field = mime_field_create(heap, mh); |
1649 | 0 | mime_field_name_set(heap, mh, field, wks_idx, name, true); |
1650 | 0 | mime_hdr_field_attach(mh, field, 0, nullptr); |
1651 | 0 | } |
1652 | 0 | return field; |
1653 | 0 | } |
1654 | | |
1655 | | void |
1656 | | mime_field_destroy(MIMEHdrImpl * /* mh ATS_UNUSED */, MIMEField *field) |
1657 | 1.09k | { |
1658 | 1.09k | ink_assert(field->m_readiness == MIME_FIELD_SLOT_READINESS_DETACHED); |
1659 | 1.09k | field->m_readiness = MIME_FIELD_SLOT_READINESS_DELETED; |
1660 | 1.09k | } |
1661 | | |
1662 | | std::string_view |
1663 | | MIMEField::name_get() const |
1664 | 20.0k | { |
1665 | 20.0k | if (m_wks_idx >= 0) { |
1666 | 4.24k | return {hdrtoken_index_to_wks(m_wks_idx), m_len_name}; |
1667 | 4.24k | } |
1668 | 15.7k | return {m_ptr_name, m_len_name}; |
1669 | 20.0k | } |
1670 | | |
1671 | | void |
1672 | | mime_field_name_set(HdrHeap *heap, MIMEHdrImpl * /* mh ATS_UNUSED */, MIMEField *field, int16_t name_wks_idx_or_neg1, |
1673 | | std::string_view name, bool must_copy_string) |
1674 | 31.2k | { |
1675 | 31.2k | ink_assert(field->m_readiness == MIME_FIELD_SLOT_READINESS_DETACHED); |
1676 | | |
1677 | 31.2k | field->m_wks_idx = name_wks_idx_or_neg1; |
1678 | 31.2k | mime_str_u16_set(heap, name, &(field->m_ptr_name), &(field->m_len_name), must_copy_string); |
1679 | | |
1680 | 31.2k | if ((name_wks_idx_or_neg1 == MIME_WKSIDX_CACHE_CONTROL) || (name_wks_idx_or_neg1 == MIME_WKSIDX_PRAGMA)) { |
1681 | 0 | field->m_flags |= MIME_FIELD_SLOT_FLAGS_COOKED; |
1682 | 0 | } |
1683 | 31.2k | } |
1684 | | |
1685 | | int |
1686 | | MIMEField::value_get_index(std::string_view value) const |
1687 | 0 | { |
1688 | 0 | int retval = -1; |
1689 | 0 | auto length{static_cast<int>(value.length())}; |
1690 | | |
1691 | | // if field doesn't support commas and there is just one instance, just compare the value |
1692 | 0 | if (!this->supports_commas() && !this->has_dups()) { |
1693 | 0 | if (this->m_len_value == static_cast<uint32_t>(length) && strncasecmp(value.data(), this->m_ptr_value, length) == 0) { |
1694 | 0 | retval = 0; |
1695 | 0 | } |
1696 | 0 | } else { |
1697 | 0 | HdrCsvIter iter; |
1698 | 0 | int tok_len; |
1699 | 0 | int index = 0; |
1700 | 0 | const char *tok = iter.get_first(this, &tok_len); |
1701 | |
|
1702 | 0 | while (tok) { |
1703 | 0 | if (tok_len == length && strncasecmp(tok, value.data(), length) == 0) { |
1704 | 0 | retval = index; |
1705 | 0 | break; |
1706 | 0 | } else { |
1707 | 0 | index++; |
1708 | 0 | } |
1709 | 0 | tok = iter.get_next(&tok_len); |
1710 | 0 | } |
1711 | 0 | } |
1712 | |
|
1713 | 0 | return retval; |
1714 | 0 | } |
1715 | | |
1716 | | std::string_view |
1717 | | MIMEField::value_get() const |
1718 | 1.32k | { |
1719 | 1.32k | return {m_ptr_value, m_len_value}; |
1720 | 1.32k | } |
1721 | | |
1722 | | int32_t |
1723 | | mime_field_value_get_int(const MIMEField *field) |
1724 | 0 | { |
1725 | 0 | std::string_view value{field->value_get()}; |
1726 | |
|
1727 | 0 | return mime_parse_int(value.data(), value.data() + value.size()); |
1728 | 0 | } |
1729 | | |
1730 | | uint32_t |
1731 | | mime_field_value_get_uint(const MIMEField *field) |
1732 | 0 | { |
1733 | 0 | std::string_view value{field->value_get()}; |
1734 | |
|
1735 | 0 | return mime_parse_uint(value.data(), value.data() + value.size()); |
1736 | 0 | } |
1737 | | |
1738 | | int64_t |
1739 | | mime_field_value_get_int64(const MIMEField *field) |
1740 | 0 | { |
1741 | 0 | std::string_view value{field->value_get()}; |
1742 | |
|
1743 | 0 | return mime_parse_int64(value.data(), value.data() + value.size()); |
1744 | 0 | } |
1745 | | |
1746 | | time_t |
1747 | | mime_field_value_get_date(const MIMEField *field) |
1748 | 0 | { |
1749 | 0 | std::string_view value{field->value_get()}; |
1750 | |
|
1751 | 0 | return mime_parse_date(value.data(), value.data() + value.size()); |
1752 | 0 | } |
1753 | | |
1754 | | const char * |
1755 | | mime_field_value_get_comma_val(const MIMEField *field, int *length, int idx) |
1756 | 0 | { |
1757 | | // some fields (like Date) contain commas but should not be ripped apart |
1758 | 0 | if (!field->supports_commas()) { |
1759 | 0 | if (idx == 0) { |
1760 | 0 | auto value{field->value_get()}; |
1761 | 0 | *length = static_cast<int>(value.size()); |
1762 | 0 | return value.data(); |
1763 | 0 | } |
1764 | 0 | return nullptr; |
1765 | 0 | } else { |
1766 | 0 | Str *str; |
1767 | 0 | StrList list(false); |
1768 | |
|
1769 | 0 | mime_field_value_get_comma_list(field, &list); |
1770 | 0 | str = list.get_idx(idx); |
1771 | 0 | if (str != nullptr) { |
1772 | 0 | *length = static_cast<int>(str->len); |
1773 | 0 | return str->str; |
1774 | 0 | } else { |
1775 | 0 | *length = 0; |
1776 | 0 | return nullptr; |
1777 | 0 | } |
1778 | 0 | } |
1779 | 0 | } |
1780 | | |
1781 | | int |
1782 | | mime_field_value_get_comma_val_count(const MIMEField *field) |
1783 | 0 | { |
1784 | | // some fields (like Date) contain commas but should not be ripped apart |
1785 | 0 | if (!field->supports_commas()) { |
1786 | 0 | return ((field->m_len_value == 0) ? 0 : 1); |
1787 | 0 | } else { |
1788 | 0 | StrList list(false); |
1789 | 0 | int count = mime_field_value_get_comma_list(field, &list); |
1790 | 0 | return count; |
1791 | 0 | } |
1792 | 0 | } |
1793 | | |
1794 | | int |
1795 | | mime_field_value_get_comma_list(const MIMEField *field, StrList *list) |
1796 | 0 | { |
1797 | 0 | std::string_view value{field->value_get()}; |
1798 | | |
1799 | | // if field doesn't support commas, don't rip apart. |
1800 | 0 | if (!field->supports_commas()) { |
1801 | 0 | list->append_string(value.data(), static_cast<int>(value.size())); |
1802 | 0 | } else { |
1803 | 0 | HttpCompat::parse_tok_list(list, 1, value.data(), static_cast<int>(value.size()), ','); |
1804 | 0 | } |
1805 | |
|
1806 | 0 | return list->count; |
1807 | 0 | } |
1808 | | |
1809 | | const char * |
1810 | | mime_field_value_str_from_strlist(HdrHeap *heap, int *new_str_len_return, StrList *list) |
1811 | 0 | { |
1812 | 0 | Str *cell; |
1813 | 0 | char *new_value, *dest; |
1814 | 0 | int i, new_value_len; |
1815 | | // This works, because all strings are from the same heap when it is "split" into the list. |
1816 | 0 | HdrHeap::HeapGuard guard(heap, list->head->str); |
1817 | |
|
1818 | 0 | new_value_len = 0; |
1819 | | |
1820 | | // (1) walk the StrList cells, summing each cell's string lengths, |
1821 | | // and add 2 bytes for each ", " between cells |
1822 | 0 | cell = list->head; |
1823 | 0 | for (i = 0; i < list->count; i++) { |
1824 | 0 | new_value_len += cell->len; |
1825 | 0 | cell = cell->next; |
1826 | 0 | } |
1827 | 0 | if (list->count > 1) { |
1828 | 0 | new_value_len += (2 * (list->count - 1)); |
1829 | 0 | } |
1830 | | |
1831 | | // (2) allocate new heap string |
1832 | 0 | new_value = heap->allocate_str(new_value_len); |
1833 | | |
1834 | | // (3) copy string pieces into new heap string |
1835 | 0 | dest = new_value; |
1836 | 0 | cell = list->head; |
1837 | 0 | for (i = 0; i < list->count; i++) { |
1838 | 0 | if (i != 0) { |
1839 | 0 | *dest++ = ','; |
1840 | 0 | *dest++ = ' '; |
1841 | 0 | } |
1842 | 0 | memcpy(dest, cell->str, cell->len); |
1843 | 0 | dest += cell->len; |
1844 | 0 | cell = cell->next; |
1845 | 0 | } |
1846 | 0 | ink_assert(dest - new_value == new_value_len); |
1847 | |
|
1848 | 0 | *new_str_len_return = new_value_len; |
1849 | 0 | return new_value; |
1850 | 0 | } |
1851 | | |
1852 | | void |
1853 | | mime_field_value_set_comma_val(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int idx, std::string_view new_piece) |
1854 | 0 | { |
1855 | 0 | int len; |
1856 | 0 | Str *cell; |
1857 | 0 | StrList list(false); |
1858 | | |
1859 | | // (1) rip the value into tokens, keeping surrounding quotes, but not whitespace |
1860 | 0 | HttpCompat::parse_tok_list(&list, 0, field->m_ptr_value, field->m_len_value, ','); |
1861 | | |
1862 | | // (2) if desired index isn't valid, then don't change the field |
1863 | 0 | if ((idx < 0) || (idx >= list.count)) { |
1864 | 0 | return; |
1865 | 0 | } |
1866 | | |
1867 | | // (3) mutate cell idx |
1868 | 0 | cell = list.get_idx(idx); |
1869 | 0 | ink_assert(cell != nullptr); |
1870 | 0 | cell->str = new_piece.data(); |
1871 | 0 | cell->len = new_piece.length(); |
1872 | | |
1873 | | // (4) reassemble the new string |
1874 | 0 | field->m_ptr_value = mime_field_value_str_from_strlist(heap, &len, &list); |
1875 | 0 | field->m_len_value = len; |
1876 | | |
1877 | | // (5) keep stuff fields consistent |
1878 | 0 | field->m_n_v_raw_printable = 0; |
1879 | 0 | if (field->is_live() && field->is_cooked()) { |
1880 | 0 | mh->recompute_cooked_stuff(field); |
1881 | 0 | } |
1882 | 0 | } |
1883 | | |
1884 | | void |
1885 | | mime_field_value_delete_comma_val(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int idx) |
1886 | 0 | { |
1887 | 0 | int len; |
1888 | 0 | Str *cell; |
1889 | 0 | StrList list(false); |
1890 | | |
1891 | | // (1) rip the value into tokens, keeping surrounding quotes, but not whitespace |
1892 | 0 | HttpCompat::parse_tok_list(&list, 0, field->m_ptr_value, field->m_len_value, ','); |
1893 | | |
1894 | | // (2) if desired index isn't valid, then don't change the field |
1895 | 0 | if ((idx < 0) || (idx >= list.count)) { |
1896 | 0 | return; |
1897 | 0 | } |
1898 | | |
1899 | | // (3) delete cell idx |
1900 | 0 | cell = list.get_idx(idx); |
1901 | 0 | list.detach(cell); |
1902 | | |
1903 | | /**********************************************/ |
1904 | | /* Fix for bug INKqa09752 */ |
1905 | | /* */ |
1906 | | /* If this is the last value */ |
1907 | | /* in the field, set the m_ptr_val to NULL */ |
1908 | | /**********************************************/ |
1909 | |
|
1910 | 0 | if (list.count == 0) { |
1911 | 0 | field->m_ptr_value = nullptr; |
1912 | 0 | field->m_len_value = 0; |
1913 | 0 | } else { |
1914 | | /************************************/ |
1915 | | /* End Fix for bug INKqa09752 */ |
1916 | | /************************************/ |
1917 | | |
1918 | | // (4) reassemble the new string |
1919 | 0 | field->m_ptr_value = mime_field_value_str_from_strlist(heap, &len, &list); |
1920 | 0 | field->m_len_value = len; |
1921 | 0 | } |
1922 | | |
1923 | | // (5) keep stuff fields consistent |
1924 | 0 | field->m_n_v_raw_printable = 0; |
1925 | 0 | if (field->is_live() && field->is_cooked()) { |
1926 | 0 | mh->recompute_cooked_stuff(field); |
1927 | 0 | } |
1928 | 0 | } |
1929 | | |
1930 | | void |
1931 | | mime_field_value_insert_comma_val(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int idx, std::string_view new_piece) |
1932 | 0 | { |
1933 | 0 | int len; |
1934 | 0 | Str *cell, *prev; |
1935 | 0 | StrList list(false); |
1936 | | |
1937 | | // (1) rip the value into tokens, keeping surrounding quotes, but not whitespace |
1938 | 0 | HttpCompat::parse_tok_list(&list, 0, field->m_ptr_value, field->m_len_value, ','); |
1939 | | |
1940 | | // (2) if desired index isn't valid, then don't change the field |
1941 | 0 | if (idx < 0) { |
1942 | 0 | idx = list.count; |
1943 | 0 | } |
1944 | 0 | if (idx > list.count) { |
1945 | 0 | return; |
1946 | 0 | } |
1947 | | |
1948 | | // (3) create a new cell |
1949 | 0 | cell = list.new_cell(new_piece.data(), static_cast<int>(new_piece.length())); |
1950 | | |
1951 | | // (4) patch new cell into list at the right place |
1952 | 0 | if (idx == 0) { |
1953 | 0 | list.prepend(cell); |
1954 | 0 | } else { |
1955 | 0 | prev = list.get_idx(idx - 1); |
1956 | 0 | list.add_after(prev, cell); |
1957 | 0 | } |
1958 | | |
1959 | | // (5) reassemble the new string |
1960 | 0 | field->m_ptr_value = mime_field_value_str_from_strlist(heap, &len, &list); |
1961 | 0 | field->m_len_value = len; |
1962 | | |
1963 | | // (6) keep stuff fields consistent |
1964 | 0 | field->m_n_v_raw_printable = 0; |
1965 | 0 | if (field->is_live() && field->is_cooked()) { |
1966 | 0 | mh->recompute_cooked_stuff(field); |
1967 | 0 | } |
1968 | 0 | } |
1969 | | |
1970 | | void |
1971 | | mime_field_value_extend_comma_val(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int idx, std::string_view new_piece) |
1972 | 0 | { |
1973 | 0 | Str *cell; |
1974 | 0 | StrList list(false); |
1975 | 0 | int trimmed, len; |
1976 | 0 | size_t extended_len; |
1977 | 0 | char *dest, *temp_ptr, temp_buf[128]; |
1978 | | |
1979 | | // (1) rip the value into tokens, keeping surrounding quotes, but not whitespace |
1980 | 0 | HttpCompat::parse_tok_list(&list, 0, field->m_ptr_value, field->m_len_value, ','); |
1981 | | |
1982 | | // (2) if desired index isn't valid, then don't change the field |
1983 | 0 | if ((idx < 0) || (idx >= list.count)) { |
1984 | 0 | return; |
1985 | 0 | } |
1986 | | |
1987 | | // (3) get the cell we want to modify |
1988 | 0 | cell = list.get_idx(idx); |
1989 | 0 | ink_assert(cell != nullptr); |
1990 | | |
1991 | | // (4) trim quotes if any |
1992 | 0 | if ((cell->len >= 2) && (cell->str[0] == '\"') && (cell->str[cell->len - 1] == '\"')) { |
1993 | 0 | trimmed = 1; |
1994 | 0 | cell->str += 1; |
1995 | 0 | cell->len -= 2; |
1996 | 0 | } else { |
1997 | 0 | trimmed = 0; |
1998 | 0 | } |
1999 | | |
2000 | | // (5) compute length of extended token |
2001 | 0 | auto new_piece_len{static_cast<int>(new_piece.length())}; |
2002 | 0 | extended_len = cell->len + new_piece_len + (trimmed ? 2 : 0); |
2003 | | |
2004 | | // (6) allocate temporary space to construct new value |
2005 | 0 | if (extended_len <= sizeof(temp_buf)) { |
2006 | 0 | temp_ptr = temp_buf; |
2007 | 0 | } else { |
2008 | 0 | temp_ptr = static_cast<char *>(ats_malloc(extended_len)); |
2009 | 0 | } |
2010 | | |
2011 | | // (7) construct new extended token |
2012 | 0 | dest = temp_ptr; |
2013 | 0 | if (trimmed) { |
2014 | 0 | *dest++ = '\"'; |
2015 | 0 | } |
2016 | 0 | memcpy(dest, cell->str, cell->len); |
2017 | 0 | dest += cell->len; |
2018 | 0 | memcpy(dest, new_piece.data(), new_piece_len); |
2019 | 0 | dest += new_piece_len; |
2020 | 0 | if (trimmed) { |
2021 | 0 | *dest++ = '\"'; |
2022 | 0 | } |
2023 | 0 | ink_assert((size_t)(dest - temp_ptr) == extended_len); |
2024 | | |
2025 | | // (8) assign the new token to the cell |
2026 | 0 | cell->str = temp_ptr; |
2027 | 0 | cell->len = extended_len; |
2028 | | |
2029 | | // (9) reassemble the new string |
2030 | 0 | field->m_ptr_value = mime_field_value_str_from_strlist(heap, &len, &list); |
2031 | 0 | field->m_len_value = len; |
2032 | | |
2033 | | // (10) keep stuff fields consistent |
2034 | 0 | field->m_n_v_raw_printable = 0; |
2035 | 0 | if (field->is_live() && field->is_cooked()) { |
2036 | 0 | mh->recompute_cooked_stuff(field); |
2037 | 0 | } |
2038 | | |
2039 | | // (11) free up any temporary storage |
2040 | 0 | if (extended_len > sizeof(temp_buf)) { |
2041 | 0 | ats_free(temp_ptr); |
2042 | 0 | } |
2043 | 0 | } |
2044 | | |
2045 | | void |
2046 | | mime_field_value_set(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, std::string_view value, bool must_copy_string) |
2047 | 0 | { |
2048 | 0 | heap->free_string(field->m_ptr_value, field->m_len_value); |
2049 | |
|
2050 | 0 | if (must_copy_string && value.data()) { |
2051 | 0 | field->m_ptr_value = heap->duplicate_str(value.data(), static_cast<int>(value.length())); |
2052 | 0 | } else { |
2053 | 0 | field->m_ptr_value = nullptr; |
2054 | 0 | } |
2055 | |
|
2056 | 0 | field->m_len_value = static_cast<int>(value.length()); |
2057 | 0 | field->m_n_v_raw_printable = 0; |
2058 | | |
2059 | | // Now keep the cooked cache consistent |
2060 | 0 | if (field->is_live() && field->is_cooked()) { |
2061 | 0 | mh->recompute_cooked_stuff(field); |
2062 | 0 | } |
2063 | 0 | } |
2064 | | |
2065 | | void |
2066 | | mime_field_value_set_int(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int32_t value) |
2067 | 0 | { |
2068 | 0 | char buf[16]; |
2069 | 0 | int len = mime_format_int(buf, value, sizeof(buf)); |
2070 | 0 | mime_field_value_set(heap, mh, field, std::string_view{buf, static_cast<std::string_view::size_type>(len)}, true); |
2071 | 0 | } |
2072 | | |
2073 | | void |
2074 | | mime_field_value_set_uint(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, uint32_t value) |
2075 | 0 | { |
2076 | 0 | char buf[16]; |
2077 | 0 | int len = mime_format_uint(buf, value, sizeof(buf)); |
2078 | 0 | mime_field_value_set(heap, mh, field, std::string_view{buf, static_cast<std::string_view::size_type>(len)}, true); |
2079 | 0 | } |
2080 | | |
2081 | | void |
2082 | | mime_field_value_set_int64(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int64_t value) |
2083 | 0 | { |
2084 | 0 | char buf[21]; |
2085 | 0 | int len = mime_format_int64(buf, value, sizeof(buf)); |
2086 | 0 | mime_field_value_set(heap, mh, field, std::string_view{buf, static_cast<std::string_view::size_type>(len)}, true); |
2087 | 0 | } |
2088 | | |
2089 | | void |
2090 | | mime_field_value_set_date(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, time_t value) |
2091 | 0 | { |
2092 | 0 | char buf[33]; |
2093 | 0 | int len = mime_format_date(buf, value); |
2094 | 0 | mime_field_value_set(heap, mh, field, std::string_view{buf, static_cast<std::string_view::size_type>(len)}, true); |
2095 | 0 | } |
2096 | | |
2097 | | void |
2098 | | mime_field_name_value_set(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int16_t name_wks_idx_or_neg1, std::string_view name, |
2099 | | std::string_view value, int n_v_raw_printable, int n_v_raw_length, bool must_copy_strings) |
2100 | 19.9k | { |
2101 | 19.9k | auto name_length{static_cast<int>(name.length())}; |
2102 | 19.9k | auto value_length{static_cast<int>(value.length())}; |
2103 | 19.9k | unsigned int n_v_raw_pad = n_v_raw_length - (name_length + value_length); |
2104 | | |
2105 | 19.9k | ink_assert(field->m_readiness == MIME_FIELD_SLOT_READINESS_DETACHED); |
2106 | | |
2107 | 19.9k | if (must_copy_strings) { |
2108 | 0 | mime_field_name_set(heap, mh, field, name_wks_idx_or_neg1, name, true); |
2109 | 0 | mime_field_value_set(heap, mh, field, value, true); |
2110 | 19.9k | } else { |
2111 | 19.9k | field->m_wks_idx = name_wks_idx_or_neg1; |
2112 | 19.9k | field->m_ptr_name = name.data(); |
2113 | 19.9k | field->m_ptr_value = value.data(); |
2114 | 19.9k | field->m_len_name = name_length; |
2115 | 19.9k | field->m_len_value = value_length; |
2116 | 19.9k | if (n_v_raw_printable && (n_v_raw_pad <= 7)) { |
2117 | 231 | field->m_n_v_raw_printable = n_v_raw_printable; |
2118 | 231 | field->m_n_v_raw_printable_pad = n_v_raw_pad; |
2119 | 19.6k | } else { |
2120 | 19.6k | field->m_n_v_raw_printable = 0; |
2121 | 19.6k | } |
2122 | | |
2123 | | // Now keep the cooked cache consistent |
2124 | 19.9k | if ((name_wks_idx_or_neg1 == MIME_WKSIDX_CACHE_CONTROL) || (name_wks_idx_or_neg1 == MIME_WKSIDX_PRAGMA)) { |
2125 | 1.45k | field->m_flags |= MIME_FIELD_SLOT_FLAGS_COOKED; |
2126 | 1.45k | } |
2127 | 19.9k | if (field->is_live() && field->is_cooked()) { |
2128 | 0 | mh->recompute_cooked_stuff(field); |
2129 | 0 | } |
2130 | 19.9k | } |
2131 | 19.9k | } |
2132 | | |
2133 | | void |
2134 | | mime_field_value_append(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, std::string_view value, bool prepend_comma, |
2135 | | const char separator) |
2136 | 0 | { |
2137 | 0 | auto length{static_cast<int>(value.length())}; |
2138 | 0 | int new_length = field->m_len_value + length; |
2139 | 0 | if (prepend_comma && field->m_len_value) { |
2140 | 0 | new_length += 2; |
2141 | 0 | } |
2142 | | |
2143 | | // Start by trying expand the string we already have |
2144 | 0 | char *new_str = heap->expand_str(field->m_ptr_value, field->m_len_value, new_length); |
2145 | |
|
2146 | 0 | if (new_str == nullptr) { |
2147 | | // Expansion failed. Create a new string and copy over the value contents |
2148 | 0 | new_str = heap->allocate_str(new_length); |
2149 | 0 | memcpy(new_str, field->m_ptr_value, field->m_len_value); |
2150 | 0 | } |
2151 | |
|
2152 | 0 | char *ptr = new_str + field->m_len_value; |
2153 | 0 | if (prepend_comma && field->m_len_value) { |
2154 | 0 | *ptr++ = separator; |
2155 | 0 | *ptr++ = ' '; |
2156 | 0 | } |
2157 | |
|
2158 | 0 | memcpy(ptr, value.data(), length); |
2159 | |
|
2160 | 0 | field->m_ptr_value = new_str; |
2161 | 0 | field->m_len_value = new_length; |
2162 | 0 | field->m_n_v_raw_printable = 0; |
2163 | | |
2164 | | // Now keep the cooked cache consistent |
2165 | 0 | if (field->is_live() && field->is_cooked()) { |
2166 | 0 | mh->recompute_cooked_stuff(field); |
2167 | 0 | } |
2168 | 0 | } |
2169 | | |
2170 | | std::tuple<MIMEField *, std::string_view, std::string_view> |
2171 | | MIMEHdr::get_host_port_values() |
2172 | 0 | { |
2173 | 0 | MIMEField *field = this->field_find(static_cast<std::string_view>(MIME_FIELD_HOST)); |
2174 | 0 | std::string_view host, port; |
2175 | |
|
2176 | 0 | if (field) { |
2177 | 0 | swoc::TextView b{field->m_ptr_value, static_cast<size_t>(field->m_len_value)}; |
2178 | |
|
2179 | 0 | if (b) { |
2180 | 0 | if ('[' == *b) { |
2181 | 0 | auto idx = b.find(']'); |
2182 | 0 | if (idx < b.size() - 1 && b[idx + 1] == ':') { |
2183 | 0 | host = b.take_prefix(idx + 1); |
2184 | 0 | port = b; |
2185 | 0 | } else { |
2186 | 0 | host = b; |
2187 | 0 | } |
2188 | 0 | } else { |
2189 | 0 | host = b.take_prefix_at(':'); |
2190 | 0 | port = b; |
2191 | 0 | } |
2192 | 0 | } else { |
2193 | 0 | field = nullptr; // no value in field, signal fail. |
2194 | 0 | } |
2195 | 0 | } |
2196 | 0 | return std::make_tuple(field, host, port); |
2197 | 0 | } |
2198 | | |
2199 | | /*********************************************************************** |
2200 | | * * |
2201 | | * P A R S E R * |
2202 | | * * |
2203 | | ***********************************************************************/ |
2204 | | |
2205 | | void |
2206 | | MIMEScanner::init() |
2207 | 18.7k | { |
2208 | 18.7k | m_state = INITIAL_PARSE_STATE; |
2209 | | // Ugly, but required because of how proxy allocation works - that leaves the instance in a |
2210 | | // random state, so even assigning to it can crash. Because this method substitutes for a real |
2211 | | // constructor in the proxy allocation system, call the CTOR here. Any memory that gets allocated |
2212 | | // is supposed to be cleaned up by calling @c clear on this object. |
2213 | 18.7k | new (&m_line) std::string; |
2214 | 18.7k | } |
2215 | | |
2216 | | MIMEScanner & |
2217 | | MIMEScanner::append(TextView text) |
2218 | 2.03k | { |
2219 | 2.03k | m_line += text; |
2220 | 2.03k | return *this; |
2221 | 2.03k | } |
2222 | | |
2223 | | ParseResult |
2224 | | MIMEScanner::get(TextView &input, TextView &output, bool &output_shares_input, bool eof_p, ScanType scan_type) |
2225 | 44.7k | { |
2226 | 44.7k | ParseResult zret = ParseResult::CONT; |
2227 | | // Need this for handling dangling CR. |
2228 | 44.7k | static const char RAW_CR{ParseRules::CHAR_CR}; |
2229 | | |
2230 | 44.7k | auto text = input; |
2231 | 128k | while (ParseResult::CONT == zret && !text.empty()) { |
2232 | 84.1k | switch (m_state) { |
2233 | 31.0k | case MimeParseState::BEFORE: // waiting to find a field. |
2234 | 31.0k | m_line.resize(0); // any caller should already be done with the buffer |
2235 | 31.0k | if (ParseRules::is_cr(*text)) { |
2236 | 1.05k | ++text; |
2237 | 1.05k | if (!text.empty() && ParseRules::is_lf(*text)) { |
2238 | | // optimize a bit - this happens >99% of the time after a CR. |
2239 | 24 | ++text; |
2240 | 24 | zret = ParseResult::DONE; |
2241 | 1.02k | } else { |
2242 | 1.02k | m_state = MimeParseState::FOUND_CR; |
2243 | 1.02k | } |
2244 | 30.0k | } else if (ParseRules::is_lf(*text)) { |
2245 | 2.10k | ++text; |
2246 | 2.10k | zret = ParseResult::DONE; // Required by regression test. |
2247 | 27.9k | } else { |
2248 | | // consume this character in the next state. |
2249 | 27.9k | m_state = MimeParseState::INSIDE; |
2250 | 27.9k | } |
2251 | 31.0k | break; |
2252 | 1.01k | case MimeParseState::FOUND_CR: |
2253 | | // Looking for a field and found a CR, which should mean terminating the header. |
2254 | 1.01k | if (ParseRules::is_lf(*text)) { |
2255 | 0 | ++text; |
2256 | 0 | zret = ParseResult::DONE; |
2257 | 1.01k | } else { |
2258 | | // This really should be an error (spec doesn't permit lone CR) but the regression tests |
2259 | | // require it. |
2260 | 1.01k | this->append(TextView(&RAW_CR, 1)); // This is to fix a core dump of the icc 19.1 compiler when {&RAW_CR, 1} is used |
2261 | 1.01k | m_state = MimeParseState::INSIDE; |
2262 | 1.01k | } |
2263 | 1.01k | break; |
2264 | 29.4k | case MimeParseState::INSIDE: { |
2265 | 29.4k | auto lf_off = text.find(ParseRules::CHAR_LF); |
2266 | 29.4k | if (lf_off != TextView::npos) { |
2267 | 29.2k | text.remove_prefix(lf_off + 1); // drop up to and including LF |
2268 | 29.2k | if (ScanType::LINE == scan_type) { |
2269 | 4.95k | zret = ParseResult::OK; |
2270 | 4.95k | m_state = MimeParseState::BEFORE; |
2271 | 24.2k | } else { |
2272 | 24.2k | m_state = MimeParseState::AFTER; // looking for line folding. |
2273 | 24.2k | } |
2274 | 29.2k | } else { // no EOL, consume all text without changing state. |
2275 | 211 | text.remove_prefix(text.size()); |
2276 | 211 | } |
2277 | 29.4k | } break; |
2278 | 22.6k | case MimeParseState::AFTER: |
2279 | | // After a LF, the next line might be a continuation / folded line. That's indicated by a |
2280 | | // starting whitespace. If that's the case, back up over the preceding CR/LF with space and |
2281 | | // pretend it's the same line. |
2282 | 22.6k | if (ParseRules::is_ws(*text)) { // folded line. |
2283 | 511 | char *unfold = const_cast<char *>(text.data() - 1); |
2284 | 511 | *unfold-- = ' '; |
2285 | 511 | if (ParseRules::is_cr(*unfold)) { |
2286 | 237 | *unfold = ' '; |
2287 | 237 | } |
2288 | 511 | m_state = MimeParseState::INSIDE; // back inside the field. |
2289 | 22.1k | } else { |
2290 | 22.1k | m_state = MimeParseState::BEFORE; // field terminated. |
2291 | 22.1k | zret = ParseResult::OK; |
2292 | 22.1k | } |
2293 | 22.6k | break; |
2294 | 84.1k | } |
2295 | 84.1k | } |
2296 | | |
2297 | 44.7k | TextView parsed_text{input.data(), text.data()}; |
2298 | 44.7k | bool save_parsed_text_p = !parsed_text.empty(); |
2299 | | |
2300 | 44.7k | if (ParseResult::CONT == zret) { |
2301 | | // data ran out before we got a clear final result. There a number of things we need to check |
2302 | | // and possibly adjust that result. It's less complex to do this cleanup than handle all of |
2303 | | // these checks in the parser state machine. |
2304 | 15.4k | if (eof_p) { |
2305 | | // Should never return PARSE_CONT if we've hit EOF. |
2306 | 15.4k | if (parsed_text.empty()) { |
2307 | | // all input previously consumed. If we're between fields, that's cool. |
2308 | 13.6k | if (MimeParseState::INSIDE != m_state) { |
2309 | 13.6k | m_state = MimeParseState::BEFORE; // probably not needed... |
2310 | 13.6k | zret = ParseResult::DONE; |
2311 | 13.6k | } else { |
2312 | 0 | zret = ParseResult::ERROR; // unterminated field. |
2313 | 0 | } |
2314 | 13.6k | } else if (MimeParseState::AFTER == m_state) { |
2315 | | // Special case it seems - need to accept the final field even if there's no header |
2316 | | // terminating CR LF. This is only reasonable after absolute end of input (EOF) because |
2317 | | // otherwise this might be a multiline field where we haven't seen the next leading space. |
2318 | 1.65k | m_state = MimeParseState::BEFORE; |
2319 | 1.65k | zret = ParseResult::OK; |
2320 | 1.65k | } else { |
2321 | | // Partial input, no field / line CR LF |
2322 | 219 | zret = ParseResult::ERROR; // Unterminated field. |
2323 | 219 | } |
2324 | 15.4k | } else if (!parsed_text.empty()) { |
2325 | 0 | if (MimeParseState::INSIDE == m_state) { |
2326 | | // Inside a field but more data is expected. Save what we've got. |
2327 | 0 | this->append(parsed_text); // Do this here to force appending. |
2328 | 0 | save_parsed_text_p = false; // don't double append. |
2329 | 0 | } else if (MimeParseState::AFTER == m_state) { |
2330 | | // After a field but we still have data. Need to parse it too. |
2331 | 0 | m_state = MimeParseState::BEFORE; |
2332 | 0 | zret = ParseResult::OK; |
2333 | 0 | } |
2334 | 0 | } |
2335 | 15.4k | } |
2336 | | |
2337 | 44.7k | if (save_parsed_text_p && !m_line.empty()) { |
2338 | | // If we're already accumulating, continue to do so if we have data. |
2339 | 1.01k | this->append(parsed_text); |
2340 | 1.01k | } |
2341 | | |
2342 | | // adjust out arguments. |
2343 | 44.7k | output_shares_input = true; |
2344 | 44.7k | if (ParseResult::CONT != zret) { |
2345 | 44.7k | if (!m_line.empty()) { |
2346 | 1.03k | output = m_line; // cleared when called with state MimeParseState::BEFORE |
2347 | 1.03k | output_shares_input = false; |
2348 | 43.6k | } else { |
2349 | 43.6k | output = parsed_text; |
2350 | 43.6k | } |
2351 | 44.7k | } |
2352 | | |
2353 | | // Make sure there are no null characters in the input scanned so far |
2354 | 44.7k | if (zret != ParseResult::ERROR && TextView::npos != parsed_text.find('\0')) { |
2355 | 54 | zret = ParseResult::ERROR; |
2356 | 54 | } |
2357 | | |
2358 | 44.7k | input.remove_prefix(parsed_text.size()); |
2359 | 44.7k | return zret; |
2360 | 44.7k | } |
2361 | | |
2362 | | void |
2363 | | _mime_parser_init(MIMEParser *parser) |
2364 | 37.4k | { |
2365 | 37.4k | parser->m_field = 0; |
2366 | 37.4k | parser->m_field_flags = 0; |
2367 | 37.4k | parser->m_value = -1; |
2368 | 37.4k | } |
2369 | | ////////////////////////////////////////////////////// |
2370 | | // init first time structure setup // |
2371 | | // clear resets an already-initialized structure // |
2372 | | ////////////////////////////////////////////////////// |
2373 | | void |
2374 | | mime_parser_init(MIMEParser *parser) |
2375 | 18.7k | { |
2376 | 18.7k | parser->m_scanner.init(); |
2377 | 18.7k | _mime_parser_init(parser); |
2378 | 18.7k | } |
2379 | | |
2380 | | void |
2381 | | mime_parser_clear(MIMEParser *parser) |
2382 | 18.7k | { |
2383 | 18.7k | parser->m_scanner.clear(); |
2384 | 18.7k | _mime_parser_init(parser); |
2385 | 18.7k | } |
2386 | | |
2387 | | ParseResult |
2388 | | mime_parser_parse(MIMEParser *parser, HdrHeap *heap, MIMEHdrImpl *mh, const char **real_s, const char *real_e, |
2389 | | bool must_copy_strings, bool eof, bool remove_ws_from_field_name, size_t max_hdr_field_size) |
2390 | 2.29k | { |
2391 | 2.29k | ParseResult err; |
2392 | 2.29k | bool line_is_real; |
2393 | | |
2394 | 2.29k | MIMEScanner *scanner = &parser->m_scanner; |
2395 | | |
2396 | 25.9k | while (true) { |
2397 | | //////////////////////////////////////////////////////////////////////////// |
2398 | | // get a name:value line, with all continuation lines glued into one line // |
2399 | | //////////////////////////////////////////////////////////////////////////// |
2400 | | |
2401 | 25.9k | TextView text{*real_s, real_e}; |
2402 | 25.9k | TextView parsed; |
2403 | 25.9k | err = scanner->get(text, parsed, line_is_real, eof, MIMEScanner::ScanType::FIELD); |
2404 | 25.9k | *real_s = text.data(); |
2405 | 25.9k | if (err != ParseResult::OK) { |
2406 | 2.20k | return err; |
2407 | 2.20k | } |
2408 | | |
2409 | | ////////////////////////////////////////////////// |
2410 | | // if got a LF or CR on its own, end the header // |
2411 | | ////////////////////////////////////////////////// |
2412 | | |
2413 | 23.7k | if ((parsed.size() >= 2) && (parsed[0] == ParseRules::CHAR_CR) && (parsed[1] == ParseRules::CHAR_LF)) { |
2414 | 0 | return ParseResult::DONE; |
2415 | 0 | } |
2416 | | |
2417 | 23.7k | if ((parsed.size() >= 1) && (parsed[0] == ParseRules::CHAR_LF)) { |
2418 | 0 | return ParseResult::DONE; |
2419 | 0 | } |
2420 | | |
2421 | | ///////////////////////////////////////////// |
2422 | | // find pointers into the name:value field // |
2423 | | ///////////////////////////////////////////// |
2424 | | |
2425 | | /** |
2426 | | * Fix for INKqa09141. The is_token function fails for '@' character. |
2427 | | * Header names starting with '@' signs are valid headers. Hence we |
2428 | | * have to add one more check to see if the first parameter is '@' |
2429 | | * character then, the header name is valid. |
2430 | | **/ |
2431 | 23.7k | if ((!ParseRules::is_token(*parsed)) && (*parsed != '@')) { |
2432 | 1.50k | continue; // toss away garbage line |
2433 | 1.50k | } |
2434 | | |
2435 | | // find name last |
2436 | 22.2k | auto field_value = parsed; // need parsed as is later on. |
2437 | 22.2k | auto field_name = field_value.split_prefix_at(':'); |
2438 | 22.2k | if (field_name.empty()) { |
2439 | 2.25k | continue; // toss away garbage line |
2440 | 2.25k | } |
2441 | | |
2442 | | // RFC7230 section 3.2.4: |
2443 | | // No whitespace is allowed between the header field-name and colon. In |
2444 | | // the past, differences in the handling of such whitespace have led to |
2445 | | // security vulnerabilities in request routing and response handling. A |
2446 | | // server MUST reject any received request message that contains |
2447 | | // whitespace between a header field-name and colon with a response code |
2448 | | // of 400 (Bad Request). |
2449 | | // A proxy MUST remove any such whitespace from a response message before |
2450 | | // forwarding the message downstream. |
2451 | 20.0k | bool raw_print_field = true; |
2452 | 20.0k | if (is_ws(field_name.back())) { |
2453 | 235 | if (!remove_ws_from_field_name) { |
2454 | 6 | return ParseResult::ERROR; |
2455 | 6 | } |
2456 | 229 | field_name.rtrim_if(&ParseRules::is_ws); |
2457 | 229 | raw_print_field = false; |
2458 | 19.7k | } else if (parsed.suffix(2) != "\r\n") { |
2459 | 19.4k | raw_print_field = false; |
2460 | 19.4k | } |
2461 | | |
2462 | | // find value first |
2463 | 20.0k | field_value.ltrim_if(&ParseRules::is_ws); |
2464 | 20.0k | field_value.rtrim_if(&ParseRules::is_wslfcr); |
2465 | | |
2466 | | // Make sure the name + value is not longer than configured max_hdr_field_size |
2467 | 20.0k | if (field_name.size() + field_value.size() > max_hdr_field_size) { |
2468 | 0 | return ParseResult::ERROR; |
2469 | 0 | } |
2470 | | |
2471 | | // int total_line_length = (int)(field_line_last - field_line_first + 1); |
2472 | | |
2473 | | ////////////////////////////////////////////////////////////////////// |
2474 | | // if we can't leave the name & value in the real buffer, copy them // |
2475 | | ////////////////////////////////////////////////////////////////////// |
2476 | | |
2477 | 20.0k | if (must_copy_strings || (!line_is_real)) { |
2478 | 20.0k | char *dup = heap->duplicate_str(parsed.data(), parsed.size()); |
2479 | 20.0k | ptrdiff_t delta = dup - parsed.data(); |
2480 | 20.0k | field_name.assign(field_name.data() + delta, field_name.size()); |
2481 | 20.0k | field_value.assign(field_value.data() + delta, field_value.size()); |
2482 | 20.0k | } |
2483 | | /////////////////////// |
2484 | | // tokenize the name // |
2485 | | /////////////////////// |
2486 | | |
2487 | 20.0k | int field_name_wks_idx = hdrtoken_tokenize(field_name.data(), field_name.size()); |
2488 | | |
2489 | 20.0k | if (field_name_wks_idx < 0) { |
2490 | 22.0k | for (auto i : field_name) { |
2491 | 22.0k | if (!ParseRules::is_http_field_name(i)) { |
2492 | 57 | return ParseResult::ERROR; |
2493 | 57 | } |
2494 | 22.0k | } |
2495 | 15.8k | } |
2496 | | |
2497 | | // RFC 9110 Section 5.5. Field Values |
2498 | 46.2k | for (char i : field_value) { |
2499 | | // FIXME: ParseRules::is_http_field_value() should be used but the implementation looks wrong |
2500 | 46.2k | if (ParseRules::is_control(i)) { |
2501 | 26 | return ParseResult::ERROR; |
2502 | 26 | } |
2503 | 46.2k | } |
2504 | | |
2505 | | /////////////////////////////////////////// |
2506 | | // build and insert the new field object // |
2507 | | /////////////////////////////////////////// |
2508 | | |
2509 | 19.9k | MIMEField *field = mime_field_create(heap, mh); |
2510 | 19.9k | mime_field_name_value_set(heap, mh, field, field_name_wks_idx, field_name, field_value, raw_print_field, parsed.size(), false); |
2511 | 19.9k | mime_hdr_field_attach(mh, field, 1, nullptr); |
2512 | 19.9k | } |
2513 | 2.29k | } |
2514 | | |
2515 | | void |
2516 | | mime_hdr_describe(HdrHeapObjImpl *raw, bool recurse) |
2517 | 0 | { |
2518 | 0 | MIMEFieldBlockImpl *fblock; |
2519 | 0 | MIMEHdrImpl *obj = (MIMEHdrImpl *)raw; |
2520 | |
|
2521 | 0 | Dbg(dbg_ctl_http, "\t[PBITS: 0x%08X%08X, SLACC: 0x%04X%04X%04X%04X, HEADBLK: %p, TAILBLK: %p]", |
2522 | 0 | (uint32_t)((obj->m_presence_bits >> 32) & (TOK_64_CONST(0xFFFFFFFF))), |
2523 | 0 | (uint32_t)((obj->m_presence_bits >> 0) & (TOK_64_CONST(0xFFFFFFFF))), obj->m_slot_accelerators[0], |
2524 | 0 | obj->m_slot_accelerators[1], obj->m_slot_accelerators[2], obj->m_slot_accelerators[3], &(obj->m_first_fblock), |
2525 | 0 | obj->m_fblock_list_tail); |
2526 | |
|
2527 | 0 | Dbg(dbg_ctl_http, "\t[CBITS: 0x%08X, T_MAXAGE: %d, T_SMAXAGE: %d, T_MAXSTALE: %d, T_MINFRESH: %d, PNO$: %d]", |
2528 | 0 | obj->m_cooked_stuff.m_cache_control.m_mask, obj->m_cooked_stuff.m_cache_control.m_secs_max_age, |
2529 | 0 | obj->m_cooked_stuff.m_cache_control.m_secs_s_maxage, obj->m_cooked_stuff.m_cache_control.m_secs_max_stale, |
2530 | 0 | obj->m_cooked_stuff.m_cache_control.m_secs_min_fresh, obj->m_cooked_stuff.m_pragma.m_no_cache); |
2531 | 0 | for (fblock = &(obj->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
2532 | 0 | if (recurse || (fblock == &(obj->m_first_fblock))) { |
2533 | 0 | obj_describe((HdrHeapObjImpl *)fblock, recurse); |
2534 | 0 | } |
2535 | 0 | } |
2536 | 0 | } |
2537 | | |
2538 | | void |
2539 | | mime_field_block_describe(HdrHeapObjImpl *raw, bool /* recurse ATS_UNUSED */) |
2540 | 0 | { |
2541 | 0 | unsigned int i; |
2542 | 0 | static constexpr const char *readiness_names[] = {"EMPTY", "DETACHED", "LIVE", "DELETED"}; |
2543 | |
|
2544 | 0 | MIMEFieldBlockImpl *obj = (MIMEFieldBlockImpl *)raw; |
2545 | |
|
2546 | 0 | Dbg(dbg_ctl_http, "[FREETOP: %d, NEXTBLK: %p]", obj->m_freetop, obj->m_next); |
2547 | |
|
2548 | 0 | for (i = 0; i < obj->m_freetop; i++) { |
2549 | 0 | MIMEField *f = &(obj->m_field_slots[i]); |
2550 | 0 | Dbg(dbg_ctl_http, "\tSLOT #%2d (%p), %-8s", i, f, readiness_names[f->m_readiness]); |
2551 | |
|
2552 | 0 | switch (f->m_readiness) { |
2553 | 0 | case MIME_FIELD_SLOT_READINESS_EMPTY: |
2554 | 0 | break; |
2555 | 0 | case MIME_FIELD_SLOT_READINESS_DETACHED: |
2556 | 0 | case MIME_FIELD_SLOT_READINESS_LIVE: |
2557 | 0 | case MIME_FIELD_SLOT_READINESS_DELETED: |
2558 | 0 | Dbg(dbg_ctl_http, "[N: \"%.*s\", N_LEN: %d, N_IDX: %d, ", f->m_len_name, (f->m_ptr_name ? f->m_ptr_name : "NULL"), |
2559 | 0 | f->m_len_name, f->m_wks_idx); |
2560 | 0 | Dbg(dbg_ctl_http, "V: \"%.*s\", V_LEN: %d, ", f->m_len_value, (f->m_ptr_value ? f->m_ptr_value : "NULL"), f->m_len_value); |
2561 | 0 | Dbg(dbg_ctl_http, "NEXTDUP: %p, RAW: %d, RAWLEN: %d, F: %d]", f->m_next_dup, f->m_n_v_raw_printable, |
2562 | 0 | f->m_len_name + f->m_len_value + f->m_n_v_raw_printable_pad, f->m_flags); |
2563 | 0 | break; |
2564 | 0 | } |
2565 | 0 | Dbg(dbg_ctl_http, "\n"); |
2566 | 0 | } |
2567 | 0 | } |
2568 | | |
2569 | | int |
2570 | | mime_hdr_print(MIMEHdrImpl const *mh, char *buf_start, int buf_length, int *buf_index_inout, int *buf_chars_to_skip_inout) |
2571 | 0 | { |
2572 | 0 | MIMEFieldBlockImpl const *fblock; |
2573 | 0 | MIMEField const *field; |
2574 | 0 | uint32_t index; |
2575 | |
|
2576 | 0 | #define SIMPLE_MIME_HDR_PRINT |
2577 | 0 | #ifdef SIMPLE_MIME_HDR_PRINT |
2578 | 0 | for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
2579 | 0 | for (index = 0; index < fblock->m_freetop; index++) { |
2580 | 0 | field = &(fblock->m_field_slots[index]); |
2581 | 0 | if (field->is_live()) { |
2582 | 0 | if (!mime_field_print(field, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout)) { |
2583 | 0 | return 0; |
2584 | 0 | } |
2585 | 0 | } |
2586 | 0 | } |
2587 | 0 | } |
2588 | | #else |
2589 | | // FIX: if not raw_printable, need to print with mime_field_print, |
2590 | | // not mime_mem_print |
2591 | | for (fblock = &(mh->m_first_fblock); fblock != NULL; fblock = fblock->m_next) { |
2592 | | const char *contig_start = NULL; |
2593 | | int this_length, contig_length = 0; |
2594 | | for (index = 0; index < fblock->m_freetop; index++) { |
2595 | | field = &(fblock->m_field_slots[index]); |
2596 | | this_length = field->m_len_name + field->m_len_value + field->m_n_v_raw_printable_pad; |
2597 | | if (field->is_live()) { |
2598 | | if ((field->m_ptr_name == contig_start + contig_length) && field->m_n_v_raw_printable && |
2599 | | ((buf_index_inout == NULL) || (contig_length + this_length <= buf_length - *buf_index_inout))) { |
2600 | | contig_length += this_length; |
2601 | | } else { |
2602 | | if (contig_length > 0) { |
2603 | | if (!mime_mem_print(std::string_view{contig_start, static_cast<std::string_view::size_type>(contig_length)}, buf_start, |
2604 | | buf_length, buf_index_inout, buf_chars_to_skip_inout)) |
2605 | | return 0; |
2606 | | } |
2607 | | contig_start = field->m_ptr_name; |
2608 | | contig_length = this_length; |
2609 | | } |
2610 | | } |
2611 | | } |
2612 | | |
2613 | | if (contig_length > 0) { |
2614 | | if (!mime_mem_print(std::string_view{ontig_start, static_cast<std::string_view::size_type>(contig_length)}, buf_start, |
2615 | | buf_length, buf_index_inout, buf_chars_to_skip_inout)) |
2616 | | return 0; |
2617 | | } |
2618 | | } |
2619 | | #endif |
2620 | | |
2621 | 0 | if (!mime_mem_print("\r\n"sv, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout)) { |
2622 | 0 | return 0; |
2623 | 0 | } |
2624 | | |
2625 | 0 | return 1; |
2626 | 0 | } |
2627 | | |
2628 | | namespace |
2629 | | { |
2630 | | int |
2631 | | mime_mem_print_(std::string_view src, char *buf_start, int buf_length, int *buf_index_inout, int *buf_chars_to_skip_inout, |
2632 | | int (*char_transform)(int char_in)) |
2633 | 0 | { |
2634 | 0 | if (buf_start == nullptr) { // this case should only be used by test_header |
2635 | 0 | ink_release_assert(buf_index_inout == nullptr); |
2636 | 0 | ink_release_assert(buf_chars_to_skip_inout == nullptr); |
2637 | 0 | for (auto c : src) { |
2638 | 0 | putchar(c); |
2639 | 0 | } |
2640 | 0 | return 1; |
2641 | 0 | } |
2642 | | |
2643 | 0 | ink_assert(src.data() != nullptr); |
2644 | |
|
2645 | 0 | if (*buf_chars_to_skip_inout > 0) { |
2646 | 0 | if (*buf_chars_to_skip_inout >= static_cast<int>(src.length())) { |
2647 | 0 | *buf_chars_to_skip_inout -= static_cast<int>(src.length()); |
2648 | 0 | return 1; |
2649 | 0 | } else { |
2650 | 0 | src.remove_prefix(*buf_chars_to_skip_inout); |
2651 | 0 | *buf_chars_to_skip_inout = 0; |
2652 | 0 | } |
2653 | 0 | } |
2654 | | |
2655 | 0 | auto src_l{static_cast<int>(src.length())}; |
2656 | 0 | int copy_l = std::min(buf_length - *buf_index_inout, src_l); |
2657 | 0 | if (copy_l > 0) { |
2658 | 0 | buf_start += *buf_index_inout; |
2659 | 0 | auto src_d{src.data()}; |
2660 | 0 | std::transform(src_d, src_d + copy_l, buf_start, char_transform); |
2661 | 0 | *buf_index_inout += copy_l; |
2662 | 0 | } |
2663 | 0 | return (src_l == copy_l); |
2664 | 0 | } |
2665 | | |
2666 | | int |
2667 | | to_same_char(int ch) |
2668 | 0 | { |
2669 | 0 | return ch; |
2670 | 0 | } |
2671 | | |
2672 | | } // end anonymous namespace |
2673 | | |
2674 | | int |
2675 | | mime_mem_print(std::string_view src, char *buf_start, int buf_length, int *buf_index_inout, int *buf_chars_to_skip_inout) |
2676 | 0 | { |
2677 | 0 | return mime_mem_print_(src, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout, to_same_char); |
2678 | 0 | } |
2679 | | |
2680 | | int |
2681 | | mime_mem_print_lc(std::string_view src, char *buf_start, int buf_length, int *buf_index_inout, int *buf_chars_to_skip_inout) |
2682 | 0 | { |
2683 | 0 | return mime_mem_print_(src, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout, std::tolower); |
2684 | 0 | } |
2685 | | |
2686 | | int |
2687 | | mime_field_print(MIMEField const *field, char *buf_start, int buf_length, int *buf_index_inout, int *buf_chars_to_skip_inout) |
2688 | 0 | { |
2689 | 0 | #define TRY(x) \ |
2690 | 0 | if (!x) \ |
2691 | 0 | return 0 |
2692 | |
|
2693 | 0 | int total_len; |
2694 | | |
2695 | | // Don't print names that begin with an '@'. |
2696 | 0 | if (field->m_ptr_name[0] == '@') { |
2697 | 0 | return 1; |
2698 | 0 | } |
2699 | | |
2700 | 0 | if (field->m_n_v_raw_printable) { |
2701 | 0 | total_len = field->m_len_name + field->m_len_value + field->m_n_v_raw_printable_pad; |
2702 | |
|
2703 | 0 | if ((buf_start != nullptr) && (*buf_chars_to_skip_inout == 0) && (total_len <= (buf_length - *buf_index_inout))) { |
2704 | 0 | buf_start += *buf_index_inout; |
2705 | 0 | memcpy(buf_start, field->m_ptr_name, total_len); |
2706 | 0 | *buf_index_inout += total_len; |
2707 | |
|
2708 | 0 | } else { |
2709 | 0 | TRY(mime_mem_print(std::string_view{field->m_ptr_name, static_cast<std::string_view::size_type>(total_len)}, buf_start, |
2710 | 0 | buf_length, buf_index_inout, buf_chars_to_skip_inout)); |
2711 | 0 | } |
2712 | 0 | } else { |
2713 | 0 | total_len = field->m_len_name + field->m_len_value + 2 + 2; |
2714 | | |
2715 | | // try to handle on fast path |
2716 | |
|
2717 | 0 | if ((buf_start != nullptr) && (*buf_chars_to_skip_inout == 0) && (total_len <= (buf_length - *buf_index_inout))) { |
2718 | 0 | buf_start += *buf_index_inout; |
2719 | |
|
2720 | 0 | memcpy(buf_start, field->m_ptr_name, field->m_len_name); |
2721 | 0 | buf_start += field->m_len_name; |
2722 | |
|
2723 | 0 | buf_start[0] = ':'; |
2724 | 0 | buf_start[1] = ' '; |
2725 | 0 | buf_start += 2; |
2726 | |
|
2727 | 0 | memcpy(buf_start, field->m_ptr_value, field->m_len_value); |
2728 | 0 | buf_start += field->m_len_value; |
2729 | |
|
2730 | 0 | buf_start[0] = '\r'; |
2731 | 0 | buf_start[1] = '\n'; |
2732 | |
|
2733 | 0 | *buf_index_inout += total_len; |
2734 | 0 | } else { |
2735 | 0 | TRY(mime_mem_print(std::string_view{field->m_ptr_name, static_cast<std::string_view::size_type>(field->m_len_name)}, |
2736 | 0 | buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout)); |
2737 | 0 | TRY(mime_mem_print(": "sv, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout)); |
2738 | 0 | TRY(mime_mem_print(std::string_view{field->m_ptr_value, static_cast<std::string_view::size_type>(field->m_len_value)}, |
2739 | 0 | buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout)); |
2740 | 0 | TRY(mime_mem_print("\r\n"sv, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout)); |
2741 | 0 | } |
2742 | 0 | } |
2743 | | |
2744 | 0 | return 1; |
2745 | |
|
2746 | 0 | #undef TRY |
2747 | 0 | } |
2748 | | |
2749 | | const char * |
2750 | | mime_str_u16_set(HdrHeap *heap, std::string_view src, const char **d_str, uint16_t *d_len, bool must_copy) |
2751 | 35.1k | { |
2752 | 35.1k | auto s_len{static_cast<int>(src.length())}; |
2753 | 35.1k | ink_assert(s_len >= 0 && s_len < UINT16_MAX); |
2754 | | // INKqa08287 - keep track of free string space. |
2755 | | // INVARIANT: passed in result pointers must be to |
2756 | | // either NULL or be valid ptr for a string already |
2757 | | // the string heaps |
2758 | 35.1k | heap->free_string(*d_str, *d_len); |
2759 | | |
2760 | 35.1k | auto s_str{src.data()}; |
2761 | 35.1k | if (must_copy && s_str) { |
2762 | 34.4k | s_str = heap->duplicate_str(s_str, s_len); |
2763 | 34.4k | } |
2764 | 35.1k | *d_str = s_str; |
2765 | 35.1k | *d_len = s_len; |
2766 | 35.1k | return s_str; |
2767 | 35.1k | } |
2768 | | |
2769 | | int |
2770 | | mime_field_length_get(MIMEField *field) |
2771 | 0 | { |
2772 | 0 | if (field->m_n_v_raw_printable) { |
2773 | 0 | return (field->m_len_name + field->m_len_value + field->m_n_v_raw_printable_pad); |
2774 | 0 | } else { |
2775 | 0 | return (field->m_len_name + field->m_len_value + 4); // add ": \r\n" |
2776 | 0 | } |
2777 | 0 | } |
2778 | | |
2779 | | int |
2780 | | mime_format_int(char *buf, int32_t val, size_t buf_len) |
2781 | 0 | { |
2782 | 0 | return ink_fast_itoa(val, buf, buf_len); |
2783 | 0 | } |
2784 | | |
2785 | | int |
2786 | | mime_format_uint(char *buf, uint32_t val, size_t buf_len) |
2787 | 0 | { |
2788 | 0 | return ink_fast_uitoa(val, buf, buf_len); |
2789 | 0 | } |
2790 | | |
2791 | | int |
2792 | | mime_format_int64(char *buf, int64_t val, size_t buf_len) |
2793 | 0 | { |
2794 | 0 | return ink_fast_ltoa(val, buf, buf_len); |
2795 | 0 | } |
2796 | | |
2797 | | void |
2798 | | mime_days_since_epoch_to_mdy_slowcase(time_t days_since_jan_1_1970, int *m_return, int *d_return, int *y_return) |
2799 | 733 | { |
2800 | 733 | static constexpr int DAYS_OFFSET = 25508; |
2801 | | |
2802 | 733 | static const char months[] = { |
2803 | 733 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, |
2804 | 733 | 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, |
2805 | 733 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, |
2806 | 733 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, |
2807 | 733 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, |
2808 | 733 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, |
2809 | 733 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, |
2810 | 733 | 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, |
2811 | 733 | 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, |
2812 | 733 | 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 0, 0, 0, |
2813 | 733 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, |
2814 | 733 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; |
2815 | | |
2816 | 733 | static constexpr int days[12] = {305, 336, -1, 30, 60, 91, 121, 152, 183, 213, 244, 274}; |
2817 | | |
2818 | 733 | time_t mday, year, month, d, dp; |
2819 | | |
2820 | 733 | mday = days_since_jan_1_1970; |
2821 | | |
2822 | | /* guess the year and refine the guess */ |
2823 | 733 | year = mday / 365 + 69; |
2824 | 733 | d = dp = (year * 365) + (year / 4) - (year / 100) + (year / 100 + 3) / 4 - DAYS_OFFSET - 1; |
2825 | | |
2826 | 2.05k | while (dp < mday) { |
2827 | 1.32k | d = dp; |
2828 | 1.32k | year += 1; |
2829 | 1.32k | dp = (year * 365) + (year / 4) - (year / 100) + (year / 100 + 3) / 4 - DAYS_OFFSET - 1; |
2830 | 1.32k | } |
2831 | | |
2832 | | /* convert the days */ |
2833 | 733 | d = mday - d; |
2834 | 733 | if ((d < 0) || (d > 366)) { |
2835 | 0 | ink_assert(!"bad date"); |
2836 | 733 | } else { |
2837 | 733 | month = months[d]; |
2838 | 733 | if (month > 1) { |
2839 | 615 | year -= 1; |
2840 | 615 | } |
2841 | | |
2842 | 733 | mday = d - days[month] - 1; |
2843 | 733 | year += 1900; |
2844 | | |
2845 | | // coverity[Y2K38_SAFETY:FALSE] |
2846 | 733 | *m_return = month; |
2847 | | // coverity[Y2K38_SAFETY:FALSE] |
2848 | 733 | *d_return = mday; |
2849 | | // coverity[Y2K38_SAFETY:FALSE] |
2850 | 733 | *y_return = year; |
2851 | 733 | } |
2852 | 733 | } |
2853 | | |
2854 | | void |
2855 | | mime_days_since_epoch_to_mdy(time_t days_since_jan_1_1970, int *m_return, int *d_return, int *y_return) |
2856 | 0 | { |
2857 | 0 | ink_assert(_days_to_mdy_fast_lookup_table != nullptr); |
2858 | | |
2859 | | ///////////////////////////////////////////////////////////// |
2860 | | // if we have a fast lookup entry for this date, return it // |
2861 | | ///////////////////////////////////////////////////////////// |
2862 | |
|
2863 | 0 | if ((days_since_jan_1_1970 >= _days_to_mdy_fast_lookup_table_first_day) && |
2864 | 0 | (days_since_jan_1_1970 <= _days_to_mdy_fast_lookup_table_last_day)) { |
2865 | | //////////////////////////////////////////////////////////////// |
2866 | | // to speed up the days_since_epoch to m/d/y conversion, we // |
2867 | | // use a pre-computed lookup table to support the common case // |
2868 | | // of dates that are +/- one year from today. // |
2869 | | //////////////////////////////////////////////////////////////// |
2870 | |
|
2871 | 0 | time_t i = days_since_jan_1_1970 - _days_to_mdy_fast_lookup_table_first_day; |
2872 | 0 | *m_return = _days_to_mdy_fast_lookup_table[i].m; |
2873 | 0 | *d_return = _days_to_mdy_fast_lookup_table[i].d; |
2874 | 0 | *y_return = _days_to_mdy_fast_lookup_table[i].y; |
2875 | 0 | return; |
2876 | 0 | } |
2877 | | //////////////////////////////////// |
2878 | | // otherwise, return the slow way // |
2879 | | //////////////////////////////////// |
2880 | | |
2881 | 0 | mime_days_since_epoch_to_mdy_slowcase(days_since_jan_1_1970, m_return, d_return, y_return); |
2882 | 0 | } |
2883 | | |
2884 | | int |
2885 | | mime_format_date(char *buffer, time_t value) |
2886 | 0 | { |
2887 | | // must be 3 characters! |
2888 | 0 | static constexpr const char *daystrs[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; |
2889 | | |
2890 | | // must be 3 characters! |
2891 | 0 | static constexpr const char *monthstrs[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; |
2892 | |
|
2893 | 0 | static constexpr const char *digitstrs[] = { |
2894 | 0 | "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", |
2895 | 0 | "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", |
2896 | 0 | "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", |
2897 | 0 | "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", |
2898 | 0 | "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", |
2899 | 0 | }; |
2900 | |
|
2901 | 0 | char *buf; |
2902 | 0 | int sec, min, hour, wday, mday = 0, year = 0, month = 0; |
2903 | |
|
2904 | 0 | buf = buffer; |
2905 | |
|
2906 | 0 | sec = static_cast<int>(value % 60); |
2907 | 0 | value /= 60; |
2908 | 0 | min = static_cast<int>(value % 60); |
2909 | 0 | value /= 60; |
2910 | 0 | hour = static_cast<int>(value % 24); |
2911 | 0 | value /= 24; |
2912 | | |
2913 | | /* Jan 1, 1970 was a Thursday */ |
2914 | 0 | wday = static_cast<int>((4 + value) % 7); |
2915 | | |
2916 | | /* value is days since Jan 1, 1970 */ |
2917 | 0 | #if MIME_FORMAT_DATE_USE_LOOKUP_TABLE |
2918 | 0 | mime_days_since_epoch_to_mdy(value, &month, &mday, &year); |
2919 | | #else |
2920 | | mime_days_since_epoch_to_mdy_slowcase(value, &month, &mday, &year); |
2921 | | #endif |
2922 | | |
2923 | | /* arrange the date in the buffer */ |
2924 | 0 | ink_assert((mday >= 0) && (mday <= 99)); |
2925 | 0 | ink_assert((hour >= 0) && (hour <= 99)); |
2926 | 0 | ink_assert((min >= 0) && (min <= 99)); |
2927 | 0 | ink_assert((sec >= 0) && (sec <= 99)); |
2928 | | |
2929 | | /* the day string */ |
2930 | 0 | const char *three_char_day = daystrs[wday]; |
2931 | 0 | buf[0] = three_char_day[0]; |
2932 | 0 | buf[1] = three_char_day[1]; |
2933 | 0 | buf[2] = three_char_day[2]; |
2934 | 0 | buf += 3; |
2935 | |
|
2936 | 0 | buf[0] = ','; |
2937 | 0 | buf[1] = ' '; |
2938 | 0 | buf += 2; |
2939 | | |
2940 | | /* the day of month */ |
2941 | 0 | buf[0] = digitstrs[mday][0]; |
2942 | 0 | buf[1] = digitstrs[mday][1]; |
2943 | 0 | buf[2] = ' '; |
2944 | 0 | buf += 3; |
2945 | | |
2946 | | /* the month string */ |
2947 | 0 | const char *three_char_month = monthstrs[month]; |
2948 | 0 | buf[0] = three_char_month[0]; |
2949 | 0 | buf[1] = three_char_month[1]; |
2950 | 0 | buf[2] = three_char_month[2]; |
2951 | 0 | buf += 3; |
2952 | | |
2953 | | /* the year */ |
2954 | 0 | buf[0] = ' '; |
2955 | |
|
2956 | 0 | if ((year >= 2000) && (year <= 2009)) { |
2957 | 0 | buf[1] = '2'; |
2958 | 0 | buf[2] = '0'; |
2959 | 0 | buf[3] = '0'; |
2960 | 0 | buf[4] = (year - 2000) + '0'; |
2961 | 0 | } else if ((year >= 1990) && (year <= 1999)) { |
2962 | 0 | buf[1] = '1'; |
2963 | 0 | buf[2] = '9'; |
2964 | 0 | buf[3] = '9'; |
2965 | 0 | buf[4] = (year - 1990) + '0'; |
2966 | 0 | } else { |
2967 | 0 | buf[4] = (year % 10) + '0'; |
2968 | 0 | year /= 10; |
2969 | 0 | buf[3] = (year % 10) + '0'; |
2970 | 0 | year /= 10; |
2971 | 0 | buf[2] = (year % 10) + '0'; |
2972 | 0 | year /= 10; |
2973 | 0 | buf[1] = (year % 10) + '0'; |
2974 | 0 | } |
2975 | 0 | buf[5] = ' '; |
2976 | 0 | buf += 6; |
2977 | | |
2978 | | /* the hour */ |
2979 | 0 | buf[0] = digitstrs[hour][0]; |
2980 | 0 | buf[1] = digitstrs[hour][1]; |
2981 | 0 | buf[2] = ':'; |
2982 | 0 | buf += 3; |
2983 | | |
2984 | | /* the minute */ |
2985 | 0 | buf[0] = digitstrs[min][0]; |
2986 | 0 | buf[1] = digitstrs[min][1]; |
2987 | 0 | buf[2] = ':'; |
2988 | 0 | buf += 3; |
2989 | | |
2990 | | /* the second */ |
2991 | 0 | buf[0] = digitstrs[sec][0]; |
2992 | 0 | buf[1] = digitstrs[sec][1]; |
2993 | 0 | buf[2] = ' '; |
2994 | 0 | buf += 3; |
2995 | | |
2996 | | /* the timezone string */ |
2997 | 0 | buf[0] = 'G'; |
2998 | 0 | buf[1] = 'M'; |
2999 | 0 | buf[2] = 'T'; |
3000 | 0 | buf[3] = '\0'; |
3001 | 0 | buf += 3; |
3002 | |
|
3003 | 0 | return buf - buffer; // not counting NUL |
3004 | 0 | } |
3005 | | |
3006 | | int32_t |
3007 | | mime_parse_int(const char *buf, const char *end) |
3008 | 0 | { |
3009 | 0 | int32_t num; |
3010 | 0 | bool negative; |
3011 | |
|
3012 | 0 | if (!buf || (buf == end)) { |
3013 | 0 | return 0; |
3014 | 0 | } |
3015 | | |
3016 | 0 | if (is_digit(*buf)) { // fast case |
3017 | 0 | num = *buf++ - '0'; |
3018 | 0 | while ((buf != end) && is_digit(*buf)) { |
3019 | 0 | if (num != INT_MAX) { |
3020 | 0 | int new_num = (num * 10) + (*buf++ - '0'); |
3021 | |
|
3022 | 0 | num = (new_num < num ? INT_MAX : new_num); // Check for overflow |
3023 | 0 | } else { |
3024 | 0 | ++buf; // Skip the remaining (valid) digits since we reached MAX/MIN_INT |
3025 | 0 | } |
3026 | 0 | } |
3027 | |
|
3028 | 0 | return num; |
3029 | 0 | } else { |
3030 | 0 | num = 0; |
3031 | 0 | negative = false; |
3032 | |
|
3033 | 0 | while ((buf != end) && ParseRules::is_space(*buf)) { |
3034 | 0 | buf += 1; |
3035 | 0 | } |
3036 | |
|
3037 | 0 | if ((buf != end) && (*buf == '-')) { |
3038 | 0 | negative = true; |
3039 | 0 | buf += 1; |
3040 | 0 | } |
3041 | | // NOTE: we first compute the value as negative then correct the |
3042 | | // sign back to positive. This enables us to correctly parse MININT. |
3043 | 0 | while ((buf != end) && is_digit(*buf)) { |
3044 | 0 | if (num != INT_MIN) { |
3045 | 0 | int new_num = (num * 10) - (*buf++ - '0'); |
3046 | |
|
3047 | 0 | num = (new_num > num ? INT_MIN : new_num); // Check for overflow, so to speak, see above re: negative |
3048 | 0 | } else { |
3049 | 0 | ++buf; // Skip the remaining (valid) digits since we reached MAX/MIN_INT |
3050 | 0 | } |
3051 | 0 | } |
3052 | |
|
3053 | 0 | if (!negative) { |
3054 | 0 | num = -num; |
3055 | 0 | } |
3056 | |
|
3057 | 0 | return num; |
3058 | 0 | } |
3059 | 0 | } |
3060 | | |
3061 | | uint32_t |
3062 | | mime_parse_uint(const char *buf, const char *end) |
3063 | 0 | { |
3064 | 0 | uint32_t num; |
3065 | |
|
3066 | 0 | if (!buf || (buf == end)) { |
3067 | 0 | return 0; |
3068 | 0 | } |
3069 | | |
3070 | 0 | if (is_digit(*buf)) // fast case |
3071 | 0 | { |
3072 | 0 | num = *buf++ - '0'; |
3073 | 0 | while ((buf != end) && is_digit(*buf)) { |
3074 | 0 | num = (num * 10) + (*buf++ - '0'); |
3075 | 0 | } |
3076 | 0 | return num; |
3077 | 0 | } else { |
3078 | 0 | num = 0; |
3079 | 0 | while ((buf != end) && ParseRules::is_space(*buf)) { |
3080 | 0 | buf += 1; |
3081 | 0 | } |
3082 | 0 | while ((buf != end) && is_digit(*buf)) { |
3083 | 0 | num = (num * 10) + (*buf++ - '0'); |
3084 | 0 | } |
3085 | 0 | return num; |
3086 | 0 | } |
3087 | 0 | } |
3088 | | |
3089 | | int64_t |
3090 | | mime_parse_int64(const char *buf, const char *end) |
3091 | 0 | { |
3092 | 0 | int64_t num; |
3093 | 0 | bool negative; |
3094 | |
|
3095 | 0 | if (!buf || (buf == end)) { |
3096 | 0 | return 0; |
3097 | 0 | } |
3098 | | |
3099 | 0 | if (is_digit(*buf)) // fast case |
3100 | 0 | { |
3101 | 0 | num = *buf++ - '0'; |
3102 | 0 | while ((buf != end) && is_digit(*buf)) { |
3103 | 0 | num = (num * 10) + (*buf++ - '0'); |
3104 | 0 | } |
3105 | 0 | return num; |
3106 | 0 | } else { |
3107 | 0 | num = 0; |
3108 | 0 | negative = false; |
3109 | |
|
3110 | 0 | while ((buf != end) && ParseRules::is_space(*buf)) { |
3111 | 0 | buf += 1; |
3112 | 0 | } |
3113 | |
|
3114 | 0 | if ((buf != end) && (*buf == '-')) { |
3115 | 0 | negative = true; |
3116 | 0 | buf += 1; |
3117 | 0 | } |
3118 | | // NOTE: we first compute the value as negative then correct the |
3119 | | // sign back to positive. This enables us to correctly parse MININT. |
3120 | 0 | while ((buf != end) && is_digit(*buf)) { |
3121 | 0 | num = (num * 10) - (*buf++ - '0'); |
3122 | 0 | } |
3123 | |
|
3124 | 0 | if (!negative) { |
3125 | 0 | num = -num; |
3126 | 0 | } |
3127 | |
|
3128 | 0 | return num; |
3129 | 0 | } |
3130 | 0 | } |
3131 | | |
3132 | | /*------------------------------------------------------------------------- |
3133 | | |
3134 | | mime_parse_rfc822_date_fastcase (const char *buf, int length, struct tm *tp) |
3135 | | |
3136 | | This routine is a fast case parser for date strings that are guaranteed |
3137 | | to be in the rfc822/rfc1123 format. It uses integer binary searches to |
3138 | | convert daya and month names into integral indices. |
3139 | | |
3140 | | This routine only supports rfc822/rfc1123 dates. It must be called |
3141 | | with NO leading whitespace, with a length >= 29 characters, and with |
3142 | | a comma in buf[3]. The caller MUST ensure these conditions. |
3143 | | |
3144 | | Sun, 06 Nov 1994 08:49:37 GMT |
3145 | | 01234567890123456789012345678 |
3146 | | |
3147 | | This function handles the common case dates fast. The day and month |
3148 | | are processed as 3 character integers before falling to DFA. This |
3149 | | table shows string, hash (24 bit values of 3 characters), and the |
3150 | | resulting string index. |
3151 | | |
3152 | | Fri 0x467269 5 Apr 0x417072 3 |
3153 | | Mon 0x4D6F6E 1 Aug 0x417567 7 |
3154 | | Sat 0x536174 6 Dec 0x446563 11 |
3155 | | Sun 0x53756E 0 Feb 0x466562 1 |
3156 | | Thu 0x546875 4 Jan 0x4A616E 0 |
3157 | | Tue 0x547565 2 Jul 0x4A756C 6 |
3158 | | Wed 0x576564 3 Jun 0x4A756E 5 |
3159 | | Mar 0x4D6172 2 |
3160 | | May 0x4D6179 4 |
3161 | | Nov 0x4E6F76 10 |
3162 | | Oct 0x4F6374 9 |
3163 | | Sep 0x536570 8 |
3164 | | |
3165 | | -------------------------------------------------------------------------*/ |
3166 | | int |
3167 | | mime_parse_rfc822_date_fastcase(const char *buf, int length, struct tm *tp) |
3168 | 0 | { |
3169 | 0 | unsigned int three_char_wday, three_char_mon; |
3170 | |
|
3171 | 0 | ink_assert(length >= 29); |
3172 | 0 | ink_assert(!is_ws(buf[0])); |
3173 | 0 | ink_assert(buf[3] == ','); |
3174 | | |
3175 | | //////////////////////////// |
3176 | | // binary search for wday // |
3177 | | //////////////////////////// |
3178 | 0 | tp->tm_wday = -1; |
3179 | 0 | three_char_wday = (buf[0] << 16) | (buf[1] << 8) | buf[2]; |
3180 | 0 | if (three_char_wday <= 0x53756E) { |
3181 | 0 | if (three_char_wday == 0x467269) { |
3182 | 0 | tp->tm_wday = 5; |
3183 | 0 | } else if (three_char_wday == 0x4D6F6E) { |
3184 | 0 | tp->tm_wday = 1; |
3185 | 0 | } else if (three_char_wday == 0x536174) { |
3186 | 0 | tp->tm_wday = 6; |
3187 | 0 | } else if (three_char_wday == 0x53756E) { |
3188 | 0 | tp->tm_wday = 0; |
3189 | 0 | } |
3190 | 0 | } else { |
3191 | 0 | if (three_char_wday == 0x546875) { |
3192 | 0 | tp->tm_wday = 4; |
3193 | 0 | } else if (three_char_wday == 0x547565) { |
3194 | 0 | tp->tm_wday = 2; |
3195 | 0 | } else if (three_char_wday == 0x576564) { |
3196 | 0 | tp->tm_wday = 3; |
3197 | 0 | } |
3198 | 0 | } |
3199 | 0 | if (tp->tm_wday < 0) { |
3200 | 0 | tp->tm_wday = match_3char_ci({buf, 3}, day_names_packed); |
3201 | 0 | if (tp->tm_wday < 0) { |
3202 | 0 | return 0; |
3203 | 0 | } |
3204 | 0 | } |
3205 | | ////////////////////////// |
3206 | | // extract day of month // |
3207 | | ////////////////////////// |
3208 | 0 | tp->tm_mday = (buf[5] - '0') * 10 + (buf[6] - '0'); |
3209 | | |
3210 | | ///////////////////////////// |
3211 | | // binary search for month // |
3212 | | ///////////////////////////// |
3213 | 0 | tp->tm_mon = -1; |
3214 | 0 | three_char_mon = (buf[8] << 16) | (buf[9] << 8) | buf[10]; |
3215 | 0 | if (three_char_mon <= 0x4A756C) { |
3216 | 0 | if (three_char_mon <= 0x446563) { |
3217 | 0 | if (three_char_mon == 0x417072) { |
3218 | 0 | tp->tm_mon = 3; |
3219 | 0 | } else if (three_char_mon == 0x417567) { |
3220 | 0 | tp->tm_mon = 7; |
3221 | 0 | } else if (three_char_mon == 0x446563) { |
3222 | 0 | tp->tm_mon = 11; |
3223 | 0 | } |
3224 | 0 | } else { |
3225 | 0 | if (three_char_mon == 0x466562) { |
3226 | 0 | tp->tm_mon = 1; |
3227 | 0 | } else if (three_char_mon == 0x4A616E) { |
3228 | 0 | tp->tm_mon = 0; |
3229 | 0 | } else if (three_char_mon == 0x4A756C) { |
3230 | 0 | tp->tm_mon = 6; |
3231 | 0 | } |
3232 | 0 | } |
3233 | 0 | } else { |
3234 | 0 | if (three_char_mon <= 0x4D6179) { |
3235 | 0 | if (three_char_mon == 0x4A756E) { |
3236 | 0 | tp->tm_mon = 5; |
3237 | 0 | } else if (three_char_mon == 0x4D6172) { |
3238 | 0 | tp->tm_mon = 2; |
3239 | 0 | } else if (three_char_mon == 0x4D6179) { |
3240 | 0 | tp->tm_mon = 4; |
3241 | 0 | } |
3242 | 0 | } else { |
3243 | 0 | if (three_char_mon == 0x4E6F76) { |
3244 | 0 | tp->tm_mon = 10; |
3245 | 0 | } else if (three_char_mon == 0x4F6374) { |
3246 | 0 | tp->tm_mon = 9; |
3247 | 0 | } else if (three_char_mon == 0x536570) { |
3248 | 0 | tp->tm_mon = 8; |
3249 | 0 | } |
3250 | 0 | } |
3251 | 0 | } |
3252 | 0 | if (tp->tm_mon < 0) { |
3253 | 0 | tp->tm_mon = match_3char_ci({buf + 8, 3}, month_names_packed); |
3254 | 0 | if (tp->tm_mon < 0) { |
3255 | 0 | return 0; |
3256 | 0 | } |
3257 | 0 | } |
3258 | | ////////////////// |
3259 | | // extract year // |
3260 | | ////////////////// |
3261 | 0 | tp->tm_year = ((buf[12] - '0') * 1000 + (buf[13] - '0') * 100 + (buf[14] - '0') * 10 + (buf[15] - '0')) - 1900; |
3262 | | |
3263 | | ////////////////// |
3264 | | // extract time // |
3265 | | ////////////////// |
3266 | 0 | tp->tm_hour = (buf[17] - '0') * 10 + (buf[18] - '0'); |
3267 | 0 | tp->tm_min = (buf[20] - '0') * 10 + (buf[21] - '0'); |
3268 | 0 | tp->tm_sec = (buf[23] - '0') * 10 + (buf[24] - '0'); |
3269 | 0 | if ((buf[19] != ':') || (buf[22] != ':')) { |
3270 | 0 | return 0; |
3271 | 0 | } |
3272 | 0 | return 1; |
3273 | 0 | } |
3274 | | |
3275 | | /*------------------------------------------------------------------------- |
3276 | | Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 |
3277 | | Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 |
3278 | | Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format |
3279 | | 6 Nov 1994 08:49:37 GMT ; NNTP-style date |
3280 | | -------------------------------------------------------------------------*/ |
3281 | | time_t |
3282 | | mime_parse_date(const char *buf, const char *end) |
3283 | 0 | { |
3284 | 0 | static constexpr int DAYS_OFFSET = 25508; |
3285 | 0 | static constexpr int days[12] = {305, 336, -1, 30, 60, 91, 121, 152, 183, 213, 244, 274}; |
3286 | |
|
3287 | 0 | struct tm tp; |
3288 | 0 | time_t t; |
3289 | 0 | int year; |
3290 | 0 | int month; |
3291 | 0 | int mday; |
3292 | |
|
3293 | 0 | if (!buf) { |
3294 | 0 | return static_cast<time_t>(0); |
3295 | 0 | } |
3296 | | |
3297 | 0 | while ((buf != end) && is_ws(*buf)) { |
3298 | 0 | buf += 1; |
3299 | 0 | } |
3300 | |
|
3301 | 0 | if ((buf != end) && is_digit(*buf)) { // NNTP date |
3302 | 0 | if (!mime_parse_mday(buf, end, &tp.tm_mday)) { |
3303 | 0 | return static_cast<time_t>(0); |
3304 | 0 | } |
3305 | 0 | if (!mime_parse_month(buf, end, &tp.tm_mon)) { |
3306 | 0 | return static_cast<time_t>(0); |
3307 | 0 | } |
3308 | 0 | if (!mime_parse_year(buf, end, &tp.tm_year)) { |
3309 | 0 | return static_cast<time_t>(0); |
3310 | 0 | } |
3311 | 0 | if (!mime_parse_time(buf, end, &tp.tm_hour, &tp.tm_min, &tp.tm_sec)) { |
3312 | 0 | return static_cast<time_t>(0); |
3313 | 0 | } |
3314 | 0 | } else if (end && (end - buf >= 29) && (buf[3] == ',')) { |
3315 | 0 | if (!mime_parse_rfc822_date_fastcase(buf, end - buf, &tp)) { |
3316 | 0 | return static_cast<time_t>(0); |
3317 | 0 | } |
3318 | 0 | } else { |
3319 | 0 | if (!mime_parse_day(buf, end, &tp.tm_wday)) { |
3320 | 0 | return static_cast<time_t>(0); |
3321 | 0 | } |
3322 | | |
3323 | 0 | while ((buf != end) && is_ws(*buf)) { |
3324 | 0 | buf += 1; |
3325 | 0 | } |
3326 | |
|
3327 | 0 | if ((buf != end) && ((*buf == ',') || is_digit(*buf))) { |
3328 | | // RFC 822 or RFC 850 time format |
3329 | 0 | if (!mime_parse_mday(buf, end, &tp.tm_mday)) { |
3330 | 0 | return static_cast<time_t>(0); |
3331 | 0 | } |
3332 | 0 | if (!mime_parse_month(buf, end, &tp.tm_mon)) { |
3333 | 0 | return static_cast<time_t>(0); |
3334 | 0 | } |
3335 | 0 | if (!mime_parse_year(buf, end, &tp.tm_year)) { |
3336 | 0 | return static_cast<time_t>(0); |
3337 | 0 | } |
3338 | 0 | if (!mime_parse_time(buf, end, &tp.tm_hour, &tp.tm_min, &tp.tm_sec)) { |
3339 | 0 | return static_cast<time_t>(0); |
3340 | 0 | } |
3341 | | // ignore timezone specifier...should always be GMT anyways |
3342 | 0 | } else { |
3343 | | // ANSI C's asctime format |
3344 | 0 | if (!mime_parse_month(buf, end, &tp.tm_mon)) { |
3345 | 0 | return static_cast<time_t>(0); |
3346 | 0 | } |
3347 | 0 | if (!mime_parse_mday(buf, end, &tp.tm_mday)) { |
3348 | 0 | return static_cast<time_t>(0); |
3349 | 0 | } |
3350 | 0 | if (!mime_parse_time(buf, end, &tp.tm_hour, &tp.tm_min, &tp.tm_sec)) { |
3351 | 0 | return static_cast<time_t>(0); |
3352 | 0 | } |
3353 | 0 | if (!mime_parse_year(buf, end, &tp.tm_year)) { |
3354 | 0 | return static_cast<time_t>(0); |
3355 | 0 | } |
3356 | 0 | } |
3357 | 0 | } |
3358 | | |
3359 | 0 | year = tp.tm_year; |
3360 | 0 | month = tp.tm_mon; |
3361 | 0 | mday = tp.tm_mday; |
3362 | | |
3363 | | // what should we do? |
3364 | 0 | if (year > 137) { |
3365 | 0 | return static_cast<time_t>(INT_MAX); |
3366 | 0 | } |
3367 | 0 | if (year < 70) { |
3368 | 0 | return static_cast<time_t>(0); |
3369 | 0 | } |
3370 | | |
3371 | 0 | mday += days[month]; |
3372 | | /* month base == march */ |
3373 | 0 | if (month < 2) { |
3374 | 0 | year -= 1; |
3375 | 0 | } |
3376 | 0 | mday += (year * 365) + (year / 4) - (year / 100) + (year / 100 + 3) / 4; |
3377 | 0 | mday -= DAYS_OFFSET; |
3378 | |
|
3379 | 0 | t = ((mday * 24 + tp.tm_hour) * 60 + tp.tm_min) * 60 + tp.tm_sec; |
3380 | |
|
3381 | 0 | return t; |
3382 | 0 | } |
3383 | | |
3384 | | bool |
3385 | | mime_parse_day(const char *&buf, const char *end, int *day) |
3386 | 0 | { |
3387 | 0 | const char *e; |
3388 | |
|
3389 | 0 | while ((buf != end) && *buf && !ParseRules::is_alpha(*buf)) { |
3390 | 0 | buf += 1; |
3391 | 0 | } |
3392 | |
|
3393 | 0 | e = buf; |
3394 | 0 | while ((e != end) && *e && ParseRules::is_alpha(*e)) { |
3395 | 0 | e += 1; |
3396 | 0 | } |
3397 | |
|
3398 | 0 | *day = match_3char_ci({buf, static_cast<size_t>(e - buf)}, day_names_packed); |
3399 | 0 | if (*day < 0) { |
3400 | 0 | return false; |
3401 | 0 | } else { |
3402 | 0 | buf = e; |
3403 | 0 | return true; |
3404 | 0 | } |
3405 | 0 | } |
3406 | | |
3407 | | bool |
3408 | | mime_parse_month(const char *&buf, const char *end, int *month) |
3409 | 0 | { |
3410 | 0 | const char *e; |
3411 | |
|
3412 | 0 | while ((buf != end) && *buf && !ParseRules::is_alpha(*buf)) { |
3413 | 0 | buf += 1; |
3414 | 0 | } |
3415 | |
|
3416 | 0 | e = buf; |
3417 | 0 | while ((e != end) && *e && ParseRules::is_alpha(*e)) { |
3418 | 0 | e += 1; |
3419 | 0 | } |
3420 | |
|
3421 | 0 | *month = match_3char_ci({buf, static_cast<size_t>(e - buf)}, month_names_packed); |
3422 | 0 | if (*month < 0) { |
3423 | 0 | return false; |
3424 | 0 | } else { |
3425 | 0 | buf = e; |
3426 | 0 | return true; |
3427 | 0 | } |
3428 | 0 | } |
3429 | | |
3430 | | bool |
3431 | | mime_parse_mday(const char *&buf, const char *end, int *mday) |
3432 | 0 | { |
3433 | 0 | return mime_parse_integer(buf, end, mday); |
3434 | 0 | } |
3435 | | |
3436 | | bool |
3437 | | mime_parse_year(const char *&buf, const char *end, int *year) |
3438 | 0 | { |
3439 | 0 | int val; |
3440 | |
|
3441 | 0 | while ((buf != end) && *buf && !is_digit(*buf)) { |
3442 | 0 | buf += 1; |
3443 | 0 | } |
3444 | |
|
3445 | 0 | if ((buf == end) || (*buf == '\0')) { |
3446 | 0 | return false; |
3447 | 0 | } |
3448 | | |
3449 | 0 | val = 0; |
3450 | |
|
3451 | 0 | while ((buf != end) && *buf && is_digit(*buf)) { |
3452 | 0 | val = (val * 10) + (*buf++ - '0'); |
3453 | 0 | } |
3454 | |
|
3455 | 0 | if (val >= 1900) { |
3456 | 0 | val -= 1900; |
3457 | 0 | } else if (val < 70) { |
3458 | 0 | val += 100; |
3459 | 0 | } |
3460 | |
|
3461 | 0 | *year = val; |
3462 | |
|
3463 | 0 | return true; |
3464 | 0 | } |
3465 | | |
3466 | | bool |
3467 | | mime_parse_time(const char *&buf, const char *end, int *hour, int *min, int *sec) |
3468 | 0 | { |
3469 | 0 | if (!mime_parse_integer(buf, end, hour)) { |
3470 | 0 | return false; |
3471 | 0 | } |
3472 | 0 | if (!mime_parse_integer(buf, end, min)) { |
3473 | 0 | return false; |
3474 | 0 | } |
3475 | 0 | if (!mime_parse_integer(buf, end, sec)) { |
3476 | 0 | return false; |
3477 | 0 | } |
3478 | 0 | return true; |
3479 | 0 | } |
3480 | | |
3481 | | // This behaves slightly different than mime_parse_int(), int that we actually |
3482 | | // return a "bool" for success / failure on "reasonable" parsing. This kinda |
3483 | | // dumb, because we have two interfaces, where one does not move along the |
3484 | | // buf pointer, but this one does (and the ones using this function do). |
3485 | | bool |
3486 | | mime_parse_integer(const char *&buf, const char *end, int *integer) |
3487 | 0 | { |
3488 | 0 | while ((buf != end) && *buf && !is_digit(*buf) && (*buf != '-')) { |
3489 | 0 | buf += 1; |
3490 | 0 | } |
3491 | |
|
3492 | 0 | if ((buf == end) || (*buf == '\0')) { |
3493 | 0 | return false; |
3494 | 0 | } |
3495 | | |
3496 | 0 | int32_t num; |
3497 | 0 | bool negative; |
3498 | | |
3499 | | // This code is copied verbatim from mime_parse_int ... Sigh. Maybe amc is right, and |
3500 | | // we really need to clean this up. But, as such, we should redo all these interfaces, |
3501 | | // and that's a big undertaking (and we'd want to move these strings all to string_view's). |
3502 | 0 | if (is_digit(*buf)) { // fast case |
3503 | 0 | num = *buf++ - '0'; |
3504 | 0 | while ((buf != end) && is_digit(*buf)) { |
3505 | 0 | if (num != INT_MAX) { |
3506 | 0 | int new_num = (num * 10) + (*buf++ - '0'); |
3507 | |
|
3508 | 0 | num = (new_num < num ? INT_MAX : new_num); // Check for overflow |
3509 | 0 | } else { |
3510 | 0 | ++buf; // Skip the remaining (valid) digits since we reached MAX/MIN_INT |
3511 | 0 | } |
3512 | 0 | } |
3513 | 0 | } else { |
3514 | 0 | num = 0; |
3515 | 0 | negative = false; |
3516 | |
|
3517 | 0 | while ((buf != end) && ParseRules::is_space(*buf)) { |
3518 | 0 | buf += 1; |
3519 | 0 | } |
3520 | |
|
3521 | 0 | if ((buf != end) && (*buf == '-')) { |
3522 | 0 | negative = true; |
3523 | 0 | buf += 1; |
3524 | 0 | } |
3525 | | // NOTE: we first compute the value as negative then correct the |
3526 | | // sign back to positive. This enables us to correctly parse MININT. |
3527 | 0 | while ((buf != end) && is_digit(*buf)) { |
3528 | 0 | if (num != INT_MIN) { |
3529 | 0 | int new_num = (num * 10) - (*buf++ - '0'); |
3530 | |
|
3531 | 0 | num = (new_num > num ? INT_MIN : new_num); // Check for overflow, so to speak, see above re: negative |
3532 | 0 | } else { |
3533 | 0 | ++buf; // Skip the remaining (valid) digits since we reached MAX/MIN_INT |
3534 | 0 | } |
3535 | 0 | } |
3536 | |
|
3537 | 0 | if (!negative) { |
3538 | 0 | num = -num; |
3539 | 0 | } |
3540 | 0 | } |
3541 | |
|
3542 | 0 | *integer = num; |
3543 | |
|
3544 | 0 | return true; |
3545 | 0 | } |
3546 | | |
3547 | | /*********************************************************************** |
3548 | | * * |
3549 | | * M A R S H A L I N G * |
3550 | | * * |
3551 | | ***********************************************************************/ |
3552 | | int |
3553 | | MIMEFieldBlockImpl::marshal(MarshalXlate *ptr_xlate, int num_ptr, MarshalXlate *str_xlate, int num_str) |
3554 | 0 | { |
3555 | | // printf("FieldBlockImpl:marshal num_ptr = %d num_str = %d\n", num_ptr, num_str); |
3556 | 0 | HDR_MARSHAL_PTR(m_next, MIMEFieldBlockImpl, ptr_xlate, num_ptr); |
3557 | |
|
3558 | 0 | if ((num_str == 1) && (num_ptr == 1)) { |
3559 | 0 | for (uint32_t index = 0; index < m_freetop; index++) { |
3560 | 0 | MIMEField *field = &(m_field_slots[index]); |
3561 | |
|
3562 | 0 | if (field->is_live()) { |
3563 | 0 | HDR_MARSHAL_STR_1(field->m_ptr_name, str_xlate); |
3564 | 0 | HDR_MARSHAL_STR_1(field->m_ptr_value, str_xlate); |
3565 | 0 | if (field->m_next_dup) { |
3566 | 0 | HDR_MARSHAL_PTR_1(field->m_next_dup, MIMEField, ptr_xlate); |
3567 | 0 | } |
3568 | 0 | } |
3569 | 0 | } |
3570 | 0 | } else { |
3571 | 0 | for (uint32_t index = 0; index < m_freetop; index++) { |
3572 | 0 | MIMEField *field = &(m_field_slots[index]); |
3573 | |
|
3574 | 0 | if (field->is_live()) { |
3575 | 0 | HDR_MARSHAL_STR(field->m_ptr_name, str_xlate, num_str); |
3576 | 0 | HDR_MARSHAL_STR(field->m_ptr_value, str_xlate, num_str); |
3577 | 0 | if (field->m_next_dup) { |
3578 | 0 | HDR_MARSHAL_PTR(field->m_next_dup, MIMEField, ptr_xlate, num_ptr); |
3579 | 0 | } |
3580 | 0 | } |
3581 | 0 | } |
3582 | 0 | } |
3583 | 0 | return 0; |
3584 | 0 | } |
3585 | | |
3586 | | void |
3587 | | MIMEFieldBlockImpl::unmarshal(intptr_t offset) |
3588 | 0 | { |
3589 | 0 | HDR_UNMARSHAL_PTR(m_next, MIMEFieldBlockImpl, offset); |
3590 | |
|
3591 | 0 | for (uint32_t index = 0; index < m_freetop; index++) { |
3592 | 0 | MIMEField *field = &(m_field_slots[index]); |
3593 | |
|
3594 | 0 | if (field->is_live()) { |
3595 | 0 | HDR_UNMARSHAL_STR(field->m_ptr_name, offset); |
3596 | 0 | HDR_UNMARSHAL_STR(field->m_ptr_value, offset); |
3597 | 0 | if (field->m_next_dup) { |
3598 | 0 | HDR_UNMARSHAL_PTR(field->m_next_dup, MIMEField, offset); |
3599 | 0 | } |
3600 | 0 | } else { |
3601 | | // Clear out other types of slots |
3602 | 0 | field->m_readiness = MIME_FIELD_SLOT_READINESS_EMPTY; |
3603 | 0 | } |
3604 | 0 | } |
3605 | 0 | } |
3606 | | |
3607 | | void |
3608 | | MIMEFieldBlockImpl::move_strings(HdrStrHeap *new_heap) |
3609 | 0 | { |
3610 | 0 | for (uint32_t index = 0; index < m_freetop; index++) { |
3611 | 0 | MIMEField *field = &(m_field_slots[index]); |
3612 | |
|
3613 | 0 | if (field->is_live() || field->is_detached()) { |
3614 | | // FIX ME - Should do the field in one shot and preserve |
3615 | | // raw_printable if it's set |
3616 | 0 | field->m_n_v_raw_printable = 0; |
3617 | |
|
3618 | 0 | HDR_MOVE_STR(field->m_ptr_name, field->m_len_name); |
3619 | 0 | HDR_MOVE_STR(field->m_ptr_value, field->m_len_value); |
3620 | 0 | } |
3621 | 0 | } |
3622 | 0 | } |
3623 | | |
3624 | | size_t |
3625 | | MIMEFieldBlockImpl::strings_length() |
3626 | 0 | { |
3627 | 0 | size_t ret = 0; |
3628 | |
|
3629 | 0 | for (uint32_t index = 0; index < m_freetop; index++) { |
3630 | 0 | MIMEField *field = &(m_field_slots[index]); |
3631 | |
|
3632 | 0 | if (field->m_readiness == MIME_FIELD_SLOT_READINESS_LIVE || field->m_readiness == MIME_FIELD_SLOT_READINESS_DETACHED) { |
3633 | 0 | ret += field->m_len_name; |
3634 | 0 | ret += field->m_len_value; |
3635 | 0 | } |
3636 | 0 | } |
3637 | 0 | return ret; |
3638 | 0 | } |
3639 | | |
3640 | | bool |
3641 | | MIMEFieldBlockImpl::contains(const MIMEField *field) |
3642 | 122k | { |
3643 | 122k | MIMEField *first = &(m_field_slots[0]); |
3644 | 122k | MIMEField *last = &(m_field_slots[MIME_FIELD_BLOCK_SLOTS - 1]); |
3645 | 122k | return (field >= first) && (field <= last); |
3646 | 122k | } |
3647 | | |
3648 | | void |
3649 | | MIMEFieldBlockImpl::check_strings(HeapCheck *heaps, int num_heaps) |
3650 | 0 | { |
3651 | 0 | for (uint32_t index = 0; index < m_freetop; index++) { |
3652 | 0 | MIMEField *field = &(m_field_slots[index]); |
3653 | |
|
3654 | 0 | if (field->is_live() || field->is_detached()) { |
3655 | | // FIX ME - Should check raw printing characters as well |
3656 | 0 | CHECK_STR(field->m_ptr_name, field->m_len_name, heaps, num_heaps); |
3657 | 0 | CHECK_STR(field->m_ptr_value, field->m_len_value, heaps, num_heaps); |
3658 | 0 | } |
3659 | 0 | } |
3660 | 0 | } |
3661 | | |
3662 | | int |
3663 | | MIMEHdrImpl::marshal(MarshalXlate *ptr_xlate, int num_ptr, MarshalXlate *str_xlate, int num_str) |
3664 | 0 | { |
3665 | | // printf("MIMEHdrImpl:marshal num_ptr = %d num_str = %d\n", num_ptr, num_str); |
3666 | 0 | HDR_MARSHAL_PTR(m_fblock_list_tail, MIMEFieldBlockImpl, ptr_xlate, num_ptr); |
3667 | 0 | return m_first_fblock.marshal(ptr_xlate, num_ptr, str_xlate, num_str); |
3668 | 0 | } |
3669 | | |
3670 | | void |
3671 | | MIMEHdrImpl::unmarshal(intptr_t offset) |
3672 | 0 | { |
3673 | 0 | HDR_UNMARSHAL_PTR(m_fblock_list_tail, MIMEFieldBlockImpl, offset); |
3674 | 0 | m_first_fblock.unmarshal(offset); |
3675 | 0 | } |
3676 | | |
3677 | | void |
3678 | | MIMEHdrImpl::move_strings(HdrStrHeap *new_heap) |
3679 | 0 | { |
3680 | 0 | m_first_fblock.move_strings(new_heap); |
3681 | 0 | } |
3682 | | |
3683 | | size_t |
3684 | | MIMEHdrImpl::strings_length() |
3685 | 0 | { |
3686 | 0 | return m_first_fblock.strings_length(); |
3687 | 0 | } |
3688 | | |
3689 | | void |
3690 | | MIMEHdrImpl::check_strings(HeapCheck *heaps, int num_heaps) |
3691 | 0 | { |
3692 | 0 | m_first_fblock.check_strings(heaps, num_heaps); |
3693 | 0 | } |
3694 | | |
3695 | | void |
3696 | | MIMEHdrImpl::recompute_accelerators_and_presence_bits() |
3697 | 0 | { |
3698 | 0 | mime_hdr_reset_accelerators_and_presence_bits(this); |
3699 | 0 | } |
3700 | | |
3701 | | /*********************************************************************** |
3702 | | * * |
3703 | | * C O O K E D V A L U E C A C H E * |
3704 | | * * |
3705 | | ***********************************************************************/ |
3706 | | |
3707 | | //////////////////////////////////////////////////////// |
3708 | | // we need to recook the cooked values cache when: // |
3709 | | // // |
3710 | | // setting the value and the field is live // |
3711 | | // attaching the field and the field isn't empty // |
3712 | | // detaching the field // |
3713 | | //////////////////////////////////////////////////////// |
3714 | | |
3715 | | void |
3716 | | MIMEHdrImpl::recompute_cooked_stuff(MIMEField *changing_field_or_null, const std::string_view *targeted_headers, |
3717 | | size_t targeted_headers_count) |
3718 | 1.45k | { |
3719 | 1.45k | int len, tlen; |
3720 | 1.45k | const char *s; |
3721 | 1.45k | const char *c; |
3722 | 1.45k | const char *e; |
3723 | 1.45k | const char *token_wks; |
3724 | 1.45k | MIMEField *field; |
3725 | 1.45k | uint32_t mask = 0; |
3726 | | |
3727 | 1.45k | mime_hdr_cooked_stuff_init(this, changing_field_or_null); |
3728 | | |
3729 | | ///////////////////////////////////////////////////////////////////////////// |
3730 | | // (1) cook the Cache-Control header (or targeted variant) if present // |
3731 | | ///////////////////////////////////////////////////////////////////////////// |
3732 | | |
3733 | | // to be safe, recompute unless you know this call is for other cooked field |
3734 | 1.45k | if ((changing_field_or_null == nullptr) || (changing_field_or_null->m_wks_idx != MIME_WKSIDX_PRAGMA)) { |
3735 | 394 | ink_assert(targeted_headers != nullptr || targeted_headers_count == 0); |
3736 | 394 | field = nullptr; |
3737 | | |
3738 | | // Check for targeted cache control headers first (in priority order). |
3739 | 394 | for (size_t i = 0; i < targeted_headers_count; ++i) { |
3740 | 0 | field = mime_hdr_field_find(this, targeted_headers[i]); |
3741 | 0 | if (field) { |
3742 | 0 | break; |
3743 | 0 | } |
3744 | 0 | } |
3745 | | |
3746 | | // If no targeted header was found, fall back to standard Cache-Control. |
3747 | 394 | if (!field) { |
3748 | 394 | field = mime_hdr_field_find(this, static_cast<std::string_view>(MIME_FIELD_CACHE_CONTROL)); |
3749 | 394 | } |
3750 | | |
3751 | 394 | if (field) { |
3752 | | // try pathpaths first -- unlike most other fastpaths, this one |
3753 | | // is probably more useful for polygraph than for the real world |
3754 | 394 | if (!field->has_dups()) { |
3755 | 187 | auto val{field->value_get()}; |
3756 | 187 | if (ptr_len_casecmp(val.data(), val.length(), "public", 6) == 0) { |
3757 | 17 | mask = MIME_COOKED_MASK_CC_PUBLIC; |
3758 | 17 | m_cooked_stuff.m_cache_control.m_mask |= mask; |
3759 | 170 | } else if (ptr_len_casecmp(val.data(), val.length(), "private,no-cache", 16) == 0) { |
3760 | 10 | mask = MIME_COOKED_MASK_CC_PRIVATE | MIME_COOKED_MASK_CC_NO_CACHE; |
3761 | 10 | m_cooked_stuff.m_cache_control.m_mask |= mask; |
3762 | 10 | } |
3763 | 187 | } |
3764 | | |
3765 | 394 | if (mask == 0) { |
3766 | 367 | HdrCsvIter csv_iter; |
3767 | | |
3768 | 1.30k | for (s = csv_iter.get_first(field, &len); s != nullptr; s = csv_iter.get_next(&len)) { |
3769 | 941 | e = s + len; |
3770 | | // Store set mask bits from this CSV value so we can clear them if needed. |
3771 | 941 | uint32_t csv_value_mask = 0; |
3772 | | |
3773 | 2.70k | for (c = s; (c < e) && (ParseRules::is_token(*c)); c++) { |
3774 | 1.76k | ; |
3775 | 1.76k | } |
3776 | 941 | tlen = c - s; |
3777 | | |
3778 | | // If >= 0 then this is a well known token |
3779 | 941 | if (hdrtoken_tokenize(s, tlen, &token_wks) >= 0) { |
3780 | | #if TRACK_COOKING |
3781 | | Dbg(dbg_ctl_http, "recompute_cooked_stuff: got field '%s'", token_wks); |
3782 | | #endif |
3783 | | |
3784 | 256 | HdrTokenHeapPrefix *p = hdrtoken_wks_to_prefix(token_wks); |
3785 | 256 | mask = p->wks_type_specific.u.cache_control.cc_mask; |
3786 | 256 | m_cooked_stuff.m_cache_control.m_mask |= mask; |
3787 | 256 | csv_value_mask |= mask; |
3788 | | |
3789 | | #if TRACK_COOKING |
3790 | | Dbg(dbg_ctl_http, " set mask 0x%0X", mask); |
3791 | | #endif |
3792 | | |
3793 | 256 | if (mask & (MIME_COOKED_MASK_CC_MAX_AGE | MIME_COOKED_MASK_CC_S_MAXAGE | MIME_COOKED_MASK_CC_MAX_STALE | |
3794 | 256 | MIME_COOKED_MASK_CC_MIN_FRESH)) { |
3795 | 0 | int value; |
3796 | | // Per RFC 7230 Section 3.2.3, there should be no whitespace around '='. |
3797 | 0 | const char *value_start = c; |
3798 | | |
3799 | | // Check if the next character is '=' (no space allowed before '='). |
3800 | 0 | if (c < e && *c == '=') { |
3801 | 0 | ++c; // Move past the '=' |
3802 | | |
3803 | | // Again: no whitespace after the '=' either. Keep in mind that values can be negative. |
3804 | 0 | bool valid_syntax = (c < e) && (is_digit(*c) || *c == '-'); |
3805 | |
|
3806 | 0 | if (valid_syntax) { |
3807 | | // Reset to value_start to let mime_parse_integer do its work. |
3808 | 0 | c = value_start; |
3809 | 0 | if (mime_parse_integer(c, e, &value)) { |
3810 | | #if TRACK_COOKING |
3811 | | Dbg(dbg_ctl_http, " set integer value %d", value); |
3812 | | #endif |
3813 | 0 | if (token_wks == MIME_VALUE_MAX_AGE.c_str()) { |
3814 | 0 | m_cooked_stuff.m_cache_control.m_secs_max_age = value; |
3815 | 0 | } else if (token_wks == MIME_VALUE_MIN_FRESH.c_str()) { |
3816 | 0 | m_cooked_stuff.m_cache_control.m_secs_min_fresh = value; |
3817 | 0 | } else if (token_wks == MIME_VALUE_MAX_STALE.c_str()) { |
3818 | 0 | m_cooked_stuff.m_cache_control.m_secs_max_stale = value; |
3819 | 0 | } else if (token_wks == MIME_VALUE_S_MAXAGE.c_str()) { |
3820 | 0 | m_cooked_stuff.m_cache_control.m_secs_s_maxage = value; |
3821 | 0 | } |
3822 | 0 | } else { |
3823 | | #if TRACK_COOKING |
3824 | | Dbg(dbg_ctl_http, " set integer value %d", INT_MAX); |
3825 | | #endif |
3826 | 0 | if (token_wks == MIME_VALUE_MAX_STALE.c_str()) { |
3827 | 0 | m_cooked_stuff.m_cache_control.m_secs_max_stale = INT_MAX; |
3828 | 0 | } |
3829 | 0 | } |
3830 | 0 | } else { |
3831 | | // Syntax is malformed (e.g., whitespace after '=', quotes around value, or no value). |
3832 | | // Treat this as unrecognized and clear the mask. |
3833 | 0 | csv_value_mask = 0; |
3834 | 0 | m_cooked_stuff.m_cache_control.m_mask &= ~mask; |
3835 | 0 | } |
3836 | 0 | } else { |
3837 | | // No '=' found, or whitespace before '='. This is malformed. |
3838 | | // For directives that require values, this is an error. |
3839 | | // Clear the mask for this directive. |
3840 | 0 | csv_value_mask = 0; |
3841 | 0 | m_cooked_stuff.m_cache_control.m_mask &= ~mask; |
3842 | 0 | } |
3843 | 0 | } |
3844 | | |
3845 | | // Detect whether there is any more non-whitespace content after the |
3846 | | // directive. This indicates an unrecognized or malformed directive. |
3847 | | // This can happen, for instance, if the host uses semicolons |
3848 | | // instead of commas as separators which is against RFC 7234 (see |
3849 | | // issue #12029). Regardless of the cause, this means we need to |
3850 | | // ignore the directive and clear any mask bits we set from it. |
3851 | 258 | while (c < e && ParseRules::is_ws(*c)) { |
3852 | 2 | ++c; |
3853 | 2 | } |
3854 | 256 | if (c < e) { |
3855 | | // There's non-whitespace content that wasn't parsed. This means |
3856 | | // that we cannot really understand what this directive is. |
3857 | | // Per RFC 7234 Section 5.2: "A cache MUST ignore unrecognized cache |
3858 | | // directives." |
3859 | 5 | if (csv_value_mask != 0) { |
3860 | | // Reverse the mask that we set above. |
3861 | 0 | m_cooked_stuff.m_cache_control.m_mask &= ~csv_value_mask; |
3862 | 0 | } |
3863 | 5 | } |
3864 | 256 | } |
3865 | 941 | } |
3866 | 367 | } |
3867 | 394 | } |
3868 | 394 | } |
3869 | | /////////////////////////////////////////// |
3870 | | // (2) cook the Pragma header if present // |
3871 | | /////////////////////////////////////////// |
3872 | | |
3873 | 1.45k | if ((changing_field_or_null == nullptr) || (changing_field_or_null->m_wks_idx != MIME_WKSIDX_CACHE_CONTROL)) { |
3874 | 1.06k | field = mime_hdr_field_find(this, static_cast<std::string_view>(MIME_FIELD_PRAGMA)); |
3875 | 1.06k | if (field) { |
3876 | 1.06k | if (!field->has_dups()) { // try fastpath first |
3877 | 349 | auto val{field->value_get()}; |
3878 | 349 | if (ptr_len_casecmp(val.data(), val.length(), "no-cache", 8) == 0) { |
3879 | 10 | m_cooked_stuff.m_pragma.m_no_cache = true; |
3880 | 10 | return; |
3881 | 10 | } |
3882 | 349 | } |
3883 | | |
3884 | 1.05k | { |
3885 | 1.05k | HdrCsvIter csv_iter; |
3886 | | |
3887 | 5.36k | for (s = csv_iter.get_first(field, &len); s != nullptr; s = csv_iter.get_next(&len)) { |
3888 | 4.31k | e = s + len; |
3889 | 12.1k | for (c = s; (c < e) && (ParseRules::is_token(*c)); c++) { |
3890 | 7.84k | ; |
3891 | 7.84k | } |
3892 | 4.31k | tlen = c - s; |
3893 | | |
3894 | 4.31k | if (hdrtoken_tokenize(s, tlen, &token_wks) >= 0) { |
3895 | 398 | if (token_wks == MIME_VALUE_NO_CACHE.c_str()) { |
3896 | 197 | m_cooked_stuff.m_pragma.m_no_cache = true; |
3897 | 197 | } |
3898 | 398 | } |
3899 | 4.31k | } |
3900 | 1.05k | } |
3901 | 1.05k | } |
3902 | 1.06k | } |
3903 | 1.45k | } |