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(©_pri_spec);
158 } else {
159 nghttp2_priority_spec_default_init(©_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, ©_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(©_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, ©_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