nghttp2_submit.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. /*
  2. * Copyright (C) 2015-2018 Alibaba Group Holding Limited
  3. */
  4. #include "nghttp2_submit.h"
  5. #include <string.h>
  6. #include <assert.h>
  7. #include "nghttp2_session.h"
  8. #include "nghttp2_frame.h"
  9. #include "nghttp2_helper.h"
  10. #include "nghttp2_priority_spec.h"
  11. /*
  12. * Detects the dependency error, that is stream attempted to depend on
  13. * itself. If |stream_id| is -1, we use session->next_stream_id as
  14. * stream ID.
  15. *
  16. * This function returns 0 if it succeeds, or one of the following
  17. * error codes:
  18. *
  19. * NGHTTP2_ERR_INVALID_ARGUMENT
  20. * Stream attempted to depend on itself.
  21. */
  22. static int detect_self_dependency(nghttp2_session *session, int32_t stream_id,
  23. const nghttp2_priority_spec *pri_spec) {
  24. assert(pri_spec);
  25. if (stream_id == -1) {
  26. if ((int32_t)session->next_stream_id == pri_spec->stream_id) {
  27. return NGHTTP2_ERR_INVALID_ARGUMENT;
  28. }
  29. return 0;
  30. }
  31. if (stream_id == pri_spec->stream_id) {
  32. return NGHTTP2_ERR_INVALID_ARGUMENT;
  33. }
  34. return 0;
  35. }
  36. /* This function takes ownership of |nva_copy|. Regardless of the
  37. return value, the caller must not free |nva_copy| after this
  38. function returns. */
  39. static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
  40. int32_t stream_id,
  41. const nghttp2_priority_spec *pri_spec,
  42. nghttp2_nv *nva_copy, size_t nvlen,
  43. const nghttp2_data_provider *data_prd,
  44. void *stream_user_data) {
  45. int rv;
  46. uint8_t flags_copy;
  47. nghttp2_outbound_item *item = NULL;
  48. nghttp2_frame *frame = NULL;
  49. nghttp2_headers_category hcat;
  50. nghttp2_mem *mem;
  51. mem = &session->mem;
  52. item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
  53. if (item == NULL) {
  54. rv = NGHTTP2_ERR_NOMEM;
  55. goto fail;
  56. }
  57. nghttp2_outbound_item_init(item);
  58. if (data_prd != NULL && data_prd->read_callback != NULL) {
  59. item->aux_data.headers.data_prd = *data_prd;
  60. }
  61. item->aux_data.headers.stream_user_data = stream_user_data;
  62. flags_copy =
  63. (uint8_t)((flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) |
  64. NGHTTP2_FLAG_END_HEADERS);
  65. if (stream_id == -1) {
  66. if (session->next_stream_id > INT32_MAX) {
  67. rv = NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
  68. goto fail;
  69. }
  70. stream_id = (int32_t)session->next_stream_id;
  71. session->next_stream_id += 2;
  72. hcat = NGHTTP2_HCAT_REQUEST;
  73. } else {
  74. /* More specific categorization will be done later. */
  75. hcat = NGHTTP2_HCAT_HEADERS;
  76. }
  77. frame = &item->frame;
  78. nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat,
  79. pri_spec, nva_copy, nvlen);
  80. rv = nghttp2_session_add_item(session, item);
  81. if (rv != 0) {
  82. nghttp2_frame_headers_free(&frame->headers, mem);
  83. goto fail2;
  84. }
  85. if (hcat == NGHTTP2_HCAT_REQUEST) {
  86. return stream_id;
  87. }
  88. return 0;
  89. fail:
  90. /* nghttp2_frame_headers_init() takes ownership of nva_copy. */
  91. nghttp2_nv_array_del(nva_copy, mem);
  92. fail2:
  93. nghttp2_mem_free(mem, item);
  94. return rv;
  95. }
  96. static int32_t submit_headers_shared_nva(nghttp2_session *session,
  97. uint8_t flags, int32_t stream_id,
  98. const nghttp2_priority_spec *pri_spec,
  99. const nghttp2_nv *nva, size_t nvlen,
  100. const nghttp2_data_provider *data_prd,
  101. void *stream_user_data) {
  102. int rv;
  103. nghttp2_nv *nva_copy;
  104. nghttp2_priority_spec copy_pri_spec;
  105. nghttp2_mem *mem;
  106. mem = &session->mem;
  107. if (pri_spec) {
  108. copy_pri_spec = *pri_spec;
  109. nghttp2_priority_spec_normalize_weight(&copy_pri_spec);
  110. } else {
  111. nghttp2_priority_spec_default_init(&copy_pri_spec);
  112. }
  113. rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem);
  114. if (rv < 0) {
  115. return rv;
  116. }
  117. return submit_headers_shared(session, flags, stream_id, &copy_pri_spec,
  118. nva_copy, nvlen, data_prd, stream_user_data);
  119. }
  120. int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
  121. const nghttp2_nv *nva, size_t nvlen) {
  122. if (stream_id <= 0) {
  123. return NGHTTP2_ERR_INVALID_ARGUMENT;
  124. }
  125. return (int)submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM,
  126. stream_id, NULL, nva, nvlen, NULL,
  127. NULL);
  128. }
  129. int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
  130. int32_t stream_id,
  131. const nghttp2_priority_spec *pri_spec,
  132. const nghttp2_nv *nva, size_t nvlen,
  133. void *stream_user_data) {
  134. int rv;
  135. if (stream_id == -1) {
  136. if (session->server) {
  137. return NGHTTP2_ERR_PROTO;
  138. }
  139. } else if (stream_id <= 0) {
  140. return NGHTTP2_ERR_INVALID_ARGUMENT;
  141. }
  142. flags &= NGHTTP2_FLAG_END_STREAM;
  143. if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
  144. rv = detect_self_dependency(session, stream_id, pri_spec);
  145. if (rv != 0) {
  146. return rv;
  147. }
  148. flags |= NGHTTP2_FLAG_PRIORITY;
  149. } else {
  150. pri_spec = NULL;
  151. }
  152. return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva,
  153. nvlen, NULL, stream_user_data);
  154. }
  155. int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,
  156. const uint8_t *opaque_data) {
  157. flags &= NGHTTP2_FLAG_ACK;
  158. return nghttp2_session_add_ping(session, flags, opaque_data);
  159. }
  160. int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
  161. int32_t stream_id,
  162. const nghttp2_priority_spec *pri_spec) {
  163. int rv;
  164. nghttp2_outbound_item *item;
  165. nghttp2_frame *frame;
  166. nghttp2_priority_spec copy_pri_spec;
  167. nghttp2_mem *mem;
  168. (void)flags;
  169. mem = &session->mem;
  170. if (stream_id == 0 || pri_spec == NULL) {
  171. return NGHTTP2_ERR_INVALID_ARGUMENT;
  172. }
  173. if (stream_id == pri_spec->stream_id) {
  174. return NGHTTP2_ERR_INVALID_ARGUMENT;
  175. }
  176. copy_pri_spec = *pri_spec;
  177. nghttp2_priority_spec_normalize_weight(&copy_pri_spec);
  178. item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
  179. if (item == NULL) {
  180. return NGHTTP2_ERR_NOMEM;
  181. }
  182. nghttp2_outbound_item_init(item);
  183. frame = &item->frame;
  184. nghttp2_frame_priority_init(&frame->priority, stream_id, &copy_pri_spec);
  185. rv = nghttp2_session_add_item(session, item);
  186. if (rv != 0) {
  187. nghttp2_frame_priority_free(&frame->priority);
  188. nghttp2_mem_free(mem, item);
  189. return rv;
  190. }
  191. return 0;
  192. }
  193. int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags,
  194. int32_t stream_id, uint32_t error_code) {
  195. (void)flags;
  196. if (stream_id == 0) {
  197. return NGHTTP2_ERR_INVALID_ARGUMENT;
  198. }
  199. return nghttp2_session_add_rst_stream(session, stream_id, error_code);
  200. }
  201. int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags,
  202. int32_t last_stream_id, uint32_t error_code,
  203. const uint8_t *opaque_data, size_t opaque_data_len) {
  204. (void)flags;
  205. if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
  206. return 0;
  207. }
  208. return nghttp2_session_add_goaway(session, last_stream_id, error_code,
  209. opaque_data, opaque_data_len,
  210. NGHTTP2_GOAWAY_AUX_NONE);
  211. }
  212. int nghttp2_submit_shutdown_notice(nghttp2_session *session) {
  213. if (!session->server) {
  214. return NGHTTP2_ERR_INVALID_STATE;
  215. }
  216. if (session->goaway_flags) {
  217. return 0;
  218. }
  219. return nghttp2_session_add_goaway(session, (1u << 31) - 1, NGHTTP2_NO_ERROR,
  220. NULL, 0,
  221. NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE);
  222. }
  223. int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
  224. const nghttp2_settings_entry *iv, size_t niv) {
  225. (void)flags;
  226. return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv);
  227. }
  228. int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
  229. int32_t stream_id, const nghttp2_nv *nva,
  230. size_t nvlen,
  231. void *promised_stream_user_data) {
  232. nghttp2_outbound_item *item;
  233. nghttp2_frame *frame;
  234. nghttp2_nv *nva_copy;
  235. uint8_t flags_copy;
  236. int32_t promised_stream_id;
  237. int rv;
  238. nghttp2_mem *mem;
  239. (void)flags;
  240. mem = &session->mem;
  241. if (stream_id <= 0 || nghttp2_session_is_my_stream_id(session, stream_id)) {
  242. return NGHTTP2_ERR_INVALID_ARGUMENT;
  243. }
  244. if (!session->server) {
  245. return NGHTTP2_ERR_PROTO;
  246. }
  247. /* All 32bit signed stream IDs are spent. */
  248. if (session->next_stream_id > INT32_MAX) {
  249. return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
  250. }
  251. item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
  252. if (item == NULL) {
  253. return NGHTTP2_ERR_NOMEM;
  254. }
  255. nghttp2_outbound_item_init(item);
  256. item->aux_data.headers.stream_user_data = promised_stream_user_data;
  257. frame = &item->frame;
  258. rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem);
  259. if (rv < 0) {
  260. nghttp2_mem_free(mem, item);
  261. return rv;
  262. }
  263. flags_copy = NGHTTP2_FLAG_END_HEADERS;
  264. promised_stream_id = (int32_t)session->next_stream_id;
  265. session->next_stream_id += 2;
  266. nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, stream_id,
  267. promised_stream_id, nva_copy, nvlen);
  268. rv = nghttp2_session_add_item(session, item);
  269. if (rv != 0) {
  270. nghttp2_frame_push_promise_free(&frame->push_promise, mem);
  271. nghttp2_mem_free(mem, item);
  272. return rv;
  273. }
  274. return promised_stream_id;
  275. }
  276. int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
  277. int32_t stream_id,
  278. int32_t window_size_increment) {
  279. int rv;
  280. nghttp2_stream *stream = 0;
  281. (void)flags;
  282. if (window_size_increment == 0) {
  283. return 0;
  284. }
  285. if (stream_id == 0) {
  286. rv = nghttp2_adjust_local_window_size(
  287. &session->local_window_size, &session->recv_window_size,
  288. &session->recv_reduction, &window_size_increment);
  289. if (rv != 0) {
  290. return rv;
  291. }
  292. } else {
  293. stream = nghttp2_session_get_stream(session, stream_id);
  294. if (!stream) {
  295. return 0;
  296. }
  297. rv = nghttp2_adjust_local_window_size(
  298. &stream->local_window_size, &stream->recv_window_size,
  299. &stream->recv_reduction, &window_size_increment);
  300. if (rv != 0) {
  301. return rv;
  302. }
  303. }
  304. if (window_size_increment > 0) {
  305. if (stream_id == 0) {
  306. session->consumed_size =
  307. nghttp2_max(0, session->consumed_size - window_size_increment);
  308. } else {
  309. stream->consumed_size =
  310. nghttp2_max(0, stream->consumed_size - window_size_increment);
  311. }
  312. return nghttp2_session_add_window_update(session, 0, stream_id,
  313. window_size_increment);
  314. }
  315. return 0;
  316. }
  317. int nghttp2_session_set_local_window_size(nghttp2_session *session,
  318. uint8_t flags, int32_t stream_id,
  319. int32_t window_size) {
  320. int32_t window_size_increment;
  321. nghttp2_stream *stream;
  322. int rv;
  323. (void)flags;
  324. if (window_size < 0) {
  325. return NGHTTP2_ERR_INVALID_ARGUMENT;
  326. }
  327. if (stream_id == 0) {
  328. window_size_increment = window_size - session->local_window_size;
  329. if (window_size_increment == 0) {
  330. return 0;
  331. }
  332. if (window_size_increment < 0) {
  333. return nghttp2_adjust_local_window_size(
  334. &session->local_window_size, &session->recv_window_size,
  335. &session->recv_reduction, &window_size_increment);
  336. }
  337. rv = nghttp2_increase_local_window_size(
  338. &session->local_window_size, &session->recv_window_size,
  339. &session->recv_reduction, &window_size_increment);
  340. if (rv != 0) {
  341. return rv;
  342. }
  343. } else {
  344. stream = nghttp2_session_get_stream(session, stream_id);
  345. if (stream == NULL) {
  346. return 0;
  347. }
  348. window_size_increment = window_size - stream->local_window_size;
  349. if (window_size_increment == 0) {
  350. return 0;
  351. }
  352. if (window_size_increment < 0) {
  353. return nghttp2_adjust_local_window_size(
  354. &stream->local_window_size, &stream->recv_window_size,
  355. &stream->recv_reduction, &window_size_increment);
  356. }
  357. rv = nghttp2_increase_local_window_size(
  358. &stream->local_window_size, &stream->recv_window_size,
  359. &stream->recv_reduction, &window_size_increment);
  360. if (rv != 0) {
  361. return rv;
  362. }
  363. }
  364. if (window_size_increment > 0) {
  365. return nghttp2_session_add_window_update(session, 0, stream_id,
  366. window_size_increment);
  367. }
  368. return 0;
  369. }
  370. int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
  371. int32_t stream_id, const uint8_t *origin,
  372. size_t origin_len, const uint8_t *field_value,
  373. size_t field_value_len) {
  374. nghttp2_mem *mem;
  375. uint8_t *buf, *p;
  376. uint8_t *origin_copy;
  377. uint8_t *field_value_copy;
  378. nghttp2_outbound_item *item;
  379. nghttp2_frame *frame;
  380. nghttp2_ext_altsvc *altsvc;
  381. int rv;
  382. (void)flags;
  383. mem = &session->mem;
  384. if (!session->server) {
  385. return NGHTTP2_ERR_INVALID_STATE;
  386. }
  387. if (2 + origin_len + field_value_len > NGHTTP2_MAX_PAYLOADLEN) {
  388. return NGHTTP2_ERR_INVALID_ARGUMENT;
  389. }
  390. if (stream_id == 0) {
  391. if (origin_len == 0) {
  392. return NGHTTP2_ERR_INVALID_ARGUMENT;
  393. }
  394. } else if (origin_len != 0) {
  395. return NGHTTP2_ERR_INVALID_ARGUMENT;
  396. }
  397. buf = nghttp2_mem_malloc(mem, origin_len + field_value_len + 2);
  398. if (buf == NULL) {
  399. return NGHTTP2_ERR_NOMEM;
  400. }
  401. p = buf;
  402. origin_copy = p;
  403. if (origin_len) {
  404. p = nghttp2_cpymem(p, origin, origin_len);
  405. }
  406. *p++ = '\0';
  407. field_value_copy = p;
  408. if (field_value_len) {
  409. p = nghttp2_cpymem(p, field_value, field_value_len);
  410. }
  411. *p++ = '\0';
  412. item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
  413. if (item == NULL) {
  414. rv = NGHTTP2_ERR_NOMEM;
  415. goto fail_item_malloc;
  416. }
  417. nghttp2_outbound_item_init(item);
  418. item->aux_data.ext.builtin = 1;
  419. altsvc = &item->ext_frame_payload.altsvc;
  420. frame = &item->frame;
  421. frame->ext.payload = altsvc;
  422. nghttp2_frame_altsvc_init(&frame->ext, stream_id, origin_copy, origin_len,
  423. field_value_copy, field_value_len);
  424. rv = nghttp2_session_add_item(session, item);
  425. if (rv != 0) {
  426. nghttp2_frame_altsvc_free(&frame->ext, mem);
  427. nghttp2_mem_free(mem, item);
  428. return rv;
  429. }
  430. return 0;
  431. fail_item_malloc:
  432. nghttp2_mem_free(mem, buf);
  433. return rv;
  434. }
  435. static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
  436. const nghttp2_data_provider *data_prd) {
  437. uint8_t flags = NGHTTP2_FLAG_NONE;
  438. if (data_prd == NULL || data_prd->read_callback == NULL) {
  439. flags |= NGHTTP2_FLAG_END_STREAM;
  440. }
  441. if (pri_spec) {
  442. flags |= NGHTTP2_FLAG_PRIORITY;
  443. }
  444. return flags;
  445. }
  446. int32_t nghttp2_submit_request(nghttp2_session *session,
  447. const nghttp2_priority_spec *pri_spec,
  448. const nghttp2_nv *nva, size_t nvlen,
  449. const nghttp2_data_provider *data_prd,
  450. void *stream_user_data) {
  451. uint8_t flags;
  452. int rv;
  453. if (session->server) {
  454. return NGHTTP2_ERR_PROTO;
  455. }
  456. if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
  457. rv = detect_self_dependency(session, -1, pri_spec);
  458. if (rv != 0) {
  459. return rv;
  460. }
  461. } else {
  462. pri_spec = NULL;
  463. }
  464. flags = set_request_flags(pri_spec, data_prd);
  465. return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen,
  466. data_prd, stream_user_data);
  467. }
  468. static uint8_t set_response_flags(const nghttp2_data_provider *data_prd) {
  469. uint8_t flags = NGHTTP2_FLAG_NONE;
  470. if (data_prd == NULL || data_prd->read_callback == NULL) {
  471. flags |= NGHTTP2_FLAG_END_STREAM;
  472. }
  473. return flags;
  474. }
  475. int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
  476. const nghttp2_nv *nva, size_t nvlen,
  477. const nghttp2_data_provider *data_prd) {
  478. uint8_t flags;
  479. if (stream_id <= 0) {
  480. return NGHTTP2_ERR_INVALID_ARGUMENT;
  481. }
  482. if (!session->server) {
  483. return NGHTTP2_ERR_PROTO;
  484. }
  485. flags = set_response_flags(data_prd);
  486. return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen,
  487. data_prd, NULL);
  488. }
  489. int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
  490. int32_t stream_id,
  491. const nghttp2_data_provider *data_prd) {
  492. int rv;
  493. nghttp2_outbound_item *item;
  494. nghttp2_frame *frame;
  495. nghttp2_data_aux_data *aux_data;
  496. uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM;
  497. nghttp2_mem *mem;
  498. mem = &session->mem;
  499. if (stream_id == 0) {
  500. return NGHTTP2_ERR_INVALID_ARGUMENT;
  501. }
  502. item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
  503. if (item == NULL) {
  504. return NGHTTP2_ERR_NOMEM;
  505. }
  506. nghttp2_outbound_item_init(item);
  507. frame = &item->frame;
  508. aux_data = &item->aux_data.data;
  509. aux_data->data_prd = *data_prd;
  510. aux_data->eof = 0;
  511. aux_data->flags = nflags;
  512. /* flags are sent on transmission */
  513. nghttp2_frame_data_init(&frame->data, NGHTTP2_FLAG_NONE, stream_id);
  514. rv = nghttp2_session_add_item(session, item);
  515. if (rv != 0) {
  516. nghttp2_frame_data_free(&frame->data);
  517. nghttp2_mem_free(mem, item);
  518. return rv;
  519. }
  520. return 0;
  521. }
  522. ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen,
  523. const nghttp2_settings_entry *iv,
  524. size_t niv) {
  525. if (!nghttp2_iv_check(iv, niv)) {
  526. return NGHTTP2_ERR_INVALID_ARGUMENT;
  527. }
  528. if (buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) {
  529. return NGHTTP2_ERR_INSUFF_BUFSIZE;
  530. }
  531. return (ssize_t)nghttp2_frame_pack_settings_payload(buf, iv, niv);
  532. }
  533. int nghttp2_submit_extension(nghttp2_session *session, uint8_t type,
  534. uint8_t flags, int32_t stream_id, void *payload) {
  535. int rv;
  536. nghttp2_outbound_item *item;
  537. nghttp2_frame *frame;
  538. nghttp2_mem *mem;
  539. mem = &session->mem;
  540. if (type <= NGHTTP2_CONTINUATION) {
  541. return NGHTTP2_ERR_INVALID_ARGUMENT;
  542. }
  543. if (!session->callbacks.pack_extension_callback) {
  544. return NGHTTP2_ERR_INVALID_STATE;
  545. }
  546. item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
  547. if (item == NULL) {
  548. return NGHTTP2_ERR_NOMEM;
  549. }
  550. nghttp2_outbound_item_init(item);
  551. frame = &item->frame;
  552. nghttp2_frame_extension_init(&frame->ext, type, flags, stream_id, payload);
  553. rv = nghttp2_session_add_item(session, item);
  554. if (rv != 0) {
  555. nghttp2_frame_extension_free(&frame->ext);
  556. nghttp2_mem_free(mem, item);
  557. return rv;
  558. }
  559. return 0;
  560. }