]>
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"
17 Adaptation::Iterator::Iterator(
18 HttpMsg
*aMsg
, HttpRequest
*aCause
,
19 const ServiceGroupPointer
&aGroup
):
21 Adaptation::Initiate("Iterator"),
23 theMsg(HTTPMSGLOCK(aMsg
)),
24 theCause(aCause
? HTTPMSGLOCK(aCause
) : NULL
),
31 Adaptation::Iterator::~Iterator()
34 HTTPMSGUNLOCK(theMsg
);
35 HTTPMSGUNLOCK(theCause
);
38 void Adaptation::Iterator::start()
40 Adaptation::Initiate::start();
42 thePlan
= ServicePlan(theGroup
, filter());
46 void Adaptation::Iterator::step()
49 debugs(93,5, HERE
<< '#' << iterations
<< " plan: " << thePlan
);
53 if (thePlan
.exhausted()) { // nothing more to do
54 sendAnswer(Answer::Forward(theMsg
));
59 HttpRequest
*request
= dynamic_cast<HttpRequest
*>(theMsg
);
63 request
->clearError();
65 if (iterations
> Adaptation::Config::service_iteration_limit
) {
66 debugs(93,DBG_CRITICAL
, "Adaptation iterations limit (" <<
67 Adaptation::Config::service_iteration_limit
<< ") exceeded:\n" <<
68 "\tPossible service loop with " <<
69 theGroup
->kind
<< " " << theGroup
->id
<< ", plan=" << thePlan
);
70 throw TexcHere("too many adaptations");
73 ServicePointer service
= thePlan
.current();
74 Must(service
!= NULL
);
75 debugs(93,5, HERE
<< "using adaptation service: " << service
->cfg().key
);
77 theLauncher
= initiateAdaptation(
78 service
->makeXactLauncher(theMsg
, theCause
));
79 Must(initiated(theLauncher
));
84 Adaptation::Iterator::noteAdaptationAnswer(const Answer
&answer
)
86 switch (answer
.kind
) {
87 case Answer::akForward
:
88 handleAdaptedHeader(answer
.message
);
92 handleAdaptationBlock(answer
);
96 handleAdaptationError(answer
.final
);
102 Adaptation::Iterator::handleAdaptedHeader(HttpMsg
*aMsg
)
104 // set theCause if we switched to request satisfaction mode
105 if (!theCause
) { // probably sent a request message
106 if (dynamic_cast<HttpReply
*>(aMsg
)) { // we got a response message
107 if (HttpRequest
*cause
= dynamic_cast<HttpRequest
*>(theMsg
)) {
108 // definately sent request, now use it as the cause
109 theCause
= cause
; // moving the lock
111 debugs(93,3, HERE
<< "in request satisfaction mode");
117 HTTPMSGUNLOCK(theMsg
);
118 theMsg
= HTTPMSGLOCK(aMsg
);
121 clearAdaptation(theLauncher
);
122 if (!updatePlan(true)) // do not immediatelly advance the new plan
123 thePlan
.next(filter());
127 void Adaptation::Iterator::noteInitiatorAborted()
129 announceInitiatorAbort(theLauncher
); // propogate to the transaction
131 mustStop("initiator gone");
134 void Adaptation::Iterator::handleAdaptationBlock(const Answer
&answer
)
136 debugs(93,5, HERE
<< "blocked by " << answer
);
137 clearAdaptation(theLauncher
);
143 void Adaptation::Iterator::handleAdaptationError(bool final
)
145 debugs(93,5, HERE
<< "final: " << final
<< " plan: " << thePlan
);
146 clearAdaptation(theLauncher
);
149 // can we replace the failed service (group-level bypass)?
150 const bool srcIntact
= !theMsg
->body_pipe
||
151 !theMsg
->body_pipe
->consumedSize();
152 // can we ignore the failure (compute while thePlan is not exhausted)?
153 Must(!thePlan
.exhausted());
154 const bool canIgnore
= thePlan
.current()->cfg().bypass
;
155 debugs(85,5, HERE
<< "flags: " << srcIntact
<< canIgnore
<< adapted
);
158 if (thePlan
.replacement(filter()) != NULL
) {
159 debugs(93,3, HERE
<< "trying a replacement service");
165 if (canIgnore
&& srcIntact
&& adapted
) {
166 debugs(85,3, HERE
<< "responding with older adapted msg");
167 sendAnswer(Answer::Forward(theMsg
));
168 mustStop("sent older adapted msg");
172 // caller may recover if we can ignore the error and virgin msg is intact
173 const bool useVirgin
= canIgnore
&& !adapted
&& srcIntact
;
174 tellQueryAborted(!useVirgin
);
175 mustStop("group failure");
178 bool Adaptation::Iterator::doneAll() const
180 return Adaptation::Initiate::doneAll() && thePlan
.exhausted();
183 void Adaptation::Iterator::swanSong()
185 if (theInitiator
.set())
186 tellQueryAborted(true); // abnormal condition that should not happen
188 if (initiated(theLauncher
))
189 clearAdaptation(theLauncher
);
191 Adaptation::Initiate::swanSong();
194 bool Adaptation::Iterator::updatePlan(bool adopt
)
196 HttpRequest
*r
= theCause
? theCause
: dynamic_cast<HttpRequest
*>(theMsg
);
199 Adaptation::History::Pointer ah
= r
->adaptHistory();
201 debugs(85,9, HERE
<< "no history to store a service-proposed plan");
202 return false; // the feature is not enabled or is not triggered
206 if (!ah
->extractNextServices(services
)) { // clears history
207 debugs(85,9, HERE
<< "no service-proposed plan received");
208 return false; // the service did not provide a new plan
212 debugs(85,3, HERE
<< "rejecting service-proposed plan");
216 debugs(85,3, HERE
<< "retiring old plan: " << thePlan
);
218 Adaptation::ServiceFilter filter
= this->filter();
219 DynamicGroupCfg current
, future
;
220 DynamicServiceChain::Split(filter
, services
, current
, future
);
222 if (!future
.empty()) {
223 ah
->setFutureServices(future
);
224 debugs(85,3, HERE
<< "noted future service-proposed plan: " << future
);
227 // use the current config even if it is empty; we must replace the old plan
228 theGroup
= new DynamicServiceChain(current
, filter
); // refcounted
229 thePlan
= ServicePlan(theGroup
, filter
);
230 debugs(85,3, HERE
<< "adopted service-proposed plan: " << thePlan
);
234 Adaptation::ServiceFilter
Adaptation::Iterator::filter() const
236 // the method may differ from theGroup->method due to request satisfaction
237 Method method
= methodNone
;
238 // temporary variables, no locking needed
239 HttpRequest
*req
= NULL
;
240 HttpReply
*rep
= NULL
;
242 if (HttpRequest
*r
= dynamic_cast<HttpRequest
*>(theMsg
)) {
243 method
= methodReqmod
;
246 } else if (HttpReply
*theReply
= dynamic_cast<HttpReply
*>(theMsg
)) {
247 method
= methodRespmod
;
251 Must(false); // should not happen
254 return ServiceFilter(method
, theGroup
->point
, req
, rep
);
257 CBDATA_NAMESPACED_CLASS_INIT(Adaptation
, Iterator
);