• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_wrap * dpw,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_wrap *dpw,
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 (dpw != NULL && dpw->data_prd.read_callback != NULL) {
91     item->aux_data.headers.dpw = *dpw;
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_wrap * dpw,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_wrap *dpw,
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, dpw, 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(
178     session, NGHTTP2_FLAG_END_STREAM, stream_id, NULL, nva, nvlen, NULL, NULL);
179 }
180 
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)181 int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
182                                int32_t stream_id,
183                                const nghttp2_priority_spec *pri_spec,
184                                const nghttp2_nv *nva, size_t nvlen,
185                                void *stream_user_data) {
186   int rv;
187 
188   if (stream_id == -1) {
189     if (session->server) {
190       return NGHTTP2_ERR_PROTO;
191     }
192   } else if (stream_id <= 0) {
193     return NGHTTP2_ERR_INVALID_ARGUMENT;
194   }
195 
196   flags &= NGHTTP2_FLAG_END_STREAM;
197 
198   if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec) &&
199       session->remote_settings.no_rfc7540_priorities != 1) {
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 (session->remote_settings.no_rfc7540_priorities == 1) {
233     return 0;
234   }
235 
236   if (stream_id == 0 || pri_spec == NULL) {
237     return NGHTTP2_ERR_INVALID_ARGUMENT;
238   }
239 
240   if (stream_id == pri_spec->stream_id) {
241     return NGHTTP2_ERR_INVALID_ARGUMENT;
242   }
243 
244   copy_pri_spec = *pri_spec;
245 
246   nghttp2_priority_spec_normalize_weight(&copy_pri_spec);
247 
248   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
249 
250   if (item == NULL) {
251     return NGHTTP2_ERR_NOMEM;
252   }
253 
254   nghttp2_outbound_item_init(item);
255 
256   frame = &item->frame;
257 
258   nghttp2_frame_priority_init(&frame->priority, stream_id, &copy_pri_spec);
259 
260   rv = nghttp2_session_add_item(session, item);
261 
262   if (rv != 0) {
263     nghttp2_frame_priority_free(&frame->priority);
264     nghttp2_mem_free(mem, item);
265 
266     return rv;
267   }
268 
269   return 0;
270 }
271 
nghttp2_submit_rst_stream(nghttp2_session * session,uint8_t flags,int32_t stream_id,uint32_t error_code)272 int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags,
273                               int32_t stream_id, uint32_t error_code) {
274   (void)flags;
275 
276   if (stream_id == 0) {
277     return NGHTTP2_ERR_INVALID_ARGUMENT;
278   }
279 
280   return nghttp2_session_add_rst_stream(session, stream_id, error_code);
281 }
282 
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)283 int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags,
284                           int32_t last_stream_id, uint32_t error_code,
285                           const uint8_t *opaque_data, size_t opaque_data_len) {
286   (void)flags;
287 
288   if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
289     return 0;
290   }
291   return nghttp2_session_add_goaway(session, last_stream_id, error_code,
292                                     opaque_data, opaque_data_len,
293                                     NGHTTP2_GOAWAY_AUX_NONE);
294 }
295 
nghttp2_submit_shutdown_notice(nghttp2_session * session)296 int nghttp2_submit_shutdown_notice(nghttp2_session *session) {
297   if (!session->server) {
298     return NGHTTP2_ERR_INVALID_STATE;
299   }
300   if (session->goaway_flags) {
301     return 0;
302   }
303   return nghttp2_session_add_goaway(session, (1u << 31) - 1, NGHTTP2_NO_ERROR,
304                                     NULL, 0,
305                                     NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE);
306 }
307 
nghttp2_submit_settings(nghttp2_session * session,uint8_t flags,const nghttp2_settings_entry * iv,size_t niv)308 int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
309                             const nghttp2_settings_entry *iv, size_t niv) {
310   (void)flags;
311   return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv);
312 }
313 
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)314 int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
315                                     int32_t stream_id, const nghttp2_nv *nva,
316                                     size_t nvlen,
317                                     void *promised_stream_user_data) {
318   nghttp2_outbound_item *item;
319   nghttp2_frame *frame;
320   nghttp2_nv *nva_copy;
321   uint8_t flags_copy;
322   int32_t promised_stream_id;
323   int rv;
324   nghttp2_mem *mem;
325   (void)flags;
326 
327   mem = &session->mem;
328 
329   if (stream_id <= 0 || nghttp2_session_is_my_stream_id(session, stream_id)) {
330     return NGHTTP2_ERR_INVALID_ARGUMENT;
331   }
332 
333   if (!session->server) {
334     return NGHTTP2_ERR_PROTO;
335   }
336 
337   /* All 32bit signed stream IDs are spent. */
338   if (session->next_stream_id > INT32_MAX) {
339     return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
340   }
341 
342   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
343   if (item == NULL) {
344     return NGHTTP2_ERR_NOMEM;
345   }
346 
347   nghttp2_outbound_item_init(item);
348 
349   item->aux_data.headers.stream_user_data = promised_stream_user_data;
350 
351   frame = &item->frame;
352 
353   rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem);
354   if (rv < 0) {
355     nghttp2_mem_free(mem, item);
356     return rv;
357   }
358 
359   flags_copy = NGHTTP2_FLAG_END_HEADERS;
360 
361   promised_stream_id = (int32_t)session->next_stream_id;
362   session->next_stream_id += 2;
363 
364   nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, stream_id,
365                                   promised_stream_id, nva_copy, nvlen);
366 
367   rv = nghttp2_session_add_item(session, item);
368 
369   if (rv != 0) {
370     nghttp2_frame_push_promise_free(&frame->push_promise, mem);
371     nghttp2_mem_free(mem, item);
372 
373     return rv;
374   }
375 
376   return promised_stream_id;
377 }
378 
nghttp2_submit_window_update(nghttp2_session * session,uint8_t flags,int32_t stream_id,int32_t window_size_increment)379 int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
380                                  int32_t stream_id,
381                                  int32_t window_size_increment) {
382   int rv;
383   nghttp2_stream *stream = 0;
384   (void)flags;
385 
386   if (window_size_increment == 0) {
387     return 0;
388   }
389   if (stream_id == 0) {
390     rv = nghttp2_adjust_local_window_size(
391       &session->local_window_size, &session->recv_window_size,
392       &session->recv_reduction, &window_size_increment);
393     if (rv != 0) {
394       return rv;
395     }
396   } else {
397     stream = nghttp2_session_get_stream(session, stream_id);
398     if (!stream) {
399       return 0;
400     }
401 
402     rv = nghttp2_adjust_local_window_size(
403       &stream->local_window_size, &stream->recv_window_size,
404       &stream->recv_reduction, &window_size_increment);
405     if (rv != 0) {
406       return rv;
407     }
408   }
409 
410   if (window_size_increment > 0) {
411     if (stream_id == 0) {
412       session->consumed_size =
413         nghttp2_max_int32(0, session->consumed_size - window_size_increment);
414     } else {
415       stream->consumed_size =
416         nghttp2_max_int32(0, stream->consumed_size - window_size_increment);
417     }
418 
419     return nghttp2_session_add_window_update(session, 0, stream_id,
420                                              window_size_increment);
421   }
422   return 0;
423 }
424 
nghttp2_session_set_local_window_size(nghttp2_session * session,uint8_t flags,int32_t stream_id,int32_t window_size)425 int nghttp2_session_set_local_window_size(nghttp2_session *session,
426                                           uint8_t flags, int32_t stream_id,
427                                           int32_t window_size) {
428   int32_t window_size_increment;
429   nghttp2_stream *stream;
430   int rv;
431   (void)flags;
432 
433   if (window_size < 0) {
434     return NGHTTP2_ERR_INVALID_ARGUMENT;
435   }
436 
437   if (stream_id == 0) {
438     window_size_increment = window_size - session->local_window_size;
439 
440     if (window_size_increment == 0) {
441       return 0;
442     }
443 
444     if (window_size_increment < 0) {
445       return nghttp2_adjust_local_window_size(
446         &session->local_window_size, &session->recv_window_size,
447         &session->recv_reduction, &window_size_increment);
448     }
449 
450     rv = nghttp2_increase_local_window_size(
451       &session->local_window_size, &session->recv_window_size,
452       &session->recv_reduction, &window_size_increment);
453 
454     if (rv != 0) {
455       return rv;
456     }
457 
458     if (window_size_increment > 0) {
459       return nghttp2_session_add_window_update(session, 0, stream_id,
460                                                window_size_increment);
461     }
462 
463     return nghttp2_session_update_recv_connection_window_size(session, 0);
464   } else {
465     stream = nghttp2_session_get_stream(session, stream_id);
466 
467     if (stream == NULL) {
468       return 0;
469     }
470 
471     window_size_increment = window_size - stream->local_window_size;
472 
473     if (window_size_increment == 0) {
474       return 0;
475     }
476 
477     if (window_size_increment < 0) {
478       return nghttp2_adjust_local_window_size(
479         &stream->local_window_size, &stream->recv_window_size,
480         &stream->recv_reduction, &window_size_increment);
481     }
482 
483     rv = nghttp2_increase_local_window_size(
484       &stream->local_window_size, &stream->recv_window_size,
485       &stream->recv_reduction, &window_size_increment);
486 
487     if (rv != 0) {
488       return rv;
489     }
490 
491     if (window_size_increment > 0) {
492       return nghttp2_session_add_window_update(session, 0, stream_id,
493                                                window_size_increment);
494     }
495 
496     return nghttp2_session_update_recv_stream_window_size(session, stream, 0,
497                                                           1);
498   }
499 }
500 
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)501 int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
502                           int32_t stream_id, const uint8_t *origin,
503                           size_t origin_len, const uint8_t *field_value,
504                           size_t field_value_len) {
505   nghttp2_mem *mem;
506   uint8_t *buf, *p;
507   uint8_t *origin_copy;
508   uint8_t *field_value_copy;
509   nghttp2_outbound_item *item;
510   nghttp2_frame *frame;
511   nghttp2_ext_altsvc *altsvc;
512   int rv;
513   (void)flags;
514 
515   mem = &session->mem;
516 
517   if (!session->server) {
518     return NGHTTP2_ERR_INVALID_STATE;
519   }
520 
521   if (2 + origin_len + field_value_len > NGHTTP2_MAX_PAYLOADLEN) {
522     return NGHTTP2_ERR_INVALID_ARGUMENT;
523   }
524 
525   if (stream_id == 0) {
526     if (origin_len == 0) {
527       return NGHTTP2_ERR_INVALID_ARGUMENT;
528     }
529   } else if (origin_len != 0) {
530     return NGHTTP2_ERR_INVALID_ARGUMENT;
531   }
532 
533   buf = nghttp2_mem_malloc(mem, origin_len + field_value_len + 2);
534   if (buf == NULL) {
535     return NGHTTP2_ERR_NOMEM;
536   }
537 
538   p = buf;
539 
540   origin_copy = p;
541   if (origin_len) {
542     p = nghttp2_cpymem(p, origin, origin_len);
543   }
544   *p++ = '\0';
545 
546   field_value_copy = p;
547   if (field_value_len) {
548     p = nghttp2_cpymem(p, field_value, field_value_len);
549   }
550   *p++ = '\0';
551 
552   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
553   if (item == NULL) {
554     rv = NGHTTP2_ERR_NOMEM;
555     goto fail_item_malloc;
556   }
557 
558   nghttp2_outbound_item_init(item);
559 
560   item->aux_data.ext.builtin = 1;
561 
562   altsvc = &item->ext_frame_payload.altsvc;
563 
564   frame = &item->frame;
565   frame->ext.payload = altsvc;
566 
567   nghttp2_frame_altsvc_init(&frame->ext, stream_id, origin_copy, origin_len,
568                             field_value_copy, field_value_len);
569 
570   rv = nghttp2_session_add_item(session, item);
571   if (rv != 0) {
572     nghttp2_frame_altsvc_free(&frame->ext, mem);
573     nghttp2_mem_free(mem, item);
574 
575     return rv;
576   }
577 
578   return 0;
579 
580 fail_item_malloc:
581   free(buf);
582 
583   return rv;
584 }
585 
nghttp2_submit_origin(nghttp2_session * session,uint8_t flags,const nghttp2_origin_entry * ov,size_t nov)586 int nghttp2_submit_origin(nghttp2_session *session, uint8_t flags,
587                           const nghttp2_origin_entry *ov, size_t nov) {
588   nghttp2_mem *mem;
589   uint8_t *p;
590   nghttp2_outbound_item *item;
591   nghttp2_frame *frame;
592   nghttp2_ext_origin *origin;
593   nghttp2_origin_entry *ov_copy;
594   size_t len = 0;
595   size_t i;
596   int rv;
597   (void)flags;
598 
599   mem = &session->mem;
600 
601   if (!session->server) {
602     return NGHTTP2_ERR_INVALID_STATE;
603   }
604 
605   if (nov) {
606     for (i = 0; i < nov; ++i) {
607       len += ov[i].origin_len;
608     }
609 
610     if (2 * nov + len > NGHTTP2_MAX_PAYLOADLEN) {
611       return NGHTTP2_ERR_INVALID_ARGUMENT;
612     }
613 
614     /* The last nov is added for terminal NULL character. */
615     ov_copy =
616       nghttp2_mem_malloc(mem, nov * sizeof(nghttp2_origin_entry) + len + nov);
617     if (ov_copy == NULL) {
618       return NGHTTP2_ERR_NOMEM;
619     }
620 
621     p = (uint8_t *)ov_copy + nov * sizeof(nghttp2_origin_entry);
622 
623     for (i = 0; i < nov; ++i) {
624       ov_copy[i].origin = p;
625       ov_copy[i].origin_len = ov[i].origin_len;
626       p = nghttp2_cpymem(p, ov[i].origin, ov[i].origin_len);
627       *p++ = '\0';
628     }
629 
630     assert((size_t)(p - (uint8_t *)ov_copy) ==
631            nov * sizeof(nghttp2_origin_entry) + len + nov);
632   } else {
633     ov_copy = NULL;
634   }
635 
636   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
637   if (item == NULL) {
638     rv = NGHTTP2_ERR_NOMEM;
639     goto fail_item_malloc;
640   }
641 
642   nghttp2_outbound_item_init(item);
643 
644   item->aux_data.ext.builtin = 1;
645 
646   origin = &item->ext_frame_payload.origin;
647 
648   frame = &item->frame;
649   frame->ext.payload = origin;
650 
651   nghttp2_frame_origin_init(&frame->ext, ov_copy, nov);
652 
653   rv = nghttp2_session_add_item(session, item);
654   if (rv != 0) {
655     nghttp2_frame_origin_free(&frame->ext, mem);
656     nghttp2_mem_free(mem, item);
657 
658     return rv;
659   }
660 
661   return 0;
662 
663 fail_item_malloc:
664   free(ov_copy);
665 
666   return rv;
667 }
668 
nghttp2_submit_priority_update(nghttp2_session * session,uint8_t flags,int32_t stream_id,const uint8_t * field_value,size_t field_value_len)669 int nghttp2_submit_priority_update(nghttp2_session *session, uint8_t flags,
670                                    int32_t stream_id,
671                                    const uint8_t *field_value,
672                                    size_t field_value_len) {
673   nghttp2_mem *mem;
674   uint8_t *buf, *p;
675   nghttp2_outbound_item *item;
676   nghttp2_frame *frame;
677   nghttp2_ext_priority_update *priority_update;
678   int rv;
679   (void)flags;
680 
681   mem = &session->mem;
682 
683   if (session->server) {
684     return NGHTTP2_ERR_INVALID_STATE;
685   }
686 
687   if (session->remote_settings.no_rfc7540_priorities == 0) {
688     return 0;
689   }
690 
691   if (stream_id == 0 || 4 + field_value_len > NGHTTP2_MAX_PAYLOADLEN) {
692     return NGHTTP2_ERR_INVALID_ARGUMENT;
693   }
694 
695   if (field_value_len) {
696     buf = nghttp2_mem_malloc(mem, field_value_len + 1);
697     if (buf == NULL) {
698       return NGHTTP2_ERR_NOMEM;
699     }
700 
701     p = nghttp2_cpymem(buf, field_value, field_value_len);
702     *p = '\0';
703   } else {
704     buf = NULL;
705   }
706 
707   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
708   if (item == NULL) {
709     rv = NGHTTP2_ERR_NOMEM;
710     goto fail_item_malloc;
711   }
712 
713   nghttp2_outbound_item_init(item);
714 
715   item->aux_data.ext.builtin = 1;
716 
717   priority_update = &item->ext_frame_payload.priority_update;
718 
719   frame = &item->frame;
720   frame->ext.payload = priority_update;
721 
722   nghttp2_frame_priority_update_init(&frame->ext, stream_id, buf,
723                                      field_value_len);
724 
725   rv = nghttp2_session_add_item(session, item);
726   if (rv != 0) {
727     nghttp2_frame_priority_update_free(&frame->ext, mem);
728     nghttp2_mem_free(mem, item);
729 
730     return rv;
731   }
732 
733   return 0;
734 
735 fail_item_malloc:
736   free(buf);
737 
738   return rv;
739 }
740 
set_request_flags(const nghttp2_priority_spec * pri_spec,const nghttp2_data_provider_wrap * dpw)741 static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
742                                  const nghttp2_data_provider_wrap *dpw) {
743   uint8_t flags = NGHTTP2_FLAG_NONE;
744   if (dpw == NULL || dpw->data_prd.read_callback == NULL) {
745     flags |= NGHTTP2_FLAG_END_STREAM;
746   }
747 
748   if (pri_spec) {
749     flags |= NGHTTP2_FLAG_PRIORITY;
750   }
751 
752   return flags;
753 }
754 
submit_request_shared(nghttp2_session * session,const nghttp2_priority_spec * pri_spec,const nghttp2_nv * nva,size_t nvlen,const nghttp2_data_provider_wrap * dpw,void * stream_user_data)755 static int32_t submit_request_shared(nghttp2_session *session,
756                                      const nghttp2_priority_spec *pri_spec,
757                                      const nghttp2_nv *nva, size_t nvlen,
758                                      const nghttp2_data_provider_wrap *dpw,
759                                      void *stream_user_data) {
760   uint8_t flags;
761   int rv;
762 
763   if (session->server) {
764     return NGHTTP2_ERR_PROTO;
765   }
766 
767   if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec) &&
768       session->remote_settings.no_rfc7540_priorities != 1) {
769     rv = detect_self_dependency(session, -1, pri_spec);
770     if (rv != 0) {
771       return rv;
772     }
773   } else {
774     pri_spec = NULL;
775   }
776 
777   flags = set_request_flags(pri_spec, dpw);
778 
779   return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen,
780                                    dpw, stream_user_data);
781 }
782 
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)783 int32_t nghttp2_submit_request(nghttp2_session *session,
784                                const nghttp2_priority_spec *pri_spec,
785                                const nghttp2_nv *nva, size_t nvlen,
786                                const nghttp2_data_provider *data_prd,
787                                void *stream_user_data) {
788   nghttp2_data_provider_wrap dpw;
789 
790   return submit_request_shared(session, pri_spec, nva, nvlen,
791                                nghttp2_data_provider_wrap_v1(&dpw, data_prd),
792                                stream_user_data);
793 }
794 
nghttp2_submit_request2(nghttp2_session * session,const nghttp2_priority_spec * pri_spec,const nghttp2_nv * nva,size_t nvlen,const nghttp2_data_provider2 * data_prd,void * stream_user_data)795 int32_t nghttp2_submit_request2(nghttp2_session *session,
796                                 const nghttp2_priority_spec *pri_spec,
797                                 const nghttp2_nv *nva, size_t nvlen,
798                                 const nghttp2_data_provider2 *data_prd,
799                                 void *stream_user_data) {
800   nghttp2_data_provider_wrap dpw;
801 
802   return submit_request_shared(session, pri_spec, nva, nvlen,
803                                nghttp2_data_provider_wrap_v2(&dpw, data_prd),
804                                stream_user_data);
805 }
806 
set_response_flags(const nghttp2_data_provider_wrap * dpw)807 static uint8_t set_response_flags(const nghttp2_data_provider_wrap *dpw) {
808   uint8_t flags = NGHTTP2_FLAG_NONE;
809   if (dpw == NULL || dpw->data_prd.read_callback == NULL) {
810     flags |= NGHTTP2_FLAG_END_STREAM;
811   }
812   return flags;
813 }
814 
submit_response_shared(nghttp2_session * session,int32_t stream_id,const nghttp2_nv * nva,size_t nvlen,const nghttp2_data_provider_wrap * dpw)815 static int submit_response_shared(nghttp2_session *session, int32_t stream_id,
816                                   const nghttp2_nv *nva, size_t nvlen,
817                                   const nghttp2_data_provider_wrap *dpw) {
818   uint8_t flags;
819 
820   if (stream_id <= 0) {
821     return NGHTTP2_ERR_INVALID_ARGUMENT;
822   }
823 
824   if (!session->server) {
825     return NGHTTP2_ERR_PROTO;
826   }
827 
828   flags = set_response_flags(dpw);
829   return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen,
830                                    dpw, NULL);
831 }
832 
nghttp2_submit_response(nghttp2_session * session,int32_t stream_id,const nghttp2_nv * nva,size_t nvlen,const nghttp2_data_provider * data_prd)833 int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
834                             const nghttp2_nv *nva, size_t nvlen,
835                             const nghttp2_data_provider *data_prd) {
836   nghttp2_data_provider_wrap dpw;
837 
838   return submit_response_shared(session, stream_id, nva, nvlen,
839                                 nghttp2_data_provider_wrap_v1(&dpw, data_prd));
840 }
841 
nghttp2_submit_response2(nghttp2_session * session,int32_t stream_id,const nghttp2_nv * nva,size_t nvlen,const nghttp2_data_provider2 * data_prd)842 int nghttp2_submit_response2(nghttp2_session *session, int32_t stream_id,
843                              const nghttp2_nv *nva, size_t nvlen,
844                              const nghttp2_data_provider2 *data_prd) {
845   nghttp2_data_provider_wrap dpw;
846 
847   return submit_response_shared(session, stream_id, nva, nvlen,
848                                 nghttp2_data_provider_wrap_v2(&dpw, data_prd));
849 }
850 
nghttp2_submit_data_shared(nghttp2_session * session,uint8_t flags,int32_t stream_id,const nghttp2_data_provider_wrap * dpw)851 int nghttp2_submit_data_shared(nghttp2_session *session, uint8_t flags,
852                                int32_t stream_id,
853                                const nghttp2_data_provider_wrap *dpw) {
854   int rv;
855   nghttp2_outbound_item *item;
856   nghttp2_frame *frame;
857   nghttp2_data_aux_data *aux_data;
858   uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM;
859   nghttp2_mem *mem;
860 
861   mem = &session->mem;
862 
863   if (stream_id == 0) {
864     return NGHTTP2_ERR_INVALID_ARGUMENT;
865   }
866 
867   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
868   if (item == NULL) {
869     return NGHTTP2_ERR_NOMEM;
870   }
871 
872   nghttp2_outbound_item_init(item);
873 
874   frame = &item->frame;
875   aux_data = &item->aux_data.data;
876   aux_data->dpw = *dpw;
877   aux_data->eof = 0;
878   aux_data->flags = nflags;
879 
880   /* flags are sent on transmission */
881   nghttp2_frame_data_init(&frame->data, NGHTTP2_FLAG_NONE, stream_id);
882 
883   rv = nghttp2_session_add_item(session, item);
884   if (rv != 0) {
885     nghttp2_frame_data_free(&frame->data);
886     nghttp2_mem_free(mem, item);
887     return rv;
888   }
889   return 0;
890 }
891 
nghttp2_submit_data(nghttp2_session * session,uint8_t flags,int32_t stream_id,const nghttp2_data_provider * data_prd)892 int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
893                         int32_t stream_id,
894                         const nghttp2_data_provider *data_prd) {
895   nghttp2_data_provider_wrap dpw;
896 
897   assert(data_prd);
898 
899   return nghttp2_submit_data_shared(
900     session, flags, stream_id, nghttp2_data_provider_wrap_v1(&dpw, data_prd));
901 }
902 
nghttp2_submit_data2(nghttp2_session * session,uint8_t flags,int32_t stream_id,const nghttp2_data_provider2 * data_prd)903 int nghttp2_submit_data2(nghttp2_session *session, uint8_t flags,
904                          int32_t stream_id,
905                          const nghttp2_data_provider2 *data_prd) {
906   nghttp2_data_provider_wrap dpw;
907 
908   assert(data_prd);
909 
910   return nghttp2_submit_data_shared(
911     session, flags, stream_id, nghttp2_data_provider_wrap_v2(&dpw, data_prd));
912 }
913 
nghttp2_pack_settings_payload(uint8_t * buf,size_t buflen,const nghttp2_settings_entry * iv,size_t niv)914 ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen,
915                                       const nghttp2_settings_entry *iv,
916                                       size_t niv) {
917   return (ssize_t)nghttp2_pack_settings_payload2(buf, buflen, iv, niv);
918 }
919 
nghttp2_pack_settings_payload2(uint8_t * buf,size_t buflen,const nghttp2_settings_entry * iv,size_t niv)920 nghttp2_ssize nghttp2_pack_settings_payload2(uint8_t *buf, size_t buflen,
921                                              const nghttp2_settings_entry *iv,
922                                              size_t niv) {
923   if (!nghttp2_iv_check(iv, niv)) {
924     return NGHTTP2_ERR_INVALID_ARGUMENT;
925   }
926 
927   if (buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) {
928     return NGHTTP2_ERR_INSUFF_BUFSIZE;
929   }
930 
931   return (nghttp2_ssize)nghttp2_frame_pack_settings_payload(buf, iv, niv);
932 }
933 
nghttp2_submit_extension(nghttp2_session * session,uint8_t type,uint8_t flags,int32_t stream_id,void * payload)934 int nghttp2_submit_extension(nghttp2_session *session, uint8_t type,
935                              uint8_t flags, int32_t stream_id, void *payload) {
936   int rv;
937   nghttp2_outbound_item *item;
938   nghttp2_frame *frame;
939   nghttp2_mem *mem;
940 
941   mem = &session->mem;
942 
943   if (type <= NGHTTP2_CONTINUATION) {
944     return NGHTTP2_ERR_INVALID_ARGUMENT;
945   }
946 
947   if (!session->callbacks.pack_extension_callback2 &&
948       !session->callbacks.pack_extension_callback) {
949     return NGHTTP2_ERR_INVALID_STATE;
950   }
951 
952   item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
953   if (item == NULL) {
954     return NGHTTP2_ERR_NOMEM;
955   }
956 
957   nghttp2_outbound_item_init(item);
958 
959   frame = &item->frame;
960   nghttp2_frame_extension_init(&frame->ext, type, flags, stream_id, payload);
961 
962   rv = nghttp2_session_add_item(session, item);
963   if (rv != 0) {
964     nghttp2_frame_extension_free(&frame->ext);
965     nghttp2_mem_free(mem, item);
966     return rv;
967   }
968 
969   return 0;
970 }
971