/*
- * DEBUG: section 93 eCAP Interface
+ * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
*/
+
+/* DEBUG: section 93 eCAP Interface */
+
#include "squid.h"
#include <libecap/common/area.h>
#include <libecap/common/delay.h>
#include <libecap/common/named_values.h>
#include <libecap/common/names.h>
#include <libecap/adapter/xaction.h>
-#include "HttpRequest.h"
-#include "HttpReply.h"
-#include "SquidTime.h"
-#include "adaptation/ecap/XactionRep.h"
+#include "adaptation/Answer.h"
#include "adaptation/ecap/Config.h"
+#include "adaptation/ecap/XactionRep.h"
#include "adaptation/Initiator.h"
+#include "base/AsyncJobCalls.h"
#include "base/TextException.h"
+#include "format/Format.h"
+#include "HttpReply.h"
+#include "HttpRequest.h"
+#include "MasterXaction.h"
+#include "SquidTime.h"
CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Ecap::XactionRep, XactionRep);
-
/// a libecap Visitor for converting adapter transaction options to HttpHeader
class OptionsExtractor: public libecap::NamedValueVisitor
{
OptionsExtractor(HttpHeader &aMeta): meta(aMeta) {}
// libecap::NamedValueVisitor API
- virtual void visit(const Name &name, const Area &value)
- {
+ virtual void visit(const Name &name, const Area &value) {
meta.putExt(name.image().c_str(), value.toString().c_str());
}
};
Adaptation::Ecap::XactionRep::XactionRep(
- HttpMsg *virginHeader, HttpRequest *virginCause,
+ Http::Message *virginHeader, HttpRequest *virginCause, AccessLogEntry::Pointer &alp,
const Adaptation::ServicePointer &aService):
- AsyncJob("Adaptation::Ecap::XactionRep"),
- Adaptation::Initiate("Adaptation::Ecap::XactionRep"),
- theService(aService),
- theVirginRep(virginHeader), theCauseRep(NULL),
- makingVb(opUndecided), proxyingAb(opUndecided),
- adaptHistoryId(-1),
- vbProductionFinished(false),
- abProductionFinished(false), abProductionAtEnd(false)
+ AsyncJob("Adaptation::Ecap::XactionRep"),
+ Adaptation::Initiate("Adaptation::Ecap::XactionRep"),
+ theService(aService),
+ theVirginRep(virginHeader), theCauseRep(NULL),
+ makingVb(opUndecided), proxyingAb(opUndecided),
+ adaptHistoryId(-1),
+ vbProductionFinished(false),
+ abProductionFinished(false), abProductionAtEnd(false),
+ al(alp)
{
if (virginCause)
theCauseRep = new MessageRep(virginCause);
Adaptation::Ecap::XactionRep::master(const AdapterXaction &x)
{
Must(!theMaster);
- Must(x != NULL);
+ Must(x);
theMaster = x;
}
return clientIpValue();
if (name == libecap::metaUserName)
return usernameValue();
- if (name == Adaptation::Config::masterx_shared_name)
+ if (Adaptation::Config::masterx_shared_name && name == Adaptation::Config::masterx_shared_name)
return masterxSharedValue(name);
// TODO: metaServerIp, metaAuthenticatedUser, and metaAuthenticatedGroups
- return libecap::Area();
+
+ // If the name is unknown, metaValue returns an emtpy area
+ return metaValue(name);
}
void
Adaptation::Ecap::XactionRep::visitEachOption(libecap::NamedValueVisitor &visitor) const
{
if (const libecap::Area value = clientIpValue())
- visitor.visit(libecap::metaClientIp, value);
+ visitor.visit(libecap::metaClientIp, value);
if (const libecap::Area value = usernameValue())
- visitor.visit(libecap::metaUserName, value);
+ visitor.visit(libecap::metaUserName, value);
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);
+ const libecap::Name name(Adaptation::Config::masterx_shared_name);
+ if (const libecap::Area value = masterxSharedValue(name))
+ visitor.visit(name, value);
}
+ visitEachMetaHeader(visitor);
+
// TODO: metaServerIp, metaAuthenticatedUser, and metaAuthenticatedGroups
}
#if FOLLOW_X_FORWARDED_FOR
if (TheConfig.use_indirect_client) {
client_addr = request->indirect_client_addr;
- } else
+ } else
#endif
client_addr = request->client_addr;
- if (!client_addr.IsAnyAddr() && !client_addr.IsNoAddr()) {
+ if (!client_addr.isAnyAddr() && !client_addr.isNoAddr()) {
char ntoabuf[MAX_IPSTRLEN] = "";
- client_addr.NtoA(ntoabuf,MAX_IPSTRLEN);
+ client_addr.toStr(ntoabuf,MAX_IPSTRLEN);
return libecap::Area::FromTempBuffer(ntoabuf, strlen(ntoabuf));
}
- }
+ }
return libecap::Area();
}
const libecap::Area
Adaptation::Ecap::XactionRep::usernameValue() const
{
+#if USE_AUTH
const HttpRequest *request = dynamic_cast<const HttpRequest*>(theCauseRep ?
theCauseRep->raw().header : theVirginRep.raw().header);
Must(request);
if (request->auth_user_request != NULL) {
if (char const *name = request->auth_user_request->username())
return libecap::Area::FromTempBuffer(name, strlen(name));
- }
+ else if (request->extacl_user.size() > 0)
+ return libecap::Area::FromTempBuffer(request->extacl_user.rawBuf(),
+ request->extacl_user.size());
+ }
+#endif
return libecap::Area();
}
return libecap::Area();
}
+const libecap::Area
+Adaptation::Ecap::XactionRep::metaValue(const libecap::Name &name) const
+{
+ HttpRequest *request = dynamic_cast<HttpRequest*>(theCauseRep ?
+ theCauseRep->raw().header : theVirginRep.raw().header);
+ Must(request);
+ HttpReply *reply = dynamic_cast<HttpReply*>(theVirginRep.raw().header);
+
+ if (name.known()) { // must check to avoid empty names matching unset cfg
+ typedef Notes::iterator ACAMLI;
+ for (auto h: Adaptation::Config::metaHeaders) {
+ if (name == h->key().toStdString()) {
+ SBuf matched;
+ if (h->match(request, reply, al, matched))
+ return libecap::Area::FromTempString(matched.toStdString());
+ else
+ return libecap::Area();
+ }
+ }
+ }
+
+ return libecap::Area();
+}
+
+void
+Adaptation::Ecap::XactionRep::visitEachMetaHeader(libecap::NamedValueVisitor &visitor) const
+{
+ HttpRequest *request = dynamic_cast<HttpRequest*>(theCauseRep ?
+ theCauseRep->raw().header : theVirginRep.raw().header);
+ Must(request);
+ HttpReply *reply = dynamic_cast<HttpReply*>(theVirginRep.raw().header);
+
+ for (auto h: Adaptation::Config::metaHeaders) {
+ SBuf matched;
+ if (h->match(request, reply, al, matched)) {
+ const libecap::Name name(h->key().toStdString());
+ const libecap::Area value = libecap::Area::FromTempString(matched.toStdString());
+ visitor.visit(name, value);
+ }
+ }
+}
+
void
Adaptation::Ecap::XactionRep::start()
{
if (!theVirginRep.raw().body_pipe)
makingVb = opNever; // there is nothing to deliver
- const HttpRequest *request = dynamic_cast<const HttpRequest*> (theCauseRep ?
- theCauseRep->raw().header : theVirginRep.raw().header);
+ HttpRequest *request = dynamic_cast<HttpRequest*> (theCauseRep ?
+ theCauseRep->raw().header : theVirginRep.raw().header);
Must(request);
+
+ HttpReply *reply = dynamic_cast<HttpReply*>(theVirginRep.raw().header);
+
Adaptation::History::Pointer ah = request->adaptLogHistory();
if (ah != NULL) {
// retrying=false because ecap never retries transactions
adaptHistoryId = ah->recordXactStart(service().cfg().key, current_time, false);
+ SBuf matched;
+ for (auto h: Adaptation::Config::metaHeaders) {
+ if (h->match(request, reply, al, matched)) {
+ if (ah->metaHeaders == NULL)
+ ah->metaHeaders = new NotePairs();
+ if (!ah->metaHeaders->hasPair(h->key(), matched))
+ ah->metaHeaders->add(h->key(), matched);
+ }
+ }
}
theMaster->start();
// clear body_pipes, if any
// this code does not maintain proxying* and canAccessVb states; should it?
- if (theAnswerRep != NULL) {
+ if (theAnswerRep) {
BodyPipe::Pointer body_pipe = answer().body_pipe;
if (body_pipe != NULL) {
Must(body_pipe->stillProducing(this));
Adaptation::Initiate::swanSong();
}
+void
+Adaptation::Ecap::XactionRep::resume()
+{
+ // go async to gain exception protection and done()-based job destruction
+ typedef NullaryMemFunT<Adaptation::Ecap::XactionRep> Dialer;
+ AsyncCall::Pointer call = asyncCall(93, 5, "Adaptation::Ecap::XactionRep::doResume",
+ Dialer(this, &Adaptation::Ecap::XactionRep::doResume));
+ ScheduleCallHere(call);
+}
+
+/// the guts of libecap::host::Xaction::resume() API implementation
+/// which just goes async in Adaptation::Ecap::XactionRep::resume().
+void
+Adaptation::Ecap::XactionRep::doResume()
+{
+ Must(theMaster);
+ theMaster->resume();
+}
+
libecap::Message &
Adaptation::Ecap::XactionRep::virgin()
{
libecap::Message &
Adaptation::Ecap::XactionRep::adapted()
{
- Must(theAnswerRep != NULL);
+ Must(theAnswerRep);
return *theAnswerRep;
}
preserveVb("useVirgin");
- HttpMsg *clone = theVirginRep.raw().header->clone();
+ Http::Message *clone = theVirginRep.raw().header->clone();
// check that clone() copies the pipe so that we do not have to
Must(!theVirginRep.raw().header->body_pipe == !clone->body_pipe);
- updateHistory();
+ updateHistory(clone);
sendAnswer(Answer::Forward(clone));
Must(done());
}
theAnswerRep = m;
Must(proxyingAb == opUndecided);
- HttpMsg *msg = answer().header;
+ Http::Message *msg = answer().header;
+ updateSources(msg);
if (!theAnswerRep->body()) { // final, bodyless answer
proxyingAb = opNever;
- updateHistory();
+ updateHistory(msg);
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();
+ updateHistory(msg);
sendAnswer(Answer::Forward(msg));
debugs(93,4, HERE << "adapter will produce body" << status());
sinkVb("blockVirgin");
- updateHistory();
+ updateHistory(NULL);
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()
+Adaptation::Ecap::XactionRep::updateHistory(Http::Message *adapted)
{
if (!theMaster) // all updates rely on being able to query the adapter
return;
theMaster->visitEachOption(extractor);
ah->recordMeta(&meta);
}
-}
+ // Add just-created history to the adapted/cloned request that lacks it.
+ if (HttpRequest *adaptedReq = dynamic_cast<HttpRequest*>(adapted))
+ adaptedReq->adaptHistoryImport(*request);
+}
void
Adaptation::Ecap::XactionRep::vbDiscard()
mustStop("adaptationAborted");
}
-bool
-Adaptation::Ecap::XactionRep::callable() const
-{
- return !done();
-}
-
void
Adaptation::Ecap::XactionRep::noteMoreBodySpaceAvailable(RefCount<BodyPipe> bp)
{
buf.append(" [", 2);
if (makingVb)
- buf.Printf("M%d", static_cast<int>(makingVb));
+ buf.appendf("M%d", static_cast<int>(makingVb));
const BodyPipePointer &vp = theVirginRep.raw().body_pipe;
if (!vp)
buf.append(" !V", 3);
- else
- if (vp->stillConsuming(const_cast<XactionRep*>(this)))
+ else if (vp->stillConsuming(const_cast<XactionRep*>(this)))
buf.append(" Vc", 3);
else
buf.append(" V?", 3);
if (vbProductionFinished)
buf.append(".", 1);
-
- buf.Printf(" A%d", static_cast<int>(proxyingAb));
+ buf.appendf(" A%d", static_cast<int>(proxyingAb));
if (proxyingAb == opOn) {
MessageRep *rep = dynamic_cast<MessageRep*>(theAnswerRep.get());
buf.append(" A?", 3);
}
- buf.Printf(" %s%u]", id.Prefix, id.value);
+ buf.appendf(" %s%u]", id.prefix(), id.value);
buf.terminate();
return buf.content();
}
+
+void
+Adaptation::Ecap::XactionRep::updateSources(Http::Message *adapted)
+{
+ adapted->sources |= service().cfg().connectionEncryption ? Http::Message::srcEcaps : Http::Message::srcEcap;
+
+ // Update masterXaction object for adapted HTTP requests.
+ if (HttpRequest *adaptedReq = dynamic_cast<HttpRequest*>(adapted)) {
+ HttpRequest *request = dynamic_cast<HttpRequest*> (theCauseRep ?
+ theCauseRep->raw().header : theVirginRep.raw().header);
+ Must(request);
+ adaptedReq->masterXaction = request->masterXaction;
+ }
+}
+