source: bin/varnishd/cache_backend_simple.c @ 86a6b9

Revision 86a6b9, 9.7 KB checked in by Poul-Henning Kamp <phk@…>, 7 years ago (diff)

Fix typo that made worker process panic whenever a request came in without
a Host: header.

git-svn-id:  http://www.varnish-cache.org/svn/trunk/varnish-cache@1930 d4fa192b-c00b-0410-8231-f00ffab90ce4

  • Property mode set to 100644
Line 
1/*-
2 * Copyright (c) 2006 Verdens Gang AS
3 * Copyright (c) 2006-2007 Linpro 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 * $Id$
30 *
31 *
32 * XXX: When we switch VCL we can have vbe_conn's dangling from
33 * XXX: the backends no longer used.  When the VCL's refcount
34 * XXX: drops to zero we should zap them.
35 */
36
37#include <sys/types.h>
38#include <sys/socket.h>
39
40#include <netdb.h>
41#include <errno.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46#include <poll.h>
47
48#include "shmlog.h"
49#include "cache.h"
50#include "vrt.h"
51
52struct bes {
53        unsigned                magic;
54#define BES_MAGIC               0x015e17ac
55        char                    *hostname;
56        char                    *portname;
57        struct addrinfo         *addr;
58        struct addrinfo         *last_addr;
59        double                  dnsttl;
60        double                  dnstime;
61        unsigned                dnsseq;
62        TAILQ_HEAD(, vbe_conn)  connlist;
63};
64
65/*--------------------------------------------------------------------*/
66
67
68static int
69bes_conn_try_list(struct sess *sp, struct bes *bes)
70{
71        struct addrinfo *ai, *from;
72        struct sockaddr_storage ss;
73        int fam, sockt, proto;
74        socklen_t alen;
75        int s, loops;
76        char abuf1[TCP_ADDRBUFSIZE], abuf2[TCP_ADDRBUFSIZE];
77        char pbuf1[TCP_PORTBUFSIZE], pbuf2[TCP_PORTBUFSIZE];
78        unsigned myseq;
79
80        /* Called with lock held */
81        myseq = bes->dnsseq;
82        loops = 0;
83        from = bes->last_addr;
84        for (ai = from; ai != NULL && (loops != 1 || ai != from);) {
85                fam = ai->ai_family;
86                sockt = ai->ai_socktype;
87                proto = ai->ai_protocol;
88                alen = ai->ai_addrlen;
89                assert(alen <= sizeof ss);
90                memcpy(&ss, ai->ai_addr, alen);
91                UNLOCK(&sp->backend->mtx);
92                s = socket(fam, sockt, proto);
93                if (s >= 0 && connect(s, (void *)&ss, alen)) {
94                        AZ(close(s));
95                        s = -1;
96                }
97                if (s >= 0) {
98                        TCP_myname(s, abuf1, sizeof abuf1, pbuf1, sizeof pbuf1);
99                        TCP_name((void*)&ss, alen,
100                            abuf2, sizeof abuf2, pbuf2, sizeof pbuf2);
101                        WSL(sp->wrk, SLT_BackendOpen, s, "%s %s %s %s %s",
102                            sp->backend->vcl_name, abuf1, pbuf1, abuf2, pbuf2);
103                }
104                LOCK(&sp->backend->mtx);
105                if (s >= 0) {
106                        if (myseq == bes->dnsseq)
107                                bes->last_addr = ai;
108                        return (s);
109                }
110                if (myseq != bes->dnsseq) {
111                        loops = 0;
112                        from = bes->last_addr;
113                        ai = from;
114                } else {
115                        ai = ai->ai_next;
116                        if (ai == NULL) {
117                                loops++;
118                                ai = bes->addr;
119                        }
120                }
121        }
122        return (-1);
123}
124
125/*--------------------------------------------------------------------*/
126
127static int
128bes_conn_try(struct sess *sp, struct backend *bp)
129{
130        int s;
131        struct bes *bes;
132        struct addrinfo *res, hint, *old;
133        int error;
134
135        CAST_OBJ_NOTNULL(bes, bp->priv, BES_MAGIC);
136
137        LOCK(&bp->mtx);
138
139        s = bes_conn_try_list(sp, bes);
140        if (s >= 0) {
141                bp->refcount++;
142                UNLOCK(&bp->mtx);
143                return (s);
144        }
145
146        if (bes->dnstime + bes->dnsttl >= TIM_mono()) {
147                UNLOCK(&bp->mtx);
148                return (-1);
149        }
150
151        /* Then do another lookup to catch DNS changes */
152        bes->dnstime = TIM_mono();
153        UNLOCK(&bp->mtx);
154
155        memset(&hint, 0, sizeof hint);
156        hint.ai_family = PF_UNSPEC;
157        hint.ai_socktype = SOCK_STREAM;
158        res = NULL;
159        error = getaddrinfo(bes->hostname,
160            bes->portname == NULL ? "http" : bes->portname,
161            &hint, &res);
162        if (error) {
163                if (res != NULL)
164                        freeaddrinfo(res);
165                printf("getaddrinfo: %s\n", gai_strerror(error)); /* XXX */
166                LOCK(&bp->mtx);
167        } else {
168                LOCK(&bp->mtx);
169                bes->dnsseq++;
170                old = bes->addr;
171                bes->last_addr = res;
172                bes->addr = res;
173                if (old != NULL)
174                        freeaddrinfo(old);
175        }
176
177        /* And try the entire list */
178        s = bes_conn_try_list(sp, bes);
179        if (s >= 0) {
180                bp->refcount++;
181                UNLOCK(&bp->mtx);
182                return (s);
183        }
184
185        UNLOCK(&bp->mtx);
186        return (-1);
187}
188
189/* Get a backend connection ------------------------------------------
190 *
191 * Try all cached backend connections for this backend, and use the
192 * first one that is looks like it is still connected.
193 * If that fails to get us a connection, create a new one, reusing a
194 * connection from the freelist, if possible.
195 *
196 * This function is slightly complicated by optimizations on besmtx.
197 */
198
199static struct vbe_conn *
200bes_nextfd(struct sess *sp)
201{
202        struct vbe_conn *vc;
203        struct pollfd pfd;
204        struct backend *bp;
205        int reuse = 0;
206        struct bes *bes;
207
208        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
209        CHECK_OBJ_NOTNULL(sp->backend, BACKEND_MAGIC);
210        bp = sp->backend;
211        CAST_OBJ_NOTNULL(bes, bp->priv, BES_MAGIC);
212        while (1) {
213                LOCK(&bp->mtx);
214                vc = TAILQ_FIRST(&bes->connlist);
215                if (vc != NULL) {
216                        bp->refcount++;
217                        assert(vc->backend == bp);
218                        assert(vc->fd >= 0);
219                        TAILQ_REMOVE(&bes->connlist, vc, list);
220                }
221                UNLOCK(&bp->mtx);
222                if (vc == NULL)
223                        break;
224
225                /* Test the connection for remote close before we use it */
226                pfd.fd = vc->fd;
227                pfd.events = POLLIN;
228                pfd.revents = 0;
229                if (!poll(&pfd, 1, 0)) {
230                        /* XXX locking of stats */
231                        VSL_stats->backend_reuse += reuse;
232                        VSL_stats->backend_conn++;
233                        return (vc);
234                }
235                VBE_ClosedFd(sp->wrk, vc);
236        }
237
238        vc = VBE_NewConn();
239        assert(vc->fd == -1);
240        AZ(vc->backend);
241        vc->fd = bes_conn_try(sp, bp);
242        if (vc->fd < 0) {
243                VBE_ReleaseConn(vc);
244                VSL_stats->backend_fail++;
245                return (NULL);
246        } 
247        vc->backend = bp;
248        VSL_stats->backend_conn++;
249        return (vc);
250}
251
252/*--------------------------------------------------------------------*/
253
254static struct vbe_conn *
255bes_GetFd(struct sess *sp)
256{
257        struct vbe_conn *vc;
258        unsigned n;
259        for (n = 1; n < 5; n++) {
260                vc = bes_nextfd(sp);
261                if (vc == NULL) {
262                        usleep(100000 * n);
263                        continue;
264                }
265                assert(vc->fd >= 0);
266                assert(vc->backend == sp->backend);
267                WSL(sp->wrk, SLT_BackendXID, vc->fd, "%u", sp->xid);
268                WSL(sp->wrk, SLT_Backend, sp->fd, "%d %s", vc->fd,
269                    sp->backend->vcl_name);
270                return (vc);
271        }
272        return (NULL);
273}
274
275/* Close a connection ------------------------------------------------*/
276
277static void
278bes_ClosedFd(struct worker *w, struct vbe_conn *vc)
279{
280
281        CHECK_OBJ_NOTNULL(vc, VBE_CONN_MAGIC);
282        CHECK_OBJ_NOTNULL(vc->backend, BACKEND_MAGIC);
283        assert(vc->fd >= 0);
284        WSL(w, SLT_BackendClose, vc->fd, "%s", vc->backend->vcl_name);
285        AZ(close(vc->fd));
286        vc->fd = -1;
287        VBE_DropRef(vc->backend);
288        vc->backend = NULL;
289        VBE_ReleaseConn(vc);
290}
291
292/* Recycle a connection ----------------------------------------------*/
293
294static void
295bes_RecycleFd(struct worker *w, struct vbe_conn *vc)
296{
297        struct bes *bes;
298
299        CHECK_OBJ_NOTNULL(vc, VBE_CONN_MAGIC);
300        CHECK_OBJ_NOTNULL(vc->backend, BACKEND_MAGIC);
301        CAST_OBJ_NOTNULL(bes, vc->backend->priv, BES_MAGIC);
302
303        assert(vc->fd >= 0);
304        WSL(w, SLT_BackendReuse, vc->fd, "%s", vc->backend->vcl_name);
305        LOCK(&vc->backend->mtx);
306        VSL_stats->backend_recycle++;
307        TAILQ_INSERT_HEAD(&bes->connlist, vc, list);
308        VBE_DropRefLocked(vc->backend);
309}
310
311/*--------------------------------------------------------------------*/
312
313static void
314bes_Cleanup(struct backend *b)
315{
316        struct bes *bes;
317        struct vbe_conn *vbe;
318
319        assert(b->refcount == 0);
320        CAST_OBJ_NOTNULL(bes, b->priv, BES_MAGIC);
321        free(bes->portname);
322        free(bes->hostname);
323        freeaddrinfo(bes->addr);
324        while (1) {
325                vbe = TAILQ_FIRST(&bes->connlist);
326                if (vbe == NULL)
327                        break;
328                TAILQ_REMOVE(&bes->connlist, vbe, list);
329                if (vbe->fd >= 0)
330                        close(vbe->fd);
331                free(vbe);
332        }
333        free(bes);
334}
335
336/*--------------------------------------------------------------------*/
337
338static const char *
339bes_GetHostname(struct backend *b)
340{
341        struct bes *bes;
342
343        CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
344        CAST_OBJ_NOTNULL(bes, b->priv, BES_MAGIC);
345        return (bes->hostname);
346}
347
348/*--------------------------------------------------------------------*/
349
350static void
351bes_Init(void)
352{
353
354}
355
356/*--------------------------------------------------------------------*/
357
358struct backend_method backend_method_simple = {
359        .name =                 "simple",
360        .getfd =                bes_GetFd,
361        .close =                bes_ClosedFd,
362        .recycle =              bes_RecycleFd,
363        .gethostname =          bes_GetHostname,
364        .cleanup =              bes_Cleanup,
365        .init =                 bes_Init
366};
367
368/*--------------------------------------------------------------------*/
369
370void
371VRT_init_simple_backend(struct backend **bp, struct vrt_simple_backend *t)
372{
373        struct backend *b;
374        struct bes *bes;
375       
376        /*
377         * Scan existing backends to see if we can recycle one of them.
378         */
379        TAILQ_FOREACH(b, &backendlist, list) {
380                CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
381                if (b->method != &backend_method_simple)
382                        continue;
383                if (strcmp(b->vcl_name, t->name))
384                        continue;
385                CAST_OBJ_NOTNULL(bes, b->priv, BES_MAGIC);
386                if (strcmp(bes->portname, t->port))
387                        continue;
388                if (strcmp(bes->hostname, t->host))
389                        continue;
390                b->refcount++;
391                *bp = b;
392                return;
393        }
394
395        b = VBE_NewBackend(&backend_method_simple);
396
397        bes = calloc(sizeof *bes, 1);
398        XXXAN(bes);
399        bes->magic = BES_MAGIC;
400
401        b->priv = bes;
402
403        bes->dnsttl = 300;
404
405        AN(t->name);
406        b->vcl_name = strdup(t->name);
407        XXXAN(b->vcl_name);
408
409        AN(t->port);
410        bes->portname = strdup(t->port);
411        XXXAN(bes->portname);
412
413        AN(t->host);
414        bes->hostname = strdup(t->host);
415        XXXAN(bes->hostname);
416
417        *bp = b;
418}
Note: See TracBrowser for help on using the repository browser.