]> git.ipfire.org Git - thirdparty/squid.git/blame - src/adaptation/Iterator.cc
Source Maintenance: enforce #include statement block ordering
[thirdparty/squid.git] / src / adaptation / Iterator.cc
CommitLineData
a22e6cd3 1/*
b510f3a1 2 * DEBUG: section 93 Adaptation
a22e6cd3
AR
3 */
4
582c2af2 5#include "squid.h"
1adcebc3 6#include "adaptation/Answer.h"
a22e6cd3
AR
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"
3d93a84d
AJ
12#include "base/TextException.h"
13#include "HttpRequest.h"
14#include "HttpReply.h"
15#include "HttpMsg.h"
a22e6cd3 16
4299f876 17Adaptation::Iterator::Iterator(
4cb2536f
A
18 HttpMsg *aMsg, HttpRequest *aCause,
19 const ServiceGroupPointer &aGroup):
e1381638 20 AsyncJob("Iterator"),
4299f876 21 Adaptation::Initiate("Iterator"),
e1381638 22 theGroup(aGroup),
b248c2a3
AJ
23 theMsg(aMsg),
24 theCause(aCause),
e1381638
AJ
25 theLauncher(0),
26 iterations(0),
27 adapted(false)
a22e6cd3 28{
b248c2a3
AJ
29 if (theCause != NULL)
30 HTTPMSGLOCK(theCause);
31
32 if (theMsg != NULL)
33 HTTPMSGLOCK(theMsg);
a22e6cd3
AR
34}
35
36Adaptation::Iterator::~Iterator()
37{
38 assert(!theLauncher);
39 HTTPMSGUNLOCK(theMsg);
40 HTTPMSGUNLOCK(theCause);
41}
42
43void Adaptation::Iterator::start()
44{
45 Adaptation::Initiate::start();
46
47 thePlan = ServicePlan(theGroup, filter());
48 step();
49}
50
51void Adaptation::Iterator::step()
52{
53 ++iterations;
54 debugs(93,5, HERE << '#' << iterations << " plan: " << thePlan);
55
56 Must(!theLauncher);
57
58 if (thePlan.exhausted()) { // nothing more to do
3af10ac0 59 sendAnswer(Answer::Forward(theMsg));
a22e6cd3
AR
60 Must(done());
61 return;
62 }
63
129fe2a1
CT
64 HttpRequest *request = dynamic_cast<HttpRequest*>(theMsg);
65 if (!request)
66 request = theCause;
67 assert(request);
68 request->clearError();
69
a22e6cd3
AR
70 if (iterations > Adaptation::Config::service_iteration_limit) {
71 debugs(93,DBG_CRITICAL, "Adaptation iterations limit (" <<
e1381638
AJ
72 Adaptation::Config::service_iteration_limit << ") exceeded:\n" <<
73 "\tPossible service loop with " <<
74 theGroup->kind << " " << theGroup->id << ", plan=" << thePlan);
a22e6cd3
AR
75 throw TexcHere("too many adaptations");
76 }
77
78 ServicePointer service = thePlan.current();
79 Must(service != NULL);
80 debugs(93,5, HERE << "using adaptation service: " << service->cfg().key);
81
82 theLauncher = initiateAdaptation(
4cb2536f 83 service->makeXactLauncher(theMsg, theCause));
4299f876 84 Must(initiated(theLauncher));
a22e6cd3
AR
85 Must(!done());
86}
87
3af10ac0
AR
88void
89Adaptation::Iterator::noteAdaptationAnswer(const Answer &answer)
90{
91 switch (answer.kind) {
92 case Answer::akForward:
b248c2a3 93 handleAdaptedHeader(const_cast<HttpMsg*>(answer.message.getRaw()));
3af10ac0
AR
94 break;
95
96 case Answer::akBlock:
97 handleAdaptationBlock(answer);
98 break;
99
100 case Answer::akError:
101 handleAdaptationError(answer.final);
102 break;
103 }
104}
105
106void
107Adaptation::Iterator::handleAdaptedHeader(HttpMsg *aMsg)
a22e6cd3
AR
108{
109 // set theCause if we switched to request satisfaction mode
110 if (!theCause) { // probably sent a request message
111 if (dynamic_cast<HttpReply*>(aMsg)) { // we got a response message
112 if (HttpRequest *cause = dynamic_cast<HttpRequest*>(theMsg)) {
113 // definately sent request, now use it as the cause
114 theCause = cause; // moving the lock
115 theMsg = 0;
116 debugs(93,3, HERE << "in request satisfaction mode");
117 }
118 }
119 }
120
121 Must(aMsg);
122 HTTPMSGUNLOCK(theMsg);
b248c2a3
AJ
123 theMsg = aMsg;
124 HTTPMSGLOCK(theMsg);
a22e6cd3
AR
125 adapted = true;
126
127 clearAdaptation(theLauncher);
128 if (!updatePlan(true)) // do not immediatelly advance the new plan
129 thePlan.next(filter());
130 step();
131}
132
133void Adaptation::Iterator::noteInitiatorAborted()
134{
135 announceInitiatorAbort(theLauncher); // propogate to the transaction
136 clearInitiator();
137 mustStop("initiator gone");
138}
139
3af10ac0
AR
140void Adaptation::Iterator::handleAdaptationBlock(const Answer &answer)
141{
142 debugs(93,5, HERE << "blocked by " << answer);
143 clearAdaptation(theLauncher);
144 updatePlan(false);
145 sendAnswer(answer);
146 mustStop("blocked");
147}
148
149void Adaptation::Iterator::handleAdaptationError(bool final)
a22e6cd3
AR
150{
151 debugs(93,5, HERE << "final: " << final << " plan: " << thePlan);
152 clearAdaptation(theLauncher);
153 updatePlan(false);
154
155 // can we replace the failed service (group-level bypass)?
e1381638
AJ
156 const bool srcIntact = !theMsg->body_pipe ||
157 !theMsg->body_pipe->consumedSize();
a22e6cd3
AR
158 // can we ignore the failure (compute while thePlan is not exhausted)?
159 Must(!thePlan.exhausted());
160 const bool canIgnore = thePlan.current()->cfg().bypass;
161 debugs(85,5, HERE << "flags: " << srcIntact << canIgnore << adapted);
162
163 if (srcIntact) {
164 if (thePlan.replacement(filter()) != NULL) {
165 debugs(93,3, HERE << "trying a replacement service");
166 step();
167 return;
168 }
169 }
170
171 if (canIgnore && srcIntact && adapted) {
172 debugs(85,3, HERE << "responding with older adapted msg");
3af10ac0 173 sendAnswer(Answer::Forward(theMsg));
a22e6cd3
AR
174 mustStop("sent older adapted msg");
175 return;
176 }
177
178 // caller may recover if we can ignore the error and virgin msg is intact
179 const bool useVirgin = canIgnore && !adapted && srcIntact;
180 tellQueryAborted(!useVirgin);
181 mustStop("group failure");
182}
183
184bool Adaptation::Iterator::doneAll() const
185{
186 return Adaptation::Initiate::doneAll() && thePlan.exhausted();
187}
188
189void Adaptation::Iterator::swanSong()
190{
4299f876 191 if (theInitiator.set())
a22e6cd3
AR
192 tellQueryAborted(true); // abnormal condition that should not happen
193
4299f876 194 if (initiated(theLauncher))
a22e6cd3
AR
195 clearAdaptation(theLauncher);
196
197 Adaptation::Initiate::swanSong();
198}
199
200bool Adaptation::Iterator::updatePlan(bool adopt)
201{
202 HttpRequest *r = theCause ? theCause : dynamic_cast<HttpRequest*>(theMsg);
203 Must(r);
204
205 Adaptation::History::Pointer ah = r->adaptHistory();
aaf0559d
AR
206 if (!ah) {
207 debugs(85,9, HERE << "no history to store a service-proposed plan");
a22e6cd3 208 return false; // the feature is not enabled or is not triggered
aaf0559d 209 }
a22e6cd3
AR
210
211 String services;
212 if (!ah->extractNextServices(services)) { // clears history
213 debugs(85,9, HERE << "no service-proposed plan received");
214 return false; // the service did not provide a new plan
215 }
216
217 if (!adopt) {
218 debugs(85,3, HERE << "rejecting service-proposed plan");
219 return false;
220 }
e1381638 221
a22e6cd3 222 debugs(85,3, HERE << "retiring old plan: " << thePlan);
53340485 223
eb898410 224 Adaptation::ServiceFilter f = this->filter();
53340485 225 DynamicGroupCfg current, future;
eb898410 226 DynamicServiceChain::Split(f, services, current, future);
53340485
AR
227
228 if (!future.empty()) {
229 ah->setFutureServices(future);
230 debugs(85,3, HERE << "noted future service-proposed plan: " << future);
231 }
232
233 // use the current config even if it is empty; we must replace the old plan
eb898410
AJ
234 theGroup = new DynamicServiceChain(current, f); // refcounted
235 thePlan = ServicePlan(theGroup, f);
a22e6cd3
AR
236 debugs(85,3, HERE << "adopted service-proposed plan: " << thePlan);
237 return true;
238}
239
240Adaptation::ServiceFilter Adaptation::Iterator::filter() const
241{
242 // the method may differ from theGroup->method due to request satisfaction
243 Method method = methodNone;
244 // temporary variables, no locking needed
245 HttpRequest *req = NULL;
246 HttpReply *rep = NULL;
247
248 if (HttpRequest *r = dynamic_cast<HttpRequest*>(theMsg)) {
249 method = methodReqmod;
250 req = r;
251 rep = NULL;
b0365bd9 252 } else if (HttpReply *theReply = dynamic_cast<HttpReply*>(theMsg)) {
a22e6cd3
AR
253 method = methodRespmod;
254 req = theCause;
b0365bd9 255 rep = theReply;
a22e6cd3
AR
256 } else {
257 Must(false); // should not happen
258 }
259
260 return ServiceFilter(method, theGroup->point, req, rep);
261}
262
263CBDATA_NAMESPACED_CLASS_INIT(Adaptation, Iterator);