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
32 #include "nghttp2_frame.h"
33 #include "nghttp2_helper.h"
34 #include "nghttp2_priority_spec.h"
35
36 /*
37 * Detects the dependency error, that is stream attempted to depend on
38 * itself. If |stream_id| is -1, we use session->next_stream_id as
39 * stream ID.
40 *
41 * This function returns 0 if it succeeds, or one of the following
42 * error codes:
43 *
44 * NGHTTP2_ERR_INVALID_ARGUMENT
45 * Stream attempted to depend on itself.
46 */
detect_self_dependency(nghttp2_session * session,int32_t stream_id,const nghttp2_priority_spec * pri_spec)47 static int detect_self_dependency(nghttp2_session *session, int32_t stream_id,
48 const nghttp2_priority_spec *pri_spec)
49 {
50 assert(pri_spec);
51
52 if (stream_id == -1) {
53 if ((int32_t)session->next_stream_id == pri_spec->stream_id) {
54 return NGHTTP2_ERR_INVALID_ARGUMENT;
55 }
56 return 0;
57 }
58
59 if (stream_id == pri_spec->stream_id) {
60 return NGHTTP2_ERR_INVALID_ARGUMENT;
61 }
62
63 return 0;
64 }
65
66 /* This function takes ownership of |nva_copy|. Regardless of the
67 return value, the caller must not free |nva_copy| after this
68 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)69 static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
70 int32_t stream_id,
71 const nghttp2_priority_spec *pri_spec,
72 nghttp2_nv *nva_copy, size_t nvlen,
73 const nghttp2_data_provider *data_prd,
74 void *stream_user_data)
75 {
76 int rv;
77 uint8_t flags_copy;
78 nghttp2_outbound_item *item = NULL;
79 nghttp2_frame *frame = NULL;
80 nghttp2_headers_category hcat;
81 nghttp2_mem *mem;
82
83 mem = &session->mem;
84
85 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
86 if (item == NULL) {
87 rv = NGHTTP2_ERR_NOMEM;
88 goto fail;
89 }
90
91 nghttp2_outbound_item_init(item);
92
93 if (data_prd != NULL && data_prd->read_callback != NULL) {
94 item->aux_data.headers.data_prd = *data_prd;
95 }
96
97 item->aux_data.headers.stream_user_data = stream_user_data;
98
99 flags_copy =
100 (uint8_t)((flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) |
101 NGHTTP2_FLAG_END_HEADERS);
102
103 if (stream_id == -1) {
104 if (session->next_stream_id > INT32_MAX) {
105 rv = NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
106 goto fail;
107 }
108
109 stream_id = (int32_t)session->next_stream_id;
110 session->next_stream_id += 2;
111
112 hcat = NGHTTP2_HCAT_REQUEST;
113 } else {
114 /* More specific categorization will be done later. */
115 hcat = NGHTTP2_HCAT_HEADERS;
116 }
117
118 frame = &item->frame;
119
120 nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat,
121 pri_spec, nva_copy, nvlen);
122
123 rv = nghttp2_session_add_item(session, item);
124
125 if (rv != 0) {
126 nghttp2_frame_headers_free(&frame->headers, mem);
127 goto fail2;
128 }
129
130 if (hcat == NGHTTP2_HCAT_REQUEST) {
131 return stream_id;
132 }
133
134 return 0;
135
136 fail:
137 /* nghttp2_frame_headers_init() takes ownership of nva_copy. */
138 nghttp2_nv_array_del(nva_copy, mem);
139 fail2:
140 nghttp2_mem_free(mem, item);
141
142 return rv;
143 }
144
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)145 static int32_t submit_headers_shared_nva(nghttp2_session *session,
146 uint8_t flags, int32_t stream_id,
147 const nghttp2_priority_spec *pri_spec,
148 const nghttp2_nv *nva, size_t nvlen,
149 const nghttp2_data_provider *data_prd,
150 void *stream_user_data)
151 {
152 int rv;
153 nghttp2_nv *nva_copy;
154 nghttp2_priority_spec copy_pri_spec;
155 nghttp2_mem *mem;
156
157 mem = &session->mem;
158
159 if (pri_spec) {
160 copy_pri_spec = *pri_spec;
161 nghttp2_priority_spec_normalize_weight(©_pri_spec);
162 } else {
163 nghttp2_priority_spec_default_init(©_pri_spec);
164 }
165
166 rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem);
167 if (rv < 0) {
168 return rv;
169 }
170
171 return submit_headers_shared(session, flags, stream_id, ©_pri_spec,
172 nva_copy, nvlen, data_prd, stream_user_data);
173 }
174
nghttp2_submit_trailer(nghttp2_session * session,int32_t stream_id,const nghttp2_nv * nva,size_t nvlen)175 int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
176 const nghttp2_nv *nva, size_t nvlen)
177 {
178 if (stream_id <= 0) {
179 return NGHTTP2_ERR_INVALID_ARGUMENT;
180 }
181
182 return (int)submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM,
183 stream_id, NULL, nva, nvlen, NULL,
184 NULL);
185 }
186
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)187 int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
188 int32_t stream_id,
189 const nghttp2_priority_spec *pri_spec,
190 const nghttp2_nv *nva, size_t nvlen,
191 void *stream_user_data)
192 {
193 int rv;
194
195 if (stream_id == -1) {
196 if (session->server) {
197 return NGHTTP2_ERR_PROTO;
198 }
199 } else if (stream_id <= 0) {
200 return NGHTTP2_ERR_INVALID_ARGUMENT;
201 }
202
203 flags &= NGHTTP2_FLAG_END_STREAM;
204
205 if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
206 rv = detect_self_dependency(session, stream_id, pri_spec);
207 if (rv != 0) {
208 return rv;
209 }
210
211 flags |= NGHTTP2_FLAG_PRIORITY;
212 } else {
213 pri_spec = NULL;
214 }
215
216 return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva,
217 nvlen, NULL, stream_user_data);
218 }
219
nghttp2_submit_ping(nghttp2_session * session,uint8_t flags,const uint8_t * opaque_data)220 int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,
221 const uint8_t *opaque_data)
222 {
223 flags &= NGHTTP2_FLAG_ACK;
224 return nghttp2_session_add_ping(session, flags, opaque_data);
225 }
226
nghttp2_submit_priority(nghttp2_session * session,uint8_t flags,int32_t stream_id,const nghttp2_priority_spec * pri_spec)227 int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
228 int32_t stream_id,
229 const nghttp2_priority_spec *pri_spec)
230 {
231 int rv;
232 nghttp2_outbound_item *item;
233 nghttp2_frame *frame;
234 nghttp2_priority_spec copy_pri_spec;
235 nghttp2_mem *mem;
236 (void)flags;
237
238 mem = &session->mem;
239
240 if (stream_id == 0 || pri_spec == NULL) {
241 return NGHTTP2_ERR_INVALID_ARGUMENT;
242 }
243
244 if (stream_id == pri_spec->stream_id) {
245 return NGHTTP2_ERR_INVALID_ARGUMENT;
246 }
247
248 copy_pri_spec = *pri_spec;
249
250 nghttp2_priority_spec_normalize_weight(©_pri_spec);
251
252 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
253
254 if (item == NULL) {
255 return NGHTTP2_ERR_NOMEM;
256 }
257
258 nghttp2_outbound_item_init(item);
259
260 frame = &item->frame;
261
262 nghttp2_frame_priority_init(&frame->priority, stream_id, ©_pri_spec);
263
264 rv = nghttp2_session_add_item(session, item);
265
266 if (rv != 0) {
267 nghttp2_frame_priority_free(&frame->priority);
268 nghttp2_mem_free(mem, item);
269
270 return rv;
271 }
272
273 return 0;
274 }
275
nghttp2_submit_rst_stream(nghttp2_session * session,uint8_t flags,int32_t stream_id,uint32_t error_code)276 int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags,
277 int32_t stream_id, uint32_t error_code)
278 {
279 (void)flags;
280
281 if (stream_id == 0) {
282 return NGHTTP2_ERR_INVALID_ARGUMENT;
283 }
284
285 return nghttp2_session_add_rst_stream(session, stream_id, error_code);
286 }
287
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)288 int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags,
289 int32_t last_stream_id, uint32_t error_code,
290 const uint8_t *opaque_data, size_t opaque_data_len)
291 {
292 (void)flags;
293
294 if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
295 return 0;
296 }
297 return nghttp2_session_add_goaway(session, last_stream_id, error_code,
298 opaque_data, opaque_data_len,
299 NGHTTP2_GOAWAY_AUX_NONE);
300 }
301
nghttp2_submit_shutdown_notice(nghttp2_session * session)302 int nghttp2_submit_shutdown_notice(nghttp2_session *session)
303 {
304 if (!session->server) {
305 return NGHTTP2_ERR_INVALID_STATE;
306 }
307 if (session->goaway_flags) {
308 return 0;
309 }
310 return nghttp2_session_add_goaway(session, (1u << 31) - 1, NGHTTP2_NO_ERROR,
311 NULL, 0,
312 NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE);
313 }
314
nghttp2_submit_settings(nghttp2_session * session,uint8_t flags,const nghttp2_settings_entry * iv,size_t niv)315 int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags,
316 const nghttp2_settings_entry *iv, size_t niv)
317 {
318 (void)flags;
319 return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv);
320 }
321
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)322 int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,
323 int32_t stream_id, const nghttp2_nv *nva,
324 size_t nvlen,
325 void *promised_stream_user_data)
326 {
327 nghttp2_outbound_item *item;
328 nghttp2_frame *frame;
329 nghttp2_nv *nva_copy;
330 uint8_t flags_copy;
331 int32_t promised_stream_id;
332 int rv;
333 nghttp2_mem *mem;
334 (void)flags;
335
336 mem = &session->mem;
337
338 if (stream_id <= 0 || nghttp2_session_is_my_stream_id(session, stream_id)) {
339 return NGHTTP2_ERR_INVALID_ARGUMENT;
340 }
341
342 if (!session->server) {
343 return NGHTTP2_ERR_PROTO;
344 }
345
346 /* All 32bit signed stream IDs are spent. */
347 if (session->next_stream_id > INT32_MAX) {
348 return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
349 }
350
351 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
352 if (item == NULL) {
353 return NGHTTP2_ERR_NOMEM;
354 }
355
356 nghttp2_outbound_item_init(item);
357
358 item->aux_data.headers.stream_user_data = promised_stream_user_data;
359
360 frame = &item->frame;
361
362 rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem);
363 if (rv < 0) {
364 nghttp2_mem_free(mem, item);
365 return rv;
366 }
367
368 flags_copy = NGHTTP2_FLAG_END_HEADERS;
369
370 promised_stream_id = (int32_t)session->next_stream_id;
371 session->next_stream_id += 2;
372
373 nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, stream_id,
374 promised_stream_id, nva_copy, nvlen);
375
376 rv = nghttp2_session_add_item(session, item);
377
378 if (rv != 0) {
379 nghttp2_frame_push_promise_free(&frame->push_promise, mem);
380 nghttp2_mem_free(mem, item);
381
382 return rv;
383 }
384
385 return promised_stream_id;
386 }
387
nghttp2_submit_window_update(nghttp2_session * session,uint8_t flags,int32_t stream_id,int32_t window_size_increment)388 int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
389 int32_t stream_id,
390 int32_t window_size_increment)
391 {
392 int rv;
393 nghttp2_stream *stream = 0;
394 (void)flags;
395
396 if (window_size_increment == 0) {
397 return 0;
398 }
399 if (stream_id == 0) {
400 rv = nghttp2_adjust_local_window_size(
401 &session->local_window_size, &session->recv_window_size,
402 &session->recv_reduction, &window_size_increment);
403 if (rv != 0) {
404 return rv;
405 }
406 } else {
407 stream = nghttp2_session_get_stream(session, stream_id);
408 if (!stream) {
409 return 0;
410 }
411
412 rv = nghttp2_adjust_local_window_size(
413 &stream->local_window_size, &stream->recv_window_size,
414 &stream->recv_reduction, &window_size_increment);
415 if (rv != 0) {
416 return rv;
417 }
418 }
419
420 if (window_size_increment > 0) {
421 if (stream_id == 0) {
422 session->consumed_size =
423 nghttp2_max(0, session->consumed_size - window_size_increment);
424 } else {
425 stream->consumed_size =
426 nghttp2_max(0, stream->consumed_size - window_size_increment);
427 }
428
429 return nghttp2_session_add_window_update(session, 0, stream_id,
430 window_size_increment);
431 }
432 return 0;
433 }
434
nghttp2_session_set_local_window_size(nghttp2_session * session,uint8_t flags,int32_t stream_id,int32_t window_size)435 int nghttp2_session_set_local_window_size(nghttp2_session *session,
436 uint8_t flags, int32_t stream_id,
437 int32_t window_size)
438 {
439 int32_t window_size_increment;
440 nghttp2_stream *stream;
441 int rv;
442 (void)flags;
443
444 if (window_size < 0) {
445 return NGHTTP2_ERR_INVALID_ARGUMENT;
446 }
447
448 if (stream_id == 0) {
449 window_size_increment = window_size - session->local_window_size;
450
451 if (window_size_increment == 0) {
452 return 0;
453 }
454
455 if (window_size_increment < 0) {
456 return nghttp2_adjust_local_window_size(
457 &session->local_window_size, &session->recv_window_size,
458 &session->recv_reduction, &window_size_increment);
459 }
460
461 rv = nghttp2_increase_local_window_size(
462 &session->local_window_size, &session->recv_window_size,
463 &session->recv_reduction, &window_size_increment);
464
465 if (rv != 0) {
466 return rv;
467 }
468 } else {
469 stream = nghttp2_session_get_stream(session, stream_id);
470
471 if (stream == NULL) {
472 return 0;
473 }
474
475 window_size_increment = window_size - stream->local_window_size;
476
477 if (window_size_increment == 0) {
478 return 0;
479 }
480
481 if (window_size_increment < 0) {
482 return nghttp2_adjust_local_window_size(
483 &stream->local_window_size, &stream->recv_window_size,
484 &stream->recv_reduction, &window_size_increment);
485 }
486
487 rv = nghttp2_increase_local_window_size(
488 &stream->local_window_size, &stream->recv_window_size,
489 &stream->recv_reduction, &window_size_increment);
490
491 if (rv != 0) {
492 return rv;
493 }
494 }
495
496 if (window_size_increment > 0) {
497 return nghttp2_session_add_window_update(session, 0, stream_id,
498 window_size_increment);
499 }
500
501 return 0;
502 }
503
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)504 int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags,
505 int32_t stream_id, const uint8_t *origin,
506 size_t origin_len, const uint8_t *field_value,
507 size_t field_value_len)
508 {
509 nghttp2_mem *mem;
510 uint8_t *buf, *p;
511 uint8_t *origin_copy;
512 uint8_t *field_value_copy;
513 nghttp2_outbound_item *item;
514 nghttp2_frame *frame;
515 nghttp2_ext_altsvc *altsvc;
516 int rv;
517 (void)flags;
518
519 mem = &session->mem;
520
521 if (!session->server) {
522 return NGHTTP2_ERR_INVALID_STATE;
523 }
524
525 if (2 + origin_len + field_value_len > NGHTTP2_MAX_PAYLOADLEN) {
526 return NGHTTP2_ERR_INVALID_ARGUMENT;
527 }
528
529 if (stream_id == 0) {
530 if (origin_len == 0) {
531 return NGHTTP2_ERR_INVALID_ARGUMENT;
532 }
533 } else if (origin_len != 0) {
534 return NGHTTP2_ERR_INVALID_ARGUMENT;
535 }
536
537 buf = nghttp2_mem_malloc(mem, origin_len + field_value_len + 2);
538 if (buf == NULL) {
539 return NGHTTP2_ERR_NOMEM;
540 }
541
542 p = buf;
543
544 origin_copy = p;
545 if (origin_len) {
546 p = nghttp2_cpymem(p, origin, origin_len);
547 }
548 *p++ = '\0';
549
550 field_value_copy = p;
551 if (field_value_len) {
552 p = nghttp2_cpymem(p, field_value, field_value_len);
553 }
554 *p++ = '\0';
555
556 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
557 if (item == NULL) {
558 rv = NGHTTP2_ERR_NOMEM;
559 goto fail_item_malloc;
560 }
561
562 nghttp2_outbound_item_init(item);
563
564 item->aux_data.ext.builtin = 1;
565
566 altsvc = &item->ext_frame_payload.altsvc;
567
568 frame = &item->frame;
569 frame->ext.payload = altsvc;
570
571 nghttp2_frame_altsvc_init(&frame->ext, stream_id, origin_copy, origin_len,
572 field_value_copy, field_value_len);
573
574 rv = nghttp2_session_add_item(session, item);
575 if (rv != 0) {
576 nghttp2_frame_altsvc_free(&frame->ext, mem);
577 nghttp2_mem_free(mem, item);
578
579 return rv;
580 }
581
582 return 0;
583
584 fail_item_malloc:
585 nghttp2_mem_free(mem, buf);
586
587 return rv;
588 }
589
set_request_flags(const nghttp2_priority_spec * pri_spec,const nghttp2_data_provider * data_prd)590 static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
591 const nghttp2_data_provider *data_prd)
592 {
593 uint8_t flags = NGHTTP2_FLAG_NONE;
594 if (data_prd == NULL || data_prd->read_callback == NULL) {
595 flags |= NGHTTP2_FLAG_END_STREAM;
596 }
597
598 if (pri_spec) {
599 flags |= NGHTTP2_FLAG_PRIORITY;
600 }
601
602 return flags;
603 }
604
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)605 int32_t nghttp2_submit_request(nghttp2_session *session,
606 const nghttp2_priority_spec *pri_spec,
607 const nghttp2_nv *nva, size_t nvlen,
608 const nghttp2_data_provider *data_prd,
609 void *stream_user_data)
610 {
611 uint8_t flags;
612 int rv;
613
614 if (session->server) {
615 return NGHTTP2_ERR_PROTO;
616 }
617
618 if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
619 rv = detect_self_dependency(session, -1, pri_spec);
620 if (rv != 0) {
621 return rv;
622 }
623 } else {
624 pri_spec = NULL;
625 }
626
627 flags = set_request_flags(pri_spec, data_prd);
628
629 return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen,
630 data_prd, stream_user_data);
631 }
632
set_response_flags(const nghttp2_data_provider * data_prd)633 static uint8_t set_response_flags(const nghttp2_data_provider *data_prd)
634 {
635 uint8_t flags = NGHTTP2_FLAG_NONE;
636 if (data_prd == NULL || data_prd->read_callback == NULL) {
637 flags |= NGHTTP2_FLAG_END_STREAM;
638 }
639 return flags;
640 }
641
nghttp2_submit_response(nghttp2_session * session,int32_t stream_id,const nghttp2_nv * nva,size_t nvlen,const nghttp2_data_provider * data_prd)642 int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
643 const nghttp2_nv *nva, size_t nvlen,
644 const nghttp2_data_provider *data_prd)
645 {
646 uint8_t flags;
647
648 if (stream_id <= 0) {
649 return NGHTTP2_ERR_INVALID_ARGUMENT;
650 }
651
652 if (!session->server) {
653 return NGHTTP2_ERR_PROTO;
654 }
655
656 flags = set_response_flags(data_prd);
657 return submit_headers_shared_nva(session, flags, stream_id, NULL, nva,
658 nvlen, data_prd, NULL);
659 }
660
nghttp2_submit_data(nghttp2_session * session,uint8_t flags,int32_t stream_id,const nghttp2_data_provider * data_prd)661 int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
662 int32_t stream_id,
663 const nghttp2_data_provider *data_prd)
664 {
665 int rv;
666 nghttp2_outbound_item *item;
667 nghttp2_frame *frame;
668 nghttp2_data_aux_data *aux_data;
669 uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM;
670 nghttp2_mem *mem;
671
672 mem = &session->mem;
673
674 if (stream_id == 0) {
675 return NGHTTP2_ERR_INVALID_ARGUMENT;
676 }
677
678 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
679 if (item == NULL) {
680 return NGHTTP2_ERR_NOMEM;
681 }
682
683 nghttp2_outbound_item_init(item);
684
685 frame = &item->frame;
686 aux_data = &item->aux_data.data;
687 aux_data->data_prd = *data_prd;
688 aux_data->eof = 0;
689 aux_data->flags = nflags;
690
691 /* flags are sent on transmission */
692 nghttp2_frame_data_init(&frame->data, NGHTTP2_FLAG_NONE, stream_id);
693
694 rv = nghttp2_session_add_item(session, item);
695 if (rv != 0) {
696 nghttp2_frame_data_free(&frame->data);
697 nghttp2_mem_free(mem, item);
698 return rv;
699 }
700 return 0;
701 }
702
nghttp2_pack_settings_payload(uint8_t * buf,size_t buflen,const nghttp2_settings_entry * iv,size_t niv)703 ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen,
704 const nghttp2_settings_entry *iv,
705 size_t niv)
706 {
707 if (!nghttp2_iv_check(iv, niv)) {
708 return NGHTTP2_ERR_INVALID_ARGUMENT;
709 }
710
711 if (buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) {
712 return NGHTTP2_ERR_INSUFF_BUFSIZE;
713 }
714
715 return (ssize_t)nghttp2_frame_pack_settings_payload(buf, iv, niv);
716 }
717
nghttp2_submit_extension(nghttp2_session * session,uint8_t type,uint8_t flags,int32_t stream_id,void * payload)718 int nghttp2_submit_extension(nghttp2_session *session, uint8_t type,
719 uint8_t flags, int32_t stream_id, void *payload)
720 {
721 int rv;
722 nghttp2_outbound_item *item;
723 nghttp2_frame *frame;
724 nghttp2_mem *mem;
725
726 mem = &session->mem;
727
728 if (type <= NGHTTP2_CONTINUATION) {
729 return NGHTTP2_ERR_INVALID_ARGUMENT;
730 }
731
732 if (!session->callbacks.pack_extension_callback) {
733 return NGHTTP2_ERR_INVALID_STATE;
734 }
735
736 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
737 if (item == NULL) {
738 return NGHTTP2_ERR_NOMEM;
739 }
740
741 nghttp2_outbound_item_init(item);
742
743 frame = &item->frame;
744 nghttp2_frame_extension_init(&frame->ext, type, flags, stream_id, payload);
745
746 rv = nghttp2_session_add_item(session, item);
747 if (rv != 0) {
748 nghttp2_frame_extension_free(&frame->ext);
749 nghttp2_mem_free(mem, item);
750 return rv;
751 }
752
753 return 0;
754 }
755