1 /*
2  * Copyright (c) 2014 DeNA Co., Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to
6  * deal in the Software without restriction, including without limitation the
7  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8  * sell copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20  * IN THE SOFTWARE.
21  */
22 #include "h2o.h"
23 #include "h2o/http2.h"
24 #include "h2o/http2_internal.h"
25 #include "h2o/absprio.h"
26 #include "../probes_.h"
27 
28 static void finalostream_send(h2o_ostream_t *self, h2o_req_t *req, h2o_sendvec_t *bufs, size_t bufcnt, h2o_send_state_t state);
29 static void finalostream_send_informational(h2o_ostream_t *_self, h2o_req_t *req);
30 
sz_min(size_t x,size_t y)31 static size_t sz_min(size_t x, size_t y)
32 {
33     return x < y ? x : y;
34 }
35 
h2o_http2_stream_open(h2o_http2_conn_t * conn,uint32_t stream_id,h2o_req_t * src_req,const h2o_http2_priority_t * received_priority)36 h2o_http2_stream_t *h2o_http2_stream_open(h2o_http2_conn_t *conn, uint32_t stream_id, h2o_req_t *src_req,
37                                           const h2o_http2_priority_t *received_priority)
38 {
39     h2o_http2_stream_t *stream = h2o_mem_alloc(sizeof(*stream));
40 
41     /* init properties (other than req) */
42     memset(stream, 0, offsetof(h2o_http2_stream_t, req));
43     stream->stream_id = stream_id;
44     stream->_ostr_final.do_send = finalostream_send;
45     stream->_ostr_final.send_informational =
46         conn->super.ctx->globalconf->send_informational_mode == H2O_SEND_INFORMATIONAL_MODE_NONE ? NULL
47                                                                                                  : finalostream_send_informational;
48     stream->state = H2O_HTTP2_STREAM_STATE_IDLE;
49     h2o_http2_window_init(&stream->output_window, conn->peer_settings.initial_window_size);
50     h2o_http2_window_init(&stream->input_window.window, H2O_HTTP2_SETTINGS_HOST_STREAM_INITIAL_WINDOW_SIZE);
51     stream->received_priority = *received_priority;
52 
53     /* init request */
54     h2o_init_request(&stream->req, &conn->super, src_req);
55     stream->req.version = 0x200;
56     if (src_req != NULL)
57         memset(&stream->req.upgrade, 0, sizeof(stream->req.upgrade));
58     stream->req._ostr_top = &stream->_ostr_final;
59 
60     h2o_http2_conn_register_stream(conn, stream);
61 
62     ++conn->num_streams.priority.open;
63     stream->_num_streams_slot = &conn->num_streams.priority;
64 
65     return stream;
66 }
67 
h2o_http2_stream_close(h2o_http2_conn_t * conn,h2o_http2_stream_t * stream)68 void h2o_http2_stream_close(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream)
69 {
70     h2o_http2_conn_unregister_stream(conn, stream);
71     if (stream->cache_digests != NULL)
72         h2o_cache_digests_destroy(stream->cache_digests);
73     if (stream->req_body.buf != NULL)
74         h2o_buffer_dispose(&stream->req_body.buf);
75     h2o_dispose_request(&stream->req);
76     if (stream->stream_id == 1 && conn->_http1_req_input != NULL)
77         h2o_buffer_dispose(&conn->_http1_req_input);
78     free(stream);
79 }
80 
h2o_http2_stream_reset(h2o_http2_conn_t * conn,h2o_http2_stream_t * stream)81 void h2o_http2_stream_reset(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream)
82 {
83     switch (stream->state) {
84     case H2O_HTTP2_STREAM_STATE_IDLE:
85     case H2O_HTTP2_STREAM_STATE_RECV_HEADERS:
86     case H2O_HTTP2_STREAM_STATE_RECV_BODY:
87     case H2O_HTTP2_STREAM_STATE_REQ_PENDING:
88         h2o_http2_stream_close(conn, stream);
89         break;
90     case H2O_HTTP2_STREAM_STATE_SEND_HEADERS:
91     case H2O_HTTP2_STREAM_STATE_SEND_BODY:
92     case H2O_HTTP2_STREAM_STATE_SEND_BODY_IS_FINAL:
93         h2o_http2_stream_set_state(conn, stream, H2O_HTTP2_STREAM_STATE_END_STREAM);
94     /* continues */
95     case H2O_HTTP2_STREAM_STATE_END_STREAM:
96         /* clear all the queued bufs, and close the connection in the callback */
97         stream->_data.size = 0;
98         if (h2o_linklist_is_linked(&stream->_link)) {
99             /* will be closed in the callback */
100         } else {
101             h2o_http2_stream_close(conn, stream);
102         }
103         break;
104     }
105 }
106 
calc_max_payload_size(h2o_http2_conn_t * conn,h2o_http2_stream_t * stream)107 static size_t calc_max_payload_size(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream)
108 {
109     ssize_t conn_max, stream_max;
110 
111     if ((conn_max = h2o_http2_conn_get_buffer_window(conn)) <= 0)
112         return 0;
113     if ((stream_max = h2o_http2_window_get_avail(&stream->output_window)) <= 0)
114         return 0;
115     return sz_min(sz_min(conn_max, stream_max), conn->peer_settings.max_frame_size);
116 }
117 
commit_data_header(h2o_http2_conn_t * conn,h2o_http2_stream_t * stream,h2o_buffer_t ** outbuf,size_t length,h2o_send_state_t send_state)118 static void commit_data_header(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream, h2o_buffer_t **outbuf, size_t length,
119                                h2o_send_state_t send_state)
120 {
121     assert(outbuf != NULL);
122     /* send a DATA frame if there's data or the END_STREAM flag to send */
123     if (length || send_state == H2O_SEND_STATE_FINAL) {
124         h2o_http2_encode_frame_header(
125             (void *)((*outbuf)->bytes + (*outbuf)->size), length, H2O_HTTP2_FRAME_TYPE_DATA,
126             (send_state == H2O_SEND_STATE_FINAL && !stream->req.send_server_timing) ? H2O_HTTP2_FRAME_FLAG_END_STREAM : 0,
127             stream->stream_id);
128         h2o_http2_window_consume_window(&conn->_write.window, length);
129         h2o_http2_window_consume_window(&stream->output_window, length);
130         (*outbuf)->size += length + H2O_HTTP2_FRAME_HEADER_SIZE;
131         stream->req.bytes_sent += length;
132     }
133     /* send a RST_STREAM if there's an error */
134     if (send_state == H2O_SEND_STATE_ERROR) {
135         h2o_http2_encode_rst_stream_frame(
136             outbuf, stream->stream_id, -(stream->req.upstream_refused ? H2O_HTTP2_ERROR_REFUSED_STREAM : H2O_HTTP2_ERROR_PROTOCOL));
137     }
138 }
139 
send_data(h2o_http2_conn_t * conn,h2o_http2_stream_t * stream,h2o_sendvec_t * bufs,size_t bufcnt,size_t * off_within_buf,h2o_send_state_t send_state)140 static h2o_sendvec_t *send_data(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream, h2o_sendvec_t *bufs, size_t bufcnt,
141                                 size_t *off_within_buf, h2o_send_state_t send_state)
142 {
143     h2o_iovec_t dst;
144     size_t max_payload_size;
145 
146     if ((max_payload_size = calc_max_payload_size(conn, stream)) == 0)
147         goto Exit;
148 
149     /* reserve buffer and point dst to the payload */
150     dst.base =
151         h2o_buffer_reserve(&conn->_write.buf, H2O_HTTP2_FRAME_HEADER_SIZE + max_payload_size).base + H2O_HTTP2_FRAME_HEADER_SIZE;
152     dst.len = max_payload_size;
153 
154     /* emit data */
155     while (bufcnt != 0) {
156         if (bufs->len != *off_within_buf)
157             break;
158         ++bufs;
159         --bufcnt;
160         *off_within_buf = 0;
161     }
162     while (bufcnt != 0) {
163         size_t fill_size = sz_min(dst.len, bufs->len - *off_within_buf);
164         if (!(*bufs->callbacks->flatten)(bufs, &stream->req, h2o_iovec_init(dst.base, fill_size), *off_within_buf))
165             return NULL;
166         dst.base += fill_size;
167         dst.len -= fill_size;
168         *off_within_buf += fill_size;
169         while (bufs->len == *off_within_buf) {
170             ++bufs;
171             --bufcnt;
172             *off_within_buf = 0;
173             if (bufcnt == 0)
174                 break;
175         }
176         if (dst.len == 0)
177             break;
178     }
179 
180     /* commit the DATA frame if we have actually emitted payload */
181     if (dst.len != max_payload_size || !h2o_send_state_is_in_progress(send_state)) {
182         size_t payload_len = max_payload_size - dst.len;
183         if (bufcnt != 0) {
184             send_state = H2O_SEND_STATE_IN_PROGRESS;
185         }
186         commit_data_header(conn, stream, &conn->_write.buf, payload_len, send_state);
187     }
188 
189 Exit:
190     return bufs;
191 }
192 
is_blocking_asset(h2o_req_t * req)193 static int is_blocking_asset(h2o_req_t *req)
194 {
195     if (req->res.mime_attr == NULL)
196         h2o_req_fill_mime_attributes(req);
197     return req->res.mime_attr->priority == H2O_MIME_ATTRIBUTE_PRIORITY_HIGHEST;
198 }
199 
send_refused_stream(h2o_http2_conn_t * conn,h2o_http2_stream_t * stream)200 static void send_refused_stream(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream)
201 {
202     h2o_http2_encode_rst_stream_frame(&conn->_write.buf, stream->stream_id, -H2O_HTTP2_ERROR_REFUSED_STREAM);
203     h2o_http2_conn_request_write(conn);
204     h2o_http2_stream_close(conn, stream);
205 }
206 
send_headers(h2o_http2_conn_t * conn,h2o_http2_stream_t * stream)207 static int send_headers(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream)
208 {
209     if (stream->req.res.status == 425 && stream->req.reprocess_if_too_early) {
210         h2o_http2_conn_register_for_replay(conn, stream);
211         return -1;
212     }
213 
214     stream->req.timestamps.response_start_at = h2o_gettimeofday(conn->super.ctx->loop);
215 
216     /* cancel push with an error response */
217     if (h2o_http2_stream_is_push(stream->stream_id)) {
218         if (400 <= stream->req.res.status)
219             goto CancelPush;
220         if (stream->cache_digests != NULL) {
221             ssize_t etag_index = h2o_find_header(&stream->req.headers, H2O_TOKEN_ETAG, -1);
222             if (etag_index != -1) {
223                 h2o_iovec_t url = h2o_concat(&stream->req.pool, stream->req.input.scheme->name, h2o_iovec_init(H2O_STRLIT("://")),
224                                              stream->req.input.authority, stream->req.input.path);
225                 h2o_iovec_t *etag = &stream->req.headers.entries[etag_index].value;
226                 if (h2o_cache_digests_lookup_by_url_and_etag(stream->cache_digests, url.base, url.len, etag->base, etag->len) ==
227                     H2O_CACHE_DIGESTS_STATE_FRESH)
228                     goto CancelPush;
229             }
230         }
231     }
232 
233     /* reset casper cookie in case cache-digests exist */
234     if (stream->cache_digests != NULL && stream->req.hostconf->http2.casper.capacity_bits != 0) {
235         h2o_add_header(&stream->req.pool, &stream->req.res.headers, H2O_TOKEN_SET_COOKIE, NULL,
236                        H2O_STRLIT("h2o_casper=; Path=/; Expires=Sat, 01 Jan 2000 00:00:00 GMT"));
237     }
238 
239     /* CASPER */
240     if (conn->casper != NULL) {
241         /* update casper if necessary */
242         if (stream->req.hostconf->http2.casper.track_all_types || is_blocking_asset(&stream->req)) {
243             if (h2o_http2_casper_lookup(conn->casper, stream->req.path.base, stream->req.path.len, 1)) {
244                 /* cancel if the pushed resource is already marked as cached */
245                 if (h2o_http2_stream_is_push(stream->stream_id))
246                     goto CancelPush;
247             }
248         }
249         if (stream->cache_digests != NULL)
250             goto SkipCookie;
251         /* browsers might ignore push responses, or they may process the responses in a different order than they were pushed.
252          * Therefore H2O tries to include casper cookie only in the last stream that may be received by the client, or when the
253          * value become stable; see also: https://github.com/h2o/h2o/issues/421
254          */
255         if (h2o_http2_stream_is_push(stream->stream_id)) {
256             if (!(conn->num_streams.pull.open == 0 && (conn->num_streams.push.half_closed - conn->num_streams.push.send_body) == 1))
257                 goto SkipCookie;
258         } else {
259             if (conn->num_streams.push.half_closed - conn->num_streams.push.send_body != 0)
260                 goto SkipCookie;
261         }
262         h2o_iovec_t cookie = h2o_http2_casper_get_cookie(conn->casper);
263         h2o_add_header(&stream->req.pool, &stream->req.res.headers, H2O_TOKEN_SET_COOKIE, NULL, cookie.base, cookie.len);
264     SkipCookie:;
265     }
266 
267     if (h2o_http2_stream_is_push(stream->stream_id)) {
268         /* for push, send the push promise */
269         if (!stream->push.promise_sent)
270             h2o_http2_stream_send_push_promise(conn, stream);
271         /* send ASAP if it is a blocking asset (even in case of Firefox we can't wait 1RTT for it to reprioritize the asset) */
272         if (is_blocking_asset(&stream->req))
273             h2o_http2_scheduler_rebind(&stream->_scheduler, &conn->scheduler, 257, 0);
274     } else {
275         /* Handle absolute priority header */
276         ssize_t absprio_cursor = h2o_find_header(&stream->req.res.headers, H2O_TOKEN_PRIORITY, -1);
277         if (absprio_cursor != -1 && conn->is_chromium_dependency_tree) {
278             /* Found absolute priority header in the response header */
279             h2o_absprio_t prio = h2o_absprio_default;
280             h2o_iovec_t *header_value = &stream->req.res.headers.entries[absprio_cursor].value;
281             h2o_absprio_parse_priority(header_value->base, header_value->len, &prio);
282             uint16_t new_weight = h2o_absprio_urgency_to_chromium_weight(prio.urgency);
283             h2o_http2_scheduler_node_t *new_parent = h2o_http2_scheduler_find_parent_by_weight(&conn->scheduler, new_weight);
284             if (new_parent == &stream->_scheduler.node) {
285                 /* find_new_parent might return `stream` itself. In this case re-specify the current
286                  * parent as a new parent */
287                 new_parent = h2o_http2_scheduler_get_parent(&stream->_scheduler);
288             }
289             if (new_parent != h2o_http2_scheduler_get_parent(&stream->_scheduler) ||
290                 new_weight != h2o_http2_scheduler_get_weight(&stream->_scheduler)) {
291                 /* Reprioritize the stream based on priority header information */
292 
293                 /* First, preserve the current (client-given) priority information so that subsequent
294                  * streams from the client can correctly refer to the original priority. */
295                 h2o_http2_conn_preserve_stream_scheduler(conn, stream);
296                 /* Open a new scheduler for the modified priority information for this stream */
297                 h2o_http2_scheduler_open(&stream->_scheduler, new_parent, new_weight, 1);
298             }
299         } else if (conn->num_streams.priority.open == 0 && stream->req.hostconf->http2.reprioritize_blocking_assets &&
300                    h2o_http2_scheduler_get_parent(&stream->_scheduler) == &conn->scheduler && is_blocking_asset(&stream->req)) {
301             /* raise the priority of asset files that block rendering to highest if the user-agent is _not_ using dependency-based
302              * prioritization (e.g. that of Firefox)
303              */
304             h2o_http2_scheduler_rebind(&stream->_scheduler, &conn->scheduler, 257, 0);
305         }
306     }
307 
308     /* send HEADERS, as well as start sending body */
309     if (h2o_http2_stream_is_push(stream->stream_id))
310         h2o_add_header_by_str(&stream->req.pool, &stream->req.res.headers, H2O_STRLIT("x-http2-push"), 0, NULL,
311                               H2O_STRLIT("pushed"));
312     if (stream->req.send_server_timing)
313         h2o_add_server_timing_header(&stream->req, 1);
314     h2o_hpack_flatten_response(&conn->_write.buf, &conn->_output_header_table, conn->peer_settings.header_table_size,
315                                stream->stream_id, conn->peer_settings.max_frame_size, stream->req.res.status,
316                                stream->req.res.headers.entries, stream->req.res.headers.size,
317                                &conn->super.ctx->globalconf->server_name, stream->req.res.content_length);
318     h2o_http2_conn_request_write(conn);
319     h2o_http2_stream_set_state(conn, stream, H2O_HTTP2_STREAM_STATE_SEND_BODY);
320 
321     return 0;
322 
323 CancelPush:
324     h2o_add_header_by_str(&stream->req.pool, &stream->req.res.headers, H2O_STRLIT("x-http2-push"), 0, NULL,
325                           H2O_STRLIT("cancelled"));
326     h2o_http2_stream_set_state(conn, stream, H2O_HTTP2_STREAM_STATE_END_STREAM);
327     h2o_linklist_insert(&conn->_write.streams_to_proceed, &stream->_link);
328     if (stream->push.promise_sent) {
329         h2o_http2_encode_rst_stream_frame(&conn->_write.buf, stream->stream_id, -H2O_HTTP2_ERROR_INTERNAL);
330         h2o_http2_conn_request_write(conn);
331     }
332     return -1;
333 }
334 
finalostream_send(h2o_ostream_t * self,h2o_req_t * req,h2o_sendvec_t * bufs,size_t bufcnt,h2o_send_state_t state)335 void finalostream_send(h2o_ostream_t *self, h2o_req_t *req, h2o_sendvec_t *bufs, size_t bufcnt, h2o_send_state_t state)
336 {
337     h2o_http2_stream_t *stream = H2O_STRUCT_FROM_MEMBER(h2o_http2_stream_t, _ostr_final, self);
338     h2o_http2_conn_t *conn = (h2o_http2_conn_t *)req->conn;
339 
340     assert(stream->_data.size == 0);
341 
342     if (stream->blocked_by_server)
343         h2o_http2_stream_set_blocked_by_server(conn, stream, 0);
344 
345     stream->send_state = state;
346 
347     /* send headers */
348     switch (stream->state) {
349     case H2O_HTTP2_STREAM_STATE_RECV_BODY:
350         h2o_http2_stream_set_state(conn, stream, H2O_HTTP2_STREAM_STATE_REQ_PENDING);
351     /* fallthru */
352     case H2O_HTTP2_STREAM_STATE_REQ_PENDING:
353         h2o_http2_stream_set_state(conn, stream, H2O_HTTP2_STREAM_STATE_SEND_HEADERS);
354     /* fallthru */
355     case H2O_HTTP2_STREAM_STATE_SEND_HEADERS:
356         if (stream->req.upstream_refused) {
357             send_refused_stream(conn, stream);
358             return;
359         }
360         h2o_probe_log_response(&stream->req, stream->stream_id);
361         if (send_headers(conn, stream) != 0)
362             return;
363     /* fallthru */
364     case H2O_HTTP2_STREAM_STATE_SEND_BODY:
365         if (state != H2O_SEND_STATE_IN_PROGRESS) {
366             h2o_http2_stream_set_state(conn, stream, H2O_HTTP2_STREAM_STATE_SEND_BODY_IS_FINAL);
367         }
368         break;
369     case H2O_HTTP2_STREAM_STATE_END_STREAM:
370         /* might get set by h2o_http2_stream_reset */
371         return;
372     default:
373         assert(!"cannot be in a receiving state");
374     }
375 
376     /* save the contents in queue */
377     if (bufcnt != 0) {
378         h2o_vector_reserve(&req->pool, &stream->_data, bufcnt);
379         memcpy(stream->_data.entries, bufs, sizeof(*bufs) * bufcnt);
380         stream->_data.size = bufcnt;
381     }
382 
383     h2o_http2_conn_register_for_proceed_callback(conn, stream);
384 }
385 
finalostream_send_informational(h2o_ostream_t * self,h2o_req_t * req)386 static void finalostream_send_informational(h2o_ostream_t *self, h2o_req_t *req)
387 {
388     h2o_http2_stream_t *stream = H2O_STRUCT_FROM_MEMBER(h2o_http2_stream_t, _ostr_final, self);
389     h2o_http2_conn_t *conn = (h2o_http2_conn_t *)req->conn;
390 
391     h2o_hpack_flatten_response(&conn->_write.buf, &conn->_output_header_table, conn->peer_settings.header_table_size,
392                                stream->stream_id, conn->peer_settings.max_frame_size, req->res.status, req->res.headers.entries,
393                                req->res.headers.size, NULL, SIZE_MAX);
394     h2o_http2_conn_request_write(conn);
395 }
396 
h2o_http2_stream_send_pending_data(h2o_http2_conn_t * conn,h2o_http2_stream_t * stream)397 void h2o_http2_stream_send_pending_data(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream)
398 {
399     if (h2o_http2_window_get_avail(&stream->output_window) <= 0)
400         return;
401 
402     h2o_send_state_t send_state = stream->send_state;
403     h2o_sendvec_t *nextbuf =
404         send_data(conn, stream, stream->_data.entries, stream->_data.size, &stream->_data_off, stream->send_state);
405     if (nextbuf == NULL) {
406         /* error */
407         stream->_data.size = 0;
408         stream->send_state = H2O_SEND_STATE_ERROR;
409         h2o_http2_stream_set_state(conn, stream, H2O_HTTP2_STREAM_STATE_END_STREAM);
410     } else if (nextbuf == stream->_data.entries + stream->_data.size) {
411         /* sent all data */
412         stream->_data.size = 0;
413         if (stream->state == H2O_HTTP2_STREAM_STATE_SEND_BODY_IS_FINAL)
414             h2o_http2_stream_set_state(conn, stream, H2O_HTTP2_STREAM_STATE_END_STREAM);
415     } else if (nextbuf != stream->_data.entries) {
416         /* adjust the buffer */
417         size_t newsize = stream->_data.size - (nextbuf - stream->_data.entries);
418         memmove(stream->_data.entries, nextbuf, sizeof(stream->_data.entries[0]) * newsize);
419         stream->_data.size = newsize;
420     }
421 
422     if (send_state == H2O_SEND_STATE_ERROR) {
423         stream->req.send_server_timing = 0; /* suppress sending trailers */
424     }
425 }
426 
h2o_http2_stream_proceed(h2o_http2_conn_t * conn,h2o_http2_stream_t * stream)427 void h2o_http2_stream_proceed(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream)
428 {
429     if (stream->state == H2O_HTTP2_STREAM_STATE_END_STREAM) {
430         if (stream->req_body.state == H2O_HTTP2_REQ_BODY_CLOSE_DELIVERED)
431             h2o_http2_stream_close(conn, stream);
432     } else {
433         if (!stream->blocked_by_server)
434             h2o_http2_stream_set_blocked_by_server(conn, stream, 1);
435         h2o_proceed_response(&stream->req);
436     }
437 }
438