]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/adaptation/ecap/XactionRep.cc
c167cd6acf73d5a43b4acffa48f4741a5686f60c
2 * Copyright (C) 1996-2023 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 "adaptation/Answer.h"
13 #include "adaptation/ecap/Config.h"
14 #include "adaptation/ecap/XactionRep.h"
15 #include "adaptation/Initiator.h"
16 #include "base/AsyncJobCalls.h"
17 #include "base/TextException.h"
18 #include "format/Format.h"
19 #include "HttpReply.h"
20 #include "MasterXaction.h"
22 #if HAVE_LIBECAP_COMMON_AREA_H
23 #include <libecap/common/area.h>
25 #if HAVE_LIBECAP_COMMON_DELAY_H
26 #include <libecap/common/delay.h>
28 #if HAVE_LIBECAP_COMMON_NAMED_VALUES_H
29 #include <libecap/common/named_values.h>
31 #if HAVE_LIBECAP_COMMON_NAMES_H
32 #include <libecap/common/names.h>
35 CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Ecap::XactionRep
, XactionRep
);
37 /// a libecap Visitor for converting adapter transaction options to HttpHeader
38 class OptionsExtractor
: public libecap::NamedValueVisitor
41 typedef libecap::Name Name
;
42 typedef libecap::Area Area
;
44 OptionsExtractor(HttpHeader
&aMeta
): meta(aMeta
) {}
46 // libecap::NamedValueVisitor API
47 void visit(const Name
&name
, const Area
&value
) override
{
48 meta
.putExt(name
.image().c_str(), value
.toString().c_str());
51 HttpHeader
&meta
; ///< where to put extracted options
54 Adaptation::Ecap::XactionRep::XactionRep(
55 Http::Message
*virginHeader
, HttpRequest
*virginCause
, AccessLogEntry::Pointer
&alp
,
56 const Adaptation::ServicePointer
&aService
):
57 AsyncJob("Adaptation::Ecap::XactionRep"),
58 Adaptation::Initiate("Adaptation::Ecap::XactionRep"),
60 theVirginRep(virginHeader
), theCauseRep(nullptr),
61 makingVb(opUndecided
), proxyingAb(opUndecided
),
63 vbProductionFinished(false),
64 abProductionFinished(false), abProductionAtEnd(false),
68 theCauseRep
= new MessageRep(virginCause
);
71 Adaptation::Ecap::XactionRep::~XactionRep()
79 Adaptation::Ecap::XactionRep::master(const AdapterXaction
&x
)
87 Adaptation::Ecap::XactionRep::service()
89 Must(theService
!= nullptr);
94 Adaptation::Ecap::XactionRep::option(const libecap::Name
&name
) const
96 if (name
== libecap::metaClientIp
)
97 return clientIpValue();
98 if (name
== libecap::metaUserName
)
99 return usernameValue();
100 if (Adaptation::Config::masterx_shared_name
&& name
== Adaptation::Config::masterx_shared_name
)
101 return masterxSharedValue(name
);
103 // TODO: metaServerIp, metaAuthenticatedUser, and metaAuthenticatedGroups
105 // If the name is unknown, metaValue returns an empty area
106 return metaValue(name
);
110 Adaptation::Ecap::XactionRep::visitEachOption(libecap::NamedValueVisitor
&visitor
) const
112 if (const libecap::Area value
= clientIpValue())
113 visitor
.visit(libecap::metaClientIp
, value
);
114 if (const libecap::Area value
= usernameValue())
115 visitor
.visit(libecap::metaUserName
, value
);
117 if (Adaptation::Config::masterx_shared_name
) {
118 const libecap::Name
name(Adaptation::Config::masterx_shared_name
);
119 if (const libecap::Area value
= masterxSharedValue(name
))
120 visitor
.visit(name
, value
);
123 visitEachMetaHeader(visitor
);
125 // TODO: metaServerIp, metaAuthenticatedUser, and metaAuthenticatedGroups
129 Adaptation::Ecap::XactionRep::clientIpValue() const
131 const HttpRequest
*request
= dynamic_cast<const HttpRequest
*>(theCauseRep
?
132 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
134 // TODO: move this logic into HttpRequest::clientIp(bool) and
135 // HttpRequest::clientIpString(bool) and reuse everywhere
136 if (TheConfig
.send_client_ip
&& request
) {
137 Ip::Address client_addr
;
138 #if FOLLOW_X_FORWARDED_FOR
139 if (TheConfig
.use_indirect_client
) {
140 client_addr
= request
->indirect_client_addr
;
143 client_addr
= request
->client_addr
;
144 if (!client_addr
.isAnyAddr() && !client_addr
.isNoAddr()) {
145 char ntoabuf
[MAX_IPSTRLEN
] = "";
146 client_addr
.toStr(ntoabuf
,MAX_IPSTRLEN
);
147 return libecap::Area::FromTempBuffer(ntoabuf
, strlen(ntoabuf
));
150 return libecap::Area();
154 Adaptation::Ecap::XactionRep::usernameValue() const
157 const HttpRequest
*request
= dynamic_cast<const HttpRequest
*>(theCauseRep
?
158 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
160 if (request
->auth_user_request
!= nullptr) {
161 if (char const *name
= request
->auth_user_request
->username())
162 return libecap::Area::FromTempBuffer(name
, strlen(name
));
163 else if (request
->extacl_user
.size() > 0)
164 return libecap::Area::FromTempBuffer(request
->extacl_user
.rawBuf(),
165 request
->extacl_user
.size());
168 return libecap::Area();
172 Adaptation::Ecap::XactionRep::masterxSharedValue(const libecap::Name
&sharedName
) const
174 const HttpRequest
*request
= dynamic_cast<const HttpRequest
*>(theCauseRep
?
175 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
177 if (sharedName
.known()) { // must check to avoid empty names matching unset cfg
178 Adaptation::History::Pointer ah
= request
->adaptHistory(false);
181 if (ah
->getXxRecord(name
, value
))
182 return libecap::Area::FromTempBuffer(value
.rawBuf(), value
.size());
185 return libecap::Area();
189 Adaptation::Ecap::XactionRep::metaValue(const libecap::Name
&name
) const
191 HttpRequest
*request
= dynamic_cast<HttpRequest
*>(theCauseRep
?
192 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
194 HttpReply
*reply
= dynamic_cast<HttpReply
*>(theVirginRep
.raw().header
);
196 if (name
.known()) { // must check to avoid empty names matching unset cfg
197 for (auto h
: Adaptation::Config::metaHeaders
) {
198 if (name
== h
->key().toStdString()) {
200 if (h
->match(request
, reply
, al
, matched
))
201 return libecap::Area::FromTempString(matched
.toStdString());
203 return libecap::Area();
208 return libecap::Area();
212 Adaptation::Ecap::XactionRep::visitEachMetaHeader(libecap::NamedValueVisitor
&visitor
) const
214 HttpRequest
*request
= dynamic_cast<HttpRequest
*>(theCauseRep
?
215 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
217 HttpReply
*reply
= dynamic_cast<HttpReply
*>(theVirginRep
.raw().header
);
219 for (auto h
: Adaptation::Config::metaHeaders
) {
221 if (h
->match(request
, reply
, al
, matched
)) {
222 const libecap::Name
name(h
->key().toStdString());
223 const libecap::Area value
= libecap::Area::FromTempString(matched
.toStdString());
224 visitor
.visit(name
, value
);
230 Adaptation::Ecap::XactionRep::start()
234 if (!theVirginRep
.raw().body_pipe
)
235 makingVb
= opNever
; // there is nothing to deliver
237 HttpRequest
*request
= dynamic_cast<HttpRequest
*> (theCauseRep
?
238 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
241 HttpReply
*reply
= dynamic_cast<HttpReply
*>(theVirginRep
.raw().header
);
243 Adaptation::History::Pointer ah
= request
->adaptLogHistory();
245 // retrying=false because ecap never retries transactions
246 adaptHistoryId
= ah
->recordXactStart(service().cfg().key
, current_time
, false);
248 for (auto h
: Adaptation::Config::metaHeaders
) {
249 if (h
->match(request
, reply
, al
, matched
)) {
250 if (ah
->metaHeaders
== nullptr)
251 ah
->metaHeaders
= new NotePairs();
252 if (!ah
->metaHeaders
->hasPair(h
->key(), matched
))
253 ah
->metaHeaders
->add(h
->key(), matched
);
262 Adaptation::Ecap::XactionRep::swanSong()
264 // clear body_pipes, if any
265 // this code does not maintain proxying* and canAccessVb states; should it?
268 BodyPipe::Pointer body_pipe
= answer().body_pipe
;
269 if (body_pipe
!= nullptr) {
270 Must(body_pipe
->stillProducing(this));
271 stopProducingFor(body_pipe
, false);
275 BodyPipe::Pointer
&body_pipe
= theVirginRep
.raw().body_pipe
;
276 if (body_pipe
!= nullptr && body_pipe
->stillConsuming(this))
277 stopConsumingFrom(body_pipe
);
281 const HttpRequest
*request
= dynamic_cast<const HttpRequest
*>(theCauseRep
?
282 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
284 Adaptation::History::Pointer ah
= request
->adaptLogHistory();
285 if (ah
!= nullptr && adaptHistoryId
>= 0)
286 ah
->recordXactFinish(adaptHistoryId
);
288 Adaptation::Initiate::swanSong();
292 Adaptation::Ecap::XactionRep::resume()
294 // go async to gain exception protection and done()-based job destruction
295 typedef NullaryMemFunT
<Adaptation::Ecap::XactionRep
> Dialer
;
296 AsyncCall::Pointer call
= asyncCall(93, 5, "Adaptation::Ecap::XactionRep::doResume",
297 Dialer(this, &Adaptation::Ecap::XactionRep::doResume
));
298 ScheduleCallHere(call
);
301 /// the guts of libecap::host::Xaction::resume() API implementation
302 /// which just goes async in Adaptation::Ecap::XactionRep::resume().
304 Adaptation::Ecap::XactionRep::doResume()
311 Adaptation::Ecap::XactionRep::virgin()
316 const libecap::Message
&
317 Adaptation::Ecap::XactionRep::cause()
319 Must(theCauseRep
!= nullptr);
324 Adaptation::Ecap::XactionRep::adapted()
327 return *theAnswerRep
;
330 Adaptation::Message
&
331 Adaptation::Ecap::XactionRep::answer()
333 MessageRep
*rep
= dynamic_cast<MessageRep
*>(theAnswerRep
.get());
339 Adaptation::Ecap::XactionRep::terminateMaster()
342 AdapterXaction x
= theMaster
;
349 Adaptation::Ecap::XactionRep::doneAll() const
351 return makingVb
>= opComplete
&& proxyingAb
>= opComplete
&&
352 Adaptation::Initiate::doneAll();
355 // stops receiving virgin and enables auto-consumption, dropping any vb bytes
357 Adaptation::Ecap::XactionRep::sinkVb(const char *reason
)
359 debugs(93,4, "sink for " << reason
<< "; status:" << status());
361 // we reset raw().body_pipe when we are done, so use this one for checking
362 const BodyPipePointer
&permPipe
= theVirginRep
.raw().header
->body_pipe
;
363 if (permPipe
!= nullptr)
364 permPipe
->enableAutoConsumption();
369 // stops receiving virgin but preserves it for others to use
371 Adaptation::Ecap::XactionRep::preserveVb(const char *reason
)
373 debugs(93,4, "preserve for " << reason
<< "; status:" << status());
375 // we reset raw().body_pipe when we are done, so use this one for checking
376 const BodyPipePointer
&permPipe
= theVirginRep
.raw().header
->body_pipe
;
377 if (permPipe
!= nullptr) {
378 // if libecap consumed, we cannot preserve
379 Must(!permPipe
->consumedSize());
385 // disassociates us from vb; the last step of sinking or preserving vb
387 Adaptation::Ecap::XactionRep::forgetVb(const char *reason
)
389 debugs(93,9, "forget vb " << reason
<< "; status:" << status());
391 BodyPipePointer
&p
= theVirginRep
.raw().body_pipe
;
392 if (p
!= nullptr && p
->stillConsuming(this))
393 stopConsumingFrom(p
);
395 if (makingVb
== opUndecided
)
397 else if (makingVb
== opOn
)
398 makingVb
= opComplete
;
402 Adaptation::Ecap::XactionRep::useVirgin()
404 debugs(93,3, status());
405 Must(proxyingAb
== opUndecided
);
406 proxyingAb
= opNever
;
408 preserveVb("useVirgin");
410 Http::Message
*clone
= theVirginRep
.raw().header
->clone();
411 // check that clone() copies the pipe so that we do not have to
412 Must(!theVirginRep
.raw().header
->body_pipe
== !clone
->body_pipe
);
414 updateHistory(clone
);
415 sendAnswer(Answer::Forward(clone
));
420 Adaptation::Ecap::XactionRep::useAdapted(const libecap::shared_ptr
<libecap::Message
> &m
)
422 debugs(93,3, status());
425 Must(proxyingAb
== opUndecided
);
427 Http::Message
*msg
= answer().header
;
429 if (!theAnswerRep
->body()) { // final, bodyless answer
430 proxyingAb
= opNever
;
432 sendAnswer(Answer::Forward(msg
));
433 } else { // got answer headers but need to handle body
435 Must(!msg
->body_pipe
); // only host can set body pipes
436 MessageRep
*rep
= dynamic_cast<MessageRep
*>(theAnswerRep
.get());
438 rep
->tieBody(this); // sets us as a producer
439 Must(msg
->body_pipe
!= nullptr); // check tieBody
442 sendAnswer(Answer::Forward(msg
));
444 debugs(93,4, "adapter will produce body" << status());
445 theMaster
->abMake(); // libecap will produce
450 Adaptation::Ecap::XactionRep::blockVirgin()
452 debugs(93,3, status());
453 Must(proxyingAb
== opUndecided
);
454 proxyingAb
= opNever
;
456 sinkVb("blockVirgin");
458 updateHistory(nullptr);
459 sendAnswer(Answer::Block(service().cfg().key
));
463 /// Called just before sendAnswer() to record adapter meta-information
464 /// which may affect answer processing and may be needed for logging.
466 Adaptation::Ecap::XactionRep::updateHistory(Http::Message
*adapted
)
468 if (!theMaster
) // all updates rely on being able to query the adapter
471 const HttpRequest
*request
= dynamic_cast<const HttpRequest
*>(theCauseRep
?
472 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
475 // TODO: move common ICAP/eCAP logic to Adaptation::Xaction or similar
476 // TODO: optimize Area-to-String conversion
478 // update the cross-transactional database if needed
479 if (const char *xxNameStr
= Adaptation::Config::masterx_shared_name
) {
480 Adaptation::History::Pointer ah
= request
->adaptHistory(true);
482 libecap::Name
xxName(xxNameStr
); // TODO: optimize?
483 if (const libecap::Area val
= theMaster
->option(xxName
))
484 ah
->updateXxRecord(xxNameStr
, val
.toString().c_str());
488 // update the adaptation plan if needed
489 if (service().cfg().routing
) {
490 if (const libecap::Area services
= theMaster
->option(libecap::metaNextServices
)) {
491 Adaptation::History::Pointer ah
= request
->adaptHistory(true);
493 ah
->updateNextServices(services
.toString().c_str());
495 } // TODO: else warn (occasionally!) if we got libecap::metaNextServices
497 // Store received meta headers for adapt::<last_h logformat code use.
498 // If we already have stored headers from a previous adaptation transaction
499 // related to the same master transction, they will be replaced.
500 Adaptation::History::Pointer ah
= request
->adaptLogHistory();
502 HttpHeader
meta(hoReply
);
503 OptionsExtractor
extractor(meta
);
504 theMaster
->visitEachOption(extractor
);
505 ah
->recordMeta(&meta
);
508 // Add just-created history to the adapted/cloned request that lacks it.
509 if (HttpRequest
*adaptedReq
= dynamic_cast<HttpRequest
*>(adapted
))
510 adaptedReq
->adaptHistoryImport(*request
);
514 Adaptation::Ecap::XactionRep::vbDiscard()
516 Must(makingVb
== opUndecided
);
517 // if adapter does not need vb, we do not need to send it
519 Must(makingVb
== opNever
);
523 Adaptation::Ecap::XactionRep::vbMake()
525 Must(makingVb
== opUndecided
);
526 BodyPipePointer
&p
= theVirginRep
.raw().body_pipe
;
528 Must(p
->setConsumerIfNotLate(this)); // to deliver vb, we must receive vb
533 Adaptation::Ecap::XactionRep::vbStopMaking()
535 Must(makingVb
== opOn
);
536 // if adapter does not need vb, we do not need to receive it
537 sinkVb("vbStopMaking");
538 Must(makingVb
== opComplete
);
542 Adaptation::Ecap::XactionRep::vbMakeMore()
544 Must(makingVb
== opOn
); // cannot make more if done proxying
545 // we cannot guarantee more vb, but we can check that there is a chance
546 const BodyPipePointer
&p
= theVirginRep
.raw().body_pipe
;
547 Must(p
!= nullptr && p
->stillConsuming(this)); // we are plugged in
548 Must(!p
->productionEnded() && p
->mayNeedMoreData()); // and may get more
552 Adaptation::Ecap::XactionRep::vbContent(libecap::size_type o
, libecap::size_type s
)
554 // We may not be makingVb yet. It should be OK, but see vbContentShift().
556 const BodyPipePointer
&p
= theVirginRep
.raw().body_pipe
;
559 // TODO: make MemBuf use size_t?
560 const size_t haveSize
= static_cast<size_t>(p
->buf().contentSize());
562 // convert to Squid types; XXX: check for overflow
563 const uint64_t offset
= static_cast<uint64_t>(o
);
564 Must(offset
<= haveSize
); // equal iff at the end of content
566 // nsize means no size limit: all content starting from offset
567 const size_t size
= s
== libecap::nsize
?
568 haveSize
- offset
: static_cast<size_t>(s
);
570 // XXX: optimize by making theBody a shared_ptr (see Area::FromTemp*() src)
571 return libecap::Area::FromTempBuffer(p
->buf().content() + offset
,
572 min(static_cast<size_t>(haveSize
- offset
), size
));
576 Adaptation::Ecap::XactionRep::vbContentShift(libecap::size_type n
)
578 // We may not be makingVb yet. It should be OK now, but if BodyPipe
579 // consume() requirements change, we would have to return empty vbContent
580 // until the adapter registers as a consumer
582 BodyPipePointer
&p
= theVirginRep
.raw().body_pipe
;
584 const size_t size
= static_cast<size_t>(n
); // XXX: check for overflow
585 const size_t haveSize
= static_cast<size_t>(p
->buf().contentSize()); // TODO: make MemBuf use size_t?
586 p
->consume(min(size
, haveSize
));
590 Adaptation::Ecap::XactionRep::noteAbContentDone(bool atEnd
)
592 Must(proxyingAb
== opOn
&& !abProductionFinished
);
593 abProductionFinished
= true;
594 abProductionAtEnd
= atEnd
; // store until ready to stop producing ourselves
595 debugs(93,5, "adapted body production ended");
600 Adaptation::Ecap::XactionRep::noteAbContentAvailable()
602 Must(proxyingAb
== opOn
&& !abProductionFinished
);
606 #if 0 /* XXX: implement */
608 Adaptation::Ecap::XactionRep::setAdaptedBodySize(const libecap::BodySize
&size
)
610 Must(answer().body_pipe
!= NULL
);
612 answer().body_pipe
->setBodySize(size
.value());
613 // else the piped body size is unknown by default
618 Adaptation::Ecap::XactionRep::adaptationDelayed(const libecap::Delay
&d
)
620 debugs(93,3, "adapter needs time: " <<
621 d
.state
<< '/' << d
.progress
);
626 Adaptation::Ecap::XactionRep::adaptationAborted()
628 tellQueryAborted(true); // should eCAP support retries?
629 mustStop("adaptationAborted");
633 Adaptation::Ecap::XactionRep::noteMoreBodySpaceAvailable(RefCount
<BodyPipe
>)
635 Must(proxyingAb
== opOn
);
640 Adaptation::Ecap::XactionRep::noteBodyConsumerAborted(RefCount
<BodyPipe
>)
642 Must(proxyingAb
== opOn
);
643 stopProducingFor(answer().body_pipe
, false);
645 theMaster
->abStopMaking();
646 proxyingAb
= opComplete
;
650 Adaptation::Ecap::XactionRep::noteMoreBodyDataAvailable(RefCount
<BodyPipe
>)
652 Must(makingVb
== opOn
); // or we would not be registered as a consumer
654 theMaster
->noteVbContentAvailable();
658 Adaptation::Ecap::XactionRep::noteBodyProductionEnded(RefCount
<BodyPipe
>)
660 Must(makingVb
== opOn
); // or we would not be registered as a consumer
662 theMaster
->noteVbContentDone(true);
663 vbProductionFinished
= true;
667 Adaptation::Ecap::XactionRep::noteBodyProducerAborted(RefCount
<BodyPipe
>)
669 Must(makingVb
== opOn
); // or we would not be registered as a consumer
671 theMaster
->noteVbContentDone(false);
672 vbProductionFinished
= true;
676 Adaptation::Ecap::XactionRep::noteInitiatorAborted()
678 mustStop("initiator aborted");
681 // get content from the adapter and put it into the adapted pipe
683 Adaptation::Ecap::XactionRep::moveAbContent()
685 Must(proxyingAb
== opOn
);
686 const libecap::Area c
= theMaster
->abContent(0, libecap::nsize
);
687 debugs(93,5, "up to " << c
.size
<< " bytes");
688 if (c
.size
== 0 && abProductionFinished
) { // no ab now and in the future
689 stopProducingFor(answer().body_pipe
, abProductionAtEnd
);
690 proxyingAb
= opComplete
;
691 debugs(93,5, "last adapted body data retrieved");
692 } else if (c
.size
> 0) {
693 if (const size_t used
= answer().body_pipe
->putMoreData(c
.start
, c
.size
))
694 theMaster
->abContentShift(used
);
699 Adaptation::Ecap::XactionRep::status() const
707 buf
.appendf("M%d", static_cast<int>(makingVb
));
709 const BodyPipePointer
&vp
= theVirginRep
.raw().body_pipe
;
711 buf
.append(" !V", 3);
712 else if (vp
->stillConsuming(const_cast<XactionRep
*>(this)))
713 buf
.append(" Vc", 3);
715 buf
.append(" V?", 3);
717 if (vbProductionFinished
)
720 buf
.appendf(" A%d", static_cast<int>(proxyingAb
));
722 if (proxyingAb
== opOn
) {
723 MessageRep
*rep
= dynamic_cast<MessageRep
*>(theAnswerRep
.get());
725 const BodyPipePointer
&ap
= rep
->raw().body_pipe
;
727 buf
.append(" !A", 3);
728 else if (ap
->stillProducing(const_cast<XactionRep
*>(this)))
729 buf
.append(" Ap", 3);
731 buf
.append(" A?", 3);
734 buf
.appendf(" %s%u]", id
.prefix(), id
.value
);
738 return buf
.content();
742 Adaptation::Ecap::XactionRep::updateSources(Http::Message
*adapted
)
744 adapted
->sources
|= service().cfg().connectionEncryption
? Http::Message::srcEcaps
: Http::Message::srcEcap
;
746 // Update masterXaction object for adapted HTTP requests.
747 if (HttpRequest
*adaptedReq
= dynamic_cast<HttpRequest
*>(adapted
)) {
748 HttpRequest
*request
= dynamic_cast<HttpRequest
*> (theCauseRep
?
749 theCauseRep
->raw().header
: theVirginRep
.raw().header
);
751 adaptedReq
->masterXaction
= request
->masterXaction
;