source: bin/varnishd/cache_center.c @ 8f85e3

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

Move request vary string from wrk to sess

  • Property mode set to 100644
Line 
1/*-
2 * Copyright (c) 2006 Verdens Gang AS
3 * Copyright (c) 2006-2011 Varnish Software AS
4 * All rights reserved.
5 *
6 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * This file contains the central state machine for pushing requests.
30 *
31 * We cannot just use direct calls because it is possible to kick a
32 * request back to the lookup stage (usually after a rewrite).  The
33 * state engine also allows us to break the processing up into some
34 * logical chunks which improves readability a little bit.
35 *
36 * Since the states are rather nasty in detail, I have decided to embedd
37 * a dot(1) graph in the source code comments.  So to see the big picture,
38 * extract the DOT lines and run though dot(1), for instance with the
39 * command:
40 *      sed -n '/^DOT/s///p' cache_center.c | dot -Tps > /tmp/_.ps
41 */
42
43/*
44DOT digraph vcl_center {
45xDOT    page="8.2,11.5"
46DOT     size="7.2,10.5"
47DOT     margin="0.5"
48DOT     center="1"
49DOT acceptor [
50DOT     shape=hexagon
51DOT     label="Request received"
52DOT ]
53DOT ERROR [shape=plaintext]
54DOT RESTART [shape=plaintext]
55DOT acceptor -> start [style=bold,color=green]
56 */
57
58#include "config.h"
59
60#include <stdio.h>
61#include <errno.h>
62#include <math.h>
63#include <poll.h>
64#include <stdlib.h>
65#include <string.h>
66#include <unistd.h>
67
68#ifndef HAVE_SRANDOMDEV
69#include "compat/srandomdev.h"
70#endif
71
72#include "vcl.h"
73#include "cli_priv.h"
74#include "cache.h"
75#include "hash_slinger.h"
76#include "stevedore.h"
77#include "vsha256.h"
78
79static unsigned xids;
80
81/*--------------------------------------------------------------------
82 * WAIT
83 * Wait (briefly) until we have a full request in our htc.
84 */
85
86static int
87cnt_wait(struct sess *sp)
88{
89        int i;
90        struct pollfd pfd[1];
91
92        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
93        AZ(sp->vcl);
94        AZ(sp->obj);
95        assert(sp->xid == 0);
96
97        i = HTC_Complete(sp->htc);
98        if (i == 0 && params->session_linger > 0) {
99                pfd[0].fd = sp->fd;
100                pfd[0].events = POLLIN;
101                pfd[0].revents = 0;
102                i = poll(pfd, 1, params->session_linger);
103                if (i)
104                        i = HTC_Rx(sp->htc);
105        }
106        if (i == 0) {
107                WSL(sp->wrk, SLT_Debug, sp->fd, "herding");
108                sp->wrk->stats.sess_herd++;
109                SES_Charge(sp);
110                sp->wrk = NULL;
111                vca_return_session(sp);
112                return (1);
113        }
114        if (i == 1) {
115                sp->step = STP_START;
116                return (0);
117        }
118        if (i == -2) {
119                vca_close_session(sp, "overflow");
120                return (0);
121        }
122        if (i == -1 && Tlen(sp->htc->rxbuf) == 0 &&
123            (errno == 0 || errno == ECONNRESET))
124                vca_close_session(sp, "EOF");
125        else
126                vca_close_session(sp, "error");
127        sp->step = STP_DONE;
128        return (0);
129}
130
131/*--------------------------------------------------------------------
132 * We have a refcounted object on the session, now deliver it.
133 *
134DOT subgraph xcluster_prepresp {
135DOT     prepresp [
136DOT             shape=ellipse
137DOT             label="Filter obj.->resp."
138DOT     ]
139DOT     vcl_deliver [
140DOT             shape=record
141DOT             label="vcl_deliver()|resp."
142DOT     ]
143DOT     prepresp -> vcl_deliver [style=bold,color=green]
144DOT     prepresp -> vcl_deliver [style=bold,color=cyan]
145DOT     prepresp -> vcl_deliver [style=bold,color=red]
146DOT     prepresp -> vcl_deliver [style=bold,color=blue,]
147DOT     vcl_deliver -> deliver [style=bold,color=green,label=deliver]
148DOT     vcl_deliver -> deliver [style=bold,color=red]
149DOT     vcl_deliver -> deliver [style=bold,color=blue]
150DOT     vcl_deliver -> errdeliver [label="error"]
151DOT     errdeliver [label="ERROR",shape=plaintext]
152DOT     vcl_deliver -> rstdeliver [label="restart",color=purple]
153DOT     rstdeliver [label="RESTART",shape=plaintext]
154DOT     vcl_deliver -> streambody [style=bold,color=cyan,label="deliver"]
155DOT }
156 *
157 */
158
159static int
160cnt_prepresp(struct sess *sp)
161{
162
163        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
164        CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
165        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
166
167        sp->wrk->res_mode = 0;
168
169        if (!sp->wrk->do_stream ||
170            (sp->wrk->h_content_length != NULL && !sp->wrk->do_gunzip))
171                sp->wrk->res_mode |= RES_LEN;
172
173        if (!sp->disable_esi && sp->obj->esidata != NULL) {
174                /* In ESI mode, we don't know the aggregate length */
175                sp->wrk->res_mode &= ~RES_LEN;
176                sp->wrk->res_mode |= RES_ESI;
177        }
178
179        if (sp->esi_level > 0) {
180                sp->wrk->res_mode &= ~RES_LEN;
181                sp->wrk->res_mode |= RES_ESI_CHILD;
182        }
183
184        if (params->http_gzip_support && sp->obj->gziped &&
185            !RFC2616_Req_Gzip(sp)) {
186                /*
187                 * We don't know what it uncompresses to
188                 * XXX: we could cache that
189                 */
190                sp->wrk->res_mode &= ~RES_LEN;
191                sp->wrk->res_mode |= RES_GUNZIP;
192        }
193
194        if (!(sp->wrk->res_mode & (RES_LEN|RES_CHUNKED|RES_EOF))) {
195                if (sp->obj->len == 0 && !sp->wrk->do_stream)
196                        /*
197                         * If the object is empty, neither ESI nor GUNZIP
198                         * can make it any different size
199                         */
200                        sp->wrk->res_mode |= RES_LEN;
201                else if (!sp->wantbody) {
202                        /* Nothing */
203                } else if (sp->http->protover >= 11) {
204                        sp->wrk->res_mode |= RES_CHUNKED;
205                } else {
206                        sp->wrk->res_mode |= RES_EOF;
207                        sp->doclose = "EOF mode";
208                }
209        }
210
211        sp->t_resp = TIM_real();
212        if (sp->obj->objcore != NULL) {
213                if ((sp->t_resp - sp->obj->last_lru) > params->lru_timeout &&
214                    EXP_Touch(sp->obj->objcore))
215                        sp->obj->last_lru = sp->t_resp;
216                sp->obj->last_use = sp->t_resp; /* XXX: locking ? */
217        }
218        http_Setup(sp->wrk->resp, sp->wrk->ws);
219        RES_BuildHttp(sp);
220        VCL_deliver_method(sp);
221        switch (sp->handling) {
222        case VCL_RET_DELIVER:
223                break;
224        case VCL_RET_RESTART:
225                if (sp->restarts >= params->max_restarts)
226                        break;
227                if (sp->wrk->do_stream) {
228                        VDI_CloseFd(sp);
229                        HSH_Drop(sp);
230                } else {
231                        (void)HSH_Deref(sp->wrk, NULL, &sp->obj);
232                }
233                AZ(sp->obj);
234                sp->restarts++;
235                sp->director = NULL;
236                http_Setup(sp->wrk->bereq, NULL);
237                http_Setup(sp->wrk->beresp, NULL);
238                http_Setup(sp->wrk->resp, NULL);
239                sp->step = STP_RECV;
240                return (0);
241        default:
242                WRONG("Illegal action in vcl_deliver{}");
243        }
244        if (sp->wrk->do_stream)
245                sp->step = STP_STREAMBODY;
246        else
247                sp->step = STP_DELIVER;
248        return (0);
249}
250
251/*--------------------------------------------------------------------
252 * Deliver an already stored object
253 *
254DOT subgraph xcluster_deliver {
255DOT     deliver [
256DOT             shape=ellipse
257DOT             label="Send body"
258DOT     ]
259DOT }
260DOT deliver -> DONE [style=bold,color=green]
261DOT deliver -> DONE [style=bold,color=red]
262DOT deliver -> DONE [style=bold,color=blue]
263 *
264 */
265
266static int
267cnt_deliver(struct sess *sp)
268{
269
270        sp->director = NULL;
271        sp->restarts = 0;
272
273        RES_WriteObj(sp);
274
275        assert(WRW_IsReleased(sp->wrk));
276        assert(sp->wrk->wrw.ciov == sp->wrk->wrw.siov);
277        (void)HSH_Deref(sp->wrk, NULL, &sp->obj);
278        http_Setup(sp->wrk->resp, NULL);
279        sp->step = STP_DONE;
280        return (0);
281}
282
283/*--------------------------------------------------------------------
284 * This is the final state, figure out if we should close or recycle
285 * the client connection
286 *
287DOT     DONE [
288DOT             shape=hexagon
289DOT             label="Request completed"
290DOT     ]
291 */
292
293static int
294cnt_done(struct sess *sp)
295{
296        double dh, dp, da;
297        int i;
298
299        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
300        CHECK_OBJ_ORNULL(sp->vcl, VCL_CONF_MAGIC);
301
302        AZ(sp->obj);
303        AZ(sp->vbc);
304        sp->director = NULL;
305        sp->restarts = 0;
306
307        if (sp->vcl != NULL && sp->esi_level == 0) {
308                if (sp->wrk->vcl != NULL)
309                        VCL_Rel(&sp->wrk->vcl);
310                sp->wrk->vcl = sp->vcl;
311                sp->vcl = NULL;
312        }
313
314        SES_Charge(sp);
315
316        sp->t_end = TIM_real();
317        sp->wrk->lastused = sp->t_end;
318        if (sp->xid == 0) {
319                sp->t_req = sp->t_end;
320                sp->t_resp = sp->t_end;
321        } else if (sp->esi_level == 0) {
322                dp = sp->t_resp - sp->t_req;
323                da = sp->t_end - sp->t_resp;
324                dh = sp->t_req - sp->t_open;
325                /* XXX: Add StatReq == StatSess */
326                /* XXX: Workaround for pipe */
327                if (sp->fd >= 0) {
328                        WSP(sp, SLT_Length, "%ju", (uintmax_t)sp->acct_req.bodybytes);
329                }
330                WSL(sp->wrk, SLT_ReqEnd, sp->id, "%u %.9f %.9f %.9f %.9f %.9f",
331                    sp->xid, sp->t_req, sp->t_end, dh, dp, da);
332        }
333        sp->xid = 0;
334        sp->t_open = sp->t_end;
335        sp->t_resp = NAN;
336        WSL_Flush(sp->wrk, 0);
337
338        /* If we did an ESI include, don't mess up our state */
339        if (sp->esi_level > 0)
340                return (1);
341
342        memset(&sp->acct_req, 0, sizeof sp->acct_req);
343
344        sp->t_req = NAN;
345        sp->hash_always_miss = 0;
346        sp->hash_ignore_busy = 0;
347
348        if (sp->fd >= 0 && sp->doclose != NULL) {
349                /*
350                 * This is an orderly close of the connection; ditch nolinger
351                 * before we close, to get queued data transmitted.
352                 */
353                // XXX: not yet (void)VTCP_linger(sp->fd, 0);
354                vca_close_session(sp, sp->doclose);
355        }
356
357        if (sp->fd < 0) {
358                sp->wrk->stats.sess_closed++;
359                sp->wrk = NULL;
360                SES_Delete(sp);
361                return (1);
362        }
363
364        if (sp->wrk->stats.client_req >= params->wthread_stats_rate)
365                WRK_SumStat(sp->wrk);
366        /* Reset the workspace to the session-watermark */
367        WS_Reset(sp->ws, sp->ws_ses);
368        WS_Reset(sp->wrk->ws, NULL);
369
370        i = HTC_Reinit(sp->htc);
371        if (i == 1) {
372                sp->wrk->stats.sess_pipeline++;
373                sp->step = STP_START;
374                return (0);
375        }
376        if (Tlen(sp->htc->rxbuf)) {
377                sp->wrk->stats.sess_readahead++;
378                sp->step = STP_WAIT;
379                return (0);
380        }
381        if (params->session_linger > 0) {
382                sp->wrk->stats.sess_linger++;
383                sp->step = STP_WAIT;
384                return (0);
385        }
386        sp->wrk->stats.sess_herd++;
387        sp->wrk = NULL;
388        vca_return_session(sp);
389        return (1);
390}
391
392/*--------------------------------------------------------------------
393 * Emit an error
394 *
395DOT subgraph xcluster_error {
396DOT     vcl_error [
397DOT             shape=record
398DOT             label="vcl_error()|resp."
399DOT     ]
400DOT     ERROR -> vcl_error
401DOT     vcl_error-> prepresp [label=deliver]
402DOT }
403 */
404
405static int
406cnt_error(struct sess *sp)
407{
408        struct worker *w;
409        struct http *h;
410        char date[40];
411
412        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
413
414        w = sp->wrk;
415        if (sp->obj == NULL) {
416                HSH_Prealloc(sp);
417                /* XXX: 1024 is a pure guess */
418                EXP_Clr(&w->exp);
419                sp->obj = STV_NewObject(sp, NULL, 1024, &w->exp,
420                     params->http_max_hdr);
421                if (sp->obj == NULL) 
422                        sp->obj = STV_NewObject(sp, TRANSIENT_STORAGE,
423                            1024, &w->exp, params->http_max_hdr);
424                if (sp->obj == NULL) {
425                        sp->doclose = "Out of objects";
426                        sp->step = STP_DONE;
427                        return(0);
428                }
429                AN(sp->obj);
430                sp->obj->xid = sp->xid;
431                sp->obj->entered = sp->t_req;
432        } else {
433                /* XXX: Null the headers ? */
434        }
435        CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
436        h = sp->obj->http;
437
438        if (sp->err_code < 100 || sp->err_code > 999)
439                sp->err_code = 501;
440
441        http_PutProtocol(w, sp->fd, h, "HTTP/1.1");
442        http_PutStatus(h, sp->err_code);
443        TIM_format(TIM_real(), date);
444        http_PrintfHeader(w, sp->fd, h, "Date: %s", date);
445        http_PrintfHeader(w, sp->fd, h, "Server: Varnish");
446
447        if (sp->err_reason != NULL)
448                http_PutResponse(w, sp->fd, h, sp->err_reason);
449        else
450                http_PutResponse(w, sp->fd, h,
451                    http_StatusMessage(sp->err_code));
452        VCL_error_method(sp);
453
454        if (sp->handling == VCL_RET_RESTART &&
455            sp->restarts <  params->max_restarts) {
456                HSH_Drop(sp);
457                sp->director = NULL;
458                sp->restarts++;
459                sp->step = STP_RECV;
460                return (0);
461        } else if (sp->handling == VCL_RET_RESTART)
462                sp->handling = VCL_RET_DELIVER;
463
464
465        /* We always close when we take this path */
466        sp->doclose = "error";
467        sp->wantbody = 1;
468
469        assert(sp->handling == VCL_RET_DELIVER);
470        sp->err_code = 0;
471        sp->err_reason = NULL;
472        http_Setup(sp->wrk->bereq, NULL);
473        sp->step = STP_PREPRESP;
474        return (0);
475}
476
477/*--------------------------------------------------------------------
478 * Fetch response headers from the backend
479 *
480DOT subgraph xcluster_fetch {
481DOT     fetch [
482DOT             shape=ellipse
483DOT             label="fetch hdr\nfrom backend\n(find obj.ttl)"
484DOT     ]
485DOT     vcl_fetch [
486DOT             shape=record
487DOT             label="vcl_fetch()|req.\nbereq.\nberesp."
488DOT     ]
489DOT     fetch -> vcl_fetch [style=bold,color=blue]
490DOT     fetch -> vcl_fetch [style=bold,color=red]
491DOT     fetch_pass [
492DOT             shape=ellipse
493DOT             label="obj.f.pass=true"
494DOT     ]
495DOT     vcl_fetch -> fetch_pass [label="hit_for_pass",style=bold,color=red]
496DOT }
497DOT fetch_pass -> fetchbody [style=bold,color=red]
498DOT vcl_fetch -> fetchbody [label="deliver",style=bold,color=blue]
499DOT vcl_fetch -> rstfetch [label="restart",color=purple]
500DOT rstfetch [label="RESTART",shape=plaintext]
501DOT fetch -> errfetch
502DOT vcl_fetch -> errfetch [label="error"]
503DOT errfetch [label="ERROR",shape=plaintext]
504 */
505
506static int
507cnt_fetch(struct sess *sp)
508{
509        int i;
510
511        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
512        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
513
514        AN(sp->director);
515        AZ(sp->vbc);
516        AZ(sp->wrk->h_content_length);
517        AZ(sp->wrk->do_close);
518        AZ(sp->wrk->storage_hint);
519
520        http_Setup(sp->wrk->beresp, sp->wrk->ws);
521
522        i = FetchHdr(sp);
523        /*
524         * If we recycle a backend connection, there is a finite chance
525         * that the backend closed it before we get a request to it.
526         * Do a single retry in that case.
527         */
528        if (i == 1) {
529                VSC_C_main->backend_retry++;
530                i = FetchHdr(sp);
531        }
532
533        if (i) {
534                sp->handling = VCL_RET_ERROR;
535                sp->err_code = 503;
536        } else {
537                /*
538                 * These two headers can be spread over multiple actual headers
539                 * and we rely on their content outside of VCL, so collect them
540                 * into one line here.
541                 */
542                http_CollectHdr(sp->wrk->beresp, H_Cache_Control);
543                http_CollectHdr(sp->wrk->beresp, H_Vary);
544
545                /*
546                 * Figure out how the fetch is supposed to happen, before the
547                 * headers are adultered by VCL
548                 * NB: Also sets other sp->wrk variables
549                 */
550                sp->wrk->body_status = RFC2616_Body(sp);
551
552                sp->err_code = http_GetStatus(sp->wrk->beresp);
553
554                /*
555                 * What does RFC2616 think about TTL ?
556                 */
557                sp->wrk->entered = TIM_real();
558                sp->wrk->age = 0;
559                EXP_Clr(&sp->wrk->exp);
560                sp->wrk->exp.ttl = RFC2616_Ttl(sp);
561
562                /* pass from vclrecv{} has negative TTL */
563                if (sp->objcore == NULL)
564                        sp->wrk->exp.ttl = -1.;
565
566                sp->wrk->do_esi = 0;
567
568                VCL_fetch_method(sp);
569
570                switch (sp->handling) {
571                case VCL_RET_HIT_FOR_PASS:
572                        if (sp->objcore != NULL)
573                                sp->objcore->flags |= OC_F_PASS;
574                        sp->step = STP_FETCHBODY;
575                        return (0);
576                case VCL_RET_DELIVER:
577                        sp->step = STP_FETCHBODY;
578                        return (0);
579                default:
580                        break;
581                }
582
583                /* We are not going to fetch the body, Close the connection */
584                VDI_CloseFd(sp);
585        }
586
587        /* Clean up partial fetch */
588        AZ(sp->vbc);
589
590        if (sp->objcore != NULL) {
591                CHECK_OBJ_NOTNULL(sp->objcore, OBJCORE_MAGIC);
592                AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
593                sp->objcore = NULL;
594        }
595        http_Setup(sp->wrk->bereq, NULL);
596        http_Setup(sp->wrk->beresp, NULL);
597        sp->wrk->h_content_length = NULL;
598        sp->director = NULL;
599        sp->wrk->storage_hint = NULL;
600
601        switch (sp->handling) {
602        case VCL_RET_RESTART:
603                sp->restarts++;
604                sp->step = STP_RECV;
605                return (0);
606        case VCL_RET_ERROR:
607                sp->step = STP_ERROR;
608                return (0);
609        default:
610                WRONG("Illegal action in vcl_fetch{}");
611        }
612}
613
614/*--------------------------------------------------------------------
615 * Fetch response body from the backend
616 *
617DOT subgraph xcluster_body {
618DOT     fetchbody [
619DOT             shape=diamond
620DOT             label="stream ?"
621DOT     ]
622DOT     fetchbody2 [
623DOT             shape=ellipse
624DOT             label="fetch body\nfrom backend\n"
625DOT     ]
626DOT }
627DOT fetchbody -> fetchbody2 [label=no,style=bold,color=red]
628DOT fetchbody -> fetchbody2 [style=bold,color=blue]
629DOT fetchbody -> prepresp [label=yes,style=bold,color=cyan]
630DOT fetchbody2 -> prepresp [style=bold,color=red]
631DOT fetchbody2 -> prepresp [style=bold,color=blue]
632 */
633
634
635static int
636cnt_fetchbody(struct sess *sp)
637{
638        int i;
639        struct http *hp, *hp2;
640        char *b;
641        unsigned l, nhttp;
642        struct vsb *vary = NULL;
643        int varyl = 0, pass;
644
645        assert(sp->handling == VCL_RET_HIT_FOR_PASS ||
646            sp->handling == VCL_RET_DELIVER);
647
648        if (sp->objcore == NULL) {
649                /* This is a pass from vcl_recv */
650                pass = 1;
651                /* VCL may have fiddled this, but that doesn't help */
652                sp->wrk->exp.ttl = -1.;
653        } else if (sp->handling == VCL_RET_HIT_FOR_PASS) {
654                /* pass from vcl_fetch{} -> hit-for-pass */
655                /* XXX: the bereq was not filtered pass... */
656                pass = 1;
657        } else {
658                /* regular object */
659                pass = 0;
660        }
661
662        /*
663         * The VCL variables beresp.do_g[un]zip tells us how we want the
664         * object processed before it is stored.
665         *
666         * The backend Content-Encoding header tells us what we are going
667         * to receive, which we classify in the following three classes:
668         *
669         *      "Content-Encoding: gzip"        --> object is gzip'ed.
670         *      no Content-Encoding             --> object is not gzip'ed.
671         *      anything else                   --> do nothing wrt gzip
672         *
673         */
674
675        AZ(sp->wrk->vfp);
676
677        /* We do nothing unless the param is set */
678        if (!params->http_gzip_support)
679                sp->wrk->do_gzip = sp->wrk->do_gunzip = 0;
680
681        sp->wrk->is_gzip =
682            http_HdrIs(sp->wrk->beresp, H_Content_Encoding, "gzip");
683
684        sp->wrk->is_gunzip =
685            !http_GetHdr(sp->wrk->beresp, H_Content_Encoding, NULL);
686
687        /* It can't be both */
688        assert(sp->wrk->is_gzip == 0 || sp->wrk->is_gunzip == 0);
689
690        /* We won't gunzip unless it is gzip'ed */
691        if (sp->wrk->do_gunzip && !sp->wrk->is_gzip)
692                sp->wrk->do_gunzip = 0;
693
694        /* If we do gunzip, remove the C-E header */
695        if (sp->wrk->do_gunzip)
696                http_Unset(sp->wrk->beresp, H_Content_Encoding);
697
698        /* We wont gzip unless it is ungziped */
699        if (sp->wrk->do_gzip && !sp->wrk->is_gunzip)
700                sp->wrk->do_gzip = 0;
701
702        /* If we do gzip, add the C-E header */
703        if (sp->wrk->do_gzip)
704                http_PrintfHeader(sp->wrk, sp->fd, sp->wrk->beresp,
705                    "Content-Encoding: %s", "gzip");
706
707        /* But we can't do both at the same time */
708        assert(sp->wrk->do_gzip == 0 || sp->wrk->do_gunzip == 0);
709
710        /* ESI takes precedence and handles gzip/gunzip itself */
711        if (sp->wrk->do_esi)
712                sp->wrk->vfp = &vfp_esi;
713        else if (sp->wrk->do_gunzip)
714                sp->wrk->vfp = &vfp_gunzip;
715        else if (sp->wrk->do_gzip)
716                sp->wrk->vfp = &vfp_gzip;
717        else if (sp->wrk->is_gzip)
718                sp->wrk->vfp = &vfp_testgzip;
719
720        if (sp->wrk->do_esi)
721                sp->wrk->do_stream = 0;
722        if (!sp->wantbody)
723                sp->wrk->do_stream = 0;
724
725        l = http_EstimateWS(sp->wrk->beresp,
726            pass ? HTTPH_R_PASS : HTTPH_A_INS, &nhttp);
727
728        /* Create Vary instructions */
729        if (sp->objcore != NULL) {
730                CHECK_OBJ_NOTNULL(sp->objcore, OBJCORE_MAGIC);
731                vary = VRY_Create(sp, sp->wrk->beresp);
732                if (vary != NULL) {
733                        varyl = VSB_len(vary);
734                        assert(varyl > 0);
735                        l += varyl;
736                }
737        }
738
739        /*
740         * Space for producing a Content-Length: header including padding
741         * A billion gigabytes is enough for anybody.
742         */
743        l += strlen("Content-Length: XxxXxxXxxXxxXxxXxx") + sizeof(void *);
744
745        if (sp->wrk->exp.ttl < params->shortlived || sp->objcore == NULL)
746                sp->wrk->storage_hint = TRANSIENT_STORAGE;
747
748        sp->obj = STV_NewObject(sp, sp->wrk->storage_hint, l,
749            &sp->wrk->exp, nhttp);
750        if (sp->obj == NULL) {
751                /*
752                 * Try to salvage the transaction by allocating a
753                 * shortlived object on Transient storage.
754                 */
755                sp->obj = STV_NewObject(sp, TRANSIENT_STORAGE, l,
756                    &sp->wrk->exp, nhttp);
757                sp->wrk->exp.ttl = params->shortlived;
758        }
759        if (sp->obj == NULL) {
760                sp->err_code = 503;
761                sp->step = STP_ERROR;
762                VDI_CloseFd(sp);
763                return (0);
764        }
765        CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
766
767        sp->wrk->storage_hint = NULL;
768
769        if (sp->wrk->do_gzip || (sp->wrk->is_gzip && !sp->wrk->do_gunzip))
770                sp->obj->gziped = 1;
771
772        if (vary != NULL) {
773                sp->obj->vary =
774                    (void *)WS_Alloc(sp->obj->http->ws, varyl);
775                AN(sp->obj->vary);
776                memcpy(sp->obj->vary, VSB_data(vary), varyl);
777                VSB_delete(vary);
778        }
779
780        sp->obj->xid = sp->xid;
781        sp->obj->response = sp->err_code;
782        sp->obj->age = sp->wrk->age;
783        sp->obj->entered = sp->wrk->entered;
784        WS_Assert(sp->obj->ws_o);
785
786        /* Filter into object */
787        hp = sp->wrk->beresp;
788        hp2 = sp->obj->http;
789
790        hp2->logtag = HTTP_Obj;
791        http_CopyResp(hp2, hp);
792        http_FilterFields(sp->wrk, sp->fd, hp2, hp,
793            pass ? HTTPH_R_PASS : HTTPH_A_INS);
794        http_CopyHome(sp->wrk, sp->fd, hp2);
795
796        if (http_GetHdr(hp, H_Last_Modified, &b))
797                sp->obj->last_modified = TIM_parse(b);
798        else
799                sp->obj->last_modified = floor(sp->wrk->entered);
800
801        assert(WRW_IsReleased(sp->wrk));
802
803        if (sp->wrk->do_stream) {
804                sp->step = STP_PREPRESP;
805                return (0);
806        }
807
808        /* Use unmodified headers*/
809        i = FetchBody(sp);
810
811        sp->wrk->h_content_length = NULL;
812
813        http_Setup(sp->wrk->bereq, NULL);
814        http_Setup(sp->wrk->beresp, NULL);
815        sp->wrk->vfp = NULL;
816        assert(WRW_IsReleased(sp->wrk));
817        AZ(sp->vbc);
818        AN(sp->director);
819
820        if (i) {
821                HSH_Drop(sp);
822                AZ(sp->obj);
823                sp->err_code = 503;
824                sp->step = STP_ERROR;
825                return (0);
826        }
827
828        if (sp->obj->objcore != NULL) {
829                EXP_Insert(sp->obj);
830                AN(sp->obj->objcore);
831                AN(sp->obj->objcore->ban);
832                HSH_Unbusy(sp);
833        }
834        sp->acct_tmp.fetch++;
835        sp->step = STP_PREPRESP;
836        return (0);
837}
838
839/*--------------------------------------------------------------------
840 * Stream the body as we fetch it
841DOT subgraph xstreambody {
842DOT     streambody [
843DOT             shape=ellipse
844DOT             label="streaming\nfetch/deliver"
845DOT     ]
846DOT }
847DOT streambody -> DONE [style=bold,color=cyan]
848 */
849
850static int
851cnt_streambody(struct sess *sp)
852{
853        int i;
854        struct stream_ctx sctx;
855        uint8_t obuf[sp->wrk->res_mode & RES_GUNZIP ?
856            params->gzip_stack_buffer : 1];
857
858        memset(&sctx, 0, sizeof sctx);
859        sctx.magic = STREAM_CTX_MAGIC;
860        AZ(sp->wrk->sctx);
861        sp->wrk->sctx = &sctx;
862
863        if (sp->wrk->res_mode & RES_GUNZIP) {
864                sctx.vgz = VGZ_NewUngzip(sp, "U S -");
865                sctx.obuf = obuf;
866                sctx.obuf_len = sizeof (obuf);
867        }
868
869        RES_StreamStart(sp);
870
871        i = FetchBody(sp);
872
873        sp->wrk->h_content_length = NULL;
874
875        http_Setup(sp->wrk->bereq, NULL);
876        http_Setup(sp->wrk->beresp, NULL);
877        sp->wrk->vfp = NULL;
878        AZ(sp->vbc);
879        AN(sp->director);
880
881        if (!i && sp->obj->objcore != NULL) {
882                EXP_Insert(sp->obj);
883                AN(sp->obj->objcore);
884                AN(sp->obj->objcore->ban);
885                HSH_Unbusy(sp);
886        } else {
887                sp->doclose = "Stream error";
888        }
889        sp->acct_tmp.fetch++;
890        sp->director = NULL;
891        sp->restarts = 0;
892
893        RES_StreamEnd(sp);
894        if (sp->wrk->res_mode & RES_GUNZIP)
895                VGZ_Destroy(&sctx.vgz);
896
897        sp->wrk->sctx = NULL;
898        assert(WRW_IsReleased(sp->wrk));
899        assert(sp->wrk->wrw.ciov == sp->wrk->wrw.siov);
900        (void)HSH_Deref(sp->wrk, NULL, &sp->obj);
901        http_Setup(sp->wrk->resp, NULL);
902        sp->step = STP_DONE;
903        return (0);
904}
905
906/*--------------------------------------------------------------------
907 * The very first request
908 */
909static int
910cnt_first(struct sess *sp)
911{
912
913        /*
914         * XXX: If we don't have acceptfilters we are somewhat subject
915         * XXX: to DoS'ing here.  One remedy would be to set a shorter
916         * XXX: SO_RCVTIMEO and once we have received something here
917         * XXX: increase it to the normal value.
918         */
919
920        assert(sp->xid == 0);
921        assert(sp->restarts == 0);
922        VCA_Prep(sp);
923
924        /* Record the session watermark */
925        sp->ws_ses = WS_Snapshot(sp->ws);
926
927        /* Receive a HTTP protocol request */
928        HTC_Init(sp->htc, sp->ws, sp->fd, params->http_req_size,
929            params->http_req_hdr_len);
930        sp->wrk->lastused = sp->t_open;
931        sp->acct_tmp.sess++;
932
933        sp->step = STP_WAIT;
934        return (0);
935}
936
937/*--------------------------------------------------------------------
938 * HIT
939 * We had a cache hit.  Ask VCL, then march off as instructed.
940 *
941DOT subgraph xcluster_hit {
942DOT     hit [
943DOT             shape=record
944DOT             label="vcl_hit()|req.\nobj."
945DOT     ]
946DOT }
947DOT hit -> err_hit [label="error"]
948DOT err_hit [label="ERROR",shape=plaintext]
949DOT hit -> rst_hit [label="restart",color=purple]
950DOT rst_hit [label="RESTART",shape=plaintext]
951DOT hit -> pass [label=pass,style=bold,color=red]
952DOT hit -> prepresp [label="deliver",style=bold,color=green]
953 */
954
955static int
956cnt_hit(struct sess *sp)
957{
958
959        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
960        CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
961        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
962
963        assert(!(sp->obj->objcore->flags & OC_F_PASS));
964
965        VCL_hit_method(sp);
966
967        if (sp->handling == VCL_RET_DELIVER) {
968                /* Dispose of any body part of the request */
969                (void)FetchReqBody(sp);
970                AZ(sp->wrk->bereq->ws);
971                AZ(sp->wrk->beresp->ws);
972                sp->step = STP_PREPRESP;
973                return (0);
974        }
975
976        /* Drop our object, we won't need it */
977        (void)HSH_Deref(sp->wrk, NULL, &sp->obj);
978        sp->objcore = NULL;
979
980        switch(sp->handling) {
981        case VCL_RET_PASS:
982                sp->step = STP_PASS;
983                return (0);
984        case VCL_RET_ERROR:
985                sp->step = STP_ERROR;
986                return (0);
987        case VCL_RET_RESTART:
988                sp->director = NULL;
989                sp->restarts++;
990                sp->step = STP_RECV;
991                return (0);
992        default:
993                WRONG("Illegal action in vcl_hit{}");
994        }
995}
996
997/*--------------------------------------------------------------------
998 * LOOKUP
999 * Hash things together and look object up in hash-table.
1000 *
1001 * LOOKUP consists of two substates so that we can reenter if we
1002 * encounter a busy object.
1003 *
1004DOT subgraph xcluster_lookup {
1005DOT     hash [
1006DOT             shape=record
1007DOT             label="vcl_hash()|req."
1008DOT     ]
1009DOT     lookup [
1010DOT             shape=diamond
1011DOT             label="obj in cache ?\ncreate if not"
1012DOT     ]
1013DOT     lookup2 [
1014DOT             shape=diamond
1015DOT             label="obj.f.pass ?"
1016DOT     ]
1017DOT     hash -> lookup [label="hash",style=bold,color=green]
1018DOT     lookup -> lookup2 [label="yes",style=bold,color=green]
1019DOT }
1020DOT lookup2 -> hit [label="no", style=bold,color=green]
1021DOT lookup2 -> pass [label="yes",style=bold,color=red]
1022DOT lookup -> miss [label="no",style=bold,color=blue]
1023 */
1024
1025static int
1026cnt_lookup(struct sess *sp)
1027{
1028        struct objcore *oc;
1029        struct object *o;
1030        struct objhead *oh;
1031
1032        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1033        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
1034
1035        if (sp->hash_objhead == NULL) {
1036                /* Not a waiting list return */
1037                AZ(sp->vary_b);
1038                AZ(sp->vary_l);
1039                AZ(sp->vary_e);
1040                (void)WS_Reserve(sp->ws, 0);
1041                sp->vary_b = (void*)sp->ws->f;
1042                sp->vary_e = (void*)sp->ws->r;
1043                sp->vary_b[2] = '\0';
1044        }
1045
1046        oc = HSH_Lookup(sp, &oh);
1047
1048        if (oc == NULL) {
1049                /*
1050                 * We lost the session to a busy object, disembark the
1051                 * worker thread.   The hash code to restart the session,
1052                 * still in STP_LOOKUP, later when the busy object isn't.
1053                 * NB:  Do not access sp any more !
1054                 */
1055                return (1);
1056        }
1057
1058
1059        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
1060        CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
1061
1062        /* If we inserted a new object it's a miss */
1063        if (oc->flags & OC_F_BUSY) {
1064                sp->wrk->stats.cache_miss++;
1065
1066                if (sp->vary_l != NULL)
1067                        WS_ReleaseP(sp->ws, (void*)sp->vary_l);
1068                else
1069                        WS_Release(sp->ws, 0);
1070                sp->vary_b = NULL;
1071                sp->vary_l = NULL;
1072                sp->vary_e = NULL;
1073
1074                sp->objcore = oc;
1075                sp->step = STP_MISS;
1076                return (0);
1077        }
1078
1079        o = oc_getobj(sp->wrk, oc);
1080        CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
1081        sp->obj = o;
1082
1083        WS_Release(sp->ws, 0);
1084        sp->vary_b = NULL;
1085        sp->vary_l = NULL;
1086        sp->vary_e = NULL;
1087
1088        if (oc->flags & OC_F_PASS) {
1089                sp->wrk->stats.cache_hitpass++;
1090                WSP(sp, SLT_HitPass, "%u", sp->obj->xid);
1091                (void)HSH_Deref(sp->wrk, NULL, &sp->obj);
1092                sp->objcore = NULL;
1093                sp->step = STP_PASS;
1094                return (0);
1095        }
1096
1097        sp->wrk->stats.cache_hit++;
1098        WSP(sp, SLT_Hit, "%u", sp->obj->xid);
1099        sp->step = STP_HIT;
1100        return (0);
1101}
1102
1103/*--------------------------------------------------------------------
1104 * We had a miss, ask VCL, proceed as instructed
1105 *
1106DOT subgraph xcluster_miss {
1107DOT     miss [
1108DOT             shape=ellipse
1109DOT             label="filter req.->bereq."
1110DOT     ]
1111DOT     vcl_miss [
1112DOT             shape=record
1113DOT             label="vcl_miss()|req.\nbereq."
1114DOT     ]
1115DOT     miss -> vcl_miss [style=bold,color=blue]
1116DOT }
1117DOT vcl_miss -> rst_miss [label="restart",color=purple]
1118DOT rst_miss [label="RESTART",shape=plaintext]
1119DOT vcl_miss -> err_miss [label="error"]
1120DOT err_miss [label="ERROR",shape=plaintext]
1121DOT vcl_miss -> fetch [label="fetch",style=bold,color=blue]
1122DOT vcl_miss -> pass [label="pass",style=bold,color=red]
1123DOT
1124 */
1125
1126static int
1127cnt_miss(struct sess *sp)
1128{
1129
1130        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1131        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
1132
1133        AZ(sp->obj);
1134        AN(sp->objcore);
1135        WS_Reset(sp->wrk->ws, NULL);
1136        http_Setup(sp->wrk->bereq, sp->wrk->ws);
1137        http_FilterHeader(sp, HTTPH_R_FETCH);
1138        http_ForceGet(sp->wrk->bereq);
1139        if (params->http_gzip_support) {
1140                /*
1141                 * We always ask the backend for gzip, even if the
1142                 * client doesn't grok it.  We will uncompress for
1143                 * the minority of clients which don't.
1144                 */
1145                http_Unset(sp->wrk->bereq, H_Accept_Encoding);
1146                http_PrintfHeader(sp->wrk, sp->fd, sp->wrk->bereq,
1147                    "Accept-Encoding: gzip");
1148        }
1149        sp->wrk->connect_timeout = 0;
1150        sp->wrk->first_byte_timeout = 0;
1151        sp->wrk->between_bytes_timeout = 0;
1152        VCL_miss_method(sp);
1153        switch(sp->handling) {
1154        case VCL_RET_ERROR:
1155                AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
1156                sp->objcore = NULL;
1157                sp->step = STP_ERROR;
1158                return (0);
1159        case VCL_RET_PASS:
1160                AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
1161                sp->objcore = NULL;
1162                sp->step = STP_PASS;
1163                return (0);
1164        case VCL_RET_FETCH:
1165                sp->step = STP_FETCH;
1166                return (0);
1167        case VCL_RET_RESTART:
1168                AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
1169                sp->objcore = NULL;
1170                INCOMPL();
1171        default:
1172                WRONG("Illegal action in vcl_miss{}");
1173        }
1174}
1175
1176/*--------------------------------------------------------------------
1177 * Start pass processing by getting headers from backend, then
1178 * continue in passbody.
1179 *
1180DOT subgraph xcluster_pass {
1181DOT     pass [
1182DOT             shape=ellipse
1183DOT             label="deref obj."
1184DOT     ]
1185DOT     pass2 [
1186DOT             shape=ellipse
1187DOT             label="filter req.->bereq."
1188DOT     ]
1189DOT     vcl_pass [
1190DOT             shape=record
1191DOT             label="vcl_pass()|req.\nbereq."
1192DOT     ]
1193DOT     pass_do [
1194DOT             shape=ellipse
1195DOT             label="create anon object\n"
1196DOT     ]
1197DOT     pass -> pass2 [style=bold, color=red]
1198DOT     pass2 -> vcl_pass [style=bold, color=red]
1199DOT     vcl_pass -> pass_do [label="pass"] [style=bold, color=red]
1200DOT }
1201DOT pass_do -> fetch [style=bold, color=red]
1202DOT vcl_pass -> rst_pass [label="restart",color=purple]
1203DOT rst_pass [label="RESTART",shape=plaintext]
1204DOT vcl_pass -> err_pass [label="error"]
1205DOT err_pass [label="ERROR",shape=plaintext]
1206 */
1207
1208static int
1209cnt_pass(struct sess *sp)
1210{
1211
1212        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1213        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
1214        AZ(sp->obj);
1215
1216        WS_Reset(sp->wrk->ws, NULL);
1217        http_Setup(sp->wrk->bereq, sp->wrk->ws);
1218        http_FilterHeader(sp, HTTPH_R_PASS);
1219
1220        sp->wrk->connect_timeout = 0;
1221        sp->wrk->first_byte_timeout = 0;
1222        sp->wrk->between_bytes_timeout = 0;
1223        VCL_pass_method(sp);
1224        if (sp->handling == VCL_RET_ERROR) {
1225                sp->step = STP_ERROR;
1226                return (0);
1227        }
1228        assert(sp->handling == VCL_RET_PASS);
1229        sp->acct_tmp.pass++;
1230        sp->sendbody = 1;
1231        sp->step = STP_FETCH;
1232        return (0);
1233}
1234
1235/*--------------------------------------------------------------------
1236 * Ship the request header to the backend unchanged, then pipe
1237 * until one of the ends close the connection.
1238 *
1239DOT subgraph xcluster_pipe {
1240DOT     pipe [
1241DOT             shape=ellipse
1242DOT             label="Filter req.->bereq."
1243DOT     ]
1244DOT     vcl_pipe [
1245DOT             shape=record
1246DOT             label="vcl_pipe()|req.\nbereq\."
1247DOT     ]
1248DOT     pipe_do [
1249DOT             shape=ellipse
1250DOT             label="send bereq.\npipe until close"
1251DOT     ]
1252DOT     vcl_pipe -> pipe_do [label="pipe",style=bold,color=orange]
1253DOT     pipe -> vcl_pipe [style=bold,color=orange]
1254DOT }
1255DOT pipe_do -> DONE [style=bold,color=orange]
1256DOT vcl_pipe -> err_pipe [label="error"]
1257DOT err_pipe [label="ERROR",shape=plaintext]
1258 */
1259
1260static int
1261cnt_pipe(struct sess *sp)
1262{
1263
1264        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1265        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
1266
1267        sp->acct_tmp.pipe++;
1268        WS_Reset(sp->wrk->ws, NULL);
1269        http_Setup(sp->wrk->bereq, sp->wrk->ws);
1270        http_FilterHeader(sp, HTTPH_R_PIPE);
1271
1272        VCL_pipe_method(sp);
1273
1274        if (sp->handling == VCL_RET_ERROR)
1275                INCOMPL();
1276        assert(sp->handling == VCL_RET_PIPE);
1277
1278        PipeSession(sp);
1279        assert(WRW_IsReleased(sp->wrk));
1280        http_Setup(sp->wrk->bereq, NULL);
1281        sp->step = STP_DONE;
1282        return (0);
1283}
1284
1285/*--------------------------------------------------------------------
1286 * RECV
1287 * We have a complete request, set everything up and start it.
1288 *
1289DOT subgraph xcluster_recv {
1290DOT     recv [
1291DOT             shape=record
1292DOT             label="vcl_recv()|req."
1293DOT     ]
1294DOT }
1295DOT RESTART -> recv
1296DOT recv -> pipe [label="pipe",style=bold,color=orange]
1297DOT recv -> pass2 [label="pass",style=bold,color=red]
1298DOT recv -> err_recv [label="error"]
1299DOT err_recv [label="ERROR",shape=plaintext]
1300DOT recv -> hash [label="lookup",style=bold,color=green]
1301 */
1302
1303static int
1304cnt_recv(struct sess *sp)
1305{
1306        unsigned recv_handling;
1307
1308        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1309        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
1310        AZ(sp->obj);
1311        assert(sp->wrk->wrw.ciov == sp->wrk->wrw.siov);
1312
1313        /* By default we use the first backend */
1314        AZ(sp->director);
1315        sp->director = sp->vcl->director[0];
1316        AN(sp->director);
1317
1318        sp->disable_esi = 0;
1319        sp->hash_always_miss = 0;
1320        sp->hash_ignore_busy = 0;
1321        sp->client_identity = NULL;
1322
1323        http_CollectHdr(sp->http, H_Cache_Control);
1324
1325        VCL_recv_method(sp);
1326        recv_handling = sp->handling;
1327
1328        if (sp->restarts >= params->max_restarts) {
1329                if (sp->err_code == 0)
1330                        sp->err_code = 503;
1331                sp->step = STP_ERROR;
1332                return (0);
1333        }
1334
1335        /* XXX: do_esi ? */
1336        sp->wrk->is_gzip = 0;
1337        sp->wrk->is_gunzip = 0;
1338        sp->wrk->do_gzip = 0;
1339        sp->wrk->do_gunzip = 0;
1340        sp->wrk->do_stream = 0;
1341
1342        if (params->http_gzip_support &&
1343             (recv_handling != VCL_RET_PIPE) &&
1344             (recv_handling != VCL_RET_PASS)) {
1345                if (RFC2616_Req_Gzip(sp)) {
1346                        http_Unset(sp->http, H_Accept_Encoding);
1347                        http_PrintfHeader(sp->wrk, sp->fd, sp->http,
1348                            "Accept-Encoding: gzip");
1349                } else {
1350                        http_Unset(sp->http, H_Accept_Encoding);
1351                }
1352        }
1353
1354        SHA256_Init(sp->wrk->sha256ctx);
1355        VCL_hash_method(sp);
1356        assert(sp->handling == VCL_RET_HASH);
1357        SHA256_Final(sp->digest, sp->wrk->sha256ctx);
1358
1359        if (!strcmp(sp->http->hd[HTTP_HDR_REQ].b, "HEAD"))
1360                sp->wantbody = 0;
1361        else
1362                sp->wantbody = 1;
1363
1364        sp->sendbody = 0;
1365        switch(recv_handling) {
1366        case VCL_RET_LOOKUP:
1367                /* XXX: discard req body, if any */
1368                sp->step = STP_LOOKUP;
1369                return (0);
1370        case VCL_RET_PIPE:
1371                if (sp->esi_level > 0) {
1372                        /* XXX: VSL something */
1373                        INCOMPL();
1374                        /* sp->step = STP_DONE; */
1375                        return (1);
1376                }
1377                sp->step = STP_PIPE;
1378                return (0);
1379        case VCL_RET_PASS:
1380                sp->step = STP_PASS;
1381                return (0);
1382        case VCL_RET_ERROR:
1383                /* XXX: discard req body, if any */
1384                sp->step = STP_ERROR;
1385                return (0);
1386        default:
1387                WRONG("Illegal action in vcl_recv{}");
1388        }
1389}
1390
1391/*--------------------------------------------------------------------
1392 * START
1393 * Handle a request, wherever it came from recv/restart.
1394 *
1395DOT start [shape=box,label="Dissect request"]
1396DOT start -> recv [style=bold,color=green]
1397 */
1398
1399static int
1400cnt_start(struct sess *sp)
1401{
1402        int done;
1403        char *p;
1404        const char *r = "HTTP/1.1 100 Continue\r\n\r\n";
1405
1406        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1407        AZ(sp->restarts);
1408        AZ(sp->obj);
1409        AZ(sp->vcl);
1410
1411        /* Update stats of various sorts */
1412        sp->wrk->stats.client_req++;
1413        sp->t_req = TIM_real();
1414        sp->wrk->lastused = sp->t_req;
1415        sp->acct_tmp.req++;
1416
1417        /* Assign XID and log */
1418        sp->xid = ++xids;                               /* XXX not locked */
1419        WSP(sp, SLT_ReqStart, "%s %s %u", sp->addr, sp->port,  sp->xid);
1420
1421        /* Borrow VCL reference from worker thread */
1422        VCL_Refresh(&sp->wrk->vcl);
1423        sp->vcl = sp->wrk->vcl;
1424        sp->wrk->vcl = NULL;
1425
1426        http_Setup(sp->http, sp->ws);
1427        done = http_DissectRequest(sp);
1428
1429        /* If we could not even parse the request, just close */
1430        if (done < 0) {
1431                sp->step = STP_DONE;
1432                vca_close_session(sp, "junk");
1433                return (0);
1434        }
1435
1436        /* Catch request snapshot */
1437        sp->ws_req = WS_Snapshot(sp->ws);
1438
1439        /* Catch original request, before modification */
1440        HTTP_Copy(sp->http0, sp->http);
1441
1442        if (done != 0) {
1443                sp->err_code = done;
1444                sp->step = STP_ERROR;
1445                return (0);
1446        }
1447
1448        sp->doclose = http_DoConnection(sp->http);
1449
1450        /* XXX: Handle TRACE & OPTIONS of Max-Forwards = 0 */
1451
1452        /*
1453         * Handle Expect headers
1454         */
1455        if (http_GetHdr(sp->http, H_Expect, &p)) {
1456                if (strcasecmp(p, "100-continue")) {
1457                        sp->err_code = 417;
1458                        sp->step = STP_ERROR;
1459                        return (0);
1460                }
1461
1462                /* XXX: Don't bother with write failures for now */
1463                (void)write(sp->fd, r, strlen(r));
1464                /* XXX: When we do ESI includes, this is not removed
1465                 * XXX: because we use http0 as our basis.  Believed
1466                 * XXX: safe, but potentially confusing.
1467                 */
1468                http_Unset(sp->http, H_Expect);
1469        }
1470
1471        sp->step = STP_RECV;
1472        return (0);
1473}
1474
1475/*--------------------------------------------------------------------
1476 * Central state engine dispatcher.
1477 *
1478 * Kick the session around until it has had enough.
1479 *
1480 */
1481
1482static void
1483cnt_diag(struct sess *sp, const char *state)
1484{
1485        if (sp->wrk != NULL) {
1486                WSL(sp->wrk, SLT_Debug, sp->id,
1487                    "thr %p STP_%s sp %p obj %p vcl %p",
1488                    pthread_self(), state, sp, sp->obj, sp->vcl);
1489                WSL_Flush(sp->wrk, 0);
1490        } else {
1491                VSL(SLT_Debug, sp->id,
1492                    "thr %p STP_%s sp %p obj %p vcl %p",
1493                    pthread_self(), state, sp, sp->obj, sp->vcl);
1494        }
1495}
1496
1497void
1498CNT_Session(struct sess *sp)
1499{
1500        int done;
1501        struct worker *w;
1502
1503        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1504        w = sp->wrk;
1505        CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
1506
1507        /*
1508         * Possible entrance states
1509         */
1510        assert(
1511            sp->step == STP_FIRST ||
1512            sp->step == STP_START ||
1513            sp->step == STP_LOOKUP ||
1514            sp->step == STP_RECV);
1515
1516        /*
1517         * Whenever we come in from the acceptor we need to set blocking
1518         * mode, but there is no point in setting it when we come from
1519         * ESI or when a parked sessions returns.
1520         * It would be simpler to do this in the acceptor, but we'd rather
1521         * do the syscall in the worker thread.
1522         */
1523        if (sp->step == STP_FIRST || sp->step == STP_START)
1524                (void)VTCP_blocking(sp->fd);
1525
1526        /*
1527         * NB: Once done is set, we can no longer touch sp!
1528         */
1529        for (done = 0; !done; ) {
1530                assert(sp->wrk == w);
1531                /*
1532                 * This is a good place to be paranoid about the various
1533                 * pointers still pointing to the things we expect.
1534                 */
1535                CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1536                CHECK_OBJ_ORNULL(sp->obj, OBJECT_MAGIC);
1537                CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
1538                CHECK_OBJ_ORNULL(w->nobjhead, OBJHEAD_MAGIC);
1539                WS_Assert(w->ws);
1540
1541                switch (sp->step) {
1542#define STEP(l,u) \
1543                    case STP_##u: \
1544                        if (params->diag_bitmap & 0x01) \
1545                                cnt_diag(sp, #u); \
1546                        done = cnt_##l(sp); \
1547                        break;
1548#include "steps.h"
1549#undef STEP
1550                default:
1551                        WRONG("State engine misfire");
1552                }
1553                WS_Assert(w->ws);
1554                CHECK_OBJ_ORNULL(w->nobjhead, OBJHEAD_MAGIC);
1555        }
1556        WSL_Flush(w, 0);
1557        assert(WRW_IsReleased(w));
1558}
1559
1560/*
1561DOT }
1562*/
1563
1564/*--------------------------------------------------------------------
1565 * Debugging aids
1566 */
1567
1568static void
1569cli_debug_xid(struct cli *cli, const char * const *av, void *priv)
1570{
1571        (void)priv;
1572        if (av[2] != NULL)
1573                xids = strtoul(av[2], NULL, 0);
1574        VCLI_Out(cli, "XID is %u", xids);
1575}
1576
1577/*
1578 * Default to seed=1, this is the only seed value POSIXl guarantees will
1579 * result in a reproducible random number sequence.
1580 */
1581static void
1582cli_debug_srandom(struct cli *cli, const char * const *av, void *priv)
1583{
1584        (void)priv;
1585        unsigned seed = 1;
1586
1587        if (av[2] != NULL)
1588                seed = strtoul(av[2], NULL, 0);
1589        srandom(seed);
1590        srand48(random());
1591        VCLI_Out(cli, "Random(3) seeded with %lu", seed);
1592}
1593
1594static struct cli_proto debug_cmds[] = {
1595        { "debug.xid", "debug.xid",
1596                "\tExamine or set XID\n", 0, 1, "d", cli_debug_xid },
1597        { "debug.srandom", "debug.srandom",
1598                "\tSeed the random(3) function\n", 0, 1, "d", cli_debug_srandom },
1599        { NULL }
1600};
1601
1602/*--------------------------------------------------------------------
1603 *
1604 */
1605
1606void
1607CNT_Init(void)
1608{
1609
1610        srandomdev();
1611        srand48(random());
1612        xids = random();
1613        CLI_AddFuncs(debug_cmds);
1614}
1615
1616
Note: See TracBrowser for help on using the repository browser.