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 <errno.h>
46
#include <stdio.h>
47
48
#include "cache_http1.h"
49
#include "vtim.h"
50
51
/*--------------------------------------------------------------------*/
52
53
struct v1l {
54
        unsigned                magic;
55
#define V1L_MAGIC               0x2f2142e5
56
        int                     *wfd;
57
        unsigned                werr;   /* valid after V1L_Flush() */
58
        struct iovec            *iov;
59
        unsigned                siov;
60
        unsigned                niov;
61
        ssize_t                 liov;
62
        ssize_t                 cliov;
63
        unsigned                ciov;   /* Chunked header marker */
64
        double                  t0;
65
        struct vsl_log          *vsl;
66
        ssize_t                 cnt;    /* Flushed byte count */
67
        struct ws               *ws;
68
        uintptr_t               res;
69
};
70
71
/*--------------------------------------------------------------------
72
 * for niov == 0, reserve the ws for max number of iovs
73
 * otherwise, up to niov
74
 */
75
76
void
77 3023
V1L_Open(struct worker *wrk, struct ws *ws, int *fd, struct vsl_log *vsl,
78
    double t0, unsigned niov)
79
{
80
        struct v1l *v1l;
81
        unsigned u;
82
        uintptr_t res;
83
84 3023
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
85 3023
        AZ(wrk->v1l);
86
87 3023
        if (WS_Overflowed(ws))
88 0
                return;
89
90 3023
        if (niov != 0)
91 86
                assert(niov >= 3);
92
93 3023
        res = WS_Snapshot(ws);
94
95 3023
        v1l = WS_Alloc(ws, sizeof *v1l);
96 3023
        if (v1l == NULL)
97 0
                return;
98 3023
        INIT_OBJ(v1l, V1L_MAGIC);
99
100 3023
        v1l->ws = ws;
101 3023
        v1l->res = res;
102
103 3023
        u = WS_ReserveLumps(ws, sizeof(struct iovec));
104 3023
        if (u < 3) {
105
                /* Must have at least 3 in case of chunked encoding */
106 0
                WS_Release(ws, 0);
107 0
                WS_MarkOverflow(ws);
108 0
                return;
109
        }
110 3023
        if (u > IOV_MAX)
111 0
                u = IOV_MAX;
112 3023
        if (niov != 0 && u > niov)
113 86
                u = niov;
114 3023
        v1l->iov = (void*)ws->f;
115 3023
        v1l->siov = u;
116 3023
        v1l->ciov = u;
117 3023
        v1l->werr = 0;
118 3023
        v1l->liov = 0;
119 3023
        v1l->niov = 0;
120 3023
        v1l->wfd = fd;
121 3023
        v1l->t0 = t0;
122 3023
        v1l->vsl = vsl;
123 3023
        wrk->v1l = v1l;
124
125 3023
        if (niov != 0)
126 86
                WS_Release(ws, u * sizeof(struct iovec));
127
}
128
129
unsigned
130 3022
V1L_Close(struct worker *wrk)
131
{
132
        struct v1l *v1l;
133
        unsigned u;
134
135 3022
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
136 3022
        u = V1L_Flush(wrk);
137 3023
        v1l = wrk->v1l;
138 3023
        wrk->v1l = NULL;
139 3023
        CHECK_OBJ_NOTNULL(v1l, V1L_MAGIC);
140 3023
        if (v1l->ws->r)
141 2937
                WS_Release(v1l->ws, 0);
142 3023
        WS_Reset(v1l->ws, v1l->res);
143 3023
        return (u);
144
}
145
146
static void
147 0
v1l_prune(struct v1l *v1l, ssize_t bytes)
148
{
149 0
        ssize_t used = 0;
150
        ssize_t j, used_here;
151
152 0
        for (j = 0; j < v1l->niov; j++) {
153 0
                if (used + v1l->iov[j].iov_len > bytes) {
154
                        /* Cutoff is in this iov */
155 0
                        used_here = bytes - used;
156 0
                        v1l->iov[j].iov_len -= used_here;
157 0
                        v1l->iov[j].iov_base =
158 0
                            (char*)v1l->iov[j].iov_base + used_here;
159 0
                        memmove(v1l->iov, &v1l->iov[j],
160 0
                            (v1l->niov - j) * sizeof(struct iovec));
161 0
                        v1l->niov -= j;
162 0
                        v1l->liov -= bytes;
163 0
                        return;
164
                }
165 0
                used += v1l->iov[j].iov_len;
166
        }
167 0
        AZ(v1l->liov);
168
}
169
170
unsigned
171 5293
V1L_Flush(const struct worker *wrk)
172
{
173
        ssize_t i;
174
        struct v1l *v1l;
175
        char cbuf[32];
176
177 5293
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
178 5293
        v1l = wrk->v1l;
179 5293
        CHECK_OBJ_NOTNULL(v1l, V1L_MAGIC);
180 5293
        AN(v1l->wfd);
181
182 5293
        assert(v1l->niov <= v1l->siov);
183
184 5293
        if (*v1l->wfd >= 0 && v1l->liov > 0 && v1l->werr == 0) {
185 3770
                if (v1l->ciov < v1l->siov && v1l->cliov > 0) {
186
                        /* Add chunk head & tail */
187 652
                        bprintf(cbuf, "00%zx\r\n", v1l->cliov);
188 652
                        i = strlen(cbuf);
189 652
                        v1l->iov[v1l->ciov].iov_base = cbuf;
190 652
                        v1l->iov[v1l->ciov].iov_len = i;
191 652
                        v1l->liov += i;
192
193
                        /* This is OK, because siov was --'ed */
194 652
                        v1l->iov[v1l->niov].iov_base = cbuf + i - 2;
195 652
                        v1l->iov[v1l->niov++].iov_len = 2;
196 652
                        v1l->liov += 2;
197 3118
                } else if (v1l->ciov < v1l->siov) {
198 4
                        v1l->iov[v1l->ciov].iov_base = cbuf;
199 4
                        v1l->iov[v1l->ciov].iov_len = 0;
200
                }
201
202 3770
                i = writev(*v1l->wfd, v1l->iov, v1l->niov);
203 3770
                if (i > 0)
204 3767
                        v1l->cnt += i;
205 7540
                while (i != v1l->liov && i > 0) {
206
                        /* Remove sent data from start of I/O vector,
207
                         * then retry; we hit a timeout, but some data
208
                         * was sent.
209
                         *
210
                         * XXX: Add a "minimum sent data per timeout
211
                         * counter to prevent slowlaris attacks
212
                        */
213
214 0
                        if (VTIM_real() - v1l->t0 > cache_param->send_timeout) {
215 0
                                VSLb(v1l->vsl, SLT_Debug,
216
                                    "Hit total send timeout, "
217
                                    "wrote = %zd/%zd; not retrying",
218
                                    i, v1l->liov);
219 0
                                i = -1;
220 0
                                break;
221
                        }
222
223 0
                        VSLb(v1l->vsl, SLT_Debug,
224
                            "Hit idle send timeout, wrote = %zd/%zd; retrying",
225
                            i, v1l->liov);
226
227 0
                        v1l_prune(v1l, i);
228 0
                        i = writev(*v1l->wfd, v1l->iov, v1l->niov);
229 0
                        if (i > 0)
230 0
                                v1l->cnt += i;
231
                }
232 3770
                if (i <= 0) {
233 3
                        v1l->werr++;
234 3
                        VSLb(v1l->vsl, SLT_Debug,
235
                            "Write error, retval = %zd, len = %zd, errno = %s",
236 3
                            i, v1l->liov, strerror(errno));
237
                }
238
        }
239 5293
        v1l->liov = 0;
240 5293
        v1l->cliov = 0;
241 5293
        v1l->niov = 0;
242 5293
        if (v1l->ciov < v1l->siov)
243 1260
                v1l->ciov = v1l->niov++;
244 5293
        return (v1l->werr);
245
}
246
247
size_t
248 59891
V1L_Write(const struct worker *wrk, const void *ptr, ssize_t len)
249
{
250
        struct v1l *v1l;
251
252 59891
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
253 59891
        v1l = wrk->v1l;
254 59891
        CHECK_OBJ_NOTNULL(v1l, V1L_MAGIC);
255 59891
        AN(v1l->wfd);
256 59891
        if (len == 0 || *v1l->wfd < 0)
257 0
                return (0);
258 59891
        if (len == -1)
259 30618
                len = strlen(ptr);
260 59891
        assert(v1l->niov < v1l->siov);
261 59891
        v1l->iov[v1l->niov].iov_base = TRUST_ME(ptr);
262 59891
        v1l->iov[v1l->niov].iov_len = len;
263 59891
        v1l->liov += len;
264 59891
        v1l->niov++;
265 59891
        v1l->cliov += len;
266 59891
        if (v1l->niov >= v1l->siov)
267 0
                (void)V1L_Flush(wrk);
268 59889
        return (len);
269
}
270
271
void
272 136
V1L_Chunked(const struct worker *wrk)
273
{
274
        struct v1l *v1l;
275
276 136
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
277 136
        v1l = wrk->v1l;
278 136
        CHECK_OBJ_NOTNULL(v1l, V1L_MAGIC);
279
280 136
        assert(v1l->ciov == v1l->siov);
281 136
        assert(v1l->siov >= 3);
282
        /*
283
         * If there are not space for chunked header, a chunk of data and
284
         * a chunk tail, we might as well flush right away.
285
         */
286 136
        if (v1l->niov + 3 >= v1l->siov)
287 0
                (void)V1L_Flush(wrk);
288 136
        v1l->siov--;
289 136
        v1l->ciov = v1l->niov++;
290 136
        v1l->cliov = 0;
291 136
        assert(v1l->ciov < v1l->siov);
292 136
        assert(v1l->niov < v1l->siov);
293 136
}
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 126
V1L_EndChunk(const struct worker *wrk)
304
{
305
        struct v1l *v1l;
306
307 126
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
308 126
        v1l = wrk->v1l;
309 126
        CHECK_OBJ_NOTNULL(v1l, V1L_MAGIC);
310
311 126
        assert(v1l->ciov < v1l->siov);
312 126
        (void)V1L_Flush(wrk);
313 126
        v1l->siov++;
314 126
        v1l->ciov = v1l->siov;
315 126
        v1l->niov = 0;
316 126
        v1l->cliov = 0;
317 126
        (void)V1L_Write(wrk, "0\r\n\r\n", -1);
318 126
}