r3839 - trunk/varnish-cache/bin/varnishd
phk at projects.linpro.no
phk at projects.linpro.no
Fri Feb 27 17:09:03 CET 2009
Author: phk
Date: 2009-02-27 17:09:03 +0100 (Fri, 27 Feb 2009)
New Revision: 3839
Added:
trunk/varnish-cache/bin/varnishd/cache_esi.c
Removed:
trunk/varnish-cache/bin/varnishd/cache_vrt_esi.c
Modified:
trunk/varnish-cache/bin/varnishd/Makefile.am
trunk/varnish-cache/bin/varnishd/cache_vrt.c
Log:
Now that ESI parsing is not done while we execute vcl_fetch{}, the
majority of the cache_vrt_esi.c file is no longer "VRT" relevant.
Move the one function which is VRT to cache_vrt.c and rename
cache_vrt_esi.c to cache_esi.c
Modified: trunk/varnish-cache/bin/varnishd/Makefile.am
===================================================================
--- trunk/varnish-cache/bin/varnishd/Makefile.am 2009-02-27 15:20:57 UTC (rev 3838)
+++ trunk/varnish-cache/bin/varnishd/Makefile.am 2009-02-27 16:09:03 UTC (rev 3839)
@@ -21,6 +21,7 @@
cache_dir_random.c \
cache_dir_round_robin.c \
cache_dir_simple.c \
+ cache_esi.c \
cache_expire.c \
cache_fetch.c \
cache_hash.c \
@@ -36,7 +37,6 @@
cache_vary.c \
cache_vcl.c \
cache_vrt.c \
- cache_vrt_esi.c \
cache_vrt_re.c \
cache_ws.c \
hash_classic.c \
Copied: trunk/varnish-cache/bin/varnishd/cache_esi.c (from rev 3838, trunk/varnish-cache/bin/varnishd/cache_vrt_esi.c)
===================================================================
--- trunk/varnish-cache/bin/varnishd/cache_esi.c (rev 0)
+++ trunk/varnish-cache/bin/varnishd/cache_esi.c 2009-02-27 16:09:03 UTC (rev 3839)
@@ -0,0 +1,845 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2008 Linpro AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ *
+ * Runtime support for compiled VCL programs ESI processing.
+ *
+ * The basic ESI 1.0 is a very simple specification:
+ * http://www.w3.org/TR/esi-lang
+ * But it seems that Oracle and Akamai has embrodiered it to be almost a new
+ * layer of scripting language in HTTP transmission chains.
+ *
+ * It is not obvious how much help the "advanced" features of ESI really
+ * are to users, so our aim is to pick the fruit starting with the lowest
+ * hanging, esi:include
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "shmlog.h"
+#include "vrt.h"
+#include "vcl.h"
+#include "cache.h"
+
+#define NDEFELEM 10
+
+/*--------------------------------------------------------------------*/
+
+struct esi_bit {
+ VTAILQ_ENTRY(esi_bit) list;
+ char chunk_length[20];
+ txt verbatim;
+ txt host;
+ txt include;
+ int free_this;
+};
+
+VTAILQ_HEAD(esibithead, esi_bit);
+
+struct esi_ptr {
+ const char *p;
+ struct storage *st;
+};
+
+struct esi_work {
+ struct sess *sp;
+ size_t off;
+
+ struct esi_ptr s;
+ struct esi_ptr p;
+
+ txt tag;
+
+ txt t;
+ struct esi_bit *eb;
+ struct esi_bit *ebl; /* list of */
+ int neb;
+ int remflg; /* inside <esi:remove> </esi:remove> */
+ int incmt; /* inside <!--esi ... --> comment */
+};
+
+/*--------------------------------------------------------------------
+ * Move the parse-pointer forward.
+ */
+
+static void
+Nep(struct esi_ptr *ep)
+{
+ static const char * const finis = "";
+
+ if (ep->p == finis)
+ return;
+ ep->p++;
+ if (ep->p < (char*)ep->st->ptr + ep->st->len)
+ return;
+ ep->st = VTAILQ_NEXT(ep->st, list);
+ if (ep->st != NULL) {
+ ep->p = (char *)ep->st->ptr;
+ return;
+ }
+ ep->p = finis;
+ return;
+}
+
+static void
+N(struct esi_work *ew)
+{
+
+ if (*ew->p.p != '\0')
+ ew->off++;
+ Nep(&ew->p);
+}
+
+/*--------------------------------------------------------------------
+ * Strcmp for objects pointers
+ */
+
+static int
+CMP(const struct esi_ptr *ep, const char *str)
+{
+ struct esi_ptr p2;
+
+ for (p2 = *ep; *str == *p2.p; str++)
+ Nep(&p2);
+ return (*str);
+}
+
+
+/*--------------------------------------------------------------------
+ * Report a parsing error
+ *
+ * XXX: The "at xxx" count is usually the tail of the sequence. Since we
+ * XXX: wander over the storage in an oderly manner now, we could keep
+ * XXX: track of line+pos and record the beginning of the stuff that
+ * XXX: offends os in the central dispatch loop.
+ * XXX: This is left a an excercise for the reader.
+ */
+
+static void
+esi_error(const struct esi_work *ew, const char *p, int i, const char *err)
+{
+ int ellipsis = 0;
+ char buf[256], *q;
+ txt t;
+
+ VSL_stats->esi_errors++;
+ if (i == 0)
+ i = p - ew->t.b;
+ if (i > 20) {
+ i = 20;
+ ellipsis = 1;
+ }
+ q = buf;
+ q += sprintf(buf, "at %zu: %s \"", ew->off, err);
+ while (i > 0) {
+ if (*p >= ' ' && *p <= '~') {
+ *q++ = *p;
+ } else if (*p == '\n') {
+ *q++ = '\\';
+ *q++ = 'n';
+ } else if (*p == '\r') {
+ *q++ = '\\';
+ *q++ = 'r';
+ } else if (*p == '\t') {
+ *q++ = '\\';
+ *q++ = 't';
+ } else {
+ /* XXX: use %%%02x instead ? */
+ q += sprintf(q, "\\x%02x", *p & 0xff);
+ }
+ p++;
+ i--;
+ }
+ if (ellipsis) {
+ *q++ = '[';
+ *q++ = '.';
+ *q++ = '.';
+ *q++ = '.';
+ *q++ = ']';
+ }
+ *q++ = '"';
+ *q++ = '\0';
+ t.b = buf;
+ t.e = q;
+ WSPR(ew->sp, SLT_ESI_xmlerror, t);
+}
+
+/*--------------------------------------------------------------------
+ * Add ESI bit to object
+ */
+
+static void
+esi_addbit(struct esi_work *ew, const char *verbatim, unsigned len)
+{
+
+ if (ew->neb == 0) {
+ ew->ebl = calloc(NDEFELEM, sizeof(struct esi_bit));
+ XXXAN(ew->ebl);
+ ew->neb = NDEFELEM;
+ ew->ebl->free_this = 1;
+ }
+ ew->eb = ew->ebl;
+ ew->ebl++;
+ ew->neb--;
+
+
+ VTAILQ_INSERT_TAIL(&ew->sp->obj->esibits, ew->eb, list);
+ if (verbatim != NULL) {
+ ew->eb->verbatim.b = TRUST_ME(verbatim);
+ if (len > 0)
+ ew->eb->verbatim.e = TRUST_ME(verbatim + len);
+ sprintf(ew->eb->chunk_length, "%x\r\n", Tlen(ew->eb->verbatim));
+ if (params->esi_syntax & 0x4)
+ VSL(SLT_Debug, ew->sp->fd, "AddBit: %d <%.*s>",
+ Tlen(ew->eb->verbatim),
+ Tlen(ew->eb->verbatim),
+ ew->eb->verbatim.b);
+ } else
+ ew->eb->verbatim.b = ew->eb->verbatim.e = (void*)ew->eb;
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+esi_addpfx(struct esi_work *ew)
+{
+ const char *ep;
+
+ if (ew->remflg) {
+ /* In <esi:remove...> don't add anything */
+ ew->s = ew->p;
+ return;
+ }
+ while (ew->s.st != ew->p.st) {
+ ep = (const char *)(ew->s.st->ptr + ew->s.st->len);
+ esi_addbit(ew, ew->s.p, ep - ew->s.p);
+ ew->s.p = ep;
+ Nep(&ew->s);
+ }
+ if (ew->s.st != NULL && ew->p.p != ew->s.p)
+ esi_addbit(ew, ew->s.p, ew->p.p - ew->s.p);
+ ew->s.p = ew->p.p;
+}
+
+/*--------------------------------------------------------------------
+ * Tease the next attribute and value out of an XML element.
+ *
+ * XXX: is the syntax correct ?
+ */
+
+static int
+esi_attrib(const struct esi_work *ew, txt *in, txt *attrib, txt *val)
+{
+
+ AN(*in->b);
+ /* Skip leading blanks */
+ while(in->b < in->e && isspace(*in->b))
+ in->b++;
+
+ /* Nothing found */
+ if (in->b >= in->e)
+ return (0);
+
+ if (!isalpha(*in->b)) {
+ /* XXX error */
+ esi_error(ew, in->b, 1, "XML 1.0 Illegal attribute character");
+ return (-1);
+ }
+
+ /* Attribute name until '=' or space */
+ *attrib = *in;
+ while(in->b < in->e && *in->b != '=' && !isspace(*in->b)) {
+ if (!isalnum(*in->b)) {
+ esi_error(ew, attrib->b, 1 + (in->b - attrib->b),
+ "XML 1.0 Illegal attribute character");
+ return (-1);
+ }
+ in->b++;
+ }
+ attrib->e = in->b;
+
+ if (in->b >= in->e || isspace(*in->b)) {
+ /* Attribute without value */
+ val->b = val->e = in->b;
+ return (1);
+ }
+
+ /* skip '=' */
+ in->b++;
+
+ if (isspace(*in->b)) {
+ val->e = val->b = in->b;;
+ *val->e = '\0';
+ in->b++;
+ return (1);
+ }
+
+ /* Value, if any ? */
+ *val = *in;
+ if (in->b >= in->e)
+ return (1);
+
+ if (*in->b == '"') {
+ /* Skip quote */
+ in->b++;
+ val->b++;
+
+ /* Anything goes, until next quote */
+ while(in->b < in->e && *in->b != '"')
+ in->b++;
+ val->e = in->b;
+
+ if (in->b >= in->e) {
+ esi_error(ew, val->b, in->e - val->b,
+ "XML 1.0 missing ending quote");
+ return (-1);
+ }
+
+ /* Skip quote */
+ in->b++;
+ } else {
+ /* Anything until whitespace */
+ while(in->b < in->e && !isspace(*in->b))
+ in->b++;
+ val->e = in->b;
+ in->b++;
+ }
+ *val->e = '\0';
+ return (1);
+}
+
+/*--------------------------------------------------------------------
+ * Add one piece to the output, either verbatim or include
+ */
+
+static void
+esi_handle_include(struct esi_work *ew)
+{
+ struct esi_bit *eb;
+ char *p, *q;
+ txt t = ew->tag;
+ txt tag;
+ txt val;
+ unsigned u, v;
+ struct ws *ws;
+
+ if (ew->eb == NULL || ew->eb->include.b != NULL)
+ esi_addbit(ew, NULL, 0);
+ eb = ew->eb;
+ VSL(SLT_Debug, ew->sp->fd, "Incl \"%.*s\"", t.e - t.b, t.b);
+ while (esi_attrib(ew, &t, &tag, &val) == 1) {
+ if (params->esi_syntax & 0x4)
+ VSL(SLT_Debug, ew->sp->fd, "<%.*s> -> <%.*s>",
+ tag.e - tag.b, tag.b, val.e - val.b, val.b);
+ if (Tlen(tag) != 3 || memcmp(tag.b, "src", 3))
+ continue;
+ if (Tlen(val) == 0) {
+ esi_error(ew, tag.b, Tlen(tag),
+ "ESI esi:include src attribute withou value");
+ continue;
+ }
+
+ if (Tlen(val) > 7 && !memcmp(val.b, "http://", 7)) {
+ /* Rewrite to Host: header inplace */
+ eb->host.b = val.b;
+ memcpy(eb->host.b, "Host: ", 6);
+ q = eb->host.b + 6;
+ for (p = eb->host.b + 7; p < val.e && *p != '/'; p++)
+ *q++ = *p;
+ *q++ = '\0';
+ eb->host.e = q;
+ assert(*p == '/'); /* XXX */
+ /* The rest is the URL */
+ eb->include.b = p;
+ eb->include.e = val.e;
+ } else if (Tlen(val) > 0 && *val.b == '/') {
+ /* Absolute on this host */
+ eb->include = val;
+ } else {
+
+ /*
+ * Decision: We interpret the relative URL against
+ * the actual URL we asked the backend for.
+ * The client's request URL may be entirely
+ * different and have been rewritten underway.
+ */
+ CHECK_OBJ_NOTNULL(ew->sp, SESS_MAGIC);
+ CHECK_OBJ_NOTNULL(ew->sp->bereq, BEREQ_MAGIC);
+ CHECK_OBJ_NOTNULL(ew->sp->bereq->bereq, HTTP_MAGIC);
+ tag = ew->sp->bereq->bereq->hd[HTTP_HDR_URL];
+
+ /* Use the objects WS to store the result */
+ CHECK_OBJ_NOTNULL(ew->sp->obj, OBJECT_MAGIC);
+ ws = ew->sp->obj->ws_o;
+ WS_Assert(ws);
+
+ /* Look for the last '/' before a '?' */
+ q = NULL;
+ for (p = tag.b; p < tag.e && *p != '?'; p++)
+ if (*p == '/')
+ q = p;
+ if (q != NULL)
+ tag.e = q + 1;
+
+ u = WS_Reserve(ws, 0);
+ v = snprintf(ws->f, u - 1, "%.*s%.*s",
+ pdiff(tag.b, tag.e), tag.b,
+ pdiff(val.b, val.e), val.b);
+ v++;
+ xxxassert(v < u);
+ eb->include.b = ws->f;
+ eb->include.e = ws->f + v;
+ WS_Release(ws, v);
+ }
+ }
+}
+
+/*--------------------------------------------------------------------
+ * See if this looks like XML: first non-white char must be '<'
+ */
+
+static int
+looks_like_xml(const struct object *obj) {
+ struct storage *st;
+ unsigned u;
+
+ VTAILQ_FOREACH(st, &obj->store, list) {
+ AN(st);
+ for (u = 0; u < st->len; u++) {
+ if (isspace(st->ptr[u]))
+ continue;
+ if (st->ptr[u] == '<')
+ return (1);
+ else
+ return (0);
+ }
+ }
+ return (0);
+}
+
+/*--------------------------------------------------------------------
+ * A quick stroll through the object, to find out if it contains any
+ * esi sequences at all.
+ */
+
+static int
+contain_esi(const struct object *obj) {
+ struct storage *st;
+ unsigned u;
+ const char *r, *r2;
+ static const char * const wanted = "<esi:";
+ static const char * const wanted2 = "<!--esi";
+
+ /*
+ * Do a fast check to see if there is any '<esi:' sequences at all
+ */
+ r = wanted;
+ r2 = wanted2;
+ VTAILQ_FOREACH(st, &obj->store, list) {
+ AN(st);
+ for (u = 0; u < st->len; u++) {
+ if (st->ptr[u] != *r) {
+ r = wanted;
+ } else if (*++r == '\0')
+ return (1);
+ if (st->ptr[u] != *r2) {
+ r2 = wanted2;
+ } else if (*++r2 == '\0')
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+parse_esi_comment(struct esi_work *ew)
+{
+
+ esi_addpfx(ew);
+
+ N(ew); N(ew); N(ew); N(ew); N(ew); N(ew); N(ew);
+ assert(!ew->incmt);
+ ew->incmt = 1;
+ ew->s.p = ew->p.p;
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+parse_comment(struct esi_work *ew)
+{
+
+ do {
+ N(ew);
+ if (*ew->p.p == '-' && !CMP(&ew->p, "-->")) {
+ N(ew);
+ N(ew);
+ N(ew);
+ break;
+ }
+ } while (*ew->p.p != '\0');
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+parse_cdata(struct esi_work *ew)
+{
+
+ esi_addpfx(ew);
+
+ do {
+ N(ew);
+ if (*ew->p.p == ']' && !CMP(&ew->p, "]]>")) {
+ N(ew);
+ N(ew);
+ N(ew);
+ break;
+ }
+ } while (*ew->p.p != '\0');
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+parse_esi_tag(struct esi_work *ew, int closing)
+{
+ int l, ll, empty;
+ struct esi_ptr px;
+ char *q;
+
+ esi_addpfx(ew);
+
+ do
+ N(ew);
+ while (*ew->p.p != '>' && *ew->p.p != '\0');
+ if (*ew->p.p == '\0') {
+ esi_addpfx(ew);
+ esi_error(ew, ew->s.p, 0,
+ "XML 1.0 incomplete language element");
+ return;
+ }
+ N(ew);
+
+ if (ew->p.st == ew->s.st) {
+ ew->tag.b = TRUST_ME(ew->s.p);
+ ew->tag.e = TRUST_ME(ew->p.p);
+ } else {
+ /*
+ * The element is spread over more than one storage
+ * segment, pull it together in the object workspace
+ * XXX: Ideally, we should only pull together the bits
+ * XXX: we need, like the filename.
+ */
+ ew->tag.b = ew->sp->obj->ws_o->f;
+ ew->tag.e = ew->tag.b + WS_Reserve(ew->sp->obj->ws_o, 0);
+ px = ew->s;
+ q = ew->tag.b;
+ while (px.p != ew->p.p) {
+ xxxassert(q < ew->tag.e);
+ *q++ = *px.p;
+ Nep(&px);
+ }
+ ew->tag.e = q;
+ WS_Release(ew->sp->obj->ws_o, Tlen(ew->tag));
+ }
+ ll = Tlen(ew->tag);
+ ew->tag.b++;
+ ew->tag.e--;
+ empty = (ew->tag.e[-1] == '/') ? 1 : 0;
+ if (empty)
+ ew->tag.e--;
+
+ if (empty && closing)
+ esi_error(ew, ew->s.p, ll,
+ "XML 1.0 empty and closing element");
+
+ ew->tag.b += 4 + (closing ? 1 : 0);
+ l = Tlen(ew->tag);
+ VSL(SLT_Debug, ew->sp->fd,
+ "tag {%.*s} %d %d %d", l, ew->tag.b, ew->remflg, empty, closing);
+ if (l >= 6 && !memcmp(ew->tag.b, "remove", 6)) {
+ if (empty) {
+ /* XXX ?? */
+ } else if (closing) {
+ if (!ew->remflg)
+ esi_error(ew, ew->s.p, ll,
+ "ESI 1.0 esi:remove not opened");
+ ew->remflg = 0;
+ } else {
+ if (ew->remflg)
+ esi_error(ew, ew->s.p, ll,
+ "ESI 1.0 forbids nested esi:remove");
+ ew->remflg = 1;
+ }
+ } else if (ew->remflg) {
+ esi_error(ew, ew->s.p, ll,
+ "ESI 1.0 forbids esi: elements inside esi:remove");
+ } else if (l >= 7 && !memcmp(ew->tag.b, "comment", 7)) {
+ if (closing)
+ esi_error(ew, ew->s.p, ll,
+ "ESI 1.0 closing esi:comment illegal");
+ else if (!empty)
+ esi_error(ew, ew->s.p, ll,
+ "ESI 1.0 wants empty esi:comment");
+ } else if (l >= 7 && !memcmp(ew->tag.b, "include", 7)) {
+ if (closing) {
+ esi_error(ew, ew->s.p, ll,
+ "ESI 1.0 closing esi:include illegal");
+ } else if (!empty) {
+ esi_error(ew, ew->s.p, ll,
+ "ESI 1.0 wants empty esi:include");
+ }
+ ew->tag.b += 7;
+ esi_handle_include(ew);
+ } else {
+ esi_error(ew, ew->s.p, ll,
+ "ESI 1.0 unimplemented element");
+ }
+ ew->s = ew->p;
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+ESI_Parse(struct sess *sp)
+{
+ struct esi_work *ew, eww[1];
+
+ CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+ CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
+ if (sp->obj->objcore != NULL) /* Pass has no objcore */
+ AN(ObjIsBusy(sp->obj));
+ if (VTAILQ_EMPTY(&sp->obj->store))
+ return;
+
+ if (!(params->esi_syntax & 0x00000001)) {
+ /*
+ * By default, we will not ESI process an object where
+ * the first non-space character is different from '<'
+ */
+ if (!looks_like_xml(sp->obj)) {
+ WSP(sp, SLT_ESI_xmlerror,
+ "No ESI processing, first char not '<'");
+ return;
+ }
+ }
+
+ /*
+ * Do a fast check to see if there is any '<esi:' sequences at all
+ */
+ if (!contain_esi(sp->obj))
+ return;
+
+ VSL_stats->esi_parse++;
+ /* XXX: only if GET ? */
+ ew = eww;
+ memset(eww, 0, sizeof eww);
+ ew->sp = sp;
+ ew->off = 1;
+
+ ew->p.st = VTAILQ_FIRST(&sp->obj->store);
+ AN(ew->p.st);
+ ew->p.p = (char *)ew->p.st->ptr;
+
+ /* ->s points to the first un-dealt-with byte */
+ ew->s = ew->p;
+
+ while (*ew->p.p != '\0') {
+
+ if (ew->incmt && *ew->p.p == '-' && !CMP(&ew->p, "-->")) {
+ /* End of ESI comment */
+ esi_addpfx(ew);
+ N(ew);
+ N(ew);
+ N(ew);
+ ew->s = ew->p;
+ ew->incmt = 0;
+ continue;
+ }
+ /* Skip forward to the first '<' */
+ if (*ew->p.p != '<') {
+ N(ew);
+ continue;
+ }
+
+ if (!CMP(&ew->p, "<!--esi")) {
+ parse_esi_comment(ew);
+ } else if (!CMP(&ew->p, "<!--")) {
+ parse_comment(ew);
+ } else if (!CMP(&ew->p, "</esi")) {
+ parse_esi_tag(ew, 1);
+ } else if (!CMP(&ew->p, "<esi:")) {
+ parse_esi_tag(ew, 0);
+ } else if (!CMP(&ew->p, "<![CDATA[")) {
+ parse_cdata(ew);
+ } else {
+ /*
+ * Something we don't care about, just skip it.
+ */
+ N(ew);
+ if (!(params->esi_syntax & 0x2)) {
+ /* XXX: drop this ? */
+ do {
+ N(ew);
+ } while (*ew->p.p != '>' && *ew->p.p != '\0');
+ }
+ }
+ }
+ esi_addpfx(ew);
+
+ /*
+ * XXX: we could record the starting point of these elements
+ * XXX: so that the char-index were more useful, but we are
+ * XXX: not trivially able to print their contents, so leave
+ * XXX: it like this for now, pending more thought about the
+ * XXX: proper way to report these errors.
+ */
+ if (ew->remflg)
+ esi_error(ew, ew->t.e, -1,
+ "ESI 1.0 unterminated <esi:remove> element");
+ if (ew->incmt)
+ esi_error(ew, ew->t.e, -1,
+ "ESI 1.0 unterminated <!--esi comment");
+
+ /*
+ * Our ESI implementation needs chunked encoding
+ */
+ http_Unset(sp->obj->http, H_Content_Length);
+ http_PrintfHeader(sp->wrk, sp->fd, sp->obj->http,
+ "Transfer-Encoding: chunked");
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+ESI_Deliver(struct sess *sp)
+{
+ struct esi_bit *eb;
+ struct object *obj;
+ struct worker *w;
+
+ w = sp->wrk;
+ WRW_Reserve(w, &sp->fd);
+ VTAILQ_FOREACH(eb, &sp->obj->esibits, list) {
+ if (Tlen(eb->verbatim)) {
+ if (sp->http->protover >= 1.1)
+ (void)WRW_Write(w, eb->chunk_length, -1);
+ sp->acct_req.bodybytes += WRW_Write(w,
+ eb->verbatim.b, Tlen(eb->verbatim));
+ if (sp->http->protover >= 1.1)
+ (void)WRW_Write(w, "\r\n", -1);
+ }
+ if (eb->include.b == NULL ||
+ sp->esis >= params->max_esi_includes)
+ continue;
+
+ if (WRW_FlushRelease(w)) {
+ vca_close_session(sp, "remote closed");
+ return;
+ }
+
+ sp->esis++;
+ obj = sp->obj;
+ sp->obj = NULL;
+ *sp->http = *sp->http0;
+ /* XXX: reset sp->ws */
+ http_SetH(sp->http, HTTP_HDR_URL, eb->include.b);
+ if (eb->host.b != NULL) {
+ http_Unset(sp->http, H_Host);
+ http_Unset(sp->http, H_If_Modified_Since);
+ http_SetHeader(w, sp->fd, sp->http, eb->host.b);
+ }
+ /*
+ * XXX: We should decide if we should cache the director
+ * XXX: or not (for session/backend coupling). Until then
+ * XXX: make sure we don't trip up the check in vcl_recv.
+ */
+ sp->director = NULL;
+ sp->step = STP_RECV;
+ http_ForceGet(sp->http);
+
+ /* Don't do conditionals */
+ sp->http->conds = 0;
+ http_Unset(sp->http, H_If_Modified_Since);
+
+ /* Client content already taken care of */
+ http_Unset(sp->http, H_Content_Length);
+
+ while (1) {
+ sp->wrk = w;
+ CNT_Session(sp);
+ if (sp->step == STP_DONE)
+ break;
+ AZ(sp->wrk);
+ WSL_Flush(w, 0);
+ DSL(0x20, SLT_Debug, sp->id, "loop waiting for ESI");
+ (void)usleep(10000);
+ }
+ AN(sp->wrk);
+ assert(sp->step == STP_DONE);
+ sp->esis--;
+ sp->obj = obj;
+ WRW_Reserve(sp->wrk, &sp->fd);
+ if (sp->fd < 0)
+ break;
+ }
+ if (sp->esis == 0 && sp->http->protover >= 1.1)
+ (void)WRW_Write(sp->wrk, "0\r\n\r\n", -1);
+ if (WRW_FlushRelease(sp->wrk))
+ vca_close_session(sp, "remote closed");
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+ESI_Destroy(struct object *o)
+{
+ struct esi_bit *eb;
+
+ /*
+ * Delete esi_bits from behind and free(3) the ones that want to be.
+ */
+ while (!VTAILQ_EMPTY(&o->esibits)) {
+ eb = VTAILQ_LAST(&o->esibits, esibithead);
+ VTAILQ_REMOVE(&o->esibits, eb, list);
+ if (eb->free_this)
+ free(eb);
+ }
+}
+
Modified: trunk/varnish-cache/bin/varnishd/cache_vrt.c
===================================================================
--- trunk/varnish-cache/bin/varnishd/cache_vrt.c 2009-02-27 15:20:57 UTC (rev 3838)
+++ trunk/varnish-cache/bin/varnishd/cache_vrt.c 2009-02-27 16:09:03 UTC (rev 3839)
@@ -776,6 +776,23 @@
/*--------------------------------------------------------------------*/
+void
+VRT_ESI(struct sess *sp)
+{
+ CHECK_OBJ_NOTNULL(sp->bereq, BEREQ_MAGIC);
+
+ if (sp->cur_method != VCL_MET_FETCH) {
+ /* XXX: we should catch this at compile time */
+ WSP(sp, SLT_VCL_error,
+ "esi can only be called from vcl_fetch");
+ return;
+ }
+
+ sp->bereq->do_esi = 1;
+}
+
+/*--------------------------------------------------------------------*/
+
/*lint -e{818} sp could be const */
void
VRT_panic(struct sess *sp, const char *str, ...)
Deleted: trunk/varnish-cache/bin/varnishd/cache_vrt_esi.c
===================================================================
--- trunk/varnish-cache/bin/varnishd/cache_vrt_esi.c 2009-02-27 15:20:57 UTC (rev 3838)
+++ trunk/varnish-cache/bin/varnishd/cache_vrt_esi.c 2009-02-27 16:09:03 UTC (rev 3839)
@@ -1,861 +0,0 @@
-/*-
- * Copyright (c) 2006 Verdens Gang AS
- * Copyright (c) 2006-2008 Linpro AS
- * All rights reserved.
- *
- * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $Id$
- *
- * Runtime support for compiled VCL programs ESI processing.
- *
- * The basic ESI 1.0 is a very simple specification:
- * http://www.w3.org/TR/esi-lang
- * But it seems that Oracle and Akamai has embrodiered it to be almost a new
- * layer of scripting language in HTTP transmission chains.
- *
- * It is not obvious how much help the "advanced" features of ESI really
- * are to users, so our aim is to pick the fruit starting with the lowest
- * hanging, esi:include
- */
-
-#include "config.h"
-
-#include <stdio.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdarg.h>
-
-#include "shmlog.h"
-#include "vrt.h"
-#include "vcl.h"
-#include "cache.h"
-
-#define NDEFELEM 10
-
-/*--------------------------------------------------------------------*/
-
-struct esi_bit {
- VTAILQ_ENTRY(esi_bit) list;
- char chunk_length[20];
- txt verbatim;
- txt host;
- txt include;
- int free_this;
-};
-
-VTAILQ_HEAD(esibithead, esi_bit);
-
-struct esi_ptr {
- const char *p;
- struct storage *st;
-};
-
-struct esi_work {
- struct sess *sp;
- size_t off;
-
- struct esi_ptr s;
- struct esi_ptr p;
-
- txt tag;
-
- txt t;
- struct esi_bit *eb;
- struct esi_bit *ebl; /* list of */
- int neb;
- int remflg; /* inside <esi:remove> </esi:remove> */
- int incmt; /* inside <!--esi ... --> comment */
-};
-
-/*--------------------------------------------------------------------
- * Move the parse-pointer forward.
- */
-
-static void
-Nep(struct esi_ptr *ep)
-{
- static const char * const finis = "";
-
- if (ep->p == finis)
- return;
- ep->p++;
- if (ep->p < (char*)ep->st->ptr + ep->st->len)
- return;
- ep->st = VTAILQ_NEXT(ep->st, list);
- if (ep->st != NULL) {
- ep->p = (char *)ep->st->ptr;
- return;
- }
- ep->p = finis;
- return;
-}
-
-static void
-N(struct esi_work *ew)
-{
-
- if (*ew->p.p != '\0')
- ew->off++;
- Nep(&ew->p);
-}
-
-/*--------------------------------------------------------------------
- * Strcmp for objects pointers
- */
-
-static int
-CMP(const struct esi_ptr *ep, const char *str)
-{
- struct esi_ptr p2;
-
- for (p2 = *ep; *str == *p2.p; str++)
- Nep(&p2);
- return (*str);
-}
-
-
-/*--------------------------------------------------------------------
- * Report a parsing error
- *
- * XXX: The "at xxx" count is usually the tail of the sequence. Since we
- * XXX: wander over the storage in an oderly manner now, we could keep
- * XXX: track of line+pos and record the beginning of the stuff that
- * XXX: offends os in the central dispatch loop.
- * XXX: This is left a an excercise for the reader.
- */
-
-static void
-esi_error(const struct esi_work *ew, const char *p, int i, const char *err)
-{
- int ellipsis = 0;
- char buf[256], *q;
- txt t;
-
- VSL_stats->esi_errors++;
- if (i == 0)
- i = p - ew->t.b;
- if (i > 20) {
- i = 20;
- ellipsis = 1;
- }
- q = buf;
- q += sprintf(buf, "at %zu: %s \"", ew->off, err);
- while (i > 0) {
- if (*p >= ' ' && *p <= '~') {
- *q++ = *p;
- } else if (*p == '\n') {
- *q++ = '\\';
- *q++ = 'n';
- } else if (*p == '\r') {
- *q++ = '\\';
- *q++ = 'r';
- } else if (*p == '\t') {
- *q++ = '\\';
- *q++ = 't';
- } else {
- /* XXX: use %%%02x instead ? */
- q += sprintf(q, "\\x%02x", *p & 0xff);
- }
- p++;
- i--;
- }
- if (ellipsis) {
- *q++ = '[';
- *q++ = '.';
- *q++ = '.';
- *q++ = '.';
- *q++ = ']';
- }
- *q++ = '"';
- *q++ = '\0';
- t.b = buf;
- t.e = q;
- WSPR(ew->sp, SLT_ESI_xmlerror, t);
-}
-
-/*--------------------------------------------------------------------
- * Add ESI bit to object
- */
-
-static void
-esi_addbit(struct esi_work *ew, const char *verbatim, unsigned len)
-{
-
- if (ew->neb == 0) {
- ew->ebl = calloc(NDEFELEM, sizeof(struct esi_bit));
- XXXAN(ew->ebl);
- ew->neb = NDEFELEM;
- ew->ebl->free_this = 1;
- }
- ew->eb = ew->ebl;
- ew->ebl++;
- ew->neb--;
-
-
- VTAILQ_INSERT_TAIL(&ew->sp->obj->esibits, ew->eb, list);
- if (verbatim != NULL) {
- ew->eb->verbatim.b = TRUST_ME(verbatim);
- if (len > 0)
- ew->eb->verbatim.e = TRUST_ME(verbatim + len);
- sprintf(ew->eb->chunk_length, "%x\r\n", Tlen(ew->eb->verbatim));
- if (params->esi_syntax & 0x4)
- VSL(SLT_Debug, ew->sp->fd, "AddBit: %d <%.*s>",
- Tlen(ew->eb->verbatim),
- Tlen(ew->eb->verbatim),
- ew->eb->verbatim.b);
- } else
- ew->eb->verbatim.b = ew->eb->verbatim.e = (void*)ew->eb;
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-esi_addpfx(struct esi_work *ew)
-{
- const char *ep;
-
- if (ew->remflg) {
- /* In <esi:remove...> don't add anything */
- ew->s = ew->p;
- return;
- }
- while (ew->s.st != ew->p.st) {
- ep = (const char *)(ew->s.st->ptr + ew->s.st->len);
- esi_addbit(ew, ew->s.p, ep - ew->s.p);
- ew->s.p = ep;
- Nep(&ew->s);
- }
- if (ew->s.st != NULL && ew->p.p != ew->s.p)
- esi_addbit(ew, ew->s.p, ew->p.p - ew->s.p);
- ew->s.p = ew->p.p;
-}
-
-/*--------------------------------------------------------------------
- * Tease the next attribute and value out of an XML element.
- *
- * XXX: is the syntax correct ?
- */
-
-static int
-esi_attrib(const struct esi_work *ew, txt *in, txt *attrib, txt *val)
-{
-
- AN(*in->b);
- /* Skip leading blanks */
- while(in->b < in->e && isspace(*in->b))
- in->b++;
-
- /* Nothing found */
- if (in->b >= in->e)
- return (0);
-
- if (!isalpha(*in->b)) {
- /* XXX error */
- esi_error(ew, in->b, 1, "XML 1.0 Illegal attribute character");
- return (-1);
- }
-
- /* Attribute name until '=' or space */
- *attrib = *in;
- while(in->b < in->e && *in->b != '=' && !isspace(*in->b)) {
- if (!isalnum(*in->b)) {
- esi_error(ew, attrib->b, 1 + (in->b - attrib->b),
- "XML 1.0 Illegal attribute character");
- return (-1);
- }
- in->b++;
- }
- attrib->e = in->b;
-
- if (in->b >= in->e || isspace(*in->b)) {
- /* Attribute without value */
- val->b = val->e = in->b;
- return (1);
- }
-
- /* skip '=' */
- in->b++;
-
- if (isspace(*in->b)) {
- val->e = val->b = in->b;;
- *val->e = '\0';
- in->b++;
- return (1);
- }
-
- /* Value, if any ? */
- *val = *in;
- if (in->b >= in->e)
- return (1);
-
- if (*in->b == '"') {
- /* Skip quote */
- in->b++;
- val->b++;
-
- /* Anything goes, until next quote */
- while(in->b < in->e && *in->b != '"')
- in->b++;
- val->e = in->b;
-
- if (in->b >= in->e) {
- esi_error(ew, val->b, in->e - val->b,
- "XML 1.0 missing ending quote");
- return (-1);
- }
-
- /* Skip quote */
- in->b++;
- } else {
- /* Anything until whitespace */
- while(in->b < in->e && !isspace(*in->b))
- in->b++;
- val->e = in->b;
- in->b++;
- }
- *val->e = '\0';
- return (1);
-}
-
-/*--------------------------------------------------------------------
- * Add one piece to the output, either verbatim or include
- */
-
-static void
-esi_handle_include(struct esi_work *ew)
-{
- struct esi_bit *eb;
- char *p, *q;
- txt t = ew->tag;
- txt tag;
- txt val;
- unsigned u, v;
- struct ws *ws;
-
- if (ew->eb == NULL || ew->eb->include.b != NULL)
- esi_addbit(ew, NULL, 0);
- eb = ew->eb;
- VSL(SLT_Debug, ew->sp->fd, "Incl \"%.*s\"", t.e - t.b, t.b);
- while (esi_attrib(ew, &t, &tag, &val) == 1) {
- if (params->esi_syntax & 0x4)
- VSL(SLT_Debug, ew->sp->fd, "<%.*s> -> <%.*s>",
- tag.e - tag.b, tag.b, val.e - val.b, val.b);
- if (Tlen(tag) != 3 || memcmp(tag.b, "src", 3))
- continue;
- if (Tlen(val) == 0) {
- esi_error(ew, tag.b, Tlen(tag),
- "ESI esi:include src attribute withou value");
- continue;
- }
-
- if (Tlen(val) > 7 && !memcmp(val.b, "http://", 7)) {
- /* Rewrite to Host: header inplace */
- eb->host.b = val.b;
- memcpy(eb->host.b, "Host: ", 6);
- q = eb->host.b + 6;
- for (p = eb->host.b + 7; p < val.e && *p != '/'; p++)
- *q++ = *p;
- *q++ = '\0';
- eb->host.e = q;
- assert(*p == '/'); /* XXX */
- /* The rest is the URL */
- eb->include.b = p;
- eb->include.e = val.e;
- } else if (Tlen(val) > 0 && *val.b == '/') {
- /* Absolute on this host */
- eb->include = val;
- } else {
-
- /*
- * Decision: We interpret the relative URL against
- * the actual URL we asked the backend for.
- * The client's request URL may be entirely
- * different and have been rewritten underway.
- */
- CHECK_OBJ_NOTNULL(ew->sp, SESS_MAGIC);
- CHECK_OBJ_NOTNULL(ew->sp->bereq, BEREQ_MAGIC);
- CHECK_OBJ_NOTNULL(ew->sp->bereq->bereq, HTTP_MAGIC);
- tag = ew->sp->bereq->bereq->hd[HTTP_HDR_URL];
-
- /* Use the objects WS to store the result */
- CHECK_OBJ_NOTNULL(ew->sp->obj, OBJECT_MAGIC);
- ws = ew->sp->obj->ws_o;
- WS_Assert(ws);
-
- /* Look for the last '/' before a '?' */
- q = NULL;
- for (p = tag.b; p < tag.e && *p != '?'; p++)
- if (*p == '/')
- q = p;
- if (q != NULL)
- tag.e = q + 1;
-
- u = WS_Reserve(ws, 0);
- v = snprintf(ws->f, u - 1, "%.*s%.*s",
- pdiff(tag.b, tag.e), tag.b,
- pdiff(val.b, val.e), val.b);
- v++;
- xxxassert(v < u);
- eb->include.b = ws->f;
- eb->include.e = ws->f + v;
- WS_Release(ws, v);
- }
- }
-}
-
-/*--------------------------------------------------------------------
- * See if this looks like XML: first non-white char must be '<'
- */
-
-static int
-looks_like_xml(const struct object *obj) {
- struct storage *st;
- unsigned u;
-
- VTAILQ_FOREACH(st, &obj->store, list) {
- AN(st);
- for (u = 0; u < st->len; u++) {
- if (isspace(st->ptr[u]))
- continue;
- if (st->ptr[u] == '<')
- return (1);
- else
- return (0);
- }
- }
- return (0);
-}
-
-/*--------------------------------------------------------------------
- * A quick stroll through the object, to find out if it contains any
- * esi sequences at all.
- */
-
-static int
-contain_esi(const struct object *obj) {
- struct storage *st;
- unsigned u;
- const char *r, *r2;
- static const char * const wanted = "<esi:";
- static const char * const wanted2 = "<!--esi";
-
- /*
- * Do a fast check to see if there is any '<esi:' sequences at all
- */
- r = wanted;
- r2 = wanted2;
- VTAILQ_FOREACH(st, &obj->store, list) {
- AN(st);
- for (u = 0; u < st->len; u++) {
- if (st->ptr[u] != *r) {
- r = wanted;
- } else if (*++r == '\0')
- return (1);
- if (st->ptr[u] != *r2) {
- r2 = wanted2;
- } else if (*++r2 == '\0')
- return (1);
- }
- }
- return (0);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-parse_esi_comment(struct esi_work *ew)
-{
-
- esi_addpfx(ew);
-
- N(ew); N(ew); N(ew); N(ew); N(ew); N(ew); N(ew);
- assert(!ew->incmt);
- ew->incmt = 1;
- ew->s.p = ew->p.p;
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-parse_comment(struct esi_work *ew)
-{
-
- do {
- N(ew);
- if (*ew->p.p == '-' && !CMP(&ew->p, "-->")) {
- N(ew);
- N(ew);
- N(ew);
- break;
- }
- } while (*ew->p.p != '\0');
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-parse_cdata(struct esi_work *ew)
-{
-
- esi_addpfx(ew);
-
- do {
- N(ew);
- if (*ew->p.p == ']' && !CMP(&ew->p, "]]>")) {
- N(ew);
- N(ew);
- N(ew);
- break;
- }
- } while (*ew->p.p != '\0');
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-parse_esi_tag(struct esi_work *ew, int closing)
-{
- int l, ll, empty;
- struct esi_ptr px;
- char *q;
-
- esi_addpfx(ew);
-
- do
- N(ew);
- while (*ew->p.p != '>' && *ew->p.p != '\0');
- if (*ew->p.p == '\0') {
- esi_addpfx(ew);
- esi_error(ew, ew->s.p, 0,
- "XML 1.0 incomplete language element");
- return;
- }
- N(ew);
-
- if (ew->p.st == ew->s.st) {
- ew->tag.b = TRUST_ME(ew->s.p);
- ew->tag.e = TRUST_ME(ew->p.p);
- } else {
- /*
- * The element is spread over more than one storage
- * segment, pull it together in the object workspace
- * XXX: Ideally, we should only pull together the bits
- * XXX: we need, like the filename.
- */
- ew->tag.b = ew->sp->obj->ws_o->f;
- ew->tag.e = ew->tag.b + WS_Reserve(ew->sp->obj->ws_o, 0);
- px = ew->s;
- q = ew->tag.b;
- while (px.p != ew->p.p) {
- xxxassert(q < ew->tag.e);
- *q++ = *px.p;
- Nep(&px);
- }
- ew->tag.e = q;
- WS_Release(ew->sp->obj->ws_o, Tlen(ew->tag));
- }
- ll = Tlen(ew->tag);
- ew->tag.b++;
- ew->tag.e--;
- empty = (ew->tag.e[-1] == '/') ? 1 : 0;
- if (empty)
- ew->tag.e--;
-
- if (empty && closing)
- esi_error(ew, ew->s.p, ll,
- "XML 1.0 empty and closing element");
-
- ew->tag.b += 4 + (closing ? 1 : 0);
- l = Tlen(ew->tag);
- VSL(SLT_Debug, ew->sp->fd,
- "tag {%.*s} %d %d %d", l, ew->tag.b, ew->remflg, empty, closing);
- if (l >= 6 && !memcmp(ew->tag.b, "remove", 6)) {
- if (empty) {
- /* XXX ?? */
- } else if (closing) {
- if (!ew->remflg)
- esi_error(ew, ew->s.p, ll,
- "ESI 1.0 esi:remove not opened");
- ew->remflg = 0;
- } else {
- if (ew->remflg)
- esi_error(ew, ew->s.p, ll,
- "ESI 1.0 forbids nested esi:remove");
- ew->remflg = 1;
- }
- } else if (ew->remflg) {
- esi_error(ew, ew->s.p, ll,
- "ESI 1.0 forbids esi: elements inside esi:remove");
- } else if (l >= 7 && !memcmp(ew->tag.b, "comment", 7)) {
- if (closing)
- esi_error(ew, ew->s.p, ll,
- "ESI 1.0 closing esi:comment illegal");
- else if (!empty)
- esi_error(ew, ew->s.p, ll,
- "ESI 1.0 wants empty esi:comment");
- } else if (l >= 7 && !memcmp(ew->tag.b, "include", 7)) {
- if (closing) {
- esi_error(ew, ew->s.p, ll,
- "ESI 1.0 closing esi:include illegal");
- } else if (!empty) {
- esi_error(ew, ew->s.p, ll,
- "ESI 1.0 wants empty esi:include");
- }
- ew->tag.b += 7;
- esi_handle_include(ew);
- } else {
- esi_error(ew, ew->s.p, ll,
- "ESI 1.0 unimplemented element");
- }
- ew->s = ew->p;
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VRT_ESI(struct sess *sp)
-{
- CHECK_OBJ_NOTNULL(sp->bereq, BEREQ_MAGIC);
-
- if (sp->cur_method != VCL_MET_FETCH) {
- /* XXX: we should catch this at compile time */
- WSP(sp, SLT_VCL_error,
- "esi can only be called from vcl_fetch");
- return;
- }
-
- sp->bereq->do_esi = 1;
-}
-
-
-void
-ESI_Parse(struct sess *sp)
-{
- struct esi_work *ew, eww[1];
-
- CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
- CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
- if (sp->obj->objcore != NULL) /* Pass has no objcore */
- AN(ObjIsBusy(sp->obj));
- if (VTAILQ_EMPTY(&sp->obj->store))
- return;
-
- if (!(params->esi_syntax & 0x00000001)) {
- /*
- * By default, we will not ESI process an object where
- * the first non-space character is different from '<'
- */
- if (!looks_like_xml(sp->obj)) {
- WSP(sp, SLT_ESI_xmlerror,
- "No ESI processing, first char not '<'");
- return;
- }
- }
-
- /*
- * Do a fast check to see if there is any '<esi:' sequences at all
- */
- if (!contain_esi(sp->obj))
- return;
-
- VSL_stats->esi_parse++;
- /* XXX: only if GET ? */
- ew = eww;
- memset(eww, 0, sizeof eww);
- ew->sp = sp;
- ew->off = 1;
-
- ew->p.st = VTAILQ_FIRST(&sp->obj->store);
- AN(ew->p.st);
- ew->p.p = (char *)ew->p.st->ptr;
-
- /* ->s points to the first un-dealt-with byte */
- ew->s = ew->p;
-
- while (*ew->p.p != '\0') {
-
- if (ew->incmt && *ew->p.p == '-' && !CMP(&ew->p, "-->")) {
- /* End of ESI comment */
- esi_addpfx(ew);
- N(ew);
- N(ew);
- N(ew);
- ew->s = ew->p;
- ew->incmt = 0;
- continue;
- }
- /* Skip forward to the first '<' */
- if (*ew->p.p != '<') {
- N(ew);
- continue;
- }
-
- if (!CMP(&ew->p, "<!--esi")) {
- parse_esi_comment(ew);
- } else if (!CMP(&ew->p, "<!--")) {
- parse_comment(ew);
- } else if (!CMP(&ew->p, "</esi")) {
- parse_esi_tag(ew, 1);
- } else if (!CMP(&ew->p, "<esi:")) {
- parse_esi_tag(ew, 0);
- } else if (!CMP(&ew->p, "<![CDATA[")) {
- parse_cdata(ew);
- } else {
- /*
- * Something we don't care about, just skip it.
- */
- N(ew);
- if (!(params->esi_syntax & 0x2)) {
- /* XXX: drop this ? */
- do {
- N(ew);
- } while (*ew->p.p != '>' && *ew->p.p != '\0');
- }
- }
- }
- esi_addpfx(ew);
-
- /*
- * XXX: we could record the starting point of these elements
- * XXX: so that the char-index were more useful, but we are
- * XXX: not trivially able to print their contents, so leave
- * XXX: it like this for now, pending more thought about the
- * XXX: proper way to report these errors.
- */
- if (ew->remflg)
- esi_error(ew, ew->t.e, -1,
- "ESI 1.0 unterminated <esi:remove> element");
- if (ew->incmt)
- esi_error(ew, ew->t.e, -1,
- "ESI 1.0 unterminated <!--esi comment");
-
- /*
- * Our ESI implementation needs chunked encoding
- */
- http_Unset(sp->obj->http, H_Content_Length);
- http_PrintfHeader(sp->wrk, sp->fd, sp->obj->http,
- "Transfer-Encoding: chunked");
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-ESI_Deliver(struct sess *sp)
-{
- struct esi_bit *eb;
- struct object *obj;
- struct worker *w;
-
- w = sp->wrk;
- WRW_Reserve(w, &sp->fd);
- VTAILQ_FOREACH(eb, &sp->obj->esibits, list) {
- if (Tlen(eb->verbatim)) {
- if (sp->http->protover >= 1.1)
- (void)WRW_Write(w, eb->chunk_length, -1);
- sp->acct_req.bodybytes += WRW_Write(w,
- eb->verbatim.b, Tlen(eb->verbatim));
- if (sp->http->protover >= 1.1)
- (void)WRW_Write(w, "\r\n", -1);
- }
- if (eb->include.b == NULL ||
- sp->esis >= params->max_esi_includes)
- continue;
-
- if (WRW_FlushRelease(w)) {
- vca_close_session(sp, "remote closed");
- return;
- }
-
- sp->esis++;
- obj = sp->obj;
- sp->obj = NULL;
- *sp->http = *sp->http0;
- /* XXX: reset sp->ws */
- http_SetH(sp->http, HTTP_HDR_URL, eb->include.b);
- if (eb->host.b != NULL) {
- http_Unset(sp->http, H_Host);
- http_Unset(sp->http, H_If_Modified_Since);
- http_SetHeader(w, sp->fd, sp->http, eb->host.b);
- }
- /*
- * XXX: We should decide if we should cache the director
- * XXX: or not (for session/backend coupling). Until then
- * XXX: make sure we don't trip up the check in vcl_recv.
- */
- sp->director = NULL;
- sp->step = STP_RECV;
- http_ForceGet(sp->http);
-
- /* Don't do conditionals */
- sp->http->conds = 0;
- http_Unset(sp->http, H_If_Modified_Since);
-
- /* Client content already taken care of */
- http_Unset(sp->http, H_Content_Length);
-
- while (1) {
- sp->wrk = w;
- CNT_Session(sp);
- if (sp->step == STP_DONE)
- break;
- AZ(sp->wrk);
- WSL_Flush(w, 0);
- DSL(0x20, SLT_Debug, sp->id, "loop waiting for ESI");
- (void)usleep(10000);
- }
- AN(sp->wrk);
- assert(sp->step == STP_DONE);
- sp->esis--;
- sp->obj = obj;
- WRW_Reserve(sp->wrk, &sp->fd);
- if (sp->fd < 0)
- break;
- }
- if (sp->esis == 0 && sp->http->protover >= 1.1)
- (void)WRW_Write(sp->wrk, "0\r\n\r\n", -1);
- if (WRW_FlushRelease(sp->wrk))
- vca_close_session(sp, "remote closed");
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-ESI_Destroy(struct object *o)
-{
- struct esi_bit *eb;
-
- /*
- * Delete esi_bits from behind and free(3) the ones that want to be.
- */
- while (!VTAILQ_EMPTY(&o->esibits)) {
- eb = VTAILQ_LAST(&o->esibits, esibithead);
- VTAILQ_REMOVE(&o->esibits, eb, list);
- if (eb->free_this)
- free(eb);
- }
-}
-
More information about the varnish-commit
mailing list