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