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