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"
33 #include "nghttp2_helper.h"
34 #include "nghttp2_net.h"
35 #include "nghttp2_priority_spec.h"
36 #include "nghttp2_option.h"
37 #include "nghttp2_http.h"
38 #include "nghttp2_pq.h"
39 #include "nghttp2_debug.h"
42 * Returns non-zero if the number of outgoing opened streams is larger
44 * remote_settings.max_concurrent_streams.
47 session_is_outgoing_concurrent_streams_max(nghttp2_session *session) {
48 return session->remote_settings.max_concurrent_streams <=
49 session->num_outgoing_streams;
53 * Returns non-zero if the number of incoming opened streams is larger
55 * local_settings.max_concurrent_streams.
58 session_is_incoming_concurrent_streams_max(nghttp2_session *session) {
59 return session->local_settings.max_concurrent_streams <=
60 session->num_incoming_streams;
64 * Returns non-zero if the number of incoming opened streams is larger
66 * session->pending_local_max_concurrent_stream.
69 session_is_incoming_concurrent_streams_pending_max(nghttp2_session *session) {
70 return session->pending_local_max_concurrent_stream <=
71 session->num_incoming_streams;
75 * Returns non-zero if |lib_error| is non-fatal error.
77 static int is_non_fatal(int lib_error_code) {
78 return lib_error_code < 0 && lib_error_code > NGHTTP2_ERR_FATAL;
81 int nghttp2_is_fatal(int lib_error_code) {
82 return lib_error_code < NGHTTP2_ERR_FATAL;
85 static int session_enforce_http_messaging(nghttp2_session *session) {
86 return (session->opt_flags & NGHTTP2_OPTMASK_NO_HTTP_MESSAGING) == 0;
90 * Returns nonzero if |frame| is trailer headers.
92 static int session_trailer_headers(nghttp2_session *session,
93 nghttp2_stream *stream,
94 nghttp2_frame *frame) {
95 if (!stream || frame->hd.type != NGHTTP2_HEADERS) {
98 if (session->server) {
99 return frame->headers.cat == NGHTTP2_HCAT_HEADERS;
102 return frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
103 (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) == 0;
106 /* Returns nonzero if the |stream| is in reserved(remote) state */
107 static int state_reserved_remote(nghttp2_session *session,
108 nghttp2_stream *stream) {
109 return stream->state == NGHTTP2_STREAM_RESERVED &&
110 !nghttp2_session_is_my_stream_id(session, stream->stream_id);
113 /* Returns nonzero if the |stream| is in reserved(local) state */
114 static int state_reserved_local(nghttp2_session *session,
115 nghttp2_stream *stream) {
116 return stream->state == NGHTTP2_STREAM_RESERVED &&
117 nghttp2_session_is_my_stream_id(session, stream->stream_id);
121 * Checks whether received stream_id is valid. This function returns
122 * 1 if it succeeds, or 0.
124 static int session_is_new_peer_stream_id(nghttp2_session *session,
126 return stream_id != 0 &&
127 !nghttp2_session_is_my_stream_id(session, stream_id) &&
128 session->last_recv_stream_id < stream_id;
131 static int session_detect_idle_stream(nghttp2_session *session,
133 /* Assume that stream object with stream_id does not exist */
134 if (nghttp2_session_is_my_stream_id(session, stream_id)) {
135 if (session->last_sent_stream_id < stream_id) {
140 if (session_is_new_peer_stream_id(session, stream_id)) {
146 static int check_ext_type_set(const uint8_t *ext_types, uint8_t type) {
147 return (ext_types[type / 8] & (1 << (type & 0x7))) > 0;
150 static int session_call_error_callback(nghttp2_session *session,
151 int lib_error_code, const char *fmt,
159 if (!session->callbacks.error_callback &&
160 !session->callbacks.error_callback2) {
167 rv = vsnprintf(NULL, 0, fmt, ap);
171 return NGHTTP2_ERR_NOMEM;
174 bufsize = (size_t)(rv + 1);
176 buf = nghttp2_mem_malloc(mem, bufsize);
178 return NGHTTP2_ERR_NOMEM;
182 rv = vsnprintf(buf, bufsize, fmt, ap);
186 nghttp2_mem_free(mem, buf);
187 /* vsnprintf may return error because of various things we can
188 imagine, but typically we don't want to drop session just for
190 DEBUGF("error_callback: vsnprintf failed. The template was %s\n", fmt);
194 if (session->callbacks.error_callback2) {
195 rv = session->callbacks.error_callback2(session, lib_error_code, buf,
196 (size_t)rv, session->user_data);
198 rv = session->callbacks.error_callback(session, buf, (size_t)rv,
202 nghttp2_mem_free(mem, buf);
205 return NGHTTP2_ERR_CALLBACK_FAILURE;
211 static int session_terminate_session(nghttp2_session *session,
212 int32_t last_stream_id,
213 uint32_t error_code, const char *reason) {
215 const uint8_t *debug_data;
216 size_t debug_datalen;
218 if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
222 /* Ignore all incoming frames because we are going to tear down the
224 session->iframe.state = NGHTTP2_IB_IGN_ALL;
226 if (reason == NULL) {
230 debug_data = (const uint8_t *)reason;
231 debug_datalen = strlen(reason);
234 rv = nghttp2_session_add_goaway(session, last_stream_id, error_code,
235 debug_data, debug_datalen,
236 NGHTTP2_GOAWAY_AUX_TERM_ON_SEND);
242 session->goaway_flags |= NGHTTP2_GOAWAY_TERM_ON_SEND;
247 int nghttp2_session_terminate_session(nghttp2_session *session,
248 uint32_t error_code) {
249 return session_terminate_session(session, session->last_proc_stream_id,
253 int nghttp2_session_terminate_session2(nghttp2_session *session,
254 int32_t last_stream_id,
255 uint32_t error_code) {
256 return session_terminate_session(session, last_stream_id, error_code, NULL);
259 int nghttp2_session_terminate_session_with_reason(nghttp2_session *session,
261 const char *reason) {
262 return session_terminate_session(session, session->last_proc_stream_id,
266 int nghttp2_session_is_my_stream_id(nghttp2_session *session,
269 if (stream_id == 0) {
272 rem = stream_id & 0x1;
273 if (session->server) {
279 nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session,
281 nghttp2_stream *stream;
283 stream = (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id);
285 if (stream == NULL || (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) ||
286 stream->state == NGHTTP2_STREAM_IDLE) {
293 nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session,
295 return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id);
298 static void session_inbound_frame_reset(nghttp2_session *session) {
299 nghttp2_inbound_frame *iframe = &session->iframe;
300 nghttp2_mem *mem = &session->mem;
301 /* A bit risky code, since if this function is called from
302 nghttp2_session_new(), we rely on the fact that
303 iframe->frame.hd.type is 0, so that no free is performed. */
304 switch (iframe->frame.hd.type) {
307 case NGHTTP2_HEADERS:
308 nghttp2_frame_headers_free(&iframe->frame.headers, mem);
310 case NGHTTP2_PRIORITY:
311 nghttp2_frame_priority_free(&iframe->frame.priority);
313 case NGHTTP2_RST_STREAM:
314 nghttp2_frame_rst_stream_free(&iframe->frame.rst_stream);
316 case NGHTTP2_SETTINGS:
317 nghttp2_frame_settings_free(&iframe->frame.settings, mem);
319 nghttp2_mem_free(mem, iframe->iv);
326 case NGHTTP2_PUSH_PROMISE:
327 nghttp2_frame_push_promise_free(&iframe->frame.push_promise, mem);
330 nghttp2_frame_ping_free(&iframe->frame.ping);
333 nghttp2_frame_goaway_free(&iframe->frame.goaway, mem);
335 case NGHTTP2_WINDOW_UPDATE:
336 nghttp2_frame_window_update_free(&iframe->frame.window_update);
339 /* extension frame */
340 if (check_ext_type_set(session->user_recv_ext_types,
341 iframe->frame.hd.type)) {
342 nghttp2_frame_extension_free(&iframe->frame.ext);
344 switch (iframe->frame.hd.type) {
346 if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) == 0) {
349 nghttp2_frame_altsvc_free(&iframe->frame.ext, mem);
352 if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN) == 0) {
355 nghttp2_frame_origin_free(&iframe->frame.ext, mem);
363 memset(&iframe->frame, 0, sizeof(nghttp2_frame));
364 memset(&iframe->ext_frame_payload, 0, sizeof(nghttp2_ext_frame_payload));
366 iframe->state = NGHTTP2_IB_READ_HEAD;
368 nghttp2_buf_wrap_init(&iframe->sbuf, iframe->raw_sbuf,
369 sizeof(iframe->raw_sbuf));
370 iframe->sbuf.mark += NGHTTP2_FRAME_HDLEN;
372 nghttp2_buf_free(&iframe->lbuf, mem);
373 nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
375 iframe->raw_lbuf = NULL;
377 iframe->payloadleft = 0;
381 static void init_settings(nghttp2_settings_storage *settings) {
382 settings->header_table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
383 settings->enable_push = 1;
384 settings->max_concurrent_streams = NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;
385 settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE;
386 settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN;
387 settings->max_header_list_size = UINT32_MAX;
390 static void active_outbound_item_reset(nghttp2_active_outbound_item *aob,
392 DEBUGF("send: reset nghttp2_active_outbound_item\n");
393 DEBUGF("send: aob->item = %p\n", aob->item);
394 nghttp2_outbound_item_free(aob->item, mem);
395 nghttp2_mem_free(mem, aob->item);
397 nghttp2_bufs_reset(&aob->framebufs);
398 aob->state = NGHTTP2_OB_POP_ITEM;
401 int nghttp2_enable_strict_preface = 1;
403 static int session_new(nghttp2_session **session_ptr,
404 const nghttp2_session_callbacks *callbacks,
405 void *user_data, int server,
406 const nghttp2_option *option, nghttp2_mem *mem) {
409 size_t max_deflate_dynamic_table_size =
410 NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE;
413 mem = nghttp2_mem_default();
416 *session_ptr = nghttp2_mem_calloc(mem, 1, sizeof(nghttp2_session));
417 if (*session_ptr == NULL) {
418 rv = NGHTTP2_ERR_NOMEM;
422 (*session_ptr)->mem = *mem;
423 mem = &(*session_ptr)->mem;
425 /* next_stream_id is initialized in either
426 nghttp2_session_client_new2 or nghttp2_session_server_new2 */
428 nghttp2_stream_init(&(*session_ptr)->root, 0, NGHTTP2_STREAM_FLAG_NONE,
429 NGHTTP2_STREAM_IDLE, NGHTTP2_DEFAULT_WEIGHT, 0, 0, NULL,
432 (*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
433 (*session_ptr)->recv_window_size = 0;
434 (*session_ptr)->consumed_size = 0;
435 (*session_ptr)->recv_reduction = 0;
436 (*session_ptr)->local_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
438 (*session_ptr)->goaway_flags = NGHTTP2_GOAWAY_NONE;
439 (*session_ptr)->local_last_stream_id = (1u << 31) - 1;
440 (*session_ptr)->remote_last_stream_id = (1u << 31) - 1;
442 (*session_ptr)->pending_local_max_concurrent_stream =
443 NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;
444 (*session_ptr)->pending_enable_push = 1;
447 (*session_ptr)->server = 1;
450 init_settings(&(*session_ptr)->remote_settings);
451 init_settings(&(*session_ptr)->local_settings);
453 (*session_ptr)->max_incoming_reserved_streams =
454 NGHTTP2_MAX_INCOMING_RESERVED_STREAMS;
456 /* Limit max outgoing concurrent streams to sensible value */
457 (*session_ptr)->remote_settings.max_concurrent_streams = 100;
459 (*session_ptr)->max_send_header_block_length = NGHTTP2_MAX_HEADERSLEN;
460 (*session_ptr)->max_outbound_ack = NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM;
461 (*session_ptr)->max_settings = NGHTTP2_DEFAULT_MAX_SETTINGS;
464 if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) &&
465 option->no_auto_window_update) {
467 (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE;
470 if (option->opt_set_mask & NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS) {
472 (*session_ptr)->remote_settings.max_concurrent_streams =
473 option->peer_max_concurrent_streams;
476 if (option->opt_set_mask & NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS) {
478 (*session_ptr)->max_incoming_reserved_streams =
479 option->max_reserved_remote_streams;
482 if ((option->opt_set_mask & NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC) &&
483 option->no_recv_client_magic) {
485 (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC;
488 if ((option->opt_set_mask & NGHTTP2_OPT_NO_HTTP_MESSAGING) &&
489 option->no_http_messaging) {
491 (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING;
494 if (option->opt_set_mask & NGHTTP2_OPT_USER_RECV_EXT_TYPES) {
495 memcpy((*session_ptr)->user_recv_ext_types, option->user_recv_ext_types,
496 sizeof((*session_ptr)->user_recv_ext_types));
499 if (option->opt_set_mask & NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES) {
500 (*session_ptr)->builtin_recv_ext_types = option->builtin_recv_ext_types;
503 if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_PING_ACK) &&
504 option->no_auto_ping_ack) {
505 (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_PING_ACK;
508 if (option->opt_set_mask & NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH) {
509 (*session_ptr)->max_send_header_block_length =
510 option->max_send_header_block_length;
513 if (option->opt_set_mask & NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE) {
514 max_deflate_dynamic_table_size = option->max_deflate_dynamic_table_size;
517 if ((option->opt_set_mask & NGHTTP2_OPT_NO_CLOSED_STREAMS) &&
518 option->no_closed_streams) {
519 (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_CLOSED_STREAMS;
522 if (option->opt_set_mask & NGHTTP2_OPT_MAX_OUTBOUND_ACK) {
523 (*session_ptr)->max_outbound_ack = option->max_outbound_ack;
526 if ((option->opt_set_mask & NGHTTP2_OPT_MAX_SETTINGS) &&
527 option->max_settings) {
528 (*session_ptr)->max_settings = option->max_settings;
532 rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater,
533 max_deflate_dynamic_table_size, mem);
535 goto fail_hd_deflater;
537 rv = nghttp2_hd_inflate_init(&(*session_ptr)->hd_inflater, mem);
539 goto fail_hd_inflater;
541 rv = nghttp2_map_init(&(*session_ptr)->streams, mem);
546 nbuffer = ((*session_ptr)->max_send_header_block_length +
547 NGHTTP2_FRAMEBUF_CHUNKLEN - 1) /
548 NGHTTP2_FRAMEBUF_CHUNKLEN;
554 /* 1 for Pad Field. */
555 rv = nghttp2_bufs_init3(&(*session_ptr)->aob.framebufs,
556 NGHTTP2_FRAMEBUF_CHUNKLEN, nbuffer, 1,
557 NGHTTP2_FRAME_HDLEN + 1, mem);
559 goto fail_aob_framebuf;
562 active_outbound_item_reset(&(*session_ptr)->aob, mem);
564 (*session_ptr)->callbacks = *callbacks;
565 (*session_ptr)->user_data = user_data;
567 session_inbound_frame_reset(*session_ptr);
569 if (nghttp2_enable_strict_preface) {
570 nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe;
572 if (server && ((*session_ptr)->opt_flags &
573 NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) == 0) {
574 iframe->state = NGHTTP2_IB_READ_CLIENT_MAGIC;
575 iframe->payloadleft = NGHTTP2_CLIENT_MAGIC_LEN;
577 iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS;
581 (*session_ptr)->aob.state = NGHTTP2_OB_SEND_CLIENT_MAGIC;
582 nghttp2_bufs_add(&(*session_ptr)->aob.framebufs, NGHTTP2_CLIENT_MAGIC,
583 NGHTTP2_CLIENT_MAGIC_LEN);
590 nghttp2_map_free(&(*session_ptr)->streams);
592 nghttp2_hd_inflate_free(&(*session_ptr)->hd_inflater);
594 nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater);
596 nghttp2_mem_free(mem, *session_ptr);
601 int nghttp2_session_client_new(nghttp2_session **session_ptr,
602 const nghttp2_session_callbacks *callbacks,
604 return nghttp2_session_client_new3(session_ptr, callbacks, user_data, NULL,
608 int nghttp2_session_client_new2(nghttp2_session **session_ptr,
609 const nghttp2_session_callbacks *callbacks,
610 void *user_data, const nghttp2_option *option) {
611 return nghttp2_session_client_new3(session_ptr, callbacks, user_data, option,
615 int nghttp2_session_client_new3(nghttp2_session **session_ptr,
616 const nghttp2_session_callbacks *callbacks,
617 void *user_data, const nghttp2_option *option,
620 nghttp2_session *session;
622 rv = session_new(&session, callbacks, user_data, 0, option, mem);
627 /* IDs for use in client */
628 session->next_stream_id = 1;
630 *session_ptr = session;
635 int nghttp2_session_server_new(nghttp2_session **session_ptr,
636 const nghttp2_session_callbacks *callbacks,
638 return nghttp2_session_server_new3(session_ptr, callbacks, user_data, NULL,
642 int nghttp2_session_server_new2(nghttp2_session **session_ptr,
643 const nghttp2_session_callbacks *callbacks,
644 void *user_data, const nghttp2_option *option) {
645 return nghttp2_session_server_new3(session_ptr, callbacks, user_data, option,
649 int nghttp2_session_server_new3(nghttp2_session **session_ptr,
650 const nghttp2_session_callbacks *callbacks,
651 void *user_data, const nghttp2_option *option,
654 nghttp2_session *session;
656 rv = session_new(&session, callbacks, user_data, 1, option, mem);
661 /* IDs for use in client */
662 session->next_stream_id = 2;
664 *session_ptr = session;
669 static int free_streams(nghttp2_map_entry *entry, void *ptr) {
670 nghttp2_session *session;
671 nghttp2_stream *stream;
672 nghttp2_outbound_item *item;
675 session = (nghttp2_session *)ptr;
677 stream = (nghttp2_stream *)entry;
680 if (item && !item->queued && item != session->aob.item) {
681 nghttp2_outbound_item_free(item, mem);
682 nghttp2_mem_free(mem, item);
685 nghttp2_stream_free(stream);
686 nghttp2_mem_free(mem, stream);
691 static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) {
692 nghttp2_outbound_item *item, *next;
693 for (item = q->head; item;) {
695 nghttp2_outbound_item_free(item, mem);
696 nghttp2_mem_free(mem, item);
701 static int inflight_settings_new(nghttp2_inflight_settings **settings_ptr,
702 const nghttp2_settings_entry *iv, size_t niv,
704 *settings_ptr = nghttp2_mem_malloc(mem, sizeof(nghttp2_inflight_settings));
705 if (!*settings_ptr) {
706 return NGHTTP2_ERR_NOMEM;
710 (*settings_ptr)->iv = nghttp2_frame_iv_copy(iv, niv, mem);
711 if (!(*settings_ptr)->iv) {
712 nghttp2_mem_free(mem, *settings_ptr);
713 return NGHTTP2_ERR_NOMEM;
716 (*settings_ptr)->iv = NULL;
719 (*settings_ptr)->niv = niv;
720 (*settings_ptr)->next = NULL;
725 static void inflight_settings_del(nghttp2_inflight_settings *settings,
731 nghttp2_mem_free(mem, settings->iv);
732 nghttp2_mem_free(mem, settings);
735 void nghttp2_session_del(nghttp2_session *session) {
737 nghttp2_inflight_settings *settings;
739 if (session == NULL) {
745 for (settings = session->inflight_settings_head; settings;) {
746 nghttp2_inflight_settings *next = settings->next;
747 inflight_settings_del(settings, mem);
751 nghttp2_stream_free(&session->root);
753 /* Have to free streams first, so that we can check
754 stream->item->queued */
755 nghttp2_map_each_free(&session->streams, free_streams, session);
756 nghttp2_map_free(&session->streams);
758 ob_q_free(&session->ob_urgent, mem);
759 ob_q_free(&session->ob_reg, mem);
760 ob_q_free(&session->ob_syn, mem);
762 active_outbound_item_reset(&session->aob, mem);
763 session_inbound_frame_reset(session);
764 nghttp2_hd_deflate_free(&session->hd_deflater);
765 nghttp2_hd_inflate_free(&session->hd_inflater);
766 nghttp2_bufs_free(&session->aob.framebufs);
767 nghttp2_mem_free(mem, session);
770 int nghttp2_session_reprioritize_stream(
771 nghttp2_session *session, nghttp2_stream *stream,
772 const nghttp2_priority_spec *pri_spec_in) {
774 nghttp2_stream *dep_stream = NULL;
775 nghttp2_priority_spec pri_spec_default;
776 const nghttp2_priority_spec *pri_spec = pri_spec_in;
778 assert(pri_spec->stream_id != stream->stream_id);
780 if (!nghttp2_stream_in_dep_tree(stream)) {
784 if (pri_spec->stream_id != 0) {
785 dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id);
788 session_detect_idle_stream(session, pri_spec->stream_id)) {
790 nghttp2_priority_spec_default_init(&pri_spec_default);
792 dep_stream = nghttp2_session_open_stream(
793 session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default,
794 NGHTTP2_STREAM_IDLE, NULL);
796 if (dep_stream == NULL) {
797 return NGHTTP2_ERR_NOMEM;
799 } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) {
800 nghttp2_priority_spec_default_init(&pri_spec_default);
801 pri_spec = &pri_spec_default;
805 if (pri_spec->stream_id == 0) {
806 dep_stream = &session->root;
807 } else if (nghttp2_stream_dep_find_ancestor(dep_stream, stream)) {
808 DEBUGF("stream: cycle detected, dep_stream(%p)=%d stream(%p)=%d\n",
809 dep_stream, dep_stream->stream_id, stream, stream->stream_id);
811 nghttp2_stream_dep_remove_subtree(dep_stream);
812 rv = nghttp2_stream_dep_add_subtree(stream->dep_prev, dep_stream);
820 if (dep_stream == stream->dep_prev && !pri_spec->exclusive) {
821 /* This is minor optimization when just weight is changed. */
822 nghttp2_stream_change_weight(stream, pri_spec->weight);
827 nghttp2_stream_dep_remove_subtree(stream);
829 /* We have to update weight after removing stream from tree */
830 stream->weight = pri_spec->weight;
832 if (pri_spec->exclusive) {
833 rv = nghttp2_stream_dep_insert_subtree(dep_stream, stream);
835 rv = nghttp2_stream_dep_add_subtree(dep_stream, stream);
845 int nghttp2_session_add_item(nghttp2_session *session,
846 nghttp2_outbound_item *item) {
847 /* TODO Return error if stream is not found for the frame requiring
850 nghttp2_stream *stream;
851 nghttp2_frame *frame;
853 frame = &item->frame;
854 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
856 switch (frame->hd.type) {
859 return NGHTTP2_ERR_STREAM_CLOSED;
863 return NGHTTP2_ERR_DATA_EXIST;
866 rv = nghttp2_stream_attach_item(stream, item);
873 case NGHTTP2_HEADERS:
874 /* We push request HEADERS and push response HEADERS to
875 dedicated queue because their transmission is affected by
876 SETTINGS_MAX_CONCURRENT_STREAMS */
877 /* TODO If 2 HEADERS are submitted for reserved stream, then
878 both of them are queued into ob_syn, which is not
880 if (frame->headers.cat == NGHTTP2_HCAT_REQUEST ||
881 (stream && stream->state == NGHTTP2_STREAM_RESERVED)) {
882 nghttp2_outbound_queue_push(&session->ob_syn, item);
888 nghttp2_outbound_queue_push(&session->ob_reg, item);
891 case NGHTTP2_SETTINGS:
893 nghttp2_outbound_queue_push(&session->ob_urgent, item);
896 case NGHTTP2_RST_STREAM:
898 stream->state = NGHTTP2_STREAM_CLOSING;
900 nghttp2_outbound_queue_push(&session->ob_reg, item);
903 case NGHTTP2_PUSH_PROMISE: {
904 nghttp2_headers_aux_data *aux_data;
905 nghttp2_priority_spec pri_spec;
907 aux_data = &item->aux_data.headers;
910 return NGHTTP2_ERR_STREAM_CLOSED;
913 nghttp2_priority_spec_init(&pri_spec, stream->stream_id,
914 NGHTTP2_DEFAULT_WEIGHT, 0);
916 if (!nghttp2_session_open_stream(
917 session, frame->push_promise.promised_stream_id,
918 NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_RESERVED,
919 aux_data->stream_user_data)) {
920 return NGHTTP2_ERR_NOMEM;
923 /* We don't have to call nghttp2_session_adjust_closed_stream()
924 here, since stream->stream_id is local stream_id, and it does
925 not affect closed stream count. */
927 nghttp2_outbound_queue_push(&session->ob_reg, item);
932 case NGHTTP2_WINDOW_UPDATE:
934 stream->window_update_queued = 1;
935 } else if (frame->hd.stream_id == 0) {
936 session->window_update_queued = 1;
938 nghttp2_outbound_queue_push(&session->ob_reg, item);
942 nghttp2_outbound_queue_push(&session->ob_reg, item);
948 int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
949 uint32_t error_code) {
951 nghttp2_outbound_item *item;
952 nghttp2_frame *frame;
953 nghttp2_stream *stream;
957 stream = nghttp2_session_get_stream(session, stream_id);
958 if (stream && stream->state == NGHTTP2_STREAM_CLOSING) {
962 /* Cancel pending request HEADERS in ob_syn if this RST_STREAM
963 refers to that stream. */
964 if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) &&
965 nghttp2_outbound_queue_top(&session->ob_syn)) {
966 nghttp2_headers_aux_data *aux_data;
967 nghttp2_frame *headers_frame;
969 headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame;
970 assert(headers_frame->hd.type == NGHTTP2_HEADERS);
972 if (headers_frame->hd.stream_id <= stream_id &&
973 (uint32_t)stream_id < session->next_stream_id) {
975 for (item = session->ob_syn.head; item; item = item->qnext) {
976 aux_data = &item->aux_data.headers;
978 if (item->frame.hd.stream_id < stream_id) {
982 /* stream_id in ob_syn queue must be strictly increasing. If
983 we found larger ID, then we can break here. */
984 if (item->frame.hd.stream_id > stream_id || aux_data->canceled) {
988 aux_data->error_code = error_code;
989 aux_data->canceled = 1;
996 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
998 return NGHTTP2_ERR_NOMEM;
1001 nghttp2_outbound_item_init(item);
1003 frame = &item->frame;
1005 nghttp2_frame_rst_stream_init(&frame->rst_stream, stream_id, error_code);
1006 rv = nghttp2_session_add_item(session, item);
1008 nghttp2_frame_rst_stream_free(&frame->rst_stream);
1009 nghttp2_mem_free(mem, item);
1015 nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
1016 int32_t stream_id, uint8_t flags,
1017 nghttp2_priority_spec *pri_spec_in,
1018 nghttp2_stream_state initial_state,
1019 void *stream_user_data) {
1021 nghttp2_stream *stream;
1022 nghttp2_stream *dep_stream = NULL;
1023 int stream_alloc = 0;
1024 nghttp2_priority_spec pri_spec_default;
1025 nghttp2_priority_spec *pri_spec = pri_spec_in;
1028 mem = &session->mem;
1029 stream = nghttp2_session_get_stream_raw(session, stream_id);
1032 assert(stream->state == NGHTTP2_STREAM_IDLE);
1033 assert(nghttp2_stream_in_dep_tree(stream));
1034 nghttp2_session_detach_idle_stream(session, stream);
1035 rv = nghttp2_stream_dep_remove(stream);
1040 stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream));
1041 if (stream == NULL) {
1048 if (pri_spec->stream_id != 0) {
1049 dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id);
1052 session_detect_idle_stream(session, pri_spec->stream_id)) {
1053 /* Depends on idle stream, which does not exist in memory.
1054 Assign default priority for it. */
1055 nghttp2_priority_spec_default_init(&pri_spec_default);
1057 dep_stream = nghttp2_session_open_stream(
1058 session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default,
1059 NGHTTP2_STREAM_IDLE, NULL);
1061 if (dep_stream == NULL) {
1063 nghttp2_mem_free(mem, stream);
1068 } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) {
1069 /* If dep_stream is not part of dependency tree, stream will get
1070 default priority. This handles the case when
1071 pri_spec->stream_id == stream_id. This happens because we
1072 don't check pri_spec->stream_id against new stream ID in
1073 nghttp2_submit_request. This also handles the case when idle
1074 stream created by PRIORITY frame was opened. Somehow we
1075 first remove the idle stream from dependency tree. This is
1076 done to simplify code base, but ideally we should retain old
1077 dependency. But I'm not sure this adds values. */
1078 nghttp2_priority_spec_default_init(&pri_spec_default);
1079 pri_spec = &pri_spec_default;
1083 if (initial_state == NGHTTP2_STREAM_RESERVED) {
1084 flags |= NGHTTP2_STREAM_FLAG_PUSH;
1088 nghttp2_stream_init(stream, stream_id, flags, initial_state,
1090 (int32_t)session->remote_settings.initial_window_size,
1091 (int32_t)session->local_settings.initial_window_size,
1092 stream_user_data, mem);
1094 rv = nghttp2_map_insert(&session->streams, &stream->map_entry);
1096 nghttp2_stream_free(stream);
1097 nghttp2_mem_free(mem, stream);
1101 stream->flags = flags;
1102 stream->state = initial_state;
1103 stream->weight = pri_spec->weight;
1104 stream->stream_user_data = stream_user_data;
1107 switch (initial_state) {
1108 case NGHTTP2_STREAM_RESERVED:
1109 if (nghttp2_session_is_my_stream_id(session, stream_id)) {
1110 /* reserved (local) */
1111 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
1113 /* reserved (remote) */
1114 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
1115 ++session->num_incoming_reserved_streams;
1117 /* Reserved stream does not count in the concurrent streams
1118 limit. That is one of the DOS vector. */
1120 case NGHTTP2_STREAM_IDLE:
1121 /* Idle stream does not count toward the concurrent streams limit.
1122 This is used as anchor node in dependency tree. */
1123 nghttp2_session_keep_idle_stream(session, stream);
1126 if (nghttp2_session_is_my_stream_id(session, stream_id)) {
1127 ++session->num_outgoing_streams;
1129 ++session->num_incoming_streams;
1133 if (pri_spec->stream_id == 0) {
1134 dep_stream = &session->root;
1139 if (pri_spec->exclusive) {
1140 rv = nghttp2_stream_dep_insert(dep_stream, stream);
1145 nghttp2_stream_dep_add(dep_stream, stream);
1151 int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
1152 uint32_t error_code) {
1154 nghttp2_stream *stream;
1156 int is_my_stream_id;
1158 mem = &session->mem;
1159 stream = nghttp2_session_get_stream(session, stream_id);
1162 return NGHTTP2_ERR_INVALID_ARGUMENT;
1165 DEBUGF("stream: stream(%p)=%d close\n", stream, stream->stream_id);
1168 nghttp2_outbound_item *item;
1170 item = stream->item;
1172 rv = nghttp2_stream_detach_item(stream);
1178 /* If item is queued, it will be deleted when it is popped
1179 (nghttp2_session_prep_frame() will fail). If session->aob.item
1180 points to this item, let active_outbound_item_reset()
1182 if (!item->queued && item != session->aob.item) {
1183 nghttp2_outbound_item_free(item, mem);
1184 nghttp2_mem_free(mem, item);
1188 /* We call on_stream_close_callback even if stream->state is
1189 NGHTTP2_STREAM_INITIAL. This will happen while sending request
1190 HEADERS, a local endpoint receives RST_STREAM for that stream. It
1191 may be PROTOCOL_ERROR, but without notifying stream closure will
1192 hang the stream in a local endpoint.
1195 if (session->callbacks.on_stream_close_callback) {
1196 if (session->callbacks.on_stream_close_callback(
1197 session, stream_id, error_code, session->user_data) != 0) {
1199 return NGHTTP2_ERR_CALLBACK_FAILURE;
1203 is_my_stream_id = nghttp2_session_is_my_stream_id(session, stream_id);
1205 /* pushed streams which is not opened yet is not counted toward max
1206 concurrent limits */
1207 if ((stream->flags & NGHTTP2_STREAM_FLAG_PUSH)) {
1208 if (!is_my_stream_id) {
1209 --session->num_incoming_reserved_streams;
1212 if (is_my_stream_id) {
1213 --session->num_outgoing_streams;
1215 --session->num_incoming_streams;
1219 /* Closes both directions just in case they are not closed yet */
1220 stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED;
1222 if ((session->opt_flags & NGHTTP2_OPTMASK_NO_CLOSED_STREAMS) == 0 &&
1223 session->server && !is_my_stream_id &&
1224 nghttp2_stream_in_dep_tree(stream)) {
1225 /* On server side, retain stream at most MAX_CONCURRENT_STREAMS
1226 combined with the current active incoming streams to make
1227 dependency tree work better. */
1228 nghttp2_session_keep_closed_stream(session, stream);
1230 rv = nghttp2_session_destroy_stream(session, stream);
1239 int nghttp2_session_destroy_stream(nghttp2_session *session,
1240 nghttp2_stream *stream) {
1244 DEBUGF("stream: destroy closed stream(%p)=%d\n", stream, stream->stream_id);
1246 mem = &session->mem;
1248 if (nghttp2_stream_in_dep_tree(stream)) {
1249 rv = nghttp2_stream_dep_remove(stream);
1255 nghttp2_map_remove(&session->streams, stream->stream_id);
1256 nghttp2_stream_free(stream);
1257 nghttp2_mem_free(mem, stream);
1262 void nghttp2_session_keep_closed_stream(nghttp2_session *session,
1263 nghttp2_stream *stream) {
1264 DEBUGF("stream: keep closed stream(%p)=%d, state=%d\n", stream,
1265 stream->stream_id, stream->state);
1267 if (session->closed_stream_tail) {
1268 session->closed_stream_tail->closed_next = stream;
1269 stream->closed_prev = session->closed_stream_tail;
1271 session->closed_stream_head = stream;
1273 session->closed_stream_tail = stream;
1275 ++session->num_closed_streams;
1278 void nghttp2_session_keep_idle_stream(nghttp2_session *session,
1279 nghttp2_stream *stream) {
1280 DEBUGF("stream: keep idle stream(%p)=%d, state=%d\n", stream,
1281 stream->stream_id, stream->state);
1283 if (session->idle_stream_tail) {
1284 session->idle_stream_tail->closed_next = stream;
1285 stream->closed_prev = session->idle_stream_tail;
1287 session->idle_stream_head = stream;
1289 session->idle_stream_tail = stream;
1291 ++session->num_idle_streams;
1294 void nghttp2_session_detach_idle_stream(nghttp2_session *session,
1295 nghttp2_stream *stream) {
1296 nghttp2_stream *prev_stream, *next_stream;
1298 DEBUGF("stream: detach idle stream(%p)=%d, state=%d\n", stream,
1299 stream->stream_id, stream->state);
1301 prev_stream = stream->closed_prev;
1302 next_stream = stream->closed_next;
1305 prev_stream->closed_next = next_stream;
1307 session->idle_stream_head = next_stream;
1311 next_stream->closed_prev = prev_stream;
1313 session->idle_stream_tail = prev_stream;
1316 stream->closed_prev = NULL;
1317 stream->closed_next = NULL;
1319 --session->num_idle_streams;
1322 int nghttp2_session_adjust_closed_stream(nghttp2_session *session) {
1323 size_t num_stream_max;
1326 if (session->local_settings.max_concurrent_streams ==
1327 NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS) {
1328 num_stream_max = session->pending_local_max_concurrent_stream;
1330 num_stream_max = session->local_settings.max_concurrent_streams;
1333 DEBUGF("stream: adjusting kept closed streams num_closed_streams=%zu, "
1334 "num_incoming_streams=%zu, max_concurrent_streams=%zu\n",
1335 session->num_closed_streams, session->num_incoming_streams,
1338 while (session->num_closed_streams > 0 &&
1339 session->num_closed_streams + session->num_incoming_streams >
1341 nghttp2_stream *head_stream;
1342 nghttp2_stream *next;
1344 head_stream = session->closed_stream_head;
1346 assert(head_stream);
1348 next = head_stream->closed_next;
1350 rv = nghttp2_session_destroy_stream(session, head_stream);
1355 /* head_stream is now freed */
1357 session->closed_stream_head = next;
1359 if (session->closed_stream_head) {
1360 session->closed_stream_head->closed_prev = NULL;
1362 session->closed_stream_tail = NULL;
1365 --session->num_closed_streams;
1371 int nghttp2_session_adjust_idle_stream(nghttp2_session *session) {
1375 /* Make minimum number of idle streams 16, and maximum 100, which
1376 are arbitrary chosen numbers. */
1379 16, nghttp2_min(session->local_settings.max_concurrent_streams,
1380 session->pending_local_max_concurrent_stream)));
1382 DEBUGF("stream: adjusting kept idle streams num_idle_streams=%zu, max=%zu\n",
1383 session->num_idle_streams, max);
1385 while (session->num_idle_streams > max) {
1386 nghttp2_stream *head;
1387 nghttp2_stream *next;
1389 head = session->idle_stream_head;
1392 next = head->closed_next;
1394 rv = nghttp2_session_destroy_stream(session, head);
1399 /* head is now destroyed */
1401 session->idle_stream_head = next;
1403 if (session->idle_stream_head) {
1404 session->idle_stream_head->closed_prev = NULL;
1406 session->idle_stream_tail = NULL;
1409 --session->num_idle_streams;
1416 * Closes stream with stream ID |stream_id| if both transmission and
1417 * reception of the stream were disallowed. The |error_code| indicates
1418 * the reason of the closure.
1420 * This function returns 0 if it succeeds, or one of the following
1421 * negative error codes:
1423 * NGHTTP2_ERR_INVALID_ARGUMENT
1424 * The stream is not found.
1425 * NGHTTP2_ERR_CALLBACK_FAILURE
1426 * The callback function failed.
1428 int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session,
1429 nghttp2_stream *stream) {
1430 if ((stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR) {
1431 return nghttp2_session_close_stream(session, stream->stream_id,
1438 * Returns nonzero if local endpoint allows reception of new stream
1441 static int session_allow_incoming_new_stream(nghttp2_session *session) {
1442 return (session->goaway_flags &
1443 (NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_SENT)) == 0;
1447 * This function returns nonzero if session is closing.
1449 static int session_is_closing(nghttp2_session *session) {
1450 return (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) != 0 ||
1451 (nghttp2_session_want_read(session) == 0 &&
1452 nghttp2_session_want_write(session) == 0);
1456 * Check that we can send a frame to the |stream|. This function
1457 * returns 0 if we can send a frame to the |frame|, or one of the
1458 * following negative error codes:
1460 * NGHTTP2_ERR_STREAM_CLOSED
1461 * The stream is already closed.
1462 * NGHTTP2_ERR_STREAM_SHUT_WR
1463 * The stream is half-closed for transmission.
1464 * NGHTTP2_ERR_SESSION_CLOSING
1465 * This session is closing.
1467 static int session_predicate_for_stream_send(nghttp2_session *session,
1468 nghttp2_stream *stream) {
1469 if (stream == NULL) {
1470 return NGHTTP2_ERR_STREAM_CLOSED;
1472 if (session_is_closing(session)) {
1473 return NGHTTP2_ERR_SESSION_CLOSING;
1475 if (stream->shut_flags & NGHTTP2_SHUT_WR) {
1476 return NGHTTP2_ERR_STREAM_SHUT_WR;
1481 int nghttp2_session_check_request_allowed(nghttp2_session *session) {
1482 return !session->server && session->next_stream_id <= INT32_MAX &&
1483 (session->goaway_flags & NGHTTP2_GOAWAY_RECV) == 0 &&
1484 !session_is_closing(session);
1488 * This function checks request HEADERS frame, which opens stream, can
1489 * be sent at this time.
1491 * This function returns 0 if it succeeds, or one of the following
1492 * negative error codes:
1494 * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED
1495 * New stream cannot be created because of GOAWAY: session is
1496 * going down or received last_stream_id is strictly less than
1497 * frame->hd.stream_id.
1498 * NGHTTP2_ERR_STREAM_CLOSING
1499 * request HEADERS was canceled by RST_STREAM while it is in queue.
1501 static int session_predicate_request_headers_send(nghttp2_session *session,
1502 nghttp2_outbound_item *item) {
1503 if (item->aux_data.headers.canceled) {
1504 return NGHTTP2_ERR_STREAM_CLOSING;
1506 /* If we are terminating session (NGHTTP2_GOAWAY_TERM_ON_SEND),
1507 GOAWAY was received from peer, or session is about to close, new
1508 request is not allowed. */
1509 if ((session->goaway_flags & NGHTTP2_GOAWAY_RECV) ||
1510 session_is_closing(session)) {
1511 return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
1517 * This function checks HEADERS, which is the first frame from the
1518 * server, with the |stream| can be sent at this time. The |stream|
1521 * This function returns 0 if it succeeds, or one of the following
1522 * negative error codes:
1524 * NGHTTP2_ERR_STREAM_CLOSED
1525 * The stream is already closed or does not exist.
1526 * NGHTTP2_ERR_STREAM_SHUT_WR
1527 * The transmission is not allowed for this stream (e.g., a frame
1528 * with END_STREAM flag set has already sent)
1529 * NGHTTP2_ERR_INVALID_STREAM_ID
1530 * The stream ID is invalid.
1531 * NGHTTP2_ERR_STREAM_CLOSING
1532 * RST_STREAM was queued for this stream.
1533 * NGHTTP2_ERR_INVALID_STREAM_STATE
1534 * The state of the stream is not valid.
1535 * NGHTTP2_ERR_SESSION_CLOSING
1536 * This session is closing.
1538 * Client side attempted to send response.
1540 static int session_predicate_response_headers_send(nghttp2_session *session,
1541 nghttp2_stream *stream) {
1543 rv = session_predicate_for_stream_send(session, stream);
1548 if (!session->server) {
1549 return NGHTTP2_ERR_PROTO;
1551 if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
1552 return NGHTTP2_ERR_INVALID_STREAM_ID;
1554 switch (stream->state) {
1555 case NGHTTP2_STREAM_OPENING:
1557 case NGHTTP2_STREAM_CLOSING:
1558 return NGHTTP2_ERR_STREAM_CLOSING;
1560 return NGHTTP2_ERR_INVALID_STREAM_STATE;
1565 * This function checks HEADERS for reserved stream can be sent. The
1566 * |stream| must be reserved state and the |session| is server side.
1567 * The |stream| can be NULL.
1569 * This function returns 0 if it succeeds, or one of the following
1572 * NGHTTP2_ERR_STREAM_CLOSED
1573 * The stream is already closed.
1574 * NGHTTP2_ERR_STREAM_SHUT_WR
1575 * The stream is half-closed for transmission.
1577 * The stream is not reserved state
1578 * NGHTTP2_ERR_STREAM_CLOSED
1579 * RST_STREAM was queued for this stream.
1580 * NGHTTP2_ERR_SESSION_CLOSING
1581 * This session is closing.
1582 * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED
1583 * New stream cannot be created because GOAWAY is already sent or
1586 * Client side attempted to send push response.
1589 session_predicate_push_response_headers_send(nghttp2_session *session,
1590 nghttp2_stream *stream) {
1592 /* TODO Should disallow HEADERS if GOAWAY has already been issued? */
1593 rv = session_predicate_for_stream_send(session, stream);
1598 if (!session->server) {
1599 return NGHTTP2_ERR_PROTO;
1601 if (stream->state != NGHTTP2_STREAM_RESERVED) {
1602 return NGHTTP2_ERR_PROTO;
1604 if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) {
1605 return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
1611 * This function checks HEADERS, which is neither stream-opening nor
1612 * first response header, with the |stream| can be sent at this time.
1613 * The |stream| can be NULL.
1615 * This function returns 0 if it succeeds, or one of the following
1616 * negative error codes:
1618 * NGHTTP2_ERR_STREAM_CLOSED
1619 * The stream is already closed or does not exist.
1620 * NGHTTP2_ERR_STREAM_SHUT_WR
1621 * The transmission is not allowed for this stream (e.g., a frame
1622 * with END_STREAM flag set has already sent)
1623 * NGHTTP2_ERR_STREAM_CLOSING
1624 * RST_STREAM was queued for this stream.
1625 * NGHTTP2_ERR_INVALID_STREAM_STATE
1626 * The state of the stream is not valid.
1627 * NGHTTP2_ERR_SESSION_CLOSING
1628 * This session is closing.
1630 static int session_predicate_headers_send(nghttp2_session *session,
1631 nghttp2_stream *stream) {
1633 rv = session_predicate_for_stream_send(session, stream);
1639 switch (stream->state) {
1640 case NGHTTP2_STREAM_OPENED:
1642 case NGHTTP2_STREAM_CLOSING:
1643 return NGHTTP2_ERR_STREAM_CLOSING;
1645 if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
1648 return NGHTTP2_ERR_INVALID_STREAM_STATE;
1653 * This function checks PUSH_PROMISE frame |frame| with the |stream|
1654 * can be sent at this time. The |stream| can be NULL.
1656 * This function returns 0 if it succeeds, or one of the following
1657 * negative error codes:
1659 * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED
1660 * New stream cannot be created because GOAWAY is already sent or
1663 * The client side attempts to send PUSH_PROMISE, or the server
1664 * sends PUSH_PROMISE for the stream not initiated by the client.
1665 * NGHTTP2_ERR_STREAM_CLOSED
1666 * The stream is already closed or does not exist.
1667 * NGHTTP2_ERR_STREAM_CLOSING
1668 * RST_STREAM was queued for this stream.
1669 * NGHTTP2_ERR_STREAM_SHUT_WR
1670 * The transmission is not allowed for this stream (e.g., a frame
1671 * with END_STREAM flag set has already sent)
1672 * NGHTTP2_ERR_PUSH_DISABLED
1673 * The remote peer disabled reception of PUSH_PROMISE.
1674 * NGHTTP2_ERR_SESSION_CLOSING
1675 * This session is closing.
1677 static int session_predicate_push_promise_send(nghttp2_session *session,
1678 nghttp2_stream *stream) {
1681 if (!session->server) {
1682 return NGHTTP2_ERR_PROTO;
1685 rv = session_predicate_for_stream_send(session, stream);
1692 if (session->remote_settings.enable_push == 0) {
1693 return NGHTTP2_ERR_PUSH_DISABLED;
1695 if (stream->state == NGHTTP2_STREAM_CLOSING) {
1696 return NGHTTP2_ERR_STREAM_CLOSING;
1698 if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) {
1699 return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
1705 * This function checks WINDOW_UPDATE with the stream ID |stream_id|
1706 * can be sent at this time. Note that END_STREAM flag of the previous
1707 * frame does not affect the transmission of the WINDOW_UPDATE frame.
1709 * This function returns 0 if it succeeds, or one of the following
1710 * negative error codes:
1712 * NGHTTP2_ERR_STREAM_CLOSED
1713 * The stream is already closed or does not exist.
1714 * NGHTTP2_ERR_STREAM_CLOSING
1715 * RST_STREAM was queued for this stream.
1716 * NGHTTP2_ERR_INVALID_STREAM_STATE
1717 * The state of the stream is not valid.
1718 * NGHTTP2_ERR_SESSION_CLOSING
1719 * This session is closing.
1721 static int session_predicate_window_update_send(nghttp2_session *session,
1722 int32_t stream_id) {
1723 nghttp2_stream *stream;
1725 if (session_is_closing(session)) {
1726 return NGHTTP2_ERR_SESSION_CLOSING;
1729 if (stream_id == 0) {
1730 /* Connection-level window update */
1733 stream = nghttp2_session_get_stream(session, stream_id);
1734 if (stream == NULL) {
1735 return NGHTTP2_ERR_STREAM_CLOSED;
1737 if (stream->state == NGHTTP2_STREAM_CLOSING) {
1738 return NGHTTP2_ERR_STREAM_CLOSING;
1740 if (state_reserved_local(session, stream)) {
1741 return NGHTTP2_ERR_INVALID_STREAM_STATE;
1746 static int session_predicate_altsvc_send(nghttp2_session *session,
1747 int32_t stream_id) {
1748 nghttp2_stream *stream;
1750 if (session_is_closing(session)) {
1751 return NGHTTP2_ERR_SESSION_CLOSING;
1754 if (stream_id == 0) {
1758 stream = nghttp2_session_get_stream(session, stream_id);
1759 if (stream == NULL) {
1760 return NGHTTP2_ERR_STREAM_CLOSED;
1762 if (stream->state == NGHTTP2_STREAM_CLOSING) {
1763 return NGHTTP2_ERR_STREAM_CLOSING;
1769 static int session_predicate_origin_send(nghttp2_session *session) {
1770 if (session_is_closing(session)) {
1771 return NGHTTP2_ERR_SESSION_CLOSING;
1776 /* Take into account settings max frame size and both connection-level
1777 flow control here */
1779 nghttp2_session_enforce_flow_control_limits(nghttp2_session *session,
1780 nghttp2_stream *stream,
1781 ssize_t requested_window_size) {
1782 DEBUGF("send: remote windowsize connection=%d, remote maxframsize=%u, "
1783 "stream(id %d)=%d\n",
1784 session->remote_window_size, session->remote_settings.max_frame_size,
1785 stream->stream_id, stream->remote_window_size);
1787 return nghttp2_min(nghttp2_min(nghttp2_min(requested_window_size,
1788 stream->remote_window_size),
1789 session->remote_window_size),
1790 (int32_t)session->remote_settings.max_frame_size);
1794 * Returns the maximum length of next data read. If the
1795 * connection-level and/or stream-wise flow control are enabled, the
1796 * return value takes into account those current window sizes. The remote
1797 * settings for max frame size is also taken into account.
1799 static size_t nghttp2_session_next_data_read(nghttp2_session *session,
1800 nghttp2_stream *stream) {
1801 ssize_t window_size;
1803 window_size = nghttp2_session_enforce_flow_control_limits(
1804 session, stream, NGHTTP2_DATA_PAYLOADLEN);
1806 DEBUGF("send: available window=%zd\n", window_size);
1808 return window_size > 0 ? (size_t)window_size : 0;
1812 * This function checks DATA with the |stream| can be sent at this
1813 * time. The |stream| can be NULL.
1815 * This function returns 0 if it succeeds, or one of the following
1816 * negative error codes:
1818 * NGHTTP2_ERR_STREAM_CLOSED
1819 * The stream is already closed or does not exist.
1820 * NGHTTP2_ERR_STREAM_SHUT_WR
1821 * The transmission is not allowed for this stream (e.g., a frame
1822 * with END_STREAM flag set has already sent)
1823 * NGHTTP2_ERR_STREAM_CLOSING
1824 * RST_STREAM was queued for this stream.
1825 * NGHTTP2_ERR_INVALID_STREAM_STATE
1826 * The state of the stream is not valid.
1827 * NGHTTP2_ERR_SESSION_CLOSING
1828 * This session is closing.
1830 static int nghttp2_session_predicate_data_send(nghttp2_session *session,
1831 nghttp2_stream *stream) {
1833 rv = session_predicate_for_stream_send(session, stream);
1838 if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
1839 /* Request body data */
1840 /* If stream->state is NGHTTP2_STREAM_CLOSING, RST_STREAM was
1841 queued but not yet sent. In this case, we won't send DATA
1843 if (stream->state == NGHTTP2_STREAM_CLOSING) {
1844 return NGHTTP2_ERR_STREAM_CLOSING;
1846 if (stream->state == NGHTTP2_STREAM_RESERVED) {
1847 return NGHTTP2_ERR_INVALID_STREAM_STATE;
1851 /* Response body data */
1852 if (stream->state == NGHTTP2_STREAM_OPENED) {
1855 if (stream->state == NGHTTP2_STREAM_CLOSING) {
1856 return NGHTTP2_ERR_STREAM_CLOSING;
1858 return NGHTTP2_ERR_INVALID_STREAM_STATE;
1861 static ssize_t session_call_select_padding(nghttp2_session *session,
1862 const nghttp2_frame *frame,
1863 size_t max_payloadlen) {
1866 if (frame->hd.length >= max_payloadlen) {
1867 return (ssize_t)frame->hd.length;
1870 if (session->callbacks.select_padding_callback) {
1871 size_t max_paddedlen;
1874 nghttp2_min(frame->hd.length + NGHTTP2_MAX_PADLEN, max_payloadlen);
1876 rv = session->callbacks.select_padding_callback(
1877 session, frame, max_paddedlen, session->user_data);
1878 if (rv < (ssize_t)frame->hd.length || rv > (ssize_t)max_paddedlen) {
1879 return NGHTTP2_ERR_CALLBACK_FAILURE;
1883 return (ssize_t)frame->hd.length;
1886 /* Add padding to HEADERS or PUSH_PROMISE. We use
1887 frame->headers.padlen in this function to use the fact that
1888 frame->push_promise has also padlen in the same position. */
1889 static int session_headers_add_pad(nghttp2_session *session,
1890 nghttp2_frame *frame) {
1892 ssize_t padded_payloadlen;
1893 nghttp2_active_outbound_item *aob;
1894 nghttp2_bufs *framebufs;
1896 size_t max_payloadlen;
1898 aob = &session->aob;
1899 framebufs = &aob->framebufs;
1901 max_payloadlen = nghttp2_min(NGHTTP2_MAX_PAYLOADLEN,
1902 frame->hd.length + NGHTTP2_MAX_PADLEN);
1905 session_call_select_padding(session, frame, max_payloadlen);
1907 if (nghttp2_is_fatal((int)padded_payloadlen)) {
1908 return (int)padded_payloadlen;
1911 padlen = (size_t)padded_payloadlen - frame->hd.length;
1913 DEBUGF("send: padding selected: payloadlen=%zd, padlen=%zu\n",
1914 padded_payloadlen, padlen);
1916 rv = nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, 0);
1922 frame->headers.padlen = padlen;
1927 static size_t session_estimate_headers_payload(nghttp2_session *session,
1928 const nghttp2_nv *nva,
1930 size_t additional) {
1931 return nghttp2_hd_deflate_bound(&session->hd_deflater, nva, nvlen) +
1935 static int session_pack_extension(nghttp2_session *session, nghttp2_bufs *bufs,
1936 nghttp2_frame *frame) {
1942 assert(session->callbacks.pack_extension_callback);
1944 buf = &bufs->head->buf;
1945 buflen = nghttp2_min(nghttp2_buf_avail(buf), NGHTTP2_MAX_PAYLOADLEN);
1947 rv = session->callbacks.pack_extension_callback(session, buf->last, buflen,
1948 frame, session->user_data);
1949 if (rv == NGHTTP2_ERR_CANCEL) {
1953 if (rv < 0 || (size_t)rv > buflen) {
1954 return NGHTTP2_ERR_CALLBACK_FAILURE;
1957 framelen = (size_t)rv;
1959 frame->hd.length = framelen;
1961 assert(buf->pos == buf->last);
1962 buf->last += framelen;
1963 buf->pos -= NGHTTP2_FRAME_HDLEN;
1965 nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
1971 * This function serializes frame for transmission.
1973 * This function returns 0 if it succeeds, or one of negative error
1974 * codes, including both fatal and non-fatal ones.
1976 static int session_prep_frame(nghttp2_session *session,
1977 nghttp2_outbound_item *item) {
1979 nghttp2_frame *frame;
1982 mem = &session->mem;
1983 frame = &item->frame;
1985 switch (frame->hd.type) {
1986 case NGHTTP2_DATA: {
1987 size_t next_readmax;
1988 nghttp2_stream *stream;
1990 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
1993 assert(stream->item == item);
1996 rv = nghttp2_session_predicate_data_send(session, stream);
1998 // If stream was already closed, nghttp2_session_get_stream()
1999 // returns NULL, but item is still attached to the stream.
2000 // Search stream including closed again.
2001 stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);
2005 rv2 = nghttp2_stream_detach_item(stream);
2007 if (nghttp2_is_fatal(rv2)) {
2014 /* Assuming stream is not NULL */
2016 next_readmax = nghttp2_session_next_data_read(session, stream);
2018 if (next_readmax == 0) {
2020 /* This must be true since we only pop DATA frame item from
2021 queue when session->remote_window_size > 0 */
2022 assert(session->remote_window_size > 0);
2024 rv = nghttp2_stream_defer_item(stream,
2025 NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
2027 if (nghttp2_is_fatal(rv)) {
2031 session->aob.item = NULL;
2032 active_outbound_item_reset(&session->aob, mem);
2033 return NGHTTP2_ERR_DEFERRED;
2036 rv = nghttp2_session_pack_data(session, &session->aob.framebufs,
2037 next_readmax, frame, &item->aux_data.data,
2039 if (rv == NGHTTP2_ERR_PAUSE) {
2042 if (rv == NGHTTP2_ERR_DEFERRED) {
2043 rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER);
2045 if (nghttp2_is_fatal(rv)) {
2049 session->aob.item = NULL;
2050 active_outbound_item_reset(&session->aob, mem);
2051 return NGHTTP2_ERR_DEFERRED;
2053 if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
2054 rv = nghttp2_stream_detach_item(stream);
2056 if (nghttp2_is_fatal(rv)) {
2060 rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,
2061 NGHTTP2_INTERNAL_ERROR);
2062 if (nghttp2_is_fatal(rv)) {
2065 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
2070 rv2 = nghttp2_stream_detach_item(stream);
2072 if (nghttp2_is_fatal(rv2)) {
2080 case NGHTTP2_HEADERS: {
2081 nghttp2_headers_aux_data *aux_data;
2082 size_t estimated_payloadlen;
2084 aux_data = &item->aux_data.headers;
2086 if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
2087 /* initial HEADERS, which opens stream */
2088 nghttp2_stream *stream;
2090 stream = nghttp2_session_open_stream(
2091 session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE,
2092 &frame->headers.pri_spec, NGHTTP2_STREAM_INITIAL,
2093 aux_data->stream_user_data);
2095 if (stream == NULL) {
2096 return NGHTTP2_ERR_NOMEM;
2099 /* We don't call nghttp2_session_adjust_closed_stream() here,
2100 since we don't keep closed stream in client side */
2102 rv = session_predicate_request_headers_send(session, item);
2107 if (session_enforce_http_messaging(session)) {
2108 nghttp2_http_record_request_method(stream, frame);
2111 nghttp2_stream *stream;
2113 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2115 if (stream && stream->state == NGHTTP2_STREAM_RESERVED) {
2116 rv = session_predicate_push_response_headers_send(session, stream);
2118 frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE;
2120 if (aux_data->stream_user_data) {
2121 stream->stream_user_data = aux_data->stream_user_data;
2124 } else if (session_predicate_response_headers_send(session, stream) ==
2126 frame->headers.cat = NGHTTP2_HCAT_RESPONSE;
2129 frame->headers.cat = NGHTTP2_HCAT_HEADERS;
2131 rv = session_predicate_headers_send(session, stream);
2139 estimated_payloadlen = session_estimate_headers_payload(
2140 session, frame->headers.nva, frame->headers.nvlen,
2141 NGHTTP2_PRIORITY_SPECLEN);
2143 if (estimated_payloadlen > session->max_send_header_block_length) {
2144 return NGHTTP2_ERR_FRAME_SIZE_ERROR;
2147 rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers,
2148 &session->hd_deflater);
2154 DEBUGF("send: before padding, HEADERS serialized in %zd bytes\n",
2155 nghttp2_bufs_len(&session->aob.framebufs));
2157 rv = session_headers_add_pad(session, frame);
2163 DEBUGF("send: HEADERS finally serialized in %zd bytes\n",
2164 nghttp2_bufs_len(&session->aob.framebufs));
2166 if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
2167 assert(session->last_sent_stream_id < frame->hd.stream_id);
2168 session->last_sent_stream_id = frame->hd.stream_id;
2173 case NGHTTP2_PRIORITY: {
2174 if (session_is_closing(session)) {
2175 return NGHTTP2_ERR_SESSION_CLOSING;
2177 /* PRIORITY frame can be sent at any time and to any stream
2179 nghttp2_frame_pack_priority(&session->aob.framebufs, &frame->priority);
2181 /* Peer can send PRIORITY frame against idle stream to create
2182 "anchor" in dependency tree. Only client can do this in
2183 nghttp2. In nghttp2, only server retains non-active (closed
2184 or idle) streams in memory, so we don't open stream here. */
2187 case NGHTTP2_RST_STREAM:
2188 if (session_is_closing(session)) {
2189 return NGHTTP2_ERR_SESSION_CLOSING;
2191 nghttp2_frame_pack_rst_stream(&session->aob.framebufs, &frame->rst_stream);
2193 case NGHTTP2_SETTINGS: {
2194 if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
2195 assert(session->obq_flood_counter_ > 0);
2196 --session->obq_flood_counter_;
2197 /* When session is about to close, don't send SETTINGS ACK.
2198 We are required to send SETTINGS without ACK though; for
2199 example, we have to send SETTINGS as a part of connection
2201 if (session_is_closing(session)) {
2202 return NGHTTP2_ERR_SESSION_CLOSING;
2206 rv = nghttp2_frame_pack_settings(&session->aob.framebufs, &frame->settings);
2212 case NGHTTP2_PUSH_PROMISE: {
2213 nghttp2_stream *stream;
2214 size_t estimated_payloadlen;
2216 /* stream could be NULL if associated stream was already
2218 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2220 /* predicate should fail if stream is NULL. */
2221 rv = session_predicate_push_promise_send(session, stream);
2228 estimated_payloadlen = session_estimate_headers_payload(
2229 session, frame->push_promise.nva, frame->push_promise.nvlen, 0);
2231 if (estimated_payloadlen > session->max_send_header_block_length) {
2232 return NGHTTP2_ERR_FRAME_SIZE_ERROR;
2235 rv = nghttp2_frame_pack_push_promise(
2236 &session->aob.framebufs, &frame->push_promise, &session->hd_deflater);
2240 rv = session_headers_add_pad(session, frame);
2245 assert(session->last_sent_stream_id + 2 <=
2246 frame->push_promise.promised_stream_id);
2247 session->last_sent_stream_id = frame->push_promise.promised_stream_id;
2252 if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
2253 assert(session->obq_flood_counter_ > 0);
2254 --session->obq_flood_counter_;
2256 /* PING frame is allowed to be sent unless termination GOAWAY is
2258 if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
2259 return NGHTTP2_ERR_SESSION_CLOSING;
2261 nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping);
2263 case NGHTTP2_GOAWAY:
2264 rv = nghttp2_frame_pack_goaway(&session->aob.framebufs, &frame->goaway);
2268 session->local_last_stream_id = frame->goaway.last_stream_id;
2271 case NGHTTP2_WINDOW_UPDATE:
2272 rv = session_predicate_window_update_send(session, frame->hd.stream_id);
2276 nghttp2_frame_pack_window_update(&session->aob.framebufs,
2277 &frame->window_update);
2279 case NGHTTP2_CONTINUATION:
2280 /* We never handle CONTINUATION here. */
2284 nghttp2_ext_aux_data *aux_data;
2286 /* extension frame */
2288 aux_data = &item->aux_data.ext;
2290 if (aux_data->builtin == 0) {
2291 if (session_is_closing(session)) {
2292 return NGHTTP2_ERR_SESSION_CLOSING;
2295 return session_pack_extension(session, &session->aob.framebufs, frame);
2298 switch (frame->hd.type) {
2299 case NGHTTP2_ALTSVC:
2300 rv = session_predicate_altsvc_send(session, frame->hd.stream_id);
2305 nghttp2_frame_pack_altsvc(&session->aob.framebufs, &frame->ext);
2308 case NGHTTP2_ORIGIN:
2309 rv = session_predicate_origin_send(session);
2314 rv = nghttp2_frame_pack_origin(&session->aob.framebufs, &frame->ext);
2321 /* Unreachable here */
2329 nghttp2_outbound_item *
2330 nghttp2_session_get_next_ob_item(nghttp2_session *session) {
2331 if (nghttp2_outbound_queue_top(&session->ob_urgent)) {
2332 return nghttp2_outbound_queue_top(&session->ob_urgent);
2335 if (nghttp2_outbound_queue_top(&session->ob_reg)) {
2336 return nghttp2_outbound_queue_top(&session->ob_reg);
2339 if (!session_is_outgoing_concurrent_streams_max(session)) {
2340 if (nghttp2_outbound_queue_top(&session->ob_syn)) {
2341 return nghttp2_outbound_queue_top(&session->ob_syn);
2345 if (session->remote_window_size > 0) {
2346 return nghttp2_stream_next_outbound_item(&session->root);
2352 nghttp2_outbound_item *
2353 nghttp2_session_pop_next_ob_item(nghttp2_session *session) {
2354 nghttp2_outbound_item *item;
2356 item = nghttp2_outbound_queue_top(&session->ob_urgent);
2358 nghttp2_outbound_queue_pop(&session->ob_urgent);
2363 item = nghttp2_outbound_queue_top(&session->ob_reg);
2365 nghttp2_outbound_queue_pop(&session->ob_reg);
2370 if (!session_is_outgoing_concurrent_streams_max(session)) {
2371 item = nghttp2_outbound_queue_top(&session->ob_syn);
2373 nghttp2_outbound_queue_pop(&session->ob_syn);
2379 if (session->remote_window_size > 0) {
2380 return nghttp2_stream_next_outbound_item(&session->root);
2386 static int session_call_before_frame_send(nghttp2_session *session,
2387 nghttp2_frame *frame) {
2389 if (session->callbacks.before_frame_send_callback) {
2390 rv = session->callbacks.before_frame_send_callback(session, frame,
2391 session->user_data);
2392 if (rv == NGHTTP2_ERR_CANCEL) {
2397 return NGHTTP2_ERR_CALLBACK_FAILURE;
2403 static int session_call_on_frame_send(nghttp2_session *session,
2404 nghttp2_frame *frame) {
2406 if (session->callbacks.on_frame_send_callback) {
2407 rv = session->callbacks.on_frame_send_callback(session, frame,
2408 session->user_data);
2410 return NGHTTP2_ERR_CALLBACK_FAILURE;
2416 static int find_stream_on_goaway_func(nghttp2_map_entry *entry, void *ptr) {
2417 nghttp2_close_stream_on_goaway_arg *arg;
2418 nghttp2_stream *stream;
2420 arg = (nghttp2_close_stream_on_goaway_arg *)ptr;
2421 stream = (nghttp2_stream *)entry;
2423 if (nghttp2_session_is_my_stream_id(arg->session, stream->stream_id)) {
2424 if (arg->incoming) {
2427 } else if (!arg->incoming) {
2431 if (stream->state != NGHTTP2_STREAM_IDLE &&
2432 (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) == 0 &&
2433 stream->stream_id > arg->last_stream_id) {
2434 /* We are collecting streams to close because we cannot call
2435 nghttp2_session_close_stream() inside nghttp2_map_each().
2436 Reuse closed_next member.. bad choice? */
2437 assert(stream->closed_next == NULL);
2438 assert(stream->closed_prev == NULL);
2441 stream->closed_next = arg->head;
2451 /* Closes non-idle and non-closed streams whose stream ID >
2452 last_stream_id. If incoming is nonzero, we are going to close
2453 incoming streams. Otherwise, close outgoing streams. */
2454 static int session_close_stream_on_goaway(nghttp2_session *session,
2455 int32_t last_stream_id,
2458 nghttp2_stream *stream, *next_stream;
2459 nghttp2_close_stream_on_goaway_arg arg = {session, NULL, last_stream_id,
2462 rv = nghttp2_map_each(&session->streams, find_stream_on_goaway_func, &arg);
2467 next_stream = stream->closed_next;
2468 stream->closed_next = NULL;
2469 rv = nghttp2_session_close_stream(session, stream->stream_id,
2470 NGHTTP2_REFUSED_STREAM);
2472 /* stream may be deleted here */
2474 stream = next_stream;
2476 if (nghttp2_is_fatal(rv)) {
2477 /* Clean up closed_next member just in case */
2479 next_stream = stream->closed_next;
2480 stream->closed_next = NULL;
2481 stream = next_stream;
2490 static void reschedule_stream(nghttp2_stream *stream) {
2491 stream->last_writelen = stream->item->frame.hd.length;
2493 nghttp2_stream_reschedule(stream);
2496 static int session_update_stream_consumed_size(nghttp2_session *session,
2497 nghttp2_stream *stream,
2500 static int session_update_connection_consumed_size(nghttp2_session *session,
2504 * Called after a frame is sent. This function runs
2505 * on_frame_send_callback and handles stream closure upon END_STREAM
2506 * or RST_STREAM. This function does not reset session->aob. It is a
2507 * responsibility of session_after_frame_sent2.
2509 * This function returns 0 if it succeeds, or one of the following
2510 * negative error codes:
2514 * NGHTTP2_ERR_CALLBACK_FAILURE
2515 * The callback function failed.
2517 static int session_after_frame_sent1(nghttp2_session *session) {
2519 nghttp2_active_outbound_item *aob = &session->aob;
2520 nghttp2_outbound_item *item = aob->item;
2521 nghttp2_bufs *framebufs = &aob->framebufs;
2522 nghttp2_frame *frame;
2523 nghttp2_stream *stream;
2525 frame = &item->frame;
2527 if (frame->hd.type == NGHTTP2_DATA) {
2528 nghttp2_data_aux_data *aux_data;
2530 aux_data = &item->aux_data.data;
2532 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2533 /* We update flow control window after a frame was completely
2534 sent. This is possible because we choose payload length not to
2535 exceed the window */
2536 session->remote_window_size -= (int32_t)frame->hd.length;
2538 stream->remote_window_size -= (int32_t)frame->hd.length;
2541 if (stream && aux_data->eof) {
2542 rv = nghttp2_stream_detach_item(stream);
2543 if (nghttp2_is_fatal(rv)) {
2547 /* Call on_frame_send_callback after
2548 nghttp2_stream_detach_item(), so that application can issue
2549 nghttp2_submit_data() in the callback. */
2550 if (session->callbacks.on_frame_send_callback) {
2551 rv = session_call_on_frame_send(session, frame);
2552 if (nghttp2_is_fatal(rv)) {
2557 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
2561 (stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR;
2563 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
2565 rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
2566 if (nghttp2_is_fatal(rv)) {
2569 /* stream may be NULL if it was closed */
2570 if (stream_closed) {
2577 if (session->callbacks.on_frame_send_callback) {
2578 rv = session_call_on_frame_send(session, frame);
2579 if (nghttp2_is_fatal(rv)) {
2587 /* non-DATA frame */
2589 if (frame->hd.type == NGHTTP2_HEADERS ||
2590 frame->hd.type == NGHTTP2_PUSH_PROMISE) {
2591 if (nghttp2_bufs_next_present(framebufs)) {
2592 DEBUGF("send: CONTINUATION exists, just return\n");
2596 rv = session_call_on_frame_send(session, frame);
2597 if (nghttp2_is_fatal(rv)) {
2600 switch (frame->hd.type) {
2601 case NGHTTP2_HEADERS: {
2602 nghttp2_headers_aux_data *aux_data;
2604 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2609 switch (frame->headers.cat) {
2610 case NGHTTP2_HCAT_REQUEST: {
2611 stream->state = NGHTTP2_STREAM_OPENING;
2612 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
2613 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
2615 rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
2616 if (nghttp2_is_fatal(rv)) {
2619 /* We assume aux_data is a pointer to nghttp2_headers_aux_data */
2620 aux_data = &item->aux_data.headers;
2621 if (aux_data->data_prd.read_callback) {
2622 /* nghttp2_submit_data() makes a copy of aux_data->data_prd */
2623 rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM,
2624 frame->hd.stream_id, &aux_data->data_prd);
2625 if (nghttp2_is_fatal(rv)) {
2628 /* TODO nghttp2_submit_data() may fail if stream has already
2629 DATA frame item. We might have to handle it here. */
2633 case NGHTTP2_HCAT_PUSH_RESPONSE:
2634 stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH);
2635 ++session->num_outgoing_streams;
2637 case NGHTTP2_HCAT_RESPONSE:
2638 stream->state = NGHTTP2_STREAM_OPENED;
2640 case NGHTTP2_HCAT_HEADERS:
2641 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
2642 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
2644 rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
2645 if (nghttp2_is_fatal(rv)) {
2648 /* We assume aux_data is a pointer to nghttp2_headers_aux_data */
2649 aux_data = &item->aux_data.headers;
2650 if (aux_data->data_prd.read_callback) {
2651 rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM,
2652 frame->hd.stream_id, &aux_data->data_prd);
2653 if (nghttp2_is_fatal(rv)) {
2656 /* TODO nghttp2_submit_data() may fail if stream has already
2657 DATA frame item. We might have to handle it here. */
2666 case NGHTTP2_PRIORITY:
2667 if (session->server) {
2672 stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);
2675 if (!session_detect_idle_stream(session, frame->hd.stream_id)) {
2679 stream = nghttp2_session_open_stream(
2680 session, frame->hd.stream_id, NGHTTP2_FLAG_NONE,
2681 &frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL);
2683 return NGHTTP2_ERR_NOMEM;
2686 rv = nghttp2_session_reprioritize_stream(session, stream,
2687 &frame->priority.pri_spec);
2688 if (nghttp2_is_fatal(rv)) {
2693 rv = nghttp2_session_adjust_idle_stream(session);
2695 if (nghttp2_is_fatal(rv)) {
2700 case NGHTTP2_RST_STREAM:
2701 rv = nghttp2_session_close_stream(session, frame->hd.stream_id,
2702 frame->rst_stream.error_code);
2703 if (nghttp2_is_fatal(rv)) {
2707 case NGHTTP2_GOAWAY: {
2708 nghttp2_goaway_aux_data *aux_data;
2710 aux_data = &item->aux_data.goaway;
2712 if ((aux_data->flags & NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE) == 0) {
2714 if (aux_data->flags & NGHTTP2_GOAWAY_AUX_TERM_ON_SEND) {
2715 session->goaway_flags |= NGHTTP2_GOAWAY_TERM_SENT;
2718 session->goaway_flags |= NGHTTP2_GOAWAY_SENT;
2720 rv = session_close_stream_on_goaway(session, frame->goaway.last_stream_id,
2723 if (nghttp2_is_fatal(rv)) {
2730 case NGHTTP2_WINDOW_UPDATE:
2731 if (frame->hd.stream_id == 0) {
2732 session->window_update_queued = 0;
2733 if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
2734 rv = session_update_connection_consumed_size(session, 0);
2736 rv = nghttp2_session_update_recv_connection_window_size(session, 0);
2739 if (nghttp2_is_fatal(rv)) {
2746 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2751 stream->window_update_queued = 0;
2753 /* We don't have to send WINDOW_UPDATE if END_STREAM from peer
2755 if (stream->shut_flags & NGHTTP2_SHUT_RD) {
2759 if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
2760 rv = session_update_stream_consumed_size(session, stream, 0);
2763 nghttp2_session_update_recv_stream_window_size(session, stream, 0, 1);
2766 if (nghttp2_is_fatal(rv)) {
2777 * Called after a frame is sent and session_after_frame_sent1. This
2778 * function is responsible to reset session->aob.
2780 * This function returns 0 if it succeeds, or one of the following
2781 * negative error codes:
2785 * NGHTTP2_ERR_CALLBACK_FAILURE
2786 * The callback function failed.
2788 static int session_after_frame_sent2(nghttp2_session *session) {
2790 nghttp2_active_outbound_item *aob = &session->aob;
2791 nghttp2_outbound_item *item = aob->item;
2792 nghttp2_bufs *framebufs = &aob->framebufs;
2793 nghttp2_frame *frame;
2795 nghttp2_stream *stream;
2796 nghttp2_data_aux_data *aux_data;
2798 mem = &session->mem;
2799 frame = &item->frame;
2801 if (frame->hd.type != NGHTTP2_DATA) {
2803 if (frame->hd.type == NGHTTP2_HEADERS ||
2804 frame->hd.type == NGHTTP2_PUSH_PROMISE) {
2806 if (nghttp2_bufs_next_present(framebufs)) {
2807 framebufs->cur = framebufs->cur->next;
2809 DEBUGF("send: next CONTINUATION frame, %zu bytes\n",
2810 nghttp2_buf_len(&framebufs->cur->buf));
2816 active_outbound_item_reset(&session->aob, mem);
2823 aux_data = &item->aux_data.data;
2825 /* On EOF, we have already detached data. Please note that
2826 application may issue nghttp2_submit_data() in
2827 on_frame_send_callback (call from session_after_frame_sent1),
2828 which attach data to stream. We don't want to detach it. */
2829 if (aux_data->eof) {
2830 active_outbound_item_reset(aob, mem);
2835 /* Reset no_copy here because next write may not use this. */
2836 aux_data->no_copy = 0;
2838 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2840 /* If session is closed or RST_STREAM was queued, we won't send
2842 if (nghttp2_session_predicate_data_send(session, stream) != 0) {
2844 rv = nghttp2_stream_detach_item(stream);
2846 if (nghttp2_is_fatal(rv)) {
2851 active_outbound_item_reset(aob, mem);
2857 active_outbound_item_reset(&session->aob, mem);
2862 static int session_call_send_data(nghttp2_session *session,
2863 nghttp2_outbound_item *item,
2864 nghttp2_bufs *framebufs) {
2868 nghttp2_frame *frame;
2869 nghttp2_data_aux_data *aux_data;
2871 buf = &framebufs->cur->buf;
2872 frame = &item->frame;
2873 length = frame->hd.length - frame->data.padlen;
2874 aux_data = &item->aux_data.data;
2876 rv = session->callbacks.send_data_callback(session, frame, buf->pos, length,
2877 &aux_data->data_prd.source,
2878 session->user_data);
2882 case NGHTTP2_ERR_WOULDBLOCK:
2883 case NGHTTP2_ERR_PAUSE:
2884 case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE:
2887 return NGHTTP2_ERR_CALLBACK_FAILURE;
2891 static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session,
2892 const uint8_t **data_ptr,
2895 nghttp2_active_outbound_item *aob;
2896 nghttp2_bufs *framebufs;
2899 mem = &session->mem;
2900 aob = &session->aob;
2901 framebufs = &aob->framebufs;
2903 /* We may have idle streams more than we expect (e.g.,
2904 nghttp2_session_change_stream_priority() or
2905 nghttp2_session_create_idle_stream()). Adjust them here. */
2906 rv = nghttp2_session_adjust_idle_stream(session);
2907 if (nghttp2_is_fatal(rv)) {
2912 switch (aob->state) {
2913 case NGHTTP2_OB_POP_ITEM: {
2914 nghttp2_outbound_item *item;
2916 item = nghttp2_session_pop_next_ob_item(session);
2921 rv = session_prep_frame(session, item);
2922 if (rv == NGHTTP2_ERR_PAUSE) {
2925 if (rv == NGHTTP2_ERR_DEFERRED) {
2926 DEBUGF("send: frame transmission deferred\n");
2930 int32_t opened_stream_id = 0;
2931 uint32_t error_code = NGHTTP2_INTERNAL_ERROR;
2933 DEBUGF("send: frame preparation failed with %s\n",
2934 nghttp2_strerror(rv));
2935 /* TODO If the error comes from compressor, the connection
2937 if (item->frame.hd.type != NGHTTP2_DATA &&
2938 session->callbacks.on_frame_not_send_callback && is_non_fatal(rv)) {
2939 nghttp2_frame *frame = &item->frame;
2940 /* The library is responsible for the transmission of
2941 WINDOW_UPDATE frame, so we don't call error callback for
2943 if (frame->hd.type != NGHTTP2_WINDOW_UPDATE &&
2944 session->callbacks.on_frame_not_send_callback(
2945 session, frame, rv, session->user_data) != 0) {
2947 nghttp2_outbound_item_free(item, mem);
2948 nghttp2_mem_free(mem, item);
2950 return NGHTTP2_ERR_CALLBACK_FAILURE;
2953 /* We have to close stream opened by failed request HEADERS
2955 switch (item->frame.hd.type) {
2956 case NGHTTP2_HEADERS:
2957 if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) {
2958 opened_stream_id = item->frame.hd.stream_id;
2959 if (item->aux_data.headers.canceled) {
2960 error_code = item->aux_data.headers.error_code;
2962 /* Set error_code to REFUSED_STREAM so that application
2963 can send request again. */
2964 error_code = NGHTTP2_REFUSED_STREAM;
2968 case NGHTTP2_PUSH_PROMISE:
2969 opened_stream_id = item->frame.push_promise.promised_stream_id;
2972 if (opened_stream_id) {
2973 /* careful not to override rv */
2975 rv2 = nghttp2_session_close_stream(session, opened_stream_id,
2978 if (nghttp2_is_fatal(rv2)) {
2983 nghttp2_outbound_item_free(item, mem);
2984 nghttp2_mem_free(mem, item);
2985 active_outbound_item_reset(aob, mem);
2987 if (rv == NGHTTP2_ERR_HEADER_COMP) {
2988 /* If header compression error occurred, should terminiate
2990 rv = nghttp2_session_terminate_session(session,
2991 NGHTTP2_INTERNAL_ERROR);
2993 if (nghttp2_is_fatal(rv)) {
3001 nghttp2_bufs_rewind(framebufs);
3003 if (item->frame.hd.type != NGHTTP2_DATA) {
3004 nghttp2_frame *frame;
3006 frame = &item->frame;
3008 DEBUGF("send: next frame: payloadlen=%zu, type=%u, flags=0x%02x, "
3010 frame->hd.length, frame->hd.type, frame->hd.flags,
3011 frame->hd.stream_id);
3013 rv = session_call_before_frame_send(session, frame);
3014 if (nghttp2_is_fatal(rv)) {
3018 if (rv == NGHTTP2_ERR_CANCEL) {
3019 int32_t opened_stream_id = 0;
3020 uint32_t error_code = NGHTTP2_INTERNAL_ERROR;
3022 if (session->callbacks.on_frame_not_send_callback) {
3023 if (session->callbacks.on_frame_not_send_callback(
3024 session, frame, rv, session->user_data) != 0) {
3025 return NGHTTP2_ERR_CALLBACK_FAILURE;
3029 /* We have to close stream opened by canceled request
3030 HEADERS or PUSH_PROMISE. */
3031 switch (item->frame.hd.type) {
3032 case NGHTTP2_HEADERS:
3033 if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) {
3034 opened_stream_id = item->frame.hd.stream_id;
3035 /* We don't have to check
3036 item->aux_data.headers.canceled since it has already
3038 /* Set error_code to REFUSED_STREAM so that application
3039 can send request again. */
3040 error_code = NGHTTP2_REFUSED_STREAM;
3043 case NGHTTP2_PUSH_PROMISE:
3044 opened_stream_id = item->frame.push_promise.promised_stream_id;
3047 if (opened_stream_id) {
3048 /* careful not to override rv */
3050 rv2 = nghttp2_session_close_stream(session, opened_stream_id,
3053 if (nghttp2_is_fatal(rv2)) {
3058 active_outbound_item_reset(aob, mem);
3063 DEBUGF("send: next frame: DATA\n");
3065 if (item->aux_data.data.no_copy) {
3066 aob->state = NGHTTP2_OB_SEND_NO_COPY;
3071 DEBUGF("send: start transmitting frame type=%u, length=%zd\n",
3072 framebufs->cur->buf.pos[3],
3073 framebufs->cur->buf.last - framebufs->cur->buf.pos);
3075 aob->state = NGHTTP2_OB_SEND_DATA;
3079 case NGHTTP2_OB_SEND_DATA: {
3083 buf = &framebufs->cur->buf;
3085 if (buf->pos == buf->last) {
3086 DEBUGF("send: end transmission of a frame\n");
3088 /* Frame has completely sent */
3090 rv = session_after_frame_sent2(session);
3092 rv = session_after_frame_sent1(session);
3095 assert(nghttp2_is_fatal(rv));
3098 rv = session_after_frame_sent2(session);
3102 assert(nghttp2_is_fatal(rv));
3105 /* We have already adjusted the next state */
3109 *data_ptr = buf->pos;
3110 datalen = nghttp2_buf_len(buf);
3112 /* We increment the offset here. If send_callback does not send
3113 everything, we will adjust it. */
3114 buf->pos += datalen;
3116 return (ssize_t)datalen;
3118 case NGHTTP2_OB_SEND_NO_COPY: {
3119 nghttp2_stream *stream;
3120 nghttp2_frame *frame;
3123 DEBUGF("send: no copy DATA\n");
3125 frame = &aob->item->frame;
3127 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
3128 if (stream == NULL) {
3129 DEBUGF("send: no copy DATA cancelled because stream was closed\n");
3131 active_outbound_item_reset(aob, mem);
3136 rv = session_call_send_data(session, aob->item, framebufs);
3137 if (nghttp2_is_fatal(rv)) {
3141 if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
3142 rv = nghttp2_stream_detach_item(stream);
3144 if (nghttp2_is_fatal(rv)) {
3148 rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,
3149 NGHTTP2_INTERNAL_ERROR);
3150 if (nghttp2_is_fatal(rv)) {
3154 active_outbound_item_reset(aob, mem);
3159 if (rv == NGHTTP2_ERR_WOULDBLOCK) {
3163 pause = (rv == NGHTTP2_ERR_PAUSE);
3165 rv = session_after_frame_sent1(session);
3167 assert(nghttp2_is_fatal(rv));
3170 rv = session_after_frame_sent2(session);
3172 assert(nghttp2_is_fatal(rv));
3176 /* We have already adjusted the next state */
3184 case NGHTTP2_OB_SEND_CLIENT_MAGIC: {
3188 buf = &framebufs->cur->buf;
3190 if (buf->pos == buf->last) {
3191 DEBUGF("send: end transmission of client magic\n");
3192 active_outbound_item_reset(aob, mem);
3196 *data_ptr = buf->pos;
3197 datalen = nghttp2_buf_len(buf);
3199 buf->pos += datalen;
3201 return (ssize_t)datalen;
3207 ssize_t nghttp2_session_mem_send(nghttp2_session *session,
3208 const uint8_t **data_ptr) {
3214 len = nghttp2_session_mem_send_internal(session, data_ptr, 1);
3219 if (session->aob.item) {
3220 /* We have to call session_after_frame_sent1 here to handle stream
3221 closure upon transmission of frames. Otherwise, END_STREAM may
3222 be reached to client before we call nghttp2_session_mem_send
3223 again and we may get exceeding number of incoming streams. */
3224 rv = session_after_frame_sent1(session);
3226 assert(nghttp2_is_fatal(rv));
3234 int nghttp2_session_send(nghttp2_session *session) {
3235 const uint8_t *data = NULL;
3238 nghttp2_bufs *framebufs;
3240 framebufs = &session->aob.framebufs;
3243 datalen = nghttp2_session_mem_send_internal(session, &data, 0);
3245 return (int)datalen;
3247 sentlen = session->callbacks.send_callback(session, data, (size_t)datalen,
3248 0, session->user_data);
3250 if (sentlen == NGHTTP2_ERR_WOULDBLOCK) {
3251 /* Transmission canceled. Rewind the offset */
3252 framebufs->cur->buf.pos -= datalen;
3256 return NGHTTP2_ERR_CALLBACK_FAILURE;
3258 /* Rewind the offset to the amount of unsent bytes */
3259 framebufs->cur->buf.pos -= datalen - sentlen;
3263 static ssize_t session_recv(nghttp2_session *session, uint8_t *buf,
3266 rv = session->callbacks.recv_callback(session, buf, len, 0,
3267 session->user_data);
3269 if ((size_t)rv > len) {
3270 return NGHTTP2_ERR_CALLBACK_FAILURE;
3272 } else if (rv < 0 && rv != NGHTTP2_ERR_WOULDBLOCK && rv != NGHTTP2_ERR_EOF) {
3273 return NGHTTP2_ERR_CALLBACK_FAILURE;
3278 static int session_call_on_begin_frame(nghttp2_session *session,
3279 const nghttp2_frame_hd *hd) {
3282 if (session->callbacks.on_begin_frame_callback) {
3284 rv = session->callbacks.on_begin_frame_callback(session, hd,
3285 session->user_data);
3288 return NGHTTP2_ERR_CALLBACK_FAILURE;
3295 static int session_call_on_frame_received(nghttp2_session *session,
3296 nghttp2_frame *frame) {
3298 if (session->callbacks.on_frame_recv_callback) {
3299 rv = session->callbacks.on_frame_recv_callback(session, frame,
3300 session->user_data);
3302 return NGHTTP2_ERR_CALLBACK_FAILURE;
3308 static int session_call_on_begin_headers(nghttp2_session *session,
3309 nghttp2_frame *frame) {
3311 DEBUGF("recv: call on_begin_headers callback stream_id=%d\n",
3312 frame->hd.stream_id);
3313 if (session->callbacks.on_begin_headers_callback) {
3314 rv = session->callbacks.on_begin_headers_callback(session, frame,
3315 session->user_data);
3316 if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
3320 return NGHTTP2_ERR_CALLBACK_FAILURE;
3326 static int session_call_on_header(nghttp2_session *session,
3327 const nghttp2_frame *frame,
3328 const nghttp2_hd_nv *nv) {
3330 if (session->callbacks.on_header_callback2) {
3331 rv = session->callbacks.on_header_callback2(
3332 session, frame, nv->name, nv->value, nv->flags, session->user_data);
3333 } else if (session->callbacks.on_header_callback) {
3334 rv = session->callbacks.on_header_callback(
3335 session, frame, nv->name->base, nv->name->len, nv->value->base,
3336 nv->value->len, nv->flags, session->user_data);
3339 if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
3343 return NGHTTP2_ERR_CALLBACK_FAILURE;
3349 static int session_call_on_invalid_header(nghttp2_session *session,
3350 const nghttp2_frame *frame,
3351 const nghttp2_hd_nv *nv) {
3353 if (session->callbacks.on_invalid_header_callback2) {
3354 rv = session->callbacks.on_invalid_header_callback2(
3355 session, frame, nv->name, nv->value, nv->flags, session->user_data);
3356 } else if (session->callbacks.on_invalid_header_callback) {
3357 rv = session->callbacks.on_invalid_header_callback(
3358 session, frame, nv->name->base, nv->name->len, nv->value->base,
3359 nv->value->len, nv->flags, session->user_data);
3361 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
3364 if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
3368 return NGHTTP2_ERR_CALLBACK_FAILURE;
3375 session_call_on_extension_chunk_recv_callback(nghttp2_session *session,
3376 const uint8_t *data, size_t len) {
3378 nghttp2_inbound_frame *iframe = &session->iframe;
3379 nghttp2_frame *frame = &iframe->frame;
3381 if (session->callbacks.on_extension_chunk_recv_callback) {
3382 rv = session->callbacks.on_extension_chunk_recv_callback(
3383 session, &frame->hd, data, len, session->user_data);
3384 if (rv == NGHTTP2_ERR_CANCEL) {
3388 return NGHTTP2_ERR_CALLBACK_FAILURE;
3395 static int session_call_unpack_extension_callback(nghttp2_session *session) {
3397 nghttp2_inbound_frame *iframe = &session->iframe;
3398 nghttp2_frame *frame = &iframe->frame;
3399 void *payload = NULL;
3401 rv = session->callbacks.unpack_extension_callback(
3402 session, &payload, &frame->hd, session->user_data);
3403 if (rv == NGHTTP2_ERR_CANCEL) {
3407 return NGHTTP2_ERR_CALLBACK_FAILURE;
3410 frame->ext.payload = payload;
3416 * Handles frame size error.
3418 * This function returns 0 if it succeeds, or one of the following
3419 * negative error codes:
3424 static int session_handle_frame_size_error(nghttp2_session *session) {
3425 /* TODO Currently no callback is called for this error, because we
3426 call this callback before reading any payload */
3427 return nghttp2_session_terminate_session(session, NGHTTP2_FRAME_SIZE_ERROR);
3430 static uint32_t get_error_code_from_lib_error_code(int lib_error_code) {
3431 switch (lib_error_code) {
3432 case NGHTTP2_ERR_STREAM_CLOSED:
3433 return NGHTTP2_STREAM_CLOSED;
3434 case NGHTTP2_ERR_HEADER_COMP:
3435 return NGHTTP2_COMPRESSION_ERROR;
3436 case NGHTTP2_ERR_FRAME_SIZE_ERROR:
3437 return NGHTTP2_FRAME_SIZE_ERROR;
3438 case NGHTTP2_ERR_FLOW_CONTROL:
3439 return NGHTTP2_FLOW_CONTROL_ERROR;
3440 case NGHTTP2_ERR_REFUSED_STREAM:
3441 return NGHTTP2_REFUSED_STREAM;
3442 case NGHTTP2_ERR_PROTO:
3443 case NGHTTP2_ERR_HTTP_HEADER:
3444 case NGHTTP2_ERR_HTTP_MESSAGING:
3445 return NGHTTP2_PROTOCOL_ERROR;
3447 return NGHTTP2_INTERNAL_ERROR;
3452 * Calls on_invalid_frame_recv_callback if it is set to |session|.
3454 * This function returns 0 if it succeeds, or one of the following
3455 * negative error codes:
3457 * NGHTTP2_ERR_CALLBACK_FAILURE
3458 * User defined callback function fails.
3460 static int session_call_on_invalid_frame_recv_callback(nghttp2_session *session,
3461 nghttp2_frame *frame,
3462 int lib_error_code) {
3463 if (session->callbacks.on_invalid_frame_recv_callback) {
3464 if (session->callbacks.on_invalid_frame_recv_callback(
3465 session, frame, lib_error_code, session->user_data) != 0) {
3466 return NGHTTP2_ERR_CALLBACK_FAILURE;
3472 static int session_handle_invalid_stream2(nghttp2_session *session,
3474 nghttp2_frame *frame,
3475 int lib_error_code) {
3477 rv = nghttp2_session_add_rst_stream(
3478 session, stream_id, get_error_code_from_lib_error_code(lib_error_code));
3482 if (session->callbacks.on_invalid_frame_recv_callback) {
3483 if (session->callbacks.on_invalid_frame_recv_callback(
3484 session, frame, lib_error_code, session->user_data) != 0) {
3485 return NGHTTP2_ERR_CALLBACK_FAILURE;
3491 static int session_handle_invalid_stream(nghttp2_session *session,
3492 nghttp2_frame *frame,
3493 int lib_error_code) {
3494 return session_handle_invalid_stream2(session, frame->hd.stream_id, frame,
3498 static int session_inflate_handle_invalid_stream(nghttp2_session *session,
3499 nghttp2_frame *frame,
3500 int lib_error_code) {
3502 rv = session_handle_invalid_stream(session, frame, lib_error_code);
3503 if (nghttp2_is_fatal(rv)) {
3506 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3510 * Handles invalid frame which causes connection error.
3512 static int session_handle_invalid_connection(nghttp2_session *session,
3513 nghttp2_frame *frame,
3515 const char *reason) {
3516 if (session->callbacks.on_invalid_frame_recv_callback) {
3517 if (session->callbacks.on_invalid_frame_recv_callback(
3518 session, frame, lib_error_code, session->user_data) != 0) {
3519 return NGHTTP2_ERR_CALLBACK_FAILURE;
3522 return nghttp2_session_terminate_session_with_reason(
3523 session, get_error_code_from_lib_error_code(lib_error_code), reason);
3526 static int session_inflate_handle_invalid_connection(nghttp2_session *session,
3527 nghttp2_frame *frame,
3529 const char *reason) {
3532 session_handle_invalid_connection(session, frame, lib_error_code, reason);
3533 if (nghttp2_is_fatal(rv)) {
3536 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3540 * Inflates header block in the memory pointed by |in| with |inlen|
3541 * bytes. If this function returns NGHTTP2_ERR_PAUSE, the caller must
3542 * call this function again, until it returns 0 or one of negative
3543 * error code. If |call_header_cb| is zero, the on_header_callback
3544 * are not invoked and the function never return NGHTTP2_ERR_PAUSE. If
3545 * the given |in| is the last chunk of header block, the |final| must
3546 * be nonzero. If header block is successfully processed (which is
3547 * indicated by the return value 0, NGHTTP2_ERR_PAUSE or
3548 * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE), the number of processed
3549 * input bytes is assigned to the |*readlen_ptr|.
3551 * This function return 0 if it succeeds, or one of the negative error
3554 * NGHTTP2_ERR_CALLBACK_FAILURE
3555 * The callback function failed.
3556 * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
3557 * The callback returns this error code, indicating that this
3558 * stream should be RST_STREAMed.
3562 * The callback function returned NGHTTP2_ERR_PAUSE
3563 * NGHTTP2_ERR_HEADER_COMP
3564 * Header decompression failed
3566 static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
3567 size_t *readlen_ptr, uint8_t *in, size_t inlen,
3568 int final, int call_header_cb) {
3573 nghttp2_stream *stream;
3574 nghttp2_stream *subject_stream;
3578 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
3580 if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
3581 subject_stream = nghttp2_session_get_stream(
3582 session, frame->push_promise.promised_stream_id);
3584 subject_stream = stream;
3585 trailer = session_trailer_headers(session, stream, frame);
3588 DEBUGF("recv: decoding header block %zu bytes\n", inlen);
3591 proclen = nghttp2_hd_inflate_hd_nv(&session->hd_inflater, &nv,
3592 &inflate_flags, in, inlen, final);
3593 if (nghttp2_is_fatal((int)proclen)) {
3594 return (int)proclen;
3597 if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) {
3598 if (subject_stream && subject_stream->state != NGHTTP2_STREAM_CLOSING) {
3599 /* Adding RST_STREAM here is very important. It prevents
3600 from invoking subsequent callbacks for the same stream
3602 rv = nghttp2_session_add_rst_stream(
3603 session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR);
3605 if (nghttp2_is_fatal(rv)) {
3611 nghttp2_session_terminate_session(session, NGHTTP2_COMPRESSION_ERROR);
3612 if (nghttp2_is_fatal(rv)) {
3616 return NGHTTP2_ERR_HEADER_COMP;
3619 inlen -= (size_t)proclen;
3620 *readlen_ptr += (size_t)proclen;
3622 DEBUGF("recv: proclen=%zd\n", proclen);
3624 if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) {
3626 if (subject_stream) {
3627 if (session_enforce_http_messaging(session)) {
3628 rv = nghttp2_http_on_header(session, subject_stream, frame, &nv,
3631 if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) {
3632 /* Don't overwrite rv here */
3635 rv2 = session_call_on_invalid_header(session, frame, &nv);
3636 if (rv2 == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
3637 rv = NGHTTP2_ERR_HTTP_HEADER;
3643 /* header is ignored */
3644 DEBUGF("recv: HTTP ignored: type=%u, id=%d, header %.*s: %.*s\n",
3645 frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
3646 nv.name->base, (int)nv.value->len, nv.value->base);
3648 rv2 = session_call_error_callback(
3649 session, NGHTTP2_ERR_HTTP_HEADER,
3650 "Ignoring received invalid HTTP header field: frame type: "
3651 "%u, stream: %d, name: [%.*s], value: [%.*s]",
3652 frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
3653 nv.name->base, (int)nv.value->len, nv.value->base);
3655 if (nghttp2_is_fatal(rv2)) {
3661 if (rv == NGHTTP2_ERR_HTTP_HEADER) {
3662 DEBUGF("recv: HTTP error: type=%u, id=%d, header %.*s: %.*s\n",
3663 frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
3664 nv.name->base, (int)nv.value->len, nv.value->base);
3666 rv = session_call_error_callback(
3667 session, NGHTTP2_ERR_HTTP_HEADER,
3668 "Invalid HTTP header field was received: frame type: "
3669 "%u, stream: %d, name: [%.*s], value: [%.*s]",
3670 frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
3671 nv.name->base, (int)nv.value->len, nv.value->base);
3673 if (nghttp2_is_fatal(rv)) {
3677 rv = session_handle_invalid_stream2(session,
3678 subject_stream->stream_id,
3679 frame, NGHTTP2_ERR_HTTP_HEADER);
3680 if (nghttp2_is_fatal(rv)) {
3683 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
3687 rv = session_call_on_header(session, frame, &nv);
3688 /* This handles NGHTTP2_ERR_PAUSE and
3689 NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE as well */
3696 if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
3697 nghttp2_hd_inflate_end_headers(&session->hd_inflater);
3700 if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) {
3708 * Call this function when HEADERS frame was completely received.
3710 * This function returns 0 if it succeeds, or one of negative error
3713 * NGHTTP2_ERR_CALLBACK_FAILURE
3714 * The callback function failed.
3718 static int session_end_stream_headers_received(nghttp2_session *session,
3719 nghttp2_frame *frame,
3720 nghttp2_stream *stream) {
3722 if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
3726 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
3727 rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
3728 if (nghttp2_is_fatal(rv)) {
3735 static int session_after_header_block_received(nghttp2_session *session) {
3737 nghttp2_frame *frame = &session->iframe.frame;
3738 nghttp2_stream *stream;
3740 /* We don't call on_frame_recv_callback if stream has been closed
3741 already or being closed. */
3742 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
3743 if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) {
3747 if (session_enforce_http_messaging(session)) {
3748 if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
3749 nghttp2_stream *subject_stream;
3751 subject_stream = nghttp2_session_get_stream(
3752 session, frame->push_promise.promised_stream_id);
3753 if (subject_stream) {
3754 rv = nghttp2_http_on_request_headers(subject_stream, frame);
3757 assert(frame->hd.type == NGHTTP2_HEADERS);
3758 switch (frame->headers.cat) {
3759 case NGHTTP2_HCAT_REQUEST:
3760 rv = nghttp2_http_on_request_headers(stream, frame);
3762 case NGHTTP2_HCAT_RESPONSE:
3763 case NGHTTP2_HCAT_PUSH_RESPONSE:
3764 rv = nghttp2_http_on_response_headers(stream);
3766 case NGHTTP2_HCAT_HEADERS:
3767 if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
3768 assert(!session->server);
3769 rv = nghttp2_http_on_response_headers(stream);
3771 rv = nghttp2_http_on_trailer_headers(stream, frame);
3777 if (rv == 0 && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
3778 rv = nghttp2_http_on_remote_end_stream(stream);
3784 if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
3785 stream_id = frame->push_promise.promised_stream_id;
3787 stream_id = frame->hd.stream_id;
3790 rv = session_handle_invalid_stream2(session, stream_id, frame,
3791 NGHTTP2_ERR_HTTP_MESSAGING);
3792 if (nghttp2_is_fatal(rv)) {
3796 if (frame->hd.type == NGHTTP2_HEADERS &&
3797 (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
3798 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
3799 /* Don't call nghttp2_session_close_stream_if_shut_rdwr
3800 because RST_STREAM has been submitted. */
3806 rv = session_call_on_frame_received(session, frame);
3807 if (nghttp2_is_fatal(rv)) {
3811 if (frame->hd.type != NGHTTP2_HEADERS) {
3815 return session_end_stream_headers_received(session, frame, stream);
3818 int nghttp2_session_on_request_headers_received(nghttp2_session *session,
3819 nghttp2_frame *frame) {
3821 nghttp2_stream *stream;
3822 if (frame->hd.stream_id == 0) {
3823 return session_inflate_handle_invalid_connection(
3824 session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0");
3827 /* If client receives idle stream from server, it is invalid
3828 regardless stream ID is even or odd. This is because client is
3829 not expected to receive request from server. */
3830 if (!session->server) {
3831 if (session_detect_idle_stream(session, frame->hd.stream_id)) {
3832 return session_inflate_handle_invalid_connection(
3833 session, frame, NGHTTP2_ERR_PROTO,
3834 "request HEADERS: client received request");
3837 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3840 assert(session->server);
3842 if (!session_is_new_peer_stream_id(session, frame->hd.stream_id)) {
3843 if (frame->hd.stream_id == 0 ||
3844 nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
3845 return session_inflate_handle_invalid_connection(
3846 session, frame, NGHTTP2_ERR_PROTO,
3847 "request HEADERS: invalid stream_id");
3850 /* RFC 7540 says if an endpoint receives a HEADERS with invalid
3851 * stream ID (e.g, numerically smaller than previous), it MUST
3852 * issue connection error with error code PROTOCOL_ERROR. It is a
3853 * bit hard to detect this, since we cannot remember all streams
3854 * we observed so far.
3856 * You might imagine this is really easy. But no. HTTP/2 is
3857 * asynchronous protocol, and usually client and server do not
3858 * share the complete picture of open/closed stream status. For
3859 * example, after server sends RST_STREAM for a stream, client may
3860 * send trailer HEADERS for that stream. If naive server detects
3861 * that, and issued connection error, then it is a bug of server
3862 * implementation since client is not wrong if it did not get
3863 * RST_STREAM when it issued trailer HEADERS.
3865 * At the moment, we are very conservative here. We only use
3866 * connection error if stream ID refers idle stream, or we are
3867 * sure that stream is half-closed(remote) or closed. Otherwise
3868 * we just ignore HEADERS for now.
3870 stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);
3871 if (stream && (stream->shut_flags & NGHTTP2_SHUT_RD)) {
3872 return session_inflate_handle_invalid_connection(
3873 session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed");
3876 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3878 session->last_recv_stream_id = frame->hd.stream_id;
3880 if (session_is_incoming_concurrent_streams_max(session)) {
3881 return session_inflate_handle_invalid_connection(
3882 session, frame, NGHTTP2_ERR_PROTO,
3883 "request HEADERS: max concurrent streams exceeded");
3886 if (!session_allow_incoming_new_stream(session)) {
3887 /* We just ignore stream after GOAWAY was sent */
3888 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3891 if (frame->headers.pri_spec.stream_id == frame->hd.stream_id) {
3892 return session_inflate_handle_invalid_connection(
3893 session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: depend on itself");
3896 if (session_is_incoming_concurrent_streams_pending_max(session)) {
3897 return session_inflate_handle_invalid_stream(session, frame,
3898 NGHTTP2_ERR_REFUSED_STREAM);
3901 stream = nghttp2_session_open_stream(
3902 session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE,
3903 &frame->headers.pri_spec, NGHTTP2_STREAM_OPENING, NULL);
3905 return NGHTTP2_ERR_NOMEM;
3908 rv = nghttp2_session_adjust_closed_stream(session);
3909 if (nghttp2_is_fatal(rv)) {
3913 session->last_proc_stream_id = session->last_recv_stream_id;
3915 rv = session_call_on_begin_headers(session, frame);
3922 int nghttp2_session_on_response_headers_received(nghttp2_session *session,
3923 nghttp2_frame *frame,
3924 nghttp2_stream *stream) {
3926 /* This function is only called if stream->state ==
3927 NGHTTP2_STREAM_OPENING and stream_id is local side initiated. */
3928 assert(stream->state == NGHTTP2_STREAM_OPENING &&
3929 nghttp2_session_is_my_stream_id(session, frame->hd.stream_id));
3930 if (frame->hd.stream_id == 0) {
3931 return session_inflate_handle_invalid_connection(
3932 session, frame, NGHTTP2_ERR_PROTO, "response HEADERS: stream_id == 0");
3934 if (stream->shut_flags & NGHTTP2_SHUT_RD) {
3935 /* half closed (remote): from the spec:
3937 If an endpoint receives additional frames for a stream that is
3938 in this state it MUST respond with a stream error (Section
3939 5.4.2) of type STREAM_CLOSED.
3941 We go further, and make it connection error.
3943 return session_inflate_handle_invalid_connection(
3944 session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed");
3946 stream->state = NGHTTP2_STREAM_OPENED;
3947 rv = session_call_on_begin_headers(session, frame);
3954 int nghttp2_session_on_push_response_headers_received(nghttp2_session *session,
3955 nghttp2_frame *frame,
3956 nghttp2_stream *stream) {
3958 assert(stream->state == NGHTTP2_STREAM_RESERVED);
3959 if (frame->hd.stream_id == 0) {
3960 return session_inflate_handle_invalid_connection(
3961 session, frame, NGHTTP2_ERR_PROTO,
3962 "push response HEADERS: stream_id == 0");
3965 if (session->server) {
3966 return session_inflate_handle_invalid_connection(
3967 session, frame, NGHTTP2_ERR_PROTO,
3968 "HEADERS: no HEADERS allowed from client in reserved state");
3971 if (session_is_incoming_concurrent_streams_max(session)) {
3972 return session_inflate_handle_invalid_connection(
3973 session, frame, NGHTTP2_ERR_PROTO,
3974 "push response HEADERS: max concurrent streams exceeded");
3977 if (!session_allow_incoming_new_stream(session)) {
3978 /* We don't accept new stream after GOAWAY was sent. */
3979 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3982 if (session_is_incoming_concurrent_streams_pending_max(session)) {
3983 return session_inflate_handle_invalid_stream(session, frame,
3984 NGHTTP2_ERR_REFUSED_STREAM);
3987 nghttp2_stream_promise_fulfilled(stream);
3988 if (!nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
3989 --session->num_incoming_reserved_streams;
3991 ++session->num_incoming_streams;
3992 rv = session_call_on_begin_headers(session, frame);
3999 int nghttp2_session_on_headers_received(nghttp2_session *session,
4000 nghttp2_frame *frame,
4001 nghttp2_stream *stream) {
4003 if (frame->hd.stream_id == 0) {
4004 return session_inflate_handle_invalid_connection(
4005 session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream_id == 0");
4007 if ((stream->shut_flags & NGHTTP2_SHUT_RD)) {
4008 /* half closed (remote): from the spec:
4010 If an endpoint receives additional frames for a stream that is
4011 in this state it MUST respond with a stream error (Section
4012 5.4.2) of type STREAM_CLOSED.
4014 we go further, and make it connection error.
4016 return session_inflate_handle_invalid_connection(
4017 session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed");
4019 if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
4020 if (stream->state == NGHTTP2_STREAM_OPENED) {
4021 rv = session_call_on_begin_headers(session, frame);
4028 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
4030 /* If this is remote peer initiated stream, it is OK unless it
4031 has sent END_STREAM frame already. But if stream is in
4032 NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race
4034 if (stream->state != NGHTTP2_STREAM_CLOSING) {
4035 rv = session_call_on_begin_headers(session, frame);
4041 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
4044 static int session_process_headers_frame(nghttp2_session *session) {
4046 nghttp2_inbound_frame *iframe = &session->iframe;
4047 nghttp2_frame *frame = &iframe->frame;
4048 nghttp2_stream *stream;
4050 rv = nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos);
4053 return nghttp2_session_terminate_session_with_reason(
4054 session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: could not unpack");
4056 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
4058 frame->headers.cat = NGHTTP2_HCAT_REQUEST;
4059 return nghttp2_session_on_request_headers_received(session, frame);
4062 if (stream->state == NGHTTP2_STREAM_RESERVED) {
4063 frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE;
4064 return nghttp2_session_on_push_response_headers_received(session, frame,
4068 if (stream->state == NGHTTP2_STREAM_OPENING &&
4069 nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
4070 frame->headers.cat = NGHTTP2_HCAT_RESPONSE;
4071 return nghttp2_session_on_response_headers_received(session, frame, stream);
4074 frame->headers.cat = NGHTTP2_HCAT_HEADERS;
4075 return nghttp2_session_on_headers_received(session, frame, stream);
4078 int nghttp2_session_on_priority_received(nghttp2_session *session,
4079 nghttp2_frame *frame) {
4081 nghttp2_stream *stream;
4083 if (frame->hd.stream_id == 0) {
4084 return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4085 "PRIORITY: stream_id == 0");
4088 if (frame->priority.pri_spec.stream_id == frame->hd.stream_id) {
4089 return nghttp2_session_terminate_session_with_reason(
4090 session, NGHTTP2_PROTOCOL_ERROR, "depend on itself");
4093 if (!session->server) {
4094 /* Re-prioritization works only in server */
4095 return session_call_on_frame_received(session, frame);
4098 stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);
4101 /* PRIORITY against idle stream can create anchor node in
4103 if (!session_detect_idle_stream(session, frame->hd.stream_id)) {
4107 stream = nghttp2_session_open_stream(
4108 session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE,
4109 &frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL);
4111 if (stream == NULL) {
4112 return NGHTTP2_ERR_NOMEM;
4115 rv = nghttp2_session_adjust_idle_stream(session);
4116 if (nghttp2_is_fatal(rv)) {
4120 rv = nghttp2_session_reprioritize_stream(session, stream,
4121 &frame->priority.pri_spec);
4123 if (nghttp2_is_fatal(rv)) {
4127 rv = nghttp2_session_adjust_idle_stream(session);
4128 if (nghttp2_is_fatal(rv)) {
4133 return session_call_on_frame_received(session, frame);
4136 static int session_process_priority_frame(nghttp2_session *session) {
4137 nghttp2_inbound_frame *iframe = &session->iframe;
4138 nghttp2_frame *frame = &iframe->frame;
4140 nghttp2_frame_unpack_priority_payload(&frame->priority, iframe->sbuf.pos);
4142 return nghttp2_session_on_priority_received(session, frame);
4145 int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
4146 nghttp2_frame *frame) {
4148 nghttp2_stream *stream;
4149 if (frame->hd.stream_id == 0) {
4150 return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4151 "RST_STREAM: stream_id == 0");
4154 if (session_detect_idle_stream(session, frame->hd.stream_id)) {
4155 return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4156 "RST_STREAM: stream in idle");
4159 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
4161 /* We may use stream->shut_flags for strict error checking. */
4162 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
4165 rv = session_call_on_frame_received(session, frame);
4169 rv = nghttp2_session_close_stream(session, frame->hd.stream_id,
4170 frame->rst_stream.error_code);
4171 if (nghttp2_is_fatal(rv)) {
4177 static int session_process_rst_stream_frame(nghttp2_session *session) {
4178 nghttp2_inbound_frame *iframe = &session->iframe;
4179 nghttp2_frame *frame = &iframe->frame;
4181 nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, iframe->sbuf.pos);
4183 return nghttp2_session_on_rst_stream_received(session, frame);
4186 static int update_remote_initial_window_size_func(nghttp2_map_entry *entry,
4189 nghttp2_update_window_size_arg *arg;
4190 nghttp2_stream *stream;
4192 arg = (nghttp2_update_window_size_arg *)ptr;
4193 stream = (nghttp2_stream *)entry;
4195 rv = nghttp2_stream_update_remote_initial_window_size(
4196 stream, arg->new_window_size, arg->old_window_size);
4198 return nghttp2_session_add_rst_stream(arg->session, stream->stream_id,
4199 NGHTTP2_FLOW_CONTROL_ERROR);
4202 /* If window size gets positive, push deferred DATA frame to
4204 if (stream->remote_window_size > 0 &&
4205 nghttp2_stream_check_deferred_by_flow_control(stream)) {
4207 rv = nghttp2_stream_resume_deferred_item(
4208 stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
4210 if (nghttp2_is_fatal(rv)) {
4218 * Updates the remote initial window size of all active streams. If
4219 * error occurs, all streams may not be updated.
4221 * This function returns 0 if it succeeds, or one of the following
4222 * negative error codes:
4228 session_update_remote_initial_window_size(nghttp2_session *session,
4229 int32_t new_initial_window_size) {
4230 nghttp2_update_window_size_arg arg;
4232 arg.session = session;
4233 arg.new_window_size = new_initial_window_size;
4234 arg.old_window_size = (int32_t)session->remote_settings.initial_window_size;
4236 return nghttp2_map_each(&session->streams,
4237 update_remote_initial_window_size_func, &arg);
4240 static int update_local_initial_window_size_func(nghttp2_map_entry *entry,
4243 nghttp2_update_window_size_arg *arg;
4244 nghttp2_stream *stream;
4245 arg = (nghttp2_update_window_size_arg *)ptr;
4246 stream = (nghttp2_stream *)entry;
4247 rv = nghttp2_stream_update_local_initial_window_size(
4248 stream, arg->new_window_size, arg->old_window_size);
4250 return nghttp2_session_add_rst_stream(arg->session, stream->stream_id,
4251 NGHTTP2_FLOW_CONTROL_ERROR);
4253 if (!(arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) &&
4254 stream->window_update_queued == 0 &&
4255 nghttp2_should_send_window_update(stream->local_window_size,
4256 stream->recv_window_size)) {
4258 rv = nghttp2_session_add_window_update(arg->session, NGHTTP2_FLAG_NONE,
4260 stream->recv_window_size);
4265 stream->recv_window_size = 0;
4271 * Updates the local initial window size of all active streams. If
4272 * error occurs, all streams may not be updated.
4274 * This function returns 0 if it succeeds, or one of the following
4275 * negative error codes:
4281 session_update_local_initial_window_size(nghttp2_session *session,
4282 int32_t new_initial_window_size,
4283 int32_t old_initial_window_size) {
4284 nghttp2_update_window_size_arg arg;
4285 arg.session = session;
4286 arg.new_window_size = new_initial_window_size;
4287 arg.old_window_size = old_initial_window_size;
4288 return nghttp2_map_each(&session->streams,
4289 update_local_initial_window_size_func, &arg);
4293 * Apply SETTINGS values |iv| having |niv| elements to the local
4294 * settings. We assumes that all values in |iv| is correct, since we
4295 * validated them in nghttp2_session_add_settings() already.
4297 * This function returns 0 if it succeeds, or one of the following
4298 * negative error codes:
4300 * NGHTTP2_ERR_HEADER_COMP
4301 * The header table size is out of range
4305 int nghttp2_session_update_local_settings(nghttp2_session *session,
4306 nghttp2_settings_entry *iv,
4310 int32_t new_initial_window_size = -1;
4311 uint32_t header_table_size = 0;
4312 uint32_t min_header_table_size = UINT32_MAX;
4313 uint8_t header_table_size_seen = 0;
4314 /* For NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, use the value last
4315 seen. For NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, use both minimum
4316 value and last seen value. */
4317 for (i = 0; i < niv; ++i) {
4318 switch (iv[i].settings_id) {
4319 case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
4320 header_table_size_seen = 1;
4321 header_table_size = iv[i].value;
4322 min_header_table_size = nghttp2_min(min_header_table_size, iv[i].value);
4324 case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
4325 new_initial_window_size = (int32_t)iv[i].value;
4329 if (header_table_size_seen) {
4330 if (min_header_table_size < header_table_size) {
4331 rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater,
4332 min_header_table_size);
4338 rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater,
4344 if (new_initial_window_size != -1) {
4345 rv = session_update_local_initial_window_size(
4346 session, new_initial_window_size,
4347 (int32_t)session->local_settings.initial_window_size);
4353 for (i = 0; i < niv; ++i) {
4354 switch (iv[i].settings_id) {
4355 case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
4356 session->local_settings.header_table_size = iv[i].value;
4358 case NGHTTP2_SETTINGS_ENABLE_PUSH:
4359 session->local_settings.enable_push = iv[i].value;
4361 case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
4362 session->local_settings.max_concurrent_streams = iv[i].value;
4364 case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
4365 session->local_settings.initial_window_size = iv[i].value;
4367 case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
4368 session->local_settings.max_frame_size = iv[i].value;
4370 case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
4371 session->local_settings.max_header_list_size = iv[i].value;
4373 case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
4374 session->local_settings.enable_connect_protocol = iv[i].value;
4382 int nghttp2_session_on_settings_received(nghttp2_session *session,
4383 nghttp2_frame *frame, int noack) {
4387 nghttp2_inflight_settings *settings;
4389 mem = &session->mem;
4391 if (frame->hd.stream_id != 0) {
4392 return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4393 "SETTINGS: stream_id != 0");
4395 if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
4396 if (frame->settings.niv != 0) {
4397 return session_handle_invalid_connection(
4398 session, frame, NGHTTP2_ERR_FRAME_SIZE_ERROR,
4399 "SETTINGS: ACK and payload != 0");
4402 settings = session->inflight_settings_head;
4405 return session_handle_invalid_connection(
4406 session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: unexpected ACK");
4409 rv = nghttp2_session_update_local_settings(session, settings->iv,
4412 session->inflight_settings_head = settings->next;
4414 inflight_settings_del(settings, mem);
4417 if (nghttp2_is_fatal(rv)) {
4420 return session_handle_invalid_connection(session, frame, rv, NULL);
4422 return session_call_on_frame_received(session, frame);
4425 if (!session->remote_settings_received) {
4426 session->remote_settings.max_concurrent_streams =
4427 NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;
4428 session->remote_settings_received = 1;
4431 for (i = 0; i < frame->settings.niv; ++i) {
4432 nghttp2_settings_entry *entry = &frame->settings.iv[i];
4434 switch (entry->settings_id) {
4435 case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
4437 rv = nghttp2_hd_deflate_change_table_size(&session->hd_deflater,
4440 if (nghttp2_is_fatal(rv)) {
4443 return session_handle_invalid_connection(
4444 session, frame, NGHTTP2_ERR_HEADER_COMP, NULL);
4448 session->remote_settings.header_table_size = entry->value;
4451 case NGHTTP2_SETTINGS_ENABLE_PUSH:
4453 if (entry->value != 0 && entry->value != 1) {
4454 return session_handle_invalid_connection(
4455 session, frame, NGHTTP2_ERR_PROTO,
4456 "SETTINGS: invalid SETTINGS_ENBLE_PUSH");
4459 if (!session->server && entry->value != 0) {
4460 return session_handle_invalid_connection(
4461 session, frame, NGHTTP2_ERR_PROTO,
4462 "SETTINGS: server attempted to enable push");
4465 session->remote_settings.enable_push = entry->value;
4468 case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
4470 session->remote_settings.max_concurrent_streams = entry->value;
4473 case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
4475 /* Update the initial window size of the all active streams */
4476 /* Check that initial_window_size < (1u << 31) */
4477 if (entry->value > NGHTTP2_MAX_WINDOW_SIZE) {
4478 return session_handle_invalid_connection(
4479 session, frame, NGHTTP2_ERR_FLOW_CONTROL,
4480 "SETTINGS: too large SETTINGS_INITIAL_WINDOW_SIZE");
4483 rv = session_update_remote_initial_window_size(session,
4484 (int32_t)entry->value);
4486 if (nghttp2_is_fatal(rv)) {
4491 return session_handle_invalid_connection(
4492 session, frame, NGHTTP2_ERR_FLOW_CONTROL, NULL);
4495 session->remote_settings.initial_window_size = entry->value;
4498 case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
4500 if (entry->value < NGHTTP2_MAX_FRAME_SIZE_MIN ||
4501 entry->value > NGHTTP2_MAX_FRAME_SIZE_MAX) {
4502 return session_handle_invalid_connection(
4503 session, frame, NGHTTP2_ERR_PROTO,
4504 "SETTINGS: invalid SETTINGS_MAX_FRAME_SIZE");
4507 session->remote_settings.max_frame_size = entry->value;
4510 case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
4512 session->remote_settings.max_header_list_size = entry->value;
4515 case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
4517 if (entry->value != 0 && entry->value != 1) {
4518 return session_handle_invalid_connection(
4519 session, frame, NGHTTP2_ERR_PROTO,
4520 "SETTINGS: invalid SETTINGS_ENABLE_CONNECT_PROTOCOL");
4523 if (!session->server &&
4524 session->remote_settings.enable_connect_protocol &&
4525 entry->value == 0) {
4526 return session_handle_invalid_connection(
4527 session, frame, NGHTTP2_ERR_PROTO,
4528 "SETTINGS: server attempted to disable "
4529 "SETTINGS_ENABLE_CONNECT_PROTOCOL");
4532 session->remote_settings.enable_connect_protocol = entry->value;
4538 if (!noack && !session_is_closing(session)) {
4539 rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0);
4542 if (nghttp2_is_fatal(rv)) {
4546 return session_handle_invalid_connection(session, frame,
4547 NGHTTP2_ERR_INTERNAL, NULL);
4551 return session_call_on_frame_received(session, frame);
4554 static int session_process_settings_frame(nghttp2_session *session) {
4555 nghttp2_inbound_frame *iframe = &session->iframe;
4556 nghttp2_frame *frame = &iframe->frame;
4558 nghttp2_settings_entry min_header_size_entry;
4560 if (iframe->max_niv) {
4561 min_header_size_entry = iframe->iv[iframe->max_niv - 1];
4563 if (min_header_size_entry.value < UINT32_MAX) {
4564 /* If we have less value, then we must have
4565 SETTINGS_HEADER_TABLE_SIZE in i < iframe->niv */
4566 for (i = 0; i < iframe->niv; ++i) {
4567 if (iframe->iv[i].settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) {
4572 assert(i < iframe->niv);
4574 if (min_header_size_entry.value != iframe->iv[i].value) {
4575 iframe->iv[iframe->niv++] = iframe->iv[i];
4576 iframe->iv[i] = min_header_size_entry;
4581 nghttp2_frame_unpack_settings_payload(&frame->settings, iframe->iv,
4586 iframe->max_niv = 0;
4588 return nghttp2_session_on_settings_received(session, frame, 0 /* ACK */);
4591 int nghttp2_session_on_push_promise_received(nghttp2_session *session,
4592 nghttp2_frame *frame) {
4594 nghttp2_stream *stream;
4595 nghttp2_stream *promised_stream;
4596 nghttp2_priority_spec pri_spec;
4598 if (frame->hd.stream_id == 0) {
4599 return session_inflate_handle_invalid_connection(
4600 session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream_id == 0");
4602 if (session->server || session->local_settings.enable_push == 0) {
4603 return session_inflate_handle_invalid_connection(
4604 session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: push disabled");
4607 if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
4608 return session_inflate_handle_invalid_connection(
4609 session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: invalid stream_id");
4612 if (!session_allow_incoming_new_stream(session)) {
4613 /* We just discard PUSH_PROMISE after GOAWAY was sent */
4614 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
4617 if (!session_is_new_peer_stream_id(session,
4618 frame->push_promise.promised_stream_id)) {
4619 /* The spec says if an endpoint receives a PUSH_PROMISE with
4620 illegal stream ID is subject to a connection error of type
4622 return session_inflate_handle_invalid_connection(
4623 session, frame, NGHTTP2_ERR_PROTO,
4624 "PUSH_PROMISE: invalid promised_stream_id");
4627 if (session_detect_idle_stream(session, frame->hd.stream_id)) {
4628 return session_inflate_handle_invalid_connection(
4629 session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream in idle");
4632 session->last_recv_stream_id = frame->push_promise.promised_stream_id;
4633 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
4634 if (!stream || stream->state == NGHTTP2_STREAM_CLOSING ||
4635 !session->pending_enable_push ||
4636 session->num_incoming_reserved_streams >=
4637 session->max_incoming_reserved_streams) {
4638 /* Currently, client does not retain closed stream, so we don't
4639 check NGHTTP2_SHUT_RD condition here. */
4641 rv = nghttp2_session_add_rst_stream(
4642 session, frame->push_promise.promised_stream_id, NGHTTP2_CANCEL);
4646 return NGHTTP2_ERR_IGN_HEADER_BLOCK;
4649 if (stream->shut_flags & NGHTTP2_SHUT_RD) {
4650 return session_inflate_handle_invalid_connection(
4651 session, frame, NGHTTP2_ERR_STREAM_CLOSED,
4652 "PUSH_PROMISE: stream closed");
4655 nghttp2_priority_spec_init(&pri_spec, stream->stream_id,
4656 NGHTTP2_DEFAULT_WEIGHT, 0);
4658 promised_stream = nghttp2_session_open_stream(
4659 session, frame->push_promise.promised_stream_id, NGHTTP2_STREAM_FLAG_NONE,
4660 &pri_spec, NGHTTP2_STREAM_RESERVED, NULL);
4662 if (!promised_stream) {
4663 return NGHTTP2_ERR_NOMEM;
4666 /* We don't call nghttp2_session_adjust_closed_stream(), since we
4667 don't keep closed stream in client side */
4669 session->last_proc_stream_id = session->last_recv_stream_id;
4670 rv = session_call_on_begin_headers(session, frame);
4677 static int session_process_push_promise_frame(nghttp2_session *session) {
4679 nghttp2_inbound_frame *iframe = &session->iframe;
4680 nghttp2_frame *frame = &iframe->frame;
4682 rv = nghttp2_frame_unpack_push_promise_payload(&frame->push_promise,
4686 return nghttp2_session_terminate_session_with_reason(
4687 session, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: could not unpack");
4690 return nghttp2_session_on_push_promise_received(session, frame);
4693 int nghttp2_session_on_ping_received(nghttp2_session *session,
4694 nghttp2_frame *frame) {
4696 if (frame->hd.stream_id != 0) {
4697 return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4698 "PING: stream_id != 0");
4700 if ((session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_PING_ACK) == 0 &&
4701 (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 &&
4702 !session_is_closing(session)) {
4703 /* Peer sent ping, so ping it back */
4704 rv = nghttp2_session_add_ping(session, NGHTTP2_FLAG_ACK,
4705 frame->ping.opaque_data);
4710 return session_call_on_frame_received(session, frame);
4713 static int session_process_ping_frame(nghttp2_session *session) {
4714 nghttp2_inbound_frame *iframe = &session->iframe;
4715 nghttp2_frame *frame = &iframe->frame;
4717 nghttp2_frame_unpack_ping_payload(&frame->ping, iframe->sbuf.pos);
4719 return nghttp2_session_on_ping_received(session, frame);
4722 int nghttp2_session_on_goaway_received(nghttp2_session *session,
4723 nghttp2_frame *frame) {
4726 if (frame->hd.stream_id != 0) {
4727 return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4728 "GOAWAY: stream_id != 0");
4730 /* Spec says Endpoints MUST NOT increase the value they send in the
4731 last stream identifier. */
4732 if ((frame->goaway.last_stream_id > 0 &&
4733 !nghttp2_session_is_my_stream_id(session,
4734 frame->goaway.last_stream_id)) ||
4735 session->remote_last_stream_id < frame->goaway.last_stream_id) {
4736 return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4737 "GOAWAY: invalid last_stream_id");
4740 session->goaway_flags |= NGHTTP2_GOAWAY_RECV;
4742 session->remote_last_stream_id = frame->goaway.last_stream_id;
4744 rv = session_call_on_frame_received(session, frame);
4746 if (nghttp2_is_fatal(rv)) {
4750 return session_close_stream_on_goaway(session, frame->goaway.last_stream_id,
4754 static int session_process_goaway_frame(nghttp2_session *session) {
4755 nghttp2_inbound_frame *iframe = &session->iframe;
4756 nghttp2_frame *frame = &iframe->frame;
4758 nghttp2_frame_unpack_goaway_payload(&frame->goaway, iframe->sbuf.pos,
4760 nghttp2_buf_len(&iframe->lbuf));
4762 nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
4764 return nghttp2_session_on_goaway_received(session, frame);
4768 session_on_connection_window_update_received(nghttp2_session *session,
4769 nghttp2_frame *frame) {
4770 /* Handle connection-level flow control */
4771 if (frame->window_update.window_size_increment == 0) {
4772 return session_handle_invalid_connection(
4773 session, frame, NGHTTP2_ERR_PROTO,
4774 "WINDOW_UPDATE: window_size_increment == 0");
4777 if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
4778 session->remote_window_size) {
4779 return session_handle_invalid_connection(session, frame,
4780 NGHTTP2_ERR_FLOW_CONTROL, NULL);
4782 session->remote_window_size += frame->window_update.window_size_increment;
4784 return session_call_on_frame_received(session, frame);
4787 static int session_on_stream_window_update_received(nghttp2_session *session,
4788 nghttp2_frame *frame) {
4790 nghttp2_stream *stream;
4792 if (session_detect_idle_stream(session, frame->hd.stream_id)) {
4793 return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4794 "WINDOW_UPDATE to idle stream");
4797 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
4801 if (state_reserved_remote(session, stream)) {
4802 return session_handle_invalid_connection(
4803 session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPADATE to reserved stream");
4805 if (frame->window_update.window_size_increment == 0) {
4806 return session_handle_invalid_connection(
4807 session, frame, NGHTTP2_ERR_PROTO,
4808 "WINDOW_UPDATE: window_size_increment == 0");
4810 if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
4811 stream->remote_window_size) {
4812 return session_handle_invalid_stream(session, frame,
4813 NGHTTP2_ERR_FLOW_CONTROL);
4815 stream->remote_window_size += frame->window_update.window_size_increment;
4817 if (stream->remote_window_size > 0 &&
4818 nghttp2_stream_check_deferred_by_flow_control(stream)) {
4820 rv = nghttp2_stream_resume_deferred_item(
4821 stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
4823 if (nghttp2_is_fatal(rv)) {
4827 return session_call_on_frame_received(session, frame);
4830 int nghttp2_session_on_window_update_received(nghttp2_session *session,
4831 nghttp2_frame *frame) {
4832 if (frame->hd.stream_id == 0) {
4833 return session_on_connection_window_update_received(session, frame);
4835 return session_on_stream_window_update_received(session, frame);
4839 static int session_process_window_update_frame(nghttp2_session *session) {
4840 nghttp2_inbound_frame *iframe = &session->iframe;
4841 nghttp2_frame *frame = &iframe->frame;
4843 nghttp2_frame_unpack_window_update_payload(&frame->window_update,
4846 return nghttp2_session_on_window_update_received(session, frame);
4849 int nghttp2_session_on_altsvc_received(nghttp2_session *session,
4850 nghttp2_frame *frame) {
4851 nghttp2_ext_altsvc *altsvc;
4852 nghttp2_stream *stream;
4854 altsvc = frame->ext.payload;
4856 /* session->server case has been excluded */
4858 if (frame->hd.stream_id == 0) {
4859 if (altsvc->origin_len == 0) {
4860 return session_call_on_invalid_frame_recv_callback(session, frame,
4864 if (altsvc->origin_len > 0) {
4865 return session_call_on_invalid_frame_recv_callback(session, frame,
4869 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
4874 if (stream->state == NGHTTP2_STREAM_CLOSING) {
4879 if (altsvc->field_value_len == 0) {
4880 return session_call_on_invalid_frame_recv_callback(session, frame,
4884 return session_call_on_frame_received(session, frame);
4887 int nghttp2_session_on_origin_received(nghttp2_session *session,
4888 nghttp2_frame *frame) {
4889 return session_call_on_frame_received(session, frame);
4892 static int session_process_altsvc_frame(nghttp2_session *session) {
4893 nghttp2_inbound_frame *iframe = &session->iframe;
4894 nghttp2_frame *frame = &iframe->frame;
4896 nghttp2_frame_unpack_altsvc_payload(
4897 &frame->ext, nghttp2_get_uint16(iframe->sbuf.pos), iframe->lbuf.pos,
4898 nghttp2_buf_len(&iframe->lbuf));
4900 /* nghttp2_frame_unpack_altsvc_payload steals buffer from
4902 nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
4904 return nghttp2_session_on_altsvc_received(session, frame);
4907 static int session_process_origin_frame(nghttp2_session *session) {
4908 nghttp2_inbound_frame *iframe = &session->iframe;
4909 nghttp2_frame *frame = &iframe->frame;
4910 nghttp2_mem *mem = &session->mem;
4913 rv = nghttp2_frame_unpack_origin_payload(&frame->ext, iframe->lbuf.pos,
4914 nghttp2_buf_len(&iframe->lbuf), mem);
4916 if (nghttp2_is_fatal(rv)) {
4919 /* Ignore ORIGIN frame which cannot be parsed. */
4923 return nghttp2_session_on_origin_received(session, frame);
4926 static int session_process_extension_frame(nghttp2_session *session) {
4928 nghttp2_inbound_frame *iframe = &session->iframe;
4929 nghttp2_frame *frame = &iframe->frame;
4931 rv = session_call_unpack_extension_callback(session);
4932 if (nghttp2_is_fatal(rv)) {
4936 /* This handles the case where rv == NGHTTP2_ERR_CANCEL as well */
4941 return session_call_on_frame_received(session, frame);
4944 int nghttp2_session_on_data_received(nghttp2_session *session,
4945 nghttp2_frame *frame) {
4947 nghttp2_stream *stream;
4949 /* We don't call on_frame_recv_callback if stream has been closed
4950 already or being closed. */
4951 stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
4952 if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) {
4953 /* This should be treated as stream error, but it results in lots
4954 of RST_STREAM. So just ignore frame against nonexistent stream
4959 if (session_enforce_http_messaging(session) &&
4960 (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
4961 if (nghttp2_http_on_remote_end_stream(stream) != 0) {
4962 rv = nghttp2_session_add_rst_stream(session, stream->stream_id,
4963 NGHTTP2_PROTOCOL_ERROR);
4964 if (nghttp2_is_fatal(rv)) {
4968 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
4969 /* Don't call nghttp2_session_close_stream_if_shut_rdwr because
4970 RST_STREAM has been submitted. */
4975 rv = session_call_on_frame_received(session, frame);
4976 if (nghttp2_is_fatal(rv)) {
4980 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
4981 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
4982 rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
4983 if (nghttp2_is_fatal(rv)) {
4990 /* For errors, this function only returns FATAL error. */
4991 static int session_process_data_frame(nghttp2_session *session) {
4993 nghttp2_frame *public_data_frame = &session->iframe.frame;
4994 rv = nghttp2_session_on_data_received(session, public_data_frame);
4995 if (nghttp2_is_fatal(rv)) {
5002 * Now we have SETTINGS synchronization, flow control error can be
5003 * detected strictly. If DATA frame is received with length > 0 and
5004 * current received window size + delta length is strictly larger than
5005 * local window size, it is subject to FLOW_CONTROL_ERROR, so return
5006 * -1. Note that local_window_size is calculated after SETTINGS ACK is
5007 * received from peer, so peer must honor this limit. If the resulting
5008 * recv_window_size is strictly larger than NGHTTP2_MAX_WINDOW_SIZE,
5011 static int adjust_recv_window_size(int32_t *recv_window_size_ptr, size_t delta,
5012 int32_t local_window_size) {
5013 if (*recv_window_size_ptr > local_window_size - (int32_t)delta ||
5014 *recv_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - (int32_t)delta) {
5017 *recv_window_size_ptr += (int32_t)delta;
5021 int nghttp2_session_update_recv_stream_window_size(nghttp2_session *session,
5022 nghttp2_stream *stream,
5024 int send_window_update) {
5026 rv = adjust_recv_window_size(&stream->recv_window_size, delta_size,
5027 stream->local_window_size);
5029 return nghttp2_session_add_rst_stream(session, stream->stream_id,
5030 NGHTTP2_FLOW_CONTROL_ERROR);
5032 /* We don't have to send WINDOW_UPDATE if the data received is the
5033 last chunk in the incoming stream. */
5034 /* We have to use local_settings here because it is the constraint
5035 the remote endpoint should honor. */
5036 if (send_window_update &&
5037 !(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) &&
5038 stream->window_update_queued == 0 &&
5039 nghttp2_should_send_window_update(stream->local_window_size,
5040 stream->recv_window_size)) {
5041 rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE,
5043 stream->recv_window_size);
5048 stream->recv_window_size = 0;
5053 int nghttp2_session_update_recv_connection_window_size(nghttp2_session *session,
5054 size_t delta_size) {
5056 rv = adjust_recv_window_size(&session->recv_window_size, delta_size,
5057 session->local_window_size);
5059 return nghttp2_session_terminate_session(session,
5060 NGHTTP2_FLOW_CONTROL_ERROR);
5062 if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) &&
5063 session->window_update_queued == 0 &&
5064 nghttp2_should_send_window_update(session->local_window_size,
5065 session->recv_window_size)) {
5066 /* Use stream ID 0 to update connection-level flow control
5068 rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, 0,
5069 session->recv_window_size);
5074 session->recv_window_size = 0;
5079 static int session_update_consumed_size(nghttp2_session *session,
5080 int32_t *consumed_size_ptr,
5081 int32_t *recv_window_size_ptr,
5082 uint8_t window_update_queued,
5083 int32_t stream_id, size_t delta_size,
5084 int32_t local_window_size) {
5088 if ((size_t)*consumed_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta_size) {
5089 return nghttp2_session_terminate_session(session,
5090 NGHTTP2_FLOW_CONTROL_ERROR);
5093 *consumed_size_ptr += (int32_t)delta_size;
5095 if (window_update_queued == 0) {
5096 /* recv_window_size may be smaller than consumed_size, because it
5097 may be decreased by negative value with
5098 nghttp2_submit_window_update(). */
5099 recv_size = nghttp2_min(*consumed_size_ptr, *recv_window_size_ptr);
5101 if (nghttp2_should_send_window_update(local_window_size, recv_size)) {
5102 rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE,
5103 stream_id, recv_size);
5109 *recv_window_size_ptr -= recv_size;
5110 *consumed_size_ptr -= recv_size;
5117 static int session_update_stream_consumed_size(nghttp2_session *session,
5118 nghttp2_stream *stream,
5119 size_t delta_size) {
5120 return session_update_consumed_size(
5121 session, &stream->consumed_size, &stream->recv_window_size,
5122 stream->window_update_queued, stream->stream_id, delta_size,
5123 stream->local_window_size);
5126 static int session_update_connection_consumed_size(nghttp2_session *session,
5127 size_t delta_size) {
5128 return session_update_consumed_size(
5129 session, &session->consumed_size, &session->recv_window_size,
5130 session->window_update_queued, 0, delta_size, session->local_window_size);
5134 * Checks that we can receive the DATA frame for stream, which is
5135 * indicated by |session->iframe.frame.hd.stream_id|. If it is a
5136 * connection error situation, GOAWAY frame will be issued by this
5139 * If the DATA frame is allowed, returns 0.
5141 * This function returns 0 if it succeeds, or one of the following
5142 * negative error codes:
5144 * NGHTTP2_ERR_IGN_PAYLOAD
5145 * The reception of DATA frame is connection error; or should be
5150 static int session_on_data_received_fail_fast(nghttp2_session *session) {
5152 nghttp2_stream *stream;
5153 nghttp2_inbound_frame *iframe;
5155 const char *failure_reason;
5156 uint32_t error_code = NGHTTP2_PROTOCOL_ERROR;
5158 iframe = &session->iframe;
5159 stream_id = iframe->frame.hd.stream_id;
5161 if (stream_id == 0) {
5162 /* The spec says that if a DATA frame is received whose stream ID
5163 is 0, the recipient MUST respond with a connection error of
5164 type PROTOCOL_ERROR. */
5165 failure_reason = "DATA: stream_id == 0";
5169 if (session_detect_idle_stream(session, stream_id)) {
5170 failure_reason = "DATA: stream in idle";
5171 error_code = NGHTTP2_PROTOCOL_ERROR;
5175 stream = nghttp2_session_get_stream(session, stream_id);
5177 stream = nghttp2_session_get_stream_raw(session, stream_id);
5178 if (stream && (stream->shut_flags & NGHTTP2_SHUT_RD)) {
5179 failure_reason = "DATA: stream closed";
5180 error_code = NGHTTP2_STREAM_CLOSED;
5184 return NGHTTP2_ERR_IGN_PAYLOAD;
5186 if (stream->shut_flags & NGHTTP2_SHUT_RD) {
5187 failure_reason = "DATA: stream in half-closed(remote)";
5188 error_code = NGHTTP2_STREAM_CLOSED;
5192 if (nghttp2_session_is_my_stream_id(session, stream_id)) {
5193 if (stream->state == NGHTTP2_STREAM_CLOSING) {
5194 return NGHTTP2_ERR_IGN_PAYLOAD;
5196 if (stream->state != NGHTTP2_STREAM_OPENED) {
5197 failure_reason = "DATA: stream not opened";
5202 if (stream->state == NGHTTP2_STREAM_RESERVED) {
5203 failure_reason = "DATA: stream in reserved";
5206 if (stream->state == NGHTTP2_STREAM_CLOSING) {
5207 return NGHTTP2_ERR_IGN_PAYLOAD;
5211 rv = nghttp2_session_terminate_session_with_reason(session, error_code,
5213 if (nghttp2_is_fatal(rv)) {
5216 return NGHTTP2_ERR_IGN_PAYLOAD;
5219 static size_t inbound_frame_payload_readlen(nghttp2_inbound_frame *iframe,
5221 const uint8_t *last) {
5222 return nghttp2_min((size_t)(last - in), iframe->payloadleft);
5226 * Resets iframe->sbuf and advance its mark pointer by |left| bytes.
5228 static void inbound_frame_set_mark(nghttp2_inbound_frame *iframe, size_t left) {
5229 nghttp2_buf_reset(&iframe->sbuf);
5230 iframe->sbuf.mark += left;
5233 static size_t inbound_frame_buf_read(nghttp2_inbound_frame *iframe,
5234 const uint8_t *in, const uint8_t *last) {
5238 nghttp2_min((size_t)(last - in), nghttp2_buf_mark_avail(&iframe->sbuf));
5240 iframe->sbuf.last = nghttp2_cpymem(iframe->sbuf.last, in, readlen);
5246 * Unpacks SETTINGS entry in iframe->sbuf.
5248 static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) {
5249 nghttp2_settings_entry iv;
5250 nghttp2_settings_entry *min_header_table_size_entry;
5253 nghttp2_frame_unpack_settings_entry(&iv, iframe->sbuf.pos);
5255 switch (iv.settings_id) {
5256 case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
5257 case NGHTTP2_SETTINGS_ENABLE_PUSH:
5258 case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
5259 case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
5260 case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
5261 case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
5262 case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
5265 DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id);
5267 iframe->iv[iframe->niv++] = iv;
5272 for (i = 0; i < iframe->niv; ++i) {
5273 if (iframe->iv[i].settings_id == iv.settings_id) {
5279 if (i == iframe->niv) {
5280 iframe->iv[iframe->niv++] = iv;
5283 if (iv.settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) {
5284 /* Keep track of minimum value of SETTINGS_HEADER_TABLE_SIZE */
5285 min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1];
5287 if (iv.value < min_header_table_size_entry->value) {
5288 min_header_table_size_entry->value = iv.value;
5294 * Checks PADDED flags and set iframe->sbuf to read them accordingly.
5295 * If padding is set, this function returns 1. If no padding is set,
5296 * this function returns 0. On error, returns -1.
5298 static int inbound_frame_handle_pad(nghttp2_inbound_frame *iframe,
5299 nghttp2_frame_hd *hd) {
5300 if (hd->flags & NGHTTP2_FLAG_PADDED) {
5301 if (hd->length < 1) {
5304 inbound_frame_set_mark(iframe, 1);
5307 DEBUGF("recv: no padding in payload\n");
5312 * Computes number of padding based on flags. This function returns
5313 * the calculated length if it succeeds, or -1.
5315 static ssize_t inbound_frame_compute_pad(nghttp2_inbound_frame *iframe) {
5318 /* 1 for Pad Length field */
5319 padlen = (size_t)(iframe->sbuf.pos[0] + 1);
5321 DEBUGF("recv: padlen=%zu\n", padlen);
5323 /* We cannot use iframe->frame.hd.length because of CONTINUATION */
5324 if (padlen - 1 > iframe->payloadleft) {
5328 iframe->padlen = padlen;
5330 return (ssize_t)padlen;
5334 * This function returns the effective payload length in the data of
5335 * length |readlen| when the remaning payload is |payloadleft|. The
5336 * |payloadleft| does not include |readlen|. If padding was started
5337 * strictly before this data chunk, this function returns -1.
5339 static ssize_t inbound_frame_effective_readlen(nghttp2_inbound_frame *iframe,
5342 size_t trail_padlen =
5343 nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen);
5345 if (trail_padlen > payloadleft) {
5347 padlen = trail_padlen - payloadleft;
5348 if (readlen < padlen) {
5351 return (ssize_t)(readlen - padlen);
5353 return (ssize_t)(readlen);
5356 ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
5358 const uint8_t *first = in, *last = in + inlen;
5359 nghttp2_inbound_frame *iframe = &session->iframe;
5364 nghttp2_frame_hd cont_hd;
5365 nghttp2_stream *stream;
5366 size_t pri_fieldlen;
5369 DEBUGF("recv: connection recv_window_size=%d, local_window=%d\n",
5370 session->recv_window_size, session->local_window_size);
5372 mem = &session->mem;
5374 /* We may have idle streams more than we expect (e.g.,
5375 nghttp2_session_change_stream_priority() or
5376 nghttp2_session_create_idle_stream()). Adjust them here. */
5377 rv = nghttp2_session_adjust_idle_stream(session);
5378 if (nghttp2_is_fatal(rv)) {
5382 if (!nghttp2_session_want_read(session)) {
5383 return (ssize_t)inlen;
5387 switch (iframe->state) {
5388 case NGHTTP2_IB_READ_CLIENT_MAGIC:
5389 readlen = nghttp2_min(inlen, iframe->payloadleft);
5391 if (memcmp(&NGHTTP2_CLIENT_MAGIC[NGHTTP2_CLIENT_MAGIC_LEN -
5392 iframe->payloadleft],
5393 in, readlen) != 0) {
5394 return NGHTTP2_ERR_BAD_CLIENT_MAGIC;
5397 iframe->payloadleft -= readlen;
5400 if (iframe->payloadleft == 0) {
5401 session_inbound_frame_reset(session);
5402 iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS;
5406 case NGHTTP2_IB_READ_FIRST_SETTINGS:
5407 DEBUGF("recv: [IB_READ_FIRST_SETTINGS]\n");
5409 readlen = inbound_frame_buf_read(iframe, in, last);
5412 if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
5416 if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS ||
5417 (iframe->sbuf.pos[4] & NGHTTP2_FLAG_ACK)) {
5418 rv = session_call_error_callback(
5419 session, NGHTTP2_ERR_SETTINGS_EXPECTED,
5420 "Remote peer returned unexpected data while we expected "
5421 "SETTINGS frame. Perhaps, peer does not support HTTP/2 "
5424 if (nghttp2_is_fatal(rv)) {
5428 rv = nghttp2_session_terminate_session_with_reason(
5429 session, NGHTTP2_PROTOCOL_ERROR, "SETTINGS expected");
5431 if (nghttp2_is_fatal(rv)) {
5435 return (ssize_t)inlen;
5438 iframe->state = NGHTTP2_IB_READ_HEAD;
5441 case NGHTTP2_IB_READ_HEAD: {
5442 int on_begin_frame_called = 0;
5444 DEBUGF("recv: [IB_READ_HEAD]\n");
5446 readlen = inbound_frame_buf_read(iframe, in, last);
5449 if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
5453 nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->sbuf.pos);
5454 iframe->payloadleft = iframe->frame.hd.length;
5456 DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n",
5457 iframe->frame.hd.length, iframe->frame.hd.type,
5458 iframe->frame.hd.flags, iframe->frame.hd.stream_id);
5460 if (iframe->frame.hd.length > session->local_settings.max_frame_size) {
5461 DEBUGF("recv: length is too large %zu > %u\n", iframe->frame.hd.length,
5462 session->local_settings.max_frame_size);
5464 rv = nghttp2_session_terminate_session_with_reason(
5465 session, NGHTTP2_FRAME_SIZE_ERROR, "too large frame size");
5467 if (nghttp2_is_fatal(rv)) {
5471 return (ssize_t)inlen;
5474 switch (iframe->frame.hd.type) {
5475 case NGHTTP2_DATA: {
5476 DEBUGF("recv: DATA\n");
5478 iframe->frame.hd.flags &=
5479 (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PADDED);
5480 /* Check stream is open. If it is not open or closing,
5484 rv = session_on_data_received_fail_fast(session);
5485 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
5486 return (ssize_t)inlen;
5488 if (rv == NGHTTP2_ERR_IGN_PAYLOAD) {
5489 DEBUGF("recv: DATA not allowed stream_id=%d\n",
5490 iframe->frame.hd.stream_id);
5491 iframe->state = NGHTTP2_IB_IGN_DATA;
5495 if (nghttp2_is_fatal(rv)) {
5499 rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
5501 rv = nghttp2_session_terminate_session_with_reason(
5502 session, NGHTTP2_PROTOCOL_ERROR,
5503 "DATA: insufficient padding space");
5505 if (nghttp2_is_fatal(rv)) {
5508 return (ssize_t)inlen;
5512 iframe->state = NGHTTP2_IB_READ_PAD_DATA;
5516 iframe->state = NGHTTP2_IB_READ_DATA;
5519 case NGHTTP2_HEADERS:
5521 DEBUGF("recv: HEADERS\n");
5523 iframe->frame.hd.flags &=
5524 (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS |
5525 NGHTTP2_FLAG_PADDED | NGHTTP2_FLAG_PRIORITY);
5527 rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
5529 rv = nghttp2_session_terminate_session_with_reason(
5530 session, NGHTTP2_PROTOCOL_ERROR,
5531 "HEADERS: insufficient padding space");
5532 if (nghttp2_is_fatal(rv)) {
5535 return (ssize_t)inlen;
5539 iframe->state = NGHTTP2_IB_READ_NBYTE;
5543 pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags);
5545 if (pri_fieldlen > 0) {
5546 if (iframe->payloadleft < pri_fieldlen) {
5548 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5552 iframe->state = NGHTTP2_IB_READ_NBYTE;
5554 inbound_frame_set_mark(iframe, pri_fieldlen);
5559 /* Call on_begin_frame_callback here because
5560 session_process_headers_frame() may call
5561 on_begin_headers_callback */
5562 rv = session_call_on_begin_frame(session, &iframe->frame.hd);
5564 if (nghttp2_is_fatal(rv)) {
5568 on_begin_frame_called = 1;
5570 rv = session_process_headers_frame(session);
5571 if (nghttp2_is_fatal(rv)) {
5577 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
5578 return (ssize_t)inlen;
5581 if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
5582 rv = nghttp2_session_add_rst_stream(
5583 session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR);
5584 if (nghttp2_is_fatal(rv)) {
5587 iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
5591 if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
5592 iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
5596 iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
5599 case NGHTTP2_PRIORITY:
5600 DEBUGF("recv: PRIORITY\n");
5602 iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
5604 if (iframe->payloadleft != NGHTTP2_PRIORITY_SPECLEN) {
5607 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5612 iframe->state = NGHTTP2_IB_READ_NBYTE;
5614 inbound_frame_set_mark(iframe, NGHTTP2_PRIORITY_SPECLEN);
5617 case NGHTTP2_RST_STREAM:
5618 case NGHTTP2_WINDOW_UPDATE:
5620 switch (iframe->frame.hd.type) {
5621 case NGHTTP2_RST_STREAM:
5622 DEBUGF("recv: RST_STREAM\n");
5624 case NGHTTP2_WINDOW_UPDATE:
5625 DEBUGF("recv: WINDOW_UPDATE\n");
5628 #endif /* DEBUGBUILD */
5630 iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
5632 if (iframe->payloadleft != 4) {
5634 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5638 iframe->state = NGHTTP2_IB_READ_NBYTE;
5640 inbound_frame_set_mark(iframe, 4);
5643 case NGHTTP2_SETTINGS:
5644 DEBUGF("recv: SETTINGS\n");
5646 iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK;
5648 if ((iframe->frame.hd.length % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) ||
5649 ((iframe->frame.hd.flags & NGHTTP2_FLAG_ACK) &&
5650 iframe->payloadleft > 0)) {
5652 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5656 /* Check the settings flood counter early to be safe */
5657 if (session->obq_flood_counter_ >= session->max_outbound_ack &&
5658 !(iframe->frame.hd.flags & NGHTTP2_FLAG_ACK)) {
5659 return NGHTTP2_ERR_FLOODED;
5662 iframe->state = NGHTTP2_IB_READ_SETTINGS;
5664 if (iframe->payloadleft) {
5665 nghttp2_settings_entry *min_header_table_size_entry;
5667 /* We allocate iv with additional one entry, to store the
5668 minimum header table size. */
5670 iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1;
5672 if (iframe->max_niv - 1 > session->max_settings) {
5673 rv = nghttp2_session_terminate_session_with_reason(
5674 session, NGHTTP2_ENHANCE_YOUR_CALM,
5675 "SETTINGS: too many setting entries");
5676 if (nghttp2_is_fatal(rv)) {
5679 return (ssize_t)inlen;
5682 iframe->iv = nghttp2_mem_malloc(mem, sizeof(nghttp2_settings_entry) *
5686 return NGHTTP2_ERR_NOMEM;
5689 min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1];
5690 min_header_table_size_entry->settings_id =
5691 NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
5692 min_header_table_size_entry->value = UINT32_MAX;
5694 inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH);
5700 inbound_frame_set_mark(iframe, 0);
5703 case NGHTTP2_PUSH_PROMISE:
5704 DEBUGF("recv: PUSH_PROMISE\n");
5706 iframe->frame.hd.flags &=
5707 (NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PADDED);
5709 rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
5711 rv = nghttp2_session_terminate_session_with_reason(
5712 session, NGHTTP2_PROTOCOL_ERROR,
5713 "PUSH_PROMISE: insufficient padding space");
5714 if (nghttp2_is_fatal(rv)) {
5717 return (ssize_t)inlen;
5721 iframe->state = NGHTTP2_IB_READ_NBYTE;
5725 if (iframe->payloadleft < 4) {
5727 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5731 iframe->state = NGHTTP2_IB_READ_NBYTE;
5733 inbound_frame_set_mark(iframe, 4);
5737 DEBUGF("recv: PING\n");
5739 iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK;
5741 if (iframe->payloadleft != 8) {
5743 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5747 iframe->state = NGHTTP2_IB_READ_NBYTE;
5748 inbound_frame_set_mark(iframe, 8);
5751 case NGHTTP2_GOAWAY:
5752 DEBUGF("recv: GOAWAY\n");
5754 iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
5756 if (iframe->payloadleft < 8) {
5758 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5762 iframe->state = NGHTTP2_IB_READ_NBYTE;
5763 inbound_frame_set_mark(iframe, 8);
5766 case NGHTTP2_CONTINUATION:
5767 DEBUGF("recv: unexpected CONTINUATION\n");
5769 /* Receiving CONTINUATION in this state are subject to
5770 connection error of type PROTOCOL_ERROR */
5771 rv = nghttp2_session_terminate_session_with_reason(
5772 session, NGHTTP2_PROTOCOL_ERROR, "CONTINUATION: unexpected");
5773 if (nghttp2_is_fatal(rv)) {
5777 return (ssize_t)inlen;
5779 DEBUGF("recv: extension frame\n");
5781 if (check_ext_type_set(session->user_recv_ext_types,
5782 iframe->frame.hd.type)) {
5783 if (!session->callbacks.unpack_extension_callback) {
5784 /* Silently ignore unknown frame type. */
5788 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5795 iframe->state = NGHTTP2_IB_READ_EXTENSION_PAYLOAD;
5799 switch (iframe->frame.hd.type) {
5800 case NGHTTP2_ALTSVC:
5801 if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) ==
5804 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5808 DEBUGF("recv: ALTSVC\n");
5810 iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
5811 iframe->frame.ext.payload = &iframe->ext_frame_payload.altsvc;
5813 if (session->server) {
5815 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5819 if (iframe->payloadleft < 2) {
5821 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5827 iframe->state = NGHTTP2_IB_READ_NBYTE;
5828 inbound_frame_set_mark(iframe, 2);
5831 case NGHTTP2_ORIGIN:
5832 if (!(session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN)) {
5834 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5838 DEBUGF("recv: ORIGIN\n");
5840 iframe->frame.ext.payload = &iframe->ext_frame_payload.origin;
5842 if (session->server || iframe->frame.hd.stream_id ||
5843 (iframe->frame.hd.flags & 0xf0)) {
5845 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5849 iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
5851 if (iframe->payloadleft) {
5852 iframe->raw_lbuf = nghttp2_mem_malloc(mem, iframe->payloadleft);
5854 if (iframe->raw_lbuf == NULL) {
5855 return NGHTTP2_ERR_NOMEM;
5858 nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf,
5859 iframe->payloadleft);
5864 iframe->state = NGHTTP2_IB_READ_ORIGIN_PAYLOAD;
5870 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5877 if (!on_begin_frame_called) {
5878 switch (iframe->state) {
5879 case NGHTTP2_IB_IGN_HEADER_BLOCK:
5880 case NGHTTP2_IB_IGN_PAYLOAD:
5881 case NGHTTP2_IB_FRAME_SIZE_ERROR:
5882 case NGHTTP2_IB_IGN_DATA:
5883 case NGHTTP2_IB_IGN_ALL:
5886 rv = session_call_on_begin_frame(session, &iframe->frame.hd);
5888 if (nghttp2_is_fatal(rv)) {
5896 case NGHTTP2_IB_READ_NBYTE:
5897 DEBUGF("recv: [IB_READ_NBYTE]\n");
5899 readlen = inbound_frame_buf_read(iframe, in, last);
5901 iframe->payloadleft -= readlen;
5903 DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zd\n", readlen,
5904 iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf));
5906 if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
5910 switch (iframe->frame.hd.type) {
5911 case NGHTTP2_HEADERS:
5912 if (iframe->padlen == 0 &&
5913 (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) {
5914 pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags);
5915 padlen = inbound_frame_compute_pad(iframe);
5917 (size_t)padlen + pri_fieldlen > 1 + iframe->payloadleft) {
5918 rv = nghttp2_session_terminate_session_with_reason(
5919 session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: invalid padding");
5920 if (nghttp2_is_fatal(rv)) {
5923 return (ssize_t)inlen;
5925 iframe->frame.headers.padlen = (size_t)padlen;
5927 if (pri_fieldlen > 0) {
5928 if (iframe->payloadleft < pri_fieldlen) {
5930 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5933 iframe->state = NGHTTP2_IB_READ_NBYTE;
5934 inbound_frame_set_mark(iframe, pri_fieldlen);
5937 /* Truncate buffers used for padding spec */
5938 inbound_frame_set_mark(iframe, 0);
5942 rv = session_process_headers_frame(session);
5943 if (nghttp2_is_fatal(rv)) {
5949 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
5950 return (ssize_t)inlen;
5953 if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
5954 rv = nghttp2_session_add_rst_stream(
5955 session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR);
5956 if (nghttp2_is_fatal(rv)) {
5959 iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
5963 if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
5964 iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
5968 iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
5971 case NGHTTP2_PRIORITY:
5972 rv = session_process_priority_frame(session);
5973 if (nghttp2_is_fatal(rv)) {
5977 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
5978 return (ssize_t)inlen;
5981 session_inbound_frame_reset(session);
5984 case NGHTTP2_RST_STREAM:
5985 rv = session_process_rst_stream_frame(session);
5986 if (nghttp2_is_fatal(rv)) {
5990 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
5991 return (ssize_t)inlen;
5994 session_inbound_frame_reset(session);
5997 case NGHTTP2_PUSH_PROMISE:
5998 if (iframe->padlen == 0 &&
5999 (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) {
6000 padlen = inbound_frame_compute_pad(iframe);
6001 if (padlen < 0 || (size_t)padlen + 4 /* promised stream id */
6002 > 1 + iframe->payloadleft) {
6003 rv = nghttp2_session_terminate_session_with_reason(
6004 session, NGHTTP2_PROTOCOL_ERROR,
6005 "PUSH_PROMISE: invalid padding");
6006 if (nghttp2_is_fatal(rv)) {
6009 return (ssize_t)inlen;
6012 iframe->frame.push_promise.padlen = (size_t)padlen;
6014 if (iframe->payloadleft < 4) {
6016 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
6020 iframe->state = NGHTTP2_IB_READ_NBYTE;
6022 inbound_frame_set_mark(iframe, 4);
6027 rv = session_process_push_promise_frame(session);
6028 if (nghttp2_is_fatal(rv)) {
6034 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6035 return (ssize_t)inlen;
6038 if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
6039 rv = nghttp2_session_add_rst_stream(
6040 session, iframe->frame.push_promise.promised_stream_id,
6041 NGHTTP2_INTERNAL_ERROR);
6042 if (nghttp2_is_fatal(rv)) {
6045 iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
6049 if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
6050 iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
6054 iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
6058 rv = session_process_ping_frame(session);
6059 if (nghttp2_is_fatal(rv)) {
6063 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6064 return (ssize_t)inlen;
6067 session_inbound_frame_reset(session);
6070 case NGHTTP2_GOAWAY: {
6073 /* 8 is Last-stream-ID + Error Code */
6074 debuglen = iframe->frame.hd.length - 8;
6077 iframe->raw_lbuf = nghttp2_mem_malloc(mem, debuglen);
6079 if (iframe->raw_lbuf == NULL) {
6080 return NGHTTP2_ERR_NOMEM;
6083 nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, debuglen);
6088 iframe->state = NGHTTP2_IB_READ_GOAWAY_DEBUG;
6092 case NGHTTP2_WINDOW_UPDATE:
6093 rv = session_process_window_update_frame(session);
6094 if (nghttp2_is_fatal(rv)) {
6098 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6099 return (ssize_t)inlen;
6102 session_inbound_frame_reset(session);
6105 case NGHTTP2_ALTSVC: {
6108 origin_len = nghttp2_get_uint16(iframe->sbuf.pos);
6110 DEBUGF("recv: origin_len=%zu\n", origin_len);
6112 if (origin_len > iframe->payloadleft) {
6114 iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
6118 if (iframe->frame.hd.length > 2) {
6120 nghttp2_mem_malloc(mem, iframe->frame.hd.length - 2);
6122 if (iframe->raw_lbuf == NULL) {
6123 return NGHTTP2_ERR_NOMEM;
6126 nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf,
6127 iframe->frame.hd.length);
6132 iframe->state = NGHTTP2_IB_READ_ALTSVC_PAYLOAD;
6137 /* This is unknown frame */
6138 session_inbound_frame_reset(session);
6143 case NGHTTP2_IB_READ_HEADER_BLOCK:
6144 case NGHTTP2_IB_IGN_HEADER_BLOCK: {
6145 ssize_t data_readlen;
6146 size_t trail_padlen;
6149 if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {
6150 DEBUGF("recv: [IB_READ_HEADER_BLOCK]\n");
6152 DEBUGF("recv: [IB_IGN_HEADER_BLOCK]\n");
6154 #endif /* DEBUGBUILD */
6156 readlen = inbound_frame_payload_readlen(iframe, in, last);
6158 DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6159 iframe->payloadleft - readlen);
6161 data_readlen = inbound_frame_effective_readlen(
6162 iframe, iframe->payloadleft - readlen, readlen);
6164 if (data_readlen == -1) {
6165 /* everything is padding */
6169 trail_padlen = nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen);
6171 final = (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) &&
6172 iframe->payloadleft - (size_t)data_readlen == trail_padlen;
6174 if (data_readlen > 0 || (data_readlen == 0 && final)) {
6175 size_t hd_proclen = 0;
6177 DEBUGF("recv: block final=%d\n", final);
6180 inflate_header_block(session, &iframe->frame, &hd_proclen,
6181 (uint8_t *)in, (size_t)data_readlen, final,
6182 iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK);
6184 if (nghttp2_is_fatal(rv)) {
6188 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6189 return (ssize_t)inlen;
6192 if (rv == NGHTTP2_ERR_PAUSE) {
6194 iframe->payloadleft -= hd_proclen;
6199 if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
6200 /* The application says no more headers. We decompress the
6201 rest of the header block but not invoke on_header_callback
6202 and on_frame_recv_callback. */
6204 iframe->payloadleft -= hd_proclen;
6206 /* Use promised stream ID for PUSH_PROMISE */
6207 rv = nghttp2_session_add_rst_stream(
6209 iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE
6210 ? iframe->frame.push_promise.promised_stream_id
6211 : iframe->frame.hd.stream_id,
6212 NGHTTP2_INTERNAL_ERROR);
6213 if (nghttp2_is_fatal(rv)) {
6217 iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
6222 iframe->payloadleft -= readlen;
6224 if (rv == NGHTTP2_ERR_HEADER_COMP) {
6225 /* GOAWAY is already issued */
6226 if (iframe->payloadleft == 0) {
6227 session_inbound_frame_reset(session);
6230 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
6236 iframe->payloadleft -= readlen;
6239 if (iframe->payloadleft) {
6243 if ((iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) {
6245 inbound_frame_set_mark(iframe, NGHTTP2_FRAME_HDLEN);
6249 if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {
6250 iframe->state = NGHTTP2_IB_EXPECT_CONTINUATION;
6252 iframe->state = NGHTTP2_IB_IGN_CONTINUATION;
6255 if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {
6256 rv = session_after_header_block_received(session);
6257 if (nghttp2_is_fatal(rv)) {
6261 session_inbound_frame_reset(session);
6265 case NGHTTP2_IB_IGN_PAYLOAD:
6266 DEBUGF("recv: [IB_IGN_PAYLOAD]\n");
6268 readlen = inbound_frame_payload_readlen(iframe, in, last);
6269 iframe->payloadleft -= readlen;
6272 DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6273 iframe->payloadleft);
6275 if (iframe->payloadleft) {
6279 switch (iframe->frame.hd.type) {
6280 case NGHTTP2_HEADERS:
6281 case NGHTTP2_PUSH_PROMISE:
6282 case NGHTTP2_CONTINUATION:
6283 /* Mark inflater bad so that we won't perform further decoding */
6284 session->hd_inflater.ctx.bad = 1;
6290 session_inbound_frame_reset(session);
6293 case NGHTTP2_IB_FRAME_SIZE_ERROR:
6294 DEBUGF("recv: [IB_FRAME_SIZE_ERROR]\n");
6296 rv = session_handle_frame_size_error(session);
6297 if (nghttp2_is_fatal(rv)) {
6301 assert(iframe->state == NGHTTP2_IB_IGN_ALL);
6303 return (ssize_t)inlen;
6304 case NGHTTP2_IB_READ_SETTINGS:
6305 DEBUGF("recv: [IB_READ_SETTINGS]\n");
6307 readlen = inbound_frame_buf_read(iframe, in, last);
6308 iframe->payloadleft -= readlen;
6311 DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6312 iframe->payloadleft);
6314 if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
6319 inbound_frame_set_settings_entry(iframe);
6321 if (iframe->payloadleft) {
6322 inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH);
6326 rv = session_process_settings_frame(session);
6328 if (nghttp2_is_fatal(rv)) {
6332 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6333 return (ssize_t)inlen;
6336 session_inbound_frame_reset(session);
6339 case NGHTTP2_IB_READ_GOAWAY_DEBUG:
6340 DEBUGF("recv: [IB_READ_GOAWAY_DEBUG]\n");
6342 readlen = inbound_frame_payload_readlen(iframe, in, last);
6345 iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
6347 iframe->payloadleft -= readlen;
6351 DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6352 iframe->payloadleft);
6354 if (iframe->payloadleft) {
6355 assert(nghttp2_buf_avail(&iframe->lbuf) > 0);
6360 rv = session_process_goaway_frame(session);
6362 if (nghttp2_is_fatal(rv)) {
6366 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6367 return (ssize_t)inlen;
6370 session_inbound_frame_reset(session);
6373 case NGHTTP2_IB_EXPECT_CONTINUATION:
6374 case NGHTTP2_IB_IGN_CONTINUATION:
6376 if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) {
6377 fprintf(stderr, "recv: [IB_EXPECT_CONTINUATION]\n");
6379 fprintf(stderr, "recv: [IB_IGN_CONTINUATION]\n");
6381 #endif /* DEBUGBUILD */
6383 readlen = inbound_frame_buf_read(iframe, in, last);
6386 if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
6390 nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->sbuf.pos);
6391 iframe->payloadleft = cont_hd.length;
6393 DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n",
6394 cont_hd.length, cont_hd.type, cont_hd.flags, cont_hd.stream_id);
6396 if (cont_hd.type != NGHTTP2_CONTINUATION ||
6397 cont_hd.stream_id != iframe->frame.hd.stream_id) {
6398 DEBUGF("recv: expected stream_id=%d, type=%d, but got stream_id=%d, "
6400 iframe->frame.hd.stream_id, NGHTTP2_CONTINUATION,
6401 cont_hd.stream_id, cont_hd.type);
6402 rv = nghttp2_session_terminate_session_with_reason(
6403 session, NGHTTP2_PROTOCOL_ERROR,
6404 "unexpected non-CONTINUATION frame or stream_id is invalid");
6405 if (nghttp2_is_fatal(rv)) {
6409 return (ssize_t)inlen;
6412 /* CONTINUATION won't bear NGHTTP2_PADDED flag */
6414 iframe->frame.hd.flags = (uint8_t)(
6415 iframe->frame.hd.flags | (cont_hd.flags & NGHTTP2_FLAG_END_HEADERS));
6416 iframe->frame.hd.length += cont_hd.length;
6420 if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) {
6421 iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
6423 rv = session_call_on_begin_frame(session, &cont_hd);
6425 if (nghttp2_is_fatal(rv)) {
6429 iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
6433 case NGHTTP2_IB_READ_PAD_DATA:
6434 DEBUGF("recv: [IB_READ_PAD_DATA]\n");
6436 readlen = inbound_frame_buf_read(iframe, in, last);
6438 iframe->payloadleft -= readlen;
6440 DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zu\n", readlen,
6441 iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf));
6443 if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
6447 /* Pad Length field is subject to flow control */
6448 rv = nghttp2_session_update_recv_connection_window_size(session, readlen);
6449 if (nghttp2_is_fatal(rv)) {
6453 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6454 return (ssize_t)inlen;
6457 /* Pad Length field is consumed immediately */
6459 nghttp2_session_consume(session, iframe->frame.hd.stream_id, readlen);
6461 if (nghttp2_is_fatal(rv)) {
6465 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6466 return (ssize_t)inlen;
6469 stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id);
6471 rv = nghttp2_session_update_recv_stream_window_size(
6472 session, stream, readlen,
6473 iframe->payloadleft ||
6474 (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0);
6475 if (nghttp2_is_fatal(rv)) {
6482 padlen = inbound_frame_compute_pad(iframe);
6484 rv = nghttp2_session_terminate_session_with_reason(
6485 session, NGHTTP2_PROTOCOL_ERROR, "DATA: invalid padding");
6486 if (nghttp2_is_fatal(rv)) {
6489 return (ssize_t)inlen;
6492 iframe->frame.data.padlen = (size_t)padlen;
6494 iframe->state = NGHTTP2_IB_READ_DATA;
6497 case NGHTTP2_IB_READ_DATA:
6498 stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id);
6502 iframe->state = NGHTTP2_IB_IGN_DATA;
6506 DEBUGF("recv: [IB_READ_DATA]\n");
6508 readlen = inbound_frame_payload_readlen(iframe, in, last);
6509 iframe->payloadleft -= readlen;
6512 DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6513 iframe->payloadleft);
6516 ssize_t data_readlen;
6518 rv = nghttp2_session_update_recv_connection_window_size(session,
6520 if (nghttp2_is_fatal(rv)) {
6524 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6525 return (ssize_t)inlen;
6528 rv = nghttp2_session_update_recv_stream_window_size(
6529 session, stream, readlen,
6530 iframe->payloadleft ||
6531 (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0);
6532 if (nghttp2_is_fatal(rv)) {
6536 data_readlen = inbound_frame_effective_readlen(
6537 iframe, iframe->payloadleft, readlen);
6539 if (data_readlen == -1) {
6540 /* everything is padding */
6544 padlen = (ssize_t)readlen - data_readlen;
6547 /* Padding is considered as "consumed" immediately */
6548 rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id,
6551 if (nghttp2_is_fatal(rv)) {
6555 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6556 return (ssize_t)inlen;
6560 DEBUGF("recv: data_readlen=%zd\n", data_readlen);
6562 if (data_readlen > 0) {
6563 if (session_enforce_http_messaging(session)) {
6564 if (nghttp2_http_on_data_chunk(stream, (size_t)data_readlen) != 0) {
6565 if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
6566 /* Consume all data for connection immediately here */
6567 rv = session_update_connection_consumed_size(
6568 session, (size_t)data_readlen);
6570 if (nghttp2_is_fatal(rv)) {
6574 if (iframe->state == NGHTTP2_IB_IGN_DATA) {
6575 return (ssize_t)inlen;
6579 rv = nghttp2_session_add_rst_stream(
6580 session, iframe->frame.hd.stream_id, NGHTTP2_PROTOCOL_ERROR);
6581 if (nghttp2_is_fatal(rv)) {
6585 iframe->state = NGHTTP2_IB_IGN_DATA;
6589 if (session->callbacks.on_data_chunk_recv_callback) {
6590 rv = session->callbacks.on_data_chunk_recv_callback(
6591 session, iframe->frame.hd.flags, iframe->frame.hd.stream_id,
6592 in - readlen, (size_t)data_readlen, session->user_data);
6593 if (rv == NGHTTP2_ERR_PAUSE) {
6597 if (nghttp2_is_fatal(rv)) {
6598 return NGHTTP2_ERR_CALLBACK_FAILURE;
6604 if (iframe->payloadleft) {
6608 rv = session_process_data_frame(session);
6609 if (nghttp2_is_fatal(rv)) {
6613 session_inbound_frame_reset(session);
6616 case NGHTTP2_IB_IGN_DATA:
6617 DEBUGF("recv: [IB_IGN_DATA]\n");
6619 readlen = inbound_frame_payload_readlen(iframe, in, last);
6620 iframe->payloadleft -= readlen;
6623 DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6624 iframe->payloadleft);
6627 /* Update connection-level flow control window for ignored
6629 rv = nghttp2_session_update_recv_connection_window_size(session,
6631 if (nghttp2_is_fatal(rv)) {
6635 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6636 return (ssize_t)inlen;
6639 if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
6641 /* Ignored DATA is considered as "consumed" immediately. */
6642 rv = session_update_connection_consumed_size(session, readlen);
6644 if (nghttp2_is_fatal(rv)) {
6648 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6649 return (ssize_t)inlen;
6654 if (iframe->payloadleft) {
6658 session_inbound_frame_reset(session);
6661 case NGHTTP2_IB_IGN_ALL:
6662 return (ssize_t)inlen;
6663 case NGHTTP2_IB_READ_EXTENSION_PAYLOAD:
6664 DEBUGF("recv: [IB_READ_EXTENSION_PAYLOAD]\n");
6666 readlen = inbound_frame_payload_readlen(iframe, in, last);
6667 iframe->payloadleft -= readlen;
6670 DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6671 iframe->payloadleft);
6674 rv = session_call_on_extension_chunk_recv_callback(
6675 session, in - readlen, readlen);
6676 if (nghttp2_is_fatal(rv)) {
6683 iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
6689 if (iframe->payloadleft > 0) {
6693 rv = session_process_extension_frame(session);
6694 if (nghttp2_is_fatal(rv)) {
6698 session_inbound_frame_reset(session);
6701 case NGHTTP2_IB_READ_ALTSVC_PAYLOAD:
6702 DEBUGF("recv: [IB_READ_ALTSVC_PAYLOAD]\n");
6704 readlen = inbound_frame_payload_readlen(iframe, in, last);
6706 iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
6708 iframe->payloadleft -= readlen;
6712 DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6713 iframe->payloadleft);
6715 if (iframe->payloadleft) {
6716 assert(nghttp2_buf_avail(&iframe->lbuf) > 0);
6721 rv = session_process_altsvc_frame(session);
6722 if (nghttp2_is_fatal(rv)) {
6726 session_inbound_frame_reset(session);
6729 case NGHTTP2_IB_READ_ORIGIN_PAYLOAD:
6730 DEBUGF("recv: [IB_READ_ORIGIN_PAYLOAD]\n");
6732 readlen = inbound_frame_payload_readlen(iframe, in, last);
6735 iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
6737 iframe->payloadleft -= readlen;
6741 DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6742 iframe->payloadleft);
6744 if (iframe->payloadleft) {
6745 assert(nghttp2_buf_avail(&iframe->lbuf) > 0);
6750 rv = session_process_origin_frame(session);
6752 if (nghttp2_is_fatal(rv)) {
6756 if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6757 return (ssize_t)inlen;
6760 session_inbound_frame_reset(session);
6765 if (!busy && in == last) {
6777 int nghttp2_session_recv(nghttp2_session *session) {
6778 uint8_t buf[NGHTTP2_INBOUND_BUFFER_LENGTH];
6781 readlen = session_recv(session, buf, sizeof(buf));
6783 ssize_t proclen = nghttp2_session_mem_recv(session, buf, (size_t)readlen);
6785 return (int)proclen;
6787 assert(proclen == readlen);
6788 } else if (readlen == 0 || readlen == NGHTTP2_ERR_WOULDBLOCK) {
6790 } else if (readlen == NGHTTP2_ERR_EOF) {
6791 return NGHTTP2_ERR_EOF;
6792 } else if (readlen < 0) {
6793 return NGHTTP2_ERR_CALLBACK_FAILURE;
6799 * Returns the number of active streams, which includes streams in
6802 static size_t session_get_num_active_streams(nghttp2_session *session) {
6803 return nghttp2_map_size(&session->streams) - session->num_closed_streams -
6804 session->num_idle_streams;
6807 int nghttp2_session_want_read(nghttp2_session *session) {
6808 size_t num_active_streams;
6810 /* If this flag is set, we don't want to read. The application
6811 should drop the connection. */
6812 if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) {
6816 num_active_streams = session_get_num_active_streams(session);
6818 /* Unless termination GOAWAY is sent or received, we always want to
6819 read incoming frames. */
6821 if (num_active_streams > 0) {
6825 /* If there is no active streams and GOAWAY has been sent or
6826 received, we are done with this session. */
6827 return (session->goaway_flags &
6828 (NGHTTP2_GOAWAY_SENT | NGHTTP2_GOAWAY_RECV)) == 0;
6831 int nghttp2_session_want_write(nghttp2_session *session) {
6832 /* If these flag is set, we don't want to write any data. The
6833 application should drop the connection. */
6834 if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) {
6839 * Unless termination GOAWAY is sent or received, we want to write
6840 * frames if there is pending ones. If pending frame is request/push
6841 * response HEADERS and concurrent stream limit is reached, we don't
6842 * want to write them.
6844 return session->aob.item || nghttp2_outbound_queue_top(&session->ob_urgent) ||
6845 nghttp2_outbound_queue_top(&session->ob_reg) ||
6846 (!nghttp2_pq_empty(&session->root.obq) &&
6847 session->remote_window_size > 0) ||
6848 (nghttp2_outbound_queue_top(&session->ob_syn) &&
6849 !session_is_outgoing_concurrent_streams_max(session));
6852 int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
6853 const uint8_t *opaque_data) {
6855 nghttp2_outbound_item *item;
6856 nghttp2_frame *frame;
6859 mem = &session->mem;
6861 if ((flags & NGHTTP2_FLAG_ACK) &&
6862 session->obq_flood_counter_ >= session->max_outbound_ack) {
6863 return NGHTTP2_ERR_FLOODED;
6866 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
6868 return NGHTTP2_ERR_NOMEM;
6871 nghttp2_outbound_item_init(item);
6873 frame = &item->frame;
6875 nghttp2_frame_ping_init(&frame->ping, flags, opaque_data);
6877 rv = nghttp2_session_add_item(session, item);
6880 nghttp2_frame_ping_free(&frame->ping);
6881 nghttp2_mem_free(mem, item);
6885 if (flags & NGHTTP2_FLAG_ACK) {
6886 ++session->obq_flood_counter_;
6892 int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id,
6893 uint32_t error_code, const uint8_t *opaque_data,
6894 size_t opaque_data_len, uint8_t aux_flags) {
6896 nghttp2_outbound_item *item;
6897 nghttp2_frame *frame;
6898 uint8_t *opaque_data_copy = NULL;
6899 nghttp2_goaway_aux_data *aux_data;
6902 mem = &session->mem;
6904 if (nghttp2_session_is_my_stream_id(session, last_stream_id)) {
6905 return NGHTTP2_ERR_INVALID_ARGUMENT;
6908 if (opaque_data_len) {
6909 if (opaque_data_len + 8 > NGHTTP2_MAX_PAYLOADLEN) {
6910 return NGHTTP2_ERR_INVALID_ARGUMENT;
6912 opaque_data_copy = nghttp2_mem_malloc(mem, opaque_data_len);
6913 if (opaque_data_copy == NULL) {
6914 return NGHTTP2_ERR_NOMEM;
6916 memcpy(opaque_data_copy, opaque_data, opaque_data_len);
6919 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
6921 nghttp2_mem_free(mem, opaque_data_copy);
6922 return NGHTTP2_ERR_NOMEM;
6925 nghttp2_outbound_item_init(item);
6927 frame = &item->frame;
6929 /* last_stream_id must not be increased from the value previously
6931 last_stream_id = nghttp2_min(last_stream_id, session->local_last_stream_id);
6933 nghttp2_frame_goaway_init(&frame->goaway, last_stream_id, error_code,
6934 opaque_data_copy, opaque_data_len);
6936 aux_data = &item->aux_data.goaway;
6937 aux_data->flags = aux_flags;
6939 rv = nghttp2_session_add_item(session, item);
6941 nghttp2_frame_goaway_free(&frame->goaway, mem);
6942 nghttp2_mem_free(mem, item);
6948 int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags,
6950 int32_t window_size_increment) {
6952 nghttp2_outbound_item *item;
6953 nghttp2_frame *frame;
6956 mem = &session->mem;
6957 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
6959 return NGHTTP2_ERR_NOMEM;
6962 nghttp2_outbound_item_init(item);
6964 frame = &item->frame;
6966 nghttp2_frame_window_update_init(&frame->window_update, flags, stream_id,
6967 window_size_increment);
6969 rv = nghttp2_session_add_item(session, item);
6972 nghttp2_frame_window_update_free(&frame->window_update);
6973 nghttp2_mem_free(mem, item);
6980 session_append_inflight_settings(nghttp2_session *session,
6981 nghttp2_inflight_settings *settings) {
6982 nghttp2_inflight_settings **i;
6984 for (i = &session->inflight_settings_head; *i; i = &(*i)->next)
6990 int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
6991 const nghttp2_settings_entry *iv, size_t niv) {
6992 nghttp2_outbound_item *item;
6993 nghttp2_frame *frame;
6994 nghttp2_settings_entry *iv_copy;
6998 nghttp2_inflight_settings *inflight_settings = NULL;
7000 mem = &session->mem;
7002 if (flags & NGHTTP2_FLAG_ACK) {
7004 return NGHTTP2_ERR_INVALID_ARGUMENT;
7007 if (session->obq_flood_counter_ >= session->max_outbound_ack) {
7008 return NGHTTP2_ERR_FLOODED;
7012 if (!nghttp2_iv_check(iv, niv)) {
7013 return NGHTTP2_ERR_INVALID_ARGUMENT;
7016 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
7018 return NGHTTP2_ERR_NOMEM;
7022 iv_copy = nghttp2_frame_iv_copy(iv, niv, mem);
7023 if (iv_copy == NULL) {
7024 nghttp2_mem_free(mem, item);
7025 return NGHTTP2_ERR_NOMEM;
7031 if ((flags & NGHTTP2_FLAG_ACK) == 0) {
7032 rv = inflight_settings_new(&inflight_settings, iv, niv, mem);
7034 assert(nghttp2_is_fatal(rv));
7035 nghttp2_mem_free(mem, iv_copy);
7036 nghttp2_mem_free(mem, item);
7041 nghttp2_outbound_item_init(item);
7043 frame = &item->frame;
7045 nghttp2_frame_settings_init(&frame->settings, flags, iv_copy, niv);
7046 rv = nghttp2_session_add_item(session, item);
7048 /* The only expected error is fatal one */
7049 assert(nghttp2_is_fatal(rv));
7051 inflight_settings_del(inflight_settings, mem);
7053 nghttp2_frame_settings_free(&frame->settings, mem);
7054 nghttp2_mem_free(mem, item);
7059 if (flags & NGHTTP2_FLAG_ACK) {
7060 ++session->obq_flood_counter_;
7062 session_append_inflight_settings(session, inflight_settings);
7065 /* Extract NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS and ENABLE_PUSH
7066 here. We use it to refuse the incoming stream and PUSH_PROMISE
7069 for (i = niv; i > 0; --i) {
7070 if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) {
7071 session->pending_local_max_concurrent_stream = iv[i - 1].value;
7076 for (i = niv; i > 0; --i) {
7077 if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_PUSH) {
7078 session->pending_enable_push = (uint8_t)iv[i - 1].value;
7083 for (i = niv; i > 0; --i) {
7084 if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL) {
7085 session->pending_enable_connect_protocol = (uint8_t)iv[i - 1].value;
7093 int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
7094 size_t datamax, nghttp2_frame *frame,
7095 nghttp2_data_aux_data *aux_data,
7096 nghttp2_stream *stream) {
7098 uint32_t data_flags;
7100 ssize_t padded_payloadlen;
7102 size_t max_payloadlen;
7104 assert(bufs->head == bufs->cur);
7106 buf = &bufs->cur->buf;
7108 if (session->callbacks.read_length_callback) {
7110 payloadlen = session->callbacks.read_length_callback(
7111 session, frame->hd.type, stream->stream_id, session->remote_window_size,
7112 stream->remote_window_size, session->remote_settings.max_frame_size,
7113 session->user_data);
7115 DEBUGF("send: read_length_callback=%zd\n", payloadlen);
7117 payloadlen = nghttp2_session_enforce_flow_control_limits(session, stream,
7120 DEBUGF("send: read_length_callback after flow control=%zd\n", payloadlen);
7122 if (payloadlen <= 0) {
7123 return NGHTTP2_ERR_CALLBACK_FAILURE;
7126 if ((size_t)payloadlen > nghttp2_buf_avail(buf)) {
7127 /* Resize the current buffer(s). The reason why we do +1 for
7128 buffer size is for possible padding field. */
7129 rv = nghttp2_bufs_realloc(&session->aob.framebufs,
7130 (size_t)(NGHTTP2_FRAME_HDLEN + 1 + payloadlen));
7133 DEBUGF("send: realloc buffer failed rv=%d", rv);
7134 /* If reallocation failed, old buffers are still in tact. So
7136 payloadlen = (ssize_t)datamax;
7138 DEBUGF("send: use safe limit payloadlen=%zd", payloadlen);
7140 assert(&session->aob.framebufs == bufs);
7142 buf = &bufs->cur->buf;
7145 datamax = (size_t)payloadlen;
7148 /* Current max DATA length is less then buffer chunk size */
7149 assert(nghttp2_buf_avail(buf) >= datamax);
7151 data_flags = NGHTTP2_DATA_FLAG_NONE;
7152 payloadlen = aux_data->data_prd.read_callback(
7153 session, frame->hd.stream_id, buf->pos, datamax, &data_flags,
7154 &aux_data->data_prd.source, session->user_data);
7156 if (payloadlen == NGHTTP2_ERR_DEFERRED ||
7157 payloadlen == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE ||
7158 payloadlen == NGHTTP2_ERR_PAUSE) {
7159 DEBUGF("send: DATA postponed due to %s\n",
7160 nghttp2_strerror((int)payloadlen));
7162 return (int)payloadlen;
7165 if (payloadlen < 0 || datamax < (size_t)payloadlen) {
7166 /* This is the error code when callback is failed. */
7167 return NGHTTP2_ERR_CALLBACK_FAILURE;
7170 buf->last = buf->pos + payloadlen;
7171 buf->pos -= NGHTTP2_FRAME_HDLEN;
7173 /* Clear flags, because this may contain previous flags of previous
7175 frame->hd.flags = NGHTTP2_FLAG_NONE;
7177 if (data_flags & NGHTTP2_DATA_FLAG_EOF) {
7179 /* If NGHTTP2_DATA_FLAG_NO_END_STREAM is set, don't set
7180 NGHTTP2_FLAG_END_STREAM */
7181 if ((aux_data->flags & NGHTTP2_FLAG_END_STREAM) &&
7182 (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) {
7183 frame->hd.flags |= NGHTTP2_FLAG_END_STREAM;
7187 if (data_flags & NGHTTP2_DATA_FLAG_NO_COPY) {
7188 if (session->callbacks.send_data_callback == NULL) {
7189 DEBUGF("NGHTTP2_DATA_FLAG_NO_COPY requires send_data_callback set\n");
7191 return NGHTTP2_ERR_CALLBACK_FAILURE;
7193 aux_data->no_copy = 1;
7196 frame->hd.length = (size_t)payloadlen;
7197 frame->data.padlen = 0;
7199 max_payloadlen = nghttp2_min(datamax, frame->hd.length + NGHTTP2_MAX_PADLEN);
7202 session_call_select_padding(session, frame, max_payloadlen);
7204 if (nghttp2_is_fatal((int)padded_payloadlen)) {
7205 return (int)padded_payloadlen;
7208 frame->data.padlen = (size_t)(padded_payloadlen - payloadlen);
7210 nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
7212 rv = nghttp2_frame_add_pad(bufs, &frame->hd, frame->data.padlen,
7218 reschedule_stream(stream);
7220 if (frame->hd.length == 0 && (data_flags & NGHTTP2_DATA_FLAG_EOF) &&
7221 (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM)) {
7222 /* DATA payload length is 0, and DATA frame does not bear
7223 END_STREAM. In this case, there is no point to send 0 length
7225 return NGHTTP2_ERR_CANCEL;
7231 void *nghttp2_session_get_stream_user_data(nghttp2_session *session,
7232 int32_t stream_id) {
7233 nghttp2_stream *stream;
7234 stream = nghttp2_session_get_stream(session, stream_id);
7236 return stream->stream_user_data;
7242 int nghttp2_session_set_stream_user_data(nghttp2_session *session,
7244 void *stream_user_data) {
7245 nghttp2_stream *stream;
7246 nghttp2_frame *frame;
7247 nghttp2_outbound_item *item;
7249 stream = nghttp2_session_get_stream(session, stream_id);
7251 stream->stream_user_data = stream_user_data;
7255 if (session->server || !nghttp2_session_is_my_stream_id(session, stream_id) ||
7256 !nghttp2_outbound_queue_top(&session->ob_syn)) {
7257 return NGHTTP2_ERR_INVALID_ARGUMENT;
7260 frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame;
7261 assert(frame->hd.type == NGHTTP2_HEADERS);
7263 if (frame->hd.stream_id > stream_id ||
7264 (uint32_t)stream_id >= session->next_stream_id) {
7265 return NGHTTP2_ERR_INVALID_ARGUMENT;
7268 for (item = session->ob_syn.head; item; item = item->qnext) {
7269 if (item->frame.hd.stream_id < stream_id) {
7273 if (item->frame.hd.stream_id > stream_id) {
7277 item->aux_data.headers.stream_user_data = stream_user_data;
7281 return NGHTTP2_ERR_INVALID_ARGUMENT;
7284 int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) {
7286 nghttp2_stream *stream;
7287 stream = nghttp2_session_get_stream(session, stream_id);
7288 if (stream == NULL || !nghttp2_stream_check_deferred_item(stream)) {
7289 return NGHTTP2_ERR_INVALID_ARGUMENT;
7292 rv = nghttp2_stream_resume_deferred_item(stream,
7293 NGHTTP2_STREAM_FLAG_DEFERRED_USER);
7295 if (nghttp2_is_fatal(rv)) {
7302 size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) {
7303 return nghttp2_outbound_queue_size(&session->ob_urgent) +
7304 nghttp2_outbound_queue_size(&session->ob_reg) +
7305 nghttp2_outbound_queue_size(&session->ob_syn);
7306 /* TODO account for item attached to stream */
7310 nghttp2_session_get_stream_effective_recv_data_length(nghttp2_session *session,
7311 int32_t stream_id) {
7312 nghttp2_stream *stream;
7313 stream = nghttp2_session_get_stream(session, stream_id);
7314 if (stream == NULL) {
7317 return stream->recv_window_size < 0 ? 0 : stream->recv_window_size;
7321 nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session,
7322 int32_t stream_id) {
7323 nghttp2_stream *stream;
7324 stream = nghttp2_session_get_stream(session, stream_id);
7325 if (stream == NULL) {
7328 return stream->local_window_size;
7331 int32_t nghttp2_session_get_stream_local_window_size(nghttp2_session *session,
7332 int32_t stream_id) {
7333 nghttp2_stream *stream;
7335 stream = nghttp2_session_get_stream(session, stream_id);
7336 if (stream == NULL) {
7340 size = stream->local_window_size - stream->recv_window_size;
7342 /* size could be negative if local endpoint reduced
7343 SETTINGS_INITIAL_WINDOW_SIZE */
7352 nghttp2_session_get_effective_recv_data_length(nghttp2_session *session) {
7353 return session->recv_window_size < 0 ? 0 : session->recv_window_size;
7357 nghttp2_session_get_effective_local_window_size(nghttp2_session *session) {
7358 return session->local_window_size;
7361 int32_t nghttp2_session_get_local_window_size(nghttp2_session *session) {
7362 return session->local_window_size - session->recv_window_size;
7365 int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session,
7366 int32_t stream_id) {
7367 nghttp2_stream *stream;
7369 stream = nghttp2_session_get_stream(session, stream_id);
7370 if (stream == NULL) {
7374 /* stream->remote_window_size can be negative when
7375 SETTINGS_INITIAL_WINDOW_SIZE is changed. */
7376 return nghttp2_max(0, stream->remote_window_size);
7379 int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session) {
7380 return session->remote_window_size;
7383 uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,
7384 nghttp2_settings_id id) {
7386 case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
7387 return session->remote_settings.header_table_size;
7388 case NGHTTP2_SETTINGS_ENABLE_PUSH:
7389 return session->remote_settings.enable_push;
7390 case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
7391 return session->remote_settings.max_concurrent_streams;
7392 case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
7393 return session->remote_settings.initial_window_size;
7394 case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
7395 return session->remote_settings.max_frame_size;
7396 case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
7397 return session->remote_settings.max_header_list_size;
7398 case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
7399 return session->remote_settings.enable_connect_protocol;
7403 abort(); /* if NDEBUG is set */
7406 uint32_t nghttp2_session_get_local_settings(nghttp2_session *session,
7407 nghttp2_settings_id id) {
7409 case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
7410 return session->local_settings.header_table_size;
7411 case NGHTTP2_SETTINGS_ENABLE_PUSH:
7412 return session->local_settings.enable_push;
7413 case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
7414 return session->local_settings.max_concurrent_streams;
7415 case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
7416 return session->local_settings.initial_window_size;
7417 case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
7418 return session->local_settings.max_frame_size;
7419 case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
7420 return session->local_settings.max_header_list_size;
7421 case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
7422 return session->local_settings.enable_connect_protocol;
7426 abort(); /* if NDEBUG is set */
7429 static int nghttp2_session_upgrade_internal(nghttp2_session *session,
7430 const uint8_t *settings_payload,
7431 size_t settings_payloadlen,
7432 void *stream_user_data) {
7433 nghttp2_stream *stream;
7434 nghttp2_frame frame;
7435 nghttp2_settings_entry *iv;
7438 nghttp2_priority_spec pri_spec;
7441 mem = &session->mem;
7443 if ((!session->server && session->next_stream_id != 1) ||
7444 (session->server && session->last_recv_stream_id >= 1)) {
7445 return NGHTTP2_ERR_PROTO;
7447 if (settings_payloadlen % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) {
7448 return NGHTTP2_ERR_INVALID_ARGUMENT;
7450 /* SETTINGS frame contains too many settings */
7451 if (settings_payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH
7452 > session->max_settings) {
7453 return NGHTTP2_ERR_TOO_MANY_SETTINGS;
7455 rv = nghttp2_frame_unpack_settings_payload2(&iv, &niv, settings_payload,
7456 settings_payloadlen, mem);
7461 if (session->server) {
7462 nghttp2_frame_hd_init(&frame.hd, settings_payloadlen, NGHTTP2_SETTINGS,
7463 NGHTTP2_FLAG_NONE, 0);
7464 frame.settings.iv = iv;
7465 frame.settings.niv = niv;
7466 rv = nghttp2_session_on_settings_received(session, &frame, 1 /* No ACK */);
7468 rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, niv);
7470 nghttp2_mem_free(mem, iv);
7475 nghttp2_priority_spec_default_init(&pri_spec);
7477 stream = nghttp2_session_open_stream(
7478 session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_OPENING,
7479 session->server ? NULL : stream_user_data);
7480 if (stream == NULL) {
7481 return NGHTTP2_ERR_NOMEM;
7484 /* We don't call nghttp2_session_adjust_closed_stream(), since this
7485 should be the first stream open. */
7487 if (session->server) {
7488 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
7489 session->last_recv_stream_id = 1;
7490 session->last_proc_stream_id = 1;
7492 nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
7493 session->last_sent_stream_id = 1;
7494 session->next_stream_id += 2;
7499 int nghttp2_session_upgrade(nghttp2_session *session,
7500 const uint8_t *settings_payload,
7501 size_t settings_payloadlen,
7502 void *stream_user_data) {
7504 nghttp2_stream *stream;
7506 rv = nghttp2_session_upgrade_internal(session, settings_payload,
7507 settings_payloadlen, stream_user_data);
7512 stream = nghttp2_session_get_stream(session, 1);
7515 /* We have no information about request header fields when Upgrade
7516 was happened. So we don't know the request method here. If
7517 request method is HEAD, we have a trouble because we may have
7518 nonzero content-length header field in response headers, and we
7519 will going to check it against the actual DATA frames, but we may
7520 get mismatch because HEAD response body must be empty. Because
7521 of this reason, nghttp2_session_upgrade() was deprecated in favor
7522 of nghttp2_session_upgrade2(), which has |head_request| parameter
7523 to indicate that request method is HEAD or not. */
7524 stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND;
7528 int nghttp2_session_upgrade2(nghttp2_session *session,
7529 const uint8_t *settings_payload,
7530 size_t settings_payloadlen, int head_request,
7531 void *stream_user_data) {
7533 nghttp2_stream *stream;
7535 rv = nghttp2_session_upgrade_internal(session, settings_payload,
7536 settings_payloadlen, stream_user_data);
7541 stream = nghttp2_session_get_stream(session, 1);
7545 stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
7551 int nghttp2_session_get_stream_local_close(nghttp2_session *session,
7552 int32_t stream_id) {
7553 nghttp2_stream *stream;
7555 stream = nghttp2_session_get_stream(session, stream_id);
7561 return (stream->shut_flags & NGHTTP2_SHUT_WR) != 0;
7564 int nghttp2_session_get_stream_remote_close(nghttp2_session *session,
7565 int32_t stream_id) {
7566 nghttp2_stream *stream;
7568 stream = nghttp2_session_get_stream(session, stream_id);
7574 return (stream->shut_flags & NGHTTP2_SHUT_RD) != 0;
7577 int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id,
7580 nghttp2_stream *stream;
7582 if (stream_id == 0) {
7583 return NGHTTP2_ERR_INVALID_ARGUMENT;
7586 if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
7587 return NGHTTP2_ERR_INVALID_STATE;
7590 rv = session_update_connection_consumed_size(session, size);
7592 if (nghttp2_is_fatal(rv)) {
7596 stream = nghttp2_session_get_stream(session, stream_id);
7602 rv = session_update_stream_consumed_size(session, stream, size);
7604 if (nghttp2_is_fatal(rv)) {
7611 int nghttp2_session_consume_connection(nghttp2_session *session, size_t size) {
7614 if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
7615 return NGHTTP2_ERR_INVALID_STATE;
7618 rv = session_update_connection_consumed_size(session, size);
7620 if (nghttp2_is_fatal(rv)) {
7627 int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id,
7630 nghttp2_stream *stream;
7632 if (stream_id == 0) {
7633 return NGHTTP2_ERR_INVALID_ARGUMENT;
7636 if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
7637 return NGHTTP2_ERR_INVALID_STATE;
7640 stream = nghttp2_session_get_stream(session, stream_id);
7646 rv = session_update_stream_consumed_size(session, stream, size);
7648 if (nghttp2_is_fatal(rv)) {
7655 int nghttp2_session_set_next_stream_id(nghttp2_session *session,
7656 int32_t next_stream_id) {
7657 if (next_stream_id <= 0 ||
7658 session->next_stream_id > (uint32_t)next_stream_id) {
7659 return NGHTTP2_ERR_INVALID_ARGUMENT;
7662 if (session->server) {
7663 if (next_stream_id % 2) {
7664 return NGHTTP2_ERR_INVALID_ARGUMENT;
7666 } else if (next_stream_id % 2 == 0) {
7667 return NGHTTP2_ERR_INVALID_ARGUMENT;
7670 session->next_stream_id = (uint32_t)next_stream_id;
7674 uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session) {
7675 return session->next_stream_id;
7678 int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session) {
7679 return session->last_proc_stream_id;
7682 nghttp2_stream *nghttp2_session_find_stream(nghttp2_session *session,
7683 int32_t stream_id) {
7684 if (stream_id == 0) {
7685 return &session->root;
7688 return nghttp2_session_get_stream_raw(session, stream_id);
7691 nghttp2_stream *nghttp2_session_get_root_stream(nghttp2_session *session) {
7692 return &session->root;
7695 int nghttp2_session_check_server_session(nghttp2_session *session) {
7696 return session->server;
7699 int nghttp2_session_change_stream_priority(
7700 nghttp2_session *session, int32_t stream_id,
7701 const nghttp2_priority_spec *pri_spec) {
7703 nghttp2_stream *stream;
7704 nghttp2_priority_spec pri_spec_copy;
7706 if (stream_id == 0 || stream_id == pri_spec->stream_id) {
7707 return NGHTTP2_ERR_INVALID_ARGUMENT;
7710 stream = nghttp2_session_get_stream_raw(session, stream_id);
7712 return NGHTTP2_ERR_INVALID_ARGUMENT;
7715 pri_spec_copy = *pri_spec;
7716 nghttp2_priority_spec_normalize_weight(&pri_spec_copy);
7718 rv = nghttp2_session_reprioritize_stream(session, stream, &pri_spec_copy);
7720 if (nghttp2_is_fatal(rv)) {
7724 /* We don't intentionally call nghttp2_session_adjust_idle_stream()
7725 so that idle stream created by this function, and existing ones
7726 are kept for application. We will adjust number of idle stream
7727 in nghttp2_session_mem_send or nghttp2_session_mem_recv is
7732 int nghttp2_session_create_idle_stream(nghttp2_session *session,
7734 const nghttp2_priority_spec *pri_spec) {
7735 nghttp2_stream *stream;
7736 nghttp2_priority_spec pri_spec_copy;
7738 if (stream_id == 0 || stream_id == pri_spec->stream_id ||
7739 !session_detect_idle_stream(session, stream_id)) {
7740 return NGHTTP2_ERR_INVALID_ARGUMENT;
7743 stream = nghttp2_session_get_stream_raw(session, stream_id);
7745 return NGHTTP2_ERR_INVALID_ARGUMENT;
7748 pri_spec_copy = *pri_spec;
7749 nghttp2_priority_spec_normalize_weight(&pri_spec_copy);
7752 nghttp2_session_open_stream(session, stream_id, NGHTTP2_STREAM_FLAG_NONE,
7753 &pri_spec_copy, NGHTTP2_STREAM_IDLE, NULL);
7755 return NGHTTP2_ERR_NOMEM;
7758 /* We don't intentionally call nghttp2_session_adjust_idle_stream()
7759 so that idle stream created by this function, and existing ones
7760 are kept for application. We will adjust number of idle stream
7761 in nghttp2_session_mem_send or nghttp2_session_mem_recv is
7767 nghttp2_session_get_hd_inflate_dynamic_table_size(nghttp2_session *session) {
7768 return nghttp2_hd_inflate_get_dynamic_table_size(&session->hd_inflater);
7772 nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session) {
7773 return nghttp2_hd_deflate_get_dynamic_table_size(&session->hd_deflater);
7776 void nghttp2_session_set_user_data(nghttp2_session *session, void *user_data) {
7777 session->user_data = user_data;