/src/h2o/lib/common/http1client.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2014 DeNA Co., Ltd. |
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 <arpa/inet.h> |
23 | | #include <fcntl.h> |
24 | | #include <netdb.h> |
25 | | #include <netinet/in.h> |
26 | | #include <sys/socket.h> |
27 | | #include <sys/types.h> |
28 | | #include <sys/un.h> |
29 | | #include "picohttpparser.h" |
30 | | #include "h2o/httpclient.h" |
31 | | #include "h2o/token.h" |
32 | | |
33 | | #if !H2O_USE_LIBUV && defined(__linux__) |
34 | | #define USE_PIPE_READER 1 |
35 | | #else |
36 | | #define USE_PIPE_READER 0 |
37 | | #endif |
38 | | |
39 | | enum enum_h2o_http1client_stream_state { |
40 | | STREAM_STATE_HEAD, |
41 | | STREAM_STATE_BODY, |
42 | | STREAM_STATE_CLOSED, |
43 | | }; |
44 | | |
45 | | struct st_h2o_http1client_t { |
46 | | h2o_httpclient_t super; |
47 | | h2o_socket_t *sock; |
48 | | struct { |
49 | | enum enum_h2o_http1client_stream_state req; |
50 | | enum enum_h2o_http1client_stream_state res; |
51 | | } state; |
52 | | h2o_url_t *_origin; |
53 | | int _method_is_head; |
54 | | int _do_keepalive; |
55 | | union { |
56 | | struct { |
57 | | size_t bytesleft; |
58 | | } content_length; |
59 | | struct { |
60 | | struct phr_chunked_decoder decoder; |
61 | | size_t bytes_decoded_in_buf; |
62 | | } chunked; |
63 | | } _body_decoder; |
64 | | h2o_socket_cb reader; |
65 | | h2o_httpclient_proceed_req_cb proceed_req; |
66 | | /** |
67 | | * buffer used to hold chunk headers of a request body; the size is SIZE_MAX in hex + CRLF + '\0' |
68 | | */ |
69 | | char _chunk_len_str[(sizeof(H2O_UINT64_LONGEST_HEX_STR) - 1) + 2 + 1]; |
70 | | /** |
71 | | * buffer used to retain request body that is inflight |
72 | | */ |
73 | | struct { |
74 | | h2o_buffer_t *buf; |
75 | | int is_end_stream; |
76 | | } body_buf; |
77 | | /** |
78 | | * `on_body_piped` is non-NULL iff used |
79 | | */ |
80 | | h2o_httpclient_pipe_reader_t pipe_reader; |
81 | | /** |
82 | | * maintain the number of bytes being already processed on the associated socket |
83 | | */ |
84 | | uint64_t _socket_bytes_processed; |
85 | | unsigned _is_chunked : 1; |
86 | | unsigned _seen_at_least_one_chunk : 1; |
87 | | unsigned _delay_free : 1; |
88 | | unsigned _app_prefers_pipe_reader : 1; |
89 | | unsigned _send_own_expect : 1; |
90 | | }; |
91 | | |
92 | | static void on_body_to_pipe(h2o_socket_t *_sock, const char *err); |
93 | | |
94 | | static void req_body_send(struct st_h2o_http1client_t *client); |
95 | | static void update_read_state(struct st_h2o_http1client_t *client); |
96 | | |
97 | | static void close_client(struct st_h2o_http1client_t *client) |
98 | 617 | { |
99 | 617 | if (client->sock != NULL) { |
100 | 617 | if (client->super.connpool != NULL && client->_do_keepalive && client->super.connpool->socketpool->timeout > 0) { |
101 | | /* we do not send pipelined requests, and thus can trash all the received input at the end of the request */ |
102 | 0 | h2o_buffer_consume(&client->sock->input, client->sock->input->size); |
103 | 0 | h2o_socketpool_return(client->super.connpool->socketpool, client->sock); |
104 | 617 | } else { |
105 | 617 | h2o_socket_close(client->sock); |
106 | 617 | } |
107 | 617 | } |
108 | 617 | if (h2o_timer_is_linked(&client->super._timeout)) |
109 | 0 | h2o_timer_unlink(&client->super._timeout); |
110 | 617 | if (client->body_buf.buf != NULL) |
111 | 0 | h2o_buffer_dispose(&client->body_buf.buf); |
112 | 617 | if (!client->_delay_free) |
113 | 617 | free(client); |
114 | 617 | } |
115 | | |
116 | | static void close_response(struct st_h2o_http1client_t *client) |
117 | 601 | { |
118 | 601 | assert(client->state.res == STREAM_STATE_CLOSED); |
119 | 601 | if (client->state.req == STREAM_STATE_CLOSED) { |
120 | 599 | close_client(client); |
121 | 599 | } else { |
122 | 2 | h2o_socket_read_stop(client->sock); |
123 | 2 | } |
124 | 601 | } |
125 | | |
126 | | static h2o_httpclient_body_cb call_on_head(struct st_h2o_http1client_t *client, const char *errstr, h2o_httpclient_on_head_t *args) |
127 | 617 | { |
128 | 617 | assert(!client->_delay_free); |
129 | 617 | client->_delay_free = 1; |
130 | 617 | h2o_httpclient_body_cb cb = client->super._cb.on_head(&client->super, errstr, args); |
131 | 617 | client->_delay_free = 0; |
132 | 617 | return cb; |
133 | 617 | } |
134 | | |
135 | | static int call_on_body(struct st_h2o_http1client_t *client, const char *errstr) |
136 | 998 | { |
137 | 998 | assert(!client->_delay_free); |
138 | 998 | client->_delay_free = 1; |
139 | 998 | int ret = (client->reader == on_body_to_pipe ? client->pipe_reader.on_body_piped : client->super._cb.on_body)(&client->super, |
140 | 998 | errstr, NULL, 0); |
141 | 998 | client->_delay_free = 0; |
142 | 998 | return ret; |
143 | 998 | } |
144 | | |
145 | | static void call_proceed_req(struct st_h2o_http1client_t *client, const char *errstr) |
146 | 0 | { |
147 | 0 | assert(!client->_delay_free); |
148 | 0 | client->_delay_free = 1; |
149 | 0 | client->proceed_req(&client->super, errstr); |
150 | 0 | client->_delay_free = 0; |
151 | 0 | } |
152 | | |
153 | | static void on_error(struct st_h2o_http1client_t *client, const char *errstr) |
154 | 18 | { |
155 | 18 | switch (client->state.res) { |
156 | 2 | case STREAM_STATE_HEAD: |
157 | 2 | call_on_head(client, errstr, NULL); |
158 | 2 | break; |
159 | 14 | case STREAM_STATE_BODY: |
160 | 14 | call_on_body(client, errstr); |
161 | 14 | break; |
162 | 2 | case STREAM_STATE_CLOSED: |
163 | 2 | if (client->proceed_req != NULL) |
164 | 0 | call_proceed_req(client, errstr); |
165 | 2 | break; |
166 | 18 | } |
167 | 18 | close_client(client); |
168 | 18 | } |
169 | | |
170 | | static void on_body_timeout(h2o_timer_t *entry) |
171 | 0 | { |
172 | 0 | struct st_h2o_http1client_t *client = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http1client_t, super._timeout, entry); |
173 | 0 | on_error(client, h2o_httpclient_error_io_timeout); |
174 | 0 | } |
175 | | |
176 | | static void on_body_until_close(h2o_socket_t *sock, const char *err) |
177 | 984 | { |
178 | 984 | struct st_h2o_http1client_t *client = sock->data; |
179 | | |
180 | 984 | h2o_timer_unlink(&client->super._timeout); |
181 | | |
182 | 984 | if (err != NULL) { |
183 | 485 | client->state.res = STREAM_STATE_CLOSED; |
184 | 485 | client->super.timings.response_end_at = h2o_gettimeofday(client->super.ctx->loop); |
185 | 485 | call_on_body(client, h2o_httpclient_error_is_eos); |
186 | 485 | close_response(client); |
187 | 485 | return; |
188 | 485 | } |
189 | 499 | uint64_t size = sock->bytes_read - client->_socket_bytes_processed; |
190 | 499 | client->_socket_bytes_processed = sock->bytes_read; |
191 | | |
192 | 499 | client->super.bytes_read.body += size; |
193 | 499 | client->super.bytes_read.total += size; |
194 | | |
195 | 499 | if (size != 0) { |
196 | 499 | if (call_on_body(client, NULL) != 0) { |
197 | 0 | close_client(client); |
198 | 0 | return; |
199 | 0 | } |
200 | 499 | update_read_state(client); |
201 | 499 | } |
202 | 499 | } |
203 | | |
204 | | static void on_body_content_length(h2o_socket_t *sock, const char *err) |
205 | 0 | { |
206 | 0 | struct st_h2o_http1client_t *client = sock->data; |
207 | |
|
208 | 0 | h2o_timer_unlink(&client->super._timeout); |
209 | |
|
210 | 0 | if (err != NULL) { |
211 | 0 | on_error(client, h2o_httpclient_error_io); |
212 | 0 | return; |
213 | 0 | } |
214 | 0 | uint64_t size = sock->bytes_read - client->_socket_bytes_processed; |
215 | 0 | client->_socket_bytes_processed = sock->bytes_read; |
216 | |
|
217 | 0 | client->super.bytes_read.body += size; |
218 | 0 | client->super.bytes_read.total += size; |
219 | |
|
220 | 0 | if (size != 0 || client->_body_decoder.content_length.bytesleft == 0) { |
221 | 0 | int ret; |
222 | 0 | if (client->_body_decoder.content_length.bytesleft <= size) { |
223 | 0 | if (client->_body_decoder.content_length.bytesleft < size) { |
224 | | /* remove the trailing garbage from buf, and disable keepalive */ |
225 | 0 | client->sock->input->size -= size - client->_body_decoder.content_length.bytesleft; |
226 | 0 | client->_do_keepalive = 0; |
227 | 0 | } |
228 | 0 | client->_body_decoder.content_length.bytesleft = 0; |
229 | 0 | client->state.res = STREAM_STATE_CLOSED; |
230 | 0 | client->super.timings.response_end_at = h2o_gettimeofday(client->super.ctx->loop); |
231 | 0 | } else { |
232 | 0 | client->_body_decoder.content_length.bytesleft -= size; |
233 | 0 | } |
234 | 0 | ret = call_on_body(client, client->state.res == STREAM_STATE_CLOSED ? h2o_httpclient_error_is_eos : NULL); |
235 | 0 | if (client->state.res == STREAM_STATE_CLOSED) { |
236 | 0 | close_response(client); |
237 | 0 | return; |
238 | 0 | } else if (ret != 0) { |
239 | 0 | client->_do_keepalive = 0; |
240 | 0 | close_client(client); |
241 | 0 | return; |
242 | 0 | } |
243 | 0 | } |
244 | | |
245 | 0 | #if USE_PIPE_READER |
246 | 0 | if (client->pipe_reader.on_body_piped != NULL) { |
247 | 0 | h2o_socket_dont_read(client->sock, 1); |
248 | 0 | client->reader = on_body_to_pipe; |
249 | 0 | } |
250 | 0 | #endif |
251 | 0 | update_read_state(client); |
252 | 0 | } |
253 | | |
254 | | void on_body_to_pipe(h2o_socket_t *_sock, const char *err) |
255 | 0 | { |
256 | 0 | #if USE_PIPE_READER |
257 | 0 | struct st_h2o_http1client_t *client = _sock->data; |
258 | |
|
259 | 0 | h2o_timer_unlink(&client->super._timeout); |
260 | 0 | h2o_socket_read_stop(client->sock); |
261 | |
|
262 | 0 | if (err != NULL) { |
263 | 0 | on_error(client, h2o_httpclient_error_io); |
264 | 0 | return; |
265 | 0 | } |
266 | | |
267 | 0 | ssize_t bytes_read; |
268 | 0 | while ((bytes_read = splice(h2o_socket_get_fd(client->sock), NULL, client->pipe_reader.fd, NULL, |
269 | 0 | client->_body_decoder.content_length.bytesleft, SPLICE_F_NONBLOCK)) == -1 && |
270 | 0 | errno == EINTR) |
271 | 0 | ; |
272 | 0 | if (bytes_read == -1 && errno == EAGAIN) { |
273 | 0 | update_read_state(client); |
274 | 0 | return; |
275 | 0 | } |
276 | 0 | if (bytes_read <= 0) { |
277 | 0 | on_error(client, h2o_httpclient_error_io); |
278 | 0 | return; |
279 | 0 | } |
280 | | |
281 | 0 | client->_socket_bytes_processed += bytes_read; |
282 | 0 | client->sock->bytes_read += bytes_read; |
283 | 0 | client->super.bytes_read.body += bytes_read; |
284 | 0 | client->super.bytes_read.total += bytes_read; |
285 | |
|
286 | 0 | client->_body_decoder.content_length.bytesleft -= bytes_read; |
287 | 0 | if (client->_body_decoder.content_length.bytesleft == 0) { |
288 | 0 | client->state.res = STREAM_STATE_CLOSED; |
289 | 0 | client->super.timings.response_end_at = h2o_gettimeofday(client->super.ctx->loop); |
290 | 0 | h2o_socket_dont_read(client->sock, 0); |
291 | 0 | } |
292 | |
|
293 | 0 | int ret = call_on_body(client, client->state.res == STREAM_STATE_CLOSED ? h2o_httpclient_error_is_eos : NULL); |
294 | |
|
295 | 0 | if (client->state.res == STREAM_STATE_CLOSED) { |
296 | 0 | close_response(client); |
297 | 0 | } else if (ret != 0) { |
298 | 0 | client->_do_keepalive = 0; |
299 | 0 | close_client(client); |
300 | 0 | } |
301 | | #else |
302 | | h2o_fatal("%s cannot be called", __FUNCTION__); |
303 | | #endif |
304 | 0 | } |
305 | | |
306 | | static void on_body_chunked(h2o_socket_t *sock, const char *err) |
307 | 0 | { |
308 | 0 | struct st_h2o_http1client_t *client = sock->data; |
309 | 0 | h2o_buffer_t *inbuf; |
310 | |
|
311 | 0 | h2o_timer_unlink(&client->super._timeout); |
312 | |
|
313 | 0 | if (err != NULL) { |
314 | 0 | if (err == h2o_socket_error_closed && !phr_decode_chunked_is_in_data(&client->_body_decoder.chunked.decoder) && |
315 | 0 | client->_seen_at_least_one_chunk) { |
316 | | /* |
317 | | * if the peer closed after a full chunk, treat this |
318 | | * as if the transfer had complete, browsers appear to ignore |
319 | | * a missing 0\r\n chunk |
320 | | */ |
321 | 0 | client->_do_keepalive = 0; |
322 | 0 | client->state.res = STREAM_STATE_CLOSED; |
323 | 0 | client->super.timings.response_end_at = h2o_gettimeofday(client->super.ctx->loop); |
324 | 0 | call_on_body(client, h2o_httpclient_error_is_eos); |
325 | 0 | close_response(client); |
326 | 0 | } else { |
327 | 0 | on_error(client, h2o_httpclient_error_io); |
328 | 0 | } |
329 | 0 | return; |
330 | 0 | } |
331 | 0 | uint64_t size = sock->bytes_read - client->_socket_bytes_processed; |
332 | 0 | client->_socket_bytes_processed = sock->bytes_read; |
333 | |
|
334 | 0 | client->super.bytes_read.body += size; |
335 | 0 | client->super.bytes_read.total += size; |
336 | |
|
337 | 0 | inbuf = client->sock->input; |
338 | 0 | if (size != 0) { |
339 | 0 | const char *errstr; |
340 | 0 | int cb_ret; |
341 | 0 | size_t newsz = size; |
342 | |
|
343 | 0 | switch (phr_decode_chunked(&client->_body_decoder.chunked.decoder, inbuf->bytes + inbuf->size - newsz, &newsz)) { |
344 | 0 | case -1: /* error */ |
345 | 0 | newsz = size; |
346 | 0 | client->_do_keepalive = 0; |
347 | 0 | errstr = h2o_httpclient_error_http1_parse_failed; |
348 | 0 | break; |
349 | 0 | case -2: /* incomplete */ |
350 | 0 | errstr = NULL; |
351 | 0 | break; |
352 | 0 | default: /* complete, with garbage on tail; should disable keepalive */ |
353 | 0 | client->_do_keepalive = 0; |
354 | | /* fallthru */ |
355 | 0 | case 0: /* complete */ |
356 | 0 | client->state.res = STREAM_STATE_CLOSED; |
357 | 0 | errstr = h2o_httpclient_error_is_eos; |
358 | 0 | client->super.timings.response_end_at = h2o_gettimeofday(client->super.ctx->loop); |
359 | 0 | break; |
360 | 0 | } |
361 | 0 | inbuf->size -= size - newsz; |
362 | 0 | if (inbuf->size > 0) |
363 | 0 | client->_seen_at_least_one_chunk = 1; |
364 | 0 | cb_ret = call_on_body(client, errstr); |
365 | 0 | if (client->state.res == STREAM_STATE_CLOSED) { |
366 | 0 | close_response(client); |
367 | 0 | return; |
368 | 0 | } else if (errstr != NULL) { |
369 | 0 | close_client(client); |
370 | 0 | return; |
371 | 0 | } else if (cb_ret != 0) { |
372 | 0 | client->_do_keepalive = 0; |
373 | 0 | close_client(client); |
374 | 0 | return; |
375 | 0 | } |
376 | 0 | update_read_state(client); |
377 | 0 | } |
378 | 0 | } |
379 | | |
380 | | static void on_head_timeout(h2o_timer_t *entry) |
381 | 0 | { |
382 | 0 | struct st_h2o_http1client_t *client = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http1client_t, super._timeout, entry); |
383 | 0 | on_error(client, h2o_httpclient_error_io_timeout); |
384 | 0 | } |
385 | | |
386 | | static void on_head(h2o_socket_t *sock, const char *err) |
387 | 615 | { |
388 | 615 | struct st_h2o_http1client_t *client = sock->data; |
389 | 615 | int minor_version, version, http_status, rlen; |
390 | 615 | const char *msg; |
391 | 615 | #define MAX_HEADERS 100 |
392 | 615 | h2o_header_t *headers; |
393 | 615 | h2o_iovec_t *header_names; |
394 | 615 | size_t msg_len, num_headers, i; |
395 | 615 | h2o_socket_cb reader; |
396 | | |
397 | 615 | h2o_timer_unlink(&client->super._timeout); |
398 | | |
399 | 615 | if (err != NULL) { |
400 | 0 | on_error(client, h2o_httpclient_error_io); |
401 | 0 | return; |
402 | 0 | } |
403 | | |
404 | | /* revert max read size to 1MB now that we have received the first chunk, presumably carrying all the response headers */ |
405 | 615 | #if USE_PIPE_READER |
406 | 615 | if (client->_app_prefers_pipe_reader) |
407 | 0 | h2o_evloop_socket_set_max_read_size(client->sock, h2o_evloop_socket_max_read_size); |
408 | 615 | #endif |
409 | | |
410 | 615 | client->super._timeout.cb = on_head_timeout; |
411 | | |
412 | 615 | headers = h2o_mem_alloc_pool(client->super.pool, *headers, MAX_HEADERS); |
413 | 615 | header_names = h2o_mem_alloc_pool(client->super.pool, *header_names, MAX_HEADERS); |
414 | | |
415 | | /* continue parsing the responses until we see a final one */ |
416 | 615 | while (1) { |
417 | | /* parse response */ |
418 | 615 | struct phr_header src_headers[MAX_HEADERS]; |
419 | 615 | num_headers = MAX_HEADERS; |
420 | 615 | rlen = phr_parse_response(sock->input->bytes, sock->input->size, &minor_version, &http_status, &msg, &msg_len, src_headers, |
421 | 615 | &num_headers, 0); |
422 | 615 | switch (rlen) { |
423 | 0 | case -1: /* error */ |
424 | 0 | on_error(client, h2o_httpclient_error_http1_parse_failed); |
425 | 0 | return; |
426 | 0 | case -2: /* incomplete */ |
427 | 0 | h2o_timer_link(client->super.ctx->loop, client->super.ctx->io_timeout, &client->super._timeout); |
428 | 0 | return; |
429 | 615 | } |
430 | | |
431 | 615 | client->super.bytes_read.header += rlen; |
432 | 615 | client->super.bytes_read.total += rlen; |
433 | | |
434 | 615 | version = 0x100 | (minor_version != 0); |
435 | | |
436 | | /* fill-in the headers */ |
437 | 1.23k | for (i = 0; i != num_headers; ++i) { |
438 | 615 | if (src_headers[i].name_len == 0) { |
439 | | /* reject multiline header */ |
440 | 0 | on_error(client, h2o_httpclient_error_http1_line_folding); |
441 | 0 | return; |
442 | 0 | } |
443 | 615 | const h2o_token_t *token; |
444 | 615 | char *orig_name = h2o_strdup(client->super.pool, src_headers[i].name, src_headers[i].name_len).base; |
445 | 615 | h2o_strtolower((char *)src_headers[i].name, src_headers[i].name_len); |
446 | 615 | token = h2o_lookup_token(src_headers[i].name, src_headers[i].name_len); |
447 | 615 | if (token != NULL) { |
448 | 615 | headers[i].name = (h2o_iovec_t *)&token->buf; |
449 | 615 | } else { |
450 | 0 | header_names[i] = h2o_iovec_init(src_headers[i].name, src_headers[i].name_len); |
451 | 0 | headers[i].name = &header_names[i]; |
452 | 0 | } |
453 | 615 | headers[i].value = h2o_iovec_init(src_headers[i].value, src_headers[i].value_len); |
454 | 615 | headers[i].orig_name = orig_name; |
455 | 615 | headers[i].flags = (h2o_header_flags_t){0}; |
456 | 615 | } |
457 | | |
458 | 615 | if (http_status == 101) { |
459 | 0 | if (client->_send_own_expect) { |
460 | | /* expect: 100-continue is incompatible CONNECT or upgrade (when trying to establish a tunnel */ |
461 | 0 | on_error(client, h2o_httpclient_error_unexpected_101); |
462 | 0 | return; |
463 | 0 | } |
464 | 0 | break; |
465 | 615 | } else if (http_status == 100 || http_status >= 200) { |
466 | | /* When request body has been withheld and a 100 or a final response has been received, start sending the request body, |
467 | | * see: https://github.com/h2o/h2o/pull/3316#discussion_r1456859634. */ |
468 | 615 | if (client->_send_own_expect) { |
469 | 0 | client->_send_own_expect = 0; |
470 | 0 | req_body_send(client); |
471 | 0 | } |
472 | 615 | if (http_status >= 200) |
473 | 615 | break; |
474 | 615 | } |
475 | 0 | assert(http_status <= 199); |
476 | 0 | if (client->super.informational_cb != NULL && |
477 | 0 | client->super.informational_cb(&client->super, version, http_status, h2o_iovec_init(msg, msg_len), headers, |
478 | 0 | num_headers) != 0) { |
479 | 0 | close_client(client); |
480 | 0 | return; |
481 | 0 | } |
482 | | |
483 | 0 | h2o_buffer_consume(&client->sock->input, rlen); |
484 | 0 | if (client->sock->input->size == 0) { |
485 | 0 | if (!h2o_timer_is_linked(&client->super._timeout)) { |
486 | 0 | h2o_timer_link(client->super.ctx->loop, client->super.ctx->io_timeout, &client->super._timeout); |
487 | 0 | } |
488 | 0 | return; |
489 | 0 | } |
490 | 0 | } |
491 | | |
492 | | /* recognize hop-by-hop response headers */ |
493 | 615 | reader = on_body_until_close; |
494 | 615 | if (!h2o_httpclient__tunnel_is_ready(&client->super, http_status, version)) { |
495 | 615 | client->_do_keepalive = minor_version >= 1; |
496 | 1.23k | for (i = 0; i != num_headers; ++i) { |
497 | 615 | if (headers[i].name == &H2O_TOKEN_CONNECTION->buf) { |
498 | 615 | if (h2o_contains_token(headers[i].value.base, headers[i].value.len, H2O_STRLIT("keep-alive"), ',')) { |
499 | 0 | client->_do_keepalive = 1; |
500 | 615 | } else { |
501 | 615 | client->_do_keepalive = 0; |
502 | 615 | } |
503 | 615 | } else if (headers[i].name == &H2O_TOKEN_TRANSFER_ENCODING->buf) { |
504 | 0 | if (h2o_memis(headers[i].value.base, headers[i].value.len, H2O_STRLIT("chunked"))) { |
505 | | /* precond: _body_decoder.chunked is zero-filled */ |
506 | 0 | client->_body_decoder.chunked.decoder.consume_trailer = 1; |
507 | 0 | reader = on_body_chunked; |
508 | 0 | } else if (h2o_memis(headers[i].value.base, headers[i].value.len, H2O_STRLIT("identity"))) { |
509 | | /* continue */ |
510 | 0 | } else { |
511 | 0 | on_error(client, h2o_httpclient_error_http1_unexpected_transfer_encoding); |
512 | 0 | return; |
513 | 0 | } |
514 | 0 | } else if (headers[i].name == &H2O_TOKEN_CONTENT_LENGTH->buf) { |
515 | 0 | if ((client->_body_decoder.content_length.bytesleft = h2o_strtosize(headers[i].value.base, headers[i].value.len)) == |
516 | 0 | SIZE_MAX) { |
517 | 0 | on_error(client, h2o_httpclient_error_invalid_content_length); |
518 | 0 | return; |
519 | 0 | } |
520 | 0 | if (reader != on_body_chunked) |
521 | 0 | reader = on_body_content_length; |
522 | 0 | } |
523 | 615 | } |
524 | 615 | } |
525 | | |
526 | 615 | client->state.res = STREAM_STATE_BODY; |
527 | 615 | client->super.timings.response_start_at = h2o_gettimeofday(client->super.ctx->loop); |
528 | | |
529 | | /* RFC 2616 4.4 */ |
530 | 615 | if (client->_method_is_head || http_status == 204 || http_status == 304) { |
531 | 116 | client->state.res = STREAM_STATE_CLOSED; |
532 | 116 | client->super.timings.response_end_at = h2o_gettimeofday(client->super.ctx->loop); |
533 | 499 | } else { |
534 | | /* close the connection if impossible to determine the end of the response (RFC 7230 3.3.3) */ |
535 | 499 | if (reader == on_body_until_close) |
536 | 499 | client->_do_keepalive = 0; |
537 | 499 | } |
538 | | |
539 | 615 | h2o_httpclient_on_head_t on_head = { |
540 | 615 | .version = version, |
541 | 615 | .status = http_status, |
542 | 615 | .msg = h2o_iovec_init(msg, msg_len), |
543 | 615 | .headers = headers, |
544 | 615 | .num_headers = num_headers, |
545 | 615 | .header_requires_dup = 1, |
546 | 615 | }; |
547 | 615 | #if USE_PIPE_READER |
548 | | /* If there is no less than 64KB of data to be read from the socket, offer the application the opportunity to use pipe for |
549 | | * transferring the content zero-copy. As switching to pipe involves the cost of creating a pipe (and disposing it when the |
550 | | * request is complete), we adopt this margin of 64KB, which offers clear improvement (5%) on 9th-gen Intel Core. */ |
551 | 615 | if (client->_app_prefers_pipe_reader && reader == on_body_content_length && |
552 | 615 | client->sock->input->size + 65536 <= client->_body_decoder.content_length.bytesleft && |
553 | 615 | client->sock->ssl == NULL) |
554 | 0 | on_head.pipe_reader = &client->pipe_reader; |
555 | 615 | #endif |
556 | | |
557 | | /* call the callback */ |
558 | 615 | client->super._cb.on_body = |
559 | 615 | call_on_head(client, client->state.res == STREAM_STATE_CLOSED ? h2o_httpclient_error_is_eos : NULL, &on_head); |
560 | | |
561 | 615 | if (client->state.res == STREAM_STATE_CLOSED) { |
562 | 116 | close_response(client); |
563 | 116 | return; |
564 | 499 | } else if (client->super._cb.on_body == NULL) { |
565 | 0 | client->_do_keepalive = 0; |
566 | 0 | close_client(client); |
567 | 0 | return; |
568 | 0 | } |
569 | | |
570 | 499 | h2o_buffer_consume(&sock->input, rlen); |
571 | 499 | client->_socket_bytes_processed = client->sock->bytes_read - client->sock->input->size; |
572 | | |
573 | 499 | client->super._timeout.cb = on_body_timeout; |
574 | 499 | h2o_socket_read_start(sock, reader); |
575 | 499 | reader(client->sock, 0); |
576 | | |
577 | 499 | #undef MAX_HEADERS |
578 | 499 | } |
579 | | |
580 | | static void on_head_first_byte_timeout(h2o_timer_t *entry) |
581 | 0 | { |
582 | 0 | struct st_h2o_http1client_t *client = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http1client_t, super._timeout, entry); |
583 | 0 | on_error(client, h2o_httpclient_error_first_byte_timeout); |
584 | 0 | } |
585 | | |
586 | | static void on_whole_request_sent(h2o_socket_t *sock, const char *err) |
587 | 617 | { |
588 | 617 | struct st_h2o_http1client_t *client = sock->data; |
589 | | |
590 | 617 | h2o_timer_unlink(&client->super._timeout); |
591 | | |
592 | 617 | if (err != NULL) { |
593 | 18 | on_error(client, h2o_httpclient_error_io); |
594 | 18 | return; |
595 | 18 | } |
596 | | |
597 | 599 | client->state.req = STREAM_STATE_CLOSED; |
598 | 599 | client->super.timings.request_end_at = h2o_gettimeofday(client->super.ctx->loop); |
599 | | |
600 | 599 | if (client->super.upgrade_to != NULL) { |
601 | | /* TODO use shutdown(2) to signal the peer that our send side has been closed, but continue reading on the receive side. */ |
602 | 0 | on_error(client, client->state.res < STREAM_STATE_BODY ? h2o_httpclient_error_io : h2o_httpclient_error_is_eos); |
603 | 599 | } else { |
604 | 599 | switch (client->state.res) { |
605 | 599 | case STREAM_STATE_HEAD: |
606 | 599 | client->super._timeout.cb = on_head_first_byte_timeout; |
607 | 599 | h2o_timer_link(client->super.ctx->loop, client->super.ctx->first_byte_timeout, &client->super._timeout); |
608 | 599 | break; |
609 | 0 | case STREAM_STATE_BODY: |
610 | 0 | break; |
611 | 0 | case STREAM_STATE_CLOSED: |
612 | 0 | close_client(client); |
613 | 0 | break; |
614 | 599 | } |
615 | 599 | } |
616 | 599 | } |
617 | | |
618 | | static void on_header_sent_wait_100(h2o_socket_t *sock, const char *err) |
619 | 0 | { |
620 | 0 | struct st_h2o_http1client_t *client = sock->data; |
621 | |
|
622 | 0 | h2o_timer_unlink(&client->super._timeout); |
623 | |
|
624 | 0 | if (err != NULL) { |
625 | 0 | on_error(client, h2o_httpclient_error_io); |
626 | 0 | return; |
627 | 0 | } |
628 | | |
629 | 0 | if (client->state.res == STREAM_STATE_HEAD) { |
630 | 0 | client->super._timeout.cb = on_head_first_byte_timeout; |
631 | 0 | h2o_timer_link(client->super.ctx->loop, client->super.ctx->first_byte_timeout, &client->super._timeout); |
632 | 0 | } |
633 | 0 | } |
634 | | |
635 | | static void req_body_send_complete(h2o_socket_t *sock, const char *err) |
636 | 0 | { |
637 | 0 | struct st_h2o_http1client_t *client = sock->data; |
638 | |
|
639 | 0 | h2o_buffer_consume(&client->body_buf.buf, client->body_buf.buf->size); |
640 | |
|
641 | 0 | if (err != NULL) { |
642 | 0 | on_whole_request_sent(client->sock, err); |
643 | 0 | return; |
644 | 0 | } |
645 | | |
646 | 0 | int is_end_stream = client->body_buf.is_end_stream; |
647 | |
|
648 | 0 | if (client->proceed_req != NULL) { |
649 | 0 | call_proceed_req(client, NULL); |
650 | 0 | } |
651 | |
|
652 | 0 | if (is_end_stream) |
653 | 0 | on_whole_request_sent(client->sock, NULL); |
654 | 0 | } |
655 | | |
656 | | /** |
657 | | * Encodes data. `bufs` must have at least 4 elements of space. |
658 | | */ |
659 | | static size_t req_body_send_prepare(struct st_h2o_http1client_t *client, h2o_iovec_t *bufs, size_t *bytes) |
660 | 0 | { |
661 | 0 | size_t bufcnt = 0; |
662 | 0 | *bytes = 0; |
663 | |
|
664 | 0 | if (client->_is_chunked) { |
665 | 0 | if (client->body_buf.buf->size != 0) { |
666 | | /* build chunk header */ |
667 | 0 | bufs[bufcnt].base = client->_chunk_len_str; |
668 | 0 | bufs[bufcnt].len = |
669 | 0 | snprintf(client->_chunk_len_str, sizeof(client->_chunk_len_str), "%zx\r\n", client->body_buf.buf->size); |
670 | 0 | *bytes += bufs[bufcnt].len; |
671 | 0 | ++bufcnt; |
672 | | /* append chunk body */ |
673 | 0 | bufs[bufcnt++] = h2o_iovec_init(client->body_buf.buf->bytes, client->body_buf.buf->size); |
674 | 0 | *bytes += client->body_buf.buf->size; |
675 | | /* append CRLF */ |
676 | 0 | bufs[bufcnt++] = h2o_iovec_init("\r\n", 2); |
677 | 0 | *bytes += 2; |
678 | 0 | } |
679 | 0 | if (client->body_buf.is_end_stream) { |
680 | 0 | static const h2o_iovec_t terminator = {H2O_STRLIT("0\r\n\r\n")}; |
681 | 0 | bufs[bufcnt++] = terminator; |
682 | 0 | *bytes += terminator.len; |
683 | 0 | } |
684 | 0 | } else if (client->body_buf.buf->size != 0) { |
685 | 0 | bufs[bufcnt++] = h2o_iovec_init(client->body_buf.buf->bytes, client->body_buf.buf->size); |
686 | 0 | *bytes += client->body_buf.buf->size; |
687 | 0 | } |
688 | |
|
689 | 0 | return bufcnt; |
690 | 0 | } |
691 | | |
692 | | static void req_body_send(struct st_h2o_http1client_t *client) |
693 | 0 | { |
694 | 0 | h2o_iovec_t bufs[4]; |
695 | 0 | size_t bytes, bufcnt = req_body_send_prepare(client, bufs, &bytes); |
696 | |
|
697 | 0 | h2o_timer_unlink(&client->super._timeout); |
698 | |
|
699 | 0 | h2o_socket_write(client->sock, bufs, bufcnt, req_body_send_complete); |
700 | 0 | client->super.bytes_written.body += bytes; |
701 | 0 | client->super.bytes_written.total += bytes; |
702 | |
|
703 | 0 | h2o_timer_link(client->super.ctx->loop, client->super.ctx->io_timeout, &client->super._timeout); |
704 | 0 | } |
705 | | |
706 | | static int do_write_req(h2o_httpclient_t *_client, h2o_iovec_t chunk, int is_end_stream) |
707 | 0 | { |
708 | 0 | struct st_h2o_http1client_t *client = (struct st_h2o_http1client_t *)_client; |
709 | |
|
710 | 0 | assert(chunk.len != 0 || is_end_stream); |
711 | 0 | assert(!h2o_socket_is_writing(client->sock)); |
712 | 0 | assert(client->body_buf.buf->size == 0); |
713 | | |
714 | | /* store given content to buffer */ |
715 | 0 | if (chunk.len != 0) { |
716 | 0 | if (!h2o_buffer_try_append(&client->body_buf.buf, chunk.base, chunk.len)) |
717 | 0 | return -1; |
718 | 0 | } |
719 | 0 | client->body_buf.is_end_stream = is_end_stream; |
720 | | |
721 | | /* check if the connection has to be closed for correct framing */ |
722 | 0 | if (client->state.res == STREAM_STATE_CLOSED) |
723 | 0 | client->_do_keepalive = 0; |
724 | |
|
725 | 0 | req_body_send(client); |
726 | |
|
727 | 0 | return 0; |
728 | 0 | } |
729 | | |
730 | | static void on_send_timeout(h2o_timer_t *entry) |
731 | 0 | { |
732 | 0 | struct st_h2o_http1client_t *client = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http1client_t, super._timeout, entry); |
733 | 0 | on_error(client, h2o_httpclient_error_io_timeout); |
734 | 0 | } |
735 | | |
736 | | static h2o_iovec_t build_request(struct st_h2o_http1client_t *client, h2o_iovec_t method, const h2o_url_t *url, |
737 | | h2o_iovec_t connection, const h2o_header_t *headers, size_t num_headers) |
738 | 617 | { |
739 | 617 | h2o_iovec_t buf; |
740 | 617 | size_t offset = 0; |
741 | | |
742 | 617 | buf.len = method.len + url->path.len + url->authority.len + 512; |
743 | 617 | buf.base = h2o_mem_alloc_pool(client->super.pool, char, buf.len); |
744 | | |
745 | 617 | #define RESERVE(sz) \ |
746 | 272k | do { \ |
747 | 272k | size_t required = offset + sz + 4 /* for "\r\n\r\n" */; \ |
748 | 272k | if (required > buf.len) { \ |
749 | 1.45k | do { \ |
750 | 1.45k | buf.len *= 2; \ |
751 | 1.45k | } while (required > buf.len); \ |
752 | 1.39k | char *newp = h2o_mem_alloc_pool(client->super.pool, char, buf.len); \ |
753 | 1.39k | memcpy(newp, buf.base, offset); \ |
754 | 1.39k | buf.base = newp; \ |
755 | 1.39k | } \ |
756 | 272k | } while (0) |
757 | 617 | #define APPEND(s, l) \ |
758 | 548k | do { \ |
759 | 548k | h2o_memcpy(buf.base + offset, (s), (l)); \ |
760 | 548k | offset += (l); \ |
761 | 548k | } while (0) |
762 | 1.23k | #define APPEND_STRLIT(lit) APPEND((lit), sizeof(lit) - 1) |
763 | 617 | #define APPEND_HEADER(h) \ |
764 | 272k | do { \ |
765 | 272k | RESERVE((h)->name->len + (h)->value.len + 4); \ |
766 | 272k | APPEND((h)->orig_name ? (h)->orig_name : (h)->name->base, (h)->name->len); \ |
767 | 272k | buf.base[offset++] = ':'; \ |
768 | 272k | buf.base[offset++] = ' '; \ |
769 | 272k | APPEND((h)->value.base, (h)->value.len); \ |
770 | 272k | buf.base[offset++] = '\r'; \ |
771 | 272k | buf.base[offset++] = '\n'; \ |
772 | 272k | } while (0) |
773 | | |
774 | 617 | APPEND(method.base, method.len); |
775 | 617 | buf.base[offset++] = ' '; |
776 | 617 | if (client->super.upgrade_to == h2o_httpclient_upgrade_to_connect) { |
777 | 0 | if (h2o_memis(method.base, method.len, H2O_STRLIT("CONNECT-UDP"))) { |
778 | 0 | APPEND_STRLIT("masque://"); |
779 | 0 | APPEND(url->authority.base, url->authority.len); |
780 | 0 | APPEND_STRLIT("/"); |
781 | 0 | } else { |
782 | 0 | APPEND(url->authority.base, url->authority.len); |
783 | 0 | } |
784 | 617 | } else { |
785 | 617 | APPEND(url->path.base, url->path.len); |
786 | 617 | } |
787 | 617 | APPEND_STRLIT(" HTTP/1.1\r\nhost: "); |
788 | 617 | APPEND(url->authority.base, url->authority.len); |
789 | 617 | buf.base[offset++] = '\r'; |
790 | 617 | buf.base[offset++] = '\n'; |
791 | 617 | assert(offset <= buf.len); |
792 | | |
793 | | /* append supplied connection header, or "connection: upgrade" and upgrade header when request an upgrade */ |
794 | 617 | if (client->super.upgrade_to != NULL && client->super.upgrade_to != h2o_httpclient_upgrade_to_connect) { |
795 | 0 | h2o_header_t c = {&H2O_TOKEN_CONNECTION->buf, NULL, h2o_iovec_init(H2O_STRLIT("upgrade"))}, |
796 | 0 | u = {&H2O_TOKEN_UPGRADE->buf, NULL, |
797 | 0 | h2o_iovec_init(client->super.upgrade_to, strlen(client->super.upgrade_to))}; |
798 | 0 | APPEND_HEADER(&c); |
799 | 0 | APPEND_HEADER(&u); |
800 | 617 | } else if (connection.base != NULL) { |
801 | 617 | h2o_header_t h = {&H2O_TOKEN_CONNECTION->buf, NULL, connection}; |
802 | 617 | APPEND_HEADER(&h); |
803 | 617 | } |
804 | | |
805 | 617 | if (client->_send_own_expect) { |
806 | 0 | h2o_header_t h = {&H2O_TOKEN_EXPECT->buf, NULL, h2o_iovec_init(H2O_STRLIT("100-continue"))}; |
807 | 0 | APPEND_HEADER(&h); |
808 | 0 | } |
809 | | |
810 | 617 | if (num_headers != 0) { |
811 | 272k | for (const h2o_header_t *h = headers, *h_end = h + num_headers; h != h_end; ++h) |
812 | 271k | APPEND_HEADER(h); |
813 | 617 | } |
814 | | |
815 | 617 | APPEND_STRLIT("\r\n"); |
816 | | |
817 | | /* set the length */ |
818 | 617 | assert(offset <= buf.len); |
819 | 617 | buf.len = offset; |
820 | | |
821 | 617 | return buf; |
822 | | |
823 | 617 | #undef RESERVE |
824 | 617 | #undef APPEND |
825 | 617 | #undef APPEND_STRLIT |
826 | 617 | } |
827 | | |
828 | | static void start_request(struct st_h2o_http1client_t *client, h2o_iovec_t method, const h2o_url_t *url, |
829 | | const h2o_header_t *headers, size_t num_headers, h2o_iovec_t body, |
830 | | const h2o_httpclient_properties_t *props) |
831 | 617 | { |
832 | 617 | h2o_iovec_t reqbufs[6]; /* 6 should be the maximum possible elements used */ |
833 | 617 | size_t reqbufcnt = 0; |
834 | 617 | if (props->proxy_protocol->base != NULL) |
835 | 0 | reqbufs[reqbufcnt++] = *props->proxy_protocol; |
836 | | |
837 | 617 | if (props->send_own_expect && (client->proceed_req != NULL || body.len != 0) && client->super.upgrade_to == NULL) |
838 | 0 | client->_send_own_expect = 1; /* this must be set before calling build_request */ |
839 | | |
840 | 617 | h2o_iovec_t header = build_request(client, method, url, *props->connection_header, headers, num_headers); |
841 | 617 | reqbufs[reqbufcnt++] = header; |
842 | 617 | client->super.bytes_written.header = header.len; |
843 | | |
844 | 617 | client->_is_chunked = *props->chunked; |
845 | 617 | client->_method_is_head = h2o_memis(method.base, method.len, H2O_STRLIT("HEAD")); |
846 | | |
847 | 617 | assert(PTLS_ELEMENTSOF(reqbufs) - reqbufcnt >= 4); /* req_body_send_prepare could write to 4 additional elements */ |
848 | 617 | if (client->proceed_req != NULL) { |
849 | 0 | h2o_buffer_init(&client->body_buf.buf, &h2o_socket_buffer_prototype); |
850 | 0 | if (body.len != 0 && !h2o_buffer_try_append(&client->body_buf.buf, body.base, body.len)) { |
851 | 0 | on_whole_request_sent(client->sock, h2o_httpclient_error_internal); |
852 | 0 | return; |
853 | 0 | } |
854 | 0 | if (client->_send_own_expect) { |
855 | 0 | h2o_socket_write(client->sock, reqbufs, reqbufcnt, on_header_sent_wait_100); |
856 | 0 | } else { |
857 | 0 | size_t bytes_written; |
858 | 0 | reqbufcnt += req_body_send_prepare(client, reqbufs + reqbufcnt, &bytes_written); |
859 | 0 | client->super.bytes_written.body = bytes_written; |
860 | 0 | h2o_socket_write(client->sock, reqbufs, reqbufcnt, req_body_send_complete); |
861 | 0 | } |
862 | 617 | } else if (body.len != 0) { |
863 | 41 | assert(!client->_is_chunked); |
864 | 41 | if (client->_send_own_expect) { |
865 | 0 | h2o_buffer_init(&client->body_buf.buf, &h2o_socket_buffer_prototype); |
866 | 0 | client->body_buf.is_end_stream = 1; |
867 | 0 | if (!h2o_buffer_try_append(&client->body_buf.buf, body.base, body.len)) { |
868 | 0 | on_whole_request_sent(client->sock, h2o_httpclient_error_internal); |
869 | 0 | return; |
870 | 0 | } |
871 | 0 | h2o_socket_write(client->sock, reqbufs, reqbufcnt, on_header_sent_wait_100); |
872 | 41 | } else { |
873 | 41 | reqbufs[reqbufcnt++] = body; |
874 | 41 | client->super.bytes_written.body = body.len; |
875 | 41 | h2o_socket_write(client->sock, reqbufs, reqbufcnt, on_whole_request_sent); |
876 | 41 | } |
877 | 576 | } else { |
878 | 576 | assert(!client->_is_chunked); |
879 | 576 | h2o_socket_write(client->sock, reqbufs, reqbufcnt, on_whole_request_sent); |
880 | 576 | } |
881 | 617 | client->super.bytes_written.total = client->sock->bytes_written; |
882 | | |
883 | | /* Even all data highly likely has been written into TCP sendbuf, it is our practice to assume the socket write operation is |
884 | | * asynchronous and link the timer. */ |
885 | 617 | client->super._timeout.cb = on_send_timeout; |
886 | 617 | h2o_timer_link(client->super.ctx->loop, client->super.ctx->io_timeout, &client->super._timeout); |
887 | | |
888 | 617 | client->state.req = STREAM_STATE_BODY; |
889 | 617 | client->super.timings.request_begin_at = h2o_gettimeofday(client->super.ctx->loop); |
890 | | |
891 | | /* If there's possibility of using a pipe for forwarding the content, reduce maximum read size before fetching headers. The |
892 | | * intent here is to not do a full-sized read of 1MB. 16KB has been chosen so that all HTTP response headers would be available, |
893 | | * and that an almost full-sized HTTP/2 frame / TLS record can be generated for the first chunk of data that we pass through |
894 | | * memory. */ |
895 | 617 | #if USE_PIPE_READER |
896 | 617 | if (client->_app_prefers_pipe_reader && h2o_evloop_socket_max_read_size > 16384) |
897 | 0 | h2o_evloop_socket_set_max_read_size(client->sock, 16384); |
898 | 617 | #endif |
899 | | |
900 | 617 | h2o_socket_read_start(client->sock, on_head); |
901 | 617 | } |
902 | | |
903 | | static void on_connection_ready(struct st_h2o_http1client_t *client) |
904 | 617 | { |
905 | 617 | h2o_iovec_t proxy_protocol = h2o_iovec_init(NULL, 0); |
906 | 617 | int chunked = 0; |
907 | 617 | h2o_iovec_t connection_header = h2o_iovec_init(NULL, 0); |
908 | 617 | h2o_httpclient_properties_t props = { |
909 | 617 | &proxy_protocol, |
910 | 617 | &chunked, |
911 | 617 | &connection_header, |
912 | 617 | }; |
913 | 617 | h2o_iovec_t method; |
914 | 617 | h2o_url_t url; |
915 | 617 | h2o_header_t *headers; |
916 | 617 | size_t num_headers; |
917 | 617 | h2o_iovec_t body; |
918 | | |
919 | 617 | client->super._cb.on_head = client->super._cb.on_connect(&client->super, NULL, &method, &url, (const h2o_header_t **)&headers, |
920 | 617 | &num_headers, &body, &client->proceed_req, &props, client->_origin); |
921 | 617 | client->_app_prefers_pipe_reader = props.prefer_pipe_reader; |
922 | | |
923 | 617 | if (client->super._cb.on_head == NULL) { |
924 | 0 | close_client(client); |
925 | 0 | return; |
926 | 0 | } |
927 | | |
928 | 617 | start_request(client, method, &url, headers, num_headers, body, &props); |
929 | 617 | } |
930 | | |
931 | | static void do_cancel(h2o_httpclient_t *_client) |
932 | 0 | { |
933 | 0 | struct st_h2o_http1client_t *client = (struct st_h2o_http1client_t *)_client; |
934 | 0 | client->_do_keepalive = 0; |
935 | 0 | close_client(client); |
936 | 0 | } |
937 | | |
938 | | void update_read_state(struct st_h2o_http1client_t *client) |
939 | 499 | { |
940 | | /* If pipe used, `client->reader` would have switched to `on_body_pipe` by the time this function is called for the first time. |
941 | | */ |
942 | 499 | assert((client->pipe_reader.on_body_piped != NULL) == (client->reader == on_body_to_pipe)); |
943 | | |
944 | 499 | if (client->reader == on_body_to_pipe) { |
945 | | /* When pipe is being used, resume read when consumption is notified from user. `h2o_socket_read_start` is invoked without |
946 | | * checking if we are already reading; this is because we want to make sure that the read callback replaced to the current |
947 | | * one. */ |
948 | 0 | h2o_socket_read_start(client->sock, client->reader); |
949 | 499 | } else { |
950 | | /* When buffer is used, start / stop reading based on the amount of data being buffered. */ |
951 | 499 | if ((*client->super.buf)->size >= client->super.ctx->max_buffer_size) { |
952 | 0 | if (h2o_socket_is_reading(client->sock)) { |
953 | 0 | client->reader = client->sock->_cb.read; |
954 | 0 | h2o_socket_read_stop(client->sock); |
955 | 0 | } |
956 | 499 | } else { |
957 | 499 | if (!h2o_socket_is_reading(client->sock)) |
958 | 0 | h2o_socket_read_start(client->sock, client->reader); |
959 | 499 | } |
960 | 499 | } |
961 | | |
962 | | /* arm or unarm i/o timeout depending on if we are reading */ |
963 | 499 | if (h2o_socket_is_reading(client->sock)) { |
964 | 499 | if (h2o_timer_is_linked(&client->super._timeout)) |
965 | 0 | h2o_timer_unlink(&client->super._timeout); |
966 | 499 | h2o_timer_link(client->super.ctx->loop, client->super.ctx->io_timeout, &client->super._timeout); |
967 | 499 | } else { |
968 | 0 | if (h2o_timer_is_linked(&client->super._timeout)) |
969 | 0 | h2o_timer_unlink(&client->super._timeout); |
970 | 0 | } |
971 | 499 | } |
972 | | |
973 | | static void do_update_window(struct st_h2o_httpclient_t *_client) |
974 | 0 | { |
975 | 0 | struct st_h2o_http1client_t *client = (void *)_client; |
976 | | |
977 | | /* When we are splicing to pipe, read synchronously. For prioritization logic to work correctly, it is important to provide |
978 | | * additional data synchronously in response to the invocation of `h2o_proceed_response`. When memory buffers are used, |
979 | | * lib/core/proxy.c uses a double buffering to prepare next chunk of data while a chunk of data is being fed to the HTTP |
980 | | * handlers via `h2o_sendvec`. But when using splice, the pipe is the only one buffer available. */ |
981 | 0 | if (client->reader == on_body_to_pipe) { |
982 | 0 | on_body_to_pipe(client->sock, NULL); |
983 | 0 | return; |
984 | 0 | } |
985 | | |
986 | 0 | update_read_state(client); |
987 | 0 | } |
988 | | |
989 | | static void do_get_conn_properties(h2o_httpclient_t *_client, h2o_httpclient_conn_properties_t *properties) |
990 | 617 | { |
991 | 617 | struct st_h2o_http1client_t *client = (void *)_client; |
992 | 617 | h2o_httpclient_set_conn_properties_of_socket(client->sock, properties); |
993 | 617 | } |
994 | | |
995 | | static void setup_client(struct st_h2o_http1client_t *client, h2o_socket_t *sock, h2o_url_t *origin) |
996 | 617 | { |
997 | 617 | memset(&client->sock, 0, sizeof(*client) - offsetof(struct st_h2o_http1client_t, sock)); |
998 | 617 | client->super.cancel = do_cancel; |
999 | 617 | client->super.get_conn_properties = do_get_conn_properties; |
1000 | 617 | client->super.update_window = do_update_window; |
1001 | 617 | client->super.write_req = do_write_req; |
1002 | 617 | client->super.buf = &sock->input; |
1003 | 617 | client->sock = sock; |
1004 | 617 | sock->data = client; |
1005 | 617 | client->_origin = origin; |
1006 | 617 | } |
1007 | | |
1008 | | void h2o_httpclient__h1_on_connect(h2o_httpclient_t *_client, h2o_socket_t *sock, h2o_url_t *origin) |
1009 | 617 | { |
1010 | 617 | struct st_h2o_http1client_t *client = (void *)_client; |
1011 | | |
1012 | 617 | assert(!h2o_timer_is_linked(&client->super._timeout)); |
1013 | | |
1014 | 617 | setup_client(client, sock, origin); |
1015 | 617 | on_connection_ready(client); |
1016 | 617 | } |
1017 | | |
1018 | | const size_t h2o_httpclient__h1_size = sizeof(struct st_h2o_http1client_t); |