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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors