varnish-cache/bin/varnishd/http1/cache_http1_fsm.c
1
/*-
2
 * Copyright (c) 2006 Verdens Gang AS
3
 * Copyright (c) 2006-2015 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
 * This file contains the two central state machine for pushing HTTP1
30
 * sessions through their states.
31
 *
32
 */
33
34
#include "config.h"
35
36
#include "cache/cache_varnishd.h"
37
38
#include <errno.h>
39
#include <stdio.h>
40
#include <stdlib.h>
41
42
#include "cache/cache_objhead.h"
43
#include "cache/cache_transport.h"
44
#include "cache_http1.h"
45
46
#include "vtcp.h"
47
48
static const char H1NEWREQ[] = "HTTP1::NewReq";
49
static const char H1PROC[] = "HTTP1::Proc";
50
static const char H1BUSY[] = "HTTP1::Busy";
51
static const char H1CLEANUP[] = "HTTP1::Cleanup";
52
53
static void HTTP1_Session(struct worker *, struct req *);
54
55
static void
56 12107
http1_setstate(const struct sess *sp, const char *s)
57
{
58
        uintptr_t p;
59
60 12107
        p = (uintptr_t)s;
61 12107
        AZ(SES_Set_xport_priv(sp, &p));
62 12110
}
63
64
static const char *
65 14371
http1_getstate(const struct sess *sp)
66
{
67
        uintptr_t *p;
68
69 14371
        AZ(SES_Get_xport_priv(sp, &p));
70 14370
        return (const char *)*p;
71
}
72
73
/*--------------------------------------------------------------------
74
 * Call protocol for this request
75
 */
76
77
static void v_matchproto_(task_func_t)
78 2369
http1_req(struct worker *wrk, void *arg)
79
{
80
        struct req *req;
81
82 2369
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
83 2369
        CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC);
84
85 2369
        THR_SetRequest(req);
86 2369
        req->transport = &HTTP1_transport;
87 2369
        AZ(wrk->aws->r);
88 2369
        HTTP1_Session(wrk, req);
89 2363
        AZ(wrk->v1l);
90 2363
        WS_Assert(wrk->aws);
91 2363
        THR_SetRequest(NULL);
92 2363
}
93
94
/*--------------------------------------------------------------------
95
 * Call protocol for this session (new or from waiter)
96
 *
97
 * When sessions are rescheduled from the waiter, a struct pool_task
98
 * is put on the reserved session workspace (for reasons of memory
99
 * conservation).  This reservation is released as the first thing.
100
 * The acceptor and any other code which schedules this function
101
 * must obey this calling convention with a dummy reservation.
102
 */
103
104
static void v_matchproto_(task_func_t)
105 2146
http1_new_session(struct worker *wrk, void *arg)
106
{
107
        struct sess *sp;
108
        struct req *req;
109
        uintptr_t *u;
110
111 2146
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
112 2146
        CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC);
113 2146
        sp = req->sp;
114 2146
        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
115
116 2146
        HTC_RxInit(req->htc, req->ws);
117 2146
        SES_Reserve_xport_priv(sp, &u);
118 2145
        http1_setstate(sp, H1NEWREQ);
119 2146
        wrk->task.func = http1_req;
120 2146
        wrk->task.priv = req;
121 2146
}
122
123
static void v_matchproto_(task_func_t)
124 188
http1_unwait(struct worker *wrk, void *arg)
125
{
126
        struct sess *sp;
127
        struct req *req;
128
129 188
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
130 188
        CAST_OBJ_NOTNULL(sp, arg, SESS_MAGIC);
131 188
        WS_Release(sp->ws, 0);
132 188
        req = Req_New(wrk, sp);
133 188
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
134 188
        req->htc->rfd = &sp->fd;
135 188
        HTC_RxInit(req->htc, req->ws);
136 188
        http1_setstate(sp, H1NEWREQ);
137 188
        wrk->task.func = http1_req;
138 188
        wrk->task.priv = req;
139 188
}
140
141
static void v_matchproto_(vtr_req_body_t)
142 86
http1_req_body(struct req *req)
143
{
144
145 86
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
146 86
        switch (req->htc->body_status) {
147
        case BS_EOF:
148
        case BS_LENGTH:
149
        case BS_CHUNKED:
150 86
                if (V1F_Setup_Fetch(req->vfc, req->htc) != 0)
151 0
                        req->req_body_status = REQ_BODY_FAIL;
152 86
                break;
153
        default:
154 0
                break;
155
        }
156 86
}
157
158
static void
159 4
http1_sess_panic(struct vsb *vsb, const struct sess *sp)
160
{
161
162 4
        VSB_printf(vsb, "state = %s\n", http1_getstate(sp));
163 4
}
164
165
static void
166 4
http1_req_panic(struct vsb *vsb, const struct req *req)
167
{
168
169 4
        VSB_printf(vsb, "state = %s\n", http1_getstate(req->sp));
170 4
}
171
172
static void v_matchproto_(vtr_req_fail_f)
173 24
http1_req_fail(struct req *req, enum sess_close reason)
174
{
175 24
        assert(reason > 0);
176 24
        assert(req->sp->fd != 0);
177 24
        if (req->sp->fd > 0)
178 22
                SES_Close(req->sp, reason);
179 24
}
180
181
static void v_matchproto_(vtr_reembark_f)
182 35
http1_reembark(struct worker *wrk, struct req *req)
183
{
184
        struct sess *sp;
185
186 35
        sp = req->sp;
187 35
        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
188
189 35
        http1_setstate(sp, H1BUSY);
190
191 35
        if (!SES_Reschedule_Req(req, TASK_QUEUE_REQ))
192 70
                return;
193
194
        /* Couldn't schedule, ditch */
195 0
        wrk->stats->busy_wakeup--;
196 0
        wrk->stats->busy_killed++;
197 0
        AN (req->vcl);
198 0
        VCL_Rel(&req->vcl);
199 0
        Req_AcctLogCharge(wrk->stats, req);
200 0
        Req_Release(req);
201 0
        SES_Delete(sp, SC_OVERLOAD, NAN);
202 0
        DSL(DBG_WAITINGLIST, req->vsl->wid, "kill from waiting list");
203 0
        usleep(10000);
204
}
205
206
static int v_matchproto_(vtr_minimal_response_f)
207 66
http1_minimal_response(struct req *req, uint16_t status)
208
{
209
        size_t wl, l;
210
        char buf[80];
211
        const char *reason;
212
213 66
        assert(status >= 100);
214 66
        assert(status < 1000);
215
216 66
        reason = http_Status2Reason(status, NULL);
217
218 66
        l = snprintf(buf, sizeof(buf),
219
            "HTTP/1.1 %03d %s\r\n\r\n", status, reason);
220 66
        assert (l < sizeof(buf));
221
222 66
        VSLb(req->vsl, SLT_RespProtocol, "HTTP/1.1");
223 66
        VSLb(req->vsl, SLT_RespStatus, "%03d", status);
224 66
        VSLb(req->vsl, SLT_RespReason, "%s", reason);
225
226 66
        if (status >= 400)
227 56
                req->err_code = status;
228 66
        wl = write(req->sp->fd, buf, l);
229
230 66
        if (wl > 0)
231 66
                req->acct.resp_hdrbytes += wl;
232 66
        if (wl != l) {
233 0
                VTCP_Assert(1);
234 0
                if (!req->doclose)
235 0
                        req->doclose = SC_REM_CLOSE;
236 0
                return (-1);
237
        }
238 66
        return (0);
239
}
240
241
struct transport HTTP1_transport = {
242
        .name =                 "HTTP/1",
243
        .proto_ident =          "HTTP",
244
        .magic =                TRANSPORT_MAGIC,
245
        .deliver =              V1D_Deliver,
246
        .minimal_response =     http1_minimal_response,
247
        .new_session =          http1_new_session,
248
        .reembark =             http1_reembark,
249
        .req_body =             http1_req_body,
250
        .req_fail =             http1_req_fail,
251
        .req_panic =            http1_req_panic,
252
        .sess_panic =           http1_sess_panic,
253
        .unwait =               http1_unwait,
254
};
255
256
/*----------------------------------------------------------------------
257
 */
258
259
static inline void
260 50
http1_abort(struct req *req, uint16_t status)
261
{
262 50
        AN(req->doclose);
263 50
        assert(status >= 400);
264 50
        (void)http1_minimal_response(req, status);
265 50
}
266
267
static int
268 3336
http1_dissect(struct worker *wrk, struct req *req)
269
{
270
271 3336
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
272 3336
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
273 3336
        CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC);
274
275
        /* Allocate a new vxid now that we know we'll need it. */
276 3336
        AZ(req->vsl->wid);
277 3336
        req->vsl->wid = VXID_Get(wrk, VSL_CLIENTMARKER);
278
279 3338
        VSLb(req->vsl, SLT_Begin, "req %u rxreq", VXID(req->sp->vxid));
280 3338
        VSL(SLT_Link, req->sp->vxid, "req %u rxreq", VXID(req->vsl->wid));
281 3338
        AZ(isnan(req->t_first)); /* First byte timestamp set by http1_wait */
282 3338
        AZ(isnan(req->t_req));   /* Complete req rcvd set by http1_wait */
283 3338
        req->t_prev = req->t_first;
284 3338
        VSLb_ts_req(req, "Start", req->t_first);
285 3338
        VSLb_ts_req(req, "Req", req->t_req);
286
287
        /* Borrow VCL reference from worker thread */
288 3338
        VCL_Refresh(&wrk->vcl);
289 3338
        req->vcl = wrk->vcl;
290 3338
        wrk->vcl = NULL;
291
292 3338
        HTTP_Setup(req->http, req->ws, req->vsl, SLT_ReqMethod);
293 3338
        req->err_code = HTTP1_DissectRequest(req->htc, req->http);
294
295
        /* If we could not even parse the request, just close */
296 3336
        if (req->err_code != 0) {
297 100
                VSLb(req->vsl, SLT_HttpGarbage, "%.*s",
298 50
                    (int)(req->htc->rxbuf_e - req->htc->rxbuf_b),
299
                    req->htc->rxbuf_b);
300 50
                wrk->stats->client_req_400++;
301 50
                req->doclose = SC_RX_JUNK;
302 50
                http1_abort(req, 400);
303 50
                return (-1);
304
        }
305
306 3286
        assert (req->req_body_status == REQ_BODY_INIT);
307
308 3286
        switch (req->htc->body_status) {
309
        case BS_CHUNKED:
310 13
                req->req_body_status = REQ_BODY_WITHOUT_LEN;
311 13
                break;
312
        case BS_LENGTH:
313 76
                req->req_body_status = REQ_BODY_WITH_LEN;
314 76
                break;
315
        case BS_NONE:
316 3197
                req->req_body_status = REQ_BODY_NONE;
317 3197
                break;
318
        case BS_EOF:
319 0
                req->req_body_status = REQ_BODY_WITHOUT_LEN;
320 0
                break;
321
        default:
322 0
                WRONG("Unknown req_body_status situation");
323
        }
324 3286
        return (0);
325
}
326
327
/*----------------------------------------------------------------------
328
 */
329
330
static int
331 3324
http1_req_cleanup(struct sess *sp, struct worker *wrk, struct req *req)
332
{
333 3324
        AZ(wrk->aws->r);
334 3324
        AZ(req->ws->r);
335 3324
        Req_Cleanup(sp, wrk, req);
336
337 3324
        if (sp->fd >= 0 && req->doclose != SC_NULL)
338 240
                SES_Close(sp, req->doclose);
339
340 3324
        if (sp->fd < 0) {
341 334
                wrk->stats->sess_closed++;
342 334
                AZ(req->vcl);
343 334
                Req_Release(req);
344 334
                SES_Delete(sp, SC_NULL, NAN);
345 334
                return (1);
346
        }
347
348 2990
        return (0);
349
}
350
351
/*----------------------------------------------------------------------
352
 */
353
354
static void
355 2358
HTTP1_Session(struct worker *wrk, struct req *req)
356
{
357
        enum htc_status_e hs;
358
        struct sess *sp;
359
        const char *st;
360
        int i;
361
362 2358
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
363 2358
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
364 2358
        sp = req->sp;
365 2358
        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
366
367
        /*
368
         * Whenever we come in from the acceptor or waiter, we need to set
369
         * blocking mode.  It would be simpler to do this in the acceptor
370
         * or waiter, but we'd rather do the syscall in the worker thread.
371
         * On systems which return errors for ioctl, we close early
372
         */
373 2358
        if (http1_getstate(sp) == H1NEWREQ && VTCP_blocking(sp->fd)) {
374 0
                AN(req->htc->ws->r);
375 0
                if (errno == ECONNRESET)
376 0
                        SES_Close(sp, SC_REM_CLOSE);
377
                else
378 0
                        SES_Close(sp, SC_TX_ERROR);
379 0
                WS_Release(req->htc->ws, 0);
380 0
                AN(http1_req_cleanup(sp, wrk, req));
381 0
                return;
382
        }
383
384 2369
        req->transport = &HTTP1_transport;
385
386
        while (1) {
387 11994
                st = http1_getstate(sp);
388 11992
                if (st == H1NEWREQ) {
389 5324
                        CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC);
390 5324
                        assert(isnan(req->t_prev));
391 5323
                        assert(isnan(req->t_req));
392 5323
                        AZ(req->vcl);
393 5323
                        AZ(req->esi_level);
394 5323
                        AN(req->htc->ws->r);
395
396 15969
                        hs = HTC_RxStuff(req->htc, HTTP1_Complete,
397
                            &req->t_first, &req->t_req,
398 5323
                            sp->t_idle + cache_param->timeout_linger,
399 5323
                            sp->t_idle + cache_param->timeout_idle,
400 5323
                            cache_param->http_req_size);
401 5324
                        AZ(req->htc->ws->r);
402 5324
                        if (hs < HTC_S_EMPTY) {
403 3320
                                req->acct.req_hdrbytes +=
404 1660
                                    req->htc->rxbuf_e - req->htc->rxbuf_b;
405 1660
                                Req_AcctLogCharge(wrk->stats, req);
406 1660
                                Req_Release(req);
407 1660
                                switch (hs) {
408
                                case HTC_S_CLOSE:
409 0
                                        SES_Delete(sp, SC_REM_CLOSE, NAN);
410 0
                                        return;
411
                                case HTC_S_TIMEOUT:
412 0
                                        SES_Delete(sp, SC_RX_TIMEOUT, NAN);
413 0
                                        return;
414
                                case HTC_S_OVERFLOW:
415 4
                                        SES_Delete(sp, SC_RX_OVERFLOW, NAN);
416 4
                                        return;
417
                                case HTC_S_EOF:
418 1656
                                        SES_Delete(sp, SC_REM_CLOSE, NAN);
419 1656
                                        return;
420
                                default:
421 0
                                        WRONG("htc_status (bad)");
422
                                }
423
                        }
424 3664
                        if (hs == HTC_S_IDLE) {
425 220
                                wrk->stats->sess_herd++;
426 220
                                Req_Release(req);
427 220
                                SES_Wait(sp, &HTTP1_transport);
428 220
                                return;
429
                        }
430 3444
                        if (hs != HTC_S_COMPLETE)
431 0
                                WRONG("htc_status (nonbad)");
432
433 3444
                        if (H2_prism_complete(req->htc) == HTC_S_COMPLETE) {
434 106
                                if (!FEATURE(FEATURE_HTTP2)) {
435 2
                                        SES_Close(req->sp, SC_REQ_HTTP20);
436 2
                                        AZ(req->ws->r);
437 2
                                        AZ(wrk->aws->r);
438 2
                                        http1_setstate(sp, H1CLEANUP);
439 2
                                        continue;
440
                                }
441 104
                                http1_setstate(sp, NULL);
442 104
                                H2_PU_Sess(wrk, sp, req);
443 104
                                return;
444
                        }
445
446 3338
                        i = http1_dissect(wrk, req);
447 6672
                        req->acct.req_hdrbytes +=
448 3336
                            req->htc->rxbuf_e - req->htc->rxbuf_b;
449 3336
                        if (i) {
450 50
                                assert(req->doclose > 0);
451 50
                                SES_Close(req->sp, req->doclose);
452 50
                                AZ(req->ws->r);
453 50
                                AZ(wrk->aws->r);
454 50
                                http1_setstate(sp, H1CLEANUP);
455 50
                                continue;
456
                        }
457 3286
                        if (http_HdrIs(req->http, H_Upgrade, "h2c")) {
458 14
                                if (!FEATURE(FEATURE_HTTP2)) {
459 2
                                        VSLb(req->vsl, SLT_Debug,
460
                                            "H2 upgrade attempt");
461 12
                                } else if (req->htc->body_status != BS_NONE) {
462 2
                                        VSLb(req->vsl, SLT_Debug,
463
                                            "H2 upgrade attempt has body");
464
                                } else {
465 10
                                        http1_setstate(sp, NULL);
466 10
                                        req->err_code = 2;
467 10
                                        H2_OU_Sess(wrk, sp, req);
468 10
                                        return;
469
                                }
470
                        }
471 3276
                        req->req_step = R_STP_TRANSPORT;
472 3276
                        http1_setstate(sp, H1PROC);
473 6668
                } else if (st == H1BUSY) {
474 35
                        CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC);
475
                        /*
476
                         * Return from waitinglist.
477
                         * Check to see if the remote has left.
478
                         */
479 35
                        if (VTCP_check_hup(sp->fd)) {
480 0
                                AN(req->ws->r);
481 0
                                WS_Release(req->ws, 0);
482 0
                                AN(req->hash_objhead);
483 0
                                (void)HSH_DerefObjHead(wrk, &req->hash_objhead);
484 0
                                AZ(req->hash_objhead);
485 0
                                SES_Close(sp, SC_REM_CLOSE);
486 0
                                AN(http1_req_cleanup(sp, wrk, req));
487 0
                                return;
488
                        }
489 35
                        http1_setstate(sp, H1PROC);
490 6633
                } else if (st == H1PROC) {
491 3310
                        req->task.func = http1_req;
492 3310
                        req->task.priv = req;
493 3310
                        if (CNT_Request(wrk, req) == REQ_FSM_DISEMBARK)
494 35
                                return;
495 3272
                        req->task.func = NULL;
496 3272
                        req->task.priv = NULL;
497 3272
                        AZ(req->ws->r);
498 3272
                        AZ(wrk->aws->r);
499 3272
                        http1_setstate(sp, H1CLEANUP);
500 3323
                } else if (st == H1CLEANUP) {
501 3323
                        if (http1_req_cleanup(sp, wrk, req))
502 334
                                return;
503 2990
                        HTC_RxInit(req->htc, req->ws);
504 2990
                        if (req->htc->rxbuf_e != req->htc->rxbuf_b)
505 16
                                wrk->stats->sess_readahead++;
506 2990
                        http1_setstate(sp, H1NEWREQ);
507
                } else {
508 0
                        WRONG("Wrong H1 session state");
509
                }
510 9625
        }
511
}