source: squid-ssl/trunk/fuentes/src/adaptation/Iterator.cc @ 5495

Last change on this file since 5495 was 5495, checked in by Juanma, 2 years ago

Initial release

File size: 8.5 KB
Line 
1/*
2 * Copyright (C) 1996-2015 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
23Adaptation::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
44Adaptation::Iterator::~Iterator()
45{
46    assert(!theLauncher);
47    HTTPMSGUNLOCK(theMsg);
48    HTTPMSGUNLOCK(theCause);
49}
50
51void 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
72void 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
115void
116Adaptation::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
133void
134Adaptation::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
160void Adaptation::Iterator::noteInitiatorAborted()
161{
162    announceInitiatorAbort(theLauncher); // propogate to the transaction
163    clearInitiator();
164    mustStop("initiator gone");
165}
166
167void 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
176void 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
211bool Adaptation::Iterator::doneAll() const
212{
213    return Adaptation::Initiate::doneAll() && thePlan.exhausted();
214}
215
216void 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
227bool 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
267Adaptation::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
290CBDATA_NAMESPACED_CLASS_INIT(Adaptation, Iterator);
291
Note: See TracBrowser for help on using the repository browser.