Coverage Report

Created: 2024-02-25 06:15

/src/h2o/lib/handler/fastcgi.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2015-2016 DeNA Co., Ltd. Kazuho Oku
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a copy
5
 * of this software and associated documentation files (the "Software"), to
6
 * deal in the Software without restriction, including without limitation the
7
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8
 * sell copies of the Software, and to permit persons to whom the Software is
9
 * furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in
12
 * all copies or substantial portions of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20
 * IN THE SOFTWARE.
21
 */
22
#include <inttypes.h>
23
#include <stdio.h>
24
#include "picohttpparser.h"
25
#include "h2o.h"
26
27
0
#define FCGI_VERSION_1 1
28
29
0
#define FCGI_RESPONDER 1
30
0
#define FCGI_KEEP_CONN 1
31
32
0
#define FCGI_BEGIN_REQUEST 1
33
0
#define FCGI_END_REQUEST 3
34
0
#define FCGI_PARAMS 4
35
0
#define FCGI_STDIN 5
36
0
#define FCGI_STDOUT 6
37
0
#define FCGI_STDERR 7
38
#define FCGI_DATA 8
39
40
0
#define FCGI_RECORD_HEADER_SIZE (sizeof(struct st_fcgi_record_header_t))
41
0
#define FCGI_BEGIN_REQUEST_BODY_SIZE 8
42
43
0
#define MODULE_NAME "lib/handler/fastcgi.c"
44
45
0
#define APPEND_BLOCKSIZE 512 /* the size should be small enough to be allocated within the buffer of the memory pool */
46
47
struct st_fcgi_record_header_t {
48
    uint8_t version;
49
    uint8_t type;
50
    uint16_t requestId;
51
    uint16_t contentLength;
52
    uint8_t paddingLength;
53
    uint8_t reserved;
54
};
55
56
struct st_fcgi_begin_request_body_t {
57
    uint16_t role;
58
    uint8_t flags;
59
    uint8_t reserved[5];
60
};
61
62
typedef H2O_VECTOR(h2o_iovec_t) iovec_vector_t;
63
64
struct st_fcgi_generator_t {
65
    h2o_generator_t super;
66
    h2o_fastcgi_handler_t *handler;
67
    h2o_req_t *req;
68
    h2o_socketpool_connect_request_t *connect_req;
69
    h2o_socket_t *sock;
70
    int sent_headers;
71
    size_t leftsize; /* remaining amount of the content to receive (or SIZE_MAX if unknown) */
72
    struct {
73
        h2o_doublebuffer_t sending;
74
        h2o_buffer_t *receiving;
75
    } resp;
76
    h2o_timer_t timeout;
77
};
78
79
struct st_h2o_fastcgi_handler_t {
80
    h2o_handler_t super;
81
    h2o_socketpool_t sockpool;
82
    h2o_fastcgi_config_vars_t config;
83
};
84
85
static void encode_uint16(void *_p, unsigned v)
86
0
{
87
0
    unsigned char *p = _p;
88
0
    p[0] = (unsigned char)(v >> 8);
89
0
    p[1] = (unsigned char)v;
90
0
}
91
92
static void encode_record_header(void *p, uint8_t type, uint16_t reqId, uint16_t sz)
93
0
{
94
0
    struct st_fcgi_record_header_t *header = p;
95
0
    header->version = FCGI_VERSION_1;
96
0
    header->type = type;
97
0
    encode_uint16(&header->requestId, reqId);
98
0
    encode_uint16(&header->contentLength, sz);
99
0
    header->paddingLength = 0;
100
0
    header->reserved = 0;
101
0
}
102
103
static void encode_begin_request(void *p, uint16_t reqId, uint16_t role, uint8_t flags)
104
0
{
105
0
    encode_record_header(p, FCGI_BEGIN_REQUEST, reqId, FCGI_BEGIN_REQUEST_BODY_SIZE);
106
0
    struct st_fcgi_begin_request_body_t *body = (void *)((char *)p + FCGI_RECORD_HEADER_SIZE);
107
0
    encode_uint16(&body->role, role);
108
0
    body->flags = flags;
109
0
    memset(body->reserved, 0, sizeof(body->reserved));
110
0
}
111
112
static h2o_iovec_t create_begin_request(h2o_mem_pool_t *pool, uint16_t reqId, uint16_t role, uint8_t flags)
113
0
{
114
0
    h2o_iovec_t rec = h2o_iovec_init(h2o_mem_alloc_pool(pool, char, FCGI_RECORD_HEADER_SIZE + FCGI_BEGIN_REQUEST_BODY_SIZE),
115
0
                                     FCGI_RECORD_HEADER_SIZE + FCGI_BEGIN_REQUEST_BODY_SIZE);
116
0
    encode_begin_request(rec.base, reqId, role, flags);
117
0
    return rec;
118
0
}
119
120
static h2o_iovec_t create_header(h2o_mem_pool_t *pool, uint8_t type, uint16_t reqId, uint16_t sz)
121
0
{
122
0
    h2o_iovec_t rec = h2o_iovec_init(h2o_mem_alloc_pool(pool, char, FCGI_RECORD_HEADER_SIZE), FCGI_RECORD_HEADER_SIZE);
123
0
    encode_record_header(rec.base, type, reqId, sz);
124
0
    return rec;
125
0
}
126
127
static void decode_header(struct st_fcgi_record_header_t *decoded, const void *s)
128
0
{
129
0
    memcpy(decoded, s, sizeof(*decoded));
130
0
    decoded->requestId = htons(decoded->requestId);
131
0
    decoded->contentLength = htons(decoded->contentLength);
132
0
}
133
134
static void *append(h2o_mem_pool_t *pool, iovec_vector_t *blocks, const void *s, size_t len)
135
0
{
136
0
    h2o_iovec_t *slot;
137
138
0
    if (blocks->entries[blocks->size - 1].len + len > APPEND_BLOCKSIZE) {
139
0
        h2o_vector_reserve(pool, blocks, blocks->size + 1);
140
0
        slot = blocks->entries + blocks->size++;
141
0
        slot->base = h2o_mem_alloc_pool(pool, char, len < APPEND_BLOCKSIZE ? APPEND_BLOCKSIZE : len);
142
0
        slot->len = 0;
143
0
    } else {
144
0
        slot = blocks->entries + blocks->size - 1;
145
0
    }
146
147
0
    if (s != NULL)
148
0
        memcpy(slot->base + slot->len, s, len);
149
0
    slot->len += len;
150
151
0
    return slot->base + slot->len - len;
152
0
}
153
154
static char *encode_length_of_pair(char *p, size_t len)
155
0
{
156
0
    if (len < 0x80) {
157
0
        *p++ = (char)len;
158
0
    } else {
159
0
        *p++ = (unsigned char)(len >> 24) | 0x80;
160
0
        *p++ = (unsigned char)(len >> 16);
161
0
        *p++ = (unsigned char)(len >> 8);
162
0
        *p++ = (unsigned char)len;
163
0
    }
164
0
    return p;
165
0
}
166
167
static void *append_pair(h2o_mem_pool_t *pool, iovec_vector_t *blocks, const char *name, size_t namelen, const char *value,
168
                         size_t valuelen)
169
0
{
170
0
    char lenbuf[8];
171
0
    void *name_buf;
172
173
0
    append(pool, blocks, lenbuf, encode_length_of_pair(encode_length_of_pair(lenbuf, namelen), valuelen) - lenbuf);
174
0
    name_buf = append(pool, blocks, name, namelen);
175
0
    if (valuelen != 0)
176
0
        append(pool, blocks, value, valuelen);
177
178
0
    return name_buf;
179
0
}
180
181
static void append_address_info(h2o_req_t *req, iovec_vector_t *vecs, const char *addrlabel, size_t addrlabel_len,
182
                                const char *portlabel, size_t portlabel_len, socklen_t (*cb)(h2o_conn_t *conn, struct sockaddr *))
183
0
{
184
0
    struct sockaddr_storage ss;
185
0
    socklen_t sslen;
186
0
    char buf[NI_MAXHOST];
187
188
0
    if ((sslen = cb(req->conn, (void *)&ss)) == 0)
189
0
        return;
190
191
0
    size_t l = h2o_socket_getnumerichost((void *)&ss, sslen, buf);
192
0
    if (l != SIZE_MAX)
193
0
        append_pair(&req->pool, vecs, addrlabel, addrlabel_len, buf, l);
194
0
    int32_t port = h2o_socket_getport((void *)&ss);
195
0
    if (port != -1) {
196
0
        char buf[6];
197
0
        int l = sprintf(buf, "%" PRIu16, (uint16_t)port);
198
0
        append_pair(&req->pool, vecs, portlabel, portlabel_len, buf, (size_t)l);
199
0
    }
200
0
}
201
202
static int envname_is_headername(const h2o_iovec_t *env, const h2o_iovec_t *header)
203
0
{
204
0
    const char *ep, *hp, *hend;
205
206
0
    if (env->len != 5 + header->len)
207
0
        return 0;
208
0
    if (memcmp(env->base, "HTTP_", 5) != 0)
209
0
        return 0;
210
0
    for (ep = env->base + 5, hp = header->base, hend = hp + header->len; hp < hend; ++ep, ++hp)
211
0
        if (*ep != h2o_toupper(*hp))
212
0
            return 0;
213
0
    return 1;
214
0
}
215
216
static void append_params(h2o_req_t *req, iovec_vector_t *vecs, h2o_fastcgi_config_vars_t *config)
217
0
{
218
0
    h2o_iovec_t path_info = {NULL};
219
220
    /* CONTENT_LENGTH */
221
0
    if (req->entity.base != NULL) {
222
0
        char buf[32];
223
0
        int l = sprintf(buf, "%zu", req->entity.len);
224
0
        append_pair(&req->pool, vecs, H2O_STRLIT("CONTENT_LENGTH"), buf, (size_t)l);
225
0
    }
226
    /* SCRIPT_FILENAME, SCRIPT_NAME, PATH_INFO */
227
0
    if (req->filereq != NULL) {
228
0
        h2o_filereq_t *filereq = req->filereq;
229
0
        append_pair(&req->pool, vecs, H2O_STRLIT("SCRIPT_FILENAME"), filereq->local_path.base, filereq->local_path.len);
230
0
        append_pair(&req->pool, vecs, H2O_STRLIT("SCRIPT_NAME"), filereq->script_name.base, filereq->script_name.len);
231
0
        path_info = filereq->path_info;
232
0
    } else {
233
0
        append_pair(&req->pool, vecs, H2O_STRLIT("SCRIPT_NAME"), NULL, 0);
234
0
        path_info = req->path_normalized;
235
0
    }
236
0
    if (path_info.base != NULL)
237
0
        append_pair(&req->pool, vecs, H2O_STRLIT("PATH_INFO"), path_info.base, path_info.len);
238
    /* DOCUMENT_ROOT and PATH_TRANSLATED */
239
0
    if (config->document_root.base != NULL) {
240
0
        append_pair(&req->pool, vecs, H2O_STRLIT("DOCUMENT_ROOT"), config->document_root.base, config->document_root.len);
241
0
        if (path_info.base != NULL) {
242
0
            append_pair(&req->pool, vecs, H2O_STRLIT("PATH_TRANSLATED"), NULL, config->document_root.len + path_info.len);
243
0
            char *dst_end = vecs->entries[vecs->size - 1].base + vecs->entries[vecs->size - 1].len;
244
0
            memcpy(dst_end - path_info.len, path_info.base, path_info.len);
245
0
            memcpy(dst_end - path_info.len - config->document_root.len, config->document_root.base, config->document_root.len);
246
0
        }
247
0
    }
248
    /* QUERY_STRING (and adjust PATH_INFO) */
249
0
    if (req->query_at != SIZE_MAX) {
250
0
        append_pair(&req->pool, vecs, H2O_STRLIT("QUERY_STRING"), req->path.base + req->query_at + 1,
251
0
                    req->path.len - (req->query_at + 1));
252
0
    } else {
253
0
        append_pair(&req->pool, vecs, H2O_STRLIT("QUERY_STRING"), NULL, 0);
254
0
    }
255
    /* REMOTE_ADDR & REMOTE_PORT */
256
0
    append_address_info(req, vecs, H2O_STRLIT("REMOTE_ADDR"), H2O_STRLIT("REMOTE_PORT"), req->conn->callbacks->get_peername);
257
0
    { /* environment variables (REMOTE_USER, etc.) */
258
0
        size_t i;
259
0
        for (i = 0; i != req->env.size; i += 2) {
260
0
            h2o_iovec_t *name = req->env.entries + i, *value = name + 1;
261
0
            append_pair(&req->pool, vecs, name->base, name->len, value->base, value->len);
262
0
        }
263
0
    }
264
    /* REQUEST_METHOD */
265
0
    append_pair(&req->pool, vecs, H2O_STRLIT("REQUEST_METHOD"), req->method.base, req->method.len);
266
    /* HTTP_HOST & REQUEST_URI */
267
0
    if (config->send_delegated_uri) {
268
0
        append_pair(&req->pool, vecs, H2O_STRLIT("HTTP_HOST"), req->authority.base, req->authority.len);
269
0
        append_pair(&req->pool, vecs, H2O_STRLIT("REQUEST_URI"), req->path.base, req->path.len);
270
0
    } else {
271
0
        append_pair(&req->pool, vecs, H2O_STRLIT("HTTP_HOST"), req->input.authority.base, req->input.authority.len);
272
0
        append_pair(&req->pool, vecs, H2O_STRLIT("REQUEST_URI"), req->input.path.base, req->input.path.len);
273
0
    }
274
    /* SERVER_ADDR & SERVER_PORT */
275
0
    append_address_info(req, vecs, H2O_STRLIT("SERVER_ADDR"), H2O_STRLIT("SERVER_PORT"), req->conn->callbacks->get_sockname);
276
    /* SERVER_NAME */
277
0
    append_pair(&req->pool, vecs, H2O_STRLIT("SERVER_NAME"), req->hostconf->authority.host.base, req->hostconf->authority.host.len);
278
0
    { /* SERVER_PROTOCOL */
279
0
        char buf[sizeof("HTTP/1.1")];
280
0
        size_t l = h2o_stringify_protocol_version(buf, req->version);
281
0
        append_pair(&req->pool, vecs, H2O_STRLIT("SERVER_PROTOCOL"), buf, l);
282
0
    }
283
    /* SERVER_SOFTWARE */
284
0
    append_pair(&req->pool, vecs, H2O_STRLIT("SERVER_SOFTWARE"), req->conn->ctx->globalconf->server_name.base,
285
0
                req->conn->ctx->globalconf->server_name.len);
286
    /* set HTTPS: on if necessary */
287
0
    if (req->scheme == &H2O_URL_SCHEME_HTTPS)
288
0
        append_pair(&req->pool, vecs, H2O_STRLIT("HTTPS"), H2O_STRLIT("on"));
289
0
    { /* headers */
290
0
        const h2o_header_t *h = req->headers.entries, *h_end = h + req->headers.size;
291
0
        size_t cookie_length = 0;
292
0
        int found_early_data = 0;
293
0
        for (; h != h_end; ++h) {
294
0
            if (h->name == &H2O_TOKEN_CONTENT_TYPE->buf) {
295
0
                append_pair(&req->pool, vecs, H2O_STRLIT("CONTENT_TYPE"), h->value.base, h->value.len);
296
0
            } else if (h->name == &H2O_TOKEN_COOKIE->buf) {
297
                /* accumulate the length of the cookie, together with the separator */
298
0
                cookie_length += h->value.len + 1;
299
0
            } else {
300
0
                if (h->name == &H2O_TOKEN_EARLY_DATA->buf)
301
0
                    found_early_data = 1;
302
0
                size_t i;
303
0
                for (i = 0; i != req->env.size; i += 2) {
304
0
                    h2o_iovec_t *envname = req->env.entries + i;
305
0
                    if (envname_is_headername(envname, h->name))
306
0
                        goto NextHeader;
307
0
                }
308
0
                char *dst = append_pair(&req->pool, vecs, NULL, h->name->len + sizeof("HTTP_") - 1, h->value.base, h->value.len);
309
0
                const char *src = h->name->base, *src_end = src + h->name->len;
310
0
                *dst++ = 'H';
311
0
                *dst++ = 'T';
312
0
                *dst++ = 'T';
313
0
                *dst++ = 'P';
314
0
                *dst++ = '_';
315
0
                for (; src != src_end; ++src)
316
0
                    *dst++ = *src == '-' ? '_' : h2o_toupper(*src);
317
0
            }
318
0
        NextHeader:;
319
0
        }
320
0
        if (cookie_length != 0) {
321
            /* emit the cookie merged */
322
0
            cookie_length -= 1;
323
0
            append_pair(&req->pool, vecs, H2O_STRLIT("HTTP_COOKIE"), NULL, cookie_length);
324
0
            char *dst = vecs->entries[vecs->size - 1].base + vecs->entries[vecs->size - 1].len - cookie_length;
325
0
            for (h = req->headers.entries;; ++h) {
326
0
                if (h->name == &H2O_TOKEN_COOKIE->buf) {
327
0
                    if (cookie_length == h->value.len)
328
0
                        break;
329
0
                    memcpy(dst, h->value.base, h->value.len);
330
0
                    dst += h->value.len;
331
0
                    *dst++ = ';';
332
0
                    cookie_length -= h->value.len + 1;
333
0
                }
334
0
            }
335
0
            memcpy(dst, h->value.base, h->value.len);
336
0
        }
337
0
        if (!found_early_data && h2o_conn_is_early_data(req->conn)) {
338
0
            append_pair(&req->pool, vecs, H2O_STRLIT("HTTP_EARLY_DATA"), H2O_STRLIT("1"));
339
0
            req->reprocess_if_too_early = 1;
340
0
        }
341
0
    }
342
0
}
343
344
static void annotate_params(h2o_mem_pool_t *pool, iovec_vector_t *vecs, unsigned request_id, size_t max_record_size)
345
0
{
346
0
    size_t index = 2, recsize = 0, header_slot = 1;
347
348
0
    while (index != vecs->size) {
349
0
        if (recsize + vecs->entries[index].len < max_record_size) {
350
0
            recsize += vecs->entries[index].len;
351
0
            ++index;
352
0
        } else {
353
0
            vecs->entries[header_slot] = create_header(pool, FCGI_PARAMS, request_id, max_record_size);
354
0
            if (recsize + vecs->entries[index].len == max_record_size) {
355
0
                h2o_vector_reserve(pool, vecs, vecs->size + 1);
356
0
                memmove(vecs->entries + index + 2, vecs->entries + index + 1,
357
0
                        (vecs->size - (index + 1)) * sizeof(vecs->entries[0]));
358
0
                ++vecs->size;
359
0
            } else {
360
0
                h2o_vector_reserve(pool, vecs, vecs->size + 2);
361
0
                memmove(vecs->entries + index + 2, vecs->entries + index, (vecs->size - index) * sizeof(vecs->entries[0]));
362
0
                vecs->size += 2;
363
0
                size_t lastsz = max_record_size - recsize;
364
0
                vecs->entries[index].len = lastsz;
365
0
                vecs->entries[index + 2].base += lastsz;
366
0
                vecs->entries[index + 2].len -= lastsz;
367
0
            }
368
0
            header_slot = index + 1;
369
0
            index += 2;
370
0
            recsize = 0;
371
0
        }
372
0
    }
373
374
0
    vecs->entries[header_slot] = create_header(pool, FCGI_PARAMS, request_id, recsize);
375
0
    if (recsize != 0) {
376
0
        h2o_vector_reserve(pool, vecs, vecs->size + 1);
377
0
        vecs->entries[vecs->size++] = create_header(pool, FCGI_PARAMS, request_id, 0);
378
0
    }
379
0
}
380
381
static void build_request(h2o_req_t *req, iovec_vector_t *vecs, unsigned request_id, size_t max_record_size,
382
                          h2o_fastcgi_config_vars_t *config)
383
0
{
384
0
    *vecs = (iovec_vector_t){NULL};
385
386
    /* first entry is FCGI_BEGIN_REQUEST */
387
0
    h2o_vector_reserve(&req->pool, vecs, 5 /* we send at least 5 iovecs */);
388
0
    vecs->entries[0] =
389
0
        create_begin_request(&req->pool, request_id, FCGI_RESPONDER, config->keepalive_timeout != 0 ? FCGI_KEEP_CONN : 0);
390
    /* second entry is reserved for FCGI_PARAMS header */
391
0
    vecs->entries[1] = h2o_iovec_init(NULL, APPEND_BLOCKSIZE); /* dummy value set to prevent params being appended to the entry */
392
0
    vecs->size = 2;
393
    /* accumulate the params data, and annotate them with FCGI_PARAM headers */
394
0
    append_params(req, vecs, config);
395
0
    annotate_params(&req->pool, vecs, request_id, max_record_size);
396
    /* setup FCGI_STDIN headers */
397
0
    if (req->entity.len != 0) {
398
0
        size_t off = 0;
399
0
        for (; off + max_record_size < req->entity.len; off += max_record_size) {
400
0
            h2o_vector_reserve(&req->pool, vecs, vecs->size + 2);
401
0
            vecs->entries[vecs->size++] = create_header(&req->pool, FCGI_STDIN, request_id, max_record_size);
402
0
            vecs->entries[vecs->size++] = h2o_iovec_init(req->entity.base + off, max_record_size);
403
0
        }
404
0
        if (off != req->entity.len) {
405
0
            h2o_vector_reserve(&req->pool, vecs, vecs->size + 2);
406
0
            vecs->entries[vecs->size++] = create_header(&req->pool, FCGI_STDIN, request_id, req->entity.len - off);
407
0
            vecs->entries[vecs->size++] = h2o_iovec_init(req->entity.base + off, req->entity.len - off);
408
0
        }
409
0
    }
410
0
    h2o_vector_reserve(&req->pool, vecs, vecs->size + 1);
411
0
    vecs->entries[vecs->size++] = create_header(&req->pool, FCGI_STDIN, request_id, 0);
412
0
}
413
414
static void set_timeout(struct st_fcgi_generator_t *generator, uint64_t timeout, h2o_timer_cb cb)
415
0
{
416
0
    if (h2o_timer_is_linked(&generator->timeout))
417
0
        h2o_timer_unlink(&generator->timeout);
418
0
    generator->timeout.cb = cb;
419
0
    h2o_timer_link(generator->req->conn->ctx->loop, timeout, &generator->timeout);
420
0
}
421
422
static void close_generator(struct st_fcgi_generator_t *generator)
423
0
{
424
    /* can be called more than once */
425
0
    if (h2o_timer_is_linked(&generator->timeout))
426
0
        h2o_timer_unlink(&generator->timeout);
427
0
    if (generator->connect_req != NULL) {
428
0
        h2o_socketpool_cancel_connect(generator->connect_req);
429
0
        generator->connect_req = NULL;
430
0
    }
431
0
    if (generator->sock != NULL) {
432
0
        h2o_socket_close(generator->sock);
433
0
        generator->sock = NULL;
434
0
    }
435
0
    if (generator->resp.sending.buf != NULL)
436
0
        h2o_doublebuffer_dispose(&generator->resp.sending);
437
0
    if (generator->resp.receiving != NULL)
438
0
        h2o_buffer_dispose(&generator->resp.receiving);
439
0
}
440
441
static void do_send(struct st_fcgi_generator_t *generator)
442
0
{
443
0
    h2o_iovec_t vecs[1];
444
0
    size_t veccnt;
445
0
    h2o_send_state_t send_state;
446
447
0
    vecs[0] = h2o_doublebuffer_prepare(&generator->resp.sending, &generator->resp.receiving, generator->req->preferred_chunk_size);
448
0
    veccnt = vecs[0].len != 0 ? 1 : 0;
449
0
    if (generator->sock == NULL && vecs[0].len == generator->resp.sending.buf->size && generator->resp.receiving->size == 0) {
450
0
        if (generator->leftsize == 0 || generator->leftsize == SIZE_MAX) {
451
0
            send_state = H2O_SEND_STATE_FINAL;
452
0
        } else {
453
0
            send_state = H2O_SEND_STATE_ERROR;
454
0
        }
455
0
    } else {
456
0
        if (veccnt == 0)
457
0
            return;
458
0
        send_state = H2O_SEND_STATE_IN_PROGRESS;
459
0
    }
460
0
    h2o_send(generator->req, vecs, veccnt, send_state);
461
0
}
462
463
static void send_eos_and_close(struct st_fcgi_generator_t *generator, int can_keepalive)
464
0
{
465
0
    if (generator->handler->config.keepalive_timeout != 0 && can_keepalive)
466
0
        h2o_socketpool_return(&generator->handler->sockpool, generator->sock);
467
0
    else
468
0
        h2o_socket_close(generator->sock);
469
0
    generator->sock = NULL;
470
0
    if (h2o_timer_is_linked(&generator->timeout))
471
0
        h2o_timer_unlink(&generator->timeout);
472
0
    if (!generator->resp.sending.inflight)
473
0
        do_send(generator);
474
0
}
475
476
static void errorclose(struct st_fcgi_generator_t *generator)
477
0
{
478
0
    if (generator->sent_headers) {
479
0
        send_eos_and_close(generator, 0);
480
0
    } else {
481
0
        h2o_req_t *req = generator->req;
482
0
        close_generator(generator);
483
0
        h2o_send_error_503(req, "Internal Server Error", "Internal Server Error", 0);
484
0
    }
485
0
}
486
487
static int _isdigit(int ch)
488
0
{
489
0
    return '0' <= ch && ch <= '9';
490
0
}
491
492
static int fill_headers(h2o_req_t *req, struct phr_header *headers, size_t num_headers)
493
0
{
494
0
    size_t i;
495
496
    /* set the defaults */
497
0
    req->res.status = 200;
498
0
    req->res.reason = "OK";
499
0
    req->res.content_length = SIZE_MAX;
500
501
0
    for (i = 0; i != num_headers; ++i) {
502
0
        const h2o_token_t *token;
503
0
        h2o_strtolower((char *)headers[i].name, headers[i].name_len);
504
0
        if ((token = h2o_lookup_token(headers[i].name, headers[i].name_len)) != NULL) {
505
0
            if (token->flags.proxy_should_drop_for_res) {
506
                /* skip */
507
0
            } else if (token == H2O_TOKEN_CONTENT_LENGTH) {
508
0
                if (req->res.content_length != SIZE_MAX) {
509
0
                    h2o_req_log_error(req, MODULE_NAME, "received multiple content-length headers from fcgi");
510
0
                    return -1;
511
0
                }
512
0
                if ((req->res.content_length = h2o_strtosize(headers[i].value, headers[i].value_len)) == SIZE_MAX) {
513
0
                    h2o_req_log_error(req, MODULE_NAME, "failed to parse content-length header sent from fcgi: %.*s",
514
0
                                      (int)headers[i].value_len, headers[i].value);
515
0
                    return -1;
516
0
                }
517
0
            } else {
518
                /*
519
                RFC 3875 defines three headers to have special meaning: Content-Type, Status, Location.
520
                Status is handled as below.
521
                Content-Type does not seem to have any need to be handled specially.
522
                RFC suggests abs-path-style Location headers should trigger an internal redirection, but is that how the web servers
523
                work?
524
                 */
525
0
                h2o_add_header(&req->pool, &req->res.headers, token, NULL,
526
0
                               h2o_strdup(&req->pool, headers[i].value, headers[i].value_len).base, headers[i].value_len);
527
0
                if (token == H2O_TOKEN_LINK)
528
0
                    h2o_push_path_in_link_header(req, headers[i].value, headers[i].value_len);
529
0
            }
530
0
        } else if (h2o_memis(headers[i].name, headers[i].name_len, H2O_STRLIT("status"))) {
531
0
            h2o_iovec_t value = h2o_iovec_init(headers[i].value, headers[i].value_len);
532
0
            if (value.len < 3 || !(_isdigit(value.base[0]) && _isdigit(value.base[1]) && _isdigit(value.base[2])) ||
533
0
                (value.len >= 4 && value.base[3] != ' ')) {
534
0
                h2o_req_log_error(req, MODULE_NAME, "failed to parse Status header, got: %.*s", (int)value.len, value.base);
535
0
                return -1;
536
0
            }
537
0
            req->res.status = (value.base[0] - '0') * 100 + (value.base[1] - '0') * 10 + (value.base[2] - '0');
538
0
            req->res.reason = value.len >= 5 ? h2o_strdup(&req->pool, value.base + 4, value.len - 4).base : "OK";
539
0
        } else {
540
0
            h2o_iovec_t name_duped = h2o_strdup(&req->pool, headers[i].name, headers[i].name_len),
541
0
                        value_duped = h2o_strdup(&req->pool, headers[i].value, headers[i].value_len);
542
0
            h2o_add_header_by_str(&req->pool, &req->res.headers, name_duped.base, name_duped.len, 0, NULL, value_duped.base,
543
0
                                  value_duped.len);
544
0
        }
545
0
    }
546
547
    /* add date: if it's missing from the response */
548
0
    if (h2o_find_header(&req->res.headers, H2O_TOKEN_DATE, -1) == -1)
549
0
        h2o_resp_add_date_header(req);
550
551
0
    return 0;
552
0
}
553
554
static int append_content(struct st_fcgi_generator_t *generator, const void *src, size_t len)
555
0
{
556
    /* do not accumulate more than content-length bytes */
557
0
    if (generator->leftsize != SIZE_MAX) {
558
0
        if (generator->leftsize < len) {
559
0
            len = generator->leftsize;
560
0
            if (len == 0)
561
0
                return 0;
562
0
        }
563
0
        generator->leftsize -= len;
564
0
    }
565
0
    h2o_iovec_t reserved = h2o_buffer_try_reserve(&generator->resp.receiving, len);
566
0
    if (reserved.base == NULL) {
567
0
        return -1;
568
0
    }
569
0
    memcpy(reserved.base, src, len);
570
0
    generator->resp.receiving->size += len;
571
0
    return 0;
572
0
}
573
574
static int handle_stdin_record(struct st_fcgi_generator_t *generator, struct st_fcgi_record_header_t *header)
575
0
{
576
0
    h2o_buffer_t *input = generator->sock->input;
577
0
    struct phr_header headers[100];
578
0
    size_t num_headers;
579
0
    int parse_result;
580
581
0
    if (header->contentLength == 0)
582
0
        return 0;
583
584
0
    if (generator->sent_headers) {
585
        /* simply accumulate the data to response buffer */
586
0
        if (append_content(generator, input->bytes + FCGI_RECORD_HEADER_SIZE, header->contentLength) != 0) {
587
0
            h2o_req_log_error(generator->req, MODULE_NAME, "failed to allocate memory");
588
0
            return -1;
589
0
        }
590
0
        return 0;
591
0
    }
592
593
    /* parse the headers using the input buffer (or keep it in response buffer and parse) */
594
0
    num_headers = sizeof(headers) / sizeof(headers[0]);
595
0
    if (generator->resp.receiving->size == 0) {
596
0
        parse_result = phr_parse_headers(input->bytes + FCGI_RECORD_HEADER_SIZE, header->contentLength, headers, &num_headers, 0);
597
0
    } else {
598
0
        size_t prevlen = generator->resp.receiving->size;
599
0
        memcpy(h2o_buffer_reserve(&generator->resp.receiving, header->contentLength).base, input->bytes + FCGI_RECORD_HEADER_SIZE,
600
0
               header->contentLength);
601
0
        generator->resp.receiving->size = prevlen + header->contentLength;
602
0
        parse_result =
603
0
            phr_parse_headers(generator->resp.receiving->bytes, generator->resp.receiving->size, headers, &num_headers, prevlen);
604
0
    }
605
0
    if (parse_result < 0) {
606
0
        if (parse_result == -2) {
607
            /* incomplete */
608
0
            if (generator->resp.receiving->size == 0) {
609
0
                memcpy(h2o_buffer_reserve(&generator->resp.receiving, header->contentLength).base,
610
0
                       input->bytes + FCGI_RECORD_HEADER_SIZE, header->contentLength);
611
0
                generator->resp.receiving->size = header->contentLength;
612
0
            }
613
0
            return 0;
614
0
        } else {
615
0
            h2o_req_log_error(generator->req, MODULE_NAME, "received broken response");
616
0
            return -1;
617
0
        }
618
0
    }
619
620
    /* fill-in the headers, and start the response */
621
0
    if (fill_headers(generator->req, headers, num_headers) != 0)
622
0
        return -1;
623
0
    generator->leftsize = generator->req->res.content_length;
624
0
    h2o_start_response(generator->req, &generator->super);
625
0
    generator->sent_headers = 1;
626
627
    /* rest of the contents should be stored in the response buffer */
628
0
    if (generator->resp.receiving->size == 0) {
629
0
        size_t leftlen = header->contentLength - parse_result;
630
0
        if (leftlen != 0) {
631
0
            if (append_content(generator, input->bytes + FCGI_RECORD_HEADER_SIZE + parse_result, leftlen) != 0) {
632
0
                h2o_req_log_error(generator->req, MODULE_NAME, "failed to allocate memory");
633
0
                return -1;
634
0
            }
635
0
        }
636
0
    } else {
637
0
        h2o_buffer_consume(&generator->resp.receiving, parse_result);
638
0
    }
639
640
0
    return 0;
641
0
}
642
643
static void on_rw_timeout(h2o_timer_t *entry)
644
0
{
645
0
    struct st_fcgi_generator_t *generator = H2O_STRUCT_FROM_MEMBER(struct st_fcgi_generator_t, timeout, entry);
646
647
0
    h2o_req_log_error(generator->req, MODULE_NAME, "I/O timeout");
648
0
    errorclose(generator);
649
0
}
650
651
static void on_read(h2o_socket_t *sock, const char *err)
652
0
{
653
0
    struct st_fcgi_generator_t *generator = sock->data;
654
0
    int can_keepalive = 0;
655
0
    int sent_headers_before = generator->sent_headers;
656
657
0
    if (err != NULL) {
658
        /* note: FastCGI server is allowed to close the connection any time after sending an empty FCGI_STDOUT record */
659
0
        if (!generator->sent_headers)
660
0
            h2o_req_log_error(generator->req, MODULE_NAME, "fastcgi connection closed unexpectedly");
661
0
        errorclose(generator);
662
0
        return;
663
0
    }
664
665
    /* handle the records */
666
0
    while (1) {
667
0
        struct st_fcgi_record_header_t header;
668
0
        size_t recsize;
669
0
        if (sock->input->size < FCGI_RECORD_HEADER_SIZE)
670
0
            break;
671
0
        decode_header(&header, sock->input->bytes);
672
0
        recsize = FCGI_RECORD_HEADER_SIZE + header.contentLength + header.paddingLength;
673
0
        if (sock->input->size < recsize)
674
0
            break;
675
        /* we have a complete record */
676
0
        switch (header.type) {
677
0
        case FCGI_STDOUT:
678
0
            if (handle_stdin_record(generator, &header) != 0)
679
0
                goto Error;
680
0
            h2o_buffer_consume(&sock->input, recsize);
681
0
            break;
682
0
        case FCGI_STDERR:
683
0
            if (header.contentLength != 0)
684
0
                h2o_req_log_error(generator->req, MODULE_NAME, "%.*s", (int)header.contentLength,
685
0
                                  sock->input->bytes + FCGI_RECORD_HEADER_SIZE);
686
0
            h2o_buffer_consume(&sock->input, recsize);
687
0
            break;
688
0
        case FCGI_END_REQUEST:
689
0
            if (!generator->sent_headers) {
690
0
                h2o_req_log_error(generator->req, MODULE_NAME, "received FCGI_END_REQUEST before end of the headers");
691
0
                goto Error;
692
0
            }
693
0
            h2o_buffer_consume(&sock->input, recsize);
694
0
            can_keepalive = 1;
695
0
            goto EOS_Received;
696
0
        default:
697
0
            h2o_req_log_error(generator->req, MODULE_NAME, "received unexpected record, type: %u", header.type);
698
0
            h2o_buffer_consume(&sock->input, recsize);
699
0
            if (!generator->sent_headers)
700
0
                goto Error;
701
0
            goto EOS_Received;
702
0
        }
703
0
    }
704
705
    /* send data if necessary */
706
0
    if (generator->sent_headers) {
707
0
        if (!sent_headers_before && generator->resp.receiving->size == 0) {
708
            /* send headers immediately */
709
0
            h2o_doublebuffer_prepare_empty(&generator->resp.sending);
710
0
            h2o_send(generator->req, NULL, 0, H2O_SEND_STATE_IN_PROGRESS);
711
0
        } else if (!generator->resp.sending.inflight) {
712
0
            do_send(generator);
713
0
        }
714
0
    }
715
716
0
    set_timeout(generator, generator->handler->config.io_timeout, on_rw_timeout);
717
0
    return;
718
719
0
EOS_Received:
720
0
    send_eos_and_close(generator, can_keepalive);
721
0
    return;
722
723
0
Error:
724
0
    errorclose(generator);
725
0
}
726
727
static void on_send_complete(h2o_socket_t *sock, const char *err)
728
0
{
729
0
    struct st_fcgi_generator_t *generator = sock->data;
730
731
0
    set_timeout(generator, generator->handler->config.io_timeout, on_rw_timeout);
732
    /* do nothing else!  all the rest is handled by the on_read */
733
0
}
734
735
static void on_connect(h2o_socket_t *sock, const char *errstr, void *data, h2o_url_t *_dummy)
736
0
{
737
0
    struct st_fcgi_generator_t *generator = data;
738
0
    iovec_vector_t vecs;
739
740
0
    generator->connect_req = NULL;
741
742
0
    if (sock == NULL) {
743
0
        h2o_req_log_error(generator->req, MODULE_NAME, "connection failed:%s", errstr);
744
0
        errorclose(generator);
745
0
        return;
746
0
    }
747
748
0
    generator->sock = sock;
749
0
    sock->data = generator;
750
751
0
    build_request(generator->req, &vecs, 1, 65535, &generator->handler->config);
752
753
    /* start sending the response */
754
0
    h2o_socket_write(generator->sock, vecs.entries, vecs.size, on_send_complete);
755
756
0
    set_timeout(generator, generator->handler->config.io_timeout, on_rw_timeout);
757
758
    /* activate the receiver; note: FCGI spec allows the app to start sending the response before it receives FCGI_STDIN */
759
0
    h2o_socket_read_start(sock, on_read);
760
0
}
761
762
static void do_proceed(h2o_generator_t *_generator, h2o_req_t *req)
763
0
{
764
0
    struct st_fcgi_generator_t *generator = (void *)_generator;
765
766
0
    h2o_doublebuffer_consume(&generator->resp.sending);
767
0
    do_send(generator);
768
0
}
769
770
static void do_stop(h2o_generator_t *_generator, h2o_req_t *req)
771
0
{
772
0
    struct st_fcgi_generator_t *generator = (void *)_generator;
773
0
    close_generator(generator);
774
0
}
775
776
static void on_connect_timeout(h2o_timer_t *entry)
777
0
{
778
0
    struct st_fcgi_generator_t *generator = H2O_STRUCT_FROM_MEMBER(struct st_fcgi_generator_t, timeout, entry);
779
780
0
    h2o_req_log_error(generator->req, MODULE_NAME, "connect timeout");
781
0
    errorclose(generator);
782
0
}
783
784
static int on_req(h2o_handler_t *_handler, h2o_req_t *req)
785
0
{
786
0
    h2o_fastcgi_handler_t *handler = (void *)_handler;
787
0
    struct st_fcgi_generator_t *generator;
788
789
0
    generator = h2o_mem_alloc_shared(&req->pool, sizeof(*generator), (void (*)(void *))close_generator);
790
0
    generator->super.proceed = do_proceed;
791
0
    generator->super.stop = do_stop;
792
0
    generator->handler = handler;
793
0
    generator->req = req;
794
0
    generator->sock = NULL;
795
0
    generator->sent_headers = 0;
796
0
    h2o_doublebuffer_init(&generator->resp.sending, &h2o_socket_buffer_prototype);
797
0
    h2o_buffer_init(&generator->resp.receiving, &h2o_socket_buffer_prototype);
798
0
    h2o_timer_init(&generator->timeout, on_connect_timeout);
799
0
    h2o_timer_link(req->conn->ctx->loop, generator->handler->config.io_timeout, &generator->timeout);
800
801
0
    h2o_socketpool_connect(&generator->connect_req, &handler->sockpool, &handler->sockpool.targets.entries[0]->url,
802
0
                           req->conn->ctx->loop, &req->conn->ctx->receivers.hostinfo_getaddr, h2o_iovec_init(NULL, 0), on_connect,
803
0
                           generator);
804
805
0
    return 0;
806
0
}
807
808
static void on_context_init(h2o_handler_t *_handler, h2o_context_t *ctx)
809
0
{
810
0
    h2o_fastcgi_handler_t *handler = (void *)_handler;
811
0
    h2o_socketpool_register_loop(&handler->sockpool, ctx->loop);
812
0
}
813
814
static void on_context_dispose(h2o_handler_t *_handler, h2o_context_t *ctx)
815
0
{
816
0
    h2o_fastcgi_handler_t *handler = (void *)_handler;
817
0
    h2o_socketpool_unregister_loop(&handler->sockpool, ctx->loop);
818
0
}
819
820
static void on_handler_dispose(h2o_handler_t *_handler)
821
0
{
822
0
    h2o_fastcgi_handler_t *handler = (void *)_handler;
823
824
0
    if (handler->config.callbacks.dispose != NULL)
825
0
        handler->config.callbacks.dispose(handler, handler->config.callbacks.data);
826
827
0
    h2o_socketpool_dispose(&handler->sockpool);
828
0
    free(handler->config.document_root.base);
829
0
}
830
831
h2o_fastcgi_handler_t *h2o_fastcgi_register(h2o_pathconf_t *pathconf, h2o_url_t *upstream, h2o_fastcgi_config_vars_t *vars)
832
0
{
833
0
    h2o_fastcgi_handler_t *handler = (void *)h2o_create_handler(pathconf, sizeof(*handler));
834
835
0
    handler->super.on_context_init = on_context_init;
836
0
    handler->super.on_context_dispose = on_context_dispose;
837
0
    handler->super.dispose = on_handler_dispose;
838
0
    handler->super.on_req = on_req;
839
0
    handler->config = *vars;
840
0
    if (vars->document_root.base != NULL)
841
0
        handler->config.document_root = h2o_strdup(NULL, vars->document_root.base, vars->document_root.len);
842
843
0
    h2o_socketpool_target_t *target = h2o_socketpool_create_target(upstream, NULL);
844
0
    h2o_socketpool_target_t **targets = &target;
845
0
    h2o_socketpool_init_specific(&handler->sockpool, SIZE_MAX /* FIXME */, targets, 1, NULL);
846
0
    h2o_socketpool_set_timeout(&handler->sockpool, handler->config.keepalive_timeout);
847
0
    return handler;
848
0
}