]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/adaptation/Iterator.cc
2 * DEBUG: section 93 Adaptation
6 #include "adaptation/Answer.h"
7 #include "adaptation/Config.h"
8 #include "adaptation/Iterator.h"
9 #include "adaptation/Service.h"
10 #include "adaptation/ServiceFilter.h"
11 #include "adaptation/ServiceGroups.h"
12 #include "base/TextException.h"
13 #include "HttpRequest.h"
14 #include "HttpReply.h"
18 Adaptation::Iterator::Iterator(
19 HttpMsg
*aMsg
, HttpRequest
*aCause
,
20 const ServiceGroupPointer
&aGroup
):
22 Adaptation::Initiate("Iterator"),
24 theMsg(HTTPMSGLOCK(aMsg
)),
25 theCause(aCause
? HTTPMSGLOCK(aCause
) : NULL
),
32 Adaptation::Iterator::~Iterator()
35 HTTPMSGUNLOCK(theMsg
);
36 HTTPMSGUNLOCK(theCause
);
39 void Adaptation::Iterator::start()
41 Adaptation::Initiate::start();
43 thePlan
= ServicePlan(theGroup
, filter());
47 void Adaptation::Iterator::step()
50 debugs(93,5, HERE
<< '#' << iterations
<< " plan: " << thePlan
);
54 if (thePlan
.exhausted()) { // nothing more to do
55 sendAnswer(Answer::Forward(theMsg
));
60 HttpRequest
*request
= dynamic_cast<HttpRequest
*>(theMsg
);
64 request
->clearError();
66 if (iterations
> Adaptation::Config::service_iteration_limit
) {
67 debugs(93,DBG_CRITICAL
, "Adaptation iterations limit (" <<
68 Adaptation::Config::service_iteration_limit
<< ") exceeded:\n" <<
69 "\tPossible service loop with " <<
70 theGroup
->kind
<< " " << theGroup
->id
<< ", plan=" << thePlan
);
71 throw TexcHere("too many adaptations");
74 ServicePointer service
= thePlan
.current();
75 Must(service
!= NULL
);
76 debugs(93,5, HERE
<< "using adaptation service: " << service
->cfg().key
);
78 theLauncher
= initiateAdaptation(
79 service
->makeXactLauncher(theMsg
, theCause
));
80 Must(initiated(theLauncher
));
85 Adaptation::Iterator::noteAdaptationAnswer(const Answer
&answer
)
87 switch (answer
.kind
) {
88 case Answer::akForward
:
89 handleAdaptedHeader(answer
.message
);
93 handleAdaptationBlock(answer
);
97 handleAdaptationError(answer
.final
);
103 Adaptation::Iterator::handleAdaptedHeader(HttpMsg
*aMsg
)
105 // set theCause if we switched to request satisfaction mode
106 if (!theCause
) { // probably sent a request message
107 if (dynamic_cast<HttpReply
*>(aMsg
)) { // we got a response message
108 if (HttpRequest
*cause
= dynamic_cast<HttpRequest
*>(theMsg
)) {
109 // definately sent request, now use it as the cause
110 theCause
= cause
; // moving the lock
112 debugs(93,3, HERE
<< "in request satisfaction mode");
118 HTTPMSGUNLOCK(theMsg
);
119 theMsg
= HTTPMSGLOCK(aMsg
);
122 clearAdaptation(theLauncher
);
123 if (!updatePlan(true)) // do not immediatelly advance the new plan
124 thePlan
.next(filter());
128 void Adaptation::Iterator::noteInitiatorAborted()
130 announceInitiatorAbort(theLauncher
); // propogate to the transaction
132 mustStop("initiator gone");
135 void Adaptation::Iterator::handleAdaptationBlock(const Answer
&answer
)
137 debugs(93,5, HERE
<< "blocked by " << answer
);
138 clearAdaptation(theLauncher
);
144 void Adaptation::Iterator::handleAdaptationError(bool final
)
146 debugs(93,5, HERE
<< "final: " << final
<< " plan: " << thePlan
);
147 clearAdaptation(theLauncher
);
150 // can we replace the failed service (group-level bypass)?
151 const bool srcIntact
= !theMsg
->body_pipe
||
152 !theMsg
->body_pipe
->consumedSize();
153 // can we ignore the failure (compute while thePlan is not exhausted)?
154 Must(!thePlan
.exhausted());
155 const bool canIgnore
= thePlan
.current()->cfg().bypass
;
156 debugs(85,5, HERE
<< "flags: " << srcIntact
<< canIgnore
<< adapted
);
159 if (thePlan
.replacement(filter()) != NULL
) {
160 debugs(93,3, HERE
<< "trying a replacement service");
166 if (canIgnore
&& srcIntact
&& adapted
) {
167 debugs(85,3, HERE
<< "responding with older adapted msg");
168 sendAnswer(Answer::Forward(theMsg
));
169 mustStop("sent older adapted msg");
173 // caller may recover if we can ignore the error and virgin msg is intact
174 const bool useVirgin
= canIgnore
&& !adapted
&& srcIntact
;
175 tellQueryAborted(!useVirgin
);
176 mustStop("group failure");
179 bool Adaptation::Iterator::doneAll() const
181 return Adaptation::Initiate::doneAll() && thePlan
.exhausted();
184 void Adaptation::Iterator::swanSong()
186 if (theInitiator
.set())
187 tellQueryAborted(true); // abnormal condition that should not happen
189 if (initiated(theLauncher
))
190 clearAdaptation(theLauncher
);
192 Adaptation::Initiate::swanSong();
195 bool Adaptation::Iterator::updatePlan(bool adopt
)
197 HttpRequest
*r
= theCause
? theCause
: dynamic_cast<HttpRequest
*>(theMsg
);
200 Adaptation::History::Pointer ah
= r
->adaptHistory();
202 debugs(85,9, HERE
<< "no history to store a service-proposed plan");
203 return false; // the feature is not enabled or is not triggered
207 if (!ah
->extractNextServices(services
)) { // clears history
208 debugs(85,9, HERE
<< "no service-proposed plan received");
209 return false; // the service did not provide a new plan
213 debugs(85,3, HERE
<< "rejecting service-proposed plan");
217 debugs(85,3, HERE
<< "retiring old plan: " << thePlan
);
219 Adaptation::ServiceFilter filter
= this->filter();
220 DynamicGroupCfg current
, future
;
221 DynamicServiceChain::Split(filter
, services
, current
, future
);
223 if (!future
.empty()) {
224 ah
->setFutureServices(future
);
225 debugs(85,3, HERE
<< "noted future service-proposed plan: " << future
);
228 // use the current config even if it is empty; we must replace the old plan
229 theGroup
= new DynamicServiceChain(current
, filter
); // refcounted
230 thePlan
= ServicePlan(theGroup
, filter
);
231 debugs(85,3, HERE
<< "adopted service-proposed plan: " << thePlan
);
235 Adaptation::ServiceFilter
Adaptation::Iterator::filter() const
237 // the method may differ from theGroup->method due to request satisfaction
238 Method method
= methodNone
;
239 // temporary variables, no locking needed
240 HttpRequest
*req
= NULL
;
241 HttpReply
*rep
= NULL
;
243 if (HttpRequest
*r
= dynamic_cast<HttpRequest
*>(theMsg
)) {
244 method
= methodReqmod
;
247 } else if (HttpReply
*theReply
= dynamic_cast<HttpReply
*>(theMsg
)) {
248 method
= methodRespmod
;
252 Must(false); // should not happen
255 return ServiceFilter(method
, theGroup
->point
, req
, rep
);
258 CBDATA_NAMESPACED_CLASS_INIT(Adaptation
, Iterator
);