Esi.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2021 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 
53 class ESIStreamContext;
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 */
77 static bool
79 {
80  return lhs == rhs.getRaw();
81 }
82 
84 
85 /* some core operators */
86 
87 class esiComment : public ESIElement
88 {
90 
91 public:
92  ~esiComment();
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 
109 class esiRemove : public ESIElement
110 {
112 
113 public:
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 
124 class esiTry : public ESIElement
125 {
127 
128 public:
129  esiTry(esiTreeParentPtr aParent);
130  ~esiTry();
131 
134  void fail(ESIElement *, char const * = NULL);
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 */
148  } flags;
149  void finish();
150 
151 private:
152  void notifyParent();
155  esiTry (esiTry const &);
157 };
158 
159 #include "esi/Var.h"
160 
161 class esiChoose : public ESIElement
162 {
164 
165 public:
167  ~esiChoose();
168 
171  void fail(ESIElement *, char const * = NULL);
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 
186 private:
187  esiChoose(esiChoose const &);
189  void checkValidSource (ESIElement::Pointer source) const;
190  void selectElement();
191 };
192 
193 class esiWhen : public esiSequence
194 {
196 
197 public:
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 
207 private:
208  esiWhen (esiWhen const &);
209  bool testValue;
212  void evaluate();
213 };
214 
215 struct esiOtherwise : public esiSequence {
216  esiOtherwise(esiTreeParentPtr aParent) : esiSequence (aParent) {}
217 };
218 
220 
222 {
223  assert (!reading_);
224  reading_ = true;
225 }
226 
228 {
229  assert (reading_);
230  reading_ = false;
231 }
232 
234 {
235  return reading_;
236 }
237 
238 ESIStreamContext::ESIStreamContext() : finished(false), include (NULL), localbuffer (new ESISegment), buffer (NULL)
239 {}
240 
241 /* Local functions */
242 /* ESIContext */
244 
245 void
247 {
248  errorpage = ERR_ESI;
250  flags.error = 1;
251 }
252 
253 void
255 {
256  if (!outbound.getRaw()) {
257  outbound = theData;
259  } else {
261  outboundtail->next = theData;
262  }
263 
265  debugs(86, 9, "ESIContext::appendOutboundData: outbound " << outbound.getRaw());
266 }
267 
268 void
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 
282 void
283 ESIContext::fail(ESIElement *, char const *anError)
284 {
285  setError();
286  setErrorMessage (anError);
287  fail ();
288  send ();
289 }
290 
291 void
293 {
294  /* TODO: fixup thisNode outboundtail dross a little */
295 
296  if (outboundtail.getRaw())
298 }
299 
300 esiKick_t
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 
330  case ESI_PROCESS_FAILED:
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()) {
348  outboundtail = new ESISegment;
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  */
372 void
374 {
375  clientStreamNode *next;
376  /* Test preconditions */
377  assert (thisNode != NULL);
378  assert (cbdataReferenceValid (thisNode));
379  /* we are not in the chain until ESI is detected on a data callback */
380  assert (thisNode->node.prev != NULL);
381  assert (thisNode->node.next != NULL);
382 
383  ESIContext::Pointer context = dynamic_cast<ESIContext *>(thisNode->data.getRaw());
384  assert (context.getRaw() != NULL);
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, NULL, 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 != NULL);
470  assert (cbdataReferenceValid (thisNode));
471  /* we are not in the chain until ESI is detected on a data callback */
472  assert (thisNode->node.prev != NULL);
473  assert (thisNode->node.next != NULL);
474 
475  ESIContext::Pointer context = dynamic_cast<ESIContext *>(thisNode->data.getRaw());
476  assert (context.getRaw() != NULL);
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 
491 static 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 
518 void
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())
530  assert (outbound.getRaw());
531 }
532 
533 /* Send data downstream
534  * Returns 0 if nothing was sent. Non-zero if data was sent.
535  */
536 size_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())
571  len = min (next->readBuffer.length, outbound->len - outbound_offset);
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 != NULL);
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 = NULL;
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 = NULL; /* 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 
624 void
626 {
627  if (tree.getRaw())
628  tree->finish();
629 
630  tree = NULL;
631 }
632 
633 /* Detach event from a client Stream */
634 void
636 {
637  /* if we have pending callbacks, tell them we're done. */
638  /* test preconditions */
639  assert (thisNode != NULL);
640  assert (cbdataReferenceValid (thisNode));
641  ESIContext::Pointer context = dynamic_cast<ESIContext *>(thisNode->data.getRaw());
642  assert (context.getRaw() != NULL);
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 = NULL;
647  context->flags.detached = 1;
648  context->finishChildren();
649  /* HACK for parser stack not being emptied */
650  context->parserState.stack[0] = NULL;
651  /* allow refcount logic to trigger */
652  context->cbdataLocker = NULL;
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  */
668 void
670 {
671  /* test preconditions */
672  assert (thisNode != NULL);
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() != NULL || rep);
683  assert (thisNode->node.next != NULL);
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() != NULL);
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 == NULL && receivedData.data == NULL && 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 
821 ESIContext *
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))
868  return ESI_ELEMENT_OTHERWISE;
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 
915 bool
917 {
918  return inited_;
919 }
920 
921 void
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 
944 void
945 ESIContext::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 
1072 void
1073 ESIContext::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 
1125 void
1126 ESIContext::parserDefault (const char *s, int len)
1127 {
1128  if (failed())
1129  return;
1130 
1131  /* handle any skipped data */
1132  addLiteral (s, len);
1133 }
1134 
1135 void
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, "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 
1181 void
1182 ESIContext::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 
1194 void
1196 {
1197  theParser = ESIParser::NewParser (userData);
1198  inited_ = true;
1199 }
1200 
1201 void
1203 {
1204  assert (buffered.getRaw());
1205 
1206  debugs (86,9,"ESIContext::parseOneBuffer: " << buffered->len << " bytes");
1207  bool lastBlock = buffered->next.getRaw() == NULL && 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 
1233 void
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)
1253  parseOneBuffer();
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 = NULL;
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 ();
1304  parserState.popAll();
1305  return ESI_PROCESS_FAILED;
1306  }
1307 
1308  if (!flags.finishedtemplate && !incoming.getRaw() && !cachedASTInUse) {
1309  buffered = new ESISegment;
1310  incoming = buffered;
1311  }
1312 
1313  if (!flags.finishedtemplate && !cachedASTInUse) {
1315  }
1316 
1317  assert (flags.finishedtemplate || cachedASTInUse);
1318  updateCachedAST();
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 
1329  case ESI_PROCESS_COMPLETE:
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, "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 
1370 void
1372 {
1373  theParser = NULL;
1374  inited_ = false;
1375 }
1376 
1377 void
1379 {
1380  while (stackdepth)
1381  stack[--stackdepth] = NULL;
1382 }
1383 
1384 void
1386 {
1387  debugs(86, 5, HERE << "Freeing for this=" << this);
1388 
1389  rep = nullptr; // refcounted
1390 
1391  finishChildren ();
1392 
1393  if (parserState.inited()) {
1395  }
1396 
1397  parserState.popAll();
1401  delete varState;
1402  varState=NULL;
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 */
1409 void
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 = NULL;
1427  rep = err->BuildHttpReply();
1428  // XXX: Leaking err!
1429  assert (rep->body.hasContent());
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 
1462 void
1464 {}
1465 
1466 void
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 NULL;
1478 }
1479 
1482 {
1483  fatal ("esiComment::Usable: unreachable code!\n");
1484  return NULL;
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 
1503 void
1505 {}
1506 
1507 /* precondition: the buffer chain has at least start + length bytes of data
1508  */
1509 esiLiteral::esiLiteral(ESIContext *context, const char *s, int numberOfCharacters)
1510 {
1511  assert (s);
1512  flags.donevars = 0;
1513  buffer = new ESISegment;
1514  ESISegment::Pointer local = buffer;
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 
1532 void
1534 {
1535  debugs(86, 9, "esiLiteral::render: Rendering " << this);
1536  /* append the entire chain */
1537  assert (output->next.getRaw() == NULL);
1538  output->next = buffer;
1539  buffer = NULL;
1540 }
1541 
1544 {
1545  if (flags.donevars)
1546  return ESI_PROCESS_COMPLETE;
1547 
1548  if (dovars) {
1549  ESISegment::Pointer temp = buffer;
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 
1560  buffer = varState->extractList ();
1561  }
1562 
1563  flags.donevars = 1;
1564  return ESI_PROCESS_COMPLETE;
1565 }
1566 
1567 esiLiteral::esiLiteral(esiLiteral const &old) : buffer (old.buffer->cloneList()),
1568  varState (NULL)
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 */
1589 void
1591 {
1592  /* Removes do nothing dude */
1593  debugs(86, 5, "esiRemoveRender: Rendering remove " << this);
1594 }
1595 
1596 /* Accept non-ESI children */
1597 bool
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 NULL;
1613 }
1614 
1617 {
1618  fatal ("esiRemove::Usable: unreachable code!\n");
1619  return NULL;
1620 }
1621 
1622 /* esiTry */
1624 {
1625  debugs(86, 5, "esiTry::~esiTry " << this);
1626 }
1627 
1629  parent(aParent),
1630  exceptbuffer(NULL)
1631 {
1632  memset(&flags, 0, sizeof(flags));
1633 }
1634 
1635 void
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 */
1657 bool
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, "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, "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, "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 
1703 esiTry::process (int dovars)
1704 {
1706 
1707  if (!attempt.getRaw()) {
1708  debugs(86, DBG_CRITICAL, "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, "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 
1721  case ESI_PROCESS_COMPLETE:
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 
1750  case ESI_PROCESS_COMPLETE:
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 
1778 void
1780 {
1781  if (flags.attemptfailed) {
1782  if (flags.exceptok) {
1783  parent->provideData (exceptbuffer, this);
1784  exceptbuffer = NULL;
1785  } else if (flags.exceptfailed || except.getRaw() == NULL) {
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 
1793 void
1794 esiTry::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 
1809 void
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 == NULL);
1819  notifyParent();
1820  }
1821 }
1822 
1824 {
1825  attempt = NULL;
1826  except = NULL;
1827  flags.attemptok = 0;
1828  flags.exceptok = 0;
1829  flags.attemptfailed = 0;
1830  flags.exceptfailed = 0;
1831  parent = NULL;
1832  exceptbuffer = NULL;
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 
1852 esiTry::makeUsable(esiTreeParentPtr newParent, ESIVarState &newVarState) const
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 
1869 void
1871 {
1872  parent = NULL;
1873 
1874  if (attempt.getRaw())
1875  attempt->finish();
1876 
1877  attempt = NULL;
1878 
1879  if (except.getRaw())
1880  except->finish();
1881 
1882  except = NULL;
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 
1898 void
1900 {
1901  /* append all processed elements, and trim processed and rendered elements */
1902  assert (output->next == NULL);
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 
1912 bool
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, "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 
1953 void
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
1972 void
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 
1982 void
1984 {
1985  int pos = 0;
1986  for (auto &element : elements)
1987  FinishAnElement(element, pos++);
1988 }
1989 
1990 void
1992 {
1994 
1995  if (otherwise.getRaw())
1996  otherwise->finish();
1997 
1998  otherwise = nullptr;
1999  parent = nullptr;
2000 }
2001 
2002 void
2004 {
2005  if (chosenelement >= 0) {
2006  if (otherwise.getRaw())
2007  otherwise->finish();
2008 
2009  otherwise = NULL;
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 = NULL;
2038 
2039  parent = NULL;
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 
2052 void
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 
2066 void
2067 esiChoose::fail(ESIElement * source, char const *anError)
2068 {
2069  checkValidSource (source);
2071 
2072  if (otherwise.getRaw())
2073  otherwise->finish();
2074 
2075  otherwise = NULL;
2076 
2077  parent->fail(this, anError);
2078 
2079  parent = NULL;
2080 }
2081 
2082 void
2084 {
2085  checkValidSource (source);
2086  parent->provideData (data, this);
2087 }
2088 
2089 esiChoose::esiChoose(esiChoose const &old) : chosenelement(-1), otherwise (NULL), parent (NULL)
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 
2099 void
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 
2110 void
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 */
2150 esiWhen::esiWhen(esiTreeParentPtr aParent, int attrcount, const char **attr,ESIVarState *aVar) :
2151  esiSequence(aParent),
2152  testValue(false),
2153  unevaluatedExpression(NULL),
2154  varState(NULL)
2155 {
2156  char const *expression = NULL;
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 == NULL);
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 
2192 void
2194 {
2195  if (!unevaluatedExpression)
2196  return;
2197 
2198  assert(varState);
2199 
2201 
2202  char const *expression = varState->extractChar ();
2203 
2204  setTestResult(ESIExpression::Evaluate (expression));
2205 
2206  safe_free (expression);
2207 }
2208 
2210  esiSequence(old),
2211  testValue(false),
2212  unevaluatedExpression(NULL),
2213  varState(NULL)
2214 {
2215  if (old.unevaluatedExpression)
2217 }
2218 
2221 {
2222  return new esiWhen(*this);
2223 }
2224 
2226 esiWhen::makeUsable(esiTreeParentPtr newParent, ESIVarState &newVarState) const
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 */
2238 int
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 
~esiLiteral()
Definition: Esi.cc:1488
esiChoose(esiTreeParentPtr)
Definition: Esi.cc:1892
void fatal(const char *message)
Definition: fatal.cc:28
static Pointer NewParser(ESIParserClient *aClient)
Definition: Parser.cc:26
esiKick_t kick()
Definition: Esi.cc:301
void FinishAnElement(ESIElement::Pointer &element, int pos)
Definition: Esi.cc:1973
Pointer makeCacheable() const
Definition: Esi.cc:1574
int oktosend
Definition: Context.h:79
@ ESI_ELEMENT_TRY
Definition: Element.h:54
@ ESI_PROCESS_PENDING_WONTFAIL
Definition: Element.h:20
void freeResources()
Definition: Esi.cc:1385
@ ESI_KICK_INPROGRESS
Definition: Context.h:54
Definition: Esi.cc:125
void FinishAllElements(Esi::Elements &elements)
Definition: Esi.cc:1983
Definition: Var.h:20
int64_t pos
Definition: Context.h:111
void fail()
Definition: Esi.cc:1410
virtual bool mayFail() const
Definition: Element.h:77
clientStream_status_t clientStreamStatus(clientStreamNode *thisObject, ClientHttpRequest *http)
virtual ~esiRemove()
Definition: Esi.cc:115
@ ESI_ELEMENT_INCLUDE
Definition: Element.h:51
ESIElement::Pointer tree
Definition: Context.h:134
void esiProcessStream(clientStreamNode *thisNode, ClientHttpRequest *http, HttpReply *rep, StoreIOBuffer receivedData)
Definition: Esi.cc:669
void trimBlanks()
Definition: Esi.cc:519
dlink_node node
Definition: clientStream.h:87
@ scProcessing
Definition: StatusCode.h:24
ClientStreamData data
Definition: clientStream.h:93
ESISegment::Pointer incoming
Definition: Context.h:99
bool testValue
Definition: Esi.cc:209
int esiEnableProcessing(HttpReply *rep)
Definition: Esi.cc:2239
Pointer makeCacheable() const
Definition: Esi.cc:1836
@ ESI_ELEMENT_ATTEMPT
Definition: Element.h:55
void provideData(ESISegment::Pointer data, ESIElement *source)
Definition: Esi.cc:2083
virtual void render(ESISegment::Pointer)
Definition: Esi.cc:1590
ESISegment::Pointer buffered
Definition: Context.h:98
int finished
Definition: Context.h:80
esiOtherwise(esiTreeParentPtr aParent)
Definition: Esi.cc:216
HttpHeader header
Definition: Message.h:75
int passthrough
Definition: Context.h:78
esiTry(esiTreeParentPtr aParent)
Definition: Esi.cc:1628
virtual long int lineNumber() const =0
size_t len
Definition: Segment.h:41
void finish()
Definition: Esi.cc:1870
void provideData(ESISegment::Pointer data, ESIElement *source)
Definition: Esi.cc:1810
int finishedtemplate
Definition: Context.h:88
void clientStreamRead(clientStreamNode *thisObject, ClientHttpRequest *http, StoreIOBuffer readBuffer)
void ESISegmentFreeList(ESISegment::Pointer &head)
Definition: Segment.cc:19
@ ESI_ELEMENT_EXCEPT
Definition: Element.h:56
ConnStateData * getConn() const
Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const
Definition: Esi.cc:2226
virtual esiProcessResult_t process(int)
Definition: Element.h:72
esiWhen(esiTreeParentPtr aParent, int attributes, const char **attr, ESIVarState *)
Definition: Esi.cc:2150
@ STREAM_NONE
Definition: enums.h:126
virtual char const * errorString() const =0
ClientHttpRequest * http
Definition: Context.h:75
int64_t offset
Definition: StoreIOBuffer.h:55
size_t contentSize() const
Definition: HttpBody.h:41
esiRemove()
Definition: Esi.cc:114
Definition: SBuf.h:87
void esiStreamRead(clientStreamNode *thisNode, ClientHttpRequest *http)
Definition: Esi.cc:373
void appendOutboundData(ESISegment::Pointer theData)
Definition: Esi.cc:254
MEMPROXY_CLASS(esiChoose)
bool addElement(ESIElement::Pointer)
Definition: Esi.cc:1658
static bool operator==(ESIElement const *lhs, ESIElement::Pointer const &rhs)
Definition: Esi.cc:78
#define xstrdup
RefCount< ESIElement > Pointer
Definition: Element.h:46
Pointer next
Definition: Segment.h:42
int exceptfailed
Definition: Esi.cc:147
MEMPROXY_CLASS(esiComment)
C * getRaw() const
Definition: RefCount.h:80
bool addElement(ESIElement::Pointer)
Definition: Esi.cc:1913
Esi::ErrorDetail Error(const char *msg)
prepare an Esi::ErrorDetail for throw on ESI parser internal errors
Definition: Esi.h:31
@ ESI_ELEMENT_CHOOSE
Definition: Element.h:58
const char * content() const
Definition: HttpBody.h:44
char * xstrncpy(char *dst, const char *src, size_t n)
Definition: xstring.cc:37
int cbdataReferenceValid(const void *p)
Definition: cbdata.cc:398
Http::StatusLine sline
Definition: HttpReply.h:56
ESIElement::Pointer top()
Definition: Esi.cc:904
Http::StatusCode errorstatus
Definition: Context.h:95
int attemptfailed
Definition: Esi.cc:146
#define DBG_CRITICAL
Definition: Debug.h:40
StatusCode
Definition: StatusCode.h:20
err_type
Definition: forward.h:14
static void ListTransfer(Pointer &from, Pointer &to)
Definition: Segment.cc:53
#define cbdataReference(var)
Definition: cbdata.h:341
~esiChoose()
Definition: Esi.cc:1886
@ ESI_ELEMENT_COMMENT
Definition: Element.h:52
#define DBG_IMPORTANT
Definition: Debug.h:41
virtual void end(const char *el)
Definition: Esi.cc:1073
@ CONTENT_LENGTH
void clientStreamDetach(clientStreamNode *thisObject, ClientHttpRequest *http)
esiTreeParentPtr parent
Definition: Sequence.h:49
ESISegment::Pointer outbound
Definition: Context.h:103
ESISegment::Pointer extractList()
Definition: VarState.cc:107
@ ESI_KICK_FAILED
Definition: Context.h:51
void finish()
Definition: Esi.cc:1504
HttpBody body
Definition: HttpReply.h:58
Pointer makeCacheable() const
Definition: Esi.cc:2122
HttpHdrScTarget * getMergedTarget(const char *ourtarget)
Definition: HttpHdrSc.cc:296
void parseOneBuffer()
Definition: Esi.cc:1202
ESIVarState * varState
Definition: Literal.h:38
ESIElement::Pointer except
Definition: Esi.cc:141
HttpReplyPointer rep
Definition: Context.h:97
esiProcessResult_t process()
Definition: Esi.cc:1278
struct esiLiteral::@64 flags
struct ESIContext::@60 flags
void render(ESISegment::Pointer)
Definition: Esi.cc:1899
#define NULL
Definition: types.h:166
char * errormessage
Definition: Context.h:96
void makeUsableElements(esiChoose const &old, ESIVarState &)
Definition: Esi.cc:2111
esiProcessResult_t process(int dovars)
Definition: Esi.cc:1543
clientStream_status_t esiStreamStatus(clientStreamNode *thisNode, ClientHttpRequest *http)
Definition: Esi.cc:466
virtual void parserDefault(const char *s, int len)
Definition: Esi.cc:1126
virtual void finish()
Definition: Esi.cc:121
@ scSwitchingProtocols
Definition: StatusCode.h:23
~esiWhen()
Definition: Esi.cc:2184
MEMPROXY_CLASS(esiTry)
esiProcessResult_t process(int dovars)
Definition: Esi.cc:1703
ESIContext::esiKick_t esiKick_t
Definition: Esi.cc:83
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:123
@ ESI_ELEMENT_REMOVE
Definition: Element.h:53
char const * unevaluatedExpression
Definition: Esi.cc:210
Http::StatusCode status() const
retrieve the status code for this status line
Definition: StatusLine.h:45
virtual void parserComment(const char *s)
Definition: Esi.cc:1136
bool inited() const
Definition: Esi.cc:916
static ESIElementType_t IdentifyElement(const char *)
Definition: Esi.cc:852
#define true
Definition: GnuRegex.c:234
bool cachedASTInUse
Definition: Context.h:140
@ STREAM_UNPLANNED_COMPLETE
Definition: enums.h:132
void finishChildren()
Definition: Esi.cc:625
#define ESI_STACK_DEPTH_LIMIT
Definition: Esi.h:16
@ ESI_ELEMENT_NONE
Definition: Element.h:50
@ ESI_KICK_SENT
Definition: Context.h:53
std::ostream & HERE(std::ostream &s)
Definition: Debug.h:152
bool testsTrue() const
Definition: Esi.cc:203
int delById(Http::HdrType id)
Definition: HttpHeader.cc:694
Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const
Definition: Esi.cc:1852
ErrorState * clientBuildError(err_type, Http::StatusCode, char const *, const ConnStateData *, HttpRequest *, const AccessLogEntryPointer &)
clientStreamNode * next() const
ESIElementType_t
Definition: Element.h:49
#define safe_free(x)
Definition: xalloc.h:73
@ ESI_PROCESS_FAILED
Definition: Element.h:22
int detached
Definition: Context.h:91
void addLiteral(const char *s, int len)
Definition: Esi.cc:1182
class ESIContext::ParserState parserState
bool hasContent() const
Definition: HttpBody.h:38
void buildVary(HttpReply *rep)
Definition: VarState.cc:810
bool failed() const
Definition: Context.h:138
void init(ESIParserClient *)
Definition: Esi.cc:1195
#define assert(EX)
Definition: assert.h:19
ESIElement::Pointer otherwise
Definition: Esi.cc:183
~ESIContext()
Definition: Esi.cc:813
~esiTry()
Definition: Esi.cc:1623
struct SquidConfig::@104 Accel
esiTreeParentPtr parent
Definition: Esi.cc:153
@ scContinue
Definition: StatusCode.h:22
~esiComment()
Definition: Esi.cc:1454
void finishRead()
Definition: Esi.cc:227
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
void parse()
Definition: Esi.cc:1234
#define cbdataReferenceDone(var)
Definition: cbdata.h:350
size_t append(char const *, size_t)
Definition: Segment.cc:138
ESIVarState * varState
Definition: Context.h:133
@ scInternalServerError
Definition: StatusCode.h:71
@ ESI_ELEMENT_ASSIGN
Definition: Element.h:61
virtual void finish()=0
const char * c_str()
Definition: SBuf.cc:516
Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const
Definition: Esi.cc:1580
String content() const
virtual void render(ESISegment::Pointer)=0
static int esiAlwaysPassthrough(Http::StatusCode sline)
Definition: Esi.cc:492
MEMPROXY_CLASS(esiRemove)
const char * pos(char const *aString) const
Definition: String.cc:486
char buf[HTTP_REQBUF_SZ]
Definition: Segment.h:40
esiTreeParentPtr parent
Definition: Esi.cc:188
@ scNotModified
Definition: StatusCode.h:40
void finish()
Definition: Esi.cc:1463
void fixupOutboundTail()
Definition: Esi.cc:292
MEMPROXY_CLASS(esiWhen)
esiProcessResult_t
Definition: Element.h:18
void startRead()
Definition: Esi.cc:221
Pointer makeCacheable() const
Definition: Esi.cc:2220
ESISegment::Pointer exceptbuffer
Definition: Esi.cc:154
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
CBDATA_CLASS_INIT(ESIContext)
static ESIContext * ESIContextNew(HttpReply *, clientStreamNode *, ClientHttpRequest *)
Definition: Esi.cc:822
void esiStreamDetach(clientStreamNode *thisNode, ClientHttpRequest *http)
Definition: Esi.cc:635
size_t outbound_offset
Definition: Context.h:109
static int Evaluate(char const *)
Definition: Expression.cc:999
Definition: Esi.cc:194
void fail(ESIElement *, char const *=NULL)
Definition: Esi.cc:2067
void updateCachedAST()
Definition: Context.cc:24
RefCount< ESIContext > cbdataLocker
Definition: Context.h:137
bool reading_
Definition: Context.h:148
@ ESI_PROCESS_PENDING_MAYFAIL
Definition: Element.h:21
Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const
Definition: Esi.cc:1481
void provideData(ESISegment::Pointer, ESIElement *source)
Definition: Esi.cc:269
int attemptok
Definition: Esi.cc:144
ESIElement::Pointer attempt
Definition: Esi.cc:140
clientStreamNode * thisNode
Definition: Context.h:71
void notifyParent()
Definition: Esi.cc:1779
@ scNoContent
Definition: StatusCode.h:30
@ ESI_KICK_PENDING
Definition: Context.h:52
ESIParser::Pointer theParser
Definition: Context.h:119
bool hasCachedAST() const
Definition: Context.cc:52
virtual bool addElement(ESIElement::Pointer)
Definition: Element.h:64
virtual bool parse(char const *dataToParse, size_t const lengthOfData, bool const endOfStream)=0
virtual Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const =0
Counts events, forgetting old ones. Useful for "3 errors/minute" limits.
Definition: FadingCounter.h:16
esiProcessResult_t process(int dovars)
Definition: Esi.cc:2023
@ ESI_PROCESS_COMPLETE
Definition: Element.h:19
ESISegment::Pointer outboundtail
Definition: Context.h:104
@ ESI_ELEMENT_WHEN
Definition: Element.h:59
Esi::Elements elements
Definition: Esi.cc:181
clientStream_status_t
Definition: enums.h:125
#define Must(condition)
Like assert() but throws an exception instead of aborting the process.
Definition: TextException.h:73
void setTestResult(bool aBool)
Definition: Esi.cc:205
void checkValidSource(ESIElement::Pointer source) const
Definition: Esi.cc:2053
char * extractChar()
Definition: VarState.cc:117
Pointer makeCacheable() const
Definition: Esi.cc:1474
@ ERR_ESI
Definition: forward.h:61
void setError()
Definition: Esi.cc:246
char * surrogate_id
Definition: SquidConfig.h:218
void getCachedAST()
Definition: Context.cc:69
ESIVarState * varState
Definition: Esi.cc:211
void render(ESISegment::Pointer)
Definition: Esi.cc:1533
const AccessLogEntry::Pointer al
access.log entry
void selectElement()
Definition: Esi.cc:1954
virtual bool addElement(ESIElement::Pointer)
Definition: Esi.cc:1598
HttpHdrSc * surrogate_control
Definition: HttpReply.h:48
int clientwantsdata
Definition: Context.h:89
@ ESI_ELEMENT_OTHERWISE
Definition: Element.h:60
#define HTTP_REQBUF_SZ
Definition: forward.h:14
virtual void fail(ESIElement *, char const *=nullptr)
Definition: Element.h:33
void addStackElement(ESIElement::Pointer element)
Definition: Esi.cc:922
void setErrorMessage(char const *)
Definition: Context.cc:86
struct esiTry::@61 flags
void clientStreamCallback(clientStreamNode *thisObject, ClientHttpRequest *http, HttpReply *rep, StoreIOBuffer replyBuffer)
esiComment()
Definition: Esi.cc:1459
Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const
Definition: Esi.cc:2135
void fail(ESIElement *, char const *=NULL)
Definition: Esi.cc:1794
StoreIOBuffer readBuffer
Definition: clientStream.h:94
virtual Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const
Definition: Esi.cc:1616
void render(ESISegment::Pointer)
Definition: Esi.cc:1467
int chosenelement
Definition: Esi.cc:182
@ ESI_ELEMENT_VARS
Definition: Element.h:57
void makeUsableElements(esiSequence const &old, ESIVarState &)
Definition: Sequence.cc:347
int count(int howMany)
count fresh, return #events remembered
esiProcessResult_t bestAttemptRV() const
Definition: Esi.cc:1694
void render(ESISegment::Pointer)
Definition: Esi.cc:1636
static StatHist s
#define false
Definition: GnuRegex.c:233
int exceptok
Definition: Esi.cc:145
A const & min(A const &lhs, A const &rhs)
virtual Pointer makeCacheable() const
Definition: Esi.cc:1609
ESISegment const * tail() const
Definition: Segment.cc:153
void makeCachableElements(esiChoose const &old)
Definition: Esi.cc:2100
bool hasContent() const
virtual Pointer makeCacheable() const =0
void evaluate()
Definition: Esi.cc:2193
std::vector< ESIElement::Pointer > Elements
an ordered set of ESI elements
Definition: Element.h:92
void feedData(const char *buf, size_t len)
Definition: VarState.cc:99
void finish()
Definition: Esi.cc:1991
virtual void provideData(ESISegment::Pointer, ESIElement *)
Definition: Element.h:28
err_type errorpage
Definition: Context.h:94
bool reading() const
Definition: Esi.cc:233
ESIStreamContext()
Definition: Esi.cc:238
class SquidConfig Config
Definition: SquidConfig.cc:12
void NULLUnChosen()
Definition: Esi.cc:2003
HttpRequest *const request
ESISegment::Pointer buffer
Definition: Literal.h:32
ESIElement::Pointer stack[ESI_STACK_DEPTH_LIMIT]
Definition: Context.h:117
esiLiteral(ESISegment::Pointer)
Definition: Esi.cc:1495
bool processing
Definition: Context.h:160

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors