return false;
}
+ // XXX: we are just parsing HTTP headers, not the whole message prefix here
+ hdr_sz = hp.messageHeaderSize();
pstate = psParsed;
hdrCacheInit();
return true;
void Adaptation::Icap::ModXact::finalizeLogInfo()
{
- HttpRequest * request_ = NULL;
- HttpRequest * adapted_request_ = NULL;
- HttpReply * reply_ = NULL;
- request_ = (virgin.cause? virgin.cause: dynamic_cast<HttpRequest*>(virgin.header));
+ HttpRequest *adapted_request_ = nullptr;
+ HttpReply *adapted_reply_ = nullptr;
+ HttpRequest *virgin_request_ = (virgin.cause ? virgin.cause : dynamic_cast<HttpRequest*>(virgin.header));
if (!(adapted_request_ = dynamic_cast<HttpRequest*>(adapted.header))) {
- adapted_request_ = request_;
- reply_ = dynamic_cast<HttpReply*>(adapted.header);
+ // if the request was not adapted, use virgin request to simplify
+ // the code further below
+ adapted_request_ = virgin_request_;
+ adapted_reply_ = dynamic_cast<HttpReply*>(adapted.header);
}
- Adaptation::Icap::History::Pointer h = (request_ ? request_->icapHistory() : NULL);
+ Adaptation::Icap::History::Pointer h = (virgin_request_ ? virgin_request_->icapHistory() : NULL);
Must(h != NULL); // ICAPXaction::maybeLog calls only if there is a log
al.icp.opcode = ICP_INVALID;
al.url = h->log_uri.termedBuf();
const Adaptation::Icap::ServiceRep &s = service();
al.icap.reqMethod = s.cfg().method;
- al.cache.caddr = request_->client_addr;
+ al.cache.caddr = virgin_request_->client_addr;
- al.request = request_;
+ al.request = virgin_request_;
HTTPMSGLOCK(al.request);
al.adapted_request = adapted_request_;
HTTPMSGLOCK(al.adapted_request);
- if (reply_) {
- al.reply = reply_;
+ if (adapted_reply_) {
+ al.reply = adapted_reply_;
HTTPMSGLOCK(al.reply);
} else
al.reply = NULL;
al.cache.ssluser = h->ssluser.termedBuf();
#endif
al.cache.code = h->logType;
- // XXX: should use icap-specific counters instead ?
- al.http.clientRequestSz.payloadData = h->req_sz;
+
+ const HttpMsg *virgin_msg = dynamic_cast<HttpReply*>(virgin.header);
+ if (!virgin_msg)
+ virgin_msg = virgin_request_;
+ assert(virgin_msg != virgin.cause);
+ al.http.clientRequestSz.header = virgin_msg->hdr_sz;
+ al.http.clientRequestSz.payloadData = virgin_msg->body_pipe->producedSize();
// leave al.icap.bodyBytesRead negative if no body
if (replyHttpHeaderSize >= 0 || replyHttpBodySize >= 0) {
const int64_t zero = 0; // to make max() argument types the same
- al.icap.bodyBytesRead =
- max(zero, replyHttpHeaderSize) + max(zero, replyHttpBodySize);
+ const uint64_t headerSize = max(zero, replyHttpHeaderSize);
+ const uint64_t bodySize = max(zero, replyHttpBodySize);
+ al.icap.bodyBytesRead = headerSize + bodySize;
+ al.http.clientReplySz.header = headerSize;
+ al.http.clientReplySz.payloadData = bodySize;
}
- if (reply_) {
- al.http.code = reply_->sline.status();
- al.http.content_type = reply_->content_type.termedBuf();
- if (replyHttpBodySize >= 0) {
- // XXX: should use icap-specific counters instead ?
- al.http.clientReplySz.payloadData = replyHttpBodySize;
- al.http.clientReplySz.header = reply_->hdr_sz;
+ if (adapted_reply_) {
+ al.http.code = adapted_reply_->sline.status();
+ al.http.content_type = adapted_reply_->content_type.termedBuf();
+ if (replyHttpBodySize >= 0)
al.cache.highOffset = replyHttpBodySize;
- }
//don't set al.cache.objectSize because it hasn't exist yet
MemBuf mb;
mb.init();
- reply_->header.packInto(&mb);
+ adapted_reply_->header.packInto(&mb);
al.headers.reply = xstrdup(mb.buf);
mb.clean();
}
ICAP transaction log lines will correspond to a single access
log line.
- ICAP log uses logformat codes that make sense for an ICAP
- transaction. Header-related codes are applied to the HTTP header
- embedded in an ICAP server response, with the following caveats:
- For REQMOD, there is no HTTP response header unless the ICAP
- server performed request satisfaction. For RESPMOD, the HTTP
- request header is the header sent to the ICAP server. For
- OPTIONS, there are no HTTP headers.
+ ICAP log supports many access.log logformat %codes. In ICAP context,
+ HTTP message-related %codes are applied to the HTTP message embedded
+ in an ICAP message. Logformat "%http::>..." codes are used for HTTP
+ messages embedded in ICAP requests while "%http::<..." codes are used
+ for HTTP messages embedded in ICAP responses. For example:
+
+ http::>h To-be-adapted HTTP message headers sent by Squid to
+ the ICAP service. For REQMOD transactions, these are
+ HTTP request headers. For RESPMOD, these are HTTP
+ response headers, but Squid currently cannot log them
+ (i.e., %http::>h will expand to "-" for RESPMOD).
+
+ http::<h Adapted HTTP message headers sent by the ICAP
+ service to Squid (i.e., HTTP request headers in regular
+ REQMOD; HTTP response headers in RESPMOD and during
+ request satisfaction in REQMOD).
+
+ ICAP OPTIONS transactions do not embed HTTP messages.
+
+ Several logformat codes below deal with ICAP message bodies. An ICAP
+ message body, if any, typically includes a complete HTTP message
+ (required HTTP headers plus optional HTTP message body). When
+ computing HTTP message body size for these logformat codes, Squid
+ either includes or excludes chunked encoding overheads; see
+ code-specific documentation for details.
+
+ For Secure ICAP services, all size-related information is currently
+ computed before/after TLS encryption/decryption, as if TLS was not
+ in use at all.
The following format codes are also available for ICAP logs:
icap::rm ICAP request method (REQMOD, RESPMOD, or
OPTIONS). Similar to existing rm.
- icap::>st Bytes sent to the ICAP server (TCP payload
- only; i.e., what Squid writes to the socket).
+ icap::>st The total size of the ICAP request sent to the ICAP
+ server (ICAP headers + ICAP body), including chunking
+ metadata (if any).
- icap::<st Bytes received from the ICAP server (TCP
- payload only; i.e., what Squid reads from
- the socket).
+ icap::<st The total size of the ICAP response received from the
+ ICAP server (ICAP headers + ICAP body), including
+ chunking metadata (if any).
- icap::<bs Number of message body bytes received from the
- ICAP server. ICAP message body, if any, usually
- includes encapsulated HTTP message headers and
- possibly encapsulated HTTP message body. The
- HTTP body part is dechunked before its size is
- computed.
+ icap::<bs The size of the ICAP response body received from the
+ ICAP server, excluding chunking metadata (if any).
icap::tr Transaction response time (in
milliseconds). The timer starts when
The default ICAP log format, which can be used without an explicit
definition, is called icap_squid:
-logformat icap_squid %ts.%03tu %6icap::tr %>a %icap::to/%03icap::Hs %icap::<size %icap::rm %icap::ru% %un -/%icap::<A -
+logformat icap_squid %ts.%03tu %6icap::tr %>A %icap::to/%03icap::Hs %icap::<st %icap::rm %icap::ru %un -/%icap::<A -
- See also: logformat, log_icap, and %adapt::<last_h
+ See also: logformat and %adapt::<last_h
DOC_END
NAME: logfile_daemon
aLogEntry->http.method = request->method;
aLogEntry->http.version = request->http_ver;
aLogEntry->hier = request->hier;
- if (request->content_length > 0) // negative when no body or unknown length
- aLogEntry->http.clientRequestSz.payloadData += request->content_length; // XXX: actually adaptedRequest payload size ??
aLogEntry->cache.extuser = request->extacl_user.termedBuf();
// Adapted request, if any, inherits and then collects all the stats, but
al->cache.objectSize = loggingEntry()->contentLen(); // payload duplicate ?? with or without TE ?
al->http.clientRequestSz.header = req_sz;
+ // the virgin request is saved to al->request
+ if (al->request && al->request->body_pipe)
+ al->http.clientRequestSz.payloadData = al->request->body_pipe->producedSize();
al->http.clientReplySz.header = out.headers_sz;
// XXX: calculate without payload encoding or headers !!
al->http.clientReplySz.payloadData = out.size - out.headers_sz; // pretend its all un-encoded data for now.
}
#endif
+static const HttpMsg *
+actualReplyHeader(const AccessLogEntry::Pointer &al)
+{
+ const HttpMsg *msg = al->reply;
+ if (!msg && al->icap.reqMethod == Adaptation::methodReqmod)
+ msg = al->adapted_request;
+ return msg;
+}
+
+static const HttpMsg *
+actualRequestHeader(const AccessLogEntry::Pointer &al)
+{
+ if (al->icap.reqMethod == Adaptation::methodRespmod) {
+ // XXX: for now AccessLogEntry lacks virgin response headers
+ return nullptr;
+ }
+ return al->request;
+}
+
void
Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logSequenceNumber) const
{
break;
case LFT_REQUEST_HEADER:
-
- if (al->request)
- sb = al->request->header.getByName(fmt->data.header.header);
+ if (const HttpMsg *msg = actualRequestHeader(al))
+ sb = msg->header.getByName(fmt->data.header.header);
out = sb.termedBuf();
break;
- case LFT_REPLY_HEADER:
- if (al->reply)
- sb = al->reply->header.getByName(fmt->data.header.header);
+ case LFT_REPLY_HEADER: {
+ if (const HttpMsg *msg = actualReplyHeader(al))
+ sb = msg->header.getByName(fmt->data.header.header);
out = sb.termedBuf();
quote = 1;
-
- break;
+ }
+ break;
#if USE_ADAPTATION
case LFT_ADAPTATION_SUM_XACT_TIMES:
break;
#endif
case LFT_REQUEST_HEADER_ELEM:
- if (al->request)
- sb = al->request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+ if (const HttpMsg *msg = actualRequestHeader(al))
+ sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
out = sb.termedBuf();
break;
- case LFT_REPLY_HEADER_ELEM:
- if (al->reply)
- sb = al->reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+ case LFT_REPLY_HEADER_ELEM: {
+ if (const HttpMsg *msg = actualReplyHeader(al))
+ sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
out = sb.termedBuf();
quote = 1;
-
- break;
+ }
+ break;
case LFT_REQUEST_ALL_HEADERS:
- out = al->headers.request;
+ if (al->icap.reqMethod == Adaptation::methodRespmod) {
+ // XXX: since AccessLogEntry::Headers lacks virgin response
+ // headers, do nothing for now
+ out = nullptr;
+ } else {
+ out = al->headers.request;
+ }
quote = 1;
case LFT_REPLY_ALL_HEADERS:
out = al->headers.reply;
+ if (!out && al->icap.reqMethod == Adaptation::methodReqmod)
+ out = al->headers.adapted_request;
quote = 1;