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