2 * nghttp2 - HTTP/2 C Library
4 * Copyright (c) 2012 Tatsuhiro Tsujikawa
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 #include "nghttp2_session.h"
32 #include "nghttp2_helper.h"
33 #include "nghttp2_net.h"
34 #include "nghttp2_priority_spec.h"
35 #include "nghttp2_option.h"
36 #include "nghttp2_http.h"
39 * Returns non-zero if the number of outgoing opened streams is larger
41 * remote_settings.max_concurrent_streams.
44 session_is_outgoing_concurrent_streams_max(nghttp2_session *session) {
45 return session->remote_settings.max_concurrent_streams <=
46 session->num_outgoing_streams;
50 * Returns non-zero if the number of incoming opened streams is larger
52 * local_settings.max_concurrent_streams.
55 session_is_incoming_concurrent_streams_max(nghttp2_session *session) {
56 return session->local_settings.max_concurrent_streams <=
57 session->num_incoming_streams;
61 * Returns non-zero if the number of incoming opened streams is larger
63 * session->pending_local_max_concurrent_stream.
66 session_is_incoming_concurrent_streams_pending_max(nghttp2_session *session) {
67 return session->pending_local_max_concurrent_stream <=
68 session->num_incoming_streams;
72 * Returns non-zero if |lib_error| is non-fatal error.
74 static int is_non_fatal(int lib_error_code) {
75 return lib_error_code < 0 && lib_error_code > NGHTTP2_ERR_FATAL;
78 int nghttp2_is_fatal(int lib_error_code) {
79 return lib_error_code < NGHTTP2_ERR_FATAL;
82 static int session_enforce_http_messaging(nghttp2_session *session) {
83 return (session->opt_flags & NGHTTP2_OPTMASK_NO_HTTP_MESSAGING) == 0;
87 * Returns nonzero if |frame| is trailer headers.
89 static int session_trailer_headers(nghttp2_session *session,
90 nghttp2_stream *stream,
91 nghttp2_frame *frame) {
92 if (!stream || frame->hd.type != NGHTTP2_HEADERS) {
95 if (session->server) {
96 return frame->headers.cat == NGHTTP2_HCAT_HEADERS;
99 return frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
100 (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) == 0;
103 /* Returns nonzero if the |stream| is in reserved(remote) state */
104 static int state_reserved_remote(nghttp2_session *session,
105 nghttp2_stream *stream) {
106 return stream->state == NGHTTP2_STREAM_RESERVED &&
107 !nghttp2_session_is_my_stream_id(session, stream->stream_id);
110 /* Returns nonzero if the |stream| is in reserved(local) state */
111 static int state_reserved_local(nghttp2_session *session,
112 nghttp2_stream *stream) {
113 return stream->state == NGHTTP2_STREAM_RESERVED &&
114 nghttp2_session_is_my_stream_id(session, stream->stream_id);
118 * Checks whether received stream_id is valid. This function returns
119 * 1 if it succeeds, or 0.
121 static int session_is_new_peer_stream_id(nghttp2_session *session,
123 return stream_id != 0 &&
124 !nghttp2_session_is_my_stream_id(session, stream_id) &&
125 session->last_recv_stream_id < stream_id;
128 static int session_detect_idle_stream(nghttp2_session *session,
130 /* Assume that stream object with stream_id does not exist */
131 if (nghttp2_session_is_my_stream_id(session, stream_id)) {
132 if (session->next_stream_id <= (uint32_t)stream_id) {
137 if (session_is_new_peer_stream_id(session, stream_id)) {
143 static int session_terminate_session(nghttp2_session *session,
144 int32_t last_stream_id,
145 uint32_t error_code, const char *reason) {
147 const uint8_t *debug_data;
148 size_t debug_datalen;
150 if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
154 if (reason == NULL) {
158 debug_data = (const uint8_t *)reason;
159 debug_datalen = strlen(reason);
162 rv = nghttp2_session_add_goaway(session, last_stream_id, error_code,
163 debug_data, debug_datalen,
164 NGHTTP2_GOAWAY_AUX_TERM_ON_SEND);
170 session->goaway_flags |= NGHTTP2_GOAWAY_TERM_ON_SEND;
175 int nghttp2_session_terminate_session(nghttp2_session *session,
176 uint32_t error_code) {
177 return session_terminate_session(session, session->last_proc_stream_id,
181 int nghttp2_session_terminate_session2(nghttp2_session *session,
182 int32_t last_stream_id,
183 uint32_t error_code) {
184 return session_terminate_session(session, last_stream_id, error_code, NULL);
187 int nghttp2_session_terminate_session_with_reason(nghttp2_session *session,
189 const char *reason) {
190 return session_terminate_session(session, session->last_proc_stream_id,
194 int nghttp2_session_is_my_stream_id(nghttp2_session *session,
197 if (stream_id == 0) {
200 rem = stream_id & 0x1;
201 if (session->server) {
207 nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session,
209 nghttp2_stream *stream;
211 stream = (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id);
213 if (stream == NULL || (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) ||
214 stream->state == NGHTTP2_STREAM_IDLE) {
221 nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session,
223 return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id);
226 static int outbound_item_less(const void *lhsx, const void *rhsx) {
227 const nghttp2_outbound_item *lhs, *rhs;
229 lhs = (const nghttp2_outbound_item *)lhsx;
230 rhs = (const nghttp2_outbound_item *)rhsx;
232 return (lhs->cycle < rhs->cycle) ? 1 : 0;
235 static void session_inbound_frame_reset(nghttp2_session *session) {
236 nghttp2_inbound_frame *iframe = &session->iframe;
237 nghttp2_mem *mem = &session->mem;
238 /* A bit risky code, since if this function is called from
239 nghttp2_session_new(), we rely on the fact that
240 iframe->frame.hd.type is 0, so that no free is performed. */
241 switch (iframe->frame.hd.type) {
242 case NGHTTP2_HEADERS:
243 nghttp2_frame_headers_free(&iframe->frame.headers, mem);
245 case NGHTTP2_PRIORITY:
246 nghttp2_frame_priority_free(&iframe->frame.priority);
248 case NGHTTP2_RST_STREAM:
249 nghttp2_frame_rst_stream_free(&iframe->frame.rst_stream);
251 case NGHTTP2_SETTINGS:
252 nghttp2_frame_settings_free(&iframe->frame.settings, mem);
254 case NGHTTP2_PUSH_PROMISE:
255 nghttp2_frame_push_promise_free(&iframe->frame.push_promise, mem);
258 nghttp2_frame_ping_free(&iframe->frame.ping);
261 nghttp2_frame_goaway_free(&iframe->frame.goaway, mem);
263 case NGHTTP2_WINDOW_UPDATE:
264 nghttp2_frame_window_update_free(&iframe->frame.window_update);
268 memset(&iframe->frame, 0, sizeof(nghttp2_frame));
269 memset(&iframe->ext_frame_payload, 0, sizeof(nghttp2_ext_frame_payload));
271 iframe->state = NGHTTP2_IB_READ_HEAD;
273 nghttp2_buf_wrap_init(&iframe->sbuf, iframe->raw_sbuf,
274 sizeof(iframe->raw_sbuf));
275 iframe->sbuf.mark += NGHTTP2_FRAME_HDLEN;
277 nghttp2_buf_free(&iframe->lbuf, mem);
278 nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
281 iframe->payloadleft = 0;
283 iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1].settings_id =
284 NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
285 iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1].value = UINT32_MAX;
288 static void init_settings(nghttp2_settings_storage *settings) {
289 settings->header_table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
290 settings->enable_push = 1;
291 settings->max_concurrent_streams = NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS;
292 settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE;
293 settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN;
294 settings->max_header_list_size = UINT32_MAX;
297 static void active_outbound_item_reset(nghttp2_active_outbound_item *aob,
299 DEBUGF(fprintf(stderr, "send: reset nghttp2_active_outbound_item\n"));
300 DEBUGF(fprintf(stderr, "send: aob->item = %p\n", aob->item));
301 nghttp2_outbound_item_free(aob->item, mem);
302 nghttp2_mem_free(mem, aob->item);
304 nghttp2_bufs_reset(&aob->framebufs);
305 aob->state = NGHTTP2_OB_POP_ITEM;
308 /* The global variable for tests where we want to disable strict
310 int nghttp2_enable_strict_preface = 1;
312 static int session_new(nghttp2_session **session_ptr,
313 const nghttp2_session_callbacks *callbacks,
314 void *user_data, int server,
315 const nghttp2_option *option, nghttp2_mem *mem) {
319 mem = nghttp2_mem_default();
322 *session_ptr = nghttp2_mem_calloc(mem, 1, sizeof(nghttp2_session));
323 if (*session_ptr == NULL) {
324 rv = NGHTTP2_ERR_NOMEM;
328 (*session_ptr)->mem = *mem;
329 mem = &(*session_ptr)->mem;
331 /* next_stream_id is initialized in either
332 nghttp2_session_client_new2 or nghttp2_session_server_new2 */
334 rv = nghttp2_pq_init(&(*session_ptr)->ob_da_pq, outbound_item_less, mem);
339 rv = nghttp2_hd_deflate_init(&(*session_ptr)->hd_deflater, mem);
341 goto fail_hd_deflater;
343 rv = nghttp2_hd_inflate_init(&(*session_ptr)->hd_inflater, mem);
345 goto fail_hd_inflater;
347 rv = nghttp2_map_init(&(*session_ptr)->streams, mem);
352 nghttp2_stream_roots_init(&(*session_ptr)->roots);
354 (*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
355 (*session_ptr)->recv_window_size = 0;
356 (*session_ptr)->consumed_size = 0;
357 (*session_ptr)->recv_reduction = 0;
358 (*session_ptr)->local_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
360 (*session_ptr)->goaway_flags = NGHTTP2_GOAWAY_NONE;
361 (*session_ptr)->local_last_stream_id = (1u << 31) - 1;
362 (*session_ptr)->remote_last_stream_id = (1u << 31) - 1;
364 (*session_ptr)->inflight_niv = -1;
366 (*session_ptr)->pending_local_max_concurrent_stream =
367 NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS;
368 (*session_ptr)->pending_enable_push = 1;
371 (*session_ptr)->server = 1;
374 /* 1 for Pad Field. */
375 rv = nghttp2_bufs_init3(&(*session_ptr)->aob.framebufs,
376 NGHTTP2_FRAMEBUF_CHUNKLEN, NGHTTP2_FRAMEBUF_MAX_NUM,
377 1, NGHTTP2_FRAME_HDLEN + 1, mem);
379 goto fail_aob_framebuf;
382 active_outbound_item_reset(&(*session_ptr)->aob, mem);
384 init_settings(&(*session_ptr)->remote_settings);
385 init_settings(&(*session_ptr)->local_settings);
388 if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) &&
389 option->no_auto_window_update) {
391 (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE;
394 if (option->opt_set_mask & NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS) {
396 (*session_ptr)->remote_settings.max_concurrent_streams =
397 option->peer_max_concurrent_streams;
400 if ((option->opt_set_mask & NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC) &&
401 option->no_recv_client_magic) {
403 (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC;
406 if ((option->opt_set_mask & NGHTTP2_OPT_NO_HTTP_MESSAGING) &&
407 option->no_http_messaging) {
409 (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING;
413 (*session_ptr)->callbacks = *callbacks;
414 (*session_ptr)->user_data = user_data;
416 session_inbound_frame_reset(*session_ptr);
418 if (nghttp2_enable_strict_preface) {
419 nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe;
422 ((*session_ptr)->opt_flags & NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) ==
424 iframe->state = NGHTTP2_IB_READ_CLIENT_MAGIC;
425 iframe->payloadleft = NGHTTP2_CLIENT_MAGIC_LEN;
427 iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS;
431 (*session_ptr)->aob.state = NGHTTP2_OB_SEND_CLIENT_MAGIC;
432 nghttp2_bufs_add(&(*session_ptr)->aob.framebufs, NGHTTP2_CLIENT_MAGIC,
433 NGHTTP2_CLIENT_MAGIC_LEN);
440 nghttp2_map_free(&(*session_ptr)->streams);
442 nghttp2_hd_inflate_free(&(*session_ptr)->hd_inflater);
444 nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater);
446 nghttp2_pq_free(&(*session_ptr)->ob_da_pq);
448 nghttp2_mem_free(mem, *session_ptr);
453 int nghttp2_session_client_new(nghttp2_session **session_ptr,
454 const nghttp2_session_callbacks *callbacks,
456 return nghttp2_session_client_new3(session_ptr, callbacks, user_data, NULL,
460 int nghttp2_session_client_new2(nghttp2_session **session_ptr,
461 const nghttp2_session_callbacks *callbacks,
462 void *user_data, const nghttp2_option *option) {
463 return nghttp2_session_client_new3(session_ptr, callbacks, user_data, option,
467 int nghttp2_session_client_new3(nghttp2_session **session_ptr,
468 const nghttp2_session_callbacks *callbacks,
469 void *user_data, const nghttp2_option *option,
472 nghttp2_session *session;
474 rv = session_new(&session, callbacks, user_data, 0, option, mem);
479 /* IDs for use in client */
480 session->next_stream_id = 1;
482 *session_ptr = session;
487 int nghttp2_session_server_new(nghttp2_session **session_ptr,
488 const nghttp2_session_callbacks *callbacks,
490 return nghttp2_session_server_new3(session_ptr, callbacks, user_data, NULL,
494 int nghttp2_session_server_new2(nghttp2_session **session_ptr,
495 const nghttp2_session_callbacks *callbacks,
496 void *user_data, const nghttp2_option *option) {
497 return nghttp2_session_server_new3(session_ptr, callbacks, user_data, option,
501 int nghttp2_session_server_new3(nghttp2_session **session_ptr,
502 const nghttp2_session_callbacks *callbacks,
503 void *user_data, const nghttp2_option *option,
506 nghttp2_session *session;
508 rv = session_new(&session, callbacks, user_data, 1, option, mem);
513 /* IDs for use in client */
514 session->next_stream_id = 2;
516 *session_ptr = session;
521 static int free_streams(nghttp2_map_entry *entry, void *ptr) {
522 nghttp2_session *session;
523 nghttp2_stream *stream;
524 nghttp2_outbound_item *item;
527 session = (nghttp2_session *)ptr;
529 stream = (nghttp2_stream *)entry;
532 if (item && !item->queued && item != session->aob.item) {
533 nghttp2_outbound_item_free(item, mem);
534 nghttp2_mem_free(mem, item);
537 nghttp2_stream_free(stream);
538 nghttp2_mem_free(mem, stream);
543 static void ob_pq_free(nghttp2_pq *pq, nghttp2_mem *mem) {
544 while (!nghttp2_pq_empty(pq)) {
545 nghttp2_outbound_item *item = (nghttp2_outbound_item *)nghttp2_pq_top(pq);
546 nghttp2_outbound_item_free(item, mem);
547 nghttp2_mem_free(mem, item);
553 static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) {
554 nghttp2_outbound_item *item, *next;
555 for (item = q->head; item;) {
557 nghttp2_outbound_item_free(item, mem);
558 nghttp2_mem_free(mem, item);
563 void nghttp2_session_del(nghttp2_session *session) {
566 if (session == NULL) {
572 nghttp2_mem_free(mem, session->inflight_iv);
574 nghttp2_stream_roots_free(&session->roots);
576 /* Have to free streams first, so that we can check
577 stream->item->queued */
578 nghttp2_map_each_free(&session->streams, free_streams, session);
579 nghttp2_map_free(&session->streams);
581 ob_q_free(&session->ob_urgent, mem);
582 ob_q_free(&session->ob_reg, mem);
583 ob_q_free(&session->ob_syn, mem);
584 ob_pq_free(&session->ob_da_pq, mem);
585 active_outbound_item_reset(&session->aob, mem);
586 session_inbound_frame_reset(session);
587 nghttp2_hd_deflate_free(&session->hd_deflater);
588 nghttp2_hd_inflate_free(&session->hd_inflater);
589 nghttp2_bufs_free(&session->aob.framebufs);
590 nghttp2_mem_free(mem, session);
594 nghttp2_session_reprioritize_stream(nghttp2_session *session,
595 nghttp2_stream *stream,
596 const nghttp2_priority_spec *pri_spec_in) {
598 nghttp2_stream *dep_stream = NULL;
599 nghttp2_stream *root_stream;
600 nghttp2_priority_spec pri_spec_default;
601 const nghttp2_priority_spec *pri_spec = pri_spec_in;
603 if (!nghttp2_stream_in_dep_tree(stream)) {
607 if (pri_spec->stream_id == stream->stream_id) {
608 return nghttp2_session_terminate_session_with_reason(
609 session, NGHTTP2_PROTOCOL_ERROR, "depend on itself");
612 if (pri_spec->stream_id != 0) {
613 dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id);
615 if (session->server && !dep_stream &&
616 session_detect_idle_stream(session, pri_spec->stream_id)) {
618 nghttp2_priority_spec_default_init(&pri_spec_default);
620 dep_stream = nghttp2_session_open_stream(
621 session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default,
622 NGHTTP2_STREAM_IDLE, NULL);
624 if (dep_stream == NULL) {
625 return NGHTTP2_ERR_NOMEM;
627 } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) {
628 nghttp2_priority_spec_default_init(&pri_spec_default);
629 pri_spec = &pri_spec_default;
633 if (pri_spec->stream_id == 0) {
634 nghttp2_stream_dep_remove_subtree(stream);
636 /* We have to update weight after removing stream from tree */
637 stream->weight = pri_spec->weight;
639 if (pri_spec->exclusive &&
640 session->roots.num_streams <= NGHTTP2_MAX_DEP_TREE_LENGTH) {
642 rv = nghttp2_stream_dep_all_your_stream_are_belong_to_us(stream, session);
644 rv = nghttp2_stream_dep_make_root(stream, session);
652 if (nghttp2_stream_dep_subtree_find(stream, dep_stream)) {
653 DEBUGF(fprintf(stderr, "stream: cycle detected, dep_stream(%p)=%d "
655 dep_stream, dep_stream->stream_id, stream,
658 nghttp2_stream_dep_remove_subtree(dep_stream);
659 nghttp2_stream_dep_make_root(dep_stream, session);
662 nghttp2_stream_dep_remove_subtree(stream);
664 /* We have to update weight after removing stream from tree */
665 stream->weight = pri_spec->weight;
667 root_stream = nghttp2_stream_get_dep_root(dep_stream);
669 if (root_stream->num_substreams + stream->num_substreams >
670 NGHTTP2_MAX_DEP_TREE_LENGTH) {
671 stream->weight = NGHTTP2_DEFAULT_WEIGHT;
673 rv = nghttp2_stream_dep_make_root(stream, session);
675 if (pri_spec->exclusive) {
676 rv = nghttp2_stream_dep_insert_subtree(dep_stream, stream, session);
678 rv = nghttp2_stream_dep_add_subtree(dep_stream, stream, session);
689 int nghttp2_session_add_item(nghttp2_session *session,
690 nghttp2_outbound_item *item) {
691 /* TODO Return error if stream is not found for the frame requiring
694 nghttp2_stream *stream;
695 nghttp2_frame *frame;
697 frame = &item->frame;
698 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
700 if (frame->hd.type != NGHTTP2_DATA) {
702 switch (frame->hd.type) {
703 case NGHTTP2_HEADERS:
704 /* We push request HEADERS and push response HEADERS to
705 dedicated queue because their transmission is affected by
706 SETTINGS_MAX_CONCURRENT_STREAMS */
707 /* TODO If 2 HEADERS are submitted for reserved stream, then
708 both of them are queued into ob_syn, which is not
710 if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
711 nghttp2_outbound_queue_push(&session->ob_syn, item);
716 if (stream && (stream->state == NGHTTP2_STREAM_RESERVED ||
717 item->aux_data.headers.attach_stream)) {
718 rv = nghttp2_stream_attach_item(stream, item, session);
727 nghttp2_outbound_queue_push(&session->ob_reg, item);
730 case NGHTTP2_SETTINGS:
732 nghttp2_outbound_queue_push(&session->ob_urgent, item);
735 case NGHTTP2_RST_STREAM:
737 stream->state = NGHTTP2_STREAM_CLOSING;
741 nghttp2_outbound_queue_push(&session->ob_reg, item);
749 return NGHTTP2_ERR_STREAM_CLOSED;
753 return NGHTTP2_ERR_DATA_EXIST;
756 rv = nghttp2_stream_attach_item(stream, item, session);
765 int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
766 uint32_t error_code) {
768 nghttp2_outbound_item *item;
769 nghttp2_frame *frame;
770 nghttp2_stream *stream;
774 stream = nghttp2_session_get_stream(session, stream_id);
775 if (stream && stream->state == NGHTTP2_STREAM_CLOSING) {
779 /* Cancel pending request HEADERS in ob_syn if this RST_STREAM
780 refers to that stream. */
781 if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) &&
782 nghttp2_outbound_queue_top(&session->ob_syn)) {
783 nghttp2_headers_aux_data *aux_data;
784 nghttp2_frame *headers_frame;
786 headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame;
787 assert(headers_frame->hd.type == NGHTTP2_HEADERS);
789 if (headers_frame->hd.stream_id <= stream_id &&
790 (uint32_t)stream_id < session->next_stream_id) {
792 for (item = session->ob_syn.head; item; item = item->qnext) {
793 aux_data = &item->aux_data.headers;
795 if (item->frame.hd.stream_id < stream_id) {
799 /* stream_id in ob_syn queue must be strictly increasing. If
800 we found larger ID, then we can break here. */
801 if (item->frame.hd.stream_id > stream_id || aux_data->canceled) {
805 aux_data->error_code = error_code;
806 aux_data->canceled = 1;
813 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
815 return NGHTTP2_ERR_NOMEM;
818 nghttp2_outbound_item_init(item);
820 frame = &item->frame;
822 nghttp2_frame_rst_stream_init(&frame->rst_stream, stream_id, error_code);
823 rv = nghttp2_session_add_item(session, item);
825 nghttp2_frame_rst_stream_free(&frame->rst_stream);
826 nghttp2_mem_free(mem, item);
832 nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
833 int32_t stream_id, uint8_t flags,
834 nghttp2_priority_spec *pri_spec_in,
835 nghttp2_stream_state initial_state,
836 void *stream_user_data) {
838 nghttp2_stream *stream;
839 nghttp2_stream *dep_stream = NULL;
840 nghttp2_stream *root_stream;
841 int stream_alloc = 0;
842 nghttp2_priority_spec pri_spec_default;
843 nghttp2_priority_spec *pri_spec = pri_spec_in;
847 stream = nghttp2_session_get_stream_raw(session, stream_id);
850 assert(stream->state == NGHTTP2_STREAM_IDLE);
851 assert(nghttp2_stream_in_dep_tree(stream));
852 nghttp2_session_detach_idle_stream(session, stream);
853 nghttp2_stream_dep_remove(stream);
855 if (session->server && initial_state != NGHTTP2_STREAM_IDLE &&
856 !nghttp2_session_is_my_stream_id(session, stream_id)) {
858 nghttp2_session_adjust_closed_stream(session, 1);
861 stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream));
862 if (stream == NULL) {
869 if (pri_spec->stream_id != 0) {
870 dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id);
872 if (session->server && !dep_stream &&
873 session_detect_idle_stream(session, pri_spec->stream_id)) {
874 /* Depends on idle stream, which does not exist in memory.
875 Assign default priority for it. */
876 nghttp2_priority_spec_default_init(&pri_spec_default);
878 dep_stream = nghttp2_session_open_stream(
879 session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default,
880 NGHTTP2_STREAM_IDLE, NULL);
882 if (dep_stream == NULL) {
884 nghttp2_mem_free(mem, stream);
889 } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) {
890 /* If dep_stream is not part of dependency tree, stream will get
892 nghttp2_priority_spec_default_init(&pri_spec_default);
893 pri_spec = &pri_spec_default;
897 if (initial_state == NGHTTP2_STREAM_RESERVED) {
898 flags |= NGHTTP2_STREAM_FLAG_PUSH;
902 stream, stream_id, flags, initial_state, pri_spec->weight,
903 &session->roots, session->remote_settings.initial_window_size,
904 session->local_settings.initial_window_size, stream_user_data);
907 rv = nghttp2_map_insert(&session->streams, &stream->map_entry);
909 nghttp2_mem_free(mem, stream);
914 switch (initial_state) {
915 case NGHTTP2_STREAM_RESERVED:
916 if (nghttp2_session_is_my_stream_id(session, stream_id)) {
917 /* half closed (remote) */
918 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
920 /* half closed (local) */
921 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
923 /* Reserved stream does not count in the concurrent streams
924 limit. That is one of the DOS vector. */
926 case NGHTTP2_STREAM_IDLE:
927 /* Idle stream does not count toward the concurrent streams limit.
928 This is used as anchor node in dependency tree. */
929 assert(session->server);
930 nghttp2_session_keep_idle_stream(session, stream);
933 if (nghttp2_session_is_my_stream_id(session, stream_id)) {
934 ++session->num_outgoing_streams;
936 ++session->num_incoming_streams;
940 /* We don't have to track dependency of received reserved stream */
941 if (stream->shut_flags & NGHTTP2_SHUT_WR) {
945 if (pri_spec->stream_id == 0) {
947 ++session->roots.num_streams;
949 if (pri_spec->exclusive &&
950 session->roots.num_streams <= NGHTTP2_MAX_DEP_TREE_LENGTH) {
951 rv = nghttp2_stream_dep_all_your_stream_are_belong_to_us(stream, session);
953 /* Since no dpri is changed in dependency tree, the above
954 function call never fail. */
957 nghttp2_stream_roots_add(&session->roots, stream);
963 /* TODO Client does not have to track dependencies of streams except
964 for those which have upload data. Currently, we just track
969 root_stream = nghttp2_stream_get_dep_root(dep_stream);
971 if (root_stream->num_substreams < NGHTTP2_MAX_DEP_TREE_LENGTH) {
972 if (pri_spec->exclusive) {
973 nghttp2_stream_dep_insert(dep_stream, stream);
975 nghttp2_stream_dep_add(dep_stream, stream);
978 stream->weight = NGHTTP2_DEFAULT_WEIGHT;
980 nghttp2_stream_roots_add(&session->roots, stream);
986 int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
987 uint32_t error_code) {
989 nghttp2_stream *stream;
993 stream = nghttp2_session_get_stream(session, stream_id);
996 return NGHTTP2_ERR_INVALID_ARGUMENT;
999 DEBUGF(fprintf(stderr, "stream: stream(%p)=%d close\n", stream,
1000 stream->stream_id));
1003 nghttp2_outbound_item *item;
1005 item = stream->item;
1007 rv = nghttp2_stream_detach_item(stream, session);
1013 /* If item is queued, it will be deleted when it is popped
1014 (nghttp2_session_prep_frame() will fail). If session->aob.item
1015 points to this item, let active_outbound_item_reset()
1017 if (!item->queued && item != session->aob.item) {
1018 nghttp2_outbound_item_free(item, mem);
1019 nghttp2_mem_free(mem, item);
1023 /* We call on_stream_close_callback even if stream->state is
1024 NGHTTP2_STREAM_INITIAL. This will happen while sending request
1025 HEADERS, a local endpoint receives RST_STREAM for that stream. It
1026 may be PROTOCOL_ERROR, but without notifying stream closure will
1027 hang the stream in a local endpoint.
1030 if (session->callbacks.on_stream_close_callback) {
1031 if (session->callbacks.on_stream_close_callback(
1032 session, stream_id, error_code, session->user_data) != 0) {
1034 return NGHTTP2_ERR_CALLBACK_FAILURE;
1038 /* pushed streams which is not opened yet is not counted toward max
1039 concurrent limits */
1040 if ((stream->flags & NGHTTP2_STREAM_FLAG_PUSH) == 0) {
1041 if (nghttp2_session_is_my_stream_id(session, stream_id)) {
1042 --session->num_outgoing_streams;
1044 --session->num_incoming_streams;
1048 /* Closes both directions just in case they are not closed yet */
1049 stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED;
1051 if (session->server && nghttp2_stream_in_dep_tree(stream)) {
1052 /* On server side, retain stream at most MAX_CONCURRENT_STREAMS
1053 combined with the current active incoming streams to make
1054 dependency tree work better. */
1055 nghttp2_session_keep_closed_stream(session, stream);
1057 nghttp2_session_destroy_stream(session, stream);
1063 void nghttp2_session_destroy_stream(nghttp2_session *session,
1064 nghttp2_stream *stream) {
1067 DEBUGF(fprintf(stderr, "stream: destroy closed stream(%p)=%d\n", stream,
1068 stream->stream_id));
1070 mem = &session->mem;
1072 nghttp2_stream_dep_remove(stream);
1074 nghttp2_map_remove(&session->streams, stream->stream_id);
1075 nghttp2_stream_free(stream);
1076 nghttp2_mem_free(mem, stream);
1079 void nghttp2_session_keep_closed_stream(nghttp2_session *session,
1080 nghttp2_stream *stream) {
1081 DEBUGF(fprintf(stderr, "stream: keep closed stream(%p)=%d, state=%d\n",
1082 stream, stream->stream_id, stream->state));
1084 if (session->closed_stream_tail) {
1085 session->closed_stream_tail->closed_next = stream;
1086 stream->closed_prev = session->closed_stream_tail;
1088 session->closed_stream_head = stream;
1090 session->closed_stream_tail = stream;
1092 ++session->num_closed_streams;
1094 nghttp2_session_adjust_closed_stream(session, 0);
1097 void nghttp2_session_keep_idle_stream(nghttp2_session *session,
1098 nghttp2_stream *stream) {
1099 DEBUGF(fprintf(stderr, "stream: keep idle stream(%p)=%d, state=%d\n", stream,
1100 stream->stream_id, stream->state));
1102 if (session->idle_stream_tail) {
1103 session->idle_stream_tail->closed_next = stream;
1104 stream->closed_prev = session->idle_stream_tail;
1106 session->idle_stream_head = stream;
1108 session->idle_stream_tail = stream;
1110 ++session->num_idle_streams;
1112 nghttp2_session_adjust_idle_stream(session);
1115 void nghttp2_session_detach_idle_stream(nghttp2_session *session,
1116 nghttp2_stream *stream) {
1117 nghttp2_stream *prev_stream, *next_stream;
1119 DEBUGF(fprintf(stderr, "stream: detach idle stream(%p)=%d, state=%d\n",
1120 stream, stream->stream_id, stream->state));
1122 prev_stream = stream->closed_prev;
1123 next_stream = stream->closed_next;
1126 prev_stream->closed_next = next_stream;
1128 session->idle_stream_head = next_stream;
1132 next_stream->closed_prev = prev_stream;
1134 session->idle_stream_tail = prev_stream;
1137 stream->closed_prev = NULL;
1138 stream->closed_next = NULL;
1140 --session->num_idle_streams;
1143 void nghttp2_session_adjust_closed_stream(nghttp2_session *session,
1145 size_t num_stream_max;
1147 num_stream_max = nghttp2_min(session->local_settings.max_concurrent_streams,
1148 session->pending_local_max_concurrent_stream);
1150 DEBUGF(fprintf(stderr, "stream: adjusting kept closed streams "
1151 "num_closed_streams=%zu, num_incoming_streams=%zu, "
1152 "max_concurrent_streams=%zu\n",
1153 session->num_closed_streams, session->num_incoming_streams,
1156 while (session->num_closed_streams > 0 &&
1157 session->num_closed_streams + session->num_incoming_streams + offset >
1159 nghttp2_stream *head_stream;
1161 head_stream = session->closed_stream_head;
1163 assert(head_stream);
1165 session->closed_stream_head = head_stream->closed_next;
1167 if (session->closed_stream_head) {
1168 session->closed_stream_head->closed_prev = NULL;
1170 session->closed_stream_tail = NULL;
1173 nghttp2_session_destroy_stream(session, head_stream);
1174 /* head_stream is now freed */
1175 --session->num_closed_streams;
1179 void nghttp2_session_adjust_idle_stream(nghttp2_session *session) {
1182 /* Make minimum number of idle streams 2 so that allocating 2
1183 streams at once is easy. This happens when PRIORITY frame to
1184 idle stream, which depends on idle stream which does not
1187 nghttp2_max(2, nghttp2_min(session->local_settings.max_concurrent_streams,
1188 session->pending_local_max_concurrent_stream));
1190 DEBUGF(fprintf(stderr, "stream: adjusting kept idle streams "
1191 "num_idle_streams=%zu, max=%zu\n",
1192 session->num_idle_streams, max));
1194 while (session->num_idle_streams > max) {
1195 nghttp2_stream *head;
1197 head = session->idle_stream_head;
1200 session->idle_stream_head = head->closed_next;
1202 if (session->idle_stream_head) {
1203 session->idle_stream_head->closed_prev = NULL;
1205 session->idle_stream_tail = NULL;
1208 nghttp2_session_destroy_stream(session, head);
1209 /* head is now destroyed */
1210 --session->num_idle_streams;
1215 * Closes stream with stream ID |stream_id| if both transmission and
1216 * reception of the stream were disallowed. The |error_code| indicates
1217 * the reason of the closure.
1219 * This function returns 0 if it succeeds, or one of the following
1220 * negative error codes:
1222 * NGHTTP2_ERR_INVALID_ARGUMENT
1223 * The stream is not found.
1224 * NGHTTP2_ERR_CALLBACK_FAILURE
1225 * The callback function failed.
1227 int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session,
1228 nghttp2_stream *stream) {
1229 if ((stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR) {
1230 return nghttp2_session_close_stream(session, stream->stream_id,
1237 * This function returns nonzero if session is closing.
1239 static int session_is_closing(nghttp2_session *session) {
1240 return (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) != 0;
1244 * Check that we can send a frame to the |stream|. This function
1245 * returns 0 if we can send a frame to the |frame|, or one of the
1246 * following negative error codes:
1248 * NGHTTP2_ERR_STREAM_CLOSED
1249 * The stream is already closed.
1250 * NGHTTP2_ERR_STREAM_SHUT_WR
1251 * The stream is half-closed for transmission.
1252 * NGHTTP2_ERR_SESSION_CLOSING
1253 * This session is closing.
1255 static int session_predicate_for_stream_send(nghttp2_session *session,
1256 nghttp2_stream *stream) {
1257 if (stream == NULL) {
1258 return NGHTTP2_ERR_STREAM_CLOSED;
1260 if (session_is_closing(session)) {
1261 return NGHTTP2_ERR_SESSION_CLOSING;
1263 if (stream->shut_flags & NGHTTP2_SHUT_WR) {
1264 return NGHTTP2_ERR_STREAM_SHUT_WR;
1270 * This function checks request HEADERS frame, which opens stream, can
1271 * be sent at this time.
1273 * This function returns 0 if it succeeds, or one of the following
1274 * negative error codes:
1276 * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED
1277 * New stream cannot be created because of GOAWAY: session is
1278 * going down or received last_stream_id is strictly less than
1279 * frame->hd.stream_id.
1280 * NGHTTP2_ERR_STREAM_CLOSING
1281 * request HEADERS was canceled by RST_STREAM while it is in queue.
1283 static int session_predicate_request_headers_send(nghttp2_session *session,
1284 nghttp2_outbound_item *item) {
1285 if (item->aux_data.headers.canceled) {
1286 return NGHTTP2_ERR_STREAM_CLOSING;
1288 /* If we are terminating session (NGHTTP2_GOAWAY_TERM_ON_SEND) or
1289 GOAWAY was received from peer, new request is not allowed. */
1290 if (session->goaway_flags &
1291 (NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_RECV)) {
1292 return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
1298 * This function checks HEADERS, which is the first frame from the
1299 * server, with the |stream| can be sent at this time. The |stream|
1302 * This function returns 0 if it succeeds, or one of the following
1303 * negative error codes:
1305 * NGHTTP2_ERR_STREAM_CLOSED
1306 * The stream is already closed or does not exist.
1307 * NGHTTP2_ERR_STREAM_SHUT_WR
1308 * The transmission is not allowed for this stream (e.g., a frame
1309 * with END_STREAM flag set has already sent)
1310 * NGHTTP2_ERR_INVALID_STREAM_ID
1311 * The stream ID is invalid.
1312 * NGHTTP2_ERR_STREAM_CLOSING
1313 * RST_STREAM was queued for this stream.
1314 * NGHTTP2_ERR_INVALID_STREAM_STATE
1315 * The state of the stream is not valid.
1316 * NGHTTP2_ERR_SESSION_CLOSING
1317 * This session is closing.
1319 static int session_predicate_response_headers_send(nghttp2_session *session,
1320 nghttp2_stream *stream) {
1322 rv = session_predicate_for_stream_send(session, stream);
1327 if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
1328 return NGHTTP2_ERR_INVALID_STREAM_ID;
1330 if (stream->state == NGHTTP2_STREAM_OPENING) {
1333 if (stream->state == NGHTTP2_STREAM_CLOSING) {
1334 return NGHTTP2_ERR_STREAM_CLOSING;
1336 return NGHTTP2_ERR_INVALID_STREAM_STATE;
1340 * This function checks HEADERS for reserved stream can be sent. The
1341 * |stream| must be reserved state and the |session| is server side.
1342 * The |stream| can be NULL.
1344 * This function returns 0 if it succeeds, or one of the following
1347 * NGHTTP2_ERR_STREAM_CLOSED
1348 * The stream is already closed.
1349 * NGHTTP2_ERR_STREAM_SHUT_WR
1350 * The stream is half-closed for transmission.
1352 * The stream is not reserved state
1353 * NGHTTP2_ERR_STREAM_CLOSED
1354 * RST_STREAM was queued for this stream.
1355 * NGHTTP2_ERR_SESSION_CLOSING
1356 * This session is closing.
1359 session_predicate_push_response_headers_send(nghttp2_session *session,
1360 nghttp2_stream *stream) {
1362 /* TODO Should disallow HEADERS if GOAWAY has already been issued? */
1363 rv = session_predicate_for_stream_send(session, stream);
1368 if (stream->state != NGHTTP2_STREAM_RESERVED) {
1369 return NGHTTP2_ERR_PROTO;
1371 if (stream->state == NGHTTP2_STREAM_CLOSING) {
1372 return NGHTTP2_ERR_STREAM_CLOSING;
1378 * This function checks HEADERS, which is neither stream-opening nor
1379 * first response header, with the |stream| can be sent at this time.
1380 * The |stream| can be NULL.
1382 * This function returns 0 if it succeeds, or one of the following
1383 * negative error codes:
1385 * NGHTTP2_ERR_STREAM_CLOSED
1386 * The stream is already closed or does not exist.
1387 * NGHTTP2_ERR_STREAM_SHUT_WR
1388 * The transmission is not allowed for this stream (e.g., a frame
1389 * with END_STREAM flag set has already sent)
1390 * NGHTTP2_ERR_STREAM_CLOSING
1391 * RST_STREAM was queued for this stream.
1392 * NGHTTP2_ERR_INVALID_STREAM_STATE
1393 * The state of the stream is not valid.
1394 * NGHTTP2_ERR_SESSION_CLOSING
1395 * This session is closing.
1397 static int session_predicate_headers_send(nghttp2_session *session,
1398 nghttp2_stream *stream) {
1400 rv = session_predicate_for_stream_send(session, stream);
1405 if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
1406 if (stream->state == NGHTTP2_STREAM_CLOSING) {
1407 return NGHTTP2_ERR_STREAM_CLOSING;
1411 if (stream->state == NGHTTP2_STREAM_OPENED) {
1414 if (stream->state == NGHTTP2_STREAM_CLOSING) {
1415 return NGHTTP2_ERR_STREAM_CLOSING;
1417 return NGHTTP2_ERR_INVALID_STREAM_STATE;
1421 * This function checks PUSH_PROMISE frame |frame| with the |stream|
1422 * can be sent at this time. The |stream| can be NULL.
1424 * This function returns 0 if it succeeds, or one of the following
1425 * negative error codes:
1427 * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED
1428 * New stream cannot be created because GOAWAY is already sent or
1431 * The client side attempts to send PUSH_PROMISE, or the server
1432 * sends PUSH_PROMISE for the stream not initiated by the client.
1433 * NGHTTP2_ERR_STREAM_CLOSED
1434 * The stream is already closed or does not exist.
1435 * NGHTTP2_ERR_STREAM_CLOSING
1436 * RST_STREAM was queued for this stream.
1437 * NGHTTP2_ERR_STREAM_SHUT_WR
1438 * The transmission is not allowed for this stream (e.g., a frame
1439 * with END_STREAM flag set has already sent)
1440 * NGHTTP2_ERR_PUSH_DISABLED
1441 * The remote peer disabled reception of PUSH_PROMISE.
1442 * NGHTTP2_ERR_SESSION_CLOSING
1443 * This session is closing.
1445 static int session_predicate_push_promise_send(nghttp2_session *session,
1446 nghttp2_stream *stream) {
1449 if (!session->server) {
1450 return NGHTTP2_ERR_PROTO;
1453 rv = session_predicate_for_stream_send(session, stream);
1460 if (session->remote_settings.enable_push == 0) {
1461 return NGHTTP2_ERR_PUSH_DISABLED;
1463 if (stream->state == NGHTTP2_STREAM_CLOSING) {
1464 return NGHTTP2_ERR_STREAM_CLOSING;
1466 if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) {
1467 return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
1473 * This function checks WINDOW_UPDATE with the stream ID |stream_id|
1474 * can be sent at this time. Note that END_STREAM flag of the previous
1475 * frame does not affect the transmission of the WINDOW_UPDATE frame.
1477 * This function returns 0 if it succeeds, or one of the following
1478 * negative error codes:
1480 * NGHTTP2_ERR_STREAM_CLOSED
1481 * The stream is already closed or does not exist.
1482 * NGHTTP2_ERR_STREAM_CLOSING
1483 * RST_STREAM was queued for this stream.
1484 * NGHTTP2_ERR_INVALID_STREAM_STATE
1485 * The state of the stream is not valid.
1486 * NGHTTP2_ERR_SESSION_CLOSING
1487 * This session is closing.
1489 static int session_predicate_window_update_send(nghttp2_session *session,
1490 int32_t stream_id) {
1491 nghttp2_stream *stream;
1492 if (stream_id == 0) {
1493 /* Connection-level window update */
1496 stream = nghttp2_session_get_stream(session, stream_id);
1497 if (stream == NULL) {
1498 return NGHTTP2_ERR_STREAM_CLOSED;
1500 if (session_is_closing(session)) {
1501 return NGHTTP2_ERR_SESSION_CLOSING;
1503 if (stream->state == NGHTTP2_STREAM_CLOSING) {
1504 return NGHTTP2_ERR_STREAM_CLOSING;
1506 if (state_reserved_local(session, stream)) {
1507 return NGHTTP2_ERR_INVALID_STREAM_STATE;
1512 /* Take into account settings max frame size and both connection-level
1513 flow control here */
1515 nghttp2_session_enforce_flow_control_limits(nghttp2_session *session,
1516 nghttp2_stream *stream,
1517 ssize_t requested_window_size) {
1518 DEBUGF(fprintf(stderr, "send: remote windowsize connection=%d, "
1519 "remote maxframsize=%u, stream(id %d)=%d\n",
1520 session->remote_window_size,
1521 session->remote_settings.max_frame_size, stream->stream_id,
1522 stream->remote_window_size));
1524 return nghttp2_min(nghttp2_min(nghttp2_min(requested_window_size,
1525 stream->remote_window_size),
1526 session->remote_window_size),
1527 (int32_t)session->remote_settings.max_frame_size);
1531 * Returns the maximum length of next data read. If the
1532 * connection-level and/or stream-wise flow control are enabled, the
1533 * return value takes into account those current window sizes. The remote
1534 * settings for max frame size is also taken into account.
1536 static size_t nghttp2_session_next_data_read(nghttp2_session *session,
1537 nghttp2_stream *stream) {
1538 ssize_t window_size;
1540 window_size = nghttp2_session_enforce_flow_control_limits(
1541 session, stream, NGHTTP2_DATA_PAYLOADLEN);
1543 DEBUGF(fprintf(stderr, "send: available window=%zd\n", window_size));
1545 return window_size > 0 ? (size_t)window_size : 0;
1549 * This function checks DATA with the |stream| can be sent at this
1550 * time. The |stream| can be NULL.
1552 * This function returns 0 if it succeeds, or one of the following
1553 * negative error codes:
1555 * NGHTTP2_ERR_STREAM_CLOSED
1556 * The stream is already closed or does not exist.
1557 * NGHTTP2_ERR_STREAM_SHUT_WR
1558 * The transmission is not allowed for this stream (e.g., a frame
1559 * with END_STREAM flag set has already sent)
1560 * NGHTTP2_ERR_STREAM_CLOSING
1561 * RST_STREAM was queued for this stream.
1562 * NGHTTP2_ERR_INVALID_STREAM_STATE
1563 * The state of the stream is not valid.
1564 * NGHTTP2_ERR_SESSION_CLOSING
1565 * This session is closing.
1567 static int nghttp2_session_predicate_data_send(nghttp2_session *session,
1568 nghttp2_stream *stream) {
1570 rv = session_predicate_for_stream_send(session, stream);
1575 if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
1576 /* Request body data */
1577 /* If stream->state is NGHTTP2_STREAM_CLOSING, RST_STREAM was
1578 queued but not yet sent. In this case, we won't send DATA
1580 if (stream->state == NGHTTP2_STREAM_CLOSING) {
1581 return NGHTTP2_ERR_STREAM_CLOSING;
1583 if (stream->state == NGHTTP2_STREAM_RESERVED) {
1584 return NGHTTP2_ERR_INVALID_STREAM_STATE;
1588 /* Response body data */
1589 if (stream->state == NGHTTP2_STREAM_OPENED) {
1592 if (stream->state == NGHTTP2_STREAM_CLOSING) {
1593 return NGHTTP2_ERR_STREAM_CLOSING;
1595 return NGHTTP2_ERR_INVALID_STREAM_STATE;
1598 static ssize_t session_call_select_padding(nghttp2_session *session,
1599 const nghttp2_frame *frame,
1600 size_t max_payloadlen) {
1603 if (frame->hd.length >= max_payloadlen) {
1604 return frame->hd.length;
1607 if (session->callbacks.select_padding_callback) {
1608 size_t max_paddedlen;
1611 nghttp2_min(frame->hd.length + NGHTTP2_MAX_PADLEN, max_payloadlen);
1613 rv = session->callbacks.select_padding_callback(
1614 session, frame, max_paddedlen, session->user_data);
1615 if (rv < (ssize_t)frame->hd.length || rv > (ssize_t)max_paddedlen) {
1616 return NGHTTP2_ERR_CALLBACK_FAILURE;
1620 return frame->hd.length;
1623 /* Add padding to HEADERS or PUSH_PROMISE. We use
1624 frame->headers.padlen in this function to use the fact that
1625 frame->push_promise has also padlen in the same position. */
1626 static int session_headers_add_pad(nghttp2_session *session,
1627 nghttp2_frame *frame) {
1629 ssize_t padded_payloadlen;
1630 nghttp2_active_outbound_item *aob;
1631 nghttp2_bufs *framebufs;
1633 size_t max_payloadlen;
1635 aob = &session->aob;
1636 framebufs = &aob->framebufs;
1638 max_payloadlen = nghttp2_min(NGHTTP2_MAX_PAYLOADLEN,
1639 frame->hd.length + NGHTTP2_MAX_PADLEN);
1642 session_call_select_padding(session, frame, max_payloadlen);
1644 if (nghttp2_is_fatal((int)padded_payloadlen)) {
1645 return (int)padded_payloadlen;
1648 padlen = padded_payloadlen - frame->hd.length;
1650 DEBUGF(fprintf(stderr, "send: padding selected: payloadlen=%zd, padlen=%zu\n",
1651 padded_payloadlen, padlen));
1653 rv = nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, 0);
1659 frame->headers.padlen = padlen;
1664 static size_t session_estimate_headers_payload(nghttp2_session *session,
1665 const nghttp2_nv *nva,
1667 size_t additional) {
1668 return nghttp2_hd_deflate_bound(&session->hd_deflater, nva, nvlen) +
1673 * This function serializes frame for transmission.
1675 * This function returns 0 if it succeeds, or one of negative error
1676 * codes, including both fatal and non-fatal ones.
1678 static int session_prep_frame(nghttp2_session *session,
1679 nghttp2_outbound_item *item) {
1681 nghttp2_frame *frame;
1684 mem = &session->mem;
1685 frame = &item->frame;
1687 if (frame->hd.type != NGHTTP2_DATA) {
1688 switch (frame->hd.type) {
1689 case NGHTTP2_HEADERS: {
1690 nghttp2_headers_aux_data *aux_data;
1691 size_t estimated_payloadlen;
1693 aux_data = &item->aux_data.headers;
1695 estimated_payloadlen = session_estimate_headers_payload(
1696 session, frame->headers.nva, frame->headers.nvlen,
1697 NGHTTP2_PRIORITY_SPECLEN);
1699 if (estimated_payloadlen > NGHTTP2_MAX_HEADERSLEN) {
1700 return NGHTTP2_ERR_FRAME_SIZE_ERROR;
1703 if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
1704 /* initial HEADERS, which opens stream */
1705 nghttp2_stream *stream;
1707 stream = nghttp2_session_open_stream(
1708 session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE,
1709 &frame->headers.pri_spec, NGHTTP2_STREAM_INITIAL,
1710 aux_data->stream_user_data);
1712 if (stream == NULL) {
1713 return NGHTTP2_ERR_NOMEM;
1716 rv = session_predicate_request_headers_send(session, item);
1721 if (session_enforce_http_messaging(session)) {
1722 nghttp2_http_record_request_method(stream, frame);
1725 nghttp2_stream *stream;
1727 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
1729 if (session_predicate_push_response_headers_send(session, stream) ==
1731 frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE;
1733 if (aux_data->stream_user_data) {
1734 stream->stream_user_data = aux_data->stream_user_data;
1736 } else if (session_predicate_response_headers_send(session, stream) ==
1738 frame->headers.cat = NGHTTP2_HCAT_RESPONSE;
1740 frame->headers.cat = NGHTTP2_HCAT_HEADERS;
1742 rv = session_predicate_headers_send(session, stream);
1745 if (stream && stream->item == item) {
1748 rv2 = nghttp2_stream_detach_item(stream, session);
1750 if (nghttp2_is_fatal(rv2)) {
1760 rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers,
1761 &session->hd_deflater);
1767 DEBUGF(fprintf(stderr,
1768 "send: before padding, HEADERS serialized in %zd bytes\n",
1769 nghttp2_bufs_len(&session->aob.framebufs)));
1771 rv = session_headers_add_pad(session, frame);
1777 DEBUGF(fprintf(stderr, "send: HEADERS finally serialized in %zd bytes\n",
1778 nghttp2_bufs_len(&session->aob.framebufs)));
1782 case NGHTTP2_PRIORITY: {
1783 if (session_is_closing(session)) {
1784 return NGHTTP2_ERR_SESSION_CLOSING;
1786 /* PRIORITY frame can be sent at any time and to any stream
1788 nghttp2_frame_pack_priority(&session->aob.framebufs, &frame->priority);
1790 /* Peer can send PRIORITY frame against idle stream to create
1791 "anchor" in dependency tree. Only client can do this in
1792 nghttp2. In nghttp2, only server retains non-active (closed
1793 or idle) streams in memory, so we don't open stream here. */
1796 case NGHTTP2_RST_STREAM:
1797 if (session_is_closing(session)) {
1798 return NGHTTP2_ERR_SESSION_CLOSING;
1800 nghttp2_frame_pack_rst_stream(&session->aob.framebufs,
1801 &frame->rst_stream);
1803 case NGHTTP2_SETTINGS: {
1804 rv = nghttp2_frame_pack_settings(&session->aob.framebufs,
1811 case NGHTTP2_PUSH_PROMISE: {
1812 nghttp2_stream *stream;
1813 nghttp2_headers_aux_data *aux_data;
1814 nghttp2_priority_spec pri_spec;
1815 size_t estimated_payloadlen;
1817 aux_data = &item->aux_data.headers;
1819 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
1821 /* stream could be NULL if associated stream was already
1824 nghttp2_priority_spec_init(&pri_spec, stream->stream_id,
1825 NGHTTP2_DEFAULT_WEIGHT, 0);
1827 nghttp2_priority_spec_default_init(&pri_spec);
1830 if (!nghttp2_session_open_stream(
1831 session, frame->push_promise.promised_stream_id,
1832 NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_RESERVED,
1833 aux_data->stream_user_data)) {
1834 return NGHTTP2_ERR_NOMEM;
1837 estimated_payloadlen = session_estimate_headers_payload(
1838 session, frame->push_promise.nva, frame->push_promise.nvlen, 0);
1840 if (estimated_payloadlen > NGHTTP2_MAX_HEADERSLEN) {
1841 return NGHTTP2_ERR_FRAME_SIZE_ERROR;
1844 /* predicte should fail if stream is NULL. */
1845 rv = session_predicate_push_promise_send(session, stream);
1852 rv = nghttp2_frame_pack_push_promise(
1853 &session->aob.framebufs, &frame->push_promise, &session->hd_deflater);
1857 rv = session_headers_add_pad(session, frame);
1865 if (session_is_closing(session)) {
1866 return NGHTTP2_ERR_SESSION_CLOSING;
1868 nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping);
1870 case NGHTTP2_WINDOW_UPDATE: {
1871 rv = session_predicate_window_update_send(session, frame->hd.stream_id);
1875 nghttp2_frame_pack_window_update(&session->aob.framebufs,
1876 &frame->window_update);
1879 case NGHTTP2_GOAWAY:
1880 rv = nghttp2_frame_pack_goaway(&session->aob.framebufs, &frame->goaway);
1884 session->local_last_stream_id = frame->goaway.last_stream_id;
1888 return NGHTTP2_ERR_INVALID_ARGUMENT;
1892 size_t next_readmax;
1893 nghttp2_stream *stream;
1895 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
1898 assert(stream->item == item);
1901 rv = nghttp2_session_predicate_data_send(session, stream);
1906 rv2 = nghttp2_stream_detach_item(stream, session);
1908 if (nghttp2_is_fatal(rv2)) {
1915 /* Assuming stream is not NULL */
1917 next_readmax = nghttp2_session_next_data_read(session, stream);
1919 if (next_readmax == 0) {
1921 /* This must be true since we only pop DATA frame item from
1922 queue when session->remote_window_size > 0 */
1923 assert(session->remote_window_size > 0);
1925 rv = nghttp2_stream_defer_item(
1926 stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session);
1928 if (nghttp2_is_fatal(rv)) {
1932 session->aob.item = NULL;
1933 active_outbound_item_reset(&session->aob, mem);
1934 return NGHTTP2_ERR_DEFERRED;
1937 rv = nghttp2_session_pack_data(session, &session->aob.framebufs,
1938 next_readmax, frame, &item->aux_data.data,
1940 if (rv == NGHTTP2_ERR_DEFERRED) {
1941 rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER,
1944 if (nghttp2_is_fatal(rv)) {
1948 session->aob.item = NULL;
1949 active_outbound_item_reset(&session->aob, mem);
1950 return NGHTTP2_ERR_DEFERRED;
1952 if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
1953 rv = nghttp2_stream_detach_item(stream, session);
1955 if (nghttp2_is_fatal(rv)) {
1959 rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,
1960 NGHTTP2_INTERNAL_ERROR);
1961 if (nghttp2_is_fatal(rv)) {
1964 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1969 rv2 = nghttp2_stream_detach_item(stream, session);
1971 if (nghttp2_is_fatal(rv2)) {
1981 nghttp2_outbound_item *
1982 nghttp2_session_get_next_ob_item(nghttp2_session *session) {
1983 if (nghttp2_outbound_queue_top(&session->ob_urgent)) {
1984 return nghttp2_outbound_queue_top(&session->ob_urgent);
1987 if (nghttp2_outbound_queue_top(&session->ob_reg)) {
1988 return nghttp2_outbound_queue_top(&session->ob_reg);
1991 if (!session_is_outgoing_concurrent_streams_max(session)) {
1992 if (nghttp2_outbound_queue_top(&session->ob_syn)) {
1993 return nghttp2_outbound_queue_top(&session->ob_syn);
1997 if (session->remote_window_size > 0 &&
1998 !nghttp2_pq_empty(&session->ob_da_pq)) {
1999 return nghttp2_pq_top(&session->ob_da_pq);
2005 nghttp2_outbound_item *
2006 nghttp2_session_pop_next_ob_item(nghttp2_session *session) {
2007 nghttp2_outbound_item *item;
2009 item = nghttp2_outbound_queue_top(&session->ob_urgent);
2011 nghttp2_outbound_queue_pop(&session->ob_urgent);
2016 item = nghttp2_outbound_queue_top(&session->ob_reg);
2018 nghttp2_outbound_queue_pop(&session->ob_reg);
2023 if (!session_is_outgoing_concurrent_streams_max(session)) {
2024 item = nghttp2_outbound_queue_top(&session->ob_syn);
2026 nghttp2_outbound_queue_pop(&session->ob_syn);
2032 if (session->remote_window_size > 0 &&
2033 !nghttp2_pq_empty(&session->ob_da_pq)) {
2034 item = nghttp2_pq_top(&session->ob_da_pq);
2035 nghttp2_pq_pop(&session->ob_da_pq);
2043 static int session_call_before_frame_send(nghttp2_session *session,
2044 nghttp2_frame *frame) {
2046 if (session->callbacks.before_frame_send_callback) {
2047 rv = session->callbacks.before_frame_send_callback(session, frame,
2048 session->user_data);
2050 return NGHTTP2_ERR_CALLBACK_FAILURE;
2056 static int session_call_on_frame_send(nghttp2_session *session,
2057 nghttp2_frame *frame) {
2059 if (session->callbacks.on_frame_send_callback) {
2060 rv = session->callbacks.on_frame_send_callback(session, frame,
2061 session->user_data);
2063 return NGHTTP2_ERR_CALLBACK_FAILURE;
2069 static int find_stream_on_goaway_func(nghttp2_map_entry *entry, void *ptr) {
2070 nghttp2_close_stream_on_goaway_arg *arg;
2071 nghttp2_stream *stream;
2073 arg = (nghttp2_close_stream_on_goaway_arg *)ptr;
2074 stream = (nghttp2_stream *)entry;
2076 if (nghttp2_session_is_my_stream_id(arg->session, stream->stream_id)) {
2077 if (arg->incoming) {
2080 } else if (!arg->incoming) {
2084 if (stream->state != NGHTTP2_STREAM_IDLE &&
2085 (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) == 0 &&
2086 stream->stream_id > arg->last_stream_id) {
2087 /* We are collecting streams to close because we cannot call
2088 nghttp2_session_close_stream() inside nghttp2_map_each().
2089 Reuse closed_next member.. bad choice? */
2090 assert(stream->closed_next == NULL);
2091 assert(stream->closed_prev == NULL);
2094 stream->closed_next = arg->head;
2104 /* Closes non-idle and non-closed streams whose stream ID >
2105 last_stream_id. If incoming is nonzero, we are going to close
2106 incoming streams. Otherwise, close outgoing streams. */
2107 static int session_close_stream_on_goaway(nghttp2_session *session,
2108 int32_t last_stream_id,
2111 nghttp2_stream *stream, *next_stream;
2112 nghttp2_close_stream_on_goaway_arg arg = {session, NULL, last_stream_id,
2115 rv = nghttp2_map_each(&session->streams, find_stream_on_goaway_func, &arg);
2120 next_stream = stream->closed_next;
2121 stream->closed_next = NULL;
2122 rv = nghttp2_session_close_stream(session, stream->stream_id,
2123 NGHTTP2_REFUSED_STREAM);
2125 /* stream may be deleted here */
2127 stream = next_stream;
2129 if (nghttp2_is_fatal(rv)) {
2130 /* Clean up closed_next member just in case */
2132 next_stream = stream->closed_next;
2133 stream->closed_next = NULL;
2134 stream = next_stream;
2143 static void session_outbound_item_schedule(nghttp2_session *session,
2144 nghttp2_outbound_item *item,
2146 /* Schedule next write. Offset proportional to the write size.
2147 Stream with heavier weight is scheduled earlier. */
2148 size_t delta = item->frame.hd.length * NGHTTP2_MAX_WEIGHT / weight;
2150 if (session->last_cycle < item->cycle) {
2151 session->last_cycle = item->cycle;
2154 /* We pretend to ignore overflow given that the value range of
2155 item->cycle, which is uint64_t. nghttp2 won't explode even when
2156 overflow occurs, there might be some disturbance of priority. */
2157 item->cycle = session->last_cycle + delta;
2161 * Called after a frame is sent. This function runs
2162 * on_frame_send_callback and handles stream closure upon END_STREAM
2163 * or RST_STREAM. This function does not reset session->aob. It is a
2164 * responsibility of session_after_frame_sent2.
2166 * This function returns 0 if it succeeds, or one of the following
2167 * negative error codes:
2171 * NGHTTP2_ERR_CALLBACK_FAILURE
2172 * The callback function failed.
2174 static int session_after_frame_sent1(nghttp2_session *session) {
2176 nghttp2_active_outbound_item *aob = &session->aob;
2177 nghttp2_outbound_item *item = aob->item;
2178 nghttp2_bufs *framebufs = &aob->framebufs;
2179 nghttp2_frame *frame;
2181 frame = &item->frame;
2183 if (frame->hd.type != NGHTTP2_DATA) {
2185 if (frame->hd.type == NGHTTP2_HEADERS ||
2186 frame->hd.type == NGHTTP2_PUSH_PROMISE) {
2188 if (nghttp2_bufs_next_present(framebufs)) {
2189 DEBUGF(fprintf(stderr, "send: CONTINUATION exists, just return\n"));
2193 rv = session_call_on_frame_send(session, frame);
2194 if (nghttp2_is_fatal(rv)) {
2197 switch (frame->hd.type) {
2198 case NGHTTP2_HEADERS: {
2199 nghttp2_headers_aux_data *aux_data;
2200 nghttp2_stream *stream;
2202 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2207 if (stream->item == item) {
2208 rv = nghttp2_stream_detach_item(stream, session);
2210 if (nghttp2_is_fatal(rv)) {
2215 switch (frame->headers.cat) {
2216 case NGHTTP2_HCAT_REQUEST: {
2217 stream->state = NGHTTP2_STREAM_OPENING;
2218 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
2219 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
2221 rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
2222 if (nghttp2_is_fatal(rv)) {
2225 /* We assume aux_data is a pointer to nghttp2_headers_aux_data */
2226 aux_data = &item->aux_data.headers;
2227 if (aux_data->data_prd.read_callback) {
2228 /* nghttp2_submit_data() makes a copy of aux_data->data_prd */
2229 rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM,
2230 frame->hd.stream_id, &aux_data->data_prd);
2231 if (nghttp2_is_fatal(rv)) {
2234 /* TODO nghttp2_submit_data() may fail if stream has already
2235 DATA frame item. We might have to handle it here. */
2239 case NGHTTP2_HCAT_PUSH_RESPONSE:
2240 stream->flags &= ~NGHTTP2_STREAM_FLAG_PUSH;
2241 ++session->num_outgoing_streams;
2243 case NGHTTP2_HCAT_RESPONSE:
2244 stream->state = NGHTTP2_STREAM_OPENED;
2246 case NGHTTP2_HCAT_HEADERS:
2247 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
2248 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
2250 rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
2251 if (nghttp2_is_fatal(rv)) {
2254 /* We assume aux_data is a pointer to nghttp2_headers_aux_data */
2255 aux_data = &item->aux_data.headers;
2256 if (aux_data->data_prd.read_callback) {
2257 rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM,
2258 frame->hd.stream_id, &aux_data->data_prd);
2259 if (nghttp2_is_fatal(rv)) {
2262 /* TODO nghttp2_submit_data() may fail if stream has already
2263 DATA frame item. We might have to handle it here. */
2269 case NGHTTP2_PRIORITY: {
2270 nghttp2_stream *stream;
2272 if (session->server) {
2276 stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);
2282 rv = nghttp2_session_reprioritize_stream(session, stream,
2283 &frame->priority.pri_spec);
2285 if (nghttp2_is_fatal(rv)) {
2291 case NGHTTP2_RST_STREAM:
2292 rv = nghttp2_session_close_stream(session, frame->hd.stream_id,
2293 frame->rst_stream.error_code);
2294 if (nghttp2_is_fatal(rv)) {
2298 case NGHTTP2_GOAWAY: {
2299 nghttp2_goaway_aux_data *aux_data;
2301 aux_data = &item->aux_data.goaway;
2303 if ((aux_data->flags & NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE) == 0) {
2305 if (aux_data->flags & NGHTTP2_GOAWAY_AUX_TERM_ON_SEND) {
2306 session->goaway_flags |= NGHTTP2_GOAWAY_TERM_SENT;
2309 session->goaway_flags |= NGHTTP2_GOAWAY_SENT;
2311 rv = session_close_stream_on_goaway(session,
2312 frame->goaway.last_stream_id, 1);
2314 if (nghttp2_is_fatal(rv)) {
2327 nghttp2_stream *stream;
2328 nghttp2_data_aux_data *aux_data;
2330 aux_data = &item->aux_data.data;
2332 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2333 /* We update flow control window after a frame was completely
2334 sent. This is possible because we choose payload length not to
2335 exceed the window */
2336 session->remote_window_size -= frame->hd.length;
2338 stream->remote_window_size -= frame->hd.length;
2341 if (stream && aux_data->eof) {
2342 rv = nghttp2_stream_detach_item(stream, session);
2344 if (nghttp2_is_fatal(rv)) {
2348 /* Call on_frame_send_callback after
2349 nghttp2_stream_detach_item(), so that application can issue
2350 nghttp2_submit_data() in the callback. */
2351 if (session->callbacks.on_frame_send_callback) {
2352 rv = session_call_on_frame_send(session, frame);
2354 if (nghttp2_is_fatal(rv)) {
2359 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
2363 (stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR;
2365 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
2367 rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
2368 if (nghttp2_is_fatal(rv)) {
2371 /* stream may be NULL if it was closed */
2372 if (stream_closed) {
2379 if (session->callbacks.on_frame_send_callback) {
2380 rv = session_call_on_frame_send(session, frame);
2382 if (nghttp2_is_fatal(rv)) {
2395 * Called after a frame is sent and session_after_frame_sent1. This
2396 * function is responsible to reset session->aob.
2398 * This function returns 0 if it succeeds, or one of the following
2399 * negative error codes:
2403 * NGHTTP2_ERR_CALLBACK_FAILURE
2404 * The callback function failed.
2406 static int session_after_frame_sent2(nghttp2_session *session) {
2408 nghttp2_active_outbound_item *aob = &session->aob;
2409 nghttp2_outbound_item *item = aob->item;
2410 nghttp2_bufs *framebufs = &aob->framebufs;
2411 nghttp2_frame *frame;
2414 mem = &session->mem;
2415 frame = &item->frame;
2417 if (frame->hd.type != NGHTTP2_DATA) {
2419 if (frame->hd.type == NGHTTP2_HEADERS ||
2420 frame->hd.type == NGHTTP2_PUSH_PROMISE) {
2422 if (nghttp2_bufs_next_present(framebufs)) {
2423 framebufs->cur = framebufs->cur->next;
2425 DEBUGF(fprintf(stderr, "send: next CONTINUATION frame, %zu bytes\n",
2426 nghttp2_buf_len(&framebufs->cur->buf)));
2432 active_outbound_item_reset(&session->aob, mem);
2436 nghttp2_outbound_item *next_item;
2437 nghttp2_stream *stream;
2438 nghttp2_data_aux_data *aux_data;
2440 aux_data = &item->aux_data.data;
2442 /* On EOF, we have already detached data. Please note that
2443 application may issue nghttp2_submit_data() in
2444 on_frame_send_callback (call from session_after_frame_sent1),
2445 which attach data to stream. We don't want to detach it. */
2446 if (aux_data->eof) {
2447 active_outbound_item_reset(aob, mem);
2452 /* Reset no_copy here because next write may not use this. */
2453 aux_data->no_copy = 0;
2455 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2457 /* If session is closed or RST_STREAM was queued, we won't send
2459 if (nghttp2_session_predicate_data_send(session, stream) != 0) {
2461 rv = nghttp2_stream_detach_item(stream, session);
2463 if (nghttp2_is_fatal(rv)) {
2468 active_outbound_item_reset(aob, mem);
2473 /* Assuming stream is not NULL */
2475 next_item = nghttp2_session_get_next_ob_item(session);
2477 /* If priority of this stream is higher or equal to other stream
2478 waiting at the top of the queue, we continue to send this
2480 if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP &&
2481 (next_item == NULL || (next_item->frame.hd.type == NGHTTP2_DATA &&
2482 outbound_item_less(item, next_item)))) {
2483 size_t next_readmax;
2485 next_readmax = nghttp2_session_next_data_read(session, stream);
2487 if (next_readmax == 0) {
2489 if (session->remote_window_size == 0 &&
2490 stream->remote_window_size > 0) {
2492 /* If DATA cannot be sent solely due to connection level
2493 window size, just push item to queue again. We never pop
2494 DATA item while connection level window size is 0. */
2495 rv = nghttp2_pq_push(&session->ob_da_pq, aob->item);
2497 if (nghttp2_is_fatal(rv)) {
2501 aob->item->queued = 1;
2503 rv = nghttp2_stream_defer_item(
2504 stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session);
2506 if (nghttp2_is_fatal(rv)) {
2512 active_outbound_item_reset(aob, mem);
2517 nghttp2_bufs_reset(framebufs);
2519 rv = nghttp2_session_pack_data(session, framebufs, next_readmax, frame,
2521 if (nghttp2_is_fatal(rv)) {
2524 if (rv == NGHTTP2_ERR_DEFERRED) {
2525 rv = nghttp2_stream_defer_item(
2526 stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER, session);
2528 if (nghttp2_is_fatal(rv)) {
2533 active_outbound_item_reset(aob, mem);
2538 if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
2539 /* Stop DATA frame chain and issue RST_STREAM to close the
2540 stream. We don't return
2541 NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE intentionally. */
2542 rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,
2543 NGHTTP2_INTERNAL_ERROR);
2545 if (nghttp2_is_fatal(rv)) {
2549 rv = nghttp2_stream_detach_item(stream, session);
2551 if (nghttp2_is_fatal(rv)) {
2555 active_outbound_item_reset(aob, mem);
2561 if (aux_data->no_copy) {
2562 aob->state = NGHTTP2_OB_SEND_NO_COPY;
2564 aob->state = NGHTTP2_OB_SEND_DATA;
2570 if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) {
2571 rv = nghttp2_pq_push(&session->ob_da_pq, aob->item);
2573 if (nghttp2_is_fatal(rv)) {
2577 aob->item->queued = 1;
2581 active_outbound_item_reset(&session->aob, mem);
2589 static int session_call_send_data(nghttp2_session *session,
2590 nghttp2_outbound_item *item,
2591 nghttp2_bufs *framebufs) {
2595 nghttp2_frame *frame;
2596 nghttp2_data_aux_data *aux_data;
2598 buf = &framebufs->cur->buf;
2599 frame = &item->frame;
2600 length = frame->hd.length - frame->data.padlen;
2601 aux_data = &item->aux_data.data;
2603 rv = session->callbacks.send_data_callback(session, frame, buf->pos, length,
2604 &aux_data->data_prd.source,
2605 session->user_data);
2607 if (rv == 0 || rv == NGHTTP2_ERR_WOULDBLOCK ||
2608 rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
2612 return NGHTTP2_ERR_CALLBACK_FAILURE;
2615 static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session,
2616 const uint8_t **data_ptr,
2619 nghttp2_active_outbound_item *aob;
2620 nghttp2_bufs *framebufs;
2623 mem = &session->mem;
2624 aob = &session->aob;
2625 framebufs = &aob->framebufs;
2629 switch (aob->state) {
2630 case NGHTTP2_OB_POP_ITEM: {
2631 nghttp2_outbound_item *item;
2633 item = nghttp2_session_pop_next_ob_item(session);
2638 if (item->frame.hd.type == NGHTTP2_DATA ||
2639 item->frame.hd.type == NGHTTP2_HEADERS) {
2640 nghttp2_frame *frame;
2641 nghttp2_stream *stream;
2643 frame = &item->frame;
2644 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2646 if (stream && item == stream->item &&
2647 stream->dpri != NGHTTP2_STREAM_DPRI_TOP) {
2648 /* We have DATA with higher priority in queue within the
2649 same dependency tree. */
2654 rv = session_prep_frame(session, item);
2655 if (rv == NGHTTP2_ERR_DEFERRED) {
2656 DEBUGF(fprintf(stderr, "send: frame transmission deferred\n"));
2660 int32_t opened_stream_id = 0;
2661 uint32_t error_code = NGHTTP2_INTERNAL_ERROR;
2663 DEBUGF(fprintf(stderr, "send: frame preparation failed with %s\n",
2664 nghttp2_strerror(rv)));
2665 /* TODO If the error comes from compressor, the connection
2667 if (item->frame.hd.type != NGHTTP2_DATA &&
2668 session->callbacks.on_frame_not_send_callback && is_non_fatal(rv)) {
2669 nghttp2_frame *frame = &item->frame;
2670 /* The library is responsible for the transmission of
2671 WINDOW_UPDATE frame, so we don't call error callback for
2673 if (frame->hd.type != NGHTTP2_WINDOW_UPDATE &&
2674 session->callbacks.on_frame_not_send_callback(
2675 session, frame, rv, session->user_data) != 0) {
2677 nghttp2_outbound_item_free(item, mem);
2678 nghttp2_mem_free(mem, item);
2680 return NGHTTP2_ERR_CALLBACK_FAILURE;
2683 /* We have to close stream opened by failed request HEADERS
2685 switch (item->frame.hd.type) {
2686 case NGHTTP2_HEADERS:
2687 if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) {
2688 opened_stream_id = item->frame.hd.stream_id;
2689 if (item->aux_data.headers.canceled) {
2690 error_code = item->aux_data.headers.error_code;
2694 case NGHTTP2_PUSH_PROMISE:
2695 opened_stream_id = item->frame.push_promise.promised_stream_id;
2698 if (opened_stream_id) {
2699 /* careful not to override rv */
2701 rv2 = nghttp2_session_close_stream(session, opened_stream_id,
2704 if (nghttp2_is_fatal(rv2)) {
2709 nghttp2_outbound_item_free(item, mem);
2710 nghttp2_mem_free(mem, item);
2711 active_outbound_item_reset(aob, mem);
2713 if (rv == NGHTTP2_ERR_HEADER_COMP) {
2714 /* If header compression error occurred, should terminiate
2716 rv = nghttp2_session_terminate_session(session,
2717 NGHTTP2_INTERNAL_ERROR);
2719 if (nghttp2_is_fatal(rv)) {
2727 nghttp2_bufs_rewind(framebufs);
2729 if (item->frame.hd.type != NGHTTP2_DATA) {
2730 nghttp2_frame *frame;
2732 frame = &item->frame;
2734 DEBUGF(fprintf(stderr, "send: next frame: payloadlen=%zu, type=%u, "
2735 "flags=0x%02x, stream_id=%d\n",
2736 frame->hd.length, frame->hd.type, frame->hd.flags,
2737 frame->hd.stream_id));
2739 rv = session_call_before_frame_send(session, frame);
2740 if (nghttp2_is_fatal(rv)) {
2744 DEBUGF(fprintf(stderr, "send: next frame: DATA\n"));
2746 if (item->aux_data.data.no_copy) {
2747 aob->state = NGHTTP2_OB_SEND_NO_COPY;
2752 DEBUGF(fprintf(stderr,
2753 "send: start transmitting frame type=%u, length=%zd\n",
2754 framebufs->cur->buf.pos[3],
2755 framebufs->cur->buf.last - framebufs->cur->buf.pos));
2757 aob->state = NGHTTP2_OB_SEND_DATA;
2761 case NGHTTP2_OB_SEND_DATA: {
2765 buf = &framebufs->cur->buf;
2767 if (buf->pos == buf->last) {
2768 DEBUGF(fprintf(stderr, "send: end transmission of a frame\n"));
2770 /* Frame has completely sent */
2772 rv = session_after_frame_sent2(session);
2774 rv = session_after_frame_sent1(session);
2777 assert(nghttp2_is_fatal(rv));
2780 rv = session_after_frame_sent2(session);
2784 assert(nghttp2_is_fatal(rv));
2787 /* We have already adjusted the next state */
2791 *data_ptr = buf->pos;
2792 datalen = nghttp2_buf_len(buf);
2794 /* We increment the offset here. If send_callback does not send
2795 everything, we will adjust it. */
2796 buf->pos += datalen;
2800 case NGHTTP2_OB_SEND_NO_COPY: {
2801 nghttp2_stream *stream;
2802 nghttp2_frame *frame;
2804 DEBUGF(fprintf(stderr, "send: no copy DATA\n"));
2806 frame = &aob->item->frame;
2808 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2809 if (stream == NULL) {
2812 "send: no copy DATA cancelled because stream was closed\n"));
2814 active_outbound_item_reset(aob, mem);
2819 rv = session_call_send_data(session, aob->item, framebufs);
2820 if (nghttp2_is_fatal(rv)) {
2824 if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
2825 rv = nghttp2_stream_detach_item(stream, session);
2827 if (nghttp2_is_fatal(rv)) {
2831 rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,
2832 NGHTTP2_INTERNAL_ERROR);
2833 if (nghttp2_is_fatal(rv)) {
2837 active_outbound_item_reset(aob, mem);
2842 if (rv == NGHTTP2_ERR_WOULDBLOCK) {
2848 rv = session_after_frame_sent1(session);
2850 assert(nghttp2_is_fatal(rv));
2853 rv = session_after_frame_sent2(session);
2855 assert(nghttp2_is_fatal(rv));
2859 /* We have already adjusted the next state */
2863 case NGHTTP2_OB_SEND_CLIENT_MAGIC: {
2867 buf = &framebufs->cur->buf;
2869 if (buf->pos == buf->last) {
2870 DEBUGF(fprintf(stderr, "send: end transmission of client magic\n"));
2871 active_outbound_item_reset(aob, mem);
2875 *data_ptr = buf->pos;
2876 datalen = nghttp2_buf_len(buf);
2878 buf->pos += datalen;
2886 ssize_t nghttp2_session_mem_send(nghttp2_session *session,
2887 const uint8_t **data_ptr) {
2891 len = nghttp2_session_mem_send_internal(session, data_ptr, 1);
2896 if (session->aob.item) {
2897 /* We have to call session_after_frame_sent1 here to handle stream
2898 closure upon transmission of frames. Otherwise, END_STREAM may
2899 be reached to client before we call nghttp2_session_mem_send
2900 again and we may get exceeding number of incoming streams. */
2901 rv = session_after_frame_sent1(session);
2903 assert(nghttp2_is_fatal(rv));
2911 int nghttp2_session_send(nghttp2_session *session) {
2912 const uint8_t *data;
2915 nghttp2_bufs *framebufs;
2917 framebufs = &session->aob.framebufs;
2920 datalen = nghttp2_session_mem_send_internal(session, &data, 0);
2922 return (int)datalen;
2924 sentlen = session->callbacks.send_callback(session, data, datalen, 0,
2925 session->user_data);
2927 if (sentlen == NGHTTP2_ERR_WOULDBLOCK) {
2928 /* Transmission canceled. Rewind the offset */
2929 framebufs->cur->buf.pos -= datalen;
2933 return NGHTTP2_ERR_CALLBACK_FAILURE;
2935 /* Rewind the offset to the amount of unsent bytes */
2936 framebufs->cur->buf.pos -= datalen - sentlen;
2940 static ssize_t session_recv(nghttp2_session *session, uint8_t *buf,
2943 rv = session->callbacks.recv_callback(session, buf, len, 0,
2944 session->user_data);
2946 if ((size_t)rv > len) {
2947 return NGHTTP2_ERR_CALLBACK_FAILURE;
2949 } else if (rv < 0 && rv != NGHTTP2_ERR_WOULDBLOCK && rv != NGHTTP2_ERR_EOF) {
2950 return NGHTTP2_ERR_CALLBACK_FAILURE;
2955 static int session_call_on_begin_frame(nghttp2_session *session,
2956 const nghttp2_frame_hd *hd) {
2959 if (session->callbacks.on_begin_frame_callback) {
2961 rv = session->callbacks.on_begin_frame_callback(session, hd,
2962 session->user_data);
2965 return NGHTTP2_ERR_CALLBACK_FAILURE;
2972 static int session_call_on_frame_received(nghttp2_session *session,
2973 nghttp2_frame *frame) {
2975 if (session->callbacks.on_frame_recv_callback) {
2976 rv = session->callbacks.on_frame_recv_callback(session, frame,
2977 session->user_data);
2979 return NGHTTP2_ERR_CALLBACK_FAILURE;
2985 static int session_call_on_begin_headers(nghttp2_session *session,
2986 nghttp2_frame *frame) {
2988 DEBUGF(fprintf(stderr, "recv: call on_begin_headers callback stream_id=%d\n",
2989 frame->hd.stream_id));
2990 if (session->callbacks.on_begin_headers_callback) {
2991 rv = session->callbacks.on_begin_headers_callback(session, frame,
2992 session->user_data);
2993 if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
2997 return NGHTTP2_ERR_CALLBACK_FAILURE;
3003 static int session_call_on_header(nghttp2_session *session,
3004 const nghttp2_frame *frame,
3005 const nghttp2_nv *nv) {
3007 if (session->callbacks.on_header_callback) {
3008 rv = session->callbacks.on_header_callback(
3009 session, frame, nv->name, nv->namelen, nv->value, nv->valuelen,
3010 nv->flags, session->user_data);
3011 if (rv == NGHTTP2_ERR_PAUSE ||
3012 rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
3016 return NGHTTP2_ERR_CALLBACK_FAILURE;
3023 * Handles frame size error.
3025 * This function returns 0 if it succeeds, or one of the following
3026 * negative error codes:
3031 static int session_handle_frame_size_error(nghttp2_session *session,
3032 nghttp2_frame *frame _U_) {
3033 /* TODO Currently no callback is called for this error, because we
3034 call this callback before reading any payload */
3035 return nghttp2_session_terminate_session(session, NGHTTP2_FRAME_SIZE_ERROR);
3038 static int get_error_code_from_lib_error_code(int lib_error_code) {
3039 switch (lib_error_code) {
3040 case NGHTTP2_ERR_STREAM_CLOSED:
3041 return NGHTTP2_STREAM_CLOSED;
3042 case NGHTTP2_ERR_HEADER_COMP:
3043 return NGHTTP2_COMPRESSION_ERROR;
3044 case NGHTTP2_ERR_FRAME_SIZE_ERROR:
3045 return NGHTTP2_FRAME_SIZE_ERROR;
3046 case NGHTTP2_ERR_FLOW_CONTROL:
3047 return NGHTTP2_FLOW_CONTROL_ERROR;
3048 case NGHTTP2_ERR_REFUSED_STREAM:
3049 return NGHTTP2_REFUSED_STREAM;
3050 case NGHTTP2_ERR_PROTO:
3051 case NGHTTP2_ERR_HTTP_HEADER:
3052 case NGHTTP2_ERR_HTTP_MESSAGING:
3053 return NGHTTP2_PROTOCOL_ERROR;
3055 return NGHTTP2_INTERNAL_ERROR;
3059 static int session_handle_invalid_stream2(nghttp2_session *session,
3061 nghttp2_frame *frame,
3062 int lib_error_code) {
3064 rv = nghttp2_session_add_rst_stream(
3065 session, stream_id, get_error_code_from_lib_error_code(lib_error_code));
3069 if (session->callbacks.on_invalid_frame_recv_callback) {
3070 if (session->callbacks.on_invalid_frame_recv_callback(
3071 session, frame, lib_error_code, session->user_data) != 0) {
3072 return NGHTTP2_ERR_CALLBACK_FAILURE;
3078 static int session_handle_invalid_stream(nghttp2_session *session,
3079 nghttp2_frame *frame,
3080 int lib_error_code) {
3081 return session_handle_invalid_stream2(session, frame->hd.stream_id, frame,
3085 static int session_inflate_handle_invalid_stream(nghttp2_session *session,
3086 nghttp2_frame *frame,
3087 int lib_error_code) {
3089 rv = session_handle_invalid_stream(session, frame, lib_error_code);
3090 if (nghttp2_is_fatal(rv)) {
3093 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3097 * Handles invalid frame which causes connection error.
3099 static int session_handle_invalid_connection(nghttp2_session *session,
3100 nghttp2_frame *frame,
3102 const char *reason) {
3103 if (session->callbacks.on_invalid_frame_recv_callback) {
3104 if (session->callbacks.on_invalid_frame_recv_callback(
3105 session, frame, lib_error_code, session->user_data) != 0) {
3106 return NGHTTP2_ERR_CALLBACK_FAILURE;
3109 return nghttp2_session_terminate_session_with_reason(
3110 session, get_error_code_from_lib_error_code(lib_error_code), reason);
3113 static int session_inflate_handle_invalid_connection(nghttp2_session *session,
3114 nghttp2_frame *frame,
3116 const char *reason) {
3119 session_handle_invalid_connection(session, frame, lib_error_code, reason);
3120 if (nghttp2_is_fatal(rv)) {
3123 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3127 * Inflates header block in the memory pointed by |in| with |inlen|
3128 * bytes. If this function returns NGHTTP2_ERR_PAUSE, the caller must
3129 * call this function again, until it returns 0 or one of negative
3130 * error code. If |call_header_cb| is zero, the on_header_callback
3131 * are not invoked and the function never return NGHTTP2_ERR_PAUSE. If
3132 * the given |in| is the last chunk of header block, the |final| must
3133 * be nonzero. If header block is successfully processed (which is
3134 * indicated by the return value 0, NGHTTP2_ERR_PAUSE or
3135 * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE), the number of processed
3136 * input bytes is assigned to the |*readlen_ptr|.
3138 * This function return 0 if it succeeds, or one of the negative error
3141 * NGHTTP2_ERR_CALLBACK_FAILURE
3142 * The callback function failed.
3143 * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
3144 * The callback returns this error code, indicating that this
3145 * stream should be RST_STREAMed.
3149 * The callback function returned NGHTTP2_ERR_PAUSE
3150 * NGHTTP2_ERR_HEADER_COMP
3151 * Header decompression failed
3153 static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
3154 size_t *readlen_ptr, uint8_t *in, size_t inlen,
3155 int final, int call_header_cb) {
3160 nghttp2_stream *stream;
3161 nghttp2_stream *subject_stream;
3166 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
3168 if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
3169 subject_stream = nghttp2_session_get_stream(
3170 session, frame->push_promise.promised_stream_id);
3172 subject_stream = stream;
3173 trailer = session_trailer_headers(session, stream, frame);
3176 DEBUGF(fprintf(stderr, "recv: decoding header block %zu bytes\n", inlen));
3179 proclen = nghttp2_hd_inflate_hd2(&session->hd_inflater, &nv, &inflate_flags,
3180 &token, in, inlen, final);
3181 if (nghttp2_is_fatal((int)proclen)) {
3182 return (int)proclen;
3185 if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) {
3186 if (subject_stream && subject_stream->state != NGHTTP2_STREAM_CLOSING) {
3187 /* Adding RST_STREAM here is very important. It prevents
3188 from invoking subsequent callbacks for the same stream
3190 rv = nghttp2_session_add_rst_stream(
3191 session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR);
3193 if (nghttp2_is_fatal(rv)) {
3199 nghttp2_session_terminate_session(session, NGHTTP2_COMPRESSION_ERROR);
3200 if (nghttp2_is_fatal(rv)) {
3204 return NGHTTP2_ERR_HEADER_COMP;
3208 *readlen_ptr += proclen;
3210 DEBUGF(fprintf(stderr, "recv: proclen=%zd\n", proclen));
3212 if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) {
3214 if (subject_stream && session_enforce_http_messaging(session)) {
3215 rv = nghttp2_http_on_header(session, subject_stream, frame, &nv, token,
3217 if (rv == NGHTTP2_ERR_HTTP_HEADER) {
3219 stderr, "recv: HTTP error: type=%d, id=%d, header %.*s: %.*s\n",
3220 frame->hd.type, subject_stream->stream_id, (int)nv.namelen,
3221 nv.name, (int)nv.valuelen, nv.value));
3224 session_handle_invalid_stream2(session, subject_stream->stream_id,
3225 frame, NGHTTP2_ERR_HTTP_HEADER);
3226 if (nghttp2_is_fatal(rv)) {
3229 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
3232 if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) {
3233 /* header is ignored */
3235 stderr, "recv: HTTP ignored: type=%d, id=%d, header %.*s: %.*s\n",
3236 frame->hd.type, subject_stream->stream_id, (int)nv.namelen,
3237 nv.name, (int)nv.valuelen, nv.value));
3241 rv = session_call_on_header(session, frame, &nv);
3242 /* This handles NGHTTP2_ERR_PAUSE and
3243 NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE as well */
3249 if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
3250 nghttp2_hd_inflate_end_headers(&session->hd_inflater);
3253 if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) {
3261 * Decompress header blocks of incoming request HEADERS and also call
3262 * additional callbacks. This function can be called again if this
3263 * function returns NGHTTP2_ERR_PAUSE.
3265 * This function returns 0 if it succeeds, or one of negative error
3268 * NGHTTP2_ERR_CALLBACK_FAILURE
3269 * The callback function failed.
3273 int nghttp2_session_end_request_headers_received(nghttp2_session *session _U_,
3274 nghttp2_frame *frame,
3275 nghttp2_stream *stream) {
3276 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
3277 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
3279 /* Here we assume that stream is not shutdown in NGHTTP2_SHUT_WR */
3284 * Decompress header blocks of incoming (push-)response HEADERS and
3285 * also call additional callbacks. This function can be called again
3286 * if this function returns NGHTTP2_ERR_PAUSE.
3288 * This function returns 0 if it succeeds, or one of negative error
3291 * NGHTTP2_ERR_CALLBACK_FAILURE
3292 * The callback function failed.
3296 int nghttp2_session_end_response_headers_received(nghttp2_session *session,
3297 nghttp2_frame *frame,
3298 nghttp2_stream *stream) {
3300 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
3301 /* This is the last frame of this stream, so disallow
3302 further receptions. */
3303 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
3304 rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
3305 if (nghttp2_is_fatal(rv)) {
3313 * Decompress header blocks of incoming HEADERS and also call
3314 * additional callbacks. This function can be called again if this
3315 * function returns NGHTTP2_ERR_PAUSE.
3317 * This function returns 0 if it succeeds, or one of negative error
3320 * NGHTTP2_ERR_CALLBACK_FAILURE
3321 * The callback function failed.
3325 int nghttp2_session_end_headers_received(nghttp2_session *session,
3326 nghttp2_frame *frame,
3327 nghttp2_stream *stream) {
3329 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
3330 if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
3332 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
3333 rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
3334 if (nghttp2_is_fatal(rv)) {
3341 static int session_after_header_block_received(nghttp2_session *session) {
3344 nghttp2_frame *frame = &session->iframe.frame;
3345 nghttp2_stream *stream;
3347 /* We don't call on_frame_recv_callback if stream has been closed
3348 already or being closed. */
3349 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
3350 if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) {
3354 if (session_enforce_http_messaging(session)) {
3355 if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
3356 nghttp2_stream *subject_stream;
3358 subject_stream = nghttp2_session_get_stream(
3359 session, frame->push_promise.promised_stream_id);
3360 if (subject_stream) {
3361 rv = nghttp2_http_on_request_headers(subject_stream, frame);
3364 assert(frame->hd.type == NGHTTP2_HEADERS);
3365 switch (frame->headers.cat) {
3366 case NGHTTP2_HCAT_REQUEST:
3367 rv = nghttp2_http_on_request_headers(stream, frame);
3369 case NGHTTP2_HCAT_RESPONSE:
3370 case NGHTTP2_HCAT_PUSH_RESPONSE:
3371 rv = nghttp2_http_on_response_headers(stream);
3373 case NGHTTP2_HCAT_HEADERS:
3374 if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
3375 assert(!session->server);
3376 rv = nghttp2_http_on_response_headers(stream);
3378 rv = nghttp2_http_on_trailer_headers(stream, frame);
3384 if (rv == 0 && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
3385 rv = nghttp2_http_on_remote_end_stream(stream);
3391 if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
3392 stream_id = frame->push_promise.promised_stream_id;
3394 stream_id = frame->hd.stream_id;
3399 rv = session_handle_invalid_stream2(session, stream_id, frame,
3400 NGHTTP2_ERR_HTTP_MESSAGING);
3401 if (nghttp2_is_fatal(rv)) {
3408 rv = session_call_on_frame_received(session, frame);
3409 if (nghttp2_is_fatal(rv)) {
3414 if (frame->hd.type != NGHTTP2_HEADERS) {
3418 switch (frame->headers.cat) {
3419 case NGHTTP2_HCAT_REQUEST:
3420 return nghttp2_session_end_request_headers_received(session, frame, stream);
3421 case NGHTTP2_HCAT_RESPONSE:
3422 case NGHTTP2_HCAT_PUSH_RESPONSE:
3423 return nghttp2_session_end_response_headers_received(session, frame,
3425 case NGHTTP2_HCAT_HEADERS:
3426 return nghttp2_session_end_headers_received(session, frame, stream);
3433 int nghttp2_session_on_request_headers_received(nghttp2_session *session,
3434 nghttp2_frame *frame) {
3436 nghttp2_stream *stream;
3437 if (frame->hd.stream_id == 0) {
3438 return session_inflate_handle_invalid_connection(
3439 session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0");
3442 /* If client recieves idle stream from server, it is invalid
3443 regardless stream ID is even or odd. This is because client is
3444 not expected to receive request from server. */
3445 if (!session->server) {
3446 if (session_detect_idle_stream(session, frame->hd.stream_id)) {
3447 return session_inflate_handle_invalid_connection(
3448 session, frame, NGHTTP2_ERR_PROTO,
3449 "request HEADERS: client received request");
3452 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3455 if (!session_is_new_peer_stream_id(session, frame->hd.stream_id)) {
3456 /* The spec says if an endpoint receives a HEADERS with invalid
3457 stream ID, it MUST issue connection error with error code
3458 PROTOCOL_ERROR. But we could get trailer HEADERS after we have
3459 sent RST_STREAM to this stream and peer have not received it.
3460 Then connection error is too harsh. It means that we only use
3461 connection error if stream ID refers idle stream. OTherwise we
3462 just ignore HEADERS for now. */
3463 if (session_detect_idle_stream(session, frame->hd.stream_id)) {
3464 return session_inflate_handle_invalid_connection(
3465 session, frame, NGHTTP2_ERR_PROTO,
3466 "request HEADERS: invalid stream_id");
3469 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3471 session->last_recv_stream_id = frame->hd.stream_id;
3473 if (session->goaway_flags & NGHTTP2_GOAWAY_SENT) {
3474 /* We just ignore stream after GOAWAY was queued */
3475 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3478 if (session_is_incoming_concurrent_streams_max(session)) {
3479 return session_inflate_handle_invalid_connection(
3480 session, frame, NGHTTP2_ERR_PROTO,
3481 "request HEADERS: max concurrent streams exceeded");
3484 if (frame->headers.pri_spec.stream_id == frame->hd.stream_id) {
3485 return session_inflate_handle_invalid_connection(
3486 session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: depend on itself");
3489 if (session_is_incoming_concurrent_streams_pending_max(session)) {
3490 return session_inflate_handle_invalid_stream(session, frame,
3491 NGHTTP2_ERR_REFUSED_STREAM);
3494 stream = nghttp2_session_open_stream(
3495 session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE,
3496 &frame->headers.pri_spec, NGHTTP2_STREAM_OPENING, NULL);
3498 return NGHTTP2_ERR_NOMEM;
3500 session->last_proc_stream_id = session->last_recv_stream_id;
3501 rv = session_call_on_begin_headers(session, frame);
3508 int nghttp2_session_on_response_headers_received(nghttp2_session *session,
3509 nghttp2_frame *frame,
3510 nghttp2_stream *stream) {
3512 /* This function is only called if stream->state ==
3513 NGHTTP2_STREAM_OPENING and stream_id is local side initiated. */
3514 assert(stream->state == NGHTTP2_STREAM_OPENING &&
3515 nghttp2_session_is_my_stream_id(session, frame->hd.stream_id));
3516 if (frame->hd.stream_id == 0) {
3517 return session_inflate_handle_invalid_connection(
3518 session, frame, NGHTTP2_ERR_PROTO, "response HEADERS: stream_id == 0");
3520 if (stream->shut_flags & NGHTTP2_SHUT_RD) {
3521 /* half closed (remote): from the spec:
3523 If an endpoint receives additional frames for a stream that is
3524 in this state it MUST respond with a stream error (Section
3525 5.4.2) of type STREAM_CLOSED.
3527 return session_inflate_handle_invalid_stream(session, frame,
3528 NGHTTP2_ERR_STREAM_CLOSED);
3530 stream->state = NGHTTP2_STREAM_OPENED;
3531 rv = session_call_on_begin_headers(session, frame);
3538 int nghttp2_session_on_push_response_headers_received(nghttp2_session *session,
3539 nghttp2_frame *frame,
3540 nghttp2_stream *stream) {
3542 assert(stream->state == NGHTTP2_STREAM_RESERVED);
3543 if (frame->hd.stream_id == 0) {
3544 return session_inflate_handle_invalid_connection(
3545 session, frame, NGHTTP2_ERR_PROTO,
3546 "push response HEADERS: stream_id == 0");
3548 if (session->goaway_flags) {
3549 /* We don't accept new stream after GOAWAY is sent or received. */
3550 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3553 if (session_is_incoming_concurrent_streams_max(session)) {
3554 return session_inflate_handle_invalid_connection(
3555 session, frame, NGHTTP2_ERR_PROTO,
3556 "push response HEADERS: max concurrent streams exceeded");
3558 if (session_is_incoming_concurrent_streams_pending_max(session)) {
3559 return session_inflate_handle_invalid_stream(session, frame,
3560 NGHTTP2_ERR_REFUSED_STREAM);
3563 nghttp2_stream_promise_fulfilled(stream);
3564 ++session->num_incoming_streams;
3565 rv = session_call_on_begin_headers(session, frame);
3572 int nghttp2_session_on_headers_received(nghttp2_session *session,
3573 nghttp2_frame *frame,
3574 nghttp2_stream *stream) {
3576 if (frame->hd.stream_id == 0) {
3577 return session_inflate_handle_invalid_connection(
3578 session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream_id == 0");
3580 if (stream->state == NGHTTP2_STREAM_RESERVED) {
3581 /* reserved. The valid push response HEADERS is processed by
3582 nghttp2_session_on_push_response_headers_received(). This
3583 generic HEADERS is called invalid cases for HEADERS against
3585 return session_inflate_handle_invalid_connection(
3586 session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream in reserved");
3588 if ((stream->shut_flags & NGHTTP2_SHUT_RD)) {
3589 /* half closed (remote): from the spec:
3591 If an endpoint receives additional frames for a stream that is
3592 in this state it MUST respond with a stream error (Section
3593 5.4.2) of type STREAM_CLOSED.
3595 return session_inflate_handle_invalid_stream(session, frame,
3596 NGHTTP2_ERR_STREAM_CLOSED);
3598 if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
3599 if (stream->state == NGHTTP2_STREAM_OPENED) {
3600 rv = session_call_on_begin_headers(session, frame);
3605 } else if (stream->state == NGHTTP2_STREAM_CLOSING) {
3606 /* This is race condition. NGHTTP2_STREAM_CLOSING indicates
3607 that we queued RST_STREAM but it has not been sent. It will
3608 eventually sent, so we just ignore this frame. */
3609 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3611 return session_inflate_handle_invalid_stream(session, frame,
3615 /* If this is remote peer initiated stream, it is OK unless it
3616 has sent END_STREAM frame already. But if stream is in
3617 NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race
3619 if (stream->state != NGHTTP2_STREAM_CLOSING) {
3620 rv = session_call_on_begin_headers(session, frame);
3626 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3629 static int session_process_headers_frame(nghttp2_session *session) {
3631 nghttp2_inbound_frame *iframe = &session->iframe;
3632 nghttp2_frame *frame = &iframe->frame;
3633 nghttp2_stream *stream;
3635 rv = nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos,
3636 nghttp2_buf_len(&iframe->sbuf));
3639 return nghttp2_session_terminate_session_with_reason(
3640 session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: could not unpack");
3642 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
3644 frame->headers.cat = NGHTTP2_HCAT_REQUEST;
3645 return nghttp2_session_on_request_headers_received(session, frame);
3648 if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
3649 if (stream->state == NGHTTP2_STREAM_OPENING) {
3650 frame->headers.cat = NGHTTP2_HCAT_RESPONSE;
3651 return nghttp2_session_on_response_headers_received(session, frame,
3654 frame->headers.cat = NGHTTP2_HCAT_HEADERS;
3655 return nghttp2_session_on_headers_received(session, frame, stream);
3657 if (stream->state == NGHTTP2_STREAM_RESERVED) {
3658 frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE;
3659 return nghttp2_session_on_push_response_headers_received(session, frame,
3662 frame->headers.cat = NGHTTP2_HCAT_HEADERS;
3663 return nghttp2_session_on_headers_received(session, frame, stream);
3666 int nghttp2_session_on_priority_received(nghttp2_session *session,
3667 nghttp2_frame *frame) {
3669 nghttp2_stream *stream;
3671 if (frame->hd.stream_id == 0) {
3672 return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
3673 "PRIORITY: stream_id == 0");
3676 if (!session->server) {
3677 /* Re-prioritization works only in server */
3678 return session_call_on_frame_received(session, frame);
3681 stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);
3684 /* PRIORITY against idle stream can create anchor node in
3686 if (!session_detect_idle_stream(session, frame->hd.stream_id)) {
3690 stream = nghttp2_session_open_stream(
3691 session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE,
3692 &frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL);
3694 if (stream == NULL) {
3695 return NGHTTP2_ERR_NOMEM;
3698 rv = nghttp2_session_reprioritize_stream(session, stream,
3699 &frame->priority.pri_spec);
3701 if (nghttp2_is_fatal(rv)) {
3706 return session_call_on_frame_received(session, frame);
3709 static int session_process_priority_frame(nghttp2_session *session) {
3710 nghttp2_inbound_frame *iframe = &session->iframe;
3711 nghttp2_frame *frame = &iframe->frame;
3713 nghttp2_frame_unpack_priority_payload(&frame->priority, iframe->sbuf.pos,
3714 nghttp2_buf_len(&iframe->sbuf));
3716 return nghttp2_session_on_priority_received(session, frame);
3719 int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
3720 nghttp2_frame *frame) {
3722 nghttp2_stream *stream;
3723 if (frame->hd.stream_id == 0) {
3724 return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
3725 "RST_STREAM: stream_id == 0");
3727 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
3729 if (session_detect_idle_stream(session, frame->hd.stream_id)) {
3730 return session_handle_invalid_connection(
3731 session, frame, NGHTTP2_ERR_PROTO, "RST_STREAM: stream in idle");
3735 rv = session_call_on_frame_received(session, frame);
3739 rv = nghttp2_session_close_stream(session, frame->hd.stream_id,
3740 frame->rst_stream.error_code);
3741 if (nghttp2_is_fatal(rv)) {
3747 static int session_process_rst_stream_frame(nghttp2_session *session) {
3748 nghttp2_inbound_frame *iframe = &session->iframe;
3749 nghttp2_frame *frame = &iframe->frame;
3751 nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, iframe->sbuf.pos,
3752 nghttp2_buf_len(&iframe->sbuf));
3754 return nghttp2_session_on_rst_stream_received(session, frame);
3757 static int update_remote_initial_window_size_func(nghttp2_map_entry *entry,
3760 nghttp2_update_window_size_arg *arg;
3761 nghttp2_stream *stream;
3763 arg = (nghttp2_update_window_size_arg *)ptr;
3764 stream = (nghttp2_stream *)entry;
3766 rv = nghttp2_stream_update_remote_initial_window_size(
3767 stream, arg->new_window_size, arg->old_window_size);
3769 return nghttp2_session_terminate_session(arg->session,
3770 NGHTTP2_FLOW_CONTROL_ERROR);
3773 /* If window size gets positive, push deferred DATA frame to
3775 if (stream->remote_window_size > 0 &&
3776 nghttp2_stream_check_deferred_by_flow_control(stream)) {
3778 rv = nghttp2_stream_resume_deferred_item(
3779 stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, arg->session);
3781 if (nghttp2_is_fatal(rv)) {
3789 * Updates the remote initial window size of all active streams. If
3790 * error occurs, all streams may not be updated.
3792 * This function returns 0 if it succeeds, or one of the following
3793 * negative error codes:
3799 session_update_remote_initial_window_size(nghttp2_session *session,
3800 int32_t new_initial_window_size) {
3801 nghttp2_update_window_size_arg arg;
3803 arg.session = session;
3804 arg.new_window_size = new_initial_window_size;
3805 arg.old_window_size = session->remote_settings.initial_window_size;
3807 return nghttp2_map_each(&session->streams,
3808 update_remote_initial_window_size_func, &arg);
3811 static int update_local_initial_window_size_func(nghttp2_map_entry *entry,
3814 nghttp2_update_window_size_arg *arg;
3815 nghttp2_stream *stream;
3816 arg = (nghttp2_update_window_size_arg *)ptr;
3817 stream = (nghttp2_stream *)entry;
3818 rv = nghttp2_stream_update_local_initial_window_size(
3819 stream, arg->new_window_size, arg->old_window_size);
3821 return nghttp2_session_terminate_session(arg->session,
3822 NGHTTP2_FLOW_CONTROL_ERROR);
3824 if (!(arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
3826 if (nghttp2_should_send_window_update(stream->local_window_size,
3827 stream->recv_window_size)) {
3829 rv = nghttp2_session_add_window_update(arg->session, NGHTTP2_FLAG_NONE,
3831 stream->recv_window_size);
3835 stream->recv_window_size = 0;
3842 * Updates the local initial window size of all active streams. If
3843 * error occurs, all streams may not be updated.
3845 * This function returns 0 if it succeeds, or one of the following
3846 * negative error codes:
3852 session_update_local_initial_window_size(nghttp2_session *session,
3853 int32_t new_initial_window_size,
3854 int32_t old_initial_window_size) {
3855 nghttp2_update_window_size_arg arg;
3856 arg.session = session;
3857 arg.new_window_size = new_initial_window_size;
3858 arg.old_window_size = old_initial_window_size;
3859 return nghttp2_map_each(&session->streams,
3860 update_local_initial_window_size_func, &arg);
3864 * Apply SETTINGS values |iv| having |niv| elements to the local
3865 * settings. We assumes that all values in |iv| is correct, since we
3866 * validated them in nghttp2_session_add_settings() already.
3868 * This function returns 0 if it succeeds, or one of the following
3869 * negative error codes:
3871 * NGHTTP2_ERR_HEADER_COMP
3872 * The header table size is out of range
3876 int nghttp2_session_update_local_settings(nghttp2_session *session,
3877 nghttp2_settings_entry *iv,
3881 int32_t new_initial_window_size = -1;
3882 int32_t header_table_size = -1;
3883 uint8_t header_table_size_seen = 0;
3884 /* Use the value last seen. */
3885 for (i = 0; i < niv; ++i) {
3886 switch (iv[i].settings_id) {
3887 case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
3888 header_table_size_seen = 1;
3889 header_table_size = iv[i].value;
3891 case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
3892 new_initial_window_size = iv[i].value;
3896 if (header_table_size_seen) {
3897 rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater,
3903 if (new_initial_window_size != -1) {
3904 rv = session_update_local_initial_window_size(
3905 session, new_initial_window_size,
3906 session->local_settings.initial_window_size);
3912 for (i = 0; i < niv; ++i) {
3913 switch (iv[i].settings_id) {
3914 case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
3915 session->local_settings.header_table_size = iv[i].value;
3917 case NGHTTP2_SETTINGS_ENABLE_PUSH:
3918 session->local_settings.enable_push = iv[i].value;
3920 case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
3921 session->local_settings.max_concurrent_streams = iv[i].value;
3923 case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
3924 session->local_settings.initial_window_size = iv[i].value;
3926 case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
3927 session->local_settings.max_frame_size = iv[i].value;
3929 case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
3930 session->local_settings.max_header_list_size = iv[i].value;
3935 session->pending_local_max_concurrent_stream =
3936 NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS;
3937 session->pending_enable_push = 1;
3942 int nghttp2_session_on_settings_received(nghttp2_session *session,
3943 nghttp2_frame *frame, int noack) {
3948 mem = &session->mem;
3950 if (frame->hd.stream_id != 0) {
3951 return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
3952 "SETTINGS: stream_id != 0");
3954 if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
3955 if (frame->settings.niv != 0) {
3956 return session_handle_invalid_connection(
3957 session, frame, NGHTTP2_ERR_FRAME_SIZE_ERROR,
3958 "SETTINGS: ACK and payload != 0");
3960 if (session->inflight_niv == -1) {
3961 return session_handle_invalid_connection(
3962 session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: unexpected ACK");
3964 rv = nghttp2_session_update_local_settings(session, session->inflight_iv,
3965 session->inflight_niv);
3966 nghttp2_mem_free(mem, session->inflight_iv);
3967 session->inflight_iv = NULL;
3968 session->inflight_niv = -1;
3970 if (nghttp2_is_fatal(rv)) {
3973 return session_handle_invalid_connection(session, frame, rv, NULL);
3975 return session_call_on_frame_received(session, frame);
3978 for (i = 0; i < frame->settings.niv; ++i) {
3979 nghttp2_settings_entry *entry = &frame->settings.iv[i];
3981 switch (entry->settings_id) {
3982 case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
3984 if (entry->value > NGHTTP2_MAX_HEADER_TABLE_SIZE) {
3985 return session_handle_invalid_connection(
3986 session, frame, NGHTTP2_ERR_HEADER_COMP,
3987 "SETTINGS: too large SETTINGS_HEADER_TABLE_SIZE");
3990 rv = nghttp2_hd_deflate_change_table_size(&session->hd_deflater,
3993 if (nghttp2_is_fatal(rv)) {
3996 return session_handle_invalid_connection(
3997 session, frame, NGHTTP2_ERR_HEADER_COMP, NULL);
4001 session->remote_settings.header_table_size = entry->value;
4004 case NGHTTP2_SETTINGS_ENABLE_PUSH:
4006 if (entry->value != 0 && entry->value != 1) {
4007 return session_handle_invalid_connection(
4008 session, frame, NGHTTP2_ERR_PROTO,
4009 "SETTINGS: invalid SETTINGS_ENBLE_PUSH");
4012 if (!session->server && entry->value != 0) {
4013 return session_handle_invalid_connection(
4014 session, frame, NGHTTP2_ERR_PROTO,
4015 "SETTINGS: server attempted to enable push");
4018 session->remote_settings.enable_push = entry->value;
4021 case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
4023 session->remote_settings.max_concurrent_streams = entry->value;
4026 case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
4028 /* Update the initial window size of the all active streams */
4029 /* Check that initial_window_size < (1u << 31) */
4030 if (entry->value > NGHTTP2_MAX_WINDOW_SIZE) {
4031 return session_handle_invalid_connection(
4032 session, frame, NGHTTP2_ERR_FLOW_CONTROL,
4033 "SETTINGS: too large SETTINGS_INITIAL_WINDOW_SIZE");
4036 rv = session_update_remote_initial_window_size(session, entry->value);
4038 if (nghttp2_is_fatal(rv)) {
4043 return session_handle_invalid_connection(
4044 session, frame, NGHTTP2_ERR_FLOW_CONTROL, NULL);
4047 session->remote_settings.initial_window_size = entry->value;
4050 case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
4052 if (entry->value < NGHTTP2_MAX_FRAME_SIZE_MIN ||
4053 entry->value > NGHTTP2_MAX_FRAME_SIZE_MAX) {
4054 return session_handle_invalid_connection(
4055 session, frame, NGHTTP2_ERR_PROTO,
4056 "SETTINGS: invalid SETTINGS_MAX_FRAME_SIZE");
4059 session->remote_settings.max_frame_size = entry->value;
4062 case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
4064 session->remote_settings.max_header_list_size = entry->value;
4070 if (!noack && !session_is_closing(session)) {
4071 rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0);
4074 if (nghttp2_is_fatal(rv)) {
4078 return session_handle_invalid_connection(session, frame,
4079 NGHTTP2_ERR_INTERNAL, NULL);
4083 return session_call_on_frame_received(session, frame);
4086 static int session_process_settings_frame(nghttp2_session *session) {
4088 nghttp2_inbound_frame *iframe = &session->iframe;
4089 nghttp2_frame *frame = &iframe->frame;
4091 nghttp2_settings_entry min_header_size_entry;
4094 mem = &session->mem;
4095 min_header_size_entry = iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1];
4097 if (min_header_size_entry.value < UINT32_MAX) {
4098 /* If we have less value, then we must have
4099 SETTINGS_HEADER_TABLE_SIZE in i < iframe->niv */
4100 for (i = 0; i < iframe->niv; ++i) {
4101 if (iframe->iv[i].settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) {
4106 assert(i < iframe->niv);
4108 if (min_header_size_entry.value != iframe->iv[i].value) {
4109 iframe->iv[iframe->niv++] = iframe->iv[i];
4110 iframe->iv[i] = min_header_size_entry;
4114 rv = nghttp2_frame_unpack_settings_payload(&frame->settings, iframe->iv,
4117 assert(nghttp2_is_fatal(rv));
4120 return nghttp2_session_on_settings_received(session, frame, 0 /* ACK */);
4123 int nghttp2_session_on_push_promise_received(nghttp2_session *session,
4124 nghttp2_frame *frame) {
4126 nghttp2_stream *stream;
4127 nghttp2_stream *promised_stream;
4128 nghttp2_priority_spec pri_spec;
4130 if (frame->hd.stream_id == 0) {
4131 return session_inflate_handle_invalid_connection(
4132 session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream_id == 0");
4134 if (session->server || session->local_settings.enable_push == 0) {
4135 return session_inflate_handle_invalid_connection(
4136 session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: push disabled");
4138 if (session->goaway_flags) {
4139 /* We just dicard PUSH_PROMISE after GOAWAY is sent or
4141 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
4144 if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
4145 return session_inflate_handle_invalid_connection(
4146 session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: invalid stream_id");
4149 if (!session_is_new_peer_stream_id(session,
4150 frame->push_promise.promised_stream_id)) {
4151 /* The spec says if an endpoint receives a PUSH_PROMISE with
4152 illegal stream ID is subject to a connection error of type
4154 return session_inflate_handle_invalid_connection(
4155 session, frame, NGHTTP2_ERR_PROTO,
4156 "PUSH_PROMISE: invalid promised_stream_id");
4158 session->last_recv_stream_id = frame->push_promise.promised_stream_id;
4159 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
4160 if (!stream || stream->state == NGHTTP2_STREAM_CLOSING ||
4161 !session->pending_enable_push) {
4163 if (session_detect_idle_stream(session, frame->hd.stream_id)) {
4164 return session_inflate_handle_invalid_connection(
4165 session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream in idle");
4168 rv = nghttp2_session_add_rst_stream(session,
4169 frame->push_promise.promised_stream_id,
4170 NGHTTP2_REFUSED_STREAM);
4174 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
4176 if (stream->shut_flags & NGHTTP2_SHUT_RD) {
4177 if (session->callbacks.on_invalid_frame_recv_callback) {
4178 if (session->callbacks.on_invalid_frame_recv_callback(
4179 session, frame, NGHTTP2_PROTOCOL_ERROR, session->user_data) !=
4181 return NGHTTP2_ERR_CALLBACK_FAILURE;
4184 rv = nghttp2_session_add_rst_stream(session,
4185 frame->push_promise.promised_stream_id,
4186 NGHTTP2_PROTOCOL_ERROR);
4190 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
4193 /* TODO It is unclear reserved stream dpeneds on associated
4194 stream with or without exclusive flag set */
4195 nghttp2_priority_spec_init(&pri_spec, stream->stream_id,
4196 NGHTTP2_DEFAULT_WEIGHT, 0);
4198 promised_stream = nghttp2_session_open_stream(
4199 session, frame->push_promise.promised_stream_id, NGHTTP2_STREAM_FLAG_NONE,
4200 &pri_spec, NGHTTP2_STREAM_RESERVED, NULL);
4202 if (!promised_stream) {
4203 return NGHTTP2_ERR_NOMEM;
4206 session->last_proc_stream_id = session->last_recv_stream_id;
4207 rv = session_call_on_begin_headers(session, frame);
4214 static int session_process_push_promise_frame(nghttp2_session *session) {
4216 nghttp2_inbound_frame *iframe = &session->iframe;
4217 nghttp2_frame *frame = &iframe->frame;
4219 rv = nghttp2_frame_unpack_push_promise_payload(
4220 &frame->push_promise, iframe->sbuf.pos, nghttp2_buf_len(&iframe->sbuf));
4223 return nghttp2_session_terminate_session_with_reason(
4224 session, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: could not unpack");
4227 return nghttp2_session_on_push_promise_received(session, frame);
4230 int nghttp2_session_on_ping_received(nghttp2_session *session,
4231 nghttp2_frame *frame) {
4233 if (frame->hd.stream_id != 0) {
4234 return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4235 "PING: stream_id != 0");
4237 if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 &&
4238 !session_is_closing(session)) {
4239 /* Peer sent ping, so ping it back */
4240 rv = nghttp2_session_add_ping(session, NGHTTP2_FLAG_ACK,
4241 frame->ping.opaque_data);
4246 return session_call_on_frame_received(session, frame);
4249 static int session_process_ping_frame(nghttp2_session *session) {
4250 nghttp2_inbound_frame *iframe = &session->iframe;
4251 nghttp2_frame *frame = &iframe->frame;
4253 nghttp2_frame_unpack_ping_payload(&frame->ping, iframe->sbuf.pos,
4254 nghttp2_buf_len(&iframe->sbuf));
4256 return nghttp2_session_on_ping_received(session, frame);
4259 int nghttp2_session_on_goaway_received(nghttp2_session *session,
4260 nghttp2_frame *frame) {
4263 if (frame->hd.stream_id != 0) {
4264 return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4265 "GOAWAY: stream_id != 0");
4267 /* Spec says Endpoints MUST NOT increase the value they send in the
4268 last stream identifier. */
4269 if ((frame->goaway.last_stream_id > 0 &&
4270 !nghttp2_session_is_my_stream_id(session,
4271 frame->goaway.last_stream_id)) ||
4272 session->remote_last_stream_id < frame->goaway.last_stream_id) {
4273 return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4274 "GOAWAY: invalid last_stream_id");
4277 session->goaway_flags |= NGHTTP2_GOAWAY_RECV;
4279 session->remote_last_stream_id = frame->goaway.last_stream_id;
4281 rv = session_call_on_frame_received(session, frame);
4283 if (nghttp2_is_fatal(rv)) {
4287 return session_close_stream_on_goaway(session, frame->goaway.last_stream_id,
4291 static int session_process_goaway_frame(nghttp2_session *session) {
4292 nghttp2_inbound_frame *iframe = &session->iframe;
4293 nghttp2_frame *frame = &iframe->frame;
4295 nghttp2_frame_unpack_goaway_payload(
4296 &frame->goaway, iframe->sbuf.pos, nghttp2_buf_len(&iframe->sbuf),
4297 iframe->lbuf.pos, nghttp2_buf_len(&iframe->lbuf));
4299 nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
4301 return nghttp2_session_on_goaway_received(session, frame);
4305 session_on_connection_window_update_received(nghttp2_session *session,
4306 nghttp2_frame *frame) {
4307 /* Handle connection-level flow control */
4308 if (frame->window_update.window_size_increment == 0) {
4309 return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4313 if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
4314 session->remote_window_size) {
4315 return session_handle_invalid_connection(session, frame,
4316 NGHTTP2_ERR_FLOW_CONTROL, NULL);
4318 session->remote_window_size += frame->window_update.window_size_increment;
4320 return session_call_on_frame_received(session, frame);
4323 static int session_on_stream_window_update_received(nghttp2_session *session,
4324 nghttp2_frame *frame) {
4326 nghttp2_stream *stream;
4327 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
4329 if (session_detect_idle_stream(session, frame->hd.stream_id)) {
4330 return session_handle_invalid_connection(
4331 session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPDATE to idle stream");
4335 if (state_reserved_remote(session, stream)) {
4336 return session_handle_invalid_connection(
4337 session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPADATE to reserved stream");
4339 if (frame->window_update.window_size_increment == 0) {
4340 return session_handle_invalid_stream(session, frame, NGHTTP2_ERR_PROTO);
4342 if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
4343 stream->remote_window_size) {
4344 return session_handle_invalid_stream(session, frame,
4345 NGHTTP2_ERR_FLOW_CONTROL);
4347 stream->remote_window_size += frame->window_update.window_size_increment;
4349 if (stream->remote_window_size > 0 &&
4350 nghttp2_stream_check_deferred_by_flow_control(stream)) {
4352 rv = nghttp2_stream_resume_deferred_item(
4353 stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session);
4355 if (nghttp2_is_fatal(rv)) {
4359 return session_call_on_frame_received(session, frame);
4362 int nghttp2_session_on_window_update_received(nghttp2_session *session,
4363 nghttp2_frame *frame) {
4364 if (frame->hd.stream_id == 0) {
4365 return session_on_connection_window_update_received(session, frame);
4367 return session_on_stream_window_update_received(session, frame);
4371 static int session_process_window_update_frame(nghttp2_session *session) {
4372 nghttp2_inbound_frame *iframe = &session->iframe;
4373 nghttp2_frame *frame = &iframe->frame;
4375 nghttp2_frame_unpack_window_update_payload(
4376 &frame->window_update, iframe->sbuf.pos, nghttp2_buf_len(&iframe->sbuf));
4378 return nghttp2_session_on_window_update_received(session, frame);
4381 int nghttp2_session_on_data_received(nghttp2_session *session,
4382 nghttp2_frame *frame) {
4385 nghttp2_stream *stream;
4387 /* We don't call on_frame_recv_callback if stream has been closed
4388 already or being closed. */
4389 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
4390 if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) {
4391 /* This should be treated as stream error, but it results in lots
4392 of RST_STREAM. So just ignore frame against nonexistent stream
4397 if (session_enforce_http_messaging(session) &&
4398 (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
4399 if (nghttp2_http_on_remote_end_stream(stream) != 0) {
4401 rv = nghttp2_session_add_rst_stream(session, stream->stream_id,
4402 NGHTTP2_PROTOCOL_ERROR);
4403 if (nghttp2_is_fatal(rv)) {
4410 rv = session_call_on_frame_received(session, frame);
4411 if (nghttp2_is_fatal(rv)) {
4416 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
4417 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
4418 rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
4419 if (nghttp2_is_fatal(rv)) {
4426 /* For errors, this function only returns FATAL error. */
4427 static int session_process_data_frame(nghttp2_session *session) {
4429 nghttp2_frame *public_data_frame = &session->iframe.frame;
4430 rv = nghttp2_session_on_data_received(session, public_data_frame);
4431 if (nghttp2_is_fatal(rv)) {
4438 * Now we have SETTINGS synchronization, flow control error can be
4439 * detected strictly. If DATA frame is received with length > 0 and
4440 * current received window size + delta length is strictly larger than
4441 * local window size, it is subject to FLOW_CONTROL_ERROR, so return
4442 * -1. Note that local_window_size is calculated after SETTINGS ACK is
4443 * received from peer, so peer must honor this limit. If the resulting
4444 * recv_window_size is strictly larger than NGHTTP2_MAX_WINDOW_SIZE,
4447 static int adjust_recv_window_size(int32_t *recv_window_size_ptr, size_t delta,
4448 int32_t local_window_size) {
4449 if (*recv_window_size_ptr > local_window_size - (int32_t)delta ||
4450 *recv_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - (int32_t)delta) {
4453 *recv_window_size_ptr += delta;
4458 * Accumulates received bytes |delta_size| for stream-level flow
4459 * control and decides whether to send WINDOW_UPDATE to that stream.
4460 * If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set, WINDOW_UPDATE will not
4463 * This function returns 0 if it succeeds, or one of the following
4464 * negative error codes:
4469 static int session_update_recv_stream_window_size(nghttp2_session *session,
4470 nghttp2_stream *stream,
4472 int send_window_update) {
4474 rv = adjust_recv_window_size(&stream->recv_window_size, delta_size,
4475 stream->local_window_size);
4477 return nghttp2_session_add_rst_stream(session, stream->stream_id,
4478 NGHTTP2_FLOW_CONTROL_ERROR);
4480 /* We don't have to send WINDOW_UPDATE if the data received is the
4481 last chunk in the incoming stream. */
4482 if (send_window_update &&
4483 !(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
4484 /* We have to use local_settings here because it is the constraint
4485 the remote endpoint should honor. */
4486 if (nghttp2_should_send_window_update(stream->local_window_size,
4487 stream->recv_window_size)) {
4488 rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE,
4490 stream->recv_window_size);
4492 stream->recv_window_size = 0;
4502 * Accumulates received bytes |delta_size| for connection-level flow
4503 * control and decides whether to send WINDOW_UPDATE to the
4504 * connection. If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set,
4505 * WINDOW_UPDATE will not be sent.
4507 * This function returns 0 if it succeeds, or one of the following
4508 * negative error codes:
4513 static int session_update_recv_connection_window_size(nghttp2_session *session,
4514 size_t delta_size) {
4516 rv = adjust_recv_window_size(&session->recv_window_size, delta_size,
4517 session->local_window_size);
4519 return nghttp2_session_terminate_session(session,
4520 NGHTTP2_FLOW_CONTROL_ERROR);
4522 if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
4524 if (nghttp2_should_send_window_update(session->local_window_size,
4525 session->recv_window_size)) {
4526 /* Use stream ID 0 to update connection-level flow control
4528 rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, 0,
4529 session->recv_window_size);
4534 session->recv_window_size = 0;
4540 static int session_update_consumed_size(nghttp2_session *session,
4541 int32_t *consumed_size_ptr,
4542 int32_t *recv_window_size_ptr,
4543 int32_t stream_id, size_t delta_size,
4544 int32_t local_window_size) {
4548 if ((size_t)*consumed_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta_size) {
4549 return nghttp2_session_terminate_session(session,
4550 NGHTTP2_FLOW_CONTROL_ERROR);
4553 *consumed_size_ptr += delta_size;
4555 /* recv_window_size may be smaller than consumed_size, because it
4556 may be decreased by negative value with
4557 nghttp2_submit_window_update(). */
4558 recv_size = nghttp2_min(*consumed_size_ptr, *recv_window_size_ptr);
4560 if (nghttp2_should_send_window_update(local_window_size, recv_size)) {
4561 rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE,
4562 stream_id, recv_size);
4568 *recv_window_size_ptr -= recv_size;
4569 *consumed_size_ptr -= recv_size;
4575 static int session_update_stream_consumed_size(nghttp2_session *session,
4576 nghttp2_stream *stream,
4577 size_t delta_size) {
4578 return session_update_consumed_size(
4579 session, &stream->consumed_size, &stream->recv_window_size,
4580 stream->stream_id, delta_size, stream->local_window_size);
4583 static int session_update_connection_consumed_size(nghttp2_session *session,
4584 size_t delta_size) {
4585 return session_update_consumed_size(session, &session->consumed_size,
4586 &session->recv_window_size, 0, delta_size,
4587 session->local_window_size);
4591 * Checks that we can receive the DATA frame for stream, which is
4592 * indicated by |session->iframe.frame.hd.stream_id|. If it is a
4593 * connection error situation, GOAWAY frame will be issued by this
4596 * If the DATA frame is allowed, returns 0.
4598 * This function returns 0 if it succeeds, or one of the following
4599 * negative error codes:
4601 * NGHTTP2_ERR_IGN_PAYLOAD
4602 * The reception of DATA frame is connection error; or should be
4607 static int session_on_data_received_fail_fast(nghttp2_session *session) {
4609 nghttp2_stream *stream;
4610 nghttp2_inbound_frame *iframe;
4612 const char *failure_reason;
4613 uint32_t error_code = NGHTTP2_PROTOCOL_ERROR;
4615 iframe = &session->iframe;
4616 stream_id = iframe->frame.hd.stream_id;
4618 if (stream_id == 0) {
4619 /* The spec says that if a DATA frame is received whose stream ID
4620 is 0, the recipient MUST respond with a connection error of
4621 type PROTOCOL_ERROR. */
4622 failure_reason = "DATA: stream_id == 0";
4625 stream = nghttp2_session_get_stream(session, stream_id);
4627 if (session_detect_idle_stream(session, stream_id)) {
4628 failure_reason = "DATA: stream in idle";
4629 error_code = NGHTTP2_STREAM_CLOSED;
4632 return NGHTTP2_ERR_IGN_PAYLOAD;
4634 if (stream->shut_flags & NGHTTP2_SHUT_RD) {
4635 failure_reason = "DATA: stream in half-closed(remote)";
4636 error_code = NGHTTP2_STREAM_CLOSED;
4640 if (nghttp2_session_is_my_stream_id(session, stream_id)) {
4641 if (stream->state == NGHTTP2_STREAM_CLOSING) {
4642 return NGHTTP2_ERR_IGN_PAYLOAD;
4644 if (stream->state != NGHTTP2_STREAM_OPENED) {
4645 failure_reason = "DATA: stream not opened";
4650 if (stream->state == NGHTTP2_STREAM_RESERVED) {
4651 failure_reason = "DATA: stream in reserved";
4654 if (stream->state == NGHTTP2_STREAM_CLOSING) {
4655 return NGHTTP2_ERR_IGN_PAYLOAD;
4659 rv = nghttp2_session_terminate_session_with_reason(session, error_code,
4661 if (nghttp2_is_fatal(rv)) {
4664 return NGHTTP2_ERR_IGN_PAYLOAD;
4667 static size_t inbound_frame_payload_readlen(nghttp2_inbound_frame *iframe,
4669 const uint8_t *last) {
4670 return nghttp2_min((size_t)(last - in), iframe->payloadleft);
4674 * Resets iframe->sbuf and advance its mark pointer by |left| bytes.
4676 static void inbound_frame_set_mark(nghttp2_inbound_frame *iframe, size_t left) {
4677 nghttp2_buf_reset(&iframe->sbuf);
4678 iframe->sbuf.mark += left;
4681 static size_t inbound_frame_buf_read(nghttp2_inbound_frame *iframe,
4682 const uint8_t *in, const uint8_t *last) {
4685 readlen = nghttp2_min(last - in, nghttp2_buf_mark_avail(&iframe->sbuf));
4687 iframe->sbuf.last = nghttp2_cpymem(iframe->sbuf.last, in, readlen);
4693 * Unpacks SETTINGS entry in iframe->sbuf.
4695 static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) {
4696 nghttp2_settings_entry iv;
4699 nghttp2_frame_unpack_settings_entry(&iv, iframe->sbuf.pos);
4701 switch (iv.settings_id) {
4702 case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
4703 case NGHTTP2_SETTINGS_ENABLE_PUSH:
4704 case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
4705 case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
4706 case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
4707 case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
4710 DEBUGF(fprintf(stderr, "recv: ignore unknown settings id=0x%02x\n",
4715 for (i = 0; i < iframe->niv; ++i) {
4716 if (iframe->iv[i].settings_id == iv.settings_id) {
4722 if (i == iframe->niv) {
4723 iframe->iv[iframe->niv++] = iv;
4726 if (iv.settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE &&
4727 iv.value < iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1].value) {
4729 iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1] = iv;
4734 * Checks PADDED flags and set iframe->sbuf to read them accordingly.
4735 * If padding is set, this function returns 1. If no padding is set,
4736 * this function returns 0. On error, returns -1.
4738 static int inbound_frame_handle_pad(nghttp2_inbound_frame *iframe,
4739 nghttp2_frame_hd *hd) {
4740 if (hd->flags & NGHTTP2_FLAG_PADDED) {
4741 if (hd->length < 1) {
4744 inbound_frame_set_mark(iframe, 1);
4747 DEBUGF(fprintf(stderr, "recv: no padding in payload\n"));
4752 * Computes number of padding based on flags. This function returns
4753 * the calculated length if it succeeds, or -1.
4755 static ssize_t inbound_frame_compute_pad(nghttp2_inbound_frame *iframe) {
4758 /* 1 for Pad Length field */
4759 padlen = iframe->sbuf.pos[0] + 1;
4761 DEBUGF(fprintf(stderr, "recv: padlen=%zu\n", padlen));
4763 /* We cannot use iframe->frame.hd.length because of CONTINUATION */
4764 if (padlen - 1 > iframe->payloadleft) {
4768 iframe->padlen = padlen;
4774 * This function returns the effective payload length in the data of
4775 * length |readlen| when the remaning payload is |payloadleft|. The
4776 * |payloadleft| does not include |readlen|. If padding was started
4777 * strictly before this data chunk, this function returns -1.
4779 static ssize_t inbound_frame_effective_readlen(nghttp2_inbound_frame *iframe,
4782 size_t trail_padlen =
4783 nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen);
4785 if (trail_padlen > payloadleft) {
4787 padlen = trail_padlen - payloadleft;
4788 if (readlen < padlen) {
4791 return readlen - padlen;
4797 ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
4799 const uint8_t *first = in, *last = in + inlen;
4800 nghttp2_inbound_frame *iframe = &session->iframe;
4805 nghttp2_frame_hd cont_hd;
4806 nghttp2_stream *stream;
4807 size_t pri_fieldlen;
4810 DEBUGF(fprintf(stderr,
4811 "recv: connection recv_window_size=%d, local_window=%d\n",
4812 session->recv_window_size, session->local_window_size));
4814 mem = &session->mem;
4817 switch (iframe->state) {
4818 case NGHTTP2_IB_READ_CLIENT_MAGIC:
4819 readlen = nghttp2_min(inlen, iframe->payloadleft);
4821 if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN -
4822 iframe->payloadleft,
4823 in, readlen) != 0) {
4824 return NGHTTP2_ERR_BAD_CLIENT_MAGIC;
4827 iframe->payloadleft -= readlen;
4830 if (iframe->payloadleft == 0) {
4831 session_inbound_frame_reset(session);
4832 iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS;
4836 case NGHTTP2_IB_READ_FIRST_SETTINGS:
4837 DEBUGF(fprintf(stderr, "recv: [IB_READ_FIRST_SETTINGS]\n"));
4839 readlen = inbound_frame_buf_read(iframe, in, last);
4842 if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
4846 if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS ||
4847 (iframe->sbuf.pos[4] & NGHTTP2_FLAG_ACK)) {
4849 iframe->state = NGHTTP2_IB_IGN_ALL;
4851 rv = nghttp2_session_terminate_session_with_reason(
4852 session, NGHTTP2_PROTOCOL_ERROR, "SETTINGS expected");
4854 if (nghttp2_is_fatal(rv)) {
4861 iframe->state = NGHTTP2_IB_READ_HEAD;
4864 case NGHTTP2_IB_READ_HEAD: {
4865 int on_begin_frame_called = 0;
4867 DEBUGF(fprintf(stderr, "recv: [IB_READ_HEAD]\n"));
4869 readlen = inbound_frame_buf_read(iframe, in, last);
4872 if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
4876 nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->sbuf.pos);
4877 iframe->payloadleft = iframe->frame.hd.length;
4879 DEBUGF(fprintf(stderr, "recv: payloadlen=%zu, type=%u, flags=0x%02x, "
4881 iframe->frame.hd.length, iframe->frame.hd.type,
4882 iframe->frame.hd.flags, iframe->frame.hd.stream_id));
4884 if (iframe->frame.hd.length > session->local_settings.max_frame_size) {
4885 DEBUGF(fprintf(stderr, "recv: length is too large %zu > %u\n",
4886 iframe->frame.hd.length,
4887 session->local_settings.max_frame_size));
4891 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
4893 rv = nghttp2_session_terminate_session_with_reason(
4894 session, NGHTTP2_FRAME_SIZE_ERROR, "too large frame size");
4896 if (nghttp2_is_fatal(rv)) {
4903 switch (iframe->frame.hd.type) {
4904 case NGHTTP2_DATA: {
4905 DEBUGF(fprintf(stderr, "recv: DATA\n"));
4907 iframe->frame.hd.flags &=
4908 (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PADDED);
4909 /* Check stream is open. If it is not open or closing,
4913 rv = session_on_data_received_fail_fast(session);
4914 if (rv == NGHTTP2_ERR_IGN_PAYLOAD) {
4915 DEBUGF(fprintf(stderr, "recv: DATA not allowed stream_id=%d\n",
4916 iframe->frame.hd.stream_id));
4917 iframe->state = NGHTTP2_IB_IGN_DATA;
4921 if (nghttp2_is_fatal(rv)) {
4925 rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
4927 iframe->state = NGHTTP2_IB_IGN_DATA;
4928 rv = nghttp2_session_terminate_session_with_reason(
4929 session, NGHTTP2_PROTOCOL_ERROR,
4930 "DATA: insufficient padding space");
4932 if (nghttp2_is_fatal(rv)) {
4939 iframe->state = NGHTTP2_IB_READ_PAD_DATA;
4943 iframe->state = NGHTTP2_IB_READ_DATA;
4946 case NGHTTP2_HEADERS:
4948 DEBUGF(fprintf(stderr, "recv: HEADERS\n"));
4950 iframe->frame.hd.flags &=
4951 (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS |
4952 NGHTTP2_FLAG_PADDED | NGHTTP2_FLAG_PRIORITY);
4954 rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
4958 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
4960 rv = nghttp2_session_terminate_session_with_reason(
4961 session, NGHTTP2_PROTOCOL_ERROR,
4962 "HEADERS: insufficient padding space");
4963 if (nghttp2_is_fatal(rv)) {
4970 iframe->state = NGHTTP2_IB_READ_NBYTE;
4974 pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags);
4976 if (pri_fieldlen > 0) {
4977 if (iframe->payloadleft < pri_fieldlen) {
4979 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
4983 iframe->state = NGHTTP2_IB_READ_NBYTE;
4985 inbound_frame_set_mark(iframe, pri_fieldlen);
4990 /* Call on_begin_frame_callback here because
4991 session_process_headers_frame() may call
4992 on_begin_headers_callback */
4993 rv = session_call_on_begin_frame(session, &iframe->frame.hd);
4995 if (nghttp2_is_fatal(rv)) {
4999 on_begin_frame_called = 1;
5001 rv = session_process_headers_frame(session);
5002 if (nghttp2_is_fatal(rv)) {
5008 if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
5009 rv = nghttp2_session_add_rst_stream(
5010 session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR);
5011 if (nghttp2_is_fatal(rv)) {
5014 iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
5018 if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
5019 iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
5023 iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
5026 case NGHTTP2_PRIORITY:
5027 DEBUGF(fprintf(stderr, "recv: PRIORITY\n"));
5029 iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
5031 if (iframe->payloadleft != NGHTTP2_PRIORITY_SPECLEN) {
5034 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5039 iframe->state = NGHTTP2_IB_READ_NBYTE;
5041 inbound_frame_set_mark(iframe, NGHTTP2_PRIORITY_SPECLEN);
5044 case NGHTTP2_RST_STREAM:
5045 case NGHTTP2_WINDOW_UPDATE:
5047 switch (iframe->frame.hd.type) {
5048 case NGHTTP2_RST_STREAM:
5049 DEBUGF(fprintf(stderr, "recv: RST_STREAM\n"));
5051 case NGHTTP2_WINDOW_UPDATE:
5052 DEBUGF(fprintf(stderr, "recv: WINDOW_UPDATE\n"));
5055 #endif /* DEBUGBUILD */
5057 iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
5059 if (iframe->payloadleft != 4) {
5061 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5065 iframe->state = NGHTTP2_IB_READ_NBYTE;
5067 inbound_frame_set_mark(iframe, 4);
5070 case NGHTTP2_SETTINGS:
5071 DEBUGF(fprintf(stderr, "recv: SETTINGS\n"));
5073 iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK;
5075 if ((iframe->frame.hd.length % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) ||
5076 ((iframe->frame.hd.flags & NGHTTP2_FLAG_ACK) &&
5077 iframe->payloadleft > 0)) {
5079 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5083 iframe->state = NGHTTP2_IB_READ_SETTINGS;
5085 if (iframe->payloadleft) {
5086 inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH);
5092 inbound_frame_set_mark(iframe, 0);
5095 case NGHTTP2_PUSH_PROMISE:
5096 DEBUGF(fprintf(stderr, "recv: PUSH_PROMISE\n"));
5098 iframe->frame.hd.flags &=
5099 (NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PADDED);
5101 rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
5104 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5105 rv = nghttp2_session_terminate_session_with_reason(
5106 session, NGHTTP2_PROTOCOL_ERROR,
5107 "PUSH_PROMISE: insufficient padding space");
5108 if (nghttp2_is_fatal(rv)) {
5115 iframe->state = NGHTTP2_IB_READ_NBYTE;
5119 if (iframe->payloadleft < 4) {
5121 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5125 iframe->state = NGHTTP2_IB_READ_NBYTE;
5127 inbound_frame_set_mark(iframe, 4);
5131 DEBUGF(fprintf(stderr, "recv: PING\n"));
5133 iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK;
5135 if (iframe->payloadleft != 8) {
5137 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5141 iframe->state = NGHTTP2_IB_READ_NBYTE;
5142 inbound_frame_set_mark(iframe, 8);
5145 case NGHTTP2_GOAWAY:
5146 DEBUGF(fprintf(stderr, "recv: GOAWAY\n"));
5148 iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
5150 if (iframe->payloadleft < 8) {
5152 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5156 iframe->state = NGHTTP2_IB_READ_NBYTE;
5157 inbound_frame_set_mark(iframe, 8);
5160 case NGHTTP2_CONTINUATION:
5161 DEBUGF(fprintf(stderr, "recv: unexpected CONTINUATION\n"));
5163 /* Receiving CONTINUATION in this state are subject to
5164 connection error of type PROTOCOL_ERROR */
5165 rv = nghttp2_session_terminate_session_with_reason(
5166 session, NGHTTP2_PROTOCOL_ERROR, "CONTINUATION: unexpected");
5167 if (nghttp2_is_fatal(rv)) {
5173 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5177 DEBUGF(fprintf(stderr, "recv: unknown frame\n"));
5179 /* Silently ignore unknown frame type. */
5183 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5188 if (!on_begin_frame_called) {
5189 switch (iframe->state) {
5190 case NGHTTP2_IB_IGN_HEADER_BLOCK:
5191 case NGHTTP2_IB_IGN_PAYLOAD:
5192 case NGHTTP2_IB_FRAME_SIZE_ERROR:
5193 case NGHTTP2_IB_IGN_DATA:
5196 rv = session_call_on_begin_frame(session, &iframe->frame.hd);
5198 if (nghttp2_is_fatal(rv)) {
5206 case NGHTTP2_IB_READ_NBYTE:
5207 DEBUGF(fprintf(stderr, "recv: [IB_READ_NBYTE]\n"));
5209 readlen = inbound_frame_buf_read(iframe, in, last);
5211 iframe->payloadleft -= readlen;
5213 DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu, left=%zd\n",
5214 readlen, iframe->payloadleft,
5215 nghttp2_buf_mark_avail(&iframe->sbuf)));
5217 if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
5221 switch (iframe->frame.hd.type) {
5222 case NGHTTP2_HEADERS:
5223 if (iframe->padlen == 0 &&
5224 (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) {
5225 padlen = inbound_frame_compute_pad(iframe);
5228 rv = nghttp2_session_terminate_session_with_reason(
5229 session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: invalid padding");
5230 if (nghttp2_is_fatal(rv)) {
5233 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5236 iframe->frame.headers.padlen = padlen;
5238 pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags);
5240 if (pri_fieldlen > 0) {
5241 if (iframe->payloadleft < pri_fieldlen) {
5243 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5246 iframe->state = NGHTTP2_IB_READ_NBYTE;
5247 inbound_frame_set_mark(iframe, pri_fieldlen);
5250 /* Truncate buffers used for padding spec */
5251 inbound_frame_set_mark(iframe, 0);
5255 rv = session_process_headers_frame(session);
5256 if (nghttp2_is_fatal(rv)) {
5262 if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
5263 rv = nghttp2_session_add_rst_stream(
5264 session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR);
5265 if (nghttp2_is_fatal(rv)) {
5268 iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
5272 if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
5273 iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
5277 iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
5280 case NGHTTP2_PRIORITY:
5281 rv = session_process_priority_frame(session);
5282 if (nghttp2_is_fatal(rv)) {
5286 session_inbound_frame_reset(session);
5289 case NGHTTP2_RST_STREAM:
5290 rv = session_process_rst_stream_frame(session);
5291 if (nghttp2_is_fatal(rv)) {
5295 session_inbound_frame_reset(session);
5298 case NGHTTP2_PUSH_PROMISE:
5299 if (iframe->padlen == 0 &&
5300 (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) {
5301 padlen = inbound_frame_compute_pad(iframe);
5304 rv = nghttp2_session_terminate_session_with_reason(
5305 session, NGHTTP2_PROTOCOL_ERROR,
5306 "PUSH_PROMISE: invalid padding");
5307 if (nghttp2_is_fatal(rv)) {
5310 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5314 iframe->frame.push_promise.padlen = padlen;
5316 if (iframe->payloadleft < 4) {
5318 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5322 iframe->state = NGHTTP2_IB_READ_NBYTE;
5324 inbound_frame_set_mark(iframe, 4);
5329 rv = session_process_push_promise_frame(session);
5330 if (nghttp2_is_fatal(rv)) {
5336 if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
5337 rv = nghttp2_session_add_rst_stream(
5338 session, iframe->frame.push_promise.promised_stream_id,
5339 NGHTTP2_INTERNAL_ERROR);
5340 if (nghttp2_is_fatal(rv)) {
5343 iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
5347 if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
5348 iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
5352 iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
5356 rv = session_process_ping_frame(session);
5357 if (nghttp2_is_fatal(rv)) {
5361 session_inbound_frame_reset(session);
5364 case NGHTTP2_GOAWAY: {
5367 /* 8 is Last-stream-ID + Error Code */
5368 debuglen = iframe->frame.hd.length - 8;
5371 iframe->raw_lbuf = nghttp2_mem_malloc(mem, debuglen);
5373 if (iframe->raw_lbuf == NULL) {
5374 return NGHTTP2_ERR_NOMEM;
5377 nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, debuglen);
5382 iframe->state = NGHTTP2_IB_READ_GOAWAY_DEBUG;
5386 case NGHTTP2_WINDOW_UPDATE:
5387 rv = session_process_window_update_frame(session);
5388 if (nghttp2_is_fatal(rv)) {
5392 session_inbound_frame_reset(session);
5396 /* This is unknown frame */
5397 session_inbound_frame_reset(session);
5402 case NGHTTP2_IB_READ_HEADER_BLOCK:
5403 case NGHTTP2_IB_IGN_HEADER_BLOCK: {
5404 ssize_t data_readlen;
5406 if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {
5407 fprintf(stderr, "recv: [IB_READ_HEADER_BLOCK]\n");
5409 fprintf(stderr, "recv: [IB_IGN_HEADER_BLOCK]\n");
5411 #endif /* DEBUGBUILD */
5413 readlen = inbound_frame_payload_readlen(iframe, in, last);
5415 DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen,
5416 iframe->payloadleft - readlen));
5418 data_readlen = inbound_frame_effective_readlen(
5419 iframe, iframe->payloadleft - readlen, readlen);
5420 if (data_readlen >= 0) {
5421 size_t trail_padlen;
5422 size_t hd_proclen = 0;
5424 nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen);
5425 DEBUGF(fprintf(stderr, "recv: block final=%d\n",
5426 (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) &&
5427 iframe->payloadleft - data_readlen == trail_padlen));
5429 rv = inflate_header_block(
5430 session, &iframe->frame, &hd_proclen, (uint8_t *)in, data_readlen,
5431 (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) &&
5432 iframe->payloadleft - data_readlen == trail_padlen,
5433 iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK);
5435 if (nghttp2_is_fatal(rv)) {
5439 if (rv == NGHTTP2_ERR_PAUSE) {
5441 iframe->payloadleft -= hd_proclen;
5446 if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
5447 /* The application says no more headers. We decompress the
5448 rest of the header block but not invoke on_header_callback
5449 and on_frame_recv_callback. */
5451 iframe->payloadleft -= hd_proclen;
5453 /* Use promised stream ID for PUSH_PROMISE */
5454 rv = nghttp2_session_add_rst_stream(
5455 session, iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE
5456 ? iframe->frame.push_promise.promised_stream_id
5457 : iframe->frame.hd.stream_id,
5458 NGHTTP2_INTERNAL_ERROR);
5459 if (nghttp2_is_fatal(rv)) {
5463 iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
5468 iframe->payloadleft -= readlen;
5470 if (rv == NGHTTP2_ERR_HEADER_COMP) {
5471 /* GOAWAY is already issued */
5472 if (iframe->payloadleft == 0) {
5473 session_inbound_frame_reset(session);
5476 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5482 iframe->payloadleft -= readlen;
5485 if (iframe->payloadleft) {
5489 if ((iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) {
5491 inbound_frame_set_mark(iframe, NGHTTP2_FRAME_HDLEN);
5495 if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {
5496 iframe->state = NGHTTP2_IB_EXPECT_CONTINUATION;
5498 iframe->state = NGHTTP2_IB_IGN_CONTINUATION;
5501 if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {
5502 rv = session_after_header_block_received(session);
5503 if (nghttp2_is_fatal(rv)) {
5507 session_inbound_frame_reset(session);
5511 case NGHTTP2_IB_IGN_PAYLOAD:
5512 DEBUGF(fprintf(stderr, "recv: [IB_IGN_PAYLOAD]\n"));
5514 readlen = inbound_frame_payload_readlen(iframe, in, last);
5515 iframe->payloadleft -= readlen;
5518 DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen,
5519 iframe->payloadleft));
5521 if (iframe->payloadleft) {
5525 switch (iframe->frame.hd.type) {
5526 case NGHTTP2_HEADERS:
5527 case NGHTTP2_PUSH_PROMISE:
5528 case NGHTTP2_CONTINUATION:
5529 /* Mark inflater bad so that we won't perform further decoding */
5530 session->hd_inflater.ctx.bad = 1;
5536 session_inbound_frame_reset(session);
5539 case NGHTTP2_IB_FRAME_SIZE_ERROR:
5540 DEBUGF(fprintf(stderr, "recv: [IB_FRAME_SIZE_ERROR]\n"));
5542 rv = session_handle_frame_size_error(session, &iframe->frame);
5543 if (nghttp2_is_fatal(rv)) {
5549 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5552 case NGHTTP2_IB_READ_SETTINGS:
5553 DEBUGF(fprintf(stderr, "recv: [IB_READ_SETTINGS]\n"));
5555 readlen = inbound_frame_buf_read(iframe, in, last);
5556 iframe->payloadleft -= readlen;
5559 DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen,
5560 iframe->payloadleft));
5562 if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
5567 inbound_frame_set_settings_entry(iframe);
5569 if (iframe->payloadleft) {
5570 inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH);
5574 rv = session_process_settings_frame(session);
5576 if (nghttp2_is_fatal(rv)) {
5580 session_inbound_frame_reset(session);
5583 case NGHTTP2_IB_READ_GOAWAY_DEBUG:
5584 DEBUGF(fprintf(stderr, "recv: [IB_READ_GOAWAY_DEBUG]\n"));
5586 readlen = inbound_frame_payload_readlen(iframe, in, last);
5588 iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
5590 iframe->payloadleft -= readlen;
5593 DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen,
5594 iframe->payloadleft));
5596 if (iframe->payloadleft) {
5597 assert(nghttp2_buf_avail(&iframe->lbuf) > 0);
5602 rv = session_process_goaway_frame(session);
5604 if (nghttp2_is_fatal(rv)) {
5608 session_inbound_frame_reset(session);
5611 case NGHTTP2_IB_EXPECT_CONTINUATION:
5612 case NGHTTP2_IB_IGN_CONTINUATION:
5614 if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) {
5615 fprintf(stderr, "recv: [IB_EXPECT_CONTINUATION]\n");
5617 fprintf(stderr, "recv: [IB_IGN_CONTINUATION]\n");
5619 #endif /* DEBUGBUILD */
5621 readlen = inbound_frame_buf_read(iframe, in, last);
5624 if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
5628 nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->sbuf.pos);
5629 iframe->payloadleft = cont_hd.length;
5631 DEBUGF(fprintf(stderr, "recv: payloadlen=%zu, type=%u, flags=0x%02x, "
5633 cont_hd.length, cont_hd.type, cont_hd.flags,
5634 cont_hd.stream_id));
5636 if (cont_hd.type != NGHTTP2_CONTINUATION ||
5637 cont_hd.stream_id != iframe->frame.hd.stream_id) {
5638 DEBUGF(fprintf(stderr, "recv: expected stream_id=%d, type=%d, but "
5639 "got stream_id=%d, type=%d\n",
5640 iframe->frame.hd.stream_id, NGHTTP2_CONTINUATION,
5641 cont_hd.stream_id, cont_hd.type));
5642 rv = nghttp2_session_terminate_session_with_reason(
5643 session, NGHTTP2_PROTOCOL_ERROR,
5644 "unexpected non-CONTINUATION frame or stream_id is invalid");
5645 if (nghttp2_is_fatal(rv)) {
5651 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5656 /* CONTINUATION won't bear NGHTTP2_PADDED flag */
5658 iframe->frame.hd.flags |= cont_hd.flags & NGHTTP2_FLAG_END_HEADERS;
5659 iframe->frame.hd.length += cont_hd.length;
5663 if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) {
5664 iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
5666 rv = session_call_on_begin_frame(session, &cont_hd);
5668 if (nghttp2_is_fatal(rv)) {
5672 iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
5676 case NGHTTP2_IB_READ_PAD_DATA:
5677 DEBUGF(fprintf(stderr, "recv: [IB_READ_PAD_DATA]\n"));
5679 readlen = inbound_frame_buf_read(iframe, in, last);
5681 iframe->payloadleft -= readlen;
5683 DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu, left=%zu\n",
5684 readlen, iframe->payloadleft,
5685 nghttp2_buf_mark_avail(&iframe->sbuf)));
5687 if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
5691 /* Pad Length field is subject to flow control */
5692 rv = session_update_recv_connection_window_size(session, readlen);
5693 if (nghttp2_is_fatal(rv)) {
5697 /* Pad Length field is consumed immediately */
5699 nghttp2_session_consume(session, iframe->frame.hd.stream_id, readlen);
5701 if (nghttp2_is_fatal(rv)) {
5705 stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id);
5707 rv = session_update_recv_stream_window_size(
5708 session, stream, readlen,
5709 iframe->payloadleft ||
5710 (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0);
5711 if (nghttp2_is_fatal(rv)) {
5718 padlen = inbound_frame_compute_pad(iframe);
5720 rv = nghttp2_session_terminate_session_with_reason(
5721 session, NGHTTP2_PROTOCOL_ERROR, "DATA: invalid padding");
5722 if (nghttp2_is_fatal(rv)) {
5725 iframe->state = NGHTTP2_IB_IGN_DATA;
5729 iframe->frame.data.padlen = padlen;
5731 iframe->state = NGHTTP2_IB_READ_DATA;
5734 case NGHTTP2_IB_READ_DATA:
5735 DEBUGF(fprintf(stderr, "recv: [IB_READ_DATA]\n"));
5737 readlen = inbound_frame_payload_readlen(iframe, in, last);
5738 iframe->payloadleft -= readlen;
5741 DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen,
5742 iframe->payloadleft));
5745 ssize_t data_readlen;
5747 rv = session_update_recv_connection_window_size(session, readlen);
5748 if (nghttp2_is_fatal(rv)) {
5753 nghttp2_session_get_stream(session, iframe->frame.hd.stream_id);
5755 rv = session_update_recv_stream_window_size(
5756 session, stream, readlen,
5757 iframe->payloadleft ||
5758 (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0);
5759 if (nghttp2_is_fatal(rv)) {
5764 data_readlen = inbound_frame_effective_readlen(
5765 iframe, iframe->payloadleft, readlen);
5767 padlen = readlen - data_readlen;
5770 /* Padding is considered as "consumed" immediately */
5771 rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id,
5774 if (nghttp2_is_fatal(rv)) {
5779 DEBUGF(fprintf(stderr, "recv: data_readlen=%zd\n", data_readlen));
5781 if (stream && data_readlen > 0) {
5782 if (session_enforce_http_messaging(session)) {
5783 if (nghttp2_http_on_data_chunk(stream, data_readlen) != 0) {
5784 rv = nghttp2_session_add_rst_stream(
5785 session, iframe->frame.hd.stream_id, NGHTTP2_PROTOCOL_ERROR);
5786 if (nghttp2_is_fatal(rv)) {
5790 iframe->state = NGHTTP2_IB_IGN_DATA;
5794 if (session->callbacks.on_data_chunk_recv_callback) {
5795 rv = session->callbacks.on_data_chunk_recv_callback(
5796 session, iframe->frame.hd.flags, iframe->frame.hd.stream_id,
5797 in - readlen, data_readlen, session->user_data);
5798 if (rv == NGHTTP2_ERR_PAUSE) {
5802 if (nghttp2_is_fatal(rv)) {
5803 return NGHTTP2_ERR_CALLBACK_FAILURE;
5809 if (iframe->payloadleft) {
5813 rv = session_process_data_frame(session);
5814 if (nghttp2_is_fatal(rv)) {
5818 session_inbound_frame_reset(session);
5821 case NGHTTP2_IB_IGN_DATA:
5822 DEBUGF(fprintf(stderr, "recv: [IB_IGN_DATA]\n"));
5824 readlen = inbound_frame_payload_readlen(iframe, in, last);
5825 iframe->payloadleft -= readlen;
5828 DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen,
5829 iframe->payloadleft));
5832 /* Update connection-level flow control window for ignored
5834 rv = session_update_recv_connection_window_size(session, readlen);
5835 if (nghttp2_is_fatal(rv)) {
5839 if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
5841 /* Ignored DATA is considered as "consumed" immediately. */
5842 rv = session_update_connection_consumed_size(session, readlen);
5844 if (nghttp2_is_fatal(rv)) {
5850 if (iframe->payloadleft) {
5854 session_inbound_frame_reset(session);
5857 case NGHTTP2_IB_IGN_ALL:
5861 if (!busy && in == last) {
5873 int nghttp2_session_recv(nghttp2_session *session) {
5874 uint8_t buf[NGHTTP2_INBOUND_BUFFER_LENGTH];
5877 readlen = session_recv(session, buf, sizeof(buf));
5879 ssize_t proclen = nghttp2_session_mem_recv(session, buf, readlen);
5881 return (int)proclen;
5883 assert(proclen == readlen);
5884 } else if (readlen == 0 || readlen == NGHTTP2_ERR_WOULDBLOCK) {
5886 } else if (readlen == NGHTTP2_ERR_EOF) {
5887 return NGHTTP2_ERR_EOF;
5888 } else if (readlen < 0) {
5889 return NGHTTP2_ERR_CALLBACK_FAILURE;
5895 * Returns the number of active streams, which includes streams in
5898 static size_t session_get_num_active_streams(nghttp2_session *session) {
5899 return nghttp2_map_size(&session->streams) - session->num_closed_streams -
5900 session->num_idle_streams;
5903 int nghttp2_session_want_read(nghttp2_session *session) {
5904 size_t num_active_streams;
5906 /* If this flag is set, we don't want to read. The application
5907 should drop the connection. */
5908 if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) {
5912 num_active_streams = session_get_num_active_streams(session);
5914 /* Unless termination GOAWAY is sent or received, we always want to
5915 read incoming frames. */
5917 if (num_active_streams > 0) {
5921 /* If there is no active streams and GOAWAY has been sent or
5922 received, we are done with this session. */
5923 return (session->goaway_flags &
5924 (NGHTTP2_GOAWAY_SENT | NGHTTP2_GOAWAY_RECV)) == 0;
5927 int nghttp2_session_want_write(nghttp2_session *session) {
5928 /* If these flag is set, we don't want to write any data. The
5929 application should drop the connection. */
5930 if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) {
5935 * Unless termination GOAWAY is sent or received, we want to write
5936 * frames if there is pending ones. If pending frame is request/push
5937 * response HEADERS and concurrent stream limit is reached, we don't
5938 * want to write them.
5941 if (session->aob.item == NULL &&
5942 nghttp2_outbound_queue_top(&session->ob_urgent) == NULL &&
5943 nghttp2_outbound_queue_top(&session->ob_reg) == NULL &&
5944 (nghttp2_pq_empty(&session->ob_da_pq) ||
5945 session->remote_window_size == 0) &&
5946 (nghttp2_outbound_queue_top(&session->ob_syn) == NULL ||
5947 session_is_outgoing_concurrent_streams_max(session))) {
5951 /* If there is no active streams and GOAWAY has been sent or
5952 received, we are done with this session. */
5953 return (session->goaway_flags &
5954 (NGHTTP2_GOAWAY_SENT | NGHTTP2_GOAWAY_RECV)) == 0;
5957 int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
5958 const uint8_t *opaque_data) {
5960 nghttp2_outbound_item *item;
5961 nghttp2_frame *frame;
5964 mem = &session->mem;
5965 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
5967 return NGHTTP2_ERR_NOMEM;
5970 nghttp2_outbound_item_init(item);
5972 frame = &item->frame;
5974 nghttp2_frame_ping_init(&frame->ping, flags, opaque_data);
5976 rv = nghttp2_session_add_item(session, item);
5979 nghttp2_frame_ping_free(&frame->ping);
5980 nghttp2_mem_free(mem, item);
5986 int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id,
5987 uint32_t error_code, const uint8_t *opaque_data,
5988 size_t opaque_data_len, uint8_t aux_flags) {
5990 nghttp2_outbound_item *item;
5991 nghttp2_frame *frame;
5992 uint8_t *opaque_data_copy = NULL;
5993 nghttp2_goaway_aux_data *aux_data;
5996 mem = &session->mem;
5998 if (nghttp2_session_is_my_stream_id(session, last_stream_id)) {
5999 return NGHTTP2_ERR_INVALID_ARGUMENT;
6002 if (opaque_data_len) {
6003 if (opaque_data_len + 8 > NGHTTP2_MAX_PAYLOADLEN) {
6004 return NGHTTP2_ERR_INVALID_ARGUMENT;
6006 opaque_data_copy = nghttp2_mem_malloc(mem, opaque_data_len);
6007 if (opaque_data_copy == NULL) {
6008 return NGHTTP2_ERR_NOMEM;
6010 memcpy(opaque_data_copy, opaque_data, opaque_data_len);
6013 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
6015 nghttp2_mem_free(mem, opaque_data_copy);
6016 return NGHTTP2_ERR_NOMEM;
6019 nghttp2_outbound_item_init(item);
6021 frame = &item->frame;
6023 /* last_stream_id must not be increased from the value previously
6025 last_stream_id = nghttp2_min(last_stream_id, session->local_last_stream_id);
6027 nghttp2_frame_goaway_init(&frame->goaway, last_stream_id, error_code,
6028 opaque_data_copy, opaque_data_len);
6030 aux_data = &item->aux_data.goaway;
6031 aux_data->flags = aux_flags;
6033 rv = nghttp2_session_add_item(session, item);
6035 nghttp2_frame_goaway_free(&frame->goaway, mem);
6036 nghttp2_mem_free(mem, item);
6042 int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags,
6044 int32_t window_size_increment) {
6046 nghttp2_outbound_item *item;
6047 nghttp2_frame *frame;
6050 mem = &session->mem;
6051 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
6053 return NGHTTP2_ERR_NOMEM;
6056 nghttp2_outbound_item_init(item);
6058 frame = &item->frame;
6060 nghttp2_frame_window_update_init(&frame->window_update, flags, stream_id,
6061 window_size_increment);
6063 rv = nghttp2_session_add_item(session, item);
6066 nghttp2_frame_window_update_free(&frame->window_update);
6067 nghttp2_mem_free(mem, item);
6073 int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
6074 const nghttp2_settings_entry *iv, size_t niv) {
6075 nghttp2_outbound_item *item;
6076 nghttp2_frame *frame;
6077 nghttp2_settings_entry *iv_copy;
6082 mem = &session->mem;
6084 if (flags & NGHTTP2_FLAG_ACK) {
6086 return NGHTTP2_ERR_INVALID_ARGUMENT;
6088 } else if (session->inflight_niv != -1) {
6089 return NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS;
6092 if (!nghttp2_iv_check(iv, niv)) {
6093 return NGHTTP2_ERR_INVALID_ARGUMENT;
6096 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
6098 return NGHTTP2_ERR_NOMEM;
6102 iv_copy = nghttp2_frame_iv_copy(iv, niv, mem);
6103 if (iv_copy == NULL) {
6104 nghttp2_mem_free(mem, item);
6105 return NGHTTP2_ERR_NOMEM;
6111 if ((flags & NGHTTP2_FLAG_ACK) == 0) {
6113 session->inflight_iv = nghttp2_frame_iv_copy(iv, niv, mem);
6115 if (session->inflight_iv == NULL) {
6116 nghttp2_mem_free(mem, iv_copy);
6117 nghttp2_mem_free(mem, item);
6118 return NGHTTP2_ERR_NOMEM;
6121 session->inflight_iv = NULL;
6124 session->inflight_niv = niv;
6127 nghttp2_outbound_item_init(item);
6129 frame = &item->frame;
6131 nghttp2_frame_settings_init(&frame->settings, flags, iv_copy, niv);
6132 rv = nghttp2_session_add_item(session, item);
6134 /* The only expected error is fatal one */
6135 assert(nghttp2_is_fatal(rv));
6137 if ((flags & NGHTTP2_FLAG_ACK) == 0) {
6138 nghttp2_mem_free(mem, session->inflight_iv);
6139 session->inflight_iv = NULL;
6140 session->inflight_niv = -1;
6143 nghttp2_frame_settings_free(&frame->settings, mem);
6144 nghttp2_mem_free(mem, item);
6149 /* Extract NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS and ENABLE_PUSH
6150 here. We use it to refuse the incoming stream and PUSH_PROMISE
6153 for (i = niv; i > 0; --i) {
6154 if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) {
6155 session->pending_local_max_concurrent_stream = iv[i - 1].value;
6160 for (i = niv; i > 0; --i) {
6161 if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_PUSH) {
6162 session->pending_enable_push = iv[i - 1].value;
6170 int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
6171 size_t datamax, nghttp2_frame *frame,
6172 nghttp2_data_aux_data *aux_data,
6173 nghttp2_stream *stream) {
6175 uint32_t data_flags;
6177 ssize_t padded_payloadlen;
6179 size_t max_payloadlen;
6181 assert(bufs->head == bufs->cur);
6183 buf = &bufs->cur->buf;
6185 if (session->callbacks.read_length_callback) {
6187 payloadlen = session->callbacks.read_length_callback(
6188 session, frame->hd.type, stream->stream_id, session->remote_window_size,
6189 stream->remote_window_size, session->remote_settings.max_frame_size,
6190 session->user_data);
6192 DEBUGF(fprintf(stderr, "send: read_length_callback=%zd\n", payloadlen));
6194 payloadlen = nghttp2_session_enforce_flow_control_limits(session, stream,
6197 DEBUGF(fprintf(stderr,
6198 "send: read_length_callback after flow control=%zd\n",
6201 if (payloadlen <= 0) {
6202 return NGHTTP2_ERR_CALLBACK_FAILURE;
6205 if (payloadlen > nghttp2_buf_avail(buf)) {
6206 /* Resize the current buffer(s). The reason why we do +1 for
6207 buffer size is for possible padding field. */
6208 rv = nghttp2_bufs_realloc(&session->aob.framebufs,
6209 NGHTTP2_FRAME_HDLEN + 1 + payloadlen);
6212 DEBUGF(fprintf(stderr, "send: realloc buffer failed rv=%d", rv));
6213 /* If reallocation failed, old buffers are still in tact. So
6215 payloadlen = datamax;
6218 fprintf(stderr, "send: use safe limit payloadlen=%zd", payloadlen));
6220 assert(&session->aob.framebufs == bufs);
6222 buf = &bufs->cur->buf;
6225 datamax = (size_t)payloadlen;
6228 /* Current max DATA length is less then buffer chunk size */
6229 assert(nghttp2_buf_avail(buf) >= (ssize_t)datamax);
6231 data_flags = NGHTTP2_DATA_FLAG_NONE;
6232 payloadlen = aux_data->data_prd.read_callback(
6233 session, frame->hd.stream_id, buf->pos, datamax, &data_flags,
6234 &aux_data->data_prd.source, session->user_data);
6236 if (payloadlen == NGHTTP2_ERR_DEFERRED ||
6237 payloadlen == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
6238 DEBUGF(fprintf(stderr, "send: DATA postponed due to %s\n",
6239 nghttp2_strerror((int)payloadlen)));
6241 return (int)payloadlen;
6244 if (payloadlen < 0 || datamax < (size_t)payloadlen) {
6245 /* This is the error code when callback is failed. */
6246 return NGHTTP2_ERR_CALLBACK_FAILURE;
6249 buf->last = buf->pos + payloadlen;
6250 buf->pos -= NGHTTP2_FRAME_HDLEN;
6252 /* Clear flags, because this may contain previous flags of previous
6254 frame->hd.flags = NGHTTP2_FLAG_NONE;
6256 if (data_flags & NGHTTP2_DATA_FLAG_EOF) {
6258 /* If NGHTTP2_DATA_FLAG_NO_END_STREAM is set, don't set
6259 NGHTTP2_FLAG_END_STREAM */
6260 if ((aux_data->flags & NGHTTP2_FLAG_END_STREAM) &&
6261 (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) {
6262 frame->hd.flags |= NGHTTP2_FLAG_END_STREAM;
6266 if (data_flags & NGHTTP2_DATA_FLAG_NO_COPY) {
6267 if (session->callbacks.send_data_callback == NULL) {
6270 "NGHTTP2_DATA_FLAG_NO_COPY requires send_data_callback set\n"));
6272 return NGHTTP2_ERR_CALLBACK_FAILURE;
6274 aux_data->no_copy = 1;
6277 frame->hd.length = payloadlen;
6278 frame->data.padlen = 0;
6280 max_payloadlen = nghttp2_min(datamax, frame->hd.length + NGHTTP2_MAX_PADLEN);
6283 session_call_select_padding(session, frame, max_payloadlen);
6285 if (nghttp2_is_fatal((int)padded_payloadlen)) {
6286 return (int)padded_payloadlen;
6289 frame->data.padlen = padded_payloadlen - payloadlen;
6291 nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
6293 rv = nghttp2_frame_add_pad(bufs, &frame->hd, frame->data.padlen,
6299 session_outbound_item_schedule(session, stream->item,
6300 stream->effective_weight);
6305 void *nghttp2_session_get_stream_user_data(nghttp2_session *session,
6306 int32_t stream_id) {
6307 nghttp2_stream *stream;
6308 stream = nghttp2_session_get_stream(session, stream_id);
6310 return stream->stream_user_data;
6316 int nghttp2_session_set_stream_user_data(nghttp2_session *session,
6318 void *stream_user_data) {
6319 nghttp2_stream *stream;
6320 stream = nghttp2_session_get_stream(session, stream_id);
6322 return NGHTTP2_ERR_INVALID_ARGUMENT;
6324 stream->stream_user_data = stream_user_data;
6328 int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) {
6330 nghttp2_stream *stream;
6331 stream = nghttp2_session_get_stream(session, stream_id);
6332 if (stream == NULL || !nghttp2_stream_check_deferred_item(stream)) {
6333 return NGHTTP2_ERR_INVALID_ARGUMENT;
6336 rv = nghttp2_stream_resume_deferred_item(
6337 stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER, session);
6339 if (nghttp2_is_fatal(rv)) {
6346 size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) {
6347 return nghttp2_outbound_queue_size(&session->ob_urgent) +
6348 nghttp2_outbound_queue_size(&session->ob_reg) +
6349 nghttp2_outbound_queue_size(&session->ob_syn) +
6350 nghttp2_pq_size(&session->ob_da_pq);
6354 nghttp2_session_get_stream_effective_recv_data_length(nghttp2_session *session,
6355 int32_t stream_id) {
6356 nghttp2_stream *stream;
6357 stream = nghttp2_session_get_stream(session, stream_id);
6358 if (stream == NULL) {
6361 return stream->recv_window_size < 0 ? 0 : stream->recv_window_size;
6365 nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session,
6366 int32_t stream_id) {
6367 nghttp2_stream *stream;
6368 stream = nghttp2_session_get_stream(session, stream_id);
6369 if (stream == NULL) {
6372 return stream->local_window_size;
6376 nghttp2_session_get_effective_recv_data_length(nghttp2_session *session) {
6377 return session->recv_window_size < 0 ? 0 : session->recv_window_size;
6381 nghttp2_session_get_effective_local_window_size(nghttp2_session *session) {
6382 return session->local_window_size;
6385 int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session,
6386 int32_t stream_id) {
6387 nghttp2_stream *stream;
6389 stream = nghttp2_session_get_stream(session, stream_id);
6390 if (stream == NULL) {
6394 /* stream->remote_window_size can be negative when
6395 SETTINGS_INITIAL_WINDOW_SIZE is changed. */
6396 return nghttp2_max(0, stream->remote_window_size);
6399 int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session) {
6400 return session->remote_window_size;
6403 uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,
6404 nghttp2_settings_id id) {
6406 case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
6407 return session->remote_settings.header_table_size;
6408 case NGHTTP2_SETTINGS_ENABLE_PUSH:
6409 return session->remote_settings.enable_push;
6410 case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
6411 return session->remote_settings.max_concurrent_streams;
6412 case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
6413 return session->remote_settings.initial_window_size;
6414 case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
6415 return session->remote_settings.max_frame_size;
6416 case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
6417 return session->remote_settings.max_header_list_size;
6423 int nghttp2_session_upgrade(nghttp2_session *session,
6424 const uint8_t *settings_payload,
6425 size_t settings_payloadlen,
6426 void *stream_user_data) {
6427 nghttp2_stream *stream;
6428 nghttp2_frame frame;
6429 nghttp2_settings_entry *iv;
6432 nghttp2_priority_spec pri_spec;
6435 mem = &session->mem;
6437 if ((!session->server && session->next_stream_id != 1) ||
6438 (session->server && session->last_recv_stream_id >= 1)) {
6439 return NGHTTP2_ERR_PROTO;
6441 if (settings_payloadlen % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) {
6442 return NGHTTP2_ERR_INVALID_ARGUMENT;
6444 rv = nghttp2_frame_unpack_settings_payload2(&iv, &niv, settings_payload,
6445 settings_payloadlen, mem);
6450 if (session->server) {
6451 nghttp2_frame_hd_init(&frame.hd, settings_payloadlen, NGHTTP2_SETTINGS,
6452 NGHTTP2_FLAG_NONE, 0);
6453 frame.settings.iv = iv;
6454 frame.settings.niv = niv;
6455 rv = nghttp2_session_on_settings_received(session, &frame, 1 /* No ACK */);
6457 rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, niv);
6459 nghttp2_mem_free(mem, iv);
6464 nghttp2_priority_spec_default_init(&pri_spec);
6466 stream = nghttp2_session_open_stream(
6467 session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_OPENING,
6468 session->server ? NULL : stream_user_data);
6469 if (stream == NULL) {
6470 return NGHTTP2_ERR_NOMEM;
6472 if (session->server) {
6473 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
6474 session->last_recv_stream_id = 1;
6475 session->last_proc_stream_id = 1;
6477 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
6478 session->next_stream_id += 2;
6483 int nghttp2_session_get_stream_local_close(nghttp2_session *session,
6484 int32_t stream_id) {
6485 nghttp2_stream *stream;
6487 stream = nghttp2_session_get_stream(session, stream_id);
6493 return (stream->shut_flags & NGHTTP2_SHUT_WR) != 0;
6496 int nghttp2_session_get_stream_remote_close(nghttp2_session *session,
6497 int32_t stream_id) {
6498 nghttp2_stream *stream;
6500 stream = nghttp2_session_get_stream(session, stream_id);
6506 return (stream->shut_flags & NGHTTP2_SHUT_RD) != 0;
6509 int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id,
6512 nghttp2_stream *stream;
6514 if (stream_id == 0) {
6515 return NGHTTP2_ERR_INVALID_ARGUMENT;
6518 if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
6519 return NGHTTP2_ERR_INVALID_STATE;
6522 rv = session_update_connection_consumed_size(session, size);
6524 if (nghttp2_is_fatal(rv)) {
6528 stream = nghttp2_session_get_stream(session, stream_id);
6534 rv = session_update_stream_consumed_size(session, stream, size);
6536 if (nghttp2_is_fatal(rv)) {
6543 int nghttp2_session_consume_connection(nghttp2_session *session, size_t size) {
6546 if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
6547 return NGHTTP2_ERR_INVALID_STATE;
6550 rv = session_update_connection_consumed_size(session, size);
6552 if (nghttp2_is_fatal(rv)) {
6559 int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id,
6562 nghttp2_stream *stream;
6564 if (stream_id == 0) {
6565 return NGHTTP2_ERR_INVALID_ARGUMENT;
6568 if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
6569 return NGHTTP2_ERR_INVALID_STATE;
6572 stream = nghttp2_session_get_stream(session, stream_id);
6578 rv = session_update_stream_consumed_size(session, stream, size);
6580 if (nghttp2_is_fatal(rv)) {
6587 int nghttp2_session_set_next_stream_id(nghttp2_session *session,
6588 int32_t next_stream_id) {
6589 if (next_stream_id <= 0 ||
6590 session->next_stream_id > (uint32_t)next_stream_id) {
6591 return NGHTTP2_ERR_INVALID_ARGUMENT;
6594 if (session->server) {
6595 if (next_stream_id % 2) {
6596 return NGHTTP2_ERR_INVALID_ARGUMENT;
6598 } else if (next_stream_id % 2 == 0) {
6599 return NGHTTP2_ERR_INVALID_ARGUMENT;
6602 session->next_stream_id = next_stream_id;
6606 uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session) {
6607 return session->next_stream_id;
6610 int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session) {
6611 return session->last_proc_stream_id;