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