source: bin/varnishd/cache_esi_parse.c @ 0b06c85

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

Replay of SVN r5764:

Retire the old ESI code.

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