Iterator.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 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"
18#include "base/TextException.h"
19#include "HttpReply.h"
20#include "sbuf/StringConvert.h"
21
23 Http::Message *aMsg, HttpRequest *aCause,
24 const AccessLogEntryPointer &alp,
25 const ServiceGroupPointer &aGroup):
26 AsyncJob("Iterator"),
27 Adaptation::Initiate("Iterator"),
28 theGroup(aGroup),
29 theMsg(aMsg),
30 theCause(aCause),
31 al(alp),
32 theLauncher(nullptr),
33 iterations(0),
34 adapted(false)
35{
36 if (theCause != nullptr)
38
39 if (theMsg != nullptr)
41}
42
44{
45 assert(!theLauncher);
46 HTTPMSGUNLOCK(theMsg);
47 HTTPMSGUNLOCK(theCause);
48}
49
51{
53
54 thePlan = ServicePlan(theGroup, filter());
55
56 // Add adaptation group name once and now, before
57 // dynamic groups change it at step() time.
58 if (Adaptation::Config::needHistory && !thePlan.exhausted() && (dynamic_cast<ServiceSet *>(theGroup.getRaw()) || dynamic_cast<ServiceChain *>(theGroup.getRaw()))) {
59 HttpRequest *request = dynamic_cast<HttpRequest*>(theMsg);
60 if (!request)
61 request = theCause;
62 Must(request);
64 auto gid = StringToSBuf(theGroup->id);
66 }
67
68 step();
69}
70
72{
73 ++iterations;
74 debugs(93,5, '#' << iterations << " plan: " << thePlan);
75
76 Must(!theLauncher);
77
78 if (thePlan.exhausted()) { // nothing more to do
79 sendAnswer(Answer::Forward(theMsg));
80 Must(done());
81 return;
82 }
83
84 HttpRequest *request = dynamic_cast<HttpRequest*>(theMsg);
85 if (!request)
86 request = theCause;
87 assert(request);
88 request->clearError();
89
91 debugs(93,DBG_CRITICAL, "Adaptation iterations limit (" <<
93 "\tPossible service loop with " <<
94 theGroup->kind << " " << theGroup->id << ", plan=" << thePlan);
95 throw TexcHere("too many adaptations");
96 }
97
98 ServicePointer service = thePlan.current();
99 Must(service != nullptr);
100 debugs(93,5, "using adaptation service: " << service->cfg().key);
101
103 Adaptation::History::Pointer ah = request->adaptHistory(true);
104 auto uid = StringToSBuf(thePlan.current()->cfg().key);
106 }
107
108 theLauncher = initiateAdaptation(
109 service->makeXactLauncher(theMsg, theCause, al));
110 Must(initiated(theLauncher));
111 Must(!done());
112}
113
114void
116{
117 switch (answer.kind) {
119 handleAdaptedHeader(const_cast<Http::Message*>(answer.message.getRaw()));
120 break;
121
122 case Answer::akBlock:
123 handleAdaptationBlock(answer);
124 break;
125
126 case Answer::akError:
127 handleAdaptationError(answer.final);
128 break;
129 }
130}
131
132void
134{
135 // set theCause if we switched to request satisfaction mode
136 if (!theCause) { // probably sent a request message
137 if (dynamic_cast<HttpReply*>(aMsg)) { // we got a response message
138 if (HttpRequest *cause = dynamic_cast<HttpRequest*>(theMsg)) {
139 // definitely sent request, now use it as the cause
140 theCause = cause; // moving the lock
141 theMsg = nullptr;
142 debugs(93,3, "in request satisfaction mode");
143 }
144 }
145 }
146
147 Must(aMsg);
148 HTTPMSGUNLOCK(theMsg);
149 theMsg = aMsg;
150 HTTPMSGLOCK(theMsg);
151 adapted = true;
152
153 clearAdaptation(theLauncher);
154 if (!updatePlan(true)) // do not immediately advance the new plan
155 thePlan.next(filter());
156 step();
157}
158
160{
161 announceInitiatorAbort(theLauncher); // propagate to the transaction
162 clearInitiator();
163 mustStop("initiator gone");
164}
165
167{
168 debugs(93,5, "blocked by " << answer);
169 clearAdaptation(theLauncher);
170 updatePlan(false);
171 sendAnswer(answer);
172 mustStop("blocked");
173}
174
176{
177 debugs(93,5, "final: " << final << " plan: " << thePlan);
178 clearAdaptation(theLauncher);
179 updatePlan(false);
180
181 // can we replace the failed service (group-level bypass)?
182 const bool srcIntact = !theMsg->body_pipe ||
183 !theMsg->body_pipe->consumedSize();
184 // can we ignore the failure (compute while thePlan is not exhausted)?
185 Must(!thePlan.exhausted());
186 const bool canIgnore = thePlan.current()->cfg().bypass;
187 debugs(85,5, "flags: " << srcIntact << canIgnore << adapted);
188
189 if (srcIntact) {
190 if (thePlan.replacement(filter()) != nullptr) {
191 debugs(93,3, "trying a replacement service");
192 step();
193 return;
194 }
195 }
196
197 if (canIgnore && srcIntact && adapted) {
198 debugs(85,3, "responding with older adapted msg");
199 sendAnswer(Answer::Forward(theMsg));
200 mustStop("sent older adapted msg");
201 return;
202 }
203
204 // caller may recover if we can ignore the error and virgin msg is intact
205 const bool useVirgin = canIgnore && !adapted && srcIntact;
206 tellQueryAborted(!useVirgin);
207 mustStop("group failure");
208}
209
211{
212 return Adaptation::Initiate::doneAll() && thePlan.exhausted();
213}
214
216{
217 if (theInitiator.set())
218 tellQueryAborted(true); // abnormal condition that should not happen
219
220 if (initiated(theLauncher))
221 clearAdaptation(theLauncher);
222
224}
225
227{
228 HttpRequest *r = theCause ? theCause : dynamic_cast<HttpRequest*>(theMsg);
229 Must(r);
230
232 if (!ah) {
233 debugs(85,9, "no history to store a service-proposed plan");
234 return false; // the feature is not enabled or is not triggered
235 }
236
237 String services;
238 if (!ah->extractNextServices(services)) { // clears history
239 debugs(85,9, "no service-proposed plan received");
240 return false; // the service did not provide a new plan
241 }
242
243 if (!adopt) {
244 debugs(85,3, "rejecting service-proposed plan");
245 return false;
246 }
247
248 debugs(85,3, "retiring old plan: " << thePlan);
249
250 Adaptation::ServiceFilter f = this->filter();
251 DynamicGroupCfg current, future;
252 DynamicServiceChain::Split(f, services, current, future);
253
254 if (!future.empty()) {
255 ah->setFutureServices(future);
256 debugs(85,3, "noted future service-proposed plan: " << future);
257 }
258
259 // use the current config even if it is empty; we must replace the old plan
260 theGroup = new DynamicServiceChain(current, f); // refcounted
261 thePlan = ServicePlan(theGroup, f);
262 debugs(85,3, "adopted service-proposed plan: " << thePlan);
263 return true;
264}
265
267{
268 // the method may differ from theGroup->method due to request satisfaction
269 Method method = methodNone;
270 // temporary variables, no locking needed
271 HttpRequest *req = nullptr;
272 HttpReply *rep = nullptr;
273
274 if (HttpRequest *r = dynamic_cast<HttpRequest*>(theMsg)) {
275 method = methodReqmod;
276 req = r;
277 rep = nullptr;
278 } else if (HttpReply *theReply = dynamic_cast<HttpReply*>(theMsg)) {
279 method = methodRespmod;
280 req = theCause;
281 rep = theReply;
282 } else {
283 Must(false); // should not happen
284 }
285
286 return ServiceFilter(method, theGroup->point, req, rep, al);
287}
288
290
CBDATA_NAMESPACED_CLASS_INIT(Adaptation, Iterator)
SBuf StringToSBuf(const String &s)
create a new SBuf from a String by copying contents
Definition: StringConvert.h:17
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
Definition: TextException.h:63
#define Must(condition)
Definition: TextException.h:75
#define assert(EX)
Definition: assert.h:17
summarizes adaptation service answer for the noteAdaptationAnswer() API
Definition: Answer.h:23
static Answer Forward(Http::Message *aMsg)
create an akForward answer
Definition: Answer.cc:26
Kind kind
the type of the answer
Definition: Answer.h:42
Http::MessagePointer message
HTTP request or response to forward.
Definition: Answer.h:39
bool final
whether the error, if any, cannot be bypassed
Definition: Answer.h:41
@ akForward
forward the supplied adapted HTTP message
Definition: Answer.h:27
@ akBlock
block or deny the master xaction; see authority
Definition: Answer.h:28
@ akError
no adapted message will come; see bypassable
Definition: Answer.h:29
static bool needHistory
HttpRequest adaptation history should recorded.
Definition: Config.h:60
static int service_iteration_limit
Definition: Config.h:46
DynamicServiceGroup configuration to remember future dynamic chains.
bool empty() const
no services added
a temporary service chain built upon another service request
static void Split(const ServiceFilter &filter, const String &ids, DynamicGroupCfg &current, DynamicGroupCfg &future)
separates dynamic services matching current location from future ones
bool extractNextServices(String &value)
returns true, fills the value, and resets iff next services were set
Definition: History.cc:130
void setFutureServices(const DynamicGroupCfg &services)
sets future services for the Adaptation::AccessCheck to notice
Definition: History.cc:156
void recordAdaptationService(SBuf &srvId)
Definition: History.cc:150
void swanSong() override
Definition: Initiate.cc:62
void start() override
called by AsyncStart; do not call directly
Definition: Iterator.cc:50
void step()
launches adaptation for the service selected by the plan
Definition: Iterator.cc:71
~Iterator() override
Definition: Iterator.cc:43
ServiceFilter filter() const
creates service filter for the current step
Definition: Iterator.cc:266
Iterator(Http::Message *virginHeader, HttpRequest *virginCause, const AccessLogEntryPointer &, const Adaptation::ServiceGroupPointer &aGroup)
Definition: Iterator.cc:22
bool updatePlan(bool adopt)
replace the current group and plan with service-proposed ones if needed
Definition: Iterator.cc:226
void handleAdaptedHeader(Http::Message *msg)
Definition: Iterator.cc:133
void noteAdaptationAnswer(const Answer &answer) override
Definition: Iterator.cc:115
void handleAdaptationError(bool final)
Definition: Iterator.cc:175
HttpRequest * theCause
the cause of the original virgin message
Definition: Iterator.h:69
Http::Message * theMsg
the message being adapted (virgin for each step)
Definition: Iterator.h:68
void handleAdaptationBlock(const Answer &answer)
Definition: Iterator.cc:166
bool doneAll() const override
whether positive goal has been reached
Definition: Iterator.cc:210
void swanSong() override
Definition: Iterator.cc:215
void noteInitiatorAborted() override
Definition: Iterator.cc:159
a group of services that must be used one after another
information used to search for adaptation services
Definition: ServiceFilter.h:23
const ServiceConfig & cfg() const
Definition: Service.h:51
virtual Initiate * makeXactLauncher(Http::Message *virginHeader, HttpRequest *virginCause, AccessLogEntry::Pointer &alp)=0
virtual bool doneAll() const
whether positive goal has been reached
Definition: AsyncJob.cc:112
virtual void start()
called by AsyncStart; do not call directly
Definition: AsyncJob.cc:59
Adaptation::History::Pointer adaptHistory(bool createIfNone=false) const
Returns possibly nil history, creating it if requested.
Definition: HttpRequest.cc:404
void clearError()
clear error details, useful for retries/repeats
Definition: HttpRequest.cc:465
common parts of HttpRequest and HttpReply
Definition: Message.h:26
C * getRaw() const
Definition: RefCount.h:89
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
#define DBG_CRITICAL
Definition: Stream.h:37
void HTTPMSGUNLOCK(M *&a)
Definition: Message.h:150
void HTTPMSGLOCK(Http::Message *a)
Definition: Message.h:161
@ methodRespmod
Definition: Elements.h:17
@ methodNone
Definition: Elements.h:17
@ methodReqmod
Definition: Elements.h:17

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors