]> git.ipfire.org Git - thirdparty/squid.git/blob - src/esi/Esi.cc
SourceLayout: esi/, take 1
[thirdparty/squid.git] / src / esi / Esi.cc
1 /*
2 * DEBUG: section 86 ESI processing
3 * AUTHOR: Robert Collins
4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
32 */
33
34 #include "squid.h"
35
36 /* MS Visual Studio Projects are monolithic, so we need the following
37 * #if to exclude the ESI code from compile process when not needed.
38 */
39 #if (USE_SQUID_ESI == 1)
40
41 #include "esi/Esi.h"
42 #include "clientStream.h"
43 #include "client_side_request.h"
44 #include "errorpage.h"
45 #include "esi/Segment.h"
46 #include "esi/Element.h"
47 #include "esi/Context.h"
48 #include "HttpHdrSc.h"
49 #include "HttpHdrScTarget.h"
50 #include "HttpReply.h"
51 #include "esi/Attempt.h"
52 #include "esi/Except.h"
53 #include "client_side.h"
54 #include "esi/VarState.h"
55 #include "esi/Assign.h"
56 #include "esi/Expression.h"
57 #include "HttpRequest.h"
58 #include "MemBuf.h"
59 #include "ip/IpAddress.h"
60
61 /* quick reference on behaviour here.
62 * The ESI specification 1.0 requires the ESI processor to be able to
63 * return an error code at any point in the processing. To that end
64 * we buffer the incoming esi body until we know we will be able to
65 * satisfy the request. At that point we start streaming the queued
66 * data downstream.
67 *
68 */
69
70 class ESIStreamContext;
71
72 /* TODO: split this out into separate files ? */
73 /* Parsing: quick and dirty. ESI files are not valid XML, so a generic
74 * XML parser is not much use. Also we need a push parser not a pull
75 * parser, so LibXML is out.
76 *
77 * Interpreter methods:
78 * Render: May only ever be called after Process returns PROCESS_COMPLETE.
79 * Renders the resulting content into a ESISegment chain.
80 * Process: returns the status of the node.
81 * COMPLETE - processing is complete, rendering may staret
82 * PENDING_WONTFAIL - process is incomplete, but the element *will*
83 * be able to be rendered given time.
84 * PENDING_MAYFAIL - processing is incomplete, and the element *may*
85 * fail to be able to rendered.
86 * FAILED - processing failed, return an error to the client.
87 */
88
89 /*
90 * NOT TODO: esi:inline - out of scope.
91 */
92
93 /* make comparisons with refcount pointers easy */
94 bool operator == (ESIElement const *lhs, ESIElement::Pointer const &rhs)
95 {
96 return lhs == rhs.getRaw();
97 }
98
99 typedef ESIContext::esiKick_t esiKick_t;
100
101
102 /* some core operators */
103
104 /* esiComment */
105
106 struct esiComment : public ESIElement {
107 MEMPROXY_CLASS(esiComment);
108 ~esiComment();
109 esiComment();
110 Pointer makeCacheable() const;
111 Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const;
112
113 void render(ESISegment::Pointer);
114 void finish();
115 };
116
117 MEMPROXY_CLASS_INLINE(esiComment) /**DOCS_NOSEMI*/
118
119 #include "esi/Literal.h"
120
121 #include "esi/Sequence.h"
122
123 #include "esi/Include.h"
124
125 /* esiRemove */
126
127 class esiRemove : public ESIElement
128 {
129
130 public:
131 void *operator new (size_t byteCount);
132 void operator delete (void *address);
133
134 esiRemove();
135 void render(ESISegment::Pointer);
136 bool addElement (ESIElement::Pointer);
137 Pointer makeCacheable() const;
138 Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const;
139 void finish();
140 };
141
142 CBDATA_TYPE (esiRemove);
143 static FREE esiRemoveFree;
144 static ESIElement * esiRemoveNew(void);
145
146
147 /* esiTry */
148
149 struct esiTry : public ESIElement {
150 MEMPROXY_CLASS(esiTry);
151
152 esiTry(esiTreeParentPtr aParent);
153 ~esiTry();
154
155 void render(ESISegment::Pointer);
156 bool addElement (ESIElement::Pointer);
157 void fail(ESIElement *, char const * = NULL);
158 esiProcessResult_t process (int dovars);
159 void provideData (ESISegment::Pointer data, ESIElement * source);
160 Pointer makeCacheable() const;
161 Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const;
162
163 ESIElement::Pointer attempt;
164 ESIElement::Pointer except;
165
166 struct {
167 int attemptok:1; /* the attempt branch process correctly */
168 int exceptok:1; /* likewise */
169 int attemptfailed:1; /* The attempt branch failed */
170 int exceptfailed:1; /* the except branch failed */
171 } flags;
172 void finish();
173
174 private:
175 void notifyParent();
176 esiTreeParentPtr parent;
177 ESISegment::Pointer exceptbuffer;
178 esiTry (esiTry const &);
179 esiProcessResult_t bestAttemptRV() const;
180 };
181
182 MEMPROXY_CLASS_INLINE(esiTry) /**DOCS_NOSEMI*/
183
184 #include "esi/Var.h"
185
186 /* esiChoose */
187
188 struct esiChoose : public ESIElement {
189 MEMPROXY_CLASS(esiChoose);
190
191 esiChoose(esiTreeParentPtr);
192 ~esiChoose();
193
194 void render(ESISegment::Pointer);
195 bool addElement (ESIElement::Pointer);
196 void fail(ESIElement *, char const * = NULL);
197 esiProcessResult_t process (int dovars);
198
199 void provideData (ESISegment::Pointer data, ESIElement *source);
200 void makeCachableElements(esiChoose const &old);
201 void makeUsableElements(esiChoose const &old, ESIVarState &);
202 Pointer makeCacheable() const;
203 Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const;
204 void NULLUnChosen();
205
206 ElementList elements;
207 int chosenelement;
208 ESIElement::Pointer otherwise;
209 void finish();
210
211 private:
212 esiChoose(esiChoose const &);
213 esiTreeParentPtr parent;
214 void checkValidSource (ESIElement::Pointer source) const;
215 void selectElement();
216 };
217
218 MEMPROXY_CLASS_INLINE(esiChoose) /**DOCS_NOSEMI*/
219
220 /* esiWhen */
221
222 struct esiWhen : public esiSequence {
223 MEMPROXY_CLASS(esiWhen);
224 esiWhen(esiTreeParentPtr aParent, int attributes, const char **attr, ESIVarState *);
225 ~esiWhen();
226 Pointer makeCacheable() const;
227 Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const;
228
229 bool testsTrue() const { return testValue;}
230
231 void setTestResult(bool aBool) {testValue = aBool;}
232
233 private:
234 esiWhen (esiWhen const &);
235 bool testValue;
236 char const *unevaluatedExpression;
237 ESIVarState *varState;
238 void evaluate();
239 };
240
241 MEMPROXY_CLASS_INLINE(esiWhen) /**DOCS_NOSEMI*/
242
243 /* esiOtherwise */
244
245 struct esiOtherwise : public esiSequence {
246 // void *operator new (size_t byteCount);
247 // void operator delete (void *address);
248 esiOtherwise(esiTreeParentPtr aParent) : esiSequence (aParent) {}
249 };
250
251 CBDATA_CLASS_INIT(ESIContext);
252
253 void ESIContext::startRead()
254 {
255 assert (!reading_);
256 reading_ = true;
257 }
258
259 void ESIContext::finishRead()
260 {
261 assert (reading_);
262 reading_ = false;
263 }
264
265 bool ESIContext::reading() const
266 {
267 return reading_;
268 }
269
270
271 ESIStreamContext::ESIStreamContext() : finished(false), include (NULL), localbuffer (new ESISegment), buffer (NULL)
272 {}
273
274 /* Local functions */
275 /* ESIContext */
276 static ESIContext *ESIContextNew(HttpReply *, clientStreamNode *, ClientHttpRequest *);
277
278
279 void *
280 ESIContext::operator new(size_t byteCount)
281 {
282 assert (byteCount == sizeof (ESIContext));
283 CBDATA_INIT_TYPE(ESIContext);
284 ESIContext *result = cbdataAlloc(ESIContext);
285 return result;
286 }
287
288 void
289 ESIContext::operator delete (void *address)
290 {
291 ESIContext *t = static_cast<ESIContext *>(address);
292 cbdataFree(t);
293 }
294
295 void
296 ESIContext::setError()
297 {
298 errorpage = ERR_ESI;
299 errorstatus = HTTP_INTERNAL_SERVER_ERROR;
300 flags.error = 1;
301 }
302
303 void
304 ESIContext::appendOutboundData(ESISegment::Pointer theData)
305 {
306 if (!outbound.getRaw()) {
307 outbound = theData;
308 outboundtail = outbound;
309 } else {
310 assert (outboundtail->next.getRaw() == NULL);
311 outboundtail->next = theData;
312 }
313
314 fixupOutboundTail();
315 debugs(86, 9, "ESIContext::appendOutboundData: outbound " << outbound.getRaw());
316 }
317
318 void
319 ESIContext::provideData (ESISegment::Pointer theData, ESIElement * source)
320 {
321 debugs(86, 5, "ESIContext::provideData: " << this << " " << theData.getRaw() << " " << source);
322 /* No callbacks permitted after finish() called on the tree */
323 assert (tree.getRaw());
324 assert (source == tree);
325 appendOutboundData(theData);
326 trimBlanks();
327
328 if (!processing)
329 send();
330 }
331
332 void
333 ESIContext::fail (ESIElement * source, char const *anError)
334 {
335 setError();
336 setErrorMessage (anError);
337 fail ();
338 send ();
339 }
340
341 void
342 ESIContext::fixupOutboundTail()
343 {
344 /* TODO: fixup thisNode outboundtail dross a little */
345
346 if (outboundtail.getRaw())
347 outboundtail = outboundtail->tail();
348 }
349
350 esiKick_t
351 ESIContext::kick ()
352 {
353 assert (this);
354
355 if (flags.kicked) {
356 debugs(86, 5, "esiKick: Re-entered whilst in progress");
357 // return ESI_KICK_INPROGRESS;
358 } else
359 ++flags.kicked;
360
361 if (flags.detached)
362 /* we've been detached from - we can't do anything more */
363 return ESI_KICK_FAILED;
364
365 /* Something has occured. Process any remaining nodes */
366 if (!flags.finished)
367 /* Process some of our data */
368 switch (process ()) {
369
370 case ESI_PROCESS_COMPLETE:
371 debugs(86, 5, "esiKick: esiProcess OK");
372 break;
373
374 case ESI_PROCESS_PENDING_WONTFAIL:
375 debugs(86, 5, "esiKick: esiProcess PENDING OK");
376 break;
377
378 case ESI_PROCESS_PENDING_MAYFAIL:
379 debugs(86, 5, "esiKick: esiProcess PENDING UNKNOWN");
380 break;
381
382 case ESI_PROCESS_FAILED:
383 debugs(86, 2, "esiKick: esiProcess " << this << " FAILED");
384 /* this can not happen - processing can't fail until we have data,
385 * and when we come here we have sent data to the client
386 */
387
388 if (pos == 0)
389 fail ();
390
391 --flags.kicked;
392
393 return ESI_KICK_FAILED;
394 }
395
396 /* Render if we can to get maximal sent data */
397 assert (tree.getRaw() || flags.error);
398
399 if (!flags.finished && !outbound.getRaw()) {
400 outboundtail = new ESISegment;
401 outbound = outboundtail;
402 }
403
404 if (!flags.error && !flags.finished)
405 tree->render(outboundtail);
406
407 if (!flags.finished)
408 fixupOutboundTail();
409
410 /* Is there data to send? */
411 if (send ()) {
412 /* some data was sent. we're finished until the next read */
413 --flags.kicked;
414 return ESI_KICK_SENT;
415 }
416
417 --flags.kicked;
418 /* nothing to send */
419 return flags.error ? ESI_KICK_FAILED : ESI_KICK_PENDING;
420 }
421
422 /* request from downstream for more data
423 */
424 void
425 esiStreamRead (clientStreamNode *thisNode, ClientHttpRequest *http)
426 {
427 clientStreamNode *next;
428 /* Test preconditions */
429 assert (thisNode != NULL);
430 assert (cbdataReferenceValid (thisNode));
431 /* we are not in the chain until ESI is detected on a data callback */
432 assert (thisNode->node.prev != NULL);
433 assert (thisNode->node.next != NULL);
434
435 ESIContext::Pointer context = dynamic_cast<ESIContext *>(thisNode->data.getRaw());
436 assert (context.getRaw() != NULL);
437
438 if (context->flags.passthrough) {
439 /* passthru mode - read into supplied buffers */
440 next = thisNode->next();
441 clientStreamRead (thisNode, http, next->readBuffer);
442 return;
443 }
444
445 context->flags.clientwantsdata = 1;
446 debugs(86, 5, "esiStreamRead: Client now wants data");
447
448 /* Ok, not passing through */
449
450 switch (context->kick ()) {
451
452 case ESIContext::ESI_KICK_FAILED:
453 /* this can not happen - processing can't fail until we have data,
454 * and when we come here we have sent data to the client
455 */
456
457 case ESIContext::ESI_KICK_SENT:
458
459 case ESIContext::ESI_KICK_INPROGRESS:
460 return;
461
462 case ESIContext::ESI_KICK_PENDING:
463 break;
464 }
465
466 /* Nothing to send */
467
468 if (context->flags.oktosend && (context->flags.finishedtemplate
469 || context->cachedASTInUse) &&
470 ! context->flags.finished) {
471 /* we've started sending, finished reading, but not finished
472 * processing. stop here, a callback will resume the stream
473 * flow
474 */
475 debugs(86, 5, "esiStreamRead: Waiting for async resume of esi processing");
476 return;
477 }
478
479 if (context->flags.oktosend && context->flags.finished && context->outbound.getRaw()) {
480 debugs(86, 5, "all processing complete, but outbound data still buffered");
481 assert (!context->flags.clientwantsdata);
482 /* client MUST be processing the last reply */
483 return;
484 }
485
486
487 if (context->flags.oktosend && context->flags.finished) {
488 StoreIOBuffer tempBuffer;
489 assert (!context->outbound.getRaw());
490 /* We've finished processing, and there is no more data buffered */
491 debugs(86, 5, "Telling recipient EOF on READ");
492 clientStreamCallback (thisNode, http, NULL, tempBuffer);
493 return;
494 }
495
496 if (context->reading())
497 return;
498
499 /* no data that is ready to send, and still reading? well, lets get some */
500 /* secure a buffer */
501 if (!context->incoming.getRaw()) {
502 /* create a new buffer segment */
503 context->buffered = new ESISegment;
504 context->incoming = context->buffered;
505 }
506
507 assert (context->incoming.getRaw() && context->incoming->len != HTTP_REQBUF_SZ);
508 {
509 StoreIOBuffer tempBuffer;
510 tempBuffer.offset = context->readpos;
511 tempBuffer.length = context->incoming->len - HTTP_REQBUF_SZ;
512 tempBuffer.data = &context->incoming->buf[context->incoming->len];
513 context->startRead();
514 clientStreamRead (thisNode, http, tempBuffer);
515 }
516 }
517
518 clientStream_status_t
519 esiStreamStatus (clientStreamNode *thisNode, ClientHttpRequest *http)
520 {
521 /* Test preconditions */
522 assert (thisNode != NULL);
523 assert (cbdataReferenceValid (thisNode));
524 /* we are not in the chain until ESI is detected on a data callback */
525 assert (thisNode->node.prev != NULL);
526 assert (thisNode->node.next != NULL);
527
528 ESIContext::Pointer context = dynamic_cast<ESIContext *>(thisNode->data.getRaw());
529 assert (context.getRaw() != NULL);
530
531 if (context->flags.passthrough)
532 return clientStreamStatus (thisNode, http);
533
534 if (context->flags.oktosend && context->flags.finished &&
535 !(context->outbound.getRaw() && context->outbound_offset < context->outbound->len)) {
536 debugs(86, 5, "Telling recipient EOF on STATUS");
537 return STREAM_UNPLANNED_COMPLETE; /* we don't know lengths in advance */
538 }
539
540 /* ?? RC: we can't be aborted / fail ? */
541 return STREAM_NONE;
542 }
543
544 static int
545 esiAlwaysPassthrough(http_status sline)
546 {
547 int result;
548
549 switch (sline) {
550
551 case HTTP_CONTINUE: /* Should never reach us... but squid needs to alter to accomodate this */
552
553 case HTTP_SWITCHING_PROTOCOLS: /* Ditto */
554
555 case HTTP_PROCESSING: /* Unknown - some extension */
556
557 case HTTP_NO_CONTENT: /* no body, no esi */
558
559 case HTTP_NOT_MODIFIED: /* ESI does not affect assembled page headers, so 304s are valid */
560 result = 1;
561 /* unreached */
562 break;
563
564 default:
565 result = 0;
566 }
567
568 return result;
569 }
570
571 void
572 ESIContext::trimBlanks()
573 {
574 /* trim leading empty buffers ? */
575
576 while (outbound.getRaw() && outbound->next.getRaw() && !outbound->len) {
577 debugs(86, 5, "ESIContext::trimBlanks: " << this <<
578 " skipping segment " << outbound.getRaw());
579 outbound = outbound->next;
580 }
581
582 if (outboundtail.getRaw())
583 assert (outbound.getRaw());
584 }
585
586 /* Send data downstream
587 * Returns 0 if nothing was sent. Non-zero if data was sent.
588 */
589 size_t
590 ESIContext::send ()
591 {
592 debugs(86, 5, "ESIContext::send: this=" << this);
593 /* send any processed data */
594
595 trimBlanks();
596
597 if (!flags.clientwantsdata) {
598 debugs(86, 5, "ESIContext::send: Client does not want data - not sending anything");
599 return 0;
600 }
601
602 if (tree.getRaw() && tree->mayFail()) {
603 debugs(86, 5, "ESIContext::send: Tree may fail. Not sending.");
604 return 0;
605 } else
606 flags.oktosend = 1;
607
608 #if 0
609
610 if (!flags.oktosend) {
611
612 fatal("ESIContext::send: Not OK to send.\n");
613 return 0;
614 }
615
616 #endif
617
618 if (!(rep || (outbound.getRaw() &&
619 outbound->len && (outbound_offset <= outbound->len)))) {
620 debugs(86, 5, "ESIContext::send: Nothing to send.");
621 return 0;
622 }
623
624 debugs(86, 5, "ESIContext::send: Sending something...");
625 /* Yes! Send it without asking for more upstream */
626 /* memcopying because the client provided the buffer */
627 /* TODO: skip data until pos == next->readoff; */
628 assert (thisNode->data == this);
629 clientStreamNode *next = thisNode->next();
630 ESIContext *templock = cbdataReference (this);
631 size_t len = 0;
632
633 if (outbound.getRaw())
634 len = min (next->readBuffer.length, outbound->len - outbound_offset);
635
636 /* prevent corruption on range requests, even though we don't support them yet */
637 assert (pos == next->readBuffer.offset);
638
639 /* We must send data or a reply */
640 assert (len != 0 || rep != NULL);
641
642 if (len) {
643 xmemcpy (next->readBuffer.data, &outbound->buf[outbound_offset], len);
644
645 if (len + outbound_offset == outbound->len) {
646 ESISegment::Pointer temp = outbound->next;
647 /* remove the used buffer */
648 outbound_offset = 0;
649 outbound = temp;
650 }
651
652 pos += len;
653
654 if (!outbound.getRaw())
655 outboundtail = NULL;
656
657 trimBlanks();
658 }
659
660 flags.clientwantsdata = 0;
661 debugs(86, 5, "ESIContext::send: this=" << this << " Client no longer wants data ");
662 /* Deal with re-entrancy */
663 HttpReply *temprep = rep;
664 rep = NULL; /* freed downstream */
665
666 if (temprep && varState)
667 varState->buildVary (temprep);
668
669 {
670 StoreIOBuffer tempBuffer;
671 tempBuffer.length = len;
672 tempBuffer.offset = pos - len;
673 tempBuffer.data = next->readBuffer.data;
674 clientStreamCallback (thisNode, http, temprep, tempBuffer);
675 }
676
677 if (len == 0)
678 len = 1; /* tell the caller we sent something (because we sent headers */
679
680
681 cbdataReferenceDone (templock);
682
683 debugs (86,5,"ESIContext::send: this=" << this << " sent " << len);
684
685 return len;
686 }
687
688 void
689 ESIContext::finishChildren()
690 {
691 if (tree.getRaw())
692 tree->finish();
693
694 tree = NULL;
695 }
696
697 /* Detach event from a client Stream */
698 void
699 esiStreamDetach (clientStreamNode *thisNode, ClientHttpRequest *http)
700 {
701 /* if we have pending callbacks, tell them we're done. */
702 /* test preconditions */
703 assert (thisNode != NULL);
704 assert (cbdataReferenceValid (thisNode));
705 ESIContext::Pointer context = dynamic_cast<ESIContext *>(thisNode->data.getRaw());
706 assert (context.getRaw() != NULL);
707 /* detach from the stream */
708 clientStreamDetach (thisNode,http);
709 /* if we have pending callbacks (from subincludes), tell them we're done. */
710 context->thisNode = NULL;
711 context->flags.detached = 1;
712 context->finishChildren();
713 /* HACK for parser stack not being emptied */
714 context->parserState.stack[0] = NULL;
715 /* allow refcount logic to trigger */
716 context->cbdataLocker = NULL;
717 }
718
719 /* Process incoming data for ESI tags */
720 /* ESI TODO: Long term: we should have a framework to parse html/xml and
721 * callback to a set of processors like thisNode, to prevent multiple parsing
722 * overhead. More thoughts on thisNode: We have to parse multiple times, because
723 * the output of one processor may create a very different tree. What we could
724 * do is something like DOM and pass that down to a final renderer. This is
725 * getting into web server territory though...
726 *
727 * Preconditions:
728 * This is not the last node in the stream.
729 * ESI processing has been enabled.
730 * There is context data or a reply structure
731 */
732 void
733 esiProcessStream (clientStreamNode *thisNode, ClientHttpRequest *http, HttpReply *rep, StoreIOBuffer receivedData)
734 {
735 /* test preconditions */
736 assert (thisNode != NULL);
737 /* ESI TODO: handle thisNode rather than asserting - it should only ever
738 * happen if we cause an abort and the callback chain
739 * loops back to here, so we can simply return. However, that itself
740 * shouldn't happen, so it stays as an assert for now. */
741 assert (cbdataReferenceValid (thisNode));
742 /*
743 * if data is NULL thisNode is the first entrance. If rep is also NULL,
744 * something is wrong.
745 * */
746 assert (thisNode->data.getRaw() != NULL || rep);
747 assert (thisNode->node.next != NULL);
748
749 if (!thisNode->data.getRaw())
750 /* setup ESI context from reply headers */
751 thisNode->data = ESIContextNew(rep, thisNode, http);
752
753 ESIContext::Pointer context = dynamic_cast<ESIContext *>(thisNode->data.getRaw());
754
755 assert (context.getRaw() != NULL);
756
757 context->finishRead();
758
759 /* Skipping all ESI processing. All remaining data gets untouched.
760 * Mainly used when an error or other non-ESI processable entity
761 * has been detected to prevent ESI processing the error body
762 */
763 if (context->flags.passthrough) {
764 clientStreamCallback (thisNode, http, rep, receivedData);
765 return;
766 }
767
768 debugs(86, 3, "esiProcessStream: Processing thisNode " << thisNode <<
769 " context " << context.getRaw() << " offset " <<
770 (int) receivedData.offset << " length " <<
771 (unsigned int)receivedData.length);
772
773 /* once we finish the template, we *cannot* return here */
774 assert (!context->flags.finishedtemplate);
775 assert (!context->cachedASTInUse);
776
777 /* Can we generate any data ?*/
778
779 if (receivedData.data) {
780 /* Increase our buffer area with incoming data */
781 assert (receivedData.length <= HTTP_REQBUF_SZ);
782 assert (thisNode->readBuffer.offset == receivedData.offset);
783 debugs (86,5, "esiProcessStream found " << receivedData.length << " bytes of body data at offset " << receivedData.offset);
784 /* secure the data for later use */
785
786 if (!context->incoming.getRaw()) {
787 /* create a new buffer segment */
788 debugs(86, 5, "esiProcessStream: Setting up incoming buffer");
789 context->buffered = new ESISegment;
790 context->incoming = context->buffered;
791 }
792
793 if (receivedData.data != &context->incoming->buf[context->incoming->len]) {
794 /* We have to copy the data out because we didn't supply thisNode buffer */
795 size_t space = HTTP_REQBUF_SZ - context->incoming->len;
796 size_t len = min (space, receivedData.length);
797 debugs(86, 5, "Copying data from " << receivedData.data << " to " <<
798 &context->incoming->buf[context->incoming->len] <<
799 " because our buffer was not used");
800
801 xmemcpy (&context->incoming->buf[context->incoming->len], receivedData.data, len);
802 context->incoming->len += len;
803
804 if (context->incoming->len == HTTP_REQBUF_SZ) {
805 /* append another buffer */
806 context->incoming->next = new ESISegment;
807 context->incoming = context->incoming->next;
808 }
809
810 if (len != receivedData.length) {
811 /* capture the remnants */
812 xmemcpy (context->incoming->buf, &receivedData.data[len], receivedData.length - len);
813 context->incoming->len = receivedData.length - len;
814 }
815
816 /* and note where we are up to */
817 context->readpos += receivedData.length;
818 } else {
819 /* update our position counters, and if needed assign a new buffer */
820 context->incoming->len += receivedData.length;
821 assert (context->incoming->len <= HTTP_REQBUF_SZ);
822
823 if (context->incoming->len > HTTP_REQBUF_SZ * 3 / 4) {
824 /* allocate a new buffer - to stop us asking for ridiculously small amounts */
825 context->incoming->next = new ESISegment;
826 context->incoming = context->incoming->next;
827 }
828
829 context->readpos += receivedData.length;
830 }
831 }
832
833 /* EOF / Read error / aborted entry */
834 if (rep == NULL && receivedData.data == NULL && receivedData.length == 0 && !context->flags.finishedtemplate) {
835 /* TODO: get stream status to test the entry for aborts */
836 /* else flush the esi processor */
837 debugs(86, 5, "esiProcess: " << context.getRaw() << " Finished reading upstream data");
838 /* This is correct */
839 context->flags.finishedtemplate = 1;
840 }
841
842 switch (context->kick()) {
843
844 case ESIContext::ESI_KICK_FAILED:
845 /* thisNode can not happen - processing can't fail until we have data,
846 * and when we come here we have sent data to the client
847 */
848 return;
849
850 case ESIContext::ESI_KICK_SENT:
851
852 case ESIContext::ESI_KICK_INPROGRESS:
853 return;
854
855 case ESIContext::ESI_KICK_PENDING:
856 break;
857 }
858
859 /* ok.. no data sent, try to pull more data in from upstream.
860 * FIXME: Don't try thisNode if we have finished reading the template
861 */
862 if (!context->flags.finishedtemplate && !context->reading()
863 && !context->cachedASTInUse) {
864 StoreIOBuffer tempBuffer;
865 assert (context->incoming.getRaw() && context->incoming->len < HTTP_REQBUF_SZ);
866 tempBuffer.offset = context->readpos;
867 tempBuffer.length = HTTP_REQBUF_SZ - context->incoming->len;
868 tempBuffer.data = &context->incoming->buf[context->incoming->len];
869 context->startRead();
870 clientStreamRead (thisNode, http, tempBuffer);
871 return;
872 }
873
874 debugs(86, 3, "esiProcessStream: no data to send, no data to read, awaiting a callback");
875 }
876
877 ESIContext::~ESIContext()
878 {
879 freeResources ();
880 /* Not freed by freeresources because esi::fail needs it */
881 safe_free (errormessage);
882 debugs(86, 3, "ESIContext::~ESIContext: Freed " << this);
883 }
884
885 ESIContext *
886 ESIContextNew (HttpReply *rep, clientStreamNode *thisNode, ClientHttpRequest *http)
887 {
888 assert (rep);
889 ESIContext *rv = new ESIContext;
890 rv->rep = rep;
891 rv->cbdataLocker = rv;
892
893 if (esiAlwaysPassthrough(rep->sline.status)) {
894 rv->flags.passthrough = 1;
895 } else {
896 /* remove specific headers for ESI to prevent
897 * downstream cache confusion */
898 HttpHeader *hdr = &rep->header;
899 hdr->delById(HDR_ACCEPT_RANGES);
900 hdr->delById(HDR_ETAG);
901 hdr->delById(HDR_CONTENT_LENGTH);
902 hdr->delById(HDR_CONTENT_MD5);
903 rv->tree = new esiSequence (rv, true);
904 rv->thisNode = thisNode;
905 rv->http = http;
906 rv->flags.clientwantsdata = 1;
907 rv->varState = new ESIVarState (&http->request->header, http->uri);
908 debugs(86, 5, "ESIContextNew: Client wants data (always created during reply cycle");
909 }
910
911 debugs(86, 5, "ESIContextNew: Create context " << rv);
912 return rv;
913 }
914
915 ESIElement::ESIElementType_t
916 ESIElement::IdentifyElement (const char *el)
917 {
918 int offset = 0;
919 assert (el);
920
921 if (strlen (el) < 5)
922 return ESI_ELEMENT_NONE;
923
924 if (!strncmp (el, "esi:", 4))
925 offset = 4;
926 else if (!strncmp (el, "http://www.edge-delivery.org/esi/1.0|", 37))
927 offset = 37;
928 else
929 return ESI_ELEMENT_NONE;
930
931 if (!strncmp (el + offset, "otherwise", 9))
932 return ESI_ELEMENT_OTHERWISE;
933
934 if (!strncmp (el + offset, "comment", 7))
935 return ESI_ELEMENT_COMMENT;
936
937 if (!strncmp (el + offset, "include", 7))
938 return ESI_ELEMENT_INCLUDE;
939
940 if (!strncmp (el + offset, "attempt", 7))
941 return ESI_ELEMENT_ATTEMPT;
942
943 if (!strncmp (el + offset, "assign", 6))
944 return ESI_ELEMENT_ASSIGN;
945
946 if (!strncmp (el + offset, "remove", 6))
947 return ESI_ELEMENT_REMOVE;
948
949 if (!strncmp (el + offset, "except", 6))
950 return ESI_ELEMENT_EXCEPT;
951
952 if (!strncmp (el + offset, "choose", 6))
953 return ESI_ELEMENT_CHOOSE;
954
955 if (!strncmp (el + offset, "vars", 4))
956 return ESI_ELEMENT_VARS;
957
958 if (!strncmp (el + offset, "when", 4))
959 return ESI_ELEMENT_WHEN;
960
961 if (!strncmp (el + offset, "try", 3))
962 return ESI_ELEMENT_TRY;
963
964 return ESI_ELEMENT_NONE;
965 }
966
967 ESIElement::Pointer
968 ESIContext::ParserState::top()
969 {
970 return stack[stackdepth-1];
971 }
972
973 ESIContext::ParserState::ParserState() : inited_ (false)
974 {}
975
976 bool
977 ESIContext::ParserState::inited() const
978 {
979 return inited_;
980 }
981
982 void
983 ESIContext::addStackElement (ESIElement::Pointer element)
984 {
985 /* Put on the stack to allow skipping of 'invalid' markup */
986 assert (parserState.stackdepth <11);
987 assert (!failed());
988 debugs(86, 5, "ESIContext::addStackElement: About to add ESI Node " << element.getRaw());
989
990 if (!parserState.top()->addElement(element)) {
991 debugs(86, 1, "ESIContext::addStackElement: failed to add esi node, probable error in ESI template");
992 flags.error = 1;
993 } else {
994 /* added ok, push onto the stack */
995 parserState.stack[parserState.stackdepth++] = element;
996 }
997 }
998
999 void
1000 ESIContext::start(const char *el, const char **attr, size_t attrCount)
1001 {
1002 int i;
1003 unsigned int ellen = strlen (el);
1004 char localbuf [HTTP_REQBUF_SZ];
1005 ESIElement::Pointer element;
1006 int specifiedattcount = attrCount * 2;
1007 char *pos;
1008 assert (ellen < sizeof (localbuf)); /* prevent unexpected overruns. */
1009
1010 debugs(86, 5, "ESIContext::Start: element '" << el << "' with " << specifiedattcount << " tags");
1011
1012 if (failed())
1013 /* waiting for expat to finish the buffer we gave it */
1014 return;
1015
1016 switch (ESIElement::IdentifyElement (el)) {
1017
1018 case ESIElement::ESI_ELEMENT_NONE:
1019 /* Spit out elements we aren't interested in */
1020 localbuf[0] = '<';
1021 localbuf[1] = '\0';
1022 assert (xstrncpy (&localbuf[1], el, sizeof(localbuf) - 2));
1023 pos = localbuf + strlen (localbuf);
1024
1025 for (i = 0; i < specifiedattcount && attr[i]; i += 2) {
1026 *pos++ = ' ';
1027 /* TODO: handle thisNode gracefully */
1028 assert (xstrncpy (pos, attr[i], sizeof(localbuf) + (pos - localbuf)));
1029 pos += strlen (pos);
1030 *pos++ = '=';
1031 *pos++ = '\'';
1032 assert (xstrncpy (pos, attr[i + 1], sizeof(localbuf) + (pos - localbuf)));
1033 pos += strlen (pos);
1034 *pos++ = '\'';
1035 }
1036
1037 *pos++ = '>';
1038 *pos = '\0';
1039
1040 addLiteral (localbuf, pos - localbuf);
1041 debugs(86, 5, "esi stack depth " << parserState.stackdepth);
1042 return;
1043 break;
1044
1045 case ESIElement::ESI_ELEMENT_COMMENT:
1046 /* Put on the stack to allow skipping of 'invalid' markup */
1047 element = new esiComment ();
1048 break;
1049
1050 case ESIElement::ESI_ELEMENT_INCLUDE:
1051 /* Put on the stack to allow skipping of 'invalid' markup */
1052 element = new ESIInclude (parserState.top().getRaw(), specifiedattcount, attr, this);
1053 break;
1054
1055 case ESIElement::ESI_ELEMENT_REMOVE:
1056 /* Put on the stack to allow skipping of 'invalid' markup */
1057 element = esiRemoveNew ();
1058 break;
1059
1060 case ESIElement::ESI_ELEMENT_TRY:
1061 /* Put on the stack to allow skipping of 'invalid' markup */
1062 element = new esiTry (parserState.top().getRaw());
1063 break;
1064
1065 case ESIElement::ESI_ELEMENT_ATTEMPT:
1066 /* Put on the stack to allow skipping of 'invalid' markup */
1067 element = new esiAttempt (parserState.top().getRaw());
1068 break;
1069
1070 case ESIElement::ESI_ELEMENT_EXCEPT:
1071 /* Put on the stack to allow skipping of 'invalid' markup */
1072 element = new esiExcept (parserState.top().getRaw());
1073 break;
1074
1075 case ESIElement::ESI_ELEMENT_VARS:
1076 /* Put on the stack to allow skipping of 'invalid' markup */
1077 element = new ESIVar (parserState.top().getRaw());
1078 break;
1079
1080 case ESIElement::ESI_ELEMENT_CHOOSE:
1081 /* Put on the stack to allow skipping of 'invalid' markup */
1082 element = new esiChoose (parserState.top().getRaw());
1083 break;
1084
1085 case ESIElement::ESI_ELEMENT_WHEN:
1086 /* Put on the stack to allow skipping of 'invalid' markup */
1087 element = new esiWhen (parserState.top().getRaw(), specifiedattcount, attr, varState);
1088 break;
1089
1090 case ESIElement::ESI_ELEMENT_OTHERWISE:
1091 /* Put on the stack to allow skipping of 'invalid' markup */
1092 element = new esiOtherwise (parserState.top().getRaw());
1093 break;
1094
1095 case ESIElement::ESI_ELEMENT_ASSIGN:
1096 /* Put on the stack to allow skipping of 'invalid' markup */
1097 element = new ESIAssign (parserState.top().getRaw(), specifiedattcount, attr, this);
1098 break;
1099 }
1100
1101 addStackElement(element);
1102
1103 debugs(86, 5, "esi stack depth " << parserState.stackdepth);
1104
1105 } /* End of start handler */
1106
1107 void
1108 ESIContext::end(const char *el)
1109 {
1110 unsigned int ellen = strlen (el);
1111 char localbuf [HTTP_REQBUF_SZ];
1112 char *pos;
1113
1114 if (flags.error)
1115 /* waiting for expat to finish the buffer we gave it */
1116 return;
1117
1118 switch (ESIElement::IdentifyElement (el)) {
1119
1120 case ESIElement::ESI_ELEMENT_NONE:
1121 assert (ellen < sizeof (localbuf)); /* prevent unexpected overruns. */
1122 /* Add elements we aren't interested in */
1123 localbuf[0] = '<';
1124 localbuf[1] = '/';
1125 assert (xstrncpy (&localbuf[2], el, sizeof(localbuf) - 3));
1126 pos = localbuf + strlen (localbuf);
1127 *pos++ = '>';
1128 *pos = '\0';
1129 addLiteral (localbuf, pos - localbuf);
1130 break;
1131
1132 case ESIElement::ESI_ELEMENT_COMMENT:
1133
1134 case ESIElement::ESI_ELEMENT_INCLUDE:
1135
1136 case ESIElement::ESI_ELEMENT_REMOVE:
1137
1138 case ESIElement::ESI_ELEMENT_TRY:
1139
1140 case ESIElement::ESI_ELEMENT_ATTEMPT:
1141
1142 case ESIElement::ESI_ELEMENT_EXCEPT:
1143
1144 case ESIElement::ESI_ELEMENT_VARS:
1145
1146 case ESIElement::ESI_ELEMENT_CHOOSE:
1147
1148 case ESIElement::ESI_ELEMENT_WHEN:
1149
1150 case ESIElement::ESI_ELEMENT_OTHERWISE:
1151
1152 case ESIElement::ESI_ELEMENT_ASSIGN:
1153 /* pop of the stack */
1154 parserState.stack[--parserState.stackdepth] = NULL;
1155 break;
1156 }
1157 } /* End of end handler */
1158
1159 void
1160 ESIContext::parserDefault (const char *s, int len)
1161 {
1162 if (failed())
1163 return;
1164
1165 /* handle any skipped data */
1166 addLiteral (s, len);
1167 }
1168
1169 void
1170 ESIContext::parserComment (const char *s)
1171 {
1172 if (failed())
1173 return;
1174
1175 if (!strncmp(s, "esi",3)) {
1176 debugs(86, 5, "ESIContext::parserComment: ESI <!-- block encountered");
1177 ESIParser::Pointer tempParser = ESIParser::NewParser (this);
1178
1179 /* wrap the comment in some tags */
1180
1181 if (!tempParser->parse("<div>", 5,0) ||
1182 !tempParser->parse(s + 3, strlen(s) - 3, 0) ||
1183 !tempParser->parse("</div>",6,1)) {
1184 debugs(86, 0, "ESIContext::parserComment: Parsing fragment '" << s + 3 << "' failed.");
1185 setError();
1186 char tempstr[1024];
1187 snprintf(tempstr, 1023, "ESIContext::parserComment: Parse error at line %ld:\n%s\n",
1188 tempParser->lineNumber(),
1189 tempParser->errorString());
1190 debugs(86, 0, "" << tempstr << "");
1191
1192 setErrorMessage(tempstr);
1193 }
1194
1195 debugs(86, 5, "ESIContext::parserComment: ESI <!-- block parsed");
1196 return;
1197 } else {
1198 char localbuf [HTTP_REQBUF_SZ];
1199 unsigned int len;
1200 debugs(86, 5, "ESIContext::parserComment: Regenerating comment block");
1201 len = strlen (s);
1202
1203 if (len > sizeof (localbuf) - 9) {
1204 debugs(86, 0, "ESIContext::parserComment: Truncating long comment");
1205 len = sizeof (localbuf) - 9;
1206 }
1207
1208 xstrncpy(localbuf, "<!--", 5);
1209 xstrncpy(localbuf + 4, s, len + 1);
1210 xstrncpy(localbuf + 4 + len, "-->", 4);
1211 addLiteral (localbuf,len + 7);
1212 }
1213 }
1214
1215 void
1216 ESIContext::addLiteral (const char *s, int len)
1217 {
1218 /* handle any skipped data */
1219 assert (len);
1220 debugs(86, 5, "literal length is " << len);
1221 /* give a literal to the current element */
1222 assert (parserState.stackdepth <11);
1223 ESIElement::Pointer element (new esiLiteral (this, s, len));
1224
1225 if (!parserState.top()->addElement(element)) {
1226 debugs(86, 1, "ESIContext::addLiteral: failed to add esi node, probable error in ESI template");
1227 flags.error = 1;
1228 }
1229 }
1230
1231 void
1232 ESIContext::ParserState::init(ESIParserClient *userData)
1233 {
1234 theParser = ESIParser::NewParser (userData);
1235 inited_ = true;
1236 }
1237
1238 void
1239 ESIContext::parseOneBuffer()
1240 {
1241 assert (buffered.getRaw());
1242
1243 debugs (86,9,"ESIContext::parseOneBuffer: " << buffered->len << " bytes");
1244 bool lastBlock = buffered->next.getRaw() == NULL && flags.finishedtemplate ? true : false;
1245
1246 if (! parserState.theParser->parse(buffered->buf, buffered->len, lastBlock)) {
1247 setError();
1248 char tempstr[1024];
1249 snprintf (tempstr, 1023, "esiProcess: Parse error at line %ld:\n%s\n",
1250 parserState.theParser->lineNumber(),
1251 parserState.theParser->errorString());
1252 debugs(86, 0, "" << tempstr << "");
1253
1254 setErrorMessage(tempstr);
1255
1256 assert (flags.error);
1257
1258 return;
1259 }
1260
1261 if (flags.error) {
1262 setError();
1263 return;
1264 }
1265
1266 ESISegment::Pointer temp = buffered;
1267 buffered = temp->next;
1268 }
1269
1270 void
1271 ESIContext::parse()
1272 {
1273 if (!parserState.stackdepth) {
1274 debugs(86, 5, "empty parser stack, inserting the top level node");
1275 assert (tree.getRaw());
1276 parserState.stack[parserState.stackdepth++] = tree;
1277 }
1278
1279 if (rep && !parserState.inited())
1280 parserState.init(this);
1281
1282 /* we have data */
1283 if (buffered.getRaw()) {
1284 parserState.parsing = 1;
1285 /* we don't keep any data around */
1286
1287 PROF_start(esiParsing);
1288
1289 while (buffered.getRaw() && !flags.error)
1290 parseOneBuffer();
1291
1292 PROF_stop(esiParsing);
1293
1294 /* Tel the read code to allocate a new buffer */
1295 incoming = NULL;
1296
1297 parserState.parsing = 0;
1298 }
1299 }
1300
1301 esiProcessResult_t
1302 ESIContext::process ()
1303 {
1304 /* parsing:
1305 * read through buffered, skipping plain text, and skipping any
1306 * <...> entry that is not an <esi: entry.
1307 * when it's found, hand an esiLiteral of the preceeding data to our current
1308 * context
1309 */
1310
1311 if (parserState.parsing) {
1312 /* in middle of parsing - finish here */
1313 return ESI_PROCESS_PENDING_MAYFAIL;
1314 }
1315
1316 assert (flags.finished == 0);
1317
1318 assert (!flags.error);
1319
1320 if (!hasCachedAST())
1321 parse();
1322 else if (!flags.finishedtemplate)
1323 getCachedAST();
1324
1325 if (flags.error) {
1326 debugs(86, 5, "ESIContext::process: Parsing failed");
1327 finishChildren ();
1328 parserState.popAll();
1329 return ESI_PROCESS_FAILED;
1330 }
1331
1332 if (!flags.finishedtemplate && !incoming.getRaw() && !cachedASTInUse) {
1333 buffered = new ESISegment;
1334 incoming = buffered;
1335 }
1336
1337 if (!flags.finishedtemplate && !cachedASTInUse) {
1338 return ESI_PROCESS_PENDING_MAYFAIL;
1339 }
1340
1341 assert (flags.finishedtemplate || cachedASTInUse);
1342 updateCachedAST();
1343 /* ok, we've done all we can with the data. What can we process now?
1344 */
1345 {
1346 esiProcessResult_t status;
1347 PROF_start(esiProcessing);
1348 processing = true;
1349 status = tree->process(0);
1350 processing = false;
1351
1352 switch (status) {
1353
1354 case ESI_PROCESS_COMPLETE:
1355 debugs(86, 5, "esiProcess: tree Processed OK");
1356 break;
1357
1358 case ESI_PROCESS_PENDING_WONTFAIL:
1359 debugs(86, 5, "esiProcess: tree Processed PENDING OK");
1360 break;
1361
1362 case ESI_PROCESS_PENDING_MAYFAIL:
1363 debugs(86, 5, "esiProcess: tree Processed PENDING UNKNOWN");
1364 break;
1365
1366 case ESI_PROCESS_FAILED:
1367 debugs(86, 0, "esiProcess: tree Processed FAILED");
1368 setError();
1369
1370 setErrorMessage("esiProcess: ESI template Processing failed.");
1371
1372 PROF_stop(esiProcessing);
1373
1374 return ESI_PROCESS_FAILED;
1375
1376 break;
1377 }
1378
1379 if (status != ESI_PROCESS_PENDING_MAYFAIL && (flags.finishedtemplate || cachedASTInUse)) {
1380 /* We've read the entire template, and no nodes will
1381 * return failure
1382 */
1383 debugs(86, 5, "esiProcess, request will succeed");
1384 flags.oktosend = 1;
1385 }
1386
1387 if (status == ESI_PROCESS_COMPLETE
1388 && (flags.finishedtemplate || cachedASTInUse)) {
1389 /* we've finished all processing. Render and send. */
1390 debugs(86, 5, "esiProcess, processing complete");
1391 flags.finished = 1;
1392 }
1393
1394 PROF_stop(esiProcessing);
1395 return status; /* because we have no callbacks */
1396 }
1397 }
1398
1399 void
1400 ESIContext::ParserState::freeResources()
1401 {
1402 theParser = NULL;
1403 inited_ = false;
1404 }
1405
1406 void
1407 ESIContext::ParserState::popAll()
1408 {
1409 while (stackdepth)
1410 stack[--stackdepth] = NULL;
1411 }
1412
1413 void
1414 ESIContext::freeResources ()
1415 {
1416 debugs(86, 5, HERE << "Freeing for this=" << this);
1417
1418 HTTPMSGUNLOCK(rep);
1419
1420 finishChildren ();
1421
1422 if (parserState.inited()) {
1423 parserState.freeResources();
1424 }
1425
1426 parserState.popAll();
1427 ESISegmentFreeList (buffered);
1428 ESISegmentFreeList (outbound);
1429 ESISegmentFreeList (outboundtail);
1430 delete varState;
1431 varState=NULL;
1432 /* don't touch incoming, it's a pointer into buffered anyway */
1433 }
1434
1435 extern ErrorState *clientBuildError (err_type, http_status, char const *, IpAddress &, HttpRequest *);
1436
1437
1438 /* This can ONLY be used before we have sent *any* data to the client */
1439 void
1440 ESIContext::fail ()
1441 {
1442 debugs(86, 5, "ESIContext::fail: this=" << this);
1443 /* check preconditions */
1444 assert (pos == 0);
1445 /* cleanup current state */
1446 freeResources ();
1447 /* Stop altering thisNode request */
1448 flags.oktosend = 1;
1449 flags.finished = 1;
1450 /* don't honour range requests - for errors we send it all */
1451 flags.error = 1;
1452 /* create an error object */
1453 ErrorState * err = clientBuildError(errorpage, errorstatus, NULL, http->getConn()->peer, http->request);
1454 err->err_msg = errormessage;
1455 errormessage = NULL;
1456 rep = err->BuildHttpReply();
1457 assert (rep->body.mb->contentSize() >= 0);
1458 size_t errorprogress = rep->body.mb->contentSize();
1459 /* Tell esiSend where to start sending from */
1460 outbound_offset = 0;
1461 /* copy the membuf from the reply to outbound */
1462
1463 while (errorprogress < (size_t)rep->body.mb->contentSize()) {
1464 appendOutboundData(new ESISegment);
1465 errorprogress += outboundtail->append(rep->body.mb->content() + errorprogress, rep->body.mb->contentSize() - errorprogress);
1466 }
1467
1468 /* the esiCode now thinks that the error is the outbound,
1469 * and all processing has finished. */
1470 /* Send as much as we can */
1471 send ();
1472
1473 /* don't cancel anything. The stream nodes will clean up after
1474 * themselves when the reply is freed - and we don't know what to
1475 * clean anyway.
1476 */
1477 }
1478
1479 /* Implementation of ESIElements */
1480
1481 /* esiComment */
1482 esiComment::~esiComment()
1483 {
1484 debugs(86, 5, "esiComment::~esiComment " << this);
1485 }
1486
1487 esiComment::esiComment()
1488 {}
1489
1490 void
1491 esiComment::finish()
1492 {}
1493
1494 void
1495 esiComment::render(ESISegment::Pointer output)
1496 {
1497 /* Comments do nothing dude */
1498 debugs(86, 5, "esiCommentRender: Rendering comment " << this << " into " << output.getRaw());
1499 }
1500
1501 ESIElement::Pointer
1502 esiComment::makeCacheable() const
1503 {
1504 debugs(86, 5, "esiComment::makeCacheable: returning NULL");
1505 return NULL;
1506 }
1507
1508 ESIElement::Pointer
1509 esiComment::makeUsable(esiTreeParentPtr, ESIVarState &) const
1510 {
1511 fatal ("esiComment::Usable: unreachable code!\n");
1512 return NULL;
1513 }
1514
1515 /* esiLiteral */
1516 esiLiteral::~esiLiteral()
1517 {
1518 debugs(86, 5, "esiLiteral::~esiLiteral: " << this);
1519 ESISegmentFreeList (buffer);
1520 cbdataReferenceDone (varState);
1521 }
1522
1523 esiLiteral::esiLiteral(ESISegment::Pointer aSegment)
1524 {
1525 buffer = aSegment;
1526 /* we've been handed a complete, processed string */
1527 varState = NULL;
1528 /* Nothing to do */
1529 flags.donevars = 1;
1530 }
1531
1532 void
1533 esiLiteral::finish()
1534 {}
1535
1536 /* precondition: the buffer chain has at least start + length bytes of data
1537 */
1538 esiLiteral::esiLiteral(ESIContext *context, const char *s, int numberOfCharacters)
1539 {
1540 assert (s);
1541 buffer = new ESISegment;
1542 ESISegment::Pointer local = buffer;
1543 size_t start = 0;
1544 int remainingCharacters = numberOfCharacters;
1545
1546 while (remainingCharacters > 0) {
1547 if (local->len == sizeof (local->buf)) {
1548 local->next = new ESISegment;
1549 local=local->next;
1550 }
1551
1552 size_t len = local->append (&s[start], remainingCharacters);
1553 start += len;
1554 remainingCharacters -= len;
1555 }
1556
1557 varState = cbdataReference (context->varState);
1558 }
1559
1560 void
1561 esiLiteral::render (ESISegment::Pointer output)
1562 {
1563 debugs(86, 9, "esiLiteral::render: Rendering " << this);
1564 /* append the entire chain */
1565 assert (output->next.getRaw() == NULL);
1566 output->next = buffer;
1567 buffer = NULL;
1568 }
1569
1570 esiProcessResult_t
1571 esiLiteral::process (int dovars)
1572 {
1573 if (flags.donevars)
1574 return ESI_PROCESS_COMPLETE;
1575
1576 if (dovars) {
1577 ESISegment::Pointer temp = buffer;
1578 /* Ensure variable state is clean */
1579
1580 while (temp.getRaw()) {
1581 varState->feedData(temp->buf,temp->len);
1582 temp = temp->next;
1583 }
1584
1585 /* free the pre-processed content */
1586 ESISegmentFreeList (buffer);
1587
1588 buffer = varState->extractList ();
1589 }
1590
1591 flags.donevars = 1;
1592 return ESI_PROCESS_COMPLETE;
1593 }
1594
1595 esiLiteral::esiLiteral(esiLiteral const &old) : buffer (old.buffer->cloneList()),
1596 varState (NULL)
1597 {
1598 flags.donevars = 0;
1599 }
1600
1601 ESIElement::Pointer
1602 esiLiteral::makeCacheable() const
1603 {
1604 return new esiLiteral (*this);
1605 }
1606
1607 ESIElement::Pointer
1608 esiLiteral::makeUsable(esiTreeParentPtr , ESIVarState &newVarState) const
1609 {
1610 debugs(86, 5, "esiLiteral::makeUsable: Creating usable literal");
1611 esiLiteral * result = new esiLiteral (*this);
1612 result->varState = cbdataReference (&newVarState);
1613 return result;
1614 }
1615
1616 /* esiRemove */
1617 void
1618 esiRemoveFree (void *data)
1619 {
1620 esiRemove *thisNode = (esiRemove *)data;
1621 debugs(86, 5, "esiRemoveFree " << thisNode);
1622 }
1623
1624 void *
1625 esiRemove::operator new(size_t byteCount)
1626 {
1627 assert (byteCount == sizeof (esiRemove));
1628 void *rv;
1629 CBDATA_INIT_TYPE_FREECB(esiRemove, esiRemoveFree);
1630 rv = (void *)cbdataAlloc (esiRemove);
1631 return rv;
1632 }
1633
1634 void
1635 esiRemove::operator delete (void *address)
1636 {
1637 cbdataFree (address);
1638 }
1639
1640 ESIElement *
1641 esiRemoveNew ()
1642 {
1643 return new esiRemove;
1644 }
1645
1646 esiRemove::esiRemove()
1647 {}
1648
1649 void
1650 esiRemove::finish()
1651 {}
1652
1653 void
1654 esiRemove::render(ESISegment::Pointer output)
1655 {
1656 /* Removes do nothing dude */
1657 debugs(86, 5, "esiRemoveRender: Rendering remove " << this);
1658 }
1659
1660 /* Accept non-ESI children */
1661 bool
1662 esiRemove::addElement (ESIElement::Pointer element)
1663 {
1664 if (!dynamic_cast<esiLiteral*>(element.getRaw())) {
1665 debugs(86, 5, "esiRemoveAdd: Failed for " << this);
1666 return false;
1667 }
1668
1669 return true;
1670 }
1671
1672 ESIElement::Pointer
1673 esiRemove::makeCacheable() const
1674 {
1675 debugs(86, 5, "esiRemove::makeCacheable: Returning NULL");
1676 return NULL;
1677 }
1678
1679 ESIElement::Pointer
1680 esiRemove::makeUsable(esiTreeParentPtr, ESIVarState &) const
1681 {
1682 fatal ("esiRemove::Usable: unreachable code!\n");
1683 return NULL;
1684 }
1685
1686 /* esiTry */
1687 esiTry::~esiTry()
1688 {
1689 debugs(86, 5, "esiTry::~esiTry " << this);
1690 }
1691
1692 esiTry::esiTry(esiTreeParentPtr aParent) : parent (aParent) , exceptbuffer(NULL)
1693 {}
1694
1695 void
1696 esiTry::render (ESISegment::Pointer output)
1697 {
1698 /* Try renders from it's children */
1699 assert (this);
1700 assert (attempt.getRaw());
1701 assert (except.getRaw());
1702 debugs(86, 5, "esiTryRender: Rendering Try " << this);
1703
1704 if (flags.attemptok) {
1705 attempt->render(output);
1706 } else if (flags.exceptok) {
1707 /* prerendered */
1708
1709 if (exceptbuffer.getRaw())
1710 ESISegment::ListTransfer(exceptbuffer, output);
1711 else
1712 except->render(output);
1713 } else
1714 debugs(86, 5, "esiTryRender: Neither except nor attempt succeeded?!?");
1715 }
1716
1717 /* Accept attempt and except only */
1718 bool
1719 esiTry::addElement(ESIElement::Pointer element)
1720 {
1721 debugs(86, 5, "esiTryAdd: Try " << this << " adding element " <<
1722 element.getRaw());
1723
1724 if (dynamic_cast<esiLiteral*>(element.getRaw())) {
1725 /* Swallow whitespace */
1726 debugs(86, 5, "esiTryAdd: Try " << this << " skipping whitespace " << element.getRaw());
1727 return true;
1728 }
1729
1730 if (dynamic_cast<esiAttempt*>(element.getRaw())) {
1731 if (attempt.getRaw()) {
1732 debugs(86, 1, "esiTryAdd: Failed for " << this << " - try allready has an attempt node (section 3.4)");
1733 return false;
1734 }
1735
1736 attempt = element;
1737 return true;
1738 }
1739
1740 if (dynamic_cast<esiExcept*>(element.getRaw())) {
1741 if (except.getRaw()) {
1742 debugs(86, 1, "esiTryAdd: Failed for " << this << " - try already has an except node (section 3.4)");
1743 return false;
1744 }
1745
1746 except = element;
1747 return true;
1748 }
1749
1750 debugs(86, 1, "esiTryAdd: Failed to add element " << element.getRaw() << " to try " << this << ", incorrect element type (see section 3.4)");
1751 return false;
1752 }
1753
1754 esiProcessResult_t
1755 esiTry::bestAttemptRV() const
1756 {
1757 if (flags.attemptfailed)
1758 return ESI_PROCESS_COMPLETE;
1759 else
1760 return ESI_PROCESS_PENDING_MAYFAIL;
1761 }
1762
1763 esiProcessResult_t
1764 esiTry::process (int dovars)
1765 {
1766 esiProcessResult_t rv = ESI_PROCESS_PENDING_MAYFAIL;
1767 assert (this);
1768
1769 if (!attempt.getRaw()) {
1770 debugs(86, 0, "esiTryProcess: Try has no attempt element - ESI template is invalid (section 3.4)");
1771 return ESI_PROCESS_FAILED;
1772 }
1773
1774 if (!except.getRaw()) {
1775 debugs(86, 0, "esiTryProcess: Try has no except element - ESI template is invalid (section 3.4)");
1776 return ESI_PROCESS_FAILED;
1777 }
1778
1779 if (!flags.attemptfailed)
1780 /* Try the attempt branch */
1781 switch ((rv = attempt->process(dovars))) {
1782
1783 case ESI_PROCESS_COMPLETE:
1784 debugs(86, 5, "esiTryProcess: attempt Processed OK");
1785 flags.attemptok = 1;
1786 return ESI_PROCESS_COMPLETE;
1787
1788 case ESI_PROCESS_PENDING_WONTFAIL:
1789 debugs(86, 5, "esiTryProcess: attempt Processed PENDING OK");
1790 /* We're not done yet, but don't need to test except */
1791 return ESI_PROCESS_PENDING_WONTFAIL;
1792
1793 case ESI_PROCESS_PENDING_MAYFAIL:
1794 debugs(86, 5, "eseSequenceProcess: element Processed PENDING UNKNOWN");
1795 break;
1796
1797 case ESI_PROCESS_FAILED:
1798 debugs(86, 5, "esiSequenceProcess: element Processed FAILED");
1799 flags.attemptfailed = 1;
1800 break;
1801 }
1802
1803 /* attempt is either MAYFAIL or FAILED */
1804 if (flags.exceptok)
1805 return bestAttemptRV();
1806
1807 /* query except to see if it has a definite result */
1808 if (!flags.exceptfailed)
1809 /* Try the except branch */
1810 switch (except->process(dovars)) {
1811
1812 case ESI_PROCESS_COMPLETE:
1813 debugs(86, 5, "esiTryProcess: except Processed OK");
1814 flags.exceptok = 1;
1815 return bestAttemptRV();
1816
1817 case ESI_PROCESS_PENDING_WONTFAIL:
1818 debugs(86, 5, "esiTryProcess: attempt Processed PENDING OK");
1819 /* We're not done yet, but can't fail */
1820 return ESI_PROCESS_PENDING_WONTFAIL;
1821
1822 case ESI_PROCESS_PENDING_MAYFAIL:
1823 debugs(86, 5, "eseSequenceProcess: element Processed PENDING UNKNOWN");
1824 /* The except branch fail fail */
1825 return ESI_PROCESS_PENDING_MAYFAIL;
1826
1827 case ESI_PROCESS_FAILED:
1828 debugs(86, 5, "esiSequenceProcess: element Processed FAILED");
1829 flags.exceptfailed = 1;
1830 break;
1831 }
1832
1833 if (flags.exceptfailed && flags.attemptfailed)
1834 return ESI_PROCESS_FAILED;
1835
1836 /* one of attempt or except returned PENDING MAYFAIL */
1837 return ESI_PROCESS_PENDING_MAYFAIL;
1838 }
1839
1840 void
1841 esiTry::notifyParent()
1842 {
1843 if (flags.attemptfailed) {
1844 if (flags.exceptok) {
1845 parent->provideData (exceptbuffer, this);
1846 exceptbuffer = NULL;
1847 } else if (flags.exceptfailed || except.getRaw() == NULL) {
1848 parent->fail (this, "esi:try - except claused failed, or no except clause found");
1849 }
1850 }
1851
1852 /* nothing to do when except fails and attempt hasn't */
1853 }
1854
1855 void
1856 esiTry::fail(ESIElement *source, char const *anError)
1857 {
1858 assert (source);
1859 assert (source == attempt || source == except);
1860 debugs(86, 5, "esiTry::fail: this=" << this << ", source=" << source << ", message=" << anError);
1861
1862 if (source == except) {
1863 flags.exceptfailed = 1;
1864 } else {
1865 flags.attemptfailed = 1;
1866 }
1867
1868 notifyParent();
1869 }
1870
1871 void
1872 esiTry::provideData (ESISegment::Pointer data, ESIElement* source)
1873 {
1874 if (source == attempt) {
1875 flags.attemptok = 1;
1876 parent->provideData (data, this);
1877 } else if (source == except) {
1878 flags.exceptok = 1;
1879 assert (exceptbuffer == NULL);
1880 ESISegment::ListTransfer (data, exceptbuffer);
1881 notifyParent();
1882 }
1883 }
1884
1885 esiTry::esiTry(esiTry const &old)
1886 {
1887 attempt = NULL;
1888 except = NULL;
1889 flags.attemptok = 0;
1890 flags.exceptok = 0;
1891 flags.attemptfailed = 0;
1892 flags.exceptfailed = 0;
1893 parent = NULL;
1894 exceptbuffer = NULL;
1895 }
1896
1897 ESIElement::Pointer
1898 esiTry::makeCacheable() const
1899 {
1900 debugs(86, 5, "esiTry::makeCacheable: making cachable Try from " << this);
1901 esiTry *resultT = new esiTry (*this);
1902 ESIElement::Pointer result = resultT;
1903
1904 if (attempt.getRaw())
1905 resultT->attempt = attempt->makeCacheable();
1906
1907 if (except.getRaw())
1908 resultT->except = except->makeCacheable();
1909
1910 return result;
1911 }
1912
1913 ESIElement::Pointer
1914 esiTry::makeUsable(esiTreeParentPtr newParent, ESIVarState &newVarState) const
1915 {
1916 debugs(86, 5, "esiTry::makeUsable: making usable Try from " << this);
1917 esiTry *resultT = new esiTry (*this);
1918 ESIElement::Pointer result = resultT;
1919
1920 resultT->parent = newParent;
1921
1922 if (attempt.getRaw())
1923 resultT->attempt = attempt->makeUsable(resultT, newVarState);
1924
1925 if (except.getRaw())
1926 resultT->except = except->makeUsable(resultT, newVarState);
1927
1928 return result;
1929 }
1930
1931 void
1932 esiTry::finish()
1933 {
1934 parent = NULL;
1935
1936 if (attempt.getRaw())
1937 attempt->finish();
1938
1939 attempt = NULL;
1940
1941 if (except.getRaw())
1942 except->finish();
1943
1944 except = NULL;
1945 }
1946
1947 /* esiAttempt */
1948 #if 0
1949 void *
1950 esiAttempt::operator new(size_t byteCount)
1951 {
1952 assert (byteCount == sizeof (esiAttempt));
1953
1954 }
1955
1956 void
1957 esiAttempt::operator delete (void *address)
1958 {
1959 cbdataFree (address);
1960 }
1961
1962 #endif
1963
1964 /* esiExcept */
1965 #if 0
1966 void *
1967 esiExcept::operator new(size_t byteCount)
1968 {
1969 assert (byteCount == sizeof (esiExcept));
1970 void *rv;
1971 CBDATA_INIT_TYPE_FREECB(esiExcept, esiSequence::Free);
1972 rv = (void *)cbdataAlloc (esiExcept);
1973 return rv;
1974 }
1975
1976 void
1977 esiExcept::operator delete (void *address)
1978 {
1979 cbdataFree (address);
1980 }
1981
1982 #endif
1983
1984 /* ESIVar */
1985 #if 0
1986 void *
1987 esiVar::operator new(size_t byteCount)
1988 {
1989 assert (byteCount == sizeof (esiVar));
1990 void *rv;
1991 CBDATA_INIT_TYPE_FREECB(esiVar, esiSequence::Free);
1992 rv = (void *)cbdataAlloc (esiVar);
1993 return rv;
1994 }
1995
1996 void
1997 esiVar::operator delete (void *address)
1998 {
1999 cbdataFree (address);
2000 }
2001
2002 #endif
2003
2004 /* esiChoose */
2005 esiChoose::~esiChoose()
2006 {
2007 debugs(86, 5, "esiChoose::~esiChoose " << this);
2008 }
2009
2010 esiChoose::esiChoose(esiTreeParentPtr aParent) : elements (), chosenelement (-1),parent (aParent)
2011 {}
2012
2013 void
2014 esiChoose::render(ESISegment::Pointer output)
2015 {
2016 /* append all processed elements, and trim processed and rendered elements */
2017 assert (output->next == NULL);
2018 assert (elements.size() || otherwise.getRaw());
2019 debugs(86, 5, "esiChooseRender: rendering");
2020
2021 if (chosenelement >= 0)
2022 elements[chosenelement]->render(output);
2023 else if (otherwise.getRaw())
2024 otherwise->render(output);
2025 }
2026
2027 bool
2028 esiChoose::addElement(ESIElement::Pointer element)
2029 {
2030 /* add an element to the output list */
2031
2032 if (dynamic_cast<esiLiteral*>(element.getRaw())) {
2033 /* Swallow whitespace */
2034 debugs(86, 5, "esiChooseAdd: Choose " << this << " skipping whitespace " << element.getRaw());
2035 return true;
2036 }
2037
2038 /* Some elements require specific parents */
2039 if (!(dynamic_cast<esiWhen*>(element.getRaw()) || dynamic_cast<esiOtherwise*>(element.getRaw()))) {
2040 debugs(86, 0, "esiChooseAdd: invalid child node for esi:choose (section 3.3)");
2041 return false;
2042 }
2043
2044 if (dynamic_cast<esiOtherwise*>(element.getRaw())) {
2045 if (otherwise.getRaw()) {
2046 debugs(86, 0, "esiChooseAdd: only one otherwise node allowed for esi:choose (section 3.3)");
2047 return false;
2048 }
2049
2050 otherwise = element;
2051 } else {
2052 elements.push_back (element);
2053
2054 debugs (86,3, "esiChooseAdd: Added a new element, elements = " << elements.size());
2055
2056 if (chosenelement == -1)
2057 if ((dynamic_cast<esiWhen *>(element.getRaw()))->
2058 testsTrue()) {
2059 chosenelement = elements.size() - 1;
2060 debugs (86,3, "esiChooseAdd: Chose element " << elements.size());
2061 }
2062 }
2063
2064 return true;
2065 }
2066
2067 void
2068 esiChoose::selectElement()
2069 {
2070 if (chosenelement > -1)
2071 return;
2072
2073 for (size_t counter = 0; counter < elements.size(); ++counter) {
2074 if ((dynamic_cast<esiWhen *>(elements[counter].getRaw()))->
2075 testsTrue()) {
2076 chosenelement = counter;
2077 debugs (86,3, "esiChooseAdd: Chose element " << counter + 1);
2078 return;
2079 }
2080 }
2081 }
2082
2083 void
2084 esiChoose::finish()
2085 {
2086 elements.setNULL(0, elements.size());
2087
2088 if (otherwise.getRaw())
2089 otherwise->finish();
2090
2091 otherwise = NULL;
2092
2093 parent = NULL;
2094 }
2095
2096 void
2097 ElementList::setNULL (int start, int end)
2098 {
2099 assert (start >= 0 && start <= elementcount);
2100 assert (end >= 0 && end <= elementcount);
2101
2102 for (int loopPosition = start; loopPosition < end; ++loopPosition) {
2103 if (elements[loopPosition].getRaw())
2104 elements[loopPosition]->finish();
2105
2106 debugs(86, 5, "esiSequence::NULLElements: Setting index " <<
2107 loopPosition << ", pointer " <<
2108 elements[loopPosition].getRaw() << " to NULL");
2109
2110 elements[loopPosition] = NULL;
2111 }
2112 }
2113
2114 void
2115 esiChoose::NULLUnChosen()
2116 {
2117 if (chosenelement >= 0) {
2118 if (otherwise.getRaw())
2119 otherwise->finish();
2120
2121 otherwise = NULL;
2122
2123 elements.setNULL (0, chosenelement);
2124
2125 elements.setNULL (chosenelement + 1, elements.size());
2126 } else if (otherwise.getRaw()) {
2127 elements.setNULL (0, elements.size());
2128 }
2129 }
2130
2131 esiProcessResult_t
2132 esiChoose::process (int dovars)
2133 {
2134 /* process as much of the list as we can, stopping only on
2135 * faliures
2136 */
2137 /* We MUST have a when clause */
2138 NULLUnChosen();
2139
2140 if (!elements.size()) {
2141 parent->fail(this);
2142
2143 if (otherwise.getRaw())
2144 otherwise->finish();
2145
2146 otherwise = NULL;
2147
2148 parent = NULL;
2149
2150 return ESI_PROCESS_FAILED;
2151 }
2152
2153 if (chosenelement >= 0) {
2154 return elements[chosenelement]->process(dovars);
2155 } else if (otherwise.getRaw())
2156 return otherwise->process(dovars);
2157 else
2158 return ESI_PROCESS_COMPLETE;
2159 }
2160
2161 void
2162 esiChoose::checkValidSource (ESIElement::Pointer source) const
2163 {
2164 if (!elements.size())
2165 fatal ("invalid callback = no when clause\n");
2166
2167 if (chosenelement >= 0)
2168 assert (source == elements[chosenelement]);
2169 else if (otherwise.getRaw())
2170 assert (source == otherwise);
2171 else
2172 fatal ("esiChoose::checkValidSource: invalid callback - no elements chosen\n");
2173 }
2174
2175 void
2176 esiChoose::fail(ESIElement * source, char const *anError)
2177 {
2178 checkValidSource (source);
2179 elements.setNULL (0, elements.size());
2180
2181 if (otherwise.getRaw())
2182 otherwise->finish();
2183
2184 otherwise = NULL;
2185
2186 parent->fail(this, anError);
2187
2188 parent = NULL;
2189 }
2190
2191 void
2192 esiChoose::provideData (ESISegment::Pointer data, ESIElement*source)
2193 {
2194 checkValidSource (source);
2195 parent->provideData (data, this);
2196 }
2197
2198
2199 esiChoose::esiChoose(esiChoose const &old) : chosenelement(-1), otherwise (NULL), parent (NULL)
2200 {
2201 for (size_t counter = 0; counter < old.elements.size(); ++counter) {
2202 ESIElement::Pointer newElement = old.elements[counter]->makeCacheable();
2203
2204 if (newElement.getRaw())
2205 assert (addElement(newElement));
2206 }
2207 }
2208
2209 void
2210 esiChoose::makeCachableElements(esiChoose const &old)
2211 {
2212 for (size_t counter = 0; counter < old.elements.size(); ++counter) {
2213 ESIElement::Pointer newElement = old.elements[counter]->makeCacheable();
2214
2215 if (newElement.getRaw())
2216 assert (addElement(newElement));
2217 }
2218 }
2219
2220 void
2221 esiChoose::makeUsableElements(esiChoose const &old, ESIVarState &newVarState)
2222 {
2223 for (size_t counter = 0; counter < old.elements.size(); ++counter) {
2224 ESIElement::Pointer newElement = old.elements[counter]->makeUsable (this, newVarState);
2225
2226 if (newElement.getRaw())
2227 assert (addElement(newElement));
2228 }
2229 }
2230
2231 ESIElement::Pointer
2232 esiChoose::makeCacheable() const
2233 {
2234 esiChoose *resultC = new esiChoose (*this);
2235 ESIElement::Pointer result = resultC;
2236 resultC->makeCachableElements(*this);
2237
2238 if (otherwise.getRaw())
2239 resultC->otherwise = otherwise->makeCacheable();
2240
2241 return result;
2242 }
2243
2244 ESIElement::Pointer
2245 esiChoose::makeUsable(esiTreeParentPtr newParent, ESIVarState &newVarState) const
2246 {
2247 esiChoose *resultC = new esiChoose (*this);
2248 ESIElement::Pointer result = resultC;
2249 resultC->parent = newParent;
2250 resultC->makeUsableElements(*this, newVarState);
2251 resultC->selectElement();
2252
2253 if (otherwise.getRaw())
2254 resultC->otherwise = otherwise->makeUsable(resultC, newVarState);
2255
2256 return result;
2257 }
2258
2259 /* ElementList */
2260 ElementList::ElementList () : elements(NULL), allocedcount(0), allocedsize(0), elementcount (0)
2261 {}
2262
2263 ElementList::~ElementList()
2264 {
2265 debugs(86, 5, "ElementList::~ElementList " << this);
2266 setNULL(0, elementcount);
2267
2268 if (elements)
2269 memFreeBuf (allocedsize, elements);
2270 }
2271
2272 ESIElement::Pointer &
2273 ElementList::operator [] (int index)
2274 {
2275 return elements[index];
2276 }
2277
2278 ESIElement::Pointer const &
2279 ElementList::operator [] (int index) const
2280 {
2281 return elements[index];
2282 }
2283
2284 void
2285 ElementList::pop_front (size_t const count)
2286 {
2287 if (!count)
2288 return;
2289
2290 xmemmove (elements, &elements[count], (elementcount - count) * sizeof (ESIElement::Pointer));
2291
2292 elementcount -= count;
2293 }
2294
2295 void
2296 ElementList::push_back(ESIElement::Pointer &newElement)
2297 {
2298 elements = (ESIElement::Pointer *)memReallocBuf (elements, ++elementcount * sizeof (ESIElement::Pointer),
2299 &allocedsize);
2300 assert (elements);
2301 allocedcount = elementcount;
2302 memset(&elements[elementcount - 1], '\0', sizeof (ESIElement::Pointer));
2303 elements[elementcount - 1] = newElement;
2304 }
2305
2306 size_t
2307 ElementList::size() const
2308 {
2309 return elementcount;
2310 }
2311
2312 /* esiWhen */
2313 esiWhen::esiWhen (esiTreeParentPtr aParent, int attrcount, const char **attr,ESIVarState *aVar) : esiSequence (aParent)
2314 {
2315 varState = NULL;
2316 char const *expression = NULL;
2317
2318 for (int loopCounter = 0; loopCounter < attrcount && attr[loopCounter]; loopCounter += 2) {
2319 if (!strcmp(attr[loopCounter],"test")) {
2320 /* evaluate test */
2321 debugs(86, 5, "esiWhen::esiWhen: Evaluating '" << attr[loopCounter+1] << "'");
2322 /* TODO: warn the user instead of asserting */
2323 assert (expression == NULL);
2324 expression = attr[loopCounter+1];
2325 } else {
2326 /* ignore mistyped attributes.
2327 * TODO:? error on these for user feedback - config parameter needed
2328 */
2329 debugs(86, 1, "Found misttyped attribute on ESI When clause");
2330 }
2331 }
2332
2333 /* No expression ? default is not matching */
2334 if (!expression)
2335 return;
2336
2337 unevaluatedExpression = xstrdup(expression);
2338
2339 varState = cbdataReference (aVar);
2340
2341 evaluate();
2342 }
2343
2344 esiWhen::~esiWhen()
2345 {
2346 safe_free (unevaluatedExpression);
2347
2348 if (varState)
2349 cbdataReferenceDone (varState);
2350 }
2351
2352 void
2353 esiWhen::evaluate()
2354 {
2355 if (!unevaluatedExpression)
2356 return;
2357
2358 assert (varState);
2359
2360 varState->feedData(unevaluatedExpression, strlen (unevaluatedExpression));
2361
2362 char const *expression = varState->extractChar ();
2363
2364 setTestResult(ESIExpression::Evaluate (expression));
2365
2366 safe_free (expression);
2367 }
2368
2369 esiWhen::esiWhen(esiWhen const &old) : esiSequence (old)
2370 {
2371 unevaluatedExpression = NULL;
2372
2373 if (old.unevaluatedExpression)
2374 unevaluatedExpression = xstrdup(old.unevaluatedExpression);
2375
2376 varState = NULL;
2377 }
2378
2379 ESIElement::Pointer
2380 esiWhen::makeCacheable() const
2381 {
2382 return new esiWhen(*this);
2383 }
2384
2385 ESIElement::Pointer
2386 esiWhen::makeUsable(esiTreeParentPtr newParent, ESIVarState &newVarState) const
2387 {
2388 esiWhen *resultW = new esiWhen (*this);
2389 ESIElement::Pointer result = resultW;
2390 resultW->parent = newParent;
2391 resultW->makeUsableElements(*this, newVarState);
2392 resultW->varState = cbdataReference (&newVarState);
2393 resultW->evaluate();
2394 return result;
2395 }
2396
2397 /* esiOtherwise */
2398 #if 0
2399 void *
2400 esiOtherwise::operator new(size_t byteCount)
2401 {
2402 assert (byteCount == sizeof (esiOtherwise));
2403 void *rv;
2404 CBDATA_INIT_TYPE_FREECB(esiOtherwise, esiSequence::Free);
2405 rv = (void *)cbdataAlloc (esiOtherwise);
2406 return rv;
2407 }
2408
2409 void
2410 esiOtherwise::operator delete (void *address)
2411 {
2412 cbdataFree (address);
2413 }
2414
2415 #endif
2416
2417 /* TODO: implement surrogate targeting and control processing */
2418 int
2419 esiEnableProcessing (HttpReply *rep)
2420 {
2421 int rv = 0;
2422
2423 if (rep->header.has(HDR_SURROGATE_CONTROL)) {
2424 HttpHdrScTarget *sctusable = httpHdrScGetMergedTarget (rep->surrogate_control,
2425 Config.Accel.surrogate_id);
2426
2427 if (!sctusable || sctusable->content.size() == 0)
2428 /* Nothing generic or targeted at us, or no
2429 * content processing requested
2430 */
2431 return 0;
2432
2433 if (sctusable->content.pos("ESI/1.0") != NULL)
2434 rv = 1;
2435
2436 httpHdrScTargetDestroy (sctusable);
2437 }
2438
2439 return rv;
2440 }
2441
2442 #endif /* USE_SQUID_ESI == 1 */