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
 * SPDX-License-Identifier: BSD-2-Clause
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions
12
 * are met:
13
 * 1. Redistributions of source code must retain the above copyright
14
 *    notice, this list of conditions and the following disclaimer.
15
 * 2. Redistributions in binary form must reproduce the above copyright
16
 *    notice, this list of conditions and the following disclaimer in the
17
 *    documentation and/or other materials provided with the distribution.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
23
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
 * SUCH DAMAGE.
30
 *
31
 * Write data to fd
32
 * We try to use writev() if possible in order to minimize number of
33
 * syscalls made and packets sent.  It also just might allow the worker
34
 * thread to complete the request without holding stuff locked.
35
 *
36
 * XXX: chunked header (generated in Flush) and Tail (EndChunk)
37
 *      are not accounted by means of the size_t returned. Obvious ideas:
38
 *      - add size_t return value to Flush and EndChunk
39
 *      - base accounting on (struct v1l).cnt
40
 */
41
42
#include "config.h"
43
44
#include <sys/uio.h>
45
#include "cache/cache_varnishd.h"
46
47
#include <stdio.h>
48
49
#include "cache_http1.h"
50
#include "vtim.h"
51
52
/*--------------------------------------------------------------------*/
53
54
struct v1l {
55
        unsigned                magic;
56
#define V1L_MAGIC               0x2f2142e5
57
        int                     *wfd;
58
        enum sess_close         werr;   /* valid after V1L_Flush() */
59
        struct iovec            *iov;
60
        unsigned                siov;
61
        unsigned                niov;
62
        ssize_t                 liov;
63
        ssize_t                 cliov;
64
        unsigned                ciov;   /* Chunked header marker */
65
        vtim_real               deadline;
66
        struct vsl_log          *vsl;
67
        ssize_t                 cnt;    /* Flushed byte count */
68
        struct ws               *ws;
69
        uintptr_t               res;
70
};
71
72
/*--------------------------------------------------------------------
73
 * for niov == 0, reserve the ws for max number of iovs
74
 * otherwise, up to niov
75
 */
76
77
void
78 4433
V1L_Open(struct worker *wrk, struct ws *ws, int *fd, struct vsl_log *vsl,
79
    vtim_real deadline, unsigned niov)
80
{
81
        struct v1l *v1l;
82
        unsigned u;
83
        uintptr_t res;
84
85 4433
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
86 4433
        AZ(wrk->v1l);
87
88 4433
        if (WS_Overflowed(ws))
89 0
                return;
90
91 4433
        if (niov != 0)
92 2562
                assert(niov >= 3);
93
94 4433
        res = WS_Snapshot(ws);
95
96 4433
        v1l = WS_Alloc(ws, sizeof *v1l);
97 4433
        if (v1l == NULL)
98 0
                return;
99 4433
        INIT_OBJ(v1l, V1L_MAGIC);
100
101 4433
        v1l->ws = ws;
102 4433
        v1l->res = res;
103
104 4433
        u = WS_ReserveLumps(ws, sizeof(struct iovec));
105 4433
        if (u < 3) {
106
                /* Must have at least 3 in case of chunked encoding */
107 0
                WS_Release(ws, 0);
108 0
                WS_MarkOverflow(ws);
109 0
                return;
110
        }
111 4433
        if (u > IOV_MAX)
112 0
                u = IOV_MAX;
113 4433
        if (niov != 0 && u > niov)
114 2489
                u = niov;
115 4433
        v1l->iov = (void*)ws->f;
116 4433
        v1l->siov = u;
117 4433
        v1l->ciov = u;
118 4433
        v1l->wfd = fd;
119 4433
        v1l->deadline = deadline;
120 4433
        v1l->vsl = vsl;
121 4433
        wrk->v1l = v1l;
122
123 4433
        if (niov != 0)
124 2562
                WS_Release(ws, u * sizeof(struct iovec));
125 4433
}
126
127
enum sess_close
128 4433
V1L_Close(struct worker *wrk, uint64_t *cnt)
129
{
130
        struct v1l *v1l;
131
        enum sess_close sc;
132
133 4433
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
134 4433
        AN(cnt);
135 4433
        sc = V1L_Flush(wrk);
136 4433
        v1l = wrk->v1l;
137 4433
        wrk->v1l = NULL;
138 4433
        CHECK_OBJ_NOTNULL(v1l, V1L_MAGIC);
139 4433
        *cnt = v1l->cnt;
140 4433
        if (v1l->ws->r)
141 1871
                WS_Release(v1l->ws, 0);
142 4433
        WS_Rollback(v1l->ws, v1l->res);
143 4433
        ZERO_OBJ(v1l, sizeof *v1l);
144 4433
        return (sc);
145
}
146
147
static void
148 6
v1l_prune(struct v1l *v1l, size_t bytes)
149
{
150 6
        ssize_t used = 0;
151
        ssize_t j, used_here;
152
153 136
        for (j = 0; j < v1l->niov; j++) {
154 136
                if (used + v1l->iov[j].iov_len > bytes) {
155
                        /* Cutoff is in this iov */
156 6
                        used_here = bytes - used;
157 6
                        v1l->iov[j].iov_len -= used_here;
158 6
                        v1l->iov[j].iov_base =
159 6
                            (char*)v1l->iov[j].iov_base + used_here;
160 12
                        memmove(v1l->iov, &v1l->iov[j],
161 6
                            (v1l->niov - j) * sizeof(struct iovec));
162 6
                        v1l->niov -= j;
163 6
                        v1l->liov -= bytes;
164 6
                        return;
165
                }
166 130
                used += v1l->iov[j].iov_len;
167 130
        }
168 0
        AZ(v1l->liov);
169 6
}
170
171
enum sess_close
172 9093
V1L_Flush(const struct worker *wrk)
173
{
174
        ssize_t i;
175
        struct v1l *v1l;
176
        char cbuf[32];
177
178 9093
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
179 9094
        v1l = wrk->v1l;
180 9094
        CHECK_OBJ_NOTNULL(v1l, V1L_MAGIC);
181 9094
        AN(v1l->wfd);
182
183 9093
        assert(v1l->niov <= v1l->siov);
184
185 9093
        if (*v1l->wfd >= 0 && v1l->liov > 0 && v1l->werr == SC_NULL) {
186 5268
                if (v1l->ciov < v1l->siov && v1l->cliov > 0) {
187
                        /* Add chunk head & tail */
188 773
                        bprintf(cbuf, "00%zx\r\n", v1l->cliov);
189 773
                        i = strlen(cbuf);
190 773
                        v1l->iov[v1l->ciov].iov_base = cbuf;
191 773
                        v1l->iov[v1l->ciov].iov_len = i;
192 773
                        v1l->liov += i;
193
194
                        /* This is OK, because siov was --'ed */
195 773
                        v1l->iov[v1l->niov].iov_base = cbuf + i - 2;
196 773
                        v1l->iov[v1l->niov++].iov_len = 2;
197 773
                        v1l->liov += 2;
198 5268
                } else if (v1l->ciov < v1l->siov) {
199 14
                        v1l->iov[v1l->ciov].iov_base = cbuf;
200 14
                        v1l->iov[v1l->ciov].iov_len = 0;
201 14
                }
202
203 5268
                i = 0;
204 5268
                do {
205 5297
                        if (VTIM_real() > v1l->deadline) {
206 8
                                VSLb(v1l->vsl, SLT_Debug,
207
                                    "Hit total send timeout, "
208
                                    "wrote = %zd/%zd; not retrying",
209 4
                                    i, v1l->liov);
210 4
                                i = -1;
211 4
                                break;
212
                        }
213
214 5293
                        i = writev(*v1l->wfd, v1l->iov, v1l->niov);
215 5293
                        if (i > 0)
216 5264
                                v1l->cnt += i;
217
218 5293
                        if (i == v1l->liov)
219 5258
                                break;
220
221
                        /* we hit a timeout, and some data may have been sent:
222
                         * Remove sent data from start of I/O vector, then retry
223
                         *
224
                         * XXX: Add a "minimum sent data per timeout counter to
225
                         * prevent slowloris attacks
226
                         */
227
228 70
                        VSLb(v1l->vsl, SLT_Debug,
229
                            "Hit idle send timeout, wrote = %zd/%zd; retrying",
230 35
                            i, v1l->liov);
231
232 35
                        if (i > 0)
233 6
                                v1l_prune(v1l, i);
234 35
                } while (i > 0 || errno == EWOULDBLOCK);
235
236 5268
                if (i <= 0) {
237 20
                        VSLb(v1l->vsl, SLT_Debug,
238
                            "Write error, retval = %zd, len = %zd, errno = %s",
239 10
                            i, v1l->liov, vstrerror(errno));
240 10
                        AZ(v1l->werr);
241 10
                        if (errno == EPIPE)
242 6
                                v1l->werr = SC_REM_CLOSE;
243
                        else
244 4
                                v1l->werr = SC_TX_ERROR;
245 10
                }
246 5268
        }
247 9094
        v1l->liov = 0;
248 9094
        v1l->cliov = 0;
249 9094
        v1l->niov = 0;
250 9094
        if (v1l->ciov < v1l->siov)
251 1619
                v1l->ciov = v1l->niov++;
252 9094
        return (v1l->werr);
253
}
254
255
size_t
256 91208
V1L_Write(const struct worker *wrk, const void *ptr, ssize_t len)
257
{
258
        struct v1l *v1l;
259
260 91208
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
261 91221
        v1l = wrk->v1l;
262 91221
        CHECK_OBJ_NOTNULL(v1l, V1L_MAGIC);
263 91221
        AN(v1l->wfd);
264 91220
        if (len == 0 || *v1l->wfd < 0)
265 0
                return (0);
266 91220
        if (len == -1)
267 46712
                len = strlen(ptr);
268 91221
        assert(v1l->niov < v1l->siov);
269 91218
        v1l->iov[v1l->niov].iov_base = TRUST_ME(ptr);
270 91218
        v1l->iov[v1l->niov].iov_len = len;
271 91218
        v1l->liov += len;
272 91218
        v1l->niov++;
273 91218
        v1l->cliov += len;
274 91218
        if (v1l->niov >= v1l->siov)
275 28
                (void)V1L_Flush(wrk);
276 91219
        return (len);
277 91222
}
278
279
void
280 237
V1L_Chunked(const struct worker *wrk)
281
{
282
        struct v1l *v1l;
283
284 237
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
285 237
        v1l = wrk->v1l;
286 237
        CHECK_OBJ_NOTNULL(v1l, V1L_MAGIC);
287
288 237
        assert(v1l->ciov == v1l->siov);
289 237
        assert(v1l->siov >= 3);
290
        /*
291
         * If there is no space for chunked header, a chunk of data and
292
         * a chunk tail, we might as well flush right away.
293
         */
294 237
        if (v1l->niov + 3 >= v1l->siov)
295 0
                (void)V1L_Flush(wrk);
296 237
        v1l->siov--;
297 237
        v1l->ciov = v1l->niov++;
298 237
        v1l->cliov = 0;
299 237
        assert(v1l->ciov < v1l->siov);
300 237
        assert(v1l->niov < v1l->siov);
301 237
}
302
303
/*
304
 * XXX: It is not worth the complexity to attempt to get the
305
 * XXX: end of chunk into the V1L_Flush(), because most of the time
306
 * XXX: if not always, that is a no-op anyway, because the calling
307
 * XXX: code already called V1L_Flush() to release local storage.
308
 */
309
310
void
311 221
V1L_EndChunk(const struct worker *wrk)
312
{
313
        struct v1l *v1l;
314
315 221
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
316 221
        v1l = wrk->v1l;
317 221
        CHECK_OBJ_NOTNULL(v1l, V1L_MAGIC);
318
319 221
        assert(v1l->ciov < v1l->siov);
320 221
        (void)V1L_Flush(wrk);
321 221
        v1l->siov++;
322 221
        v1l->ciov = v1l->siov;
323 221
        v1l->niov = 0;
324 221
        v1l->cliov = 0;
325 221
        (void)V1L_Write(wrk, "0\r\n\r\n", -1);
326 221
}