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