Imported Upstream version 1.41.0
[platform/upstream/nghttp2.git] / lib / nghttp2_submit.c
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 #include "nghttp2_submit.h"
26
27 #include <string.h>
28 #include <assert.h>
29
30 #include "nghttp2_session.h"
31 #include "nghttp2_frame.h"
32 #include "nghttp2_helper.h"
33 #include "nghttp2_priority_spec.h"
34
35 /*
36  * Detects the dependency error, that is stream attempted to depend on
37  * itself.  If |stream_id| is -1, we use session->next_stream_id as
38  * stream ID.
39  *
40  * This function returns 0 if it succeeds, or one of the following
41  * error codes:
42  *
43  * NGHTTP2_ERR_INVALID_ARGUMENT
44  *   Stream attempted to depend on itself.
45  */
46 static int detect_self_dependency(nghttp2_session *session, int32_t stream_id,
47                                   const nghttp2_priority_spec *pri_spec) {
48   assert(pri_spec);
49
50   if (stream_id == -1) {
51     if ((int32_t)session->next_stream_id == pri_spec->stream_id) {
52       return NGHTTP2_ERR_INVALID_ARGUMENT;
53     }
54     return 0;
55   }
56
57   if (stream_id == pri_spec->stream_id) {
58     return NGHTTP2_ERR_INVALID_ARGUMENT;
59   }
60
61   return 0;
62 }
63
64 /* This function takes ownership of |nva_copy|. Regardless of the
65    return value, the caller must not free |nva_copy| after this
66    function returns. */
67 static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
68                                      int32_t stream_id,
69                                      const nghttp2_priority_spec *pri_spec,
70                                      nghttp2_nv *nva_copy, size_t nvlen,
71                                      const nghttp2_data_provider *data_prd,
72                                      void *stream_user_data) {
73   int rv;
74   uint8_t flags_copy;
75   nghttp2_outbound_item *item = NULL;
76   nghttp2_frame *frame = NULL;
77   nghttp2_headers_category hcat;
78   nghttp2_mem *mem;
79
80   mem = &session->mem;
81
82   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
83   if (item == NULL) {
84     rv = NGHTTP2_ERR_NOMEM;
85     goto fail;
86   }
87
88   nghttp2_outbound_item_init(item);
89
90   if (data_prd != NULL && data_prd->read_callback != NULL) {
91     item->aux_data.headers.data_prd = *data_prd;
92   }
93
94   item->aux_data.headers.stream_user_data = stream_user_data;
95
96   flags_copy =
97       (uint8_t)((flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) |
98                 NGHTTP2_FLAG_END_HEADERS);
99
100   if (stream_id == -1) {
101     if (session->next_stream_id > INT32_MAX) {
102       rv = NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
103       goto fail;
104     }
105
106     stream_id = (int32_t)session->next_stream_id;
107     session->next_stream_id += 2;
108
109     hcat = NGHTTP2_HCAT_REQUEST;
110   } else {
111     /* More specific categorization will be done later. */
112     hcat = NGHTTP2_HCAT_HEADERS;
113   }
114
115   frame = &item->frame;
116
117   nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat,
118                              pri_spec, nva_copy, nvlen);
119
120   rv = nghttp2_session_add_item(session, item);
121
122   if (rv != 0) {
123     nghttp2_frame_headers_free(&frame->headers, mem);
124     goto fail2;
125   }
126
127   if (hcat == NGHTTP2_HCAT_REQUEST) {
128     return stream_id;
129   }
130
131   return 0;
132
133 fail:
134   /* nghttp2_frame_headers_init() takes ownership of nva_copy. */
135   nghttp2_nv_array_del(nva_copy, mem);
136 fail2:
137   nghttp2_mem_free(mem, item);
138
139   return rv;
140 }
141
142 static int32_t submit_headers_shared_nva(nghttp2_session *session,
143                                          uint8_t flags, int32_t stream_id,
144                                          const nghttp2_priority_spec *pri_spec,
145                                          const nghttp2_nv *nva, size_t nvlen,
146                                          const nghttp2_data_provider *data_prd,
147                                          void *stream_user_data) {
148   int rv;
149   nghttp2_nv *nva_copy;
150   nghttp2_priority_spec copy_pri_spec;
151   nghttp2_mem *mem;
152
153   mem = &session->mem;
154
155   if (pri_spec) {
156     copy_pri_spec = *pri_spec;
157     nghttp2_priority_spec_normalize_weight(&copy_pri_spec);
158   } else {
159     nghttp2_priority_spec_default_init(&copy_pri_spec);
160   }
161
162   rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem);
163   if (rv < 0) {
164     return rv;
165   }
166
167   return submit_headers_shared(session, flags, stream_id, &copy_pri_spec,
168                                nva_copy, nvlen, data_prd, stream_user_data);
169 }
170
171 int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
172                            const nghttp2_nv *nva, size_t nvlen) {
173   if (stream_id <= 0) {
174     return NGHTTP2_ERR_INVALID_ARGUMENT;
175   }
176
177   return (int)submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM,
178                                         stream_id, NULL, nva, nvlen, NULL,
179                                         NULL);
180 }
181
182 int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
183                                int32_t stream_id,
184                                const nghttp2_priority_spec *pri_spec,
185                                const nghttp2_nv *nva, size_t nvlen,
186                                void *stream_user_data) {
187   int rv;
188
189   if (stream_id == -1) {
190     if (session->server) {
191       return NGHTTP2_ERR_PROTO;
192     }
193   } else if (stream_id <= 0) {
194     return NGHTTP2_ERR_INVALID_ARGUMENT;
195   }
196
197   flags &= NGHTTP2_FLAG_END_STREAM;
198
199   if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
200     rv = detect_self_dependency(session, stream_id, pri_spec);
201     if (rv != 0) {
202       return rv;
203     }
204
205     flags |= NGHTTP2_FLAG_PRIORITY;
206   } else {
207     pri_spec = NULL;
208   }
209
210   return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva,
211                                    nvlen, NULL, stream_user_data);
212 }
213
214 int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,
215                         const uint8_t *opaque_data) {
216   flags &= NGHTTP2_FLAG_ACK;
217   return nghttp2_session_add_ping(session, flags, opaque_data);
218 }
219
220 int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
221                             int32_t stream_id,
222                             const nghttp2_priority_spec *pri_spec) {
223   int rv;
224   nghttp2_outbound_item *item;
225   nghttp2_frame *frame;
226   nghttp2_priority_spec copy_pri_spec;
227   nghttp2_mem *mem;
228   (void)flags;
229
230   mem = &session->mem;
231
232   if (stream_id == 0 || pri_spec == NULL) {
233     return NGHTTP2_ERR_INVALID_ARGUMENT;
234   }
235
236   if (stream_id == pri_spec->stream_id) {
237     return NGHTTP2_ERR_INVALID_ARGUMENT;
238   }
239
240   copy_pri_spec = *pri_spec;
241
242   nghttp2_priority_spec_normalize_weight(&copy_pri_spec);
243
244   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
245
246   if (item == NULL) {
247     return NGHTTP2_ERR_NOMEM;
248   }
249
250   nghttp2_outbound_item_init(item);
251
252   frame = &item->frame;
253
254   nghttp2_frame_priority_init(&frame->priority, stream_id, &copy_pri_spec);
255
256   rv = nghttp2_session_add_item(session, item);
257
258   if (rv != 0) {
259     nghttp2_frame_priority_free(&frame->priority);
260     nghttp2_mem_free(mem, item);
261
262     return rv;
263   }
264
265   return 0;
266 }
267
268 int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags,
269                               int32_t stream_id, uint32_t error_code) {
270   (void)flags;
271
272   if (stream_id == 0) {
273     return NGHTTP2_ERR_INVALID_ARGUMENT;
274   }
275
276   return nghttp2_session_add_rst_stream(session, stream_id, error_code);
277 }
278
279 int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags,
280                           int32_t last_stream_id, uint32_t error_code,
281                           const uint8_t *opaque_data, size_t opaque_data_len) {
282   (void)flags;
283
284   if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
285     return 0;
286   }
287   return nghttp2_session_add_goaway(session, last_stream_id, error_code,
288                                     opaque_data, opaque_data_len,
289                                     NGHTTP2_GOAWAY_AUX_NONE);
290 }
291
292 int nghttp2_submit_shutdown_notice(nghttp2_session *session) {
293   if (!session->server) {
294     return NGHTTP2_ERR_INVALID_STATE;
295   }
296   if (session->goaway_flags) {
297     return 0;
298   }
299   return nghttp2_session_add_goaway(session, (1u << 31) - 1, NGHTTP2_NO_ERROR,
300                                     NULL, 0,
301                                     NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE);
302 }
303
304 int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
305                             const nghttp2_settings_entry *iv, size_t niv) {
306   (void)flags;
307   return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv);
308 }
309
310 int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
311                                     int32_t stream_id, const nghttp2_nv *nva,
312                                     size_t nvlen,
313                                     void *promised_stream_user_data) {
314   nghttp2_outbound_item *item;
315   nghttp2_frame *frame;
316   nghttp2_nv *nva_copy;
317   uint8_t flags_copy;
318   int32_t promised_stream_id;
319   int rv;
320   nghttp2_mem *mem;
321   (void)flags;
322
323   mem = &session->mem;
324
325   if (stream_id <= 0 || nghttp2_session_is_my_stream_id(session, stream_id)) {
326     return NGHTTP2_ERR_INVALID_ARGUMENT;
327   }
328
329   if (!session->server) {
330     return NGHTTP2_ERR_PROTO;
331   }
332
333   /* All 32bit signed stream IDs are spent. */
334   if (session->next_stream_id > INT32_MAX) {
335     return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
336   }
337
338   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
339   if (item == NULL) {
340     return NGHTTP2_ERR_NOMEM;
341   }
342
343   nghttp2_outbound_item_init(item);
344
345   item->aux_data.headers.stream_user_data = promised_stream_user_data;
346
347   frame = &item->frame;
348
349   rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem);
350   if (rv < 0) {
351     nghttp2_mem_free(mem, item);
352     return rv;
353   }
354
355   flags_copy = NGHTTP2_FLAG_END_HEADERS;
356
357   promised_stream_id = (int32_t)session->next_stream_id;
358   session->next_stream_id += 2;
359
360   nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, stream_id,
361                                   promised_stream_id, nva_copy, nvlen);
362
363   rv = nghttp2_session_add_item(session, item);
364
365   if (rv != 0) {
366     nghttp2_frame_push_promise_free(&frame->push_promise, mem);
367     nghttp2_mem_free(mem, item);
368
369     return rv;
370   }
371
372   return promised_stream_id;
373 }
374
375 int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
376                                  int32_t stream_id,
377                                  int32_t window_size_increment) {
378   int rv;
379   nghttp2_stream *stream = 0;
380   (void)flags;
381
382   if (window_size_increment == 0) {
383     return 0;
384   }
385   if (stream_id == 0) {
386     rv = nghttp2_adjust_local_window_size(
387         &session->local_window_size, &session->recv_window_size,
388         &session->recv_reduction, &window_size_increment);
389     if (rv != 0) {
390       return rv;
391     }
392   } else {
393     stream = nghttp2_session_get_stream(session, stream_id);
394     if (!stream) {
395       return 0;
396     }
397
398     rv = nghttp2_adjust_local_window_size(
399         &stream->local_window_size, &stream->recv_window_size,
400         &stream->recv_reduction, &window_size_increment);
401     if (rv != 0) {
402       return rv;
403     }
404   }
405
406   if (window_size_increment > 0) {
407     if (stream_id == 0) {
408       session->consumed_size =
409           nghttp2_max(0, session->consumed_size - window_size_increment);
410     } else {
411       stream->consumed_size =
412           nghttp2_max(0, stream->consumed_size - window_size_increment);
413     }
414
415     return nghttp2_session_add_window_update(session, 0, stream_id,
416                                              window_size_increment);
417   }
418   return 0;
419 }
420
421 int nghttp2_session_set_local_window_size(nghttp2_session *session,
422                                           uint8_t flags, int32_t stream_id,
423                                           int32_t window_size) {
424   int32_t window_size_increment;
425   nghttp2_stream *stream;
426   int rv;
427   (void)flags;
428
429   if (window_size < 0) {
430     return NGHTTP2_ERR_INVALID_ARGUMENT;
431   }
432
433   if (stream_id == 0) {
434     window_size_increment = window_size - session->local_window_size;
435
436     if (window_size_increment == 0) {
437       return 0;
438     }
439
440     if (window_size_increment < 0) {
441       return nghttp2_adjust_local_window_size(
442           &session->local_window_size, &session->recv_window_size,
443           &session->recv_reduction, &window_size_increment);
444     }
445
446     rv = nghttp2_increase_local_window_size(
447         &session->local_window_size, &session->recv_window_size,
448         &session->recv_reduction, &window_size_increment);
449
450     if (rv != 0) {
451       return rv;
452     }
453
454     if (window_size_increment > 0) {
455       return nghttp2_session_add_window_update(session, 0, stream_id,
456                                                window_size_increment);
457     }
458
459     return nghttp2_session_update_recv_connection_window_size(session, 0);
460   } else {
461     stream = nghttp2_session_get_stream(session, stream_id);
462
463     if (stream == NULL) {
464       return 0;
465     }
466
467     window_size_increment = window_size - stream->local_window_size;
468
469     if (window_size_increment == 0) {
470       return 0;
471     }
472
473     if (window_size_increment < 0) {
474       return nghttp2_adjust_local_window_size(
475           &stream->local_window_size, &stream->recv_window_size,
476           &stream->recv_reduction, &window_size_increment);
477     }
478
479     rv = nghttp2_increase_local_window_size(
480         &stream->local_window_size, &stream->recv_window_size,
481         &stream->recv_reduction, &window_size_increment);
482
483     if (rv != 0) {
484       return rv;
485     }
486
487     if (window_size_increment > 0) {
488       return nghttp2_session_add_window_update(session, 0, stream_id,
489                                                window_size_increment);
490     }
491
492     return nghttp2_session_update_recv_stream_window_size(session, stream, 0,
493                                                           1);
494   }
495
496   return 0;
497 }
498
499 int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
500                           int32_t stream_id, const uint8_t *origin,
501                           size_t origin_len, const uint8_t *field_value,
502                           size_t field_value_len) {
503   nghttp2_mem *mem;
504   uint8_t *buf, *p;
505   uint8_t *origin_copy;
506   uint8_t *field_value_copy;
507   nghttp2_outbound_item *item;
508   nghttp2_frame *frame;
509   nghttp2_ext_altsvc *altsvc;
510   int rv;
511   (void)flags;
512
513   mem = &session->mem;
514
515   if (!session->server) {
516     return NGHTTP2_ERR_INVALID_STATE;
517   }
518
519   if (2 + origin_len + field_value_len > NGHTTP2_MAX_PAYLOADLEN) {
520     return NGHTTP2_ERR_INVALID_ARGUMENT;
521   }
522
523   if (stream_id == 0) {
524     if (origin_len == 0) {
525       return NGHTTP2_ERR_INVALID_ARGUMENT;
526     }
527   } else if (origin_len != 0) {
528     return NGHTTP2_ERR_INVALID_ARGUMENT;
529   }
530
531   buf = nghttp2_mem_malloc(mem, origin_len + field_value_len + 2);
532   if (buf == NULL) {
533     return NGHTTP2_ERR_NOMEM;
534   }
535
536   p = buf;
537
538   origin_copy = p;
539   if (origin_len) {
540     p = nghttp2_cpymem(p, origin, origin_len);
541   }
542   *p++ = '\0';
543
544   field_value_copy = p;
545   if (field_value_len) {
546     p = nghttp2_cpymem(p, field_value, field_value_len);
547   }
548   *p++ = '\0';
549
550   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
551   if (item == NULL) {
552     rv = NGHTTP2_ERR_NOMEM;
553     goto fail_item_malloc;
554   }
555
556   nghttp2_outbound_item_init(item);
557
558   item->aux_data.ext.builtin = 1;
559
560   altsvc = &item->ext_frame_payload.altsvc;
561
562   frame = &item->frame;
563   frame->ext.payload = altsvc;
564
565   nghttp2_frame_altsvc_init(&frame->ext, stream_id, origin_copy, origin_len,
566                             field_value_copy, field_value_len);
567
568   rv = nghttp2_session_add_item(session, item);
569   if (rv != 0) {
570     nghttp2_frame_altsvc_free(&frame->ext, mem);
571     nghttp2_mem_free(mem, item);
572
573     return rv;
574   }
575
576   return 0;
577
578 fail_item_malloc:
579   free(buf);
580
581   return rv;
582 }
583
584 int nghttp2_submit_origin(nghttp2_session *session, uint8_t flags,
585                           const nghttp2_origin_entry *ov, size_t nov) {
586   nghttp2_mem *mem;
587   uint8_t *p;
588   nghttp2_outbound_item *item;
589   nghttp2_frame *frame;
590   nghttp2_ext_origin *origin;
591   nghttp2_origin_entry *ov_copy;
592   size_t len = 0;
593   size_t i;
594   int rv;
595   (void)flags;
596
597   mem = &session->mem;
598
599   if (!session->server) {
600     return NGHTTP2_ERR_INVALID_STATE;
601   }
602
603   if (nov) {
604     for (i = 0; i < nov; ++i) {
605       len += ov[i].origin_len;
606     }
607
608     if (2 * nov + len > NGHTTP2_MAX_PAYLOADLEN) {
609       return NGHTTP2_ERR_INVALID_ARGUMENT;
610     }
611
612     /* The last nov is added for terminal NULL character. */
613     ov_copy =
614         nghttp2_mem_malloc(mem, nov * sizeof(nghttp2_origin_entry) + len + nov);
615     if (ov_copy == NULL) {
616       return NGHTTP2_ERR_NOMEM;
617     }
618
619     p = (uint8_t *)ov_copy + nov * sizeof(nghttp2_origin_entry);
620
621     for (i = 0; i < nov; ++i) {
622       ov_copy[i].origin = p;
623       ov_copy[i].origin_len = ov[i].origin_len;
624       p = nghttp2_cpymem(p, ov[i].origin, ov[i].origin_len);
625       *p++ = '\0';
626     }
627
628     assert((size_t)(p - (uint8_t *)ov_copy) ==
629            nov * sizeof(nghttp2_origin_entry) + len + nov);
630   } else {
631     ov_copy = NULL;
632   }
633
634   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
635   if (item == NULL) {
636     rv = NGHTTP2_ERR_NOMEM;
637     goto fail_item_malloc;
638   }
639
640   nghttp2_outbound_item_init(item);
641
642   item->aux_data.ext.builtin = 1;
643
644   origin = &item->ext_frame_payload.origin;
645
646   frame = &item->frame;
647   frame->ext.payload = origin;
648
649   nghttp2_frame_origin_init(&frame->ext, ov_copy, nov);
650
651   rv = nghttp2_session_add_item(session, item);
652   if (rv != 0) {
653     nghttp2_frame_origin_free(&frame->ext, mem);
654     nghttp2_mem_free(mem, item);
655
656     return rv;
657   }
658
659   return 0;
660
661 fail_item_malloc:
662   free(ov_copy);
663
664   return rv;
665 }
666
667 static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
668                                  const nghttp2_data_provider *data_prd) {
669   uint8_t flags = NGHTTP2_FLAG_NONE;
670   if (data_prd == NULL || data_prd->read_callback == NULL) {
671     flags |= NGHTTP2_FLAG_END_STREAM;
672   }
673
674   if (pri_spec) {
675     flags |= NGHTTP2_FLAG_PRIORITY;
676   }
677
678   return flags;
679 }
680
681 int32_t nghttp2_submit_request(nghttp2_session *session,
682                                const nghttp2_priority_spec *pri_spec,
683                                const nghttp2_nv *nva, size_t nvlen,
684                                const nghttp2_data_provider *data_prd,
685                                void *stream_user_data) {
686   uint8_t flags;
687   int rv;
688
689   if (session->server) {
690     return NGHTTP2_ERR_PROTO;
691   }
692
693   if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
694     rv = detect_self_dependency(session, -1, pri_spec);
695     if (rv != 0) {
696       return rv;
697     }
698   } else {
699     pri_spec = NULL;
700   }
701
702   flags = set_request_flags(pri_spec, data_prd);
703
704   return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen,
705                                    data_prd, stream_user_data);
706 }
707
708 static uint8_t set_response_flags(const nghttp2_data_provider *data_prd) {
709   uint8_t flags = NGHTTP2_FLAG_NONE;
710   if (data_prd == NULL || data_prd->read_callback == NULL) {
711     flags |= NGHTTP2_FLAG_END_STREAM;
712   }
713   return flags;
714 }
715
716 int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
717                             const nghttp2_nv *nva, size_t nvlen,
718                             const nghttp2_data_provider *data_prd) {
719   uint8_t flags;
720
721   if (stream_id <= 0) {
722     return NGHTTP2_ERR_INVALID_ARGUMENT;
723   }
724
725   if (!session->server) {
726     return NGHTTP2_ERR_PROTO;
727   }
728
729   flags = set_response_flags(data_prd);
730   return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen,
731                                    data_prd, NULL);
732 }
733
734 int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
735                         int32_t stream_id,
736                         const nghttp2_data_provider *data_prd) {
737   int rv;
738   nghttp2_outbound_item *item;
739   nghttp2_frame *frame;
740   nghttp2_data_aux_data *aux_data;
741   uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM;
742   nghttp2_mem *mem;
743
744   mem = &session->mem;
745
746   if (stream_id == 0) {
747     return NGHTTP2_ERR_INVALID_ARGUMENT;
748   }
749
750   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
751   if (item == NULL) {
752     return NGHTTP2_ERR_NOMEM;
753   }
754
755   nghttp2_outbound_item_init(item);
756
757   frame = &item->frame;
758   aux_data = &item->aux_data.data;
759   aux_data->data_prd = *data_prd;
760   aux_data->eof = 0;
761   aux_data->flags = nflags;
762
763   /* flags are sent on transmission */
764   nghttp2_frame_data_init(&frame->data, NGHTTP2_FLAG_NONE, stream_id);
765
766   rv = nghttp2_session_add_item(session, item);
767   if (rv != 0) {
768     nghttp2_frame_data_free(&frame->data);
769     nghttp2_mem_free(mem, item);
770     return rv;
771   }
772   return 0;
773 }
774
775 ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen,
776                                       const nghttp2_settings_entry *iv,
777                                       size_t niv) {
778   if (!nghttp2_iv_check(iv, niv)) {
779     return NGHTTP2_ERR_INVALID_ARGUMENT;
780   }
781
782   if (buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) {
783     return NGHTTP2_ERR_INSUFF_BUFSIZE;
784   }
785
786   return (ssize_t)nghttp2_frame_pack_settings_payload(buf, iv, niv);
787 }
788
789 int nghttp2_submit_extension(nghttp2_session *session, uint8_t type,
790                              uint8_t flags, int32_t stream_id, void *payload) {
791   int rv;
792   nghttp2_outbound_item *item;
793   nghttp2_frame *frame;
794   nghttp2_mem *mem;
795
796   mem = &session->mem;
797
798   if (type <= NGHTTP2_CONTINUATION) {
799     return NGHTTP2_ERR_INVALID_ARGUMENT;
800   }
801
802   if (!session->callbacks.pack_extension_callback) {
803     return NGHTTP2_ERR_INVALID_STATE;
804   }
805
806   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
807   if (item == NULL) {
808     return NGHTTP2_ERR_NOMEM;
809   }
810
811   nghttp2_outbound_item_init(item);
812
813   frame = &item->frame;
814   nghttp2_frame_extension_init(&frame->ext, type, flags, stream_id, payload);
815
816   rv = nghttp2_session_add_item(session, item);
817   if (rv != 0) {
818     nghttp2_frame_extension_free(&frame->ext);
819     nghttp2_mem_free(mem, item);
820     return rv;
821   }
822
823   return 0;
824 }