source: bin/varnishd/cache_center.c @ beb0c5

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

Cap the TTL (to param "shortlived") when we use the Transient storage
to avoid dropping an object on out of storage conditions.

I belive this...

Fixes #953

Otherwise please reopen.

  • 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                if (sp->wrk->exp.ttl > params->shortlived)
758                        sp->wrk->exp.ttl = params->shortlived;
759        }
760        if (sp->obj == NULL) {
761                sp->err_code = 503;
762                sp->step = STP_ERROR;
763                VDI_CloseFd(sp);
764                return (0);
765        }
766        CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
767
768        sp->wrk->storage_hint = NULL;
769
770        if (sp->wrk->do_gzip || (sp->wrk->is_gzip && !sp->wrk->do_gunzip))
771                sp->obj->gziped = 1;
772
773        if (vary != NULL) {
774                sp->obj->vary =
775                    (void *)WS_Alloc(sp->obj->http->ws, varyl);
776                AN(sp->obj->vary);
777                memcpy(sp->obj->vary, VSB_data(vary), varyl);
778                VSB_delete(vary);
779        }
780
781        sp->obj->xid = sp->xid;
782        sp->obj->response = sp->err_code;
783        sp->obj->age = sp->wrk->age;
784        sp->obj->entered = sp->wrk->entered;
785        WS_Assert(sp->obj->ws_o);
786
787        /* Filter into object */
788        hp = sp->wrk->beresp;
789        hp2 = sp->obj->http;
790
791        hp2->logtag = HTTP_Obj;
792        http_CopyResp(hp2, hp);
793        http_FilterFields(sp->wrk, sp->fd, hp2, hp,
794            pass ? HTTPH_R_PASS : HTTPH_A_INS);
795        http_CopyHome(sp->wrk, sp->fd, hp2);
796
797        if (http_GetHdr(hp, H_Last_Modified, &b))
798                sp->obj->last_modified = TIM_parse(b);
799        else
800                sp->obj->last_modified = floor(sp->wrk->entered);
801
802        assert(WRW_IsReleased(sp->wrk));
803
804        if (sp->wrk->do_stream) {
805                sp->step = STP_PREPRESP;
806                return (0);
807        }
808
809        /* Use unmodified headers*/
810        i = FetchBody(sp);
811
812        sp->wrk->h_content_length = NULL;
813
814        http_Setup(sp->wrk->bereq, NULL);
815        http_Setup(sp->wrk->beresp, NULL);
816        sp->wrk->vfp = NULL;
817        assert(WRW_IsReleased(sp->wrk));
818        AZ(sp->vbc);
819        AN(sp->director);
820
821        if (i) {
822                HSH_Drop(sp);
823                AZ(sp->obj);
824                sp->err_code = 503;
825                sp->step = STP_ERROR;
826                return (0);
827        }
828
829        if (sp->obj->objcore != NULL) {
830                EXP_Insert(sp->obj);
831                AN(sp->obj->objcore);
832                AN(sp->obj->objcore->ban);
833                HSH_Unbusy(sp);
834        }
835        sp->acct_tmp.fetch++;
836        sp->step = STP_PREPRESP;
837        return (0);
838}
839
840/*--------------------------------------------------------------------
841 * Stream the body as we fetch it
842DOT subgraph xstreambody {
843DOT     streambody [
844DOT             shape=ellipse
845DOT             label="streaming\nfetch/deliver"
846DOT     ]
847DOT }
848DOT streambody -> DONE [style=bold,color=cyan]
849 */
850
851static int
852cnt_streambody(struct sess *sp)
853{
854        int i;
855        struct stream_ctx sctx;
856        uint8_t obuf[sp->wrk->res_mode & RES_GUNZIP ?
857            params->gzip_stack_buffer : 1];
858
859        memset(&sctx, 0, sizeof sctx);
860        sctx.magic = STREAM_CTX_MAGIC;
861        AZ(sp->wrk->sctx);
862        sp->wrk->sctx = &sctx;
863
864        if (sp->wrk->res_mode & RES_GUNZIP) {
865                sctx.vgz = VGZ_NewUngzip(sp, "U S -");
866                sctx.obuf = obuf;
867                sctx.obuf_len = sizeof (obuf);
868        }
869
870        RES_StreamStart(sp);
871
872        i = FetchBody(sp);
873
874        sp->wrk->h_content_length = NULL;
875
876        http_Setup(sp->wrk->bereq, NULL);
877        http_Setup(sp->wrk->beresp, NULL);
878        sp->wrk->vfp = NULL;
879        AZ(sp->vbc);
880        AN(sp->director);
881
882        if (!i && sp->obj->objcore != NULL) {
883                EXP_Insert(sp->obj);
884                AN(sp->obj->objcore);
885                AN(sp->obj->objcore->ban);
886                HSH_Unbusy(sp);
887        } else {
888                sp->doclose = "Stream error";
889        }
890        sp->acct_tmp.fetch++;
891        sp->director = NULL;
892        sp->restarts = 0;
893
894        RES_StreamEnd(sp);
895        if (sp->wrk->res_mode & RES_GUNZIP)
896                VGZ_Destroy(&sctx.vgz);
897
898        sp->wrk->sctx = NULL;
899        assert(WRW_IsReleased(sp->wrk));
900        assert(sp->wrk->wrw.ciov == sp->wrk->wrw.siov);
901        (void)HSH_Deref(sp->wrk, NULL, &sp->obj);
902        http_Setup(sp->wrk->resp, NULL);
903        sp->step = STP_DONE;
904        return (0);
905}
906
907/*--------------------------------------------------------------------
908 * The very first request
909 */
910static int
911cnt_first(struct sess *sp)
912{
913
914        /*
915         * XXX: If we don't have acceptfilters we are somewhat subject
916         * XXX: to DoS'ing here.  One remedy would be to set a shorter
917         * XXX: SO_RCVTIMEO and once we have received something here
918         * XXX: increase it to the normal value.
919         */
920
921        assert(sp->xid == 0);
922        assert(sp->restarts == 0);
923        VCA_Prep(sp);
924
925        /* Record the session watermark */
926        sp->ws_ses = WS_Snapshot(sp->ws);
927
928        /* Receive a HTTP protocol request */
929        HTC_Init(sp->htc, sp->ws, sp->fd, params->http_req_size,
930            params->http_req_hdr_len);
931        sp->wrk->lastused = sp->t_open;
932        sp->acct_tmp.sess++;
933
934        sp->step = STP_WAIT;
935        return (0);
936}
937
938/*--------------------------------------------------------------------
939 * HIT
940 * We had a cache hit.  Ask VCL, then march off as instructed.
941 *
942DOT subgraph xcluster_hit {
943DOT     hit [
944DOT             shape=record
945DOT             label="vcl_hit()|req.\nobj."
946DOT     ]
947DOT }
948DOT hit -> err_hit [label="error"]
949DOT err_hit [label="ERROR",shape=plaintext]
950DOT hit -> rst_hit [label="restart",color=purple]
951DOT rst_hit [label="RESTART",shape=plaintext]
952DOT hit -> pass [label=pass,style=bold,color=red]
953DOT hit -> prepresp [label="deliver",style=bold,color=green]
954 */
955
956static int
957cnt_hit(struct sess *sp)
958{
959
960        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
961        CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
962        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
963
964        assert(!(sp->obj->objcore->flags & OC_F_PASS));
965
966        VCL_hit_method(sp);
967
968        if (sp->handling == VCL_RET_DELIVER) {
969                /* Dispose of any body part of the request */
970                (void)FetchReqBody(sp);
971                AZ(sp->wrk->bereq->ws);
972                AZ(sp->wrk->beresp->ws);
973                sp->step = STP_PREPRESP;
974                return (0);
975        }
976
977        /* Drop our object, we won't need it */
978        (void)HSH_Deref(sp->wrk, NULL, &sp->obj);
979        sp->objcore = NULL;
980
981        switch(sp->handling) {
982        case VCL_RET_PASS:
983                sp->step = STP_PASS;
984                return (0);
985        case VCL_RET_ERROR:
986                sp->step = STP_ERROR;
987                return (0);
988        case VCL_RET_RESTART:
989                sp->director = NULL;
990                sp->restarts++;
991                sp->step = STP_RECV;
992                return (0);
993        default:
994                WRONG("Illegal action in vcl_hit{}");
995        }
996}
997
998/*--------------------------------------------------------------------
999 * LOOKUP
1000 * Hash things together and look object up in hash-table.
1001 *
1002 * LOOKUP consists of two substates so that we can reenter if we
1003 * encounter a busy object.
1004 *
1005DOT subgraph xcluster_lookup {
1006DOT     hash [
1007DOT             shape=record
1008DOT             label="vcl_hash()|req."
1009DOT     ]
1010DOT     lookup [
1011DOT             shape=diamond
1012DOT             label="obj in cache ?\ncreate if not"
1013DOT     ]
1014DOT     lookup2 [
1015DOT             shape=diamond
1016DOT             label="obj.f.pass ?"
1017DOT     ]
1018DOT     hash -> lookup [label="hash",style=bold,color=green]
1019DOT     lookup -> lookup2 [label="yes",style=bold,color=green]
1020DOT }
1021DOT lookup2 -> hit [label="no", style=bold,color=green]
1022DOT lookup2 -> pass [label="yes",style=bold,color=red]
1023DOT lookup -> miss [label="no",style=bold,color=blue]
1024 */
1025
1026static int
1027cnt_lookup(struct sess *sp)
1028{
1029        struct objcore *oc;
1030        struct object *o;
1031        struct objhead *oh;
1032
1033        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1034        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
1035
1036        if (sp->hash_objhead == NULL) {
1037                /* Not a waiting list return */
1038                AZ(sp->vary_b);
1039                AZ(sp->vary_l);
1040                AZ(sp->vary_e);
1041                (void)WS_Reserve(sp->ws, 0);
1042                sp->vary_b = (void*)sp->ws->f;
1043                sp->vary_e = (void*)sp->ws->r;
1044                sp->vary_b[2] = '\0';
1045        }
1046
1047        oc = HSH_Lookup(sp, &oh);
1048
1049        if (oc == NULL) {
1050                /*
1051                 * We lost the session to a busy object, disembark the
1052                 * worker thread.   The hash code to restart the session,
1053                 * still in STP_LOOKUP, later when the busy object isn't.
1054                 * NB:  Do not access sp any more !
1055                 */
1056                return (1);
1057        }
1058
1059
1060        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
1061        CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
1062
1063        /* If we inserted a new object it's a miss */
1064        if (oc->flags & OC_F_BUSY) {
1065                sp->wrk->stats.cache_miss++;
1066
1067                if (sp->vary_l != NULL)
1068                        WS_ReleaseP(sp->ws, (void*)sp->vary_l);
1069                else
1070                        WS_Release(sp->ws, 0);
1071                sp->vary_b = NULL;
1072                sp->vary_l = NULL;
1073                sp->vary_e = NULL;
1074
1075                sp->objcore = oc;
1076                sp->step = STP_MISS;
1077                return (0);
1078        }
1079
1080        o = oc_getobj(sp->wrk, oc);
1081        CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
1082        sp->obj = o;
1083
1084        WS_Release(sp->ws, 0);
1085        sp->vary_b = NULL;
1086        sp->vary_l = NULL;
1087        sp->vary_e = NULL;
1088
1089        if (oc->flags & OC_F_PASS) {
1090                sp->wrk->stats.cache_hitpass++;
1091                WSP(sp, SLT_HitPass, "%u", sp->obj->xid);
1092                (void)HSH_Deref(sp->wrk, NULL, &sp->obj);
1093                sp->objcore = NULL;
1094                sp->step = STP_PASS;
1095                return (0);
1096        }
1097
1098        sp->wrk->stats.cache_hit++;
1099        WSP(sp, SLT_Hit, "%u", sp->obj->xid);
1100        sp->step = STP_HIT;
1101        return (0);
1102}
1103
1104/*--------------------------------------------------------------------
1105 * We had a miss, ask VCL, proceed as instructed
1106 *
1107DOT subgraph xcluster_miss {
1108DOT     miss [
1109DOT             shape=ellipse
1110DOT             label="filter req.->bereq."
1111DOT     ]
1112DOT     vcl_miss [
1113DOT             shape=record
1114DOT             label="vcl_miss()|req.\nbereq."
1115DOT     ]
1116DOT     miss -> vcl_miss [style=bold,color=blue]
1117DOT }
1118DOT vcl_miss -> rst_miss [label="restart",color=purple]
1119DOT rst_miss [label="RESTART",shape=plaintext]
1120DOT vcl_miss -> err_miss [label="error"]
1121DOT err_miss [label="ERROR",shape=plaintext]
1122DOT vcl_miss -> fetch [label="fetch",style=bold,color=blue]
1123DOT vcl_miss -> pass [label="pass",style=bold,color=red]
1124DOT
1125 */
1126
1127static int
1128cnt_miss(struct sess *sp)
1129{
1130
1131        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1132        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
1133
1134        AZ(sp->obj);
1135        AN(sp->objcore);
1136        WS_Reset(sp->wrk->ws, NULL);
1137        http_Setup(sp->wrk->bereq, sp->wrk->ws);
1138        http_FilterHeader(sp, HTTPH_R_FETCH);
1139        http_ForceGet(sp->wrk->bereq);
1140        if (params->http_gzip_support) {
1141                /*
1142                 * We always ask the backend for gzip, even if the
1143                 * client doesn't grok it.  We will uncompress for
1144                 * the minority of clients which don't.
1145                 */
1146                http_Unset(sp->wrk->bereq, H_Accept_Encoding);
1147                http_PrintfHeader(sp->wrk, sp->fd, sp->wrk->bereq,
1148                    "Accept-Encoding: gzip");
1149        }
1150        sp->wrk->connect_timeout = 0;
1151        sp->wrk->first_byte_timeout = 0;
1152        sp->wrk->between_bytes_timeout = 0;
1153        VCL_miss_method(sp);
1154        switch(sp->handling) {
1155        case VCL_RET_ERROR:
1156                AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
1157                sp->objcore = NULL;
1158                sp->step = STP_ERROR;
1159                return (0);
1160        case VCL_RET_PASS:
1161                AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
1162                sp->objcore = NULL;
1163                sp->step = STP_PASS;
1164                return (0);
1165        case VCL_RET_FETCH:
1166                sp->step = STP_FETCH;
1167                return (0);
1168        case VCL_RET_RESTART:
1169                AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
1170                sp->objcore = NULL;
1171                INCOMPL();
1172        default:
1173                WRONG("Illegal action in vcl_miss{}");
1174        }
1175}
1176
1177/*--------------------------------------------------------------------
1178 * Start pass processing by getting headers from backend, then
1179 * continue in passbody.
1180 *
1181DOT subgraph xcluster_pass {
1182DOT     pass [
1183DOT             shape=ellipse
1184DOT             label="deref obj."
1185DOT     ]
1186DOT     pass2 [
1187DOT             shape=ellipse
1188DOT             label="filter req.->bereq."
1189DOT     ]
1190DOT     vcl_pass [
1191DOT             shape=record
1192DOT             label="vcl_pass()|req.\nbereq."
1193DOT     ]
1194DOT     pass_do [
1195DOT             shape=ellipse
1196DOT             label="create anon object\n"
1197DOT     ]
1198DOT     pass -> pass2 [style=bold, color=red]
1199DOT     pass2 -> vcl_pass [style=bold, color=red]
1200DOT     vcl_pass -> pass_do [label="pass"] [style=bold, color=red]
1201DOT }
1202DOT pass_do -> fetch [style=bold, color=red]
1203DOT vcl_pass -> rst_pass [label="restart",color=purple]
1204DOT rst_pass [label="RESTART",shape=plaintext]
1205DOT vcl_pass -> err_pass [label="error"]
1206DOT err_pass [label="ERROR",shape=plaintext]
1207 */
1208
1209static int
1210cnt_pass(struct sess *sp)
1211{
1212
1213        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1214        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
1215        AZ(sp->obj);
1216
1217        WS_Reset(sp->wrk->ws, NULL);
1218        http_Setup(sp->wrk->bereq, sp->wrk->ws);
1219        http_FilterHeader(sp, HTTPH_R_PASS);
1220
1221        sp->wrk->connect_timeout = 0;
1222        sp->wrk->first_byte_timeout = 0;
1223        sp->wrk->between_bytes_timeout = 0;
1224        VCL_pass_method(sp);
1225        if (sp->handling == VCL_RET_ERROR) {
1226                sp->step = STP_ERROR;
1227                return (0);
1228        }
1229        assert(sp->handling == VCL_RET_PASS);
1230        sp->acct_tmp.pass++;
1231        sp->sendbody = 1;
1232        sp->step = STP_FETCH;
1233        return (0);
1234}
1235
1236/*--------------------------------------------------------------------
1237 * Ship the request header to the backend unchanged, then pipe
1238 * until one of the ends close the connection.
1239 *
1240DOT subgraph xcluster_pipe {
1241DOT     pipe [
1242DOT             shape=ellipse
1243DOT             label="Filter req.->bereq."
1244DOT     ]
1245DOT     vcl_pipe [
1246DOT             shape=record
1247DOT             label="vcl_pipe()|req.\nbereq\."
1248DOT     ]
1249DOT     pipe_do [
1250DOT             shape=ellipse
1251DOT             label="send bereq.\npipe until close"
1252DOT     ]
1253DOT     vcl_pipe -> pipe_do [label="pipe",style=bold,color=orange]
1254DOT     pipe -> vcl_pipe [style=bold,color=orange]
1255DOT }
1256DOT pipe_do -> DONE [style=bold,color=orange]
1257DOT vcl_pipe -> err_pipe [label="error"]
1258DOT err_pipe [label="ERROR",shape=plaintext]
1259 */
1260
1261static int
1262cnt_pipe(struct sess *sp)
1263{
1264
1265        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1266        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
1267
1268        sp->acct_tmp.pipe++;
1269        WS_Reset(sp->wrk->ws, NULL);
1270        http_Setup(sp->wrk->bereq, sp->wrk->ws);
1271        http_FilterHeader(sp, HTTPH_R_PIPE);
1272
1273        VCL_pipe_method(sp);
1274
1275        if (sp->handling == VCL_RET_ERROR)
1276                INCOMPL();
1277        assert(sp->handling == VCL_RET_PIPE);
1278
1279        PipeSession(sp);
1280        assert(WRW_IsReleased(sp->wrk));
1281        http_Setup(sp->wrk->bereq, NULL);
1282        sp->step = STP_DONE;
1283        return (0);
1284}
1285
1286/*--------------------------------------------------------------------
1287 * RECV
1288 * We have a complete request, set everything up and start it.
1289 *
1290DOT subgraph xcluster_recv {
1291DOT     recv [
1292DOT             shape=record
1293DOT             label="vcl_recv()|req."
1294DOT     ]
1295DOT }
1296DOT RESTART -> recv
1297DOT recv -> pipe [label="pipe",style=bold,color=orange]
1298DOT recv -> pass2 [label="pass",style=bold,color=red]
1299DOT recv -> err_recv [label="error"]
1300DOT err_recv [label="ERROR",shape=plaintext]
1301DOT recv -> hash [label="lookup",style=bold,color=green]
1302 */
1303
1304static int
1305cnt_recv(struct sess *sp)
1306{
1307        unsigned recv_handling;
1308
1309        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1310        CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
1311        AZ(sp->obj);
1312        assert(sp->wrk->wrw.ciov == sp->wrk->wrw.siov);
1313
1314        /* By default we use the first backend */
1315        AZ(sp->director);
1316        sp->director = sp->vcl->director[0];
1317        AN(sp->director);
1318
1319        sp->disable_esi = 0;
1320        sp->hash_always_miss = 0;
1321        sp->hash_ignore_busy = 0;
1322        sp->client_identity = NULL;
1323
1324        http_CollectHdr(sp->http, H_Cache_Control);
1325
1326        VCL_recv_method(sp);
1327        recv_handling = sp->handling;
1328
1329        if (sp->restarts >= params->max_restarts) {
1330                if (sp->err_code == 0)
1331                        sp->err_code = 503;
1332                sp->step = STP_ERROR;
1333                return (0);
1334        }
1335
1336        /* XXX: do_esi ? */
1337        sp->wrk->is_gzip = 0;
1338        sp->wrk->is_gunzip = 0;
1339        sp->wrk->do_gzip = 0;
1340        sp->wrk->do_gunzip = 0;
1341        sp->wrk->do_stream = 0;
1342
1343        if (params->http_gzip_support &&
1344             (recv_handling != VCL_RET_PIPE) &&
1345             (recv_handling != VCL_RET_PASS)) {
1346                if (RFC2616_Req_Gzip(sp)) {
1347                        http_Unset(sp->http, H_Accept_Encoding);
1348                        http_PrintfHeader(sp->wrk, sp->fd, sp->http,
1349                            "Accept-Encoding: gzip");
1350                } else {
1351                        http_Unset(sp->http, H_Accept_Encoding);
1352                }
1353        }
1354
1355        SHA256_Init(sp->wrk->sha256ctx);
1356        VCL_hash_method(sp);
1357        assert(sp->handling == VCL_RET_HASH);
1358        SHA256_Final(sp->digest, sp->wrk->sha256ctx);
1359
1360        if (!strcmp(sp->http->hd[HTTP_HDR_REQ].b, "HEAD"))
1361                sp->wantbody = 0;
1362        else
1363                sp->wantbody = 1;
1364
1365        sp->sendbody = 0;
1366        switch(recv_handling) {
1367        case VCL_RET_LOOKUP:
1368                /* XXX: discard req body, if any */
1369                sp->step = STP_LOOKUP;
1370                return (0);
1371        case VCL_RET_PIPE:
1372                if (sp->esi_level > 0) {
1373                        /* XXX: VSL something */
1374                        INCOMPL();
1375                        /* sp->step = STP_DONE; */
1376                        return (1);
1377                }
1378                sp->step = STP_PIPE;
1379                return (0);
1380        case VCL_RET_PASS:
1381                sp->step = STP_PASS;
1382                return (0);
1383        case VCL_RET_ERROR:
1384                /* XXX: discard req body, if any */
1385                sp->step = STP_ERROR;
1386                return (0);
1387        default:
1388                WRONG("Illegal action in vcl_recv{}");
1389        }
1390}
1391
1392/*--------------------------------------------------------------------
1393 * START
1394 * Handle a request, wherever it came from recv/restart.
1395 *
1396DOT start [shape=box,label="Dissect request"]
1397DOT start -> recv [style=bold,color=green]
1398 */
1399
1400static int
1401cnt_start(struct sess *sp)
1402{
1403        int done;
1404        char *p;
1405        const char *r = "HTTP/1.1 100 Continue\r\n\r\n";
1406
1407        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1408        AZ(sp->restarts);
1409        AZ(sp->obj);
1410        AZ(sp->vcl);
1411
1412        /* Update stats of various sorts */
1413        sp->wrk->stats.client_req++;
1414        sp->t_req = TIM_real();
1415        sp->wrk->lastused = sp->t_req;
1416        sp->acct_tmp.req++;
1417
1418        /* Assign XID and log */
1419        sp->xid = ++xids;                               /* XXX not locked */
1420        WSP(sp, SLT_ReqStart, "%s %s %u", sp->addr, sp->port,  sp->xid);
1421
1422        /* Borrow VCL reference from worker thread */
1423        VCL_Refresh(&sp->wrk->vcl);
1424        sp->vcl = sp->wrk->vcl;
1425        sp->wrk->vcl = NULL;
1426
1427        http_Setup(sp->http, sp->ws);
1428        done = http_DissectRequest(sp);
1429
1430        /* If we could not even parse the request, just close */
1431        if (done < 0) {
1432                sp->step = STP_DONE;
1433                vca_close_session(sp, "junk");
1434                return (0);
1435        }
1436
1437        /* Catch request snapshot */
1438        sp->ws_req = WS_Snapshot(sp->ws);
1439
1440        /* Catch original request, before modification */
1441        HTTP_Copy(sp->http0, sp->http);
1442
1443        if (done != 0) {
1444                sp->err_code = done;
1445                sp->step = STP_ERROR;
1446                return (0);
1447        }
1448
1449        sp->doclose = http_DoConnection(sp->http);
1450
1451        /* XXX: Handle TRACE & OPTIONS of Max-Forwards = 0 */
1452
1453        /*
1454         * Handle Expect headers
1455         */
1456        if (http_GetHdr(sp->http, H_Expect, &p)) {
1457                if (strcasecmp(p, "100-continue")) {
1458                        sp->err_code = 417;
1459                        sp->step = STP_ERROR;
1460                        return (0);
1461                }
1462
1463                /* XXX: Don't bother with write failures for now */
1464                (void)write(sp->fd, r, strlen(r));
1465                /* XXX: When we do ESI includes, this is not removed
1466                 * XXX: because we use http0 as our basis.  Believed
1467                 * XXX: safe, but potentially confusing.
1468                 */
1469                http_Unset(sp->http, H_Expect);
1470        }
1471
1472        sp->step = STP_RECV;
1473        return (0);
1474}
1475
1476/*--------------------------------------------------------------------
1477 * Central state engine dispatcher.
1478 *
1479 * Kick the session around until it has had enough.
1480 *
1481 */
1482
1483static void
1484cnt_diag(struct sess *sp, const char *state)
1485{
1486        if (sp->wrk != NULL) {
1487                WSL(sp->wrk, SLT_Debug, sp->id,
1488                    "thr %p STP_%s sp %p obj %p vcl %p",
1489                    pthread_self(), state, sp, sp->obj, sp->vcl);
1490                WSL_Flush(sp->wrk, 0);
1491        } else {
1492                VSL(SLT_Debug, sp->id,
1493                    "thr %p STP_%s sp %p obj %p vcl %p",
1494                    pthread_self(), state, sp, sp->obj, sp->vcl);
1495        }
1496}
1497
1498void
1499CNT_Session(struct sess *sp)
1500{
1501        int done;
1502        struct worker *w;
1503
1504        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1505        w = sp->wrk;
1506        CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
1507
1508        /*
1509         * Possible entrance states
1510         */
1511        assert(
1512            sp->step == STP_FIRST ||
1513            sp->step == STP_START ||
1514            sp->step == STP_LOOKUP ||
1515            sp->step == STP_RECV);
1516
1517        /*
1518         * Whenever we come in from the acceptor we need to set blocking
1519         * mode, but there is no point in setting it when we come from
1520         * ESI or when a parked sessions returns.
1521         * It would be simpler to do this in the acceptor, but we'd rather
1522         * do the syscall in the worker thread.
1523         */
1524        if (sp->step == STP_FIRST || sp->step == STP_START)
1525                (void)VTCP_blocking(sp->fd);
1526
1527        /*
1528         * NB: Once done is set, we can no longer touch sp!
1529         */
1530        for (done = 0; !done; ) {
1531                assert(sp->wrk == w);
1532                /*
1533                 * This is a good place to be paranoid about the various
1534                 * pointers still pointing to the things we expect.
1535                 */
1536                CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1537                CHECK_OBJ_ORNULL(sp->obj, OBJECT_MAGIC);
1538                CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
1539                CHECK_OBJ_ORNULL(w->nobjhead, OBJHEAD_MAGIC);
1540                WS_Assert(w->ws);
1541
1542                switch (sp->step) {
1543#define STEP(l,u) \
1544                    case STP_##u: \
1545                        if (params->diag_bitmap & 0x01) \
1546                                cnt_diag(sp, #u); \
1547                        done = cnt_##l(sp); \
1548                        break;
1549#include "steps.h"
1550#undef STEP
1551                default:
1552                        WRONG("State engine misfire");
1553                }
1554                WS_Assert(w->ws);
1555                CHECK_OBJ_ORNULL(w->nobjhead, OBJHEAD_MAGIC);
1556        }
1557        WSL_Flush(w, 0);
1558        assert(WRW_IsReleased(w));
1559}
1560
1561/*
1562DOT }
1563*/
1564
1565/*--------------------------------------------------------------------
1566 * Debugging aids
1567 */
1568
1569static void
1570cli_debug_xid(struct cli *cli, const char * const *av, void *priv)
1571{
1572        (void)priv;
1573        if (av[2] != NULL)
1574                xids = strtoul(av[2], NULL, 0);
1575        VCLI_Out(cli, "XID is %u", xids);
1576}
1577
1578/*
1579 * Default to seed=1, this is the only seed value POSIXl guarantees will
1580 * result in a reproducible random number sequence.
1581 */
1582static void
1583cli_debug_srandom(struct cli *cli, const char * const *av, void *priv)
1584{
1585        (void)priv;
1586        unsigned seed = 1;
1587
1588        if (av[2] != NULL)
1589                seed = strtoul(av[2], NULL, 0);
1590        srandom(seed);
1591        srand48(random());
1592        VCLI_Out(cli, "Random(3) seeded with %lu", seed);
1593}
1594
1595static struct cli_proto debug_cmds[] = {
1596        { "debug.xid", "debug.xid",
1597                "\tExamine or set XID\n", 0, 1, "d", cli_debug_xid },
1598        { "debug.srandom", "debug.srandom",
1599                "\tSeed the random(3) function\n", 0, 1, "d", cli_debug_srandom },
1600        { NULL }
1601};
1602
1603/*--------------------------------------------------------------------
1604 *
1605 */
1606
1607void
1608CNT_Init(void)
1609{
1610
1611        srandomdev();
1612        srand48(random());
1613        xids = random();
1614        CLI_AddFuncs(debug_cmds);
1615}
1616
1617
Note: See TracBrowser for help on using the repository browser.