varnish-cache/bin/varnishd/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_varnishd.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 269050
http1_setstate(const struct sess *sp, const char *s)
55
{
56
        uintptr_t p;
57
58 269050
        p = (uintptr_t)s;
59 269050
        AZ(SES_Set_proto_priv(sp, &p));
60 269050
}
61
62
static const char *
63 320618
http1_getstate(const struct sess *sp)
64
{
65
        uintptr_t *p;
66
67 320618
        AZ(SES_Get_proto_priv(sp, &p));
68 320618
        return ((const char *)*p);
69
}
70
71
/*--------------------------------------------------------------------
72
 * Call protocol for this request
73
 */
74
75
static void v_matchproto_(task_func_t)
76 53826
http1_req(struct worker *wrk, void *arg)
77
{
78
        struct req *req;
79
80 53826
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
81 53826
        CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC);
82
83 53826
        THR_SetRequest(req);
84 53826
        req->transport = &HTTP1_transport;
85 53826
        assert(!WS_IsReserved(wrk->aws));
86 53826
        HTTP1_Session(wrk, req);
87 53826
        AZ(wrk->v1l);
88 53826
        WS_Assert(wrk->aws);
89 53826
        THR_SetRequest(NULL);
90 53826
}
91
92
/*--------------------------------------------------------------------
93
 * Call protocol for this session (new or from waiter)
94
 *
95
 * When sessions are rescheduled from the waiter, a struct pool_task
96
 * is put on the reserved session workspace (for reasons of memory
97
 * conservation).  This reservation is released as the first thing.
98
 * The acceptor and any other code which schedules this function
99
 * must obey this calling convention with a dummy reservation.
100
 */
101
102
static void v_matchproto_(task_func_t)
103 49762
http1_new_session(struct worker *wrk, void *arg)
104
{
105
        struct sess *sp;
106
        struct req *req;
107
        uintptr_t *u;
108
        ssize_t sz;
109
110 49762
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
111 49762
        CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC);
112 49762
        sp = req->sp;
113 49762
        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
114
115 49762
        HTC_RxInit(req->htc, req->ws);
116 49762
        if (!SES_Reserve_proto_priv(sp, &u, &sz)) {
117
                /* Out of session workspace. Free the req, close the sess,
118
                 * and do not set a new task func, which will exit the
119
                 * worker thread. */
120 25
                VSL(SLT_Error, req->sp->vxid,
121
                    "insufficient workspace (proto_priv)");
122 25
                WS_Release(req->ws, 0);
123 25
                Req_Release(req);
124 25
                SES_Delete(sp, SC_RX_JUNK, NAN);
125 25
                return;
126
        }
127 49737
        assert(sz == sizeof u);
128 49737
        http1_setstate(sp, H1NEWREQ);
129 49737
        wrk->task->func = http1_req;
130 49737
        wrk->task->priv = req;
131 49762
}
132
133
static void v_matchproto_(task_func_t)
134 3376
http1_unwait(struct worker *wrk, void *arg)
135
{
136
        struct sess *sp;
137
        struct req *req;
138
139 3376
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
140 3376
        CAST_OBJ_NOTNULL(sp, arg, SESS_MAGIC);
141 3376
        WS_Release(sp->ws, 0);
142 3376
        req = Req_New(sp);
143 3376
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
144 3376
        req->htc->rfd = &sp->fd;
145 3376
        HTC_RxInit(req->htc, req->ws);
146 3376
        http1_setstate(sp, H1NEWREQ);
147 3376
        wrk->task->func = http1_req;
148 3376
        wrk->task->priv = req;
149 3376
}
150
151
static void v_matchproto_(vtr_req_body_t)
152 1700
http1_req_body(struct req *req)
153
{
154
155 1700
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
156 1700
        if (V1F_Setup_Fetch(req->vfc, req->htc) != 0)
157 0
                req->req_body_status = BS_ERROR;
158 1700
}
159
160
static void
161 125
http1_sess_panic(struct vsb *vsb, const struct sess *sp)
162
{
163
164 125
        VSB_printf(vsb, "state = %s\n", http1_getstate(sp));
165 125
}
166
167
static void
168 100
http1_req_panic(struct vsb *vsb, const struct req *req)
169
{
170
171 100
        VSB_printf(vsb, "state = %s\n", http1_getstate(req->sp));
172 100
}
173
174
static void v_matchproto_(vtr_req_fail_f)
175 700
http1_req_fail(struct req *req, stream_close_t reason)
176
{
177 700
        assert(reason != SC_NULL);
178 700
        assert(req->sp->fd != 0);
179 700
        if (req->sp->fd > 0)
180 700
                SES_Close(req->sp, reason);
181 700
}
182
183
static int v_matchproto_(vtr_minimal_response_f)
184 1200
http1_minimal_response(struct req *req, uint16_t status)
185
{
186
        ssize_t wl, l;
187
        char buf[80];
188
        const char *reason;
189
190 1200
        assert(status >= 100);
191 1200
        assert(status < 1000);
192
193 1200
        reason = http_Status2Reason(status, NULL);
194
195 1200
        bprintf(buf, "HTTP/1.1 %03d %s\r\n\r\n", status, reason);
196 1200
        l = strlen(buf);
197
198 1200
        VSLb(req->vsl, SLT_RespProtocol, "HTTP/1.1");
199 1200
        VSLb(req->vsl, SLT_RespStatus, "%03d", status);
200 1200
        VSLbs(req->vsl, SLT_RespReason, TOSTRAND(reason));
201
202 1200
        if (status >= 400)
203 1075
                req->err_code = status;
204 1200
        wl = write(req->sp->fd, buf, l);
205
206 1200
        if (wl > 0)
207 1200
                req->acct.resp_hdrbytes += wl;
208 1200
        if (wl != l) {
209 0
                if (wl < 0)
210 0
                        VTCP_Assert(1);
211 0
                if (req->doclose == SC_NULL)
212 0
                        req->doclose = SC_REM_CLOSE;
213 0
                return (-1);
214
        }
215 1200
        return (0);
216 1200
}
217
218
struct transport HTTP1_transport = {
219
        .name =                 "HTTP/1",
220
        .proto_ident =          "HTTP",
221
        .magic =                TRANSPORT_MAGIC,
222
        .deliver =              V1D_Deliver,
223
        .minimal_response =     http1_minimal_response,
224
        .new_session =          http1_new_session,
225
        .req_body =             http1_req_body,
226
        .req_fail =             http1_req_fail,
227
        .req_panic =            http1_req_panic,
228
        .sess_panic =           http1_sess_panic,
229
        .unwait =               http1_unwait,
230
};
231
232
/*----------------------------------------------------------------------
233
 */
234
235
static inline void
236 825
http1_abort(struct req *req, uint16_t status)
237
{
238 825
        assert(req->doclose != SC_NULL);
239 825
        assert(status >= 400);
240 825
        (void)http1_minimal_response(req, status);
241 825
}
242
243
static int
244 75055
http1_dissect(struct worker *wrk, struct req *req)
245
{
246
247 75055
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
248 75055
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
249 75055
        CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC);
250
251
        /* Allocate a new vxid now that we know we'll need it. */
252 75055
        assert(IS_NO_VXID(req->vsl->wid));
253 75055
        req->vsl->wid = VXID_Get(wrk, VSL_CLIENTMARKER);
254
255 75055
        VSLb(req->vsl, SLT_Begin, "req %ju rxreq", VXID(req->sp->vxid));
256 75055
        VSL(SLT_Link, req->sp->vxid, "req %ju rxreq", VXID(req->vsl->wid));
257 75055
        AZ(isnan(req->t_first)); /* First byte timestamp set by http1_wait */
258 75055
        AZ(isnan(req->t_req));   /* Complete req rcvd set by http1_wait */
259 75055
        req->t_prev = req->t_first;
260 75055
        VSLb_ts_req(req, "Start", req->t_first);
261 75055
        VSLb_ts_req(req, "Req", req->t_req);
262
263 75055
        HTTP_Setup(req->http, req->ws, req->vsl, SLT_ReqMethod);
264 75055
        req->err_code = HTTP1_DissectRequest(req->htc, req->http);
265
266
        /* If we could not even parse the request, just close */
267 75055
        if (req->err_code != 0) {
268 1650
                VSLb(req->vsl, SLT_HttpGarbage, "%.*s",
269 825
                    (int)(req->htc->rxbuf_e - req->htc->rxbuf_b),
270 825
                    req->htc->rxbuf_b);
271 825
                wrk->stats->client_req_400++;
272
273 825
                (void)Req_LogStart(wrk, req);
274
275 825
                req->doclose = SC_RX_JUNK;
276 825
                http1_abort(req, 400);
277 825
                return (-1);
278
        }
279
280 74230
        AZ(req->req_body_status);
281 74230
        req->req_body_status = req->htc->body_status;
282 74230
        return (0);
283 75055
}
284
285
/*----------------------------------------------------------------------
286
 */
287
288
static void
289 53814
HTTP1_Session(struct worker *wrk, struct req *req)
290
{
291
        enum htc_status_e hs;
292
        struct sess *sp;
293
        const char *st;
294
        int i;
295
296 53814
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
297 53814
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
298 53814
        sp = req->sp;
299 53814
        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
300
301
        /*
302
         * Whenever we come in from the acceptor or waiter, we need to set
303
         * blocking mode.  It would be simpler to do this in the acceptor
304
         * or waiter, but we'd rather do the syscall in the worker thread.
305
         */
306 53814
        if (http1_getstate(sp) == H1NEWREQ)
307 53115
                VTCP_blocking(sp->fd);
308
309 53814
        req->transport = &HTTP1_transport;
310
311 265460
        while (1) {
312 266335
                st = http1_getstate(sp);
313 266335
                if (st == H1NEWREQ) {
314 116697
                        CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC);
315 116697
                        assert(isnan(req->t_prev));
316 116697
                        assert(isnan(req->t_req));
317 116697
                        AZ(req->vcl);
318 116697
                        AZ(req->esi_level);
319 116697
                        AN(WS_Reservation(req->htc->ws));
320
321 233394
                        hs = HTC_RxStuff(req->htc, HTTP1_Complete,
322 116697
                            &req->t_first, &req->t_req,
323 116697
                            sp->t_idle + SESS_TMO(sp, timeout_linger),
324 116697
                            sp->t_idle + SESS_TMO(sp, timeout_idle),
325
                            NAN,
326 116697
                            cache_param->http_req_size);
327 116697
                        assert(!WS_IsReserved(req->htc->ws));
328 116697
                        if (hs < HTC_S_EMPTY) {
329 34065
                                req->acct.req_hdrbytes +=
330 34065
                                    req->htc->rxbuf_e - req->htc->rxbuf_b;
331 34065
                                Req_AcctLogCharge(wrk->stats, req);
332 34065
                                Req_Release(req);
333 34065
                                SES_DeleteHS(sp, hs, NAN);
334 34065
                                return;
335
                        }
336 82632
                        if (hs == HTC_S_IDLE) {
337 4247
                                wrk->stats->sess_herd++;
338 4247
                                Req_Release(req);
339 4247
                                SES_Wait(sp, &HTTP1_transport);
340 4247
                                return;
341
                        }
342 78385
                        if (hs != HTC_S_COMPLETE)
343 0
                                WRONG("htc_status (nonbad)");
344
345 78385
                        if (H2_prism_complete(req->htc) == HTC_S_COMPLETE) {
346 3325
                                if (!FEATURE(FEATURE_HTTP2)) {
347 50
                                        SES_Close(req->sp, SC_REQ_HTTP20);
348 50
                                        assert(!WS_IsReserved(req->ws));
349 50
                                        assert(!WS_IsReserved(wrk->aws));
350 50
                                        http1_setstate(sp, H1CLEANUP);
351 50
                                        continue;
352
                                }
353 3275
                                http1_setstate(sp, NULL);
354 3275
                                H2_PU_Sess(wrk, sp, req);
355 3275
                                return;
356
                        }
357
358 75060
                        i = http1_dissect(wrk, req);
359 75060
                        req->acct.req_hdrbytes +=
360 75060
                            req->htc->rxbuf_e - req->htc->rxbuf_b;
361 75060
                        if (i) {
362 825
                                assert(req->doclose != SC_NULL);
363 825
                                SES_Close(req->sp, req->doclose);
364 825
                                assert(!WS_IsReserved(req->ws));
365 825
                                assert(!WS_IsReserved(wrk->aws));
366 825
                                http1_setstate(sp, H1CLEANUP);
367 825
                                continue;
368
                        }
369 74235
                        if (http_HdrIs(req->http, H_Upgrade, "h2c")) {
370 200
                                if (!FEATURE(FEATURE_HTTP2)) {
371 25
                                        VSLb(req->vsl, SLT_Debug,
372
                                            "H2 upgrade attempt");
373 200
                                } else if (req->htc->body_status != BS_NONE) {
374 25
                                        VSLb(req->vsl, SLT_Debug,
375
                                            "H2 upgrade attempt has body");
376 25
                                } else {
377 150
                                        http1_setstate(sp, NULL);
378 150
                                        req->err_code = 2;
379 150
                                        H2_OU_Sess(wrk, sp, req);
380 150
                                        return;
381
                                }
382 50
                        }
383 74085
                        assert(req->req_step == R_STP_TRANSPORT);
384 74085
                        VCL_TaskEnter(req->privs);
385 74085
                        VCL_TaskEnter(req->top->privs);
386 74085
                        http1_setstate(sp, H1PROC);
387 223723
                } else if (st == H1PROC) {
388 74798
                        req->task->func = http1_req;
389 74798
                        req->task->priv = req;
390 74798
                        CNT_Embark(wrk, req);
391 74798
                        if (CNT_Request(req) == REQ_FSM_DISEMBARK)
392 837
                                return;
393 73961
                        wrk->stats->client_req++;
394 73961
                        AZ(req->top->vcl0);
395 73961
                        req->task->func = NULL;
396 73961
                        req->task->priv = NULL;
397 73961
                        assert(!WS_IsReserved(req->ws));
398 73961
                        assert(!WS_IsReserved(wrk->aws));
399 73961
                        http1_setstate(sp, H1CLEANUP);
400 148801
                } else if (st == H1CLEANUP) {
401
402 74840
                        assert(!WS_IsReserved(wrk->aws));
403 74840
                        assert(!WS_IsReserved(req->ws));
404
405 74840
                        if (sp->fd >= 0 && req->doclose != SC_NULL)
406 9040
                                SES_Close(sp, req->doclose);
407
408 74840
                        if (sp->fd < 0) {
409 11240
                                wrk->stats->sess_closed++;
410 11240
                                Req_Cleanup(sp, wrk, req);
411 11240
                                Req_Release(req);
412 11240
                                SES_Delete(sp, SC_NULL, NAN);
413 11240
                                return;
414
                        }
415
416 63600
                        Req_Cleanup(sp, wrk, req);
417 63600
                        HTC_RxInit(req->htc, req->ws);
418 63600
                        if (req->htc->rxbuf_e != req->htc->rxbuf_b)
419 360
                                wrk->stats->sess_readahead++;
420 63600
                        if (FEATURE(FEATURE_BUSY_STATS_RATE))
421 0
                                WRK_AddStat(wrk);
422 63600
                        http1_setstate(sp, H1NEWREQ);
423 63600
                } else {
424 0
                        WRONG("Wrong H1 session state");
425
                }
426
        }
427 53814
}