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   } else {
454     stream = nghttp2_session_get_stream(session, stream_id);
455 
456     if (stream == NULL) {
457       return 0;
458     }
459 
460     window_size_increment = window_size - stream->local_window_size;
461 
462     if (window_size_increment == 0) {
463       return 0;
464     }
465 
466     if (window_size_increment < 0) {
467       return nghttp2_adjust_local_window_size(
468           &stream->local_window_size, &stream->recv_window_size,
469           &stream->recv_reduction, &window_size_increment);
470     }
471 
472     rv = nghttp2_increase_local_window_size(
473         &stream->local_window_size, &stream->recv_window_size,
474         &stream->recv_reduction, &window_size_increment);
475 
476     if (rv != 0) {
477       return rv;
478     }
479   }
480 
481   if (window_size_increment > 0) {
482     return nghttp2_session_add_window_update(session, 0, stream_id,
483                                              window_size_increment);
484   }
485 
486   return 0;
487 }
488 
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)489 int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
490                           int32_t stream_id, const uint8_t *origin,
491                           size_t origin_len, const uint8_t *field_value,
492                           size_t field_value_len) {
493   nghttp2_mem *mem;
494   uint8_t *buf, *p;
495   uint8_t *origin_copy;
496   uint8_t *field_value_copy;
497   nghttp2_outbound_item *item;
498   nghttp2_frame *frame;
499   nghttp2_ext_altsvc *altsvc;
500   int rv;
501   (void)flags;
502 
503   mem = &session->mem;
504 
505   if (!session->server) {
506     return NGHTTP2_ERR_INVALID_STATE;
507   }
508 
509   if (2 + origin_len + field_value_len > NGHTTP2_MAX_PAYLOADLEN) {
510     return NGHTTP2_ERR_INVALID_ARGUMENT;
511   }
512 
513   if (stream_id == 0) {
514     if (origin_len == 0) {
515       return NGHTTP2_ERR_INVALID_ARGUMENT;
516     }
517   } else if (origin_len != 0) {
518     return NGHTTP2_ERR_INVALID_ARGUMENT;
519   }
520 
521   buf = nghttp2_mem_malloc(mem, origin_len + field_value_len + 2);
522   if (buf == NULL) {
523     return NGHTTP2_ERR_NOMEM;
524   }
525 
526   p = buf;
527 
528   origin_copy = p;
529   if (origin_len) {
530     p = nghttp2_cpymem(p, origin, origin_len);
531   }
532   *p++ = '\0';
533 
534   field_value_copy = p;
535   if (field_value_len) {
536     p = nghttp2_cpymem(p, field_value, field_value_len);
537   }
538   *p++ = '\0';
539 
540   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
541   if (item == NULL) {
542     rv = NGHTTP2_ERR_NOMEM;
543     goto fail_item_malloc;
544   }
545 
546   nghttp2_outbound_item_init(item);
547 
548   item->aux_data.ext.builtin = 1;
549 
550   altsvc = &item->ext_frame_payload.altsvc;
551 
552   frame = &item->frame;
553   frame->ext.payload = altsvc;
554 
555   nghttp2_frame_altsvc_init(&frame->ext, stream_id, origin_copy, origin_len,
556                             field_value_copy, field_value_len);
557 
558   rv = nghttp2_session_add_item(session, item);
559   if (rv != 0) {
560     nghttp2_frame_altsvc_free(&frame->ext, mem);
561     nghttp2_mem_free(mem, item);
562 
563     return rv;
564   }
565 
566   return 0;
567 
568 fail_item_malloc:
569   free(buf);
570 
571   return rv;
572 }
573 
nghttp2_submit_origin(nghttp2_session * session,uint8_t flags,const nghttp2_origin_entry * ov,size_t nov)574 int nghttp2_submit_origin(nghttp2_session *session, uint8_t flags,
575                           const nghttp2_origin_entry *ov, size_t nov) {
576   nghttp2_mem *mem;
577   uint8_t *p;
578   nghttp2_outbound_item *item;
579   nghttp2_frame *frame;
580   nghttp2_ext_origin *origin;
581   nghttp2_origin_entry *ov_copy;
582   size_t len = 0;
583   size_t i;
584   int rv;
585   (void)flags;
586 
587   mem = &session->mem;
588 
589   if (!session->server) {
590     return NGHTTP2_ERR_INVALID_STATE;
591   }
592 
593   if (nov) {
594     for (i = 0; i < nov; ++i) {
595       len += ov[i].origin_len;
596     }
597 
598     if (2 * nov + len > NGHTTP2_MAX_PAYLOADLEN) {
599       return NGHTTP2_ERR_INVALID_ARGUMENT;
600     }
601 
602     /* The last nov is added for terminal NULL character. */
603     ov_copy =
604         nghttp2_mem_malloc(mem, nov * sizeof(nghttp2_origin_entry) + len + nov);
605     if (ov_copy == NULL) {
606       return NGHTTP2_ERR_NOMEM;
607     }
608 
609     p = (uint8_t *)ov_copy + nov * sizeof(nghttp2_origin_entry);
610 
611     for (i = 0; i < nov; ++i) {
612       ov_copy[i].origin = p;
613       ov_copy[i].origin_len = ov[i].origin_len;
614       p = nghttp2_cpymem(p, ov[i].origin, ov[i].origin_len);
615       *p++ = '\0';
616     }
617 
618     assert((size_t)(p - (uint8_t *)ov_copy) ==
619            nov * sizeof(nghttp2_origin_entry) + len + nov);
620   } else {
621     ov_copy = NULL;
622   }
623 
624   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
625   if (item == NULL) {
626     rv = NGHTTP2_ERR_NOMEM;
627     goto fail_item_malloc;
628   }
629 
630   nghttp2_outbound_item_init(item);
631 
632   item->aux_data.ext.builtin = 1;
633 
634   origin = &item->ext_frame_payload.origin;
635 
636   frame = &item->frame;
637   frame->ext.payload = origin;
638 
639   nghttp2_frame_origin_init(&frame->ext, ov_copy, nov);
640 
641   rv = nghttp2_session_add_item(session, item);
642   if (rv != 0) {
643     nghttp2_frame_origin_free(&frame->ext, mem);
644     nghttp2_mem_free(mem, item);
645 
646     return rv;
647   }
648 
649   return 0;
650 
651 fail_item_malloc:
652   free(ov_copy);
653 
654   return rv;
655 }
656 
set_request_flags(const nghttp2_priority_spec * pri_spec,const nghttp2_data_provider * data_prd)657 static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
658                                  const nghttp2_data_provider *data_prd) {
659   uint8_t flags = NGHTTP2_FLAG_NONE;
660   if (data_prd == NULL || data_prd->read_callback == NULL) {
661     flags |= NGHTTP2_FLAG_END_STREAM;
662   }
663 
664   if (pri_spec) {
665     flags |= NGHTTP2_FLAG_PRIORITY;
666   }
667 
668   return flags;
669 }
670 
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)671 int32_t nghttp2_submit_request(nghttp2_session *session,
672                                const nghttp2_priority_spec *pri_spec,
673                                const nghttp2_nv *nva, size_t nvlen,
674                                const nghttp2_data_provider *data_prd,
675                                void *stream_user_data) {
676   uint8_t flags;
677   int rv;
678 
679   if (session->server) {
680     return NGHTTP2_ERR_PROTO;
681   }
682 
683   if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
684     rv = detect_self_dependency(session, -1, pri_spec);
685     if (rv != 0) {
686       return rv;
687     }
688   } else {
689     pri_spec = NULL;
690   }
691 
692   flags = set_request_flags(pri_spec, data_prd);
693 
694   return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen,
695                                    data_prd, stream_user_data);
696 }
697 
set_response_flags(const nghttp2_data_provider * data_prd)698 static uint8_t set_response_flags(const nghttp2_data_provider *data_prd) {
699   uint8_t flags = NGHTTP2_FLAG_NONE;
700   if (data_prd == NULL || data_prd->read_callback == NULL) {
701     flags |= NGHTTP2_FLAG_END_STREAM;
702   }
703   return flags;
704 }
705 
nghttp2_submit_response(nghttp2_session * session,int32_t stream_id,const nghttp2_nv * nva,size_t nvlen,const nghttp2_data_provider * data_prd)706 int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
707                             const nghttp2_nv *nva, size_t nvlen,
708                             const nghttp2_data_provider *data_prd) {
709   uint8_t flags;
710 
711   if (stream_id <= 0) {
712     return NGHTTP2_ERR_INVALID_ARGUMENT;
713   }
714 
715   if (!session->server) {
716     return NGHTTP2_ERR_PROTO;
717   }
718 
719   flags = set_response_flags(data_prd);
720   return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen,
721                                    data_prd, NULL);
722 }
723 
nghttp2_submit_data(nghttp2_session * session,uint8_t flags,int32_t stream_id,const nghttp2_data_provider * data_prd)724 int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
725                         int32_t stream_id,
726                         const nghttp2_data_provider *data_prd) {
727   int rv;
728   nghttp2_outbound_item *item;
729   nghttp2_frame *frame;
730   nghttp2_data_aux_data *aux_data;
731   uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM;
732   nghttp2_mem *mem;
733 
734   mem = &session->mem;
735 
736   if (stream_id == 0) {
737     return NGHTTP2_ERR_INVALID_ARGUMENT;
738   }
739 
740   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
741   if (item == NULL) {
742     return NGHTTP2_ERR_NOMEM;
743   }
744 
745   nghttp2_outbound_item_init(item);
746 
747   frame = &item->frame;
748   aux_data = &item->aux_data.data;
749   aux_data->data_prd = *data_prd;
750   aux_data->eof = 0;
751   aux_data->flags = nflags;
752 
753   /* flags are sent on transmission */
754   nghttp2_frame_data_init(&frame->data, NGHTTP2_FLAG_NONE, stream_id);
755 
756   rv = nghttp2_session_add_item(session, item);
757   if (rv != 0) {
758     nghttp2_frame_data_free(&frame->data);
759     nghttp2_mem_free(mem, item);
760     return rv;
761   }
762   return 0;
763 }
764 
nghttp2_pack_settings_payload(uint8_t * buf,size_t buflen,const nghttp2_settings_entry * iv,size_t niv)765 ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen,
766                                       const nghttp2_settings_entry *iv,
767                                       size_t niv) {
768   if (!nghttp2_iv_check(iv, niv)) {
769     return NGHTTP2_ERR_INVALID_ARGUMENT;
770   }
771 
772   if (buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) {
773     return NGHTTP2_ERR_INSUFF_BUFSIZE;
774   }
775 
776   return (ssize_t)nghttp2_frame_pack_settings_payload(buf, iv, niv);
777 }
778 
nghttp2_submit_extension(nghttp2_session * session,uint8_t type,uint8_t flags,int32_t stream_id,void * payload)779 int nghttp2_submit_extension(nghttp2_session *session, uint8_t type,
780                              uint8_t flags, int32_t stream_id, void *payload) {
781   int rv;
782   nghttp2_outbound_item *item;
783   nghttp2_frame *frame;
784   nghttp2_mem *mem;
785 
786   mem = &session->mem;
787 
788   if (type <= NGHTTP2_CONTINUATION) {
789     return NGHTTP2_ERR_INVALID_ARGUMENT;
790   }
791 
792   if (!session->callbacks.pack_extension_callback) {
793     return NGHTTP2_ERR_INVALID_STATE;
794   }
795 
796   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
797   if (item == NULL) {
798     return NGHTTP2_ERR_NOMEM;
799   }
800 
801   nghttp2_outbound_item_init(item);
802 
803   frame = &item->frame;
804   nghttp2_frame_extension_init(&frame->ext, type, flags, stream_id, payload);
805 
806   rv = nghttp2_session_add_item(session, item);
807   if (rv != 0) {
808     nghttp2_frame_extension_free(&frame->ext);
809     nghttp2_mem_free(mem, item);
810     return rv;
811   }
812 
813   return 0;
814 }
815