source: bin/varnishd/cache/cache_center.c @ 58e4a2

Revision 58e4a2, 43.4 KB checked in by Poul-Henning Kamp <phk@…>, 2 years ago (diff)

s/wrk->busyobj->/bo->/ in cnt_fetchbody() (with a few notable exceptions.)

  • 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/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 -> first [style=bold,color=green]
56 */
57
58#include "config.h"
59
60#include <math.h>
61#include <poll.h>
62#include <stdio.h>
63#include <stdlib.h>
64
65#include "cache.h"
66
67#include "common/heritage.h"
68
69#include "hash/hash_slinger.h"
70#include "vcl.h"
71#include "vcli_priv.h"
72#include "vsha256.h"
73#include "vtcp.h"
74#include "vtim.h"
75
76#ifndef HAVE_SRANDOMDEV
77#include "compat/srandomdev.h"
78#endif
79
80static unsigned xids;
81
82/*--------------------------------------------------------------------
83 * WAIT
84 * Collect the request from the client.
85 *
86 * We "abuse" sp->t_req a bit here:  On input it means "request reception
87 * started at xxx" and is used to trigger timeouts.  On return it means
88 * "we had full request headers by xxx" and is used for reporting by
89 * later steps.
90 *
91DOT subgraph xcluster_wait {
92DOT     wait [
93DOT             shape=box
94DOT             label="cnt_wait:\nwait for\ncomplete\nrequest"
95DOT     ]
96DOT     herding [shape=hexagon]
97DOT     wait -> start [label="got req",style=bold,color=green]
98DOT     wait -> "SES_Delete()" [label="errors"]
99DOT     wait -> herding [label="timeout_linger"]
100DOT     herding -> wait [label="fd read_ready"]
101DOT }
102 */
103
104static int
105cnt_wait(struct sess *sp, struct worker *wrk, struct req *req)
106{
107        int i, j, tmo;
108        struct pollfd pfd[1];
109        double now, when;
110        const char *why = NULL;
111
112        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
113        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
114
115        if (req == NULL) {
116                SES_GetReq(sp);
117                req = sp->req;
118                CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
119                HTC_Init(req->htc, req->ws, sp->fd, sp->vsl_id,
120                    cache_param->http_req_size,
121                    cache_param->http_req_hdr_len);
122        }
123
124        AZ(req->vcl);
125        AZ(req->obj);
126        AZ(req->esi_level);
127        assert(req->xid == 0);
128        req->t_resp = NAN;
129
130        assert(!isnan(sp->t_req));
131        tmo = (int)(1e3 * cache_param->timeout_linger);
132        while (1) {
133                pfd[0].fd = sp->fd;
134                pfd[0].events = POLLIN;
135                pfd[0].revents = 0;
136                j = poll(pfd, 1, tmo);
137                assert(j >= 0);
138                now = VTIM_real();
139                if (j != 0)
140                        i = HTC_Rx(req->htc);
141                else
142                        i = HTC_Complete(req->htc);
143                if (i == 1) {
144                        /* Got it, run with it */
145                        sp->t_req = now;
146                        sp->step = STP_START;
147                        return (0);
148                } else if (i == -1) {
149                        why = "EOF";
150                        break;
151                } else if (i == -2) {
152                        why = "overflow";
153                        break;
154                } else if (i == -3) {
155                        /* Nothing but whitespace */
156                        when = sp->t_idle + cache_param->timeout_idle;
157                        if (when < now) {
158                                why = "timeout";
159                                break;
160                        }
161                        when = sp->t_idle + cache_param->timeout_linger;
162                        tmo = (int)(1e3 * (when - now));
163                        if (when < now || tmo == 0) {
164                                sp->t_req = NAN;
165                                wrk->stats.sess_herd++;
166                                SES_Charge(sp);
167                                SES_ReleaseReq(sp);
168                                WAIT_Enter(sp);
169                                return (1);
170                        }
171                } else {
172                        /* Working on it */
173                        when = sp->t_req + cache_param->timeout_req;
174                        tmo = (int)(1e3 * (when - now));
175                        if (when < now || tmo == 0) {
176                                why = "req timeout";
177                                break;
178                        }
179                }
180        }
181        SES_Charge(sp);
182        SES_Delete(sp, why, now);
183        return (1);
184}
185
186/*--------------------------------------------------------------------
187 * We have a refcounted object on the session, now deliver it.
188 *
189DOT subgraph xcluster_prepresp {
190DOT     prepresp [
191DOT             shape=ellipse
192DOT             label="Filter obj.->resp."
193DOT     ]
194DOT     vcl_deliver [
195DOT             shape=record
196DOT             label="vcl_deliver()|resp."
197DOT     ]
198DOT     prepresp -> vcl_deliver [style=bold,color=green]
199DOT     prepresp -> vcl_deliver [style=bold,color=cyan]
200DOT     prepresp -> vcl_deliver [style=bold,color=red]
201DOT     prepresp -> vcl_deliver [style=bold,color=blue,]
202DOT     vcl_deliver -> deliver [style=bold,color=green,label=deliver]
203DOT     vcl_deliver -> deliver [style=bold,color=red]
204DOT     vcl_deliver -> deliver [style=bold,color=blue]
205DOT     vcl_deliver -> errdeliver [label="error"]
206DOT     errdeliver [label="ERROR",shape=plaintext]
207DOT     vcl_deliver -> rstdeliver [label="restart",color=purple]
208DOT     rstdeliver [label="RESTART",shape=plaintext]
209DOT     vcl_deliver -> streambody [style=bold,color=cyan,label="deliver"]
210DOT }
211 *
212 */
213
214static int
215cnt_prepresp(struct sess *sp, struct worker *wrk, struct req *req)
216{
217
218        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
219        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
220        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
221
222        CHECK_OBJ_NOTNULL(req->obj, OBJECT_MAGIC);
223        CHECK_OBJ_NOTNULL(req->vcl, VCL_CONF_MAGIC);
224
225        if (wrk->busyobj != NULL) {
226                CHECK_OBJ_NOTNULL(wrk->busyobj, BUSYOBJ_MAGIC);
227                AN(wrk->busyobj->do_stream);
228                AssertObjCorePassOrBusy(req->obj->objcore);
229        }
230
231        wrk->res_mode = 0;
232
233        if (wrk->busyobj == NULL)
234                wrk->res_mode |= RES_LEN;
235
236        if (wrk->busyobj != NULL &&
237            (wrk->busyobj->h_content_length != NULL ||
238            !wrk->busyobj->do_stream) &&
239            !wrk->busyobj->do_gzip && !wrk->busyobj->do_gunzip)
240                wrk->res_mode |= RES_LEN;
241
242        if (!req->disable_esi && req->obj->esidata != NULL) {
243                /* In ESI mode, we don't know the aggregate length */
244                wrk->res_mode &= ~RES_LEN;
245                wrk->res_mode |= RES_ESI;
246        }
247
248        if (req->esi_level > 0) {
249                wrk->res_mode &= ~RES_LEN;
250                wrk->res_mode |= RES_ESI_CHILD;
251        }
252
253        if (cache_param->http_gzip_support && req->obj->gziped &&
254            !RFC2616_Req_Gzip(sp)) {
255                /*
256                 * We don't know what it uncompresses to
257                 * XXX: we could cache that
258                 */
259                wrk->res_mode &= ~RES_LEN;
260                wrk->res_mode |= RES_GUNZIP;
261        }
262
263        if (!(wrk->res_mode & (RES_LEN|RES_CHUNKED|RES_EOF))) {
264                if (req->obj->len == 0 &&
265                    (wrk->busyobj == NULL || !wrk->busyobj->do_stream))
266                        /*
267                         * If the object is empty, neither ESI nor GUNZIP
268                         * can make it any different size
269                         */
270                        wrk->res_mode |= RES_LEN;
271                else if (!req->wantbody) {
272                        /* Nothing */
273                } else if (req->http->protover >= 11) {
274                        wrk->res_mode |= RES_CHUNKED;
275                } else {
276                        wrk->res_mode |= RES_EOF;
277                        req->doclose = "EOF mode";
278                }
279        }
280
281        req->t_resp = W_TIM_real(wrk);
282        if (req->obj->objcore != NULL) {
283                if ((req->t_resp - req->obj->last_lru) >
284                    cache_param->lru_timeout &&
285                    EXP_Touch(req->obj->objcore))
286                        req->obj->last_lru = req->t_resp;
287                req->obj->last_use = req->t_resp;       /* XXX: locking ? */
288        }
289        http_Setup(req->resp, req->ws);
290        RES_BuildHttp(sp);
291        VCL_deliver_method(sp);
292        switch (req->handling) {
293        case VCL_RET_DELIVER:
294                break;
295        case VCL_RET_RESTART:
296                if (req->restarts >= cache_param->max_restarts)
297                        break;
298                if (wrk->busyobj != NULL) {
299                        AN(wrk->busyobj->do_stream);
300                        VDI_CloseFd(wrk, &wrk->busyobj->vbc);
301                        HSH_Drop(wrk);
302                        VBO_DerefBusyObj(wrk, &wrk->busyobj);
303                } else {
304                        (void)HSH_Deref(wrk, NULL, &req->obj);
305                }
306                AZ(req->obj);
307                req->restarts++;
308                req->director = NULL;
309                http_Setup(req->resp, NULL);
310                sp->step = STP_RECV;
311                return (0);
312        default:
313                WRONG("Illegal action in vcl_deliver{}");
314        }
315        if (wrk->busyobj != NULL && wrk->busyobj->do_stream) {
316                AssertObjCorePassOrBusy(req->obj->objcore);
317                sp->step = STP_STREAMBODY;
318        } else {
319                sp->step = STP_DELIVER;
320        }
321        return (0);
322}
323
324/*--------------------------------------------------------------------
325 * Deliver an already stored object
326 *
327DOT subgraph xcluster_deliver {
328DOT     deliver [
329DOT             shape=ellipse
330DOT             label="Send body"
331DOT     ]
332DOT }
333DOT deliver -> DONE [style=bold,color=green]
334DOT deliver -> DONE [style=bold,color=red]
335DOT deliver -> DONE [style=bold,color=blue]
336 *
337 */
338
339static int
340cnt_deliver(struct sess *sp, struct worker *wrk, struct req *req)
341{
342        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
343        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
344        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
345
346        AZ(sp->wrk->busyobj);
347        req->director = NULL;
348        req->restarts = 0;
349
350        RES_WriteObj(sp);
351
352        assert(WRW_IsReleased(wrk));
353        assert(wrk->wrw.ciov == wrk->wrw.siov);
354        (void)HSH_Deref(wrk, NULL, &req->obj);
355        http_Setup(req->resp, NULL);
356        sp->step = STP_DONE;
357        return (0);
358}
359
360/*--------------------------------------------------------------------
361 * This is the final state, figure out if we should close or recycle
362 * the client connection
363 *
364DOT     DONE [
365DOT             shape=hexagon
366DOT             label="cnt_done:\nRequest completed"
367DOT     ]
368DOT     ESI_RESP [ shape=hexagon ]
369DOT     DONE -> start [label="full pipeline"]
370DOT     DONE -> wait
371DOT     DONE -> ESI_RESP
372 */
373
374static int
375cnt_done(struct sess *sp, struct worker *wrk, struct req *req)
376{
377        double dh, dp, da;
378        int i;
379
380        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
381        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
382        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
383        CHECK_OBJ_ORNULL(req->vcl, VCL_CONF_MAGIC);
384
385        AZ(req->obj);
386        AZ(wrk->busyobj);
387        req->director = NULL;
388        req->restarts = 0;
389
390        wrk->busyobj = NULL;
391
392        SES_Charge(sp);
393
394        /* If we did an ESI include, don't mess up our state */
395        if (req->esi_level > 0)
396                return (1);
397
398        if (req->vcl != NULL) {
399                if (wrk->vcl != NULL)
400                        VCL_Rel(&wrk->vcl);
401                wrk->vcl = req->vcl;
402                req->vcl = NULL;
403        }
404
405
406        sp->t_idle = W_TIM_real(wrk);
407        if (req->xid == 0) {
408                req->t_resp = sp->t_idle;
409        } else {
410                dp = req->t_resp - sp->t_req;
411                da = sp->t_idle - req->t_resp;
412                dh = sp->t_req - sp->t_open;
413                /* XXX: Add StatReq == StatSess */
414                /* XXX: Workaround for pipe */
415                if (sp->fd >= 0) {
416                        WSP(sp, SLT_Length, "%ju",
417                            (uintmax_t)req->req_bodybytes);
418                }
419                WSP(sp, SLT_ReqEnd, "%u %.9f %.9f %.9f %.9f %.9f",
420                    req->xid, sp->t_req, sp->t_idle, dh, dp, da);
421        }
422        req->xid = 0;
423        WSL_Flush(wrk, 0);
424
425        sp->t_req = NAN;
426        req->t_resp = NAN;
427
428        req->req_bodybytes = 0;
429
430        req->hash_always_miss = 0;
431        req->hash_ignore_busy = 0;
432
433        if (sp->fd >= 0 && req->doclose != NULL) {
434                /*
435                 * This is an orderly close of the connection; ditch nolinger
436                 * before we close, to get queued data transmitted.
437                 */
438                // XXX: not yet (void)VTCP_linger(sp->fd, 0);
439                SES_Close(sp, req->doclose);
440        }
441
442        if (sp->fd < 0) {
443                wrk->stats.sess_closed++;
444                SES_Delete(sp, NULL, NAN);
445                return (1);
446        }
447
448        if (wrk->stats.client_req >= cache_param->wthread_stats_rate)
449                WRK_SumStat(wrk);
450        /* Reset the workspace to the session-watermark */
451        WS_Reset(req->ws, NULL);
452        WS_Reset(wrk->ws, NULL);
453
454        sp->t_req = sp->t_idle;
455        i = HTC_Reinit(req->htc);
456        if (i == 1) {
457                wrk->stats.sess_pipeline++;
458                sp->step = STP_START;
459        } else {
460                if (Tlen(req->htc->rxbuf))
461                        wrk->stats.sess_readahead++;
462                sp->step = STP_WAIT;
463        }
464        return (0);
465}
466
467/*--------------------------------------------------------------------
468 * Emit an error
469 *
470DOT subgraph xcluster_error {
471DOT     vcl_error [
472DOT             shape=record
473DOT             label="vcl_error()|resp."
474DOT     ]
475DOT     ERROR -> vcl_error
476DOT     vcl_error-> prepresp [label=deliver]
477DOT }
478DOT vcl_error-> rsterr [label="restart",color=purple]
479DOT rsterr [label="RESTART",shape=plaintext]
480 */
481
482static int
483cnt_error(struct sess *sp, struct worker *wrk, struct req *req)
484{
485        struct http *h;
486        char date[40];
487
488        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
489        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
490        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
491        AZ(req->objcore);
492        AZ(req->obj);
493        AZ(wrk->busyobj);
494
495        wrk->busyobj = VBO_GetBusyObj(wrk);
496        req->obj = STV_NewObject(wrk, TRANSIENT_STORAGE,
497            cache_param->http_resp_size,
498            (uint16_t)cache_param->http_max_hdr);
499        if (req->obj == NULL) {
500                req->doclose = "Out of objects";
501                req->director = NULL;
502                http_Setup(wrk->busyobj->beresp, NULL);
503                http_Setup(wrk->busyobj->bereq, NULL);
504                sp->step = STP_DONE;
505                return(0);
506        }
507        CHECK_OBJ_NOTNULL(req->obj, OBJECT_MAGIC);
508        req->obj->xid = req->xid;
509        req->obj->exp.entered = sp->t_req;
510
511        h = req->obj->http;
512
513        if (req->err_code < 100 || req->err_code > 999)
514                req->err_code = 501;
515
516        http_PutProtocol(wrk, sp->vsl_id, h, "HTTP/1.1");
517        http_PutStatus(h, req->err_code);
518        VTIM_format(W_TIM_real(wrk), date);
519        http_PrintfHeader(wrk, sp->vsl_id, h, "Date: %s", date);
520        http_SetHeader(wrk, sp->vsl_id, h, "Server: Varnish");
521
522        if (req->err_reason != NULL)
523                http_PutResponse(wrk, sp->vsl_id, h, req->err_reason);
524        else
525                http_PutResponse(wrk, sp->vsl_id, h,
526                    http_StatusMessage(req->err_code));
527        VCL_error_method(sp);
528
529        if (req->handling == VCL_RET_RESTART &&
530            req->restarts <  cache_param->max_restarts) {
531                HSH_Drop(wrk);
532                VBO_DerefBusyObj(wrk, &wrk->busyobj);
533                req->director = NULL;
534                req->restarts++;
535                sp->step = STP_RECV;
536                return (0);
537        } else if (req->handling == VCL_RET_RESTART)
538                req->handling = VCL_RET_DELIVER;
539
540
541        /* We always close when we take this path */
542        req->doclose = "error";
543        req->wantbody = 1;
544
545        assert(req->handling == VCL_RET_DELIVER);
546        req->err_code = 0;
547        req->err_reason = NULL;
548        http_Setup(wrk->busyobj->bereq, NULL);
549        VBO_DerefBusyObj(wrk, &wrk->busyobj);
550        sp->step = STP_PREPRESP;
551        return (0);
552}
553
554/*--------------------------------------------------------------------
555 * Fetch response headers from the backend
556 *
557DOT subgraph xcluster_fetch {
558DOT     fetch [
559DOT             shape=record
560DOT             label="{cnt_fetch:|fetch hdr\nfrom backend|(find obj.ttl)|{vcl_fetch\{\}|{req.|bereq.|beresp.}}|{<err>error?|<rst>restart?}|{<hfp>hit_for_pass?|<del>deliver?}}"
561DOT     ]
562DOT     fetch_pass [
563DOT             shape=ellipse
564DOT             label="obj.f.pass=true"
565DOT     ]
566DOT     vcl_fetch -> fetch_pass [label="hit_for_pass",style=bold,color=red]
567DOT }
568DOT fetch:hfp -> fetchbody [style=bold,color=red]
569DOT fetch:del -> fetchbody [label="deliver",style=bold,color=blue]
570 */
571
572static int
573cnt_fetch(struct sess *sp, struct worker *wrk, struct req *req)
574{
575        int i, need_host_hdr;
576
577        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
578        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
579        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
580
581        CHECK_OBJ_NOTNULL(req->vcl, VCL_CONF_MAGIC);
582        CHECK_OBJ_NOTNULL(wrk->busyobj, BUSYOBJ_MAGIC);
583
584        AN(req->director);
585        AZ(wrk->busyobj->vbc);
586        AZ(wrk->busyobj->should_close);
587        AZ(req->storage_hint);
588
589        http_Setup(wrk->busyobj->beresp, wrk->ws);
590
591        need_host_hdr = !http_GetHdr(wrk->busyobj->bereq, H_Host, NULL);
592
593        i = FetchHdr(sp, need_host_hdr, req->objcore == NULL);
594        /*
595         * If we recycle a backend connection, there is a finite chance
596         * that the backend closed it before we get a request to it.
597         * Do a single retry in that case.
598         */
599        if (i == 1) {
600                VSC_C_main->backend_retry++;
601                i = FetchHdr(sp, need_host_hdr, req->objcore == NULL);
602        }
603
604        if (i) {
605                req->handling = VCL_RET_ERROR;
606                req->err_code = 503;
607        } else {
608                /*
609                 * These two headers can be spread over multiple actual headers
610                 * and we rely on their content outside of VCL, so collect them
611                 * into one line here.
612                 */
613                http_CollectHdr(wrk->busyobj->beresp, H_Cache_Control);
614                http_CollectHdr(wrk->busyobj->beresp, H_Vary);
615
616                /*
617                 * Figure out how the fetch is supposed to happen, before the
618                 * headers are adultered by VCL
619                 * NB: Also sets other wrk variables
620                 */
621                wrk->busyobj->body_status = RFC2616_Body(sp);
622
623                req->err_code = http_GetStatus(wrk->busyobj->beresp);
624
625                /*
626                 * What does RFC2616 think about TTL ?
627                 */
628                EXP_Clr(&wrk->busyobj->exp);
629                wrk->busyobj->exp.entered = W_TIM_real(wrk);
630                RFC2616_Ttl(sp);
631
632                /* pass from vclrecv{} has negative TTL */
633                if (req->objcore == NULL)
634                        wrk->busyobj->exp.ttl = -1.;
635
636                AZ(wrk->busyobj->do_esi);
637                AZ(wrk->busyobj->do_pass);
638               
639                VCL_fetch_method(sp);
640
641                if (req->objcore != NULL && wrk->busyobj->do_pass)
642                        req->objcore->flags |= OC_F_PASS;
643
644                switch (req->handling) {
645                case VCL_RET_DELIVER:
646                        AssertObjCorePassOrBusy(req->objcore);
647                        sp->step = STP_FETCHBODY;
648                        return (0);
649                default:
650                        break;
651                }
652
653                /* We are not going to fetch the body, Close the connection */
654                VDI_CloseFd(wrk, &wrk->busyobj->vbc);
655        }
656
657        /* Clean up partial fetch */
658        AZ(wrk->busyobj->vbc);
659
660        if (req->objcore != NULL) {
661                CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);
662                AZ(HSH_Deref(wrk, req->objcore, NULL));
663                req->objcore = NULL;
664        }
665        VBO_DerefBusyObj(wrk, &wrk->busyobj);
666        req->director = NULL;
667        req->storage_hint = NULL;
668
669        switch (req->handling) {
670        case VCL_RET_RESTART:
671                req->restarts++;
672                sp->step = STP_RECV;
673                return (0);
674        case VCL_RET_ERROR:
675                sp->step = STP_ERROR;
676                return (0);
677        default:
678                WRONG("Illegal action in vcl_fetch{}");
679        }
680}
681
682/*--------------------------------------------------------------------
683 * Fetch response body from the backend
684 *
685DOT subgraph xcluster_body {
686DOT     fetchbody [
687DOT             shape=diamond
688DOT             label="stream ?"
689DOT     ]
690DOT     fetchbody2 [
691DOT             shape=ellipse
692DOT             label="fetch body\nfrom backend\n"
693DOT     ]
694DOT }
695DOT fetchbody -> fetchbody2 [label=no,style=bold,color=red]
696DOT fetchbody -> fetchbody2 [style=bold,color=blue]
697DOT fetchbody -> prepresp [label=yes,style=bold,color=cyan]
698DOT fetchbody2 -> prepresp [style=bold,color=red]
699DOT fetchbody2 -> prepresp [style=bold,color=blue]
700 */
701
702
703static int
704cnt_fetchbody(struct sess *sp, struct worker *wrk, struct req *req)
705{
706        int i;
707        struct http *hp, *hp2;
708        char *b;
709        uint16_t nhttp;
710        unsigned l;
711        struct vsb *vary = NULL;
712        int varyl = 0, pass;
713        struct busyobj *bo;
714
715        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
716        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
717        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
718        bo = wrk->busyobj;
719        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
720
721        assert(req->handling == VCL_RET_DELIVER);
722
723        if (req->objcore == NULL) {
724                /* This is a pass from vcl_recv */
725                pass = 1;
726                /* VCL may have fiddled this, but that doesn't help */
727                bo->exp.ttl = -1.;
728        } else if (bo->do_pass) {
729                pass = 1;
730        } else {
731                /* regular object */
732                pass = 0;
733        }
734
735        /*
736         * The VCL variables beresp.do_g[un]zip tells us how we want the
737         * object processed before it is stored.
738         *
739         * The backend Content-Encoding header tells us what we are going
740         * to receive, which we classify in the following three classes:
741         *
742         *      "Content-Encoding: gzip"        --> object is gzip'ed.
743         *      no Content-Encoding             --> object is not gzip'ed.
744         *      anything else                   --> do nothing wrt gzip
745         *
746         */
747
748        /* We do nothing unless the param is set */
749        if (!cache_param->http_gzip_support)
750                bo->do_gzip = bo->do_gunzip = 0;
751
752        bo->is_gzip = http_HdrIs(bo->beresp, H_Content_Encoding, "gzip");
753
754        bo->is_gunzip = !http_GetHdr(bo->beresp, H_Content_Encoding, NULL);
755
756        /* It can't be both */
757        assert(bo->is_gzip == 0 || bo->is_gunzip == 0);
758
759        /* We won't gunzip unless it is gzip'ed */
760        if (bo->do_gunzip && !bo->is_gzip)
761                bo->do_gunzip = 0;
762
763        /* If we do gunzip, remove the C-E header */
764        if (bo->do_gunzip)
765                http_Unset(bo->beresp, H_Content_Encoding);
766
767        /* We wont gzip unless it is ungziped */
768        if (bo->do_gzip && !bo->is_gunzip)
769                bo->do_gzip = 0;
770
771        /* If we do gzip, add the C-E header */
772        if (bo->do_gzip)
773                http_SetHeader(wrk, sp->vsl_id, bo->beresp,
774                    "Content-Encoding: gzip");
775
776        /* But we can't do both at the same time */
777        assert(bo->do_gzip == 0 || bo->do_gunzip == 0);
778
779        /* ESI takes precedence and handles gzip/gunzip itself */
780        if (bo->do_esi)
781                bo->vfp = &vfp_esi;
782        else if (bo->do_gunzip)
783                bo->vfp = &vfp_gunzip;
784        else if (bo->do_gzip)
785                bo->vfp = &vfp_gzip;
786        else if (bo->is_gzip)
787                bo->vfp = &vfp_testgzip;
788
789        if (bo->do_esi || req->esi_level > 0)
790                bo->do_stream = 0;
791        if (!req->wantbody)
792                bo->do_stream = 0;
793
794        /* No reason to try streaming a non-existing body */
795        if (bo->body_status == BS_NONE)
796                bo->do_stream = 0;
797
798        l = http_EstimateWS(bo->beresp,
799            pass ? HTTPH_R_PASS : HTTPH_A_INS, &nhttp);
800
801        /* Create Vary instructions */
802        if (req->objcore != NULL) {
803                CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);
804                vary = VRY_Create(sp, bo->beresp);
805                if (vary != NULL) {
806                        varyl = VSB_len(vary);
807                        assert(varyl > 0);
808                        l += varyl;
809                }
810        }
811
812        /*
813         * Space for producing a Content-Length: header including padding
814         * A billion gigabytes is enough for anybody.
815         */
816        l += strlen("Content-Length: XxxXxxXxxXxxXxxXxx") + sizeof(void *);
817
818        if (bo->exp.ttl < cache_param->shortlived ||
819            req->objcore == NULL)
820                req->storage_hint = TRANSIENT_STORAGE;
821
822        req->obj = STV_NewObject(wrk, req->storage_hint, l, nhttp);
823        if (req->obj == NULL) {
824                /*
825                 * Try to salvage the transaction by allocating a
826                 * shortlived object on Transient storage.
827                 */
828                req->obj = STV_NewObject(wrk, TRANSIENT_STORAGE, l, nhttp);
829                if (bo->exp.ttl > cache_param->shortlived)
830                        bo->exp.ttl = cache_param->shortlived;
831                bo->exp.grace = 0.0;
832                bo->exp.keep = 0.0;
833        }
834        if (req->obj == NULL) {
835                req->err_code = 503;
836                sp->step = STP_ERROR;
837                VDI_CloseFd(wrk, &bo->vbc);
838                VBO_DerefBusyObj(wrk, &wrk->busyobj);
839                return (0);
840        }
841        CHECK_OBJ_NOTNULL(req->obj, OBJECT_MAGIC);
842
843        req->storage_hint = NULL;
844
845        if (bo->do_gzip ||
846            (bo->is_gzip && !bo->do_gunzip))
847                req->obj->gziped = 1;
848
849        if (vary != NULL) {
850                req->obj->vary = (void *)WS_Alloc(req->obj->http->ws, varyl);
851                AN(req->obj->vary);
852                memcpy(req->obj->vary, VSB_data(vary), varyl);
853                VRY_Validate(req->obj->vary);
854                VSB_delete(vary);
855        }
856
857        req->obj->xid = req->xid;
858        req->obj->response = req->err_code;
859        WS_Assert(req->obj->ws_o);
860
861        /* Filter into object */
862        hp = bo->beresp;
863        hp2 = req->obj->http;
864
865        hp2->logtag = HTTP_Obj;
866        http_FilterResp(sp, hp, hp2, pass ? HTTPH_R_PASS : HTTPH_A_INS);
867        http_CopyHome(wrk, sp->vsl_id, hp2);
868
869        if (http_GetHdr(hp, H_Last_Modified, &b))
870                req->obj->last_modified = VTIM_parse(b);
871        else
872                req->obj->last_modified = floor(bo->exp.entered);
873
874        assert(WRW_IsReleased(wrk));
875
876        /*
877         * If we can deliver a 304 reply, we don't bother streaming.
878         * Notice that vcl_deliver{} could still nuke the headers
879         * that allow the 304, in which case we return 200 non-stream.
880         */
881        if (req->obj->response == 200 &&
882            req->http->conds &&
883            RFC2616_Do_Cond(sp))
884                bo->do_stream = 0;
885
886        AssertObjCorePassOrBusy(req->obj->objcore);
887
888        if (bo->do_stream) {
889                sp->step = STP_PREPRESP;
890                return (0);
891        }
892
893        /* Use unmodified headers*/
894        i = FetchBody(wrk, req->obj);
895
896        http_Setup(bo->bereq, NULL);
897        http_Setup(bo->beresp, NULL);
898        bo->vfp = NULL;
899        assert(WRW_IsReleased(wrk));
900        AZ(bo->vbc);
901        AN(req->director);
902
903        if (i) {
904                HSH_Drop(wrk);
905                VBO_DerefBusyObj(wrk, &wrk->busyobj);
906                AZ(req->obj);
907                req->err_code = 503;
908                sp->step = STP_ERROR;
909                return (0);
910        }
911
912        if (req->obj->objcore != NULL) {
913                EXP_Insert(req->obj);
914                AN(req->obj->objcore);
915                AN(req->obj->objcore->ban);
916                HSH_Unbusy(wrk);
917        }
918        VBO_DerefBusyObj(wrk, &wrk->busyobj);
919        wrk->acct_tmp.fetch++;
920        sp->step = STP_PREPRESP;
921        return (0);
922}
923
924/*--------------------------------------------------------------------
925 * Stream the body as we fetch it
926DOT subgraph xstreambody {
927DOT     streambody [
928DOT             shape=ellipse
929DOT             label="streaming\nfetch/deliver"
930DOT     ]
931DOT }
932DOT streambody -> DONE [style=bold,color=cyan]
933 */
934
935static int
936cnt_streambody(struct sess *sp, struct worker *wrk, struct req *req)
937{
938        int i;
939        struct stream_ctx sctx;
940        uint8_t obuf[sp->wrk->res_mode & RES_GUNZIP ?
941            cache_param->gzip_stack_buffer : 1];
942
943        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
944        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
945        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
946
947        CHECK_OBJ_NOTNULL(wrk->busyobj, BUSYOBJ_MAGIC);
948        memset(&sctx, 0, sizeof sctx);
949        sctx.magic = STREAM_CTX_MAGIC;
950        AZ(wrk->sctx);
951        wrk->sctx = &sctx;
952
953        if (wrk->res_mode & RES_GUNZIP) {
954                sctx.vgz = VGZ_NewUngzip(wrk, "U S -");
955                sctx.obuf = obuf;
956                sctx.obuf_len = sizeof (obuf);
957        }
958
959        RES_StreamStart(sp);
960
961        AssertObjCorePassOrBusy(req->obj->objcore);
962
963        i = FetchBody(wrk, req->obj);
964
965        http_Setup(wrk->busyobj->bereq, NULL);
966        http_Setup(wrk->busyobj->beresp, NULL);
967        wrk->busyobj->vfp = NULL;
968        AZ(wrk->busyobj->vbc);
969        AN(req->director);
970
971        if (!i && req->obj->objcore != NULL) {
972                EXP_Insert(req->obj);
973                AN(req->obj->objcore);
974                AN(req->obj->objcore->ban);
975                HSH_Unbusy(wrk);
976        } else {
977                req->doclose = "Stream error";
978        }
979        wrk->acct_tmp.fetch++;
980        req->director = NULL;
981        req->restarts = 0;
982
983        RES_StreamEnd(sp);
984        if (wrk->res_mode & RES_GUNZIP)
985                (void)VGZ_Destroy(&sctx.vgz, sp->vsl_id);
986
987        wrk->sctx = NULL;
988        assert(WRW_IsReleased(wrk));
989        assert(wrk->wrw.ciov == wrk->wrw.siov);
990        (void)HSH_Deref(wrk, NULL, &req->obj);
991        VBO_DerefBusyObj(wrk, &wrk->busyobj);
992        http_Setup(req->resp, NULL);
993        sp->step = STP_DONE;
994        return (0);
995}
996
997/*--------------------------------------------------------------------
998 * A freshly accepted socket
999 *
1000DOT subgraph xcluster_first {
1001DOT     first [
1002DOT             shape=box
1003DOT             label="cnt_first:\nrender\naddresses"
1004DOT     ]
1005DOT }
1006DOT first -> wait [style=bold,color=green]
1007 */
1008
1009static int
1010cnt_first(struct sess *sp, struct worker *wrk)
1011{
1012        char laddr[ADDR_BUFSIZE];
1013        char lport[PORT_BUFSIZE];
1014
1015        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1016        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
1017
1018        VTCP_name(&sp->sockaddr, sp->sockaddrlen,
1019            sp->addr, sizeof sp->addr, sp->port, sizeof sp->port);
1020        if (cache_param->log_local_addr) {
1021                AZ(getsockname(sp->fd, (void*)&sp->mysockaddr,
1022                    &sp->mysockaddrlen));
1023                VTCP_name(&sp->mysockaddr, sp->mysockaddrlen,
1024                    laddr, sizeof laddr, lport, sizeof lport);
1025                WSP(sp, SLT_SessionOpen, "%s %s %s %s",
1026                    sp->addr, sp->port, laddr, lport);
1027        } else {
1028                WSP(sp, SLT_SessionOpen, "%s %s %s",
1029                    sp->addr, sp->port, sp->mylsock->name);
1030        }
1031
1032        wrk->acct_tmp.sess++;
1033
1034        sp->t_req = sp->t_open;
1035        sp->t_idle = sp->t_open;
1036        sp->step = STP_WAIT;
1037        return (0);
1038}
1039
1040/*--------------------------------------------------------------------
1041 * HIT
1042 * We had a cache hit.  Ask VCL, then march off as instructed.
1043 *
1044DOT subgraph xcluster_hit {
1045DOT     hit [
1046DOT             shape=record
1047DOT             label="{cnt_hit:|{vcl_hit()|{req.|obj.}}|{<err>error?|<del>deliver?|<rst>restart?|<pass>pass?}}"
1048DOT     ]
1049DOT }
1050XDOT hit:err -> err_hit [label="error"]
1051XDOT err_hit [label="ERROR",shape=plaintext]
1052XDOT hit:rst -> rst_hit [label="restart",color=purple]
1053XDOT rst_hit [label="RESTART",shape=plaintext]
1054DOT hit:pass -> pass [label=pass,style=bold,color=red]
1055DOT hit:del -> prepresp [label="deliver",style=bold,color=green]
1056 */
1057
1058static int
1059cnt_hit(struct sess *sp, struct worker *wrk, struct req *req)
1060{
1061        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1062        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
1063        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
1064
1065        CHECK_OBJ_NOTNULL(req->obj, OBJECT_MAGIC);
1066        CHECK_OBJ_NOTNULL(req->vcl, VCL_CONF_MAGIC);
1067        AZ(req->objcore);
1068        AZ(wrk->busyobj);
1069
1070        assert(!(req->obj->objcore->flags & OC_F_PASS));
1071
1072        VCL_hit_method(sp);
1073
1074        if (req->handling == VCL_RET_DELIVER) {
1075                //AZ(wrk->busyobj->bereq->ws);
1076                //AZ(wrk->busyobj->beresp->ws);
1077                (void)FetchReqBody(sp, 0);
1078                sp->step = STP_PREPRESP;
1079                return (0);
1080        }
1081
1082        /* Drop our object, we won't need it */
1083        (void)HSH_Deref(wrk, NULL, &req->obj);
1084        req->objcore = NULL;
1085
1086        switch(req->handling) {
1087        case VCL_RET_PASS:
1088                sp->step = STP_PASS;
1089                return (0);
1090        case VCL_RET_ERROR:
1091                sp->step = STP_ERROR;
1092                return (0);
1093        case VCL_RET_RESTART:
1094                req->director = NULL;
1095                req->restarts++;
1096                sp->step = STP_RECV;
1097                return (0);
1098        default:
1099                WRONG("Illegal action in vcl_hit{}");
1100        }
1101}
1102
1103/*--------------------------------------------------------------------
1104 * LOOKUP
1105 * Hash things together and look object up in hash-table.
1106 *
1107 * LOOKUP consists of two substates so that we can reenter if we
1108 * encounter a busy object.
1109 *
1110DOT subgraph xcluster_lookup {
1111DOT     lookup [
1112DOT             shape=record
1113DOT             label="{<top>cnt_lookup:|hash lookup|{<busy>busy ?|<miss>miss ?}|{<no>no|obj.f.pass?|<yes>yes}}"
1114DOT     ]
1115DOT }
1116DOT lookup:busy -> lookup:top [label="(waitinglist)"]
1117DOT lookup:miss -> miss [style=bold,color=blue]
1118DOT lookup:no -> hit [style=bold,color=green]
1119DOT lookup:yes -> pass [style=bold,color=red]
1120 */
1121
1122static int
1123cnt_lookup(struct sess *sp, struct worker *wrk, struct req *req)
1124{
1125        struct objcore *oc;
1126        struct object *o;
1127        struct objhead *oh;
1128
1129        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1130        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
1131        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
1132        AZ(req->objcore);
1133
1134        CHECK_OBJ_NOTNULL(req->vcl, VCL_CONF_MAGIC);
1135        AZ(wrk->busyobj);
1136
1137        if (req->hash_objhead == NULL) {
1138                /* Not a waiting list return */
1139                AZ(req->vary_b);
1140                AZ(req->vary_l);
1141                AZ(req->vary_e);
1142                (void)WS_Reserve(req->ws, 0);
1143        } else {
1144                AN(req->ws->r);
1145        }
1146        req->vary_b = (void*)req->ws->f;
1147        req->vary_e = (void*)req->ws->r;
1148        req->vary_b[2] = '\0';
1149
1150        oc = HSH_Lookup(sp, &oh);
1151        AZ(req->objcore);
1152
1153        if (oc == NULL) {
1154                /*
1155                 * We lost the session to a busy object, disembark the
1156                 * worker thread.   We return to STP_LOOKUP when the busy
1157                 * object has been unbusied, and still have the hash digest
1158                 * around to do the lookup with.
1159                 * NB:  Do not access sp any more !
1160                 */
1161                return (1);
1162        }
1163
1164        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
1165        CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
1166
1167        /* If we inserted a new object it's a miss */
1168        if (oc->flags & OC_F_BUSY) {
1169                wrk->stats.cache_miss++;
1170
1171                if (req->vary_l != NULL) {
1172                        assert(oc->busyobj->vary == req->vary_b);
1173                        VRY_Validate(oc->busyobj->vary);
1174                        WS_ReleaseP(req->ws, (void*)req->vary_l);
1175                } else {
1176                        AZ(oc->busyobj->vary);
1177                        WS_Release(req->ws, 0);
1178                }
1179                req->vary_b = NULL;
1180                req->vary_l = NULL;
1181                req->vary_e = NULL;
1182
1183                req->objcore = oc;
1184                CHECK_OBJ_NOTNULL(wrk->busyobj, BUSYOBJ_MAGIC);
1185                sp->step = STP_MISS;
1186                return (0);
1187        }
1188
1189        o = oc_getobj(wrk, oc);
1190        CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
1191        req->obj = o;
1192
1193        WS_Release(req->ws, 0);
1194        req->vary_b = NULL;
1195        req->vary_l = NULL;
1196        req->vary_e = NULL;
1197
1198        if (oc->flags & OC_F_PASS) {
1199                wrk->stats.cache_hitpass++;
1200                WSP(sp, SLT_HitPass, "%u", req->obj->xid);
1201                (void)HSH_Deref(wrk, NULL, &req->obj);
1202                AZ(req->objcore);
1203                sp->step = STP_PASS;
1204                return (0);
1205        }
1206
1207        wrk->stats.cache_hit++;
1208        WSP(sp, SLT_Hit, "%u", req->obj->xid);
1209        sp->step = STP_HIT;
1210        return (0);
1211}
1212
1213/*--------------------------------------------------------------------
1214 * We had a miss, ask VCL, proceed as instructed
1215 *
1216DOT subgraph xcluster_miss {
1217DOT     miss [
1218DOT             shape=record
1219DOT             label="{cnt_miss:|filter req.-\>bereq.|{vcl_miss\{\}|{req.*|bereq.*}}|{<pass>pass?|<err>error?|<restart>restart?|<fetch>fetch?}}"
1220DOT     ]
1221DOT }
1222XDOT miss:restart -> rst_miss [label="restart",color=purple]
1223XDOT rst_miss [label="RESTART",shape=plaintext]
1224XDOT miss:err -> err_miss [label="error"]
1225XDOT err_miss [label="ERROR",shape=plaintext]
1226DOT miss:fetch -> fetch [label="fetch",style=bold,color=blue]
1227DOT miss:pass -> pass [label="pass",style=bold,color=red]
1228DOT
1229 */
1230
1231static int
1232cnt_miss(struct sess *sp, struct worker *wrk, struct req *req)
1233{
1234
1235        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1236        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
1237        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
1238        CHECK_OBJ_NOTNULL(req->vcl, VCL_CONF_MAGIC);
1239        CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);
1240        CHECK_OBJ_NOTNULL(wrk->busyobj, BUSYOBJ_MAGIC);
1241        AZ(req->obj);
1242
1243        WS_Reset(wrk->ws, NULL);
1244        wrk->busyobj = VBO_GetBusyObj(wrk);
1245        http_Setup(wrk->busyobj->bereq, wrk->ws);
1246        http_FilterReq(sp, HTTPH_R_FETCH);
1247        http_ForceGet(wrk->busyobj->bereq);
1248        if (cache_param->http_gzip_support) {
1249                /*
1250                 * We always ask the backend for gzip, even if the
1251                 * client doesn't grok it.  We will uncompress for
1252                 * the minority of clients which don't.
1253                 */
1254                http_Unset(wrk->busyobj->bereq, H_Accept_Encoding);
1255                http_SetHeader(wrk, sp->vsl_id, wrk->busyobj->bereq,
1256                    "Accept-Encoding: gzip");
1257        }
1258
1259        VCL_miss_method(sp);
1260
1261        if (req->handling == VCL_RET_FETCH) {
1262                CHECK_OBJ_NOTNULL(wrk->busyobj, BUSYOBJ_MAGIC);
1263                sp->step = STP_FETCH;
1264                return (0);
1265        }
1266
1267        AZ(HSH_Deref(wrk, req->objcore, NULL));
1268        req->objcore = NULL;
1269        http_Setup(wrk->busyobj->bereq, NULL);
1270        VBO_DerefBusyObj(wrk, &wrk->busyobj);
1271
1272        switch(req->handling) {
1273        case VCL_RET_ERROR:
1274                sp->step = STP_ERROR;
1275                break;
1276        case VCL_RET_PASS:
1277                sp->step = STP_PASS;
1278                break;
1279        case VCL_RET_RESTART:
1280                req->restarts++;
1281                req->director = NULL;
1282                sp->step = STP_RECV;
1283                break;
1284        default:
1285                WRONG("Illegal action in vcl_miss{}");
1286        }
1287        return (0);
1288}
1289
1290/*--------------------------------------------------------------------
1291 * Start pass processing by getting headers from backend, then
1292 * continue in passbody.
1293 *
1294DOT subgraph xcluster_pass {
1295DOT     pass [
1296DOT             shape=record
1297DOT             label="{cnt_pass:|(XXX: deref obj.)|filter req.*-\>bereq.|{vcl_pass\{\}|{req.*|bereq.*}}|{<err>error?|<rst>restart?}|<pass>create anon obj}"
1298DOT     ]
1299DOT }
1300DOT pass:pass -> fetch [style=bold, color=red]
1301XDOT pass:rst -> rst_pass [label="restart",color=purple]
1302XDOT rst_pass [label="RESTART",shape=plaintext]
1303XDOT pass:err -> err_pass [label="error"]
1304XDOT err_pass [label="ERROR",shape=plaintext]
1305 */
1306
1307static int
1308cnt_pass(struct sess *sp, struct worker *wrk, const struct req *req)
1309{
1310        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1311        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
1312        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
1313        CHECK_OBJ_NOTNULL(req->vcl, VCL_CONF_MAGIC);
1314        AZ(req->objcore);
1315        AZ(req->obj);
1316        AZ(wrk->busyobj);
1317
1318        wrk->busyobj = VBO_GetBusyObj(wrk);
1319        WS_Reset(wrk->ws, NULL);
1320        wrk->busyobj = VBO_GetBusyObj(wrk);
1321        http_Setup(wrk->busyobj->bereq, wrk->ws);
1322        http_FilterReq(sp, HTTPH_R_PASS);
1323
1324        VCL_pass_method(sp);
1325
1326        if (req->handling == VCL_RET_ERROR) {
1327                http_Setup(wrk->busyobj->bereq, NULL);
1328                VBO_DerefBusyObj(wrk, &wrk->busyobj);
1329                sp->step = STP_ERROR;
1330                return (0);
1331        }
1332        assert(req->handling == VCL_RET_PASS);
1333        wrk->acct_tmp.pass++;
1334        sp->step = STP_FETCH;
1335        return (0);
1336}
1337
1338/*--------------------------------------------------------------------
1339 * Ship the request header to the backend unchanged, then pipe
1340 * until one of the ends close the connection.
1341 *
1342DOT subgraph xcluster_pipe {
1343DOT     pipe [
1344DOT             shape=ellipse
1345DOT             label="Filter req.->bereq."
1346DOT     ]
1347DOT     vcl_pipe [
1348DOT             shape=record
1349DOT             label="vcl_pipe()|req.\nbereq\."
1350DOT     ]
1351DOT     pipe_do [
1352DOT             shape=ellipse
1353DOT             label="send bereq.\npipe until close"
1354DOT     ]
1355DOT     vcl_pipe -> pipe_do [label="pipe",style=bold,color=orange]
1356DOT     pipe -> vcl_pipe [style=bold,color=orange]
1357DOT }
1358DOT pipe_do -> DONE [style=bold,color=orange]
1359DOT vcl_pipe -> err_pipe [label="error"]
1360DOT err_pipe [label="ERROR",shape=plaintext]
1361 */
1362
1363static int
1364cnt_pipe(struct sess *sp, struct worker *wrk, const struct req *req)
1365{
1366
1367        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1368        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
1369        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
1370        CHECK_OBJ_NOTNULL(req->vcl, VCL_CONF_MAGIC);
1371        AZ(wrk->busyobj);
1372
1373        wrk->acct_tmp.pipe++;
1374        wrk->busyobj = VBO_GetBusyObj(wrk);
1375        WS_Reset(wrk->ws, NULL);
1376        wrk->busyobj = VBO_GetBusyObj(wrk);
1377        http_Setup(wrk->busyobj->bereq, wrk->ws);
1378        http_FilterReq(sp, 0);
1379
1380        VCL_pipe_method(sp);
1381
1382        if (req->handling == VCL_RET_ERROR)
1383                INCOMPL();
1384        assert(req->handling == VCL_RET_PIPE);
1385
1386        PipeSession(sp);
1387        assert(WRW_IsReleased(wrk));
1388        http_Setup(wrk->busyobj->bereq, NULL);
1389        VBO_DerefBusyObj(wrk, &wrk->busyobj);
1390        sp->step = STP_DONE;
1391        return (0);
1392}
1393
1394/*--------------------------------------------------------------------
1395 * RECV
1396 * We have a complete request, set everything up and start it.
1397 * We can come here both with a request from the client and with
1398 * a interior request during ESI delivery.
1399 *
1400DOT subgraph xcluster_recv {
1401DOT     recv [
1402DOT             shape=record
1403DOT             label="{cnt_recv:|{vcl_recv\{\}|req.*}|{<pipe>pipe?|<pass>pass?|<error>error?|<lookup>lookup?}}"
1404DOT     ]
1405DOT }
1406DOT subgraph xcluster_hash {
1407DOT     hash [
1408DOT             shape=record
1409DOT             label="{cnt_recv:|{vcl_hash\{\}|req.*}}"
1410DOT     ]
1411DOT }
1412DOT ESI_REQ [ shape=hexagon ]
1413DOT RESTART -> recv [color=purple]
1414DOT ESI_REQ -> recv
1415DOT recv:pipe -> pipe [style=bold,color=orange]
1416DOT recv:pass -> pass [style=bold,color=red]
1417#DOT recv:error -> err_recv
1418#DOT err_recv [label="ERROR",shape=plaintext]
1419DOT recv:lookup -> hash [style=bold,color=green]
1420DOT hash -> lookup [label="hash",style=bold,color=green]
1421 */
1422
1423static int
1424cnt_recv(struct sess *sp, struct worker *wrk, struct req *req)
1425{
1426        unsigned recv_handling;
1427        struct SHA256Context sha256ctx;
1428
1429        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1430        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
1431        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
1432        CHECK_OBJ_NOTNULL(req->vcl, VCL_CONF_MAGIC);
1433        AZ(req->obj);
1434        AZ(wrk->busyobj);
1435        assert(wrk->wrw.ciov == wrk->wrw.siov);
1436
1437        /* By default we use the first backend */
1438        AZ(req->director);
1439        req->director = req->vcl->director[0];
1440        AN(req->director);
1441
1442        wrk->connect_timeout = 0;
1443        wrk->first_byte_timeout = 0;
1444        wrk->between_bytes_timeout = 0;
1445
1446        req->disable_esi = 0;
1447        req->hash_always_miss = 0;
1448        req->hash_ignore_busy = 0;
1449        req->client_identity = NULL;
1450
1451        http_CollectHdr(req->http, H_Cache_Control);
1452
1453        VCL_recv_method(sp);
1454        recv_handling = req->handling;
1455
1456        if (req->restarts >= cache_param->max_restarts) {
1457                /*
1458                 * XXX: Why not before vcl_recv{} ?  We go to vcl_error{}
1459                 * XXX: without vcl_recv{} on 413 and 417 already.
1460                 * XXX tell vcl_error why we come
1461                 */
1462                if (req->err_code == 0)
1463                        req->err_code = 503;
1464                sp->step = STP_ERROR;
1465                return (0);
1466        }
1467
1468        if (cache_param->http_gzip_support &&
1469             (recv_handling != VCL_RET_PIPE) &&
1470             (recv_handling != VCL_RET_PASS)) {
1471                if (RFC2616_Req_Gzip(sp)) {
1472                        http_Unset(req->http, H_Accept_Encoding);
1473                        http_SetHeader(wrk, sp->vsl_id, req->http,
1474                            "Accept-Encoding: gzip");
1475                } else {
1476                        http_Unset(req->http, H_Accept_Encoding);
1477                }
1478        }
1479
1480        req->sha256ctx = &sha256ctx;    /* so HSH_AddString() can find it */
1481        SHA256_Init(req->sha256ctx);
1482        VCL_hash_method(sp);
1483        assert(req->handling == VCL_RET_HASH);
1484        SHA256_Final(req->digest, req->sha256ctx);
1485        req->sha256ctx = NULL;
1486
1487        if (!strcmp(req->http->hd[HTTP_HDR_REQ].b, "HEAD"))
1488                req->wantbody = 0;
1489        else
1490                req->wantbody = 1;
1491
1492        switch(recv_handling) {
1493        case VCL_RET_LOOKUP:
1494                sp->step = STP_LOOKUP;
1495                return (0);
1496        case VCL_RET_PIPE:
1497                if (req->esi_level > 0) {
1498                        /* XXX: VSL something */
1499                        INCOMPL();
1500                        /* sp->step = STP_DONE; */
1501                        return (1);
1502                }
1503                sp->step = STP_PIPE;
1504                return (0);
1505        case VCL_RET_PASS:
1506                sp->step = STP_PASS;
1507                return (0);
1508        case VCL_RET_ERROR:
1509                sp->step = STP_ERROR;
1510                return (0);
1511        default:
1512                WRONG("Illegal action in vcl_recv{}");
1513        }
1514}
1515
1516/*--------------------------------------------------------------------
1517 * START
1518 * First time we see a request
1519 *
1520DOT start [
1521DOT     shape=box
1522DOT     label="cnt_start:\nDissect request\nHandle expect"
1523DOT ]
1524DOT start -> recv [style=bold,color=green]
1525DOT start -> DONE [label=errors]
1526 */
1527
1528static int
1529cnt_start(struct sess *sp, struct worker *wrk, struct req *req)
1530{
1531        char *p;
1532        const char *r = "HTTP/1.1 100 Continue\r\n\r\n";
1533
1534        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1535        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
1536        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
1537        AZ(req->restarts);
1538        AZ(req->obj);
1539        AZ(req->vcl);
1540        AZ(req->esi_level);
1541        assert(!isnan(sp->t_req));
1542
1543        /* Update stats of various sorts */
1544        wrk->stats.client_req++;
1545        wrk->acct_tmp.req++;
1546
1547        /* Assign XID and log */
1548        req->xid = ++xids;                              /* XXX not locked */
1549        WSP(sp, SLT_ReqStart, "%s %s %u", sp->addr, sp->port,  req->xid);
1550
1551        /* Borrow VCL reference from worker thread */
1552        VCL_Refresh(&wrk->vcl);
1553        req->vcl = wrk->vcl;
1554        wrk->vcl = NULL;
1555
1556        EXP_Clr(&req->exp);
1557
1558        http_Setup(req->http, req->ws);
1559        req->err_code = http_DissectRequest(sp);
1560
1561        /* If we could not even parse the request, just close */
1562        if (req->err_code == 400) {
1563                sp->step = STP_DONE;
1564                SES_Close(sp, "junk");
1565                return (0);
1566        }
1567
1568        req->ws_req = WS_Snapshot(req->ws);
1569
1570        req->doclose = http_DoConnection(req->http);
1571
1572        /*
1573         * We want to deal with Expect: headers the first time we
1574         * attempt the request, and remove them before we move on.
1575         */
1576        if (req->err_code == 0 && http_GetHdr(req->http, H_Expect, &p)) {
1577                if (strcasecmp(p, "100-continue")) {
1578                        req->err_code = 417;
1579                } else if (strlen(r) != write(sp->fd, r, strlen(r))) {
1580                        sp->step = STP_DONE;
1581                        SES_Close(sp, "remote closed");
1582                        return (0);
1583                }
1584        }
1585        http_Unset(req->http, H_Expect);
1586
1587        /* XXX: pull in req-body and make it available instead. */
1588        req->reqbodydone = 0;
1589
1590        HTTP_Copy(req->http0, req->http);       /* Copy for restart/ESI use */
1591
1592        if (req->err_code)
1593                sp->step = STP_ERROR;
1594        else
1595                sp->step = STP_RECV;
1596        return (0);
1597}
1598
1599/*--------------------------------------------------------------------
1600 * Central state engine dispatcher.
1601 *
1602 * Kick the session around until it has had enough.
1603 *
1604 */
1605
1606static void
1607cnt_diag(struct sess *sp, const char *state)
1608{
1609        void *vcl;
1610        void *obj;
1611
1612        if (sp->req == NULL) {
1613                vcl = NULL;
1614                obj = NULL;
1615        } else {
1616                vcl = sp->req->vcl;
1617                obj = sp->req->obj;
1618        }
1619
1620        if (sp->wrk != NULL) {
1621                WSP(sp, SLT_Debug, "vsl_id %u STP_%s sp %p obj %p vcl %p",
1622                    sp->vsl_id, state, sp, obj, vcl);
1623                WSL_Flush(sp->wrk, 0);
1624        } else {
1625                VSL(SLT_Debug, sp->vsl_id,
1626                    "vsl_id %u STP_%s sp %p obj %p vcl %p",
1627                    sp->vsl_id, state, sp, obj, vcl);
1628        }
1629}
1630
1631void
1632CNT_Session(struct sess *sp)
1633{
1634        int done;
1635        struct worker *wrk;
1636
1637        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1638#if 0
1639        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
1640        MPL_AssertSane(req);
1641#endif
1642        wrk = sp->wrk;
1643        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
1644
1645        /*
1646         * Possible entrance states
1647         */
1648        assert(
1649            sp->step == STP_FIRST ||
1650            sp->step == STP_WAIT ||
1651            sp->step == STP_LOOKUP ||
1652            sp->step == STP_RECV);
1653
1654        /*
1655         * Whenever we come in from the acceptor or waiter, we need to set
1656         * blocking mode, but there is no point in setting it when we come from
1657         * ESI or when a parked sessions returns.
1658         * It would be simpler to do this in the acceptor or waiter, but we'd
1659         * rather do the syscall in the worker thread.
1660         * On systems which return errors for ioctl, we close early
1661         */
1662        if ((sp->step == STP_FIRST || sp->step == STP_START) &&
1663            VTCP_blocking(sp->fd)) {
1664                if (errno == ECONNRESET)
1665                        SES_Close(sp, "remote closed");
1666                else
1667                        SES_Close(sp, "error");
1668                sp->step = STP_DONE;
1669        }
1670
1671        /*
1672         * NB: Once done is set, we can no longer touch sp!
1673         */
1674        for (done = 0; !done; ) {
1675                assert(sp->wrk == wrk);
1676#if 0
1677                CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
1678                MPL_AssertSane(req);
1679#endif
1680                /*
1681                 * This is a good place to be paranoid about the various
1682                 * pointers still pointing to the things we expect.
1683                 */
1684                CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
1685                CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
1686                CHECK_OBJ_ORNULL(wrk->nobjhead, OBJHEAD_MAGIC);
1687                WS_Assert(wrk->ws);
1688
1689                switch (sp->step) {
1690#define STEP(l,u,arg) \
1691                    case STP_##u: \
1692                        if (cache_param->diag_bitmap & 0x01) \
1693                                cnt_diag(sp, #u); \
1694                        done = cnt_##l arg; \
1695                        break;
1696#include "tbl/steps.h"
1697#undef STEP
1698                default:
1699                        WRONG("State engine misfire");
1700                }
1701                WS_Assert(wrk->ws);
1702                CHECK_OBJ_ORNULL(wrk->nobjhead, OBJHEAD_MAGIC);
1703        }
1704        WSL_Flush(wrk, 0);
1705#define ACCT(foo)       AZ(wrk->acct_tmp.foo);
1706#include "tbl/acct_fields.h"
1707#undef ACCT
1708        assert(WRW_IsReleased(wrk));
1709}
1710
1711/*
1712DOT }
1713*/
1714
1715/*--------------------------------------------------------------------
1716 * Debugging aids
1717 */
1718
1719static void
1720cli_debug_xid(struct cli *cli, const char * const *av, void *priv)
1721{
1722        (void)priv;
1723        if (av[2] != NULL)
1724                xids = strtoul(av[2], NULL, 0);
1725        VCLI_Out(cli, "XID is %u", xids);
1726}
1727
1728/*
1729 * Default to seed=1, this is the only seed value POSIXl guarantees will
1730 * result in a reproducible random number sequence.
1731 */
1732static void
1733cli_debug_srandom(struct cli *cli, const char * const *av, void *priv)
1734{
1735        (void)priv;
1736        unsigned seed = 1;
1737
1738        if (av[2] != NULL)
1739                seed = strtoul(av[2], NULL, 0);
1740        srandom(seed);
1741        srand48(random());
1742        VCLI_Out(cli, "Random(3) seeded with %u", seed);
1743}
1744
1745static struct cli_proto debug_cmds[] = {
1746        { "debug.xid", "debug.xid",
1747                "\tExamine or set XID\n", 0, 1, "d", cli_debug_xid },
1748        { "debug.srandom", "debug.srandom",
1749                "\tSeed the random(3) function\n", 0, 1, "d",
1750                cli_debug_srandom },
1751        { NULL }
1752};
1753
1754/*--------------------------------------------------------------------
1755 *
1756 */
1757
1758void
1759CNT_Init(void)
1760{
1761
1762        srandomdev();
1763        srand48(random());
1764        xids = random();
1765        CLI_AddFuncs(debug_cmds);
1766}
1767
1768
Note: See TracBrowser for help on using the repository browser.