]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/adaptation/ecap/XactionRep.cc
2 * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 93 eCAP Interface */
12 #include <libecap/common/area.h>
13 #include <libecap/common/delay.h>
14 #include <libecap/common/named_values.h>
15 #include <libecap/common/names.h>
16 #include <libecap/adapter/xaction.h>
17 #include "adaptation/Answer.h"
18 #include "adaptation/ecap/Config.h"
19 #include "adaptation/ecap/XactionRep.h"
20 #include "adaptation/Initiator.h"
21 #include "base/AsyncJobCalls.h"
22 #include "base/TextException.h"
23 #include "format/Format.h"
24 #include "HttpReply.h"
25 #include "HttpRequest.h"
26 #include "MasterXaction.h"
27 #include "SquidTime.h"
29 CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Ecap::XactionRep
, XactionRep
);
31 /// a libecap Visitor for converting adapter transaction options to HttpHeader
32 class OptionsExtractor
: public libecap::NamedValueVisitor
35 typedef libecap::Name Name
;
36 typedef libecap::Area Area
;
38 OptionsExtractor(HttpHeader
&aMeta
): meta(aMeta
) {}
40 // libecap::NamedValueVisitor API
41 virtual void visit(const Name
&name
, const Area
&value
) {
42 meta
.putExt(name
.image().c_str(), value
.toString().c_str());
45 HttpHeader
&meta
; ///< where to put extracted options
48 Adaptation::Ecap::XactionRep::XactionRep(
49 Http::Message
*virginHeader
, HttpRequest
*virginCause
, AccessLogEntry::Pointer
&alp
,
50 const Adaptation::ServicePointer
&aService
):
51 AsyncJob("Adaptation::Ecap::XactionRep"),
52 Adaptation::Initiate("Adaptation::Ecap::XactionRep"),
54 theVirginRep(virginHeader
), theCauseRep(NULL
),
55 makingVb(opUndecided
), proxyingAb(opUndecided
),
57 vbProductionFinished(false),
58 abProductionFinished(false), abProductionAtEnd(false),
62 theCauseRep
= new MessageRep(virginCause
);
65 Adaptation::Ecap::XactionRep::~XactionRep()
73 Adaptation::Ecap::XactionRep::master(const AdapterXaction
&x
)
81 Adaptation::Ecap::XactionRep::service()
83 Must(theService
!= NULL
);
88 Adaptation::Ecap::XactionRep::option(const libecap::Name
&name
) const
90 if (name
== libecap::metaClientIp
)
91 return clientIpValue();
92 if (name
== libecap::metaUserName
)
93 return usernameValue();
94 if (Adaptation::Config::masterx_shared_name
&& name
== Adaptation::Config::masterx_shared_name
)
95 return masterxSharedValue(name
);
97 // TODO: metaServerIp, metaAuthenticatedUser, and metaAuthenticatedGroups
99 // If the name is unknown, metaValue returns an emtpy area
100 return metaValue(name
);
104 Adaptation::Ecap::XactionRep::visitEachOption(libecap::NamedValueVisitor
&visitor
) const
106 if (const libecap::Area value
= clientIpValue())
107 visitor
.visit(libecap::metaClientIp
, value
);
108 if (const libecap::Area value
= usernameValue())
109 visitor
.visit(libecap::metaUserName
, value
);
111 if (Adaptation::Config::masterx_shared_name
) {
112 const libecap::Name
name(Adaptation::Config::masterx_shared_name
);
113 if (const libecap::Area value
= masterxSharedValue(name
))
114 visitor
.visit(name
, value
);
117 visitEachMetaHeader(visitor
);
119 // TODO: metaServerIp, metaAuthenticatedUser, and metaAuthenticatedGroups
123 Adaptation::Ecap::XactionRep::clientIpValue() const
125 const HttpRequest
*request
= dynamic_cast<const HttpRequest
*>(theCauseRep
?
126 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
128 // TODO: move this logic into HttpRequest::clientIp(bool) and
129 // HttpRequest::clientIpString(bool) and reuse everywhere
130 if (TheConfig
.send_client_ip
&& request
) {
131 Ip::Address client_addr
;
132 #if FOLLOW_X_FORWARDED_FOR
133 if (TheConfig
.use_indirect_client
) {
134 client_addr
= request
->indirect_client_addr
;
137 client_addr
= request
->client_addr
;
138 if (!client_addr
.isAnyAddr() && !client_addr
.isNoAddr()) {
139 char ntoabuf
[MAX_IPSTRLEN
] = "";
140 client_addr
.toStr(ntoabuf
,MAX_IPSTRLEN
);
141 return libecap::Area::FromTempBuffer(ntoabuf
, strlen(ntoabuf
));
144 return libecap::Area();
148 Adaptation::Ecap::XactionRep::usernameValue() const
151 const HttpRequest
*request
= dynamic_cast<const HttpRequest
*>(theCauseRep
?
152 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
154 if (request
->auth_user_request
!= NULL
) {
155 if (char const *name
= request
->auth_user_request
->username())
156 return libecap::Area::FromTempBuffer(name
, strlen(name
));
157 else if (request
->extacl_user
.size() > 0)
158 return libecap::Area::FromTempBuffer(request
->extacl_user
.rawBuf(),
159 request
->extacl_user
.size());
162 return libecap::Area();
166 Adaptation::Ecap::XactionRep::masterxSharedValue(const libecap::Name
&name
) const
168 const HttpRequest
*request
= dynamic_cast<const HttpRequest
*>(theCauseRep
?
169 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
171 if (name
.known()) { // must check to avoid empty names matching unset cfg
172 Adaptation::History::Pointer ah
= request
->adaptHistory(false);
175 if (ah
->getXxRecord(name
, value
))
176 return libecap::Area::FromTempBuffer(value
.rawBuf(), value
.size());
179 return libecap::Area();
183 Adaptation::Ecap::XactionRep::metaValue(const libecap::Name
&name
) const
185 HttpRequest
*request
= dynamic_cast<HttpRequest
*>(theCauseRep
?
186 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
188 HttpReply
*reply
= dynamic_cast<HttpReply
*>(theVirginRep
.raw().header
);
190 if (name
.known()) { // must check to avoid empty names matching unset cfg
191 typedef Notes::iterator ACAMLI
;
192 for (auto h
: Adaptation::Config::metaHeaders
) {
193 if (name
== h
->key().toStdString()) {
195 if (h
->match(request
, reply
, al
, matched
))
196 return libecap::Area::FromTempString(matched
.toStdString());
198 return libecap::Area();
203 return libecap::Area();
207 Adaptation::Ecap::XactionRep::visitEachMetaHeader(libecap::NamedValueVisitor
&visitor
) const
209 HttpRequest
*request
= dynamic_cast<HttpRequest
*>(theCauseRep
?
210 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
212 HttpReply
*reply
= dynamic_cast<HttpReply
*>(theVirginRep
.raw().header
);
214 for (auto h
: Adaptation::Config::metaHeaders
) {
216 if (h
->match(request
, reply
, al
, matched
)) {
217 const libecap::Name
name(h
->key().toStdString());
218 const libecap::Area value
= libecap::Area::FromTempString(matched
.toStdString());
219 visitor
.visit(name
, value
);
225 Adaptation::Ecap::XactionRep::start()
229 if (!theVirginRep
.raw().body_pipe
)
230 makingVb
= opNever
; // there is nothing to deliver
232 HttpRequest
*request
= dynamic_cast<HttpRequest
*> (theCauseRep
?
233 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
236 HttpReply
*reply
= dynamic_cast<HttpReply
*>(theVirginRep
.raw().header
);
238 Adaptation::History::Pointer ah
= request
->adaptLogHistory();
240 // retrying=false because ecap never retries transactions
241 adaptHistoryId
= ah
->recordXactStart(service().cfg().key
, current_time
, false);
243 for (auto h
: Adaptation::Config::metaHeaders
) {
244 if (h
->match(request
, reply
, al
, matched
)) {
245 if (ah
->metaHeaders
== NULL
)
246 ah
->metaHeaders
= new NotePairs();
247 if (!ah
->metaHeaders
->hasPair(h
->key(), matched
))
248 ah
->metaHeaders
->add(h
->key(), matched
);
257 Adaptation::Ecap::XactionRep::swanSong()
259 // clear body_pipes, if any
260 // this code does not maintain proxying* and canAccessVb states; should it?
263 BodyPipe::Pointer body_pipe
= answer().body_pipe
;
264 if (body_pipe
!= NULL
) {
265 Must(body_pipe
->stillProducing(this));
266 stopProducingFor(body_pipe
, false);
270 BodyPipe::Pointer
&body_pipe
= theVirginRep
.raw().body_pipe
;
271 if (body_pipe
!= NULL
&& body_pipe
->stillConsuming(this))
272 stopConsumingFrom(body_pipe
);
276 const HttpRequest
*request
= dynamic_cast<const HttpRequest
*>(theCauseRep
?
277 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
279 Adaptation::History::Pointer ah
= request
->adaptLogHistory();
280 if (ah
!= NULL
&& adaptHistoryId
>= 0)
281 ah
->recordXactFinish(adaptHistoryId
);
283 Adaptation::Initiate::swanSong();
287 Adaptation::Ecap::XactionRep::resume()
289 // go async to gain exception protection and done()-based job destruction
290 typedef NullaryMemFunT
<Adaptation::Ecap::XactionRep
> Dialer
;
291 AsyncCall::Pointer call
= asyncCall(93, 5, "Adaptation::Ecap::XactionRep::doResume",
292 Dialer(this, &Adaptation::Ecap::XactionRep::doResume
));
293 ScheduleCallHere(call
);
296 /// the guts of libecap::host::Xaction::resume() API implementation
297 /// which just goes async in Adaptation::Ecap::XactionRep::resume().
299 Adaptation::Ecap::XactionRep::doResume()
306 Adaptation::Ecap::XactionRep::virgin()
311 const libecap::Message
&
312 Adaptation::Ecap::XactionRep::cause()
314 Must(theCauseRep
!= NULL
);
319 Adaptation::Ecap::XactionRep::adapted()
322 return *theAnswerRep
;
325 Adaptation::Message
&
326 Adaptation::Ecap::XactionRep::answer()
328 MessageRep
*rep
= dynamic_cast<MessageRep
*>(theAnswerRep
.get());
334 Adaptation::Ecap::XactionRep::terminateMaster()
337 AdapterXaction x
= theMaster
;
344 Adaptation::Ecap::XactionRep::doneAll() const
346 return makingVb
>= opComplete
&& proxyingAb
>= opComplete
&&
347 Adaptation::Initiate::doneAll();
350 // stops receiving virgin and enables auto-consumption, dropping any vb bytes
352 Adaptation::Ecap::XactionRep::sinkVb(const char *reason
)
354 debugs(93,4, HERE
<< "sink for " << reason
<< "; status:" << status());
356 // we reset raw().body_pipe when we are done, so use this one for checking
357 const BodyPipePointer
&permPipe
= theVirginRep
.raw().header
->body_pipe
;
358 if (permPipe
!= NULL
)
359 permPipe
->enableAutoConsumption();
364 // stops receiving virgin but preserves it for others to use
366 Adaptation::Ecap::XactionRep::preserveVb(const char *reason
)
368 debugs(93,4, HERE
<< "preserve for " << reason
<< "; status:" << status());
370 // we reset raw().body_pipe when we are done, so use this one for checking
371 const BodyPipePointer
&permPipe
= theVirginRep
.raw().header
->body_pipe
;
372 if (permPipe
!= NULL
) {
373 // if libecap consumed, we cannot preserve
374 Must(!permPipe
->consumedSize());
380 // disassociates us from vb; the last step of sinking or preserving vb
382 Adaptation::Ecap::XactionRep::forgetVb(const char *reason
)
384 debugs(93,9, HERE
<< "forget vb " << reason
<< "; status:" << status());
386 BodyPipePointer
&p
= theVirginRep
.raw().body_pipe
;
387 if (p
!= NULL
&& p
->stillConsuming(this))
388 stopConsumingFrom(p
);
390 if (makingVb
== opUndecided
)
392 else if (makingVb
== opOn
)
393 makingVb
= opComplete
;
397 Adaptation::Ecap::XactionRep::useVirgin()
399 debugs(93,3, HERE
<< status());
400 Must(proxyingAb
== opUndecided
);
401 proxyingAb
= opNever
;
403 preserveVb("useVirgin");
405 Http::Message
*clone
= theVirginRep
.raw().header
->clone();
406 // check that clone() copies the pipe so that we do not have to
407 Must(!theVirginRep
.raw().header
->body_pipe
== !clone
->body_pipe
);
409 updateHistory(clone
);
410 sendAnswer(Answer::Forward(clone
));
415 Adaptation::Ecap::XactionRep::useAdapted(const libecap::shared_ptr
<libecap::Message
> &m
)
417 debugs(93,3, HERE
<< status());
420 Must(proxyingAb
== opUndecided
);
422 Http::Message
*msg
= answer().header
;
424 if (!theAnswerRep
->body()) { // final, bodyless answer
425 proxyingAb
= opNever
;
427 sendAnswer(Answer::Forward(msg
));
428 } else { // got answer headers but need to handle body
430 Must(!msg
->body_pipe
); // only host can set body pipes
431 MessageRep
*rep
= dynamic_cast<MessageRep
*>(theAnswerRep
.get());
433 rep
->tieBody(this); // sets us as a producer
434 Must(msg
->body_pipe
!= NULL
); // check tieBody
437 sendAnswer(Answer::Forward(msg
));
439 debugs(93,4, HERE
<< "adapter will produce body" << status());
440 theMaster
->abMake(); // libecap will produce
445 Adaptation::Ecap::XactionRep::blockVirgin()
447 debugs(93,3, HERE
<< status());
448 Must(proxyingAb
== opUndecided
);
449 proxyingAb
= opNever
;
451 sinkVb("blockVirgin");
454 sendAnswer(Answer::Block(service().cfg().key
));
458 /// Called just before sendAnswer() to record adapter meta-information
459 /// which may affect answer processing and may be needed for logging.
461 Adaptation::Ecap::XactionRep::updateHistory(Http::Message
*adapted
)
463 if (!theMaster
) // all updates rely on being able to query the adapter
466 const HttpRequest
*request
= dynamic_cast<const HttpRequest
*>(theCauseRep
?
467 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
470 // TODO: move common ICAP/eCAP logic to Adaptation::Xaction or similar
471 // TODO: optimize Area-to-String conversion
473 // update the cross-transactional database if needed
474 if (const char *xxNameStr
= Adaptation::Config::masterx_shared_name
) {
475 Adaptation::History::Pointer ah
= request
->adaptHistory(true);
477 libecap::Name
xxName(xxNameStr
); // TODO: optimize?
478 if (const libecap::Area val
= theMaster
->option(xxName
))
479 ah
->updateXxRecord(xxNameStr
, val
.toString().c_str());
483 // update the adaptation plan if needed
484 if (service().cfg().routing
) {
486 if (const libecap::Area services
= theMaster
->option(libecap::metaNextServices
)) {
487 Adaptation::History::Pointer ah
= request
->adaptHistory(true);
489 ah
->updateNextServices(services
.toString().c_str());
491 } // TODO: else warn (occasionally!) if we got libecap::metaNextServices
493 // Store received meta headers for adapt::<last_h logformat code use.
494 // If we already have stored headers from a previous adaptation transaction
495 // related to the same master transction, they will be replaced.
496 Adaptation::History::Pointer ah
= request
->adaptLogHistory();
498 HttpHeader
meta(hoReply
);
499 OptionsExtractor
extractor(meta
);
500 theMaster
->visitEachOption(extractor
);
501 ah
->recordMeta(&meta
);
504 // Add just-created history to the adapted/cloned request that lacks it.
505 if (HttpRequest
*adaptedReq
= dynamic_cast<HttpRequest
*>(adapted
))
506 adaptedReq
->adaptHistoryImport(*request
);
510 Adaptation::Ecap::XactionRep::vbDiscard()
512 Must(makingVb
== opUndecided
);
513 // if adapter does not need vb, we do not need to send it
515 Must(makingVb
== opNever
);
519 Adaptation::Ecap::XactionRep::vbMake()
521 Must(makingVb
== opUndecided
);
522 BodyPipePointer
&p
= theVirginRep
.raw().body_pipe
;
524 Must(p
->setConsumerIfNotLate(this)); // to deliver vb, we must receive vb
529 Adaptation::Ecap::XactionRep::vbStopMaking()
531 Must(makingVb
== opOn
);
532 // if adapter does not need vb, we do not need to receive it
533 sinkVb("vbStopMaking");
534 Must(makingVb
== opComplete
);
538 Adaptation::Ecap::XactionRep::vbMakeMore()
540 Must(makingVb
== opOn
); // cannot make more if done proxying
541 // we cannot guarantee more vb, but we can check that there is a chance
542 const BodyPipePointer
&p
= theVirginRep
.raw().body_pipe
;
543 Must(p
!= NULL
&& p
->stillConsuming(this)); // we are plugged in
544 Must(!p
->productionEnded() && p
->mayNeedMoreData()); // and may get more
548 Adaptation::Ecap::XactionRep::vbContent(libecap::size_type o
, libecap::size_type s
)
550 // We may not be makingVb yet. It should be OK, but see vbContentShift().
552 const BodyPipePointer
&p
= theVirginRep
.raw().body_pipe
;
555 // TODO: make MemBuf use size_t?
556 const size_t haveSize
= static_cast<size_t>(p
->buf().contentSize());
558 // convert to Squid types; XXX: check for overflow
559 const uint64_t offset
= static_cast<uint64_t>(o
);
560 Must(offset
<= haveSize
); // equal iff at the end of content
562 // nsize means no size limit: all content starting from offset
563 const size_t size
= s
== libecap::nsize
?
564 haveSize
- offset
: static_cast<size_t>(s
);
566 // XXX: optimize by making theBody a shared_ptr (see Area::FromTemp*() src)
567 return libecap::Area::FromTempBuffer(p
->buf().content() + offset
,
568 min(static_cast<size_t>(haveSize
- offset
), size
));
572 Adaptation::Ecap::XactionRep::vbContentShift(libecap::size_type n
)
574 // We may not be makingVb yet. It should be OK now, but if BodyPipe
575 // consume() requirements change, we would have to return empty vbContent
576 // until the adapter registers as a consumer
578 BodyPipePointer
&p
= theVirginRep
.raw().body_pipe
;
580 const size_t size
= static_cast<size_t>(n
); // XXX: check for overflow
581 const size_t haveSize
= static_cast<size_t>(p
->buf().contentSize()); // TODO: make MemBuf use size_t?
582 p
->consume(min(size
, haveSize
));
586 Adaptation::Ecap::XactionRep::noteAbContentDone(bool atEnd
)
588 Must(proxyingAb
== opOn
&& !abProductionFinished
);
589 abProductionFinished
= true;
590 abProductionAtEnd
= atEnd
; // store until ready to stop producing ourselves
591 debugs(93,5, HERE
<< "adapted body production ended");
596 Adaptation::Ecap::XactionRep::noteAbContentAvailable()
598 Must(proxyingAb
== opOn
&& !abProductionFinished
);
602 #if 0 /* XXX: implement */
604 Adaptation::Ecap::XactionRep::setAdaptedBodySize(const libecap::BodySize
&size
)
606 Must(answer().body_pipe
!= NULL
);
608 answer().body_pipe
->setBodySize(size
.value());
609 // else the piped body size is unknown by default
614 Adaptation::Ecap::XactionRep::adaptationDelayed(const libecap::Delay
&d
)
616 debugs(93,3, HERE
<< "adapter needs time: " <<
617 d
.state
<< '/' << d
.progress
);
622 Adaptation::Ecap::XactionRep::adaptationAborted()
624 tellQueryAborted(true); // should eCAP support retries?
625 mustStop("adaptationAborted");
629 Adaptation::Ecap::XactionRep::noteMoreBodySpaceAvailable(RefCount
<BodyPipe
> bp
)
631 Must(proxyingAb
== opOn
);
636 Adaptation::Ecap::XactionRep::noteBodyConsumerAborted(RefCount
<BodyPipe
> bp
)
638 Must(proxyingAb
== opOn
);
639 stopProducingFor(answer().body_pipe
, false);
641 theMaster
->abStopMaking();
642 proxyingAb
= opComplete
;
646 Adaptation::Ecap::XactionRep::noteMoreBodyDataAvailable(RefCount
<BodyPipe
> bp
)
648 Must(makingVb
== opOn
); // or we would not be registered as a consumer
650 theMaster
->noteVbContentAvailable();
654 Adaptation::Ecap::XactionRep::noteBodyProductionEnded(RefCount
<BodyPipe
> bp
)
656 Must(makingVb
== opOn
); // or we would not be registered as a consumer
658 theMaster
->noteVbContentDone(true);
659 vbProductionFinished
= true;
663 Adaptation::Ecap::XactionRep::noteBodyProducerAborted(RefCount
<BodyPipe
> bp
)
665 Must(makingVb
== opOn
); // or we would not be registered as a consumer
667 theMaster
->noteVbContentDone(false);
668 vbProductionFinished
= true;
672 Adaptation::Ecap::XactionRep::noteInitiatorAborted()
674 mustStop("initiator aborted");
677 // get content from the adapter and put it into the adapted pipe
679 Adaptation::Ecap::XactionRep::moveAbContent()
681 Must(proxyingAb
== opOn
);
682 const libecap::Area c
= theMaster
->abContent(0, libecap::nsize
);
683 debugs(93,5, HERE
<< "up to " << c
.size
<< " bytes");
684 if (c
.size
== 0 && abProductionFinished
) { // no ab now and in the future
685 stopProducingFor(answer().body_pipe
, abProductionAtEnd
);
686 proxyingAb
= opComplete
;
687 debugs(93,5, HERE
<< "last adapted body data retrieved");
688 } else if (c
.size
> 0) {
689 if (const size_t used
= answer().body_pipe
->putMoreData(c
.start
, c
.size
))
690 theMaster
->abContentShift(used
);
695 Adaptation::Ecap::XactionRep::status() const
703 buf
.appendf("M%d", static_cast<int>(makingVb
));
705 const BodyPipePointer
&vp
= theVirginRep
.raw().body_pipe
;
707 buf
.append(" !V", 3);
708 else if (vp
->stillConsuming(const_cast<XactionRep
*>(this)))
709 buf
.append(" Vc", 3);
711 buf
.append(" V?", 3);
713 if (vbProductionFinished
)
716 buf
.appendf(" A%d", static_cast<int>(proxyingAb
));
718 if (proxyingAb
== opOn
) {
719 MessageRep
*rep
= dynamic_cast<MessageRep
*>(theAnswerRep
.get());
721 const BodyPipePointer
&ap
= rep
->raw().body_pipe
;
723 buf
.append(" !A", 3);
724 else if (ap
->stillProducing(const_cast<XactionRep
*>(this)))
725 buf
.append(" Ap", 3);
727 buf
.append(" A?", 3);
730 buf
.appendf(" %s%u]", id
.prefix(), id
.value
);
734 return buf
.content();
738 Adaptation::Ecap::XactionRep::updateSources(Http::Message
*adapted
)
740 adapted
->sources
|= service().cfg().connectionEncryption
? Http::Message::srcEcaps
: Http::Message::srcEcap
;
742 // Update masterXaction object for adapted HTTP requests.
743 if (HttpRequest
*adaptedReq
= dynamic_cast<HttpRequest
*>(adapted
)) {
744 HttpRequest
*request
= dynamic_cast<HttpRequest
*> (theCauseRep
?
745 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
747 adaptedReq
->masterXaction
= request
->masterXaction
;