varnish-cache/bin/varnishd/http1/cache_http1_line.c
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
 * Write data to fd
30
 * We try to use writev() if possible in order to minimize number of
31
 * syscalls made and packets sent.  It also just might allow the worker
32
 * thread to complete the request without holding stuff locked.
33
 *
34
 * XXX: chunked header (generated in Flush) and Tail (EndChunk)
35
 *      are not accounted by means of the size_t returned. Obvious ideas:
36
 *      - add size_t return value to Flush and EndChunk
37
 *      - base accounting on (struct v1l).cnt
38
 */
39
40
#include "config.h"
41
42
#include <sys/uio.h>
43
#include "cache/cache_varnishd.h"
44
45
#include <stdio.h>
46
47
#include "cache_http1.h"
48
#include "vtim.h"
49
50
/*--------------------------------------------------------------------*/
51
52
struct v1l {
53
        unsigned                magic;
54
#define V1L_MAGIC               0x2f2142e5
55
        int                     *wfd;
56
        unsigned                werr;   /* valid after V1L_Flush() */
57
        struct iovec            *iov;
58
        unsigned                siov;
59
        unsigned                niov;
60
        ssize_t                 liov;
61
        ssize_t                 cliov;
62
        unsigned                ciov;   /* Chunked header marker */
63
        vtim_real               deadline;
64
        struct vsl_log          *vsl;
65
        ssize_t                 cnt;    /* Flushed byte count */
66
        struct ws               *ws;
67
        uintptr_t               res;
68
};
69
70
/*--------------------------------------------------------------------
71
 * for niov == 0, reserve the ws for max number of iovs
72
 * otherwise, up to niov
73
 */
74
75
void
76 32120
V1L_Open(struct worker *wrk, struct ws *ws, int *fd, struct vsl_log *vsl,
77
    vtim_real deadline, unsigned niov)
78
{
79
        struct v1l *v1l;
80
        unsigned u;
81
        uintptr_t res;
82
83 32120
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
84 32120
        AZ(wrk->v1l);
85
86 32120
        if (WS_Overflowed(ws))
87 0
                return;
88
89 32120
        if (niov != 0)
90 18424
                assert(niov >= 3);
91
92 32120
        res = WS_Snapshot(ws);
93
94 32120
        v1l = WS_Alloc(ws, sizeof *v1l);
95 32120
        if (v1l == NULL)
96 0
                return;
97 32120
        INIT_OBJ(v1l, V1L_MAGIC);
98
99 32120
        v1l->ws = ws;
100 32120
        v1l->res = res;
101
102 32120
        u = WS_ReserveLumps(ws, sizeof(struct iovec));
103 32120
        if (u < 3) {
104
                /* Must have at least 3 in case of chunked encoding */
105 0
                WS_Release(ws, 0);
106 0
                WS_MarkOverflow(ws);
107 0
                return;
108
        }
109 32120
        if (u > IOV_MAX)
110 0
                u = IOV_MAX;
111 32120
        if (niov != 0 && u > niov)
112 17840
                u = niov;
113 32120
        v1l->iov = (void*)ws->f;
114 32120
        v1l->siov = u;
115 32120
        v1l->ciov = u;
116 32120
        v1l->wfd = fd;
117 32120
        v1l->deadline = deadline;
118 32120
        v1l->vsl = vsl;
119 32120
        wrk->v1l = v1l;
120
121 32120
        if (niov != 0)
122 18424
                WS_Release(ws, u * sizeof(struct iovec));
123 32120
}
124
125
unsigned
126 32120
V1L_Close(struct worker *wrk, uint64_t *cnt)
127
{
128
        struct v1l *v1l;
129
        unsigned u;
130
131 32120
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
132 32120
        AN(cnt);
133 32120
        u = V1L_Flush(wrk);
134 32120
        v1l = wrk->v1l;
135 32120
        wrk->v1l = NULL;
136 32120
        CHECK_OBJ_NOTNULL(v1l, V1L_MAGIC);
137 32120
        *cnt = v1l->cnt;
138 32120
        if (v1l->ws->r)
139 13696
                WS_Release(v1l->ws, 0);
140 32120
        WS_Reset(v1l->ws, v1l->res);
141 32120
        ZERO_OBJ(v1l, sizeof *v1l);
142 32120
        return (u);
143
}
144
145
static void
146 284
v1l_prune(struct v1l *v1l, ssize_t bytes)
147
{
148 284
        ssize_t used = 0;
149
        ssize_t j, used_here;
150
151 620
        for (j = 0; j < v1l->niov; j++) {
152 620
                if (used + v1l->iov[j].iov_len > bytes) {
153
                        /* Cutoff is in this iov */
154 284
                        used_here = bytes - used;
155 284
                        v1l->iov[j].iov_len -= used_here;
156 284
                        v1l->iov[j].iov_base =
157 284
                            (char*)v1l->iov[j].iov_base + used_here;
158 568
                        memmove(v1l->iov, &v1l->iov[j],
159 284
                            (v1l->niov - j) * sizeof(struct iovec));
160 284
                        v1l->niov -= j;
161 284
                        v1l->liov -= bytes;
162 284
                        return;
163
                }
164 336
                used += v1l->iov[j].iov_len;
165 336
        }
166 0
        AZ(v1l->liov);
167 284
}
168
169
unsigned
170 54820
V1L_Flush(const struct worker *wrk)
171
{
172
        ssize_t i;
173
        struct v1l *v1l;
174
        char cbuf[32];
175
176 54820
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
177 54820
        v1l = wrk->v1l;
178 54820
        CHECK_OBJ_NOTNULL(v1l, V1L_MAGIC);
179 54820
        AN(v1l->wfd);
180
181 54821
        assert(v1l->niov <= v1l->siov);
182
183 54820
        if (*v1l->wfd >= 0 && v1l->liov > 0 && v1l->werr == 0) {
184 38658
                if (v1l->ciov < v1l->siov && v1l->cliov > 0) {
185
                        /* Add chunk head & tail */
186 6027
                        bprintf(cbuf, "00%zx\r\n", v1l->cliov);
187 6027
                        i = strlen(cbuf);
188 6027
                        v1l->iov[v1l->ciov].iov_base = cbuf;
189 6027
                        v1l->iov[v1l->ciov].iov_len = i;
190 6027
                        v1l->liov += i;
191
192
                        /* This is OK, because siov was --'ed */
193 6027
                        v1l->iov[v1l->niov].iov_base = cbuf + i - 2;
194 6027
                        v1l->iov[v1l->niov++].iov_len = 2;
195 6027
                        v1l->liov += 2;
196 38658
                } else if (v1l->ciov < v1l->siov) {
197 108
                        v1l->iov[v1l->ciov].iov_base = cbuf;
198 108
                        v1l->iov[v1l->ciov].iov_len = 0;
199 108
                }
200
201 38658
                i = writev(*v1l->wfd, v1l->iov, v1l->niov);
202 38658
                if (i > 0)
203 38633
                        v1l->cnt += i;
204 39566
                while (i != v1l->liov && (i > 0 || errno == EWOULDBLOCK)) {
205
                        /* Remove sent data from start of I/O vector,
206
                         * then retry; we hit a timeout, but some data
207
                         * was sent.
208
                         *
209
                         * XXX: Add a "minimum sent data per timeout
210
                         * counter to prevent slowloris attacks
211
                        */
212
213 442
                        if (VTIM_real() > v1l->deadline) {
214 32
                                VSLb(v1l->vsl, SLT_Debug,
215
                                    "Hit total send timeout, "
216
                                    "wrote = %zd/%zd; not retrying",
217 16
                                    i, v1l->liov);
218 16
                                i = -1;
219 16
                                break;
220
                        }
221
222 852
                        VSLb(v1l->vsl, SLT_Debug,
223
                            "Hit idle send timeout, wrote = %zd/%zd; retrying",
224 426
                            i, v1l->liov);
225
226 426
                        if (i > 0)
227 284
                                v1l_prune(v1l, i);
228 426
                        i = writev(*v1l->wfd, v1l->iov, v1l->niov);
229 426
                        if (i > 0)
230 268
                                v1l->cnt += i;
231
                }
232 38658
                if (i <= 0) {
233 56
                        v1l->werr++;
234 112
                        VSLb(v1l->vsl, SLT_Debug,
235
                            "Write error, retval = %zd, len = %zd, errno = %s",
236 56
                            i, v1l->liov, vstrerror(errno));
237 56
                }
238 38658
        }
239 54822
        v1l->liov = 0;
240 54822
        v1l->cliov = 0;
241 54822
        v1l->niov = 0;
242 54822
        if (v1l->ciov < v1l->siov)
243 11776
                v1l->ciov = v1l->niov++;
244 54822
        return (v1l->werr);
245
}
246
247
size_t
248 660375
V1L_Write(const struct worker *wrk, const void *ptr, ssize_t len)
249
{
250
        struct v1l *v1l;
251
252 660375
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
253 660404
        v1l = wrk->v1l;
254 660404
        CHECK_OBJ_NOTNULL(v1l, V1L_MAGIC);
255 660421
        AN(v1l->wfd);
256 660465
        if (len == 0 || *v1l->wfd < 0)
257 0
                return (0);
258 660467
        if (len == -1)
259 338331
                len = strlen(ptr);
260 660479
        assert(v1l->niov < v1l->siov);
261 660456
        v1l->iov[v1l->niov].iov_base = TRUST_ME(ptr);
262 660456
        v1l->iov[v1l->niov].iov_len = len;
263 660456
        v1l->liov += len;
264 660456
        v1l->niov++;
265 660456
        v1l->cliov += len;
266 660456
        if (v1l->niov >= v1l->siov)
267 216
                (void)V1L_Flush(wrk);
268 660450
        return (len);
269 660428
}
270
271
void
272 1829
V1L_Chunked(const struct worker *wrk)
273
{
274
        struct v1l *v1l;
275
276 1829
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
277 1829
        v1l = wrk->v1l;
278 1829
        CHECK_OBJ_NOTNULL(v1l, V1L_MAGIC);
279
280 1829
        assert(v1l->ciov == v1l->siov);
281 1829
        assert(v1l->siov >= 3);
282
        /*
283
         * If there is no space for chunked header, a chunk of data and
284
         * a chunk tail, we might as well flush right away.
285
         */
286 1829
        if (v1l->niov + 3 >= v1l->siov)
287 0
                (void)V1L_Flush(wrk);
288 1829
        v1l->siov--;
289 1829
        v1l->ciov = v1l->niov++;
290 1829
        v1l->cliov = 0;
291 1829
        assert(v1l->ciov < v1l->siov);
292 1829
        assert(v1l->niov < v1l->siov);
293 1829
}
294
295
/*
296
 * XXX: It is not worth the complexity to attempt to get the
297
 * XXX: end of chunk into the V1L_Flush(), because most of the time
298
 * XXX: if not always, that is a no-op anyway, because the calling
299
 * XXX: code already called V1L_Flush() to release local storage.
300
 */
301
302
void
303 1741
V1L_EndChunk(const struct worker *wrk)
304
{
305
        struct v1l *v1l;
306
307 1741
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
308 1741
        v1l = wrk->v1l;
309 1741
        CHECK_OBJ_NOTNULL(v1l, V1L_MAGIC);
310
311 1741
        assert(v1l->ciov < v1l->siov);
312 1741
        (void)V1L_Flush(wrk);
313 1741
        v1l->siov++;
314 1741
        v1l->ciov = v1l->siov;
315 1741
        v1l->niov = 0;
316 1741
        v1l->cliov = 0;
317 1741
        (void)V1L_Write(wrk, "0\r\n\r\n", -1);
318 1741
}