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