Headers() : request(NULL),
adapted_request(NULL),
-#if ICAP_CLIENT
- icap(NULL),
+#if USE_ADAPTATION
+ adapt_last(NULL),
#endif
reply(NULL) {}
char *adapted_request; //< HTTP request headers after adaptation and redirection
-#if ICAP_CLIENT
- char * icap; ///< last matching ICAP response header.
+#if USE_ADAPTATION
+
+ char *adapt_last; ///< last ICAP response header or eCAP meta received
#endif
char *reply;
} headers;
}
-Adaptation::History::History(): theNextServices(TheNullServices)
+Adaptation::History::History():
+ lastMeta(hoReply),
+ allMeta(hoReply),
+ theNextServices(TheNullServices)
{
}
theNextServices = TheNullServices; // prevents resetting the plan twice
return true;
}
+
+void Adaptation::History::recordMeta(const HttpHeader *lm)
+{
+ lastMeta.clean();
+ lastMeta.update(lm, NULL);
+
+ allMeta.update(lm, NULL);
+ allMeta.compact();
+}
#ifndef SQUID_ADAPT_HISTORY_H
#define SQUID_ADAPT_HISTORY_H
-#include "RefCount.h"
#include "Array.h"
+#include "HttpHeader.h"
+#include "RefCount.h"
#include "SquidString.h"
namespace Adaptation
/// returns true, fills the value, and resets iff next services were set
bool extractNextServices(String &value);
+ /// store the last meta header fields received from the adaptation service
+ void recordMeta(const HttpHeader *lm);
+
+public:
+ /// Last received meta header (REQMOD or RESPMOD, whichever comes last).
+ HttpHeader lastMeta;
+ /// All REQMOD and RESPMOD meta headers merged. Last field wins conflicts.
+ HttpHeader allMeta;
+
private:
/// single Xaction stats (i.e., a historical record entry)
class Entry
CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Ecap::XactionRep, XactionRep);
+/// a libecap Visitor for converting adapter transaction options to HttpHeader
+class OptionsExtractor: public libecap::NamedValueVisitor
+{
+public:
+ typedef libecap::Name Name;
+ typedef libecap::Area Area;
+
+ OptionsExtractor(HttpHeader &aMeta): meta(aMeta) {}
+
+ // libecap::NamedValueVisitor API
+ virtual void visit(const Name &name, const Area &value)
+ {
+ meta.putExt(name.image().c_str(), value.toString().c_str());
+ }
+
+ HttpHeader &meta; ///< where to put extracted options
+};
+
Adaptation::Ecap::XactionRep::XactionRep(
HttpMsg *virginHeader, HttpRequest *virginCause,
const Adaptation::ServicePointer &aService):
return clientIpValue();
if (name == libecap::metaUserName)
return usernameValue();
- // TODO: metaServerIp, metaAuthenticatedUser, metaAuthenticatedGroups, and
- // Adaptation::Config::masterx_shared_name
+ if (name == Adaptation::Config::masterx_shared_name)
+ return masterxSharedValue(name);
+
+ // TODO: metaServerIp, metaAuthenticatedUser, and metaAuthenticatedGroups
return libecap::Area();
}
visitor.visit(libecap::metaClientIp, value);
if (const libecap::Area value = usernameValue())
visitor.visit(libecap::metaUserName, value);
- // TODO: metaServerIp, metaAuthenticatedUser, metaAuthenticatedGroups, and
- // Adaptation::Config::masterx_shared_name
+
+ if (Adaptation::Config::masterx_shared_name) {
+ const libecap::Name name(Adaptation::Config::masterx_shared_name);
+ if (const libecap::Area value = masterxSharedValue(name))
+ visitor.visit(name, value);
+ }
+
+ // TODO: metaServerIp, metaAuthenticatedUser, and metaAuthenticatedGroups
}
const libecap::Area
return libecap::Area();
}
+const libecap::Area
+Adaptation::Ecap::XactionRep::masterxSharedValue(const libecap::Name &name) const
+{
+ const HttpRequest *request = dynamic_cast<const HttpRequest*>(theCauseRep ?
+ theCauseRep->raw().header : theVirginRep.raw().header);
+ Must(request);
+ if (name.known()) { // must check to avoid empty names matching unset cfg
+ Adaptation::History::Pointer ah = request->adaptHistory(false);
+ if (ah != NULL) {
+ String name, value;
+ if (ah->getXxRecord(name, value))
+ return libecap::Area::FromTempBuffer(value.rawBuf(), value.size());
+ }
+ }
+ return libecap::Area();
+}
+
void
Adaptation::Ecap::XactionRep::start()
{
// check that clone() copies the pipe so that we do not have to
Must(!theVirginRep.raw().header->body_pipe == !clone->body_pipe);
+ updateHistory();
sendAnswer(Answer::Forward(clone));
Must(done());
}
HttpMsg *msg = answer().header;
if (!theAnswerRep->body()) { // final, bodyless answer
proxyingAb = opNever;
+ updateHistory();
sendAnswer(Answer::Forward(msg));
} else { // got answer headers but need to handle body
proxyingAb = opOn;
rep->tieBody(this); // sets us as a producer
Must(msg->body_pipe != NULL); // check tieBody
+ updateHistory();
sendAnswer(Answer::Forward(msg));
debugs(93,4, HERE << "adapter will produce body" << status());
sinkVb("blockVirgin");
+ updateHistory();
sendAnswer(Answer::Block(service().cfg().key));
Must(done());
}
+/// Called just before sendAnswer() to record adapter meta-information
+/// which may affect answer processing and may be needed for logging.
+void
+Adaptation::Ecap::XactionRep::updateHistory()
+{
+ if (!theMaster) // all updates rely on being able to query the adapter
+ return;
+
+ const HttpRequest *request = dynamic_cast<const HttpRequest*>(theCauseRep ?
+ theCauseRep->raw().header : theVirginRep.raw().header);
+ Must(request);
+
+ // TODO: move common ICAP/eCAP logic to Adaptation::Xaction or similar
+ // TODO: optimize Area-to-String conversion
+
+ // update the cross-transactional database if needed
+ if (const char *xxNameStr = Adaptation::Config::masterx_shared_name) {
+ Adaptation::History::Pointer ah = request->adaptHistory(true);
+ if (ah != NULL) {
+ libecap::Name xxName(xxNameStr); // TODO: optimize?
+ if (const libecap::Area val = theMaster->option(xxName))
+ ah->updateXxRecord(xxNameStr, val.toString().c_str());
+ }
+ }
+
+ // update the adaptation plan if needed
+ if (service().cfg().routing) {
+ String services;
+ if (const libecap::Area services = theMaster->option(libecap::metaNextServices)) {
+ Adaptation::History::Pointer ah = request->adaptHistory(true);
+ if (ah != NULL)
+ ah->updateNextServices(services.toString().c_str());
+ }
+ } // TODO: else warn (occasionally!) if we got libecap::metaNextServices
+
+ // Store received meta headers for adapt::<last_h logformat code use.
+ // If we already have stored headers from a previous adaptation transaction
+ // related to the same master transction, they will be replaced.
+ Adaptation::History::Pointer ah = request->adaptLogHistory();
+ if (ah != NULL) {
+ HttpHeader meta(hoReply);
+ OptionsExtractor extractor(meta);
+ theMaster->visitEachOption(extractor);
+ ah->recordMeta(&meta);
+ }
+}
+
+
void
Adaptation::Ecap::XactionRep::vbDiscard()
{
void moveAbContent();
+ void updateHistory();
void terminateMaster();
void scheduleStop(const char *reason);
const libecap::Area clientIpValue() const;
const libecap::Area usernameValue() const;
+ const libecap::Area masterxSharedValue(const libecap::Name &name) const;
private:
AdapterXaction theMaster; // the actual adaptation xaction we represent
#include "globals.h"
#include "SquidTime.h"
-Adaptation::Icap::History::History(): mergeOfIcapHeaders(hoRequest),
- lastIcapHeader(hoRequest), logType(LOG_TAG_NONE), req_sz(0),
+Adaptation::Icap::History::History():
+ logType(LOG_TAG_NONE), req_sz(0),
pastTime(0), concurrencyLevel(0)
{
}
-Adaptation::Icap::History::History(const Adaptation::Icap::History& ih)
-{
- assign(ih);
-}
-
-Adaptation::Icap::History::~History()
-{
- mergeOfIcapHeaders.clean();
- lastIcapHeader.clean();
- rfc931.clean();
-#if USE_SSL
- ssluser.clean();
-#endif
- log_uri.clean();
-}
-
-void Adaptation::Icap::History::assign(const Adaptation::Icap::History& ih)
-{
- mergeOfIcapHeaders.clean();
- mergeOfIcapHeaders.update(&ih.mergeOfIcapHeaders, NULL);
- lastIcapHeader.clean();
- lastIcapHeader.update(&ih.lastIcapHeader, NULL);
- rfc931 = ih.rfc931;
-
-#if USE_SSL
- ssluser = ih.ssluser;
-#endif
-
- logType = ih.logType;
- log_uri = ih.log_uri;
- req_sz = ih.req_sz;
- pastTime = ih.pastTime;
- currentStart = ih.currentStart;
- concurrencyLevel = ih.concurrencyLevel;
- debugs(93,7, HERE << this << " = " << &ih);
-}
-
-Adaptation::Icap::History& Adaptation::Icap::History::operator=(const History& ih)
-{
- if (this != &ih)
- assign(ih);
- return *this;
-}
-
-void Adaptation::Icap::History::setIcapLastHeader(const HttpHeader * lih)
-{
- lastIcapHeader.clean();
- lastIcapHeader.update(lih, NULL);
-}
-
-void Adaptation::Icap::History::mergeIcapHeaders(const HttpHeader * lih)
-{
- mergeOfIcapHeaders.update(lih, NULL);
- mergeOfIcapHeaders.compact();
-}
-
void Adaptation::Icap::History::start(const char *context)
{
if (!concurrencyLevel++)
#define SQUID_ICAPHISTORY_H
#include "RefCount.h"
-#include "HttpHeader.h"
#include "enums.h"
namespace Adaptation
typedef RefCount<History> Pointer;
History();
- History(const History& ih);
-
- ~History();
-
- History& operator=(const History& ih);
-
- ///store the last reply header from ICAP server
- void setIcapLastHeader(const HttpHeader * lih);
- ///merge new header and stored one
- void mergeIcapHeaders(const HttpHeader * lih);
/// record the start of an ICAP processing interval
void start(const char *context);
/// returns the total time of all ICAP processing intervals
int processingTime() const;
- HttpHeader mergeOfIcapHeaders; ///< Merge of REQMOD and RESPMOD responses. If both responses contain the header, the one from the last response will be used
- HttpHeader lastIcapHeader; ///< Last received reply from ICAP server
String rfc931; ///< the username from ident
#if USE_SSL
String ssluser; ///< the username from SSL
size_t req_sz; ///< the request size
private:
- void assign(const History &);
-
int currentTime() const; ///< time since current start or zero
timeval currentStart; ///< when the current processing interval started
// update the cross-transactional database if needed (all status codes!)
if (const char *xxName = Adaptation::Config::masterx_shared_name) {
Adaptation::History::Pointer ah = request->adaptHistory(true);
- if (ah != NULL) {
+ if (ah != NULL) { // TODO: reorder checks to avoid creating history
const String val = icapReply->header.getByName(xxName);
if (val.size() > 0) // XXX: HttpHeader lacks empty value detection
ah->updateXxRecord(xxName, val);
// If we already have stored headers from previous ICAP transaction related to this
// request, old headers will be replaced with the new one.
- Adaptation::Icap::History::Pointer h = request->icapHistory();
- if (h != NULL) {
- h->mergeIcapHeaders(&icapReply->header);
- h->setIcapLastHeader(&icapReply->header);
- }
+ Adaptation::History::Pointer ah = request->adaptLogHistory();
+ if (ah != NULL)
+ ah->recordMeta(&icapReply->header);
// handle100Continue() manages state.writing on its own.
// Non-100 status means the server needs no postPreview data from us.
// share the cross-transactional database records if needed
if (Adaptation::Config::masterx_shared_name) {
+ // XXX: do not create history here: there can be no values in empty ah
Adaptation::History::Pointer ah = request->adaptHistory(true);
if (ah != NULL) {
String name, value;
sent to the first selected peer. The timer stops
with the last I/O with the last peer.
- If ICAP is enabled, the following two codes become available (as
+ If ICAP is enabled, the following code becomes available (as
well as ICAP log codes documented with the icap_log option):
icap::tt Total ICAP processing time for the HTTP
ACLs are checked and when ICAP
transaction is in progress.
- icap::<last_h The header of the last ICAP response
- related to the HTTP transaction. Like
- <h, accepts an optional header name
- argument. Will not change semantics
- when multiple ICAP transactions per HTTP
- transaction are supported.
+ If adaptation is enabled the following three codes become available:
- If adaptation is enabled the following two codes become available:
+ adapt::<last_h The header of the last ICAP response or
+ meta-information from the last eCAP
+ transaction related to the HTTP transaction.
+ Like <h, accepts an optional header name
+ argument.
adapt::sum_trs Summed adaptation transaction response
times recorded as a comma-separated list in
logformat icap_squid %ts.%03tu %6icap::tr %>a %icap::to/%03icap::Hs %icap::<size %icap::rm %icap::ru% %un -/%icap::<A -
- See also: logformat, log_icap, and %icap::<last_h
+ See also: logformat, log_icap, and %adapt::<last_h
DOC_END
NAME: logfile_daemon
An ICAP REQMOD or RESPMOD transaction may set an entry in the
shared table by returning an ICAP header field with a name
- specified in adaptation_masterx_shared_names. Squid will store
- and forward that ICAP header field to subsequent ICAP
+ specified in adaptation_masterx_shared_names. An eCAP REQMOD or
+ RESPMOD transaction may set an entry in the shared table by
+ implementing the libecap::visitEachOption() API to provide an
+ option with a name specified in adaptation_masterx_shared_names.
+
+ Squid will store and forward the set entry to subsequent adaptation
transactions within the same master transaction scope.
Only one shared entry name is supported at this time.
assert(request);
assert(aLogEntry);
-#if ICAP_CLIENT
- Adaptation::Icap::History::Pointer ih = request->icapHistory();
-#endif
if (Config.onoff.log_mime_hdrs) {
Packer p;
MemBuf mb;
aLogEntry->headers.request = xstrdup(mb.buf);
}
-#if ICAP_CLIENT
- packerClean(&p);
- mb.reset();
- packerToMemInit(&p, &mb);
-
- if (ih != NULL)
- ih->lastIcapHeader.packInto(&p);
- aLogEntry->headers.icap = xstrdup(mb.buf);
+#if USE_ADAPTATION
+ const Adaptation::History::Pointer ah = request->adaptLogHistory();
+ if (ah != NULL) {
+ packerClean(&p);
+ mb.reset();
+ packerToMemInit(&p, &mb);
+ ah->lastMeta.packInto(&p);
+ aLogEntry->headers.adapt_last = xstrdup(mb.buf);
+ }
#endif
packerClean(&p);
}
#if ICAP_CLIENT
+ const Adaptation::Icap::History::Pointer ih = request->icapHistory();
if (ih != NULL)
aLogEntry->icap.processingTime = ih->processingTime();
#endif
#if USE_ADAPTATION
LTF_ADAPTATION_SUM_XACT_TIMES,
LTF_ADAPTATION_ALL_XACT_TIMES,
+ LFT_ADAPTATION_LAST_HEADER,
+ LFT_ADAPTATION_LAST_HEADER_ELEM,
+ LFT_ADAPTATION_LAST_ALL_HEADERS,
#endif
#if ICAP_CLIENT
LFT_ICAP_TOTAL_TIME,
- LFT_ICAP_LAST_MATCHED_HEADER,
- LFT_ICAP_LAST_MATCHED_HEADER_ELEM,
- LFT_ICAP_LAST_MATCHED_ALL_HEADERS,
LFT_ICAP_ADDR,
LFT_ICAP_SERV_NAME,
#if USE_ADAPTATION
{"adapt::all_trs", LTF_ADAPTATION_ALL_XACT_TIMES},
{"adapt::sum_trs", LTF_ADAPTATION_SUM_XACT_TIMES},
+ {"adapt::<last_h", LFT_ADAPTATION_LAST_HEADER},
#endif
#if ICAP_CLIENT
{"icap::tt", LFT_ICAP_TOTAL_TIME},
- {"icap::<last_h", LFT_ICAP_LAST_MATCHED_HEADER},
+ {"icap::<last_h", LFT_ADAPTATION_LAST_HEADER}, // deprecated
{"icap::<A", LFT_ICAP_ADDR},
{"icap::<service_name", LFT_ICAP_SERV_NAME},
out = sb.termedBuf();
}
break;
-#endif
-#if ICAP_CLIENT
- case LFT_ICAP_LAST_MATCHED_HEADER:
+ case LFT_ADAPTATION_LAST_HEADER:
if (al->request) {
- Adaptation::Icap::History::Pointer ih = al->request->icapHistory();
- if (ih != NULL)
- sb = ih->mergeOfIcapHeaders.getByName(fmt->data.header.header);
+ const Adaptation::History::Pointer ah = al->request->adaptHistory();
+ if (ah != NULL) // XXX: add adapt::<all_h but use lastMeta here
+ sb = ah->allMeta.getByName(fmt->data.header.header);
}
+ // XXX: here and elsewhere: move such code inside the if guard
out = sb.termedBuf();
quote = 1;
break;
- case LFT_ICAP_LAST_MATCHED_HEADER_ELEM:
+ case LFT_ADAPTATION_LAST_HEADER_ELEM:
if (al->request) {
- Adaptation::Icap::History::Pointer ih = al->request->icapHistory();
- if (ih != NULL)
- sb = ih->mergeOfIcapHeaders.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+ const Adaptation::History::Pointer ah = al->request->adaptHistory();
+ if (ah != NULL) // XXX: add adapt::<all_h but use lastMeta here
+ sb = ah->allMeta.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
}
out = sb.termedBuf();
break;
- case LFT_ICAP_LAST_MATCHED_ALL_HEADERS:
- out = al->headers.icap;
+ case LFT_ADAPTATION_LAST_ALL_HEADERS:
+ out = al->headers.adapt_last;
quote = 1;
break;
+#endif
+#if ICAP_CLIENT
case LFT_ICAP_ADDR:
if (!out)
out = al->icap.hostAddr.NtoA(tmp,1024);
switch (lt->type) {
-#if ICAP_CLIENT
- case LFT_ICAP_LAST_MATCHED_HEADER:
+#if USE_ADAPTATION
+ case LFT_ADAPTATION_LAST_HEADER:
+#endif
+#if ICAP_CLIENT
case LFT_ICAP_REQ_HEADER:
case LFT_ICAP_REP_HEADER:
case LFT_REPLY_HEADER:
lt->type = LFT_REPLY_HEADER_ELEM;
break;
-#if ICAP_CLIENT
- case LFT_ICAP_LAST_MATCHED_HEADER:
- lt->type = LFT_ICAP_LAST_MATCHED_HEADER_ELEM;
+#if USE_ADAPTATION
+ case LFT_ADAPTATION_LAST_HEADER:
+ lt->type = LFT_ADAPTATION_LAST_HEADER_ELEM;
break;
+#endif
+#if ICAP_CLIENT
case LFT_ICAP_REQ_HEADER:
lt->type = LFT_ICAP_REQ_HEADER_ELEM;
break;
case LFT_REPLY_HEADER:
lt->type = LFT_REPLY_ALL_HEADERS;
break;
-#if ICAP_CLIENT
- case LFT_ICAP_LAST_MATCHED_HEADER:
- lt->type = LFT_ICAP_LAST_MATCHED_ALL_HEADERS;
+#if USE_ADAPTATION
+ case LFT_ADAPTATION_LAST_HEADER:
+ lt->type = LFT_ADAPTATION_LAST_ALL_HEADERS;
break;
+#endif
+#if ICAP_CLIENT
case LFT_ICAP_REQ_HEADER:
lt->type = LFT_ICAP_REQ_ALL_HEADERS;
break;
case LFT_STRING:
break;
+#if USE_ADAPTATION
+ case LFT_ADAPTATION_LAST_HEADER_ELEM:
+#endif
#if ICAP_CLIENT
- case LFT_ICAP_LAST_MATCHED_HEADER_ELEM:
case LFT_ICAP_REQ_HEADER_ELEM:
case LFT_ICAP_REP_HEADER_ELEM:
#endif
switch (type) {
case LFT_REQUEST_HEADER_ELEM:
- type = LFT_REQUEST_HEADER_ELEM;
+ type = LFT_REQUEST_HEADER;
break;
case LFT_ADAPTED_REQUEST_HEADER_ELEM:
- type = LFT_ADAPTED_REQUEST_HEADER_ELEM;
+ type = LFT_ADAPTED_REQUEST_HEADER;
break;
case LFT_REPLY_HEADER_ELEM:
- type = LFT_REPLY_HEADER_ELEM;
+ type = LFT_REPLY_HEADER;
break;
-#if ICAP_CLIENT
- case LFT_ICAP_LAST_MATCHED_HEADER_ELEM:
- type = LFT_ICAP_LAST_MATCHED_HEADER;
+#if USE_ADAPTATION
+ case LFT_ADAPTATION_LAST_HEADER_ELEM:
+ type = LFT_ADAPTATION_LAST_HEADER;
break;
+#endif
+#if ICAP_CLIENT
case LFT_ICAP_REQ_HEADER_ELEM:
type = LFT_ICAP_REQ_HEADER;
break;
case LFT_ADAPTED_REQUEST_ALL_HEADERS:
case LFT_REPLY_ALL_HEADERS:
+#if USE_ADAPTATION
+ case LFT_ADAPTATION_LAST_ALL_HEADERS:
+#endif
#if ICAP_CLIENT
- case LFT_ICAP_LAST_MATCHED_ALL_HEADERS:
case LFT_ICAP_REQ_ALL_HEADERS:
case LFT_ICAP_REP_ALL_HEADERS:
#endif
case LFT_REPLY_ALL_HEADERS:
type = LFT_REPLY_HEADER;
break;
-#if ICAP_CLIENT
- case LFT_ICAP_LAST_MATCHED_ALL_HEADERS:
- type = LFT_ICAP_LAST_MATCHED_HEADER;
+#if USE_ADAPTATION
+ case LFT_ADAPTATION_LAST_ALL_HEADERS:
+ type = LFT_ADAPTATION_LAST_HEADER;
break;
+#endif
+#if ICAP_CLIENT
case LFT_ICAP_REQ_ALL_HEADERS:
type = LFT_ICAP_REQ_HEADER;
break;
LogfileStatus = LOG_ENABLE;
-#if USE_ADAPTATION || ICAP_CLIENT
- for (logformat_token * curr_token = (log->logFormat?log->logFormat->format:NULL); curr_token; curr_token = curr_token->next) {
#if USE_ADAPTATION
+ for (logformat_token * curr_token = (log->logFormat?log->logFormat->format:NULL); curr_token; curr_token = curr_token->next) {
if (curr_token->type == LTF_ADAPTATION_SUM_XACT_TIMES ||
- curr_token->type == LTF_ADAPTATION_ALL_XACT_TIMES) {
+ curr_token->type == LTF_ADAPTATION_ALL_XACT_TIMES ||
+ curr_token->type == LFT_ADAPTATION_LAST_HEADER ||
+ curr_token->type == LFT_ADAPTATION_LAST_HEADER_ELEM ||
+ curr_token->type == LFT_ADAPTATION_LAST_ALL_HEADERS) {
alLogformatHasAdaptToken = true;
}
-#endif
#if ICAP_CLIENT
- if (curr_token->type == LFT_ICAP_LAST_MATCHED_HEADER ||
- curr_token->type == LFT_ICAP_LAST_MATCHED_HEADER_ELEM ||
- curr_token->type == LFT_ICAP_LAST_MATCHED_ALL_HEADERS) {
+ if (curr_token->type == LFT_ICAP_TOTAL_TIME) {
alLogformatHasIcapToken = true;
}
#endif
safe_free(aLogEntry->headers.request);
#if ICAP_CLIENT
- safe_free(aLogEntry->headers.icap);
+ safe_free(aLogEntry->headers.adapt_last);
#endif
safe_free(aLogEntry->headers.reply);