]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/adaptation/Iterator.cc
2 * Copyright (C) 1996-2016 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"
20 #include "HttpReply.h"
21 #include "HttpRequest.h"
22 #include "sbuf/SBufStringConvert.h"
24 Adaptation::Iterator::Iterator(
25 HttpMsg
*aMsg
, HttpRequest
*aCause
,
26 AccessLogEntry::Pointer
&alp
,
27 const ServiceGroupPointer
&aGroup
):
29 Adaptation::Initiate("Iterator"),
39 HTTPMSGLOCK(theCause
);
45 Adaptation::Iterator::~Iterator()
48 HTTPMSGUNLOCK(theMsg
);
49 HTTPMSGUNLOCK(theCause
);
52 void Adaptation::Iterator::start()
54 Adaptation::Initiate::start();
56 thePlan
= ServicePlan(theGroup
, filter());
58 // Add adaptation group name once and now, before
59 // dynamic groups change it at step() time.
60 if (Adaptation::Config::needHistory
&& !thePlan
.exhausted() && (dynamic_cast<ServiceSet
*>(theGroup
.getRaw()) || dynamic_cast<ServiceChain
*>(theGroup
.getRaw()))) {
61 HttpRequest
*request
= dynamic_cast<HttpRequest
*>(theMsg
);
65 Adaptation::History::Pointer ah
= request
->adaptHistory(true);
66 auto gid
= StringToSBuf(theGroup
->id
);
67 ah
->recordAdaptationService(gid
);
73 void Adaptation::Iterator::step()
76 debugs(93,5, HERE
<< '#' << iterations
<< " plan: " << thePlan
);
80 if (thePlan
.exhausted()) { // nothing more to do
81 sendAnswer(Answer::Forward(theMsg
));
86 HttpRequest
*request
= dynamic_cast<HttpRequest
*>(theMsg
);
90 request
->clearError();
92 if (iterations
> Adaptation::Config::service_iteration_limit
) {
93 debugs(93,DBG_CRITICAL
, "Adaptation iterations limit (" <<
94 Adaptation::Config::service_iteration_limit
<< ") exceeded:\n" <<
95 "\tPossible service loop with " <<
96 theGroup
->kind
<< " " << theGroup
->id
<< ", plan=" << thePlan
);
97 throw TexcHere("too many adaptations");
100 ServicePointer service
= thePlan
.current();
101 Must(service
!= NULL
);
102 debugs(93,5, HERE
<< "using adaptation service: " << service
->cfg().key
);
104 if (Adaptation::Config::needHistory
) {
105 Adaptation::History::Pointer ah
= request
->adaptHistory(true);
106 auto uid
= StringToSBuf(thePlan
.current()->cfg().key
);
107 ah
->recordAdaptationService(uid
);
110 theLauncher
= initiateAdaptation(
111 service
->makeXactLauncher(theMsg
, theCause
, al
));
112 Must(initiated(theLauncher
));
117 Adaptation::Iterator::noteAdaptationAnswer(const Answer
&answer
)
119 switch (answer
.kind
) {
120 case Answer::akForward
:
121 handleAdaptedHeader(const_cast<HttpMsg
*>(answer
.message
.getRaw()));
124 case Answer::akBlock
:
125 handleAdaptationBlock(answer
);
128 case Answer::akError
:
129 handleAdaptationError(answer
.final
);
135 Adaptation::Iterator::handleAdaptedHeader(HttpMsg
*aMsg
)
137 // set theCause if we switched to request satisfaction mode
138 if (!theCause
) { // probably sent a request message
139 if (dynamic_cast<HttpReply
*>(aMsg
)) { // we got a response message
140 if (HttpRequest
*cause
= dynamic_cast<HttpRequest
*>(theMsg
)) {
141 // definately sent request, now use it as the cause
142 theCause
= cause
; // moving the lock
144 debugs(93,3, HERE
<< "in request satisfaction mode");
150 HTTPMSGUNLOCK(theMsg
);
155 clearAdaptation(theLauncher
);
156 if (!updatePlan(true)) // do not immediatelly advance the new plan
157 thePlan
.next(filter());
161 void Adaptation::Iterator::noteInitiatorAborted()
163 announceInitiatorAbort(theLauncher
); // propogate to the transaction
165 mustStop("initiator gone");
168 void Adaptation::Iterator::handleAdaptationBlock(const Answer
&answer
)
170 debugs(93,5, HERE
<< "blocked by " << answer
);
171 clearAdaptation(theLauncher
);
177 void Adaptation::Iterator::handleAdaptationError(bool final
)
179 debugs(93,5, HERE
<< "final: " << final
<< " plan: " << thePlan
);
180 clearAdaptation(theLauncher
);
183 // can we replace the failed service (group-level bypass)?
184 const bool srcIntact
= !theMsg
->body_pipe
||
185 !theMsg
->body_pipe
->consumedSize();
186 // can we ignore the failure (compute while thePlan is not exhausted)?
187 Must(!thePlan
.exhausted());
188 const bool canIgnore
= thePlan
.current()->cfg().bypass
;
189 debugs(85,5, HERE
<< "flags: " << srcIntact
<< canIgnore
<< adapted
);
192 if (thePlan
.replacement(filter()) != NULL
) {
193 debugs(93,3, HERE
<< "trying a replacement service");
199 if (canIgnore
&& srcIntact
&& adapted
) {
200 debugs(85,3, HERE
<< "responding with older adapted msg");
201 sendAnswer(Answer::Forward(theMsg
));
202 mustStop("sent older adapted msg");
206 // caller may recover if we can ignore the error and virgin msg is intact
207 const bool useVirgin
= canIgnore
&& !adapted
&& srcIntact
;
208 tellQueryAborted(!useVirgin
);
209 mustStop("group failure");
212 bool Adaptation::Iterator::doneAll() const
214 return Adaptation::Initiate::doneAll() && thePlan
.exhausted();
217 void Adaptation::Iterator::swanSong()
219 if (theInitiator
.set())
220 tellQueryAborted(true); // abnormal condition that should not happen
222 if (initiated(theLauncher
))
223 clearAdaptation(theLauncher
);
225 Adaptation::Initiate::swanSong();
228 bool Adaptation::Iterator::updatePlan(bool adopt
)
230 HttpRequest
*r
= theCause
? theCause
: dynamic_cast<HttpRequest
*>(theMsg
);
233 Adaptation::History::Pointer ah
= r
->adaptHistory();
235 debugs(85,9, HERE
<< "no history to store a service-proposed plan");
236 return false; // the feature is not enabled or is not triggered
240 if (!ah
->extractNextServices(services
)) { // clears history
241 debugs(85,9, HERE
<< "no service-proposed plan received");
242 return false; // the service did not provide a new plan
246 debugs(85,3, HERE
<< "rejecting service-proposed plan");
250 debugs(85,3, HERE
<< "retiring old plan: " << thePlan
);
252 Adaptation::ServiceFilter f
= this->filter();
253 DynamicGroupCfg current
, future
;
254 DynamicServiceChain::Split(f
, services
, current
, future
);
256 if (!future
.empty()) {
257 ah
->setFutureServices(future
);
258 debugs(85,3, HERE
<< "noted future service-proposed plan: " << future
);
261 // use the current config even if it is empty; we must replace the old plan
262 theGroup
= new DynamicServiceChain(current
, f
); // refcounted
263 thePlan
= ServicePlan(theGroup
, f
);
264 debugs(85,3, HERE
<< "adopted service-proposed plan: " << thePlan
);
268 Adaptation::ServiceFilter
Adaptation::Iterator::filter() const
270 // the method may differ from theGroup->method due to request satisfaction
271 Method method
= methodNone
;
272 // temporary variables, no locking needed
273 HttpRequest
*req
= NULL
;
274 HttpReply
*rep
= NULL
;
276 if (HttpRequest
*r
= dynamic_cast<HttpRequest
*>(theMsg
)) {
277 method
= methodReqmod
;
280 } else if (HttpReply
*theReply
= dynamic_cast<HttpReply
*>(theMsg
)) {
281 method
= methodRespmod
;
285 Must(false); // should not happen
288 return ServiceFilter(method
, theGroup
->point
, req
, rep
, al
);
291 CBDATA_NAMESPACED_CLASS_INIT(Adaptation
, Iterator
);