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