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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors