Coverage Report

Created: 2025-09-04 07:51

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