source: bin/varnishd/cache_esi_parse.c @ 8e00aa

Revision 8e00aa, 29.5 KB checked in by Poul-Henning Kamp <phk@…>, 3 years ago (diff)

Replay of SVN r5765

Minor cleanups

  • Property mode set to 100644
Line 
1/*-
2 * Copyright (c) 2011 Varnish Software AS
3 * All rights reserved.
4 *
5 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * VEP Varnish Esi Parsing
29 */
30
31#include "config.h"
32
33#include "svnid.h"
34SVNID("$Id")
35
36#include <stdio.h>
37#include <stdlib.h>
38
39#include "cache.h"
40#include "cache_esi.h"
41#include "vend.h"
42#include "vct.h"
43#include "zlib.h"
44#include "stevedore.h"
45
46//#define Debug(fmt, ...) printf(fmt, __VA_ARGS__)
47#define Debug(fmt, ...) /**/
48
49struct vep_state;
50
51enum dowhat {DO_ATTR, DO_TAG};
52typedef void dostuff_f(struct vep_state *, enum dowhat);
53
54struct vep_match {
55        const char      *match;
56        const char      * const *state;
57};
58
59enum vep_mark { VERBATIM = 0, SKIP };
60
61struct vep_state {
62        unsigned                magic;
63#define VEP_MAGIC               0x55cb9b82
64        vfp_bytes_f             *bytes;
65        struct vsb              *vsb;
66
67        struct sess             *sp;
68
69        /* parser state */
70        const char              *state;
71
72        unsigned                endtag;
73        unsigned                emptytag;
74        unsigned                canattr;
75
76        unsigned                remove;
77
78        ssize_t                 o_wait;
79        ssize_t                 o_pending;
80        ssize_t                 o_total;
81        uint32_t                crc;
82        ssize_t                 o_crc;
83        uint32_t                crcp;
84
85const char              *hack_p;
86        const char              *ver_p;
87
88        const char              *until;
89        const char              *until_p;
90        const char              *until_s;
91
92        int                     in_esi_tag;
93
94        const char              *esicmt;
95        const char              *esicmt_p;
96
97        struct vep_match        *attr;
98        struct vsb              *attr_vsb;
99        int                     attr_delim;
100
101        struct vep_match        *match;
102        struct vep_match        *match_hit;
103
104        char                    tag[10];
105        int                     tag_i;
106
107        dostuff_f               *dostuff;
108
109        struct vsb              *include_src;
110
111        unsigned                nm_skip;
112        unsigned                nm_verbatim;
113        unsigned                nm_pending;
114        enum vep_mark           last_mark;
115};
116
117/*---------------------------------------------------------------------*/
118
119static const char * const VEP_START =           "[Start]";
120static const char * const VEP_TESTXML =         "[TestXml]";
121static const char * const VEP_NOTXML =          "[NotXml]";
122
123static const char * const VEP_NEXTTAG =         "[NxtTag]";
124static const char * const VEP_NOTMYTAG =        "[NotMyTag]";
125
126static const char * const VEP_STARTTAG =        "[StartTag]";
127static const char * const VEP_COMMENT =         "[Comment]";
128static const char * const VEP_CDATA =           "[CDATA]";
129static const char * const VEP_ESITAG =          "[ESITag]";
130
131static const char * const VEP_ESIREMOVE =       "[ESI:Remove]";
132static const char * const VEP_ESIINCLUDE =      "[ESI:Include]";
133static const char * const VEP_ESICOMMENT =      "[ESI:Comment]";
134static const char * const VEP_ESIBOGON =        "[ESI:Bogon]";
135
136static const char * const VEP_INTAG =           "[InTag]";
137static const char * const VEP_TAGERROR =        "[TagError]";
138
139static const char * const VEP_ATTR =            "[Attribute]";
140static const char * const VEP_SKIPATTR =        "[SkipAttribute]";
141static const char * const VEP_ATTRDELIM =       "[AttrDelim]";
142static const char * const VEP_ATTRGETVAL =      "[AttrGetValue]";
143static const char * const VEP_ATTRVAL =         "[AttrValue]";
144
145static const char * const VEP_UNTIL =           "[Until]";
146static const char * const VEP_MATCHBUF =        "[MatchBuf]";
147static const char * const VEP_MATCH =           "[Match]";
148
149/*---------------------------------------------------------------------*/
150
151static struct vep_match vep_match_starttag[] = {
152        { "!--",        &VEP_COMMENT },
153        { "esi:",       &VEP_ESITAG },
154        { "![CDATA[",   &VEP_CDATA },
155        { NULL,         &VEP_NOTMYTAG }
156};
157
158/*---------------------------------------------------------------------*/
159
160static struct vep_match vep_match_esi[] = {
161        { "include",    &VEP_ESIINCLUDE },
162        { "remove",     &VEP_ESIREMOVE },
163        { "comment",    &VEP_ESICOMMENT },
164        { NULL,         &VEP_ESIBOGON }
165};
166
167/*---------------------------------------------------------------------*/
168
169static struct vep_match vep_match_attr_include[] = {
170        { "src=",       &VEP_ATTRGETVAL },
171        { NULL,         &VEP_SKIPATTR }
172};
173
174/*--------------------------------------------------------------------
175 * Report a parsing error
176 */
177
178static void
179vep_error(const struct vep_state *vep, const char *p)
180{
181        intmax_t l;
182
183        VSC_main->esi_errors++;
184        l = (intmax_t)(vep->ver_p - vep->hack_p);
185        printf("ERROR at %jd %s\n", l , p);
186        WSP(vep->sp, SLT_ESI_xmlerror, "ERR at %jd %s", l, p);
187       
188}
189
190/*--------------------------------------------------------------------
191 * Report a parsing warning
192 */
193
194static void
195vep_warn(const struct vep_state *vep, const char *p)
196{
197        intmax_t l;
198
199        VSC_main->esi_warnings++;
200        l = (intmax_t)(vep->ver_p - vep->hack_p);
201        printf("WARNING at %jd %s\n", l, p);
202        WSP(vep->sp, SLT_ESI_xmlerror, "WARN at %jd %s", l, p);
203       
204}
205
206/*---------------------------------------------------------------------
207 * return match or NULL if more input needed.
208 */
209
210static struct vep_match *
211vep_match(struct vep_state *vep, const char *b, const char *e)
212{
213        struct vep_match *vm;
214        const char *q, *r;
215        ssize_t l;
216
217        for (vm = vep->match; vm->match; vm++) {
218                r = b;
219                for (q = vm->match; *q && r < e; q++, r++)
220                        if (*q != *r)
221                                break;
222                if (*q != '\0' && r == e) {
223                        if (b != vep->tag) {
224                                l = e - b;
225                                assert(l < sizeof vep->tag);
226                                memmove(vep->tag, b, l);
227                                vep->tag_i = l;
228                        }
229                        return (NULL);
230                }
231                if (*q == '\0')
232                        return (vm);
233        }
234        return (vm);
235}
236
237/*---------------------------------------------------------------------
238 *
239 */
240
241static void
242vep_emit_len(const struct vep_state *vep, ssize_t l, int m8, int m16, int m64)
243{
244        uint8_t buf[9];
245
246        assert(l > 0);
247        if (l < 256) {
248                buf[0] = (uint8_t)m8;
249                buf[1] = (uint8_t)l;
250                assert((ssize_t)buf[1] == l);
251                vsb_bcat(vep->vsb, buf, 2);
252        } else if (l < 65536) {
253                buf[0] = (uint8_t)m16;
254                vbe16enc(buf + 1, (uint16_t)l);
255                assert((ssize_t)vbe16dec(buf + 1) == l);
256                vsb_bcat(vep->vsb, buf, 3);
257        } else {
258                buf[0] = (uint8_t)m64;
259                vbe64enc(buf + 1, l);
260                assert((ssize_t)vbe64dec(buf + 1) == l);
261                vsb_bcat(vep->vsb, buf, 9);
262        }
263} 
264
265static void
266vep_emit_skip(const struct vep_state *vep, ssize_t l)
267{
268
269        if (params->esi_syntax & 0x20) {
270                Debug("---> SKIP(%jd)\n", (intmax_t)l);
271        }
272        vep_emit_len(vep, l, VEC_S1, VEC_S2, VEC_S8);
273} 
274
275static void
276vep_emit_verbatim(const struct vep_state *vep, ssize_t l)
277{
278        uint8_t buf[4];
279
280        if (params->esi_syntax & 0x20) {
281                Debug("---> VERBATIM(%jd)\n", (intmax_t)l);
282        }
283        vep_emit_len(vep, l, VEC_V1, VEC_V2, VEC_V8);
284        vbe32enc(buf, vep->crc);
285        vsb_bcat(vep->vsb, buf, sizeof buf);
286        vsb_printf(vep->vsb, "%lx\r\n%c", l, 0);
287} 
288
289static void
290vep_emit_common(struct vep_state *vep, ssize_t *l, enum vep_mark mark)
291{
292        assert(*l > 0);
293        assert(*l == vep->o_crc);
294
295        assert(mark == SKIP || mark == VERBATIM);
296        if (mark == SKIP)
297                vep_emit_skip(vep, *l);
298        else
299                vep_emit_verbatim(vep, *l);
300
301        vep->crc = crc32(0L, Z_NULL, 0);
302        vep->o_crc = 0;
303        vep->o_total += *l;
304        *l = 0;
305}
306
307/*---------------------------------------------------------------------
308 *
309 */
310
311static void
312vep_mark_common(struct vep_state *vep, const char *p, enum vep_mark mark)
313{
314        ssize_t l;
315
316        assert(mark == SKIP || mark == VERBATIM);
317
318        /* The NO-OP case, no data, no pending data & no change of mode */
319        if (vep->last_mark == mark && p == vep->ver_p && vep->o_pending == 0)
320                return;
321
322        /*
323         * If we changed mode, emit whatever the opposite mode
324         * assembled before the pending bytes.
325         */
326
327        if (vep->last_mark != mark && vep->o_wait > 0)
328                vep_emit_common(vep, &vep->o_wait, vep->last_mark);
329
330        /* Transfer pending bytes CRC into active mode CRC */
331        if (vep->o_pending) {
332                if (vep->o_crc == 0) {
333                        vep->crc = vep->crcp;
334                        vep->o_crc = vep->o_pending;
335                } else {
336                        vep->crc = crc32_combine(vep->crc,
337                            vep->crcp, vep->o_pending);
338                        vep->o_crc += vep->o_pending;
339                }
340                vep->crcp = crc32(0L, Z_NULL, 0);
341        }
342
343        /* * Process this bit of input */
344        AN(vep->ver_p);
345        l = p - vep->ver_p;
346        assert(l >= 0);
347        vep->crc = crc32(vep->crc, (const void*)vep->ver_p, l);
348        vep->o_crc += l;
349        vep->ver_p = p;
350
351        vep->o_wait += vep->o_pending;
352        vep->o_wait += l;
353        vep->o_pending = 0;
354        vep->last_mark = mark;
355}
356
357static void
358vep_mark_verbatim(struct vep_state *vep, const char *p)
359{
360
361        vep_mark_common(vep, p, VERBATIM);
362        vep->nm_verbatim++;
363} 
364
365static void
366vep_mark_skip(struct vep_state *vep, const char *p)
367{
368
369        vep_mark_common(vep, p, SKIP);
370        vep->nm_skip++;
371} 
372
373static void
374vep_mark_pending(struct vep_state *vep, const char *p)
375{
376        ssize_t l;
377
378        AN(vep->ver_p);
379        l = p - vep->ver_p;
380        assert(l > 0);
381        assert(l >= 0);
382        vep->crcp = crc32(vep->crcp, (const void *)vep->ver_p, l);
383        vep->ver_p = p;
384
385        vep->o_pending += l;
386        vep->nm_pending++;
387}
388
389/*---------------------------------------------------------------------
390 */
391
392static void __match_proto__()
393vep_do_comment(struct vep_state *vep, enum dowhat what)
394{
395        Debug("DO_COMMENT(%d)\n", what);
396        assert(what == DO_TAG);
397        if (!vep->emptytag) 
398                vep_error(vep, "ESI 1.0 <esi:comment> needs final '/'");
399}
400
401/*---------------------------------------------------------------------
402 */
403
404static void __match_proto__()
405vep_do_remove(struct vep_state *vep, enum dowhat what)
406{
407        Debug("DO_REMOVE(%d, end %d empty %d remove %d)\n",
408            what, vep->endtag, vep->emptytag, vep->remove);
409        assert(what == DO_TAG);
410        if (vep->emptytag) {
411                vep_error(vep,
412                    "ESI 1.0 <esi:remove/> not legal");
413        } else {
414                if (vep->remove && !vep->endtag)
415                        vep_error(vep,
416                            "ESI 1.0 <esi:remove> already open");
417                else if (!vep->remove && vep->endtag)
418                        vep_error(vep,
419                            "ESI 1.0 <esi:remove> not open");
420                else 
421                        vep->remove = !vep->endtag;
422        }
423}
424
425/*---------------------------------------------------------------------
426 */
427
428static void __match_proto__()
429vep_do_include(struct vep_state *vep, enum dowhat what)
430{
431        char *p, *q, *h;
432        ssize_t l;
433        txt url;
434
435        Debug("DO_INCLUDE(%d)\n", what);
436        if (what == DO_ATTR) {
437                Debug("ATTR (%s) (%s)\n", vep->match_hit->match,
438                        vsb_data(vep->attr_vsb));
439                XXXAZ(vep->include_src);        /* multiple src= */
440                vep->include_src = vep->attr_vsb;
441                return;
442        }
443        assert(what == DO_TAG);
444        if (!vep->emptytag) 
445                vep_warn(vep,
446                    "ESI 1.0 <esi:include> lacks final '/'");
447        if (vep->include_src == NULL) {
448                vep_error(vep,
449                    "ESI 1.0 <esi:include> lacks src attr");
450                return;
451        }
452
453        /*
454         * Strictly speaking, we ought to spit out any piled up skip before
455         * emitting the VEC for the include, but objectively that makes no
456         * difference and robs us of a chance to collapse another skip into
457         * this on so we don't do that.
458         * However, we cannot tolerate any verbatim stuff piling up.
459         * The mark_skip() before calling dostuff should have taken
460         * care of that.  Make sure.
461         */
462        assert(vep->o_wait == 0 || vep->last_mark == SKIP);
463        /* XXX: what if it contains NUL bytes ?? */
464        p = vsb_data(vep->include_src);
465        l = vsb_len(vep->include_src);
466        h = 0;
467
468        vsb_printf(vep->vsb, "%c", VEC_INCL);
469        if (l > 7 && !memcmp(p, "http://", 7)) {
470                h = p + 7;
471                p = strchr(h, '/');
472                AN(p);
473                Debug("HOST <%.*s> PATH <%s>\n", (int)(p-h),h, p);
474                vsb_printf(vep->vsb, "Host: %.*s%c",
475                    (int)(p-h), h, 0);
476        } else if (*p == '/') {
477                vsb_printf(vep->vsb, "%c", 0);
478        } else {
479                vsb_printf(vep->vsb, "%c", 0);
480                url = vep->sp->wrk->bereq->hd[HTTP_HDR_URL];
481                /* Look for the last / before a '?' */
482                h = NULL;
483                for (q = url.b; q < url.e && *q != '?'; q++)
484                        if (*q == '/')
485                                h = q;
486                if (h == NULL)
487                        h = q + 1;
488                       
489                Debug("INCL:: [%.*s]/[%s]\n",
490                    (int)(h - url.b), url.b, p);
491                vsb_printf(vep->vsb, "%.*s/", (int)(h - url.b), url.b);
492        }
493        l -= (p - vsb_data(vep->include_src));
494        for (q = p; *q != '\0'; ) {
495                if (*q == '&') {
496#define R(w,f,r)                                                        \
497                        if (q + w <= p + l && !memcmp(q, f, w)) { \
498                                vsb_printf(vep->vsb, "%c", r);  \
499                                q += l;                         \
500                                continue;                       \
501                        }
502                        R(6, "&apos;", '\'');
503                        R(6, "&quot;", '"');
504                        R(4, "&lt;", '<');
505                        R(4, "&gt;", '>');
506                        R(5, "&amp;", '&');
507                }
508                vsb_printf(vep->vsb, "%c", *q++);
509        }
510#undef R
511        vsb_printf(vep->vsb, "%c", 0);
512
513        vsb_delete(vep->include_src);
514        vep->include_src = NULL;
515}
516
517/*---------------------------------------------------------------------
518 * Lex/Parse object for ESI instructions
519 *
520 * This function is called with the input object piecemal so do not
521 * assume that we have more than one char available at at time, but
522 * optimize for getting huge chunks.
523 *
524 * NB: At the bottom of this source-file, there is a dot-diagram matching
525 * NB: the state-machine.  Please maintain it along with the code.
526 */
527
528static void
529vep_parse(struct vep_state *vep, const char *p, size_t l)
530{
531        const char *e;
532        struct vep_match *vm;
533        int i;
534
535        CHECK_OBJ_NOTNULL(vep, VEP_MAGIC);
536        assert(l > 0);
537
538        e = p + l;
539
540        while (p < e) {
541                AN(vep->state);
542                i = e - p;
543                if (i > 10)
544                        i = 10;
545                Debug("EP %s %d (%.*s) [%.*s]\n",
546                    vep->state,
547                    vep->remove,
548                    vep->tag_i, vep->tag,
549                    i, p);
550                assert(p >= vep->ver_p);
551
552                /******************************************************
553                 * SECTION A
554                 */
555
556                if (vep->state == VEP_START) {
557                        if (params->esi_syntax & 0x1)
558                                vep->state = VEP_NEXTTAG;
559                        else
560                                vep->state = VEP_TESTXML;
561                } else if (vep->state == VEP_TESTXML) {
562                        /*
563                         * If the first non-whitespace char is different
564                         * from '<' we assume this is not XML.
565                         */
566                        while (p < e && vct_islws(*p)) 
567                                p++;
568                        vep_mark_verbatim(vep, p);
569                        if (p < e && *p == '<') {
570                                p++;
571                                vep->state = VEP_STARTTAG;
572                        } else if (p < e) {
573                                WSP(vep->sp, SLT_ESI_xmlerror,
574                                    "No ESI processing, first char not '<'");
575                                vep->state = VEP_NOTXML;
576                        }
577                } else if (vep->state == VEP_NOTXML) {
578                        /*
579                         * This is not recognized as XML, just skip thru
580                         * vfp_esi_end() will handle the rest
581                         */
582                        p = e;
583                        vep_mark_verbatim(vep, p);
584
585                /******************************************************
586                 * SECTION B
587                 */
588
589                } else if (vep->state == VEP_NOTMYTAG) {
590                        if (params->esi_syntax & 0x2) {
591                                p++;
592                                vep->state = VEP_NEXTTAG;
593                        } else {
594                                vep->tag_i = 0;
595                                while (p < e) {
596                                        if (*p++ == '>') {
597                                                vep->state = VEP_NEXTTAG;
598                                                break;
599                                        }
600                                }
601                        }
602                        if (p == e && !vep->remove)
603                                vep_mark_verbatim(vep, p);
604                } else if (vep->state == VEP_NEXTTAG) {
605                        /*
606                         * Hunt for start of next tag and keep an eye
607                         * out for end of EsiCmt if armed.
608                         */
609                        vep->emptytag = 0;
610                        vep->endtag = 0;
611                        vep->attr = NULL;
612                        vep->dostuff = NULL;
613                        while (p < e && *p != '<') {
614                                if (vep->esicmt_p == NULL) {
615                                        p++;
616                                        continue;
617                                }
618                                if (*p != *vep->esicmt_p) {
619                                        p++;
620                                        vep->esicmt_p = vep->esicmt;
621                                        continue;
622                                }
623                                if (!vep->remove &&
624                                    vep->esicmt_p == vep->esicmt)
625                                        vep_mark_verbatim(vep, p);
626                                p++;
627                                if (*++vep->esicmt_p == '\0') {
628                                        vep->esicmt = NULL;
629                                        vep->esicmt_p = NULL;
630                                        /*
631                                         * The end of the esicmt
632                                         * should not be emitted.
633                                         * But the stuff before should
634                                         */
635                                        vep_mark_skip(vep, p);
636                                }
637                        }
638                        if (p < e) {
639                                if (!vep->remove)
640                                        vep_mark_verbatim(vep, p);
641                                assert(*p == '<');
642                                p++;
643                                vep->state = VEP_STARTTAG;
644                        } else if (vep->esicmt_p == vep->esicmt && !vep->remove)
645                                vep_mark_verbatim(vep, p);
646
647                /******************************************************
648                 * SECTION C
649                 */
650
651                } else if (vep->state == VEP_STARTTAG) {
652                        /*
653                         * Start of tag, set up match table
654                         */
655                        if (p < e) {
656                                if (*p == '/') {
657                                        vep->endtag = 1;
658                                        p++;
659                                } 
660                                vep->match = vep_match_starttag;
661                                vep->state = VEP_MATCH;
662                        }
663                } else if (vep->state == VEP_COMMENT) {
664                        /*
665                         * We are in a comment, find out if it is an
666                         * ESI comment or a regular comment
667                         */
668                        if (vep->esicmt == NULL)
669                                vep->esicmt_p = vep->esicmt = "esi";
670                        while (p < e) {
671                                if (*p != *vep->esicmt_p) {
672                                        vep->esicmt_p = vep->esicmt = NULL; 
673                                        vep->until_p = vep->until = "-->";
674                                        vep->until_s = VEP_NEXTTAG;
675                                        vep->state = VEP_UNTIL;
676                                        break;
677                                }
678                                p++;
679                                if (*++vep->esicmt_p != '\0')
680                                        continue;
681                                if (vep->remove)
682                                        vep_error(vep,
683                                            "ESI 1.0 Nested <!--esi"
684                                            " element in <esi:remove>");
685                                vep->esicmt_p = vep->esicmt = "-->";
686                                vep->state = VEP_NEXTTAG;
687                                vep_mark_skip(vep, p);
688                                break;
689                        }
690                } else if (vep->state == VEP_CDATA) {
691                        /*
692                         * Easy: just look for the end of CDATA
693                         */
694                        vep->until_p = vep->until = "]]>";
695                        vep->until_s = VEP_NEXTTAG;
696                        vep->state = VEP_UNTIL;
697                } else if (vep->state == VEP_ESITAG) {
698                        vep->in_esi_tag = 1;
699                        vep_mark_skip(vep, p);
700                        vep->match = vep_match_esi;
701                        vep->state = VEP_MATCH;
702                } else if (vep->state == VEP_ESIINCLUDE) {
703                        if (vep->remove) {
704                                vep_error(vep,
705                                    "ESI 1.0 <esi:include> element"
706                                    " nested in <esi:remove>");
707                                vep->state = VEP_TAGERROR;
708                        } else if (vep->endtag) {
709                                vep_error(vep,
710                                    "ESI 1.0 </esi:include> illegal end-tag");
711                                vep->state = VEP_TAGERROR;
712                        } else {
713                                vep->dostuff = vep_do_include;
714                                vep->state = VEP_INTAG;
715                                vep->attr = vep_match_attr_include;
716                        }
717                } else if (vep->state == VEP_ESIREMOVE) {
718                        vep->dostuff = vep_do_remove;
719                        vep->state = VEP_INTAG;
720                } else if (vep->state == VEP_ESICOMMENT) {
721                        if (vep->remove) {
722                                vep_error(vep,
723                                    "ESI 1.0 <esi:comment> element"
724                                    " nested in <esi:remove>");
725                                vep->state = VEP_TAGERROR;
726                        } else if (vep->endtag) {
727                                vep_error(vep,
728                                    "ESI 1.0 </esi:comment> illegal end-tag");
729                                vep->state = VEP_TAGERROR;
730                        } else {
731                                vep->dostuff = vep_do_comment;
732                                vep->state = VEP_INTAG;
733                        }
734                } else if (vep->state == VEP_ESIBOGON) {
735                        vep_error(vep,
736                            "ESI 1.0 <esi:bogus> element");
737                        vep->state = VEP_TAGERROR;
738
739                /******************************************************
740                 * SECTION D
741                 */
742
743                } else if (vep->state == VEP_INTAG) {
744                        vep->tag_i = 0;
745                        while (p < e && vct_islws(*p) && !vep->emptytag) {
746                                p++;   
747                                vep->canattr = 1;
748                        }
749                        if (p < e && *p == '/' && !vep->emptytag) {
750                                p++;
751                                vep->emptytag = 1;
752                                vep->canattr = 0;
753                        }
754                        if (p < e && *p == '>') {
755                                p++;
756                                AN(vep->dostuff);
757                                vep_mark_skip(vep, p);
758                                vep->dostuff(vep, DO_TAG);
759                                vep->in_esi_tag = 0;
760                                vep->state = VEP_NEXTTAG;
761                        } else if (p < e && vep->emptytag) {
762                                vep_error(vep,
763                                    "XML 1.0 '>' does not follow '/' in tag");
764                                vep->state = VEP_TAGERROR;
765                        } else if (p < e && vep->canattr &&
766                            vct_isxmlnamestart(*p)) {
767                                vep->state = VEP_ATTR;
768                        } else if (p < e) {
769                                vep_error(vep,
770                                    "XML 1.0 Illegal attribute tart char");
771                                vep->state = VEP_TAGERROR;
772                        }
773                } else if (vep->state == VEP_TAGERROR) {
774                        while (p < e && *p != '>')
775                                p++;
776                        if (p < e) {
777                                p++;
778                                vep_mark_skip(vep, p);
779                                vep->in_esi_tag = 0;
780                                vep->state = VEP_NEXTTAG;
781                        }
782
783                /******************************************************
784                 * SECTION E
785                 */
786
787                } else if (vep->state == VEP_ATTR) {
788                        AZ(vep->attr_delim);
789                        if (vep->attr == NULL) {
790                                p++;
791                                AZ(vep->attr_vsb);
792                                vep->state = VEP_SKIPATTR;
793                        } else {
794                                vep->match = vep->attr;
795                                vep->state = VEP_MATCH;
796                        }
797                } else if (vep->state == VEP_SKIPATTR) {
798                        while (p < e && vct_isxmlname(*p))
799                                p++;
800                        if (p < e && *p == '=') {
801                                p++;
802                                vep->state = VEP_ATTRDELIM;
803                        } else if (p < e && *p == '>') {
804                                vep->state = VEP_INTAG;
805                        } else if (p < e && *p == '/') {
806                                vep->state = VEP_INTAG;
807                        } else if (p < e && vct_issp(*p)) {
808                                vep->state = VEP_INTAG;
809                        } else if (p < e) {
810                                vep_error(vep,
811                                    "XML 1.0 Illegal attr char");
812                                vep->state = VEP_TAGERROR;
813                        }
814                } else if (vep->state == VEP_ATTRGETVAL) {
815                        vep->attr_vsb = vsb_newauto();
816                        vep->state = VEP_ATTRDELIM;
817                } else if (vep->state == VEP_ATTRDELIM) {
818                        AZ(vep->attr_delim);
819                        if (*p == '"' || *p == '\'') {
820                                vep->attr_delim = *p++;
821                                vep->state = VEP_ATTRVAL;
822                        } else if (!vct_issp(*p)) {
823                                vep->attr_delim = ' ';
824                                vep->state = VEP_ATTRVAL;
825                        } else {
826                                vep_error(vep,
827                                    "XML 1.0 Illegal attribute delimiter");
828                                vep->state = VEP_TAGERROR;
829                        }
830                       
831                } else if (vep->state == VEP_ATTRVAL) {
832                        while (p < e && *p != '>' && *p != vep->attr_delim &&
833                           (vep->attr_delim != ' ' || !vct_issp(*p))) {
834                                if (vep->attr_vsb != NULL)
835                                        vsb_bcat(vep->attr_vsb, p, 1);
836                                p++;
837                        }
838                        if (p < e && *p == '>') {
839                                vep_error(vep,
840                                    "XML 1.0 Missing end attribute delimiter");
841                                vep->state = VEP_TAGERROR;
842                                vep->attr_delim = 0;
843                                if (vep->attr_vsb != NULL) {
844                                        vsb_finish(vep->attr_vsb);
845                                        vsb_delete(vep->attr_vsb);
846                                        vep->attr_vsb = NULL;
847                                }
848                        } else if (p < e) {
849                                vep->attr_delim = 0;
850                                p++;
851                                if (vep->attr_vsb != NULL) {
852                                        vsb_finish(vep->attr_vsb);
853                                        AN(vep->dostuff);
854                                        vep->dostuff(vep, DO_ATTR);
855                                        vep->attr_vsb = NULL;
856                                }
857                                vep->state = VEP_INTAG;
858                        }
859       
860
861                /******************************************************
862                 * Utility Section
863                 */
864
865                } else if (vep->state == VEP_MATCH) {
866                        /*
867                         * Match against a table
868                         */
869                        vm = vep_match(vep, p, e);
870                        vep->match_hit = vm;
871                        if (vm != NULL) {
872                                if (vm->match != NULL)
873                                        p += strlen(vm->match);
874                                vep->state = *vm->state;
875                                vep->match = NULL;
876                                vep->tag_i = 0;
877                        } else {
878                                memcpy(vep->tag, p, e - p);
879                                vep->tag_i = e - p;
880                                vep->state = VEP_MATCHBUF;
881                                p = e;
882                        }
883                } else if (vep->state == VEP_MATCHBUF) {
884                        /*
885                         * Match against a table while split over input
886                         * sections.
887                         */
888                        do {
889                                if (*p == '>') {
890                                        for (vm = vep->match;
891                                            vm->match != NULL; vm++)
892                                                continue;
893                                        AZ(vm->match);
894                                } else {
895                                        vep->tag[vep->tag_i++] = *p++;
896                                        vm = vep_match(vep,
897                                            vep->tag, vep->tag + vep->tag_i);
898                                        if (vm && vm->match == NULL) {
899                                                vep->tag_i--;
900                                                p--;
901                                        }
902                                }
903                        } while (vm == NULL && p < e);
904                        vep->match_hit = vm;
905                        if (vm == NULL) {
906                                assert(p == e);
907                        } else {
908                                vep->state = *vm->state;
909                                vep->match = NULL;
910                        }
911                } else if (vep->state == VEP_UNTIL) {
912                        /*
913                         * Skip until we see magic string
914                         */
915                        while (p < e) {
916                                if (*p++ != *vep->until_p++) {
917                                        vep->until_p = vep->until;
918                                } else if (*vep->until_p == '\0') {
919                                        vep->state = vep->until_s;
920                                        break;
921                                }
922                        if (p == e && !vep->remove)
923                                vep_mark_verbatim(vep, p);
924                        }
925                } else {
926                        Debug("*** Unknown state %s\n", vep->state);
927                        INCOMPL();
928                }
929        }
930        /*
931         * We must always mark up the storage we got, try to do so
932         * in the most efficient way, in particular with respect to
933         * minimizing and limiting use of pending.
934         */
935        if (p == vep->ver_p) 
936                ;
937        else if (vep->in_esi_tag)
938                vep_mark_skip(vep, p);
939        else if (vep->remove)
940                vep_mark_skip(vep, p);
941        else
942                vep_mark_pending(vep, p);
943}
944
945/*---------------------------------------------------------------------
946 * We receive a ungzip'ed object, and want to store it ungzip'ed.
947 */
948
949static int __match_proto__()
950vfp_esi_bytes_uu(struct sess *sp, struct http_conn *htc, size_t bytes)
951{
952        struct vep_state *vep;
953        ssize_t l, w;
954        struct storage *st;
955
956        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
957        vep = sp->wrk->vep;
958        CHECK_OBJ_NOTNULL(vep, VEP_MAGIC);
959
960        while (bytes > 0) {
961                if (sp->wrk->storage == NULL) {
962                        l = params->fetch_chunksize * 1024LL;
963                        sp->wrk->storage = STV_alloc(sp, l);
964                }
965                if (sp->wrk->storage == NULL) {
966                        errno = ENOMEM;
967                        return (-1);
968                }
969                st = sp->wrk->storage;
970                l = st->space - st->len;
971                if (l > bytes)
972                        l = bytes;
973                w = HTC_Read(htc, st->ptr + st->len, l);
974                if (w <= 0)
975                        return (w);
976                if (vep->hack_p == NULL)
977                        vep->hack_p = (const char *)st->ptr + st->len;
978                vep->ver_p = (const char *)st->ptr + st->len;
979                if (params->esi_syntax & 0x8) {
980                        ssize_t d;
981                        for (l = 0; l < w; l += d)  {
982                                d = (random() & 3) + 1;
983                                if (l + d >= w)
984                                        d = 1;
985                                vep_parse(vep,
986                                    (const char *)st->ptr + st->len + l, d);
987                        }
988                } else
989                        vep_parse(vep, (const char *)st->ptr + st->len, w);
990                st->len += w;
991                sp->obj->len += w;
992                if (st->len == st->space) {
993                        VTAILQ_INSERT_TAIL(&sp->obj->store,
994                            sp->wrk->storage, list);
995                        sp->wrk->storage = NULL;
996                        st = NULL;
997                }
998                bytes -= w;
999        }
1000        return (1);
1001}
1002
1003/*---------------------------------------------------------------------*/
1004
1005static void __match_proto__()
1006vfp_esi_begin(struct sess *sp, size_t estimate)
1007{
1008        struct vep_state *vep;
1009
1010        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1011        AZ(sp->wrk->vep);
1012        /* XXX: snapshot WS ? We'll need the space */
1013        vep = (void*)WS_Alloc(sp->wrk->ws, sizeof *vep);
1014        AN(vep);
1015
1016        Debug("BEGIN %p\n", vep);
1017
1018        memset(vep, 0, sizeof *vep);
1019        vep->magic = VEP_MAGIC;
1020        vep->sp = sp;
1021        vep->bytes = vfp_esi_bytes_uu;
1022        vep->state = VEP_START;
1023        vep->vsb = vsb_newauto();
1024        AN(vep->vsb);
1025        vep->crc = crc32(0L, Z_NULL, 0);
1026        vep->crcp = crc32(0L, Z_NULL, 0);
1027
1028        sp->wrk->vep = vep;
1029        (void)estimate;
1030}
1031
1032static int __match_proto__()
1033vfp_esi_bytes(struct sess *sp, struct http_conn *htc, size_t bytes)
1034{
1035        struct vep_state *vep;
1036
1037        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1038        vep = sp->wrk->vep;
1039        Debug("BYTES %jd\n", (intmax_t)bytes);
1040        CHECK_OBJ_NOTNULL(vep, VEP_MAGIC);
1041        AN(vep->bytes);
1042        return (vep->bytes(sp, htc, bytes));
1043}
1044
1045static int __match_proto__()
1046vfp_esi_end(struct sess *sp)
1047{
1048        struct storage *st;
1049        struct vep_state *vep;
1050        ssize_t l;
1051
1052        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1053        vep = sp->wrk->vep;
1054        sp->wrk->vep = NULL;
1055        Debug("ENDING %p\n", vep);
1056        CHECK_OBJ_NOTNULL(vep, VEP_MAGIC);
1057
1058        l = sp->obj->len - vep->o_total;
1059        assert(l >= 0);
1060        if (vep->o_pending)
1061                vep_mark_common(vep, vep->ver_p, vep->last_mark);
1062        if (vep->o_wait > 0)
1063                vep_emit_common(vep, &vep->o_wait, vep->last_mark);
1064
1065        vsb_finish(vep->vsb);
1066        l = vsb_len(vep->vsb);
1067        if (vep->state != VEP_NOTXML && l > 0) {
1068                Debug("ESI %d <%s>\n", (int)l, vsb_data(vep->vsb));
1069
1070                /* XXX: This is a huge waste of storage... */
1071                sp->obj->esidata = STV_alloc(sp, l);
1072                AN(sp->obj->esidata);
1073                memcpy(sp->obj->esidata->ptr, vsb_data(vep->vsb), l);
1074                sp->obj->esidata->len = l;
1075        }
1076        vsb_delete(vep->vsb);
1077
1078        st = sp->wrk->storage;
1079        sp->wrk->storage = NULL;
1080        if (st == NULL)
1081                return (0);
1082
1083        if (st->len == 0) {
1084                STV_free(st);
1085                return (0);
1086        }
1087        if (st->len < st->space)
1088                STV_trim(st, st->len);
1089        VTAILQ_INSERT_TAIL(&sp->obj->store, st, list);
1090        sp->wrk->vep = NULL;
1091        return (0);
1092}
1093
1094struct vfp vfp_esi = {
1095        .begin  =       vfp_esi_begin,
1096        .bytes  =       vfp_esi_bytes,
1097        .end    =       vfp_esi_end,
1098};
1099
1100#if 0
1101
1102digraph xml {
1103        rankdir="LR"
1104        size="7,10"
1105#################################################################
1106# SECTION A
1107#
1108
1109START           [shape=ellipse]
1110TESTXML         [shape=ellipse]
1111NOTXML          [shape=ellipse]
1112NEXTTAGa        [shape=hexagon, label="NEXTTAG"]
1113STARTTAGa       [shape=hexagon, label="STARTTAG"]
1114START           -> TESTXML
1115START           -> NEXTTAGa     [style=dotted, label="syntax:1"]
1116TESTXML         -> TESTXML      [label="lws"]
1117TESTXML         -> NOTXML
1118TESTXML         -> STARTTAGa    [label="'<'"]
1119
1120#################################################################
1121# SECTION B
1122
1123NOTMYTAG        [shape=ellipse]
1124NEXTTAG         [shape=ellipse]
1125NOTMYTAG        -> NEXTTAG      [style=dotted, label="syntax:2"]
1126STARTTAGb       [shape=hexagon, label="STARTTAG"]
1127NOTMYTAG        -> NEXTTAG      [label="'>'"]
1128NOTMYTAG        -> NOTMYTAG     [label="*"]
1129NEXTTAG         -> NEXTTAG      [label="'-->'"]
1130NEXTTAG         -> NEXTTAG      [label="*"]
1131NEXTTAG         -> STARTTAGb    [label="'<'"]
1132
1133#################################################################
1134# SECTION C
1135
1136STARTTAG        [shape=ellipse]
1137COMMENT         [shape=ellipse]
1138CDATA           [shape=ellipse]
1139ESITAG          [shape=ellipse]
1140ESIETAG         [shape=ellipse]
1141ESIINCLUDE      [shape=ellipse]
1142ESIREMOVE       [shape=ellipse]
1143ESICOMMENT      [shape=ellipse]
1144ESIBOGON        [shape=ellipse]
1145INTAGc          [shape=hexagon, label="INTAG"]
1146NOTMYTAGc       [shape=hexagon, label="NOTMYTAG"]
1147NEXTTAGc        [shape=hexagon, label="NEXTTAG"]
1148TAGERRORc       [shape=hexagon, label="TAGERROR"]
1149C1              [shape=circle,label=""]
1150STARTTAG        -> COMMENT      [label="'<!--'"]
1151STARTTAG        -> ESITAG       [label="'<esi'"]
1152STARTTAG        -> CDATA        [label="'<![CDATA['"]
1153STARTTAG        -> NOTMYTAGc    [label="'*'"]
1154COMMENT         -> NEXTTAGc     [label="'esi'"]
1155COMMENT         -> C1           [label="*"]
1156C1              -> C1           [label="*"]
1157C1              -> NEXTTAGc     [label="-->"]
1158CDATA           -> CDATA        [label="*"]
1159CDATA           -> NEXTTAGc     [label="]]>"]
1160ESITAG          -> ESIINCLUDE   [label="'include'"]
1161ESITAG          -> ESIREMOVE    [label="'remove'"]
1162ESITAG          -> ESICOMMENT   [label="'comment'"]
1163ESITAG          -> ESIBOGON     [label="*"]
1164ESICOMMENT      -> INTAGc
1165ESICOMMENT      -> TAGERRORc
1166ESICOMMENT      -> TAGERRORc    [style=dotted, label="nested\nin\nremove"]
1167ESIREMOVE       -> INTAGc
1168ESIREMOVE       -> TAGERRORc
1169ESIINCLUDE      -> INTAGc
1170ESIINCLUDE      -> TAGERRORc
1171ESIINCLUDE      -> TAGERRORc    [style=dotted, label="nested\nin\nremove"]
1172ESIBOGON        -> TAGERRORc
1173
1174#################################################################
1175# SECTION D
1176
1177INTAG           [shape=ellipse]
1178TAGERROR        [shape=ellipse]
1179NEXTTAGd        [shape=hexagon, label="NEXTTAG"]
1180ATTRd           [shape=hexagon, label="ATTR"]
1181D1              [shape=circle, label=""]
1182D2              [shape=circle, label=""]
1183INTAG           -> D1           [label="lws"]
1184D1              -> D2           [label="/"]
1185INTAG           -> D2           [label="/"]
1186INTAG           -> NEXTTAGd     [label=">"]
1187D1              -> NEXTTAGd     [label=">"]
1188D2              -> NEXTTAGd     [label=">"]
1189D1              -> ATTRd        [label="XMLstartchar"]
1190D1              -> TAGERROR     [label="*"]
1191D2              -> TAGERROR     [label="*"]
1192TAGERROR        -> TAGERROR     [label="*"]
1193TAGERROR        -> NEXTTAGd     [label="'>'"]
1194
1195#################################################################
1196# SECTION E
1197
1198ATTR            [shape=ellipse]
1199SKIPATTR        [shape=ellipse]
1200ATTRGETVAL      [shape=ellipse]
1201ATTRDELIM       [shape=ellipse]
1202ATTRVAL         [shape=ellipse]
1203TAGERRORe       [shape=hexagon, label="TAGERROR"]
1204INTAGe          [shape=hexagon, label="INTAG"]
1205ATTR            -> SKIPATTR     [label="*"]
1206ATTR            -> ATTRGETVAL   [label="wanted attr"]
1207SKIPATTR        -> SKIPATTR     [label="XMLname"]
1208SKIPATTR        -> ATTRDELIM    [label="'='"]
1209SKIPATTR        -> TAGERRORe    [label="*"]
1210ATTRGETVAL      -> ATTRDELIM
1211ATTRDELIM       -> ATTRVAL      [label="\""]
1212ATTRDELIM       -> ATTRVAL      [label="\'"]
1213ATTRDELIM       -> ATTRVAL      [label="*"]
1214ATTRDELIM       -> TAGERRORe    [label="lws"]
1215ATTRVAL         -> TAGERRORe    [label="'>'"]
1216ATTRVAL         -> INTAGe       [label="delim"]
1217ATTRVAL         -> ATTRVAL      [label="*"]
1218
1219}
1220
1221#endif
Note: See TracBrowser for help on using the repository browser.