Coverage Report

Created: 2025-07-11 06:26

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