/src/h2o/lib/common/http2client.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2018 Ichito Nagata, Fastly, Inc. |
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 <netdb.h> |
24 | | #include <netinet/in.h> |
25 | | #include <sys/socket.h> |
26 | | #include <sys/types.h> |
27 | | #include <sys/un.h> |
28 | | #include "khash.h" |
29 | | #include "h2o/hpack.h" |
30 | | #include "h2o/httpclient.h" |
31 | | #include "h2o/http2_common.h" |
32 | | |
33 | 0 | #define H2O_HTTP2_SETTINGS_CLIENT_CONNECTION_WINDOW_SIZE 16777216 |
34 | 0 | #define H2O_HTTP2_SETTINGS_CLIENT_HEADER_TABLE_SIZE 4096 |
35 | 0 | #define H2O_HTTP2_SETTINGS_CLIENT_MAX_FRAME_SIZE 16384 |
36 | | |
37 | | enum enum_h2o_http2client_stream_state { |
38 | | STREAM_STATE_HEAD, |
39 | | STREAM_STATE_BODY, |
40 | | STREAM_STATE_CLOSED, |
41 | | }; |
42 | | |
43 | | enum enum_h2o_http2client_conn_state { |
44 | | H2O_HTTP2CLIENT_CONN_STATE_OPEN, |
45 | | H2O_HTTP2CLIENT_CONN_STATE_HALF_CLOSED, |
46 | | H2O_HTTP2CLIENT_CONN_STATE_IS_CLOSING, |
47 | | }; |
48 | | |
49 | | struct st_h2o_http2client_stream_t; |
50 | | KHASH_MAP_INIT_INT64(stream, struct st_h2o_http2client_stream_t *) |
51 | | |
52 | | struct st_h2o_http2client_conn_t { |
53 | | h2o_httpclient__h2_conn_t super; |
54 | | enum enum_h2o_http2client_conn_state state; |
55 | | khash_t(stream) * streams; |
56 | | h2o_http2_settings_t peer_settings; |
57 | | uint32_t max_open_stream_id; |
58 | | h2o_timer_t io_timeout; |
59 | | h2o_timer_t keepalive_timeout; |
60 | | |
61 | | struct { |
62 | | h2o_hpack_header_table_t header_table; |
63 | | h2o_http2_window_t window; |
64 | | h2o_buffer_t *buf; |
65 | | h2o_buffer_t *buf_in_flight; |
66 | | h2o_timer_t defer_timeout; |
67 | | h2o_linklist_t sending_streams; |
68 | | h2o_linklist_t sent_streams; |
69 | | } output; |
70 | | |
71 | | struct { |
72 | | h2o_hpack_header_table_t header_table; |
73 | | h2o_http2_window_t window; |
74 | | ssize_t (*read_frame)(struct st_h2o_http2client_conn_t *conn, const uint8_t *src, size_t len, const char **err_desc); |
75 | | h2o_buffer_t *headers_unparsed; |
76 | | } input; |
77 | | h2o_mem_pool_t rst_streams_pool; |
78 | | }; |
79 | | |
80 | | struct st_h2o_http2client_stream_t { |
81 | | h2o_httpclient_t super; |
82 | | struct st_h2o_http2client_conn_t *conn; |
83 | | uint32_t stream_id; |
84 | | struct { |
85 | | enum enum_h2o_http2client_stream_state req; |
86 | | enum enum_h2o_http2client_stream_state res; |
87 | | } state; |
88 | | |
89 | | struct { |
90 | | h2o_http2_window_t window; |
91 | | h2o_buffer_t *buf; |
92 | | h2o_linklist_t sending_link; |
93 | | h2o_httpclient_proceed_req_cb proceed_req; /* set to NULL once entire request body is provided to http2client */ |
94 | | } output; |
95 | | |
96 | | struct { |
97 | | h2o_http2_window_t window; |
98 | | int status; |
99 | | h2o_headers_t headers; |
100 | | h2o_buffer_t *body; |
101 | | h2o_headers_t trailers; |
102 | | size_t remaining_content_length; |
103 | | unsigned message_body_forbidden : 1; |
104 | | } input; |
105 | | |
106 | | int *notify_destroyed; |
107 | | int send_own_expect; |
108 | | }; |
109 | | |
110 | | static void do_emit_writereq(struct st_h2o_http2client_conn_t *conn); |
111 | | |
112 | | static void request_write(struct st_h2o_http2client_conn_t *conn) |
113 | 0 | { |
114 | 0 | if (conn->state == H2O_HTTP2CLIENT_CONN_STATE_IS_CLOSING) |
115 | 0 | return; |
116 | 0 | if (!h2o_socket_is_writing(conn->super.sock) && !h2o_timer_is_linked(&conn->output.defer_timeout)) |
117 | 0 | h2o_timer_link(conn->super.ctx->loop, 0, &conn->output.defer_timeout); |
118 | 0 | } |
119 | | |
120 | | static void enqueue_window_update(struct st_h2o_http2client_conn_t *conn, uint32_t stream_id, h2o_http2_window_t *window, |
121 | | size_t desired) |
122 | 0 | { |
123 | 0 | assert(desired <= INT32_MAX); |
124 | 0 | if (h2o_http2_window_get_avail(window) * 2 < desired) { |
125 | 0 | int32_t delta = (int32_t)(desired - h2o_http2_window_get_avail(window)); |
126 | 0 | h2o_http2_encode_window_update_frame(&conn->output.buf, stream_id, delta); |
127 | 0 | request_write(conn); |
128 | 0 | h2o_http2_window_update(window, delta); |
129 | 0 | } |
130 | 0 | } |
131 | | |
132 | | static void stream_send_error(struct st_h2o_http2client_conn_t *conn, uint32_t stream_id, int errnum) |
133 | 0 | { |
134 | 0 | assert(stream_id != 0); |
135 | 0 | assert(conn->state != H2O_HTTP2CLIENT_CONN_STATE_IS_CLOSING); |
136 | | |
137 | 0 | h2o_http2_encode_rst_stream_frame(&conn->output.buf, stream_id, -errnum); |
138 | 0 | request_write(conn); |
139 | 0 | } |
140 | | |
141 | | static struct st_h2o_http2client_stream_t *get_stream(struct st_h2o_http2client_conn_t *conn, uint32_t stream_id) |
142 | 0 | { |
143 | 0 | khiter_t iter = kh_get(stream, conn->streams, stream_id); |
144 | 0 | if (iter != kh_end(conn->streams)) |
145 | 0 | return (struct st_h2o_http2client_stream_t *)kh_val(conn->streams, iter); |
146 | 0 | return NULL; |
147 | 0 | } |
148 | | |
149 | | static uint32_t get_max_buffer_size(h2o_httpclient_ctx_t *ctx) |
150 | 0 | { |
151 | 0 | size_t sz = ctx->max_buffer_size; |
152 | 0 | if (sz > INT32_MAX) |
153 | 0 | sz = INT32_MAX; |
154 | 0 | return (uint32_t)sz; |
155 | 0 | } |
156 | | |
157 | | uint32_t h2o_httpclient__h2_get_max_concurrent_streams(h2o_httpclient__h2_conn_t *_conn) |
158 | 0 | { |
159 | 0 | struct st_h2o_http2client_conn_t *conn = (void *)_conn; |
160 | 0 | return conn->peer_settings.max_concurrent_streams < conn->super.ctx->http2.max_concurrent_streams |
161 | 0 | ? conn->peer_settings.max_concurrent_streams |
162 | 0 | : conn->super.ctx->http2.max_concurrent_streams; |
163 | 0 | } |
164 | | |
165 | | static void adjust_conn_linkedlist(h2o_httpclient_connection_pool_t *connpool, struct st_h2o_http2client_conn_t *conn, int forward) |
166 | 0 | { |
167 | 0 | if (connpool == NULL) { |
168 | 0 | assert(!h2o_linklist_is_linked(&conn->super.link)); |
169 | 0 | return; |
170 | 0 | } |
171 | 0 | if (!h2o_linklist_is_linked(&conn->super.link)) |
172 | 0 | return; |
173 | | |
174 | 0 | double ratio = (double)conn->super.num_streams / h2o_httpclient__h2_get_max_concurrent_streams(&conn->super); |
175 | | |
176 | | /* adjust connection linked list */ |
177 | 0 | h2o_linklist_t *node = forward ? conn->super.link.next : conn->super.link.prev; |
178 | 0 | while (node != &connpool->http2.conns) { |
179 | 0 | struct st_h2o_http2client_conn_t *cur = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http2client_conn_t, super.link, node); |
180 | 0 | double cur_ratio = (double)cur->super.num_streams / h2o_httpclient__h2_get_max_concurrent_streams(&cur->super); |
181 | 0 | if (forward ? (ratio <= cur_ratio) : (ratio >= cur_ratio)) |
182 | 0 | break; |
183 | 0 | node = forward ? node->next : node->prev; |
184 | 0 | } |
185 | 0 | if (forward) { |
186 | 0 | if (node == conn->super.link.next) |
187 | 0 | return; |
188 | 0 | } else { |
189 | 0 | if (node == conn->super.link.prev) |
190 | 0 | return; |
191 | 0 | if (node != &connpool->http2.conns) |
192 | 0 | node = node->next; /* do `insert after` rather than `insert before` */ |
193 | 0 | } |
194 | 0 | h2o_linklist_unlink(&conn->super.link); |
195 | 0 | h2o_linklist_insert(node, &conn->super.link); |
196 | 0 | } |
197 | | |
198 | | static void register_stream(struct st_h2o_http2client_stream_t *stream, struct st_h2o_http2client_conn_t *conn) |
199 | 0 | { |
200 | 0 | assert(stream->stream_id == 0); |
201 | | |
202 | 0 | stream->conn = conn; |
203 | |
|
204 | 0 | stream->stream_id = conn->max_open_stream_id == 0 ? 1 : conn->max_open_stream_id + 2; |
205 | 0 | conn->max_open_stream_id = stream->stream_id; |
206 | |
|
207 | 0 | int r; |
208 | 0 | khiter_t iter = kh_put(stream, conn->streams, stream->stream_id, &r); |
209 | 0 | assert(iter != kh_end(conn->streams)); |
210 | 0 | kh_val(conn->streams, iter) = stream; |
211 | |
|
212 | 0 | ++conn->super.num_streams; |
213 | |
|
214 | 0 | if (h2o_timer_is_linked(&conn->keepalive_timeout)) |
215 | 0 | h2o_timer_unlink(&conn->keepalive_timeout); |
216 | |
|
217 | 0 | adjust_conn_linkedlist(stream->super.connpool, conn, 1); |
218 | 0 | } |
219 | | |
220 | | static void unregister_stream(struct st_h2o_http2client_stream_t *stream) |
221 | 0 | { |
222 | 0 | khiter_t iter = kh_get(stream, stream->conn->streams, stream->stream_id); |
223 | 0 | assert(iter != kh_end(stream->conn->streams)); |
224 | 0 | kh_del(stream, stream->conn->streams, iter); |
225 | |
|
226 | 0 | --stream->conn->super.num_streams; |
227 | |
|
228 | 0 | if (stream->conn->super.num_streams == 0) |
229 | 0 | h2o_timer_link(stream->conn->super.ctx->loop, stream->conn->super.ctx->keepalive_timeout, &stream->conn->keepalive_timeout); |
230 | |
|
231 | 0 | adjust_conn_linkedlist(stream->super.connpool, stream->conn, 0); |
232 | 0 | } |
233 | | |
234 | | static void close_stream(struct st_h2o_http2client_stream_t *stream) |
235 | 0 | { |
236 | 0 | if (stream->conn != NULL) { |
237 | 0 | unregister_stream(stream); |
238 | 0 | } |
239 | |
|
240 | 0 | if (h2o_timer_is_linked(&stream->super._timeout)) |
241 | 0 | h2o_timer_unlink(&stream->super._timeout); |
242 | 0 | if (h2o_linklist_is_linked(&stream->output.sending_link)) |
243 | 0 | h2o_linklist_unlink(&stream->output.sending_link); |
244 | |
|
245 | 0 | if (stream->output.buf != NULL) |
246 | 0 | h2o_buffer_dispose(&stream->output.buf); |
247 | 0 | h2o_buffer_dispose(&stream->input.body); |
248 | |
|
249 | 0 | if (stream->notify_destroyed != NULL) |
250 | 0 | *stream->notify_destroyed = 1; |
251 | |
|
252 | 0 | free(stream); |
253 | 0 | } |
254 | | |
255 | | static void close_response(struct st_h2o_http2client_stream_t *stream) |
256 | 0 | { |
257 | 0 | assert(stream->state.res != STREAM_STATE_CLOSED); |
258 | 0 | stream->state.res = STREAM_STATE_CLOSED; |
259 | 0 | if (stream->state.req == STREAM_STATE_CLOSED) { |
260 | 0 | close_stream(stream); |
261 | 0 | } |
262 | 0 | } |
263 | | |
264 | | static void call_callback_with_error(struct st_h2o_http2client_stream_t *stream, const char *errstr) |
265 | 0 | { |
266 | 0 | assert(errstr != NULL); |
267 | 0 | switch (stream->state.res) { |
268 | 0 | case STREAM_STATE_HEAD: { |
269 | 0 | h2o_httpclient_on_head_t on_head = {.version = 0x200}; |
270 | 0 | stream->super._cb.on_head(&stream->super, errstr, &on_head); |
271 | 0 | } break; |
272 | 0 | case STREAM_STATE_BODY: |
273 | 0 | stream->super._cb.on_body(&stream->super, errstr, NULL, 0); |
274 | 0 | break; |
275 | 0 | case STREAM_STATE_CLOSED: |
276 | | /* proceed_req can be called to indicate error, regardless of write being inflight */ |
277 | 0 | if (stream->output.proceed_req != NULL) { |
278 | 0 | h2o_httpclient_proceed_req_cb cb = stream->output.proceed_req; |
279 | 0 | stream->output.proceed_req = NULL; |
280 | 0 | cb(&stream->super, errstr); |
281 | 0 | } |
282 | 0 | break; |
283 | 0 | } |
284 | 0 | } |
285 | | |
286 | | static void call_stream_callbacks_with_error(struct st_h2o_http2client_conn_t *conn, const char *errstr) |
287 | 0 | { |
288 | 0 | struct st_h2o_http2client_stream_t *stream; |
289 | 0 | kh_foreach_value(conn->streams, stream, { call_callback_with_error(stream, errstr); }); |
290 | 0 | } |
291 | | |
292 | | static int extract_content_length(const h2o_headers_t *headers, size_t *content_length, const char **err_desc) |
293 | 0 | { |
294 | 0 | *content_length = SIZE_MAX; |
295 | 0 | for (size_t i = 0; i < headers->size; ++i) { |
296 | 0 | if (headers->entries[i].name == &H2O_TOKEN_CONTENT_LENGTH->buf) { |
297 | 0 | const h2o_iovec_t *value = &headers->entries[i].value; |
298 | 0 | if (*content_length != SIZE_MAX) { |
299 | 0 | *err_desc = "duplicate content-length"; |
300 | 0 | return 0; |
301 | 0 | } |
302 | 0 | *content_length = h2o_strtosize(value->base, value->len); |
303 | 0 | if (*content_length == SIZE_MAX) { |
304 | 0 | *err_desc = "malformed content-length"; |
305 | 0 | return 0; |
306 | 0 | } |
307 | 0 | } |
308 | 0 | } |
309 | 0 | return 1; |
310 | 0 | } |
311 | | |
312 | | static int on_head(struct st_h2o_http2client_conn_t *conn, struct st_h2o_http2client_stream_t *stream, const uint8_t *src, |
313 | | size_t len, const char **err_desc, int is_end_stream) |
314 | 0 | { |
315 | 0 | int ret; |
316 | 0 | h2o_mem_pool_t *pool; |
317 | 0 | int *status; |
318 | 0 | h2o_headers_t *headers; |
319 | 0 | int dummy_status; |
320 | 0 | h2o_headers_t dummy_headers = {0}; |
321 | |
|
322 | 0 | if (stream != NULL) { |
323 | 0 | pool = stream->super.pool; |
324 | 0 | status = &stream->input.status; |
325 | 0 | headers = &stream->input.headers; |
326 | 0 | } else { |
327 | 0 | pool = &conn->rst_streams_pool; |
328 | 0 | status = &dummy_status; |
329 | 0 | headers = &dummy_headers; |
330 | 0 | } |
331 | |
|
332 | 0 | if ((ret = h2o_hpack_parse_response(pool, h2o_hpack_decode_header, &conn->input.header_table, status, headers, NULL, src, len, |
333 | 0 | err_desc)) != 0) { |
334 | 0 | if (ret == H2O_HTTP2_ERROR_INVALID_HEADER_CHAR) { |
335 | 0 | ret = H2O_HTTP2_ERROR_PROTOCOL; |
336 | 0 | goto Failed; |
337 | 0 | } |
338 | 0 | return ret; |
339 | 0 | } |
340 | | |
341 | 0 | if (stream == NULL) |
342 | 0 | return 0; |
343 | | |
344 | 0 | if (100 <= stream->input.status && stream->input.status <= 199) { |
345 | 0 | if (stream->input.status == 101) { |
346 | 0 | ret = H2O_HTTP2_ERROR_PROTOCOL; // TODO is this alright? |
347 | 0 | goto Failed; |
348 | 0 | } |
349 | 0 | if (stream->input.status == 100 && stream->send_own_expect) { |
350 | 0 | stream->input.status = 0; |
351 | 0 | stream->send_own_expect = 0; |
352 | 0 | if (stream->output.buf != NULL && !h2o_linklist_is_linked(&stream->output.sending_link)) { |
353 | 0 | h2o_linklist_insert(&stream->conn->output.sending_streams, &stream->output.sending_link); |
354 | 0 | request_write(stream->conn); |
355 | 0 | } |
356 | |
|
357 | 0 | return 0; |
358 | 0 | } |
359 | 0 | if (stream->super.informational_cb != NULL && |
360 | 0 | stream->super.informational_cb(&stream->super, 0, stream->input.status, h2o_iovec_init(NULL, 0), |
361 | 0 | stream->input.headers.entries, stream->input.headers.size) != 0) { |
362 | 0 | ret = H2O_HTTP2_ERROR_INTERNAL; |
363 | 0 | goto SendRSTStream; |
364 | 0 | } |
365 | 0 | return 0; |
366 | 0 | } |
367 | | |
368 | 0 | if ((100 <= stream->input.status && stream->input.status <= 199) || |
369 | 0 | (stream->input.status == 204 || stream->input.status == 304)) { |
370 | 0 | stream->input.message_body_forbidden = 1; |
371 | 0 | } |
372 | |
|
373 | 0 | if (!extract_content_length(&stream->input.headers, &stream->input.remaining_content_length, err_desc)) { |
374 | 0 | ret = H2O_HTTP2_ERROR_PROTOCOL; |
375 | 0 | goto Failed; |
376 | 0 | } |
377 | | |
378 | 0 | h2o_httpclient_on_head_t on_head = {.version = 0x200, |
379 | 0 | .status = stream->input.status, |
380 | 0 | .msg = h2o_iovec_init(NULL, 0), |
381 | 0 | .headers = stream->input.headers.entries, |
382 | 0 | .num_headers = stream->input.headers.size}; |
383 | 0 | stream->super._cb.on_body = |
384 | 0 | stream->super._cb.on_head(&stream->super, is_end_stream ? h2o_httpclient_error_is_eos : NULL, &on_head); |
385 | |
|
386 | 0 | if (is_end_stream) { |
387 | 0 | close_response(stream); |
388 | 0 | return 0; |
389 | 0 | } |
390 | 0 | if (stream->super._cb.on_body == NULL) { |
391 | | /** |
392 | | * NOTE: if on_head returns NULL due to invalid response (e.g. invalid content-length header) |
393 | | * sending RST_STREAM with PROTOCOL_ERROR might be more suitable than CANCEL |
394 | | * (see: https://tools.ietf.org/html/rfc7540#section-8.1.2.6) |
395 | | * but sending CANCEL is not wrong, so we leave this as-is for now. |
396 | | */ |
397 | 0 | ret = H2O_HTTP2_ERROR_CANCEL; |
398 | 0 | goto SendRSTStream; |
399 | 0 | } |
400 | | |
401 | 0 | stream->state.res = STREAM_STATE_BODY; |
402 | |
|
403 | 0 | return 0; |
404 | | |
405 | 0 | Failed: |
406 | 0 | assert(ret == H2O_HTTP2_ERROR_PROTOCOL); |
407 | 0 | call_callback_with_error(stream, h2o_httpclient_error_protocol_violation); |
408 | 0 | SendRSTStream: |
409 | 0 | stream_send_error(conn, stream->stream_id, ret); |
410 | 0 | close_stream(stream); |
411 | 0 | return 0; |
412 | 0 | } |
413 | | |
414 | | static int on_trailers(struct st_h2o_http2client_conn_t *conn, struct st_h2o_http2client_stream_t *stream, const uint8_t *src, |
415 | | size_t len, const char **err_desc) |
416 | 0 | { |
417 | 0 | int ret; |
418 | |
|
419 | 0 | assert(stream->state.res == STREAM_STATE_BODY); |
420 | | |
421 | 0 | if ((ret = h2o_hpack_parse_response(stream->super.pool, h2o_hpack_decode_header, &conn->input.header_table, NULL, |
422 | 0 | &stream->input.trailers, NULL, src, len, err_desc)) != 0) { |
423 | 0 | if (ret == H2O_HTTP2_ERROR_INVALID_HEADER_CHAR) { |
424 | 0 | ret = H2O_HTTP2_ERROR_PROTOCOL; |
425 | 0 | goto Failed; |
426 | 0 | } |
427 | 0 | return ret; |
428 | 0 | } |
429 | | |
430 | 0 | if (stream->super._cb.on_body(&stream->super, h2o_httpclient_error_is_eos, stream->input.trailers.entries, |
431 | 0 | stream->input.trailers.size) != 0) { |
432 | 0 | ret = H2O_HTTP2_ERROR_INTERNAL; |
433 | 0 | goto SendRSTStream; |
434 | 0 | } |
435 | 0 | close_response(stream); |
436 | 0 | return 0; |
437 | | |
438 | 0 | Failed: |
439 | 0 | assert(ret == H2O_HTTP2_ERROR_PROTOCOL); |
440 | 0 | call_callback_with_error(stream, h2o_httpclient_error_protocol_violation); |
441 | 0 | SendRSTStream: |
442 | 0 | stream_send_error(conn, stream->stream_id, ret); |
443 | 0 | close_stream(stream); |
444 | 0 | return 0; |
445 | 0 | } |
446 | | |
447 | | ssize_t expect_default(struct st_h2o_http2client_conn_t *conn, const uint8_t *src, size_t len, const char **err_desc); |
448 | | static ssize_t expect_continuation_of_headers(struct st_h2o_http2client_conn_t *conn, const uint8_t *src, size_t len, |
449 | | const char **err_desc) |
450 | 0 | { |
451 | 0 | h2o_http2_frame_t frame; |
452 | 0 | ssize_t ret; |
453 | 0 | struct st_h2o_http2client_stream_t *stream; |
454 | 0 | int hret; |
455 | |
|
456 | 0 | if ((ret = h2o_http2_decode_frame(&frame, src, len, H2O_HTTP2_SETTINGS_CLIENT_MAX_FRAME_SIZE, err_desc)) < 0) |
457 | 0 | return ret; |
458 | 0 | if (frame.type != H2O_HTTP2_FRAME_TYPE_CONTINUATION) { |
459 | 0 | *err_desc = "expected CONTINUATION frame"; |
460 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
461 | 0 | } |
462 | | |
463 | 0 | stream = get_stream(conn, frame.stream_id); |
464 | 0 | if (stream != NULL && stream->state.res == STREAM_STATE_CLOSED) { |
465 | 0 | *err_desc = "unexpected stream id in CONTINUATION frame"; |
466 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
467 | 0 | } |
468 | | |
469 | 0 | h2o_buffer_reserve(&conn->input.headers_unparsed, frame.length); |
470 | 0 | memcpy(conn->input.headers_unparsed->bytes + conn->input.headers_unparsed->size, frame.payload, frame.length); |
471 | 0 | conn->input.headers_unparsed->size += frame.length; |
472 | |
|
473 | 0 | if ((frame.flags & H2O_HTTP2_FRAME_FLAG_END_HEADERS) != 0) { |
474 | 0 | int is_end_stream = (frame.flags & H2O_HTTP2_FRAME_FLAG_END_STREAM) != 0; |
475 | 0 | conn->input.read_frame = expect_default; |
476 | |
|
477 | 0 | if (stream != NULL && stream->state.res == STREAM_STATE_BODY) { |
478 | 0 | hret = on_trailers(conn, stream, (const uint8_t *)conn->input.headers_unparsed->bytes, |
479 | 0 | conn->input.headers_unparsed->size, err_desc); |
480 | 0 | } else { |
481 | 0 | hret = on_head(conn, stream, (const uint8_t *)conn->input.headers_unparsed->bytes, conn->input.headers_unparsed->size, |
482 | 0 | err_desc, is_end_stream); |
483 | 0 | } |
484 | 0 | if (hret != 0) |
485 | 0 | ret = hret; |
486 | |
|
487 | 0 | h2o_buffer_dispose(&conn->input.headers_unparsed); |
488 | 0 | conn->input.headers_unparsed = NULL; |
489 | 0 | } |
490 | |
|
491 | 0 | return ret; |
492 | 0 | } |
493 | | |
494 | | static void do_update_window(h2o_httpclient_t *client); |
495 | | static int handle_data_frame(struct st_h2o_http2client_conn_t *conn, h2o_http2_frame_t *frame, const char **err_desc) |
496 | 0 | { |
497 | 0 | h2o_http2_data_payload_t payload; |
498 | 0 | struct st_h2o_http2client_stream_t *stream; |
499 | 0 | int ret; |
500 | |
|
501 | 0 | if ((ret = h2o_http2_decode_data_payload(&payload, frame, err_desc)) != 0) |
502 | 0 | return ret; |
503 | | |
504 | | /* save the input in the request body buffer, or send error (and close the stream) */ |
505 | 0 | if ((stream = get_stream(conn, frame->stream_id)) == NULL) { |
506 | 0 | if (frame->stream_id <= conn->max_open_stream_id) { |
507 | 0 | h2o_http2_window_consume_window(&conn->input.window, payload.length); |
508 | 0 | enqueue_window_update(conn, 0, &conn->input.window, H2O_HTTP2_SETTINGS_CLIENT_CONNECTION_WINDOW_SIZE); |
509 | 0 | return 0; |
510 | 0 | } else { |
511 | 0 | *err_desc = "invalid DATA frame"; |
512 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
513 | 0 | } |
514 | 0 | } |
515 | | |
516 | 0 | if (stream->state.res != STREAM_STATE_BODY) { |
517 | 0 | stream_send_error(conn, frame->stream_id, H2O_HTTP2_ERROR_PROTOCOL); |
518 | 0 | call_callback_with_error(stream, h2o_httpclient_error_protocol_violation); |
519 | 0 | close_stream(stream); |
520 | 0 | return 0; |
521 | 0 | } |
522 | | |
523 | | /** |
524 | | * RFC7230 Section 3.3. |
525 | | * |
526 | | * Responses to the HEAD request method (Section 4.3.2 of [RFC7231]) never |
527 | | * include a message body because the associated response header fields |
528 | | * (e.g., Transfer-Encoding, Content-Length, etc.), if present, indicate |
529 | | * only what their values would have been if the request method had been |
530 | | * GET (Section 4.3.1 of [RFC7231]). |
531 | | * |
532 | | * 2xx (Successful) responses to a CONNECT request method (Section 4.3.6 |
533 | | * of [RFC7231]) switch to tunnel mode instead of having a message body. |
534 | | * |
535 | | * All 1xx (Informational), 204 (No Content), and 304 (Not Modified) |
536 | | * responses do not include a message body. |
537 | | * |
538 | | * All other responses do include a message body, although the body might |
539 | | * be of zero length. |
540 | | */ |
541 | 0 | if (stream->input.message_body_forbidden && payload.length != 0) { |
542 | 0 | stream_send_error(conn, frame->stream_id, H2O_HTTP2_ERROR_PROTOCOL); |
543 | 0 | call_callback_with_error(stream, h2o_httpclient_error_protocol_violation); |
544 | 0 | close_stream(stream); |
545 | 0 | return 0; |
546 | 0 | } |
547 | | |
548 | | /** |
549 | | * RFC 7540 Section 8.1.2.6. |
550 | | * A request or response is also malformed if the value of |
551 | | * a content-length header field does not equal the sum of the DATA frame |
552 | | * payload lengths that form the body. |
553 | | */ |
554 | 0 | if (stream->input.remaining_content_length != SIZE_MAX) { |
555 | 0 | if (payload.length > stream->input.remaining_content_length) { |
556 | 0 | stream_send_error(conn, frame->stream_id, H2O_HTTP2_ERROR_PROTOCOL); |
557 | 0 | call_callback_with_error(stream, h2o_httpclient_error_protocol_violation); |
558 | 0 | close_stream(stream); |
559 | 0 | return 0; |
560 | 0 | } |
561 | 0 | stream->input.remaining_content_length -= payload.length; |
562 | 0 | } |
563 | | |
564 | 0 | size_t max_size = get_max_buffer_size(stream->super.ctx); |
565 | 0 | if (stream->input.body->size + payload.length > max_size) { |
566 | 0 | call_callback_with_error(stream, h2o_httpclient_error_flow_control); |
567 | 0 | stream_send_error(stream->conn, stream->stream_id, H2O_HTTP2_ERROR_FLOW_CONTROL); |
568 | 0 | close_stream(stream); |
569 | 0 | return 0; |
570 | 0 | } |
571 | | |
572 | 0 | h2o_buffer_append(&stream->input.body, (void *)payload.data, payload.length); |
573 | |
|
574 | 0 | h2o_http2_window_consume_window(&conn->input.window, payload.length); |
575 | 0 | h2o_http2_window_consume_window(&stream->input.window, payload.length); |
576 | |
|
577 | 0 | int is_final = (frame->flags & H2O_HTTP2_FRAME_FLAG_END_STREAM) != 0; |
578 | 0 | if (stream->super._cb.on_body(&stream->super, is_final ? h2o_httpclient_error_is_eos : NULL, NULL, 0) != 0) { |
579 | 0 | stream_send_error(conn, frame->stream_id, H2O_HTTP2_ERROR_INTERNAL); |
580 | 0 | close_stream(stream); |
581 | 0 | return 0; |
582 | 0 | } |
583 | | |
584 | 0 | if (is_final) { |
585 | 0 | close_response(stream); |
586 | 0 | } else { |
587 | | /* update connection-level window */ |
588 | 0 | enqueue_window_update(stream->conn, 0, &stream->conn->input.window, H2O_HTTP2_SETTINGS_CLIENT_CONNECTION_WINDOW_SIZE); |
589 | | /* update stream-level window */ |
590 | 0 | do_update_window(&stream->super); |
591 | 0 | } |
592 | |
|
593 | 0 | return 0; |
594 | 0 | } |
595 | | |
596 | | static int handle_headers_frame(struct st_h2o_http2client_conn_t *conn, h2o_http2_frame_t *frame, const char **err_desc) |
597 | 0 | { |
598 | 0 | h2o_http2_headers_payload_t payload; |
599 | 0 | struct st_h2o_http2client_stream_t *stream; |
600 | 0 | int ret; |
601 | | |
602 | | /* decode */ |
603 | 0 | if ((ret = h2o_http2_decode_headers_payload(&payload, frame, err_desc)) != 0) |
604 | 0 | return ret; |
605 | 0 | if ((frame->stream_id & 1) == 0) { |
606 | 0 | *err_desc = "invalid stream id in HEADERS frame"; |
607 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
608 | 0 | } |
609 | | |
610 | 0 | if (frame->stream_id == payload.priority.dependency) { |
611 | 0 | *err_desc = "stream cannot depend on itself"; |
612 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
613 | 0 | } |
614 | | |
615 | 0 | if ((stream = get_stream(conn, frame->stream_id)) == NULL) { |
616 | 0 | if (frame->stream_id > conn->max_open_stream_id) { |
617 | 0 | *err_desc = "invalid stream id in HEADERS frame"; |
618 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
619 | 0 | } |
620 | 0 | } |
621 | | |
622 | 0 | int is_end_stream = (frame->flags & H2O_HTTP2_FRAME_FLAG_END_STREAM) != 0; |
623 | |
|
624 | 0 | if (stream != NULL) { |
625 | 0 | h2o_timer_unlink(&stream->super._timeout); |
626 | |
|
627 | 0 | if (stream->state.res == STREAM_STATE_BODY && !is_end_stream) { |
628 | 0 | *err_desc = "trailing HEADERS frame MUST have END_STREAM flag set"; |
629 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
630 | 0 | } |
631 | 0 | } |
632 | | |
633 | 0 | if ((frame->flags & H2O_HTTP2_FRAME_FLAG_END_HEADERS) == 0) { |
634 | | /* header is not complete, store in buffer */ |
635 | 0 | conn->input.read_frame = expect_continuation_of_headers; |
636 | 0 | h2o_buffer_init(&conn->input.headers_unparsed, &h2o_socket_buffer_prototype); |
637 | 0 | h2o_buffer_reserve(&conn->input.headers_unparsed, payload.headers_len); |
638 | 0 | memcpy(conn->input.headers_unparsed->bytes, payload.headers, payload.headers_len); |
639 | 0 | conn->input.headers_unparsed->size = payload.headers_len; |
640 | 0 | return 0; |
641 | 0 | } |
642 | | |
643 | | /* response header is complete, handle it */ |
644 | 0 | if (stream != NULL && stream->state.res == STREAM_STATE_BODY) { |
645 | 0 | return on_trailers(conn, stream, payload.headers, payload.headers_len, err_desc); |
646 | 0 | } else { |
647 | 0 | return on_head(conn, stream, payload.headers, payload.headers_len, err_desc, is_end_stream); |
648 | 0 | } |
649 | 0 | } |
650 | | |
651 | | static int handle_priority_frame(struct st_h2o_http2client_conn_t *conn, h2o_http2_frame_t *frame, const char **err_desc) |
652 | 0 | { |
653 | 0 | h2o_http2_priority_t payload; |
654 | 0 | int ret; |
655 | |
|
656 | 0 | if ((ret = h2o_http2_decode_priority_payload(&payload, frame, err_desc)) != 0) |
657 | 0 | return ret; |
658 | 0 | if (frame->stream_id == payload.dependency) { |
659 | 0 | *err_desc = "stream cannot depend on itself"; |
660 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
661 | 0 | } |
662 | | |
663 | | /* Ignore PRIORITY frames */ |
664 | 0 | return 0; |
665 | 0 | } |
666 | | |
667 | | static int handle_rst_stream_frame(struct st_h2o_http2client_conn_t *conn, h2o_http2_frame_t *frame, const char **err_desc) |
668 | 0 | { |
669 | 0 | h2o_http2_rst_stream_payload_t payload; |
670 | 0 | struct st_h2o_http2client_stream_t *stream; |
671 | 0 | int ret; |
672 | |
|
673 | 0 | if ((ret = h2o_http2_decode_rst_stream_payload(&payload, frame, err_desc)) != 0) |
674 | 0 | return ret; |
675 | 0 | if (frame->stream_id > conn->max_open_stream_id) { |
676 | 0 | *err_desc = "unexpected stream id in RST_STREAM frame"; |
677 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
678 | 0 | } |
679 | | |
680 | 0 | stream = get_stream(conn, frame->stream_id); |
681 | 0 | if (stream != NULL) { |
682 | | /* reset the stream */ |
683 | 0 | call_callback_with_error(stream, payload.error_code == -H2O_HTTP2_ERROR_REFUSED_STREAM ? h2o_httpclient_error_refused_stream |
684 | 0 | : h2o_httpclient_error_io); |
685 | 0 | close_stream(stream); |
686 | 0 | } |
687 | |
|
688 | 0 | return 0; |
689 | 0 | } |
690 | | |
691 | | static int update_stream_output_window(struct st_h2o_http2client_stream_t *stream, ssize_t delta) |
692 | 0 | { |
693 | 0 | if (h2o_http2_window_update(&stream->output.window, delta) != 0) |
694 | 0 | return -1; |
695 | 0 | ssize_t after = h2o_http2_window_get_avail(&stream->output.window); |
696 | 0 | if (after > 0 && stream->output.buf != NULL && stream->output.buf->size != 0 && |
697 | 0 | !h2o_linklist_is_linked(&stream->output.sending_link)) |
698 | 0 | h2o_linklist_insert(&stream->conn->output.sending_streams, &stream->output.sending_link); |
699 | 0 | return 0; |
700 | 0 | } |
701 | | |
702 | | static ssize_t conn_get_buffer_window(struct st_h2o_http2client_conn_t *conn) |
703 | 0 | { |
704 | 0 | ssize_t ret, winsz; |
705 | 0 | size_t capacity, cwnd_left; |
706 | |
|
707 | 0 | capacity = conn->output.buf->capacity; |
708 | 0 | if ((cwnd_left = h2o_socket_prepare_for_latency_optimized_write(conn->super.sock, |
709 | 0 | &conn->super.ctx->http2.latency_optimization)) < capacity) { |
710 | 0 | capacity = cwnd_left; |
711 | 0 | if (capacity < conn->output.buf->size) |
712 | 0 | return 0; |
713 | 0 | } |
714 | | |
715 | 0 | ret = capacity - conn->output.buf->size; |
716 | 0 | if (ret < H2O_HTTP2_FRAME_HEADER_SIZE) |
717 | 0 | return 0; |
718 | 0 | ret -= H2O_HTTP2_FRAME_HEADER_SIZE; |
719 | 0 | winsz = h2o_http2_window_get_avail(&conn->output.window); |
720 | 0 | if (winsz < ret) |
721 | 0 | ret = winsz; |
722 | 0 | return ret; |
723 | 0 | } |
724 | | |
725 | | static int handle_settings_frame(struct st_h2o_http2client_conn_t *conn, h2o_http2_frame_t *frame, const char **err_desc) |
726 | 0 | { |
727 | 0 | if (frame->stream_id != 0) { |
728 | 0 | *err_desc = "invalid stream id in SETTINGS frame"; |
729 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
730 | 0 | } |
731 | | |
732 | 0 | if ((frame->flags & H2O_HTTP2_FRAME_FLAG_ACK) != 0) { |
733 | 0 | if (frame->length != 0) { |
734 | 0 | *err_desc = "invalid SETTINGS frame (+ACK)"; |
735 | 0 | return H2O_HTTP2_ERROR_FRAME_SIZE; |
736 | 0 | } |
737 | 0 | } else { |
738 | 0 | uint32_t prev_initial_window_size = conn->peer_settings.initial_window_size; |
739 | 0 | int ret = h2o_http2_update_peer_settings(&conn->peer_settings, frame->payload, frame->length, err_desc); |
740 | 0 | if (ret != 0) |
741 | 0 | return ret; |
742 | 0 | { /* schedule ack */ |
743 | 0 | h2o_iovec_t header_buf = h2o_buffer_reserve(&conn->output.buf, H2O_HTTP2_FRAME_HEADER_SIZE); |
744 | 0 | h2o_http2_encode_frame_header((void *)header_buf.base, 0, H2O_HTTP2_FRAME_TYPE_SETTINGS, H2O_HTTP2_FRAME_FLAG_ACK, 0); |
745 | 0 | conn->output.buf->size += H2O_HTTP2_FRAME_HEADER_SIZE; |
746 | 0 | request_write(conn); |
747 | 0 | } |
748 | | /* apply the change to window size (to all the streams but not the connection, see 6.9.2 of draft-15) */ |
749 | 0 | if (prev_initial_window_size != conn->peer_settings.initial_window_size) { |
750 | 0 | ssize_t delta = conn->peer_settings.initial_window_size - prev_initial_window_size; |
751 | 0 | struct st_h2o_http2client_stream_t *stream; |
752 | 0 | kh_foreach_value(conn->streams, stream, { update_stream_output_window((void *)stream, delta); }); |
753 | |
|
754 | 0 | if (conn_get_buffer_window(conn) > 0) |
755 | 0 | request_write(conn); |
756 | 0 | } |
757 | 0 | } |
758 | | |
759 | 0 | return 0; |
760 | 0 | } |
761 | | |
762 | | static int handle_push_promise_frame(struct st_h2o_http2client_conn_t *conn, h2o_http2_frame_t *frame, const char **err_desc) |
763 | 0 | { |
764 | 0 | *err_desc = "received PUSH_PROMISE frame"; |
765 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
766 | 0 | } |
767 | | |
768 | | static int handle_ping_frame(struct st_h2o_http2client_conn_t *conn, h2o_http2_frame_t *frame, const char **err_desc) |
769 | 0 | { |
770 | 0 | h2o_http2_ping_payload_t payload; |
771 | 0 | int ret; |
772 | |
|
773 | 0 | if ((ret = h2o_http2_decode_ping_payload(&payload, frame, err_desc)) != 0) |
774 | 0 | return ret; |
775 | | |
776 | 0 | if ((frame->flags & H2O_HTTP2_FRAME_FLAG_ACK) == 0) { |
777 | 0 | h2o_http2_encode_ping_frame(&conn->output.buf, 1, payload.data); |
778 | 0 | request_write(conn); |
779 | 0 | } |
780 | |
|
781 | 0 | return 0; |
782 | 0 | } |
783 | | |
784 | | static int handle_goaway_frame(struct st_h2o_http2client_conn_t *conn, h2o_http2_frame_t *frame, const char **err_desc) |
785 | 0 | { |
786 | 0 | h2o_http2_goaway_payload_t payload; |
787 | 0 | int ret; |
788 | |
|
789 | 0 | if ((ret = h2o_http2_decode_goaway_payload(&payload, frame, err_desc)) != 0) |
790 | 0 | return ret; |
791 | | |
792 | 0 | struct st_h2o_http2client_stream_t *stream; |
793 | 0 | kh_foreach_value(conn->streams, stream, { |
794 | 0 | if (stream->stream_id > payload.last_stream_id) { |
795 | 0 | call_callback_with_error(stream, h2o_httpclient_error_refused_stream); |
796 | 0 | close_stream(stream); |
797 | 0 | } |
798 | 0 | }); |
799 | | |
800 | | /* stop opening new streams */ |
801 | 0 | if (h2o_linklist_is_linked(&conn->super.link)) |
802 | 0 | h2o_linklist_unlink(&conn->super.link); |
803 | |
|
804 | 0 | return 0; |
805 | 0 | } |
806 | | |
807 | | static int handle_window_update_frame(struct st_h2o_http2client_conn_t *conn, h2o_http2_frame_t *frame, const char **err_desc) |
808 | 0 | { |
809 | 0 | h2o_http2_window_update_payload_t payload; |
810 | 0 | int ret, err_is_stream_level; |
811 | |
|
812 | 0 | if ((ret = h2o_http2_decode_window_update_payload(&payload, frame, err_desc, &err_is_stream_level)) != 0) { |
813 | 0 | if (err_is_stream_level) { |
814 | 0 | stream_send_error(conn, frame->stream_id, ret); |
815 | 0 | struct st_h2o_http2client_stream_t *stream = get_stream(conn, frame->stream_id); |
816 | 0 | if (stream != NULL) { |
817 | 0 | call_callback_with_error(stream, h2o_httpclient_error_protocol_violation); |
818 | 0 | close_stream(stream); |
819 | 0 | } |
820 | 0 | return 0; |
821 | 0 | } else { |
822 | 0 | return ret; |
823 | 0 | } |
824 | 0 | } |
825 | | |
826 | 0 | if (frame->stream_id == 0) { |
827 | 0 | if (h2o_http2_window_update(&conn->output.window, payload.window_size_increment) != 0) { |
828 | 0 | *err_desc = "flow control window overflow"; |
829 | 0 | return H2O_HTTP2_ERROR_FLOW_CONTROL; |
830 | 0 | } |
831 | 0 | } else if (frame->stream_id <= conn->max_open_stream_id) { |
832 | 0 | struct st_h2o_http2client_stream_t *stream = get_stream(conn, frame->stream_id); |
833 | 0 | if (stream != NULL) { |
834 | 0 | if (update_stream_output_window(stream, payload.window_size_increment) != 0) { |
835 | 0 | stream_send_error(conn, frame->stream_id, H2O_HTTP2_ERROR_FLOW_CONTROL); |
836 | 0 | call_callback_with_error(stream, h2o_httpclient_error_flow_control); |
837 | 0 | close_stream(stream); |
838 | 0 | return 0; |
839 | 0 | } |
840 | 0 | } |
841 | 0 | } else { |
842 | 0 | *err_desc = "invalid stream id in WINDOW_UPDATE frame"; |
843 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
844 | 0 | } |
845 | | |
846 | 0 | if (conn_get_buffer_window(conn) > 0) |
847 | 0 | request_write(conn); |
848 | |
|
849 | 0 | return 0; |
850 | 0 | } |
851 | | |
852 | | static int handle_invalid_continuation_frame(struct st_h2o_http2client_conn_t *conn, h2o_http2_frame_t *frame, |
853 | | const char **err_desc) |
854 | 0 | { |
855 | 0 | *err_desc = "received invalid CONTINUATION frame"; |
856 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
857 | 0 | } |
858 | | |
859 | | ssize_t expect_default(struct st_h2o_http2client_conn_t *conn, const uint8_t *src, size_t len, const char **err_desc) |
860 | 0 | { |
861 | 0 | assert(conn->state != H2O_HTTP2CLIENT_CONN_STATE_IS_CLOSING); |
862 | | |
863 | 0 | h2o_http2_frame_t frame; |
864 | 0 | ssize_t ret; |
865 | 0 | static int (*FRAME_HANDLERS[])(struct st_h2o_http2client_conn_t *conn, h2o_http2_frame_t *frame, const char **err_desc) = { |
866 | 0 | handle_data_frame, /* DATA */ |
867 | 0 | handle_headers_frame, /* HEADERS */ |
868 | 0 | handle_priority_frame, /* PRIORITY */ |
869 | 0 | handle_rst_stream_frame, /* RST_STREAM */ |
870 | 0 | handle_settings_frame, /* SETTINGS */ |
871 | 0 | handle_push_promise_frame, /* PUSH_PROMISE */ |
872 | 0 | handle_ping_frame, /* PING */ |
873 | 0 | handle_goaway_frame, /* GOAWAY */ |
874 | 0 | handle_window_update_frame, /* WINDOW_UPDATE */ |
875 | 0 | handle_invalid_continuation_frame /* CONTINUATION */ |
876 | 0 | }; |
877 | |
|
878 | 0 | if ((ret = h2o_http2_decode_frame(&frame, src, len, H2O_HTTP2_SETTINGS_CLIENT_MAX_FRAME_SIZE, err_desc)) < 0) |
879 | 0 | return ret; |
880 | | |
881 | 0 | if (frame.type < sizeof(FRAME_HANDLERS) / sizeof(FRAME_HANDLERS[0])) { |
882 | 0 | int hret = FRAME_HANDLERS[frame.type](conn, &frame, err_desc); |
883 | 0 | if (hret != 0) |
884 | 0 | ret = hret; |
885 | 0 | } else { |
886 | 0 | h2o_error_printf("skipping frame (type:%d)\n", frame.type); |
887 | 0 | } |
888 | |
|
889 | 0 | return ret; |
890 | 0 | } |
891 | | |
892 | | static ssize_t expect_settings(struct st_h2o_http2client_conn_t *conn, const uint8_t *src, size_t len, const char **err_desc) |
893 | 0 | { |
894 | 0 | assert(conn->state != H2O_HTTP2CLIENT_CONN_STATE_IS_CLOSING); |
895 | | |
896 | 0 | h2o_http2_frame_t frame; |
897 | 0 | ssize_t ret; |
898 | |
|
899 | 0 | if ((ret = h2o_http2_decode_frame(&frame, src, len, H2O_HTTP2_SETTINGS_CLIENT_MAX_FRAME_SIZE, err_desc)) < 0) |
900 | 0 | return ret; |
901 | | |
902 | 0 | if (frame.type != H2O_HTTP2_FRAME_TYPE_SETTINGS) |
903 | 0 | return H2O_HTTP2_ERROR_PROTOCOL_CLOSE_IMMEDIATELY; |
904 | | |
905 | 0 | int hret = handle_settings_frame(conn, &frame, err_desc); |
906 | 0 | if (hret != 0) |
907 | 0 | return hret; |
908 | | |
909 | 0 | conn->input.read_frame = expect_default; |
910 | 0 | return ret; |
911 | 0 | } |
912 | | |
913 | | static void close_connection_now(struct st_h2o_http2client_conn_t *conn) |
914 | 0 | { |
915 | 0 | free(conn->super.origin_url.authority.base); |
916 | 0 | free(conn->super.origin_url.host.base); |
917 | 0 | free(conn->super.origin_url.path.base); |
918 | |
|
919 | 0 | h2o_socket_close(conn->super.sock); |
920 | |
|
921 | 0 | struct st_h2o_http2client_stream_t *stream; |
922 | 0 | kh_foreach_value(conn->streams, stream, { close_stream(stream); }); |
923 | 0 | kh_destroy(stream, conn->streams); |
924 | |
|
925 | 0 | if (h2o_linklist_is_linked(&conn->super.link)) |
926 | 0 | h2o_linklist_unlink(&conn->super.link); |
927 | |
|
928 | 0 | if (h2o_timer_is_linked(&conn->io_timeout)) |
929 | 0 | h2o_timer_unlink(&conn->io_timeout); |
930 | 0 | if (h2o_timer_is_linked(&conn->keepalive_timeout)) |
931 | 0 | h2o_timer_unlink(&conn->keepalive_timeout); |
932 | | |
933 | | /* output */ |
934 | 0 | h2o_hpack_dispose_header_table(&conn->output.header_table); |
935 | 0 | h2o_buffer_dispose(&conn->output.buf); |
936 | 0 | if (conn->output.buf_in_flight != NULL) |
937 | 0 | h2o_buffer_dispose(&conn->output.buf_in_flight); |
938 | 0 | if (h2o_timer_is_linked(&conn->output.defer_timeout)) |
939 | 0 | h2o_timer_unlink(&conn->output.defer_timeout); |
940 | 0 | assert(h2o_linklist_is_empty(&conn->output.sending_streams)); |
941 | 0 | assert(h2o_linklist_is_empty(&conn->output.sent_streams)); |
942 | | |
943 | | /* input */ |
944 | 0 | h2o_hpack_dispose_header_table(&conn->input.header_table); |
945 | 0 | if (conn->input.headers_unparsed != NULL) |
946 | 0 | h2o_buffer_dispose(&conn->input.headers_unparsed); |
947 | |
|
948 | 0 | h2o_mem_clear_pool(&conn->rst_streams_pool); |
949 | 0 | free(conn); |
950 | 0 | } |
951 | | |
952 | | static int close_connection_if_necessary(struct st_h2o_http2client_conn_t *conn) |
953 | 0 | { |
954 | 0 | if (conn->state == H2O_HTTP2CLIENT_CONN_STATE_HALF_CLOSED && conn->super.num_streams == 0) |
955 | 0 | conn->state = H2O_HTTP2CLIENT_CONN_STATE_IS_CLOSING; |
956 | 0 | if (conn->state == H2O_HTTP2CLIENT_CONN_STATE_IS_CLOSING) { |
957 | 0 | close_connection_now(conn); |
958 | 0 | return 1; |
959 | 0 | } |
960 | 0 | return 0; |
961 | 0 | } |
962 | | |
963 | | static int close_connection(struct st_h2o_http2client_conn_t *conn) |
964 | 0 | { |
965 | 0 | conn->state = H2O_HTTP2CLIENT_CONN_STATE_IS_CLOSING; |
966 | 0 | h2o_socket_read_stop(conn->super.sock); |
967 | |
|
968 | 0 | if (conn->output.buf_in_flight != NULL || h2o_timer_is_linked(&conn->output.defer_timeout)) { |
969 | | /* there is a pending write, let close_connection_if_necessary actually close the connection */ |
970 | 0 | } else { |
971 | 0 | close_connection_now(conn); |
972 | 0 | return -1; |
973 | 0 | } |
974 | 0 | return 0; |
975 | 0 | } |
976 | | |
977 | | static void enqueue_goaway(struct st_h2o_http2client_conn_t *conn, int errnum, h2o_iovec_t additional_data) |
978 | 0 | { |
979 | 0 | if (conn->state == H2O_HTTP2CLIENT_CONN_STATE_IS_CLOSING) |
980 | 0 | return; |
981 | | |
982 | 0 | h2o_http2_encode_goaway_frame(&conn->output.buf, 0, errnum, additional_data); |
983 | 0 | request_write(conn); |
984 | 0 | conn->state = H2O_HTTP2CLIENT_CONN_STATE_HALF_CLOSED; |
985 | | |
986 | | /* stop opening new streams */ |
987 | 0 | if (h2o_linklist_is_linked(&conn->super.link)) |
988 | 0 | h2o_linklist_unlink(&conn->super.link); |
989 | 0 | } |
990 | | |
991 | | static void on_connect_error(struct st_h2o_http2client_stream_t *stream, const char *errstr) |
992 | 0 | { |
993 | 0 | assert(errstr != NULL); |
994 | 0 | stream->super._cb.on_connect(&stream->super, errstr, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL); |
995 | 0 | close_stream(stream); |
996 | 0 | } |
997 | | |
998 | | static void do_stream_timeout(struct st_h2o_http2client_stream_t *stream) |
999 | 0 | { |
1000 | 0 | if (stream->conn == NULL) { |
1001 | 0 | on_connect_error(stream, h2o_httpclient_error_connect_timeout); |
1002 | 0 | return; |
1003 | 0 | } |
1004 | 0 | const char *errstr = |
1005 | 0 | stream->state.res == STREAM_STATE_HEAD ? h2o_httpclient_error_first_byte_timeout : h2o_httpclient_error_io_timeout; |
1006 | 0 | call_callback_with_error(stream, errstr); |
1007 | 0 | close_stream(stream); |
1008 | 0 | } |
1009 | | |
1010 | | static void on_stream_timeout(h2o_timer_t *entry) |
1011 | 0 | { |
1012 | 0 | struct st_h2o_http2client_stream_t *stream = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http2client_stream_t, super._timeout, entry); |
1013 | 0 | do_stream_timeout(stream); |
1014 | 0 | } |
1015 | | |
1016 | | static void on_io_timeout(h2o_timer_t *entry) |
1017 | 0 | { |
1018 | 0 | struct st_h2o_http2client_conn_t *conn = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http2client_conn_t, io_timeout, entry); |
1019 | 0 | struct st_h2o_http2client_stream_t *stream; |
1020 | 0 | kh_foreach_value(conn->streams, stream, { do_stream_timeout(stream); }); |
1021 | 0 | close_connection_now(conn); |
1022 | 0 | } |
1023 | | |
1024 | | static void on_keepalive_timeout(h2o_timer_t *entry) |
1025 | 0 | { |
1026 | 0 | struct st_h2o_http2client_conn_t *conn = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http2client_conn_t, keepalive_timeout, entry); |
1027 | 0 | enqueue_goaway(conn, H2O_HTTP2_ERROR_NONE, h2o_iovec_init(NULL, 0)); |
1028 | 0 | request_write(conn); |
1029 | 0 | close_connection(conn); |
1030 | 0 | } |
1031 | | |
1032 | | static int parse_input(struct st_h2o_http2client_conn_t *conn) |
1033 | 0 | { |
1034 | | /* handle the input */ |
1035 | 0 | while (conn->state != H2O_HTTP2CLIENT_CONN_STATE_IS_CLOSING && conn->super.sock->input->size != 0) { |
1036 | | /* process a frame */ |
1037 | 0 | const char *err_desc = NULL; |
1038 | 0 | ssize_t ret = |
1039 | 0 | conn->input.read_frame(conn, (uint8_t *)conn->super.sock->input->bytes, conn->super.sock->input->size, &err_desc); |
1040 | 0 | if (ret == H2O_HTTP2_ERROR_INCOMPLETE) { |
1041 | 0 | break; |
1042 | 0 | } else if (ret < 0) { |
1043 | 0 | if (ret != H2O_HTTP2_ERROR_PROTOCOL_CLOSE_IMMEDIATELY) { |
1044 | 0 | enqueue_goaway(conn, (int)ret, |
1045 | 0 | err_desc != NULL ? (h2o_iovec_t){(char *)err_desc, strlen(err_desc)} : (h2o_iovec_t){NULL}); |
1046 | 0 | } |
1047 | 0 | call_stream_callbacks_with_error(conn, h2o_httpclient_error_protocol_violation); |
1048 | 0 | return close_connection(conn); |
1049 | 0 | } |
1050 | | /* advance to the next frame */ |
1051 | 0 | h2o_buffer_consume(&conn->super.sock->input, ret); |
1052 | 0 | } |
1053 | 0 | return 0; |
1054 | 0 | } |
1055 | | |
1056 | | static void on_read(h2o_socket_t *sock, const char *err) |
1057 | 0 | { |
1058 | 0 | struct st_h2o_http2client_conn_t *conn = sock->data; |
1059 | |
|
1060 | 0 | h2o_timer_unlink(&conn->io_timeout); |
1061 | |
|
1062 | 0 | if (err != NULL) { |
1063 | 0 | call_stream_callbacks_with_error(conn, h2o_httpclient_error_io); |
1064 | 0 | close_connection(conn); |
1065 | 0 | return; |
1066 | 0 | } |
1067 | | |
1068 | 0 | if (parse_input(conn) != 0) |
1069 | 0 | return; |
1070 | | |
1071 | | /* write immediately, if pending write exists */ |
1072 | 0 | if (h2o_timer_is_linked(&conn->output.defer_timeout)) { |
1073 | 0 | h2o_timer_unlink(&conn->output.defer_timeout); |
1074 | 0 | do_emit_writereq(conn); |
1075 | 0 | } |
1076 | |
|
1077 | 0 | if (!h2o_timer_is_linked(&conn->io_timeout)) |
1078 | 0 | h2o_timer_link(conn->super.ctx->loop, conn->super.ctx->io_timeout, &conn->io_timeout); |
1079 | 0 | } |
1080 | | |
1081 | | static void on_connection_ready(struct st_h2o_http2client_stream_t *stream, struct st_h2o_http2client_conn_t *conn) |
1082 | 0 | { |
1083 | 0 | h2o_iovec_t method; |
1084 | 0 | h2o_url_t url; |
1085 | 0 | h2o_header_t *headers; |
1086 | 0 | size_t num_headers; |
1087 | 0 | h2o_iovec_t body; |
1088 | 0 | h2o_httpclient_properties_t props = (h2o_httpclient_properties_t){NULL}; |
1089 | |
|
1090 | 0 | register_stream(stream, conn); |
1091 | |
|
1092 | 0 | stream->super._cb.on_head = |
1093 | 0 | stream->super._cb.on_connect(&stream->super, NULL, &method, &url, (const h2o_header_t **)&headers, &num_headers, &body, |
1094 | 0 | &stream->output.proceed_req, &props, &conn->super.origin_url); |
1095 | 0 | if (stream->super._cb.on_head == NULL) { |
1096 | 0 | close_stream(stream); |
1097 | 0 | return; |
1098 | 0 | } |
1099 | | |
1100 | 0 | h2o_http2_window_init(&stream->output.window, conn->peer_settings.initial_window_size); |
1101 | | |
1102 | | /* forward request state */ |
1103 | 0 | if (stream->output.proceed_req != NULL) { |
1104 | 0 | stream->state.req = STREAM_STATE_BODY; |
1105 | 0 | } else if (body.base != NULL) { |
1106 | 0 | stream->state.req = STREAM_STATE_BODY; |
1107 | 0 | } else { |
1108 | 0 | stream->state.req = STREAM_STATE_CLOSED; |
1109 | 0 | } |
1110 | |
|
1111 | 0 | if (h2o_memis(method.base, method.len, H2O_STRLIT("HEAD"))) { |
1112 | 0 | stream->input.message_body_forbidden = 1; |
1113 | 0 | } |
1114 | |
|
1115 | 0 | if (props.send_own_expect && (stream->output.proceed_req != NULL || body.len != 0)) |
1116 | 0 | stream->send_own_expect = 1; |
1117 | | |
1118 | | /* send headers */ |
1119 | 0 | h2o_hpack_flatten_request(&conn->output.buf, &conn->output.header_table, conn->peer_settings.header_table_size, |
1120 | 0 | stream->stream_id, conn->peer_settings.max_frame_size, method, &url, |
1121 | 0 | stream->super.upgrade_to != NULL && stream->super.upgrade_to != h2o_httpclient_upgrade_to_connect |
1122 | 0 | ? h2o_iovec_init(stream->super.upgrade_to, strlen(stream->super.upgrade_to)) |
1123 | 0 | : h2o_iovec_init(NULL, 0), |
1124 | 0 | headers, num_headers, stream->state.req == STREAM_STATE_CLOSED, stream->send_own_expect); |
1125 | |
|
1126 | 0 | if (stream->state.req == STREAM_STATE_BODY) { |
1127 | 0 | h2o_buffer_init(&stream->output.buf, &h2o_socket_buffer_prototype); |
1128 | 0 | h2o_buffer_append(&stream->output.buf, body.base, body.len); |
1129 | 0 | } |
1130 | 0 | h2o_linklist_insert(&conn->output.sending_streams, &stream->output.sending_link); |
1131 | 0 | request_write(conn); |
1132 | 0 | } |
1133 | | |
1134 | | static void on_notify_write(h2o_socket_t *sock, const char *err) |
1135 | 0 | { |
1136 | 0 | struct st_h2o_http2client_conn_t *conn = sock->data; |
1137 | |
|
1138 | 0 | if (err != NULL) { |
1139 | 0 | call_stream_callbacks_with_error(conn, h2o_httpclient_error_io); |
1140 | 0 | close_connection_now(conn); |
1141 | 0 | return; |
1142 | 0 | } |
1143 | 0 | do_emit_writereq(conn); |
1144 | 0 | close_connection_if_necessary(conn); |
1145 | 0 | } |
1146 | | |
1147 | | static void on_write_complete(h2o_socket_t *sock, const char *err) |
1148 | 0 | { |
1149 | 0 | struct st_h2o_http2client_conn_t *conn = sock->data; |
1150 | |
|
1151 | 0 | assert(conn->output.buf_in_flight != NULL); |
1152 | | |
1153 | 0 | h2o_timer_unlink(&conn->io_timeout); |
1154 | | |
1155 | | /* close by error if necessary */ |
1156 | 0 | if (err != NULL) { |
1157 | 0 | if (conn->state != H2O_HTTP2CLIENT_CONN_STATE_IS_CLOSING) |
1158 | 0 | call_stream_callbacks_with_error(conn, h2o_httpclient_error_io); |
1159 | 0 | close_connection_now(conn); |
1160 | 0 | return; |
1161 | 0 | } |
1162 | | |
1163 | 0 | if (close_connection_if_necessary(conn)) |
1164 | 0 | return; |
1165 | | |
1166 | | /* unlink timeouts of streams that has finished sending requests */ |
1167 | 0 | while (!h2o_linklist_is_empty(&conn->output.sent_streams)) { |
1168 | 0 | h2o_linklist_t *link = conn->output.sent_streams.next; |
1169 | 0 | struct st_h2o_http2client_stream_t *stream = |
1170 | 0 | H2O_STRUCT_FROM_MEMBER(struct st_h2o_http2client_stream_t, output.sending_link, link); |
1171 | 0 | h2o_linklist_unlink(link); |
1172 | |
|
1173 | 0 | if (stream->send_own_expect && stream->state.req == STREAM_STATE_BODY) { |
1174 | 0 | h2o_timer_link(stream->super.ctx->loop, stream->super.ctx->first_byte_timeout, &stream->super._timeout); |
1175 | 0 | continue; |
1176 | 0 | } |
1177 | | |
1178 | | /* request the app to send more, unless the stream is already closed (note: invocation of `proceed_req` might invoke |
1179 | | * `do_write_req` synchronously) */ |
1180 | 0 | int stream_destroyed = 0; |
1181 | 0 | if (stream->output.proceed_req != NULL) { |
1182 | 0 | stream->notify_destroyed = &stream_destroyed; |
1183 | 0 | stream->output.proceed_req(&stream->super, NULL); |
1184 | 0 | if (stream_destroyed) |
1185 | 0 | continue; |
1186 | 0 | stream->notify_destroyed = NULL; |
1187 | 0 | } |
1188 | | |
1189 | 0 | if (stream->output.proceed_req == NULL && !h2o_linklist_is_linked(&stream->output.sending_link)) { |
1190 | 0 | stream->state.req = STREAM_STATE_CLOSED; |
1191 | 0 | h2o_timer_link(stream->super.ctx->loop, stream->super.ctx->first_byte_timeout, &stream->super._timeout); |
1192 | 0 | } |
1193 | 0 | } |
1194 | | |
1195 | | /* reset the other buffer */ |
1196 | 0 | h2o_buffer_dispose(&conn->output.buf_in_flight); |
1197 | | |
1198 | | /* bail out if nothing can be written */ |
1199 | 0 | if (conn->output.buf->size == 0 && h2o_linklist_is_empty(&conn->output.sending_streams)) { |
1200 | 0 | assert(!h2o_timer_is_linked(&conn->output.defer_timeout)); |
1201 | 0 | close_connection_if_necessary(conn); |
1202 | 0 | return; |
1203 | 0 | } |
1204 | | |
1205 | | /* run next write now instead of relying on the deferred timeout */ |
1206 | 0 | if (h2o_timer_is_linked(&conn->output.defer_timeout)) |
1207 | 0 | h2o_timer_unlink(&conn->output.defer_timeout); |
1208 | 0 | #if !H2O_USE_LIBUV |
1209 | 0 | if (conn->state == H2O_HTTP2CLIENT_CONN_STATE_OPEN) { |
1210 | 0 | h2o_socket_notify_write(sock, on_notify_write); |
1211 | 0 | return; |
1212 | 0 | } |
1213 | 0 | #endif |
1214 | 0 | do_emit_writereq(conn); |
1215 | 0 | close_connection_if_necessary(conn); |
1216 | 0 | } |
1217 | | |
1218 | | static size_t sz_min(size_t x, size_t y) |
1219 | 0 | { |
1220 | 0 | return x < y ? x : y; |
1221 | 0 | } |
1222 | | |
1223 | | static size_t calc_max_payload_size(struct st_h2o_http2client_stream_t *stream) |
1224 | 0 | { |
1225 | 0 | ssize_t conn_max, stream_max; |
1226 | |
|
1227 | 0 | if ((conn_max = conn_get_buffer_window(stream->conn)) <= 0) |
1228 | 0 | return 0; |
1229 | 0 | if ((stream_max = h2o_http2_window_get_avail(&stream->output.window)) <= 0) |
1230 | 0 | return 0; |
1231 | 0 | return sz_min(sz_min(conn_max, stream_max), stream->conn->peer_settings.max_frame_size); |
1232 | 0 | } |
1233 | | |
1234 | | static void stream_emit_pending_data(struct st_h2o_http2client_stream_t *stream) |
1235 | 0 | { |
1236 | 0 | size_t max_payload_size = calc_max_payload_size(stream); |
1237 | 0 | size_t payload_size = sz_min(max_payload_size, stream->output.buf->size); |
1238 | 0 | int end_stream = stream->output.proceed_req == NULL && payload_size == stream->output.buf->size; |
1239 | 0 | if (payload_size == 0 && !end_stream) |
1240 | 0 | return; |
1241 | 0 | char *dst = h2o_buffer_reserve(&stream->conn->output.buf, H2O_HTTP2_FRAME_HEADER_SIZE + payload_size).base; |
1242 | 0 | h2o_http2_encode_frame_header((void *)dst, payload_size, H2O_HTTP2_FRAME_TYPE_DATA, |
1243 | 0 | end_stream ? H2O_HTTP2_FRAME_FLAG_END_STREAM : 0, stream->stream_id); |
1244 | 0 | h2o_memcpy(dst + H2O_HTTP2_FRAME_HEADER_SIZE, stream->output.buf->bytes, payload_size); |
1245 | 0 | stream->conn->output.buf->size += H2O_HTTP2_FRAME_HEADER_SIZE + payload_size; |
1246 | 0 | h2o_buffer_consume(&stream->output.buf, payload_size); |
1247 | |
|
1248 | 0 | h2o_http2_window_consume_window(&stream->conn->output.window, payload_size); |
1249 | 0 | h2o_http2_window_consume_window(&stream->output.window, payload_size); |
1250 | 0 | } |
1251 | | |
1252 | | static void do_emit_writereq(struct st_h2o_http2client_conn_t *conn) |
1253 | 0 | { |
1254 | 0 | assert(conn->output.buf_in_flight == NULL); |
1255 | | |
1256 | | /* send DATA frames */ |
1257 | 0 | h2o_linklist_t pending; |
1258 | 0 | h2o_linklist_init_anchor(&pending); |
1259 | 0 | h2o_linklist_insert_list(&pending, &conn->output.sending_streams); |
1260 | 0 | while (!h2o_linklist_is_empty(&pending)) { |
1261 | 0 | struct st_h2o_http2client_stream_t *stream = |
1262 | 0 | H2O_STRUCT_FROM_MEMBER(struct st_h2o_http2client_stream_t, output.sending_link, pending.next); |
1263 | 0 | h2o_linklist_unlink(&stream->output.sending_link); |
1264 | |
|
1265 | 0 | if (stream->output.buf != NULL && !stream->send_own_expect) |
1266 | 0 | stream_emit_pending_data(stream); |
1267 | |
|
1268 | 0 | if (stream->output.buf == NULL || stream->output.buf->size == 0) { |
1269 | 0 | h2o_linklist_insert(&conn->output.sent_streams, &stream->output.sending_link); |
1270 | 0 | } else if (h2o_http2_window_get_avail(&stream->output.window) > 0 && !stream->send_own_expect) { |
1271 | | /* re-insert to tail so that streams would be sent round-robin */ |
1272 | 0 | h2o_linklist_insert(&conn->output.sending_streams, &stream->output.sending_link); |
1273 | 0 | } else { |
1274 | | /* stream is flow-control-blocked; is not linked until WINDOW_UPDATE frame is received */ |
1275 | 0 | } |
1276 | 0 | } |
1277 | |
|
1278 | 0 | if (conn->output.buf->size != 0) { |
1279 | | /* write and wait for completion */ |
1280 | 0 | h2o_iovec_t buf = {conn->output.buf->bytes, conn->output.buf->size}; |
1281 | 0 | h2o_socket_write(conn->super.sock, &buf, 1, on_write_complete); |
1282 | 0 | conn->output.buf_in_flight = conn->output.buf; |
1283 | 0 | h2o_buffer_init(&conn->output.buf, &h2o_http2_wbuf_buffer_prototype); |
1284 | 0 | if (!h2o_timer_is_linked(&conn->io_timeout)) |
1285 | 0 | h2o_timer_link(conn->super.ctx->loop, conn->super.ctx->io_timeout, &conn->io_timeout); |
1286 | 0 | } |
1287 | 0 | } |
1288 | | |
1289 | | static void emit_writereq(h2o_timer_t *entry) |
1290 | 0 | { |
1291 | 0 | struct st_h2o_http2client_conn_t *conn = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http2client_conn_t, output.defer_timeout, entry); |
1292 | 0 | do_emit_writereq(conn); |
1293 | 0 | } |
1294 | | |
1295 | | static struct st_h2o_http2client_conn_t *create_connection(h2o_httpclient_ctx_t *ctx, h2o_socket_t *sock, h2o_url_t *origin_url, |
1296 | | h2o_httpclient_connection_pool_t *connpool) |
1297 | 0 | { |
1298 | 0 | struct st_h2o_http2client_conn_t *conn = h2o_mem_alloc(sizeof(*conn)); |
1299 | |
|
1300 | 0 | memset(conn, 0, offsetof(struct st_h2o_http2client_conn_t, rst_streams_pool)); |
1301 | 0 | h2o_mem_init_pool(&conn->rst_streams_pool); |
1302 | |
|
1303 | 0 | conn->super.ctx = ctx; |
1304 | 0 | conn->super.sock = sock; |
1305 | 0 | conn->state = H2O_HTTP2CLIENT_CONN_STATE_OPEN; |
1306 | 0 | conn->peer_settings = H2O_HTTP2_SETTINGS_DEFAULT; |
1307 | 0 | conn->streams = kh_init(stream); |
1308 | 0 | h2o_url_copy(NULL, &conn->super.origin_url, origin_url); |
1309 | 0 | if (connpool != NULL) |
1310 | 0 | h2o_linklist_insert(&connpool->http2.conns, &conn->super.link); |
1311 | 0 | conn->io_timeout.cb = on_io_timeout; |
1312 | 0 | conn->keepalive_timeout.cb = on_keepalive_timeout; |
1313 | | |
1314 | | /* output */ |
1315 | 0 | conn->output.header_table.hpack_capacity = H2O_HTTP2_SETTINGS_CLIENT_HEADER_TABLE_SIZE; |
1316 | 0 | h2o_http2_window_init(&conn->output.window, conn->peer_settings.initial_window_size); |
1317 | 0 | h2o_buffer_init(&conn->output.buf, &h2o_http2_wbuf_buffer_prototype); |
1318 | 0 | conn->output.defer_timeout.cb = emit_writereq; |
1319 | 0 | h2o_linklist_init_anchor(&conn->output.sending_streams); |
1320 | 0 | h2o_linklist_init_anchor(&conn->output.sent_streams); |
1321 | | |
1322 | | /* input */ |
1323 | 0 | conn->input.header_table.hpack_capacity = conn->input.header_table.hpack_max_capacity = |
1324 | 0 | H2O_HTTP2_SETTINGS_DEFAULT.header_table_size; |
1325 | 0 | h2o_http2_window_init(&conn->input.window, H2O_HTTP2_SETTINGS_DEFAULT.initial_window_size); |
1326 | 0 | conn->input.read_frame = expect_settings; |
1327 | |
|
1328 | 0 | return conn; |
1329 | 0 | } |
1330 | | |
1331 | | static void send_client_preface(struct st_h2o_http2client_conn_t *conn, h2o_httpclient_ctx_t *ctx) |
1332 | 0 | { |
1333 | 0 | #define PREFIX \ |
1334 | 0 | "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" \ |
1335 | 0 | "\x00\x00\x12" /* frame size */ \ |
1336 | 0 | "\x04" /* settings frame */ \ |
1337 | 0 | "\x00" /* no flags */ \ |
1338 | 0 | "\x00\x00\x00\x00" /* stream id */ \ |
1339 | 0 | "\x00\x02" /* enable_push */ \ |
1340 | 0 | "\x00\x00\x00\x00" /* 0 */ \ |
1341 | 0 | "\x00\x03" /* max_concurrent_streams */ \ |
1342 | 0 | "\x00\x00\x00\x64" /* 100 */ \ |
1343 | 0 | "\x00\x04" /* initial_window_size */ |
1344 | 0 | static const size_t len = sizeof(PREFIX) - 1 + 4; |
1345 | |
|
1346 | 0 | uint32_t initial_window_size = get_max_buffer_size(ctx); |
1347 | |
|
1348 | 0 | h2o_iovec_t vec = h2o_buffer_reserve(&conn->output.buf, len); |
1349 | 0 | memcpy(vec.base, PREFIX, sizeof(PREFIX) - 1); |
1350 | | |
1351 | | /* encode max_buffer_size */ |
1352 | 0 | vec.base[len - 4] = (char)((initial_window_size >> 24) & 0xff); |
1353 | 0 | vec.base[len - 3] = (char)((initial_window_size >> 16) & 0xff); |
1354 | 0 | vec.base[len - 2] = (char)((initial_window_size >> 8) & 0xff); |
1355 | 0 | vec.base[len - 1] = (char)(initial_window_size & 0xff); |
1356 | |
|
1357 | 0 | conn->output.buf->size += len; |
1358 | 0 | request_write(conn); |
1359 | 0 | #undef PREFIX |
1360 | 0 | } |
1361 | | |
1362 | | static void do_cancel(h2o_httpclient_t *_client) |
1363 | 0 | { |
1364 | 0 | struct st_h2o_http2client_stream_t *stream = (void *)_client; |
1365 | 0 | stream_send_error(stream->conn, stream->stream_id, H2O_HTTP2_ERROR_CANCEL); |
1366 | 0 | close_stream(stream); |
1367 | 0 | } |
1368 | | |
1369 | | static void do_get_conn_properties(h2o_httpclient_t *_client, h2o_httpclient_conn_properties_t *properties) |
1370 | 0 | { |
1371 | 0 | struct st_h2o_http2client_stream_t *stream = (void *)_client; |
1372 | 0 | h2o_httpclient_set_conn_properties_of_socket(stream->conn->super.sock, properties); |
1373 | 0 | } |
1374 | | |
1375 | | static void do_update_window(h2o_httpclient_t *_client) |
1376 | 0 | { |
1377 | 0 | struct st_h2o_http2client_stream_t *stream = (void *)_client; |
1378 | 0 | size_t max = get_max_buffer_size(stream->super.ctx); |
1379 | 0 | size_t bufsize = stream->input.body->size; |
1380 | 0 | assert(bufsize <= max); |
1381 | 0 | enqueue_window_update(stream->conn, stream->stream_id, &stream->input.window, max - bufsize); |
1382 | 0 | } |
1383 | | |
1384 | | static int do_write_req(h2o_httpclient_t *_client, h2o_iovec_t chunk, int is_end_stream) |
1385 | 0 | { |
1386 | 0 | struct st_h2o_http2client_stream_t *stream = (void *)_client; |
1387 | 0 | assert(stream->output.proceed_req != NULL); |
1388 | 0 | assert(!h2o_linklist_is_linked(&stream->output.sending_link)); |
1389 | | |
1390 | 0 | if (is_end_stream) |
1391 | 0 | stream->output.proceed_req = NULL; |
1392 | |
|
1393 | 0 | if (stream->output.buf == NULL) |
1394 | 0 | h2o_buffer_init(&stream->output.buf, &h2o_socket_buffer_prototype); |
1395 | |
|
1396 | 0 | if (chunk.len != 0) |
1397 | 0 | h2o_buffer_append(&stream->output.buf, chunk.base, chunk.len); |
1398 | |
|
1399 | 0 | h2o_linklist_insert(&stream->conn->output.sending_streams, &stream->output.sending_link); |
1400 | 0 | request_write(stream->conn); |
1401 | |
|
1402 | 0 | return 0; |
1403 | 0 | } |
1404 | | |
1405 | | static void setup_stream(struct st_h2o_http2client_stream_t *stream) |
1406 | 0 | { |
1407 | 0 | memset(&stream->conn, 0, sizeof(*stream) - offsetof(struct st_h2o_http2client_stream_t, conn)); |
1408 | |
|
1409 | 0 | stream->super._timeout.cb = on_stream_timeout; |
1410 | 0 | h2o_http2_window_init(&stream->input.window, get_max_buffer_size(stream->super.ctx)); |
1411 | 0 | h2o_buffer_init(&stream->input.body, &h2o_socket_buffer_prototype); |
1412 | 0 | stream->input.remaining_content_length = SIZE_MAX; |
1413 | 0 | stream->input.message_body_forbidden = 0; |
1414 | |
|
1415 | 0 | stream->super.buf = &stream->input.body; |
1416 | 0 | stream->super.cancel = do_cancel; |
1417 | 0 | stream->super.get_conn_properties = do_get_conn_properties; |
1418 | 0 | stream->super.update_window = do_update_window; |
1419 | 0 | stream->super.write_req = do_write_req; |
1420 | 0 | } |
1421 | | |
1422 | | void h2o_httpclient__h2_on_connect(h2o_httpclient_t *_client, h2o_socket_t *sock, h2o_url_t *origin) |
1423 | 0 | { |
1424 | 0 | struct st_h2o_http2client_stream_t *stream = (void *)_client; |
1425 | |
|
1426 | 0 | assert(!h2o_timer_is_linked(&stream->super._timeout)); |
1427 | | |
1428 | 0 | struct st_h2o_http2client_conn_t *conn = sock->data; |
1429 | 0 | if (conn == NULL) { |
1430 | 0 | conn = create_connection(stream->super.ctx, sock, origin, stream->super.connpool); |
1431 | 0 | sock->data = conn; |
1432 | | /* send preface, settings, and connection-level window update */ |
1433 | 0 | send_client_preface(conn, stream->super.ctx); |
1434 | 0 | h2o_socket_read_start(conn->super.sock, on_read); |
1435 | 0 | } |
1436 | |
|
1437 | 0 | setup_stream(stream); |
1438 | |
|
1439 | 0 | if (!h2o_timer_is_linked(&conn->io_timeout)) |
1440 | 0 | h2o_timer_link(conn->super.ctx->loop, conn->super.ctx->io_timeout, &conn->io_timeout); |
1441 | 0 | on_connection_ready(stream, conn); |
1442 | 0 | } |
1443 | | |
1444 | | const size_t h2o_httpclient__h2_size = sizeof(struct st_h2o_http2client_stream_t); |