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