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 23452
http1_setstate(const struct sess *sp, const char *s)
57
{
58
        uintptr_t p;
59
60 23452
        p = (uintptr_t)s;
61 23452
        AZ(SES_Set_proto_priv(sp, &p));
62 23452
}
63
64
static const char *
65 27965
http1_getstate(const struct sess *sp)
66
{
67
        uintptr_t *p;
68
69 27965
        AZ(SES_Get_proto_priv(sp, &p));
70 27963
        return (const char *)*p;
71
}
72
73
/*--------------------------------------------------------------------
74
 * Call protocol for this request
75
 */
76
77
static void v_matchproto_(task_func_t)
78 4709
http1_req(struct worker *wrk, void *arg)
79
{
80
        struct req *req;
81
82 4709
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
83 4709
        CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC);
84
85 4709
        THR_SetRequest(req);
86 4708
        req->transport = &HTTP1_transport;
87 4708
        AZ(wrk->aws->r);
88 4708
        HTTP1_Session(wrk, req);
89 4697
        AZ(wrk->v1l);
90 4697
        WS_Assert(wrk->aws);
91 4697
        THR_SetRequest(NULL);
92 4697
}
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 4307
http1_new_session(struct worker *wrk, void *arg)
106
{
107
        struct sess *sp;
108
        struct req *req;
109
        uintptr_t *u;
110
111 4307
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
112 4307
        CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC);
113 4307
        sp = req->sp;
114 4307
        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
115
116 4307
        HTC_RxInit(req->htc, req->ws);
117 4317
        SES_Reserve_proto_priv(sp, &u);
118 4317
        http1_setstate(sp, H1NEWREQ);
119 4317
        wrk->task.func = http1_req;
120 4317
        wrk->task.priv = req;
121 4317
}
122
123
static void v_matchproto_(task_func_t)
124 327
http1_unwait(struct worker *wrk, void *arg)
125
{
126
        struct sess *sp;
127
        struct req *req;
128
129 327
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
130 327
        CAST_OBJ_NOTNULL(sp, arg, SESS_MAGIC);
131 327
        WS_Release(sp->ws, 0);
132 327
        req = Req_New(wrk, sp);
133 327
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
134 327
        req->htc->rfd = &sp->fd;
135 327
        HTC_RxInit(req->htc, req->ws);
136 327
        http1_setstate(sp, H1NEWREQ);
137 327
        wrk->task.func = http1_req;
138 327
        wrk->task.priv = req;
139 327
}
140
141
static void v_matchproto_(vtr_req_body_t)
142 129
http1_req_body(struct req *req)
143
{
144
145 129
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
146 129
        switch (req->htc->body_status) {
147
        case BS_EOF:
148
        case BS_LENGTH:
149
        case BS_CHUNKED:
150 129
                if (V1F_Setup_Fetch(req->vfc, req->htc) != 0)
151 0
                        req->req_body_status = REQ_BODY_FAIL;
152 129
                break;
153
        default:
154 0
                break;
155
        }
156 129
}
157
158
static void
159 9
http1_sess_panic(struct vsb *vsb, const struct sess *sp)
160
{
161
162 9
        VSB_printf(vsb, "state = %s\n", http1_getstate(sp));
163 9
}
164
165
static void
166 9
http1_req_panic(struct vsb *vsb, const struct req *req)
167
{
168
169 9
        VSB_printf(vsb, "state = %s\n", http1_getstate(req->sp));
170 9
}
171
172
static void v_matchproto_(vtr_req_fail_f)
173 39
http1_req_fail(struct req *req, enum sess_close reason)
174
{
175 39
        assert(reason > 0);
176 39
        assert(req->sp->fd != 0);
177 39
        if (req->sp->fd > 0)
178 36
                SES_Close(req->sp, reason);
179 39
}
180
181
/*----------------------------------------------------------------------
182
 */
183
184
static int
185 6411
http1_req_cleanup(struct sess *sp, struct worker *wrk, struct req *req)
186
{
187 6411
        AZ(wrk->aws->r);
188 6411
        AZ(req->ws->r);
189 6411
        Req_Cleanup(sp, wrk, req);
190
191 6411
        if (sp->fd >= 0 && req->doclose != SC_NULL)
192 522
                SES_Close(sp, req->doclose);
193
194 6411
        if (sp->fd < 0) {
195 687
                wrk->stats->sess_closed++;
196 687
                AZ(req->vcl);
197 687
                Req_Release(req);
198 687
                SES_Delete(sp, SC_NULL, NAN);
199 687
                return (1);
200
        }
201
202 5724
        return (0);
203
}
204
205
/*----------------------------------------------------------------------
206
 * Clean up a req from waiting list which cannot complete
207
 */
208
209
static void
210 3
http1_cleanup_waiting(struct worker *wrk, struct req *req,
211
    enum sess_close reason)
212
{
213
        struct sess *sp;
214
215 3
        sp = req->sp;
216 3
        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
217 3
        AN(req->ws->r);
218 3
        WS_Release(req->ws, 0);
219 3
        AN(req->hash_objhead);
220 3
        (void)HSH_DerefObjHead(wrk, &req->hash_objhead);
221 3
        AZ(req->hash_objhead);
222 3
        SES_Close(sp, reason);
223 3
        AN(http1_req_cleanup(sp, wrk, req));
224 3
}
225
226
static void v_matchproto_(vtr_reembark_f)
227 68
http1_reembark(struct worker *wrk, struct req *req)
228
{
229
        struct sess *sp;
230
231 68
        sp = req->sp;
232 68
        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
233
234 68
        http1_setstate(sp, H1BUSY);
235
236 133
        if (!DO_DEBUG(DBG_FAILRESCHED) &&
237 65
            !SES_Reschedule_Req(req, TASK_QUEUE_REQ))
238 65
                return;
239
240
        /* Couldn't schedule, ditch */
241 3
        wrk->stats->busy_wakeup--;
242 3
        wrk->stats->busy_killed++;
243 3
        VSLb(req->vsl, SLT_Error, "Fail to reschedule req from waiting list");
244 3
        http1_cleanup_waiting(wrk, req, SC_OVERLOAD);
245
}
246
247
static int v_matchproto_(vtr_minimal_response_f)
248 108
http1_minimal_response(struct req *req, uint16_t status)
249
{
250
        ssize_t wl, l;
251
        char buf[80];
252
        const char *reason;
253
254 108
        assert(status >= 100);
255 108
        assert(status < 1000);
256
257 108
        reason = http_Status2Reason(status, NULL);
258
259 108
        l = snprintf(buf, sizeof(buf),
260
            "HTTP/1.1 %03d %s\r\n\r\n", status, reason);
261 108
        assert (l < sizeof(buf));
262
263 108
        VSLb(req->vsl, SLT_RespProtocol, "HTTP/1.1");
264 108
        VSLb(req->vsl, SLT_RespStatus, "%03d", status);
265 108
        VSLb(req->vsl, SLT_RespReason, "%s", reason);
266
267 108
        if (status >= 400)
268 93
                req->err_code = status;
269 108
        wl = write(req->sp->fd, buf, l);
270
271 108
        if (wl > 0)
272 108
                req->acct.resp_hdrbytes += wl;
273 108
        if (wl != l) {
274 0
                if (wl < 0)
275 0
                        VTCP_Assert(1);
276 0
                if (!req->doclose)
277 0
                        req->doclose = SC_REM_CLOSE;
278 0
                return (-1);
279
        }
280 108
        return (0);
281
}
282
283
struct transport HTTP1_transport = {
284
        .name =                 "HTTP/1",
285
        .proto_ident =          "HTTP",
286
        .magic =                TRANSPORT_MAGIC,
287
        .deliver =              V1D_Deliver,
288
        .minimal_response =     http1_minimal_response,
289
        .new_session =          http1_new_session,
290
        .reembark =             http1_reembark,
291
        .req_body =             http1_req_body,
292
        .req_fail =             http1_req_fail,
293
        .req_panic =            http1_req_panic,
294
        .sess_panic =           http1_sess_panic,
295
        .unwait =               http1_unwait,
296
};
297
298
/*----------------------------------------------------------------------
299
 */
300
301
static inline void
302 84
http1_abort(struct req *req, uint16_t status)
303
{
304 84
        AN(req->doclose);
305 84
        assert(status >= 400);
306 84
        (void)http1_minimal_response(req, status);
307 84
}
308
309
static int
310 6432
http1_dissect(struct worker *wrk, struct req *req)
311
{
312
313 6432
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
314 6432
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
315 6432
        CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC);
316
317
        /* Allocate a new vxid now that we know we'll need it. */
318 6432
        AZ(req->vsl->wid);
319 6432
        req->vsl->wid = VXID_Get(wrk, VSL_CLIENTMARKER);
320
321 6432
        VSLb(req->vsl, SLT_Begin, "req %u rxreq", VXID(req->sp->vxid));
322 6432
        VSL(SLT_Link, req->sp->vxid, "req %u rxreq", VXID(req->vsl->wid));
323 6432
        AZ(isnan(req->t_first)); /* First byte timestamp set by http1_wait */
324 6432
        AZ(isnan(req->t_req));   /* Complete req rcvd set by http1_wait */
325 6432
        req->t_prev = req->t_first;
326 6432
        VSLb_ts_req(req, "Start", req->t_first);
327 6432
        VSLb_ts_req(req, "Req", req->t_req);
328
329
        /* Borrow VCL reference from worker thread */
330 6432
        VCL_Refresh(&wrk->vcl);
331 6432
        req->vcl = wrk->vcl;
332 6432
        wrk->vcl = NULL;
333
334 6432
        HTTP_Setup(req->http, req->ws, req->vsl, SLT_ReqMethod);
335 6432
        req->err_code = HTTP1_DissectRequest(req->htc, req->http);
336
337
        /* If we could not even parse the request, just close */
338 6432
        if (req->err_code != 0) {
339 168
                VSLb(req->vsl, SLT_HttpGarbage, "%.*s",
340 84
                    (int)(req->htc->rxbuf_e - req->htc->rxbuf_b),
341 84
                    req->htc->rxbuf_b);
342 84
                wrk->stats->client_req_400++;
343 84
                req->doclose = SC_RX_JUNK;
344 84
                http1_abort(req, 400);
345 84
                return (-1);
346
        }
347
348 6348
        assert (req->req_body_status == REQ_BODY_INIT);
349
350 6348
        switch (req->htc->body_status) {
351
        case BS_CHUNKED:
352 18
                req->req_body_status = REQ_BODY_WITHOUT_LEN;
353 18
                break;
354
        case BS_LENGTH:
355 114
                req->req_body_status = REQ_BODY_WITH_LEN;
356 114
                break;
357
        case BS_NONE:
358 6216
                req->req_body_status = REQ_BODY_NONE;
359 6216
                break;
360
        case BS_EOF:
361 0
                req->req_body_status = REQ_BODY_WITHOUT_LEN;
362 0
                break;
363
        default:
364 0
                WRONG("Unknown req_body_status situation");
365
        }
366 6348
        return (0);
367
}
368
369
/*----------------------------------------------------------------------
370
 */
371
372
static void
373 4709
HTTP1_Session(struct worker *wrk, struct req *req)
374
{
375
        enum htc_status_e hs;
376
        struct sess *sp;
377
        const char *st;
378
        int i;
379
380 4709
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
381 4709
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
382 4709
        sp = req->sp;
383 4709
        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
384
385
        /*
386
         * Whenever we come in from the acceptor or waiter, we need to set
387
         * blocking mode.  It would be simpler to do this in the acceptor
388
         * or waiter, but we'd rather do the syscall in the worker thread.
389
         * On systems which return errors for ioctl, we close early
390
         */
391 4709
        if (http1_getstate(sp) == H1NEWREQ && VTCP_blocking(sp->fd)) {
392 0
                AN(req->htc->ws->r);
393 0
                if (errno == ECONNRESET)
394 0
                        SES_Close(sp, SC_REM_CLOSE);
395
                else
396 0
                        SES_Close(sp, SC_TX_ERROR);
397 0
                WS_Release(req->htc->ws, 0);
398 0
                AN(http1_req_cleanup(sp, wrk, req));
399 0
                return;
400
        }
401
402 4709
        req->transport = &HTTP1_transport;
403
404
        while (1) {
405 41769
                st = http1_getstate(sp);
406 23238
                if (st == H1NEWREQ) {
407 10367
                        CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC);
408 10367
                        assert(isnan(req->t_prev));
409 10367
                        assert(isnan(req->t_req));
410 10367
                        AZ(req->vcl);
411 10367
                        AZ(req->esi_level);
412 10367
                        AN(req->htc->ws->r);
413
414 31101
                        hs = HTC_RxStuff(req->htc, HTTP1_Complete,
415
                            &req->t_first, &req->t_req,
416 10367
                            sp->t_idle + cache_param->timeout_linger,
417 10367
                            sp->t_idle + cache_param->timeout_idle,
418 10367
                            cache_param->http_req_size);
419 10368
                        AZ(req->htc->ws->r);
420 10368
                        if (hs < HTC_S_EMPTY) {
421 6726
                                req->acct.req_hdrbytes +=
422 3363
                                    req->htc->rxbuf_e - req->htc->rxbuf_b;
423 3363
                                Req_AcctLogCharge(wrk->stats, req);
424 3363
                                Req_Release(req);
425 3362
                                switch (hs) {
426
                                case HTC_S_CLOSE:
427 0
                                        SES_Delete(sp, SC_REM_CLOSE, NAN);
428 0
                                        return;
429
                                case HTC_S_TIMEOUT:
430 0
                                        SES_Delete(sp, SC_RX_TIMEOUT, NAN);
431 0
                                        return;
432
                                case HTC_S_OVERFLOW:
433 6
                                        SES_Delete(sp, SC_RX_OVERFLOW, NAN);
434 6
                                        return;
435
                                case HTC_S_EOF:
436 3356
                                        SES_Delete(sp, SC_REM_CLOSE, NAN);
437 3357
                                        return;
438
                                default:
439 0
                                        WRONG("htc_status (bad)");
440
                                }
441
                        }
442 7005
                        if (hs == HTC_S_IDLE) {
443 372
                                wrk->stats->sess_herd++;
444 372
                                Req_Release(req);
445 372
                                SES_Wait(sp, &HTTP1_transport);
446 372
                                return;
447
                        }
448 6633
                        if (hs != HTC_S_COMPLETE)
449 0
                                WRONG("htc_status (nonbad)");
450
451 6633
                        if (H2_prism_complete(req->htc) == HTC_S_COMPLETE) {
452 201
                                if (!FEATURE(FEATURE_HTTP2)) {
453 6
                                        SES_Close(req->sp, SC_REQ_HTTP20);
454 6
                                        AZ(req->ws->r);
455 6
                                        AZ(wrk->aws->r);
456 6
                                        http1_setstate(sp, H1CLEANUP);
457 6
                                        continue;
458
                                }
459 195
                                http1_setstate(sp, NULL);
460 195
                                H2_PU_Sess(wrk, sp, req);
461 195
                                return;
462
                        }
463
464 6432
                        i = http1_dissect(wrk, req);
465 12864
                        req->acct.req_hdrbytes +=
466 6432
                            req->htc->rxbuf_e - req->htc->rxbuf_b;
467 6432
                        if (i) {
468 84
                                assert(req->doclose > 0);
469 84
                                SES_Close(req->sp, req->doclose);
470 84
                                AZ(req->ws->r);
471 84
                                AZ(wrk->aws->r);
472 84
                                http1_setstate(sp, H1CLEANUP);
473 84
                                continue;
474
                        }
475 6348
                        if (http_HdrIs(req->http, H_Upgrade, "h2c")) {
476 21
                                if (!FEATURE(FEATURE_HTTP2)) {
477 3
                                        VSLb(req->vsl, SLT_Debug,
478
                                            "H2 upgrade attempt");
479 18
                                } else if (req->htc->body_status != BS_NONE) {
480 3
                                        VSLb(req->vsl, SLT_Debug,
481
                                            "H2 upgrade attempt has body");
482
                                } else {
483 15
                                        http1_setstate(sp, NULL);
484 15
                                        req->err_code = 2;
485 15
                                        H2_OU_Sess(wrk, sp, req);
486 15
                                        return;
487
                                }
488
                        }
489 6333
                        req->req_step = R_STP_TRANSPORT;
490 6333
                        http1_setstate(sp, H1PROC);
491 12871
                } else if (st == H1BUSY) {
492 65
                        CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC);
493
                        /*
494
                         * Return from waitinglist.
495
                         * Check to see if the remote has left.
496
                         */
497 65
                        if (VTCP_check_hup(sp->fd)) {
498 0
                                http1_cleanup_waiting(wrk, req, SC_REM_CLOSE);
499 0
                                return;
500
                        }
501 65
                        http1_setstate(sp, H1PROC);
502 12806
                } else if (st == H1PROC) {
503 6398
                        req->task.func = http1_req;
504 6398
                        req->task.priv = req;
505 6398
                        if (CNT_Request(wrk, req) == REQ_FSM_DISEMBARK)
506 68
                                return;
507 6318
                        req->task.func = NULL;
508 6318
                        req->task.priv = NULL;
509 6318
                        AZ(req->ws->r);
510 6318
                        AZ(wrk->aws->r);
511 6318
                        http1_setstate(sp, H1CLEANUP);
512 6408
                } else if (st == H1CLEANUP) {
513 6408
                        if (http1_req_cleanup(sp, wrk, req))
514 684
                                return;
515 5724
                        HTC_RxInit(req->htc, req->ws);
516 5724
                        if (req->htc->rxbuf_e != req->htc->rxbuf_b)
517 36
                                wrk->stats->sess_readahead++;
518 5724
                        http1_setstate(sp, H1NEWREQ);
519
                } else {
520 0
                        WRONG("Wrong H1 session state");
521
                }
522
        }
523
}