]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/adaptation/Iterator.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 Adaptation */
12 #include "adaptation/Answer.h"
13 #include "adaptation/Config.h"
14 #include "adaptation/Iterator.h"
15 #include "adaptation/Service.h"
16 #include "adaptation/ServiceFilter.h"
17 #include "adaptation/ServiceGroups.h"
18 #include "base/TextException.h"
19 #include "HttpReply.h"
20 #include "sbuf/StringConvert.h"
22 Adaptation::Iterator::Iterator(
23 Http::Message
*aMsg
, HttpRequest
*aCause
,
24 AccessLogEntry::Pointer
&alp
,
25 const ServiceGroupPointer
&aGroup
):
27 Adaptation::Initiate("Iterator"),
37 HTTPMSGLOCK(theCause
);
43 Adaptation::Iterator::~Iterator()
46 HTTPMSGUNLOCK(theMsg
);
47 HTTPMSGUNLOCK(theCause
);
50 void Adaptation::Iterator::start()
52 Adaptation::Initiate::start();
54 thePlan
= ServicePlan(theGroup
, filter());
56 // Add adaptation group name once and now, before
57 // dynamic groups change it at step() time.
58 if (Adaptation::Config::needHistory
&& !thePlan
.exhausted() && (dynamic_cast<ServiceSet
*>(theGroup
.getRaw()) || dynamic_cast<ServiceChain
*>(theGroup
.getRaw()))) {
59 HttpRequest
*request
= dynamic_cast<HttpRequest
*>(theMsg
);
63 Adaptation::History::Pointer ah
= request
->adaptHistory(true);
64 auto gid
= StringToSBuf(theGroup
->id
);
65 ah
->recordAdaptationService(gid
);
71 void Adaptation::Iterator::step()
74 debugs(93,5, HERE
<< '#' << iterations
<< " plan: " << thePlan
);
78 if (thePlan
.exhausted()) { // nothing more to do
79 sendAnswer(Answer::Forward(theMsg
));
84 HttpRequest
*request
= dynamic_cast<HttpRequest
*>(theMsg
);
88 request
->clearError();
90 if (iterations
> Adaptation::Config::service_iteration_limit
) {
91 debugs(93,DBG_CRITICAL
, "Adaptation iterations limit (" <<
92 Adaptation::Config::service_iteration_limit
<< ") exceeded:\n" <<
93 "\tPossible service loop with " <<
94 theGroup
->kind
<< " " << theGroup
->id
<< ", plan=" << thePlan
);
95 throw TexcHere("too many adaptations");
98 ServicePointer service
= thePlan
.current();
99 Must(service
!= NULL
);
100 debugs(93,5, HERE
<< "using adaptation service: " << service
->cfg().key
);
102 if (Adaptation::Config::needHistory
) {
103 Adaptation::History::Pointer ah
= request
->adaptHistory(true);
104 auto uid
= StringToSBuf(thePlan
.current()->cfg().key
);
105 ah
->recordAdaptationService(uid
);
108 theLauncher
= initiateAdaptation(
109 service
->makeXactLauncher(theMsg
, theCause
, al
));
110 Must(initiated(theLauncher
));
115 Adaptation::Iterator::noteAdaptationAnswer(const Answer
&answer
)
117 switch (answer
.kind
) {
118 case Answer::akForward
:
119 handleAdaptedHeader(const_cast<Http::Message
*>(answer
.message
.getRaw()));
122 case Answer::akBlock
:
123 handleAdaptationBlock(answer
);
126 case Answer::akError
:
127 handleAdaptationError(answer
.final
);
133 Adaptation::Iterator::handleAdaptedHeader(Http::Message
*aMsg
)
135 // set theCause if we switched to request satisfaction mode
136 if (!theCause
) { // probably sent a request message
137 if (dynamic_cast<HttpReply
*>(aMsg
)) { // we got a response message
138 if (HttpRequest
*cause
= dynamic_cast<HttpRequest
*>(theMsg
)) {
139 // definately sent request, now use it as the cause
140 theCause
= cause
; // moving the lock
142 debugs(93,3, HERE
<< "in request satisfaction mode");
148 HTTPMSGUNLOCK(theMsg
);
153 clearAdaptation(theLauncher
);
154 if (!updatePlan(true)) // do not immediatelly advance the new plan
155 thePlan
.next(filter());
159 void Adaptation::Iterator::noteInitiatorAborted()
161 announceInitiatorAbort(theLauncher
); // propogate to the transaction
163 mustStop("initiator gone");
166 void Adaptation::Iterator::handleAdaptationBlock(const Answer
&answer
)
168 debugs(93,5, HERE
<< "blocked by " << answer
);
169 clearAdaptation(theLauncher
);
175 void Adaptation::Iterator::handleAdaptationError(bool final
)
177 debugs(93,5, HERE
<< "final: " << final
<< " plan: " << thePlan
);
178 clearAdaptation(theLauncher
);
181 // can we replace the failed service (group-level bypass)?
182 const bool srcIntact
= !theMsg
->body_pipe
||
183 !theMsg
->body_pipe
->consumedSize();
184 // can we ignore the failure (compute while thePlan is not exhausted)?
185 Must(!thePlan
.exhausted());
186 const bool canIgnore
= thePlan
.current()->cfg().bypass
;
187 debugs(85,5, HERE
<< "flags: " << srcIntact
<< canIgnore
<< adapted
);
190 if (thePlan
.replacement(filter()) != NULL
) {
191 debugs(93,3, HERE
<< "trying a replacement service");
197 if (canIgnore
&& srcIntact
&& adapted
) {
198 debugs(85,3, HERE
<< "responding with older adapted msg");
199 sendAnswer(Answer::Forward(theMsg
));
200 mustStop("sent older adapted msg");
204 // caller may recover if we can ignore the error and virgin msg is intact
205 const bool useVirgin
= canIgnore
&& !adapted
&& srcIntact
;
206 tellQueryAborted(!useVirgin
);
207 mustStop("group failure");
210 bool Adaptation::Iterator::doneAll() const
212 return Adaptation::Initiate::doneAll() && thePlan
.exhausted();
215 void Adaptation::Iterator::swanSong()
217 if (theInitiator
.set())
218 tellQueryAborted(true); // abnormal condition that should not happen
220 if (initiated(theLauncher
))
221 clearAdaptation(theLauncher
);
223 Adaptation::Initiate::swanSong();
226 bool Adaptation::Iterator::updatePlan(bool adopt
)
228 HttpRequest
*r
= theCause
? theCause
: dynamic_cast<HttpRequest
*>(theMsg
);
231 Adaptation::History::Pointer ah
= r
->adaptHistory();
233 debugs(85,9, HERE
<< "no history to store a service-proposed plan");
234 return false; // the feature is not enabled or is not triggered
238 if (!ah
->extractNextServices(services
)) { // clears history
239 debugs(85,9, HERE
<< "no service-proposed plan received");
240 return false; // the service did not provide a new plan
244 debugs(85,3, HERE
<< "rejecting service-proposed plan");
248 debugs(85,3, HERE
<< "retiring old plan: " << thePlan
);
250 Adaptation::ServiceFilter f
= this->filter();
251 DynamicGroupCfg current
, future
;
252 DynamicServiceChain::Split(f
, services
, current
, future
);
254 if (!future
.empty()) {
255 ah
->setFutureServices(future
);
256 debugs(85,3, HERE
<< "noted future service-proposed plan: " << future
);
259 // use the current config even if it is empty; we must replace the old plan
260 theGroup
= new DynamicServiceChain(current
, f
); // refcounted
261 thePlan
= ServicePlan(theGroup
, f
);
262 debugs(85,3, HERE
<< "adopted service-proposed plan: " << thePlan
);
266 Adaptation::ServiceFilter
Adaptation::Iterator::filter() const
268 // the method may differ from theGroup->method due to request satisfaction
269 Method method
= methodNone
;
270 // temporary variables, no locking needed
271 HttpRequest
*req
= NULL
;
272 HttpReply
*rep
= NULL
;
274 if (HttpRequest
*r
= dynamic_cast<HttpRequest
*>(theMsg
)) {
275 method
= methodReqmod
;
278 } else if (HttpReply
*theReply
= dynamic_cast<HttpReply
*>(theMsg
)) {
279 method
= methodRespmod
;
283 Must(false); // should not happen
286 return ServiceFilter(method
, theGroup
->point
, req
, rep
, al
);
289 CBDATA_NAMESPACED_CLASS_INIT(Adaptation
, Iterator
);