| /** @file |
| |
| A brief file description |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| #include "tscore/ink_defs.h" |
| #include "tscore/ink_platform.h" |
| #include "tscore/ink_memory.h" |
| #include "tscore/TsBuffer.h" |
| #include <cassert> |
| #include <cstdio> |
| #include <cstring> |
| #include <cctype> |
| #include <algorithm> |
| #include "MIME.h" |
| #include "HdrHeap.h" |
| #include "HdrToken.h" |
| #include "HdrUtils.h" |
| #include "HttpCompat.h" |
| |
| using ts::TextView; |
| |
| /*********************************************************************** |
| * * |
| * C O M P I L E O P T I O N S * |
| * * |
| ***********************************************************************/ |
| #define TRACK_FIELD_FIND_CALLS 0 |
| #define TRACK_COOKING 0 |
| #define MIME_FORMAT_DATE_USE_LOOKUP_TABLE 1 |
| |
| /*********************************************************************** |
| * * |
| * C O N S T A N T S * |
| * * |
| ***********************************************************************/ |
| static DFA *day_names_dfa = nullptr; |
| static DFA *month_names_dfa = nullptr; |
| |
| static const char *day_names[] = { |
| "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", |
| }; |
| |
| static const char *month_names[] = { |
| "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", |
| }; |
| |
| struct MDY { |
| uint8_t m; |
| uint8_t d; |
| uint16_t y; |
| }; |
| |
| static MDY *_days_to_mdy_fast_lookup_table = nullptr; |
| static unsigned int _days_to_mdy_fast_lookup_table_first_day; |
| static unsigned int _days_to_mdy_fast_lookup_table_last_day; |
| |
| /*********************************************************************** |
| * * |
| * G L O B A L S * |
| * * |
| ***********************************************************************/ |
| const char *MIME_FIELD_ACCEPT; |
| const char *MIME_FIELD_ACCEPT_CHARSET; |
| const char *MIME_FIELD_ACCEPT_ENCODING; |
| const char *MIME_FIELD_ACCEPT_LANGUAGE; |
| const char *MIME_FIELD_ACCEPT_RANGES; |
| const char *MIME_FIELD_AGE; |
| const char *MIME_FIELD_ALLOW; |
| const char *MIME_FIELD_APPROVED; |
| const char *MIME_FIELD_AUTHORIZATION; |
| const char *MIME_FIELD_BYTES; |
| const char *MIME_FIELD_CACHE_CONTROL; |
| const char *MIME_FIELD_CLIENT_IP; |
| const char *MIME_FIELD_CONNECTION; |
| const char *MIME_FIELD_CONTENT_BASE; |
| const char *MIME_FIELD_CONTENT_ENCODING; |
| const char *MIME_FIELD_CONTENT_LANGUAGE; |
| const char *MIME_FIELD_CONTENT_LENGTH; |
| const char *MIME_FIELD_CONTENT_LOCATION; |
| const char *MIME_FIELD_CONTENT_MD5; |
| const char *MIME_FIELD_CONTENT_RANGE; |
| const char *MIME_FIELD_CONTENT_TYPE; |
| const char *MIME_FIELD_CONTROL; |
| const char *MIME_FIELD_COOKIE; |
| const char *MIME_FIELD_DATE; |
| const char *MIME_FIELD_DISTRIBUTION; |
| const char *MIME_FIELD_ETAG; |
| const char *MIME_FIELD_EXPECT; |
| const char *MIME_FIELD_EXPIRES; |
| const char *MIME_FIELD_FOLLOWUP_TO; |
| const char *MIME_FIELD_FROM; |
| const char *MIME_FIELD_HOST; |
| const char *MIME_FIELD_IF_MATCH; |
| const char *MIME_FIELD_IF_MODIFIED_SINCE; |
| const char *MIME_FIELD_IF_NONE_MATCH; |
| const char *MIME_FIELD_IF_RANGE; |
| const char *MIME_FIELD_IF_UNMODIFIED_SINCE; |
| const char *MIME_FIELD_KEEP_ALIVE; |
| const char *MIME_FIELD_KEYWORDS; |
| const char *MIME_FIELD_LAST_MODIFIED; |
| const char *MIME_FIELD_LINES; |
| const char *MIME_FIELD_LOCATION; |
| const char *MIME_FIELD_MAX_FORWARDS; |
| const char *MIME_FIELD_MESSAGE_ID; |
| const char *MIME_FIELD_NEWSGROUPS; |
| const char *MIME_FIELD_ORGANIZATION; |
| const char *MIME_FIELD_PATH; |
| const char *MIME_FIELD_PRAGMA; |
| const char *MIME_FIELD_PROXY_AUTHENTICATE; |
| const char *MIME_FIELD_PROXY_AUTHORIZATION; |
| const char *MIME_FIELD_PROXY_CONNECTION; |
| const char *MIME_FIELD_PUBLIC; |
| const char *MIME_FIELD_RANGE; |
| const char *MIME_FIELD_REFERENCES; |
| const char *MIME_FIELD_REFERER; |
| const char *MIME_FIELD_REPLY_TO; |
| const char *MIME_FIELD_RETRY_AFTER; |
| const char *MIME_FIELD_SENDER; |
| const char *MIME_FIELD_SERVER; |
| const char *MIME_FIELD_SET_COOKIE; |
| const char *MIME_FIELD_STRICT_TRANSPORT_SECURITY; |
| const char *MIME_FIELD_SUBJECT; |
| const char *MIME_FIELD_SUMMARY; |
| const char *MIME_FIELD_TE; |
| const char *MIME_FIELD_TRANSFER_ENCODING; |
| const char *MIME_FIELD_UPGRADE; |
| const char *MIME_FIELD_USER_AGENT; |
| const char *MIME_FIELD_VARY; |
| const char *MIME_FIELD_VIA; |
| const char *MIME_FIELD_WARNING; |
| const char *MIME_FIELD_WWW_AUTHENTICATE; |
| const char *MIME_FIELD_XREF; |
| const char *MIME_FIELD_ATS_INTERNAL; |
| const char *MIME_FIELD_X_ID; |
| const char *MIME_FIELD_X_FORWARDED_FOR; |
| const char *MIME_FIELD_FORWARDED; |
| const char *MIME_FIELD_SEC_WEBSOCKET_KEY; |
| const char *MIME_FIELD_SEC_WEBSOCKET_VERSION; |
| const char *MIME_FIELD_HTTP2_SETTINGS; |
| const char *MIME_FIELD_EARLY_DATA; |
| |
| const char *MIME_VALUE_BYTES; |
| const char *MIME_VALUE_CHUNKED; |
| const char *MIME_VALUE_CLOSE; |
| const char *MIME_VALUE_COMPRESS; |
| const char *MIME_VALUE_DEFLATE; |
| const char *MIME_VALUE_GZIP; |
| const char *MIME_VALUE_IDENTITY; |
| const char *MIME_VALUE_KEEP_ALIVE; |
| const char *MIME_VALUE_MAX_AGE; |
| const char *MIME_VALUE_MAX_STALE; |
| const char *MIME_VALUE_MIN_FRESH; |
| const char *MIME_VALUE_MUST_REVALIDATE; |
| const char *MIME_VALUE_NONE; |
| const char *MIME_VALUE_NO_CACHE; |
| const char *MIME_VALUE_NO_STORE; |
| const char *MIME_VALUE_NO_TRANSFORM; |
| const char *MIME_VALUE_ONLY_IF_CACHED; |
| const char *MIME_VALUE_PRIVATE; |
| const char *MIME_VALUE_PROXY_REVALIDATE; |
| const char *MIME_VALUE_PUBLIC; |
| const char *MIME_VALUE_S_MAXAGE; |
| const char *MIME_VALUE_NEED_REVALIDATE_ONCE; |
| const char *MIME_VALUE_WEBSOCKET; |
| const char *MIME_VALUE_H2C; |
| |
| // Cache-control: extension "need-revalidate-once" is used internally by T.S. |
| // to invalidate a document, and it is not returned/forwarded. |
| // If a cached document has this extension set (ie, is invalidated), |
| // then the T.S. needs to revalidate the document once before returning it. |
| // After a successful revalidation, the extension will be removed by T.S. |
| // To set or unset this directive should be done via the following two |
| // function: |
| // set_cooked_cc_need_revalidate_once() |
| // unset_cooked_cc_need_revalidate_once() |
| // To test, use regular Cache-control testing functions, eg, |
| // is_cache_control_set(HTTP_VALUE_NEED_REVALIDATE_ONCE) |
| |
| int MIME_LEN_ACCEPT; |
| int MIME_LEN_ACCEPT_CHARSET; |
| int MIME_LEN_ACCEPT_ENCODING; |
| int MIME_LEN_ACCEPT_LANGUAGE; |
| int MIME_LEN_ACCEPT_RANGES; |
| int MIME_LEN_AGE; |
| int MIME_LEN_ALLOW; |
| int MIME_LEN_APPROVED; |
| int MIME_LEN_AUTHORIZATION; |
| int MIME_LEN_BYTES; |
| int MIME_LEN_CACHE_CONTROL; |
| int MIME_LEN_CLIENT_IP; |
| int MIME_LEN_CONNECTION; |
| int MIME_LEN_CONTENT_BASE; |
| int MIME_LEN_CONTENT_ENCODING; |
| int MIME_LEN_CONTENT_LANGUAGE; |
| int MIME_LEN_CONTENT_LENGTH; |
| int MIME_LEN_CONTENT_LOCATION; |
| int MIME_LEN_CONTENT_MD5; |
| int MIME_LEN_CONTENT_RANGE; |
| int MIME_LEN_CONTENT_TYPE; |
| int MIME_LEN_CONTROL; |
| int MIME_LEN_COOKIE; |
| int MIME_LEN_DATE; |
| int MIME_LEN_DISTRIBUTION; |
| int MIME_LEN_ETAG; |
| int MIME_LEN_EXPECT; |
| int MIME_LEN_EXPIRES; |
| int MIME_LEN_FOLLOWUP_TO; |
| int MIME_LEN_FROM; |
| int MIME_LEN_HOST; |
| int MIME_LEN_IF_MATCH; |
| int MIME_LEN_IF_MODIFIED_SINCE; |
| int MIME_LEN_IF_NONE_MATCH; |
| int MIME_LEN_IF_RANGE; |
| int MIME_LEN_IF_UNMODIFIED_SINCE; |
| int MIME_LEN_KEEP_ALIVE; |
| int MIME_LEN_KEYWORDS; |
| int MIME_LEN_LAST_MODIFIED; |
| int MIME_LEN_LINES; |
| int MIME_LEN_LOCATION; |
| int MIME_LEN_MAX_FORWARDS; |
| int MIME_LEN_MESSAGE_ID; |
| int MIME_LEN_NEWSGROUPS; |
| int MIME_LEN_ORGANIZATION; |
| int MIME_LEN_PATH; |
| int MIME_LEN_PRAGMA; |
| int MIME_LEN_PROXY_AUTHENTICATE; |
| int MIME_LEN_PROXY_AUTHORIZATION; |
| int MIME_LEN_PROXY_CONNECTION; |
| int MIME_LEN_PUBLIC; |
| int MIME_LEN_RANGE; |
| int MIME_LEN_REFERENCES; |
| int MIME_LEN_REFERER; |
| int MIME_LEN_REPLY_TO; |
| int MIME_LEN_RETRY_AFTER; |
| int MIME_LEN_SENDER; |
| int MIME_LEN_SERVER; |
| int MIME_LEN_SET_COOKIE; |
| int MIME_LEN_STRICT_TRANSPORT_SECURITY; |
| int MIME_LEN_SUBJECT; |
| int MIME_LEN_SUMMARY; |
| int MIME_LEN_TE; |
| int MIME_LEN_TRANSFER_ENCODING; |
| int MIME_LEN_UPGRADE; |
| int MIME_LEN_USER_AGENT; |
| int MIME_LEN_VARY; |
| int MIME_LEN_VIA; |
| int MIME_LEN_WARNING; |
| int MIME_LEN_WWW_AUTHENTICATE; |
| int MIME_LEN_XREF; |
| int MIME_LEN_ATS_INTERNAL; |
| int MIME_LEN_X_ID; |
| int MIME_LEN_X_FORWARDED_FOR; |
| int MIME_LEN_FORWARDED; |
| int MIME_LEN_SEC_WEBSOCKET_KEY; |
| int MIME_LEN_SEC_WEBSOCKET_VERSION; |
| int MIME_LEN_HTTP2_SETTINGS; |
| int MIME_LEN_EARLY_DATA; |
| |
| int MIME_WKSIDX_ACCEPT; |
| int MIME_WKSIDX_ACCEPT_CHARSET; |
| int MIME_WKSIDX_ACCEPT_ENCODING; |
| int MIME_WKSIDX_ACCEPT_LANGUAGE; |
| int MIME_WKSIDX_ACCEPT_RANGES; |
| int MIME_WKSIDX_AGE; |
| int MIME_WKSIDX_ALLOW; |
| int MIME_WKSIDX_APPROVED; |
| int MIME_WKSIDX_AUTHORIZATION; |
| int MIME_WKSIDX_BYTES; |
| int MIME_WKSIDX_CACHE_CONTROL; |
| int MIME_WKSIDX_CLIENT_IP; |
| int MIME_WKSIDX_CONNECTION; |
| int MIME_WKSIDX_CONTENT_BASE; |
| int MIME_WKSIDX_CONTENT_ENCODING; |
| int MIME_WKSIDX_CONTENT_LANGUAGE; |
| int MIME_WKSIDX_CONTENT_LENGTH; |
| int MIME_WKSIDX_CONTENT_LOCATION; |
| int MIME_WKSIDX_CONTENT_MD5; |
| int MIME_WKSIDX_CONTENT_RANGE; |
| int MIME_WKSIDX_CONTENT_TYPE; |
| int MIME_WKSIDX_CONTROL; |
| int MIME_WKSIDX_COOKIE; |
| int MIME_WKSIDX_DATE; |
| int MIME_WKSIDX_DISTRIBUTION; |
| int MIME_WKSIDX_ETAG; |
| int MIME_WKSIDX_EXPECT; |
| int MIME_WKSIDX_EXPIRES; |
| int MIME_WKSIDX_FOLLOWUP_TO; |
| int MIME_WKSIDX_FROM; |
| int MIME_WKSIDX_HOST; |
| int MIME_WKSIDX_IF_MATCH; |
| int MIME_WKSIDX_IF_MODIFIED_SINCE; |
| int MIME_WKSIDX_IF_NONE_MATCH; |
| int MIME_WKSIDX_IF_RANGE; |
| int MIME_WKSIDX_IF_UNMODIFIED_SINCE; |
| int MIME_WKSIDX_KEEP_ALIVE; |
| int MIME_WKSIDX_KEYWORDS; |
| int MIME_WKSIDX_LAST_MODIFIED; |
| int MIME_WKSIDX_LINES; |
| int MIME_WKSIDX_LOCATION; |
| int MIME_WKSIDX_MAX_FORWARDS; |
| int MIME_WKSIDX_MESSAGE_ID; |
| int MIME_WKSIDX_NEWSGROUPS; |
| int MIME_WKSIDX_ORGANIZATION; |
| int MIME_WKSIDX_PATH; |
| int MIME_WKSIDX_PRAGMA; |
| int MIME_WKSIDX_PROXY_AUTHENTICATE; |
| int MIME_WKSIDX_PROXY_AUTHORIZATION; |
| int MIME_WKSIDX_PROXY_CONNECTION; |
| int MIME_WKSIDX_PUBLIC; |
| int MIME_WKSIDX_RANGE; |
| int MIME_WKSIDX_REFERENCES; |
| int MIME_WKSIDX_REFERER; |
| int MIME_WKSIDX_REPLY_TO; |
| int MIME_WKSIDX_RETRY_AFTER; |
| int MIME_WKSIDX_SENDER; |
| int MIME_WKSIDX_SERVER; |
| int MIME_WKSIDX_SET_COOKIE; |
| int MIME_WKSIDX_STRICT_TRANSPORT_SECURITY; |
| int MIME_WKSIDX_SUBJECT; |
| int MIME_WKSIDX_SUMMARY; |
| int MIME_WKSIDX_TE; |
| int MIME_WKSIDX_TRANSFER_ENCODING; |
| int MIME_WKSIDX_UPGRADE; |
| int MIME_WKSIDX_USER_AGENT; |
| int MIME_WKSIDX_VARY; |
| int MIME_WKSIDX_VIA; |
| int MIME_WKSIDX_WARNING; |
| int MIME_WKSIDX_WWW_AUTHENTICATE; |
| int MIME_WKSIDX_XREF; |
| int MIME_WKSIDX_ATS_INTERNAL; |
| int MIME_WKSIDX_X_ID; |
| int MIME_WKSIDX_X_FORWARDED_FOR; |
| int MIME_WKSIDX_FORWARDED; |
| int MIME_WKSIDX_SEC_WEBSOCKET_KEY; |
| int MIME_WKSIDX_SEC_WEBSOCKET_VERSION; |
| int MIME_WKSIDX_HTTP2_SETTINGS; |
| int MIME_WKSIDX_EARLY_DATA; |
| |
| /*********************************************************************** |
| * * |
| * U T I L I T Y R O U T I N E S * |
| * * |
| ***********************************************************************/ |
| inline static int |
| is_digit(char c) |
| { |
| return ((c <= '9') && (c >= '0')); |
| } |
| |
| inline static int |
| is_ws(char c) |
| { |
| return ((c == ParseRules::CHAR_SP) || (c == ParseRules::CHAR_HT)); |
| } |
| |
| /*********************************************************************** |
| * * |
| * P R E S E N C E B I T S * |
| * * |
| ***********************************************************************/ |
| uint64_t |
| mime_field_presence_mask(const char *well_known_str) |
| { |
| return hdrtoken_wks_to_mask(well_known_str); |
| } |
| |
| uint64_t |
| mime_field_presence_mask(int well_known_str_index) |
| { |
| return hdrtoken_index_to_mask(well_known_str_index); |
| } |
| |
| int |
| mime_field_presence_get(MIMEHdrImpl *h, const char *well_known_str) |
| { |
| uint64_t mask = mime_field_presence_mask(well_known_str); |
| return ((mask == 0) ? 1 : ((h->m_presence_bits & mask) == 0 ? 0 : 1)); |
| } |
| |
| int |
| mime_field_presence_get(MIMEHdrImpl *h, int well_known_str_index) |
| { |
| const char *wks = hdrtoken_index_to_wks(well_known_str_index); |
| return mime_field_presence_get(h, wks); |
| } |
| |
| void |
| mime_hdr_presence_set(MIMEHdrImpl *h, const char *well_known_str) |
| { |
| uint64_t mask = mime_field_presence_mask(well_known_str); |
| if (mask != 0) { |
| h->m_presence_bits |= mask; |
| } |
| } |
| |
| void |
| mime_hdr_presence_set(MIMEHdrImpl *h, int well_known_str_index) |
| { |
| const char *wks = hdrtoken_index_to_wks(well_known_str_index); |
| mime_hdr_presence_set(h, wks); |
| } |
| |
| void |
| mime_hdr_presence_unset(MIMEHdrImpl *h, const char *well_known_str) |
| { |
| uint64_t mask = mime_field_presence_mask(well_known_str); |
| if (mask != 0) { |
| h->m_presence_bits &= (~mask); |
| } |
| } |
| |
| void |
| mime_hdr_presence_unset(MIMEHdrImpl *h, int well_known_str_index) |
| { |
| const char *wks = hdrtoken_index_to_wks(well_known_str_index); |
| mime_hdr_presence_unset(h, wks); |
| } |
| |
| /*********************************************************************** |
| * * |
| * S L O T A C C E L E R A T O R S * |
| * * |
| ***********************************************************************/ |
| inline void |
| mime_hdr_init_accelerators_and_presence_bits(MIMEHdrImpl *mh) |
| { |
| mh->m_presence_bits = 0; |
| mh->m_slot_accelerators[0] = 0xFFFFFFFF; |
| mh->m_slot_accelerators[1] = 0xFFFFFFFF; |
| mh->m_slot_accelerators[2] = 0xFFFFFFFF; |
| mh->m_slot_accelerators[3] = 0xFFFFFFFF; |
| } |
| |
| inline uint32_t |
| mime_hdr_get_accelerator_slotnum(MIMEHdrImpl *mh, int32_t slot_id) |
| { |
| ink_assert((slot_id != MIME_SLOTID_NONE) && (slot_id < 32)); |
| |
| uint32_t word_index = slot_id / 8; // 4 words of 8 slots |
| uint32_t word = mh->m_slot_accelerators[word_index]; // 8 slots of 4 bits each |
| uint32_t nybble = slot_id % 8; // which of the 8 nybbles? |
| uint32_t slot = ((word >> (nybble * 4)) & 15); // grab the 4 bit slotnum |
| return slot; |
| } |
| |
| inline void |
| mime_hdr_set_accelerator_slotnum(MIMEHdrImpl *mh, int32_t slot_id, uint32_t slot_num) |
| { |
| ink_assert((slot_id != MIME_SLOTID_NONE) && (slot_id < 32)); |
| ink_assert(slot_num < 16); |
| |
| uint32_t word_index = slot_id / 8; // 4 words of 8 slots |
| uint32_t word = mh->m_slot_accelerators[word_index]; // 8 slots of 4 bits each |
| uint32_t nybble = slot_id % 8; // which of the 8 nybbles? |
| uint32_t shift = nybble * 4; // shift in chunks of 4 bits |
| uint32_t mask = ~(MIME_FIELD_SLOTNUM_MASK << shift); // mask to zero out old slot |
| uint32_t graft = (slot_num << shift); // plug to insert into slot |
| uint32_t new_word = (word & mask) | graft; // new value |
| |
| mh->m_slot_accelerators[word_index] = new_word; |
| } |
| |
| inline void |
| mime_hdr_set_accelerators_and_presence_bits(MIMEHdrImpl *mh, MIMEField *field) |
| { |
| int slot_id; |
| ptrdiff_t slot_num; |
| if (field->m_wks_idx < 0) { |
| return; |
| } |
| |
| ink_assert(mh); |
| |
| mime_hdr_presence_set(mh, field->m_wks_idx); |
| |
| slot_id = hdrtoken_index_to_slotid(field->m_wks_idx); |
| if (slot_id != MIME_SLOTID_NONE) { |
| if (mh->m_first_fblock.contains(field)) { |
| slot_num = (field - &(mh->m_first_fblock.m_field_slots[0])); |
| // constains() assure that the field is in the block, and the calculated |
| // slot_num will be between 0 and 15, which seem valid. |
| // However, strangely, this function regards slot number 14 and 15 as |
| // unknown for some reason that is not clear. It might be a bug. |
| // The block below is left to keep the original behavior. See also TS-4316. |
| if (slot_num >= MIME_FIELD_SLOTNUM_UNKNOWN) { |
| slot_num = MIME_FIELD_SLOTNUM_UNKNOWN; |
| } |
| } else { |
| slot_num = MIME_FIELD_SLOTNUM_UNKNOWN; |
| } |
| mime_hdr_set_accelerator_slotnum(mh, slot_id, slot_num); |
| } |
| } |
| |
| inline void |
| mime_hdr_unset_accelerators_and_presence_bits(MIMEHdrImpl *mh, MIMEField *field) |
| { |
| int slot_id; |
| if (field->m_wks_idx < 0) { |
| return; |
| } |
| |
| mime_hdr_presence_unset(mh, field->m_wks_idx); |
| |
| slot_id = hdrtoken_index_to_slotid(field->m_wks_idx); |
| if (slot_id != MIME_SLOTID_NONE) { |
| mime_hdr_set_accelerator_slotnum(mh, slot_id, MIME_FIELD_SLOTNUM_MAX); |
| } |
| } |
| |
| /// Reset data in the header. |
| /// Clear all the presence bits and accelerators. |
| /// Update all the m_wks_idx values, presence bits and accelerators. |
| inline void |
| mime_hdr_reset_accelerators_and_presence_bits(MIMEHdrImpl *mh) |
| { |
| mime_hdr_init_accelerators_and_presence_bits(mh); |
| |
| for (MIMEFieldBlockImpl *fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
| for (MIMEField *field = fblock->m_field_slots, *limit = field + fblock->m_freetop; field < limit; ++field) { |
| if (field->is_live()) { |
| field->m_wks_idx = hdrtoken_tokenize(field->m_ptr_name, field->m_len_name); |
| if (field->is_dup_head()) { |
| mime_hdr_set_accelerators_and_presence_bits(mh, field); |
| } |
| } |
| } |
| } |
| } |
| |
| int |
| checksum_block(const char *s, int len) |
| { |
| int sum = 0; |
| while (len--) { |
| sum ^= *s++; |
| } |
| return sum; |
| } |
| |
| #ifdef DEBUG |
| void |
| mime_hdr_sanity_check(MIMEHdrImpl *mh) |
| { |
| MIMEFieldBlockImpl *fblock, *blk, *last_fblock; |
| MIMEField *field, *next_dup; |
| uint32_t slot_index, index; |
| uint64_t masksum; |
| |
| ink_assert(mh != nullptr); |
| |
| masksum = 0; |
| slot_index = 0; |
| last_fblock = nullptr; |
| |
| for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
| for (index = 0; index < fblock->m_freetop; index++) { |
| field = &(fblock->m_field_slots[index]); |
| |
| if (field->is_live()) { |
| // dummy operations just to make sure deref doesn't crash |
| checksum_block(field->m_ptr_name, field->m_len_name); |
| if (field->m_ptr_value) { |
| checksum_block(field->m_ptr_value, field->m_len_value); |
| } |
| |
| if (field->m_n_v_raw_printable) { |
| int total_len = field->m_len_name + field->m_len_value + field->m_n_v_raw_printable_pad; |
| checksum_block(field->m_ptr_name, total_len); |
| } |
| // walk the dup list, quickly checking each cell |
| if (field->m_next_dup != nullptr) { |
| int field_slotnum = mime_hdr_field_slotnum(mh, field); |
| |
| for (next_dup = field->m_next_dup; next_dup; next_dup = next_dup->m_next_dup) { |
| int next_slotnum = mime_hdr_field_slotnum(mh, next_dup); |
| ink_release_assert((next_dup->m_flags & MIME_FIELD_SLOT_FLAGS_DUP_HEAD) == 0); |
| ink_release_assert((next_dup->m_readiness == MIME_FIELD_SLOT_READINESS_LIVE)); |
| ink_release_assert(next_dup->m_wks_idx == field->m_wks_idx); |
| ink_release_assert(next_dup->m_len_name == field->m_len_name); |
| ink_release_assert(strncasecmp(field->m_ptr_name, next_dup->m_ptr_name, field->m_len_name) == 0); |
| ink_release_assert(next_slotnum > field_slotnum); |
| } |
| } |
| // if this is a well known string, check presence bits & slot accelerators |
| if (field->m_wks_idx >= 0) { |
| const char *wks = hdrtoken_index_to_wks(field->m_wks_idx); |
| int len = hdrtoken_index_to_length(field->m_wks_idx); |
| |
| if (field->m_len_name != len || strncasecmp(field->m_ptr_name, wks, field->m_len_name) != 0) { |
| Warning("Encountered WKS hash collision on '%.*s'", field->m_len_name, field->m_ptr_name); |
| } |
| |
| uint64_t mask = mime_field_presence_mask(field->m_wks_idx); |
| masksum |= mask; |
| |
| int32_t slot_id = hdrtoken_index_to_slotid(field->m_wks_idx); |
| if ((slot_id != MIME_SLOTID_NONE) && (slot_index < MIME_FIELD_SLOTNUM_UNKNOWN) && |
| (field->m_flags & MIME_FIELD_SLOT_FLAGS_DUP_HEAD)) { |
| uint32_t slot_num = mime_hdr_get_accelerator_slotnum(mh, slot_id); |
| if (slot_num <= 14) { |
| ink_release_assert(slot_num == slot_index); |
| } |
| } |
| } else { |
| int idx = hdrtoken_tokenize(field->m_ptr_name, field->m_len_name, nullptr); |
| ink_release_assert(idx < 0); |
| } |
| |
| // verify that the next dup pointer points to a block in this list |
| if (field->m_next_dup) { |
| bool found = false; |
| for (blk = &(mh->m_first_fblock); blk != nullptr; blk = blk->m_next) { |
| const char *addr = reinterpret_cast<const char *>(field->m_next_dup); |
| if ((addr >= reinterpret_cast<const char *>(blk)) && |
| (addr < reinterpret_cast<const char *>(blk) + sizeof(MIMEFieldBlockImpl))) { |
| found = true; |
| break; |
| } |
| } |
| ink_release_assert(found); |
| } |
| // re-find the field --- should always find the head dup |
| MIMEField *mf = mime_hdr_field_find(mh, field->m_ptr_name, field->m_len_name); |
| ink_release_assert(mf != nullptr); |
| if (mf == field) { |
| ink_release_assert((field->m_flags & MIME_FIELD_SLOT_FLAGS_DUP_HEAD) != 0); |
| } else { |
| ink_release_assert((field->m_flags & MIME_FIELD_SLOT_FLAGS_DUP_HEAD) == 0); |
| } |
| } |
| |
| ++slot_index; |
| } |
| last_fblock = fblock; |
| } |
| |
| ink_release_assert(last_fblock == mh->m_fblock_list_tail); |
| ink_release_assert(masksum == mh->m_presence_bits); |
| } |
| #endif |
| |
| void |
| mime_init() |
| { |
| static int init = 1; |
| |
| if (init) { |
| init = 0; |
| |
| hdrtoken_init(); |
| day_names_dfa = new DFA; |
| day_names_dfa->compile(day_names, SIZEOF(day_names), RE_CASE_INSENSITIVE); |
| |
| month_names_dfa = new DFA; |
| month_names_dfa->compile(month_names, SIZEOF(month_names), RE_CASE_INSENSITIVE); |
| |
| MIME_FIELD_ACCEPT = hdrtoken_string_to_wks("Accept"); |
| MIME_FIELD_ACCEPT_CHARSET = hdrtoken_string_to_wks("Accept-Charset"); |
| MIME_FIELD_ACCEPT_ENCODING = hdrtoken_string_to_wks("Accept-Encoding"); |
| MIME_FIELD_ACCEPT_LANGUAGE = hdrtoken_string_to_wks("Accept-Language"); |
| MIME_FIELD_ACCEPT_RANGES = hdrtoken_string_to_wks("Accept-Ranges"); |
| MIME_FIELD_AGE = hdrtoken_string_to_wks("Age"); |
| MIME_FIELD_ALLOW = hdrtoken_string_to_wks("Allow"); |
| MIME_FIELD_APPROVED = hdrtoken_string_to_wks("Approved"); |
| MIME_FIELD_AUTHORIZATION = hdrtoken_string_to_wks("Authorization"); |
| MIME_FIELD_BYTES = hdrtoken_string_to_wks("Bytes"); |
| MIME_FIELD_CACHE_CONTROL = hdrtoken_string_to_wks("Cache-Control"); |
| MIME_FIELD_CLIENT_IP = hdrtoken_string_to_wks("Client-ip"); |
| MIME_FIELD_CONNECTION = hdrtoken_string_to_wks("Connection"); |
| MIME_FIELD_CONTENT_BASE = hdrtoken_string_to_wks("Content-Base"); |
| MIME_FIELD_CONTENT_ENCODING = hdrtoken_string_to_wks("Content-Encoding"); |
| MIME_FIELD_CONTENT_LANGUAGE = hdrtoken_string_to_wks("Content-Language"); |
| MIME_FIELD_CONTENT_LENGTH = hdrtoken_string_to_wks("Content-Length"); |
| MIME_FIELD_CONTENT_LOCATION = hdrtoken_string_to_wks("Content-Location"); |
| MIME_FIELD_CONTENT_MD5 = hdrtoken_string_to_wks("Content-MD5"); |
| MIME_FIELD_CONTENT_RANGE = hdrtoken_string_to_wks("Content-Range"); |
| MIME_FIELD_CONTENT_TYPE = hdrtoken_string_to_wks("Content-Type"); |
| MIME_FIELD_CONTROL = hdrtoken_string_to_wks("Control"); |
| MIME_FIELD_COOKIE = hdrtoken_string_to_wks("Cookie"); |
| MIME_FIELD_DATE = hdrtoken_string_to_wks("Date"); |
| MIME_FIELD_DISTRIBUTION = hdrtoken_string_to_wks("Distribution"); |
| MIME_FIELD_ETAG = hdrtoken_string_to_wks("Etag"); |
| MIME_FIELD_EXPECT = hdrtoken_string_to_wks("Expect"); |
| MIME_FIELD_EXPIRES = hdrtoken_string_to_wks("Expires"); |
| MIME_FIELD_FOLLOWUP_TO = hdrtoken_string_to_wks("Followup-To"); |
| MIME_FIELD_FROM = hdrtoken_string_to_wks("From"); |
| MIME_FIELD_HOST = hdrtoken_string_to_wks("Host"); |
| MIME_FIELD_IF_MATCH = hdrtoken_string_to_wks("If-Match"); |
| MIME_FIELD_IF_MODIFIED_SINCE = hdrtoken_string_to_wks("If-Modified-Since"); |
| MIME_FIELD_IF_NONE_MATCH = hdrtoken_string_to_wks("If-None-Match"); |
| MIME_FIELD_IF_RANGE = hdrtoken_string_to_wks("If-Range"); |
| MIME_FIELD_IF_UNMODIFIED_SINCE = hdrtoken_string_to_wks("If-Unmodified-Since"); |
| MIME_FIELD_KEEP_ALIVE = hdrtoken_string_to_wks("Keep-Alive"); |
| MIME_FIELD_KEYWORDS = hdrtoken_string_to_wks("Keywords"); |
| MIME_FIELD_LAST_MODIFIED = hdrtoken_string_to_wks("Last-Modified"); |
| MIME_FIELD_LINES = hdrtoken_string_to_wks("Lines"); |
| MIME_FIELD_LOCATION = hdrtoken_string_to_wks("Location"); |
| MIME_FIELD_MAX_FORWARDS = hdrtoken_string_to_wks("Max-Forwards"); |
| MIME_FIELD_MESSAGE_ID = hdrtoken_string_to_wks("Message-ID"); |
| MIME_FIELD_NEWSGROUPS = hdrtoken_string_to_wks("Newsgroups"); |
| MIME_FIELD_ORGANIZATION = hdrtoken_string_to_wks("Organization"); |
| MIME_FIELD_PATH = hdrtoken_string_to_wks("Path"); |
| MIME_FIELD_PRAGMA = hdrtoken_string_to_wks("Pragma"); |
| MIME_FIELD_PROXY_AUTHENTICATE = hdrtoken_string_to_wks("Proxy-Authenticate"); |
| MIME_FIELD_PROXY_AUTHORIZATION = hdrtoken_string_to_wks("Proxy-Authorization"); |
| MIME_FIELD_PROXY_CONNECTION = hdrtoken_string_to_wks("Proxy-Connection"); |
| MIME_FIELD_PUBLIC = hdrtoken_string_to_wks("Public"); |
| MIME_FIELD_RANGE = hdrtoken_string_to_wks("Range"); |
| MIME_FIELD_REFERENCES = hdrtoken_string_to_wks("References"); |
| MIME_FIELD_REFERER = hdrtoken_string_to_wks("Referer"); |
| MIME_FIELD_REPLY_TO = hdrtoken_string_to_wks("Reply-To"); |
| MIME_FIELD_RETRY_AFTER = hdrtoken_string_to_wks("Retry-After"); |
| MIME_FIELD_SENDER = hdrtoken_string_to_wks("Sender"); |
| MIME_FIELD_SERVER = hdrtoken_string_to_wks("Server"); |
| MIME_FIELD_SET_COOKIE = hdrtoken_string_to_wks("Set-Cookie"); |
| MIME_FIELD_STRICT_TRANSPORT_SECURITY = hdrtoken_string_to_wks("Strict-Transport-Security"); |
| MIME_FIELD_SUBJECT = hdrtoken_string_to_wks("Subject"); |
| MIME_FIELD_SUMMARY = hdrtoken_string_to_wks("Summary"); |
| MIME_FIELD_TE = hdrtoken_string_to_wks("TE"); |
| MIME_FIELD_TRANSFER_ENCODING = hdrtoken_string_to_wks("Transfer-Encoding"); |
| MIME_FIELD_UPGRADE = hdrtoken_string_to_wks("Upgrade"); |
| MIME_FIELD_USER_AGENT = hdrtoken_string_to_wks("User-Agent"); |
| MIME_FIELD_VARY = hdrtoken_string_to_wks("Vary"); |
| MIME_FIELD_VIA = hdrtoken_string_to_wks("Via"); |
| MIME_FIELD_WARNING = hdrtoken_string_to_wks("Warning"); |
| MIME_FIELD_WWW_AUTHENTICATE = hdrtoken_string_to_wks("Www-Authenticate"); |
| MIME_FIELD_XREF = hdrtoken_string_to_wks("Xref"); |
| MIME_FIELD_ATS_INTERNAL = hdrtoken_string_to_wks("@Ats-Internal"); |
| MIME_FIELD_X_ID = hdrtoken_string_to_wks("X-ID"); |
| MIME_FIELD_X_FORWARDED_FOR = hdrtoken_string_to_wks("X-Forwarded-For"); |
| MIME_FIELD_FORWARDED = hdrtoken_string_to_wks("Forwarded"); |
| MIME_FIELD_SEC_WEBSOCKET_KEY = hdrtoken_string_to_wks("Sec-WebSocket-Key"); |
| MIME_FIELD_SEC_WEBSOCKET_VERSION = hdrtoken_string_to_wks("Sec-WebSocket-Version"); |
| MIME_FIELD_HTTP2_SETTINGS = hdrtoken_string_to_wks("HTTP2-Settings"); |
| MIME_FIELD_EARLY_DATA = hdrtoken_string_to_wks("Early-Data"); |
| |
| MIME_LEN_ACCEPT = hdrtoken_wks_to_length(MIME_FIELD_ACCEPT); |
| MIME_LEN_ACCEPT_CHARSET = hdrtoken_wks_to_length(MIME_FIELD_ACCEPT_CHARSET); |
| MIME_LEN_ACCEPT_ENCODING = hdrtoken_wks_to_length(MIME_FIELD_ACCEPT_ENCODING); |
| MIME_LEN_ACCEPT_LANGUAGE = hdrtoken_wks_to_length(MIME_FIELD_ACCEPT_LANGUAGE); |
| MIME_LEN_ACCEPT_RANGES = hdrtoken_wks_to_length(MIME_FIELD_ACCEPT_RANGES); |
| MIME_LEN_AGE = hdrtoken_wks_to_length(MIME_FIELD_AGE); |
| MIME_LEN_ALLOW = hdrtoken_wks_to_length(MIME_FIELD_ALLOW); |
| MIME_LEN_APPROVED = hdrtoken_wks_to_length(MIME_FIELD_APPROVED); |
| MIME_LEN_AUTHORIZATION = hdrtoken_wks_to_length(MIME_FIELD_AUTHORIZATION); |
| MIME_LEN_BYTES = hdrtoken_wks_to_length(MIME_FIELD_BYTES); |
| MIME_LEN_CACHE_CONTROL = hdrtoken_wks_to_length(MIME_FIELD_CACHE_CONTROL); |
| MIME_LEN_CLIENT_IP = hdrtoken_wks_to_length(MIME_FIELD_CLIENT_IP); |
| MIME_LEN_CONNECTION = hdrtoken_wks_to_length(MIME_FIELD_CONNECTION); |
| MIME_LEN_CONTENT_BASE = hdrtoken_wks_to_length(MIME_FIELD_CONTENT_BASE); |
| MIME_LEN_CONTENT_ENCODING = hdrtoken_wks_to_length(MIME_FIELD_CONTENT_ENCODING); |
| MIME_LEN_CONTENT_LANGUAGE = hdrtoken_wks_to_length(MIME_FIELD_CONTENT_LANGUAGE); |
| MIME_LEN_CONTENT_LENGTH = hdrtoken_wks_to_length(MIME_FIELD_CONTENT_LENGTH); |
| MIME_LEN_CONTENT_LOCATION = hdrtoken_wks_to_length(MIME_FIELD_CONTENT_LOCATION); |
| MIME_LEN_CONTENT_MD5 = hdrtoken_wks_to_length(MIME_FIELD_CONTENT_MD5); |
| MIME_LEN_CONTENT_RANGE = hdrtoken_wks_to_length(MIME_FIELD_CONTENT_RANGE); |
| MIME_LEN_CONTENT_TYPE = hdrtoken_wks_to_length(MIME_FIELD_CONTENT_TYPE); |
| MIME_LEN_CONTROL = hdrtoken_wks_to_length(MIME_FIELD_CONTROL); |
| MIME_LEN_COOKIE = hdrtoken_wks_to_length(MIME_FIELD_COOKIE); |
| MIME_LEN_DATE = hdrtoken_wks_to_length(MIME_FIELD_DATE); |
| MIME_LEN_DISTRIBUTION = hdrtoken_wks_to_length(MIME_FIELD_DISTRIBUTION); |
| MIME_LEN_ETAG = hdrtoken_wks_to_length(MIME_FIELD_ETAG); |
| MIME_LEN_EXPECT = hdrtoken_wks_to_length(MIME_FIELD_EXPECT); |
| MIME_LEN_EXPIRES = hdrtoken_wks_to_length(MIME_FIELD_EXPIRES); |
| MIME_LEN_FOLLOWUP_TO = hdrtoken_wks_to_length(MIME_FIELD_FOLLOWUP_TO); |
| MIME_LEN_FROM = hdrtoken_wks_to_length(MIME_FIELD_FROM); |
| MIME_LEN_HOST = hdrtoken_wks_to_length(MIME_FIELD_HOST); |
| MIME_LEN_IF_MATCH = hdrtoken_wks_to_length(MIME_FIELD_IF_MATCH); |
| MIME_LEN_IF_MODIFIED_SINCE = hdrtoken_wks_to_length(MIME_FIELD_IF_MODIFIED_SINCE); |
| MIME_LEN_IF_NONE_MATCH = hdrtoken_wks_to_length(MIME_FIELD_IF_NONE_MATCH); |
| MIME_LEN_IF_RANGE = hdrtoken_wks_to_length(MIME_FIELD_IF_RANGE); |
| MIME_LEN_IF_UNMODIFIED_SINCE = hdrtoken_wks_to_length(MIME_FIELD_IF_UNMODIFIED_SINCE); |
| MIME_LEN_KEEP_ALIVE = hdrtoken_wks_to_length(MIME_FIELD_KEEP_ALIVE); |
| MIME_LEN_KEYWORDS = hdrtoken_wks_to_length(MIME_FIELD_KEYWORDS); |
| MIME_LEN_LAST_MODIFIED = hdrtoken_wks_to_length(MIME_FIELD_LAST_MODIFIED); |
| MIME_LEN_LINES = hdrtoken_wks_to_length(MIME_FIELD_LINES); |
| MIME_LEN_LOCATION = hdrtoken_wks_to_length(MIME_FIELD_LOCATION); |
| MIME_LEN_MAX_FORWARDS = hdrtoken_wks_to_length(MIME_FIELD_MAX_FORWARDS); |
| MIME_LEN_MESSAGE_ID = hdrtoken_wks_to_length(MIME_FIELD_MESSAGE_ID); |
| MIME_LEN_NEWSGROUPS = hdrtoken_wks_to_length(MIME_FIELD_NEWSGROUPS); |
| MIME_LEN_ORGANIZATION = hdrtoken_wks_to_length(MIME_FIELD_ORGANIZATION); |
| MIME_LEN_PATH = hdrtoken_wks_to_length(MIME_FIELD_PATH); |
| MIME_LEN_PRAGMA = hdrtoken_wks_to_length(MIME_FIELD_PRAGMA); |
| MIME_LEN_PROXY_AUTHENTICATE = hdrtoken_wks_to_length(MIME_FIELD_PROXY_AUTHENTICATE); |
| MIME_LEN_PROXY_AUTHORIZATION = hdrtoken_wks_to_length(MIME_FIELD_PROXY_AUTHORIZATION); |
| MIME_LEN_PROXY_CONNECTION = hdrtoken_wks_to_length(MIME_FIELD_PROXY_CONNECTION); |
| MIME_LEN_PUBLIC = hdrtoken_wks_to_length(MIME_FIELD_PUBLIC); |
| MIME_LEN_RANGE = hdrtoken_wks_to_length(MIME_FIELD_RANGE); |
| MIME_LEN_REFERENCES = hdrtoken_wks_to_length(MIME_FIELD_REFERENCES); |
| MIME_LEN_REFERER = hdrtoken_wks_to_length(MIME_FIELD_REFERER); |
| MIME_LEN_REPLY_TO = hdrtoken_wks_to_length(MIME_FIELD_REPLY_TO); |
| MIME_LEN_RETRY_AFTER = hdrtoken_wks_to_length(MIME_FIELD_RETRY_AFTER); |
| MIME_LEN_SENDER = hdrtoken_wks_to_length(MIME_FIELD_SENDER); |
| MIME_LEN_SERVER = hdrtoken_wks_to_length(MIME_FIELD_SERVER); |
| MIME_LEN_SET_COOKIE = hdrtoken_wks_to_length(MIME_FIELD_SET_COOKIE); |
| MIME_LEN_STRICT_TRANSPORT_SECURITY = hdrtoken_wks_to_length(MIME_FIELD_STRICT_TRANSPORT_SECURITY); |
| MIME_LEN_SUBJECT = hdrtoken_wks_to_length(MIME_FIELD_SUBJECT); |
| MIME_LEN_SUMMARY = hdrtoken_wks_to_length(MIME_FIELD_SUMMARY); |
| MIME_LEN_TE = hdrtoken_wks_to_length(MIME_FIELD_TE); |
| MIME_LEN_TRANSFER_ENCODING = hdrtoken_wks_to_length(MIME_FIELD_TRANSFER_ENCODING); |
| MIME_LEN_UPGRADE = hdrtoken_wks_to_length(MIME_FIELD_UPGRADE); |
| MIME_LEN_USER_AGENT = hdrtoken_wks_to_length(MIME_FIELD_USER_AGENT); |
| MIME_LEN_VARY = hdrtoken_wks_to_length(MIME_FIELD_VARY); |
| MIME_LEN_VIA = hdrtoken_wks_to_length(MIME_FIELD_VIA); |
| MIME_LEN_WARNING = hdrtoken_wks_to_length(MIME_FIELD_WARNING); |
| MIME_LEN_WWW_AUTHENTICATE = hdrtoken_wks_to_length(MIME_FIELD_WWW_AUTHENTICATE); |
| MIME_LEN_XREF = hdrtoken_wks_to_length(MIME_FIELD_XREF); |
| MIME_LEN_ATS_INTERNAL = hdrtoken_wks_to_length(MIME_FIELD_ATS_INTERNAL); |
| MIME_LEN_X_ID = hdrtoken_wks_to_length(MIME_FIELD_X_ID); |
| MIME_LEN_X_FORWARDED_FOR = hdrtoken_wks_to_length(MIME_FIELD_X_FORWARDED_FOR); |
| MIME_LEN_FORWARDED = hdrtoken_wks_to_length(MIME_FIELD_FORWARDED); |
| MIME_LEN_SEC_WEBSOCKET_KEY = hdrtoken_wks_to_length(MIME_FIELD_SEC_WEBSOCKET_KEY); |
| MIME_LEN_SEC_WEBSOCKET_VERSION = hdrtoken_wks_to_length(MIME_FIELD_SEC_WEBSOCKET_VERSION); |
| MIME_LEN_HTTP2_SETTINGS = hdrtoken_wks_to_length(MIME_FIELD_HTTP2_SETTINGS); |
| MIME_LEN_EARLY_DATA = hdrtoken_wks_to_length(MIME_FIELD_EARLY_DATA); |
| |
| MIME_WKSIDX_ACCEPT = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT); |
| MIME_WKSIDX_ACCEPT_CHARSET = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT_CHARSET); |
| MIME_WKSIDX_ACCEPT_ENCODING = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT_ENCODING); |
| MIME_WKSIDX_ACCEPT_LANGUAGE = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT_LANGUAGE); |
| MIME_WKSIDX_ACCEPT_RANGES = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT_RANGES); |
| MIME_WKSIDX_AGE = hdrtoken_wks_to_index(MIME_FIELD_AGE); |
| MIME_WKSIDX_ALLOW = hdrtoken_wks_to_index(MIME_FIELD_ALLOW); |
| MIME_WKSIDX_APPROVED = hdrtoken_wks_to_index(MIME_FIELD_APPROVED); |
| MIME_WKSIDX_AUTHORIZATION = hdrtoken_wks_to_index(MIME_FIELD_AUTHORIZATION); |
| MIME_WKSIDX_BYTES = hdrtoken_wks_to_index(MIME_FIELD_BYTES); |
| MIME_WKSIDX_CACHE_CONTROL = hdrtoken_wks_to_index(MIME_FIELD_CACHE_CONTROL); |
| MIME_WKSIDX_CLIENT_IP = hdrtoken_wks_to_index(MIME_FIELD_CLIENT_IP); |
| MIME_WKSIDX_CONNECTION = hdrtoken_wks_to_index(MIME_FIELD_CONNECTION); |
| MIME_WKSIDX_CONTENT_BASE = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_BASE); |
| MIME_WKSIDX_CONTENT_ENCODING = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_ENCODING); |
| MIME_WKSIDX_CONTENT_LANGUAGE = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_LANGUAGE); |
| MIME_WKSIDX_CONTENT_LENGTH = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_LENGTH); |
| MIME_WKSIDX_CONTENT_LOCATION = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_LOCATION); |
| MIME_WKSIDX_CONTENT_MD5 = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_MD5); |
| MIME_WKSIDX_CONTENT_RANGE = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_RANGE); |
| MIME_WKSIDX_CONTENT_TYPE = hdrtoken_wks_to_index(MIME_FIELD_CONTENT_TYPE); |
| MIME_WKSIDX_CONTROL = hdrtoken_wks_to_index(MIME_FIELD_CONTROL); |
| MIME_WKSIDX_COOKIE = hdrtoken_wks_to_index(MIME_FIELD_COOKIE); |
| MIME_WKSIDX_DATE = hdrtoken_wks_to_index(MIME_FIELD_DATE); |
| MIME_WKSIDX_DISTRIBUTION = hdrtoken_wks_to_index(MIME_FIELD_DISTRIBUTION); |
| MIME_WKSIDX_ETAG = hdrtoken_wks_to_index(MIME_FIELD_ETAG); |
| MIME_WKSIDX_EXPECT = hdrtoken_wks_to_index(MIME_FIELD_EXPECT); |
| MIME_WKSIDX_EXPIRES = hdrtoken_wks_to_index(MIME_FIELD_EXPIRES); |
| MIME_WKSIDX_FOLLOWUP_TO = hdrtoken_wks_to_index(MIME_FIELD_FOLLOWUP_TO); |
| MIME_WKSIDX_FROM = hdrtoken_wks_to_index(MIME_FIELD_FROM); |
| MIME_WKSIDX_HOST = hdrtoken_wks_to_index(MIME_FIELD_HOST); |
| MIME_WKSIDX_IF_MATCH = hdrtoken_wks_to_index(MIME_FIELD_IF_MATCH); |
| MIME_WKSIDX_IF_MODIFIED_SINCE = hdrtoken_wks_to_index(MIME_FIELD_IF_MODIFIED_SINCE); |
| MIME_WKSIDX_IF_NONE_MATCH = hdrtoken_wks_to_index(MIME_FIELD_IF_NONE_MATCH); |
| MIME_WKSIDX_IF_RANGE = hdrtoken_wks_to_index(MIME_FIELD_IF_RANGE); |
| MIME_WKSIDX_IF_UNMODIFIED_SINCE = hdrtoken_wks_to_index(MIME_FIELD_IF_UNMODIFIED_SINCE); |
| MIME_WKSIDX_KEEP_ALIVE = hdrtoken_wks_to_index(MIME_FIELD_KEEP_ALIVE); |
| MIME_WKSIDX_KEYWORDS = hdrtoken_wks_to_index(MIME_FIELD_KEYWORDS); |
| MIME_WKSIDX_LAST_MODIFIED = hdrtoken_wks_to_index(MIME_FIELD_LAST_MODIFIED); |
| MIME_WKSIDX_LINES = hdrtoken_wks_to_index(MIME_FIELD_LINES); |
| MIME_WKSIDX_LOCATION = hdrtoken_wks_to_index(MIME_FIELD_LOCATION); |
| MIME_WKSIDX_MAX_FORWARDS = hdrtoken_wks_to_index(MIME_FIELD_MAX_FORWARDS); |
| MIME_WKSIDX_MESSAGE_ID = hdrtoken_wks_to_index(MIME_FIELD_MESSAGE_ID); |
| MIME_WKSIDX_NEWSGROUPS = hdrtoken_wks_to_index(MIME_FIELD_NEWSGROUPS); |
| MIME_WKSIDX_ORGANIZATION = hdrtoken_wks_to_index(MIME_FIELD_ORGANIZATION); |
| MIME_WKSIDX_PATH = hdrtoken_wks_to_index(MIME_FIELD_PATH); |
| MIME_WKSIDX_PRAGMA = hdrtoken_wks_to_index(MIME_FIELD_PRAGMA); |
| MIME_WKSIDX_PROXY_AUTHENTICATE = hdrtoken_wks_to_index(MIME_FIELD_PROXY_AUTHENTICATE); |
| MIME_WKSIDX_PROXY_AUTHORIZATION = hdrtoken_wks_to_index(MIME_FIELD_PROXY_AUTHORIZATION); |
| MIME_WKSIDX_PROXY_CONNECTION = hdrtoken_wks_to_index(MIME_FIELD_PROXY_CONNECTION); |
| MIME_WKSIDX_PUBLIC = hdrtoken_wks_to_index(MIME_FIELD_PUBLIC); |
| MIME_WKSIDX_RANGE = hdrtoken_wks_to_index(MIME_FIELD_RANGE); |
| MIME_WKSIDX_REFERENCES = hdrtoken_wks_to_index(MIME_FIELD_REFERENCES); |
| MIME_WKSIDX_REFERER = hdrtoken_wks_to_index(MIME_FIELD_REFERER); |
| MIME_WKSIDX_REPLY_TO = hdrtoken_wks_to_index(MIME_FIELD_REPLY_TO); |
| MIME_WKSIDX_RETRY_AFTER = hdrtoken_wks_to_index(MIME_FIELD_RETRY_AFTER); |
| MIME_WKSIDX_SENDER = hdrtoken_wks_to_index(MIME_FIELD_SENDER); |
| MIME_WKSIDX_SERVER = hdrtoken_wks_to_index(MIME_FIELD_SERVER); |
| MIME_WKSIDX_SET_COOKIE = hdrtoken_wks_to_index(MIME_FIELD_SET_COOKIE); |
| MIME_WKSIDX_STRICT_TRANSPORT_SECURITY = hdrtoken_wks_to_index(MIME_FIELD_STRICT_TRANSPORT_SECURITY); |
| MIME_WKSIDX_SUBJECT = hdrtoken_wks_to_index(MIME_FIELD_SUBJECT); |
| MIME_WKSIDX_SUMMARY = hdrtoken_wks_to_index(MIME_FIELD_SUMMARY); |
| MIME_WKSIDX_TE = hdrtoken_wks_to_index(MIME_FIELD_TE); |
| MIME_WKSIDX_TRANSFER_ENCODING = hdrtoken_wks_to_index(MIME_FIELD_TRANSFER_ENCODING); |
| MIME_WKSIDX_UPGRADE = hdrtoken_wks_to_index(MIME_FIELD_UPGRADE); |
| MIME_WKSIDX_USER_AGENT = hdrtoken_wks_to_index(MIME_FIELD_USER_AGENT); |
| MIME_WKSIDX_VARY = hdrtoken_wks_to_index(MIME_FIELD_VARY); |
| MIME_WKSIDX_VIA = hdrtoken_wks_to_index(MIME_FIELD_VIA); |
| MIME_WKSIDX_WARNING = hdrtoken_wks_to_index(MIME_FIELD_WARNING); |
| MIME_WKSIDX_WWW_AUTHENTICATE = hdrtoken_wks_to_index(MIME_FIELD_WWW_AUTHENTICATE); |
| MIME_WKSIDX_XREF = hdrtoken_wks_to_index(MIME_FIELD_XREF); |
| MIME_WKSIDX_X_ID = hdrtoken_wks_to_index(MIME_FIELD_X_ID); |
| MIME_WKSIDX_X_FORWARDED_FOR = hdrtoken_wks_to_index(MIME_FIELD_X_FORWARDED_FOR); |
| MIME_WKSIDX_FORWARDED = hdrtoken_wks_to_index(MIME_FIELD_FORWARDED); |
| MIME_WKSIDX_SEC_WEBSOCKET_KEY = hdrtoken_wks_to_index(MIME_FIELD_SEC_WEBSOCKET_KEY); |
| MIME_WKSIDX_SEC_WEBSOCKET_VERSION = hdrtoken_wks_to_index(MIME_FIELD_SEC_WEBSOCKET_VERSION); |
| MIME_WKSIDX_HTTP2_SETTINGS = hdrtoken_wks_to_index(MIME_FIELD_HTTP2_SETTINGS); |
| MIME_WKSIDX_EARLY_DATA = hdrtoken_wks_to_index(MIME_FIELD_EARLY_DATA); |
| |
| MIME_VALUE_BYTES = hdrtoken_string_to_wks("bytes"); |
| MIME_VALUE_CHUNKED = hdrtoken_string_to_wks("chunked"); |
| MIME_VALUE_CLOSE = hdrtoken_string_to_wks("close"); |
| MIME_VALUE_COMPRESS = hdrtoken_string_to_wks("compress"); |
| MIME_VALUE_DEFLATE = hdrtoken_string_to_wks("deflate"); |
| MIME_VALUE_GZIP = hdrtoken_string_to_wks("gzip"); |
| MIME_VALUE_IDENTITY = hdrtoken_string_to_wks("identity"); |
| MIME_VALUE_KEEP_ALIVE = hdrtoken_string_to_wks("keep-alive"); |
| MIME_VALUE_MAX_AGE = hdrtoken_string_to_wks("max-age"); |
| MIME_VALUE_MAX_STALE = hdrtoken_string_to_wks("max-stale"); |
| MIME_VALUE_MIN_FRESH = hdrtoken_string_to_wks("min-fresh"); |
| MIME_VALUE_MUST_REVALIDATE = hdrtoken_string_to_wks("must-revalidate"); |
| MIME_VALUE_NONE = hdrtoken_string_to_wks("none"); |
| MIME_VALUE_NO_CACHE = hdrtoken_string_to_wks("no-cache"); |
| MIME_VALUE_NO_STORE = hdrtoken_string_to_wks("no-store"); |
| MIME_VALUE_NO_TRANSFORM = hdrtoken_string_to_wks("no-transform"); |
| MIME_VALUE_ONLY_IF_CACHED = hdrtoken_string_to_wks("only-if-cached"); |
| MIME_VALUE_PRIVATE = hdrtoken_string_to_wks("private"); |
| MIME_VALUE_PROXY_REVALIDATE = hdrtoken_string_to_wks("proxy-revalidate"); |
| MIME_VALUE_PUBLIC = hdrtoken_string_to_wks("public"); |
| MIME_VALUE_S_MAXAGE = hdrtoken_string_to_wks("s-maxage"); |
| MIME_VALUE_NEED_REVALIDATE_ONCE = hdrtoken_string_to_wks("need-revalidate-once"); |
| MIME_VALUE_WEBSOCKET = hdrtoken_string_to_wks("websocket"); |
| MIME_VALUE_H2C = hdrtoken_string_to_wks(MIME_UPGRADE_H2C_TOKEN); |
| |
| mime_init_date_format_table(); |
| mime_init_cache_control_cooking_masks(); |
| } |
| } |
| |
| void |
| mime_init_cache_control_cooking_masks() |
| { |
| static struct { |
| const char *name; |
| uint32_t mask; |
| } cc_mask_table[] = {{"max-age", MIME_COOKED_MASK_CC_MAX_AGE}, |
| {"no-cache", MIME_COOKED_MASK_CC_NO_CACHE}, |
| {"no-store", MIME_COOKED_MASK_CC_NO_STORE}, |
| {"no-transform", MIME_COOKED_MASK_CC_NO_TRANSFORM}, |
| {"max-stale", MIME_COOKED_MASK_CC_MAX_STALE}, |
| {"min-fresh", MIME_COOKED_MASK_CC_MIN_FRESH}, |
| {"only-if-cached", MIME_COOKED_MASK_CC_ONLY_IF_CACHED}, |
| {"public", MIME_COOKED_MASK_CC_PUBLIC}, |
| {"private", MIME_COOKED_MASK_CC_PRIVATE}, |
| {"must-revalidate", MIME_COOKED_MASK_CC_MUST_REVALIDATE}, |
| {"proxy-revalidate", MIME_COOKED_MASK_CC_PROXY_REVALIDATE}, |
| {"s-maxage", MIME_COOKED_MASK_CC_S_MAXAGE}, |
| {"need-revalidate-once", MIME_COOKED_MASK_CC_NEED_REVALIDATE_ONCE}, |
| {nullptr, 0}}; |
| |
| for (int i = 0; cc_mask_table[i].name != nullptr; i++) { |
| const char *wks = hdrtoken_string_to_wks(cc_mask_table[i].name); |
| HdrTokenHeapPrefix *p = hdrtoken_wks_to_prefix(wks); |
| p->wks_type_specific.u.cache_control.cc_mask = cc_mask_table[i].mask; |
| } |
| } |
| |
| void |
| mime_init_date_format_table() |
| { |
| //////////////////////////////////////////////////////////////// |
| // to speed up the days_since_epoch to m/d/y conversion, we // |
| // use a pre-computed lookup table to support the common case // |
| // of dates that are +/- one year from today --- this code // |
| // builds the lookup table during the first call. // |
| //////////////////////////////////////////////////////////////// |
| |
| time_t now_secs; |
| int i, now_days, first_days, last_days, num_days; |
| int m = 0, d = 0, y = 0; |
| |
| time(&now_secs); |
| now_days = static_cast<int>(now_secs / (60 * 60 * 24)); |
| first_days = now_days - 366; |
| last_days = now_days + 366; |
| num_days = last_days - first_days + 1; |
| |
| _days_to_mdy_fast_lookup_table = static_cast<MDY *>(ats_malloc(num_days * sizeof(MDY))); |
| _days_to_mdy_fast_lookup_table_first_day = first_days; |
| _days_to_mdy_fast_lookup_table_last_day = last_days; |
| |
| for (i = 0; i < num_days; i++) { |
| mime_days_since_epoch_to_mdy_slowcase(first_days + i, &m, &d, &y); |
| _days_to_mdy_fast_lookup_table[i].m = m; |
| _days_to_mdy_fast_lookup_table[i].d = d; |
| _days_to_mdy_fast_lookup_table[i].y = y; |
| } |
| } |
| |
| MIMEHdrImpl * |
| mime_hdr_create(HdrHeap *heap) |
| { |
| MIMEHdrImpl *mh; |
| |
| mh = (MIMEHdrImpl *)heap->allocate_obj(sizeof(MIMEHdrImpl), HDR_HEAP_OBJ_MIME_HEADER); |
| mime_hdr_init(mh); |
| return mh; |
| } |
| |
| void |
| _mime_hdr_field_block_init(MIMEFieldBlockImpl *fblock) |
| { |
| fblock->m_freetop = 0; |
| fblock->m_next = nullptr; |
| |
| #ifdef BLOCK_INIT_PARANOIA |
| int i; |
| |
| // FIX: Could eliminate this initialization loop if we assumed |
| // every slot above the freetop of the block was garbage; |
| // but to be safe, and help debugging, for now we are eating |
| // the cost of initializing all slots in a block. |
| |
| for (i = 0; i < MIME_FIELD_BLOCK_SLOTS; i++) { |
| MIMEField *field = &(fblock->m_field_slots[i]); |
| field->m_readiness = MIME_FIELD_SLOT_READINESS_EMPTY; |
| } |
| #endif |
| } |
| |
| void |
| mime_hdr_cooked_stuff_init(MIMEHdrImpl *mh, MIMEField *changing_field_or_null) |
| { |
| // to be safe, reinitialize unless you know this call is for other cooked field |
| if ((changing_field_or_null == nullptr) || (changing_field_or_null->m_wks_idx != MIME_WKSIDX_PRAGMA)) { |
| mh->m_cooked_stuff.m_cache_control.m_mask = 0; |
| mh->m_cooked_stuff.m_cache_control.m_secs_max_age = 0; |
| mh->m_cooked_stuff.m_cache_control.m_secs_s_maxage = 0; |
| mh->m_cooked_stuff.m_cache_control.m_secs_max_stale = 0; |
| mh->m_cooked_stuff.m_cache_control.m_secs_min_fresh = 0; |
| } |
| if ((changing_field_or_null == nullptr) || (changing_field_or_null->m_wks_idx != MIME_WKSIDX_CACHE_CONTROL)) { |
| mh->m_cooked_stuff.m_pragma.m_no_cache = false; |
| } |
| } |
| |
| void |
| mime_hdr_init(MIMEHdrImpl *mh) |
| { |
| mime_hdr_init_accelerators_and_presence_bits(mh); |
| |
| mime_hdr_cooked_stuff_init(mh, nullptr); |
| |
| // first header is inline: fake an object header for uniformity |
| obj_init_header((HdrHeapObjImpl *)&(mh->m_first_fblock), HDR_HEAP_OBJ_FIELD_BLOCK, sizeof(MIMEFieldBlockImpl), 0); |
| |
| _mime_hdr_field_block_init(&(mh->m_first_fblock)); |
| mh->m_fblock_list_tail = &(mh->m_first_fblock); |
| |
| MIME_HDR_SANITY_CHECK(mh); |
| } |
| |
| MIMEFieldBlockImpl * |
| _mime_field_block_copy(MIMEFieldBlockImpl *s_fblock, HdrHeap * /* s_heap ATS_UNUSED */, HdrHeap *d_heap) |
| { |
| MIMEFieldBlockImpl *d_fblock; |
| |
| d_fblock = (MIMEFieldBlockImpl *)d_heap->allocate_obj(sizeof(MIMEFieldBlockImpl), HDR_HEAP_OBJ_FIELD_BLOCK); |
| memcpy(d_fblock, s_fblock, sizeof(MIMEFieldBlockImpl)); |
| return d_fblock; |
| } |
| |
| void |
| _mime_field_block_destroy(HdrHeap *heap, MIMEFieldBlockImpl *fblock) |
| { |
| heap->deallocate_obj(fblock); |
| } |
| |
| void |
| mime_hdr_destroy_field_block_list(HdrHeap *heap, MIMEFieldBlockImpl *head) |
| { |
| MIMEFieldBlockImpl *next; |
| |
| while (head != nullptr) { |
| next = head->m_next; |
| _mime_field_block_destroy(heap, head); |
| head = next; |
| } |
| } |
| |
| void |
| mime_hdr_destroy(HdrHeap *heap, MIMEHdrImpl *mh) |
| { |
| mime_hdr_destroy_field_block_list(heap, mh->m_first_fblock.m_next); |
| |
| // INKqa11458: if we deallocate mh here and call TSMLocRelease |
| // again, the plugin fails in assert. We leave deallocating to |
| // the plugin using TSMLocRelease |
| |
| // heap->deallocate_obj(mh); |
| } |
| |
| void |
| mime_hdr_copy_onto(MIMEHdrImpl *s_mh, HdrHeap *s_heap, MIMEHdrImpl *d_mh, HdrHeap *d_heap, bool inherit_strs) |
| { |
| int block_count; |
| MIMEFieldBlockImpl *s_fblock, *d_fblock, *prev_d_fblock; |
| |
| // If there are chained field blocks beyond the first one, we're just going to |
| // destroy them. Ideally, we'd use them if the copied in header needed |
| // extra blocks. It's too late in the Tomcat code cycle to implement |
| // reuse. |
| if (d_mh->m_first_fblock.m_next) { |
| mime_hdr_destroy_field_block_list(d_heap, d_mh->m_first_fblock.m_next); |
| } |
| |
| ink_assert(((char *)&(s_mh->m_first_fblock.m_field_slots[MIME_FIELD_BLOCK_SLOTS]) - (char *)s_mh) == sizeof(struct MIMEHdrImpl)); |
| |
| int top = s_mh->m_first_fblock.m_freetop; |
| char *end = reinterpret_cast<char *>(&(s_mh->m_first_fblock.m_field_slots[top])); |
| int bytes_below_top = end - reinterpret_cast<char *>(s_mh); |
| |
| // copies useful part of enclosed first block too |
| memcpy(d_mh, s_mh, bytes_below_top); |
| |
| if (d_mh->m_first_fblock.m_next == nullptr) // common case: no other block |
| { |
| d_mh->m_fblock_list_tail = &(d_mh->m_first_fblock); |
| block_count = 1; |
| } else // uncommon case: block list exists |
| { |
| prev_d_fblock = &(d_mh->m_first_fblock); |
| block_count = 1; |
| for (s_fblock = s_mh->m_first_fblock.m_next; s_fblock != nullptr; s_fblock = s_fblock->m_next) { |
| ++block_count; |
| d_fblock = _mime_field_block_copy(s_fblock, s_heap, d_heap); |
| prev_d_fblock->m_next = d_fblock; |
| prev_d_fblock = d_fblock; |
| } |
| d_mh->m_fblock_list_tail = prev_d_fblock; |
| } |
| |
| if (inherit_strs) { |
| d_heap->inherit_string_heaps(s_heap); |
| } |
| |
| mime_hdr_field_block_list_adjust(block_count, &(s_mh->m_first_fblock), &(d_mh->m_first_fblock)); |
| |
| MIME_HDR_SANITY_CHECK(s_mh); |
| MIME_HDR_SANITY_CHECK(d_mh); |
| } |
| |
| MIMEHdrImpl * |
| mime_hdr_clone(MIMEHdrImpl *s_mh, HdrHeap *s_heap, HdrHeap *d_heap, bool inherit_strs) |
| { |
| MIMEHdrImpl *d_mh; |
| |
| d_mh = mime_hdr_create(d_heap); |
| mime_hdr_copy_onto(s_mh, s_heap, d_mh, d_heap, inherit_strs); |
| return d_mh; |
| } |
| |
| /** Move a pointer from one list to another, keeping the relative offset. |
| * @return A pointer that has the same relative offset to @a dest_base as |
| * @a dest_ptr does to @a src_base. |
| */ |
| static inline MIMEField * |
| rebase(MIMEField *dest_ptr, ///< Original pointer into @src_base memory. |
| void *dest_base, ///< New base pointer. |
| void *src_base ///< Original base pointer. |
| ) |
| { |
| return reinterpret_cast<MIMEField *>(reinterpret_cast<char *>(dest_ptr) + |
| (static_cast<char *>(dest_base) - static_cast<char *>(src_base))); |
| } |
| |
| static inline void |
| relocate(MIMEField *field, MIMEFieldBlockImpl *dest_block, MIMEFieldBlockImpl *src_block) |
| { |
| for (; src_block; src_block = src_block->m_next, dest_block = dest_block->m_next) { |
| ink_release_assert(dest_block); |
| |
| if (field->m_next_dup >= src_block->m_field_slots && field->m_next_dup < src_block->m_field_slots + src_block->m_freetop) { |
| field->m_next_dup = rebase(field->m_next_dup, dest_block->m_field_slots, src_block->m_field_slots); |
| return; |
| } |
| } |
| } |
| |
| void |
| mime_hdr_field_block_list_adjust(int /* block_count ATS_UNUSED */, MIMEFieldBlockImpl *old_list, MIMEFieldBlockImpl *new_list) |
| { |
| for (MIMEFieldBlockImpl *new_blk = new_list; new_blk; new_blk = new_blk->m_next) { |
| for (MIMEField *field = new_blk->m_field_slots, *end = field + new_blk->m_freetop; field != end; ++field) { |
| if (field->is_live() && field->m_next_dup) { |
| relocate(field, new_list, old_list); |
| } |
| } |
| } |
| } |
| |
| int |
| mime_hdr_length_get(MIMEHdrImpl *mh) |
| { |
| unsigned int length, index; |
| MIMEFieldBlockImpl *fblock; |
| MIMEField *field; |
| |
| length = 2; |
| |
| for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
| for (index = 0; index < fblock->m_freetop; index++) { |
| field = &(fblock->m_field_slots[index]); |
| if (field->is_live()) { |
| length += mime_field_length_get(field); |
| } |
| } |
| } |
| |
| return length; |
| } |
| |
| void |
| mime_hdr_fields_clear(HdrHeap *heap, MIMEHdrImpl *mh) |
| { |
| mime_hdr_destroy_field_block_list(heap, mh->m_first_fblock.m_next); |
| mime_hdr_init(mh); |
| } |
| |
| MIMEField * |
| _mime_hdr_field_list_search_by_wks(MIMEHdrImpl *mh, int wks_idx) |
| { |
| MIMEFieldBlockImpl *fblock; |
| MIMEField *field, *too_far_field; |
| |
| ink_assert(hdrtoken_is_valid_wks_idx(wks_idx)); |
| |
| for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
| field = &(fblock->m_field_slots[0]); |
| |
| too_far_field = &(fblock->m_field_slots[fblock->m_freetop]); |
| while (field < too_far_field) { |
| if (field->is_live() && (field->m_wks_idx == wks_idx)) { |
| return field; |
| } |
| ++field; |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| MIMEField * |
| _mime_hdr_field_list_search_by_string(MIMEHdrImpl *mh, const char *field_name_str, int field_name_len) |
| { |
| MIMEFieldBlockImpl *fblock; |
| MIMEField *field, *too_far_field; |
| |
| ink_assert(mh); |
| for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
| field = &(fblock->m_field_slots[0]); |
| |
| too_far_field = &(fblock->m_field_slots[fblock->m_freetop]); |
| while (field < too_far_field) { |
| if (field->is_live() && (field_name_len == field->m_len_name) && |
| (strncasecmp(field->m_ptr_name, field_name_str, field_name_len) == 0)) { |
| return field; |
| } |
| ++field; |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| MIMEField * |
| _mime_hdr_field_list_search_by_slotnum(MIMEHdrImpl *mh, int slotnum) |
| { |
| unsigned int block_num, block_index; |
| MIMEFieldBlockImpl *fblock; |
| |
| if (slotnum < MIME_FIELD_BLOCK_SLOTS) { |
| fblock = &(mh->m_first_fblock); |
| block_index = slotnum; |
| if (block_index >= fblock->m_freetop) { |
| return nullptr; |
| } else { |
| return &(fblock->m_field_slots[block_index]); |
| } |
| } else { |
| block_num = slotnum / MIME_FIELD_BLOCK_SLOTS; |
| block_index = slotnum % MIME_FIELD_BLOCK_SLOTS; |
| |
| fblock = &(mh->m_first_fblock); |
| while (block_num-- && fblock) { |
| fblock = fblock->m_next; |
| } |
| if ((fblock == nullptr) || (block_index >= fblock->m_freetop)) { |
| return nullptr; |
| } else { |
| return &(fblock->m_field_slots[block_index]); |
| } |
| } |
| } |
| |
| MIMEField * |
| mime_hdr_field_find(MIMEHdrImpl *mh, const char *field_name_str, int field_name_len) |
| { |
| HdrTokenHeapPrefix *token_info; |
| const bool is_wks = hdrtoken_is_wks(field_name_str); |
| |
| ink_assert(field_name_len >= 0); |
| |
| //////////////////////////////////////////// |
| // do presence check and slot accelerator // |
| //////////////////////////////////////////// |
| |
| #if TRACK_FIELD_FIND_CALLS |
| Debug("http", "mime_hdr_field_find(hdr 0x%X, field %.*s): is_wks = %d", mh, field_name_len, field_name_str, is_wks); |
| #endif |
| |
| if (is_wks) { |
| token_info = hdrtoken_wks_to_prefix(field_name_str); |
| if ((token_info->wks_info.mask) && ((mh->m_presence_bits & token_info->wks_info.mask) == 0)) { |
| #if TRACK_FIELD_FIND_CALLS |
| Debug("http", "mime_hdr_field_find(hdr 0x%X, field %.*s): MISS (due to presence bits)", mh, field_name_len, field_name_str); |
| #endif |
| return nullptr; |
| } |
| |
| int32_t slot_id = token_info->wks_info.slotid; |
| |
| if (slot_id != MIME_SLOTID_NONE) { |
| uint32_t slotnum = mime_hdr_get_accelerator_slotnum(mh, slot_id); |
| |
| if (slotnum != MIME_FIELD_SLOTNUM_UNKNOWN) { |
| MIMEField *f = _mime_hdr_field_list_search_by_slotnum(mh, slotnum); |
| ink_assert((f == nullptr) || f->is_live()); |
| #if TRACK_FIELD_FIND_CALLS |
| Debug("http", "mime_hdr_field_find(hdr 0x%X, field %.*s): %s (due to slot accelerators)", mh, field_name_len, |
| field_name_str, (f ? "HIT" : "MISS")); |
| #endif |
| return f; |
| } else { |
| #if TRACK_FIELD_FIND_CALLS |
| Debug("http", "mime_hdr_field_find(hdr 0x%X, field %.*s): UNKNOWN (slot too big)", mh, field_name_len, field_name_str); |
| #endif |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // search by well-known string index or by case-insensitive string match // |
| /////////////////////////////////////////////////////////////////////////// |
| |
| MIMEField *f = _mime_hdr_field_list_search_by_wks(mh, token_info->wks_idx); |
| ink_assert((f == nullptr) || f->is_live()); |
| #if TRACK_FIELD_FIND_CALLS |
| Debug("http", "mime_hdr_field_find(hdr 0x%X, field %.*s): %s (due to WKS list walk)", mh, field_name_len, field_name_str, |
| (f ? "HIT" : "MISS")); |
| #endif |
| return f; |
| } else { |
| MIMEField *f = _mime_hdr_field_list_search_by_string(mh, field_name_str, field_name_len); |
| |
| ink_assert((f == nullptr) || f->is_live()); |
| #if TRACK_FIELD_FIND_CALLS |
| Debug("http", "mime_hdr_field_find(hdr 0x%X, field %.*s): %s (due to strcmp list walk)", mh, field_name_len, field_name_str, |
| (f ? "HIT" : "MISS")); |
| #endif |
| return f; |
| } |
| } |
| |
| MIMEField * |
| mime_hdr_field_get(MIMEHdrImpl *mh, int idx) |
| { |
| unsigned int index; |
| MIMEFieldBlockImpl *fblock; |
| MIMEField *field; |
| int got_idx; |
| |
| got_idx = -1; |
| |
| for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
| for (index = 0; index < fblock->m_freetop; index++) { |
| field = &(fblock->m_field_slots[index]); |
| if (field->is_live()) { |
| ++got_idx; |
| } |
| if (got_idx == idx) { |
| return field; |
| } |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| MIMEField * |
| mime_hdr_field_get_slotnum(MIMEHdrImpl *mh, int slotnum) |
| { |
| return _mime_hdr_field_list_search_by_slotnum(mh, slotnum); |
| } |
| |
| int |
| mime_hdr_fields_count(MIMEHdrImpl *mh) |
| { |
| unsigned int index; |
| MIMEFieldBlockImpl *fblock; |
| MIMEField *field; |
| int count; |
| |
| count = 0; |
| |
| for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
| for (index = 0; index < fblock->m_freetop; index++) { |
| field = &(fblock->m_field_slots[index]); |
| if (field->is_live()) { |
| ++count; |
| } |
| } |
| } |
| |
| return count; |
| } |
| |
| void |
| mime_field_init(MIMEField *field) |
| { |
| memset(field, 0, sizeof(MIMEField)); |
| field->m_readiness = MIME_FIELD_SLOT_READINESS_DETACHED; |
| field->m_wks_idx = -1; |
| } |
| |
| MIMEField * |
| mime_field_create(HdrHeap *heap, MIMEHdrImpl *mh) |
| { |
| MIMEField *field; |
| MIMEFieldBlockImpl *tail_fblock, *new_fblock; |
| |
| tail_fblock = mh->m_fblock_list_tail; |
| if (tail_fblock->m_freetop >= MIME_FIELD_BLOCK_SLOTS) { |
| new_fblock = (MIMEFieldBlockImpl *)heap->allocate_obj(sizeof(MIMEFieldBlockImpl), HDR_HEAP_OBJ_FIELD_BLOCK); |
| _mime_hdr_field_block_init(new_fblock); |
| tail_fblock->m_next = new_fblock; |
| tail_fblock = new_fblock; |
| mh->m_fblock_list_tail = new_fblock; |
| } |
| |
| field = &(tail_fblock->m_field_slots[tail_fblock->m_freetop]); |
| ++tail_fblock->m_freetop; |
| |
| mime_field_init(field); |
| |
| return field; |
| } |
| |
| MIMEField * |
| mime_field_create_named(HdrHeap *heap, MIMEHdrImpl *mh, const char *name, int length) |
| { |
| MIMEField *field = mime_field_create(heap, mh); |
| int field_name_wks_idx = hdrtoken_tokenize(name, length); |
| mime_field_name_set(heap, mh, field, field_name_wks_idx, name, length, true); |
| return field; |
| } |
| |
| void |
| mime_hdr_field_attach(MIMEHdrImpl *mh, MIMEField *field, int check_for_dups, MIMEField *prev_dup) |
| { |
| MIME_HDR_SANITY_CHECK(mh); |
| |
| if (!field->is_detached()) { |
| return; |
| } |
| |
| ink_assert(field->m_ptr_name != nullptr); |
| |
| ////////////////////////////////////////////////// |
| // if we don't know the head dup, or are given // |
| // a non-head dup, then search for the head dup // |
| ////////////////////////////////////////////////// |
| |
| if (check_for_dups || (prev_dup && (!prev_dup->is_dup_head()))) { |
| std::string_view name{field->name_get()}; |
| prev_dup = mime_hdr_field_find(mh, name.data(), static_cast<int>(name.size())); |
| ink_assert((prev_dup == nullptr) || (prev_dup->is_dup_head())); |
| } |
| |
| field->m_readiness = MIME_FIELD_SLOT_READINESS_LIVE; |
| |
| //////////////////////////////////////////////////////////////////// |
| // now, attach the new field --- if there are dups, make sure the // |
| // field is patched into the dup list in increasing slot order to // |
| // maintain the invariant that dups are chained in slot order // |
| //////////////////////////////////////////////////////////////////// |
| |
| if (prev_dup) { |
| MIMEField *next_dup; |
| int field_slotnum, prev_slotnum, next_slotnum; |
| |
| ///////////////////////////////////////////////////////////////// |
| // walk down dup list looking for the last dup in slot-order // |
| // before this field object --- meaning a dup before the field // |
| // in slot order who either has no next dup, or whose next dup // |
| // is numerically after the field in slot order. // |
| ///////////////////////////////////////////////////////////////// |
| |
| field_slotnum = mime_hdr_field_slotnum(mh, field); |
| prev_slotnum = mime_hdr_field_slotnum(mh, prev_dup); |
| next_dup = prev_dup->m_next_dup; |
| next_slotnum = (next_dup ? mime_hdr_field_slotnum(mh, next_dup) : -1); |
| |
| ink_assert(field_slotnum != prev_slotnum); |
| |
| while (prev_slotnum < field_slotnum) // break if prev after field |
| { |
| if (next_dup == nullptr) { |
| break; // no next dup, we're done |
| } |
| if (next_slotnum > field_slotnum) { |
| break; // next dup is after us, we're done |
| } |
| prev_dup = next_dup; |
| prev_slotnum = next_slotnum; |
| next_dup = prev_dup->m_next_dup; |
| } |
| |
| ///////////////////////////////////////////////////// |
| // we get here if the prev_slotnum > field_slotnum // |
| // (meaning we're now the first dup in the list), // |
| // or when we've found the correct prev and next // |
| ///////////////////////////////////////////////////// |
| |
| if (prev_slotnum > field_slotnum) // we are now the head |
| { |
| ///////////////////////////////////////////////////////////// |
| // here, it turns out that "prev_dup" is actually after // |
| // "field" in the list of fields --- so, prev_dup is a bit // |
| // of a misnomer, it is actually, the NEXT field! // |
| ///////////////////////////////////////////////////////////// |
| |
| field->m_flags = (field->m_flags | MIME_FIELD_SLOT_FLAGS_DUP_HEAD); |
| field->m_next_dup = prev_dup; |
| prev_dup->m_flags = (prev_dup->m_flags & ~MIME_FIELD_SLOT_FLAGS_DUP_HEAD); |
| mime_hdr_set_accelerators_and_presence_bits(mh, field); |
| } else // patch us after prev, and before next |
| { |
| ink_assert(prev_slotnum < field_slotnum); |
| ink_assert((next_dup == nullptr) || (next_slotnum > field_slotnum)); |
| field->m_flags = (field->m_flags & ~MIME_FIELD_SLOT_FLAGS_DUP_HEAD); |
| ink_assert((next_dup == nullptr) || next_dup->is_live()); |
| prev_dup->m_next_dup = field; |
| field->m_next_dup = next_dup; |
| } |
| } else { |
| field->m_flags = (field->m_flags | MIME_FIELD_SLOT_FLAGS_DUP_HEAD); |
| mime_hdr_set_accelerators_and_presence_bits(mh, field); |
| } |
| |
| // Now keep the cooked cache consistent |
| ink_assert(field->is_live()); |
| if (field->m_ptr_value && field->is_cooked()) { |
| mh->recompute_cooked_stuff(field); |
| } |
| |
| MIME_HDR_SANITY_CHECK(mh); |
| } |
| |
| void |
| mime_hdr_field_detach(MIMEHdrImpl *mh, MIMEField *field, bool detach_all_dups) |
| { |
| ink_assert(mh); |
| MIMEField *next_dup = field->m_next_dup; |
| |
| // If this field is already detached, there's nothing to do. There must |
| // not be a dup list if we detached correctly. |
| if (field->is_detached()) { |
| ink_assert(next_dup == nullptr); |
| return; |
| } |
| |
| ink_assert(field->is_live()); |
| MIME_HDR_SANITY_CHECK(mh); |
| |
| // Normally, this function is called with the current dup list head, |
| // so, we need to update the accelerators after the patch out. But, if |
| // this function is ever called in the middle of a dup list, we need |
| // to walk the list to find the previous dup in the list to patch out |
| // the dup being detached. |
| |
| if (field->m_flags & MIME_FIELD_SLOT_FLAGS_DUP_HEAD) // head of list? |
| { |
| if (!next_dup) // only child |
| { |
| mime_hdr_unset_accelerators_and_presence_bits(mh, field); |
| } else // next guy is dup head |
| { |
| next_dup->m_flags |= MIME_FIELD_SLOT_FLAGS_DUP_HEAD; |
| mime_hdr_set_accelerators_and_presence_bits(mh, next_dup); |
| } |
| } else // need to walk list to find and patch out from predecessor |
| { |
| std::string_view name{field->name_get()}; |
| MIMEField *prev = mime_hdr_field_find(mh, name.data(), static_cast<int>(name.size())); |
| |
| while (prev && (prev->m_next_dup != field)) { |
| prev = prev->m_next_dup; |
| } |
| ink_assert(prev != nullptr); |
| |
| if (prev->m_next_dup == field) { |
| prev->m_next_dup = next_dup; |
| } |
| } |
| |
| // Field is now detached and alone |
| field->m_readiness = MIME_FIELD_SLOT_READINESS_DETACHED; |
| field->m_next_dup = nullptr; |
| |
| // Because we changed the values through detaching,update the cooked cache |
| if (field->is_cooked()) { |
| mh->recompute_cooked_stuff(field); |
| } |
| |
| MIME_HDR_SANITY_CHECK(mh); |
| |
| // At this point, the list should be back to a valid state, either the |
| // next dup detached and the accelerators set to the next dup (if any), |
| // or an interior dup detached and patched around. If we are requested |
| // to delete the whole dup list, we tail-recurse to delete it. |
| |
| if (detach_all_dups && next_dup) { |
| mime_hdr_field_detach(mh, next_dup, detach_all_dups); |
| } |
| } |
| |
| void |
| mime_hdr_field_delete(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, bool delete_all_dups) |
| { |
| if (delete_all_dups) { |
| while (field) { |
| MIMEField *next = field->m_next_dup; |
| mime_hdr_field_delete(heap, mh, field, false); |
| field = next; |
| } |
| } else { |
| heap->free_string(field->m_ptr_name, field->m_len_name); |
| heap->free_string(field->m_ptr_value, field->m_len_value); |
| |
| MIME_HDR_SANITY_CHECK(mh); |
| mime_hdr_field_detach(mh, field, false); |
| |
| MIME_HDR_SANITY_CHECK(mh); |
| mime_field_destroy(mh, field); |
| |
| MIMEFieldBlockImpl *prev_block = nullptr; |
| bool can_destroy_block = true; |
| for (auto fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
| if (prev_block != nullptr) { |
| if (fblock->m_freetop == MIME_FIELD_BLOCK_SLOTS && fblock->contains(field)) { |
| // Check if fields in all slots are deleted |
| for (auto &m_field_slot : fblock->m_field_slots) { |
| if (m_field_slot.m_readiness != MIME_FIELD_SLOT_READINESS_DELETED) { |
| can_destroy_block = false; |
| break; |
| } |
| } |
| // Destroy a block and maintain the chain |
| if (can_destroy_block) { |
| prev_block->m_next = fblock->m_next; |
| _mime_field_block_destroy(heap, fblock); |
| if (prev_block->m_next == nullptr) { |
| mh->m_fblock_list_tail = prev_block; |
| } |
| } |
| break; |
| } |
| } |
| prev_block = fblock; |
| } |
| } |
| |
| MIME_HDR_SANITY_CHECK(mh); |
| } |
| |
| int |
| mime_hdr_field_slotnum(MIMEHdrImpl *mh, MIMEField *field) |
| { |
| int slots_so_far; |
| MIMEFieldBlockImpl *fblock; |
| |
| slots_so_far = 0; |
| for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
| if (fblock->contains(field)) { |
| MIMEField *first = &(fblock->m_field_slots[0]); |
| ptrdiff_t block_slot = field - first; // in units of MIMEField |
| return slots_so_far + block_slot; |
| } |
| slots_so_far += MIME_FIELD_BLOCK_SLOTS; |
| } |
| return -1; |
| } |
| |
| MIMEField * |
| mime_hdr_prepare_for_value_set(HdrHeap *heap, MIMEHdrImpl *mh, const char *name, int name_length) |
| { |
| int wks_idx; |
| MIMEField *field; |
| |
| field = mime_hdr_field_find(mh, name, name_length); |
| |
| ////////////////////////////////////////////////////////////////////// |
| // this function returns with exactly one attached field created, // |
| // ready to have its value set. // |
| // // |
| // on return from field_find, there are 3 possibilities: // |
| // no field found: create attached, named field // |
| // field found w/dups: delete list, create attached, named field // |
| // dupless field found: return the field for mutation // |
| ////////////////////////////////////////////////////////////////////// |
| |
| if (field == nullptr) // no fields of this name |
| { |
| wks_idx = hdrtoken_tokenize(name, name_length); |
| field = mime_field_create(heap, mh); |
| mime_field_name_set(heap, mh, field, wks_idx, name, name_length, true); |
| mime_hdr_field_attach(mh, field, 0, nullptr); |
| |
| } else if (field->m_next_dup) // list of more than 1 field |
| { |
| wks_idx = field->m_wks_idx; |
| mime_hdr_field_delete(heap, mh, field, true); |
| field = mime_field_create(heap, mh); |
| mime_field_name_set(heap, mh, field, wks_idx, name, name_length, true); |
| mime_hdr_field_attach(mh, field, 0, nullptr); |
| } |
| return field; |
| } |
| |
| void |
| mime_field_destroy(MIMEHdrImpl * /* mh ATS_UNUSED */, MIMEField *field) |
| { |
| ink_assert(field->m_readiness == MIME_FIELD_SLOT_READINESS_DETACHED); |
| field->m_readiness = MIME_FIELD_SLOT_READINESS_DELETED; |
| } |
| |
| std::string_view |
| MIMEField::name_get() const |
| { |
| if (m_wks_idx >= 0) { |
| return {hdrtoken_index_to_wks(m_wks_idx), m_len_name}; |
| } |
| return {m_ptr_name, m_len_name}; |
| } |
| |
| void |
| mime_field_name_set(HdrHeap *heap, MIMEHdrImpl * /* mh ATS_UNUSED */, MIMEField *field, int16_t name_wks_idx_or_neg1, |
| const char *name, int length, bool must_copy_string) |
| { |
| ink_assert(field->m_readiness == MIME_FIELD_SLOT_READINESS_DETACHED); |
| |
| field->m_wks_idx = name_wks_idx_or_neg1; |
| mime_str_u16_set(heap, name, length, &(field->m_ptr_name), &(field->m_len_name), must_copy_string); |
| |
| if ((name_wks_idx_or_neg1 == MIME_WKSIDX_CACHE_CONTROL) || (name_wks_idx_or_neg1 == MIME_WKSIDX_PRAGMA)) { |
| field->m_flags |= MIME_FIELD_SLOT_FLAGS_COOKED; |
| } |
| } |
| |
| int |
| MIMEField::value_get_index(const char *value, int length) const |
| { |
| int retval = -1; |
| |
| // if field doesn't support commas and there is just one instance, just compare the value |
| if (!this->supports_commas() && !this->has_dups()) { |
| if (this->m_len_value == static_cast<uint32_t>(length) && strncasecmp(value, this->m_ptr_value, length) == 0) { |
| retval = 0; |
| } |
| } else { |
| HdrCsvIter iter; |
| int tok_len; |
| int index = 0; |
| const char *tok = iter.get_first(this, &tok_len); |
| |
| while (tok) { |
| if (tok_len == length && strncasecmp(tok, value, length) == 0) { |
| retval = index; |
| break; |
| } else { |
| index++; |
| } |
| tok = iter.get_next(&tok_len); |
| } |
| } |
| |
| return retval; |
| } |
| |
| std::string_view |
| MIMEField::value_get() const |
| { |
| return {m_ptr_value, m_len_value}; |
| } |
| |
| int32_t |
| mime_field_value_get_int(const MIMEField *field) |
| { |
| std::string_view value{field->value_get()}; |
| |
| return mime_parse_int(value.data(), value.data() + value.size()); |
| } |
| |
| uint32_t |
| mime_field_value_get_uint(const MIMEField *field) |
| { |
| std::string_view value{field->value_get()}; |
| |
| return mime_parse_uint(value.data(), value.data() + value.size()); |
| } |
| |
| int64_t |
| mime_field_value_get_int64(const MIMEField *field) |
| { |
| std::string_view value{field->value_get()}; |
| |
| return mime_parse_int64(value.data(), value.data() + value.size()); |
| } |
| |
| time_t |
| mime_field_value_get_date(const MIMEField *field) |
| { |
| std::string_view value{field->value_get()}; |
| |
| return mime_parse_date(value.data(), value.data() + value.size()); |
| } |
| |
| const char * |
| mime_field_value_get_comma_val(const MIMEField *field, int *length, int idx) |
| { |
| // some fields (like Date) contain commas but should not be ripped apart |
| if (!field->supports_commas()) { |
| if (idx == 0) { |
| return field->value_get(length); |
| } |
| return nullptr; |
| } else { |
| Str *str; |
| StrList list(false); |
| |
| mime_field_value_get_comma_list(field, &list); |
| str = list.get_idx(idx); |
| if (str != nullptr) { |
| *length = static_cast<int>(str->len); |
| return str->str; |
| } else { |
| *length = 0; |
| return nullptr; |
| } |
| } |
| } |
| |
| int |
| mime_field_value_get_comma_val_count(const MIMEField *field) |
| { |
| // some fields (like Date) contain commas but should not be ripped apart |
| if (!field->supports_commas()) { |
| return ((field->m_len_value == 0) ? 0 : 1); |
| } else { |
| StrList list(false); |
| int count = mime_field_value_get_comma_list(field, &list); |
| return count; |
| } |
| } |
| |
| int |
| mime_field_value_get_comma_list(const MIMEField *field, StrList *list) |
| { |
| std::string_view value{field->value_get()}; |
| |
| // if field doesn't support commas, don't rip apart. |
| if (!field->supports_commas()) { |
| list->append_string(value.data(), static_cast<int>(value.size())); |
| } else { |
| HttpCompat::parse_tok_list(list, 1, value.data(), static_cast<int>(value.size()), ','); |
| } |
| |
| return list->count; |
| } |
| |
| const char * |
| mime_field_value_str_from_strlist(HdrHeap *heap, int *new_str_len_return, StrList *list) |
| { |
| Str *cell; |
| char *new_value, *dest; |
| int i, new_value_len; |
| // This works, because all strings are from the same heap when it is "split" into the list. |
| HdrHeap::HeapGuard guard(heap, list->head->str); |
| |
| new_value_len = 0; |
| |
| // (1) walk the StrList cells, summing each cell's string lengths, |
| // and add 2 bytes for each ", " between cells |
| cell = list->head; |
| for (i = 0; i < list->count; i++) { |
| new_value_len += cell->len; |
| cell = cell->next; |
| } |
| if (list->count > 1) { |
| new_value_len += (2 * (list->count - 1)); |
| } |
| |
| // (2) allocate new heap string |
| new_value = heap->allocate_str(new_value_len); |
| |
| // (3) copy string pieces into new heap string |
| dest = new_value; |
| cell = list->head; |
| for (i = 0; i < list->count; i++) { |
| if (i != 0) { |
| *dest++ = ','; |
| *dest++ = ' '; |
| } |
| memcpy(dest, cell->str, cell->len); |
| dest += cell->len; |
| cell = cell->next; |
| } |
| ink_assert(dest - new_value == new_value_len); |
| |
| *new_str_len_return = new_value_len; |
| return new_value; |
| } |
| |
| void |
| mime_field_value_set_comma_val(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int idx, const char *new_piece_str, |
| int new_piece_len) |
| { |
| int len; |
| Str *cell; |
| StrList list(false); |
| |
| // (1) rip the value into tokens, keeping surrounding quotes, but not whitespace |
| HttpCompat::parse_tok_list(&list, 0, field->m_ptr_value, field->m_len_value, ','); |
| |
| // (2) if desired index isn't valid, then don't change the field |
| if ((idx < 0) || (idx >= list.count)) { |
| return; |
| } |
| |
| // (3) mutate cell idx |
| cell = list.get_idx(idx); |
| ink_assert(cell != nullptr); |
| cell->str = new_piece_str; |
| cell->len = new_piece_len; |
| |
| // (4) reassemble the new string |
| field->m_ptr_value = mime_field_value_str_from_strlist(heap, &len, &list); |
| field->m_len_value = len; |
| |
| // (5) keep stuff fields consistent |
| field->m_n_v_raw_printable = 0; |
| if (field->is_live() && field->is_cooked()) { |
| mh->recompute_cooked_stuff(field); |
| } |
| } |
| |
| void |
| mime_field_value_delete_comma_val(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int idx) |
| { |
| int len; |
| Str *cell; |
| StrList list(false); |
| |
| // (1) rip the value into tokens, keeping surrounding quotes, but not whitespace |
| HttpCompat::parse_tok_list(&list, 0, field->m_ptr_value, field->m_len_value, ','); |
| |
| // (2) if desired index isn't valid, then don't change the field |
| if ((idx < 0) || (idx >= list.count)) { |
| return; |
| } |
| |
| // (3) delete cell idx |
| cell = list.get_idx(idx); |
| list.detach(cell); |
| |
| /**********************************************/ |
| /* Fix for bug INKqa09752 */ |
| /* */ |
| /* If this is the last value */ |
| /* in the field, set the m_ptr_val to NULL */ |
| /**********************************************/ |
| |
| if (list.count == 0) { |
| field->m_ptr_value = nullptr; |
| field->m_len_value = 0; |
| } else { |
| /************************************/ |
| /* End Fix for bug INKqa09752 */ |
| /************************************/ |
| |
| // (4) reassemble the new string |
| field->m_ptr_value = mime_field_value_str_from_strlist(heap, &len, &list); |
| field->m_len_value = len; |
| } |
| |
| // (5) keep stuff fields consistent |
| field->m_n_v_raw_printable = 0; |
| if (field->is_live() && field->is_cooked()) { |
| mh->recompute_cooked_stuff(field); |
| } |
| } |
| |
| void |
| mime_field_value_insert_comma_val(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int idx, const char *new_piece_str, |
| int new_piece_len) |
| { |
| int len; |
| Str *cell, *prev; |
| StrList list(false); |
| |
| // (1) rip the value into tokens, keeping surrounding quotes, but not whitespace |
| HttpCompat::parse_tok_list(&list, 0, field->m_ptr_value, field->m_len_value, ','); |
| |
| // (2) if desired index isn't valid, then don't change the field |
| if (idx < 0) { |
| idx = list.count; |
| } |
| if (idx > list.count) { |
| return; |
| } |
| |
| // (3) create a new cell |
| cell = list.new_cell(new_piece_str, new_piece_len); |
| |
| // (4) patch new cell into list at the right place |
| if (idx == 0) { |
| list.prepend(cell); |
| } else { |
| prev = list.get_idx(idx - 1); |
| list.add_after(prev, cell); |
| } |
| |
| // (5) reassemble the new string |
| field->m_ptr_value = mime_field_value_str_from_strlist(heap, &len, &list); |
| field->m_len_value = len; |
| |
| // (6) keep stuff fields consistent |
| field->m_n_v_raw_printable = 0; |
| if (field->is_live() && field->is_cooked()) { |
| mh->recompute_cooked_stuff(field); |
| } |
| } |
| |
| void |
| mime_field_value_extend_comma_val(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int idx, const char *new_piece_str, |
| int new_piece_len) |
| { |
| Str *cell; |
| StrList list(false); |
| int trimmed, len; |
| size_t extended_len; |
| char *dest, *temp_ptr, temp_buf[128]; |
| |
| // (1) rip the value into tokens, keeping surrounding quotes, but not whitespace |
| HttpCompat::parse_tok_list(&list, 0, field->m_ptr_value, field->m_len_value, ','); |
| |
| // (2) if desired index isn't valid, then don't change the field |
| if ((idx < 0) || (idx >= list.count)) { |
| return; |
| } |
| |
| // (3) get the cell we want to modify |
| cell = list.get_idx(idx); |
| ink_assert(cell != nullptr); |
| |
| // (4) trim quotes if any |
| if ((cell->len >= 2) && (cell->str[0] == '\"') && (cell->str[cell->len - 1] == '\"')) { |
| trimmed = 1; |
| cell->str += 1; |
| cell->len -= 2; |
| } else { |
| trimmed = 0; |
| } |
| |
| // (5) compute length of extended token |
| extended_len = cell->len + new_piece_len + (trimmed ? 2 : 0); |
| |
| // (6) allocate temporary space to construct new value |
| if (extended_len <= sizeof(temp_buf)) { |
| temp_ptr = temp_buf; |
| } else { |
| temp_ptr = static_cast<char *>(ats_malloc(extended_len)); |
| } |
| |
| // (7) construct new extended token |
| dest = temp_ptr; |
| if (trimmed) { |
| *dest++ = '\"'; |
| } |
| memcpy(dest, cell->str, cell->len); |
| dest += cell->len; |
| memcpy(dest, new_piece_str, new_piece_len); |
| dest += new_piece_len; |
| if (trimmed) { |
| *dest++ = '\"'; |
| } |
| ink_assert((size_t)(dest - temp_ptr) == extended_len); |
| |
| // (8) assign the new token to the cell |
| cell->str = temp_ptr; |
| cell->len = extended_len; |
| |
| // (9) reassemble the new string |
| field->m_ptr_value = mime_field_value_str_from_strlist(heap, &len, &list); |
| field->m_len_value = len; |
| |
| // (10) keep stuff fields consistent |
| field->m_n_v_raw_printable = 0; |
| if (field->is_live() && field->is_cooked()) { |
| mh->recompute_cooked_stuff(field); |
| } |
| |
| // (11) free up any temporary storage |
| if (extended_len > sizeof(temp_buf)) { |
| ats_free(temp_ptr); |
| } |
| } |
| |
| void |
| mime_field_value_set(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, const char *value, int length, bool must_copy_string) |
| { |
| heap->free_string(field->m_ptr_value, field->m_len_value); |
| |
| if (must_copy_string && value) { |
| field->m_ptr_value = heap->duplicate_str(value, length); |
| } else { |
| field->m_ptr_value = value; |
| } |
| |
| field->m_len_value = length; |
| field->m_n_v_raw_printable = 0; |
| |
| // Now keep the cooked cache consistent |
| if (field->is_live() && field->is_cooked()) { |
| mh->recompute_cooked_stuff(field); |
| } |
| } |
| |
| void |
| mime_field_value_set_int(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int32_t value) |
| { |
| char buf[16]; |
| int len = mime_format_int(buf, value, sizeof(buf)); |
| mime_field_value_set(heap, mh, field, buf, len, true); |
| } |
| |
| void |
| mime_field_value_set_uint(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, uint32_t value) |
| { |
| char buf[16]; |
| int len = mime_format_uint(buf, value, sizeof(buf)); |
| mime_field_value_set(heap, mh, field, buf, len, true); |
| } |
| |
| void |
| mime_field_value_set_int64(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int64_t value) |
| { |
| char buf[20]; |
| int len = mime_format_int64(buf, value, sizeof(buf)); |
| mime_field_value_set(heap, mh, field, buf, len, true); |
| } |
| |
| void |
| mime_field_value_set_date(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, time_t value) |
| { |
| char buf[33]; |
| int len = mime_format_date(buf, value); |
| mime_field_value_set(heap, mh, field, buf, len, true); |
| } |
| |
| void |
| mime_field_name_value_set(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, int16_t name_wks_idx_or_neg1, const char *name, |
| int name_length, const char *value, int value_length, int n_v_raw_printable, int n_v_raw_length, |
| bool must_copy_strings) |
| { |
| unsigned int n_v_raw_pad = n_v_raw_length - (name_length + value_length); |
| |
| ink_assert(field->m_readiness == MIME_FIELD_SLOT_READINESS_DETACHED); |
| |
| if (must_copy_strings) { |
| mime_field_name_set(heap, mh, field, name_wks_idx_or_neg1, name, name_length, true); |
| mime_field_value_set(heap, mh, field, value, value_length, true); |
| } else { |
| field->m_wks_idx = name_wks_idx_or_neg1; |
| field->m_ptr_name = name; |
| field->m_ptr_value = value; |
| field->m_len_name = name_length; |
| field->m_len_value = value_length; |
| if (n_v_raw_printable && (n_v_raw_pad <= 7)) { |
| field->m_n_v_raw_printable = n_v_raw_printable; |
| field->m_n_v_raw_printable_pad = n_v_raw_pad; |
| } else { |
| field->m_n_v_raw_printable = 0; |
| } |
| |
| // Now keep the cooked cache consistent |
| if ((name_wks_idx_or_neg1 == MIME_WKSIDX_CACHE_CONTROL) || (name_wks_idx_or_neg1 == MIME_WKSIDX_PRAGMA)) { |
| field->m_flags |= MIME_FIELD_SLOT_FLAGS_COOKED; |
| } |
| if (field->is_live() && field->is_cooked()) { |
| mh->recompute_cooked_stuff(field); |
| } |
| } |
| } |
| |
| void |
| mime_field_value_append(HdrHeap *heap, MIMEHdrImpl *mh, MIMEField *field, const char *value, int length, bool prepend_comma, |
| const char separator) |
| { |
| int new_length = field->m_len_value + length; |
| if (prepend_comma && field->m_len_value) { |
| new_length += 2; |
| } |
| |
| // Start by trying expand the string we already have |
| char *new_str = heap->expand_str(field->m_ptr_value, field->m_len_value, new_length); |
| |
| if (new_str == nullptr) { |
| // Expansion failed. Create a new string and copy over the value contents |
| new_str = heap->allocate_str(new_length); |
| memcpy(new_str, field->m_ptr_value, field->m_len_value); |
| } |
| |
| char *ptr = new_str + field->m_len_value; |
| if (prepend_comma && field->m_len_value) { |
| *ptr++ = separator; |
| *ptr++ = ' '; |
| } |
| |
| memcpy(ptr, value, length); |
| |
| field->m_ptr_value = new_str; |
| field->m_len_value = new_length; |
| field->m_n_v_raw_printable = 0; |
| |
| // Now keep the cooked cache consistent |
| if (field->is_live() && field->is_cooked()) { |
| mh->recompute_cooked_stuff(field); |
| } |
| } |
| |
| MIMEField * |
| MIMEHdr::get_host_port_values(const char **host_ptr, ///< Pointer to host. |
| int *host_len, ///< Length of host. |
| const char **port_ptr, ///< Pointer to port. |
| int *port_len) |
| { |
| MIMEField *field = this->field_find(MIME_FIELD_HOST, MIME_LEN_HOST); |
| if (host_ptr) { |
| *host_ptr = nullptr; |
| } |
| if (host_len) { |
| *host_len = 0; |
| } |
| if (port_ptr) { |
| *port_ptr = nullptr; |
| } |
| if (port_len) { |
| *port_len = 0; |
| } |
| |
| if (field) { |
| ts::TextView b{field->m_ptr_value, static_cast<size_t>(field->m_len_value)}; |
| ts::TextView host, port; |
| |
| if (b) { |
| if ('[' == *b) { |
| auto idx = b.find(']'); |
| if (idx <= b.size() && b[idx + 1] == ':') { |
| host = b.take_prefix_at(idx + 1); |
| port = b; |
| } else { |
| host = b; |
| } |
| } else { |
| auto x = b.split_prefix_at(':'); |
| if (x) { |
| host = x; |
| port = b; |
| } else { |
| host = b; |
| } |
| } |
| |
| if (host) { |
| if (host_ptr) { |
| *host_ptr = host.data(); |
| } |
| if (host_len) { |
| *host_len = static_cast<int>(host.size()); |
| } |
| } |
| if (port) { |
| if (port_ptr) { |
| *port_ptr = port.data(); |
| } |
| if (port_len) { |
| *port_len = static_cast<int>(port.size()); |
| } |
| } |
| } else { |
| field = nullptr; // no value in field, signal fail. |
| } |
| } |
| return field; |
| } |
| |
| /*********************************************************************** |
| * * |
| * P A R S E R * |
| * * |
| ***********************************************************************/ |
| |
| void |
| MIMEScanner::init() |
| { |
| m_state = INITIAL_PARSE_STATE; |
| // Ugly, but required because of how proxy allocation works - that leaves the instance in a |
| // random state, so even assigning to it can crash. Because this method substitutes for a real |
| // constructor in the proxy allocation system, call the CTOR here. Any memory that gets allocated |
| // is supposed to be cleaned up by calling @c clear on this object. |
| new (&m_line) std::string; |
| } |
| |
| MIMEScanner & |
| MIMEScanner::append(TextView text) |
| { |
| m_line += text; |
| return *this; |
| } |
| |
| ParseResult |
| MIMEScanner::get(TextView &input, TextView &output, bool &output_shares_input, bool eof_p, ScanType scan_type) |
| { |
| ParseResult zret = PARSE_RESULT_CONT; |
| // Need this for handling dangling CR. |
| static const char RAW_CR{ParseRules::CHAR_CR}; |
| |
| auto text = input; |
| while (PARSE_RESULT_CONT == zret && !text.empty()) { |
| switch (m_state) { |
| case MIME_PARSE_BEFORE: // waiting to find a field. |
| m_line.resize(0); // any caller should already be done with the buffer |
| if (ParseRules::is_cr(*text)) { |
| ++text; |
| if (!text.empty() && ParseRules::is_lf(*text)) { |
| // optimize a bit - this happens >99% of the time after a CR. |
| ++text; |
| zret = PARSE_RESULT_DONE; |
| } else { |
| m_state = MIME_PARSE_FOUND_CR; |
| } |
| } else if (ParseRules::is_lf(*text)) { |
| ++text; |
| zret = PARSE_RESULT_DONE; // Required by regression test. |
| } else { |
| // consume this character in the next state. |
| m_state = MIME_PARSE_INSIDE; |
| } |
| break; |
| case MIME_PARSE_FOUND_CR: |
| // Looking for a field and found a CR, which should mean terminating the header. |
| if (ParseRules::is_lf(*text)) { |
| ++text; |
| zret = PARSE_RESULT_DONE; |
| } else { |
| // This really should be an error (spec doesn't permit lone CR) but the regression tests |
| // require it. |
| 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 |
| m_state = MIME_PARSE_INSIDE; |
| } |
| break; |
| case MIME_PARSE_INSIDE: { |
| auto lf_off = text.find(ParseRules::CHAR_LF); |
| if (lf_off != TextView::npos) { |
| text.remove_prefix(lf_off + 1); // drop up to and including LF |
| if (LINE == scan_type) { |
| zret = PARSE_RESULT_OK; |
| m_state = MIME_PARSE_BEFORE; |
| } else { |
| m_state = MIME_PARSE_AFTER; // looking for line folding. |
| } |
| } else { // no EOL, consume all text without changing state. |
| text.remove_prefix(text.size()); |
| } |
| } break; |
| case MIME_PARSE_AFTER: |
| // After a LF, the next line might be a continuation / folded line. That's indicated by a |
| // starting whitespace. If that's the case, back up over the preceding CR/LF with space and |
| // pretend it's the same line. |
| if (ParseRules::is_ws(*text)) { // folded line. |
| char *unfold = const_cast<char *>(text.data() - 1); |
| *unfold-- = ' '; |
| if (ParseRules::is_cr(*unfold)) { |
| *unfold = ' '; |
| } |
| m_state = MIME_PARSE_INSIDE; // back inside the field. |
| } else { |
| m_state = MIME_PARSE_BEFORE; // field terminated. |
| zret = PARSE_RESULT_OK; |
| } |
| break; |
| } |
| } |
| |
| TextView parsed_text{input.data(), text.data()}; |
| bool save_parsed_text_p = !parsed_text.empty(); |
| |
| if (PARSE_RESULT_CONT == zret) { |
| // data ran out before we got a clear final result. There a number of things we need to check |
| // and possibly adjust that result. It's less complex to do this cleanup than handle all of |
| // these checks in the parser state machine. |
| if (eof_p) { |
| // Should never return PARSE_CONT if we've hit EOF. |
| if (parsed_text.empty()) { |
| // all input previously consumed. If we're between fields, that's cool. |
| if (MIME_PARSE_INSIDE != m_state) { |
| m_state = MIME_PARSE_BEFORE; // probably not needed... |
| zret = PARSE_RESULT_DONE; |
| } else { |
| zret = PARSE_RESULT_ERROR; // unterminated field. |
| } |
| } else if (MIME_PARSE_AFTER == m_state) { |
| // Special case it seems - need to accept the final field even if there's no header |
| // terminating CR LF. This is only reasonable after absolute end of input (EOF) because |
| // otherwise this might be a multiline field where we haven't seen the next leading space. |
| m_state = MIME_PARSE_BEFORE; |
| zret = PARSE_RESULT_OK; |
| } else { |
| // Partial input, no field / line CR LF |
| zret = PARSE_RESULT_ERROR; // Unterminated field. |
| } |
| } else if (!parsed_text.empty()) { |
| if (MIME_PARSE_INSIDE == m_state) { |
| // Inside a field but more data is expected. Save what we've got. |
| this->append(parsed_text); // Do this here to force appending. |
| save_parsed_text_p = false; // don't double append. |
| } else if (MIME_PARSE_AFTER == m_state) { |
| // After a field but we still have data. Need to parse it too. |
| m_state = MIME_PARSE_BEFORE; |
| zret = PARSE_RESULT_OK; |
| } |
| } |
| } |
| |
| if (save_parsed_text_p && !m_line.empty()) { |
| // If we're already accumulating, continue to do so if we have data. |
| this->append(parsed_text); |
| } |
| |
| // adjust out arguments. |
| output_shares_input = true; |
| if (PARSE_RESULT_CONT != zret) { |
| if (!m_line.empty()) { |
| output = m_line; // cleared when called with state MIME_PARSE_BEFORE |
| output_shares_input = false; |
| } else { |
| output = parsed_text; |
| } |
| } |
| |
| // Make sure there are no null characters in the input scanned so far |
| if (zret != PARSE_RESULT_ERROR && TextView::npos != parsed_text.find('\0')) { |
| zret = PARSE_RESULT_ERROR; |
| } |
| |
| input.remove_prefix(parsed_text.size()); |
| return zret; |
| } |
| |
| void |
| _mime_parser_init(MIMEParser *parser) |
| { |
| parser->m_field = 0; |
| parser->m_field_flags = 0; |
| parser->m_value = -1; |
| } |
| ////////////////////////////////////////////////////// |
| // init first time structure setup // |
| // clear resets an already-initialized structure // |
| ////////////////////////////////////////////////////// |
| void |
| mime_parser_init(MIMEParser *parser) |
| { |
| parser->m_scanner.init(); |
| _mime_parser_init(parser); |
| } |
| |
| void |
| mime_parser_clear(MIMEParser *parser) |
| { |
| parser->m_scanner.clear(); |
| _mime_parser_init(parser); |
| } |
| |
| ParseResult |
| mime_parser_parse(MIMEParser *parser, HdrHeap *heap, MIMEHdrImpl *mh, const char **real_s, const char *real_e, |
| bool must_copy_strings, bool eof, bool remove_ws_from_field_name, size_t max_hdr_field_size) |
| { |
| ParseResult err; |
| bool line_is_real; |
| |
| MIMEScanner *scanner = &parser->m_scanner; |
| |
| while (true) { |
| //////////////////////////////////////////////////////////////////////////// |
| // get a name:value line, with all continuation lines glued into one line // |
| //////////////////////////////////////////////////////////////////////////// |
| |
| TextView text{*real_s, real_e}; |
| TextView parsed; |
| err = scanner->get(text, parsed, line_is_real, eof, MIMEScanner::FIELD); |
| *real_s = text.data(); |
| if (err != PARSE_RESULT_OK) { |
| return err; |
| } |
| |
| ////////////////////////////////////////////////// |
| // if got a LF or CR on its own, end the header // |
| ////////////////////////////////////////////////// |
| |
| if ((parsed.size() >= 2) && (parsed[0] == ParseRules::CHAR_CR) && (parsed[1] == ParseRules::CHAR_LF)) { |
| return PARSE_RESULT_DONE; |
| } |
| |
| if ((parsed.size() >= 1) && (parsed[0] == ParseRules::CHAR_LF)) { |
| return PARSE_RESULT_DONE; |
| } |
| |
| ///////////////////////////////////////////// |
| // find pointers into the name:value field // |
| ///////////////////////////////////////////// |
| |
| /** |
| * Fix for INKqa09141. The is_token function fails for '@' character. |
| * Header names starting with '@' signs are valid headers. Hence we |
| * have to add one more check to see if the first parameter is '@' |
| * character then, the header name is valid. |
| **/ |
| if ((!ParseRules::is_token(*parsed)) && (*parsed != '@')) { |
| continue; // toss away garbage line |
| } |
| |
| // find name last |
| auto field_value = parsed; // need parsed as is later on. |
| auto field_name = field_value.split_prefix_at(':'); |
| if (field_name.empty()) { |
| continue; // toss away garbage line |
| } |
| |
| // RFC7230 section 3.2.4: |
| // No whitespace is allowed between the header field-name and colon. In |
| // the past, differences in the handling of such whitespace have led to |
| // security vulnerabilities in request routing and response handling. A |
| // server MUST reject any received request message that contains |
| // whitespace between a header field-name and colon with a response code |
| // of 400 (Bad Request). |
| // A proxy MUST remove any such whitespace from a response message before |
| // forwarding the message downstream. |
| bool raw_print_field = true; |
| if (is_ws(field_name.back())) { |
| if (!remove_ws_from_field_name) { |
| return PARSE_RESULT_ERROR; |
| } |
| field_name.rtrim_if(&ParseRules::is_ws); |
| raw_print_field = false; |
| } |
| |
| // find value first |
| field_value.ltrim_if(&ParseRules::is_ws); |
| field_value.rtrim_if(&ParseRules::is_wslfcr); |
| |
| // Make sure the name + value is not longer than configured max_hdr_field_size |
| if (field_name.size() + field_value.size() > max_hdr_field_size) { |
| return PARSE_RESULT_ERROR; |
| } |
| |
| // int total_line_length = (int)(field_line_last - field_line_first + 1); |
| |
| ////////////////////////////////////////////////////////////////////// |
| // if we can't leave the name & value in the real buffer, copy them // |
| ////////////////////////////////////////////////////////////////////// |
| |
| if (must_copy_strings || (!line_is_real)) { |
| char *dup = heap->duplicate_str(parsed.data(), parsed.size()); |
| ptrdiff_t delta = dup - parsed.data(); |
| field_name.assign(field_name.data() + delta, field_name.size()); |
| field_value.assign(field_value.data() + delta, field_value.size()); |
| } |
| /////////////////////// |
| // tokenize the name // |
| /////////////////////// |
| |
| int field_name_wks_idx = hdrtoken_tokenize(field_name.data(), field_name.size()); |
| |
| /////////////////////////////////////////// |
| // build and insert the new field object // |
| /////////////////////////////////////////// |
| |
| MIMEField *field = mime_field_create(heap, mh); |
| mime_field_name_value_set(heap, mh, field, field_name_wks_idx, field_name.data(), field_name.size(), field_value.data(), |
| field_value.size(), raw_print_field, parsed.size(), false); |
| mime_hdr_field_attach(mh, field, 1, nullptr); |
| } |
| } |
| |
| void |
| mime_hdr_describe(HdrHeapObjImpl *raw, bool recurse) |
| { |
| MIMEFieldBlockImpl *fblock; |
| MIMEHdrImpl *obj = (MIMEHdrImpl *)raw; |
| |
| Debug("http", "\t[PBITS: 0x%08X%08X, SLACC: 0x%04X%04X%04X%04X, HEADBLK: %p, TAILBLK: %p]", |
| (uint32_t)((obj->m_presence_bits >> 32) & (TOK_64_CONST(0xFFFFFFFF))), |
| (uint32_t)((obj->m_presence_bits >> 0) & (TOK_64_CONST(0xFFFFFFFF))), obj->m_slot_accelerators[0], |
| obj->m_slot_accelerators[1], obj->m_slot_accelerators[2], obj->m_slot_accelerators[3], &(obj->m_first_fblock), |
| obj->m_fblock_list_tail); |
| |
| Debug("http", "\t[CBITS: 0x%08X, T_MAXAGE: %d, T_SMAXAGE: %d, T_MAXSTALE: %d, T_MINFRESH: %d, PNO$: %d]", |
| obj->m_cooked_stuff.m_cache_control.m_mask, obj->m_cooked_stuff.m_cache_control.m_secs_max_age, |
| obj->m_cooked_stuff.m_cache_control.m_secs_s_maxage, obj->m_cooked_stuff.m_cache_control.m_secs_max_stale, |
| obj->m_cooked_stuff.m_cache_control.m_secs_min_fresh, obj->m_cooked_stuff.m_pragma.m_no_cache); |
| for (fblock = &(obj->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
| if (recurse || (fblock == &(obj->m_first_fblock))) { |
| obj_describe((HdrHeapObjImpl *)fblock, recurse); |
| } |
| } |
| } |
| |
| void |
| mime_field_block_describe(HdrHeapObjImpl *raw, bool /* recurse ATS_UNUSED */) |
| { |
| unsigned int i; |
| static const char *readiness_names[] = {"EMPTY", "DETACHED", "LIVE", "DELETED"}; |
| |
| MIMEFieldBlockImpl *obj = (MIMEFieldBlockImpl *)raw; |
| |
| Debug("http", "[FREETOP: %d, NEXTBLK: %p]", obj->m_freetop, obj->m_next); |
| |
| for (i = 0; i < obj->m_freetop; i++) { |
| MIMEField *f = &(obj->m_field_slots[i]); |
| Debug("http", "\tSLOT #%2d (%p), %-8s", i, f, readiness_names[f->m_readiness]); |
| |
| switch (f->m_readiness) { |
| case MIME_FIELD_SLOT_READINESS_EMPTY: |
| break; |
| case MIME_FIELD_SLOT_READINESS_DETACHED: |
| case MIME_FIELD_SLOT_READINESS_LIVE: |
| case MIME_FIELD_SLOT_READINESS_DELETED: |
| Debug("http", "[N: \"%.*s\", N_LEN: %d, N_IDX: %d, ", f->m_len_name, (f->m_ptr_name ? f->m_ptr_name : "NULL"), f->m_len_name, |
| f->m_wks_idx); |
| Debug("http", "V: \"%.*s\", V_LEN: %d, ", f->m_len_value, (f->m_ptr_value ? f->m_ptr_value : "NULL"), f->m_len_value); |
| Debug("http", "NEXTDUP: %p, RAW: %d, RAWLEN: %d, F: %d]", f->m_next_dup, f->m_n_v_raw_printable, |
| f->m_len_name + f->m_len_value + f->m_n_v_raw_printable_pad, f->m_flags); |
| break; |
| } |
| Debug("http", "\n"); |
| } |
| } |
| |
| int |
| mime_hdr_print(HdrHeap * /* heap ATS_UNUSED */, MIMEHdrImpl *mh, char *buf_start, int buf_length, int *buf_index_inout, |
| int *buf_chars_to_skip_inout) |
| { |
| MIMEFieldBlockImpl *fblock; |
| MIMEField *field; |
| uint32_t index; |
| |
| #define SIMPLE_MIME_HDR_PRINT |
| #ifdef SIMPLE_MIME_HDR_PRINT |
| for (fblock = &(mh->m_first_fblock); fblock != nullptr; fblock = fblock->m_next) { |
| for (index = 0; index < fblock->m_freetop; index++) { |
| field = &(fblock->m_field_slots[index]); |
| if (field->is_live()) { |
| if (!mime_field_print(field, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout)) { |
| return 0; |
| } |
| } |
| } |
| } |
| #else |
| // FIX: if not raw_printable, need to print with mime_field_print, |
| // not mime_mem_print |
| for (fblock = &(mh->m_first_fblock); fblock != NULL; fblock = fblock->m_next) { |
| const char *contig_start = NULL; |
| int this_length, contig_length = 0; |
| for (index = 0; index < fblock->m_freetop; index++) { |
| field = &(fblock->m_field_slots[index]); |
| this_length = field->m_len_name + field->m_len_value + field->m_n_v_raw_printable_pad; |
| if (field->is_live()) { |
| if ((field->m_ptr_name == contig_start + contig_length) && field->m_n_v_raw_printable && |
| ((buf_index_inout == NULL) || (contig_length + this_length <= buf_length - *buf_index_inout))) { |
| contig_length += this_length; |
| } else { |
| if (contig_length > 0) { |
| if (!mime_mem_print(contig_start, contig_length, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout)) |
| return 0; |
| } |
| contig_start = field->m_ptr_name; |
| contig_length = this_length; |
| } |
| } |
| } |
| |
| if (contig_length > 0) { |
| if (!mime_mem_print(contig_start, contig_length, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout)) |
| return 0; |
| } |
| } |
| #endif |
| |
| if (!mime_mem_print("\r\n", 2, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout)) { |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| namespace |
| { |
| int |
| mime_mem_print_(const char *src_d, int src_l, char *buf_start, int buf_length, int *buf_index_inout, int *buf_chars_to_skip_inout, |
| int (*char_transform)(int char_in)) |
| { |
| if (buf_start == nullptr) { // this case should only be used by test_header |
| ink_release_assert(buf_index_inout == nullptr); |
| ink_release_assert(buf_chars_to_skip_inout == nullptr); |
| while (src_l--) { |
| putchar(*src_d++); |
| } |
| return 1; |
| } |
| |
| ink_assert(src_d != nullptr); |
| |
| if (*buf_chars_to_skip_inout > 0) { |
| if (*buf_chars_to_skip_inout >= src_l) { |
| *buf_chars_to_skip_inout -= src_l; |
| return 1; |
| } else { |
| src_l -= *buf_chars_to_skip_inout; |
| src_d += *buf_chars_to_skip_inout; |
| *buf_chars_to_skip_inout = 0; |
| } |
| } |
| |
| int copy_l = std::min(buf_length - *buf_index_inout, src_l); |
| if (copy_l > 0) { |
| buf_start += *buf_index_inout; |
| std::transform(src_d, src_d + copy_l, buf_start, char_transform); |
| *buf_index_inout += copy_l; |
| } |
| return (src_l == copy_l); |
| } |
| |
| int |
| to_same_char(int ch) |
| { |
| return ch; |
| } |
| |
| } // end anonymous namespace |
| |
| int |
| mime_mem_print(const char *src_d, int src_l, char *buf_start, int buf_length, int *buf_index_inout, int *buf_chars_to_skip_inout) |
| { |
| return mime_mem_print_(src_d, src_l, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout, to_same_char); |
| } |
| |
| int |
| mime_mem_print_lc(const char *src_d, int src_l, char *buf_start, int buf_length, int *buf_index_inout, int *buf_chars_to_skip_inout) |
| { |
| return mime_mem_print_(src_d, src_l, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout, std::tolower); |
| } |
| |
| int |
| mime_field_print(MIMEField *field, char *buf_start, int buf_length, int *buf_index_inout, int *buf_chars_to_skip_inout) |
| { |
| #define TRY(x) \ |
| if (!x) \ |
| return 0 |
| |
| int total_len; |
| |
| // Don't print names that begin with an '@'. |
| if (field->m_ptr_name[0] == '@') { |
| return 1; |
| } |
| |
| if (field->m_n_v_raw_printable) { |
| total_len = field->m_len_name + field->m_len_value + field->m_n_v_raw_printable_pad; |
| |
| if ((buf_start != nullptr) && (*buf_chars_to_skip_inout == 0) && (total_len <= (buf_length - *buf_index_inout))) { |
| buf_start += *buf_index_inout; |
| memcpy(buf_start, field->m_ptr_name, total_len); |
| *buf_index_inout += total_len; |
| |
| } else { |
| TRY(mime_mem_print(field->m_ptr_name, total_len, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout)); |
| } |
| } else { |
| total_len = field->m_len_name + field->m_len_value + 2 + 2; |
| |
| // try to handle on fast path |
| |
| if ((buf_start != nullptr) && (*buf_chars_to_skip_inout == 0) && (total_len <= (buf_length - *buf_index_inout))) { |
| buf_start += *buf_index_inout; |
| |
| memcpy(buf_start, field->m_ptr_name, field->m_len_name); |
| buf_start += field->m_len_name; |
| |
| buf_start[0] = ':'; |
| buf_start[1] = ' '; |
| buf_start += 2; |
| |
| memcpy(buf_start, field->m_ptr_value, field->m_len_value); |
| buf_start += field->m_len_value; |
| |
| buf_start[0] = '\r'; |
| buf_start[1] = '\n'; |
| |
| *buf_index_inout += total_len; |
| } else { |
| TRY(mime_mem_print(field->m_ptr_name, field->m_len_name, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout)); |
| TRY(mime_mem_print(": ", 2, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout)); |
| TRY(mime_mem_print(field->m_ptr_value, field->m_len_value, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout)); |
| TRY(mime_mem_print("\r\n", 2, buf_start, buf_length, buf_index_inout, buf_chars_to_skip_inout)); |
| } |
| } |
| |
| return 1; |
| |
| #undef TRY |
| } |
| |
| const char * |
| mime_str_u16_set(HdrHeap *heap, const char *s_str, int s_len, const char **d_str, uint16_t *d_len, bool must_copy) |
| { |
| ink_assert(s_len >= 0 && s_len < UINT16_MAX); |
| // INKqa08287 - keep track of free string space. |
| // INVARIENT: passed in result pointers must be to |
| // either NULL or be valid ptr for a string already |
| // the string heaps |
| heap->free_string(*d_str, *d_len); |
| |
| if (must_copy && s_str) { |
| s_str = heap->duplicate_str(s_str, s_len); |
| } |
| *d_str = s_str; |
| *d_len = s_len; |
| return s_str; |
| } |
| |
| int |
| mime_field_length_get(MIMEField *field) |
| { |
| if (field->m_n_v_raw_printable) { |
| return (field->m_len_name + field->m_len_value + field->m_n_v_raw_printable_pad); |
| } else { |
| return (field->m_len_name + field->m_len_value + 4); // add ": \r\n" |
| } |
| } |
| |
| int |
| mime_format_int(char *buf, int32_t val, size_t buf_len) |
| { |
| return ink_fast_itoa(val, buf, buf_len); |
| } |
| |
| int |
| mime_format_uint(char *buf, uint32_t val, size_t buf_len) |
| { |
| return ink_fast_uitoa(val, buf, buf_len); |
| } |
| |
| int |
| mime_format_int64(char *buf, int64_t val, size_t buf_len) |
| { |
| return ink_fast_ltoa(val, buf, buf_len); |
| } |
| |
| void |
| mime_days_since_epoch_to_mdy_slowcase(unsigned int days_since_jan_1_1970, int *m_return, int *d_return, int *y_return) |
| { |
| static const int DAYS_OFFSET = 25508; |
| |
| static const char months[] = { |
| 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, |
| 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, |
| 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, |
| 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, |
| 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, |
| 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, |
| 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, |
| 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, |
| 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, |
| 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, |
| 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, |
| 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}; |
| |
| static const int days[12] = {305, 336, -1, 30, 60, 91, 121, 152, 183, 213, 244, 274}; |
| |
| int mday, year, month, d, dp; |
| |
| mday = days_since_jan_1_1970; |
| |
| /* guess the year and refine the guess */ |
| year = mday / 365 + 69; |
| d = dp = (year * 365) + (year / 4) - (year / 100) + (year / 100 + 3) / 4 - DAYS_OFFSET - 1; |
| |
| while (dp < mday) { |
| d = dp; |
| year += 1; |
| dp = (year * 365) + (year / 4) - (year / 100) + (year / 100 + 3) / 4 - DAYS_OFFSET - 1; |
| } |
| |
| /* convert the days */ |
| d = mday - d; |
| if ((d < 0) || (d > 366)) { |
| ink_assert(!"bad date"); |
| } else { |
| month = months[d]; |
| if (month > 1) { |
| year -= 1; |
| } |
| |
| mday = d - days[month] - 1; |
| year += 1900; |
| |
| *m_return = month; |
| *d_return = mday; |
| *y_return = year; |
| } |
| } |
| |
| void |
| mime_days_since_epoch_to_mdy(unsigned int days_since_jan_1_1970, int *m_return, int *d_return, int *y_return) |
| { |
| ink_assert(_days_to_mdy_fast_lookup_table != nullptr); |
| |
| ///////////////////////////////////////////////////////////// |
| // if we have a fast lookup entry for this date, return it // |
| ///////////////////////////////////////////////////////////// |
| |
| if ((days_since_jan_1_1970 >= _days_to_mdy_fast_lookup_table_first_day) && |
| (days_since_jan_1_1970 <= _days_to_mdy_fast_lookup_table_last_day)) { |
| //////////////////////////////////////////////////////////////// |
| // to speed up the days_since_epoch to m/d/y conversion, we // |
| // use a pre-computed lookup table to support the common case // |
| // of dates that are +/- one year from today. // |
| //////////////////////////////////////////////////////////////// |
| |
| int i = days_since_jan_1_1970 - _days_to_mdy_fast_lookup_table_first_day; |
| *m_return = _days_to_mdy_fast_lookup_table[i].m; |
| *d_return = _days_to_mdy_fast_lookup_table[i].d; |
| *y_return = _days_to_mdy_fast_lookup_table[i].y; |
| return; |
| } |
| //////////////////////////////////// |
| // otherwise, return the slow way // |
| //////////////////////////////////// |
| |
| mime_days_since_epoch_to_mdy_slowcase(days_since_jan_1_1970, m_return, d_return, y_return); |
| } |
| |
| int |
| mime_format_date(char *buffer, time_t value) |
| { |
| // must be 3 characters! |
| static const char *daystrs[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; |
| |
| // must be 3 characters! |
| static const char *monthstrs[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; |
| |
| static const char *digitstrs[] = { |
| "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", |
| "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", |
| "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", |
| "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", |
| "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", |
| }; |
| |
| char *buf; |
| int sec, min, hour, wday, mday = 0, year = 0, month = 0; |
| |
| buf = buffer; |
| |
| sec = static_cast<int>(value % 60); |
| value /= 60; |
| min = static_cast<int>(value % 60); |
| value /= 60; |
| hour = static_cast<int>(value % 24); |
| value /= 24; |
| |
| /* Jan 1, 1970 was a Thursday */ |
| wday = static_cast<int>((4 + value) % 7); |
| |
| /* value is days since Jan 1, 1970 */ |
| #if MIME_FORMAT_DATE_USE_LOOKUP_TABLE |
| mime_days_since_epoch_to_mdy(value, &month, &mday, &year); |
| #else |
| mime_days_since_epoch_to_mdy_slowcase(value, &month, &mday, &year); |
| #endif |
| |
| /* arrange the date in the buffer */ |
| ink_assert((mday >= 0) && (mday <= 99)); |
| ink_assert((hour >= 0) && (hour <= 99)); |
| ink_assert((min >= 0) && (min <= 99)); |
| ink_assert((sec >= 0) && (sec <= 99)); |
| |
| /* the day string */ |
| const char *three_char_day = daystrs[wday]; |
| buf[0] = three_char_day[0]; |
| buf[1] = three_char_day[1]; |
| buf[2] = three_char_day[2]; |
| buf += 3; |
| |
| buf[0] = ','; |
| buf[1] = ' '; |
| buf += 2; |
| |
| /* the day of month */ |
| buf[0] = digitstrs[mday][0]; |
| buf[1] = digitstrs[mday][1]; |
| buf[2] = ' '; |
| buf += 3; |
| |
| /* the month string */ |
| const char *three_char_month = monthstrs[month]; |
| buf[0] = three_char_month[0]; |
| buf[1] = three_char_month[1]; |
| buf[2] = three_char_month[2]; |
| buf += 3; |
| |
| /* the year */ |
| buf[0] = ' '; |
| |
| if ((year >= 2000) && (year <= 2009)) { |
| buf[1] = '2'; |
| buf[2] = '0'; |
| buf[3] = '0'; |
| buf[4] = (year - 2000) + '0'; |
| } else if ((year >= 1990) && (year <= 1999)) { |
| buf[1] = '1'; |
| buf[2] = '9'; |
| buf[3] = '9'; |
| buf[4] = (year - 1990) + '0'; |
| } else { |
| buf[4] = (year % 10) + '0'; |
| year /= 10; |
| buf[3] = (year % 10) + '0'; |
| year /= 10; |
| buf[2] = (year % 10) + '0'; |
| year /= 10; |
| buf[1] = (year % 10) + '0'; |
| } |
| buf[5] = ' '; |
| buf += 6; |
| |
| /* the hour */ |
| buf[0] = digitstrs[hour][0]; |
| buf[1] = digitstrs[hour][1]; |
| buf[2] = ':'; |
| buf += 3; |
| |
| /* the minute */ |
| buf[0] = digitstrs[min][0]; |
| buf[1] = digitstrs[min][1]; |
| buf[2] = ':'; |
| buf += 3; |
| |
| /* the second */ |
| buf[0] = digitstrs[sec][0]; |
| buf[1] = digitstrs[sec][1]; |
| buf[2] = ' '; |
| buf += 3; |
| |
| /* the timezone string */ |
| buf[0] = 'G'; |
| buf[1] = 'M'; |
| buf[2] = 'T'; |
| buf[3] = '\0'; |
| buf += 3; |
| |
| return buf - buffer; // not counting NUL |
| } |
| |
| int32_t |
| mime_parse_int(const char *buf, const char *end) |
| { |
| int32_t num; |
| bool negative; |
| |
| if (!buf || (buf == end)) { |
| return 0; |
| } |
| |
| if (is_digit(*buf)) { // fast case |
| num = *buf++ - '0'; |
| while ((buf != end) && is_digit(*buf)) { |
| if (num != INT_MAX) { |
| int new_num = (num * 10) + (*buf++ - '0'); |
| |
| num = (new_num < num ? INT_MAX : new_num); // Check for overflow |
| } else { |
| ++buf; // Skip the remaining (valid) digits since we reached MAX/MIN_INT |
| } |
| } |
| |
| return num; |
| } else { |
| num = 0; |
| negative = false; |
| |
| while ((buf != end) && ParseRules::is_space(*buf)) { |
| buf += 1; |
| } |
| |
| if ((buf != end) && (*buf == '-')) { |
| negative = true; |
| buf += 1; |
| } |
| // NOTE: we first compute the value as negative then correct the |
| // sign back to positive. This enables us to correctly parse MININT. |
| while ((buf != end) && is_digit(*buf)) { |
| if (num != INT_MIN) { |
| int new_num = (num * 10) - (*buf++ - '0'); |
| |
| num = (new_num > num ? INT_MIN : new_num); // Check for overflow, so to speak, see above re: negative |
| } else { |
| ++buf; // Skip the remaining (valid) digits since we reached MAX/MIN_INT |
| } |
| } |
| |
| if (!negative) { |
| num = -num; |
| } |
| |
| return num; |
| } |
| } |
| |
| uint32_t |
| mime_parse_uint(const char *buf, const char *end) |
| { |
| uint32_t num; |
| |
| if (!buf || (buf == end)) { |
| return 0; |
| } |
| |
| if (is_digit(*buf)) // fast case |
| { |
| num = *buf++ - '0'; |
| while ((buf != end) && is_digit(*buf)) { |
| num = (num * 10) + (*buf++ - '0'); |
| } |
| return num; |
| } else { |
| num = 0; |
| while ((buf != end) && ParseRules::is_space(*buf)) { |
| buf += 1; |
| } |
| while ((buf != end) && is_digit(*buf)) { |
| num = (num * 10) + (*buf++ - '0'); |
| } |
| return num; |
| } |
| } |
| |
| int64_t |
| mime_parse_int64(const char *buf, const char *end) |
| { |
| int64_t num; |
| bool negative; |
| |
| if (!buf || (buf == end)) { |
| return 0; |
| } |
| |
| if (is_digit(*buf)) // fast case |
| { |
| num = *buf++ - '0'; |
| while ((buf != end) && is_digit(*buf)) { |
| num = (num * 10) + (*buf++ - '0'); |
| } |
| return num; |
| } else { |
| num = 0; |
| negative = false; |
| |
| while ((buf != end) && ParseRules::is_space(*buf)) { |
| buf += 1; |
| } |
| |
| if ((buf != end) && (*buf == '-')) { |
| negative = true; |
| buf += 1; |
| } |
| // NOTE: we first compute the value as negative then correct the |
| // sign back to positive. This enables us to correctly parse MININT. |
| while ((buf != end) && is_digit(*buf)) { |
| num = (num * 10) - (*buf++ - '0'); |
| } |
| |
| if (!negative) { |
| num = -num; |
| } |
| |
| return num; |
| } |
| } |
| |
| /*------------------------------------------------------------------------- |
| |
| mime_parse_rfc822_date_fastcase (const char *buf, int length, struct tm *tp) |
| |
| This routine is a fast case parser for date strings that are guaranteed |
| to be in the rfc822/rfc1123 format. It uses integer binary searches to |
| convert daya and month names into integral indices. |
| |
| This routine only supports rfc822/rfc1123 dates. It must be called |
| with NO leading whitespace, with a length >= 29 characters, and with |
| a comma in buf[3]. The caller MUST ensure these conditions. |
| |
| Sun, 06 Nov 1994 08:49:37 GMT |
| 01234567890123456789012345678 |
| |
| This function handles the common case dates fast. The day and month |
| are processed as 3 character integers before falling to DFA. This |
| table shows string, hash (24 bit values of 3 characters), and the |
| resulting string index. |
| |
| Fri 0x467269 5 Apr 0x417072 3 |
| Mon 0x4D6F6E 1 Aug 0x417567 7 |
| Sat 0x536174 6 Dec 0x446563 11 |
| Sun 0x53756E 0 Feb 0x466562 1 |
| Thu 0x546875 4 Jan 0x4A616E 0 |
| Tue 0x547565 2 Jul 0x4A756C 6 |
| Wed 0x576564 3 Jun 0x4A756E 5 |
| Mar 0x4D6172 2 |
| May 0x4D6179 4 |
| Nov 0x4E6F76 10 |
| Oct 0x4F6374 9 |
| Sep 0x536570 8 |
| |
| -------------------------------------------------------------------------*/ |
| int |
| mime_parse_rfc822_date_fastcase(const char *buf, int length, struct tm *tp) |
| { |
| unsigned int three_char_wday, three_char_mon; |
| std::string_view view{buf, size_t(length)}; |
| |
| ink_assert(length >= 29); |
| ink_assert(!is_ws(buf[0])); |
| ink_assert(buf[3] == ','); |
| |
| //////////////////////////// |
| // binary search for wday // |
| //////////////////////////// |
| tp->tm_wday = -1; |
| three_char_wday = (buf[0] << 16) | (buf[1] << 8) | buf[2]; |
| if (three_char_wday <= 0x53756E) { |
| if (three_char_wday == 0x467269) { |
| tp->tm_wday = 5; |
| } else if (three_char_wday == 0x4D6F6E) { |
| tp->tm_wday = 1; |
| } else if (three_char_wday == 0x536174) { |
| tp->tm_wday = 6; |
| } else if (three_char_wday == 0x53756E) { |
| tp->tm_wday = 0; |
| } |
| } else { |
| if (three_char_wday == 0x546875) { |
| tp->tm_wday = 4; |
| } else if (three_char_wday == 0x547565) { |
| tp->tm_wday = 2; |
| } else if (three_char_wday == 0x576564) { |
| tp->tm_wday = 3; |
| } |
| } |
| if (tp->tm_wday < 0) { |
| tp->tm_wday = day_names_dfa->match(view); |
| if (tp->tm_wday < 0) { |
| return 0; |
| } |
| } |
| ////////////////////////// |
| // extract day of month // |
| ////////////////////////// |
| tp->tm_mday = (buf[5] - '0') * 10 + (buf[6] - '0'); |
| |
| ///////////////////////////// |
| // binary search for month // |
| ///////////////////////////// |
| tp->tm_mon = -1; |
| three_char_mon = (buf[8] << 16) | (buf[9] << 8) | buf[10]; |
| if (three_char_mon <= 0x4A756C) { |
| if (three_char_mon <= 0x446563) { |
| if (three_char_mon == 0x417072) { |
| tp->tm_mon = 3; |
| } else if (three_char_mon == 0x417567) { |
| tp->tm_mon = 7; |
| } else if (three_char_mon == 0x446563) { |
| tp->tm_mon = 11; |
| } |
| } else { |
| if (three_char_mon == 0x466562) { |
| tp->tm_mon = 1; |
| } else if (three_char_mon == 0x4A616E) { |
| tp->tm_mon = 0; |
| } else if (three_char_mon == 0x4A756C) { |
| tp->tm_mon = 6; |
| } |
| } |
| } else { |
| if (three_char_mon <= 0x4D6179) { |
| if (three_char_mon == 0x4A756E) { |
| tp->tm_mon = 5; |
| } else if (three_char_mon == 0x4D6172) { |
| tp->tm_mon = 2; |
| } else if (three_char_mon == 0x4D6179) { |
| tp->tm_mon = 4; |
| } |
| } else { |
| if (three_char_mon == 0x4E6F76) { |
| tp->tm_mon = 10; |
| } else if (three_char_mon == 0x4F6374) { |
| tp->tm_mon = 9; |
| } else if (three_char_mon == 0x536570) { |
| tp->tm_mon = 8; |
| } |
| } |
| } |
| if (tp->tm_mon < 0) { |
| tp->tm_mon = month_names_dfa->match(view); |
| if (tp->tm_mon < 0) { |
| return 0; |
| } |
| } |
| ////////////////// |
| // extract year // |
| ////////////////// |
| tp->tm_year = ((buf[12] - '0') * 1000 + (buf[13] - '0') * 100 + (buf[14] - '0') * 10 + (buf[15] - '0')) - 1900; |
| |
| ////////////////// |
| // extract time // |
| ////////////////// |
| tp->tm_hour = (buf[17] - '0') * 10 + (buf[18] - '0'); |
| tp->tm_min = (buf[20] - '0') * 10 + (buf[21] - '0'); |
| tp->tm_sec = (buf[23] - '0') * 10 + (buf[24] - '0'); |
| if ((buf[19] != ':') || (buf[22] != ':')) { |
| return 0; |
| } |
| return 1; |
| } |
| |
| /*------------------------------------------------------------------------- |
| Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 |
| Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 |
| Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format |
| 6 Nov 1994 08:49:37 GMT ; NNTP-style date |
| -------------------------------------------------------------------------*/ |
| time_t |
| mime_parse_date(const char *buf, const char *end) |
| { |
| static const int DAYS_OFFSET = 25508; |
| static const int days[12] = {305, 336, -1, 30, 60, 91, 121, 152, 183, 213, 244, 274}; |
| |
| struct tm tp; |
| time_t t; |
| int year; |
| int month; |
| int mday; |
| |
| if (!buf) { |
| return static_cast<time_t>(0); |
| } |
| |
| while ((buf != end) && is_ws(*buf)) { |
| buf += 1; |
| } |
| |
| if ((buf != end) && is_digit(*buf)) { // NNTP date |
| if (!mime_parse_mday(buf, end, &tp.tm_mday)) { |
| return static_cast<time_t>(0); |
| } |
| if (!mime_parse_month(buf, end, &tp.tm_mon)) { |
| return static_cast<time_t>(0); |
| } |
| if (!mime_parse_year(buf, end, &tp.tm_year)) { |
| return static_cast<time_t>(0); |
| } |
| if (!mime_parse_time(buf, end, &tp.tm_hour, &tp.tm_min, &tp.tm_sec)) { |
| return static_cast<time_t>(0); |
| } |
| } else if (end && (end - buf >= 29) && (buf[3] == ',')) { |
| if (!mime_parse_rfc822_date_fastcase(buf, end - buf, &tp)) { |
| return static_cast<time_t>(0); |
| } |
| } else { |
| if (!mime_parse_day(buf, end, &tp.tm_wday)) { |
| return static_cast<time_t>(0); |
| } |
| |
| while ((buf != end) && is_ws(*buf)) { |
| buf += 1; |
| } |
| |
| if ((buf != end) && ((*buf == ',') || is_digit(*buf))) { |
| // RFC 822 or RFC 850 time format |
| if (!mime_parse_mday(buf, end, &tp.tm_mday)) { |
| return static_cast<time_t>(0); |
| } |
| if (!mime_parse_month(buf, end, &tp.tm_mon)) { |
| return static_cast<time_t>(0); |
| } |
| if (!mime_parse_year(buf, end, &tp.tm_year)) { |
| return static_cast<time_t>(0); |
| } |
| if (!mime_parse_time(buf, end, &tp.tm_hour, &tp.tm_min, &tp.tm_sec)) { |
| return static_cast<time_t>(0); |
| } |
| // ignore timezone specifier...should always be GMT anyways |
| } else { |
| // ANSI C's asctime format |
| if (!mime_parse_month(buf, end, &tp.tm_mon)) { |
| return static_cast<time_t>(0); |
| } |
| if (!mime_parse_mday(buf, end, &tp.tm_mday)) { |
| return static_cast<time_t>(0); |
| } |
| if (!mime_parse_time(buf, end, &tp.tm_hour, &tp.tm_min, &tp.tm_sec)) { |
| return static_cast<time_t>(0); |
| } |
| if (!mime_parse_year(buf, end, &tp.tm_year)) { |
| return static_cast<time_t>(0); |
| } |
| } |
| } |
| |
| year = tp.tm_year; |
| month = tp.tm_mon; |
| mday = tp.tm_mday; |
| |
| // what should we do? |
| if (year > 137) { |
| return static_cast<time_t>(INT_MAX); |
| } |
| if (year < 70) { |
| return static_cast<time_t>(0); |
| } |
| |
| mday += days[month]; |
| /* month base == march */ |
| if (month < 2) { |
| year -= 1; |
| } |
| mday += (year * 365) + (year / 4) - (year / 100) + (year / 100 + 3) / 4; |
| mday -= DAYS_OFFSET; |
| |
| t = ((mday * 24 + tp.tm_hour) * 60 + tp.tm_min) * 60 + tp.tm_sec; |
| |
| return t; |
| } |
| |
| bool |
| mime_parse_day(const char *&buf, const char *end, int *day) |
| { |
| const char *e; |
| |
| while ((buf != end) && *buf && !ParseRules::is_alpha(*buf)) { |
| buf += 1; |
| } |
| |
| e = buf; |
| while ((e != end) && *e && ParseRules::is_alpha(*e)) { |
| e += 1; |
| } |
| |
| *day = day_names_dfa->match({buf, size_t(e - buf)}); |
| if (*day < 0) { |
| return false; |
| } else { |
| buf = e; |
| return true; |
| } |
| } |
| |
| bool |
| mime_parse_month(const char *&buf, const char *end, int *month) |
| { |
| const char *e; |
| |
| while ((buf != end) && *buf && !ParseRules::is_alpha(*buf)) { |
| buf += 1; |
| } |
| |
| e = buf; |
| while ((e != end) && *e && ParseRules::is_alpha(*e)) { |
| e += 1; |
| } |
| |
| *month = month_names_dfa->match({buf, size_t(e - buf)}); |
| if (*month < 0) { |
| return false; |
| } else { |
| buf = e; |
| return true; |
| } |
| } |
| |
| bool |
| mime_parse_mday(const char *&buf, const char *end, int *mday) |
| { |
| return mime_parse_integer(buf, end, mday); |
| } |
| |
| bool |
| mime_parse_year(const char *&buf, const char *end, int *year) |
| { |
| int val; |
| |
| while ((buf != end) && *buf && !is_digit(*buf)) { |
| buf += 1; |
| } |
| |
| if ((buf == end) || (*buf == '\0')) { |
| return false; |
| } |
| |
| val = 0; |
| |
| while ((buf != end) && *buf && is_digit(*buf)) { |
| val = (val * 10) + (*buf++ - '0'); |
| } |
| |
| if (val >= 1900) { |
| val -= 1900; |
| } else if (val < 70) { |
| val += 100; |
| } |
| |
| *year = val; |
| |
| return true; |
| } |
| |
| bool |
| mime_parse_time(const char *&buf, const char *end, int *hour, int *min, int *sec) |
| { |
| if (!mime_parse_integer(buf, end, hour)) { |
| return false; |
| } |
| if (!mime_parse_integer(buf, end, min)) { |
| return false; |
| } |
| if (!mime_parse_integer(buf, end, sec)) { |
| return false; |
| } |
| return true; |
| } |
| |
| // This behaves slightly different than mime_parse_int(), int that we actually |
| // return a "bool" for success / failure on "reasonable" parsing. This kinda |
| // dumb, because we have two interfaces, where one does not move along the |
| // buf pointer, but this one does (and the ones using this function do). |
| bool |
| mime_parse_integer(const char *&buf, const char *end, int *integer) |
| { |
| while ((buf != end) && *buf && !is_digit(*buf) && (*buf != '-')) { |
| buf += 1; |
| } |
| |
| if ((buf == end) || (*buf == '\0')) { |
| return false; |
| } |
| |
| int32_t num; |
| bool negative; |
| |
| // This code is copied verbatim from mime_parse_int ... Sigh. Maybe amc is right, and |
| // we really need to clean this up. But, as such, we should redo all these interfaces, |
| // and that's a big undertaking (and we'd want to move these strings all to string_view's). |
| if (is_digit(*buf)) { // fast case |
| num = *buf++ - '0'; |
| while ((buf != end) && is_digit(*buf)) { |
| if (num != INT_MAX) { |
| int new_num = (num * 10) + (*buf++ - '0'); |
| |
| num = (new_num < num ? INT_MAX : new_num); // Check for overflow |
| } else { |
| ++buf; // Skip the remaining (valid) digits since we reached MAX/MIN_INT |
| } |
| } |
| } else { |
| num = 0; |
| negative = false; |
| |
| while ((buf != end) && ParseRules::is_space(*buf)) { |
| buf += 1; |
| } |
| |
| if ((buf != end) && (*buf == '-')) { |
| negative = true; |
| buf += 1; |
| } |
| // NOTE: we first compute the value as negative then correct the |
| // sign back to positive. This enables us to correctly parse MININT. |
| while ((buf != end) && is_digit(*buf)) { |
| if (num != INT_MIN) { |
| int new_num = (num * 10) - (*buf++ - '0'); |
| |
| num = (new_num > num ? INT_MIN : new_num); // Check for overflow, so to speak, see above re: negative |
| } else { |
| ++buf; // Skip the remaining (valid) digits since we reached MAX/MIN_INT |
| } |
| } |
| |
| if (!negative) { |
| num = -num; |
| } |
| } |
| |
| *integer = num; |
| |
| return true; |
| } |
| |
| /*********************************************************************** |
| * * |
| * M A R S H A L I N G * |
| * * |
| ***********************************************************************/ |
| int |
| MIMEFieldBlockImpl::marshal(MarshalXlate *ptr_xlate, int num_ptr, MarshalXlate *str_xlate, int num_str) |
| { |
| // printf("FieldBlockImpl:marshal num_ptr = %d num_str = %d\n", num_ptr, num_str); |
| HDR_MARSHAL_PTR(m_next, MIMEFieldBlockImpl, ptr_xlate, num_ptr); |
| |
| if ((num_str == 1) && (num_ptr == 1)) { |
| for (uint32_t index = 0; index < m_freetop; index++) { |
| MIMEField *field = &(m_field_slots[index]); |
| |
| if (field->is_live()) { |
| HDR_MARSHAL_STR_1(field->m_ptr_name, str_xlate); |
| HDR_MARSHAL_STR_1(field->m_ptr_value, str_xlate); |
| if (field->m_next_dup) { |
| HDR_MARSHAL_PTR_1(field->m_next_dup, MIMEField, ptr_xlate); |
| } |
| } |
| } |
| } else { |
| for (uint32_t index = 0; index < m_freetop; index++) { |
| MIMEField *field = &(m_field_slots[index]); |
| |
| if (field->is_live()) { |
| HDR_MARSHAL_STR(field->m_ptr_name, str_xlate, num_str); |
| HDR_MARSHAL_STR(field->m_ptr_value, str_xlate, num_str); |
| if (field->m_next_dup) { |
| HDR_MARSHAL_PTR(field->m_next_dup, MIMEField, ptr_xlate, num_ptr); |
| } |
| } |
| } |
| } |
| return 0; |
| } |
| |
| void |
| MIMEFieldBlockImpl::unmarshal(intptr_t offset) |
| { |
| HDR_UNMARSHAL_PTR(m_next, MIMEFieldBlockImpl, offset); |
| |
| for (uint32_t index = 0; index < m_freetop; index++) { |
| MIMEField *field = &(m_field_slots[index]); |
| |
| if (field->is_live()) { |
| HDR_UNMARSHAL_STR(field->m_ptr_name, offset); |
| HDR_UNMARSHAL_STR(field->m_ptr_value, offset); |
| if (field->m_next_dup) { |
| HDR_UNMARSHAL_PTR(field->m_next_dup, MIMEField, offset); |
| } |
| } else { |
| // Clear out other types of slots |
| field->m_readiness = MIME_FIELD_SLOT_READINESS_EMPTY; |
| } |
| } |
| } |
| |
| void |
| MIMEFieldBlockImpl::move_strings(HdrStrHeap *new_heap) |
| { |
| for (uint32_t index = 0; index < m_freetop; index++) { |
| MIMEField *field = &(m_field_slots[index]); |
| |
| if (field->is_live() || field->is_detached()) { |
| // FIX ME - Should do the field in one shot and preserve |
| // raw_printable if it's set |
| field->m_n_v_raw_printable = 0; |
| |
| HDR_MOVE_STR(field->m_ptr_name, field->m_len_name); |
| HDR_MOVE_STR(field->m_ptr_value, field->m_len_value); |
| } |
| } |
| } |
| |
| size_t |
| MIMEFieldBlockImpl::strings_length() |
| { |
| size_t ret = 0; |
| |
| for (uint32_t index = 0; index < m_freetop; index++) { |
| MIMEField *field = &(m_field_slots[index]); |
| |
| if (field->m_readiness == MIME_FIELD_SLOT_READINESS_LIVE || field->m_readiness == MIME_FIELD_SLOT_READINESS_DETACHED) { |
| ret += field->m_len_name; |
| ret += field->m_len_value; |
| } |
| } |
| return ret; |
| } |
| |
| bool |
| MIMEFieldBlockImpl::contains(const MIMEField *field) |
| { |
| MIMEField *first = &(m_field_slots[0]); |
| MIMEField *last = &(m_field_slots[MIME_FIELD_BLOCK_SLOTS - 1]); |
| return (field >= first) && (field <= last); |
| } |
| |
| void |
| MIMEFieldBlockImpl::check_strings(HeapCheck *heaps, int num_heaps) |
| { |
| for (uint32_t index = 0; index < m_freetop; index++) { |
| MIMEField *field = &(m_field_slots[index]); |
| |
| if (field->is_live() || field->is_detached()) { |
| // FIX ME - Should check raw printing characters as well |
| CHECK_STR(field->m_ptr_name, field->m_len_name, heaps, num_heaps); |
| CHECK_STR(field->m_ptr_value, field->m_len_value, heaps, num_heaps); |
| } |
| } |
| } |
| |
| int |
| MIMEHdrImpl::marshal(MarshalXlate *ptr_xlate, int num_ptr, MarshalXlate *str_xlate, int num_str) |
| { |
| // printf("MIMEHdrImpl:marshal num_ptr = %d num_str = %d\n", num_ptr, num_str); |
| HDR_MARSHAL_PTR(m_fblock_list_tail, MIMEFieldBlockImpl, ptr_xlate, num_ptr); |
| return m_first_fblock.marshal(ptr_xlate, num_ptr, str_xlate, num_str); |
| } |
| |
| void |
| MIMEHdrImpl::unmarshal(intptr_t offset) |
| { |
| HDR_UNMARSHAL_PTR(m_fblock_list_tail, MIMEFieldBlockImpl, offset); |
| m_first_fblock.unmarshal(offset); |
| } |
| |
| void |
| MIMEHdrImpl::move_strings(HdrStrHeap *new_heap) |
| { |
| m_first_fblock.move_strings(new_heap); |
| } |
| |
| size_t |
| MIMEHdrImpl::strings_length() |
| { |
| return m_first_fblock.strings_length(); |
| } |
| |
| void |
| MIMEHdrImpl::check_strings(HeapCheck *heaps, int num_heaps) |
| { |
| m_first_fblock.check_strings(heaps, num_heaps); |
| } |
| |
| void |
| MIMEHdrImpl::recompute_accelerators_and_presence_bits() |
| { |
| mime_hdr_reset_accelerators_and_presence_bits(this); |
| } |
| |
| /*********************************************************************** |
| * * |
| * C O O K E D V A L U E C A C H E * |
| * * |
| ***********************************************************************/ |
| |
| //////////////////////////////////////////////////////// |
| // we need to recook the cooked values cache when: // |
| // // |
| // setting the value and the field is live // |
| // attaching the field and the field isn't empty // |
| // detaching the field // |
| //////////////////////////////////////////////////////// |
| |
| void |
| MIMEHdrImpl::recompute_cooked_stuff(MIMEField *changing_field_or_null) |
| { |
| int len, tlen; |
| const char *s; |
| const char *c; |
| const char *e; |
| const char *token_wks; |
| MIMEField *field; |
| uint32_t mask = 0; |
| |
| mime_hdr_cooked_stuff_init(this, changing_field_or_null); |
| |
| ////////////////////////////////////////////////// |
| // (1) cook the Cache-Control header if present // |
| ////////////////////////////////////////////////// |
| |
| // to be safe, recompute unless you know this call is for other cooked field |
| if ((changing_field_or_null == nullptr) || (changing_field_or_null->m_wks_idx != MIME_WKSIDX_PRAGMA)) { |
| field = mime_hdr_field_find(this, MIME_FIELD_CACHE_CONTROL, MIME_LEN_CACHE_CONTROL); |
| |
| if (field) { |
| // try pathpaths first -- unlike most other fastpaths, this one |
| // is probably more useful for polygraph than for the real world |
| if (!field->has_dups()) { |
| s = field->value_get(&len); |
| if (ptr_len_casecmp(s, len, "public", 6) == 0) { |
| mask = MIME_COOKED_MASK_CC_PUBLIC; |
| m_cooked_stuff.m_cache_control.m_mask |= mask; |
| } else if (ptr_len_casecmp(s, len, "private,no-cache", 16) == 0) { |
| mask = MIME_COOKED_MASK_CC_PRIVATE | MIME_COOKED_MASK_CC_NO_CACHE; |
| m_cooked_stuff.m_cache_control.m_mask |= mask; |
| } |
| } |
| |
| if (mask == 0) { |
| HdrCsvIter csv_iter; |
| |
| for (s = csv_iter.get_first(field, &len); s != nullptr; s = csv_iter.get_next(&len)) { |
| e = s + len; |
| for (c = s; (c < e) && (ParseRules::is_token(*c)); c++) { |
| ; |
| } |
| tlen = c - s; |
| |
| // If >= 0 then this is a well known token |
| if (hdrtoken_tokenize(s, tlen, &token_wks) >= 0) { |
| #if TRACK_COOKING |
| Debug("http", "recompute_cooked_stuff: got field '%s'", token_wks); |
| #endif |
| |
| HdrTokenHeapPrefix *p = hdrtoken_wks_to_prefix(token_wks); |
| mask = p->wks_type_specific.u.cache_control.cc_mask; |
| m_cooked_stuff.m_cache_control.m_mask |= mask; |
| |
| #if TRACK_COOKING |
| Debug("http", " set mask 0x%0X", mask); |
| #endif |
| |
| if (mask & (MIME_COOKED_MASK_CC_MAX_AGE | MIME_COOKED_MASK_CC_S_MAXAGE | MIME_COOKED_MASK_CC_MAX_STALE | |
| MIME_COOKED_MASK_CC_MIN_FRESH)) { |
| int value; |
| |
| if (mime_parse_integer(c, e, &value)) { |
| #if TRACK_COOKING |
| Debug("http", " set integer value %d", value); |
| #endif |
| if (token_wks == MIME_VALUE_MAX_AGE) { |
| m_cooked_stuff.m_cache_control.m_secs_max_age = value; |
| } else if (token_wks == MIME_VALUE_MIN_FRESH) { |
| m_cooked_stuff.m_cache_control.m_secs_min_fresh = value; |
| } else if (token_wks == MIME_VALUE_MAX_STALE) { |
| m_cooked_stuff.m_cache_control.m_secs_max_stale = value; |
| } else if (token_wks == MIME_VALUE_S_MAXAGE) { |
| m_cooked_stuff.m_cache_control.m_secs_s_maxage = value; |
| } |
| } else { |
| #if TRACK_COOKING |
| Debug("http", " set integer value %d", INT_MAX); |
| #endif |
| if (token_wks == MIME_VALUE_MAX_STALE) { |
| m_cooked_stuff.m_cache_control.m_secs_max_stale = INT_MAX; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| /////////////////////////////////////////// |
| // (2) cook the Pragma header if present // |
| /////////////////////////////////////////// |
| |
| if ((changing_field_or_null == nullptr) || (changing_field_or_null->m_wks_idx != MIME_WKSIDX_CACHE_CONTROL)) { |
| field = mime_hdr_field_find(this, MIME_FIELD_PRAGMA, MIME_LEN_PRAGMA); |
| if (field) { |
| if (!field->has_dups()) { // try fastpath first |
| s = field->value_get(&len); |
| if (ptr_len_casecmp(s, len, "no-cache", 8) == 0) { |
| m_cooked_stuff.m_pragma.m_no_cache = true; |
| return; |
| } |
| } |
| |
| { |
| HdrCsvIter csv_iter; |
| |
| for (s = csv_iter.get_first(field, &len); s != nullptr; s = csv_iter.get_next(&len)) { |
| e = s + len; |
| for (c = s; (c < e) && (ParseRules::is_token(*c)); c++) { |
| ; |
| } |
| tlen = c - s; |
| |
| if (hdrtoken_tokenize(s, tlen, &token_wks) >= 0) { |
| if (token_wks == MIME_VALUE_NO_CACHE) { |
| m_cooked_stuff.m_pragma.m_no_cache = true; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |