Coverage Report

Created: 2026-04-07 06:45

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