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  */
detect_self_dependency(nghttp2_session * session,int32_t stream_id,const nghttp2_priority_spec * pri_spec)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. */
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)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 
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)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 
nghttp2_submit_trailer(nghttp2_session * session,int32_t stream_id,const nghttp2_nv * nva,size_t nvlen)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 
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)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 
nghttp2_submit_ping(nghttp2_session * session,uint8_t flags,const uint8_t * opaque_data)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 
nghttp2_submit_priority(nghttp2_session * session,uint8_t flags,int32_t stream_id,const nghttp2_priority_spec * pri_spec)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 
nghttp2_submit_rst_stream(nghttp2_session * session,uint8_t flags,int32_t stream_id,uint32_t error_code)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 
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)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 
nghttp2_submit_shutdown_notice(nghttp2_session * session)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 
nghttp2_submit_settings(nghttp2_session * session,uint8_t flags,const nghttp2_settings_entry * iv,size_t niv)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 
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)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 
nghttp2_submit_window_update(nghttp2_session * session,uint8_t flags,int32_t stream_id,int32_t window_size_increment)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 
nghttp2_session_set_local_window_size(nghttp2_session * session,uint8_t flags,int32_t stream_id,int32_t window_size)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 
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)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 
nghttp2_submit_origin(nghttp2_session * session,uint8_t flags,const nghttp2_origin_entry * ov,size_t nov)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 
set_request_flags(const nghttp2_priority_spec * pri_spec,const nghttp2_data_provider * data_prd)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 
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)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 
set_response_flags(const nghttp2_data_provider * data_prd)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 
nghttp2_submit_response(nghttp2_session * session,int32_t stream_id,const nghttp2_nv * nva,size_t nvlen,const nghttp2_data_provider * data_prd)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 
nghttp2_submit_data(nghttp2_session * session,uint8_t flags,int32_t stream_id,const nghttp2_data_provider * data_prd)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 
nghttp2_pack_settings_payload(uint8_t * buf,size_t buflen,const nghttp2_settings_entry * iv,size_t niv)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 
nghttp2_submit_extension(nghttp2_session * session,uint8_t type,uint8_t flags,int32_t stream_id,void * payload)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 }
825