| /** @file | |
| This file implements the LogAccess class. | |
| @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 "proxy/logging/LogAccess.h" | |
| #include "records/RecCore.h" | |
| #include "tscore/Version.h" | |
| #include "proxy/hdrs/MIME.h" | |
| #include "proxy/logging/TransactionLogData.h" | |
| #include "iocore/utils/Machine.h" | |
| #include "proxy/logging/LogFormat.h" | |
| #include "proxy/logging/LogBuffer.h" | |
| #include "records/RecHttp.h" | |
| #include "swoc/BufferWriter.h" | |
| #include "tscore/Encoding.h" | |
| #include "tscore/ink_inet.h" | |
| #include "tscore/ink_base64.h" | |
| char INVALID_STR[] = "!INVALID_STR!"; | |
| // should be at least 22 bytes to always accommodate a converted | |
| // MgmtInt, MgmtIntCounter or MgmtFloat. 22 bytes is enough for 64 bit | |
| // ints + sign + eos, and enough for %e floating point representation | |
| // + eos | |
| // | |
| #define MARSHAL_RECORD_LENGTH 32 | |
| namespace | |
| { | |
| DbgCtl dbg_ctl_log_escape{"log-escape"}; | |
| DbgCtl dbg_ctl_log_resolve{"log-resolve"}; | |
| DbgCtl dbg_ctl_log_unmarshal_orun{"log-unmarshal-orun"}; // Overrun of unmarshaling destination buffer. | |
| DbgCtl dbg_ctl_log_unmarshal_data{"log-unmarshal-data"}; // Error in txn data when unmarshalling. | |
| int | |
| marshal_http_version_string(char *buf, HTTPHdr *response) | |
| { | |
| HTTPVersion version = response ? response->version_get() : HTTP_INVALID; | |
| char str[8]; | |
| int str_len = | |
| snprintf(str, sizeof(str), "%u.%u", static_cast<unsigned>(version.get_major()), static_cast<unsigned>(version.get_minor())); | |
| int len = LogAccess::padded_length(str_len + 1); | |
| ink_release_assert(str_len > 0 && str_len < static_cast<int>(sizeof(str))); | |
| if (buf) { | |
| LogAccess::marshal_str(buf, str, len); | |
| } | |
| return len; | |
| } | |
| } // end anonymous namespace | |
| #define DBG_UNMARSHAL_DEST_OVERRUN Dbg(dbg_ctl_log_unmarshal_orun, "Unmarshal destination buffer overrun."); | |
| /*------------------------------------------------------------------------- | |
| LogAccess | |
| Initialize from TransactionLogData supplied by the caller. | |
| -------------------------------------------------------------------------*/ | |
| LogAccess::LogAccess(TransactionLogData &data) : m_data(&data) {} | |
| void | |
| LogAccess::init() | |
| { | |
| ink_assert(m_data != nullptr); | |
| m_client_request = m_data->get_client_request(); | |
| const char *url_str = m_data->get_client_req_url_str(); | |
| int url_len = m_data->get_client_req_url_len(); | |
| if (url_str != nullptr && url_len > 0) { | |
| m_client_req_url_len = url_len; | |
| m_client_req_url_str = m_arena.str_alloc(m_client_req_url_len + 1); | |
| memcpy(m_client_req_url_str, url_str, m_client_req_url_len); | |
| m_client_req_url_str[m_client_req_url_len] = '\0'; | |
| m_client_req_url_canon_str = | |
| Encoding::escapify_url(&m_arena, m_client_req_url_str, m_client_req_url_len, &m_client_req_url_canon_len); | |
| } | |
| const char *path_str = m_data->get_client_req_url_path_str(); | |
| int path_len = m_data->get_client_req_url_path_len(); | |
| if (path_str != nullptr && path_len > 0) { | |
| m_client_req_url_path_len = path_len; | |
| char *path_copy = m_arena.str_alloc(m_client_req_url_path_len + 1); | |
| memcpy(path_copy, path_str, m_client_req_url_path_len); | |
| path_copy[m_client_req_url_path_len] = '\0'; | |
| m_client_req_url_path_str = path_copy; | |
| } else { | |
| m_client_req_url_path_str = nullptr; | |
| m_client_req_url_path_len = 0; | |
| } | |
| m_proxy_response = m_data->get_proxy_response(); | |
| char *ct_str = m_data->get_proxy_resp_content_type_str(); | |
| int ct_len = m_data->get_proxy_resp_content_type_len(); | |
| if (ct_str != nullptr && ct_len > 0) { | |
| m_proxy_resp_content_type_str = ct_str; | |
| m_proxy_resp_content_type_len = ct_len; | |
| LogUtils::remove_content_type_attributes(m_proxy_resp_content_type_str, &m_proxy_resp_content_type_len); | |
| } | |
| char *reason_str = m_data->get_proxy_resp_reason_phrase_str(); | |
| int reason_len = m_data->get_proxy_resp_reason_phrase_len(); | |
| if (reason_str != nullptr && reason_len > 0) { | |
| m_proxy_resp_reason_phrase_str = reason_str; | |
| m_proxy_resp_reason_phrase_len = reason_len; | |
| } | |
| m_proxy_request = m_data->get_proxy_request(); | |
| m_server_response = m_data->get_server_response(); | |
| m_cache_response = m_data->get_cache_response(); | |
| } | |
| int | |
| LogAccess::marshal_proxy_host_name(char *buf) | |
| { | |
| int len = 0; | |
| char const *str = nullptr; | |
| if (Machine *machine = Machine::instance(); machine) { | |
| str = machine->host_name.c_str(); | |
| len = machine->host_name.length(); | |
| } | |
| len = INK_ALIGN_DEFAULT(len + 1); | |
| if (buf) { | |
| marshal_str(buf, str, len); | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_host_ip(char *buf) | |
| { | |
| return marshal_ip(buf, &Machine::instance()->ip.sa); | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_process_uuid(char *buf) | |
| { | |
| int len = padded_length(TS_UUID_STRING_LEN + 1); | |
| if (buf) { | |
| const char *str = const_cast<char *>(Machine::instance()->process_uuid.getString()); | |
| marshal_str(buf, str, len); | |
| } | |
| return len; | |
| } | |
| int | |
| LogAccess::marshal_process_sfid(char *buf) | |
| { | |
| char const *str = nullptr; | |
| int len = 0; | |
| if (Machine *machine = Machine::instance(); machine) { | |
| std::string_view snowflake_id = machine->process_snowflake_id->get_string(); | |
| str = snowflake_id.data(); | |
| len = snowflake_id.length(); | |
| } | |
| len = INK_ALIGN_DEFAULT(len + 1); | |
| if (buf) { | |
| marshal_str(buf, str, len); | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_config_int_var(char *config_var, char *buf) | |
| { | |
| if (buf) { | |
| int64_t val; | |
| val = RecGetRecordInt(config_var).value_or(0); | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_config_str_var(char *config_var, char *buf) | |
| { | |
| auto str{RecGetRecordStringAlloc(config_var)}; | |
| auto c_str{ats_as_c_str(str)}; | |
| int len = LogAccess::padded_strlen(c_str); | |
| if (buf) { | |
| marshal_str(buf, c_str, len); | |
| } | |
| return len; | |
| } | |
| // To allow for a generic marshal_record function, rather than | |
| // multiple functions (one per data type) we always marshal a record | |
| // as a string of a fixed length. We use a fixed length because the | |
| // marshal_record function can be called with a null *buf to request | |
| // the length of the record, and later with a non-null *buf to | |
| // actually request the record to be inserted in the buffer, and both | |
| // calls should return the same number of characters. If we did not | |
| // enforce a fixed size, this would not necessarily be the case | |
| // because records --statistics in particular-- can potentially change | |
| // between one call and the other. | |
| // | |
| int | |
| LogAccess::marshal_record(char *record, char *buf) | |
| { | |
| const unsigned int max_chars = MARSHAL_RECORD_LENGTH; | |
| if (nullptr == buf) { | |
| return max_chars; | |
| } | |
| const char *record_not_found_msg = "RECORD_NOT_FOUND"; | |
| const unsigned int record_not_found_chars = ::strlen(record_not_found_msg) + 1; | |
| char ascii_buf[max_chars]; | |
| const char *out_buf; | |
| unsigned int num_chars; | |
| #define LOG_INTEGER RECD_INT | |
| #define LOG_COUNTER RECD_COUNTER | |
| #define LOG_FLOAT RECD_FLOAT | |
| #define LOG_STRING RECD_STRING | |
| RecDataT stype = RECD_NULL; | |
| bool found = false; | |
| // Since, for now at least, String metrics are still in librecords, do that lookup | |
| // first, and only do the new metrics lookup on a miss. | |
| if (RecGetRecordDataType(record, &stype) != REC_ERR_OKAY) { | |
| ts::Metrics &metrics = ts::Metrics::instance(); | |
| ts::Metrics::IdType mid = metrics[record]; | |
| if (mid != ts::Metrics::NOT_FOUND) { | |
| int64_t val = metrics[mid].load(); | |
| out_buf = int64_to_str(ascii_buf, max_chars, val, &num_chars); | |
| ink_assert(out_buf); | |
| } else { | |
| out_buf = "INVALID_RECORD"; | |
| num_chars = ::strlen(out_buf) + 1; | |
| } | |
| } else { | |
| if (LOG_INTEGER == stype || LOG_COUNTER == stype) { | |
| // we assume MgmtInt and MgmtIntCounter are int64_t for the | |
| // conversion below, if this ever changes we should modify | |
| // accordingly | |
| // | |
| ink_assert(sizeof(int64_t) >= sizeof(RecInt) && sizeof(int64_t) >= sizeof(RecCounter)); | |
| // so that a 64 bit integer will fit (including sign and eos) | |
| // | |
| ink_assert(max_chars > 21); | |
| auto tmp{LOG_INTEGER == stype ? RecGetRecordInt(record) : RecGetRecordCounter(record)}; | |
| auto found{tmp.has_value()}; | |
| if (found) { | |
| out_buf = int64_to_str(ascii_buf, max_chars, tmp.value(), &num_chars); | |
| ink_assert(out_buf); | |
| } else { | |
| out_buf = const_cast<char *>(record_not_found_msg); | |
| num_chars = record_not_found_chars; | |
| } | |
| } else if (LOG_FLOAT == stype) { | |
| // we assume MgmtFloat is at least a float for the conversion below | |
| // (the conversion itself assumes a double because of the %e) | |
| // if this ever changes we should modify accordingly | |
| // | |
| ink_assert(sizeof(double) >= sizeof(RecFloat)); | |
| auto val{RecGetRecordFloat(record)}; | |
| found = val.has_value(); | |
| if (found) { | |
| // snprintf does not support "%e" in the format | |
| // and we want to use "%e" because it is the most concise | |
| // notation | |
| num_chars = snprintf(ascii_buf, sizeof(ascii_buf), "%e", val.value()) + 1; // include eos | |
| // the "%e" field above should take 13 characters at most | |
| // | |
| ink_assert(num_chars <= max_chars); | |
| // the following should never be true | |
| // | |
| if (num_chars > max_chars) { | |
| // data does not fit, output asterisks | |
| out_buf = "***"; | |
| num_chars = ::strlen(out_buf) + 1; | |
| } else { | |
| out_buf = ascii_buf; | |
| } | |
| } else { | |
| out_buf = const_cast<char *>(record_not_found_msg); | |
| num_chars = record_not_found_chars; | |
| } | |
| } else if (LOG_STRING == stype) { | |
| if (auto sv{RecGetRecordString(record, ascii_buf, sizeof(ascii_buf))}; sv) { | |
| if (sv.value().length() > 0) { | |
| num_chars = sv.value().length() + 1; | |
| if (num_chars == max_chars) { | |
| // truncate string and write ellipsis at the end | |
| ascii_buf[max_chars - 1] = 0; | |
| ascii_buf[max_chars - 2] = '.'; | |
| ascii_buf[max_chars - 3] = '.'; | |
| ascii_buf[max_chars - 4] = '.'; | |
| } | |
| out_buf = ascii_buf; | |
| } else { | |
| out_buf = "NULL"; | |
| num_chars = ::strlen(out_buf) + 1; | |
| } | |
| } else { | |
| out_buf = const_cast<char *>(record_not_found_msg); | |
| num_chars = record_not_found_chars; | |
| } | |
| } else { | |
| out_buf = "INVALID_MgmtType"; | |
| num_chars = ::strlen(out_buf) + 1; | |
| ink_assert(!"invalid MgmtType for requested record"); | |
| } | |
| } | |
| ink_assert(num_chars <= max_chars); | |
| memcpy(buf, out_buf, num_chars); | |
| return max_chars; | |
| } | |
| /*------------------------------------------------------------------------- | |
| LogAccess::marshal_str | |
| Copy the given string to the destination buffer, including the trailing | |
| NULL. For binary formatting, we need the NULL to distinguish the end of | |
| the string, and we'll remove it for ascii formatting. | |
| ASSUMES dest IS NOT NULL. | |
| The array pointed to by dest must be at least padded_len in length. | |
| -------------------------------------------------------------------------*/ | |
| void | |
| LogAccess::marshal_str(char *dest, const char *source, int padded_len) | |
| { | |
| if (source == nullptr || source[0] == 0 || padded_len == 0) { | |
| source = DEFAULT_STR; | |
| } | |
| ink_strlcpy(dest, source, padded_len); | |
| #ifdef DEBUG | |
| // | |
| // what padded_len should be, if there is no padding, is strlen()+1. | |
| // if not, then we needed to pad and should touch the intermediate | |
| // bytes to avoid UMR errors when the buffer is written. | |
| // | |
| size_t real_len = (::strlen(source) + 1); | |
| while (static_cast<int>(real_len) < padded_len) { | |
| dest[real_len] = '$'; | |
| real_len++; | |
| } | |
| #endif | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_all_header_fields(char *buf) | |
| { | |
| return LogUtils::marshalMimeHdr(m_client_request, buf); | |
| } | |
| /*------------------------------------------------------------------------- | |
| LogAccess::marshal_mem | |
| This is a version of marshal_str that works with unterminated strings. | |
| In this case, we'll copy the buffer and then add a trailing null that | |
| the rest of the system assumes. | |
| -------------------------------------------------------------------------*/ | |
| void | |
| LogAccess::marshal_mem(char *dest, const char *source, int actual_len, int padded_len) | |
| { | |
| if (source == nullptr || source[0] == 0 || actual_len == 0) { | |
| source = DEFAULT_STR; | |
| actual_len = DEFAULT_STR_LEN; | |
| ink_assert(actual_len < padded_len); | |
| } | |
| memcpy(dest, source, actual_len); | |
| dest[actual_len] = 0; // add terminating null | |
| #ifdef DEBUG | |
| // | |
| // what len should be, if there is no padding, is strlen()+1. | |
| // if not, then we needed to pad and should touch the intermediate | |
| // bytes to avoid UMR errors when the buffer is written. | |
| // | |
| int real_len = actual_len + 1; | |
| while (real_len < padded_len) { | |
| dest[real_len] = '$'; | |
| real_len++; | |
| } | |
| #endif | |
| } | |
| /*------------------------------------------------------------------------- | |
| LogAccess::marshal_ip | |
| Marshal an IP address in a reasonably compact way. If the address isn't | |
| valid (NULL or not IP) then marshal an invalid address record. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_ip(char *dest, sockaddr const *ip) | |
| { | |
| LogFieldIpStorage data; | |
| int len = sizeof(data._ip); | |
| if (nullptr == ip) { | |
| data._ip._family = AF_UNSPEC; | |
| } else if (ats_is_ip4(ip)) { | |
| if (dest) { | |
| data._ip4._family = AF_INET; | |
| data._ip4._addr = ats_ip4_addr_cast(ip); | |
| } | |
| len = sizeof(data._ip4); | |
| } else if (ats_is_ip6(ip)) { | |
| if (dest) { | |
| data._ip6._family = AF_INET6; | |
| data._ip6._addr = ats_ip6_addr_cast(ip); | |
| } | |
| len = sizeof(data._ip6); | |
| } else if (ats_is_unix(ip)) { | |
| if (dest) { | |
| data._un._family = AF_UNIX; | |
| strncpy(data._un._path, ats_unix_cast(ip)->sun_path, TS_UNIX_SIZE); | |
| } | |
| len = sizeof(data._un); | |
| } else { | |
| data._ip._family = AF_UNSPEC; | |
| } | |
| if (dest) { | |
| memcpy(dest, &data, len); | |
| } | |
| return INK_ALIGN_DEFAULT(len); | |
| } | |
| int | |
| LogAccess::marshal_custom_field(char *buf, LogField::Type type, const LogField::CustomMarshalFunc &plugin_marshal_func) | |
| { | |
| void *sm = m_data->http_sm_for_plugins(); | |
| if (sm == nullptr) { | |
| switch (type) { | |
| case LogField::Type::sINT: | |
| [[fallthrough]]; | |
| case LogField::Type::dINT: | |
| if (buf) { | |
| marshal_int(buf, 0); | |
| } | |
| return INK_MIN_ALIGN; | |
| case LogField::Type::STRING: { | |
| int len = LogAccess::padded_strlen(nullptr); | |
| if (buf) { | |
| marshal_str(buf, nullptr, len); | |
| } | |
| return len; | |
| } | |
| case LogField::Type::IP: | |
| return marshal_ip(buf, nullptr); | |
| case LogField::Type::N_TYPES: | |
| [[fallthrough]]; | |
| case LogField::Type::INVALID: | |
| break; | |
| } | |
| } | |
| int len = plugin_marshal_func(sm, buf); | |
| return LogAccess::padded_length(len); | |
| } | |
| HTTPHdr * | |
| LogAccess::header_for_container(LogField::Container container) const | |
| { | |
| switch (container) { | |
| case LogField::CQH: | |
| case LogField::ECQH: | |
| return m_client_request; | |
| case LogField::PSH: | |
| case LogField::EPSH: | |
| return m_proxy_response; | |
| case LogField::PQH: | |
| case LogField::EPQH: | |
| return m_proxy_request; | |
| case LogField::SSH: | |
| case LogField::ESSH: | |
| return m_server_response; | |
| case LogField::CSSH: | |
| case LogField::ECSSH: | |
| return m_cache_response; | |
| default: | |
| return nullptr; | |
| } | |
| } | |
| bool | |
| LogAccess::has_http_header_field(LogField::Container container, const char *field) const | |
| { | |
| if (HTTPHdr const *header = header_for_container(container); header != nullptr) { | |
| if (header->field_find(std::string_view{field}) != nullptr) { | |
| return true; | |
| } | |
| } | |
| if (container == LogField::SSH && strcmp(field, "Transfer-Encoding") == 0) { | |
| if (!m_data->get_server_response_transfer_encoding().empty()) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| inline int | |
| LogAccess::unmarshal_with_map(int64_t code, char *dest, int len, const Ptr<LogFieldAliasMap> &map, const char *msg) | |
| { | |
| long int codeStrLen = 0; | |
| switch (map->asString(code, dest, len, reinterpret_cast<size_t *>(&codeStrLen))) { | |
| case LogFieldAliasMap::INVALID_INT: | |
| if (msg) { | |
| const int bufSize = 64; | |
| char invalidCodeMsg[bufSize]; | |
| codeStrLen = snprintf(invalidCodeMsg, 64, "%s(%" PRId64 ")", msg, code); | |
| if (codeStrLen < bufSize && codeStrLen < len) { | |
| ink_strlcpy(dest, invalidCodeMsg, len); | |
| } else { | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| codeStrLen = -1; | |
| } | |
| } else { | |
| codeStrLen = -1; | |
| } | |
| break; | |
| case LogFieldAliasMap::BUFFER_TOO_SMALL: | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| codeStrLen = -1; | |
| break; | |
| } | |
| return codeStrLen; | |
| } | |
| /*------------------------------------------------------------------------- | |
| LogAccess::unmarshal_int | |
| Return the integer pointed at by the buffer and advance the buffer | |
| pointer past the int. The int will be converted back to host byte order. | |
| -------------------------------------------------------------------------*/ | |
| int64_t | |
| LogAccess::unmarshal_int(char **buf) | |
| { | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| int64_t val; | |
| // TODO: this used to do nthol, do we need to worry? TS-1156. | |
| val = *(reinterpret_cast<int64_t *>(*buf)); | |
| *buf += INK_MIN_ALIGN; | |
| return val; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_resp_all_header_fields(char *buf) | |
| { | |
| return LogUtils::marshalMimeHdr(m_proxy_response, buf); | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| /*------------------------------------------------------------------------- | |
| unmarshal_itoa | |
| This routine provides a fast conversion from a binary int to a string. | |
| It returns the number of characters formatted. "dest" must point to the | |
| LAST character of an array large enough to store the complete formatted | |
| number. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::unmarshal_itoa(int64_t val, char *dest, int field_width, char leading_char) | |
| { | |
| ink_assert(dest != nullptr); | |
| char *p = dest; | |
| bool negative = false; | |
| if (val < 0) { | |
| negative = true; | |
| val = -val; | |
| } | |
| do { | |
| *p-- = '0' + (val % 10); | |
| val /= 10; | |
| } while (val); | |
| while (dest - p < field_width) { | |
| *p-- = leading_char; | |
| } | |
| if (negative) { | |
| *p-- = '-'; | |
| } | |
| return static_cast<int>(dest - p); | |
| } | |
| /*------------------------------------------------------------------------- | |
| unmarshal_itox | |
| This routine provides a fast conversion from a binary int to a hex string. | |
| It returns the number of characters formatted. "dest" must point to the | |
| LAST character of an array large enough to store the complete formatted | |
| number. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::unmarshal_itox(int64_t val, char *dest, int field_width, char leading_char) | |
| { | |
| ink_assert(dest != nullptr); | |
| char *p = dest; | |
| static char table[] = "0123456789abcdef?"; | |
| for (int i = 0; i < static_cast<int>(sizeof(int64_t) * 2); i++) { | |
| *p-- = table[val & 0xf]; | |
| val >>= 4; | |
| } | |
| while (dest - p < field_width) { | |
| *p-- = leading_char; | |
| } | |
| return static_cast<int64_t>(dest - p); | |
| } | |
| /*------------------------------------------------------------------------- | |
| LogAccess::unmarshal_int_to_str | |
| Return the string representation of the integer pointed at by buf. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::unmarshal_int_to_str(char **buf, char *dest, int len) | |
| { | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| ink_assert(dest != nullptr); | |
| char val_buf[128]; | |
| int64_t val = unmarshal_int(buf); | |
| int val_len = unmarshal_itoa(val, val_buf + 127); | |
| if (val_len < len) { | |
| memcpy(dest, val_buf + 128 - val_len, val_len); | |
| return val_len; | |
| } | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| return -1; | |
| } | |
| /*------------------------------------------------------------------------- | |
| LogAccess::unmarshal_milestone_diff | |
| Unmarshal a milestone difference value. Returns "-" when the | |
| marshalled value is -1 (the "missing" sentinel from difference_msec, | |
| meaning one or both milestones were unset). Other negative values | |
| (reversed milestone order) are preserved as numeric output for | |
| debugging. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::unmarshal_milestone_diff(char **buf, char *dest, int len) | |
| { | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| ink_assert(dest != nullptr); | |
| int64_t val = unmarshal_int(buf); | |
| if (val == -1) { | |
| if (len >= 1) { | |
| dest[0] = '-'; | |
| return 1; | |
| } | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| return -1; | |
| } | |
| char val_buf[128]; | |
| int val_len = unmarshal_itoa(val, val_buf + 127); | |
| if (val_len < len) { | |
| memcpy(dest, val_buf + 128 - val_len, val_len); | |
| return val_len; | |
| } | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| return -1; | |
| } | |
| /*------------------------------------------------------------------------- | |
| LogAccess::unmarshal_int_to_str_hex | |
| Return the string representation (hexadecimal) of the integer pointed at by buf. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::unmarshal_int_to_str_hex(char **buf, char *dest, int len) | |
| { | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| ink_assert(dest != nullptr); | |
| char val_buf[128]; | |
| int64_t val = unmarshal_int(buf); | |
| int val_len = unmarshal_itox(val, val_buf + 127); | |
| if (val_len < len) { | |
| memcpy(dest, val_buf + 128 - val_len, val_len); | |
| return val_len; | |
| } | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| return -1; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_req_all_header_fields(char *buf) | |
| { | |
| return LogUtils::marshalMimeHdr(m_proxy_request, buf); | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| namespace | |
| { | |
| class EscLookup | |
| { | |
| public: | |
| static const char NO_ESCAPE{'\0'}; | |
| static const char LONG_ESCAPE{'\x01'}; | |
| static char | |
| result(char c) | |
| { | |
| return _lu.table[static_cast<unsigned char>(c)]; | |
| } | |
| private: | |
| struct _LUT { | |
| _LUT(); | |
| char table[1 << 8]; | |
| }; | |
| inline static _LUT const _lu; | |
| }; | |
| EscLookup::_LUT::_LUT() | |
| { | |
| for (unsigned i = 0; i < ' '; ++i) { | |
| table[i] = LONG_ESCAPE; | |
| } | |
| for (unsigned i = '\x7f'; i < sizeof(table); ++i) { | |
| table[i] = LONG_ESCAPE; | |
| } | |
| // Short escapes. | |
| // | |
| table[static_cast<int>('\b')] = 'b'; | |
| table[static_cast<int>('\t')] = 't'; | |
| table[static_cast<int>('\n')] = 'n'; | |
| table[static_cast<int>('\f')] = 'f'; | |
| table[static_cast<int>('\r')] = 'r'; | |
| table[static_cast<int>('\\')] = '\\'; | |
| table[static_cast<int>('\"')] = '"'; | |
| table[static_cast<int>('/')] = '/'; | |
| } | |
| char | |
| nibble(int nib) | |
| { | |
| return nib >= 0xa ? 'a' + (nib - 0xa) : '0' + nib; | |
| } | |
| // Escape at most buf_len source bytes, writing at most dest_len bytes (dest_len is ignored when dest is nullptr). | |
| int | |
| escape_json(char *dest, const char *buf, int buf_len, int dest_len) | |
| { | |
| int escaped_len = 0; | |
| for (int i = 0; i < buf_len; i++) { | |
| char c = buf[i]; | |
| char ec = EscLookup::result(c); | |
| if (__builtin_expect(EscLookup::NO_ESCAPE == ec, 1)) { | |
| if (dest) { | |
| if (escaped_len + 1 > dest_len) { | |
| break; | |
| } | |
| *dest++ = c; | |
| } | |
| escaped_len++; | |
| } else if (EscLookup::LONG_ESCAPE == ec) { | |
| if (dest) { | |
| if (escaped_len + 6 > dest_len) { | |
| break; | |
| } | |
| *dest++ = '\\'; | |
| *dest++ = 'u'; | |
| *dest++ = '0'; | |
| *dest++ = '0'; | |
| *dest++ = nibble(static_cast<unsigned char>(c) >> 4); | |
| *dest++ = nibble(c & 0x0f); | |
| } | |
| escaped_len += 6; | |
| } else { // Short escape. | |
| if (dest) { | |
| if (escaped_len + 2 > dest_len) { | |
| break; | |
| } | |
| *dest++ = '\\'; | |
| *dest++ = ec; | |
| } | |
| escaped_len += 2; | |
| } | |
| } // end for | |
| return escaped_len; | |
| } | |
| int | |
| unmarshal_str_json(char **buf, char *dest, int len, LogSlice *slice) | |
| { | |
| Dbg(dbg_ctl_log_escape, "unmarshal_str_json start, len=%d, slice=%p", len, slice); | |
| char *val_buf = *buf; | |
| int val_len = static_cast<int>(::strlen(val_buf)); | |
| int escaped_len = escape_json(nullptr, val_buf, val_len, 0); | |
| *buf += LogAccess::padded_strlen(val_buf); // this is how it was stored | |
| if (slice && slice->m_enable) { | |
| int offset, n; | |
| // start is a raw position, while n caps the escaped output (see "Log Field Slicing" in the docs). | |
| n = slice->toStrOffset(escaped_len, &offset); | |
| Dbg(dbg_ctl_log_escape, "unmarshal_str_json start, n=%d, offset=%d", n, offset); | |
| if (n <= 0 || offset >= val_len) { | |
| return 0; | |
| } | |
| if (n >= len) { | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| return -1; | |
| } | |
| return escape_json(dest, (val_buf + offset), val_len - offset, n); | |
| } | |
| if (escaped_len < len) { | |
| escape_json(dest, val_buf, val_len, len); | |
| return escaped_len; | |
| } | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| return -1; | |
| } | |
| } // end anonymous namespace | |
| /*------------------------------------------------------------------------- | |
| LogAccess::unmarshal_str | |
| Retrieve the string from the location pointed at by the buffer and | |
| advance the pointer past the string. The local strlen function is used | |
| to advance the pointer, thus matching the corresponding strlen that was | |
| used to lay the string into the buffer. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::unmarshal_str(char **buf, char *dest, int len, LogSlice *slice, LogEscapeType escape_type) | |
| { | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| ink_assert(dest != nullptr); | |
| if (LOG_ESCAPE_JSON == escape_type) { | |
| return unmarshal_str_json(buf, dest, len, slice); | |
| } | |
| char *val_buf = *buf; | |
| int val_len = static_cast<int>(::strlen(val_buf)); | |
| *buf += LogAccess::padded_strlen(val_buf); // this is how it was stored | |
| if (slice && slice->m_enable) { | |
| int offset, n; | |
| n = slice->toStrOffset(val_len, &offset); | |
| if (n <= 0) { | |
| return 0; | |
| } | |
| if (n >= len) { | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| return -1; | |
| } | |
| memcpy(dest, (val_buf + offset), n); | |
| return n; | |
| } | |
| if (val_len < len) { | |
| memcpy(dest, val_buf, val_len); | |
| return val_len; | |
| } | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| return -1; | |
| } | |
| int | |
| LogAccess::unmarshal_ttmsf(char **buf, char *dest, int len) | |
| { | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| ink_assert(dest != nullptr); | |
| int64_t val = unmarshal_int(buf); | |
| int val_len = snprintf(dest, len, "%" PRId64 ".%03d", val / 1000, int((val < 0 ? -val : val) % 1000)); | |
| if (val_len >= len) { | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| return -1; | |
| } | |
| return val_len; | |
| } | |
| int | |
| LogAccess::unmarshal_int_to_date_str(char **buf, char *dest, int len) | |
| { | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| ink_assert(dest != nullptr); | |
| int64_t value = unmarshal_int(buf); | |
| char *strval = LogUtils::timestamp_to_date_str(value); | |
| int strlen = static_cast<int>(::strlen(strval)); | |
| if (strlen > len) { | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| return -1; | |
| } | |
| memcpy(dest, strval, strlen); | |
| return strlen; | |
| } | |
| int | |
| LogAccess::unmarshal_int_to_time_str(char **buf, char *dest, int len) | |
| { | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| ink_assert(dest != nullptr); | |
| int64_t value = unmarshal_int(buf); | |
| char *strval = LogUtils::timestamp_to_time_str(value); | |
| int strlen = static_cast<int>(::strlen(strval)); | |
| if (strlen > len) { | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| return -1; | |
| } | |
| memcpy(dest, strval, strlen); | |
| return strlen; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_server_resp_all_header_fields(char *buf) | |
| { | |
| return LogUtils::marshalMimeHdr(m_server_response, buf); | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::unmarshal_int_to_netscape_str(char **buf, char *dest, int len) | |
| { | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| ink_assert(dest != nullptr); | |
| int64_t value = unmarshal_int(buf); | |
| char *strval = LogUtils::timestamp_to_netscape_str(value); | |
| int strlen = static_cast<int>(::strlen(strval)); | |
| if (strlen > len) { | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| return -1; | |
| } | |
| memcpy(dest, strval, strlen); | |
| return strlen; | |
| } | |
| /*------------------------------------------------------------------------- | |
| LogAccess::unmarshal_http_method | |
| Retrieve the int pointed at by the buffer and treat as an HttpMethod | |
| enumerated type. Then lookup the string representation for that enum and | |
| return the string. Advance the buffer pointer past the enum. | |
| -------------------------------------------------------------------------*/ | |
| /* | |
| int | |
| LogAccess::unmarshal_http_method (char **buf, char *dest, int len) | |
| { | |
| return unmarshal_str (buf, dest, len); | |
| } | |
| */ | |
| int | |
| LogAccess::marshal_cache_resp_all_header_fields(char *buf) | |
| { | |
| return LogUtils::marshalMimeHdr(m_cache_response, buf); | |
| } | |
| /*------------------------------------------------------------------------- | |
| LogAccess::unmarshal_http_version | |
| The HTTP version is marshalled as a "major.minor" string. Prefix it with | |
| "HTTP/" for the established text log representation. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::unmarshal_http_version(char **buf, char *dest, int len) | |
| { | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| ink_assert(dest != nullptr); | |
| static constexpr std::string_view prefix{"HTTP/"}; | |
| char *version = *buf; | |
| int version_len = static_cast<int>(strlen(version)); | |
| int value_len = static_cast<int>(prefix.size()) + version_len; | |
| *buf += padded_strlen(version); | |
| if (value_len <= len) { | |
| memcpy(dest, prefix.data(), prefix.size()); | |
| memcpy(dest + prefix.size(), version, version_len); | |
| return value_len; | |
| } | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| return -1; | |
| } | |
| /*------------------------------------------------------------------------- | |
| LogAccess::unmarshal_http_status | |
| An http response status code (pssc,sssc) is just an INT, but it's always | |
| formatted with three digits and leading zeros. So, we need a special | |
| version of unmarshal_int_to_str that does this leading zero formatting. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::unmarshal_http_status(char **buf, char *dest, int len) | |
| { | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| ink_assert(dest != nullptr); | |
| char val_buf[128]; | |
| int64_t val = unmarshal_int(buf); | |
| int val_len = unmarshal_itoa(val, val_buf + 127, 3, '0'); | |
| if (val_len < len) { | |
| memcpy(dest, val_buf + 128 - val_len, val_len); | |
| return val_len; | |
| } | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| return -1; | |
| } | |
| /*------------------------------------------------------------------------- | |
| LogAccess::unmarshal_ip | |
| Retrieve an IP address directly. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::unmarshal_ip(char **buf, IpEndpoint *dest) | |
| { | |
| int len = sizeof(LogFieldIp); // of object processed. | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| ink_assert(dest != nullptr); | |
| LogFieldIp *raw = reinterpret_cast<LogFieldIp *>(*buf); | |
| if (AF_INET == raw->_family) { | |
| LogFieldIp4 *ip4 = static_cast<LogFieldIp4 *>(raw); | |
| ats_ip4_set(dest, ip4->_addr); | |
| len = sizeof(*ip4); | |
| } else if (AF_INET6 == raw->_family) { | |
| LogFieldIp6 *ip6 = static_cast<LogFieldIp6 *>(raw); | |
| ats_ip6_set(dest, ip6->_addr); | |
| len = sizeof(*ip6); | |
| } else if (AF_UNIX == raw->_family) { | |
| LogFieldUn *un = static_cast<LogFieldUn *>(raw); | |
| ats_unix_set(dest, un->_path, TS_UNIX_SIZE); | |
| len = sizeof(*un); | |
| } else { | |
| ats_ip_invalidate(dest); | |
| } | |
| len = INK_ALIGN_DEFAULT(len); | |
| *buf += len; | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| LogAccess::unmarshal_ip_to_str | |
| Retrieve the IP addresspointed at by the buffer and convert to a | |
| string in standard format. The string is written to @a dest and its | |
| length (not including nul) is returned. @a *buf is advanced. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::unmarshal_ip_to_str(char **buf, char *dest, int len) | |
| { | |
| IpEndpoint ip; | |
| if (len > 0) { | |
| unmarshal_ip(buf, &ip); | |
| if (!ats_is_ip(&ip) && !ats_is_unix(ip)) { | |
| *dest = '0'; | |
| Dbg(dbg_ctl_log_unmarshal_data, "Invalid IP address"); | |
| return 1; | |
| } else if (ats_ip_ntop(&ip, dest, len)) { | |
| return static_cast<int>(::strlen(dest)); | |
| } | |
| } | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| return -1; | |
| } | |
| /*------------------------------------------------------------------------- | |
| LogAccess::unmarshal_ip_to_hex | |
| Retrieve the int pointed at by the buffer and treat as an IP | |
| address. Convert to a string in byte oriented hexadecimal and | |
| return the string. Advance the buffer pointer. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::unmarshal_ip_to_hex(char **buf, char *dest, int len) | |
| { | |
| IpEndpoint ip; | |
| if (len > 0) { | |
| unmarshal_ip(buf, &ip); | |
| if (!ats_is_ip(&ip) && !ats_is_unix(ip)) { | |
| *dest = '0'; | |
| Dbg(dbg_ctl_log_unmarshal_data, "Invalid IP address"); | |
| return 1; | |
| } else { | |
| return ats_ip_to_hex(&ip.sa, dest, len); | |
| } | |
| } | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| return -1; | |
| } | |
| /*------------------------------------------------------------------------- | |
| LogAccess::unmarshal_hierarchy | |
| Retrieve the int pointed at by the buffer and treat as a | |
| SquidHierarchyCode. Use this as an index into the local string | |
| conversion tables and return the string equivalent to the enum. | |
| Advance the buffer pointer. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::unmarshal_hierarchy(char **buf, char *dest, int len, const Ptr<LogFieldAliasMap> &map) | |
| { | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| ink_assert(dest != nullptr); | |
| return (LogAccess::unmarshal_with_map(unmarshal_int(buf), dest, len, map, "INVALID_CODE")); | |
| } | |
| /*------------------------------------------------------------------------- | |
| LogAccess::unmarshal_finish_status | |
| Retrieve the int pointed at by the buffer and treat as a finish code. | |
| Use the enum as an index into a string table and return the string equiv | |
| of the enum. Advance the pointer. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::unmarshal_finish_status(char **buf, char *dest, int len, const Ptr<LogFieldAliasMap> &map) | |
| { | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| ink_assert(dest != nullptr); | |
| return (LogAccess::unmarshal_with_map(unmarshal_int(buf), dest, len, map, "UNKNOWN_FINISH_CODE")); | |
| } | |
| /*------------------------------------------------------------------------- | |
| LogAccess::unmarshal_cache_code | |
| Retrieve the int pointed at by the buffer and treat as a SquidLogCode. | |
| Use this to index into the local string tables and return the string | |
| equiv of the enum. Advance the pointer. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::unmarshal_cache_code(char **buf, char *dest, int len, const Ptr<LogFieldAliasMap> &map) | |
| { | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| ink_assert(dest != nullptr); | |
| return (LogAccess::unmarshal_with_map(unmarshal_int(buf), dest, len, map, "ERROR_UNKNOWN")); | |
| } | |
| /*------------------------------------------------------------------------- | |
| LogAccess::unmarshal_cache_hit_miss | |
| Retrieve the int pointed at by the buffer and treat as a SquidHitMissCode. | |
| Use this to index into the local string tables and return the string | |
| equiv of the enum. Advance the pointer. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::unmarshal_cache_hit_miss(char **buf, char *dest, int len, const Ptr<LogFieldAliasMap> &map) | |
| { | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| ink_assert(dest != nullptr); | |
| return (LogAccess::unmarshal_with_map(unmarshal_int(buf), dest, len, map, "HIT_MISS_UNKNOWN")); | |
| } | |
| int | |
| LogAccess::unmarshal_cache_write_code(char **buf, char *dest, int len, const Ptr<LogFieldAliasMap> &map) | |
| { | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| ink_assert(dest != nullptr); | |
| return (LogAccess::unmarshal_with_map(unmarshal_int(buf), dest, len, map, "UNKNOWN_CACHE_WRITE_CODE")); | |
| } | |
| int | |
| LogAccess::unmarshal_record(char **buf, char *dest, int len) | |
| { | |
| ink_assert(buf != nullptr); | |
| ink_assert(*buf != nullptr); | |
| ink_assert(dest != nullptr); | |
| char *val_buf = *buf; | |
| int val_len = static_cast<int>(::strlen(val_buf)); | |
| *buf += MARSHAL_RECORD_LENGTH; // this is how it was stored | |
| if (val_len < len) { | |
| memcpy(dest, val_buf, val_len); | |
| return val_len; | |
| } | |
| DBG_UNMARSHAL_DEST_OVERRUN | |
| return -1; | |
| } | |
| /*------------------------------------------------------------------------- | |
| resolve_logfield_string | |
| This function resolves the given custom log format string using the given | |
| LogAccess context and returns the resulting string, which is ats_malloc'd. | |
| The caller is responsible for ats_free'ing the return result. If there are | |
| any problems, NULL is returned. | |
| -------------------------------------------------------------------------*/ | |
| char * | |
| resolve_logfield_string(LogAccess *context, const char *format_str) | |
| { | |
| if (!context) { | |
| Dbg(dbg_ctl_log_resolve, "No context to resolve?"); | |
| return nullptr; | |
| } | |
| if (!format_str) { | |
| Dbg(dbg_ctl_log_resolve, "No format to resolve?"); | |
| return nullptr; | |
| } | |
| Dbg(dbg_ctl_log_resolve, "Resolving: %s", format_str); | |
| // | |
| // Divide the format string into two parts: one for the printf-style | |
| // string and one for the symbols. | |
| // | |
| char *printf_str = nullptr; | |
| char *fields_str = nullptr; | |
| int n_fields = LogFormat::parse_format_string(format_str, &printf_str, &fields_str); | |
| // | |
| // Perhaps there were no fields to resolve? Then just return the | |
| // format_str. Nothing to free here either. | |
| // | |
| if (!n_fields) { | |
| Dbg(dbg_ctl_log_resolve, "No fields found; returning copy of format_str"); | |
| ats_free(printf_str); | |
| ats_free(fields_str); | |
| return ats_strdup(format_str); | |
| } | |
| Dbg(dbg_ctl_log_resolve, "%d fields: %s", n_fields, fields_str); | |
| Dbg(dbg_ctl_log_resolve, "printf string: %s", printf_str); | |
| LogFieldList fields; | |
| bool contains_aggregates; | |
| int field_count = LogFormat::parse_symbol_string(fields_str, &fields, &contains_aggregates); | |
| if (field_count != n_fields) { | |
| Error("format_str contains %d invalid field symbols", n_fields - field_count); | |
| ats_free(printf_str); | |
| ats_free(fields_str); | |
| return nullptr; | |
| } | |
| // | |
| // Ok, now marshal the data out of the LogAccess object and into a | |
| // temporary storage buffer. Make sure the LogAccess context is | |
| // initialized first. | |
| // | |
| Dbg(dbg_ctl_log_resolve, "Marshaling data from LogAccess into buffer ..."); | |
| context->init(); | |
| unsigned bytes_needed = fields.marshal_len(context); | |
| char *buf = static_cast<char *>(ats_malloc(bytes_needed)); | |
| unsigned bytes_used = fields.marshal(context, buf); | |
| ink_assert(bytes_needed == bytes_used); | |
| Dbg(dbg_ctl_log_resolve, " %u bytes marshalled", bytes_used); | |
| // | |
| // Now we can "unmarshal" the data from the buffer into a string, | |
| // combining it with the data from the printf string. The problem is, | |
| // we're not sure how much space it will take when it's unmarshalled. | |
| // So, we'll just guess. | |
| // | |
| char *result = static_cast<char *>(ats_malloc(8192)); | |
| unsigned bytes_resolved = | |
| LogBuffer::resolve_custom_entry(&fields, printf_str, buf, result, 8191, LogUtils::timestamp(), 0, LOG_SEGMENT_VERSION); | |
| ink_assert(bytes_resolved < 8192); | |
| if (!bytes_resolved) { | |
| ats_free(result); | |
| result = nullptr; | |
| } else { | |
| result[bytes_resolved] = 0; // NULL terminate | |
| } | |
| ats_free(printf_str); | |
| ats_free(fields_str); | |
| ats_free(buf); | |
| return result; | |
| } | |
| void | |
| LogAccess::set_client_req_url(char *buf, int len) | |
| { | |
| if (buf) { | |
| m_client_req_url_len = std::min(len, m_client_req_url_len); | |
| ink_strlcpy(m_client_req_url_str, buf, m_client_req_url_len + 1); | |
| } | |
| } | |
| void | |
| LogAccess::set_client_req_url_canon(char *buf, int len) | |
| { | |
| if (buf) { | |
| m_client_req_url_canon_len = std::min(len, m_client_req_url_canon_len); | |
| ink_strlcpy(m_client_req_url_canon_str, buf, m_client_req_url_canon_len + 1); | |
| } | |
| } | |
| void | |
| LogAccess::set_client_req_unmapped_url_canon(char *buf, int len) | |
| { | |
| if (buf && m_client_req_unmapped_url_canon_str) { | |
| // m_client_req_unmapped_url_canon_str is not necessarily null terminated. | |
| m_client_req_unmapped_url_canon_len = std::min(len, m_client_req_unmapped_url_canon_len); | |
| memcpy(m_client_req_unmapped_url_canon_str, buf, m_client_req_unmapped_url_canon_len); | |
| } | |
| } | |
| void | |
| LogAccess::set_client_req_unmapped_url_path(char *buf, int len) | |
| { | |
| if (buf && m_client_req_unmapped_url_path_str) { | |
| m_client_req_unmapped_url_path_len = std::min(len, m_client_req_unmapped_url_path_len); | |
| ink_strlcpy(m_client_req_unmapped_url_path_str, buf, m_client_req_unmapped_url_path_len + 1); | |
| } | |
| } | |
| void | |
| LogAccess::set_client_req_unmapped_url_host(char *buf, int len) | |
| { | |
| if (buf && m_client_req_unmapped_url_host_str) { | |
| m_client_req_unmapped_url_host_len = std::min(len, m_client_req_unmapped_url_host_len); | |
| ink_strlcpy(m_client_req_unmapped_url_host_str, buf, m_client_req_unmapped_url_host_len + 1); | |
| } | |
| } | |
| void | |
| LogAccess::set_client_req_url_path(char *buf, int len) | |
| { | |
| //?? use m_client_req_unmapped_url_path_str for now..may need to enhance later.. | |
| this->set_client_req_unmapped_url_path(buf, len); | |
| } | |
| /*------------------------------------------------------------------------- | |
| The marshalling routines ... | |
| Header pointers may be null; each routine must guard before use. | |
| -------------------------------------------------------------------------*/ | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_plugin_identity_id(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_plugin_id()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_plugin_identity_tag(char *buf) | |
| { | |
| int len = INK_MIN_ALIGN; | |
| const char *tag = m_data->get_plugin_tag(); | |
| if (!tag) { | |
| tag = "*"; | |
| } else { | |
| len = LogAccess::padded_strlen(tag); | |
| } | |
| if (buf) { | |
| marshal_str(buf, tag, len); | |
| } | |
| return len; | |
| } | |
| int | |
| LogAccess::marshal_client_host_ip(char *buf) | |
| { | |
| return marshal_ip(buf, m_data->get_client_addr()); | |
| } | |
| int | |
| LogAccess::marshal_remote_host_ip(char *buf) | |
| { | |
| return marshal_ip(buf, m_data->get_client_src_addr()); | |
| } | |
| int | |
| LogAccess::marshal_host_interface_ip(char *buf) | |
| { | |
| return marshal_ip(buf, m_data->get_client_dst_addr()); | |
| } | |
| int | |
| LogAccess::marshal_client_host_ip_verified(char *buf) | |
| { | |
| if (m_data->get_verified_client_addr()) { | |
| return marshal_ip(buf, m_data->get_verified_client_addr()); | |
| } | |
| return marshal_client_host_ip(buf); | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_cache_lookup_url_canon(char *buf) | |
| { | |
| int len = INK_MIN_ALIGN; | |
| validate_lookup_url(); | |
| if (m_cache_lookup_url_canon_str == INVALID_STR) { | |
| // If the lookup URL isn't populated, we'll fall back to the request URL. | |
| len = marshal_client_req_url_canon(buf); | |
| } else { | |
| len = padded_length(m_cache_lookup_url_canon_len + 1); // +1 for eos | |
| if (buf) { | |
| marshal_mem(buf, m_cache_lookup_url_canon_str, m_cache_lookup_url_canon_len, len); | |
| } | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_sni_server_name(char *buf) | |
| { | |
| const char *sni = m_data->get_sni_server_name(); | |
| const char *server_name_cstr = sni ? sni : ""; | |
| int len = LogAccess::padded_strlen(server_name_cstr); | |
| if (buf) { | |
| marshal_str(buf, server_name_cstr, len); | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_provided_cert(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_client_provided_cert()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_provided_cert(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_server_connection_provided_cert()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_version_build_number(char *buf) | |
| { | |
| auto &version = AppVersionInfo::get_version(); | |
| int len = LogAccess::padded_strlen(version.build_number()); | |
| if (buf) { | |
| marshal_str(buf, version.build_number(), len); | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_version_string(char *buf) | |
| { | |
| auto &version = AppVersionInfo::get_version(); | |
| int len = LogAccess::padded_strlen(version.version()); | |
| if (buf) { | |
| marshal_str(buf, version.version(), len); | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_protocol_version(char *buf) | |
| { | |
| const char *version_str; | |
| switch (m_data->get_pp_version()) { | |
| case 1: | |
| version_str = "V1"; | |
| break; | |
| case 2: | |
| version_str = "V2"; | |
| break; | |
| default: | |
| version_str = "-"; | |
| break; | |
| } | |
| int len = LogAccess::padded_strlen(version_str); | |
| if (buf) { | |
| marshal_str(buf, version_str, len); | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_protocol_src_ip(char *buf) | |
| { | |
| sockaddr const *ip = nullptr; | |
| if (m_data->get_pp_version() != 0) { | |
| ip = m_data->get_pp_src_addr(); | |
| } | |
| return marshal_ip(buf, ip); | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_protocol_dst_ip(char *buf) | |
| { | |
| sockaddr const *ip = nullptr; | |
| if (m_data->get_pp_version() != 0) { | |
| ip = m_data->get_pp_dst_addr(); | |
| } | |
| return marshal_ip(buf, ip); | |
| } | |
| int | |
| LogAccess::marshal_proxy_protocol_authority(char *buf) | |
| { | |
| int len = INK_MIN_ALIGN; | |
| std::string_view authority = m_data->get_pp_authority(); | |
| if (!authority.empty()) { | |
| len = padded_length(static_cast<int>(authority.size()) + 1); | |
| if (buf) { | |
| marshal_mem(buf, authority.data(), static_cast<int>(authority.size()), len); | |
| } | |
| } | |
| return len; | |
| } | |
| int | |
| LogAccess::marshal_proxy_protocol_tls_cipher(char *buf) | |
| { | |
| int len = INK_MIN_ALIGN; | |
| std::string_view cipher = m_data->get_pp_tls_cipher(); | |
| if (!cipher.empty()) { | |
| len = padded_length(static_cast<int>(cipher.size()) + 1); | |
| if (buf) { | |
| marshal_mem(buf, cipher.data(), static_cast<int>(cipher.size()), len); | |
| } | |
| } else { | |
| if (buf) { | |
| marshal_mem(buf, nullptr, 0, len); | |
| } | |
| } | |
| return len; | |
| } | |
| int | |
| LogAccess::marshal_proxy_protocol_tls_version(char *buf) | |
| { | |
| int len = INK_MIN_ALIGN; | |
| std::string_view version = m_data->get_pp_tls_version(); | |
| if (!version.empty()) { | |
| len = padded_length(static_cast<int>(version.size()) + 1); | |
| if (buf) { | |
| marshal_mem(buf, version.data(), static_cast<int>(version.size()), len); | |
| } | |
| } else { | |
| if (buf) { | |
| marshal_mem(buf, nullptr, 0, len); | |
| } | |
| } | |
| return len; | |
| } | |
| int | |
| LogAccess::marshal_proxy_protocol_tls_group(char *buf) | |
| { | |
| int len = INK_MIN_ALIGN; | |
| std::string_view group = m_data->get_pp_tls_group(); | |
| if (!group.empty()) { | |
| len = padded_length(static_cast<int>(group.size()) + 1); | |
| if (buf) { | |
| marshal_mem(buf, group.data(), static_cast<int>(group.size()), len); | |
| } | |
| } else { | |
| if (buf) { | |
| marshal_mem(buf, nullptr, 0, len); | |
| } | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_host_port(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_client_port()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_remote_host_port(char *buf) | |
| { | |
| if (buf) { | |
| sockaddr const *src = m_data->get_client_src_addr(); | |
| uint16_t port = src ? ats_ip_port_host_order(src) : 0; | |
| marshal_int(buf, port); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| user authenticated to the proxy (RFC931) | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_auth_user_name(char *buf) | |
| { | |
| char *str = nullptr; | |
| int len = INK_MIN_ALIGN; | |
| // Jira TS-40: | |
| // NOTE: Authentication related code and modules were removed/disabled. | |
| // Uncomment code path below when re-added/enabled. | |
| /*if (auth_params.user_name) { | |
| str = auth_params.user_name; | |
| len = LogAccess::strlen(str); | |
| } */ | |
| if (buf) { | |
| marshal_str(buf, str, len); | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| Private utility function to validate m_client_req_unmapped_url_canon_str & | |
| m_client_req_unmapped_url_canon_len fields. | |
| -------------------------------------------------------------------------*/ | |
| void | |
| LogAccess::validate_unmapped_url() | |
| { | |
| char *unmapped_url_str = m_data->get_unmapped_url_str(); | |
| int unmapped_url_len = m_data->get_unmapped_url_len(); | |
| if (unmapped_url_str == nullptr) { | |
| m_client_req_unmapped_url_canon_str = INVALID_STR; | |
| return; | |
| } | |
| if (m_client_req_unmapped_url_canon_str == nullptr) { | |
| // prevent multiple validations | |
| m_client_req_unmapped_url_canon_str = INVALID_STR; | |
| if (unmapped_url_len > 0) { | |
| if (unmapped_url_str && unmapped_url_str[0] != 0) { | |
| m_client_req_unmapped_url_canon_str = | |
| Encoding::escapify_url(&m_arena, unmapped_url_str, unmapped_url_len, &m_client_req_unmapped_url_canon_len); | |
| } | |
| } | |
| } | |
| } | |
| /*------------------------------------------------------------------------- | |
| Private utility function to validate m_client_req_unmapped_url_path_str & | |
| m_client_req_unmapped_url_path_len fields. | |
| -------------------------------------------------------------------------*/ | |
| void | |
| LogAccess::validate_unmapped_url_path() | |
| { | |
| if (m_client_req_unmapped_url_path_str == nullptr && m_client_req_unmapped_url_host_str == nullptr) { | |
| // Use unmapped canonical URL as default | |
| m_client_req_unmapped_url_path_str = m_client_req_unmapped_url_canon_str; | |
| m_client_req_unmapped_url_path_len = m_client_req_unmapped_url_canon_len; | |
| // In case the code below fails, we prevent it from being used. | |
| m_client_req_unmapped_url_host_str = INVALID_STR; | |
| if (m_client_req_unmapped_url_path_len >= 6) { // xxx:// - minimum schema size | |
| int len; | |
| char *c = | |
| static_cast<char *>(memchr((void *)m_client_req_unmapped_url_path_str, ':', m_client_req_unmapped_url_path_len - 1)); | |
| if (c && (len = static_cast<int>(c - m_client_req_unmapped_url_path_str)) <= 5) { // 5 - max schema size | |
| if (len + 2 <= m_client_req_unmapped_url_canon_len && c[1] == '/' && c[2] == '/') { | |
| len += 3; // Skip "://" | |
| m_client_req_unmapped_url_host_str = &m_client_req_unmapped_url_canon_str[len]; | |
| m_client_req_unmapped_url_host_len = m_client_req_unmapped_url_path_len - len; | |
| // Attempt to find first '/' in the path | |
| if (m_client_req_unmapped_url_host_len > 0 && | |
| (c = static_cast<char *>( | |
| memchr((void *)m_client_req_unmapped_url_host_str, '/', m_client_req_unmapped_url_host_len))) != nullptr) { | |
| m_client_req_unmapped_url_host_len = static_cast<int>(c - m_client_req_unmapped_url_host_str); | |
| m_client_req_unmapped_url_path_str = &m_client_req_unmapped_url_host_str[m_client_req_unmapped_url_host_len]; | |
| m_client_req_unmapped_url_path_len = m_client_req_unmapped_url_path_len - len - m_client_req_unmapped_url_host_len; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| /*------------------------------------------------------------------------- | |
| Private utility function to validate m_cache_lookup_url_canon_str & | |
| m_cache_lookup__url_canon_len fields. | |
| -------------------------------------------------------------------------*/ | |
| void | |
| LogAccess::validate_lookup_url() | |
| { | |
| char *lookup_url_str = m_data->get_cache_lookup_url_str(); | |
| int lookup_url_len = m_data->get_cache_lookup_url_len(); | |
| if (lookup_url_str == nullptr) { | |
| m_cache_lookup_url_canon_str = INVALID_STR; | |
| return; | |
| } | |
| if (m_cache_lookup_url_canon_str == nullptr) { | |
| // prevent multiple validations | |
| m_cache_lookup_url_canon_str = INVALID_STR; | |
| if (lookup_url_len > 0) { | |
| if (lookup_url_str && lookup_url_str[0] != 0) { | |
| m_cache_lookup_url_canon_str = | |
| Encoding::escapify_url(&m_arena, lookup_url_str, lookup_url_len, &m_cache_lookup_url_canon_len); | |
| } | |
| } | |
| } | |
| } | |
| /*------------------------------------------------------------------------- | |
| This is the method, url, and version all rolled into one. Use the | |
| respective marshalling routines to do the job. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_text(char *buf) | |
| { | |
| int len = marshal_client_req_http_method(nullptr) + marshal_client_req_url(nullptr) + marshal_client_req_http_version(nullptr); | |
| if (buf) { | |
| int offset = 0; | |
| offset += marshal_client_req_http_method(&buf[offset]); | |
| offset += marshal_client_req_url(&buf[offset]); | |
| offset += marshal_client_req_http_version(&buf[offset]); | |
| len = offset; | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_timestamp_sec(char *buf) | |
| { | |
| return marshal_milestone_fmt_sec(TS_MILESTONE_UA_BEGIN, buf); | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_timestamp_ms(char *buf) | |
| { | |
| return marshal_milestone_fmt_ms(TS_MILESTONE_UA_BEGIN, buf); | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_http_method(char *buf) | |
| { | |
| std::string_view str; | |
| int plen = INK_MIN_ALIGN; | |
| if (m_client_request) { | |
| str = m_client_request->method_get(); | |
| } | |
| if (str.empty()) { | |
| str = m_data->get_method(); | |
| } | |
| // calculate the padded length only if the actual length | |
| // is not zero. We don't want the padded length to be zero | |
| // because marshal_mem should write the DEFAULT_STR to the | |
| // buffer if str is nil, and we need room for this. | |
| // | |
| if (!str.empty()) { | |
| plen = padded_length(static_cast<int>(str.length()) + 1); // +1 for trailing 0 | |
| } | |
| if (buf) { | |
| marshal_mem(buf, str.data(), static_cast<int>(str.length()), plen); | |
| } | |
| return plen; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_url(char *buf) | |
| { | |
| int len = padded_length(m_client_req_url_len + 1); // +1 for trailing 0 | |
| if (buf) { | |
| marshal_mem(buf, m_client_req_url_str, m_client_req_url_len, len); | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_url_canon(char *buf) | |
| { | |
| int len = padded_length(m_client_req_url_canon_len + 1); | |
| if (buf) { | |
| marshal_mem(buf, m_client_req_url_canon_str, m_client_req_url_canon_len, len); | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_unmapped_url_canon(char *buf) | |
| { | |
| int len = INK_MIN_ALIGN; | |
| validate_unmapped_url(); | |
| if (m_client_req_unmapped_url_canon_str == INVALID_STR) { | |
| // If the unmapped URL isn't populated, we'll fall back to the original | |
| // client URL. This helps for example server intercepts to continue to | |
| // log the requests, even when there is no remap rule for it. | |
| len = marshal_client_req_url_canon(buf); | |
| } else { | |
| len = padded_length(m_client_req_unmapped_url_canon_len + 1); // +1 for eos | |
| if (buf) { | |
| marshal_mem(buf, m_client_req_unmapped_url_canon_str, m_client_req_unmapped_url_canon_len, len); | |
| } | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_unmapped_url_path(char *buf) | |
| { | |
| int len = INK_MIN_ALIGN; | |
| validate_unmapped_url(); | |
| validate_unmapped_url_path(); | |
| if (m_client_req_unmapped_url_path_str == INVALID_STR) { | |
| len = marshal_client_req_url_path(buf); | |
| } else { | |
| len = padded_length(m_client_req_unmapped_url_path_len + 1); // +1 for eos | |
| if (buf) { | |
| marshal_mem(buf, m_client_req_unmapped_url_path_str, m_client_req_unmapped_url_path_len, len); | |
| } | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_unmapped_url_host(char *buf) | |
| { | |
| validate_unmapped_url(); | |
| validate_unmapped_url_path(); | |
| int len = padded_length(m_client_req_unmapped_url_host_len + 1); // +1 for eos | |
| if (buf) { | |
| marshal_mem(buf, m_client_req_unmapped_url_host_str, m_client_req_unmapped_url_host_len, len); | |
| } | |
| return len; | |
| } | |
| int | |
| LogAccess::marshal_client_req_url_path(char *buf) | |
| { | |
| int len = padded_length(m_client_req_url_path_len + 1); | |
| if (buf) { | |
| marshal_mem(buf, m_client_req_url_path_str, m_client_req_url_path_len, len); | |
| } | |
| return len; | |
| } | |
| int | |
| LogAccess::marshal_client_req_url_scheme(char *buf) | |
| { | |
| const char *str = nullptr; | |
| int alen = 0; | |
| int plen = INK_MIN_ALIGN; | |
| int scheme = m_data->get_orig_scheme(); | |
| std::string_view scheme_sv = m_data->get_scheme(); | |
| if (scheme >= 0) { | |
| str = hdrtoken_index_to_wks(scheme); | |
| alen = hdrtoken_index_to_length(scheme); | |
| } else if (!scheme_sv.empty()) { | |
| str = scheme_sv.data(); | |
| alen = static_cast<int>(scheme_sv.size()); | |
| } else { | |
| str = "UNKNOWN"; | |
| alen = 7; | |
| } | |
| plen = padded_length(alen + 1); | |
| if (buf) { | |
| marshal_mem(buf, str, alen, plen); | |
| } | |
| return plen; | |
| } | |
| /*------------------------------------------------------------------------- | |
| For this one we're going to marshal two INTs, one the first representing | |
| the major number and the second representing the minor. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_http_version(char *buf) | |
| { | |
| if (buf) { | |
| if (m_client_request) { | |
| HTTPVersion versionObject = m_client_request->version_get(); | |
| marshal_int(buf, versionObject.get_major()); | |
| marshal_int((buf + INK_MIN_ALIGN), versionObject.get_minor()); | |
| } else { | |
| marshal_int(buf, 0); | |
| marshal_int((buf + INK_MIN_ALIGN), 0); | |
| } | |
| } | |
| return (2 * INK_MIN_ALIGN); | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_protocol_version(char *buf) | |
| { | |
| const char *protocol_str = m_data->get_client_protocol(); | |
| std::string_view protocol_sv = m_data->get_client_protocol_str(); | |
| if (protocol_str == nullptr || protocol_str[0] == '\0') { | |
| if (!protocol_sv.empty()) { | |
| protocol_str = protocol_sv.data(); | |
| } else { | |
| protocol_str = DEFAULT_STR; | |
| } | |
| } | |
| int len = LogAccess::padded_strlen(protocol_str); | |
| // Set major & minor versions when protocol_str is not "http/2". | |
| if (::strlen(protocol_str) == 4 && strncmp("http", protocol_str, 4) == 0) { | |
| if (m_client_request) { | |
| HTTPVersion versionObject = m_client_request->version_get(); | |
| if (versionObject == HTTP_1_1) { | |
| protocol_str = "http/1.1"; | |
| } else if (versionObject == HTTP_1_0) { | |
| protocol_str = "http/1.0"; | |
| } // else invalid http version | |
| } else { | |
| protocol_str = "*"; | |
| } | |
| len = LogAccess::padded_strlen(protocol_str); | |
| } | |
| if (buf) { | |
| marshal_str(buf, protocol_str, len); | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_server_req_protocol_version(char *buf) | |
| { | |
| const char *server_proto = m_data->get_server_protocol(); | |
| const char *protocol_str = server_proto ? server_proto : DEFAULT_STR; | |
| int len = LogAccess::padded_strlen(protocol_str); | |
| // Set major & minor versions when protocol_str is not "http/2". | |
| if (::strlen(protocol_str) == 4 && strncmp("http", protocol_str, 4) == 0) { | |
| if (m_proxy_request) { | |
| HTTPVersion versionObject = m_proxy_request->version_get(); | |
| if (versionObject == HTTP_1_1) { | |
| protocol_str = "http/1.1"; | |
| } else if (versionObject == HTTP_1_0) { | |
| protocol_str = "http/1.0"; | |
| } // else invalid http version | |
| } else { | |
| protocol_str = "*"; | |
| } | |
| len = LogAccess::padded_strlen(protocol_str); | |
| } | |
| if (buf) { | |
| marshal_str(buf, protocol_str, len); | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_header_len(char *buf) | |
| { | |
| if (buf) { | |
| int64_t len = 0; | |
| if (m_client_request) { | |
| len = m_client_request->length_get(); | |
| } | |
| marshal_int(buf, len); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_content_len(char *buf) | |
| { | |
| if (buf) { | |
| int64_t len = 0; | |
| if (m_client_request) { | |
| len = m_data->get_client_request_body_bytes(); | |
| } | |
| marshal_int(buf, len); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_client_req_squid_len(char *buf) | |
| { | |
| if (buf) { | |
| int64_t val = 0; | |
| if (m_client_request) { | |
| val = m_client_request->length_get() + m_data->get_client_request_body_bytes(); | |
| } | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| Client request squid length plus TLS handshake bytes received for TLS connections. | |
| For TLS 1.3 early data (0-RTT), we subtract the early data length from the | |
| handshake bytes to avoid double-counting since the early data bytes are | |
| already included in client_request_body_bytes. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_squid_len_tls(char *buf) | |
| { | |
| if (buf) { | |
| int64_t val = 0; | |
| if (m_client_request) { | |
| val = m_client_request->length_get() + m_data->get_client_request_body_bytes(); | |
| } | |
| if (!m_data->get_client_tcp_reused()) { | |
| uint64_t handshake_rx = m_data->get_client_tls_handshake_bytes_rx(); | |
| size_t early_data_len = m_data->get_client_tls_early_data_len(); | |
| if (early_data_len > 0) { | |
| handshake_rx -= std::min(handshake_rx, static_cast<uint64_t>(early_data_len)); | |
| } | |
| val += handshake_rx; | |
| } | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_tcp_reused(char *buf) | |
| { | |
| if (buf) { | |
| int64_t val = m_data->get_client_tcp_reused() ? 1 : 0; | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_client_req_is_ssl(char *buf) | |
| { | |
| if (buf) { | |
| int64_t val = m_data->get_client_connection_is_ssl() ? 1 : 0; | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_client_req_ssl_reused(char *buf) | |
| { | |
| if (buf) { | |
| int64_t val = m_data->get_client_ssl_reused() ? 1 : 0; | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_client_ssl_resumption_type(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_client_ssl_resumption_type()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_client_req_is_internal(char *buf) | |
| { | |
| if (buf) { | |
| int64_t val = m_data->get_is_internal() ? 1 : 0; | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_client_req_mptcp_state(char *buf) | |
| { | |
| if (buf) { | |
| int val = -1; | |
| std::optional<bool> mptcp_state = m_data->get_mptcp_state(); | |
| if (mptcp_state.has_value()) { | |
| val = mptcp_state.value() ? 1 : 0; | |
| } | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_finish_status_code(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_client_finish_status_code()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_id(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_sm_id()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_req_uuid(char *buf) | |
| { | |
| char str[TS_CRUUID_STRING_LEN + 1]; | |
| const char *uuid = Machine::instance()->process_uuid.getString(); | |
| int64_t req_id = m_data->get_sm_id(); | |
| int len = snprintf(str, sizeof(str), "%s-%" PRId64 "", uuid, req_id); | |
| ink_assert(len <= TS_CRUUID_STRING_LEN); | |
| len = padded_length(len + 1); | |
| if (buf) { | |
| marshal_str(buf, str, len); // This will pad the remaining bytes properly ... | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_rx_error_code(char *buf) | |
| { | |
| const char *err_code = m_data->get_client_rx_error_code(); | |
| int round_len = LogAccess::padded_strlen(err_code); | |
| if (buf) { | |
| marshal_str(buf, err_code, round_len); | |
| } | |
| return round_len; | |
| } | |
| int | |
| LogAccess::marshal_client_tx_error_code(char *buf) | |
| { | |
| const char *err_code = m_data->get_client_tx_error_code(); | |
| int round_len = LogAccess::padded_strlen(err_code); | |
| if (buf) { | |
| marshal_str(buf, err_code, round_len); | |
| } | |
| return round_len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_security_protocol(char *buf) | |
| { | |
| const char *sec_proto = m_data->get_client_sec_protocol(); | |
| const char *proto = sec_proto ? sec_proto : DEFAULT_STR; | |
| int round_len = LogAccess::padded_strlen(proto); | |
| if (buf) { | |
| marshal_str(buf, proto, round_len); | |
| } | |
| return round_len; | |
| } | |
| int | |
| LogAccess::marshal_client_security_cipher_suite(char *buf) | |
| { | |
| const char *cipher_suite = m_data->get_client_cipher_suite(); | |
| const char *cipher = cipher_suite ? cipher_suite : DEFAULT_STR; | |
| int round_len = LogAccess::padded_strlen(cipher); | |
| if (buf) { | |
| marshal_str(buf, cipher, round_len); | |
| } | |
| return round_len; | |
| } | |
| int | |
| LogAccess::marshal_client_security_curve(char *buf) | |
| { | |
| const char *client_curve = m_data->get_client_curve(); | |
| const char *curve = client_curve ? client_curve : DEFAULT_STR; | |
| int round_len = LogAccess::padded_strlen(curve); | |
| if (buf) { | |
| marshal_str(buf, curve, round_len); | |
| } | |
| return round_len; | |
| } | |
| int | |
| LogAccess::marshal_client_security_group(char *buf) | |
| { | |
| const char *sec_group = m_data->get_client_security_group(); | |
| const char *group = sec_group ? sec_group : DEFAULT_STR; | |
| int round_len = LogAccess::padded_strlen(group); | |
| if (buf) { | |
| marshal_str(buf, group, round_len); | |
| } | |
| return round_len; | |
| } | |
| int | |
| LogAccess::marshal_client_security_alpn(char *buf) | |
| { | |
| const char *alpn = "-"; | |
| int alpn_id = m_data->get_client_alpn_id(); | |
| if (alpn_id != SessionProtocolNameRegistry::INVALID) { | |
| swoc::TextView client_sec_alpn = globalSessionProtocolNameRegistry.nameFor(alpn_id); | |
| alpn = client_sec_alpn.data(); | |
| } | |
| int round_len = LogAccess::padded_strlen(alpn); | |
| if (buf) { | |
| marshal_str(buf, alpn, round_len); | |
| } | |
| return round_len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| TLS Handshake Bytes - Bytes received from client during TLS handshake | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_tls_handshake_bytes_rx(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, static_cast<int64_t>(m_data->get_client_tls_handshake_bytes_rx())); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| TLS Handshake Bytes - Bytes sent to client during TLS handshake | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_tls_handshake_bytes_tx(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, static_cast<int64_t>(m_data->get_client_tls_handshake_bytes_tx())); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| TLS Handshake Bytes - Total bytes (rx + tx) during TLS handshake | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_tls_handshake_bytes(char *buf) | |
| { | |
| if (buf) { | |
| uint64_t total = m_data->get_client_tls_handshake_bytes_rx() + m_data->get_client_tls_handshake_bytes_tx(); | |
| marshal_int(buf, static_cast<int64_t>(total)); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_resp_content_type(char *buf) | |
| { | |
| int len = padded_length(m_proxy_resp_content_type_len + 1); | |
| if (buf) { | |
| marshal_mem(buf, m_proxy_resp_content_type_str, m_proxy_resp_content_type_len, len); | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_resp_reason_phrase(char *buf) | |
| { | |
| int len = padded_length(m_proxy_resp_reason_phrase_len + 1); | |
| if (buf) { | |
| marshal_mem(buf, m_proxy_resp_reason_phrase_str, m_proxy_resp_reason_phrase_len, len); | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| Squid returns the content-length + header length as the total length. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_resp_squid_len(char *buf) | |
| { | |
| if (buf) { | |
| int64_t val = m_data->get_client_response_hdr_bytes() + m_data->get_client_response_body_bytes(); | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| Squid length plus TLS handshake bytes sent for TLS connections. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_resp_squid_len_tls(char *buf) | |
| { | |
| if (buf) { | |
| int64_t val = m_data->get_client_response_hdr_bytes() + m_data->get_client_response_body_bytes(); | |
| if (!m_data->get_client_tcp_reused()) { | |
| val += m_data->get_client_tls_handshake_bytes_tx(); | |
| } | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_resp_content_len(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_client_response_body_bytes()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_resp_status_code(char *buf) | |
| { | |
| if (buf) { | |
| HTTPStatus status; | |
| if (m_proxy_response && m_client_request) { | |
| if (m_client_request->version_get() >= HTTPVersion(1, 0)) { | |
| status = m_proxy_response->status_get(); | |
| } | |
| // INKqa10788 | |
| // For bad/incomplete request, the request version may be 0.9. | |
| // However, we can still log the status code if there is one. | |
| else if (m_proxy_response->valid()) { | |
| status = m_proxy_response->status_get(); | |
| } else { | |
| status = HTTPStatus::OK; | |
| } | |
| } else { | |
| status = HTTPStatus::NONE; | |
| } | |
| marshal_int(buf, static_cast<int64_t>(status)); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_status_plugin_entry(char *buf) | |
| { | |
| char const *str = nullptr; | |
| int len = INK_MIN_ALIGN; | |
| std::string_view setter_name = m_data->get_http_return_code_setter_name(); | |
| if (!setter_name.empty()) { | |
| str = setter_name.data(); | |
| len = LogAccess::padded_strlen(str); | |
| } | |
| if (buf) { | |
| marshal_str(buf, str, len); | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_resp_header_len(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_client_response_hdr_bytes()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_proxy_finish_status_code(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_proxy_finish_status_code()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_host_port(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, static_cast<int64_t>(m_data->get_incoming_port())); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_cache_result_code(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, static_cast<int64_t>(m_data->get_log_code())); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_cache_result_subcode(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, static_cast<int64_t>(m_data->get_subcode())); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_cache_hit_miss(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, static_cast<int64_t>(m_data->get_hit_miss_code())); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_req_header_len(char *buf) | |
| { | |
| if (buf) { | |
| int64_t val = 0; | |
| if (m_proxy_request) { | |
| val = m_proxy_request->length_get(); | |
| } | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_req_content_len(char *buf) | |
| { | |
| if (buf) { | |
| int64_t val = 0; | |
| if (m_proxy_request) { | |
| val = m_data->get_server_request_body_bytes(); | |
| } | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_proxy_req_squid_len(char *buf) | |
| { | |
| if (buf) { | |
| int64_t val = 0; | |
| if (m_proxy_request) { | |
| val = m_proxy_request->length_get() + m_data->get_server_request_body_bytes(); | |
| } | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| // TODO: Change marshalling code to support both IPv4 and IPv6 addresses. | |
| int | |
| LogAccess::marshal_proxy_req_server_ip(char *buf) | |
| { | |
| return marshal_ip(buf, m_data->get_server_src_addr()); | |
| } | |
| int | |
| LogAccess::marshal_proxy_req_server_port(char *buf) | |
| { | |
| if (buf) { | |
| uint16_t port = 0; | |
| sockaddr const *src = m_data->get_server_src_addr(); | |
| if (src) { | |
| port = ats_ip_port_host_order(src); | |
| } | |
| marshal_int(buf, port); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_next_hop_ip(char *buf) | |
| { | |
| return marshal_ip(buf, m_data->get_server_dst_addr()); | |
| } | |
| int | |
| LogAccess::marshal_next_hop_port(char *buf) | |
| { | |
| if (buf) { | |
| uint16_t port = 0; | |
| sockaddr const *dst = m_data->get_server_dst_addr(); | |
| if (dst) { | |
| port = ats_ip_port_host_order(dst); | |
| } | |
| marshal_int(buf, port); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_req_is_ssl(char *buf) | |
| { | |
| if (buf) { | |
| int64_t is_ssl = m_data->get_server_connection_is_ssl() ? 1 : 0; | |
| marshal_int(buf, is_ssl); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_proxy_req_ssl_reused(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_server_ssl_reused() ? 1 : 0); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_proxy_hierarchy_route(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, static_cast<int64_t>(m_data->get_hier_code())); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| // TODO: Change marshalling code to support both IPv4 and IPv6 addresses. | |
| int | |
| LogAccess::marshal_server_host_ip(char *buf) | |
| { | |
| sockaddr const *ip = nullptr; | |
| sockaddr const *info_dst = m_data->get_server_info_dst_addr(); | |
| sockaddr const *server_dst = m_data->get_server_dst_addr(); | |
| if (info_dst && ats_is_ip(info_dst)) { | |
| ip = info_dst; | |
| } else if (server_dst && ats_is_ip(server_dst)) { | |
| ip = server_dst; | |
| } | |
| return marshal_ip(buf, ip); | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_server_host_name(char *buf) | |
| { | |
| const char *str = nullptr; | |
| int len = INK_MIN_ALIGN; | |
| sockaddr const *server_dst = m_data->get_server_dst_addr(); | |
| const char *name = m_data->get_server_name(); | |
| if (server_dst && name != nullptr) { | |
| str = name; | |
| len = LogAccess::padded_strlen(str); | |
| } | |
| if (buf) { | |
| marshal_str(buf, str, len); | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_server_resp_status_code(char *buf) | |
| { | |
| if (buf) { | |
| HTTPStatus status; | |
| if (m_server_response) { | |
| status = m_server_response->status_get(); | |
| } else { | |
| status = HTTPStatus::NONE; | |
| } | |
| marshal_int(buf, static_cast<int64_t>(status)); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_server_resp_content_len(char *buf) | |
| { | |
| if (buf) { | |
| int64_t val = 0; | |
| if (m_server_response) { | |
| val = m_data->get_server_response_body_bytes(); | |
| } | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_server_resp_header_len(char *buf) | |
| { | |
| if (buf) { | |
| int64_t val = 0; | |
| if (m_server_response) { | |
| val = m_server_response->length_get(); | |
| } | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_server_resp_squid_len(char *buf) | |
| { | |
| if (buf) { | |
| int64_t val = 0; | |
| if (m_server_response) { | |
| val = m_server_response->length_get() + m_data->get_server_response_body_bytes(); | |
| } | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_server_resp_http_version(char *buf) | |
| { | |
| return marshal_http_version_string(buf, m_server_response); | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_server_resp_time_ms(char *buf) | |
| { | |
| if (buf) { | |
| TransactionMilestones const *ms = m_data->get_milestones(); | |
| int64_t val = ms ? ms->difference_msec(TS_MILESTONE_SERVER_CONNECT, TS_MILESTONE_SERVER_CLOSE) : 0; | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_server_resp_time_s(char *buf) | |
| { | |
| if (buf) { | |
| TransactionMilestones const *ms = m_data->get_milestones(); | |
| int64_t val = ms ? static_cast<int64_t>(ms->difference_sec(TS_MILESTONE_SERVER_CONNECT, TS_MILESTONE_SERVER_CLOSE)) : 0; | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_server_transact_count(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_server_transact_count()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_server_simple_retry_count(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_simple_retry_attempts()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_server_unavailable_retry_count(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_unavailable_retry_attempts()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_server_connect_attempts(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_retry_attempts_saved()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_cache_resp_status_code(char *buf) | |
| { | |
| if (buf) { | |
| HTTPStatus status; | |
| if (m_cache_response) { | |
| status = m_cache_response->status_get(); | |
| } else { | |
| status = HTTPStatus::NONE; | |
| } | |
| marshal_int(buf, static_cast<int64_t>(status)); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_cache_resp_content_len(char *buf) | |
| { | |
| if (buf) { | |
| int64_t val = 0; | |
| if (m_cache_response) { | |
| val = m_data->get_cache_response_body_bytes(); | |
| } | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_cache_resp_squid_len(char *buf) | |
| { | |
| if (buf) { | |
| int64_t val = 0; | |
| if (m_cache_response) { | |
| val = m_cache_response->length_get() + m_data->get_cache_response_body_bytes(); | |
| } | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_cache_resp_header_len(char *buf) | |
| { | |
| if (buf) { | |
| int64_t val = 0; | |
| if (m_cache_response) { | |
| val = m_data->get_cache_response_hdr_bytes(); | |
| } | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_cache_resp_http_version(char *buf) | |
| { | |
| return marshal_http_version_string(buf, m_cache_response); | |
| } | |
| int | |
| LogAccess::marshal_client_retry_after_time(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_congestion_control_crat()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_cache_write_code(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_cache_write_code()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_cache_write_transform_code(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_cache_transform_write_code()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_cache_key_hash(char *buf) | |
| { | |
| const ts::CryptoHash *hash = m_data->get_cache_lookup_hash(); | |
| if (!hash || hash->is_zero()) { | |
| if (buf) { | |
| marshal_str(buf, "-", padded_length(2)); | |
| } | |
| return padded_length(2); | |
| } | |
| constexpr size_t b64_bufsize = ats_base64_encode_dstlen(CRYPTO_HASH_SIZE); | |
| char b64_str[b64_bufsize]; | |
| size_t b64_len = 0; | |
| ats_base64_encode(reinterpret_cast<const char *>(hash->u8), CRYPTO_HASH_SIZE, b64_str, b64_bufsize, &b64_len); | |
| int len = padded_length(b64_len + 1); | |
| if (buf) { | |
| marshal_str(buf, b64_str, len); | |
| } | |
| return len; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_transfer_time_ms(char *buf) | |
| { | |
| if (buf) { | |
| TransactionMilestones const *ms = m_data->get_milestones(); | |
| int64_t val = ms ? ms->difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SM_FINISH) : 0; | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_transfer_time_s(char *buf) | |
| { | |
| if (buf) { | |
| TransactionMilestones const *ms = m_data->get_milestones(); | |
| int64_t val = ms ? static_cast<int64_t>(ms->difference_sec(TS_MILESTONE_SM_START, TS_MILESTONE_SM_FINISH)) : 0; | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| Figure out the size of the object *on origin*. This is somewhat tricky | |
| since there are many variations on how this can be calculated. | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_file_size(char *buf) | |
| { | |
| if (buf) { | |
| MIMEField *fld; | |
| HTTPHdr *hdr = m_server_response ? m_server_response : m_cache_response; | |
| if (hdr && (fld = hdr->field_find(static_cast<std::string_view>(MIME_FIELD_CONTENT_RANGE)))) { | |
| auto value{fld->value_get()}; | |
| int len = value.length(); | |
| char *str = const_cast<char *>(value.data()); | |
| char *pos = static_cast<char *>(memchr(str, '/', len)); // Find the / | |
| // If the size is not /* (which means unknown) use it as the file_size. | |
| if (pos && !memchr(pos + 1, '*', len - (pos + 1 - str))) { | |
| marshal_int(buf, ink_atoi64(pos + 1, len - (pos + 1 - str))); | |
| } | |
| } else { | |
| // This is semi-broken when we serveq zero length objects. See TS-2213 | |
| int64_t server_body = m_data->get_server_response_body_bytes(); | |
| int64_t cache_body = m_data->get_cache_response_body_bytes(); | |
| if (server_body > 0) { | |
| marshal_int(buf, server_body); | |
| } else if (cache_body > 0) { | |
| marshal_int(buf, cache_body); | |
| } | |
| } | |
| } | |
| // Else, we don't set the value at all (so, -) | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_http_connection_id(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_connection_id()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_http_transaction_id(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_transaction_id()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_http_transaction_priority_weight(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_transaction_priority_weight()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_client_http_transaction_priority_dependence(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_transaction_priority_dependence()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_cache_read_retries(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_cache_open_read_tries()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_cache_write_retries(char *buf) | |
| { | |
| if (buf) { | |
| marshal_int(buf, m_data->get_cache_open_write_tries()); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_cache_collapsed_connection_success(char *buf) | |
| { | |
| if (buf) { | |
| int64_t id = 0; // default - no collapse attempt | |
| SquidLogCode code = m_data->get_log_code(); | |
| int open_write_tries = m_data->get_cache_open_write_tries(); | |
| int max_write_retries = m_data->get_max_cache_open_write_retries(); | |
| if ((open_write_tries > 0) && ((code == SquidLogCode::TCP_HIT) || (code == SquidLogCode::TCP_MEM_HIT) || | |
| (code == SquidLogCode::TCP_DISK_HIT) || (code == SquidLogCode::TCP_CF_HIT))) { | |
| id = 1; | |
| } else if (max_write_retries >= 0 && open_write_tries > max_write_retries) { | |
| id = -1; | |
| } | |
| marshal_int(buf, id); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| /*------------------------------------------------------------------------- | |
| -------------------------------------------------------------------------*/ | |
| int | |
| LogAccess::marshal_http_header_field(LogField::Container container, char *field, char *buf) | |
| { | |
| char *str = nullptr; | |
| int padded_len = INK_MIN_ALIGN; | |
| int actual_len = 0; | |
| bool valid_field = false; | |
| HTTPHdr *header = header_for_container(container); | |
| if (header) { | |
| MIMEField *fld = header->field_find(std::string_view{field}); | |
| if (fld) { | |
| valid_field = true; | |
| // Loop over dups, marshalling each one into the buffer and | |
| // summing up their length | |
| // | |
| int running_len = 0; | |
| while (fld) { | |
| auto value{fld->value_get()}; | |
| actual_len = value.length(); | |
| str = const_cast<char *>(value.data()); | |
| if (buf) { | |
| memcpy(buf, str, actual_len); | |
| buf += actual_len; | |
| } | |
| running_len += actual_len; | |
| fld = fld->m_next_dup; | |
| // Dups need to be comma separated. So if there's another | |
| // dup, then add a comma and a space ... | |
| // | |
| if (fld != nullptr) { | |
| if (buf) { | |
| memcpy(buf, ", ", 2); | |
| buf += 2; | |
| } | |
| running_len += 2; | |
| } | |
| } | |
| // Done with all dups. Ensure that the string is terminated | |
| // and that the running_len is padded. | |
| // | |
| if (buf) { | |
| *buf = '\0'; | |
| buf++; | |
| } | |
| running_len += 1; | |
| padded_len = padded_length(running_len); | |
| // Note: marshal_string fills the padding to | |
| // prevent purify UMRs so we do it here too | |
| // since we always pass the unpadded length on | |
| // our calls to marshal string | |
| #ifdef DEBUG | |
| if (buf) { | |
| int pad_len = padded_len - running_len; | |
| for (int i = 0; i < pad_len; i++) { | |
| *buf = '$'; | |
| buf++; | |
| } | |
| } | |
| #endif | |
| } | |
| } | |
| // The Transfer-Encoding:chunked value may have been removed from the header | |
| // during processing, but we store it for logging purposes. | |
| std::string_view transfer_enc = m_data->get_server_response_transfer_encoding(); | |
| if (valid_field == false && !transfer_enc.empty() && container == LogField::SSH && strcmp(field, "Transfer-Encoding") == 0) { | |
| std::string_view stored_te = transfer_enc; | |
| if (!stored_te.empty()) { | |
| valid_field = true; | |
| actual_len = stored_te.length(); | |
| str = const_cast<char *>(stored_te.data()); | |
| int running_len = actual_len + 1; // +1 for null terminator | |
| padded_len = padded_length(running_len); | |
| if (buf) { | |
| memcpy(buf, str, actual_len); | |
| buf[actual_len] = '\0'; | |
| buf += running_len; | |
| #ifdef DEBUG | |
| int pad_len = padded_len - running_len; | |
| for (int i = 0; i < pad_len; i++) { | |
| *buf = '$'; | |
| buf++; | |
| } | |
| #endif | |
| } | |
| } | |
| } | |
| if (valid_field == false) { | |
| padded_len = INK_MIN_ALIGN; | |
| if (buf) { | |
| marshal_str(buf, nullptr, padded_len); | |
| } | |
| } | |
| return (padded_len); | |
| } | |
| int | |
| LogAccess::marshal_http_header_field_escapify(LogField::Container container, char *field, char *buf) | |
| { | |
| char *str = nullptr, *new_str = nullptr; | |
| int padded_len = INK_MIN_ALIGN; | |
| int actual_len = 0, new_len = 0; | |
| bool valid_field = false; | |
| HTTPHdr *header = header_for_container(container); | |
| if (header) { | |
| MIMEField *fld = header->field_find(std::string_view{field}); | |
| if (fld) { | |
| valid_field = true; | |
| // Loop over dups, marshalling each one into the buffer and | |
| // summing up their length | |
| // | |
| int running_len = 0; | |
| while (fld) { | |
| auto value{fld->value_get()}; | |
| actual_len = value.length(); | |
| str = const_cast<char *>(value.data()); | |
| new_str = Encoding::escapify_url(&m_arena, str, actual_len, &new_len); | |
| if (buf) { | |
| memcpy(buf, new_str, new_len); | |
| buf += new_len; | |
| } | |
| running_len += new_len; | |
| fld = fld->m_next_dup; | |
| // Dups need to be comma separated. So if there's another | |
| // dup, then add a comma and an escapified space ... | |
| constexpr const char SEP[] = ",%20"; | |
| constexpr size_t SEP_LEN = sizeof(SEP) - 1; | |
| if (fld != nullptr) { | |
| if (buf) { | |
| memcpy(buf, SEP, SEP_LEN); | |
| buf += SEP_LEN; | |
| } | |
| running_len += SEP_LEN; | |
| } | |
| } | |
| // Done with all dups. Ensure that the string is terminated | |
| // and that the running_len is padded. | |
| // | |
| if (buf) { | |
| *buf = '\0'; | |
| buf++; | |
| } | |
| running_len += 1; | |
| padded_len = padded_length(running_len); | |
| // Note: marshal_string fills the padding to | |
| // prevent purify UMRs so we do it here too | |
| // since we always pass the unpadded length on | |
| // our calls to marshal string | |
| #ifdef DEBUG | |
| if (buf) { | |
| int pad_len = padded_len - running_len; | |
| for (int i = 0; i < pad_len; i++) { | |
| *buf = '$'; | |
| buf++; | |
| } | |
| } | |
| #endif | |
| } | |
| } | |
| if (valid_field == false) { | |
| padded_len = INK_MIN_ALIGN; | |
| if (buf) { | |
| marshal_str(buf, nullptr, padded_len); | |
| } | |
| } | |
| return (padded_len); | |
| } | |
| int | |
| LogAccess::marshal_milestone(TSMilestonesType ms, char *buf) | |
| { | |
| if (buf) { | |
| TransactionMilestones const *milestones = m_data->get_milestones(); | |
| int64_t val = milestones ? ink_hrtime_to_msec((*milestones)[ms]) : 0; | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_milestone_fmt_sec(TSMilestonesType type, char *buf) | |
| { | |
| if (buf) { | |
| TransactionMilestones const *milestones = m_data->get_milestones(); | |
| ink_hrtime tsec = milestones ? ink_hrtime_to_sec((*milestones)[type]) : 0; | |
| marshal_int(buf, tsec); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_milestone_fmt_ms(TSMilestonesType type, char *buf) | |
| { | |
| if (buf) { | |
| TransactionMilestones const *milestones = m_data->get_milestones(); | |
| ink_hrtime tmsec = milestones ? ink_hrtime_to_msec((*milestones)[type]) : 0; | |
| marshal_int(buf, tmsec); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_milestone_diff(TSMilestonesType ms1, TSMilestonesType ms2, char *buf) | |
| { | |
| if (buf) { | |
| TransactionMilestones const *milestones = m_data->get_milestones(); | |
| int64_t val = milestones ? milestones->difference_msec(ms2, ms1) : 0; | |
| marshal_int(buf, val); | |
| } | |
| return INK_MIN_ALIGN; | |
| } | |
| int | |
| LogAccess::marshal_milestones_csv(char *buf) | |
| { | |
| Dbg(dbg_ctl_log_unmarshal_data, "marshal_milestones_csv"); | |
| TransactionMilestones const *ms_ptr = m_data->get_milestones(); | |
| if (!ms_ptr) { | |
| int len = LogAccess::padded_strlen("-"); | |
| if (buf) { | |
| marshal_str(buf, "-", len); | |
| } | |
| return len; | |
| } | |
| auto const &milestones = *ms_ptr; | |
| swoc::LocalBufferWriter<256> bw; | |
| bw.print("{}", milestones.difference_msec(TS_MILESTONE_TLS_HANDSHAKE_START, TS_MILESTONE_TLS_HANDSHAKE_END)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_UA_BEGIN)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_UA_FIRST_READ)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_UA_READ_HEADER_DONE)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_CACHE_OPEN_READ_BEGIN)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_CACHE_OPEN_READ_END)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_CACHE_OPEN_WRITE_BEGIN)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_CACHE_OPEN_WRITE_END)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_DNS_LOOKUP_BEGIN)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_DNS_LOOKUP_END)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SERVER_CONNECT)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SERVER_CONNECT_END)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SERVER_FIRST_READ)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SERVER_READ_HEADER_DONE)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SERVER_CLOSE)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_UA_BEGIN_WRITE)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_UA_CLOSE)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SM_FINISH)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_PLUGIN_ACTIVE)); | |
| bw.print(",{}", milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_PLUGIN_TOTAL)); | |
| bw.print("\0"); | |
| auto const view = bw.view(); | |
| int const len = LogAccess::padded_strlen(view.data()); | |
| if (nullptr != buf) { | |
| marshal_str(buf, view.data(), len); | |
| } | |
| return len; | |
| } | |
| void | |
| LogAccess::set_http_header_field(LogField::Container container, char *field, char *buf, int len) | |
| { | |
| HTTPHdr *header; | |
| switch (container) { | |
| case LogField::CQH: | |
| case LogField::ECQH: | |
| header = m_client_request; | |
| break; | |
| case LogField::PSH: | |
| case LogField::EPSH: | |
| header = m_proxy_response; | |
| break; | |
| case LogField::PQH: | |
| case LogField::EPQH: | |
| header = m_proxy_request; | |
| break; | |
| case LogField::SSH: | |
| case LogField::ESSH: | |
| header = m_server_response; | |
| break; | |
| case LogField::CSSH: | |
| case LogField::ECSSH: | |
| header = m_cache_response; | |
| break; | |
| default: | |
| header = nullptr; | |
| break; | |
| } | |
| if (header && buf) { | |
| MIMEField *fld = header->field_find(std::string_view{field}); | |
| if (fld) { | |
| // Loop over dups, update each of them | |
| // | |
| while (fld) { | |
| // make sure to reuse header heaps as otherwise | |
| // coalesce logic in header heap may free up | |
| // memory pointed to by cquuc or other log fields | |
| header->field_value_set(fld, std::string_view{buf, static_cast<std::string_view::size_type>(len)}, true); | |
| fld = fld->m_next_dup; | |
| } | |
| } | |
| } | |
| } |