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