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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors