]>
Commit | Line | Data |
---|---|---|
43ae1d95 | 1 | /* |
77b1029d | 2 | * Copyright (C) 1996-2020 The Squid Software Foundation and contributors |
43ae1d95 | 3 | * |
bbc27441 AJ |
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. | |
43ae1d95 | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 86 ESI processing */ |
10 | ||
582c2af2 FC |
11 | #include "squid.h" |
12 | #include "Debug.h" | |
13 | #include "fatal.h" | |
454e8283 | 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 | ||
f99c2cfe AR |
20 | #include "esi/Attempt.h" |
21 | #include "esi/Except.h" | |
602d9612 A |
22 | #include "esi/Literal.h" |
23 | #include "esi/Sequence.h" | |
43ae1d95 | 24 | |
25 | class esiExcept; | |
26 | ||
43ae1d95 | 27 | esiSequence::~esiSequence () |
28 | { | |
bf8fe701 | 29 | debugs(86, 5, "esiSequence::~esiSequence " << this); |
b56b37cf | 30 | FinishAllElements(elements); // finish if not already done |
43ae1d95 | 31 | } |
32 | ||
a5488e5d | 33 | esiSequence::esiSequence(esiTreeParentPtr aParent, bool incrementalFlag) : |
f53969cc SM |
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) | |
a5488e5d AJ |
43 | { |
44 | memset(&flags, 0, sizeof(flags)); | |
45 | } | |
43ae1d95 | 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 | |
26ac0430 | 85 | * and rendered elements |
43ae1d95 | 86 | */ |
87 | assert (output->next == NULL); | |
137a13ea | 88 | debugs (86,5, "esiSequenceRender: rendering " << processedcount << " elements"); |
43ae1d95 | 89 | |
90 | for (size_t i = 0; i < processedcount; ++i) { | |
91 | elements[i]->render(output); | |
b56b37cf | 92 | FinishAnElement(elements[i], i); |
9837567d | 93 | // TODO: pass an "ESISegment **" ? |
43ae1d95 | 94 | output = output->tail(); |
95 | } | |
96 | ||
b56b37cf AJ |
97 | // prune completed elements |
98 | elements.erase(elements.begin(), elements.begin() + processedcount); | |
43ae1d95 | 99 | processedcount = 0; |
100 | assert (output->next == NULL); | |
101 | } | |
102 | ||
103 | void | |
104 | esiSequence::finish() | |
105 | { | |
bf8fe701 | 106 | debugs(86, 5, "esiSequence::finish: " << this << " is finished"); |
b56b37cf | 107 | FinishAllElements(elements); |
43ae1d95 | 108 | parent = NULL; |
109 | } | |
110 | ||
43ae1d95 | 111 | void |
112 | esiSequence::provideData (ESISegment::Pointer data, ESIElement *source) | |
113 | { | |
114 | ESIElement::Pointer lockthis = this; | |
115 | ||
116 | if (processing) | |
bf8fe701 | 117 | debugs(86, 5, "esiSequence::provideData: " << this << " data provided during processing"); |
26ac0430 | 118 | debugs(86, 5, "esiSequence::provideData " << this << " " << data.getRaw() << " " << source); |
43ae1d95 | 119 | |
43ae1d95 | 120 | /* when data is provided, the element *must* be completed */ |
121 | /* XXX: when the callback model is complete, | |
26ac0430 | 122 | * we can introduce 'finished'. And then this rule can be |
43ae1d95 | 123 | * relaxed |
124 | */ | |
125 | /* find the index */ | |
126 | int index = elementIndex (source); | |
127 | ||
128 | assert (index >= 0); | |
129 | ||
130 | /* remove the current node */ | |
b56b37cf | 131 | FinishAnElement(elements[index], index); |
43ae1d95 | 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())) { | |
fa84c01d | 155 | debugs(86, DBG_CRITICAL, "esiSequenceAdd: misparented Attempt or Except element (section 3.4)"); |
43ae1d95 | 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())) { | |
bf8fe701 | 162 | debugs(86, 5, "esiSequenceAdd: tying Literals " << |
163 | elements[elements.size() - 1].getRaw() << " and " << | |
164 | element.getRaw() << " together"); | |
165 | ||
43ae1d95 | 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); | |
137a13ea | 172 | debugs (86,3, "esiSequenceAdd: Added a new element, elements = " << elements.size()); |
43ae1d95 | 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) { | |
bf8fe701 | 194 | debugs(86, 5, "esiSequence::process: processingResult was " << processingResult << ", increasing to " << tempResult); |
43ae1d95 | 195 | processingResult = tempResult; |
196 | } | |
197 | } | |
198 | ||
199 | esiProcessResult_t | |
200 | esiSequence::processOne(int dovars, size_t index) | |
201 | { | |
137a13ea | 202 | debugs (86,5, "esiSequence::process " << this << " about to process element[" << index << "] " << elements[index].getRaw()); |
43ae1d95 | 203 | |
204 | switch (elements[index]->process(dovars)) { | |
205 | ||
206 | case ESI_PROCESS_COMPLETE: | |
bf8fe701 | 207 | debugs(86, 5, "esiSequenceProcess: " << this << " element " << elements[index].getRaw() << " Processed OK"); |
43ae1d95 | 208 | |
209 | if (index == processedcount) | |
210 | /* another completely ready */ | |
211 | ++processedcount; | |
212 | ||
213 | return ESI_PROCESS_COMPLETE; | |
214 | ||
215 | case ESI_PROCESS_PENDING_WONTFAIL: | |
bf8fe701 | 216 | debugs(86, 5, "esiSequenceProcess: element Processed PENDING OK"); |
43ae1d95 | 217 | |
218 | return ESI_PROCESS_PENDING_WONTFAIL; | |
219 | ||
220 | case ESI_PROCESS_PENDING_MAYFAIL: | |
bf8fe701 | 221 | debugs(86, 5, "eseSequenceProcess: element Processed PENDING UNKNOWN"); |
43ae1d95 | 222 | |
223 | return ESI_PROCESS_PENDING_MAYFAIL; | |
224 | ||
225 | case ESI_PROCESS_FAILED: | |
bf8fe701 | 226 | debugs(86, 5, "esiSequenceProcess: element Processed FAILED"); |
43ae1d95 | 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 | { | |
bf8fe701 | 240 | debugs(86, 5, "esiSequence::process: " << this << " processing"); |
43ae1d95 | 241 | |
242 | if (processing) { | |
bf8fe701 | 243 | debugs(86, 5, "esiSequence::process: " << this << |
244 | " reentry attempt during processing"); | |
43ae1d95 | 245 | } |
246 | ||
247 | /* process as much of the list as we can, stopping only on | |
2f8abb64 | 248 | * failures |
43ae1d95 | 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 | ||
bf8fe701 | 258 | debugs(86, 5, "esiSequence::process: Processing " << this << " with" << |
259 | (dovars ? "" : "out") << " variable processing"); | |
43ae1d95 | 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) { | |
b56b37cf | 272 | FinishAllElements(elements); |
43ae1d95 | 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 | ||
bf8fe701 | 299 | debugs(86, 5, "esiSequence::process: " << this << " completed"); |
43ae1d95 | 300 | |
301 | processing = false; | |
302 | ||
303 | return processingResult; | |
304 | } | |
305 | ||
306 | void | |
924f73bc | 307 | esiSequence::fail (ESIElement *source, char const *anError) |
43ae1d95 | 308 | { |
309 | failed = true; | |
310 | ||
311 | if (processing) { | |
bf8fe701 | 312 | debugs(86, 5, "esiSequence::fail: " << this << " failure callback during processing"); |
43ae1d95 | 313 | return; |
314 | } | |
315 | ||
bf8fe701 | 316 | debugs(86, 5, "esiSequence::fail: " << this << " has failed."); |
924f73bc | 317 | parent->fail (this, anError); |
b56b37cf | 318 | FinishAllElements(elements); |
43ae1d95 | 319 | parent = NULL; |
320 | } | |
321 | ||
a5488e5d | 322 | esiSequence::esiSequence(esiSequence const &old) : |
f53969cc SM |
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) | |
43ae1d95 | 331 | { |
332 | flags.dovars = old.flags.dovars; | |
43ae1d95 | 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 | |
924f73bc | 347 | esiSequence::makeUsableElements(esiSequence const &old, ESIVarState &newVarState) |
43ae1d95 | 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 | { | |
bf8fe701 | 360 | debugs(86, 5, "esiSequence::makeCacheable: Making cachable sequence from " << this); |
43ae1d95 | 361 | assert (processedcount == 0); |
362 | assert (!failed); | |
363 | ||
364 | if (elements.size() == 0) { | |
bf8fe701 | 365 | debugs(86, 5, "esiSequence::makeCacheable: No elements in sequence " << this << ", returning NULL"); |
43ae1d95 | 366 | return NULL; |
367 | } | |
368 | ||
369 | esiSequence * resultS = new esiSequence (*this); | |
370 | ESIElement::Pointer result = resultS; | |
371 | resultS->makeCachableElements(*this); | |
bf8fe701 | 372 | debugs(86, 5, "esiSequence::makeCacheable: " << this << " created " << result.getRaw()); |
43ae1d95 | 373 | return result; |
374 | } | |
375 | ||
376 | ESIElement::Pointer | |
924f73bc | 377 | esiSequence::makeUsable(esiTreeParentPtr newParent, ESIVarState &newVarState) const |
43ae1d95 | 378 | { |
bf8fe701 | 379 | debugs(86, 5, "esiSequence::makeUsable: Creating usable Sequence"); |
43ae1d95 | 380 | assert (processedcount == 0); |
381 | assert (!failed); | |
382 | ||
383 | if (elements.size() == 0) { | |
bf8fe701 | 384 | debugs(86, 5, "esiSequence::makeUsable: No elements in sequence " << this << ", returning NULL"); |
43ae1d95 | 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 | ||
454e8283 | 395 | #endif /* USE_SQUID_ESI == 1 */ |
f53969cc | 396 |