]> git.ipfire.org Git - thirdparty/squid.git/blob - src/esi/Sequence.cc
35af3cecc16e63943ac786c3f73bcd1e4abbc86a
[thirdparty/squid.git] / src / esi / Sequence.cc
1 /*
2 * Copyright (C) 1996-2020 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 86 ESI processing */
10
11 #include "squid.h"
12 #include "Debug.h"
13 #include "fatal.h"
14
15 /* MS Visual Studio Projects are monolithic, so we need the following
16 * #if to exclude the ESI code from compile process when not needed.
17 */
18 #if (USE_SQUID_ESI == 1)
19
20 #include "esi/Attempt.h"
21 #include "esi/Except.h"
22 #include "esi/Literal.h"
23 #include "esi/Sequence.h"
24
25 class esiExcept;
26
27 esiSequence::~esiSequence ()
28 {
29 debugs(86, 5, "esiSequence::~esiSequence " << this);
30 FinishAllElements(elements); // finish if not already done
31 }
32
33 esiSequence::esiSequence(esiTreeParentPtr aParent, bool incrementalFlag) :
34 elements(),
35 processedcount(0),
36 parent(aParent),
37 mayFail_(true),
38 failed(false),
39 provideIncrementalData(incrementalFlag),
40 processing(false),
41 processingResult(ESI_PROCESS_COMPLETE),
42 nextElementToProcess_(0)
43 {
44 memset(&flags, 0, sizeof(flags));
45 }
46
47 size_t
48 esiSequence::nextElementToProcess() const
49 {
50 return nextElementToProcess_;
51 }
52
53 void
54 esiSequence::nextElementToProcess(size_t const &aSizeT)
55 {
56 nextElementToProcess_ = aSizeT;
57 }
58
59 bool
60 esiSequence::finishedProcessing() const
61 {
62 return nextElementToProcess() >= elements.size();
63 }
64
65 bool
66 esiSequence::mayFail () const
67 {
68 if (failed)
69 return true;
70
71 return mayFail_;
72 }
73
74 void
75 esiSequence::wontFail()
76 {
77 assert (!failed);
78 mayFail_ = false;
79 }
80
81 void
82 esiSequence::render(ESISegment::Pointer output)
83 {
84 /* append all processed elements, and trim processed
85 * and rendered elements
86 */
87 assert (output->next == NULL);
88 debugs (86,5, "esiSequenceRender: rendering " << processedcount << " elements");
89
90 for (size_t i = 0; i < processedcount; ++i) {
91 elements[i]->render(output);
92 FinishAnElement(elements[i], i);
93 // TODO: pass an "ESISegment **" ?
94 output = output->tail();
95 }
96
97 // prune completed elements
98 elements.erase(elements.begin(), elements.begin() + processedcount);
99 processedcount = 0;
100 assert (output->next == NULL);
101 }
102
103 void
104 esiSequence::finish()
105 {
106 debugs(86, 5, "esiSequence::finish: " << this << " is finished");
107 FinishAllElements(elements);
108 parent = NULL;
109 }
110
111 void
112 esiSequence::provideData (ESISegment::Pointer data, ESIElement *source)
113 {
114 ESIElement::Pointer lockthis = this;
115
116 if (processing)
117 debugs(86, 5, "esiSequence::provideData: " << this << " data provided during processing");
118 debugs(86, 5, "esiSequence::provideData " << this << " " << data.getRaw() << " " << source);
119
120 /* when data is provided, the element *must* be completed */
121 /* XXX: when the callback model is complete,
122 * we can introduce 'finished'. And then this rule can be
123 * relaxed
124 */
125 /* find the index */
126 int index = elementIndex (source);
127
128 assert (index >= 0);
129
130 /* remove the current node */
131 FinishAnElement(elements[index], index);
132
133 /* create a literal */
134 esiLiteral *temp = new esiLiteral (data);
135
136 /* insert the literal */
137 elements[index] = temp;
138
139 /* XXX: TODO push any pushable data upwards */
140 /* fail() not done */
141 if (processing)
142 return;
143
144 assert (process (flags.dovars) != ESI_PROCESS_FAILED);
145 }
146
147 bool
148 esiSequence::addElement (ESIElement::Pointer element)
149 {
150 /* add an element to the output list */
151 /* Some elements require specific parents */
152
153 if (dynamic_cast<esiAttempt*>(element.getRaw()) ||
154 dynamic_cast<esiExcept*>(element.getRaw())) {
155 debugs(86, DBG_CRITICAL, "esiSequenceAdd: misparented Attempt or Except element (section 3.4)");
156 return false;
157 }
158
159 /* Tie literals together for efficiency */
160 if (elements.size() && dynamic_cast<esiLiteral*>(element.getRaw()) &&
161 dynamic_cast<esiLiteral*>(elements[elements.size() - 1].getRaw())) {
162 debugs(86, 5, "esiSequenceAdd: tying Literals " <<
163 elements[elements.size() - 1].getRaw() << " and " <<
164 element.getRaw() << " together");
165
166 ESISegment::ListTransfer (((esiLiteral *)element.getRaw())->buffer,
167 ((esiLiteral *)elements[elements.size() - 1].getRaw())->buffer);
168 return true;
169 }
170
171 elements.push_back(element);
172 debugs (86,3, "esiSequenceAdd: Added a new element, elements = " << elements.size());
173 return true;
174 }
175
176 int
177 esiSequence::elementIndex(ESIElement::Pointer anElement) const
178 {
179 for (size_t i = 0; i < elements.size(); ++i)
180 if (elements[i] == anElement)
181 return i;
182
183 return -1;
184 }
185
186 void
187 esiSequence::processStep(int dovars)
188 {
189 size_t elementToProcess = nextElementToProcess();
190 nextElementToProcess(elementToProcess + 1);
191 esiProcessResult_t tempResult = processOne(dovars, elementToProcess);
192
193 if (processingResult < tempResult) {
194 debugs(86, 5, "esiSequence::process: processingResult was " << processingResult << ", increasing to " << tempResult);
195 processingResult = tempResult;
196 }
197 }
198
199 esiProcessResult_t
200 esiSequence::processOne(int dovars, size_t index)
201 {
202 debugs (86,5, "esiSequence::process " << this << " about to process element[" << index << "] " << elements[index].getRaw());
203
204 switch (elements[index]->process(dovars)) {
205
206 case ESI_PROCESS_COMPLETE:
207 debugs(86, 5, "esiSequenceProcess: " << this << " element " << elements[index].getRaw() << " Processed OK");
208
209 if (index == processedcount)
210 /* another completely ready */
211 ++processedcount;
212
213 return ESI_PROCESS_COMPLETE;
214
215 case ESI_PROCESS_PENDING_WONTFAIL:
216 debugs(86, 5, "esiSequenceProcess: element Processed PENDING OK");
217
218 return ESI_PROCESS_PENDING_WONTFAIL;
219
220 case ESI_PROCESS_PENDING_MAYFAIL:
221 debugs(86, 5, "eseSequenceProcess: element Processed PENDING UNKNOWN");
222
223 return ESI_PROCESS_PENDING_MAYFAIL;
224
225 case ESI_PROCESS_FAILED:
226 debugs(86, 5, "esiSequenceProcess: element Processed FAILED");
227
228 return ESI_PROCESS_FAILED;
229
230 default:
231 fatal ("unexpected code in esiSequence::processOne\n");
232
233 return ESI_PROCESS_FAILED;
234 }
235 }
236
237 esiProcessResult_t
238 esiSequence::process (int inheritedVarsFlag)
239 {
240 debugs(86, 5, "esiSequence::process: " << this << " processing");
241
242 if (processing) {
243 debugs(86, 5, "esiSequence::process: " << this <<
244 " reentry attempt during processing");
245 }
246
247 /* process as much of the list as we can, stopping only on
248 * failures
249 */
250 if (!processing || processedcount == 0)
251 processingResult = ESI_PROCESS_COMPLETE;
252
253 int dovars = inheritedVarsFlag;
254
255 if (flags.dovars)
256 dovars = 1;
257
258 debugs(86, 5, "esiSequence::process: Processing " << this << " with" <<
259 (dovars ? "" : "out") << " variable processing");
260
261 processing = true;
262
263 nextElementToProcess(processedcount);
264
265 while (!finishedProcessing()) {
266 processStep(dovars);
267
268 if (!processing)
269 return processingResult;
270
271 if (processingResult == ESI_PROCESS_FAILED) {
272 FinishAllElements(elements);
273 failed = true;
274 parent = NULL;
275 processing = false;
276 return processingResult;
277 }
278 }
279
280 assert (processingResult != ESI_PROCESS_COMPLETE || processedcount == elements.size());
281
282 if (processingResult == ESI_PROCESS_COMPLETE || processingResult == ESI_PROCESS_PENDING_WONTFAIL)
283 wontFail();
284
285 if (processedcount == elements.size() || provideIncrementalData) {
286 ESISegment::Pointer temp(new ESISegment);
287 render (temp);
288
289 if (temp->next.getRaw() || temp->len)
290 parent->provideData(temp, this);
291 else
292 ESISegmentFreeList (temp);
293 }
294
295 /* Depends on full parsing before processing */
296 if (processedcount == elements.size())
297 parent = NULL;
298
299 debugs(86, 5, "esiSequence::process: " << this << " completed");
300
301 processing = false;
302
303 return processingResult;
304 }
305
306 void
307 esiSequence::fail (ESIElement *source, char const *anError)
308 {
309 failed = true;
310
311 if (processing) {
312 debugs(86, 5, "esiSequence::fail: " << this << " failure callback during processing");
313 return;
314 }
315
316 debugs(86, 5, "esiSequence::fail: " << this << " has failed.");
317 parent->fail (this, anError);
318 FinishAllElements(elements);
319 parent = NULL;
320 }
321
322 esiSequence::esiSequence(esiSequence const &old) :
323 processedcount(0),
324 parent(NULL),
325 mayFail_(old.mayFail_),
326 failed(old.failed),
327 provideIncrementalData(old.provideIncrementalData),
328 processing(false),
329 processingResult(ESI_PROCESS_COMPLETE),
330 nextElementToProcess_(0)
331 {
332 flags.dovars = old.flags.dovars;
333 }
334
335 void
336 esiSequence::makeCachableElements(esiSequence const &old)
337 {
338 for (size_t counter = 0; counter < old.elements.size(); ++counter) {
339 ESIElement::Pointer newElement = old.elements[counter]->makeCacheable();
340
341 if (newElement.getRaw())
342 assert (addElement(newElement));
343 }
344 }
345
346 void
347 esiSequence::makeUsableElements(esiSequence const &old, ESIVarState &newVarState)
348 {
349 for (size_t counter = 0; counter < old.elements.size(); ++counter) {
350 ESIElement::Pointer newElement = old.elements[counter]->makeUsable (this, newVarState);
351
352 if (newElement.getRaw())
353 assert (addElement(newElement));
354 }
355 }
356
357 ESIElement::Pointer
358 esiSequence::makeCacheable() const
359 {
360 debugs(86, 5, "esiSequence::makeCacheable: Making cachable sequence from " << this);
361 assert (processedcount == 0);
362 assert (!failed);
363
364 if (elements.size() == 0) {
365 debugs(86, 5, "esiSequence::makeCacheable: No elements in sequence " << this << ", returning NULL");
366 return NULL;
367 }
368
369 esiSequence * resultS = new esiSequence (*this);
370 ESIElement::Pointer result = resultS;
371 resultS->makeCachableElements(*this);
372 debugs(86, 5, "esiSequence::makeCacheable: " << this << " created " << result.getRaw());
373 return result;
374 }
375
376 ESIElement::Pointer
377 esiSequence::makeUsable(esiTreeParentPtr newParent, ESIVarState &newVarState) const
378 {
379 debugs(86, 5, "esiSequence::makeUsable: Creating usable Sequence");
380 assert (processedcount == 0);
381 assert (!failed);
382
383 if (elements.size() == 0) {
384 debugs(86, 5, "esiSequence::makeUsable: No elements in sequence " << this << ", returning NULL");
385 return NULL;
386 }
387
388 esiSequence * resultS = new esiSequence (*this);
389 ESIElement::Pointer result = resultS;
390 resultS->parent = newParent;
391 resultS->makeUsableElements(*this, newVarState);
392 return result;
393 }
394
395 #endif /* USE_SQUID_ESI == 1 */
396