Path: blob/master/Utilities/cmnghttp2/lib/nghttp2_session.c
3153 views
/*1* nghttp2 - HTTP/2 C Library2*3* Copyright (c) 2012 Tatsuhiro Tsujikawa4*5* Permission is hereby granted, free of charge, to any person obtaining6* a copy of this software and associated documentation files (the7* "Software"), to deal in the Software without restriction, including8* without limitation the rights to use, copy, modify, merge, publish,9* distribute, sublicense, and/or sell copies of the Software, and to10* permit persons to whom the Software is furnished to do so, subject to11* the following conditions:12*13* The above copyright notice and this permission notice shall be14* included in all copies or substantial portions of the Software.15*16* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,17* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF18* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND19* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE20* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION21* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION22* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.23*/24#include "nghttp2_session.h"2526#include <string.h>27#include <stddef.h>28#include <stdio.h>29#include <assert.h>30#include <stdarg.h>3132#include "nghttp2_helper.h"33#include "nghttp2_net.h"34#include "nghttp2_priority_spec.h"35#include "nghttp2_option.h"36#include "nghttp2_http.h"37#include "nghttp2_pq.h"38#include "nghttp2_extpri.h"39#include "nghttp2_debug.h"4041/*42* Returns non-zero if the number of outgoing opened streams is larger43* than or equal to44* remote_settings.max_concurrent_streams.45*/46static int47session_is_outgoing_concurrent_streams_max(nghttp2_session *session) {48return session->remote_settings.max_concurrent_streams <=49session->num_outgoing_streams;50}5152/*53* Returns non-zero if the number of incoming opened streams is larger54* than or equal to55* local_settings.max_concurrent_streams.56*/57static int58session_is_incoming_concurrent_streams_max(nghttp2_session *session) {59return session->local_settings.max_concurrent_streams <=60session->num_incoming_streams;61}6263/*64* Returns non-zero if the number of incoming opened streams is larger65* than or equal to66* session->pending_local_max_concurrent_stream.67*/68static int69session_is_incoming_concurrent_streams_pending_max(nghttp2_session *session) {70return session->pending_local_max_concurrent_stream <=71session->num_incoming_streams;72}7374/*75* Returns non-zero if |lib_error| is non-fatal error.76*/77static int is_non_fatal(int lib_error_code) {78return lib_error_code < 0 && lib_error_code > NGHTTP2_ERR_FATAL;79}8081int nghttp2_is_fatal(int lib_error_code) {82return lib_error_code < NGHTTP2_ERR_FATAL;83}8485static int session_enforce_http_messaging(nghttp2_session *session) {86return (session->opt_flags & NGHTTP2_OPTMASK_NO_HTTP_MESSAGING) == 0;87}8889/*90* Returns nonzero if |frame| is trailer headers.91*/92static int session_trailer_headers(nghttp2_session *session,93nghttp2_stream *stream,94nghttp2_frame *frame) {95if (!stream || frame->hd.type != NGHTTP2_HEADERS) {96return 0;97}98if (session->server) {99return frame->headers.cat == NGHTTP2_HCAT_HEADERS;100}101102return frame->headers.cat == NGHTTP2_HCAT_HEADERS &&103(stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) == 0;104}105106/* Returns nonzero if the |stream| is in reserved(remote) state */107static int state_reserved_remote(nghttp2_session *session,108nghttp2_stream *stream) {109return stream->state == NGHTTP2_STREAM_RESERVED &&110!nghttp2_session_is_my_stream_id(session, stream->stream_id);111}112113/* Returns nonzero if the |stream| is in reserved(local) state */114static int state_reserved_local(nghttp2_session *session,115nghttp2_stream *stream) {116return stream->state == NGHTTP2_STREAM_RESERVED &&117nghttp2_session_is_my_stream_id(session, stream->stream_id);118}119120/*121* Checks whether received stream_id is valid. This function returns122* 1 if it succeeds, or 0.123*/124static int session_is_new_peer_stream_id(nghttp2_session *session,125int32_t stream_id) {126return stream_id != 0 &&127!nghttp2_session_is_my_stream_id(session, stream_id) &&128session->last_recv_stream_id < stream_id;129}130131static int session_detect_idle_stream(nghttp2_session *session,132int32_t stream_id) {133/* Assume that stream object with stream_id does not exist */134if (nghttp2_session_is_my_stream_id(session, stream_id)) {135if (session->last_sent_stream_id < stream_id) {136return 1;137}138return 0;139}140if (session_is_new_peer_stream_id(session, stream_id)) {141return 1;142}143return 0;144}145146static int session_no_rfc7540_pri_no_fallback(nghttp2_session *session) {147return session->pending_no_rfc7540_priorities == 1 &&148!session->fallback_rfc7540_priorities;149}150151static int check_ext_type_set(const uint8_t *ext_types, uint8_t type) {152return (ext_types[type / 8] & (1 << (type & 0x7))) > 0;153}154155static int session_call_error_callback(nghttp2_session *session,156int lib_error_code, const char *fmt,157...) {158size_t bufsize;159va_list ap;160char *buf;161int rv;162nghttp2_mem *mem;163164if (!session->callbacks.error_callback &&165!session->callbacks.error_callback2) {166return 0;167}168169mem = &session->mem;170171va_start(ap, fmt);172rv = vsnprintf(NULL, 0, fmt, ap);173va_end(ap);174175if (rv < 0) {176return NGHTTP2_ERR_NOMEM;177}178179bufsize = (size_t)(rv + 1);180181buf = nghttp2_mem_malloc(mem, bufsize);182if (buf == NULL) {183return NGHTTP2_ERR_NOMEM;184}185186va_start(ap, fmt);187rv = vsnprintf(buf, bufsize, fmt, ap);188va_end(ap);189190if (rv < 0) {191nghttp2_mem_free(mem, buf);192/* vsnprintf may return error because of various things we can193imagine, but typically we don't want to drop session just for194debug callback. */195DEBUGF("error_callback: vsnprintf failed. The template was %s\n", fmt);196return 0;197}198199if (session->callbacks.error_callback2) {200rv = session->callbacks.error_callback2(session, lib_error_code, buf,201(size_t)rv, session->user_data);202} else {203rv = session->callbacks.error_callback(session, buf, (size_t)rv,204session->user_data);205}206207nghttp2_mem_free(mem, buf);208209if (rv != 0) {210return NGHTTP2_ERR_CALLBACK_FAILURE;211}212213return 0;214}215216static int session_terminate_session(nghttp2_session *session,217int32_t last_stream_id,218uint32_t error_code, const char *reason) {219int rv;220const uint8_t *debug_data;221size_t debug_datalen;222223if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {224return 0;225}226227/* Ignore all incoming frames because we are going to tear down the228session. */229session->iframe.state = NGHTTP2_IB_IGN_ALL;230231if (reason == NULL) {232debug_data = NULL;233debug_datalen = 0;234} else {235debug_data = (const uint8_t *)reason;236debug_datalen = strlen(reason);237}238239rv = nghttp2_session_add_goaway(session, last_stream_id, error_code,240debug_data, debug_datalen,241NGHTTP2_GOAWAY_AUX_TERM_ON_SEND);242243if (rv != 0) {244return rv;245}246247session->goaway_flags |= NGHTTP2_GOAWAY_TERM_ON_SEND;248249return 0;250}251252int nghttp2_session_terminate_session(nghttp2_session *session,253uint32_t error_code) {254return session_terminate_session(session, session->last_proc_stream_id,255error_code, NULL);256}257258int nghttp2_session_terminate_session2(nghttp2_session *session,259int32_t last_stream_id,260uint32_t error_code) {261return session_terminate_session(session, last_stream_id, error_code, NULL);262}263264int nghttp2_session_terminate_session_with_reason(nghttp2_session *session,265uint32_t error_code,266const char *reason) {267return session_terminate_session(session, session->last_proc_stream_id,268error_code, reason);269}270271int nghttp2_session_is_my_stream_id(nghttp2_session *session,272int32_t stream_id) {273int rem;274if (stream_id == 0) {275return 0;276}277rem = stream_id & 0x1;278if (session->server) {279return rem == 0;280}281return rem == 1;282}283284nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session,285int32_t stream_id) {286nghttp2_stream *stream;287288stream = (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id);289290if (stream == NULL || (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) ||291stream->state == NGHTTP2_STREAM_IDLE) {292return NULL;293}294295return stream;296}297298nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session,299int32_t stream_id) {300return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id);301}302303static void session_inbound_frame_reset(nghttp2_session *session) {304nghttp2_inbound_frame *iframe = &session->iframe;305nghttp2_mem *mem = &session->mem;306/* A bit risky code, since if this function is called from307nghttp2_session_new(), we rely on the fact that308iframe->frame.hd.type is 0, so that no free is performed. */309switch (iframe->frame.hd.type) {310case NGHTTP2_DATA:311break;312case NGHTTP2_HEADERS:313nghttp2_frame_headers_free(&iframe->frame.headers, mem);314break;315case NGHTTP2_PRIORITY:316nghttp2_frame_priority_free(&iframe->frame.priority);317break;318case NGHTTP2_RST_STREAM:319nghttp2_frame_rst_stream_free(&iframe->frame.rst_stream);320break;321case NGHTTP2_SETTINGS:322nghttp2_frame_settings_free(&iframe->frame.settings, mem);323324nghttp2_mem_free(mem, iframe->iv);325326iframe->iv = NULL;327iframe->niv = 0;328iframe->max_niv = 0;329330break;331case NGHTTP2_PUSH_PROMISE:332nghttp2_frame_push_promise_free(&iframe->frame.push_promise, mem);333break;334case NGHTTP2_PING:335nghttp2_frame_ping_free(&iframe->frame.ping);336break;337case NGHTTP2_GOAWAY:338nghttp2_frame_goaway_free(&iframe->frame.goaway, mem);339break;340case NGHTTP2_WINDOW_UPDATE:341nghttp2_frame_window_update_free(&iframe->frame.window_update);342break;343default:344/* extension frame */345if (check_ext_type_set(session->user_recv_ext_types,346iframe->frame.hd.type)) {347nghttp2_frame_extension_free(&iframe->frame.ext);348} else {349switch (iframe->frame.hd.type) {350case NGHTTP2_ALTSVC:351if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) == 0) {352break;353}354nghttp2_frame_altsvc_free(&iframe->frame.ext, mem);355break;356case NGHTTP2_ORIGIN:357if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN) == 0) {358break;359}360nghttp2_frame_origin_free(&iframe->frame.ext, mem);361break;362case NGHTTP2_PRIORITY_UPDATE:363if ((session->builtin_recv_ext_types &364NGHTTP2_TYPEMASK_PRIORITY_UPDATE) == 0) {365break;366}367/* Do not call nghttp2_frame_priority_update_free, because all368fields point to sbuf. */369break;370}371}372373break;374}375376memset(&iframe->frame, 0, sizeof(nghttp2_frame));377memset(&iframe->ext_frame_payload, 0, sizeof(nghttp2_ext_frame_payload));378379iframe->state = NGHTTP2_IB_READ_HEAD;380381nghttp2_buf_wrap_init(&iframe->sbuf, iframe->raw_sbuf,382sizeof(iframe->raw_sbuf));383iframe->sbuf.mark += NGHTTP2_FRAME_HDLEN;384385nghttp2_buf_free(&iframe->lbuf, mem);386nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);387388iframe->raw_lbuf = NULL;389390iframe->payloadleft = 0;391iframe->padlen = 0;392}393394static void init_settings(nghttp2_settings_storage *settings) {395settings->header_table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;396settings->enable_push = 1;397settings->max_concurrent_streams = NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;398settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE;399settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN;400settings->max_header_list_size = UINT32_MAX;401settings->no_rfc7540_priorities = UINT32_MAX;402}403404static void active_outbound_item_reset(nghttp2_active_outbound_item *aob,405nghttp2_mem *mem) {406DEBUGF("send: reset nghttp2_active_outbound_item\n");407DEBUGF("send: aob->item = %p\n", aob->item);408nghttp2_outbound_item_free(aob->item, mem);409nghttp2_mem_free(mem, aob->item);410aob->item = NULL;411nghttp2_bufs_reset(&aob->framebufs);412aob->state = NGHTTP2_OB_POP_ITEM;413}414415#define NGHTTP2_STREAM_MAX_CYCLE_GAP ((uint64_t)NGHTTP2_MAX_FRAME_SIZE_MAX)416417static int stream_less(const void *lhsx, const void *rhsx) {418const nghttp2_stream *lhs, *rhs;419420lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry);421rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry);422423if (lhs->cycle == rhs->cycle) {424return lhs->seq < rhs->seq;425}426427return rhs->cycle - lhs->cycle <= NGHTTP2_STREAM_MAX_CYCLE_GAP;428}429430int nghttp2_enable_strict_preface = 1;431432static int session_new(nghttp2_session **session_ptr,433const nghttp2_session_callbacks *callbacks,434void *user_data, int server,435const nghttp2_option *option, nghttp2_mem *mem) {436int rv;437size_t nbuffer;438size_t max_deflate_dynamic_table_size =439NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE;440size_t i;441442if (mem == NULL) {443mem = nghttp2_mem_default();444}445446*session_ptr = nghttp2_mem_calloc(mem, 1, sizeof(nghttp2_session));447if (*session_ptr == NULL) {448rv = NGHTTP2_ERR_NOMEM;449goto fail_session;450}451452(*session_ptr)->mem = *mem;453mem = &(*session_ptr)->mem;454455/* next_stream_id is initialized in either456nghttp2_session_client_new2 or nghttp2_session_server_new2 */457458nghttp2_stream_init(&(*session_ptr)->root, 0, NGHTTP2_STREAM_FLAG_NONE,459NGHTTP2_STREAM_IDLE, NGHTTP2_DEFAULT_WEIGHT, 0, 0, NULL,460mem);461462(*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;463(*session_ptr)->recv_window_size = 0;464(*session_ptr)->consumed_size = 0;465(*session_ptr)->recv_reduction = 0;466(*session_ptr)->local_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;467468(*session_ptr)->goaway_flags = NGHTTP2_GOAWAY_NONE;469(*session_ptr)->local_last_stream_id = (1u << 31) - 1;470(*session_ptr)->remote_last_stream_id = (1u << 31) - 1;471472(*session_ptr)->pending_local_max_concurrent_stream =473NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;474(*session_ptr)->pending_enable_push = 1;475(*session_ptr)->pending_no_rfc7540_priorities = UINT8_MAX;476477if (server) {478(*session_ptr)->server = 1;479}480481init_settings(&(*session_ptr)->remote_settings);482init_settings(&(*session_ptr)->local_settings);483484(*session_ptr)->max_incoming_reserved_streams =485NGHTTP2_MAX_INCOMING_RESERVED_STREAMS;486487/* Limit max outgoing concurrent streams to sensible value */488(*session_ptr)->remote_settings.max_concurrent_streams = 100;489490(*session_ptr)->max_send_header_block_length = NGHTTP2_MAX_HEADERSLEN;491(*session_ptr)->max_outbound_ack = NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM;492(*session_ptr)->max_settings = NGHTTP2_DEFAULT_MAX_SETTINGS;493494if (option) {495if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) &&496option->no_auto_window_update) {497498(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE;499}500501if (option->opt_set_mask & NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS) {502503(*session_ptr)->remote_settings.max_concurrent_streams =504option->peer_max_concurrent_streams;505}506507if (option->opt_set_mask & NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS) {508509(*session_ptr)->max_incoming_reserved_streams =510option->max_reserved_remote_streams;511}512513if ((option->opt_set_mask & NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC) &&514option->no_recv_client_magic) {515516(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC;517}518519if ((option->opt_set_mask & NGHTTP2_OPT_NO_HTTP_MESSAGING) &&520option->no_http_messaging) {521522(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING;523}524525if (option->opt_set_mask & NGHTTP2_OPT_USER_RECV_EXT_TYPES) {526memcpy((*session_ptr)->user_recv_ext_types, option->user_recv_ext_types,527sizeof((*session_ptr)->user_recv_ext_types));528}529530if (option->opt_set_mask & NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES) {531(*session_ptr)->builtin_recv_ext_types = option->builtin_recv_ext_types;532}533534if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_PING_ACK) &&535option->no_auto_ping_ack) {536(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_PING_ACK;537}538539if (option->opt_set_mask & NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH) {540(*session_ptr)->max_send_header_block_length =541option->max_send_header_block_length;542}543544if (option->opt_set_mask & NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE) {545max_deflate_dynamic_table_size = option->max_deflate_dynamic_table_size;546}547548if ((option->opt_set_mask & NGHTTP2_OPT_NO_CLOSED_STREAMS) &&549option->no_closed_streams) {550(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_CLOSED_STREAMS;551}552553if (option->opt_set_mask & NGHTTP2_OPT_MAX_OUTBOUND_ACK) {554(*session_ptr)->max_outbound_ack = option->max_outbound_ack;555}556557if ((option->opt_set_mask & NGHTTP2_OPT_MAX_SETTINGS) &&558option->max_settings) {559(*session_ptr)->max_settings = option->max_settings;560}561562if ((option->opt_set_mask &563NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES) &&564option->server_fallback_rfc7540_priorities) {565(*session_ptr)->opt_flags |=566NGHTTP2_OPTMASK_SERVER_FALLBACK_RFC7540_PRIORITIES;567}568569if ((option->opt_set_mask &570NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) &&571option->no_rfc9113_leading_and_trailing_ws_validation) {572(*session_ptr)->opt_flags |=573NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION;574}575}576577rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater,578max_deflate_dynamic_table_size, mem);579if (rv != 0) {580goto fail_hd_deflater;581}582rv = nghttp2_hd_inflate_init(&(*session_ptr)->hd_inflater, mem);583if (rv != 0) {584goto fail_hd_inflater;585}586rv = nghttp2_map_init(&(*session_ptr)->streams, mem);587if (rv != 0) {588goto fail_map;589}590591nbuffer = ((*session_ptr)->max_send_header_block_length +592NGHTTP2_FRAMEBUF_CHUNKLEN - 1) /593NGHTTP2_FRAMEBUF_CHUNKLEN;594595if (nbuffer == 0) {596nbuffer = 1;597}598599/* 1 for Pad Field. */600rv = nghttp2_bufs_init3(&(*session_ptr)->aob.framebufs,601NGHTTP2_FRAMEBUF_CHUNKLEN, nbuffer, 1,602NGHTTP2_FRAME_HDLEN + 1, mem);603if (rv != 0) {604goto fail_aob_framebuf;605}606607active_outbound_item_reset(&(*session_ptr)->aob, mem);608609(*session_ptr)->callbacks = *callbacks;610(*session_ptr)->user_data = user_data;611612session_inbound_frame_reset(*session_ptr);613614if (nghttp2_enable_strict_preface) {615nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe;616617if (server && ((*session_ptr)->opt_flags &618NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) == 0) {619iframe->state = NGHTTP2_IB_READ_CLIENT_MAGIC;620iframe->payloadleft = NGHTTP2_CLIENT_MAGIC_LEN;621} else {622iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS;623}624625if (!server) {626(*session_ptr)->aob.state = NGHTTP2_OB_SEND_CLIENT_MAGIC;627nghttp2_bufs_add(&(*session_ptr)->aob.framebufs, NGHTTP2_CLIENT_MAGIC,628NGHTTP2_CLIENT_MAGIC_LEN);629}630}631632for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) {633nghttp2_pq_init(&(*session_ptr)->sched[i].ob_data, stream_less, mem);634}635636return 0;637638fail_aob_framebuf:639nghttp2_map_free(&(*session_ptr)->streams);640fail_map:641nghttp2_hd_inflate_free(&(*session_ptr)->hd_inflater);642fail_hd_inflater:643nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater);644fail_hd_deflater:645nghttp2_mem_free(mem, *session_ptr);646fail_session:647return rv;648}649650int nghttp2_session_client_new(nghttp2_session **session_ptr,651const nghttp2_session_callbacks *callbacks,652void *user_data) {653return nghttp2_session_client_new3(session_ptr, callbacks, user_data, NULL,654NULL);655}656657int nghttp2_session_client_new2(nghttp2_session **session_ptr,658const nghttp2_session_callbacks *callbacks,659void *user_data, const nghttp2_option *option) {660return nghttp2_session_client_new3(session_ptr, callbacks, user_data, option,661NULL);662}663664int nghttp2_session_client_new3(nghttp2_session **session_ptr,665const nghttp2_session_callbacks *callbacks,666void *user_data, const nghttp2_option *option,667nghttp2_mem *mem) {668int rv;669nghttp2_session *session;670671rv = session_new(&session, callbacks, user_data, 0, option, mem);672673if (rv != 0) {674return rv;675}676/* IDs for use in client */677session->next_stream_id = 1;678679*session_ptr = session;680681return 0;682}683684int nghttp2_session_server_new(nghttp2_session **session_ptr,685const nghttp2_session_callbacks *callbacks,686void *user_data) {687return nghttp2_session_server_new3(session_ptr, callbacks, user_data, NULL,688NULL);689}690691int nghttp2_session_server_new2(nghttp2_session **session_ptr,692const nghttp2_session_callbacks *callbacks,693void *user_data, const nghttp2_option *option) {694return nghttp2_session_server_new3(session_ptr, callbacks, user_data, option,695NULL);696}697698int nghttp2_session_server_new3(nghttp2_session **session_ptr,699const nghttp2_session_callbacks *callbacks,700void *user_data, const nghttp2_option *option,701nghttp2_mem *mem) {702int rv;703nghttp2_session *session;704705rv = session_new(&session, callbacks, user_data, 1, option, mem);706707if (rv != 0) {708return rv;709}710/* IDs for use in client */711session->next_stream_id = 2;712713*session_ptr = session;714715return 0;716}717718static int free_streams(void *entry, void *ptr) {719nghttp2_session *session;720nghttp2_stream *stream;721nghttp2_outbound_item *item;722nghttp2_mem *mem;723724session = (nghttp2_session *)ptr;725mem = &session->mem;726stream = (nghttp2_stream *)entry;727item = stream->item;728729if (item && !item->queued && item != session->aob.item) {730nghttp2_outbound_item_free(item, mem);731nghttp2_mem_free(mem, item);732}733734nghttp2_stream_free(stream);735nghttp2_mem_free(mem, stream);736737return 0;738}739740static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) {741nghttp2_outbound_item *item, *next;742for (item = q->head; item;) {743next = item->qnext;744nghttp2_outbound_item_free(item, mem);745nghttp2_mem_free(mem, item);746item = next;747}748}749750static int inflight_settings_new(nghttp2_inflight_settings **settings_ptr,751const nghttp2_settings_entry *iv, size_t niv,752nghttp2_mem *mem) {753*settings_ptr = nghttp2_mem_malloc(mem, sizeof(nghttp2_inflight_settings));754if (!*settings_ptr) {755return NGHTTP2_ERR_NOMEM;756}757758if (niv > 0) {759(*settings_ptr)->iv = nghttp2_frame_iv_copy(iv, niv, mem);760if (!(*settings_ptr)->iv) {761nghttp2_mem_free(mem, *settings_ptr);762return NGHTTP2_ERR_NOMEM;763}764} else {765(*settings_ptr)->iv = NULL;766}767768(*settings_ptr)->niv = niv;769(*settings_ptr)->next = NULL;770771return 0;772}773774static void inflight_settings_del(nghttp2_inflight_settings *settings,775nghttp2_mem *mem) {776if (!settings) {777return;778}779780nghttp2_mem_free(mem, settings->iv);781nghttp2_mem_free(mem, settings);782}783784void nghttp2_session_del(nghttp2_session *session) {785nghttp2_mem *mem;786nghttp2_inflight_settings *settings;787size_t i;788789if (session == NULL) {790return;791}792793mem = &session->mem;794795for (settings = session->inflight_settings_head; settings;) {796nghttp2_inflight_settings *next = settings->next;797inflight_settings_del(settings, mem);798settings = next;799}800801for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) {802nghttp2_pq_free(&session->sched[i].ob_data);803}804nghttp2_stream_free(&session->root);805806/* Have to free streams first, so that we can check807stream->item->queued */808nghttp2_map_each_free(&session->streams, free_streams, session);809nghttp2_map_free(&session->streams);810811ob_q_free(&session->ob_urgent, mem);812ob_q_free(&session->ob_reg, mem);813ob_q_free(&session->ob_syn, mem);814815active_outbound_item_reset(&session->aob, mem);816session_inbound_frame_reset(session);817nghttp2_hd_deflate_free(&session->hd_deflater);818nghttp2_hd_inflate_free(&session->hd_inflater);819nghttp2_bufs_free(&session->aob.framebufs);820nghttp2_mem_free(mem, session);821}822823int nghttp2_session_reprioritize_stream(824nghttp2_session *session, nghttp2_stream *stream,825const nghttp2_priority_spec *pri_spec_in) {826int rv;827nghttp2_stream *dep_stream = NULL;828nghttp2_priority_spec pri_spec_default;829const nghttp2_priority_spec *pri_spec = pri_spec_in;830831assert((!session->server && session->pending_no_rfc7540_priorities != 1) ||832(session->server && !session_no_rfc7540_pri_no_fallback(session)));833assert(pri_spec->stream_id != stream->stream_id);834835if (!nghttp2_stream_in_dep_tree(stream)) {836return 0;837}838839if (pri_spec->stream_id != 0) {840dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id);841842if (!dep_stream &&843session_detect_idle_stream(session, pri_spec->stream_id)) {844845nghttp2_priority_spec_default_init(&pri_spec_default);846847dep_stream = nghttp2_session_open_stream(848session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default,849NGHTTP2_STREAM_IDLE, NULL);850851if (dep_stream == NULL) {852return NGHTTP2_ERR_NOMEM;853}854} else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) {855nghttp2_priority_spec_default_init(&pri_spec_default);856pri_spec = &pri_spec_default;857}858}859860if (pri_spec->stream_id == 0) {861dep_stream = &session->root;862} else if (nghttp2_stream_dep_find_ancestor(dep_stream, stream)) {863DEBUGF("stream: cycle detected, dep_stream(%p)=%d stream(%p)=%d\n",864dep_stream, dep_stream->stream_id, stream, stream->stream_id);865866nghttp2_stream_dep_remove_subtree(dep_stream);867rv = nghttp2_stream_dep_add_subtree(stream->dep_prev, dep_stream);868if (rv != 0) {869return rv;870}871}872873assert(dep_stream);874875if (dep_stream == stream->dep_prev && !pri_spec->exclusive) {876/* This is minor optimization when just weight is changed. */877nghttp2_stream_change_weight(stream, pri_spec->weight);878879return 0;880}881882nghttp2_stream_dep_remove_subtree(stream);883884/* We have to update weight after removing stream from tree */885stream->weight = pri_spec->weight;886887if (pri_spec->exclusive) {888rv = nghttp2_stream_dep_insert_subtree(dep_stream, stream);889} else {890rv = nghttp2_stream_dep_add_subtree(dep_stream, stream);891}892893if (rv != 0) {894return rv;895}896897return 0;898}899900static uint64_t pq_get_first_cycle(nghttp2_pq *pq) {901nghttp2_stream *stream;902903if (nghttp2_pq_empty(pq)) {904return 0;905}906907stream = nghttp2_struct_of(nghttp2_pq_top(pq), nghttp2_stream, pq_entry);908return stream->cycle;909}910911static int session_ob_data_push(nghttp2_session *session,912nghttp2_stream *stream) {913int rv;914uint32_t urgency;915int inc;916nghttp2_pq *pq;917918assert(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES);919assert(stream->queued == 0);920921urgency = nghttp2_extpri_uint8_urgency(stream->extpri);922inc = nghttp2_extpri_uint8_inc(stream->extpri);923924assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS);925926pq = &session->sched[urgency].ob_data;927928stream->cycle = pq_get_first_cycle(pq);929if (inc) {930stream->cycle += stream->last_writelen;931}932933rv = nghttp2_pq_push(pq, &stream->pq_entry);934if (rv != 0) {935return rv;936}937938stream->queued = 1;939940return 0;941}942943static int session_ob_data_remove(nghttp2_session *session,944nghttp2_stream *stream) {945uint32_t urgency;946947assert(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES);948assert(stream->queued == 1);949950urgency = nghttp2_extpri_uint8_urgency(stream->extpri);951952assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS);953954nghttp2_pq_remove(&session->sched[urgency].ob_data, &stream->pq_entry);955956stream->queued = 0;957958return 0;959}960961static int session_attach_stream_item(nghttp2_session *session,962nghttp2_stream *stream,963nghttp2_outbound_item *item) {964int rv;965966rv = nghttp2_stream_attach_item(stream, item);967if (rv != 0) {968return rv;969}970971if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) {972return 0;973}974975return session_ob_data_push(session, stream);976}977978static int session_detach_stream_item(nghttp2_session *session,979nghttp2_stream *stream) {980int rv;981982rv = nghttp2_stream_detach_item(stream);983if (rv != 0) {984return rv;985}986987if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) ||988!stream->queued) {989return 0;990}991992return session_ob_data_remove(session, stream);993}994995static int session_defer_stream_item(nghttp2_session *session,996nghttp2_stream *stream, uint8_t flags) {997int rv;998999rv = nghttp2_stream_defer_item(stream, flags);1000if (rv != 0) {1001return rv;1002}10031004if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) ||1005!stream->queued) {1006return 0;1007}10081009return session_ob_data_remove(session, stream);1010}10111012static int session_resume_deferred_stream_item(nghttp2_session *session,1013nghttp2_stream *stream,1014uint8_t flags) {1015int rv;10161017rv = nghttp2_stream_resume_deferred_item(stream, flags);1018if (rv != 0) {1019return rv;1020}10211022if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) ||1023(stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL)) {1024return 0;1025}10261027return session_ob_data_push(session, stream);1028}10291030static nghttp2_outbound_item *1031session_sched_get_next_outbound_item(nghttp2_session *session) {1032size_t i;1033nghttp2_pq_entry *ent;1034nghttp2_stream *stream;10351036for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) {1037ent = nghttp2_pq_top(&session->sched[i].ob_data);1038if (!ent) {1039continue;1040}10411042stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry);1043return stream->item;1044}10451046return NULL;1047}10481049static int session_sched_empty(nghttp2_session *session) {1050size_t i;10511052for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) {1053if (!nghttp2_pq_empty(&session->sched[i].ob_data)) {1054return 0;1055}1056}10571058return 1;1059}10601061static void session_sched_reschedule_stream(nghttp2_session *session,1062nghttp2_stream *stream) {1063nghttp2_pq *pq;1064uint32_t urgency = nghttp2_extpri_uint8_urgency(stream->extpri);1065int inc = nghttp2_extpri_uint8_inc(stream->extpri);1066uint64_t penalty = (uint64_t)stream->last_writelen;1067int rv;10681069(void)rv;10701071assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS);10721073pq = &session->sched[urgency].ob_data;10741075if (!inc || nghttp2_pq_size(pq) == 1) {1076return;1077}10781079nghttp2_pq_remove(pq, &stream->pq_entry);10801081stream->cycle += penalty;10821083rv = nghttp2_pq_push(pq, &stream->pq_entry);10841085assert(0 == rv);1086}10871088static int session_update_stream_priority(nghttp2_session *session,1089nghttp2_stream *stream,1090uint8_t u8extpri) {1091if (stream->extpri == u8extpri) {1092return 0;1093}10941095if (stream->queued) {1096session_ob_data_remove(session, stream);10971098stream->extpri = u8extpri;10991100return session_ob_data_push(session, stream);1101}11021103stream->extpri = u8extpri;11041105return 0;1106}11071108int nghttp2_session_add_item(nghttp2_session *session,1109nghttp2_outbound_item *item) {1110/* TODO Return error if stream is not found for the frame requiring1111stream presence. */1112int rv = 0;1113nghttp2_stream *stream;1114nghttp2_frame *frame;11151116frame = &item->frame;1117stream = nghttp2_session_get_stream(session, frame->hd.stream_id);11181119switch (frame->hd.type) {1120case NGHTTP2_DATA:1121if (!stream) {1122return NGHTTP2_ERR_STREAM_CLOSED;1123}11241125if (stream->item) {1126return NGHTTP2_ERR_DATA_EXIST;1127}11281129rv = session_attach_stream_item(session, stream, item);11301131if (rv != 0) {1132return rv;1133}11341135return 0;1136case NGHTTP2_HEADERS:1137/* We push request HEADERS and push response HEADERS to1138dedicated queue because their transmission is affected by1139SETTINGS_MAX_CONCURRENT_STREAMS */1140/* TODO If 2 HEADERS are submitted for reserved stream, then1141both of them are queued into ob_syn, which is not1142desirable. */1143if (frame->headers.cat == NGHTTP2_HCAT_REQUEST ||1144(stream && stream->state == NGHTTP2_STREAM_RESERVED)) {1145nghttp2_outbound_queue_push(&session->ob_syn, item);1146item->queued = 1;1147return 0;1148;1149}11501151nghttp2_outbound_queue_push(&session->ob_reg, item);1152item->queued = 1;1153return 0;1154case NGHTTP2_SETTINGS:1155case NGHTTP2_PING:1156nghttp2_outbound_queue_push(&session->ob_urgent, item);1157item->queued = 1;1158return 0;1159case NGHTTP2_RST_STREAM:1160if (stream) {1161stream->state = NGHTTP2_STREAM_CLOSING;1162}1163nghttp2_outbound_queue_push(&session->ob_reg, item);1164item->queued = 1;1165return 0;1166case NGHTTP2_PUSH_PROMISE: {1167nghttp2_headers_aux_data *aux_data;1168nghttp2_priority_spec pri_spec;11691170aux_data = &item->aux_data.headers;11711172if (!stream) {1173return NGHTTP2_ERR_STREAM_CLOSED;1174}11751176nghttp2_priority_spec_init(&pri_spec, stream->stream_id,1177NGHTTP2_DEFAULT_WEIGHT, 0);11781179if (!nghttp2_session_open_stream(1180session, frame->push_promise.promised_stream_id,1181NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_RESERVED,1182aux_data->stream_user_data)) {1183return NGHTTP2_ERR_NOMEM;1184}11851186/* We don't have to call nghttp2_session_adjust_closed_stream()1187here, since stream->stream_id is local stream_id, and it does1188not affect closed stream count. */11891190nghttp2_outbound_queue_push(&session->ob_reg, item);1191item->queued = 1;11921193return 0;1194}1195case NGHTTP2_WINDOW_UPDATE:1196if (stream) {1197stream->window_update_queued = 1;1198} else if (frame->hd.stream_id == 0) {1199session->window_update_queued = 1;1200}1201nghttp2_outbound_queue_push(&session->ob_reg, item);1202item->queued = 1;1203return 0;1204default:1205nghttp2_outbound_queue_push(&session->ob_reg, item);1206item->queued = 1;1207return 0;1208}1209}12101211int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,1212uint32_t error_code) {1213int rv;1214nghttp2_outbound_item *item;1215nghttp2_frame *frame;1216nghttp2_stream *stream;1217nghttp2_mem *mem;12181219mem = &session->mem;1220stream = nghttp2_session_get_stream(session, stream_id);1221if (stream && stream->state == NGHTTP2_STREAM_CLOSING) {1222return 0;1223}12241225/* Sending RST_STREAM to an idle stream is subject to protocol1226violation. Historically, nghttp2 allows this. In order not to1227disrupt the existing applications, we don't error out this case1228and simply ignore it. */1229if (nghttp2_session_is_my_stream_id(session, stream_id)) {1230if ((uint32_t)stream_id >= session->next_stream_id) {1231return 0;1232}1233} else if (session->last_recv_stream_id < stream_id) {1234return 0;1235}12361237/* Cancel pending request HEADERS in ob_syn if this RST_STREAM1238refers to that stream. */1239if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) &&1240nghttp2_outbound_queue_top(&session->ob_syn)) {1241nghttp2_headers_aux_data *aux_data;1242nghttp2_frame *headers_frame;12431244headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame;1245assert(headers_frame->hd.type == NGHTTP2_HEADERS);12461247if (headers_frame->hd.stream_id <= stream_id) {12481249for (item = session->ob_syn.head; item; item = item->qnext) {1250aux_data = &item->aux_data.headers;12511252if (item->frame.hd.stream_id < stream_id) {1253continue;1254}12551256/* stream_id in ob_syn queue must be strictly increasing. If1257we found larger ID, then we can break here. */1258if (item->frame.hd.stream_id > stream_id || aux_data->canceled) {1259break;1260}12611262aux_data->error_code = error_code;1263aux_data->canceled = 1;12641265return 0;1266}1267}1268}12691270item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));1271if (item == NULL) {1272return NGHTTP2_ERR_NOMEM;1273}12741275nghttp2_outbound_item_init(item);12761277frame = &item->frame;12781279nghttp2_frame_rst_stream_init(&frame->rst_stream, stream_id, error_code);1280rv = nghttp2_session_add_item(session, item);1281if (rv != 0) {1282nghttp2_frame_rst_stream_free(&frame->rst_stream);1283nghttp2_mem_free(mem, item);1284return rv;1285}1286return 0;1287}12881289nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,1290int32_t stream_id, uint8_t flags,1291nghttp2_priority_spec *pri_spec_in,1292nghttp2_stream_state initial_state,1293void *stream_user_data) {1294int rv;1295nghttp2_stream *stream;1296nghttp2_stream *dep_stream = NULL;1297int stream_alloc = 0;1298nghttp2_priority_spec pri_spec_default;1299nghttp2_priority_spec *pri_spec = pri_spec_in;1300nghttp2_mem *mem;13011302mem = &session->mem;1303stream = nghttp2_session_get_stream_raw(session, stream_id);13041305if (session->opt_flags &1306NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {1307flags |= NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION;1308}13091310if (stream) {1311assert(stream->state == NGHTTP2_STREAM_IDLE);1312assert((stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) ||1313nghttp2_stream_in_dep_tree(stream));13141315if (nghttp2_stream_in_dep_tree(stream)) {1316assert(!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES));1317nghttp2_session_detach_idle_stream(session, stream);1318rv = nghttp2_stream_dep_remove(stream);1319if (rv != 0) {1320return NULL;1321}13221323if (session_no_rfc7540_pri_no_fallback(session)) {1324stream->flags |= NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES;1325}1326}1327} else {1328stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream));1329if (stream == NULL) {1330return NULL;1331}13321333stream_alloc = 1;1334}13351336if (session_no_rfc7540_pri_no_fallback(session) ||1337session->remote_settings.no_rfc7540_priorities == 1) {1338/* For client which has not received server1339SETTINGS_NO_RFC7540_PRIORITIES = 1, send a priority signal1340opportunistically. */1341if (session->server ||1342session->remote_settings.no_rfc7540_priorities == 1) {1343nghttp2_priority_spec_default_init(&pri_spec_default);1344pri_spec = &pri_spec_default;1345}13461347if (session->pending_no_rfc7540_priorities == 1) {1348flags |= NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES;1349}1350} else if (pri_spec->stream_id != 0) {1351dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id);13521353if (!dep_stream &&1354session_detect_idle_stream(session, pri_spec->stream_id)) {1355/* Depends on idle stream, which does not exist in memory.1356Assign default priority for it. */1357nghttp2_priority_spec_default_init(&pri_spec_default);13581359dep_stream = nghttp2_session_open_stream(1360session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default,1361NGHTTP2_STREAM_IDLE, NULL);13621363if (dep_stream == NULL) {1364if (stream_alloc) {1365nghttp2_mem_free(mem, stream);1366}13671368return NULL;1369}1370} else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) {1371/* If dep_stream is not part of dependency tree, stream will get1372default priority. This handles the case when1373pri_spec->stream_id == stream_id. This happens because we1374don't check pri_spec->stream_id against new stream ID in1375nghttp2_submit_request. This also handles the case when idle1376stream created by PRIORITY frame was opened. Somehow we1377first remove the idle stream from dependency tree. This is1378done to simplify code base, but ideally we should retain old1379dependency. But I'm not sure this adds values. */1380nghttp2_priority_spec_default_init(&pri_spec_default);1381pri_spec = &pri_spec_default;1382}1383}13841385if (initial_state == NGHTTP2_STREAM_RESERVED) {1386flags |= NGHTTP2_STREAM_FLAG_PUSH;1387}13881389if (stream_alloc) {1390nghttp2_stream_init(stream, stream_id, flags, initial_state,1391pri_spec->weight,1392(int32_t)session->remote_settings.initial_window_size,1393(int32_t)session->local_settings.initial_window_size,1394stream_user_data, mem);13951396if (session_no_rfc7540_pri_no_fallback(session)) {1397stream->seq = session->stream_seq++;1398}13991400rv = nghttp2_map_insert(&session->streams, stream_id, stream);1401if (rv != 0) {1402nghttp2_stream_free(stream);1403nghttp2_mem_free(mem, stream);1404return NULL;1405}1406} else {1407stream->flags = flags;1408stream->state = initial_state;1409stream->weight = pri_spec->weight;1410stream->stream_user_data = stream_user_data;1411}14121413switch (initial_state) {1414case NGHTTP2_STREAM_RESERVED:1415if (nghttp2_session_is_my_stream_id(session, stream_id)) {1416/* reserved (local) */1417nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);1418} else {1419/* reserved (remote) */1420nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);1421++session->num_incoming_reserved_streams;1422}1423/* Reserved stream does not count in the concurrent streams1424limit. That is one of the DOS vector. */1425break;1426case NGHTTP2_STREAM_IDLE:1427/* Idle stream does not count toward the concurrent streams limit.1428This is used as anchor node in dependency tree. */1429nghttp2_session_keep_idle_stream(session, stream);1430break;1431default:1432if (nghttp2_session_is_my_stream_id(session, stream_id)) {1433++session->num_outgoing_streams;1434} else {1435++session->num_incoming_streams;1436}1437}14381439if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {1440return stream;1441}14421443if (pri_spec->stream_id == 0) {1444dep_stream = &session->root;1445}14461447assert(dep_stream);14481449if (pri_spec->exclusive) {1450rv = nghttp2_stream_dep_insert(dep_stream, stream);1451if (rv != 0) {1452return NULL;1453}1454} else {1455nghttp2_stream_dep_add(dep_stream, stream);1456}14571458return stream;1459}14601461int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,1462uint32_t error_code) {1463int rv;1464nghttp2_stream *stream;1465nghttp2_mem *mem;1466int is_my_stream_id;14671468mem = &session->mem;1469stream = nghttp2_session_get_stream(session, stream_id);14701471if (!stream) {1472return NGHTTP2_ERR_INVALID_ARGUMENT;1473}14741475DEBUGF("stream: stream(%p)=%d close\n", stream, stream->stream_id);14761477if (stream->item) {1478nghttp2_outbound_item *item;14791480item = stream->item;14811482rv = session_detach_stream_item(session, stream);14831484if (rv != 0) {1485return rv;1486}14871488/* If item is queued, it will be deleted when it is popped1489(nghttp2_session_prep_frame() will fail). If session->aob.item1490points to this item, let active_outbound_item_reset()1491free the item. */1492if (!item->queued && item != session->aob.item) {1493nghttp2_outbound_item_free(item, mem);1494nghttp2_mem_free(mem, item);1495}1496}14971498/* We call on_stream_close_callback even if stream->state is1499NGHTTP2_STREAM_INITIAL. This will happen while sending request1500HEADERS, a local endpoint receives RST_STREAM for that stream. It1501may be PROTOCOL_ERROR, but without notifying stream closure will1502hang the stream in a local endpoint.1503*/15041505if (session->callbacks.on_stream_close_callback) {1506if (session->callbacks.on_stream_close_callback(1507session, stream_id, error_code, session->user_data) != 0) {15081509return NGHTTP2_ERR_CALLBACK_FAILURE;1510}1511}15121513is_my_stream_id = nghttp2_session_is_my_stream_id(session, stream_id);15141515/* pushed streams which is not opened yet is not counted toward max1516concurrent limits */1517if ((stream->flags & NGHTTP2_STREAM_FLAG_PUSH)) {1518if (!is_my_stream_id) {1519--session->num_incoming_reserved_streams;1520}1521} else {1522if (is_my_stream_id) {1523--session->num_outgoing_streams;1524} else {1525--session->num_incoming_streams;1526}1527}15281529/* Closes both directions just in case they are not closed yet */1530stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED;15311532if (session->pending_no_rfc7540_priorities == 1) {1533return nghttp2_session_destroy_stream(session, stream);1534}15351536if ((session->opt_flags & NGHTTP2_OPTMASK_NO_CLOSED_STREAMS) == 0 &&1537session->server && !is_my_stream_id &&1538nghttp2_stream_in_dep_tree(stream)) {1539/* On server side, retain stream at most MAX_CONCURRENT_STREAMS1540combined with the current active incoming streams to make1541dependency tree work better. */1542nghttp2_session_keep_closed_stream(session, stream);1543} else {1544rv = nghttp2_session_destroy_stream(session, stream);1545if (rv != 0) {1546return rv;1547}1548}15491550return 0;1551}15521553int nghttp2_session_destroy_stream(nghttp2_session *session,1554nghttp2_stream *stream) {1555nghttp2_mem *mem;1556int rv;15571558DEBUGF("stream: destroy closed stream(%p)=%d\n", stream, stream->stream_id);15591560mem = &session->mem;15611562if (nghttp2_stream_in_dep_tree(stream)) {1563rv = nghttp2_stream_dep_remove(stream);1564if (rv != 0) {1565return rv;1566}1567}15681569nghttp2_map_remove(&session->streams, stream->stream_id);1570nghttp2_stream_free(stream);1571nghttp2_mem_free(mem, stream);15721573return 0;1574}15751576void nghttp2_session_keep_closed_stream(nghttp2_session *session,1577nghttp2_stream *stream) {1578DEBUGF("stream: keep closed stream(%p)=%d, state=%d\n", stream,1579stream->stream_id, stream->state);15801581if (session->closed_stream_tail) {1582session->closed_stream_tail->closed_next = stream;1583stream->closed_prev = session->closed_stream_tail;1584} else {1585session->closed_stream_head = stream;1586}1587session->closed_stream_tail = stream;15881589++session->num_closed_streams;1590}15911592void nghttp2_session_keep_idle_stream(nghttp2_session *session,1593nghttp2_stream *stream) {1594DEBUGF("stream: keep idle stream(%p)=%d, state=%d\n", stream,1595stream->stream_id, stream->state);15961597if (session->idle_stream_tail) {1598session->idle_stream_tail->closed_next = stream;1599stream->closed_prev = session->idle_stream_tail;1600} else {1601session->idle_stream_head = stream;1602}1603session->idle_stream_tail = stream;16041605++session->num_idle_streams;1606}16071608void nghttp2_session_detach_idle_stream(nghttp2_session *session,1609nghttp2_stream *stream) {1610nghttp2_stream *prev_stream, *next_stream;16111612DEBUGF("stream: detach idle stream(%p)=%d, state=%d\n", stream,1613stream->stream_id, stream->state);16141615prev_stream = stream->closed_prev;1616next_stream = stream->closed_next;16171618if (prev_stream) {1619prev_stream->closed_next = next_stream;1620} else {1621session->idle_stream_head = next_stream;1622}16231624if (next_stream) {1625next_stream->closed_prev = prev_stream;1626} else {1627session->idle_stream_tail = prev_stream;1628}16291630stream->closed_prev = NULL;1631stream->closed_next = NULL;16321633--session->num_idle_streams;1634}16351636int nghttp2_session_adjust_closed_stream(nghttp2_session *session) {1637size_t num_stream_max;1638int rv;16391640if (session->local_settings.max_concurrent_streams ==1641NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS) {1642num_stream_max = session->pending_local_max_concurrent_stream;1643} else {1644num_stream_max = session->local_settings.max_concurrent_streams;1645}16461647DEBUGF("stream: adjusting kept closed streams num_closed_streams=%zu, "1648"num_incoming_streams=%zu, max_concurrent_streams=%zu\n",1649session->num_closed_streams, session->num_incoming_streams,1650num_stream_max);16511652while (session->num_closed_streams > 0 &&1653session->num_closed_streams + session->num_incoming_streams >1654num_stream_max) {1655nghttp2_stream *head_stream;1656nghttp2_stream *next;16571658head_stream = session->closed_stream_head;16591660assert(head_stream);16611662next = head_stream->closed_next;16631664rv = nghttp2_session_destroy_stream(session, head_stream);1665if (rv != 0) {1666return rv;1667}16681669/* head_stream is now freed */16701671session->closed_stream_head = next;16721673if (session->closed_stream_head) {1674session->closed_stream_head->closed_prev = NULL;1675} else {1676session->closed_stream_tail = NULL;1677}16781679--session->num_closed_streams;1680}16811682return 0;1683}16841685int nghttp2_session_adjust_idle_stream(nghttp2_session *session) {1686size_t max;1687int rv;16881689/* Make minimum number of idle streams 16, and maximum 100, which1690are arbitrary chosen numbers. */1691max = nghttp2_min(1692100, nghttp2_max(169316, nghttp2_min(session->local_settings.max_concurrent_streams,1694session->pending_local_max_concurrent_stream)));16951696DEBUGF("stream: adjusting kept idle streams num_idle_streams=%zu, max=%zu\n",1697session->num_idle_streams, max);16981699while (session->num_idle_streams > max) {1700nghttp2_stream *head;1701nghttp2_stream *next;17021703head = session->idle_stream_head;1704assert(head);17051706next = head->closed_next;17071708rv = nghttp2_session_destroy_stream(session, head);1709if (rv != 0) {1710return rv;1711}17121713/* head is now destroyed */17141715session->idle_stream_head = next;17161717if (session->idle_stream_head) {1718session->idle_stream_head->closed_prev = NULL;1719} else {1720session->idle_stream_tail = NULL;1721}17221723--session->num_idle_streams;1724}17251726return 0;1727}17281729/*1730* Closes stream with stream ID |stream_id| if both transmission and1731* reception of the stream were disallowed. The |error_code| indicates1732* the reason of the closure.1733*1734* This function returns 0 if it succeeds, or one of the following1735* negative error codes:1736*1737* NGHTTP2_ERR_INVALID_ARGUMENT1738* The stream is not found.1739* NGHTTP2_ERR_CALLBACK_FAILURE1740* The callback function failed.1741*/1742int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session,1743nghttp2_stream *stream) {1744if ((stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR) {1745return nghttp2_session_close_stream(session, stream->stream_id,1746NGHTTP2_NO_ERROR);1747}1748return 0;1749}17501751/*1752* Returns nonzero if local endpoint allows reception of new stream1753* from remote.1754*/1755static int session_allow_incoming_new_stream(nghttp2_session *session) {1756return (session->goaway_flags &1757(NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_SENT)) == 0;1758}17591760/*1761* This function returns nonzero if session is closing.1762*/1763static int session_is_closing(nghttp2_session *session) {1764return (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) != 0 ||1765(nghttp2_session_want_read(session) == 0 &&1766nghttp2_session_want_write(session) == 0);1767}17681769/*1770* Check that we can send a frame to the |stream|. This function1771* returns 0 if we can send a frame to the |frame|, or one of the1772* following negative error codes:1773*1774* NGHTTP2_ERR_STREAM_CLOSED1775* The stream is already closed.1776* NGHTTP2_ERR_STREAM_SHUT_WR1777* The stream is half-closed for transmission.1778* NGHTTP2_ERR_SESSION_CLOSING1779* This session is closing.1780*/1781static int session_predicate_for_stream_send(nghttp2_session *session,1782nghttp2_stream *stream) {1783if (stream == NULL) {1784return NGHTTP2_ERR_STREAM_CLOSED;1785}1786if (session_is_closing(session)) {1787return NGHTTP2_ERR_SESSION_CLOSING;1788}1789if (stream->shut_flags & NGHTTP2_SHUT_WR) {1790return NGHTTP2_ERR_STREAM_SHUT_WR;1791}1792return 0;1793}17941795int nghttp2_session_check_request_allowed(nghttp2_session *session) {1796return !session->server && session->next_stream_id <= INT32_MAX &&1797(session->goaway_flags & NGHTTP2_GOAWAY_RECV) == 0 &&1798!session_is_closing(session);1799}18001801/*1802* This function checks request HEADERS frame, which opens stream, can1803* be sent at this time.1804*1805* This function returns 0 if it succeeds, or one of the following1806* negative error codes:1807*1808* NGHTTP2_ERR_START_STREAM_NOT_ALLOWED1809* New stream cannot be created because of GOAWAY: session is1810* going down or received last_stream_id is strictly less than1811* frame->hd.stream_id.1812* NGHTTP2_ERR_STREAM_CLOSING1813* request HEADERS was canceled by RST_STREAM while it is in queue.1814*/1815static int session_predicate_request_headers_send(nghttp2_session *session,1816nghttp2_outbound_item *item) {1817if (item->aux_data.headers.canceled) {1818return NGHTTP2_ERR_STREAM_CLOSING;1819}1820/* If we are terminating session (NGHTTP2_GOAWAY_TERM_ON_SEND),1821GOAWAY was received from peer, or session is about to close, new1822request is not allowed. */1823if ((session->goaway_flags & NGHTTP2_GOAWAY_RECV) ||1824session_is_closing(session)) {1825return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;1826}1827return 0;1828}18291830/*1831* This function checks HEADERS, which is the first frame from the1832* server, with the |stream| can be sent at this time. The |stream|1833* can be NULL.1834*1835* This function returns 0 if it succeeds, or one of the following1836* negative error codes:1837*1838* NGHTTP2_ERR_STREAM_CLOSED1839* The stream is already closed or does not exist.1840* NGHTTP2_ERR_STREAM_SHUT_WR1841* The transmission is not allowed for this stream (e.g., a frame1842* with END_STREAM flag set has already sent)1843* NGHTTP2_ERR_INVALID_STREAM_ID1844* The stream ID is invalid.1845* NGHTTP2_ERR_STREAM_CLOSING1846* RST_STREAM was queued for this stream.1847* NGHTTP2_ERR_INVALID_STREAM_STATE1848* The state of the stream is not valid.1849* NGHTTP2_ERR_SESSION_CLOSING1850* This session is closing.1851* NGHTTP2_ERR_PROTO1852* Client side attempted to send response.1853*/1854static int session_predicate_response_headers_send(nghttp2_session *session,1855nghttp2_stream *stream) {1856int rv;1857rv = session_predicate_for_stream_send(session, stream);1858if (rv != 0) {1859return rv;1860}1861assert(stream);1862if (!session->server) {1863return NGHTTP2_ERR_PROTO;1864}1865if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {1866return NGHTTP2_ERR_INVALID_STREAM_ID;1867}1868switch (stream->state) {1869case NGHTTP2_STREAM_OPENING:1870return 0;1871case NGHTTP2_STREAM_CLOSING:1872return NGHTTP2_ERR_STREAM_CLOSING;1873default:1874return NGHTTP2_ERR_INVALID_STREAM_STATE;1875}1876}18771878/*1879* This function checks HEADERS for reserved stream can be sent. The1880* |stream| must be reserved state and the |session| is server side.1881* The |stream| can be NULL.1882*1883* This function returns 0 if it succeeds, or one of the following1884* error codes:1885*1886* NGHTTP2_ERR_STREAM_CLOSED1887* The stream is already closed.1888* NGHTTP2_ERR_STREAM_SHUT_WR1889* The stream is half-closed for transmission.1890* NGHTTP2_ERR_PROTO1891* The stream is not reserved state1892* NGHTTP2_ERR_STREAM_CLOSED1893* RST_STREAM was queued for this stream.1894* NGHTTP2_ERR_SESSION_CLOSING1895* This session is closing.1896* NGHTTP2_ERR_START_STREAM_NOT_ALLOWED1897* New stream cannot be created because GOAWAY is already sent or1898* received.1899* NGHTTP2_ERR_PROTO1900* Client side attempted to send push response.1901*/1902static int1903session_predicate_push_response_headers_send(nghttp2_session *session,1904nghttp2_stream *stream) {1905int rv;1906/* TODO Should disallow HEADERS if GOAWAY has already been issued? */1907rv = session_predicate_for_stream_send(session, stream);1908if (rv != 0) {1909return rv;1910}1911assert(stream);1912if (!session->server) {1913return NGHTTP2_ERR_PROTO;1914}1915if (stream->state != NGHTTP2_STREAM_RESERVED) {1916return NGHTTP2_ERR_PROTO;1917}1918if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) {1919return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;1920}1921return 0;1922}19231924/*1925* This function checks HEADERS, which is neither stream-opening nor1926* first response header, with the |stream| can be sent at this time.1927* The |stream| can be NULL.1928*1929* This function returns 0 if it succeeds, or one of the following1930* negative error codes:1931*1932* NGHTTP2_ERR_STREAM_CLOSED1933* The stream is already closed or does not exist.1934* NGHTTP2_ERR_STREAM_SHUT_WR1935* The transmission is not allowed for this stream (e.g., a frame1936* with END_STREAM flag set has already sent)1937* NGHTTP2_ERR_STREAM_CLOSING1938* RST_STREAM was queued for this stream.1939* NGHTTP2_ERR_INVALID_STREAM_STATE1940* The state of the stream is not valid.1941* NGHTTP2_ERR_SESSION_CLOSING1942* This session is closing.1943*/1944static int session_predicate_headers_send(nghttp2_session *session,1945nghttp2_stream *stream) {1946int rv;1947rv = session_predicate_for_stream_send(session, stream);1948if (rv != 0) {1949return rv;1950}1951assert(stream);19521953switch (stream->state) {1954case NGHTTP2_STREAM_OPENED:1955return 0;1956case NGHTTP2_STREAM_CLOSING:1957return NGHTTP2_ERR_STREAM_CLOSING;1958default:1959if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {1960return 0;1961}1962return NGHTTP2_ERR_INVALID_STREAM_STATE;1963}1964}19651966/*1967* This function checks PUSH_PROMISE frame |frame| with the |stream|1968* can be sent at this time. The |stream| can be NULL.1969*1970* This function returns 0 if it succeeds, or one of the following1971* negative error codes:1972*1973* NGHTTP2_ERR_START_STREAM_NOT_ALLOWED1974* New stream cannot be created because GOAWAY is already sent or1975* received.1976* NGHTTP2_ERR_PROTO1977* The client side attempts to send PUSH_PROMISE, or the server1978* sends PUSH_PROMISE for the stream not initiated by the client.1979* NGHTTP2_ERR_STREAM_CLOSED1980* The stream is already closed or does not exist.1981* NGHTTP2_ERR_STREAM_CLOSING1982* RST_STREAM was queued for this stream.1983* NGHTTP2_ERR_STREAM_SHUT_WR1984* The transmission is not allowed for this stream (e.g., a frame1985* with END_STREAM flag set has already sent)1986* NGHTTP2_ERR_PUSH_DISABLED1987* The remote peer disabled reception of PUSH_PROMISE.1988* NGHTTP2_ERR_SESSION_CLOSING1989* This session is closing.1990*/1991static int session_predicate_push_promise_send(nghttp2_session *session,1992nghttp2_stream *stream) {1993int rv;19941995if (!session->server) {1996return NGHTTP2_ERR_PROTO;1997}19981999rv = session_predicate_for_stream_send(session, stream);2000if (rv != 0) {2001return rv;2002}20032004assert(stream);20052006if (session->remote_settings.enable_push == 0) {2007return NGHTTP2_ERR_PUSH_DISABLED;2008}2009if (stream->state == NGHTTP2_STREAM_CLOSING) {2010return NGHTTP2_ERR_STREAM_CLOSING;2011}2012if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) {2013return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;2014}2015return 0;2016}20172018/*2019* This function checks WINDOW_UPDATE with the stream ID |stream_id|2020* can be sent at this time. Note that END_STREAM flag of the previous2021* frame does not affect the transmission of the WINDOW_UPDATE frame.2022*2023* This function returns 0 if it succeeds, or one of the following2024* negative error codes:2025*2026* NGHTTP2_ERR_STREAM_CLOSED2027* The stream is already closed or does not exist.2028* NGHTTP2_ERR_STREAM_CLOSING2029* RST_STREAM was queued for this stream.2030* NGHTTP2_ERR_INVALID_STREAM_STATE2031* The state of the stream is not valid.2032* NGHTTP2_ERR_SESSION_CLOSING2033* This session is closing.2034*/2035static int session_predicate_window_update_send(nghttp2_session *session,2036int32_t stream_id) {2037nghttp2_stream *stream;20382039if (session_is_closing(session)) {2040return NGHTTP2_ERR_SESSION_CLOSING;2041}20422043if (stream_id == 0) {2044/* Connection-level window update */2045return 0;2046}2047stream = nghttp2_session_get_stream(session, stream_id);2048if (stream == NULL) {2049return NGHTTP2_ERR_STREAM_CLOSED;2050}2051if (stream->state == NGHTTP2_STREAM_CLOSING) {2052return NGHTTP2_ERR_STREAM_CLOSING;2053}2054if (state_reserved_local(session, stream)) {2055return NGHTTP2_ERR_INVALID_STREAM_STATE;2056}2057return 0;2058}20592060static int session_predicate_altsvc_send(nghttp2_session *session,2061int32_t stream_id) {2062nghttp2_stream *stream;20632064if (session_is_closing(session)) {2065return NGHTTP2_ERR_SESSION_CLOSING;2066}20672068if (stream_id == 0) {2069return 0;2070}20712072stream = nghttp2_session_get_stream(session, stream_id);2073if (stream == NULL) {2074return NGHTTP2_ERR_STREAM_CLOSED;2075}2076if (stream->state == NGHTTP2_STREAM_CLOSING) {2077return NGHTTP2_ERR_STREAM_CLOSING;2078}20792080return 0;2081}20822083static int session_predicate_origin_send(nghttp2_session *session) {2084if (session_is_closing(session)) {2085return NGHTTP2_ERR_SESSION_CLOSING;2086}2087return 0;2088}20892090static int session_predicate_priority_update_send(nghttp2_session *session,2091int32_t stream_id) {2092nghttp2_stream *stream;20932094if (session_is_closing(session)) {2095return NGHTTP2_ERR_SESSION_CLOSING;2096}20972098stream = nghttp2_session_get_stream(session, stream_id);2099if (stream == NULL) {2100return 0;2101}2102if (stream->state == NGHTTP2_STREAM_CLOSING) {2103return NGHTTP2_ERR_STREAM_CLOSING;2104}2105if (stream->shut_flags & NGHTTP2_SHUT_RD) {2106return NGHTTP2_ERR_INVALID_STREAM_STATE;2107}21082109return 0;2110}21112112/* Take into account settings max frame size and both connection-level2113flow control here */2114static ssize_t2115nghttp2_session_enforce_flow_control_limits(nghttp2_session *session,2116nghttp2_stream *stream,2117ssize_t requested_window_size) {2118DEBUGF("send: remote windowsize connection=%d, remote maxframsize=%u, "2119"stream(id %d)=%d\n",2120session->remote_window_size, session->remote_settings.max_frame_size,2121stream->stream_id, stream->remote_window_size);21222123return nghttp2_min(nghttp2_min(nghttp2_min(requested_window_size,2124stream->remote_window_size),2125session->remote_window_size),2126(int32_t)session->remote_settings.max_frame_size);2127}21282129/*2130* Returns the maximum length of next data read. If the2131* connection-level and/or stream-wise flow control are enabled, the2132* return value takes into account those current window sizes. The remote2133* settings for max frame size is also taken into account.2134*/2135static size_t nghttp2_session_next_data_read(nghttp2_session *session,2136nghttp2_stream *stream) {2137ssize_t window_size;21382139window_size = nghttp2_session_enforce_flow_control_limits(2140session, stream, NGHTTP2_DATA_PAYLOADLEN);21412142DEBUGF("send: available window=%zd\n", window_size);21432144return window_size > 0 ? (size_t)window_size : 0;2145}21462147/*2148* This function checks DATA with the |stream| can be sent at this2149* time. The |stream| can be NULL.2150*2151* This function returns 0 if it succeeds, or one of the following2152* negative error codes:2153*2154* NGHTTP2_ERR_STREAM_CLOSED2155* The stream is already closed or does not exist.2156* NGHTTP2_ERR_STREAM_SHUT_WR2157* The transmission is not allowed for this stream (e.g., a frame2158* with END_STREAM flag set has already sent)2159* NGHTTP2_ERR_STREAM_CLOSING2160* RST_STREAM was queued for this stream.2161* NGHTTP2_ERR_INVALID_STREAM_STATE2162* The state of the stream is not valid.2163* NGHTTP2_ERR_SESSION_CLOSING2164* This session is closing.2165*/2166static int nghttp2_session_predicate_data_send(nghttp2_session *session,2167nghttp2_stream *stream) {2168int rv;2169rv = session_predicate_for_stream_send(session, stream);2170if (rv != 0) {2171return rv;2172}2173assert(stream);2174if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {2175/* Request body data */2176/* If stream->state is NGHTTP2_STREAM_CLOSING, RST_STREAM was2177queued but not yet sent. In this case, we won't send DATA2178frames. */2179if (stream->state == NGHTTP2_STREAM_CLOSING) {2180return NGHTTP2_ERR_STREAM_CLOSING;2181}2182if (stream->state == NGHTTP2_STREAM_RESERVED) {2183return NGHTTP2_ERR_INVALID_STREAM_STATE;2184}2185return 0;2186}2187/* Response body data */2188if (stream->state == NGHTTP2_STREAM_OPENED) {2189return 0;2190}2191if (stream->state == NGHTTP2_STREAM_CLOSING) {2192return NGHTTP2_ERR_STREAM_CLOSING;2193}2194return NGHTTP2_ERR_INVALID_STREAM_STATE;2195}21962197static ssize_t session_call_select_padding(nghttp2_session *session,2198const nghttp2_frame *frame,2199size_t max_payloadlen) {2200ssize_t rv;22012202if (frame->hd.length >= max_payloadlen) {2203return (ssize_t)frame->hd.length;2204}22052206if (session->callbacks.select_padding_callback) {2207size_t max_paddedlen;22082209max_paddedlen =2210nghttp2_min(frame->hd.length + NGHTTP2_MAX_PADLEN, max_payloadlen);22112212rv = session->callbacks.select_padding_callback(2213session, frame, max_paddedlen, session->user_data);2214if (rv < (ssize_t)frame->hd.length || rv > (ssize_t)max_paddedlen) {2215return NGHTTP2_ERR_CALLBACK_FAILURE;2216}2217return rv;2218}2219return (ssize_t)frame->hd.length;2220}22212222/* Add padding to HEADERS or PUSH_PROMISE. We use2223frame->headers.padlen in this function to use the fact that2224frame->push_promise has also padlen in the same position. */2225static int session_headers_add_pad(nghttp2_session *session,2226nghttp2_frame *frame) {2227int rv;2228ssize_t padded_payloadlen;2229nghttp2_active_outbound_item *aob;2230nghttp2_bufs *framebufs;2231size_t padlen;2232size_t max_payloadlen;22332234aob = &session->aob;2235framebufs = &aob->framebufs;22362237max_payloadlen = nghttp2_min(NGHTTP2_MAX_PAYLOADLEN,2238frame->hd.length + NGHTTP2_MAX_PADLEN);22392240padded_payloadlen =2241session_call_select_padding(session, frame, max_payloadlen);22422243if (nghttp2_is_fatal((int)padded_payloadlen)) {2244return (int)padded_payloadlen;2245}22462247padlen = (size_t)padded_payloadlen - frame->hd.length;22482249DEBUGF("send: padding selected: payloadlen=%zd, padlen=%zu\n",2250padded_payloadlen, padlen);22512252rv = nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, 0);22532254if (rv != 0) {2255return rv;2256}22572258frame->headers.padlen = padlen;22592260return 0;2261}22622263static size_t session_estimate_headers_payload(nghttp2_session *session,2264const nghttp2_nv *nva,2265size_t nvlen,2266size_t additional) {2267return nghttp2_hd_deflate_bound(&session->hd_deflater, nva, nvlen) +2268additional;2269}22702271static int session_pack_extension(nghttp2_session *session, nghttp2_bufs *bufs,2272nghttp2_frame *frame) {2273ssize_t rv;2274nghttp2_buf *buf;2275size_t buflen;2276size_t framelen;22772278assert(session->callbacks.pack_extension_callback);22792280buf = &bufs->head->buf;2281buflen = nghttp2_min(nghttp2_buf_avail(buf), NGHTTP2_MAX_PAYLOADLEN);22822283rv = session->callbacks.pack_extension_callback(session, buf->last, buflen,2284frame, session->user_data);2285if (rv == NGHTTP2_ERR_CANCEL) {2286return (int)rv;2287}22882289if (rv < 0 || (size_t)rv > buflen) {2290return NGHTTP2_ERR_CALLBACK_FAILURE;2291}22922293framelen = (size_t)rv;22942295frame->hd.length = framelen;22962297assert(buf->pos == buf->last);2298buf->last += framelen;2299buf->pos -= NGHTTP2_FRAME_HDLEN;23002301nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);23022303return 0;2304}23052306/*2307* This function serializes frame for transmission.2308*2309* This function returns 0 if it succeeds, or one of negative error2310* codes, including both fatal and non-fatal ones.2311*/2312static int session_prep_frame(nghttp2_session *session,2313nghttp2_outbound_item *item) {2314int rv;2315nghttp2_frame *frame;2316nghttp2_mem *mem;23172318mem = &session->mem;2319frame = &item->frame;23202321switch (frame->hd.type) {2322case NGHTTP2_DATA: {2323size_t next_readmax;2324nghttp2_stream *stream;23252326stream = nghttp2_session_get_stream(session, frame->hd.stream_id);23272328if (stream) {2329assert(stream->item == item);2330}23312332rv = nghttp2_session_predicate_data_send(session, stream);2333if (rv != 0) {2334// If stream was already closed, nghttp2_session_get_stream()2335// returns NULL, but item is still attached to the stream.2336// Search stream including closed again.2337stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);2338if (stream) {2339int rv2;23402341rv2 = session_detach_stream_item(session, stream);23422343if (nghttp2_is_fatal(rv2)) {2344return rv2;2345}2346}23472348return rv;2349}2350/* Assuming stream is not NULL */2351assert(stream);2352next_readmax = nghttp2_session_next_data_read(session, stream);23532354if (next_readmax == 0) {23552356/* This must be true since we only pop DATA frame item from2357queue when session->remote_window_size > 0 */2358assert(session->remote_window_size > 0);23592360rv = session_defer_stream_item(session, stream,2361NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);23622363if (nghttp2_is_fatal(rv)) {2364return rv;2365}23662367session->aob.item = NULL;2368active_outbound_item_reset(&session->aob, mem);2369return NGHTTP2_ERR_DEFERRED;2370}23712372rv = nghttp2_session_pack_data(session, &session->aob.framebufs,2373next_readmax, frame, &item->aux_data.data,2374stream);2375if (rv == NGHTTP2_ERR_PAUSE) {2376return rv;2377}2378if (rv == NGHTTP2_ERR_DEFERRED) {2379rv = session_defer_stream_item(session, stream,2380NGHTTP2_STREAM_FLAG_DEFERRED_USER);23812382if (nghttp2_is_fatal(rv)) {2383return rv;2384}23852386session->aob.item = NULL;2387active_outbound_item_reset(&session->aob, mem);2388return NGHTTP2_ERR_DEFERRED;2389}2390if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {2391rv = session_detach_stream_item(session, stream);23922393if (nghttp2_is_fatal(rv)) {2394return rv;2395}23962397rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,2398NGHTTP2_INTERNAL_ERROR);2399if (nghttp2_is_fatal(rv)) {2400return rv;2401}2402return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;2403}2404if (rv != 0) {2405int rv2;24062407rv2 = session_detach_stream_item(session, stream);24082409if (nghttp2_is_fatal(rv2)) {2410return rv2;2411}24122413return rv;2414}2415return 0;2416}2417case NGHTTP2_HEADERS: {2418nghttp2_headers_aux_data *aux_data;2419size_t estimated_payloadlen;24202421aux_data = &item->aux_data.headers;24222423if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {2424/* initial HEADERS, which opens stream */2425nghttp2_stream *stream;24262427stream = nghttp2_session_open_stream(2428session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE,2429&frame->headers.pri_spec, NGHTTP2_STREAM_INITIAL,2430aux_data->stream_user_data);24312432if (stream == NULL) {2433return NGHTTP2_ERR_NOMEM;2434}24352436/* We don't call nghttp2_session_adjust_closed_stream() here,2437since we don't keep closed stream in client side */24382439rv = session_predicate_request_headers_send(session, item);2440if (rv != 0) {2441return rv;2442}24432444if (session_enforce_http_messaging(session)) {2445nghttp2_http_record_request_method(stream, frame);2446}2447} else {2448nghttp2_stream *stream;24492450stream = nghttp2_session_get_stream(session, frame->hd.stream_id);24512452if (stream && stream->state == NGHTTP2_STREAM_RESERVED) {2453rv = session_predicate_push_response_headers_send(session, stream);2454if (rv == 0) {2455frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE;24562457if (aux_data->stream_user_data) {2458stream->stream_user_data = aux_data->stream_user_data;2459}2460}2461} else if (session_predicate_response_headers_send(session, stream) ==24620) {2463frame->headers.cat = NGHTTP2_HCAT_RESPONSE;2464rv = 0;2465} else {2466frame->headers.cat = NGHTTP2_HCAT_HEADERS;24672468rv = session_predicate_headers_send(session, stream);2469}24702471if (rv != 0) {2472return rv;2473}2474}24752476estimated_payloadlen = session_estimate_headers_payload(2477session, frame->headers.nva, frame->headers.nvlen,2478NGHTTP2_PRIORITY_SPECLEN);24792480if (estimated_payloadlen > session->max_send_header_block_length) {2481return NGHTTP2_ERR_FRAME_SIZE_ERROR;2482}24832484rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers,2485&session->hd_deflater);24862487if (rv != 0) {2488return rv;2489}24902491DEBUGF("send: before padding, HEADERS serialized in %zd bytes\n",2492nghttp2_bufs_len(&session->aob.framebufs));24932494rv = session_headers_add_pad(session, frame);24952496if (rv != 0) {2497return rv;2498}24992500DEBUGF("send: HEADERS finally serialized in %zd bytes\n",2501nghttp2_bufs_len(&session->aob.framebufs));25022503if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {2504assert(session->last_sent_stream_id < frame->hd.stream_id);2505session->last_sent_stream_id = frame->hd.stream_id;2506}25072508return 0;2509}2510case NGHTTP2_PRIORITY: {2511if (session_is_closing(session)) {2512return NGHTTP2_ERR_SESSION_CLOSING;2513}2514/* PRIORITY frame can be sent at any time and to any stream2515ID. */2516nghttp2_frame_pack_priority(&session->aob.framebufs, &frame->priority);25172518/* Peer can send PRIORITY frame against idle stream to create2519"anchor" in dependency tree. Only client can do this in2520nghttp2. In nghttp2, only server retains non-active (closed2521or idle) streams in memory, so we don't open stream here. */2522return 0;2523}2524case NGHTTP2_RST_STREAM:2525if (session_is_closing(session)) {2526return NGHTTP2_ERR_SESSION_CLOSING;2527}2528nghttp2_frame_pack_rst_stream(&session->aob.framebufs, &frame->rst_stream);2529return 0;2530case NGHTTP2_SETTINGS: {2531if (frame->hd.flags & NGHTTP2_FLAG_ACK) {2532assert(session->obq_flood_counter_ > 0);2533--session->obq_flood_counter_;2534/* When session is about to close, don't send SETTINGS ACK.2535We are required to send SETTINGS without ACK though; for2536example, we have to send SETTINGS as a part of connection2537preface. */2538if (session_is_closing(session)) {2539return NGHTTP2_ERR_SESSION_CLOSING;2540}2541}25422543rv = nghttp2_frame_pack_settings(&session->aob.framebufs, &frame->settings);2544if (rv != 0) {2545return rv;2546}2547return 0;2548}2549case NGHTTP2_PUSH_PROMISE: {2550nghttp2_stream *stream;2551size_t estimated_payloadlen;25522553/* stream could be NULL if associated stream was already2554closed. */2555stream = nghttp2_session_get_stream(session, frame->hd.stream_id);25562557/* predicate should fail if stream is NULL. */2558rv = session_predicate_push_promise_send(session, stream);2559if (rv != 0) {2560return rv;2561}25622563assert(stream);25642565estimated_payloadlen = session_estimate_headers_payload(2566session, frame->push_promise.nva, frame->push_promise.nvlen, 0);25672568if (estimated_payloadlen > session->max_send_header_block_length) {2569return NGHTTP2_ERR_FRAME_SIZE_ERROR;2570}25712572rv = nghttp2_frame_pack_push_promise(2573&session->aob.framebufs, &frame->push_promise, &session->hd_deflater);2574if (rv != 0) {2575return rv;2576}2577rv = session_headers_add_pad(session, frame);2578if (rv != 0) {2579return rv;2580}25812582assert(session->last_sent_stream_id + 2 <=2583frame->push_promise.promised_stream_id);2584session->last_sent_stream_id = frame->push_promise.promised_stream_id;25852586return 0;2587}2588case NGHTTP2_PING:2589if (frame->hd.flags & NGHTTP2_FLAG_ACK) {2590assert(session->obq_flood_counter_ > 0);2591--session->obq_flood_counter_;2592}2593/* PING frame is allowed to be sent unless termination GOAWAY is2594sent */2595if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {2596return NGHTTP2_ERR_SESSION_CLOSING;2597}2598nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping);2599return 0;2600case NGHTTP2_GOAWAY:2601rv = nghttp2_frame_pack_goaway(&session->aob.framebufs, &frame->goaway);2602if (rv != 0) {2603return rv;2604}2605session->local_last_stream_id = frame->goaway.last_stream_id;26062607return 0;2608case NGHTTP2_WINDOW_UPDATE:2609rv = session_predicate_window_update_send(session, frame->hd.stream_id);2610if (rv != 0) {2611return rv;2612}2613nghttp2_frame_pack_window_update(&session->aob.framebufs,2614&frame->window_update);2615return 0;2616case NGHTTP2_CONTINUATION:2617/* We never handle CONTINUATION here. */2618assert(0);2619return 0;2620default: {2621nghttp2_ext_aux_data *aux_data;26222623/* extension frame */26242625aux_data = &item->aux_data.ext;26262627if (aux_data->builtin == 0) {2628if (session_is_closing(session)) {2629return NGHTTP2_ERR_SESSION_CLOSING;2630}26312632return session_pack_extension(session, &session->aob.framebufs, frame);2633}26342635switch (frame->hd.type) {2636case NGHTTP2_ALTSVC:2637rv = session_predicate_altsvc_send(session, frame->hd.stream_id);2638if (rv != 0) {2639return rv;2640}26412642nghttp2_frame_pack_altsvc(&session->aob.framebufs, &frame->ext);26432644return 0;2645case NGHTTP2_ORIGIN:2646rv = session_predicate_origin_send(session);2647if (rv != 0) {2648return rv;2649}26502651rv = nghttp2_frame_pack_origin(&session->aob.framebufs, &frame->ext);2652if (rv != 0) {2653return rv;2654}26552656return 0;2657case NGHTTP2_PRIORITY_UPDATE: {2658nghttp2_ext_priority_update *priority_update = frame->ext.payload;2659rv = session_predicate_priority_update_send(session,2660priority_update->stream_id);2661if (rv != 0) {2662return rv;2663}26642665nghttp2_frame_pack_priority_update(&session->aob.framebufs, &frame->ext);26662667return 0;2668}2669default:2670/* Unreachable here */2671assert(0);2672return 0;2673}2674}2675}2676}26772678nghttp2_outbound_item *2679nghttp2_session_get_next_ob_item(nghttp2_session *session) {2680nghttp2_outbound_item *item;26812682if (nghttp2_outbound_queue_top(&session->ob_urgent)) {2683return nghttp2_outbound_queue_top(&session->ob_urgent);2684}26852686if (nghttp2_outbound_queue_top(&session->ob_reg)) {2687return nghttp2_outbound_queue_top(&session->ob_reg);2688}26892690if (!session_is_outgoing_concurrent_streams_max(session)) {2691if (nghttp2_outbound_queue_top(&session->ob_syn)) {2692return nghttp2_outbound_queue_top(&session->ob_syn);2693}2694}26952696if (session->remote_window_size > 0) {2697item = nghttp2_stream_next_outbound_item(&session->root);2698if (item) {2699return item;2700}27012702return session_sched_get_next_outbound_item(session);2703}27042705return NULL;2706}27072708nghttp2_outbound_item *2709nghttp2_session_pop_next_ob_item(nghttp2_session *session) {2710nghttp2_outbound_item *item;27112712item = nghttp2_outbound_queue_top(&session->ob_urgent);2713if (item) {2714nghttp2_outbound_queue_pop(&session->ob_urgent);2715item->queued = 0;2716return item;2717}27182719item = nghttp2_outbound_queue_top(&session->ob_reg);2720if (item) {2721nghttp2_outbound_queue_pop(&session->ob_reg);2722item->queued = 0;2723return item;2724}27252726if (!session_is_outgoing_concurrent_streams_max(session)) {2727item = nghttp2_outbound_queue_top(&session->ob_syn);2728if (item) {2729nghttp2_outbound_queue_pop(&session->ob_syn);2730item->queued = 0;2731return item;2732}2733}27342735if (session->remote_window_size > 0) {2736item = nghttp2_stream_next_outbound_item(&session->root);2737if (item) {2738return item;2739}27402741return session_sched_get_next_outbound_item(session);2742}27432744return NULL;2745}27462747static int session_call_before_frame_send(nghttp2_session *session,2748nghttp2_frame *frame) {2749int rv;2750if (session->callbacks.before_frame_send_callback) {2751rv = session->callbacks.before_frame_send_callback(session, frame,2752session->user_data);2753if (rv == NGHTTP2_ERR_CANCEL) {2754return rv;2755}27562757if (rv != 0) {2758return NGHTTP2_ERR_CALLBACK_FAILURE;2759}2760}2761return 0;2762}27632764static int session_call_on_frame_send(nghttp2_session *session,2765nghttp2_frame *frame) {2766int rv;2767if (session->callbacks.on_frame_send_callback) {2768rv = session->callbacks.on_frame_send_callback(session, frame,2769session->user_data);2770if (rv != 0) {2771return NGHTTP2_ERR_CALLBACK_FAILURE;2772}2773}2774return 0;2775}27762777static int find_stream_on_goaway_func(void *entry, void *ptr) {2778nghttp2_close_stream_on_goaway_arg *arg;2779nghttp2_stream *stream;27802781arg = (nghttp2_close_stream_on_goaway_arg *)ptr;2782stream = (nghttp2_stream *)entry;27832784if (nghttp2_session_is_my_stream_id(arg->session, stream->stream_id)) {2785if (arg->incoming) {2786return 0;2787}2788} else if (!arg->incoming) {2789return 0;2790}27912792if (stream->state != NGHTTP2_STREAM_IDLE &&2793(stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) == 0 &&2794stream->stream_id > arg->last_stream_id) {2795/* We are collecting streams to close because we cannot call2796nghttp2_session_close_stream() inside nghttp2_map_each().2797Reuse closed_next member.. bad choice? */2798assert(stream->closed_next == NULL);2799assert(stream->closed_prev == NULL);28002801if (arg->head) {2802stream->closed_next = arg->head;2803arg->head = stream;2804} else {2805arg->head = stream;2806}2807}28082809return 0;2810}28112812/* Closes non-idle and non-closed streams whose stream ID >2813last_stream_id. If incoming is nonzero, we are going to close2814incoming streams. Otherwise, close outgoing streams. */2815static int session_close_stream_on_goaway(nghttp2_session *session,2816int32_t last_stream_id,2817int incoming) {2818int rv;2819nghttp2_stream *stream, *next_stream;2820nghttp2_close_stream_on_goaway_arg arg = {session, NULL, last_stream_id,2821incoming};28222823rv = nghttp2_map_each(&session->streams, find_stream_on_goaway_func, &arg);2824assert(rv == 0);28252826stream = arg.head;2827while (stream) {2828next_stream = stream->closed_next;2829stream->closed_next = NULL;2830rv = nghttp2_session_close_stream(session, stream->stream_id,2831NGHTTP2_REFUSED_STREAM);28322833/* stream may be deleted here */28342835stream = next_stream;28362837if (nghttp2_is_fatal(rv)) {2838/* Clean up closed_next member just in case */2839while (stream) {2840next_stream = stream->closed_next;2841stream->closed_next = NULL;2842stream = next_stream;2843}2844return rv;2845}2846}28472848return 0;2849}28502851static void session_reschedule_stream(nghttp2_session *session,2852nghttp2_stream *stream) {2853stream->last_writelen = stream->item->frame.hd.length;28542855if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) {2856nghttp2_stream_reschedule(stream);2857return;2858}28592860if (!session->server) {2861return;2862}28632864session_sched_reschedule_stream(session, stream);2865}28662867static int session_update_stream_consumed_size(nghttp2_session *session,2868nghttp2_stream *stream,2869size_t delta_size);28702871static int session_update_connection_consumed_size(nghttp2_session *session,2872size_t delta_size);28732874/*2875* Called after a frame is sent. This function runs2876* on_frame_send_callback and handles stream closure upon END_STREAM2877* or RST_STREAM. This function does not reset session->aob. It is a2878* responsibility of session_after_frame_sent2.2879*2880* This function returns 0 if it succeeds, or one of the following2881* negative error codes:2882*2883* NGHTTP2_ERR_NOMEM2884* Out of memory.2885* NGHTTP2_ERR_CALLBACK_FAILURE2886* The callback function failed.2887*/2888static int session_after_frame_sent1(nghttp2_session *session) {2889int rv;2890nghttp2_active_outbound_item *aob = &session->aob;2891nghttp2_outbound_item *item = aob->item;2892nghttp2_bufs *framebufs = &aob->framebufs;2893nghttp2_frame *frame;2894nghttp2_stream *stream;28952896frame = &item->frame;28972898if (frame->hd.type == NGHTTP2_DATA) {2899nghttp2_data_aux_data *aux_data;29002901aux_data = &item->aux_data.data;29022903stream = nghttp2_session_get_stream(session, frame->hd.stream_id);2904/* We update flow control window after a frame was completely2905sent. This is possible because we choose payload length not to2906exceed the window */2907session->remote_window_size -= (int32_t)frame->hd.length;2908if (stream) {2909stream->remote_window_size -= (int32_t)frame->hd.length;2910}29112912if (stream && aux_data->eof) {2913rv = session_detach_stream_item(session, stream);2914if (nghttp2_is_fatal(rv)) {2915return rv;2916}29172918/* Call on_frame_send_callback after2919nghttp2_stream_detach_item(), so that application can issue2920nghttp2_submit_data() in the callback. */2921if (session->callbacks.on_frame_send_callback) {2922rv = session_call_on_frame_send(session, frame);2923if (nghttp2_is_fatal(rv)) {2924return rv;2925}2926}29272928if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {2929int stream_closed;29302931stream_closed =2932(stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR;29332934nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);29352936rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);2937if (nghttp2_is_fatal(rv)) {2938return rv;2939}2940/* stream may be NULL if it was closed */2941if (stream_closed) {2942stream = NULL;2943}2944}2945return 0;2946}29472948if (session->callbacks.on_frame_send_callback) {2949rv = session_call_on_frame_send(session, frame);2950if (nghttp2_is_fatal(rv)) {2951return rv;2952}2953}29542955return 0;2956}29572958/* non-DATA frame */29592960if (frame->hd.type == NGHTTP2_HEADERS ||2961frame->hd.type == NGHTTP2_PUSH_PROMISE) {2962if (nghttp2_bufs_next_present(framebufs)) {2963DEBUGF("send: CONTINUATION exists, just return\n");2964return 0;2965}2966}2967rv = session_call_on_frame_send(session, frame);2968if (nghttp2_is_fatal(rv)) {2969return rv;2970}2971switch (frame->hd.type) {2972case NGHTTP2_HEADERS: {2973nghttp2_headers_aux_data *aux_data;29742975stream = nghttp2_session_get_stream(session, frame->hd.stream_id);2976if (!stream) {2977return 0;2978}29792980switch (frame->headers.cat) {2981case NGHTTP2_HCAT_REQUEST: {2982stream->state = NGHTTP2_STREAM_OPENING;2983if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {2984nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);2985}2986rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);2987if (nghttp2_is_fatal(rv)) {2988return rv;2989}2990/* We assume aux_data is a pointer to nghttp2_headers_aux_data */2991aux_data = &item->aux_data.headers;2992if (aux_data->data_prd.read_callback) {2993/* nghttp2_submit_data() makes a copy of aux_data->data_prd */2994rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM,2995frame->hd.stream_id, &aux_data->data_prd);2996if (nghttp2_is_fatal(rv)) {2997return rv;2998}2999/* TODO nghttp2_submit_data() may fail if stream has already3000DATA frame item. We might have to handle it here. */3001}3002return 0;3003}3004case NGHTTP2_HCAT_PUSH_RESPONSE:3005stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH);3006++session->num_outgoing_streams;3007/* Fall through */3008case NGHTTP2_HCAT_RESPONSE:3009stream->state = NGHTTP2_STREAM_OPENED;3010/* Fall through */3011case NGHTTP2_HCAT_HEADERS:3012if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {3013nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);3014}3015rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);3016if (nghttp2_is_fatal(rv)) {3017return rv;3018}3019/* We assume aux_data is a pointer to nghttp2_headers_aux_data */3020aux_data = &item->aux_data.headers;3021if (aux_data->data_prd.read_callback) {3022rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM,3023frame->hd.stream_id, &aux_data->data_prd);3024if (nghttp2_is_fatal(rv)) {3025return rv;3026}3027/* TODO nghttp2_submit_data() may fail if stream has already3028DATA frame item. We might have to handle it here. */3029}3030return 0;3031default:3032/* Unreachable */3033assert(0);3034return 0;3035}3036}3037case NGHTTP2_PRIORITY:3038if (session->server || session->pending_no_rfc7540_priorities == 1) {3039return 0;3040}30413042stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);30433044if (!stream) {3045if (!session_detect_idle_stream(session, frame->hd.stream_id)) {3046return 0;3047}30483049stream = nghttp2_session_open_stream(3050session, frame->hd.stream_id, NGHTTP2_FLAG_NONE,3051&frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL);3052if (!stream) {3053return NGHTTP2_ERR_NOMEM;3054}3055} else {3056rv = nghttp2_session_reprioritize_stream(session, stream,3057&frame->priority.pri_spec);3058if (nghttp2_is_fatal(rv)) {3059return rv;3060}3061}30623063rv = nghttp2_session_adjust_idle_stream(session);30643065if (nghttp2_is_fatal(rv)) {3066return rv;3067}30683069return 0;3070case NGHTTP2_RST_STREAM:3071rv = nghttp2_session_close_stream(session, frame->hd.stream_id,3072frame->rst_stream.error_code);3073if (nghttp2_is_fatal(rv)) {3074return rv;3075}3076return 0;3077case NGHTTP2_GOAWAY: {3078nghttp2_goaway_aux_data *aux_data;30793080aux_data = &item->aux_data.goaway;30813082if ((aux_data->flags & NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE) == 0) {30833084if (aux_data->flags & NGHTTP2_GOAWAY_AUX_TERM_ON_SEND) {3085session->goaway_flags |= NGHTTP2_GOAWAY_TERM_SENT;3086}30873088session->goaway_flags |= NGHTTP2_GOAWAY_SENT;30893090rv = session_close_stream_on_goaway(session, frame->goaway.last_stream_id,30911);30923093if (nghttp2_is_fatal(rv)) {3094return rv;3095}3096}30973098return 0;3099}3100case NGHTTP2_WINDOW_UPDATE:3101if (frame->hd.stream_id == 0) {3102session->window_update_queued = 0;3103if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {3104rv = session_update_connection_consumed_size(session, 0);3105} else {3106rv = nghttp2_session_update_recv_connection_window_size(session, 0);3107}31083109if (nghttp2_is_fatal(rv)) {3110return rv;3111}31123113return 0;3114}31153116stream = nghttp2_session_get_stream(session, frame->hd.stream_id);3117if (!stream) {3118return 0;3119}31203121stream->window_update_queued = 0;31223123/* We don't have to send WINDOW_UPDATE if END_STREAM from peer3124is seen. */3125if (stream->shut_flags & NGHTTP2_SHUT_RD) {3126return 0;3127}31283129if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {3130rv = session_update_stream_consumed_size(session, stream, 0);3131} else {3132rv =3133nghttp2_session_update_recv_stream_window_size(session, stream, 0, 1);3134}31353136if (nghttp2_is_fatal(rv)) {3137return rv;3138}31393140return 0;3141default:3142return 0;3143}3144}31453146/*3147* Called after a frame is sent and session_after_frame_sent1. This3148* function is responsible to reset session->aob.3149*3150* This function returns 0 if it succeeds, or one of the following3151* negative error codes:3152*3153* NGHTTP2_ERR_NOMEM3154* Out of memory.3155* NGHTTP2_ERR_CALLBACK_FAILURE3156* The callback function failed.3157*/3158static int session_after_frame_sent2(nghttp2_session *session) {3159int rv;3160nghttp2_active_outbound_item *aob = &session->aob;3161nghttp2_outbound_item *item = aob->item;3162nghttp2_bufs *framebufs = &aob->framebufs;3163nghttp2_frame *frame;3164nghttp2_mem *mem;3165nghttp2_stream *stream;3166nghttp2_data_aux_data *aux_data;31673168mem = &session->mem;3169frame = &item->frame;31703171if (frame->hd.type != NGHTTP2_DATA) {31723173if (frame->hd.type == NGHTTP2_HEADERS ||3174frame->hd.type == NGHTTP2_PUSH_PROMISE) {31753176if (nghttp2_bufs_next_present(framebufs)) {3177framebufs->cur = framebufs->cur->next;31783179DEBUGF("send: next CONTINUATION frame, %zu bytes\n",3180nghttp2_buf_len(&framebufs->cur->buf));31813182return 0;3183}3184}31853186active_outbound_item_reset(&session->aob, mem);31873188return 0;3189}31903191/* DATA frame */31923193aux_data = &item->aux_data.data;31943195/* On EOF, we have already detached data. Please note that3196application may issue nghttp2_submit_data() in3197on_frame_send_callback (call from session_after_frame_sent1),3198which attach data to stream. We don't want to detach it. */3199if (aux_data->eof) {3200active_outbound_item_reset(aob, mem);32013202return 0;3203}32043205/* Reset no_copy here because next write may not use this. */3206aux_data->no_copy = 0;32073208stream = nghttp2_session_get_stream(session, frame->hd.stream_id);32093210/* If session is closed or RST_STREAM was queued, we won't send3211further data. */3212if (nghttp2_session_predicate_data_send(session, stream) != 0) {3213if (stream) {3214rv = session_detach_stream_item(session, stream);32153216if (nghttp2_is_fatal(rv)) {3217return rv;3218}3219}32203221active_outbound_item_reset(aob, mem);32223223return 0;3224}32253226aob->item = NULL;3227active_outbound_item_reset(&session->aob, mem);32283229return 0;3230}32313232static int session_call_send_data(nghttp2_session *session,3233nghttp2_outbound_item *item,3234nghttp2_bufs *framebufs) {3235int rv;3236nghttp2_buf *buf;3237size_t length;3238nghttp2_frame *frame;3239nghttp2_data_aux_data *aux_data;32403241buf = &framebufs->cur->buf;3242frame = &item->frame;3243length = frame->hd.length - frame->data.padlen;3244aux_data = &item->aux_data.data;32453246rv = session->callbacks.send_data_callback(session, frame, buf->pos, length,3247&aux_data->data_prd.source,3248session->user_data);32493250switch (rv) {3251case 0:3252case NGHTTP2_ERR_WOULDBLOCK:3253case NGHTTP2_ERR_PAUSE:3254case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE:3255return rv;3256default:3257return NGHTTP2_ERR_CALLBACK_FAILURE;3258}3259}32603261static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session,3262const uint8_t **data_ptr,3263int fast_cb) {3264int rv;3265nghttp2_active_outbound_item *aob;3266nghttp2_bufs *framebufs;3267nghttp2_mem *mem;32683269mem = &session->mem;3270aob = &session->aob;3271framebufs = &aob->framebufs;32723273/* We may have idle streams more than we expect (e.g.,3274nghttp2_session_change_stream_priority() or3275nghttp2_session_create_idle_stream()). Adjust them here. */3276rv = nghttp2_session_adjust_idle_stream(session);3277if (nghttp2_is_fatal(rv)) {3278return rv;3279}32803281for (;;) {3282switch (aob->state) {3283case NGHTTP2_OB_POP_ITEM: {3284nghttp2_outbound_item *item;32853286item = nghttp2_session_pop_next_ob_item(session);3287if (item == NULL) {3288return 0;3289}32903291rv = session_prep_frame(session, item);3292if (rv == NGHTTP2_ERR_PAUSE) {3293return 0;3294}3295if (rv == NGHTTP2_ERR_DEFERRED) {3296DEBUGF("send: frame transmission deferred\n");3297break;3298}3299if (rv < 0) {3300int32_t opened_stream_id = 0;3301uint32_t error_code = NGHTTP2_INTERNAL_ERROR;33023303DEBUGF("send: frame preparation failed with %s\n",3304nghttp2_strerror(rv));3305/* TODO If the error comes from compressor, the connection3306must be closed. */3307if (item->frame.hd.type != NGHTTP2_DATA &&3308session->callbacks.on_frame_not_send_callback && is_non_fatal(rv)) {3309nghttp2_frame *frame = &item->frame;3310/* The library is responsible for the transmission of3311WINDOW_UPDATE frame, so we don't call error callback for3312it. */3313if (frame->hd.type != NGHTTP2_WINDOW_UPDATE &&3314session->callbacks.on_frame_not_send_callback(3315session, frame, rv, session->user_data) != 0) {33163317nghttp2_outbound_item_free(item, mem);3318nghttp2_mem_free(mem, item);33193320return NGHTTP2_ERR_CALLBACK_FAILURE;3321}3322}3323/* We have to close stream opened by failed request HEADERS3324or PUSH_PROMISE. */3325switch (item->frame.hd.type) {3326case NGHTTP2_HEADERS:3327if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) {3328opened_stream_id = item->frame.hd.stream_id;3329if (item->aux_data.headers.canceled) {3330error_code = item->aux_data.headers.error_code;3331} else {3332/* Set error_code to REFUSED_STREAM so that application3333can send request again. */3334error_code = NGHTTP2_REFUSED_STREAM;3335}3336}3337break;3338case NGHTTP2_PUSH_PROMISE:3339opened_stream_id = item->frame.push_promise.promised_stream_id;3340break;3341}3342if (opened_stream_id) {3343/* careful not to override rv */3344int rv2;3345rv2 = nghttp2_session_close_stream(session, opened_stream_id,3346error_code);33473348if (nghttp2_is_fatal(rv2)) {3349return rv2;3350}3351}33523353nghttp2_outbound_item_free(item, mem);3354nghttp2_mem_free(mem, item);3355active_outbound_item_reset(aob, mem);33563357if (rv == NGHTTP2_ERR_HEADER_COMP) {3358/* If header compression error occurred, should terminiate3359connection. */3360rv = nghttp2_session_terminate_session(session,3361NGHTTP2_INTERNAL_ERROR);3362}3363if (nghttp2_is_fatal(rv)) {3364return rv;3365}3366break;3367}33683369aob->item = item;33703371nghttp2_bufs_rewind(framebufs);33723373if (item->frame.hd.type != NGHTTP2_DATA) {3374nghttp2_frame *frame;33753376frame = &item->frame;33773378DEBUGF("send: next frame: payloadlen=%zu, type=%u, flags=0x%02x, "3379"stream_id=%d\n",3380frame->hd.length, frame->hd.type, frame->hd.flags,3381frame->hd.stream_id);33823383rv = session_call_before_frame_send(session, frame);3384if (nghttp2_is_fatal(rv)) {3385return rv;3386}33873388if (rv == NGHTTP2_ERR_CANCEL) {3389int32_t opened_stream_id = 0;3390uint32_t error_code = NGHTTP2_INTERNAL_ERROR;33913392if (session->callbacks.on_frame_not_send_callback) {3393if (session->callbacks.on_frame_not_send_callback(3394session, frame, rv, session->user_data) != 0) {3395return NGHTTP2_ERR_CALLBACK_FAILURE;3396}3397}33983399/* We have to close stream opened by canceled request3400HEADERS or PUSH_PROMISE. */3401switch (item->frame.hd.type) {3402case NGHTTP2_HEADERS:3403if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) {3404opened_stream_id = item->frame.hd.stream_id;3405/* We don't have to check3406item->aux_data.headers.canceled since it has already3407been checked. */3408/* Set error_code to REFUSED_STREAM so that application3409can send request again. */3410error_code = NGHTTP2_REFUSED_STREAM;3411}3412break;3413case NGHTTP2_PUSH_PROMISE:3414opened_stream_id = item->frame.push_promise.promised_stream_id;3415break;3416}3417if (opened_stream_id) {3418/* careful not to override rv */3419int rv2;3420rv2 = nghttp2_session_close_stream(session, opened_stream_id,3421error_code);34223423if (nghttp2_is_fatal(rv2)) {3424return rv2;3425}3426}34273428active_outbound_item_reset(aob, mem);34293430break;3431}3432} else {3433DEBUGF("send: next frame: DATA\n");34343435if (item->aux_data.data.no_copy) {3436aob->state = NGHTTP2_OB_SEND_NO_COPY;3437break;3438}3439}34403441DEBUGF("send: start transmitting frame type=%u, length=%zd\n",3442framebufs->cur->buf.pos[3],3443framebufs->cur->buf.last - framebufs->cur->buf.pos);34443445aob->state = NGHTTP2_OB_SEND_DATA;34463447break;3448}3449case NGHTTP2_OB_SEND_DATA: {3450size_t datalen;3451nghttp2_buf *buf;34523453buf = &framebufs->cur->buf;34543455if (buf->pos == buf->last) {3456DEBUGF("send: end transmission of a frame\n");34573458/* Frame has completely sent */3459if (fast_cb) {3460rv = session_after_frame_sent2(session);3461} else {3462rv = session_after_frame_sent1(session);3463if (rv < 0) {3464/* FATAL */3465assert(nghttp2_is_fatal(rv));3466return rv;3467}3468rv = session_after_frame_sent2(session);3469}3470if (rv < 0) {3471/* FATAL */3472assert(nghttp2_is_fatal(rv));3473return rv;3474}3475/* We have already adjusted the next state */3476break;3477}34783479*data_ptr = buf->pos;3480datalen = nghttp2_buf_len(buf);34813482/* We increment the offset here. If send_callback does not send3483everything, we will adjust it. */3484buf->pos += datalen;34853486return (ssize_t)datalen;3487}3488case NGHTTP2_OB_SEND_NO_COPY: {3489nghttp2_stream *stream;3490nghttp2_frame *frame;3491int pause;34923493DEBUGF("send: no copy DATA\n");34943495frame = &aob->item->frame;34963497stream = nghttp2_session_get_stream(session, frame->hd.stream_id);3498if (stream == NULL) {3499DEBUGF("send: no copy DATA cancelled because stream was closed\n");35003501active_outbound_item_reset(aob, mem);35023503break;3504}35053506rv = session_call_send_data(session, aob->item, framebufs);3507if (nghttp2_is_fatal(rv)) {3508return rv;3509}35103511if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {3512rv = session_detach_stream_item(session, stream);35133514if (nghttp2_is_fatal(rv)) {3515return rv;3516}35173518rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,3519NGHTTP2_INTERNAL_ERROR);3520if (nghttp2_is_fatal(rv)) {3521return rv;3522}35233524active_outbound_item_reset(aob, mem);35253526break;3527}35283529if (rv == NGHTTP2_ERR_WOULDBLOCK) {3530return 0;3531}35323533pause = (rv == NGHTTP2_ERR_PAUSE);35343535rv = session_after_frame_sent1(session);3536if (rv < 0) {3537assert(nghttp2_is_fatal(rv));3538return rv;3539}3540rv = session_after_frame_sent2(session);3541if (rv < 0) {3542assert(nghttp2_is_fatal(rv));3543return rv;3544}35453546/* We have already adjusted the next state */35473548if (pause) {3549return 0;3550}35513552break;3553}3554case NGHTTP2_OB_SEND_CLIENT_MAGIC: {3555size_t datalen;3556nghttp2_buf *buf;35573558buf = &framebufs->cur->buf;35593560if (buf->pos == buf->last) {3561DEBUGF("send: end transmission of client magic\n");3562active_outbound_item_reset(aob, mem);3563break;3564}35653566*data_ptr = buf->pos;3567datalen = nghttp2_buf_len(buf);35683569buf->pos += datalen;35703571return (ssize_t)datalen;3572}3573}3574}3575}35763577ssize_t nghttp2_session_mem_send(nghttp2_session *session,3578const uint8_t **data_ptr) {3579int rv;3580ssize_t len;35813582*data_ptr = NULL;35833584len = nghttp2_session_mem_send_internal(session, data_ptr, 1);3585if (len <= 0) {3586return len;3587}35883589if (session->aob.item) {3590/* We have to call session_after_frame_sent1 here to handle stream3591closure upon transmission of frames. Otherwise, END_STREAM may3592be reached to client before we call nghttp2_session_mem_send3593again and we may get exceeding number of incoming streams. */3594rv = session_after_frame_sent1(session);3595if (rv < 0) {3596assert(nghttp2_is_fatal(rv));3597return (ssize_t)rv;3598}3599}36003601return len;3602}36033604int nghttp2_session_send(nghttp2_session *session) {3605const uint8_t *data = NULL;3606ssize_t datalen;3607ssize_t sentlen;3608nghttp2_bufs *framebufs;36093610framebufs = &session->aob.framebufs;36113612for (;;) {3613datalen = nghttp2_session_mem_send_internal(session, &data, 0);3614if (datalen <= 0) {3615return (int)datalen;3616}3617sentlen = session->callbacks.send_callback(session, data, (size_t)datalen,36180, session->user_data);3619if (sentlen < 0) {3620if (sentlen == NGHTTP2_ERR_WOULDBLOCK) {3621/* Transmission canceled. Rewind the offset */3622framebufs->cur->buf.pos -= datalen;36233624return 0;3625}3626return NGHTTP2_ERR_CALLBACK_FAILURE;3627}3628/* Rewind the offset to the amount of unsent bytes */3629framebufs->cur->buf.pos -= datalen - sentlen;3630}3631}36323633static ssize_t session_recv(nghttp2_session *session, uint8_t *buf,3634size_t len) {3635ssize_t rv;3636rv = session->callbacks.recv_callback(session, buf, len, 0,3637session->user_data);3638if (rv > 0) {3639if ((size_t)rv > len) {3640return NGHTTP2_ERR_CALLBACK_FAILURE;3641}3642} else if (rv < 0 && rv != NGHTTP2_ERR_WOULDBLOCK && rv != NGHTTP2_ERR_EOF) {3643return NGHTTP2_ERR_CALLBACK_FAILURE;3644}3645return rv;3646}36473648static int session_call_on_begin_frame(nghttp2_session *session,3649const nghttp2_frame_hd *hd) {3650int rv;36513652if (session->callbacks.on_begin_frame_callback) {36533654rv = session->callbacks.on_begin_frame_callback(session, hd,3655session->user_data);36563657if (rv != 0) {3658return NGHTTP2_ERR_CALLBACK_FAILURE;3659}3660}36613662return 0;3663}36643665static int session_call_on_frame_received(nghttp2_session *session,3666nghttp2_frame *frame) {3667int rv;3668if (session->callbacks.on_frame_recv_callback) {3669rv = session->callbacks.on_frame_recv_callback(session, frame,3670session->user_data);3671if (rv != 0) {3672return NGHTTP2_ERR_CALLBACK_FAILURE;3673}3674}3675return 0;3676}36773678static int session_call_on_begin_headers(nghttp2_session *session,3679nghttp2_frame *frame) {3680int rv;3681DEBUGF("recv: call on_begin_headers callback stream_id=%d\n",3682frame->hd.stream_id);3683if (session->callbacks.on_begin_headers_callback) {3684rv = session->callbacks.on_begin_headers_callback(session, frame,3685session->user_data);3686if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {3687return rv;3688}3689if (rv != 0) {3690return NGHTTP2_ERR_CALLBACK_FAILURE;3691}3692}3693return 0;3694}36953696static int session_call_on_header(nghttp2_session *session,3697const nghttp2_frame *frame,3698const nghttp2_hd_nv *nv) {3699int rv = 0;3700if (session->callbacks.on_header_callback2) {3701rv = session->callbacks.on_header_callback2(3702session, frame, nv->name, nv->value, nv->flags, session->user_data);3703} else if (session->callbacks.on_header_callback) {3704rv = session->callbacks.on_header_callback(3705session, frame, nv->name->base, nv->name->len, nv->value->base,3706nv->value->len, nv->flags, session->user_data);3707}37083709if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {3710return rv;3711}3712if (rv != 0) {3713return NGHTTP2_ERR_CALLBACK_FAILURE;3714}37153716return 0;3717}37183719static int session_call_on_invalid_header(nghttp2_session *session,3720const nghttp2_frame *frame,3721const nghttp2_hd_nv *nv) {3722int rv;3723if (session->callbacks.on_invalid_header_callback2) {3724rv = session->callbacks.on_invalid_header_callback2(3725session, frame, nv->name, nv->value, nv->flags, session->user_data);3726} else if (session->callbacks.on_invalid_header_callback) {3727rv = session->callbacks.on_invalid_header_callback(3728session, frame, nv->name->base, nv->name->len, nv->value->base,3729nv->value->len, nv->flags, session->user_data);3730} else {3731return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;3732}37333734if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {3735return rv;3736}3737if (rv != 0) {3738return NGHTTP2_ERR_CALLBACK_FAILURE;3739}37403741return 0;3742}37433744static int3745session_call_on_extension_chunk_recv_callback(nghttp2_session *session,3746const uint8_t *data, size_t len) {3747int rv;3748nghttp2_inbound_frame *iframe = &session->iframe;3749nghttp2_frame *frame = &iframe->frame;37503751if (session->callbacks.on_extension_chunk_recv_callback) {3752rv = session->callbacks.on_extension_chunk_recv_callback(3753session, &frame->hd, data, len, session->user_data);3754if (rv == NGHTTP2_ERR_CANCEL) {3755return rv;3756}3757if (rv != 0) {3758return NGHTTP2_ERR_CALLBACK_FAILURE;3759}3760}37613762return 0;3763}37643765static int session_call_unpack_extension_callback(nghttp2_session *session) {3766int rv;3767nghttp2_inbound_frame *iframe = &session->iframe;3768nghttp2_frame *frame = &iframe->frame;3769void *payload = NULL;37703771rv = session->callbacks.unpack_extension_callback(3772session, &payload, &frame->hd, session->user_data);3773if (rv == NGHTTP2_ERR_CANCEL) {3774return rv;3775}3776if (rv != 0) {3777return NGHTTP2_ERR_CALLBACK_FAILURE;3778}37793780frame->ext.payload = payload;37813782return 0;3783}37843785/*3786* Handles frame size error.3787*3788* This function returns 0 if it succeeds, or one of the following3789* negative error codes:3790*3791* NGHTTP2_ERR_NOMEM3792* Out of memory.3793*/3794static int session_handle_frame_size_error(nghttp2_session *session) {3795/* TODO Currently no callback is called for this error, because we3796call this callback before reading any payload */3797return nghttp2_session_terminate_session(session, NGHTTP2_FRAME_SIZE_ERROR);3798}37993800static uint32_t get_error_code_from_lib_error_code(int lib_error_code) {3801switch (lib_error_code) {3802case NGHTTP2_ERR_STREAM_CLOSED:3803return NGHTTP2_STREAM_CLOSED;3804case NGHTTP2_ERR_HEADER_COMP:3805return NGHTTP2_COMPRESSION_ERROR;3806case NGHTTP2_ERR_FRAME_SIZE_ERROR:3807return NGHTTP2_FRAME_SIZE_ERROR;3808case NGHTTP2_ERR_FLOW_CONTROL:3809return NGHTTP2_FLOW_CONTROL_ERROR;3810case NGHTTP2_ERR_REFUSED_STREAM:3811return NGHTTP2_REFUSED_STREAM;3812case NGHTTP2_ERR_PROTO:3813case NGHTTP2_ERR_HTTP_HEADER:3814case NGHTTP2_ERR_HTTP_MESSAGING:3815return NGHTTP2_PROTOCOL_ERROR;3816default:3817return NGHTTP2_INTERNAL_ERROR;3818}3819}38203821/*3822* Calls on_invalid_frame_recv_callback if it is set to |session|.3823*3824* This function returns 0 if it succeeds, or one of the following3825* negative error codes:3826*3827* NGHTTP2_ERR_CALLBACK_FAILURE3828* User defined callback function fails.3829*/3830static int session_call_on_invalid_frame_recv_callback(nghttp2_session *session,3831nghttp2_frame *frame,3832int lib_error_code) {3833if (session->callbacks.on_invalid_frame_recv_callback) {3834if (session->callbacks.on_invalid_frame_recv_callback(3835session, frame, lib_error_code, session->user_data) != 0) {3836return NGHTTP2_ERR_CALLBACK_FAILURE;3837}3838}3839return 0;3840}38413842static int session_handle_invalid_stream2(nghttp2_session *session,3843int32_t stream_id,3844nghttp2_frame *frame,3845int lib_error_code) {3846int rv;3847rv = nghttp2_session_add_rst_stream(3848session, stream_id, get_error_code_from_lib_error_code(lib_error_code));3849if (rv != 0) {3850return rv;3851}3852if (session->callbacks.on_invalid_frame_recv_callback) {3853if (session->callbacks.on_invalid_frame_recv_callback(3854session, frame, lib_error_code, session->user_data) != 0) {3855return NGHTTP2_ERR_CALLBACK_FAILURE;3856}3857}3858return 0;3859}38603861static int session_handle_invalid_stream(nghttp2_session *session,3862nghttp2_frame *frame,3863int lib_error_code) {3864return session_handle_invalid_stream2(session, frame->hd.stream_id, frame,3865lib_error_code);3866}38673868static int session_inflate_handle_invalid_stream(nghttp2_session *session,3869nghttp2_frame *frame,3870int lib_error_code) {3871int rv;3872rv = session_handle_invalid_stream(session, frame, lib_error_code);3873if (nghttp2_is_fatal(rv)) {3874return rv;3875}3876return NGHTTP2_ERR_IGN_HEADER_BLOCK;3877}38783879/*3880* Handles invalid frame which causes connection error.3881*/3882static int session_handle_invalid_connection(nghttp2_session *session,3883nghttp2_frame *frame,3884int lib_error_code,3885const char *reason) {3886if (session->callbacks.on_invalid_frame_recv_callback) {3887if (session->callbacks.on_invalid_frame_recv_callback(3888session, frame, lib_error_code, session->user_data) != 0) {3889return NGHTTP2_ERR_CALLBACK_FAILURE;3890}3891}3892return nghttp2_session_terminate_session_with_reason(3893session, get_error_code_from_lib_error_code(lib_error_code), reason);3894}38953896static int session_inflate_handle_invalid_connection(nghttp2_session *session,3897nghttp2_frame *frame,3898int lib_error_code,3899const char *reason) {3900int rv;3901rv =3902session_handle_invalid_connection(session, frame, lib_error_code, reason);3903if (nghttp2_is_fatal(rv)) {3904return rv;3905}3906return NGHTTP2_ERR_IGN_HEADER_BLOCK;3907}39083909/*3910* Inflates header block in the memory pointed by |in| with |inlen|3911* bytes. If this function returns NGHTTP2_ERR_PAUSE, the caller must3912* call this function again, until it returns 0 or one of negative3913* error code. If |call_header_cb| is zero, the on_header_callback3914* are not invoked and the function never return NGHTTP2_ERR_PAUSE. If3915* the given |in| is the last chunk of header block, the |final| must3916* be nonzero. If header block is successfully processed (which is3917* indicated by the return value 0, NGHTTP2_ERR_PAUSE or3918* NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE), the number of processed3919* input bytes is assigned to the |*readlen_ptr|.3920*3921* This function return 0 if it succeeds, or one of the negative error3922* codes:3923*3924* NGHTTP2_ERR_CALLBACK_FAILURE3925* The callback function failed.3926* NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE3927* The callback returns this error code, indicating that this3928* stream should be RST_STREAMed.3929* NGHTTP2_ERR_NOMEM3930* Out of memory.3931* NGHTTP2_ERR_PAUSE3932* The callback function returned NGHTTP2_ERR_PAUSE3933* NGHTTP2_ERR_HEADER_COMP3934* Header decompression failed3935*/3936static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,3937size_t *readlen_ptr, uint8_t *in, size_t inlen,3938int final, int call_header_cb) {3939ssize_t proclen;3940int rv;3941int inflate_flags;3942nghttp2_hd_nv nv;3943nghttp2_stream *stream;3944nghttp2_stream *subject_stream;3945int trailer = 0;39463947*readlen_ptr = 0;3948stream = nghttp2_session_get_stream(session, frame->hd.stream_id);39493950if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {3951subject_stream = nghttp2_session_get_stream(3952session, frame->push_promise.promised_stream_id);3953} else {3954subject_stream = stream;3955trailer = session_trailer_headers(session, stream, frame);3956}39573958DEBUGF("recv: decoding header block %zu bytes\n", inlen);3959for (;;) {3960inflate_flags = 0;3961proclen = nghttp2_hd_inflate_hd_nv(&session->hd_inflater, &nv,3962&inflate_flags, in, inlen, final);3963if (nghttp2_is_fatal((int)proclen)) {3964return (int)proclen;3965}3966if (proclen < 0) {3967if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) {3968if (subject_stream && subject_stream->state != NGHTTP2_STREAM_CLOSING) {3969/* Adding RST_STREAM here is very important. It prevents3970from invoking subsequent callbacks for the same stream3971ID. */3972rv = nghttp2_session_add_rst_stream(3973session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR);39743975if (nghttp2_is_fatal(rv)) {3976return rv;3977}3978}3979}3980rv =3981nghttp2_session_terminate_session(session, NGHTTP2_COMPRESSION_ERROR);3982if (nghttp2_is_fatal(rv)) {3983return rv;3984}39853986return NGHTTP2_ERR_HEADER_COMP;3987}3988in += proclen;3989inlen -= (size_t)proclen;3990*readlen_ptr += (size_t)proclen;39913992DEBUGF("recv: proclen=%zd\n", proclen);39933994if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) {3995rv = 0;3996if (subject_stream) {3997if (session_enforce_http_messaging(session)) {3998rv = nghttp2_http_on_header(session, subject_stream, frame, &nv,3999trailer);40004001if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) {4002/* Don't overwrite rv here */4003int rv2;40044005rv2 = session_call_on_invalid_header(session, frame, &nv);4006if (rv2 == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {4007rv = NGHTTP2_ERR_HTTP_HEADER;4008} else {4009if (rv2 != 0) {4010return rv2;4011}40124013/* header is ignored */4014DEBUGF("recv: HTTP ignored: type=%u, id=%d, header %.*s: %.*s\n",4015frame->hd.type, frame->hd.stream_id, (int)nv.name->len,4016nv.name->base, (int)nv.value->len, nv.value->base);40174018rv2 = session_call_error_callback(4019session, NGHTTP2_ERR_HTTP_HEADER,4020"Ignoring received invalid HTTP header field: frame type: "4021"%u, stream: %d, name: [%.*s], value: [%.*s]",4022frame->hd.type, frame->hd.stream_id, (int)nv.name->len,4023nv.name->base, (int)nv.value->len, nv.value->base);40244025if (nghttp2_is_fatal(rv2)) {4026return rv2;4027}4028}4029}40304031if (rv == NGHTTP2_ERR_HTTP_HEADER) {4032DEBUGF("recv: HTTP error: type=%u, id=%d, header %.*s: %.*s\n",4033frame->hd.type, frame->hd.stream_id, (int)nv.name->len,4034nv.name->base, (int)nv.value->len, nv.value->base);40354036rv = session_call_error_callback(4037session, NGHTTP2_ERR_HTTP_HEADER,4038"Invalid HTTP header field was received: frame type: "4039"%u, stream: %d, name: [%.*s], value: [%.*s]",4040frame->hd.type, frame->hd.stream_id, (int)nv.name->len,4041nv.name->base, (int)nv.value->len, nv.value->base);40424043if (nghttp2_is_fatal(rv)) {4044return rv;4045}40464047rv = session_handle_invalid_stream2(session,4048subject_stream->stream_id,4049frame, NGHTTP2_ERR_HTTP_HEADER);4050if (nghttp2_is_fatal(rv)) {4051return rv;4052}4053return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;4054}4055}4056if (rv == 0) {4057rv = session_call_on_header(session, frame, &nv);4058/* This handles NGHTTP2_ERR_PAUSE and4059NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE as well */4060if (rv != 0) {4061return rv;4062}4063}4064}4065}4066if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {4067nghttp2_hd_inflate_end_headers(&session->hd_inflater);4068break;4069}4070if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) {4071break;4072}4073}4074return 0;4075}40764077/*4078* Call this function when HEADERS frame was completely received.4079*4080* This function returns 0 if it succeeds, or one of negative error4081* codes:4082*4083* NGHTTP2_ERR_CALLBACK_FAILURE4084* The callback function failed.4085* NGHTTP2_ERR_NOMEM4086* Out of memory.4087*/4088static int session_end_stream_headers_received(nghttp2_session *session,4089nghttp2_frame *frame,4090nghttp2_stream *stream) {4091int rv;40924093assert(frame->hd.type == NGHTTP2_HEADERS);40944095if (session->server && session_enforce_http_messaging(session) &&4096frame->headers.cat == NGHTTP2_HCAT_REQUEST &&4097(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) &&4098!(stream->flags & NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES) &&4099(stream->http_flags & NGHTTP2_HTTP_FLAG_PRIORITY)) {4100rv = session_update_stream_priority(session, stream, stream->http_extpri);4101if (rv != 0) {4102assert(nghttp2_is_fatal(rv));4103return rv;4104}4105}41064107if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {4108return 0;4109}41104111nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);4112rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);4113if (nghttp2_is_fatal(rv)) {4114return rv;4115}41164117return 0;4118}41194120static int session_after_header_block_received(nghttp2_session *session) {4121int rv = 0;4122nghttp2_frame *frame = &session->iframe.frame;4123nghttp2_stream *stream;41244125/* We don't call on_frame_recv_callback if stream has been closed4126already or being closed. */4127stream = nghttp2_session_get_stream(session, frame->hd.stream_id);4128if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) {4129return 0;4130}41314132if (session_enforce_http_messaging(session)) {4133if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {4134nghttp2_stream *subject_stream;41354136subject_stream = nghttp2_session_get_stream(4137session, frame->push_promise.promised_stream_id);4138if (subject_stream) {4139rv = nghttp2_http_on_request_headers(subject_stream, frame);4140}4141} else {4142assert(frame->hd.type == NGHTTP2_HEADERS);4143switch (frame->headers.cat) {4144case NGHTTP2_HCAT_REQUEST:4145rv = nghttp2_http_on_request_headers(stream, frame);4146break;4147case NGHTTP2_HCAT_RESPONSE:4148case NGHTTP2_HCAT_PUSH_RESPONSE:4149rv = nghttp2_http_on_response_headers(stream);4150break;4151case NGHTTP2_HCAT_HEADERS:4152if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {4153assert(!session->server);4154rv = nghttp2_http_on_response_headers(stream);4155} else {4156rv = nghttp2_http_on_trailer_headers(stream, frame);4157}4158break;4159default:4160assert(0);4161}4162if (rv == 0 && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {4163rv = nghttp2_http_on_remote_end_stream(stream);4164}4165}4166if (rv != 0) {4167int32_t stream_id;41684169if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {4170stream_id = frame->push_promise.promised_stream_id;4171} else {4172stream_id = frame->hd.stream_id;4173}41744175rv = session_handle_invalid_stream2(session, stream_id, frame,4176NGHTTP2_ERR_HTTP_MESSAGING);4177if (nghttp2_is_fatal(rv)) {4178return rv;4179}41804181if (frame->hd.type == NGHTTP2_HEADERS &&4182(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {4183nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);4184/* Don't call nghttp2_session_close_stream_if_shut_rdwr4185because RST_STREAM has been submitted. */4186}4187return 0;4188}4189}41904191rv = session_call_on_frame_received(session, frame);4192if (nghttp2_is_fatal(rv)) {4193return rv;4194}41954196if (frame->hd.type != NGHTTP2_HEADERS) {4197return 0;4198}41994200return session_end_stream_headers_received(session, frame, stream);4201}42024203int nghttp2_session_on_request_headers_received(nghttp2_session *session,4204nghttp2_frame *frame) {4205int rv = 0;4206nghttp2_stream *stream;4207if (frame->hd.stream_id == 0) {4208return session_inflate_handle_invalid_connection(4209session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0");4210}42114212/* If client receives idle stream from server, it is invalid4213regardless stream ID is even or odd. This is because client is4214not expected to receive request from server. */4215if (!session->server) {4216if (session_detect_idle_stream(session, frame->hd.stream_id)) {4217return session_inflate_handle_invalid_connection(4218session, frame, NGHTTP2_ERR_PROTO,4219"request HEADERS: client received request");4220}42214222return NGHTTP2_ERR_IGN_HEADER_BLOCK;4223}42244225assert(session->server);42264227if (!session_is_new_peer_stream_id(session, frame->hd.stream_id)) {4228if (frame->hd.stream_id == 0 ||4229nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {4230return session_inflate_handle_invalid_connection(4231session, frame, NGHTTP2_ERR_PROTO,4232"request HEADERS: invalid stream_id");4233}42344235/* RFC 7540 says if an endpoint receives a HEADERS with invalid4236* stream ID (e.g, numerically smaller than previous), it MUST4237* issue connection error with error code PROTOCOL_ERROR. It is a4238* bit hard to detect this, since we cannot remember all streams4239* we observed so far.4240*4241* You might imagine this is really easy. But no. HTTP/2 is4242* asynchronous protocol, and usually client and server do not4243* share the complete picture of open/closed stream status. For4244* example, after server sends RST_STREAM for a stream, client may4245* send trailer HEADERS for that stream. If naive server detects4246* that, and issued connection error, then it is a bug of server4247* implementation since client is not wrong if it did not get4248* RST_STREAM when it issued trailer HEADERS.4249*4250* At the moment, we are very conservative here. We only use4251* connection error if stream ID refers idle stream, or we are4252* sure that stream is half-closed(remote) or closed. Otherwise4253* we just ignore HEADERS for now.4254*/4255stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);4256if (stream && (stream->shut_flags & NGHTTP2_SHUT_RD)) {4257return session_inflate_handle_invalid_connection(4258session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed");4259}42604261return NGHTTP2_ERR_IGN_HEADER_BLOCK;4262}4263session->last_recv_stream_id = frame->hd.stream_id;42644265if (session_is_incoming_concurrent_streams_max(session)) {4266return session_inflate_handle_invalid_connection(4267session, frame, NGHTTP2_ERR_PROTO,4268"request HEADERS: max concurrent streams exceeded");4269}42704271if (!session_allow_incoming_new_stream(session)) {4272/* We just ignore stream after GOAWAY was sent */4273return NGHTTP2_ERR_IGN_HEADER_BLOCK;4274}42754276if (frame->headers.pri_spec.stream_id == frame->hd.stream_id) {4277return session_inflate_handle_invalid_connection(4278session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: depend on itself");4279}42804281if (session_is_incoming_concurrent_streams_pending_max(session)) {4282return session_inflate_handle_invalid_stream(session, frame,4283NGHTTP2_ERR_REFUSED_STREAM);4284}42854286stream = nghttp2_session_open_stream(4287session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE,4288&frame->headers.pri_spec, NGHTTP2_STREAM_OPENING, NULL);4289if (!stream) {4290return NGHTTP2_ERR_NOMEM;4291}42924293rv = nghttp2_session_adjust_closed_stream(session);4294if (nghttp2_is_fatal(rv)) {4295return rv;4296}42974298session->last_proc_stream_id = session->last_recv_stream_id;42994300rv = session_call_on_begin_headers(session, frame);4301if (rv != 0) {4302return rv;4303}4304return 0;4305}43064307int nghttp2_session_on_response_headers_received(nghttp2_session *session,4308nghttp2_frame *frame,4309nghttp2_stream *stream) {4310int rv;4311/* This function is only called if stream->state ==4312NGHTTP2_STREAM_OPENING and stream_id is local side initiated. */4313assert(stream->state == NGHTTP2_STREAM_OPENING &&4314nghttp2_session_is_my_stream_id(session, frame->hd.stream_id));4315if (frame->hd.stream_id == 0) {4316return session_inflate_handle_invalid_connection(4317session, frame, NGHTTP2_ERR_PROTO, "response HEADERS: stream_id == 0");4318}4319if (stream->shut_flags & NGHTTP2_SHUT_RD) {4320/* half closed (remote): from the spec:43214322If an endpoint receives additional frames for a stream that is4323in this state it MUST respond with a stream error (Section43245.4.2) of type STREAM_CLOSED.43254326We go further, and make it connection error.4327*/4328return session_inflate_handle_invalid_connection(4329session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed");4330}4331stream->state = NGHTTP2_STREAM_OPENED;4332rv = session_call_on_begin_headers(session, frame);4333if (rv != 0) {4334return rv;4335}4336return 0;4337}43384339int nghttp2_session_on_push_response_headers_received(nghttp2_session *session,4340nghttp2_frame *frame,4341nghttp2_stream *stream) {4342int rv = 0;4343assert(stream->state == NGHTTP2_STREAM_RESERVED);4344if (frame->hd.stream_id == 0) {4345return session_inflate_handle_invalid_connection(4346session, frame, NGHTTP2_ERR_PROTO,4347"push response HEADERS: stream_id == 0");4348}43494350if (session->server) {4351return session_inflate_handle_invalid_connection(4352session, frame, NGHTTP2_ERR_PROTO,4353"HEADERS: no HEADERS allowed from client in reserved state");4354}43554356if (session_is_incoming_concurrent_streams_max(session)) {4357return session_inflate_handle_invalid_connection(4358session, frame, NGHTTP2_ERR_PROTO,4359"push response HEADERS: max concurrent streams exceeded");4360}43614362if (!session_allow_incoming_new_stream(session)) {4363/* We don't accept new stream after GOAWAY was sent. */4364return NGHTTP2_ERR_IGN_HEADER_BLOCK;4365}43664367if (session_is_incoming_concurrent_streams_pending_max(session)) {4368return session_inflate_handle_invalid_stream(session, frame,4369NGHTTP2_ERR_REFUSED_STREAM);4370}43714372nghttp2_stream_promise_fulfilled(stream);4373if (!nghttp2_session_is_my_stream_id(session, stream->stream_id)) {4374--session->num_incoming_reserved_streams;4375}4376++session->num_incoming_streams;4377rv = session_call_on_begin_headers(session, frame);4378if (rv != 0) {4379return rv;4380}4381return 0;4382}43834384int nghttp2_session_on_headers_received(nghttp2_session *session,4385nghttp2_frame *frame,4386nghttp2_stream *stream) {4387int rv = 0;4388if (frame->hd.stream_id == 0) {4389return session_inflate_handle_invalid_connection(4390session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream_id == 0");4391}4392if ((stream->shut_flags & NGHTTP2_SHUT_RD)) {4393/* half closed (remote): from the spec:43944395If an endpoint receives additional frames for a stream that is4396in this state it MUST respond with a stream error (Section43975.4.2) of type STREAM_CLOSED.43984399we go further, and make it connection error.4400*/4401return session_inflate_handle_invalid_connection(4402session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed");4403}4404if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {4405if (stream->state == NGHTTP2_STREAM_OPENED) {4406rv = session_call_on_begin_headers(session, frame);4407if (rv != 0) {4408return rv;4409}4410return 0;4411}44124413return NGHTTP2_ERR_IGN_HEADER_BLOCK;4414}4415/* If this is remote peer initiated stream, it is OK unless it4416has sent END_STREAM frame already. But if stream is in4417NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race4418condition. */4419if (stream->state != NGHTTP2_STREAM_CLOSING) {4420rv = session_call_on_begin_headers(session, frame);4421if (rv != 0) {4422return rv;4423}4424return 0;4425}4426return NGHTTP2_ERR_IGN_HEADER_BLOCK;4427}44284429static int session_process_headers_frame(nghttp2_session *session) {4430int rv;4431nghttp2_inbound_frame *iframe = &session->iframe;4432nghttp2_frame *frame = &iframe->frame;4433nghttp2_stream *stream;44344435rv = nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos);44364437if (rv != 0) {4438return nghttp2_session_terminate_session_with_reason(4439session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: could not unpack");4440}4441stream = nghttp2_session_get_stream(session, frame->hd.stream_id);4442if (!stream) {4443frame->headers.cat = NGHTTP2_HCAT_REQUEST;4444return nghttp2_session_on_request_headers_received(session, frame);4445}44464447if (stream->state == NGHTTP2_STREAM_RESERVED) {4448frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE;4449return nghttp2_session_on_push_response_headers_received(session, frame,4450stream);4451}44524453if (stream->state == NGHTTP2_STREAM_OPENING &&4454nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {4455frame->headers.cat = NGHTTP2_HCAT_RESPONSE;4456return nghttp2_session_on_response_headers_received(session, frame, stream);4457}44584459frame->headers.cat = NGHTTP2_HCAT_HEADERS;4460return nghttp2_session_on_headers_received(session, frame, stream);4461}44624463int nghttp2_session_on_priority_received(nghttp2_session *session,4464nghttp2_frame *frame) {4465int rv;4466nghttp2_stream *stream;44674468assert(!session_no_rfc7540_pri_no_fallback(session));44694470if (frame->hd.stream_id == 0) {4471return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,4472"PRIORITY: stream_id == 0");4473}44744475if (frame->priority.pri_spec.stream_id == frame->hd.stream_id) {4476return nghttp2_session_terminate_session_with_reason(4477session, NGHTTP2_PROTOCOL_ERROR, "depend on itself");4478}44794480if (!session->server) {4481/* Re-prioritization works only in server */4482return session_call_on_frame_received(session, frame);4483}44844485stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);44864487if (!stream) {4488/* PRIORITY against idle stream can create anchor node in4489dependency tree. */4490if (!session_detect_idle_stream(session, frame->hd.stream_id)) {4491return 0;4492}44934494stream = nghttp2_session_open_stream(4495session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE,4496&frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL);44974498if (stream == NULL) {4499return NGHTTP2_ERR_NOMEM;4500}45014502rv = nghttp2_session_adjust_idle_stream(session);4503if (nghttp2_is_fatal(rv)) {4504return rv;4505}4506} else {4507rv = nghttp2_session_reprioritize_stream(session, stream,4508&frame->priority.pri_spec);45094510if (nghttp2_is_fatal(rv)) {4511return rv;4512}45134514rv = nghttp2_session_adjust_idle_stream(session);4515if (nghttp2_is_fatal(rv)) {4516return rv;4517}4518}45194520return session_call_on_frame_received(session, frame);4521}45224523static int session_process_priority_frame(nghttp2_session *session) {4524nghttp2_inbound_frame *iframe = &session->iframe;4525nghttp2_frame *frame = &iframe->frame;45264527assert(!session_no_rfc7540_pri_no_fallback(session));45284529nghttp2_frame_unpack_priority_payload(&frame->priority, iframe->sbuf.pos);45304531return nghttp2_session_on_priority_received(session, frame);4532}45334534int nghttp2_session_on_rst_stream_received(nghttp2_session *session,4535nghttp2_frame *frame) {4536int rv;4537nghttp2_stream *stream;4538if (frame->hd.stream_id == 0) {4539return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,4540"RST_STREAM: stream_id == 0");4541}45424543if (session_detect_idle_stream(session, frame->hd.stream_id)) {4544return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,4545"RST_STREAM: stream in idle");4546}45474548stream = nghttp2_session_get_stream(session, frame->hd.stream_id);4549if (stream) {4550/* We may use stream->shut_flags for strict error checking. */4551nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);4552}45534554rv = session_call_on_frame_received(session, frame);4555if (rv != 0) {4556return rv;4557}4558rv = nghttp2_session_close_stream(session, frame->hd.stream_id,4559frame->rst_stream.error_code);4560if (nghttp2_is_fatal(rv)) {4561return rv;4562}4563return 0;4564}45654566static int session_process_rst_stream_frame(nghttp2_session *session) {4567nghttp2_inbound_frame *iframe = &session->iframe;4568nghttp2_frame *frame = &iframe->frame;45694570nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, iframe->sbuf.pos);45714572return nghttp2_session_on_rst_stream_received(session, frame);4573}45744575static int update_remote_initial_window_size_func(void *entry, void *ptr) {4576int rv;4577nghttp2_update_window_size_arg *arg;4578nghttp2_stream *stream;45794580arg = (nghttp2_update_window_size_arg *)ptr;4581stream = (nghttp2_stream *)entry;45824583rv = nghttp2_stream_update_remote_initial_window_size(4584stream, arg->new_window_size, arg->old_window_size);4585if (rv != 0) {4586return nghttp2_session_add_rst_stream(arg->session, stream->stream_id,4587NGHTTP2_FLOW_CONTROL_ERROR);4588}45894590/* If window size gets positive, push deferred DATA frame to4591outbound queue. */4592if (stream->remote_window_size > 0 &&4593nghttp2_stream_check_deferred_by_flow_control(stream)) {45944595rv = session_resume_deferred_stream_item(4596arg->session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);45974598if (nghttp2_is_fatal(rv)) {4599return rv;4600}4601}4602return 0;4603}46044605/*4606* Updates the remote initial window size of all active streams. If4607* error occurs, all streams may not be updated.4608*4609* This function returns 0 if it succeeds, or one of the following4610* negative error codes:4611*4612* NGHTTP2_ERR_NOMEM4613* Out of memory.4614*/4615static int4616session_update_remote_initial_window_size(nghttp2_session *session,4617int32_t new_initial_window_size) {4618nghttp2_update_window_size_arg arg;46194620arg.session = session;4621arg.new_window_size = new_initial_window_size;4622arg.old_window_size = (int32_t)session->remote_settings.initial_window_size;46234624return nghttp2_map_each(&session->streams,4625update_remote_initial_window_size_func, &arg);4626}46274628static int update_local_initial_window_size_func(void *entry, void *ptr) {4629int rv;4630nghttp2_update_window_size_arg *arg;4631nghttp2_stream *stream;4632arg = (nghttp2_update_window_size_arg *)ptr;4633stream = (nghttp2_stream *)entry;4634rv = nghttp2_stream_update_local_initial_window_size(4635stream, arg->new_window_size, arg->old_window_size);4636if (rv != 0) {4637return nghttp2_session_add_rst_stream(arg->session, stream->stream_id,4638NGHTTP2_FLOW_CONTROL_ERROR);4639}46404641if (stream->window_update_queued) {4642return 0;4643}46444645if (arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {4646return session_update_stream_consumed_size(arg->session, stream, 0);4647}46484649if (nghttp2_should_send_window_update(stream->local_window_size,4650stream->recv_window_size)) {46514652rv = nghttp2_session_add_window_update(arg->session, NGHTTP2_FLAG_NONE,4653stream->stream_id,4654stream->recv_window_size);4655if (rv != 0) {4656return rv;4657}46584659stream->recv_window_size = 0;4660}4661return 0;4662}46634664/*4665* Updates the local initial window size of all active streams. If4666* error occurs, all streams may not be updated.4667*4668* This function returns 0 if it succeeds, or one of the following4669* negative error codes:4670*4671* NGHTTP2_ERR_NOMEM4672* Out of memory.4673*/4674static int4675session_update_local_initial_window_size(nghttp2_session *session,4676int32_t new_initial_window_size,4677int32_t old_initial_window_size) {4678nghttp2_update_window_size_arg arg;4679arg.session = session;4680arg.new_window_size = new_initial_window_size;4681arg.old_window_size = old_initial_window_size;4682return nghttp2_map_each(&session->streams,4683update_local_initial_window_size_func, &arg);4684}46854686/*4687* Apply SETTINGS values |iv| having |niv| elements to the local4688* settings. We assumes that all values in |iv| is correct, since we4689* validated them in nghttp2_session_add_settings() already.4690*4691* This function returns 0 if it succeeds, or one of the following4692* negative error codes:4693*4694* NGHTTP2_ERR_HEADER_COMP4695* The header table size is out of range4696* NGHTTP2_ERR_NOMEM4697* Out of memory4698*/4699int nghttp2_session_update_local_settings(nghttp2_session *session,4700nghttp2_settings_entry *iv,4701size_t niv) {4702int rv;4703size_t i;4704int32_t new_initial_window_size = -1;4705uint32_t header_table_size = 0;4706uint32_t min_header_table_size = UINT32_MAX;4707uint8_t header_table_size_seen = 0;4708/* For NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, use the value last4709seen. For NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, use both minimum4710value and last seen value. */4711for (i = 0; i < niv; ++i) {4712switch (iv[i].settings_id) {4713case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:4714header_table_size_seen = 1;4715header_table_size = iv[i].value;4716min_header_table_size = nghttp2_min(min_header_table_size, iv[i].value);4717break;4718case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:4719new_initial_window_size = (int32_t)iv[i].value;4720break;4721}4722}4723if (header_table_size_seen) {4724if (min_header_table_size < header_table_size) {4725rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater,4726min_header_table_size);4727if (rv != 0) {4728return rv;4729}4730}47314732rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater,4733header_table_size);4734if (rv != 0) {4735return rv;4736}4737}4738if (new_initial_window_size != -1) {4739rv = session_update_local_initial_window_size(4740session, new_initial_window_size,4741(int32_t)session->local_settings.initial_window_size);4742if (rv != 0) {4743return rv;4744}4745}47464747for (i = 0; i < niv; ++i) {4748switch (iv[i].settings_id) {4749case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:4750session->local_settings.header_table_size = iv[i].value;4751break;4752case NGHTTP2_SETTINGS_ENABLE_PUSH:4753session->local_settings.enable_push = iv[i].value;4754break;4755case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:4756session->local_settings.max_concurrent_streams = iv[i].value;4757break;4758case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:4759session->local_settings.initial_window_size = iv[i].value;4760break;4761case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:4762session->local_settings.max_frame_size = iv[i].value;4763break;4764case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:4765session->local_settings.max_header_list_size = iv[i].value;4766break;4767case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:4768session->local_settings.enable_connect_protocol = iv[i].value;4769break;4770case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:4771session->local_settings.no_rfc7540_priorities = iv[i].value;4772break;4773}4774}47754776return 0;4777}47784779int nghttp2_session_on_settings_received(nghttp2_session *session,4780nghttp2_frame *frame, int noack) {4781int rv;4782size_t i;4783nghttp2_mem *mem;4784nghttp2_inflight_settings *settings;47854786mem = &session->mem;47874788if (frame->hd.stream_id != 0) {4789return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,4790"SETTINGS: stream_id != 0");4791}4792if (frame->hd.flags & NGHTTP2_FLAG_ACK) {4793if (frame->settings.niv != 0) {4794return session_handle_invalid_connection(4795session, frame, NGHTTP2_ERR_FRAME_SIZE_ERROR,4796"SETTINGS: ACK and payload != 0");4797}47984799settings = session->inflight_settings_head;48004801if (!settings) {4802return session_handle_invalid_connection(4803session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: unexpected ACK");4804}48054806rv = nghttp2_session_update_local_settings(session, settings->iv,4807settings->niv);48084809session->inflight_settings_head = settings->next;48104811inflight_settings_del(settings, mem);48124813if (rv != 0) {4814if (nghttp2_is_fatal(rv)) {4815return rv;4816}4817return session_handle_invalid_connection(session, frame, rv, NULL);4818}4819return session_call_on_frame_received(session, frame);4820}48214822if (!session->remote_settings_received) {4823session->remote_settings.max_concurrent_streams =4824NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;4825session->remote_settings_received = 1;4826}48274828for (i = 0; i < frame->settings.niv; ++i) {4829nghttp2_settings_entry *entry = &frame->settings.iv[i];48304831switch (entry->settings_id) {4832case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:48334834rv = nghttp2_hd_deflate_change_table_size(&session->hd_deflater,4835entry->value);4836if (rv != 0) {4837if (nghttp2_is_fatal(rv)) {4838return rv;4839} else {4840return session_handle_invalid_connection(4841session, frame, NGHTTP2_ERR_HEADER_COMP, NULL);4842}4843}48444845session->remote_settings.header_table_size = entry->value;48464847break;4848case NGHTTP2_SETTINGS_ENABLE_PUSH:48494850if (entry->value != 0 && entry->value != 1) {4851return session_handle_invalid_connection(4852session, frame, NGHTTP2_ERR_PROTO,4853"SETTINGS: invalid SETTINGS_ENBLE_PUSH");4854}48554856if (!session->server && entry->value != 0) {4857return session_handle_invalid_connection(4858session, frame, NGHTTP2_ERR_PROTO,4859"SETTINGS: server attempted to enable push");4860}48614862session->remote_settings.enable_push = entry->value;48634864break;4865case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:48664867session->remote_settings.max_concurrent_streams = entry->value;48684869break;4870case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:48714872/* Update the initial window size of the all active streams */4873/* Check that initial_window_size < (1u << 31) */4874if (entry->value > NGHTTP2_MAX_WINDOW_SIZE) {4875return session_handle_invalid_connection(4876session, frame, NGHTTP2_ERR_FLOW_CONTROL,4877"SETTINGS: too large SETTINGS_INITIAL_WINDOW_SIZE");4878}48794880rv = session_update_remote_initial_window_size(session,4881(int32_t)entry->value);48824883if (nghttp2_is_fatal(rv)) {4884return rv;4885}48864887if (rv != 0) {4888return session_handle_invalid_connection(4889session, frame, NGHTTP2_ERR_FLOW_CONTROL, NULL);4890}48914892session->remote_settings.initial_window_size = entry->value;48934894break;4895case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:48964897if (entry->value < NGHTTP2_MAX_FRAME_SIZE_MIN ||4898entry->value > NGHTTP2_MAX_FRAME_SIZE_MAX) {4899return session_handle_invalid_connection(4900session, frame, NGHTTP2_ERR_PROTO,4901"SETTINGS: invalid SETTINGS_MAX_FRAME_SIZE");4902}49034904session->remote_settings.max_frame_size = entry->value;49054906break;4907case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:49084909session->remote_settings.max_header_list_size = entry->value;49104911break;4912case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:49134914if (entry->value != 0 && entry->value != 1) {4915return session_handle_invalid_connection(4916session, frame, NGHTTP2_ERR_PROTO,4917"SETTINGS: invalid SETTINGS_ENABLE_CONNECT_PROTOCOL");4918}49194920if (!session->server &&4921session->remote_settings.enable_connect_protocol &&4922entry->value == 0) {4923return session_handle_invalid_connection(4924session, frame, NGHTTP2_ERR_PROTO,4925"SETTINGS: server attempted to disable "4926"SETTINGS_ENABLE_CONNECT_PROTOCOL");4927}49284929session->remote_settings.enable_connect_protocol = entry->value;49304931break;4932case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:49334934if (entry->value != 0 && entry->value != 1) {4935return session_handle_invalid_connection(4936session, frame, NGHTTP2_ERR_PROTO,4937"SETTINGS: invalid SETTINGS_NO_RFC7540_PRIORITIES");4938}49394940if (session->remote_settings.no_rfc7540_priorities != UINT32_MAX &&4941session->remote_settings.no_rfc7540_priorities != entry->value) {4942return session_handle_invalid_connection(4943session, frame, NGHTTP2_ERR_PROTO,4944"SETTINGS: SETTINGS_NO_RFC7540_PRIORITIES cannot be changed");4945}49464947session->remote_settings.no_rfc7540_priorities = entry->value;49484949break;4950}4951}49524953if (session->remote_settings.no_rfc7540_priorities == UINT32_MAX) {4954session->remote_settings.no_rfc7540_priorities = 0;49554956if (session->server && session->pending_no_rfc7540_priorities &&4957(session->opt_flags &4958NGHTTP2_OPTMASK_SERVER_FALLBACK_RFC7540_PRIORITIES)) {4959session->fallback_rfc7540_priorities = 1;4960}4961}49624963if (!noack && !session_is_closing(session)) {4964rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0);49654966if (rv != 0) {4967if (nghttp2_is_fatal(rv)) {4968return rv;4969}49704971return session_handle_invalid_connection(session, frame,4972NGHTTP2_ERR_INTERNAL, NULL);4973}4974}49754976return session_call_on_frame_received(session, frame);4977}49784979static int session_process_settings_frame(nghttp2_session *session) {4980nghttp2_inbound_frame *iframe = &session->iframe;4981nghttp2_frame *frame = &iframe->frame;4982size_t i;4983nghttp2_settings_entry min_header_size_entry;49844985if (iframe->max_niv) {4986min_header_size_entry = iframe->iv[iframe->max_niv - 1];49874988if (min_header_size_entry.value < UINT32_MAX) {4989/* If we have less value, then we must have4990SETTINGS_HEADER_TABLE_SIZE in i < iframe->niv */4991for (i = 0; i < iframe->niv; ++i) {4992if (iframe->iv[i].settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) {4993break;4994}4995}49964997assert(i < iframe->niv);49984999if (min_header_size_entry.value != iframe->iv[i].value) {5000iframe->iv[iframe->niv++] = iframe->iv[i];5001iframe->iv[i] = min_header_size_entry;5002}5003}5004}50055006nghttp2_frame_unpack_settings_payload(&frame->settings, iframe->iv,5007iframe->niv);50085009iframe->iv = NULL;5010iframe->niv = 0;5011iframe->max_niv = 0;50125013return nghttp2_session_on_settings_received(session, frame, 0 /* ACK */);5014}50155016int nghttp2_session_on_push_promise_received(nghttp2_session *session,5017nghttp2_frame *frame) {5018int rv;5019nghttp2_stream *stream;5020nghttp2_stream *promised_stream;5021nghttp2_priority_spec pri_spec;50225023if (frame->hd.stream_id == 0) {5024return session_inflate_handle_invalid_connection(5025session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream_id == 0");5026}5027if (session->server || session->local_settings.enable_push == 0) {5028return session_inflate_handle_invalid_connection(5029session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: push disabled");5030}50315032if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {5033return session_inflate_handle_invalid_connection(5034session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: invalid stream_id");5035}50365037if (!session_allow_incoming_new_stream(session)) {5038/* We just discard PUSH_PROMISE after GOAWAY was sent */5039return NGHTTP2_ERR_IGN_HEADER_BLOCK;5040}50415042if (!session_is_new_peer_stream_id(session,5043frame->push_promise.promised_stream_id)) {5044/* The spec says if an endpoint receives a PUSH_PROMISE with5045illegal stream ID is subject to a connection error of type5046PROTOCOL_ERROR. */5047return session_inflate_handle_invalid_connection(5048session, frame, NGHTTP2_ERR_PROTO,5049"PUSH_PROMISE: invalid promised_stream_id");5050}50515052if (session_detect_idle_stream(session, frame->hd.stream_id)) {5053return session_inflate_handle_invalid_connection(5054session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream in idle");5055}50565057session->last_recv_stream_id = frame->push_promise.promised_stream_id;5058stream = nghttp2_session_get_stream(session, frame->hd.stream_id);5059if (!stream || stream->state == NGHTTP2_STREAM_CLOSING ||5060!session->pending_enable_push ||5061session->num_incoming_reserved_streams >=5062session->max_incoming_reserved_streams) {5063/* Currently, client does not retain closed stream, so we don't5064check NGHTTP2_SHUT_RD condition here. */50655066rv = nghttp2_session_add_rst_stream(5067session, frame->push_promise.promised_stream_id, NGHTTP2_CANCEL);5068if (rv != 0) {5069return rv;5070}5071return NGHTTP2_ERR_IGN_HEADER_BLOCK;5072}50735074if (stream->shut_flags & NGHTTP2_SHUT_RD) {5075return session_inflate_handle_invalid_connection(5076session, frame, NGHTTP2_ERR_STREAM_CLOSED,5077"PUSH_PROMISE: stream closed");5078}50795080nghttp2_priority_spec_init(&pri_spec, stream->stream_id,5081NGHTTP2_DEFAULT_WEIGHT, 0);50825083promised_stream = nghttp2_session_open_stream(5084session, frame->push_promise.promised_stream_id, NGHTTP2_STREAM_FLAG_NONE,5085&pri_spec, NGHTTP2_STREAM_RESERVED, NULL);50865087if (!promised_stream) {5088return NGHTTP2_ERR_NOMEM;5089}50905091/* We don't call nghttp2_session_adjust_closed_stream(), since we5092don't keep closed stream in client side */50935094session->last_proc_stream_id = session->last_recv_stream_id;5095rv = session_call_on_begin_headers(session, frame);5096if (rv != 0) {5097return rv;5098}5099return 0;5100}51015102static int session_process_push_promise_frame(nghttp2_session *session) {5103int rv;5104nghttp2_inbound_frame *iframe = &session->iframe;5105nghttp2_frame *frame = &iframe->frame;51065107rv = nghttp2_frame_unpack_push_promise_payload(&frame->push_promise,5108iframe->sbuf.pos);51095110if (rv != 0) {5111return nghttp2_session_terminate_session_with_reason(5112session, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: could not unpack");5113}51145115return nghttp2_session_on_push_promise_received(session, frame);5116}51175118int nghttp2_session_on_ping_received(nghttp2_session *session,5119nghttp2_frame *frame) {5120int rv = 0;5121if (frame->hd.stream_id != 0) {5122return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,5123"PING: stream_id != 0");5124}5125if ((session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_PING_ACK) == 0 &&5126(frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 &&5127!session_is_closing(session)) {5128/* Peer sent ping, so ping it back */5129rv = nghttp2_session_add_ping(session, NGHTTP2_FLAG_ACK,5130frame->ping.opaque_data);5131if (rv != 0) {5132return rv;5133}5134}5135return session_call_on_frame_received(session, frame);5136}51375138static int session_process_ping_frame(nghttp2_session *session) {5139nghttp2_inbound_frame *iframe = &session->iframe;5140nghttp2_frame *frame = &iframe->frame;51415142nghttp2_frame_unpack_ping_payload(&frame->ping, iframe->sbuf.pos);51435144return nghttp2_session_on_ping_received(session, frame);5145}51465147int nghttp2_session_on_goaway_received(nghttp2_session *session,5148nghttp2_frame *frame) {5149int rv;51505151if (frame->hd.stream_id != 0) {5152return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,5153"GOAWAY: stream_id != 0");5154}5155/* Spec says Endpoints MUST NOT increase the value they send in the5156last stream identifier. */5157if ((frame->goaway.last_stream_id > 0 &&5158!nghttp2_session_is_my_stream_id(session,5159frame->goaway.last_stream_id)) ||5160session->remote_last_stream_id < frame->goaway.last_stream_id) {5161return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,5162"GOAWAY: invalid last_stream_id");5163}51645165session->goaway_flags |= NGHTTP2_GOAWAY_RECV;51665167session->remote_last_stream_id = frame->goaway.last_stream_id;51685169rv = session_call_on_frame_received(session, frame);51705171if (nghttp2_is_fatal(rv)) {5172return rv;5173}51745175return session_close_stream_on_goaway(session, frame->goaway.last_stream_id,51760);5177}51785179static int session_process_goaway_frame(nghttp2_session *session) {5180nghttp2_inbound_frame *iframe = &session->iframe;5181nghttp2_frame *frame = &iframe->frame;51825183nghttp2_frame_unpack_goaway_payload(&frame->goaway, iframe->sbuf.pos,5184iframe->lbuf.pos,5185nghttp2_buf_len(&iframe->lbuf));51865187nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);51885189return nghttp2_session_on_goaway_received(session, frame);5190}51915192static int5193session_on_connection_window_update_received(nghttp2_session *session,5194nghttp2_frame *frame) {5195/* Handle connection-level flow control */5196if (frame->window_update.window_size_increment == 0) {5197return session_handle_invalid_connection(5198session, frame, NGHTTP2_ERR_PROTO,5199"WINDOW_UPDATE: window_size_increment == 0");5200}52015202if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <5203session->remote_window_size) {5204return session_handle_invalid_connection(session, frame,5205NGHTTP2_ERR_FLOW_CONTROL, NULL);5206}5207session->remote_window_size += frame->window_update.window_size_increment;52085209return session_call_on_frame_received(session, frame);5210}52115212static int session_on_stream_window_update_received(nghttp2_session *session,5213nghttp2_frame *frame) {5214int rv;5215nghttp2_stream *stream;52165217if (session_detect_idle_stream(session, frame->hd.stream_id)) {5218return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,5219"WINDOW_UPDATE to idle stream");5220}52215222stream = nghttp2_session_get_stream(session, frame->hd.stream_id);5223if (!stream) {5224return 0;5225}5226if (state_reserved_remote(session, stream)) {5227return session_handle_invalid_connection(5228session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPADATE to reserved stream");5229}5230if (frame->window_update.window_size_increment == 0) {5231return session_handle_invalid_connection(5232session, frame, NGHTTP2_ERR_PROTO,5233"WINDOW_UPDATE: window_size_increment == 0");5234}5235if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <5236stream->remote_window_size) {5237return session_handle_invalid_stream(session, frame,5238NGHTTP2_ERR_FLOW_CONTROL);5239}5240stream->remote_window_size += frame->window_update.window_size_increment;52415242if (stream->remote_window_size > 0 &&5243nghttp2_stream_check_deferred_by_flow_control(stream)) {52445245rv = session_resume_deferred_stream_item(5246session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);52475248if (nghttp2_is_fatal(rv)) {5249return rv;5250}5251}5252return session_call_on_frame_received(session, frame);5253}52545255int nghttp2_session_on_window_update_received(nghttp2_session *session,5256nghttp2_frame *frame) {5257if (frame->hd.stream_id == 0) {5258return session_on_connection_window_update_received(session, frame);5259} else {5260return session_on_stream_window_update_received(session, frame);5261}5262}52635264static int session_process_window_update_frame(nghttp2_session *session) {5265nghttp2_inbound_frame *iframe = &session->iframe;5266nghttp2_frame *frame = &iframe->frame;52675268nghttp2_frame_unpack_window_update_payload(&frame->window_update,5269iframe->sbuf.pos);52705271return nghttp2_session_on_window_update_received(session, frame);5272}52735274int nghttp2_session_on_altsvc_received(nghttp2_session *session,5275nghttp2_frame *frame) {5276nghttp2_ext_altsvc *altsvc;5277nghttp2_stream *stream;52785279altsvc = frame->ext.payload;52805281/* session->server case has been excluded */52825283if (frame->hd.stream_id == 0) {5284if (altsvc->origin_len == 0) {5285return session_call_on_invalid_frame_recv_callback(session, frame,5286NGHTTP2_ERR_PROTO);5287}5288} else {5289if (altsvc->origin_len > 0) {5290return session_call_on_invalid_frame_recv_callback(session, frame,5291NGHTTP2_ERR_PROTO);5292}52935294stream = nghttp2_session_get_stream(session, frame->hd.stream_id);5295if (!stream) {5296return 0;5297}52985299if (stream->state == NGHTTP2_STREAM_CLOSING) {5300return 0;5301}5302}53035304if (altsvc->field_value_len == 0) {5305return session_call_on_invalid_frame_recv_callback(session, frame,5306NGHTTP2_ERR_PROTO);5307}53085309return session_call_on_frame_received(session, frame);5310}53115312int nghttp2_session_on_origin_received(nghttp2_session *session,5313nghttp2_frame *frame) {5314return session_call_on_frame_received(session, frame);5315}53165317int nghttp2_session_on_priority_update_received(nghttp2_session *session,5318nghttp2_frame *frame) {5319nghttp2_ext_priority_update *priority_update;5320nghttp2_stream *stream;5321nghttp2_priority_spec pri_spec;5322nghttp2_extpri extpri;5323int rv;53245325assert(session->server);53265327priority_update = frame->ext.payload;53285329if (frame->hd.stream_id != 0) {5330return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,5331"PRIORITY_UPDATE: stream_id == 0");5332}53335334if (nghttp2_session_is_my_stream_id(session, priority_update->stream_id)) {5335if (session_detect_idle_stream(session, priority_update->stream_id)) {5336return session_handle_invalid_connection(5337session, frame, NGHTTP2_ERR_PROTO,5338"PRIORITY_UPDATE: prioritizing idle push is not allowed");5339}53405341/* TODO Ignore priority signal to a push stream for now */5342return session_call_on_frame_received(session, frame);5343}53445345stream = nghttp2_session_get_stream_raw(session, priority_update->stream_id);5346if (stream) {5347/* Stream already exists. */5348if (stream->flags & NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES) {5349return session_call_on_frame_received(session, frame);5350}5351} else if (session_detect_idle_stream(session, priority_update->stream_id)) {5352if (session->num_idle_streams + session->num_incoming_streams >=5353session->local_settings.max_concurrent_streams) {5354return session_handle_invalid_connection(5355session, frame, NGHTTP2_ERR_PROTO,5356"PRIORITY_UPDATE: max concurrent streams exceeded");5357}53585359nghttp2_priority_spec_default_init(&pri_spec);5360stream = nghttp2_session_open_stream(session, priority_update->stream_id,5361NGHTTP2_FLAG_NONE, &pri_spec,5362NGHTTP2_STREAM_IDLE, NULL);5363if (!stream) {5364return NGHTTP2_ERR_NOMEM;5365}5366} else {5367return session_call_on_frame_received(session, frame);5368}53695370extpri.urgency = NGHTTP2_EXTPRI_DEFAULT_URGENCY;5371extpri.inc = 0;53725373rv = nghttp2_http_parse_priority(&extpri, priority_update->field_value,5374priority_update->field_value_len);5375if (rv != 0) {5376/* Just ignore field_value if it cannot be parsed. */5377return session_call_on_frame_received(session, frame);5378}53795380rv = session_update_stream_priority(session, stream,5381nghttp2_extpri_to_uint8(&extpri));5382if (rv != 0) {5383if (nghttp2_is_fatal(rv)) {5384return rv;5385}5386}53875388return session_call_on_frame_received(session, frame);5389}53905391static int session_process_altsvc_frame(nghttp2_session *session) {5392nghttp2_inbound_frame *iframe = &session->iframe;5393nghttp2_frame *frame = &iframe->frame;53945395nghttp2_frame_unpack_altsvc_payload(5396&frame->ext, nghttp2_get_uint16(iframe->sbuf.pos), iframe->lbuf.pos,5397nghttp2_buf_len(&iframe->lbuf));53985399/* nghttp2_frame_unpack_altsvc_payload steals buffer from5400iframe->lbuf */5401nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);54025403return nghttp2_session_on_altsvc_received(session, frame);5404}54055406static int session_process_origin_frame(nghttp2_session *session) {5407nghttp2_inbound_frame *iframe = &session->iframe;5408nghttp2_frame *frame = &iframe->frame;5409nghttp2_mem *mem = &session->mem;5410int rv;54115412rv = nghttp2_frame_unpack_origin_payload(&frame->ext, iframe->lbuf.pos,5413nghttp2_buf_len(&iframe->lbuf), mem);5414if (rv != 0) {5415if (nghttp2_is_fatal(rv)) {5416return rv;5417}5418/* Ignore ORIGIN frame which cannot be parsed. */5419return 0;5420}54215422return nghttp2_session_on_origin_received(session, frame);5423}54245425static int session_process_priority_update_frame(nghttp2_session *session) {5426nghttp2_inbound_frame *iframe = &session->iframe;5427nghttp2_frame *frame = &iframe->frame;54285429nghttp2_frame_unpack_priority_update_payload(&frame->ext, iframe->sbuf.pos,5430nghttp2_buf_len(&iframe->sbuf));54315432return nghttp2_session_on_priority_update_received(session, frame);5433}54345435static int session_process_extension_frame(nghttp2_session *session) {5436int rv;5437nghttp2_inbound_frame *iframe = &session->iframe;5438nghttp2_frame *frame = &iframe->frame;54395440rv = session_call_unpack_extension_callback(session);5441if (nghttp2_is_fatal(rv)) {5442return rv;5443}54445445/* This handles the case where rv == NGHTTP2_ERR_CANCEL as well */5446if (rv != 0) {5447return 0;5448}54495450return session_call_on_frame_received(session, frame);5451}54525453int nghttp2_session_on_data_received(nghttp2_session *session,5454nghttp2_frame *frame) {5455int rv = 0;5456nghttp2_stream *stream;54575458/* We don't call on_frame_recv_callback if stream has been closed5459already or being closed. */5460stream = nghttp2_session_get_stream(session, frame->hd.stream_id);5461if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) {5462/* This should be treated as stream error, but it results in lots5463of RST_STREAM. So just ignore frame against nonexistent stream5464for now. */5465return 0;5466}54675468if (session_enforce_http_messaging(session) &&5469(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {5470if (nghttp2_http_on_remote_end_stream(stream) != 0) {5471rv = nghttp2_session_add_rst_stream(session, stream->stream_id,5472NGHTTP2_PROTOCOL_ERROR);5473if (nghttp2_is_fatal(rv)) {5474return rv;5475}54765477nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);5478/* Don't call nghttp2_session_close_stream_if_shut_rdwr because5479RST_STREAM has been submitted. */5480return 0;5481}5482}54835484rv = session_call_on_frame_received(session, frame);5485if (nghttp2_is_fatal(rv)) {5486return rv;5487}54885489if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {5490nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);5491rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);5492if (nghttp2_is_fatal(rv)) {5493return rv;5494}5495}5496return 0;5497}54985499/* For errors, this function only returns FATAL error. */5500static int session_process_data_frame(nghttp2_session *session) {5501int rv;5502nghttp2_frame *public_data_frame = &session->iframe.frame;5503rv = nghttp2_session_on_data_received(session, public_data_frame);5504if (nghttp2_is_fatal(rv)) {5505return rv;5506}5507return 0;5508}55095510/*5511* Now we have SETTINGS synchronization, flow control error can be5512* detected strictly. If DATA frame is received with length > 0 and5513* current received window size + delta length is strictly larger than5514* local window size, it is subject to FLOW_CONTROL_ERROR, so return5515* -1. Note that local_window_size is calculated after SETTINGS ACK is5516* received from peer, so peer must honor this limit. If the resulting5517* recv_window_size is strictly larger than NGHTTP2_MAX_WINDOW_SIZE,5518* return -1 too.5519*/5520static int adjust_recv_window_size(int32_t *recv_window_size_ptr, size_t delta,5521int32_t local_window_size) {5522if (*recv_window_size_ptr > local_window_size - (int32_t)delta ||5523*recv_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - (int32_t)delta) {5524return -1;5525}5526*recv_window_size_ptr += (int32_t)delta;5527return 0;5528}55295530int nghttp2_session_update_recv_stream_window_size(nghttp2_session *session,5531nghttp2_stream *stream,5532size_t delta_size,5533int send_window_update) {5534int rv;5535rv = adjust_recv_window_size(&stream->recv_window_size, delta_size,5536stream->local_window_size);5537if (rv != 0) {5538return nghttp2_session_add_rst_stream(session, stream->stream_id,5539NGHTTP2_FLOW_CONTROL_ERROR);5540}5541/* We don't have to send WINDOW_UPDATE if the data received is the5542last chunk in the incoming stream. */5543/* We have to use local_settings here because it is the constraint5544the remote endpoint should honor. */5545if (send_window_update &&5546!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) &&5547stream->window_update_queued == 0 &&5548nghttp2_should_send_window_update(stream->local_window_size,5549stream->recv_window_size)) {5550rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE,5551stream->stream_id,5552stream->recv_window_size);5553if (rv != 0) {5554return rv;5555}55565557stream->recv_window_size = 0;5558}5559return 0;5560}55615562int nghttp2_session_update_recv_connection_window_size(nghttp2_session *session,5563size_t delta_size) {5564int rv;5565rv = adjust_recv_window_size(&session->recv_window_size, delta_size,5566session->local_window_size);5567if (rv != 0) {5568return nghttp2_session_terminate_session(session,5569NGHTTP2_FLOW_CONTROL_ERROR);5570}5571if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) &&5572session->window_update_queued == 0 &&5573nghttp2_should_send_window_update(session->local_window_size,5574session->recv_window_size)) {5575/* Use stream ID 0 to update connection-level flow control5576window */5577rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, 0,5578session->recv_window_size);5579if (rv != 0) {5580return rv;5581}55825583session->recv_window_size = 0;5584}5585return 0;5586}55875588static int session_update_consumed_size(nghttp2_session *session,5589int32_t *consumed_size_ptr,5590int32_t *recv_window_size_ptr,5591uint8_t window_update_queued,5592int32_t stream_id, size_t delta_size,5593int32_t local_window_size) {5594int32_t recv_size;5595int rv;55965597if ((size_t)*consumed_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta_size) {5598return nghttp2_session_terminate_session(session,5599NGHTTP2_FLOW_CONTROL_ERROR);5600}56015602*consumed_size_ptr += (int32_t)delta_size;56035604if (window_update_queued == 0) {5605/* recv_window_size may be smaller than consumed_size, because it5606may be decreased by negative value with5607nghttp2_submit_window_update(). */5608recv_size = nghttp2_min(*consumed_size_ptr, *recv_window_size_ptr);56095610if (nghttp2_should_send_window_update(local_window_size, recv_size)) {5611rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE,5612stream_id, recv_size);56135614if (rv != 0) {5615return rv;5616}56175618*recv_window_size_ptr -= recv_size;5619*consumed_size_ptr -= recv_size;5620}5621}56225623return 0;5624}56255626static int session_update_stream_consumed_size(nghttp2_session *session,5627nghttp2_stream *stream,5628size_t delta_size) {5629return session_update_consumed_size(5630session, &stream->consumed_size, &stream->recv_window_size,5631stream->window_update_queued, stream->stream_id, delta_size,5632stream->local_window_size);5633}56345635static int session_update_connection_consumed_size(nghttp2_session *session,5636size_t delta_size) {5637return session_update_consumed_size(5638session, &session->consumed_size, &session->recv_window_size,5639session->window_update_queued, 0, delta_size, session->local_window_size);5640}56415642/*5643* Checks that we can receive the DATA frame for stream, which is5644* indicated by |session->iframe.frame.hd.stream_id|. If it is a5645* connection error situation, GOAWAY frame will be issued by this5646* function.5647*5648* If the DATA frame is allowed, returns 0.5649*5650* This function returns 0 if it succeeds, or one of the following5651* negative error codes:5652*5653* NGHTTP2_ERR_IGN_PAYLOAD5654* The reception of DATA frame is connection error; or should be5655* ignored.5656* NGHTTP2_ERR_NOMEM5657* Out of memory.5658*/5659static int session_on_data_received_fail_fast(nghttp2_session *session) {5660int rv;5661nghttp2_stream *stream;5662nghttp2_inbound_frame *iframe;5663int32_t stream_id;5664const char *failure_reason;5665uint32_t error_code = NGHTTP2_PROTOCOL_ERROR;56665667iframe = &session->iframe;5668stream_id = iframe->frame.hd.stream_id;56695670if (stream_id == 0) {5671/* The spec says that if a DATA frame is received whose stream ID5672is 0, the recipient MUST respond with a connection error of5673type PROTOCOL_ERROR. */5674failure_reason = "DATA: stream_id == 0";5675goto fail;5676}56775678if (session_detect_idle_stream(session, stream_id)) {5679failure_reason = "DATA: stream in idle";5680error_code = NGHTTP2_PROTOCOL_ERROR;5681goto fail;5682}56835684stream = nghttp2_session_get_stream(session, stream_id);5685if (!stream) {5686stream = nghttp2_session_get_stream_raw(session, stream_id);5687if (stream && (stream->shut_flags & NGHTTP2_SHUT_RD)) {5688failure_reason = "DATA: stream closed";5689error_code = NGHTTP2_STREAM_CLOSED;5690goto fail;5691}56925693return NGHTTP2_ERR_IGN_PAYLOAD;5694}5695if (stream->shut_flags & NGHTTP2_SHUT_RD) {5696failure_reason = "DATA: stream in half-closed(remote)";5697error_code = NGHTTP2_STREAM_CLOSED;5698goto fail;5699}57005701if (nghttp2_session_is_my_stream_id(session, stream_id)) {5702if (stream->state == NGHTTP2_STREAM_CLOSING) {5703return NGHTTP2_ERR_IGN_PAYLOAD;5704}5705if (stream->state != NGHTTP2_STREAM_OPENED) {5706failure_reason = "DATA: stream not opened";5707goto fail;5708}5709return 0;5710}5711if (stream->state == NGHTTP2_STREAM_RESERVED) {5712failure_reason = "DATA: stream in reserved";5713goto fail;5714}5715if (stream->state == NGHTTP2_STREAM_CLOSING) {5716return NGHTTP2_ERR_IGN_PAYLOAD;5717}5718return 0;5719fail:5720rv = nghttp2_session_terminate_session_with_reason(session, error_code,5721failure_reason);5722if (nghttp2_is_fatal(rv)) {5723return rv;5724}5725return NGHTTP2_ERR_IGN_PAYLOAD;5726}57275728static size_t inbound_frame_payload_readlen(nghttp2_inbound_frame *iframe,5729const uint8_t *in,5730const uint8_t *last) {5731return nghttp2_min((size_t)(last - in), iframe->payloadleft);5732}57335734/*5735* Resets iframe->sbuf and advance its mark pointer by |left| bytes.5736*/5737static void inbound_frame_set_mark(nghttp2_inbound_frame *iframe, size_t left) {5738nghttp2_buf_reset(&iframe->sbuf);5739iframe->sbuf.mark += left;5740}57415742static size_t inbound_frame_buf_read(nghttp2_inbound_frame *iframe,5743const uint8_t *in, const uint8_t *last) {5744size_t readlen;57455746readlen =5747nghttp2_min((size_t)(last - in), nghttp2_buf_mark_avail(&iframe->sbuf));57485749iframe->sbuf.last = nghttp2_cpymem(iframe->sbuf.last, in, readlen);57505751return readlen;5752}57535754/*5755* Unpacks SETTINGS entry in iframe->sbuf.5756*/5757static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) {5758nghttp2_settings_entry iv;5759nghttp2_settings_entry *min_header_table_size_entry;5760size_t i;57615762nghttp2_frame_unpack_settings_entry(&iv, iframe->sbuf.pos);57635764switch (iv.settings_id) {5765case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:5766case NGHTTP2_SETTINGS_ENABLE_PUSH:5767case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:5768case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:5769case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:5770case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:5771case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:5772case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:5773break;5774default:5775DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id);57765777iframe->iv[iframe->niv++] = iv;57785779return;5780}57815782for (i = 0; i < iframe->niv; ++i) {5783if (iframe->iv[i].settings_id == iv.settings_id) {5784iframe->iv[i] = iv;5785break;5786}5787}57885789if (i == iframe->niv) {5790iframe->iv[iframe->niv++] = iv;5791}57925793if (iv.settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) {5794/* Keep track of minimum value of SETTINGS_HEADER_TABLE_SIZE */5795min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1];57965797if (iv.value < min_header_table_size_entry->value) {5798min_header_table_size_entry->value = iv.value;5799}5800}5801}58025803/*5804* Checks PADDED flags and set iframe->sbuf to read them accordingly.5805* If padding is set, this function returns 1. If no padding is set,5806* this function returns 0. On error, returns -1.5807*/5808static int inbound_frame_handle_pad(nghttp2_inbound_frame *iframe,5809nghttp2_frame_hd *hd) {5810if (hd->flags & NGHTTP2_FLAG_PADDED) {5811if (hd->length < 1) {5812return -1;5813}5814inbound_frame_set_mark(iframe, 1);5815return 1;5816}5817DEBUGF("recv: no padding in payload\n");5818return 0;5819}58205821/*5822* Computes number of padding based on flags. This function returns5823* the calculated length if it succeeds, or -1.5824*/5825static ssize_t inbound_frame_compute_pad(nghttp2_inbound_frame *iframe) {5826size_t padlen;58275828/* 1 for Pad Length field */5829padlen = (size_t)(iframe->sbuf.pos[0] + 1);58305831DEBUGF("recv: padlen=%zu\n", padlen);58325833/* We cannot use iframe->frame.hd.length because of CONTINUATION */5834if (padlen - 1 > iframe->payloadleft) {5835return -1;5836}58375838iframe->padlen = padlen;58395840return (ssize_t)padlen;5841}58425843/*5844* This function returns the effective payload length in the data of5845* length |readlen| when the remaining payload is |payloadleft|. The5846* |payloadleft| does not include |readlen|. If padding was started5847* strictly before this data chunk, this function returns -1.5848*/5849static ssize_t inbound_frame_effective_readlen(nghttp2_inbound_frame *iframe,5850size_t payloadleft,5851size_t readlen) {5852size_t trail_padlen =5853nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen);58545855if (trail_padlen > payloadleft) {5856size_t padlen;5857padlen = trail_padlen - payloadleft;5858if (readlen < padlen) {5859return -1;5860}5861return (ssize_t)(readlen - padlen);5862}5863return (ssize_t)(readlen);5864}58655866static const uint8_t static_in[] = {0};58675868ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,5869size_t inlen) {5870const uint8_t *first, *last;5871nghttp2_inbound_frame *iframe = &session->iframe;5872size_t readlen;5873ssize_t padlen;5874int rv;5875int busy = 0;5876nghttp2_frame_hd cont_hd;5877nghttp2_stream *stream;5878size_t pri_fieldlen;5879nghttp2_mem *mem;58805881if (in == NULL) {5882assert(inlen == 0);5883in = static_in;5884}58855886first = in;5887last = in + inlen;58885889DEBUGF("recv: connection recv_window_size=%d, local_window=%d\n",5890session->recv_window_size, session->local_window_size);58915892mem = &session->mem;58935894/* We may have idle streams more than we expect (e.g.,5895nghttp2_session_change_stream_priority() or5896nghttp2_session_create_idle_stream()). Adjust them here. */5897rv = nghttp2_session_adjust_idle_stream(session);5898if (nghttp2_is_fatal(rv)) {5899return rv;5900}59015902if (!nghttp2_session_want_read(session)) {5903return (ssize_t)inlen;5904}59055906for (;;) {5907switch (iframe->state) {5908case NGHTTP2_IB_READ_CLIENT_MAGIC:5909readlen = nghttp2_min(inlen, iframe->payloadleft);59105911if (memcmp(&NGHTTP2_CLIENT_MAGIC[NGHTTP2_CLIENT_MAGIC_LEN -5912iframe->payloadleft],5913in, readlen) != 0) {5914return NGHTTP2_ERR_BAD_CLIENT_MAGIC;5915}59165917iframe->payloadleft -= readlen;5918in += readlen;59195920if (iframe->payloadleft == 0) {5921session_inbound_frame_reset(session);5922iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS;5923}59245925break;5926case NGHTTP2_IB_READ_FIRST_SETTINGS:5927DEBUGF("recv: [IB_READ_FIRST_SETTINGS]\n");59285929readlen = inbound_frame_buf_read(iframe, in, last);5930in += readlen;59315932if (nghttp2_buf_mark_avail(&iframe->sbuf)) {5933return in - first;5934}59355936if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS ||5937(iframe->sbuf.pos[4] & NGHTTP2_FLAG_ACK)) {5938rv = session_call_error_callback(5939session, NGHTTP2_ERR_SETTINGS_EXPECTED,5940"Remote peer returned unexpected data while we expected "5941"SETTINGS frame. Perhaps, peer does not support HTTP/2 "5942"properly.");59435944if (nghttp2_is_fatal(rv)) {5945return rv;5946}59475948rv = nghttp2_session_terminate_session_with_reason(5949session, NGHTTP2_PROTOCOL_ERROR, "SETTINGS expected");59505951if (nghttp2_is_fatal(rv)) {5952return rv;5953}59545955return (ssize_t)inlen;5956}59575958iframe->state = NGHTTP2_IB_READ_HEAD;59595960/* Fall through */5961case NGHTTP2_IB_READ_HEAD: {5962int on_begin_frame_called = 0;59635964DEBUGF("recv: [IB_READ_HEAD]\n");59655966readlen = inbound_frame_buf_read(iframe, in, last);5967in += readlen;59685969if (nghttp2_buf_mark_avail(&iframe->sbuf)) {5970return in - first;5971}59725973nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->sbuf.pos);5974iframe->payloadleft = iframe->frame.hd.length;59755976DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n",5977iframe->frame.hd.length, iframe->frame.hd.type,5978iframe->frame.hd.flags, iframe->frame.hd.stream_id);59795980if (iframe->frame.hd.length > session->local_settings.max_frame_size) {5981DEBUGF("recv: length is too large %zu > %u\n", iframe->frame.hd.length,5982session->local_settings.max_frame_size);59835984rv = nghttp2_session_terminate_session_with_reason(5985session, NGHTTP2_FRAME_SIZE_ERROR, "too large frame size");59865987if (nghttp2_is_fatal(rv)) {5988return rv;5989}59905991return (ssize_t)inlen;5992}59935994switch (iframe->frame.hd.type) {5995case NGHTTP2_DATA: {5996DEBUGF("recv: DATA\n");59975998iframe->frame.hd.flags &=5999(NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PADDED);6000/* Check stream is open. If it is not open or closing,6001ignore payload. */6002busy = 1;60036004rv = session_on_data_received_fail_fast(session);6005if (iframe->state == NGHTTP2_IB_IGN_ALL) {6006return (ssize_t)inlen;6007}6008if (rv == NGHTTP2_ERR_IGN_PAYLOAD) {6009DEBUGF("recv: DATA not allowed stream_id=%d\n",6010iframe->frame.hd.stream_id);6011iframe->state = NGHTTP2_IB_IGN_DATA;6012break;6013}60146015if (nghttp2_is_fatal(rv)) {6016return rv;6017}60186019rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);6020if (rv < 0) {6021rv = nghttp2_session_terminate_session_with_reason(6022session, NGHTTP2_PROTOCOL_ERROR,6023"DATA: insufficient padding space");60246025if (nghttp2_is_fatal(rv)) {6026return rv;6027}6028return (ssize_t)inlen;6029}60306031if (rv == 1) {6032iframe->state = NGHTTP2_IB_READ_PAD_DATA;6033break;6034}60356036iframe->state = NGHTTP2_IB_READ_DATA;6037break;6038}6039case NGHTTP2_HEADERS:60406041DEBUGF("recv: HEADERS\n");60426043iframe->frame.hd.flags &=6044(NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS |6045NGHTTP2_FLAG_PADDED | NGHTTP2_FLAG_PRIORITY);60466047rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);6048if (rv < 0) {6049rv = nghttp2_session_terminate_session_with_reason(6050session, NGHTTP2_PROTOCOL_ERROR,6051"HEADERS: insufficient padding space");6052if (nghttp2_is_fatal(rv)) {6053return rv;6054}6055return (ssize_t)inlen;6056}60576058if (rv == 1) {6059iframe->state = NGHTTP2_IB_READ_NBYTE;6060break;6061}60626063pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags);60646065if (pri_fieldlen > 0) {6066if (iframe->payloadleft < pri_fieldlen) {6067busy = 1;6068iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;6069break;6070}60716072iframe->state = NGHTTP2_IB_READ_NBYTE;60736074inbound_frame_set_mark(iframe, pri_fieldlen);60756076break;6077}60786079/* Call on_begin_frame_callback here because6080session_process_headers_frame() may call6081on_begin_headers_callback */6082rv = session_call_on_begin_frame(session, &iframe->frame.hd);60836084if (nghttp2_is_fatal(rv)) {6085return rv;6086}60876088on_begin_frame_called = 1;60896090rv = session_process_headers_frame(session);6091if (nghttp2_is_fatal(rv)) {6092return rv;6093}60946095busy = 1;60966097if (iframe->state == NGHTTP2_IB_IGN_ALL) {6098return (ssize_t)inlen;6099}61006101if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {6102rv = nghttp2_session_add_rst_stream(6103session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR);6104if (nghttp2_is_fatal(rv)) {6105return rv;6106}6107iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;6108break;6109}61106111if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {6112iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;6113break;6114}61156116iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;61176118break;6119case NGHTTP2_PRIORITY:6120DEBUGF("recv: PRIORITY\n");61216122iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;61236124if (iframe->payloadleft != NGHTTP2_PRIORITY_SPECLEN) {6125busy = 1;61266127iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;61286129break;6130}61316132iframe->state = NGHTTP2_IB_READ_NBYTE;61336134inbound_frame_set_mark(iframe, NGHTTP2_PRIORITY_SPECLEN);61356136break;6137case NGHTTP2_RST_STREAM:6138case NGHTTP2_WINDOW_UPDATE:6139#ifdef DEBUGBUILD6140switch (iframe->frame.hd.type) {6141case NGHTTP2_RST_STREAM:6142DEBUGF("recv: RST_STREAM\n");6143break;6144case NGHTTP2_WINDOW_UPDATE:6145DEBUGF("recv: WINDOW_UPDATE\n");6146break;6147}6148#endif /* DEBUGBUILD */61496150iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;61516152if (iframe->payloadleft != 4) {6153busy = 1;6154iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;6155break;6156}61576158iframe->state = NGHTTP2_IB_READ_NBYTE;61596160inbound_frame_set_mark(iframe, 4);61616162break;6163case NGHTTP2_SETTINGS:6164DEBUGF("recv: SETTINGS\n");61656166iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK;61676168if ((iframe->frame.hd.length % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) ||6169((iframe->frame.hd.flags & NGHTTP2_FLAG_ACK) &&6170iframe->payloadleft > 0)) {6171busy = 1;6172iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;6173break;6174}61756176/* Check the settings flood counter early to be safe */6177if (session->obq_flood_counter_ >= session->max_outbound_ack &&6178!(iframe->frame.hd.flags & NGHTTP2_FLAG_ACK)) {6179return NGHTTP2_ERR_FLOODED;6180}61816182iframe->state = NGHTTP2_IB_READ_SETTINGS;61836184if (iframe->payloadleft) {6185nghttp2_settings_entry *min_header_table_size_entry;61866187/* We allocate iv with additional one entry, to store the6188minimum header table size. */6189iframe->max_niv =6190iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1;61916192if (iframe->max_niv - 1 > session->max_settings) {6193rv = nghttp2_session_terminate_session_with_reason(6194session, NGHTTP2_ENHANCE_YOUR_CALM,6195"SETTINGS: too many setting entries");6196if (nghttp2_is_fatal(rv)) {6197return rv;6198}6199return (ssize_t)inlen;6200}62016202iframe->iv = nghttp2_mem_malloc(mem, sizeof(nghttp2_settings_entry) *6203iframe->max_niv);62046205if (!iframe->iv) {6206return NGHTTP2_ERR_NOMEM;6207}62086209min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1];6210min_header_table_size_entry->settings_id =6211NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;6212min_header_table_size_entry->value = UINT32_MAX;62136214inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH);6215break;6216}62176218busy = 1;62196220inbound_frame_set_mark(iframe, 0);62216222break;6223case NGHTTP2_PUSH_PROMISE:6224DEBUGF("recv: PUSH_PROMISE\n");62256226iframe->frame.hd.flags &=6227(NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PADDED);62286229rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);6230if (rv < 0) {6231rv = nghttp2_session_terminate_session_with_reason(6232session, NGHTTP2_PROTOCOL_ERROR,6233"PUSH_PROMISE: insufficient padding space");6234if (nghttp2_is_fatal(rv)) {6235return rv;6236}6237return (ssize_t)inlen;6238}62396240if (rv == 1) {6241iframe->state = NGHTTP2_IB_READ_NBYTE;6242break;6243}62446245if (iframe->payloadleft < 4) {6246busy = 1;6247iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;6248break;6249}62506251iframe->state = NGHTTP2_IB_READ_NBYTE;62526253inbound_frame_set_mark(iframe, 4);62546255break;6256case NGHTTP2_PING:6257DEBUGF("recv: PING\n");62586259iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK;62606261if (iframe->payloadleft != 8) {6262busy = 1;6263iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;6264break;6265}62666267iframe->state = NGHTTP2_IB_READ_NBYTE;6268inbound_frame_set_mark(iframe, 8);62696270break;6271case NGHTTP2_GOAWAY:6272DEBUGF("recv: GOAWAY\n");62736274iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;62756276if (iframe->payloadleft < 8) {6277busy = 1;6278iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;6279break;6280}62816282iframe->state = NGHTTP2_IB_READ_NBYTE;6283inbound_frame_set_mark(iframe, 8);62846285break;6286case NGHTTP2_CONTINUATION:6287DEBUGF("recv: unexpected CONTINUATION\n");62886289/* Receiving CONTINUATION in this state are subject to6290connection error of type PROTOCOL_ERROR */6291rv = nghttp2_session_terminate_session_with_reason(6292session, NGHTTP2_PROTOCOL_ERROR, "CONTINUATION: unexpected");6293if (nghttp2_is_fatal(rv)) {6294return rv;6295}62966297return (ssize_t)inlen;6298default:6299DEBUGF("recv: extension frame\n");63006301if (check_ext_type_set(session->user_recv_ext_types,6302iframe->frame.hd.type)) {6303if (!session->callbacks.unpack_extension_callback) {6304/* Silently ignore unknown frame type. */63056306busy = 1;63076308iframe->state = NGHTTP2_IB_IGN_PAYLOAD;63096310break;6311}63126313busy = 1;63146315iframe->state = NGHTTP2_IB_READ_EXTENSION_PAYLOAD;63166317break;6318} else {6319switch (iframe->frame.hd.type) {6320case NGHTTP2_ALTSVC:6321if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) ==63220) {6323busy = 1;6324iframe->state = NGHTTP2_IB_IGN_PAYLOAD;6325break;6326}63276328DEBUGF("recv: ALTSVC\n");63296330iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;6331iframe->frame.ext.payload = &iframe->ext_frame_payload.altsvc;63326333if (session->server) {6334busy = 1;6335iframe->state = NGHTTP2_IB_IGN_PAYLOAD;6336break;6337}63386339if (iframe->payloadleft < 2) {6340busy = 1;6341iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;6342break;6343}63446345busy = 1;63466347iframe->state = NGHTTP2_IB_READ_NBYTE;6348inbound_frame_set_mark(iframe, 2);63496350break;6351case NGHTTP2_ORIGIN:6352if (!(session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN)) {6353busy = 1;6354iframe->state = NGHTTP2_IB_IGN_PAYLOAD;6355break;6356}63576358DEBUGF("recv: ORIGIN\n");63596360iframe->frame.ext.payload = &iframe->ext_frame_payload.origin;63616362if (session->server || iframe->frame.hd.stream_id ||6363(iframe->frame.hd.flags & 0xf0)) {6364busy = 1;6365iframe->state = NGHTTP2_IB_IGN_PAYLOAD;6366break;6367}63686369iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;63706371if (iframe->payloadleft) {6372iframe->raw_lbuf = nghttp2_mem_malloc(mem, iframe->payloadleft);63736374if (iframe->raw_lbuf == NULL) {6375return NGHTTP2_ERR_NOMEM;6376}63776378nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf,6379iframe->payloadleft);6380} else {6381busy = 1;6382}63836384iframe->state = NGHTTP2_IB_READ_ORIGIN_PAYLOAD;63856386break;6387case NGHTTP2_PRIORITY_UPDATE:6388if ((session->builtin_recv_ext_types &6389NGHTTP2_TYPEMASK_PRIORITY_UPDATE) == 0) {6390busy = 1;6391iframe->state = NGHTTP2_IB_IGN_PAYLOAD;6392break;6393}63946395DEBUGF("recv: PRIORITY_UPDATE\n");63966397iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;6398iframe->frame.ext.payload =6399&iframe->ext_frame_payload.priority_update;64006401if (!session->server) {6402rv = nghttp2_session_terminate_session_with_reason(6403session, NGHTTP2_PROTOCOL_ERROR,6404"PRIORITY_UPDATE is received from server");6405if (nghttp2_is_fatal(rv)) {6406return rv;6407}6408return (ssize_t)inlen;6409}64106411if (iframe->payloadleft < 4) {6412busy = 1;6413iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;6414break;6415}64166417if (!session_no_rfc7540_pri_no_fallback(session) ||6418iframe->payloadleft > sizeof(iframe->raw_sbuf)) {6419busy = 1;6420iframe->state = NGHTTP2_IB_IGN_PAYLOAD;6421break;6422}64236424busy = 1;64256426iframe->state = NGHTTP2_IB_READ_NBYTE;6427inbound_frame_set_mark(iframe, iframe->payloadleft);64286429break;6430default:6431busy = 1;64326433iframe->state = NGHTTP2_IB_IGN_PAYLOAD;64346435break;6436}6437}6438}64396440if (!on_begin_frame_called) {6441switch (iframe->state) {6442case NGHTTP2_IB_IGN_HEADER_BLOCK:6443case NGHTTP2_IB_IGN_PAYLOAD:6444case NGHTTP2_IB_FRAME_SIZE_ERROR:6445case NGHTTP2_IB_IGN_DATA:6446case NGHTTP2_IB_IGN_ALL:6447break;6448default:6449rv = session_call_on_begin_frame(session, &iframe->frame.hd);64506451if (nghttp2_is_fatal(rv)) {6452return rv;6453}6454}6455}64566457break;6458}6459case NGHTTP2_IB_READ_NBYTE:6460DEBUGF("recv: [IB_READ_NBYTE]\n");64616462readlen = inbound_frame_buf_read(iframe, in, last);6463in += readlen;6464iframe->payloadleft -= readlen;64656466DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zd\n", readlen,6467iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf));64686469if (nghttp2_buf_mark_avail(&iframe->sbuf)) {6470return in - first;6471}64726473switch (iframe->frame.hd.type) {6474case NGHTTP2_HEADERS:6475if (iframe->padlen == 0 &&6476(iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) {6477pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags);6478padlen = inbound_frame_compute_pad(iframe);6479if (padlen < 0 ||6480(size_t)padlen + pri_fieldlen > 1 + iframe->payloadleft) {6481rv = nghttp2_session_terminate_session_with_reason(6482session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: invalid padding");6483if (nghttp2_is_fatal(rv)) {6484return rv;6485}6486return (ssize_t)inlen;6487}6488iframe->frame.headers.padlen = (size_t)padlen;64896490if (pri_fieldlen > 0) {6491if (iframe->payloadleft < pri_fieldlen) {6492busy = 1;6493iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;6494break;6495}6496iframe->state = NGHTTP2_IB_READ_NBYTE;6497inbound_frame_set_mark(iframe, pri_fieldlen);6498break;6499} else {6500/* Truncate buffers used for padding spec */6501inbound_frame_set_mark(iframe, 0);6502}6503}65046505rv = session_process_headers_frame(session);6506if (nghttp2_is_fatal(rv)) {6507return rv;6508}65096510busy = 1;65116512if (iframe->state == NGHTTP2_IB_IGN_ALL) {6513return (ssize_t)inlen;6514}65156516if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {6517rv = nghttp2_session_add_rst_stream(6518session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR);6519if (nghttp2_is_fatal(rv)) {6520return rv;6521}6522iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;6523break;6524}65256526if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {6527iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;6528break;6529}65306531iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;65326533break;6534case NGHTTP2_PRIORITY:6535if (!session_no_rfc7540_pri_no_fallback(session) &&6536session->remote_settings.no_rfc7540_priorities != 1) {6537rv = session_process_priority_frame(session);6538if (nghttp2_is_fatal(rv)) {6539return rv;6540}65416542if (iframe->state == NGHTTP2_IB_IGN_ALL) {6543return (ssize_t)inlen;6544}6545}65466547session_inbound_frame_reset(session);65486549break;6550case NGHTTP2_RST_STREAM:6551rv = session_process_rst_stream_frame(session);6552if (nghttp2_is_fatal(rv)) {6553return rv;6554}65556556if (iframe->state == NGHTTP2_IB_IGN_ALL) {6557return (ssize_t)inlen;6558}65596560session_inbound_frame_reset(session);65616562break;6563case NGHTTP2_PUSH_PROMISE:6564if (iframe->padlen == 0 &&6565(iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) {6566padlen = inbound_frame_compute_pad(iframe);6567if (padlen < 0 || (size_t)padlen + 4 /* promised stream id */6568> 1 + iframe->payloadleft) {6569rv = nghttp2_session_terminate_session_with_reason(6570session, NGHTTP2_PROTOCOL_ERROR,6571"PUSH_PROMISE: invalid padding");6572if (nghttp2_is_fatal(rv)) {6573return rv;6574}6575return (ssize_t)inlen;6576}65776578iframe->frame.push_promise.padlen = (size_t)padlen;65796580if (iframe->payloadleft < 4) {6581busy = 1;6582iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;6583break;6584}65856586iframe->state = NGHTTP2_IB_READ_NBYTE;65876588inbound_frame_set_mark(iframe, 4);65896590break;6591}65926593rv = session_process_push_promise_frame(session);6594if (nghttp2_is_fatal(rv)) {6595return rv;6596}65976598busy = 1;65996600if (iframe->state == NGHTTP2_IB_IGN_ALL) {6601return (ssize_t)inlen;6602}66036604if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {6605rv = nghttp2_session_add_rst_stream(6606session, iframe->frame.push_promise.promised_stream_id,6607NGHTTP2_INTERNAL_ERROR);6608if (nghttp2_is_fatal(rv)) {6609return rv;6610}6611iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;6612break;6613}66146615if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {6616iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;6617break;6618}66196620iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;66216622break;6623case NGHTTP2_PING:6624rv = session_process_ping_frame(session);6625if (nghttp2_is_fatal(rv)) {6626return rv;6627}66286629if (iframe->state == NGHTTP2_IB_IGN_ALL) {6630return (ssize_t)inlen;6631}66326633session_inbound_frame_reset(session);66346635break;6636case NGHTTP2_GOAWAY: {6637size_t debuglen;66386639/* 8 is Last-stream-ID + Error Code */6640debuglen = iframe->frame.hd.length - 8;66416642if (debuglen > 0) {6643iframe->raw_lbuf = nghttp2_mem_malloc(mem, debuglen);66446645if (iframe->raw_lbuf == NULL) {6646return NGHTTP2_ERR_NOMEM;6647}66486649nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, debuglen);6650}66516652busy = 1;66536654iframe->state = NGHTTP2_IB_READ_GOAWAY_DEBUG;66556656break;6657}6658case NGHTTP2_WINDOW_UPDATE:6659rv = session_process_window_update_frame(session);6660if (nghttp2_is_fatal(rv)) {6661return rv;6662}66636664if (iframe->state == NGHTTP2_IB_IGN_ALL) {6665return (ssize_t)inlen;6666}66676668session_inbound_frame_reset(session);66696670break;6671case NGHTTP2_ALTSVC: {6672size_t origin_len;66736674origin_len = nghttp2_get_uint16(iframe->sbuf.pos);66756676DEBUGF("recv: origin_len=%zu\n", origin_len);66776678if (origin_len > iframe->payloadleft) {6679busy = 1;6680iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;6681break;6682}66836684if (iframe->frame.hd.length > 2) {6685iframe->raw_lbuf =6686nghttp2_mem_malloc(mem, iframe->frame.hd.length - 2);66876688if (iframe->raw_lbuf == NULL) {6689return NGHTTP2_ERR_NOMEM;6690}66916692nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf,6693iframe->frame.hd.length);6694}66956696busy = 1;66976698iframe->state = NGHTTP2_IB_READ_ALTSVC_PAYLOAD;66996700break;6701case NGHTTP2_PRIORITY_UPDATE:6702DEBUGF("recv: prioritized_stream_id=%d\n",6703nghttp2_get_uint32(iframe->sbuf.pos) & NGHTTP2_STREAM_ID_MASK);67046705rv = session_process_priority_update_frame(session);6706if (nghttp2_is_fatal(rv)) {6707return rv;6708}67096710session_inbound_frame_reset(session);67116712break;6713}6714default:6715/* This is unknown frame */6716session_inbound_frame_reset(session);67176718break;6719}6720break;6721case NGHTTP2_IB_READ_HEADER_BLOCK:6722case NGHTTP2_IB_IGN_HEADER_BLOCK: {6723ssize_t data_readlen;6724size_t trail_padlen;6725int final;6726#ifdef DEBUGBUILD6727if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {6728DEBUGF("recv: [IB_READ_HEADER_BLOCK]\n");6729} else {6730DEBUGF("recv: [IB_IGN_HEADER_BLOCK]\n");6731}6732#endif /* DEBUGBUILD */67336734readlen = inbound_frame_payload_readlen(iframe, in, last);67356736DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,6737iframe->payloadleft - readlen);67386739data_readlen = inbound_frame_effective_readlen(6740iframe, iframe->payloadleft - readlen, readlen);67416742if (data_readlen == -1) {6743/* everything is padding */6744data_readlen = 0;6745}67466747trail_padlen = nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen);67486749final = (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) &&6750iframe->payloadleft - (size_t)data_readlen == trail_padlen;67516752if (data_readlen > 0 || (data_readlen == 0 && final)) {6753size_t hd_proclen = 0;67546755DEBUGF("recv: block final=%d\n", final);67566757rv =6758inflate_header_block(session, &iframe->frame, &hd_proclen,6759(uint8_t *)in, (size_t)data_readlen, final,6760iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK);67616762if (nghttp2_is_fatal(rv)) {6763return rv;6764}67656766if (iframe->state == NGHTTP2_IB_IGN_ALL) {6767return (ssize_t)inlen;6768}67696770if (rv == NGHTTP2_ERR_PAUSE) {6771in += hd_proclen;6772iframe->payloadleft -= hd_proclen;67736774return in - first;6775}67766777if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {6778/* The application says no more headers. We decompress the6779rest of the header block but not invoke on_header_callback6780and on_frame_recv_callback. */6781in += hd_proclen;6782iframe->payloadleft -= hd_proclen;67836784/* Use promised stream ID for PUSH_PROMISE */6785rv = nghttp2_session_add_rst_stream(6786session,6787iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE6788? iframe->frame.push_promise.promised_stream_id6789: iframe->frame.hd.stream_id,6790NGHTTP2_INTERNAL_ERROR);6791if (nghttp2_is_fatal(rv)) {6792return rv;6793}6794busy = 1;6795iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;6796break;6797}67986799in += readlen;6800iframe->payloadleft -= readlen;68016802if (rv == NGHTTP2_ERR_HEADER_COMP) {6803/* GOAWAY is already issued */6804if (iframe->payloadleft == 0) {6805session_inbound_frame_reset(session);6806} else {6807busy = 1;6808iframe->state = NGHTTP2_IB_IGN_PAYLOAD;6809}6810break;6811}6812} else {6813in += readlen;6814iframe->payloadleft -= readlen;6815}68166817if (iframe->payloadleft) {6818break;6819}68206821if ((iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) {68226823inbound_frame_set_mark(iframe, NGHTTP2_FRAME_HDLEN);68246825iframe->padlen = 0;68266827if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {6828iframe->state = NGHTTP2_IB_EXPECT_CONTINUATION;6829} else {6830iframe->state = NGHTTP2_IB_IGN_CONTINUATION;6831}6832} else {6833if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {6834rv = session_after_header_block_received(session);6835if (nghttp2_is_fatal(rv)) {6836return rv;6837}6838}6839session_inbound_frame_reset(session);6840}6841break;6842}6843case NGHTTP2_IB_IGN_PAYLOAD:6844DEBUGF("recv: [IB_IGN_PAYLOAD]\n");68456846readlen = inbound_frame_payload_readlen(iframe, in, last);6847iframe->payloadleft -= readlen;6848in += readlen;68496850DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,6851iframe->payloadleft);68526853if (iframe->payloadleft) {6854break;6855}68566857switch (iframe->frame.hd.type) {6858case NGHTTP2_HEADERS:6859case NGHTTP2_PUSH_PROMISE:6860case NGHTTP2_CONTINUATION:6861/* Mark inflater bad so that we won't perform further decoding */6862session->hd_inflater.ctx.bad = 1;6863break;6864default:6865break;6866}68676868session_inbound_frame_reset(session);68696870break;6871case NGHTTP2_IB_FRAME_SIZE_ERROR:6872DEBUGF("recv: [IB_FRAME_SIZE_ERROR]\n");68736874rv = session_handle_frame_size_error(session);6875if (nghttp2_is_fatal(rv)) {6876return rv;6877}68786879assert(iframe->state == NGHTTP2_IB_IGN_ALL);68806881return (ssize_t)inlen;6882case NGHTTP2_IB_READ_SETTINGS:6883DEBUGF("recv: [IB_READ_SETTINGS]\n");68846885readlen = inbound_frame_buf_read(iframe, in, last);6886iframe->payloadleft -= readlen;6887in += readlen;68886889DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,6890iframe->payloadleft);68916892if (nghttp2_buf_mark_avail(&iframe->sbuf)) {6893break;6894}68956896if (readlen > 0) {6897inbound_frame_set_settings_entry(iframe);6898}6899if (iframe->payloadleft) {6900inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH);6901break;6902}69036904rv = session_process_settings_frame(session);69056906if (nghttp2_is_fatal(rv)) {6907return rv;6908}69096910if (iframe->state == NGHTTP2_IB_IGN_ALL) {6911return (ssize_t)inlen;6912}69136914session_inbound_frame_reset(session);69156916break;6917case NGHTTP2_IB_READ_GOAWAY_DEBUG:6918DEBUGF("recv: [IB_READ_GOAWAY_DEBUG]\n");69196920readlen = inbound_frame_payload_readlen(iframe, in, last);69216922if (readlen > 0) {6923iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);69246925iframe->payloadleft -= readlen;6926in += readlen;6927}69286929DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,6930iframe->payloadleft);69316932if (iframe->payloadleft) {6933assert(nghttp2_buf_avail(&iframe->lbuf) > 0);69346935break;6936}69376938rv = session_process_goaway_frame(session);69396940if (nghttp2_is_fatal(rv)) {6941return rv;6942}69436944if (iframe->state == NGHTTP2_IB_IGN_ALL) {6945return (ssize_t)inlen;6946}69476948session_inbound_frame_reset(session);69496950break;6951case NGHTTP2_IB_EXPECT_CONTINUATION:6952case NGHTTP2_IB_IGN_CONTINUATION:6953#ifdef DEBUGBUILD6954if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) {6955fprintf(stderr, "recv: [IB_EXPECT_CONTINUATION]\n");6956} else {6957fprintf(stderr, "recv: [IB_IGN_CONTINUATION]\n");6958}6959#endif /* DEBUGBUILD */69606961readlen = inbound_frame_buf_read(iframe, in, last);6962in += readlen;69636964if (nghttp2_buf_mark_avail(&iframe->sbuf)) {6965return in - first;6966}69676968nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->sbuf.pos);6969iframe->payloadleft = cont_hd.length;69706971DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n",6972cont_hd.length, cont_hd.type, cont_hd.flags, cont_hd.stream_id);69736974if (cont_hd.type != NGHTTP2_CONTINUATION ||6975cont_hd.stream_id != iframe->frame.hd.stream_id) {6976DEBUGF("recv: expected stream_id=%d, type=%d, but got stream_id=%d, "6977"type=%u\n",6978iframe->frame.hd.stream_id, NGHTTP2_CONTINUATION,6979cont_hd.stream_id, cont_hd.type);6980rv = nghttp2_session_terminate_session_with_reason(6981session, NGHTTP2_PROTOCOL_ERROR,6982"unexpected non-CONTINUATION frame or stream_id is invalid");6983if (nghttp2_is_fatal(rv)) {6984return rv;6985}69866987return (ssize_t)inlen;6988}69896990/* CONTINUATION won't bear NGHTTP2_PADDED flag */69916992iframe->frame.hd.flags =6993(uint8_t)(iframe->frame.hd.flags |6994(cont_hd.flags & NGHTTP2_FLAG_END_HEADERS));6995iframe->frame.hd.length += cont_hd.length;69966997busy = 1;69986999if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) {7000iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;70017002rv = session_call_on_begin_frame(session, &cont_hd);70037004if (nghttp2_is_fatal(rv)) {7005return rv;7006}7007} else {7008iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;7009}70107011break;7012case NGHTTP2_IB_READ_PAD_DATA:7013DEBUGF("recv: [IB_READ_PAD_DATA]\n");70147015readlen = inbound_frame_buf_read(iframe, in, last);7016in += readlen;7017iframe->payloadleft -= readlen;70187019DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zu\n", readlen,7020iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf));70217022if (nghttp2_buf_mark_avail(&iframe->sbuf)) {7023return in - first;7024}70257026/* Pad Length field is subject to flow control */7027rv = nghttp2_session_update_recv_connection_window_size(session, readlen);7028if (nghttp2_is_fatal(rv)) {7029return rv;7030}70317032if (iframe->state == NGHTTP2_IB_IGN_ALL) {7033return (ssize_t)inlen;7034}70357036/* Pad Length field is consumed immediately */7037rv =7038nghttp2_session_consume(session, iframe->frame.hd.stream_id, readlen);70397040if (nghttp2_is_fatal(rv)) {7041return rv;7042}70437044if (iframe->state == NGHTTP2_IB_IGN_ALL) {7045return (ssize_t)inlen;7046}70477048stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id);7049if (stream) {7050rv = nghttp2_session_update_recv_stream_window_size(7051session, stream, readlen,7052iframe->payloadleft ||7053(iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0);7054if (nghttp2_is_fatal(rv)) {7055return rv;7056}7057}70587059busy = 1;70607061padlen = inbound_frame_compute_pad(iframe);7062if (padlen < 0) {7063rv = nghttp2_session_terminate_session_with_reason(7064session, NGHTTP2_PROTOCOL_ERROR, "DATA: invalid padding");7065if (nghttp2_is_fatal(rv)) {7066return rv;7067}7068return (ssize_t)inlen;7069}70707071iframe->frame.data.padlen = (size_t)padlen;70727073iframe->state = NGHTTP2_IB_READ_DATA;70747075break;7076case NGHTTP2_IB_READ_DATA:7077stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id);70787079if (!stream) {7080busy = 1;7081iframe->state = NGHTTP2_IB_IGN_DATA;7082break;7083}70847085DEBUGF("recv: [IB_READ_DATA]\n");70867087readlen = inbound_frame_payload_readlen(iframe, in, last);7088iframe->payloadleft -= readlen;7089in += readlen;70907091DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,7092iframe->payloadleft);70937094if (readlen > 0) {7095ssize_t data_readlen;70967097rv = nghttp2_session_update_recv_connection_window_size(session,7098readlen);7099if (nghttp2_is_fatal(rv)) {7100return rv;7101}71027103if (iframe->state == NGHTTP2_IB_IGN_ALL) {7104return (ssize_t)inlen;7105}71067107rv = nghttp2_session_update_recv_stream_window_size(7108session, stream, readlen,7109iframe->payloadleft ||7110(iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0);7111if (nghttp2_is_fatal(rv)) {7112return rv;7113}71147115data_readlen = inbound_frame_effective_readlen(7116iframe, iframe->payloadleft, readlen);71177118if (data_readlen == -1) {7119/* everything is padding */7120data_readlen = 0;7121}71227123padlen = (ssize_t)readlen - data_readlen;71247125if (padlen > 0) {7126/* Padding is considered as "consumed" immediately */7127rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id,7128(size_t)padlen);71297130if (nghttp2_is_fatal(rv)) {7131return rv;7132}71337134if (iframe->state == NGHTTP2_IB_IGN_ALL) {7135return (ssize_t)inlen;7136}7137}71387139DEBUGF("recv: data_readlen=%zd\n", data_readlen);71407141if (data_readlen > 0) {7142if (session_enforce_http_messaging(session)) {7143if (nghttp2_http_on_data_chunk(stream, (size_t)data_readlen) != 0) {7144if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {7145/* Consume all data for connection immediately here */7146rv = session_update_connection_consumed_size(7147session, (size_t)data_readlen);71487149if (nghttp2_is_fatal(rv)) {7150return rv;7151}71527153if (iframe->state == NGHTTP2_IB_IGN_DATA) {7154return (ssize_t)inlen;7155}7156}71577158rv = nghttp2_session_add_rst_stream(7159session, iframe->frame.hd.stream_id, NGHTTP2_PROTOCOL_ERROR);7160if (nghttp2_is_fatal(rv)) {7161return rv;7162}7163busy = 1;7164iframe->state = NGHTTP2_IB_IGN_DATA;7165break;7166}7167}7168if (session->callbacks.on_data_chunk_recv_callback) {7169rv = session->callbacks.on_data_chunk_recv_callback(7170session, iframe->frame.hd.flags, iframe->frame.hd.stream_id,7171in - readlen, (size_t)data_readlen, session->user_data);7172if (rv == NGHTTP2_ERR_PAUSE) {7173return in - first;7174}71757176if (nghttp2_is_fatal(rv)) {7177return NGHTTP2_ERR_CALLBACK_FAILURE;7178}7179}7180}7181}71827183if (iframe->payloadleft) {7184break;7185}71867187rv = session_process_data_frame(session);7188if (nghttp2_is_fatal(rv)) {7189return rv;7190}71917192session_inbound_frame_reset(session);71937194break;7195case NGHTTP2_IB_IGN_DATA:7196DEBUGF("recv: [IB_IGN_DATA]\n");71977198readlen = inbound_frame_payload_readlen(iframe, in, last);7199iframe->payloadleft -= readlen;7200in += readlen;72017202DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,7203iframe->payloadleft);72047205if (readlen > 0) {7206/* Update connection-level flow control window for ignored7207DATA frame too */7208rv = nghttp2_session_update_recv_connection_window_size(session,7209readlen);7210if (nghttp2_is_fatal(rv)) {7211return rv;7212}72137214if (iframe->state == NGHTTP2_IB_IGN_ALL) {7215return (ssize_t)inlen;7216}72177218if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {72197220/* Ignored DATA is considered as "consumed" immediately. */7221rv = session_update_connection_consumed_size(session, readlen);72227223if (nghttp2_is_fatal(rv)) {7224return rv;7225}72267227if (iframe->state == NGHTTP2_IB_IGN_ALL) {7228return (ssize_t)inlen;7229}7230}7231}72327233if (iframe->payloadleft) {7234break;7235}72367237session_inbound_frame_reset(session);72387239break;7240case NGHTTP2_IB_IGN_ALL:7241return (ssize_t)inlen;7242case NGHTTP2_IB_READ_EXTENSION_PAYLOAD:7243DEBUGF("recv: [IB_READ_EXTENSION_PAYLOAD]\n");72447245readlen = inbound_frame_payload_readlen(iframe, in, last);7246iframe->payloadleft -= readlen;7247in += readlen;72487249DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,7250iframe->payloadleft);72517252if (readlen > 0) {7253rv = session_call_on_extension_chunk_recv_callback(7254session, in - readlen, readlen);7255if (nghttp2_is_fatal(rv)) {7256return rv;7257}72587259if (rv != 0) {7260busy = 1;72617262iframe->state = NGHTTP2_IB_IGN_PAYLOAD;72637264break;7265}7266}72677268if (iframe->payloadleft > 0) {7269break;7270}72717272rv = session_process_extension_frame(session);7273if (nghttp2_is_fatal(rv)) {7274return rv;7275}72767277session_inbound_frame_reset(session);72787279break;7280case NGHTTP2_IB_READ_ALTSVC_PAYLOAD:7281DEBUGF("recv: [IB_READ_ALTSVC_PAYLOAD]\n");72827283readlen = inbound_frame_payload_readlen(iframe, in, last);7284if (readlen > 0) {7285iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);72867287iframe->payloadleft -= readlen;7288in += readlen;7289}72907291DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,7292iframe->payloadleft);72937294if (iframe->payloadleft) {7295assert(nghttp2_buf_avail(&iframe->lbuf) > 0);72967297break;7298}72997300rv = session_process_altsvc_frame(session);7301if (nghttp2_is_fatal(rv)) {7302return rv;7303}73047305session_inbound_frame_reset(session);73067307break;7308case NGHTTP2_IB_READ_ORIGIN_PAYLOAD:7309DEBUGF("recv: [IB_READ_ORIGIN_PAYLOAD]\n");73107311readlen = inbound_frame_payload_readlen(iframe, in, last);73127313if (readlen > 0) {7314iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);73157316iframe->payloadleft -= readlen;7317in += readlen;7318}73197320DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,7321iframe->payloadleft);73227323if (iframe->payloadleft) {7324assert(nghttp2_buf_avail(&iframe->lbuf) > 0);73257326break;7327}73287329rv = session_process_origin_frame(session);73307331if (nghttp2_is_fatal(rv)) {7332return rv;7333}73347335if (iframe->state == NGHTTP2_IB_IGN_ALL) {7336return (ssize_t)inlen;7337}73387339session_inbound_frame_reset(session);73407341break;7342}73437344if (!busy && in == last) {7345break;7346}73477348busy = 0;7349}73507351assert(in == last);73527353return in - first;7354}73557356int nghttp2_session_recv(nghttp2_session *session) {7357uint8_t buf[NGHTTP2_INBOUND_BUFFER_LENGTH];7358while (1) {7359ssize_t readlen;7360readlen = session_recv(session, buf, sizeof(buf));7361if (readlen > 0) {7362ssize_t proclen = nghttp2_session_mem_recv(session, buf, (size_t)readlen);7363if (proclen < 0) {7364return (int)proclen;7365}7366assert(proclen == readlen);7367} else if (readlen == 0 || readlen == NGHTTP2_ERR_WOULDBLOCK) {7368return 0;7369} else if (readlen == NGHTTP2_ERR_EOF) {7370return NGHTTP2_ERR_EOF;7371} else if (readlen < 0) {7372return NGHTTP2_ERR_CALLBACK_FAILURE;7373}7374}7375}73767377/*7378* Returns the number of active streams, which includes streams in7379* reserved state.7380*/7381static size_t session_get_num_active_streams(nghttp2_session *session) {7382return nghttp2_map_size(&session->streams) - session->num_closed_streams -7383session->num_idle_streams;7384}73857386int nghttp2_session_want_read(nghttp2_session *session) {7387size_t num_active_streams;73887389/* If this flag is set, we don't want to read. The application7390should drop the connection. */7391if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) {7392return 0;7393}73947395num_active_streams = session_get_num_active_streams(session);73967397/* Unless termination GOAWAY is sent or received, we always want to7398read incoming frames. */73997400if (num_active_streams > 0) {7401return 1;7402}74037404/* If there is no active streams and GOAWAY has been sent or7405received, we are done with this session. */7406return (session->goaway_flags &7407(NGHTTP2_GOAWAY_SENT | NGHTTP2_GOAWAY_RECV)) == 0;7408}74097410int nghttp2_session_want_write(nghttp2_session *session) {7411/* If these flag is set, we don't want to write any data. The7412application should drop the connection. */7413if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) {7414return 0;7415}74167417/*7418* Unless termination GOAWAY is sent or received, we want to write7419* frames if there is pending ones. If pending frame is request/push7420* response HEADERS and concurrent stream limit is reached, we don't7421* want to write them.7422*/7423return session->aob.item || nghttp2_outbound_queue_top(&session->ob_urgent) ||7424nghttp2_outbound_queue_top(&session->ob_reg) ||7425((!nghttp2_pq_empty(&session->root.obq) ||7426!session_sched_empty(session)) &&7427session->remote_window_size > 0) ||7428(nghttp2_outbound_queue_top(&session->ob_syn) &&7429!session_is_outgoing_concurrent_streams_max(session));7430}74317432int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,7433const uint8_t *opaque_data) {7434int rv;7435nghttp2_outbound_item *item;7436nghttp2_frame *frame;7437nghttp2_mem *mem;74387439mem = &session->mem;74407441if ((flags & NGHTTP2_FLAG_ACK) &&7442session->obq_flood_counter_ >= session->max_outbound_ack) {7443return NGHTTP2_ERR_FLOODED;7444}74457446item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));7447if (item == NULL) {7448return NGHTTP2_ERR_NOMEM;7449}74507451nghttp2_outbound_item_init(item);74527453frame = &item->frame;74547455nghttp2_frame_ping_init(&frame->ping, flags, opaque_data);74567457rv = nghttp2_session_add_item(session, item);74587459if (rv != 0) {7460nghttp2_frame_ping_free(&frame->ping);7461nghttp2_mem_free(mem, item);7462return rv;7463}74647465if (flags & NGHTTP2_FLAG_ACK) {7466++session->obq_flood_counter_;7467}74687469return 0;7470}74717472int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id,7473uint32_t error_code, const uint8_t *opaque_data,7474size_t opaque_data_len, uint8_t aux_flags) {7475int rv;7476nghttp2_outbound_item *item;7477nghttp2_frame *frame;7478uint8_t *opaque_data_copy = NULL;7479nghttp2_goaway_aux_data *aux_data;7480nghttp2_mem *mem;74817482mem = &session->mem;74837484if (nghttp2_session_is_my_stream_id(session, last_stream_id)) {7485return NGHTTP2_ERR_INVALID_ARGUMENT;7486}74877488if (opaque_data_len) {7489if (opaque_data_len + 8 > NGHTTP2_MAX_PAYLOADLEN) {7490return NGHTTP2_ERR_INVALID_ARGUMENT;7491}7492opaque_data_copy = nghttp2_mem_malloc(mem, opaque_data_len);7493if (opaque_data_copy == NULL) {7494return NGHTTP2_ERR_NOMEM;7495}7496memcpy(opaque_data_copy, opaque_data, opaque_data_len);7497}74987499item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));7500if (item == NULL) {7501nghttp2_mem_free(mem, opaque_data_copy);7502return NGHTTP2_ERR_NOMEM;7503}75047505nghttp2_outbound_item_init(item);75067507frame = &item->frame;75087509/* last_stream_id must not be increased from the value previously7510sent */7511last_stream_id = nghttp2_min(last_stream_id, session->local_last_stream_id);75127513nghttp2_frame_goaway_init(&frame->goaway, last_stream_id, error_code,7514opaque_data_copy, opaque_data_len);75157516aux_data = &item->aux_data.goaway;7517aux_data->flags = aux_flags;75187519rv = nghttp2_session_add_item(session, item);7520if (rv != 0) {7521nghttp2_frame_goaway_free(&frame->goaway, mem);7522nghttp2_mem_free(mem, item);7523return rv;7524}7525return 0;7526}75277528int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags,7529int32_t stream_id,7530int32_t window_size_increment) {7531int rv;7532nghttp2_outbound_item *item;7533nghttp2_frame *frame;7534nghttp2_mem *mem;75357536mem = &session->mem;7537item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));7538if (item == NULL) {7539return NGHTTP2_ERR_NOMEM;7540}75417542nghttp2_outbound_item_init(item);75437544frame = &item->frame;75457546nghttp2_frame_window_update_init(&frame->window_update, flags, stream_id,7547window_size_increment);75487549rv = nghttp2_session_add_item(session, item);75507551if (rv != 0) {7552nghttp2_frame_window_update_free(&frame->window_update);7553nghttp2_mem_free(mem, item);7554return rv;7555}7556return 0;7557}75587559static void7560session_append_inflight_settings(nghttp2_session *session,7561nghttp2_inflight_settings *settings) {7562nghttp2_inflight_settings **i;75637564for (i = &session->inflight_settings_head; *i; i = &(*i)->next)7565;75667567*i = settings;7568}75697570int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,7571const nghttp2_settings_entry *iv, size_t niv) {7572nghttp2_outbound_item *item;7573nghttp2_frame *frame;7574nghttp2_settings_entry *iv_copy;7575size_t i;7576int rv;7577nghttp2_mem *mem;7578nghttp2_inflight_settings *inflight_settings = NULL;7579uint8_t no_rfc7540_pri = session->pending_no_rfc7540_priorities;75807581mem = &session->mem;75827583if (flags & NGHTTP2_FLAG_ACK) {7584if (niv != 0) {7585return NGHTTP2_ERR_INVALID_ARGUMENT;7586}75877588if (session->obq_flood_counter_ >= session->max_outbound_ack) {7589return NGHTTP2_ERR_FLOODED;7590}7591}75927593if (!nghttp2_iv_check(iv, niv)) {7594return NGHTTP2_ERR_INVALID_ARGUMENT;7595}75967597for (i = 0; i < niv; ++i) {7598if (iv[i].settings_id != NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES) {7599continue;7600}76017602if (no_rfc7540_pri == UINT8_MAX) {7603no_rfc7540_pri = (uint8_t)iv[i].value;7604continue;7605}76067607if (iv[i].value != (uint32_t)no_rfc7540_pri) {7608return NGHTTP2_ERR_INVALID_ARGUMENT;7609}7610}76117612item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));7613if (item == NULL) {7614return NGHTTP2_ERR_NOMEM;7615}76167617if (niv > 0) {7618iv_copy = nghttp2_frame_iv_copy(iv, niv, mem);7619if (iv_copy == NULL) {7620nghttp2_mem_free(mem, item);7621return NGHTTP2_ERR_NOMEM;7622}7623} else {7624iv_copy = NULL;7625}76267627if ((flags & NGHTTP2_FLAG_ACK) == 0) {7628rv = inflight_settings_new(&inflight_settings, iv, niv, mem);7629if (rv != 0) {7630assert(nghttp2_is_fatal(rv));7631nghttp2_mem_free(mem, iv_copy);7632nghttp2_mem_free(mem, item);7633return rv;7634}7635}76367637nghttp2_outbound_item_init(item);76387639frame = &item->frame;76407641nghttp2_frame_settings_init(&frame->settings, flags, iv_copy, niv);7642rv = nghttp2_session_add_item(session, item);7643if (rv != 0) {7644/* The only expected error is fatal one */7645assert(nghttp2_is_fatal(rv));76467647inflight_settings_del(inflight_settings, mem);76487649nghttp2_frame_settings_free(&frame->settings, mem);7650nghttp2_mem_free(mem, item);76517652return rv;7653}76547655if (flags & NGHTTP2_FLAG_ACK) {7656++session->obq_flood_counter_;7657} else {7658session_append_inflight_settings(session, inflight_settings);7659}76607661/* Extract NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS and ENABLE_PUSH7662here. We use it to refuse the incoming stream and PUSH_PROMISE7663with RST_STREAM. */76647665for (i = niv; i > 0; --i) {7666if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) {7667session->pending_local_max_concurrent_stream = iv[i - 1].value;7668break;7669}7670}76717672for (i = niv; i > 0; --i) {7673if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_PUSH) {7674session->pending_enable_push = (uint8_t)iv[i - 1].value;7675break;7676}7677}76787679for (i = niv; i > 0; --i) {7680if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL) {7681session->pending_enable_connect_protocol = (uint8_t)iv[i - 1].value;7682break;7683}7684}76857686if (no_rfc7540_pri == UINT8_MAX) {7687session->pending_no_rfc7540_priorities = 0;7688} else {7689session->pending_no_rfc7540_priorities = no_rfc7540_pri;7690}76917692return 0;7693}76947695int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,7696size_t datamax, nghttp2_frame *frame,7697nghttp2_data_aux_data *aux_data,7698nghttp2_stream *stream) {7699int rv;7700uint32_t data_flags;7701ssize_t payloadlen;7702ssize_t padded_payloadlen;7703nghttp2_buf *buf;7704size_t max_payloadlen;77057706assert(bufs->head == bufs->cur);77077708buf = &bufs->cur->buf;77097710if (session->callbacks.read_length_callback) {77117712payloadlen = session->callbacks.read_length_callback(7713session, frame->hd.type, stream->stream_id, session->remote_window_size,7714stream->remote_window_size, session->remote_settings.max_frame_size,7715session->user_data);77167717DEBUGF("send: read_length_callback=%zd\n", payloadlen);77187719payloadlen = nghttp2_session_enforce_flow_control_limits(session, stream,7720payloadlen);77217722DEBUGF("send: read_length_callback after flow control=%zd\n", payloadlen);77237724if (payloadlen <= 0) {7725return NGHTTP2_ERR_CALLBACK_FAILURE;7726}77277728if ((size_t)payloadlen > nghttp2_buf_avail(buf)) {7729/* Resize the current buffer(s). The reason why we do +1 for7730buffer size is for possible padding field. */7731rv = nghttp2_bufs_realloc(&session->aob.framebufs,7732(size_t)(NGHTTP2_FRAME_HDLEN + 1 + payloadlen));77337734if (rv != 0) {7735DEBUGF("send: realloc buffer failed rv=%d", rv);7736/* If reallocation failed, old buffers are still in tact. So7737use safe limit. */7738payloadlen = (ssize_t)datamax;77397740DEBUGF("send: use safe limit payloadlen=%zd", payloadlen);7741} else {7742assert(&session->aob.framebufs == bufs);77437744buf = &bufs->cur->buf;7745}7746}7747datamax = (size_t)payloadlen;7748}77497750/* Current max DATA length is less then buffer chunk size */7751assert(nghttp2_buf_avail(buf) >= datamax);77527753data_flags = NGHTTP2_DATA_FLAG_NONE;7754payloadlen = aux_data->data_prd.read_callback(7755session, frame->hd.stream_id, buf->pos, datamax, &data_flags,7756&aux_data->data_prd.source, session->user_data);77577758if (payloadlen == NGHTTP2_ERR_DEFERRED ||7759payloadlen == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE ||7760payloadlen == NGHTTP2_ERR_PAUSE) {7761DEBUGF("send: DATA postponed due to %s\n",7762nghttp2_strerror((int)payloadlen));77637764return (int)payloadlen;7765}77667767if (payloadlen < 0 || datamax < (size_t)payloadlen) {7768/* This is the error code when callback is failed. */7769return NGHTTP2_ERR_CALLBACK_FAILURE;7770}77717772buf->last = buf->pos + payloadlen;7773buf->pos -= NGHTTP2_FRAME_HDLEN;77747775/* Clear flags, because this may contain previous flags of previous7776DATA */7777frame->hd.flags = NGHTTP2_FLAG_NONE;77787779if (data_flags & NGHTTP2_DATA_FLAG_EOF) {7780aux_data->eof = 1;7781/* If NGHTTP2_DATA_FLAG_NO_END_STREAM is set, don't set7782NGHTTP2_FLAG_END_STREAM */7783if ((aux_data->flags & NGHTTP2_FLAG_END_STREAM) &&7784(data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) {7785frame->hd.flags |= NGHTTP2_FLAG_END_STREAM;7786}7787}77887789if (data_flags & NGHTTP2_DATA_FLAG_NO_COPY) {7790if (session->callbacks.send_data_callback == NULL) {7791DEBUGF("NGHTTP2_DATA_FLAG_NO_COPY requires send_data_callback set\n");77927793return NGHTTP2_ERR_CALLBACK_FAILURE;7794}7795aux_data->no_copy = 1;7796}77977798frame->hd.length = (size_t)payloadlen;7799frame->data.padlen = 0;78007801max_payloadlen = nghttp2_min(datamax, frame->hd.length + NGHTTP2_MAX_PADLEN);78027803padded_payloadlen =7804session_call_select_padding(session, frame, max_payloadlen);78057806if (nghttp2_is_fatal((int)padded_payloadlen)) {7807return (int)padded_payloadlen;7808}78097810frame->data.padlen = (size_t)(padded_payloadlen - payloadlen);78117812nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);78137814rv = nghttp2_frame_add_pad(bufs, &frame->hd, frame->data.padlen,7815aux_data->no_copy);7816if (rv != 0) {7817return rv;7818}78197820session_reschedule_stream(session, stream);78217822if (frame->hd.length == 0 && (data_flags & NGHTTP2_DATA_FLAG_EOF) &&7823(data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM)) {7824/* DATA payload length is 0, and DATA frame does not bear7825END_STREAM. In this case, there is no point to send 0 length7826DATA frame. */7827return NGHTTP2_ERR_CANCEL;7828}78297830return 0;7831}78327833void *nghttp2_session_get_stream_user_data(nghttp2_session *session,7834int32_t stream_id) {7835nghttp2_stream *stream;7836stream = nghttp2_session_get_stream(session, stream_id);7837if (stream) {7838return stream->stream_user_data;7839} else {7840return NULL;7841}7842}78437844int nghttp2_session_set_stream_user_data(nghttp2_session *session,7845int32_t stream_id,7846void *stream_user_data) {7847nghttp2_stream *stream;7848nghttp2_frame *frame;7849nghttp2_outbound_item *item;78507851stream = nghttp2_session_get_stream(session, stream_id);7852if (stream) {7853stream->stream_user_data = stream_user_data;7854return 0;7855}78567857if (session->server || !nghttp2_session_is_my_stream_id(session, stream_id) ||7858!nghttp2_outbound_queue_top(&session->ob_syn)) {7859return NGHTTP2_ERR_INVALID_ARGUMENT;7860}78617862frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame;7863assert(frame->hd.type == NGHTTP2_HEADERS);78647865if (frame->hd.stream_id > stream_id ||7866(uint32_t)stream_id >= session->next_stream_id) {7867return NGHTTP2_ERR_INVALID_ARGUMENT;7868}78697870for (item = session->ob_syn.head; item; item = item->qnext) {7871if (item->frame.hd.stream_id < stream_id) {7872continue;7873}78747875if (item->frame.hd.stream_id > stream_id) {7876break;7877}78787879item->aux_data.headers.stream_user_data = stream_user_data;7880return 0;7881}78827883return NGHTTP2_ERR_INVALID_ARGUMENT;7884}78857886int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) {7887int rv;7888nghttp2_stream *stream;7889stream = nghttp2_session_get_stream(session, stream_id);7890if (stream == NULL || !nghttp2_stream_check_deferred_item(stream)) {7891return NGHTTP2_ERR_INVALID_ARGUMENT;7892}78937894rv = session_resume_deferred_stream_item(session, stream,7895NGHTTP2_STREAM_FLAG_DEFERRED_USER);78967897if (nghttp2_is_fatal(rv)) {7898return rv;7899}79007901return 0;7902}79037904size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) {7905return nghttp2_outbound_queue_size(&session->ob_urgent) +7906nghttp2_outbound_queue_size(&session->ob_reg) +7907nghttp2_outbound_queue_size(&session->ob_syn);7908/* TODO account for item attached to stream */7909}79107911int32_t7912nghttp2_session_get_stream_effective_recv_data_length(nghttp2_session *session,7913int32_t stream_id) {7914nghttp2_stream *stream;7915stream = nghttp2_session_get_stream(session, stream_id);7916if (stream == NULL) {7917return -1;7918}7919return stream->recv_window_size < 0 ? 0 : stream->recv_window_size;7920}79217922int32_t7923nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session,7924int32_t stream_id) {7925nghttp2_stream *stream;7926stream = nghttp2_session_get_stream(session, stream_id);7927if (stream == NULL) {7928return -1;7929}7930return stream->local_window_size;7931}79327933int32_t nghttp2_session_get_stream_local_window_size(nghttp2_session *session,7934int32_t stream_id) {7935nghttp2_stream *stream;7936int32_t size;7937stream = nghttp2_session_get_stream(session, stream_id);7938if (stream == NULL) {7939return -1;7940}79417942size = stream->local_window_size - stream->recv_window_size;79437944/* size could be negative if local endpoint reduced7945SETTINGS_INITIAL_WINDOW_SIZE */7946if (size < 0) {7947return 0;7948}79497950return size;7951}79527953int32_t7954nghttp2_session_get_effective_recv_data_length(nghttp2_session *session) {7955return session->recv_window_size < 0 ? 0 : session->recv_window_size;7956}79577958int32_t7959nghttp2_session_get_effective_local_window_size(nghttp2_session *session) {7960return session->local_window_size;7961}79627963int32_t nghttp2_session_get_local_window_size(nghttp2_session *session) {7964return session->local_window_size - session->recv_window_size;7965}79667967int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session,7968int32_t stream_id) {7969nghttp2_stream *stream;79707971stream = nghttp2_session_get_stream(session, stream_id);7972if (stream == NULL) {7973return -1;7974}79757976/* stream->remote_window_size can be negative when7977SETTINGS_INITIAL_WINDOW_SIZE is changed. */7978return nghttp2_max(0, stream->remote_window_size);7979}79807981int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session) {7982return session->remote_window_size;7983}79847985uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,7986nghttp2_settings_id id) {7987switch (id) {7988case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:7989return session->remote_settings.header_table_size;7990case NGHTTP2_SETTINGS_ENABLE_PUSH:7991return session->remote_settings.enable_push;7992case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:7993return session->remote_settings.max_concurrent_streams;7994case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:7995return session->remote_settings.initial_window_size;7996case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:7997return session->remote_settings.max_frame_size;7998case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:7999return session->remote_settings.max_header_list_size;8000case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:8001return session->remote_settings.enable_connect_protocol;8002case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:8003return session->remote_settings.no_rfc7540_priorities;8004}80058006assert(0);8007abort(); /* if NDEBUG is set */8008}80098010uint32_t nghttp2_session_get_local_settings(nghttp2_session *session,8011nghttp2_settings_id id) {8012switch (id) {8013case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:8014return session->local_settings.header_table_size;8015case NGHTTP2_SETTINGS_ENABLE_PUSH:8016return session->local_settings.enable_push;8017case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:8018return session->local_settings.max_concurrent_streams;8019case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:8020return session->local_settings.initial_window_size;8021case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:8022return session->local_settings.max_frame_size;8023case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:8024return session->local_settings.max_header_list_size;8025case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:8026return session->local_settings.enable_connect_protocol;8027case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:8028return session->local_settings.no_rfc7540_priorities;8029}80308031assert(0);8032abort(); /* if NDEBUG is set */8033}80348035static int nghttp2_session_upgrade_internal(nghttp2_session *session,8036const uint8_t *settings_payload,8037size_t settings_payloadlen,8038void *stream_user_data) {8039nghttp2_stream *stream;8040nghttp2_frame frame;8041nghttp2_settings_entry *iv;8042size_t niv;8043int rv;8044nghttp2_priority_spec pri_spec;8045nghttp2_mem *mem;80468047mem = &session->mem;80488049if ((!session->server && session->next_stream_id != 1) ||8050(session->server && session->last_recv_stream_id >= 1)) {8051return NGHTTP2_ERR_PROTO;8052}8053if (settings_payloadlen % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) {8054return NGHTTP2_ERR_INVALID_ARGUMENT;8055}8056/* SETTINGS frame contains too many settings */8057if (settings_payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH >8058session->max_settings) {8059return NGHTTP2_ERR_TOO_MANY_SETTINGS;8060}8061rv = nghttp2_frame_unpack_settings_payload2(&iv, &niv, settings_payload,8062settings_payloadlen, mem);8063if (rv != 0) {8064return rv;8065}80668067if (session->server) {8068nghttp2_frame_hd_init(&frame.hd, settings_payloadlen, NGHTTP2_SETTINGS,8069NGHTTP2_FLAG_NONE, 0);8070frame.settings.iv = iv;8071frame.settings.niv = niv;8072rv = nghttp2_session_on_settings_received(session, &frame, 1 /* No ACK */);8073} else {8074rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, niv);8075}8076nghttp2_mem_free(mem, iv);8077if (rv != 0) {8078return rv;8079}80808081nghttp2_priority_spec_default_init(&pri_spec);80828083stream = nghttp2_session_open_stream(8084session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_OPENING,8085session->server ? NULL : stream_user_data);8086if (stream == NULL) {8087return NGHTTP2_ERR_NOMEM;8088}80898090/* We don't call nghttp2_session_adjust_closed_stream(), since this8091should be the first stream open. */80928093if (session->server) {8094nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);8095session->last_recv_stream_id = 1;8096session->last_proc_stream_id = 1;8097} else {8098nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);8099session->last_sent_stream_id = 1;8100session->next_stream_id += 2;8101}8102return 0;8103}81048105int nghttp2_session_upgrade(nghttp2_session *session,8106const uint8_t *settings_payload,8107size_t settings_payloadlen,8108void *stream_user_data) {8109int rv;8110nghttp2_stream *stream;81118112rv = nghttp2_session_upgrade_internal(session, settings_payload,8113settings_payloadlen, stream_user_data);8114if (rv != 0) {8115return rv;8116}81178118stream = nghttp2_session_get_stream(session, 1);8119assert(stream);81208121/* We have no information about request header fields when Upgrade8122was happened. So we don't know the request method here. If8123request method is HEAD, we have a trouble because we may have8124nonzero content-length header field in response headers, and we8125will going to check it against the actual DATA frames, but we may8126get mismatch because HEAD response body must be empty. Because8127of this reason, nghttp2_session_upgrade() was deprecated in favor8128of nghttp2_session_upgrade2(), which has |head_request| parameter8129to indicate that request method is HEAD or not. */8130stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND;8131return 0;8132}81338134int nghttp2_session_upgrade2(nghttp2_session *session,8135const uint8_t *settings_payload,8136size_t settings_payloadlen, int head_request,8137void *stream_user_data) {8138int rv;8139nghttp2_stream *stream;81408141rv = nghttp2_session_upgrade_internal(session, settings_payload,8142settings_payloadlen, stream_user_data);8143if (rv != 0) {8144return rv;8145}81468147stream = nghttp2_session_get_stream(session, 1);8148assert(stream);81498150if (head_request) {8151stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;8152}81538154return 0;8155}81568157int nghttp2_session_get_stream_local_close(nghttp2_session *session,8158int32_t stream_id) {8159nghttp2_stream *stream;81608161stream = nghttp2_session_get_stream(session, stream_id);81628163if (!stream) {8164return -1;8165}81668167return (stream->shut_flags & NGHTTP2_SHUT_WR) != 0;8168}81698170int nghttp2_session_get_stream_remote_close(nghttp2_session *session,8171int32_t stream_id) {8172nghttp2_stream *stream;81738174stream = nghttp2_session_get_stream(session, stream_id);81758176if (!stream) {8177return -1;8178}81798180return (stream->shut_flags & NGHTTP2_SHUT_RD) != 0;8181}81828183int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id,8184size_t size) {8185int rv;8186nghttp2_stream *stream;81878188if (stream_id == 0) {8189return NGHTTP2_ERR_INVALID_ARGUMENT;8190}81918192if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {8193return NGHTTP2_ERR_INVALID_STATE;8194}81958196rv = session_update_connection_consumed_size(session, size);81978198if (nghttp2_is_fatal(rv)) {8199return rv;8200}82018202stream = nghttp2_session_get_stream(session, stream_id);82038204if (!stream) {8205return 0;8206}82078208rv = session_update_stream_consumed_size(session, stream, size);82098210if (nghttp2_is_fatal(rv)) {8211return rv;8212}82138214return 0;8215}82168217int nghttp2_session_consume_connection(nghttp2_session *session, size_t size) {8218int rv;82198220if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {8221return NGHTTP2_ERR_INVALID_STATE;8222}82238224rv = session_update_connection_consumed_size(session, size);82258226if (nghttp2_is_fatal(rv)) {8227return rv;8228}82298230return 0;8231}82328233int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id,8234size_t size) {8235int rv;8236nghttp2_stream *stream;82378238if (stream_id == 0) {8239return NGHTTP2_ERR_INVALID_ARGUMENT;8240}82418242if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {8243return NGHTTP2_ERR_INVALID_STATE;8244}82458246stream = nghttp2_session_get_stream(session, stream_id);82478248if (!stream) {8249return 0;8250}82518252rv = session_update_stream_consumed_size(session, stream, size);82538254if (nghttp2_is_fatal(rv)) {8255return rv;8256}82578258return 0;8259}82608261int nghttp2_session_set_next_stream_id(nghttp2_session *session,8262int32_t next_stream_id) {8263if (next_stream_id <= 0 ||8264session->next_stream_id > (uint32_t)next_stream_id) {8265return NGHTTP2_ERR_INVALID_ARGUMENT;8266}82678268if (session->server) {8269if (next_stream_id % 2) {8270return NGHTTP2_ERR_INVALID_ARGUMENT;8271}8272} else if (next_stream_id % 2 == 0) {8273return NGHTTP2_ERR_INVALID_ARGUMENT;8274}82758276session->next_stream_id = (uint32_t)next_stream_id;8277return 0;8278}82798280uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session) {8281return session->next_stream_id;8282}82838284int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session) {8285return session->last_proc_stream_id;8286}82878288nghttp2_stream *nghttp2_session_find_stream(nghttp2_session *session,8289int32_t stream_id) {8290if (stream_id == 0) {8291return &session->root;8292}82938294return nghttp2_session_get_stream_raw(session, stream_id);8295}82968297nghttp2_stream *nghttp2_session_get_root_stream(nghttp2_session *session) {8298return &session->root;8299}83008301int nghttp2_session_check_server_session(nghttp2_session *session) {8302return session->server;8303}83048305int nghttp2_session_change_stream_priority(8306nghttp2_session *session, int32_t stream_id,8307const nghttp2_priority_spec *pri_spec) {8308int rv;8309nghttp2_stream *stream;8310nghttp2_priority_spec pri_spec_copy;83118312if (session->pending_no_rfc7540_priorities == 1) {8313return 0;8314}83158316if (stream_id == 0 || stream_id == pri_spec->stream_id) {8317return NGHTTP2_ERR_INVALID_ARGUMENT;8318}83198320stream = nghttp2_session_get_stream_raw(session, stream_id);8321if (!stream) {8322return NGHTTP2_ERR_INVALID_ARGUMENT;8323}83248325pri_spec_copy = *pri_spec;8326nghttp2_priority_spec_normalize_weight(&pri_spec_copy);83278328rv = nghttp2_session_reprioritize_stream(session, stream, &pri_spec_copy);83298330if (nghttp2_is_fatal(rv)) {8331return rv;8332}83338334/* We don't intentionally call nghttp2_session_adjust_idle_stream()8335so that idle stream created by this function, and existing ones8336are kept for application. We will adjust number of idle stream8337in nghttp2_session_mem_send or nghttp2_session_mem_recv is8338called. */8339return 0;8340}83418342int nghttp2_session_create_idle_stream(nghttp2_session *session,8343int32_t stream_id,8344const nghttp2_priority_spec *pri_spec) {8345nghttp2_stream *stream;8346nghttp2_priority_spec pri_spec_copy;83478348if (session->pending_no_rfc7540_priorities == 1) {8349return 0;8350}83518352if (stream_id == 0 || stream_id == pri_spec->stream_id ||8353!session_detect_idle_stream(session, stream_id)) {8354return NGHTTP2_ERR_INVALID_ARGUMENT;8355}83568357stream = nghttp2_session_get_stream_raw(session, stream_id);8358if (stream) {8359return NGHTTP2_ERR_INVALID_ARGUMENT;8360}83618362pri_spec_copy = *pri_spec;8363nghttp2_priority_spec_normalize_weight(&pri_spec_copy);83648365stream =8366nghttp2_session_open_stream(session, stream_id, NGHTTP2_STREAM_FLAG_NONE,8367&pri_spec_copy, NGHTTP2_STREAM_IDLE, NULL);8368if (!stream) {8369return NGHTTP2_ERR_NOMEM;8370}83718372/* We don't intentionally call nghttp2_session_adjust_idle_stream()8373so that idle stream created by this function, and existing ones8374are kept for application. We will adjust number of idle stream8375in nghttp2_session_mem_send or nghttp2_session_mem_recv is8376called. */8377return 0;8378}83798380size_t8381nghttp2_session_get_hd_inflate_dynamic_table_size(nghttp2_session *session) {8382return nghttp2_hd_inflate_get_dynamic_table_size(&session->hd_inflater);8383}83848385size_t8386nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session) {8387return nghttp2_hd_deflate_get_dynamic_table_size(&session->hd_deflater);8388}83898390void nghttp2_session_set_user_data(nghttp2_session *session, void *user_data) {8391session->user_data = user_data;8392}83938394int nghttp2_session_change_extpri_stream_priority(8395nghttp2_session *session, int32_t stream_id,8396const nghttp2_extpri *extpri_in, int ignore_client_signal) {8397nghttp2_stream *stream;8398nghttp2_extpri extpri = *extpri_in;83998400if (!session->server) {8401return NGHTTP2_ERR_INVALID_STATE;8402}84038404if (session->pending_no_rfc7540_priorities != 1) {8405return 0;8406}84078408if (stream_id == 0) {8409return NGHTTP2_ERR_INVALID_ARGUMENT;8410}84118412stream = nghttp2_session_get_stream_raw(session, stream_id);8413if (!stream) {8414return NGHTTP2_ERR_INVALID_ARGUMENT;8415}84168417if (extpri.urgency > NGHTTP2_EXTPRI_URGENCY_LOW) {8418extpri.urgency = NGHTTP2_EXTPRI_URGENCY_LOW;8419}84208421if (ignore_client_signal) {8422stream->flags |= NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES;8423}84248425return session_update_stream_priority(session, stream,8426nghttp2_extpri_to_uint8(&extpri));8427}842884298430