vinyl-cache/bin/vinyld/http1/cache_http1_fsm.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2015 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 *
30
 * This file contains the two central state machine for pushing HTTP1
31
 * sessions through their states.
32
 *
33
 */
34
35
#include "config.h"
36
37
#include <stdio.h>
38
#include <stdlib.h>
39
40
#include "cache/cache_int.h"
41
#include "cache/cache_objhead.h"
42
#include "cache/cache_transport.h"
43
#include "cache_http1.h"
44
45
#include "vtcp.h"
46
47
static const char H1NEWREQ[] = "HTTP1::NewReq";
48
static const char H1PROC[] = "HTTP1::Proc";
49
static const char H1CLEANUP[] = "HTTP1::Cleanup";
50
51
static void HTTP1_Session(struct worker *, struct req *);
52
53
static void
54 272424
http1_setstate(const struct sess *sp, const char *s)
55
{
56
        uintptr_t p;
57
58 272424
        p = (uintptr_t)s;
59 272424
        AZ(SES_Set_proto_priv(sp, &p));
60 272424
}
61
62
static const char *
63 327035
http1_getstate(const struct sess *sp)
64
{
65
        uintptr_t *p;
66
67 327035
        AZ(SES_Get_proto_priv(sp, &p));
68 327035
        return ((const char *)*p);
69
}
70
71
/*--------------------------------------------------------------------
72
 * Call protocol for this request
73
 */
74
75
static void v_matchproto_(task_func_t)
76 55126
http1_req(struct worker *wrk, void *arg)
77
{
78
        struct req *req;
79
80 55126
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
81 55126
        CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC);
82
83 55126
        THR_SetRequest(req);
84 55126
        assert(!WS_IsReserved(wrk->aws));
85 55126
        HTTP1_Session(wrk, req);
86 55126
        WS_Assert(wrk->aws);
87 55126
        THR_SetRequest(NULL);
88 55126
}
89
90
/*--------------------------------------------------------------------
91
 * Call protocol for this session (new or from waiter)
92
 *
93
 * When sessions are rescheduled from the waiter, a struct pool_task
94
 * is put on the reserved session workspace (for reasons of memory
95
 * conservation).  This reservation is released as the first thing.
96
 * The acceptor and any other code which schedules this function
97
 * must obey this calling convention with a dummy reservation.
98
 */
99
100
static void v_matchproto_(task_func_t)
101 48561
http1_new_session(struct worker *wrk, void *arg)
102
{
103
        struct sess *sp;
104
        struct req *req;
105
        uintptr_t *u;
106
        ssize_t sz;
107
108 48561
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
109 48561
        CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC);
110 48561
        sp = req->sp;
111 48561
        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
112
113 48561
        HTC_RxInit(req->htc, req->ws);
114
115 48561
        sz = sizeof u;
116 48561
        if (SES_Get_proto_priv(sp, &u) &&
117 48558
            !SES_Reserve_proto_priv(sp, &u, &sz)) {
118
                /* Out of session workspace. Free the req, close the sess,
119
                 * and do not set a new task func, which will exit the
120
                 * worker thread. */
121 21
                VSL(SLT_Error, req->sp->vxid,
122
                    "insufficient workspace (proto_priv)");
123 21
                WS_Release(req->ws, 0);
124 21
                Req_Release(req);
125 21
                SES_Delete(sp, SC_RX_JUNK, NAN);
126 21
                return;
127
        }
128 48540
        assert(sz == sizeof u);
129 48538
        http1_setstate(sp, H1NEWREQ);
130 48538
        wrk->task->func = http1_req;
131 48538
        wrk->task->priv = req;
132 48559
}
133
134
static void v_matchproto_(task_func_t)
135 4076
http1_unwait(struct worker *wrk, void *arg)
136
{
137
        struct sess *sp;
138
        struct req *req;
139
140 4076
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
141 4076
        CAST_OBJ_NOTNULL(sp, arg, SESS_MAGIC);
142 4076
        WS_Release(sp->ws, 0);
143 4076
        req = Req_New(sp, NULL);
144 4076
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
145 4076
        req->htc->rfd = &sp->fd;
146 4076
        HTC_RxInit(req->htc, req->ws);
147 4076
        http1_setstate(sp, H1NEWREQ);
148 4076
        wrk->task->func = http1_req;
149 4076
        wrk->task->priv = req;
150 4076
}
151
152
static void v_matchproto_(vtr_req_body_t)
153 4956
http1_req_body(struct req *req)
154
{
155
156 4956
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
157 4956
        if (V1F_Setup_Fetch(req->vfc, req->htc) != 0)
158 0
                req->req_body_status = BS_ERROR;
159 4956
}
160
161
static void
162 126
http1_sess_panic(struct vsb *vsb, const struct sess *sp)
163
{
164
165 126
        VSB_printf(vsb, "state = %s\n", http1_getstate(sp));
166 126
}
167
168
static void
169 105
http1_req_panic(struct vsb *vsb, const struct req *req)
170
{
171
172 105
        VSB_printf(vsb, "state = %s\n", http1_getstate(req->sp));
173 105
}
174
175
static void v_matchproto_(vtr_req_fail_f)
176 630
http1_req_fail(struct req *req, stream_close_t reason)
177
{
178 630
        assert(reason != SC_NULL);
179 630
        assert(req->sp->fd != 0);
180 630
        if (req->sp->fd > 0)
181 630
                SES_Close(req->sp, reason);
182 630
}
183
184
static int v_matchproto_(vtr_minimal_response_f)
185 2709
http1_minimal_response(struct req *req, uint16_t status)
186
{
187
        ssize_t wl, l;
188
        char buf[80];
189
        const char *reason;
190
191 2709
        assert(status >= 100);
192 2709
        assert(status < 1000);
193
194 2709
        reason = http_Status2Reason(status, NULL);
195
196 2709
        bprintf(buf, "HTTP/1.1 %03d %s\r\n\r\n", status, reason);
197 2709
        l = vstrlen(buf);
198
199 2709
        VSLb(req->vsl, SLT_RespProtocol, "HTTP/1.1");
200 2709
        VSLb(req->vsl, SLT_RespStatus, "%03d", status);
201 2709
        VSLbs(req->vsl, SLT_RespReason, TOSTRAND(reason));
202
203 2709
        if (status >= 400)
204 2604
                req->err_code = status;
205 2709
        wl = write(req->sp->fd, buf, l);
206
207 2709
        if (wl > 0)
208 2709
                req->acct.resp_hdrbytes += wl;
209 2709
        if (wl != l) {
210 0
                if (wl < 0)
211 0
                        VTCP_Assert(1);
212 0
                if (req->doclose == SC_NULL)
213 0
                        req->doclose = SC_REM_CLOSE;
214 0
                return (-1);
215
        }
216 2709
        return (0);
217 2709
}
218
219
struct transport HTTP1_transport = {
220
        .name =                 "HTTP/1",
221
        .proto_ident =          "HTTP",
222
        .magic =                TRANSPORT_MAGIC,
223
        .deliver =              V1D_Deliver,
224
        .minimal_response =     http1_minimal_response,
225
        .new_session =          http1_new_session,
226
        .req_body =             http1_req_body,
227
        .req_fail =             http1_req_fail,
228
        .req_panic =            http1_req_panic,
229
        .sess_panic =           http1_sess_panic,
230
        .unwait =               http1_unwait,
231
};
232
233
/*----------------------------------------------------------------------
234
 */
235
236
static inline void
237 966
http1_abort(struct req *req, uint16_t status)
238
{
239 966
        assert(req->doclose != SC_NULL);
240 966
        assert(status >= 400);
241 966
        (void)http1_minimal_response(req, status);
242 966
}
243
244
static int
245 76521
http1_dissect(struct worker *wrk, struct req *req)
246
{
247
248 76521
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
249 76521
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
250 76521
        CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC);
251
252
        /* Allocate a new vxid now that we know we'll need it. */
253 76521
        assert(IS_NO_VXID(req->vsl->wid));
254 76521
        req->vsl->wid = VXID_Get(wrk, VSL_CLIENTMARKER);
255
256 76521
        VSLb(req->vsl, SLT_Begin, "req %ju rxreq", VXID(req->sp->vxid));
257 76521
        VSL(SLT_Link, req->sp->vxid, "req %ju rxreq", VXID(req->vsl->wid));
258 76521
        AZ(isnan(req->t_first)); /* First byte timestamp set by http1_wait */
259 76521
        AZ(isnan(req->t_req));   /* Complete req rcvd set by http1_wait */
260 76521
        req->t_prev = req->t_first;
261 76521
        VSLb_ts_req(req, "Start", req->t_first);
262 76521
        VSLb_ts_req(req, "Req", req->t_req);
263
264 76521
        HTTP_Setup(req->http, req->ws, req->vsl, SLT_ReqMethod);
265 76521
        req->err_code = HTTP1_DissectRequest(req->htc, req->http);
266
267
        /* If we could not even parse the request, just close */
268 76521
        if (req->err_code != 0) {
269 966
                wrk->stats->client_req_400++;
270
271 966
                (void)Req_LogStart(wrk, req);
272
273 966
                req->doclose = SC_RX_JUNK;
274 966
                http1_abort(req, 400);
275 966
                return (-1);
276
        }
277
278 75555
        AZ(req->req_body_status);
279 75555
        req->req_body_status = req->htc->body_status;
280 75555
        return (0);
281 76521
}
282
283
/*----------------------------------------------------------------------
284
 */
285
286
static void
287 54988
HTTP1_Session(struct worker *wrk, struct req *req)
288
{
289
        enum htc_status_e hs;
290
        struct sess *sp;
291
        const char *st;
292
        int i;
293
294 54988
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
295 54988
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
296 54988
        sp = req->sp;
297 54988
        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
298
299
        /*
300
         * Whenever we come in from the acceptor or waiter, we need to set
301
         * blocking mode.  It would be simpler to do this in the acceptor
302
         * or waiter, but we'd rather do the syscall in the worker thread.
303
         */
304 54988
        if (http1_getstate(sp) == H1NEWREQ)
305 52614
                VTCP_blocking(sp->fd);
306 54988
        req->transport = XPORT_ByNumber(sp->sattr[SA_TRANSPORT]);
307
308 270564
        while (1) {
309 271572
                st = http1_getstate(sp);
310 271572
                if (st == H1NEWREQ) {
311 117469
                        CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC);
312 117469
                        assert(isnan(req->t_prev));
313 117469
                        assert(isnan(req->t_req));
314 117469
                        AZ(req->vcl);
315 117469
                        AZ(req->esi_level);
316 117469
                        AN(WS_Reservation(req->htc->ws));
317
318 234938
                        hs = HTC_RxStuff(req->htc, HTTP1_Complete,
319 117469
                            &req->t_first, &req->t_req,
320 117469
                            sp->t_idle + SESS_TMO(sp, timeout_linger),
321 117469
                            sp->t_idle + SESS_TMO(sp, timeout_idle),
322
                            NAN,
323 117469
                            cache_param->http_req_size);
324 117469
                        assert(!WS_IsReserved(req->htc->ws));
325 117469
                        if (hs < HTC_S_EMPTY) {
326 32601
                                if (hs == HTC_S_OVERFLOW && cache_param->http_req_overflow_status != 0) {
327 42
                                        (void)req->transport->minimal_response(req,
328 21
                                            cache_param->http_req_overflow_status);
329 21
                                }
330 32601
                                req->acct.req_hdrbytes +=
331 32601
                                    req->htc->rxbuf_e - req->htc->rxbuf_b;
332 32601
                                Req_AcctLogCharge(wrk->stats, req);
333 32601
                                Req_Release(req);
334 32601
                                SES_DeleteHS(sp, hs, NAN);
335 32601
                                return;
336
                        }
337 84868
                        if (hs == HTC_S_EMPTY) {
338 5197
                                wrk->stats->sess_herd++;
339 5197
                                Req_Release(req);
340 5197
                                SES_Wait(sp, &HTTP1_transport);
341 5197
                                return;
342
                        }
343 79671
                        if (hs != HTC_S_COMPLETE)
344 0
                                WRONG("htc_status (nonbad)");
345
346 79671
                        if (H2_prism_complete(req->htc) == HTC_S_COMPLETE) {
347 3150
                                if (!FEATURE(FEATURE_HTTP2)) {
348 42
                                        SES_Close(req->sp, SC_REQ_HTTP20);
349 42
                                        assert(!WS_IsReserved(req->ws));
350 42
                                        assert(!WS_IsReserved(wrk->aws));
351 42
                                        http1_setstate(sp, H1CLEANUP);
352 42
                                        continue;
353
                                }
354 3108
                                http1_setstate(sp, NULL);
355 3108
                                H2_PU_Sess(wrk, sp, req);
356 3108
                                return;
357
                        }
358
359 76521
                        i = http1_dissect(wrk, req);
360 76521
                        req->acct.req_hdrbytes +=
361 76521
                            req->htc->rxbuf_e - req->htc->rxbuf_b;
362 76521
                        req->htc->rxbuf_b = NULL;
363 76521
                        req->htc->rxbuf_e = NULL;
364 76521
                        if (i) {
365 966
                                assert(req->doclose != SC_NULL);
366 966
                                SES_Close(req->sp, req->doclose);
367 966
                                assert(!WS_IsReserved(req->ws));
368 966
                                assert(!WS_IsReserved(wrk->aws));
369 966
                                http1_setstate(sp, H1CLEANUP);
370 966
                                continue;
371
                        }
372 75555
                        if (http_HdrIs(req->http, H_Upgrade, "h2c")) {
373 189
                                if (!FEATURE(FEATURE_HTTP2)) {
374 21
                                        VSLb(req->vsl, SLT_Debug,
375
                                            "H2 upgrade attempt");
376 189
                                } else if (req->htc->body_status != BS_NONE) {
377 21
                                        VSLb(req->vsl, SLT_Debug,
378
                                            "H2 upgrade attempt has body");
379 21
                                } else {
380 147
                                        http1_setstate(sp, NULL);
381 147
                                        req->err_code = 2;
382 147
                                        H2_OU_Sess(wrk, sp, req);
383 147
                                        return;
384
                                }
385 42
                        }
386 75408
                        assert(req->req_step == R_STP_TRANSPORT);
387 75408
                        VCL_TaskEnter(req->privs);
388 75408
                        VCL_TaskEnter(req->top->privs);
389 75408
                        http1_setstate(sp, H1PROC);
390 229511
                } else if (st == H1PROC) {
391 77806
                        req->task->func = http1_req;
392 77806
                        req->task->priv = req;
393 77806
                        CNT_Embark(wrk, req);
394 77806
                        if (CNT_Request(req) == REQ_FSM_DISEMBARK)
395 2523
                                return;
396 75283
                        wrk->stats->client_req++;
397 75283
                        AZ(req->top->vcl0);
398 75283
                        req->task->func = NULL;
399 75283
                        req->task->priv = NULL;
400 75283
                        assert(!WS_IsReserved(req->ws));
401 75283
                        assert(!WS_IsReserved(wrk->aws));
402 75283
                        http1_setstate(sp, H1CLEANUP);
403 151580
                } else if (st == H1CLEANUP) {
404
405 76297
                        assert(!WS_IsReserved(wrk->aws));
406 76297
                        assert(!WS_IsReserved(req->ws));
407
408 76297
                        if (sp->fd >= 0 && req->doclose != SC_NULL)
409 9270
                                SES_Close(sp, req->doclose);
410
411 76297
                        if (sp->fd < 0) {
412 11412
                                wrk->stats->sess_closed++;
413 11412
                                Req_Cleanup(sp, wrk, req);
414 11412
                                Req_Release(req);
415 11412
                                SES_Delete(sp, SC_NULL, NAN);
416 11412
                                return;
417
                        }
418
419 64885
                        Req_Cleanup(sp, wrk, req);
420 64885
                        HTC_RxInit(req->htc, req->ws);
421 64885
                        if (req->htc->rxbuf_e != req->htc->rxbuf_b)
422 301
                                wrk->stats->sess_readahead++;
423 64885
                        if (FEATURE(FEATURE_BUSY_STATS_RATE))
424 0
                                WRK_AddStat(wrk);
425 64885
                        http1_setstate(sp, H1NEWREQ);
426 64885
                } else {
427 0
                        WRONG("Wrong H1 session state");
428
                }
429
        }
430 54988
}