/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 = ⌖ |
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 | } |