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