Path: blob/master/Utilities/cmnghttp2/lib/nghttp2_submit.c
3153 views
/*1* nghttp2 - HTTP/2 C Library2*3* Copyright (c) 2012, 2013 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_submit.h"2526#include <string.h>27#include <assert.h>2829#include "nghttp2_session.h"30#include "nghttp2_frame.h"31#include "nghttp2_helper.h"32#include "nghttp2_priority_spec.h"3334/*35* Detects the dependency error, that is stream attempted to depend on36* itself. If |stream_id| is -1, we use session->next_stream_id as37* stream ID.38*39* This function returns 0 if it succeeds, or one of the following40* error codes:41*42* NGHTTP2_ERR_INVALID_ARGUMENT43* Stream attempted to depend on itself.44*/45static int detect_self_dependency(nghttp2_session *session, int32_t stream_id,46const nghttp2_priority_spec *pri_spec) {47assert(pri_spec);4849if (stream_id == -1) {50if ((int32_t)session->next_stream_id == pri_spec->stream_id) {51return NGHTTP2_ERR_INVALID_ARGUMENT;52}53return 0;54}5556if (stream_id == pri_spec->stream_id) {57return NGHTTP2_ERR_INVALID_ARGUMENT;58}5960return 0;61}6263/* This function takes ownership of |nva_copy|. Regardless of the64return value, the caller must not free |nva_copy| after this65function returns. */66static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,67int32_t stream_id,68const nghttp2_priority_spec *pri_spec,69nghttp2_nv *nva_copy, size_t nvlen,70const nghttp2_data_provider *data_prd,71void *stream_user_data) {72int rv;73uint8_t flags_copy;74nghttp2_outbound_item *item = NULL;75nghttp2_frame *frame = NULL;76nghttp2_headers_category hcat;77nghttp2_mem *mem;7879mem = &session->mem;8081item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));82if (item == NULL) {83rv = NGHTTP2_ERR_NOMEM;84goto fail;85}8687nghttp2_outbound_item_init(item);8889if (data_prd != NULL && data_prd->read_callback != NULL) {90item->aux_data.headers.data_prd = *data_prd;91}9293item->aux_data.headers.stream_user_data = stream_user_data;9495flags_copy =96(uint8_t)((flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) |97NGHTTP2_FLAG_END_HEADERS);9899if (stream_id == -1) {100if (session->next_stream_id > INT32_MAX) {101rv = NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;102goto fail;103}104105stream_id = (int32_t)session->next_stream_id;106session->next_stream_id += 2;107108hcat = NGHTTP2_HCAT_REQUEST;109} else {110/* More specific categorization will be done later. */111hcat = NGHTTP2_HCAT_HEADERS;112}113114frame = &item->frame;115116nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat,117pri_spec, nva_copy, nvlen);118119rv = nghttp2_session_add_item(session, item);120121if (rv != 0) {122nghttp2_frame_headers_free(&frame->headers, mem);123goto fail2;124}125126if (hcat == NGHTTP2_HCAT_REQUEST) {127return stream_id;128}129130return 0;131132fail:133/* nghttp2_frame_headers_init() takes ownership of nva_copy. */134nghttp2_nv_array_del(nva_copy, mem);135fail2:136nghttp2_mem_free(mem, item);137138return rv;139}140141static int32_t submit_headers_shared_nva(nghttp2_session *session,142uint8_t flags, int32_t stream_id,143const nghttp2_priority_spec *pri_spec,144const nghttp2_nv *nva, size_t nvlen,145const nghttp2_data_provider *data_prd,146void *stream_user_data) {147int rv;148nghttp2_nv *nva_copy;149nghttp2_priority_spec copy_pri_spec;150nghttp2_mem *mem;151152mem = &session->mem;153154if (pri_spec) {155copy_pri_spec = *pri_spec;156nghttp2_priority_spec_normalize_weight(©_pri_spec);157} else {158nghttp2_priority_spec_default_init(©_pri_spec);159}160161rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem);162if (rv < 0) {163return rv;164}165166return submit_headers_shared(session, flags, stream_id, ©_pri_spec,167nva_copy, nvlen, data_prd, stream_user_data);168}169170int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,171const nghttp2_nv *nva, size_t nvlen) {172if (stream_id <= 0) {173return NGHTTP2_ERR_INVALID_ARGUMENT;174}175176return (int)submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM,177stream_id, NULL, nva, nvlen, NULL,178NULL);179}180181int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,182int32_t stream_id,183const nghttp2_priority_spec *pri_spec,184const nghttp2_nv *nva, size_t nvlen,185void *stream_user_data) {186int rv;187188if (stream_id == -1) {189if (session->server) {190return NGHTTP2_ERR_PROTO;191}192} else if (stream_id <= 0) {193return NGHTTP2_ERR_INVALID_ARGUMENT;194}195196flags &= NGHTTP2_FLAG_END_STREAM;197198if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec) &&199session->remote_settings.no_rfc7540_priorities != 1) {200rv = detect_self_dependency(session, stream_id, pri_spec);201if (rv != 0) {202return rv;203}204205flags |= NGHTTP2_FLAG_PRIORITY;206} else {207pri_spec = NULL;208}209210return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva,211nvlen, NULL, stream_user_data);212}213214int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,215const uint8_t *opaque_data) {216flags &= NGHTTP2_FLAG_ACK;217return nghttp2_session_add_ping(session, flags, opaque_data);218}219220int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,221int32_t stream_id,222const nghttp2_priority_spec *pri_spec) {223int rv;224nghttp2_outbound_item *item;225nghttp2_frame *frame;226nghttp2_priority_spec copy_pri_spec;227nghttp2_mem *mem;228(void)flags;229230mem = &session->mem;231232if (session->remote_settings.no_rfc7540_priorities == 1) {233return 0;234}235236if (stream_id == 0 || pri_spec == NULL) {237return NGHTTP2_ERR_INVALID_ARGUMENT;238}239240if (stream_id == pri_spec->stream_id) {241return NGHTTP2_ERR_INVALID_ARGUMENT;242}243244copy_pri_spec = *pri_spec;245246nghttp2_priority_spec_normalize_weight(©_pri_spec);247248item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));249250if (item == NULL) {251return NGHTTP2_ERR_NOMEM;252}253254nghttp2_outbound_item_init(item);255256frame = &item->frame;257258nghttp2_frame_priority_init(&frame->priority, stream_id, ©_pri_spec);259260rv = nghttp2_session_add_item(session, item);261262if (rv != 0) {263nghttp2_frame_priority_free(&frame->priority);264nghttp2_mem_free(mem, item);265266return rv;267}268269return 0;270}271272int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags,273int32_t stream_id, uint32_t error_code) {274(void)flags;275276if (stream_id == 0) {277return NGHTTP2_ERR_INVALID_ARGUMENT;278}279280return nghttp2_session_add_rst_stream(session, stream_id, error_code);281}282283int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags,284int32_t last_stream_id, uint32_t error_code,285const uint8_t *opaque_data, size_t opaque_data_len) {286(void)flags;287288if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {289return 0;290}291return nghttp2_session_add_goaway(session, last_stream_id, error_code,292opaque_data, opaque_data_len,293NGHTTP2_GOAWAY_AUX_NONE);294}295296int nghttp2_submit_shutdown_notice(nghttp2_session *session) {297if (!session->server) {298return NGHTTP2_ERR_INVALID_STATE;299}300if (session->goaway_flags) {301return 0;302}303return nghttp2_session_add_goaway(session, (1u << 31) - 1, NGHTTP2_NO_ERROR,304NULL, 0,305NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE);306}307308int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,309const nghttp2_settings_entry *iv, size_t niv) {310(void)flags;311return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv);312}313314int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,315int32_t stream_id, const nghttp2_nv *nva,316size_t nvlen,317void *promised_stream_user_data) {318nghttp2_outbound_item *item;319nghttp2_frame *frame;320nghttp2_nv *nva_copy;321uint8_t flags_copy;322int32_t promised_stream_id;323int rv;324nghttp2_mem *mem;325(void)flags;326327mem = &session->mem;328329if (stream_id <= 0 || nghttp2_session_is_my_stream_id(session, stream_id)) {330return NGHTTP2_ERR_INVALID_ARGUMENT;331}332333if (!session->server) {334return NGHTTP2_ERR_PROTO;335}336337/* All 32bit signed stream IDs are spent. */338if (session->next_stream_id > INT32_MAX) {339return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;340}341342item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));343if (item == NULL) {344return NGHTTP2_ERR_NOMEM;345}346347nghttp2_outbound_item_init(item);348349item->aux_data.headers.stream_user_data = promised_stream_user_data;350351frame = &item->frame;352353rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem);354if (rv < 0) {355nghttp2_mem_free(mem, item);356return rv;357}358359flags_copy = NGHTTP2_FLAG_END_HEADERS;360361promised_stream_id = (int32_t)session->next_stream_id;362session->next_stream_id += 2;363364nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, stream_id,365promised_stream_id, nva_copy, nvlen);366367rv = nghttp2_session_add_item(session, item);368369if (rv != 0) {370nghttp2_frame_push_promise_free(&frame->push_promise, mem);371nghttp2_mem_free(mem, item);372373return rv;374}375376return promised_stream_id;377}378379int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,380int32_t stream_id,381int32_t window_size_increment) {382int rv;383nghttp2_stream *stream = 0;384(void)flags;385386if (window_size_increment == 0) {387return 0;388}389if (stream_id == 0) {390rv = nghttp2_adjust_local_window_size(391&session->local_window_size, &session->recv_window_size,392&session->recv_reduction, &window_size_increment);393if (rv != 0) {394return rv;395}396} else {397stream = nghttp2_session_get_stream(session, stream_id);398if (!stream) {399return 0;400}401402rv = nghttp2_adjust_local_window_size(403&stream->local_window_size, &stream->recv_window_size,404&stream->recv_reduction, &window_size_increment);405if (rv != 0) {406return rv;407}408}409410if (window_size_increment > 0) {411if (stream_id == 0) {412session->consumed_size =413nghttp2_max(0, session->consumed_size - window_size_increment);414} else {415stream->consumed_size =416nghttp2_max(0, stream->consumed_size - window_size_increment);417}418419return nghttp2_session_add_window_update(session, 0, stream_id,420window_size_increment);421}422return 0;423}424425int nghttp2_session_set_local_window_size(nghttp2_session *session,426uint8_t flags, int32_t stream_id,427int32_t window_size) {428int32_t window_size_increment;429nghttp2_stream *stream;430int rv;431(void)flags;432433if (window_size < 0) {434return NGHTTP2_ERR_INVALID_ARGUMENT;435}436437if (stream_id == 0) {438window_size_increment = window_size - session->local_window_size;439440if (window_size_increment == 0) {441return 0;442}443444if (window_size_increment < 0) {445return nghttp2_adjust_local_window_size(446&session->local_window_size, &session->recv_window_size,447&session->recv_reduction, &window_size_increment);448}449450rv = nghttp2_increase_local_window_size(451&session->local_window_size, &session->recv_window_size,452&session->recv_reduction, &window_size_increment);453454if (rv != 0) {455return rv;456}457458if (window_size_increment > 0) {459return nghttp2_session_add_window_update(session, 0, stream_id,460window_size_increment);461}462463return nghttp2_session_update_recv_connection_window_size(session, 0);464} else {465stream = nghttp2_session_get_stream(session, stream_id);466467if (stream == NULL) {468return 0;469}470471window_size_increment = window_size - stream->local_window_size;472473if (window_size_increment == 0) {474return 0;475}476477if (window_size_increment < 0) {478return nghttp2_adjust_local_window_size(479&stream->local_window_size, &stream->recv_window_size,480&stream->recv_reduction, &window_size_increment);481}482483rv = nghttp2_increase_local_window_size(484&stream->local_window_size, &stream->recv_window_size,485&stream->recv_reduction, &window_size_increment);486487if (rv != 0) {488return rv;489}490491if (window_size_increment > 0) {492return nghttp2_session_add_window_update(session, 0, stream_id,493window_size_increment);494}495496return nghttp2_session_update_recv_stream_window_size(session, stream, 0,4971);498}499}500501int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,502int32_t stream_id, const uint8_t *origin,503size_t origin_len, const uint8_t *field_value,504size_t field_value_len) {505nghttp2_mem *mem;506uint8_t *buf, *p;507uint8_t *origin_copy;508uint8_t *field_value_copy;509nghttp2_outbound_item *item;510nghttp2_frame *frame;511nghttp2_ext_altsvc *altsvc;512int rv;513(void)flags;514515mem = &session->mem;516517if (!session->server) {518return NGHTTP2_ERR_INVALID_STATE;519}520521if (2 + origin_len + field_value_len > NGHTTP2_MAX_PAYLOADLEN) {522return NGHTTP2_ERR_INVALID_ARGUMENT;523}524525if (stream_id == 0) {526if (origin_len == 0) {527return NGHTTP2_ERR_INVALID_ARGUMENT;528}529} else if (origin_len != 0) {530return NGHTTP2_ERR_INVALID_ARGUMENT;531}532533buf = nghttp2_mem_malloc(mem, origin_len + field_value_len + 2);534if (buf == NULL) {535return NGHTTP2_ERR_NOMEM;536}537538p = buf;539540origin_copy = p;541if (origin_len) {542p = nghttp2_cpymem(p, origin, origin_len);543}544*p++ = '\0';545546field_value_copy = p;547if (field_value_len) {548p = nghttp2_cpymem(p, field_value, field_value_len);549}550*p++ = '\0';551552item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));553if (item == NULL) {554rv = NGHTTP2_ERR_NOMEM;555goto fail_item_malloc;556}557558nghttp2_outbound_item_init(item);559560item->aux_data.ext.builtin = 1;561562altsvc = &item->ext_frame_payload.altsvc;563564frame = &item->frame;565frame->ext.payload = altsvc;566567nghttp2_frame_altsvc_init(&frame->ext, stream_id, origin_copy, origin_len,568field_value_copy, field_value_len);569570rv = nghttp2_session_add_item(session, item);571if (rv != 0) {572nghttp2_frame_altsvc_free(&frame->ext, mem);573nghttp2_mem_free(mem, item);574575return rv;576}577578return 0;579580fail_item_malloc:581free(buf);582583return rv;584}585586int nghttp2_submit_origin(nghttp2_session *session, uint8_t flags,587const nghttp2_origin_entry *ov, size_t nov) {588nghttp2_mem *mem;589uint8_t *p;590nghttp2_outbound_item *item;591nghttp2_frame *frame;592nghttp2_ext_origin *origin;593nghttp2_origin_entry *ov_copy;594size_t len = 0;595size_t i;596int rv;597(void)flags;598599mem = &session->mem;600601if (!session->server) {602return NGHTTP2_ERR_INVALID_STATE;603}604605if (nov) {606for (i = 0; i < nov; ++i) {607len += ov[i].origin_len;608}609610if (2 * nov + len > NGHTTP2_MAX_PAYLOADLEN) {611return NGHTTP2_ERR_INVALID_ARGUMENT;612}613614/* The last nov is added for terminal NULL character. */615ov_copy =616nghttp2_mem_malloc(mem, nov * sizeof(nghttp2_origin_entry) + len + nov);617if (ov_copy == NULL) {618return NGHTTP2_ERR_NOMEM;619}620621p = (uint8_t *)ov_copy + nov * sizeof(nghttp2_origin_entry);622623for (i = 0; i < nov; ++i) {624ov_copy[i].origin = p;625ov_copy[i].origin_len = ov[i].origin_len;626p = nghttp2_cpymem(p, ov[i].origin, ov[i].origin_len);627*p++ = '\0';628}629630assert((size_t)(p - (uint8_t *)ov_copy) ==631nov * sizeof(nghttp2_origin_entry) + len + nov);632} else {633ov_copy = NULL;634}635636item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));637if (item == NULL) {638rv = NGHTTP2_ERR_NOMEM;639goto fail_item_malloc;640}641642nghttp2_outbound_item_init(item);643644item->aux_data.ext.builtin = 1;645646origin = &item->ext_frame_payload.origin;647648frame = &item->frame;649frame->ext.payload = origin;650651nghttp2_frame_origin_init(&frame->ext, ov_copy, nov);652653rv = nghttp2_session_add_item(session, item);654if (rv != 0) {655nghttp2_frame_origin_free(&frame->ext, mem);656nghttp2_mem_free(mem, item);657658return rv;659}660661return 0;662663fail_item_malloc:664free(ov_copy);665666return rv;667}668669int nghttp2_submit_priority_update(nghttp2_session *session, uint8_t flags,670int32_t stream_id,671const uint8_t *field_value,672size_t field_value_len) {673nghttp2_mem *mem;674uint8_t *buf, *p;675nghttp2_outbound_item *item;676nghttp2_frame *frame;677nghttp2_ext_priority_update *priority_update;678int rv;679(void)flags;680681mem = &session->mem;682683if (session->server) {684return NGHTTP2_ERR_INVALID_STATE;685}686687if (session->remote_settings.no_rfc7540_priorities == 0) {688return 0;689}690691if (stream_id == 0 || 4 + field_value_len > NGHTTP2_MAX_PAYLOADLEN) {692return NGHTTP2_ERR_INVALID_ARGUMENT;693}694695if (field_value_len) {696buf = nghttp2_mem_malloc(mem, field_value_len + 1);697if (buf == NULL) {698return NGHTTP2_ERR_NOMEM;699}700701p = nghttp2_cpymem(buf, field_value, field_value_len);702*p = '\0';703} else {704buf = NULL;705}706707item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));708if (item == NULL) {709rv = NGHTTP2_ERR_NOMEM;710goto fail_item_malloc;711}712713nghttp2_outbound_item_init(item);714715item->aux_data.ext.builtin = 1;716717priority_update = &item->ext_frame_payload.priority_update;718719frame = &item->frame;720frame->ext.payload = priority_update;721722nghttp2_frame_priority_update_init(&frame->ext, stream_id, buf,723field_value_len);724725rv = nghttp2_session_add_item(session, item);726if (rv != 0) {727nghttp2_frame_priority_update_free(&frame->ext, mem);728nghttp2_mem_free(mem, item);729730return rv;731}732733return 0;734735fail_item_malloc:736free(buf);737738return rv;739}740741static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,742const nghttp2_data_provider *data_prd) {743uint8_t flags = NGHTTP2_FLAG_NONE;744if (data_prd == NULL || data_prd->read_callback == NULL) {745flags |= NGHTTP2_FLAG_END_STREAM;746}747748if (pri_spec) {749flags |= NGHTTP2_FLAG_PRIORITY;750}751752return flags;753}754755int32_t nghttp2_submit_request(nghttp2_session *session,756const nghttp2_priority_spec *pri_spec,757const nghttp2_nv *nva, size_t nvlen,758const nghttp2_data_provider *data_prd,759void *stream_user_data) {760uint8_t flags;761int rv;762763if (session->server) {764return NGHTTP2_ERR_PROTO;765}766767if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec) &&768session->remote_settings.no_rfc7540_priorities != 1) {769rv = detect_self_dependency(session, -1, pri_spec);770if (rv != 0) {771return rv;772}773} else {774pri_spec = NULL;775}776777flags = set_request_flags(pri_spec, data_prd);778779return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen,780data_prd, stream_user_data);781}782783static uint8_t set_response_flags(const nghttp2_data_provider *data_prd) {784uint8_t flags = NGHTTP2_FLAG_NONE;785if (data_prd == NULL || data_prd->read_callback == NULL) {786flags |= NGHTTP2_FLAG_END_STREAM;787}788return flags;789}790791int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,792const nghttp2_nv *nva, size_t nvlen,793const nghttp2_data_provider *data_prd) {794uint8_t flags;795796if (stream_id <= 0) {797return NGHTTP2_ERR_INVALID_ARGUMENT;798}799800if (!session->server) {801return NGHTTP2_ERR_PROTO;802}803804flags = set_response_flags(data_prd);805return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen,806data_prd, NULL);807}808809int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,810int32_t stream_id,811const nghttp2_data_provider *data_prd) {812int rv;813nghttp2_outbound_item *item;814nghttp2_frame *frame;815nghttp2_data_aux_data *aux_data;816uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM;817nghttp2_mem *mem;818819mem = &session->mem;820821if (stream_id == 0) {822return NGHTTP2_ERR_INVALID_ARGUMENT;823}824825item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));826if (item == NULL) {827return NGHTTP2_ERR_NOMEM;828}829830nghttp2_outbound_item_init(item);831832frame = &item->frame;833aux_data = &item->aux_data.data;834aux_data->data_prd = *data_prd;835aux_data->eof = 0;836aux_data->flags = nflags;837838/* flags are sent on transmission */839nghttp2_frame_data_init(&frame->data, NGHTTP2_FLAG_NONE, stream_id);840841rv = nghttp2_session_add_item(session, item);842if (rv != 0) {843nghttp2_frame_data_free(&frame->data);844nghttp2_mem_free(mem, item);845return rv;846}847return 0;848}849850ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen,851const nghttp2_settings_entry *iv,852size_t niv) {853if (!nghttp2_iv_check(iv, niv)) {854return NGHTTP2_ERR_INVALID_ARGUMENT;855}856857if (buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) {858return NGHTTP2_ERR_INSUFF_BUFSIZE;859}860861return (ssize_t)nghttp2_frame_pack_settings_payload(buf, iv, niv);862}863864int nghttp2_submit_extension(nghttp2_session *session, uint8_t type,865uint8_t flags, int32_t stream_id, void *payload) {866int rv;867nghttp2_outbound_item *item;868nghttp2_frame *frame;869nghttp2_mem *mem;870871mem = &session->mem;872873if (type <= NGHTTP2_CONTINUATION) {874return NGHTTP2_ERR_INVALID_ARGUMENT;875}876877if (!session->callbacks.pack_extension_callback) {878return NGHTTP2_ERR_INVALID_STATE;879}880881item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));882if (item == NULL) {883return NGHTTP2_ERR_NOMEM;884}885886nghttp2_outbound_item_init(item);887888frame = &item->frame;889nghttp2_frame_extension_init(&frame->ext, type, flags, stream_id, payload);890891rv = nghttp2_session_add_item(session, item);892if (rv != 0) {893nghttp2_frame_extension_free(&frame->ext);894nghttp2_mem_free(mem, item);895return rv;896}897898return 0;899}900901902