| /** @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 "LogAccess.h" |
| |
| #include "http/HttpSM.h" |
| #include "MIME.h" |
| #include "I_Machine.h" |
| #include "LogFormat.h" |
| #include "LogBuffer.h" |
| |
| extern AppVersionInfo appVersionInfo; |
| |
| char INVALID_STR[] = "!INVALID_STR!"; |
| |
| #define HIDDEN_CONTENT_TYPE "@Content-Type" |
| #define HIDDEN_CONTENT_TYPE_LEN 13 |
| |
| // 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 |
| |
| /*------------------------------------------------------------------------- |
| LogAccess |
| |
| Initialize the private data members and assert that we got a valid state |
| machine pointer. |
| -------------------------------------------------------------------------*/ |
| |
| LogAccess::LogAccess(HttpSM *sm) |
| : m_http_sm(sm), |
| m_arena(), |
| m_client_request(nullptr), |
| m_proxy_response(nullptr), |
| m_proxy_request(nullptr), |
| m_server_response(nullptr), |
| m_cache_response(nullptr), |
| m_client_req_url_str(nullptr), |
| m_client_req_url_len(0), |
| m_client_req_url_canon_str(nullptr), |
| m_client_req_url_canon_len(0), |
| m_client_req_unmapped_url_canon_str(nullptr), |
| m_client_req_unmapped_url_canon_len(0), |
| m_client_req_unmapped_url_path_str(nullptr), |
| m_client_req_unmapped_url_path_len(0), |
| m_client_req_unmapped_url_host_str(nullptr), |
| m_client_req_unmapped_url_host_len(0), |
| m_client_req_url_path_str(nullptr), |
| m_client_req_url_path_len(0), |
| m_proxy_resp_content_type_str(nullptr), |
| m_proxy_resp_content_type_len(0), |
| m_proxy_resp_reason_phrase_str(nullptr), |
| m_proxy_resp_reason_phrase_len(0), |
| m_cache_lookup_url_canon_str(nullptr), |
| m_cache_lookup_url_canon_len(0) |
| { |
| ink_assert(m_http_sm != nullptr); |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::init |
| -------------------------------------------------------------------------*/ |
| |
| void |
| LogAccess::init() |
| { |
| HttpTransact::HeaderInfo *hdr = &(m_http_sm->t_state.hdr_info); |
| |
| if (hdr->client_request.valid()) { |
| m_client_request = &(hdr->client_request); |
| |
| // make a copy of the incoming url into the arena |
| const char *url_string_ref = m_client_request->url_string_get_ref(&m_client_req_url_len); |
| m_client_req_url_str = m_arena.str_alloc(m_client_req_url_len + 1); |
| memcpy(m_client_req_url_str, url_string_ref, m_client_req_url_len); |
| m_client_req_url_str[m_client_req_url_len] = '\0'; |
| |
| m_client_req_url_canon_str = |
| LogUtils::escapify_url(&m_arena, m_client_req_url_str, m_client_req_url_len, &m_client_req_url_canon_len); |
| m_client_req_url_path_str = m_client_request->path_get(&m_client_req_url_path_len); |
| } |
| |
| if (hdr->client_response.valid()) { |
| m_proxy_response = &(hdr->client_response); |
| MIMEField *field = m_proxy_response->field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE); |
| if (field) { |
| m_proxy_resp_content_type_str = const_cast<char *>(field->value_get(&m_proxy_resp_content_type_len)); |
| |
| LogUtils::remove_content_type_attributes(m_proxy_resp_content_type_str, &m_proxy_resp_content_type_len); |
| } else { |
| // If Content-Type field is missing, check for @Content-Type |
| field = m_proxy_response->field_find(HIDDEN_CONTENT_TYPE, HIDDEN_CONTENT_TYPE_LEN); |
| if (field) { |
| m_proxy_resp_content_type_str = const_cast<char *>(field->value_get(&m_proxy_resp_content_type_len)); |
| LogUtils::remove_content_type_attributes(m_proxy_resp_content_type_str, &m_proxy_resp_content_type_len); |
| } |
| } |
| m_proxy_resp_reason_phrase_str = const_cast<char *>(m_proxy_response->reason_get(&m_proxy_resp_reason_phrase_len)); |
| } |
| if (hdr->server_request.valid()) { |
| m_proxy_request = &(hdr->server_request); |
| } |
| if (hdr->server_response.valid()) { |
| m_server_response = &(hdr->server_response); |
| } |
| if (hdr->cache_response.valid()) { |
| m_cache_response = &(hdr->cache_response); |
| } |
| } |
| |
| int |
| LogAccess::marshal_proxy_host_name(char *buf) |
| { |
| char *str = nullptr; |
| int len = 0; |
| Machine *machine = Machine::instance(); |
| |
| if (machine) { |
| str = machine->hostname; |
| } |
| |
| len = LogAccess::strlen(str); |
| |
| 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 = round_strlen(TS_UUID_STRING_LEN + 1); |
| |
| if (buf) { |
| const char *str = const_cast<char *>(Machine::instance()->uuid.getString()); |
| marshal_str(buf, str, len); |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_config_int_var(char *config_var, char *buf) |
| { |
| if (buf) { |
| int64_t val = static_cast<int64_t>(REC_ConfigReadInteger(config_var)); |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_config_str_var(char *config_var, char *buf) |
| { |
| char *str = nullptr; |
| str = REC_ConfigReadString(config_var); |
| int len = LogAccess::strlen(str); |
| if (buf) { |
| marshal_str(buf, str, len); |
| } |
| ats_free(str); |
| 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; |
| |
| if (RecGetRecordDataType(record, &stype) != REC_ERR_OKAY) { |
| 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); |
| |
| int64_t val = static_cast<int64_t>(LOG_INTEGER == stype ? REC_readInteger(record, &found) : REC_readCounter(record, &found)); |
| |
| if (found) { |
| out_buf = int64_to_str(ascii_buf, max_chars, val, &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)); |
| |
| RecFloat val = REC_readFloat(record, &found); |
| |
| 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) + 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 (RecGetRecordString(record, ascii_buf, sizeof(ascii_buf)) == REC_ERR_OKAY) { |
| if (strlen(ascii_buf) > 0) { |
| num_chars = ::strlen(ascii_buf) + 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 { |
| data._ip._family = AF_UNSPEC; |
| } |
| |
| if (dest) { |
| memcpy(dest, &data, len); |
| } |
| return INK_ALIGN_DEFAULT(len); |
| } |
| |
| 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 { |
| codeStrLen = -1; |
| } |
| } else { |
| codeStrLen = -1; |
| } |
| break; |
| case LogFieldAliasMap::BUFFER_TOO_SMALL: |
| 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; |
| } |
| 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; |
| } |
| return -1; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_req_all_header_fields(char *buf) |
| { |
| return LogUtils::marshalMimeHdr(m_proxy_request, buf); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| /*------------------------------------------------------------------------- |
| 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) |
| { |
| 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 += LogAccess::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) { |
| return -1; |
| } |
| |
| memcpy(dest, (val_buf + offset), n); |
| return n; |
| } |
| |
| if (val_len < len) { |
| memcpy(dest, val_buf, val_len); |
| return val_len; |
| } |
| 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); |
| double secs = static_cast<double>(val) / 1000; |
| int val_len = snprintf(dest, len, "%.3f", secs); |
| 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)); |
| |
| 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)); |
| |
| 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)); |
| |
| 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 two consecutive integers, the first for |
| the major number and the second for the minor number. Retrieve both |
| numbers and return the result as "HTTP/major.minor". |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_http_version(char **buf, char *dest, int len) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| static const char *http = "HTTP/"; |
| static int http_len = static_cast<int>(::strlen(http)); |
| |
| char val_buf[128]; |
| char *p = val_buf; |
| |
| memcpy(p, http, http_len); |
| p += http_len; |
| |
| int res1 = unmarshal_int_to_str(buf, p, 128 - http_len); |
| if (res1 < 0) { |
| return -1; |
| } |
| p += res1; |
| *p++ = '.'; |
| int res2 = unmarshal_int_to_str(buf, p, 128 - http_len - res1 - 1); |
| if (res2 < 0) { |
| return -1; |
| } |
| |
| int val_len = http_len + res1 + res2 + 1; |
| if (val_len < len) { |
| memcpy(dest, val_buf, val_len); |
| return val_len; |
| } |
| return -1; |
| } |
| |
| /*------------------------------------------------------------------------- |
| LogAccess::unmarshal_http_text |
| |
| The http text is simply the fields http_method (cqhm) + url (cqu) + |
| http_version (cqhv), all right next to each other, in that order. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_http_text(char **buf, char *dest, int len, LogSlice *slice) |
| { |
| ink_assert(buf != nullptr); |
| ink_assert(*buf != nullptr); |
| ink_assert(dest != nullptr); |
| |
| char *p = dest; |
| |
| // int res1 = unmarshal_http_method (buf, p, len); |
| int res1 = unmarshal_str(buf, p, len); |
| if (res1 < 0) { |
| return -1; |
| } |
| p += res1; |
| *p++ = ' '; |
| int res2 = unmarshal_str(buf, p, len - res1 - 1, slice); |
| if (res2 < 0) { |
| return -1; |
| } |
| p += res2; |
| *p++ = ' '; |
| int res3 = unmarshal_http_version(buf, p, len - res1 - res2 - 2); |
| if (res3 < 0) { |
| return -1; |
| } |
| return res1 + res2 + res3 + 2; |
| } |
| |
| /*------------------------------------------------------------------------- |
| 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; |
| } |
| 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 { |
| 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; |
| int zret = -1; |
| |
| if (len > 0) { |
| unmarshal_ip(buf, &ip); |
| if (!ats_is_ip(&ip)) { |
| *dest = '0'; |
| zret = 1; |
| } else if (ats_ip_ntop(&ip, dest, len)) { |
| zret = static_cast<int>(::strlen(dest)); |
| } |
| } |
| return zret; |
| } |
| |
| /*------------------------------------------------------------------------- |
| 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 hexadeciaml and |
| return the string. Advance the buffer pointer. |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::unmarshal_ip_to_hex(char **buf, char *dest, int len) |
| { |
| int zret = -1; |
| IpEndpoint ip; |
| |
| if (len > 0) { |
| unmarshal_ip(buf, &ip); |
| if (!ats_is_ip(&ip)) { |
| *dest = '0'; |
| zret = 1; |
| } else { |
| zret = ats_ip_to_hex(&ip.sa, dest, len); |
| } |
| } |
| return zret; |
| } |
| |
| /*------------------------------------------------------------------------- |
| 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; |
| } |
| 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) { |
| Debug("log-resolve", "No context to resolve?"); |
| return nullptr; |
| } |
| |
| if (!format_str) { |
| Debug("log-resolve", "No format to resolve?"); |
| return nullptr; |
| } |
| |
| Debug("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) { |
| Debug("log-resolve", "No fields found; returning copy of format_str"); |
| ats_free(printf_str); |
| ats_free(fields_str); |
| return ats_strdup(format_str); |
| } |
| |
| Debug("log-resolve", "%d fields: %s", n_fields, fields_str); |
| Debug("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. |
| // |
| Debug("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); |
| Debug("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_len = std::min(len, m_client_req_unmapped_url_canon_len); |
| ink_strlcpy(m_client_req_unmapped_url_canon_str, buf, m_client_req_unmapped_url_canon_len + 1); |
| } |
| } |
| |
| 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.. |
| if (buf && m_client_req_unmapped_url_path_str) { |
| m_client_req_url_path_len = std::min(len, m_client_req_url_path_len); |
| ink_strlcpy(m_client_req_unmapped_url_path_str, buf, m_client_req_url_path_len + 1); |
| } |
| } |
| |
| /*------------------------------------------------------------------------- |
| The marshalling routines ... |
| |
| We know that m_http_sm is a valid pointer (we assert so in the ctor), but |
| we still need to check the other header pointers before using them in the |
| routines. |
| -------------------------------------------------------------------------*/ |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_plugin_identity_id(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_http_sm->plugin_id); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_plugin_identity_tag(char *buf) |
| { |
| int len = INK_MIN_ALIGN; |
| const char *tag = m_http_sm->plugin_tag; |
| |
| if (!tag) { |
| tag = "*"; |
| } else { |
| len = LogAccess::strlen(tag); |
| } |
| |
| if (buf) { |
| marshal_str(buf, tag, len); |
| } |
| |
| return len; |
| } |
| |
| int |
| LogAccess::marshal_client_host_ip(char *buf) |
| { |
| return marshal_ip(buf, &m_http_sm->t_state.client_info.src_addr.sa); |
| } |
| |
| int |
| LogAccess::marshal_host_interface_ip(char *buf) |
| { |
| return marshal_ip(buf, &m_http_sm->t_state.client_info.dst_addr.sa); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| 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 = round_strlen(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) |
| { |
| // NOTE: For this string_view, data() must always be nul-terminated, but the nul character must not be included in |
| // the length. |
| // |
| std::string_view server_name = ""; |
| |
| if (m_http_sm) { |
| auto txn = m_http_sm->get_ua_txn(); |
| if (txn) { |
| auto ssn = txn->get_proxy_ssn(); |
| if (ssn) { |
| auto ssl = ssn->ssl(); |
| if (ssl) { |
| auto server_name_str = ssl->client_sni_server_name(); |
| if (server_name_str) { |
| server_name = server_name_str; |
| } |
| } |
| } |
| } |
| } |
| int len = round_strlen(server_name.length() + 1); |
| if (buf) { |
| marshal_str(buf, server_name.data(), len); |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_client_provided_cert(char *buf) |
| { |
| int provided_cert = 0; |
| if (m_http_sm) { |
| auto txn = m_http_sm->get_ua_txn(); |
| if (txn) { |
| auto ssn = txn->get_proxy_ssn(); |
| if (ssn) { |
| auto ssl = ssn->ssl(); |
| if (ssl) { |
| provided_cert = ssl->client_provided_certificate(); |
| } |
| } |
| } |
| } |
| if (buf) { |
| marshal_int(buf, provided_cert); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_proxy_provided_cert(char *buf) |
| { |
| int provided_cert = 0; |
| if (m_http_sm) { |
| provided_cert = m_http_sm->server_connection_provided_cert; |
| } |
| if (buf) { |
| marshal_int(buf, provided_cert); |
| } |
| return INK_MIN_ALIGN; |
| } |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_version_build_number(char *buf) |
| { |
| int len = LogAccess::strlen(appVersionInfo.BldNumStr); |
| if (buf) { |
| marshal_str(buf, appVersionInfo.BldNumStr, len); |
| } |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_protocol_version(char *buf) |
| { |
| const char *version_str = nullptr; |
| int len = INK_MIN_ALIGN; |
| |
| if (m_http_sm) { |
| ProxyProtocolVersion ver = m_http_sm->t_state.pp_info.version; |
| switch (ver) { |
| case ProxyProtocolVersion::V1: |
| version_str = "V1"; |
| break; |
| case ProxyProtocolVersion::V2: |
| version_str = "V2"; |
| break; |
| case ProxyProtocolVersion::UNDEFINED: |
| default: |
| version_str = "-"; |
| break; |
| } |
| len = LogAccess::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_http_sm && m_http_sm->t_state.pp_info.version != ProxyProtocolVersion::UNDEFINED) { |
| ip = &m_http_sm->t_state.pp_info.src_addr.sa; |
| } |
| return marshal_ip(buf, ip); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_proxy_protocol_dst_ip(char *buf) |
| { |
| sockaddr const *ip = nullptr; |
| if (m_http_sm && m_http_sm->t_state.pp_info.version != ProxyProtocolVersion::UNDEFINED) { |
| ip = &m_http_sm->t_state.pp_info.dst_addr.sa; |
| } |
| return marshal_ip(buf, ip); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_client_host_port(char *buf) |
| { |
| if (buf) { |
| uint16_t port = ntohs(m_http_sm->t_state.client_info.src_addr.port()); |
| 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 (m_http_sm->t_state.auth_params.user_name) { |
| str = m_http_sm->t_state.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() |
| { |
| if (m_client_req_unmapped_url_canon_str == nullptr) { |
| // prevent multiple validations |
| m_client_req_unmapped_url_canon_str = INVALID_STR; |
| |
| if (m_http_sm->t_state.unmapped_url.valid()) { |
| int unmapped_url_len; |
| char *unmapped_url = m_http_sm->t_state.unmapped_url.string_get_ref(&unmapped_url_len); |
| |
| if (unmapped_url && unmapped_url[0] != 0) { |
| m_client_req_unmapped_url_canon_str = |
| LogUtils::escapify_url(&m_arena, unmapped_url, 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; |
| // Incase 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_path_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() |
| { |
| if (m_cache_lookup_url_canon_str == nullptr) { |
| // prevent multiple validations |
| m_cache_lookup_url_canon_str = INVALID_STR; |
| |
| if (m_http_sm->t_state.cache_info.lookup_url_storage.valid()) { |
| int lookup_url_len; |
| char *lookup_url = m_http_sm->t_state.cache_info.lookup_url_storage.string_get_ref(&lookup_url_len); |
| |
| if (lookup_url && lookup_url[0] != 0) { |
| m_cache_lookup_url_canon_str = LogUtils::escapify_url(&m_arena, lookup_url, 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) |
| { |
| char *str = nullptr; |
| int alen = 0; |
| int plen = INK_MIN_ALIGN; |
| |
| if (m_client_request) { |
| str = const_cast<char *>(m_client_request->method_get(&alen)); |
| |
| // calculate the 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 (alen) { |
| plen = round_strlen(alen + 1); // +1 for trailing 0 |
| } |
| } |
| |
| if (buf) { |
| marshal_mem(buf, str, alen, plen); |
| } |
| return plen; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_url(char *buf) |
| { |
| int len = round_strlen(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 = round_strlen(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 = round_strlen(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 = round_strlen(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 = round_strlen(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 = round_strlen(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) |
| { |
| int scheme = m_http_sm->t_state.orig_scheme; |
| const char *str = nullptr; |
| int alen; |
| int plen = INK_MIN_ALIGN; |
| |
| // If the transaction aborts very early, the scheme may not be set, or so ASAN reports. |
| if (scheme >= 0) { |
| str = hdrtoken_index_to_wks(scheme); |
| alen = hdrtoken_index_to_length(scheme); |
| } else { |
| str = "UNKNOWN"; |
| alen = strlen(str); |
| } |
| |
| // calculate the 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 (alen) { |
| plen = round_strlen(alen + 1); // +1 for trailing 0 |
| } |
| |
| 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) { |
| int64_t major = 0; |
| int64_t minor = 0; |
| if (m_client_request) { |
| HTTPVersion versionObject = m_client_request->version_get(); |
| major = HTTP_MAJOR(versionObject.m_version); |
| minor = HTTP_MINOR(versionObject.m_version); |
| } |
| marshal_int(buf, major); |
| marshal_int((buf + INK_MIN_ALIGN), minor); |
| } |
| return (2 * INK_MIN_ALIGN); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_protocol_version(char *buf) |
| { |
| const char *protocol_str = m_http_sm->client_protocol; |
| int len = LogAccess::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(); |
| int64_t major = HTTP_MAJOR(versionObject.m_version); |
| int64_t minor = HTTP_MINOR(versionObject.m_version); |
| if (major == 1 && minor == 1) { |
| protocol_str = "http/1.1"; |
| } else if (major == 1 && minor == 0) { |
| protocol_str = "http/1.0"; |
| } // else invalid http version |
| } else { |
| protocol_str = "*"; |
| } |
| |
| len = LogAccess::strlen(protocol_str); |
| } |
| |
| if (buf) { |
| marshal_str(buf, protocol_str, len); |
| } |
| |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_server_req_protocol_version(char *buf) |
| { |
| const char *protocol_str = m_http_sm->server_protocol; |
| int len = LogAccess::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(); |
| int64_t major = HTTP_MAJOR(versionObject.m_version); |
| int64_t minor = HTTP_MINOR(versionObject.m_version); |
| if (major == 1 && minor == 1) { |
| protocol_str = "http/1.1"; |
| } else if (major == 1 && minor == 0) { |
| protocol_str = "http/1.0"; |
| } // else invalid http version |
| } else { |
| protocol_str = "*"; |
| } |
| |
| len = LogAccess::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_http_sm->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_http_sm->client_request_body_bytes; |
| } |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_tcp_reused(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_http_sm->client_tcp_reused ? 1 : 0); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_client_req_is_ssl(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_http_sm->client_connection_is_ssl ? 1 : 0); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_client_req_ssl_reused(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_http_sm->client_ssl_reused ? 1 : 0); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_client_req_is_internal(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_http_sm->is_internal ? 1 : 0); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_client_req_mptcp_state(char *buf) |
| { |
| if (buf) { |
| int val = -1; |
| |
| if (m_http_sm->mptcp_state.has_value()) { |
| val = m_http_sm->mptcp_state.value() ? 1 : 0; |
| } else { |
| } |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_finish_status_code(char *buf) |
| { |
| if (buf) { |
| int code = LOG_FINISH_FIN; |
| HttpTransact::AbortState_t cl_abort_state = m_http_sm->t_state.client_info.abort; |
| if (cl_abort_state == HttpTransact::ABORTED) { |
| // Check to see if the abort is due to a timeout |
| if (m_http_sm->t_state.client_info.state == HttpTransact::ACTIVE_TIMEOUT || |
| m_http_sm->t_state.client_info.state == HttpTransact::INACTIVE_TIMEOUT) { |
| code = LOG_FINISH_TIMEOUT; |
| } else { |
| code = LOG_FINISH_INTR; |
| } |
| } |
| marshal_int(buf, code); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_req_id(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_http_sm->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()->uuid.getString(); |
| int len = snprintf(str, sizeof(str), "%s-%" PRId64 "", uuid, m_http_sm->sm_id); |
| |
| ink_assert(len <= TS_CRUUID_STRING_LEN); |
| len = round_strlen(len + 1); |
| |
| if (buf) { |
| marshal_str(buf, str, len); // This will pad the remaining bytes properly ... |
| } |
| |
| return len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| // 1 ('S'/'T' flag) + 8 (Error Code) + 1 ('\0') |
| static constexpr size_t MAX_PROXY_ERROR_CODE_SIZE = 10; |
| |
| int |
| LogAccess::marshal_client_rx_error_code(char *buf) |
| { |
| char error_code[MAX_PROXY_ERROR_CODE_SIZE] = {0}; |
| m_http_sm->t_state.client_info.rx_error_code.str(error_code, sizeof(error_code)); |
| int round_len = LogAccess::strlen(error_code); |
| |
| if (buf) { |
| marshal_str(buf, error_code, round_len); |
| } |
| |
| return round_len; |
| } |
| |
| int |
| LogAccess::marshal_client_tx_error_code(char *buf) |
| { |
| char error_code[MAX_PROXY_ERROR_CODE_SIZE] = {0}; |
| m_http_sm->t_state.client_info.tx_error_code.str(error_code, sizeof(error_code)); |
| int round_len = LogAccess::strlen(error_code); |
| |
| if (buf) { |
| marshal_str(buf, error_code, round_len); |
| } |
| |
| return round_len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_client_security_protocol(char *buf) |
| { |
| const char *proto = m_http_sm->client_sec_protocol; |
| int round_len = LogAccess::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 = m_http_sm->client_cipher_suite; |
| int round_len = LogAccess::strlen(cipher); |
| |
| if (buf) { |
| marshal_str(buf, cipher, round_len); |
| } |
| |
| return round_len; |
| } |
| |
| int |
| LogAccess::marshal_client_security_curve(char *buf) |
| { |
| const char *curve = m_http_sm->client_curve; |
| int round_len = LogAccess::strlen(curve); |
| |
| if (buf) { |
| marshal_str(buf, curve, round_len); |
| } |
| |
| return round_len; |
| } |
| |
| int |
| LogAccess::marshal_client_security_alpn(char *buf) |
| { |
| const char *alpn = "-"; |
| if (const int alpn_id = m_http_sm->client_alpn_id; alpn_id != SessionProtocolNameRegistry::INVALID) { |
| ts::TextView client_sec_alpn = globalSessionProtocolNameRegistry.nameFor(alpn_id); |
| alpn = client_sec_alpn.data(); |
| } |
| |
| int round_len = LogAccess::strlen(alpn); |
| |
| if (buf) { |
| marshal_str(buf, alpn, round_len); |
| } |
| |
| return round_len; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_resp_content_type(char *buf) |
| { |
| int len = round_strlen(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 = round_strlen(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_http_sm->client_response_hdr_bytes + m_http_sm->client_response_body_bytes; |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_resp_content_len(char *buf) |
| { |
| if (buf) { |
| int64_t val = m_http_sm->client_response_body_bytes; |
| marshal_int(buf, val); |
| } |
| 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 = HTTP_STATUS_OK; |
| } |
| } else { |
| status = HTTP_STATUS_NONE; |
| } |
| marshal_int(buf, static_cast<int64_t>(status)); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_resp_header_len(char *buf) |
| { |
| if (buf) { |
| int64_t val = m_http_sm->client_response_hdr_bytes; |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_proxy_finish_status_code(char *buf) |
| { |
| /* FIXME: Should there be no server transaction code if |
| the result comes out of the cache. Right now we default |
| to FIN */ |
| if (buf) { |
| int code = LOG_FINISH_FIN; |
| if (m_http_sm->t_state.current.server) { |
| switch (m_http_sm->t_state.current.server->state) { |
| case HttpTransact::ACTIVE_TIMEOUT: |
| case HttpTransact::INACTIVE_TIMEOUT: |
| code = LOG_FINISH_TIMEOUT; |
| break; |
| case HttpTransact::CONNECTION_ERROR: |
| code = LOG_FINISH_INTR; |
| break; |
| default: |
| if (m_http_sm->t_state.current.server->abort == HttpTransact::ABORTED) { |
| code = LOG_FINISH_INTR; |
| } |
| break; |
| } |
| } |
| |
| marshal_int(buf, code); |
| } |
| |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_proxy_host_port(char *buf) |
| { |
| if (buf) { |
| uint16_t port = m_http_sm->t_state.request_data.incoming_port; |
| marshal_int(buf, port); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_cache_result_code(char *buf) |
| { |
| if (buf) { |
| SquidLogCode code = m_http_sm->t_state.squid_codes.log_code; |
| marshal_int(buf, static_cast<int64_t>(code)); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_cache_result_subcode(char *buf) |
| { |
| if (buf) { |
| SquidSubcode code = m_http_sm->t_state.squid_codes.subcode; |
| marshal_int(buf, static_cast<int64_t>(code)); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_cache_hit_miss(char *buf) |
| { |
| if (buf) { |
| SquidHitMissCode code = m_http_sm->t_state.squid_codes.hit_miss_code; |
| marshal_int(buf, static_cast<int64_t>(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_http_sm->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_http_sm->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_http_sm->t_state.current.server != nullptr ? &m_http_sm->t_state.current.server->src_addr.sa : nullptr); |
| } |
| |
| int |
| LogAccess::marshal_proxy_req_server_port(char *buf) |
| { |
| if (buf) { |
| uint16_t port = ntohs(m_http_sm->t_state.current.server != nullptr ? m_http_sm->t_state.current.server->src_addr.port() : 0); |
| marshal_int(buf, port); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_next_hop_ip(char *buf) |
| { |
| return marshal_ip(buf, m_http_sm->t_state.current.server != nullptr ? &m_http_sm->t_state.current.server->dst_addr.sa : nullptr); |
| } |
| |
| int |
| LogAccess::marshal_next_hop_port(char *buf) |
| { |
| if (buf) { |
| uint16_t port = ntohs(m_http_sm->t_state.current.server != nullptr ? m_http_sm->t_state.current.server->dst_addr.port() : 0); |
| marshal_int(buf, port); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_req_is_ssl(char *buf) |
| { |
| if (buf) { |
| int64_t is_ssl; |
| is_ssl = m_http_sm->server_connection_is_ssl; |
| marshal_int(buf, is_ssl); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_proxy_hierarchy_route(char *buf) |
| { |
| if (buf) { |
| SquidHierarchyCode code = m_http_sm->t_state.squid_codes.hier_code; |
| marshal_int(buf, static_cast<int64_t>(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; |
| ip = &m_http_sm->t_state.server_info.dst_addr.sa; |
| if (!ats_is_ip(ip)) { |
| if (m_http_sm->t_state.current.server) { |
| ip = &m_http_sm->t_state.current.server->dst_addr.sa; |
| if (!ats_is_ip(ip)) { |
| ip = nullptr; |
| } |
| } else { |
| ip = nullptr; |
| } |
| } |
| return marshal_ip(buf, ip); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_server_host_name(char *buf) |
| { |
| char *str = nullptr; |
| int len = INK_MIN_ALIGN; |
| |
| if (m_http_sm->t_state.current.server) { |
| str = m_http_sm->t_state.current.server->name; |
| len = LogAccess::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 = HTTP_STATUS_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_http_sm->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_http_sm->server_response_body_bytes; |
| } |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_server_resp_http_version(char *buf) |
| { |
| if (buf) { |
| int64_t major = 0; |
| int64_t minor = 0; |
| if (m_server_response) { |
| major = HTTP_MAJOR(m_server_response->version_get().m_version); |
| minor = HTTP_MINOR(m_server_response->version_get().m_version); |
| } |
| marshal_int(buf, major); |
| marshal_int((buf + INK_MIN_ALIGN), minor); |
| } |
| return (2 * INK_MIN_ALIGN); |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| int |
| LogAccess::marshal_server_resp_time_ms(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_http_sm->milestones.difference_msec(TS_MILESTONE_SERVER_CONNECT, TS_MILESTONE_SERVER_CLOSE)); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_server_resp_time_s(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, |
| static_cast<int64_t>(m_http_sm->milestones.difference_sec(TS_MILESTONE_SERVER_CONNECT, TS_MILESTONE_SERVER_CLOSE))); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_server_transact_count(char *buf) |
| { |
| if (buf) { |
| int64_t count; |
| count = m_http_sm->server_transact_count; |
| marshal_int(buf, count); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_server_connect_attempts(char *buf) |
| { |
| if (buf) { |
| int64_t attempts = m_http_sm->t_state.current.attempts; |
| marshal_int(buf, attempts); |
| } |
| 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 = HTTP_STATUS_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_http_sm->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_http_sm->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_http_sm->cache_response_hdr_bytes; |
| } |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_cache_resp_http_version(char *buf) |
| { |
| if (buf) { |
| int64_t major = 0; |
| int64_t minor = 0; |
| if (m_cache_response) { |
| major = HTTP_MAJOR(m_cache_response->version_get().m_version); |
| minor = HTTP_MINOR(m_cache_response->version_get().m_version); |
| } |
| marshal_int(buf, major); |
| marshal_int((buf + INK_MIN_ALIGN), minor); |
| } |
| return (2 * INK_MIN_ALIGN); |
| } |
| |
| int |
| LogAccess::marshal_client_retry_after_time(char *buf) |
| { |
| if (buf) { |
| int64_t crat = m_http_sm->t_state.congestion_control_crat; |
| marshal_int(buf, crat); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| static LogCacheWriteCodeType |
| convert_cache_write_code(HttpTransact::CacheWriteStatus_t t) |
| { |
| LogCacheWriteCodeType code; |
| switch (t) { |
| case HttpTransact::NO_CACHE_WRITE: |
| code = LOG_CACHE_WRITE_NONE; |
| break; |
| case HttpTransact::CACHE_WRITE_LOCK_MISS: |
| code = LOG_CACHE_WRITE_LOCK_MISSED; |
| break; |
| case HttpTransact::CACHE_WRITE_IN_PROGRESS: |
| // Hack - the HttpSM doesn't record |
| // cache write aborts currently so |
| // if it's not complete declare it |
| // aborted |
| code = LOG_CACHE_WRITE_LOCK_ABORTED; |
| break; |
| case HttpTransact::CACHE_WRITE_ERROR: |
| code = LOG_CACHE_WRITE_ERROR; |
| break; |
| case HttpTransact::CACHE_WRITE_COMPLETE: |
| code = LOG_CACHE_WRITE_COMPLETE; |
| break; |
| default: |
| ink_assert(!"bad cache write code"); |
| code = LOG_CACHE_WRITE_NONE; |
| break; |
| } |
| |
| return code; |
| } |
| |
| int |
| LogAccess::marshal_cache_write_code(char *buf) |
| { |
| if (buf) { |
| int code = convert_cache_write_code(m_http_sm->t_state.cache_info.write_status); |
| marshal_int(buf, code); |
| } |
| |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_cache_write_transform_code(char *buf) |
| { |
| if (buf) { |
| int code = convert_cache_write_code(m_http_sm->t_state.cache_info.transform_write_status); |
| marshal_int(buf, code); |
| } |
| |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_transfer_time_ms(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, m_http_sm->milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SM_FINISH)); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_transfer_time_s(char *buf) |
| { |
| if (buf) { |
| marshal_int(buf, static_cast<int64_t>(m_http_sm->milestones.difference_sec(TS_MILESTONE_SM_START, TS_MILESTONE_SM_FINISH))); |
| } |
| 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(MIME_FIELD_CONTENT_RANGE, MIME_LEN_CONTENT_RANGE))) { |
| int len; |
| char *str = const_cast<char *>(fld->value_get(&len)); |
| 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 |
| if (m_http_sm->server_response_body_bytes > 0) { |
| marshal_int(buf, m_http_sm->server_response_body_bytes); |
| } else if (m_http_sm->cache_response_body_bytes > 0) { |
| marshal_int(buf, m_http_sm->cache_response_body_bytes); |
| } |
| } |
| } |
| // 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) { |
| int64_t id = 0; |
| if (m_http_sm) { |
| id = m_http_sm->client_connection_id(); |
| } |
| marshal_int(buf, id); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_http_transaction_id(char *buf) |
| { |
| if (buf) { |
| int64_t id = 0; |
| if (m_http_sm) { |
| id = m_http_sm->client_transaction_id(); |
| } |
| marshal_int(buf, id); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_http_transaction_priority_weight(char *buf) |
| { |
| if (buf) { |
| int64_t id = 0; |
| if (m_http_sm) { |
| id = m_http_sm->client_transaction_priority_weight(); |
| } |
| marshal_int(buf, id); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_client_http_transaction_priority_dependence(char *buf) |
| { |
| if (buf) { |
| int64_t id = 0; |
| if (m_http_sm) { |
| id = m_http_sm->client_transaction_priority_dependence(); |
| } |
| marshal_int(buf, id); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_cache_read_retries(char *buf) |
| { |
| if (buf) { |
| int64_t id = 0; |
| if (m_http_sm) { |
| id = m_http_sm->get_cache_sm().get_open_read_tries(); |
| } |
| marshal_int(buf, id); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| /*------------------------------------------------------------------------- |
| -------------------------------------------------------------------------*/ |
| |
| int |
| LogAccess::marshal_cache_write_retries(char *buf) |
| { |
| if (buf) { |
| int64_t id = 0; |
| if (m_http_sm) { |
| id = m_http_sm->get_cache_sm().get_open_write_tries(); |
| } |
| marshal_int(buf, id); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_cache_collapsed_connection_success(char *buf) |
| { |
| if (buf) { |
| int64_t id = 0; // default - no collapse attempt |
| if (m_http_sm) { |
| SquidLogCode code = m_http_sm->t_state.squid_codes.log_code; |
| |
| // We increment open_write_tries beyond the max when we want to jump back to the read state for collapsing |
| if ((m_http_sm->get_cache_sm().get_open_write_tries() > (m_http_sm->t_state.txn_conf->max_cache_open_write_retries)) && |
| ((code == SQUID_LOG_TCP_HIT) || (code == SQUID_LOG_TCP_MEM_HIT) || (code == SQUID_LOG_TCP_DISK_HIT) || |
| (code == SQUID_LOG_TCP_CF_HIT))) { |
| // Attempted collapsed connection and got a hit, success |
| id = 1; |
| } else if (m_http_sm->get_cache_sm().get_open_write_tries() > (m_http_sm->t_state.txn_conf->max_cache_open_write_retries)) { |
| // Attempted collapsed connection with no hit, failure, we can also get +2 retries in a failure state |
| 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; |
| |
| switch (container) { |
| case LogField::CQH: |
| header = m_client_request; |
| break; |
| |
| case LogField::PSH: |
| header = m_proxy_response; |
| break; |
| |
| case LogField::PQH: |
| header = m_proxy_request; |
| break; |
| |
| case LogField::SSH: |
| header = m_server_response; |
| break; |
| |
| case LogField::CSSH: |
| header = m_cache_response; |
| break; |
| |
| default: |
| header = nullptr; |
| break; |
| } |
| |
| if (header) { |
| MIMEField *fld = header->field_find(field, static_cast<int>(::strlen(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) { |
| str = const_cast<char *>(fld->value_get(&actual_len)); |
| 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 = round_strlen(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_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; |
| |
| switch (container) { |
| case LogField::ECQH: |
| header = m_client_request; |
| break; |
| |
| case LogField::EPSH: |
| header = m_proxy_response; |
| break; |
| |
| case LogField::EPQH: |
| header = m_proxy_request; |
| break; |
| |
| case LogField::ESSH: |
| header = m_server_response; |
| break; |
| |
| case LogField::ECSSH: |
| header = m_cache_response; |
| break; |
| |
| default: |
| header = nullptr; |
| break; |
| } |
| |
| if (header) { |
| MIMEField *fld = header->field_find(field, static_cast<int>(::strlen(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) { |
| str = const_cast<char *>(fld->value_get(&actual_len)); |
| new_str = LogUtils::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 = round_strlen(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) { |
| int64_t val = ink_hrtime_to_msec(m_http_sm->milestones[ms]); |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_milestone_fmt_sec(TSMilestonesType type, char *buf) |
| { |
| if (buf) { |
| ink_hrtime tsec = ink_hrtime_to_sec(m_http_sm->milestones[type]); |
| marshal_int(buf, tsec); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_milestone_fmt_ms(TSMilestonesType type, char *buf) |
| { |
| if (buf) { |
| ink_hrtime tmsec = ink_hrtime_to_msec(m_http_sm->milestones[type]); |
| marshal_int(buf, tmsec); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| int |
| LogAccess::marshal_milestone_diff(TSMilestonesType ms1, TSMilestonesType ms2, char *buf) |
| { |
| if (buf) { |
| int64_t val = m_http_sm->milestones.difference_msec(ms2, ms1); |
| marshal_int(buf, val); |
| } |
| return INK_MIN_ALIGN; |
| } |
| |
| 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(field, static_cast<int>(::strlen(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, buf, len, true); |
| fld = fld->m_next_dup; |
| } |
| } |
| } |
| } |