Coverage Report

Created: 2026-08-31 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nghttp2/lib/nghttp2_session.c
Line
Count
Source
1
/*
2
 * nghttp2 - HTTP/2 C Library
3
 *
4
 * Copyright (c) 2012 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_session.h"
26
27
#include <string.h>
28
#include <stddef.h>
29
#include <stdio.h>
30
#include <assert.h>
31
#include <stdarg.h>
32
33
#include "nghttp2_helper.h"
34
#include "nghttp2_net.h"
35
#include "nghttp2_priority_spec.h"
36
#include "nghttp2_option.h"
37
#include "nghttp2_http.h"
38
#include "nghttp2_pq.h"
39
#include "nghttp2_extpri.h"
40
#include "nghttp2_time.h"
41
#include "nghttp2_debug.h"
42
#include "nghttp2_submit.h"
43
44
nghttp2_stream nghttp2_stream_root;
45
46
/*
47
 * Returns non-zero if the number of outgoing opened streams is larger
48
 * than or equal to
49
 * remote_settings.max_concurrent_streams.
50
 */
51
static int
52
37.3k
session_is_outgoing_concurrent_streams_max(nghttp2_session *session) {
53
37.3k
  return session->remote_settings.max_concurrent_streams <=
54
37.3k
         session->num_outgoing_streams;
55
37.3k
}
56
57
/*
58
 * Returns non-zero if the number of incoming opened streams is larger
59
 * than or equal to
60
 * local_settings.max_concurrent_streams.
61
 */
62
static int
63
32.2k
session_is_incoming_concurrent_streams_max(nghttp2_session *session) {
64
32.2k
  return session->local_settings.max_concurrent_streams <=
65
32.2k
         session->num_incoming_streams;
66
32.2k
}
67
68
/*
69
 * Returns non-zero if the number of incoming opened streams is larger
70
 * than or equal to
71
 * session->pending_local_max_concurrent_stream.
72
 */
73
static int
74
32.2k
session_is_incoming_concurrent_streams_pending_max(nghttp2_session *session) {
75
32.2k
  return session->pending_local_max_concurrent_stream <=
76
32.2k
         session->num_incoming_streams;
77
32.2k
}
78
79
/*
80
 * Returns non-zero if |lib_error| is non-fatal error.
81
 */
82
0
static int is_non_fatal(int lib_error_code) {
83
0
  return lib_error_code < 0 && lib_error_code > NGHTTP2_ERR_FATAL;
84
0
}
85
86
1.45M
int nghttp2_is_fatal(int lib_error_code) {
87
1.45M
  return lib_error_code < NGHTTP2_ERR_FATAL;
88
1.45M
}
89
90
115k
static int session_enforce_http_messaging(nghttp2_session *session) {
91
115k
  return (session->opt_flags & NGHTTP2_OPTMASK_NO_HTTP_MESSAGING) == 0;
92
115k
}
93
94
/*
95
 * Returns nonzero if |frame| is trailer headers.
96
 */
97
static int session_trailer_headers(nghttp2_session *session,
98
                                   nghttp2_stream *stream,
99
152k
                                   nghttp2_frame *frame) {
100
152k
  if (!stream || frame->hd.type != NGHTTP2_HEADERS) {
101
73.1k
    return 0;
102
73.1k
  }
103
79.1k
  if (session->server) {
104
79.1k
    return frame->headers.cat == NGHTTP2_HCAT_HEADERS;
105
79.1k
  }
106
107
0
  return frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
108
0
         (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) == 0;
109
79.1k
}
110
111
/* Returns nonzero if the |stream| is in reserved(remote) state */
112
static int state_reserved_remote(nghttp2_session *session,
113
426
                                 nghttp2_stream *stream) {
114
426
  return stream->state == NGHTTP2_STREAM_RESERVED &&
115
0
         !nghttp2_session_is_my_stream_id(session, stream->stream_id);
116
426
}
117
118
/* Returns nonzero if the |stream| is in reserved(local) state */
119
static int state_reserved_local(nghttp2_session *session,
120
81
                                nghttp2_stream *stream) {
121
81
  return stream->state == NGHTTP2_STREAM_RESERVED &&
122
0
         nghttp2_session_is_my_stream_id(session, stream->stream_id);
123
81
}
124
125
/*
126
 * Checks whether received stream_id is valid.  This function returns
127
 * 1 if it succeeds, or 0.
128
 */
129
static int session_is_new_peer_stream_id(nghttp2_session *session,
130
193k
                                         int32_t stream_id) {
131
193k
  return stream_id != 0 &&
132
193k
         !nghttp2_session_is_my_stream_id(session, stream_id) &&
133
193k
         session->last_recv_stream_id < stream_id;
134
193k
}
135
136
static int session_detect_idle_stream(nghttp2_session *session,
137
89.3k
                                      int32_t stream_id) {
138
  /* Assume that stream object with stream_id does not exist */
139
89.3k
  if (nghttp2_session_is_my_stream_id(session, stream_id)) {
140
224
    if (session->last_sent_stream_id < stream_id) {
141
224
      return 1;
142
224
    }
143
0
    return 0;
144
224
  }
145
89.1k
  if (session_is_new_peer_stream_id(session, stream_id)) {
146
184
    return 1;
147
184
  }
148
88.9k
  return 0;
149
89.1k
}
150
151
155k
static int check_ext_type_set(const uint8_t *ext_types, uint8_t type) {
152
155k
  return (ext_types[type / 8] & (1 << (type & 0x7))) > 0;
153
155k
}
154
155
static int session_call_error_callback(nghttp2_session *session,
156
                                       int lib_error_code, const char *fmt,
157
28.1k
                                       ...) {
158
28.1k
  size_t bufsize;
159
28.1k
  va_list ap;
160
28.1k
  char *buf;
161
28.1k
  int rv;
162
28.1k
  nghttp2_mem *mem;
163
164
28.1k
  if (!session->callbacks.error_callback &&
165
28.1k
      !session->callbacks.error_callback2) {
166
28.1k
    return 0;
167
28.1k
  }
168
169
0
  mem = &session->mem;
170
171
0
  va_start(ap, fmt);
172
0
  rv = vsnprintf(NULL, 0, fmt, ap);
173
0
  va_end(ap);
174
175
0
  if (rv < 0) {
176
0
    return NGHTTP2_ERR_NOMEM;
177
0
  }
178
179
0
  bufsize = (size_t)(rv + 1);
180
181
0
  buf = nghttp2_mem_malloc(mem, bufsize);
182
0
  if (buf == NULL) {
183
0
    return NGHTTP2_ERR_NOMEM;
184
0
  }
185
186
0
  va_start(ap, fmt);
187
0
  rv = vsnprintf(buf, bufsize, fmt, ap);
188
0
  va_end(ap);
189
190
0
  if (rv < 0) {
191
0
    nghttp2_mem_free(mem, buf);
192
    /* vsnprintf may return error because of various things we can
193
       imagine, but typically we don't want to drop session just for
194
       debug callback. */
195
0
    DEBUGF("error_callback: vsnprintf failed. The template was %s\n", fmt);
196
0
    return 0;
197
0
  }
198
199
0
  if (session->callbacks.error_callback2) {
200
0
    rv = session->callbacks.error_callback2(session, lib_error_code, buf,
201
0
                                            (size_t)rv, session->user_data);
202
0
  } else {
203
0
    rv = session->callbacks.error_callback(session, buf, (size_t)rv,
204
0
                                           session->user_data);
205
0
  }
206
207
0
  nghttp2_mem_free(mem, buf);
208
209
0
  if (rv != 0) {
210
0
    return NGHTTP2_ERR_CALLBACK_FAILURE;
211
0
  }
212
213
0
  return 0;
214
0
}
215
216
static int session_terminate_session(nghttp2_session *session,
217
                                     int32_t last_stream_id,
218
4.37k
                                     uint32_t error_code, const char *reason) {
219
4.37k
  int rv;
220
4.37k
  const uint8_t *debug_data;
221
4.37k
  size_t debug_datalen;
222
223
4.37k
  if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
224
0
    return 0;
225
0
  }
226
227
  /* Ignore all incoming frames because we are going to tear down the
228
     session. */
229
4.37k
  session->iframe.state = NGHTTP2_IB_IGN_ALL;
230
231
4.37k
  if (reason == NULL) {
232
2.49k
    debug_data = NULL;
233
2.49k
    debug_datalen = 0;
234
2.49k
  } else {
235
1.87k
    debug_data = (const uint8_t *)reason;
236
1.87k
    debug_datalen = strlen(reason);
237
1.87k
  }
238
239
4.37k
  rv =
240
4.37k
    nghttp2_session_add_goaway(session, last_stream_id, error_code, debug_data,
241
4.37k
                               debug_datalen, NGHTTP2_GOAWAY_AUX_TERM_ON_SEND);
242
243
4.37k
  if (rv != 0) {
244
0
    return rv;
245
0
  }
246
247
4.37k
  session->goaway_flags |= NGHTTP2_GOAWAY_TERM_ON_SEND;
248
249
4.37k
  return 0;
250
4.37k
}
251
252
int nghttp2_session_terminate_session(nghttp2_session *session,
253
2.48k
                                      uint32_t error_code) {
254
2.48k
  return session_terminate_session(session, session->last_proc_stream_id,
255
2.48k
                                   error_code, NULL);
256
2.48k
}
257
258
int nghttp2_session_terminate_session2(nghttp2_session *session,
259
                                       int32_t last_stream_id,
260
0
                                       uint32_t error_code) {
261
0
  return session_terminate_session(session, last_stream_id, error_code, NULL);
262
0
}
263
264
int nghttp2_session_terminate_session_with_reason(nghttp2_session *session,
265
                                                  uint32_t error_code,
266
1.89k
                                                  const char *reason) {
267
1.89k
  return session_terminate_session(session, session->last_proc_stream_id,
268
1.89k
                                   error_code, reason);
269
1.89k
}
270
271
int nghttp2_session_is_my_stream_id(nghttp2_session *session,
272
492k
                                    int32_t stream_id) {
273
492k
  int rem;
274
492k
  if (stream_id == 0) {
275
1.60k
    return 0;
276
1.60k
  }
277
490k
  rem = stream_id & 0x1;
278
490k
  if (session->server) {
279
490k
    return rem == 0;
280
490k
  }
281
0
  return rem == 1;
282
490k
}
283
284
nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session,
285
620k
                                           int32_t stream_id) {
286
620k
  nghttp2_stream *stream;
287
288
620k
  stream = (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id);
289
290
620k
  if (stream == NULL || (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) ||
291
381k
      stream->state == NGHTTP2_STREAM_IDLE) {
292
381k
    return NULL;
293
381k
  }
294
295
238k
  return stream;
296
620k
}
297
298
nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session,
299
115k
                                               int32_t stream_id) {
300
115k
  return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id);
301
115k
}
302
303
393k
static void session_inbound_frame_reset(nghttp2_session *session) {
304
393k
  nghttp2_inbound_frame *iframe = &session->iframe;
305
393k
  nghttp2_mem *mem = &session->mem;
306
  /* A bit risky code, since if this function is called from
307
     nghttp2_session_new(), we rely on the fact that
308
     iframe->frame.hd.type is 0, so that no free is performed. */
309
393k
  switch (iframe->frame.hd.type) {
310
63.8k
  case NGHTTP2_DATA:
311
63.8k
    break;
312
143k
  case NGHTTP2_HEADERS:
313
143k
    nghttp2_frame_headers_free(&iframe->frame.headers, mem);
314
143k
    break;
315
409
  case NGHTTP2_PRIORITY:
316
409
    nghttp2_frame_priority_free(&iframe->frame.priority);
317
409
    break;
318
66.3k
  case NGHTTP2_RST_STREAM:
319
66.3k
    nghttp2_frame_rst_stream_free(&iframe->frame.rst_stream);
320
66.3k
    break;
321
35.3k
  case NGHTTP2_SETTINGS:
322
35.3k
    nghttp2_frame_settings_free(&iframe->frame.settings, mem);
323
324
35.3k
    nghttp2_mem_free(mem, iframe->iv);
325
326
35.3k
    iframe->iv = NULL;
327
35.3k
    iframe->niv = 0;
328
35.3k
    iframe->max_niv = 0;
329
330
35.3k
    break;
331
141
  case NGHTTP2_PUSH_PROMISE:
332
141
    nghttp2_frame_push_promise_free(&iframe->frame.push_promise, mem);
333
141
    break;
334
1.83k
  case NGHTTP2_PING:
335
1.83k
    nghttp2_frame_ping_free(&iframe->frame.ping);
336
1.83k
    break;
337
1.94k
  case NGHTTP2_GOAWAY:
338
1.94k
    nghttp2_frame_goaway_free(&iframe->frame.goaway, mem);
339
1.94k
    break;
340
1.46k
  case NGHTTP2_WINDOW_UPDATE:
341
1.46k
    nghttp2_frame_window_update_free(&iframe->frame.window_update);
342
1.46k
    break;
343
78.0k
  default:
344
    /* extension frame */
345
78.0k
    if (check_ext_type_set(session->user_recv_ext_types,
346
78.0k
                           iframe->frame.hd.type)) {
347
0
      nghttp2_frame_extension_free(&iframe->frame.ext);
348
78.0k
    } else {
349
78.0k
      switch (iframe->frame.hd.type) {
350
2.19k
      case NGHTTP2_ALTSVC:
351
2.19k
        if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) == 0) {
352
2.19k
          break;
353
2.19k
        }
354
0
        nghttp2_frame_altsvc_free(&iframe->frame.ext, mem);
355
0
        break;
356
143
      case NGHTTP2_ORIGIN:
357
143
        if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN) == 0) {
358
143
          break;
359
143
        }
360
0
        nghttp2_frame_origin_free(&iframe->frame.ext, mem);
361
0
        break;
362
65.1k
      case NGHTTP2_PRIORITY_UPDATE:
363
65.1k
        if ((session->builtin_recv_ext_types &
364
65.1k
             NGHTTP2_TYPEMASK_PRIORITY_UPDATE) == 0) {
365
65.1k
          break;
366
65.1k
        }
367
        /* Do not call nghttp2_frame_priority_update_free, because all
368
           fields point to sbuf. */
369
0
        break;
370
78.0k
      }
371
78.0k
    }
372
373
78.0k
    break;
374
393k
  }
375
376
393k
  memset(&iframe->frame, 0, sizeof(nghttp2_frame));
377
393k
  memset(&iframe->ext_frame_payload, 0, sizeof(nghttp2_ext_frame_payload));
378
379
393k
  iframe->state = NGHTTP2_IB_READ_HEAD;
380
381
393k
  nghttp2_buf_wrap_init(&iframe->sbuf, iframe->raw_sbuf,
382
393k
                        sizeof(iframe->raw_sbuf));
383
393k
  iframe->sbuf.mark += NGHTTP2_FRAME_HDLEN;
384
385
393k
  nghttp2_buf_free(&iframe->lbuf, mem);
386
393k
  nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
387
388
393k
  iframe->raw_lbuf = NULL;
389
390
393k
  iframe->payloadleft = 0;
391
393k
  iframe->padlen = 0;
392
393k
}
393
394
37.3k
static void init_settings(nghttp2_settings_storage *settings) {
395
37.3k
  *settings = (nghttp2_settings_storage){
396
37.3k
    .header_table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE,
397
37.3k
    .enable_push = 1,
398
37.3k
    .max_concurrent_streams = NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS,
399
37.3k
    .initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE,
400
37.3k
    .max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN,
401
37.3k
    .max_header_list_size = UINT32_MAX,
402
37.3k
    .no_rfc7540_priorities = UINT32_MAX,
403
37.3k
  };
404
37.3k
}
405
406
static void active_outbound_item_reset(nghttp2_active_outbound_item *aob,
407
119k
                                       nghttp2_mem *mem) {
408
119k
  DEBUGF("send: reset nghttp2_active_outbound_item\n");
409
119k
  DEBUGF("send: aob->item = %p\n", aob->item);
410
119k
  nghttp2_outbound_item_free(aob->item, mem);
411
119k
  nghttp2_mem_free(mem, aob->item);
412
119k
  aob->item = NULL;
413
119k
  nghttp2_bufs_reset(&aob->framebufs);
414
119k
  aob->state = NGHTTP2_OB_POP_ITEM;
415
119k
}
416
417
0
#define NGHTTP2_STREAM_MAX_CYCLE_GAP ((uint64_t)NGHTTP2_MAX_FRAME_SIZE_MAX)
418
419
0
static int stream_less(const void *lhsx, const void *rhsx) {
420
0
  const nghttp2_stream *lhs, *rhs;
421
422
0
  lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry);
423
0
  rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry);
424
425
0
  if (lhs->cycle == rhs->cycle) {
426
0
    return lhs->seq < rhs->seq;
427
0
  }
428
429
0
  return rhs->cycle - lhs->cycle <= NGHTTP2_STREAM_MAX_CYCLE_GAP;
430
0
}
431
432
int nghttp2_enable_strict_preface = 1;
433
434
static int session_new(nghttp2_session **session_ptr,
435
                       const nghttp2_session_callbacks *callbacks,
436
                       void *user_data, int server,
437
18.6k
                       const nghttp2_option *option, nghttp2_mem *mem) {
438
18.6k
  int rv;
439
18.6k
  size_t nbuffer;
440
18.6k
  size_t max_deflate_dynamic_table_size =
441
18.6k
    NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE;
442
18.6k
  size_t i;
443
18.6k
  uint64_t map_seed;
444
445
18.6k
  if (mem == NULL) {
446
18.6k
    mem = nghttp2_mem_default();
447
18.6k
  }
448
449
18.6k
  *session_ptr = nghttp2_mem_calloc(mem, 1, sizeof(nghttp2_session));
450
18.6k
  if (*session_ptr == NULL) {
451
0
    rv = NGHTTP2_ERR_NOMEM;
452
0
    goto fail_session;
453
0
  }
454
455
18.6k
  (*session_ptr)->mem = *mem;
456
18.6k
  mem = &(*session_ptr)->mem;
457
458
  /* next_stream_id is initialized in either
459
     nghttp2_session_client_new2 or nghttp2_session_server_new2 */
460
461
18.6k
  (*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
462
18.6k
  (*session_ptr)->recv_window_size = 0;
463
18.6k
  (*session_ptr)->consumed_size = 0;
464
18.6k
  (*session_ptr)->recv_reduction = 0;
465
18.6k
  (*session_ptr)->local_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
466
467
18.6k
  (*session_ptr)->goaway_flags = NGHTTP2_GOAWAY_NONE;
468
18.6k
  (*session_ptr)->local_last_stream_id = (1u << 31) - 1;
469
18.6k
  (*session_ptr)->remote_last_stream_id = (1u << 31) - 1;
470
471
18.6k
  (*session_ptr)->pending_local_max_concurrent_stream =
472
18.6k
    NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;
473
18.6k
  (*session_ptr)->pending_enable_push = 1;
474
18.6k
  (*session_ptr)->pending_no_rfc7540_priorities = UINT8_MAX;
475
476
18.6k
  nghttp2_ratelim_init(&(*session_ptr)->stream_reset_ratelim,
477
18.6k
                       NGHTTP2_DEFAULT_STREAM_RESET_BURST,
478
18.6k
                       NGHTTP2_DEFAULT_STREAM_RESET_RATE);
479
480
18.6k
  nghttp2_ratelim_init(&(*session_ptr)->glitch_ratelim,
481
18.6k
                       NGHTTP2_DEFAULT_GLITCH_BURST,
482
18.6k
                       NGHTTP2_DEFAULT_GLITCH_RATE);
483
484
18.6k
  if (server) {
485
18.6k
    (*session_ptr)->server = 1;
486
18.6k
  }
487
488
18.6k
  init_settings(&(*session_ptr)->remote_settings);
489
18.6k
  init_settings(&(*session_ptr)->local_settings);
490
491
18.6k
  (*session_ptr)->max_incoming_reserved_streams =
492
18.6k
    NGHTTP2_MAX_INCOMING_RESERVED_STREAMS;
493
494
  /* Limit max outgoing concurrent streams to sensible value */
495
18.6k
  (*session_ptr)->remote_settings.max_concurrent_streams = 100;
496
497
18.6k
  (*session_ptr)->max_send_header_block_length = NGHTTP2_MAX_HEADERSLEN;
498
18.6k
  (*session_ptr)->max_outbound_ack = NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM;
499
18.6k
  (*session_ptr)->max_settings = NGHTTP2_DEFAULT_MAX_SETTINGS;
500
18.6k
  (*session_ptr)->max_continuations = NGHTTP2_DEFAULT_MAX_CONTINUATIONS;
501
18.6k
  (*session_ptr)->max_outbound_queue_size =
502
18.6k
    NGHTTP2_DEFAULT_MAX_OUTBOUND_QUEUE_SIZE;
503
504
18.6k
  if (option) {
505
0
    if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) &&
506
0
        option->no_auto_window_update) {
507
0
      (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE;
508
0
    }
509
510
0
    if (option->opt_set_mask & NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS) {
511
0
      (*session_ptr)->remote_settings.max_concurrent_streams =
512
0
        option->peer_max_concurrent_streams;
513
0
    }
514
515
0
    if (option->opt_set_mask & NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS) {
516
0
      (*session_ptr)->max_incoming_reserved_streams =
517
0
        option->max_reserved_remote_streams;
518
0
    }
519
520
0
    if ((option->opt_set_mask & NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC) &&
521
0
        option->no_recv_client_magic) {
522
0
      (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC;
523
0
    }
524
525
0
    if ((option->opt_set_mask & NGHTTP2_OPT_NO_HTTP_MESSAGING) &&
526
0
        option->no_http_messaging) {
527
0
      (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING;
528
0
    }
529
530
0
    if (option->opt_set_mask & NGHTTP2_OPT_USER_RECV_EXT_TYPES) {
531
0
      memcpy((*session_ptr)->user_recv_ext_types, option->user_recv_ext_types,
532
0
             sizeof((*session_ptr)->user_recv_ext_types));
533
0
    }
534
535
0
    if (option->opt_set_mask & NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES) {
536
0
      (*session_ptr)->builtin_recv_ext_types = option->builtin_recv_ext_types;
537
0
    }
538
539
0
    if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_PING_ACK) &&
540
0
        option->no_auto_ping_ack) {
541
0
      (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_PING_ACK;
542
0
    }
543
544
0
    if (option->opt_set_mask & NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH) {
545
0
      (*session_ptr)->max_send_header_block_length =
546
0
        option->max_send_header_block_length;
547
0
    }
548
549
0
    if (option->opt_set_mask & NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE) {
550
0
      max_deflate_dynamic_table_size = option->max_deflate_dynamic_table_size;
551
0
    }
552
553
0
    if (option->opt_set_mask & NGHTTP2_OPT_MAX_OUTBOUND_ACK) {
554
0
      (*session_ptr)->max_outbound_ack = option->max_outbound_ack;
555
0
    }
556
557
0
    if ((option->opt_set_mask & NGHTTP2_OPT_MAX_SETTINGS) &&
558
0
        option->max_settings) {
559
0
      (*session_ptr)->max_settings = option->max_settings;
560
0
    }
561
562
0
    if ((option->opt_set_mask &
563
0
         NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) &&
564
0
        option->no_rfc9113_leading_and_trailing_ws_validation) {
565
0
      (*session_ptr)->opt_flags |=
566
0
        NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION;
567
0
    }
568
569
0
    if (option->opt_set_mask & NGHTTP2_OPT_STREAM_RESET_RATE_LIMIT) {
570
0
      nghttp2_ratelim_init(&(*session_ptr)->stream_reset_ratelim,
571
0
                           option->stream_reset_burst,
572
0
                           option->stream_reset_rate);
573
0
    }
574
575
0
    if (option->opt_set_mask & NGHTTP2_OPT_MAX_CONTINUATIONS) {
576
0
      (*session_ptr)->max_continuations = option->max_continuations;
577
0
    }
578
579
0
    if (option->opt_set_mask & NGHTTP2_OPT_GLITCH_RATE_LIMIT) {
580
0
      nghttp2_ratelim_init(&(*session_ptr)->glitch_ratelim,
581
0
                           option->glitch_burst, option->glitch_rate);
582
0
    }
583
584
0
    if (option->opt_set_mask & NGHTTP2_OPT_MAX_OUTBOUND_QUEUE_SIZE) {
585
0
      (*session_ptr)->max_outbound_queue_size = option->max_outbound_queue_size;
586
0
    }
587
0
  }
588
589
18.6k
  rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater,
590
18.6k
                                max_deflate_dynamic_table_size, mem);
591
18.6k
  if (rv != 0) {
592
0
    goto fail_hd_deflater;
593
0
  }
594
18.6k
  rv = nghttp2_hd_inflate_init(&(*session_ptr)->hd_inflater, mem);
595
18.6k
  if (rv != 0) {
596
0
    goto fail_hd_inflater;
597
0
  }
598
599
18.6k
  nbuffer = ((*session_ptr)->max_send_header_block_length +
600
18.6k
             NGHTTP2_FRAMEBUF_CHUNKLEN - 1) /
601
18.6k
            NGHTTP2_FRAMEBUF_CHUNKLEN;
602
603
18.6k
  if (nbuffer == 0) {
604
0
    nbuffer = 1;
605
0
  }
606
607
  /* 1 for Pad Field. */
608
18.6k
  rv = nghttp2_bufs_init3(&(*session_ptr)->aob.framebufs,
609
18.6k
                          NGHTTP2_FRAMEBUF_CHUNKLEN, nbuffer, 1,
610
18.6k
                          NGHTTP2_FRAME_HDLEN + 1, mem);
611
18.6k
  if (rv != 0) {
612
0
    goto fail_aob_framebuf;
613
0
  }
614
615
18.6k
  if (callbacks->rand_callback) {
616
0
    callbacks->rand_callback((uint8_t *)&map_seed, sizeof(map_seed));
617
18.6k
  } else {
618
18.6k
    map_seed = 0;
619
18.6k
  }
620
621
18.6k
  nghttp2_map_init(&(*session_ptr)->streams, map_seed, mem);
622
623
18.6k
  active_outbound_item_reset(&(*session_ptr)->aob, mem);
624
625
18.6k
  (*session_ptr)->callbacks = *callbacks;
626
18.6k
  (*session_ptr)->user_data = user_data;
627
628
18.6k
  session_inbound_frame_reset(*session_ptr);
629
630
18.6k
  if (nghttp2_enable_strict_preface) {
631
18.6k
    nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe;
632
633
18.6k
    if (server && ((*session_ptr)->opt_flags &
634
18.6k
                   NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) == 0) {
635
18.6k
      iframe->state = NGHTTP2_IB_READ_CLIENT_MAGIC;
636
18.6k
      iframe->payloadleft = NGHTTP2_CLIENT_MAGIC_LEN;
637
18.6k
    } else {
638
0
      iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS;
639
0
    }
640
641
18.6k
    if (!server) {
642
0
      (*session_ptr)->aob.state = NGHTTP2_OB_SEND_CLIENT_MAGIC;
643
0
      nghttp2_bufs_add(&(*session_ptr)->aob.framebufs, NGHTTP2_CLIENT_MAGIC,
644
0
                       NGHTTP2_CLIENT_MAGIC_LEN);
645
0
    }
646
18.6k
  }
647
648
167k
  for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) {
649
149k
    nghttp2_pq_init(&(*session_ptr)->sched[i].ob_data, stream_less, mem);
650
149k
  }
651
652
18.6k
  return 0;
653
654
0
fail_aob_framebuf:
655
0
  nghttp2_hd_inflate_free(&(*session_ptr)->hd_inflater);
656
0
fail_hd_inflater:
657
0
  nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater);
658
0
fail_hd_deflater:
659
0
  nghttp2_mem_free(mem, *session_ptr);
660
0
fail_session:
661
0
  return rv;
662
0
}
663
664
int nghttp2_session_client_new(nghttp2_session **session_ptr,
665
                               const nghttp2_session_callbacks *callbacks,
666
0
                               void *user_data) {
667
0
  return nghttp2_session_client_new3(session_ptr, callbacks, user_data, NULL,
668
0
                                     NULL);
669
0
}
670
671
int nghttp2_session_client_new2(nghttp2_session **session_ptr,
672
                                const nghttp2_session_callbacks *callbacks,
673
0
                                void *user_data, const nghttp2_option *option) {
674
0
  return nghttp2_session_client_new3(session_ptr, callbacks, user_data, option,
675
0
                                     NULL);
676
0
}
677
678
int nghttp2_session_client_new3(nghttp2_session **session_ptr,
679
                                const nghttp2_session_callbacks *callbacks,
680
                                void *user_data, const nghttp2_option *option,
681
0
                                nghttp2_mem *mem) {
682
0
  int rv;
683
0
  nghttp2_session *session;
684
685
0
  rv = session_new(&session, callbacks, user_data, 0, option, mem);
686
687
0
  if (rv != 0) {
688
0
    return rv;
689
0
  }
690
  /* IDs for use in client */
691
0
  session->next_stream_id = 1;
692
693
0
  *session_ptr = session;
694
695
0
  return 0;
696
0
}
697
698
int nghttp2_session_server_new(nghttp2_session **session_ptr,
699
                               const nghttp2_session_callbacks *callbacks,
700
18.6k
                               void *user_data) {
701
18.6k
  return nghttp2_session_server_new3(session_ptr, callbacks, user_data, NULL,
702
18.6k
                                     NULL);
703
18.6k
}
704
705
int nghttp2_session_server_new2(nghttp2_session **session_ptr,
706
                                const nghttp2_session_callbacks *callbacks,
707
0
                                void *user_data, const nghttp2_option *option) {
708
0
  return nghttp2_session_server_new3(session_ptr, callbacks, user_data, option,
709
0
                                     NULL);
710
0
}
711
712
int nghttp2_session_server_new3(nghttp2_session **session_ptr,
713
                                const nghttp2_session_callbacks *callbacks,
714
                                void *user_data, const nghttp2_option *option,
715
18.6k
                                nghttp2_mem *mem) {
716
18.6k
  int rv;
717
18.6k
  nghttp2_session *session;
718
719
18.6k
  rv = session_new(&session, callbacks, user_data, 1, option, mem);
720
721
18.6k
  if (rv != 0) {
722
0
    return rv;
723
0
  }
724
  /* IDs for use in client */
725
18.6k
  session->next_stream_id = 2;
726
727
18.6k
  *session_ptr = session;
728
729
18.6k
  return 0;
730
18.6k
}
731
732
11.4k
static int free_streams(void *entry, void *ptr) {
733
11.4k
  nghttp2_session *session;
734
11.4k
  nghttp2_stream *stream;
735
11.4k
  nghttp2_outbound_item *item;
736
11.4k
  nghttp2_mem *mem;
737
738
11.4k
  session = (nghttp2_session *)ptr;
739
11.4k
  mem = &session->mem;
740
11.4k
  stream = (nghttp2_stream *)entry;
741
11.4k
  item = stream->item;
742
743
11.4k
  if (item && !item->queued && item != session->aob.item) {
744
0
    nghttp2_outbound_item_free(item, mem);
745
0
    nghttp2_mem_free(mem, item);
746
0
  }
747
748
11.4k
  nghttp2_stream_free(stream);
749
11.4k
  nghttp2_mem_free(mem, stream);
750
751
11.4k
  return 0;
752
11.4k
}
753
754
55.9k
static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) {
755
55.9k
  nghttp2_outbound_item *item, *next;
756
55.9k
  for (item = q->head; item;) {
757
0
    next = item->qnext;
758
0
    nghttp2_outbound_item_free(item, mem);
759
0
    nghttp2_mem_free(mem, item);
760
0
    item = next;
761
0
  }
762
55.9k
}
763
764
static int inflight_settings_new(nghttp2_inflight_settings **settings_ptr,
765
                                 const nghttp2_settings_entry *iv, size_t niv,
766
18.5k
                                 nghttp2_mem *mem) {
767
18.5k
  *settings_ptr = nghttp2_mem_malloc(mem, sizeof(nghttp2_inflight_settings));
768
18.5k
  if (!*settings_ptr) {
769
0
    return NGHTTP2_ERR_NOMEM;
770
0
  }
771
772
18.5k
  if (niv > 0) {
773
18.5k
    (*settings_ptr)->iv = nghttp2_frame_iv_copy(iv, niv, mem);
774
18.5k
    if (!(*settings_ptr)->iv) {
775
0
      nghttp2_mem_free(mem, *settings_ptr);
776
0
      return NGHTTP2_ERR_NOMEM;
777
0
    }
778
18.5k
  } else {
779
0
    (*settings_ptr)->iv = NULL;
780
0
  }
781
782
18.5k
  (*settings_ptr)->niv = niv;
783
18.5k
  (*settings_ptr)->next = NULL;
784
785
18.5k
  return 0;
786
18.5k
}
787
788
static void inflight_settings_del(nghttp2_inflight_settings *settings,
789
18.5k
                                  nghttp2_mem *mem) {
790
18.5k
  if (!settings) {
791
0
    return;
792
0
  }
793
794
18.5k
  nghttp2_mem_free(mem, settings->iv);
795
18.5k
  nghttp2_mem_free(mem, settings);
796
18.5k
}
797
798
18.6k
void nghttp2_session_del(nghttp2_session *session) {
799
18.6k
  nghttp2_mem *mem;
800
18.6k
  nghttp2_inflight_settings *settings;
801
18.6k
  size_t i;
802
803
18.6k
  if (session == NULL) {
804
0
    return;
805
0
  }
806
807
18.6k
  mem = &session->mem;
808
809
36.8k
  for (settings = session->inflight_settings_head; settings;) {
810
18.2k
    nghttp2_inflight_settings *next = settings->next;
811
18.2k
    inflight_settings_del(settings, mem);
812
18.2k
    settings = next;
813
18.2k
  }
814
815
167k
  for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) {
816
149k
    nghttp2_pq_free(&session->sched[i].ob_data);
817
149k
  }
818
819
  /* Have to free streams first, so that we can check
820
     stream->item->queued */
821
18.6k
  nghttp2_map_each(&session->streams, free_streams, session);
822
18.6k
  nghttp2_map_free(&session->streams);
823
824
18.6k
  ob_q_free(&session->ob_urgent, mem);
825
18.6k
  ob_q_free(&session->ob_reg, mem);
826
18.6k
  ob_q_free(&session->ob_syn, mem);
827
828
18.6k
  active_outbound_item_reset(&session->aob, mem);
829
18.6k
  session_inbound_frame_reset(session);
830
18.6k
  nghttp2_hd_deflate_free(&session->hd_deflater);
831
18.6k
  nghttp2_hd_inflate_free(&session->hd_inflater);
832
18.6k
  nghttp2_bufs_free(&session->aob.framebufs);
833
18.6k
  nghttp2_mem_free(mem, session);
834
18.6k
}
835
836
0
static uint64_t pq_get_first_cycle(nghttp2_pq *pq) {
837
0
  nghttp2_stream *stream;
838
839
0
  if (nghttp2_pq_empty(pq)) {
840
0
    return 0;
841
0
  }
842
843
0
  stream = nghttp2_struct_of(nghttp2_pq_top(pq), nghttp2_stream, pq_entry);
844
0
  return stream->cycle;
845
0
}
846
847
static int session_ob_data_push(nghttp2_session *session,
848
0
                                nghttp2_stream *stream) {
849
0
  int rv;
850
0
  uint32_t urgency;
851
0
  int inc;
852
0
  nghttp2_pq *pq;
853
854
0
  assert(stream->queued == 0);
855
856
0
  urgency = nghttp2_extpri_uint8_urgency(stream->extpri);
857
0
  inc = nghttp2_extpri_uint8_inc(stream->extpri);
858
859
0
  assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS);
860
861
0
  pq = &session->sched[urgency].ob_data;
862
863
0
  stream->cycle = pq_get_first_cycle(pq);
864
0
  if (inc) {
865
0
    stream->cycle += stream->last_writelen;
866
0
  }
867
868
0
  rv = nghttp2_pq_push(pq, &stream->pq_entry);
869
0
  if (rv != 0) {
870
0
    return rv;
871
0
  }
872
873
0
  stream->queued = 1;
874
875
0
  return 0;
876
0
}
877
878
static void session_ob_data_remove(nghttp2_session *session,
879
0
                                   nghttp2_stream *stream) {
880
0
  uint32_t urgency;
881
882
0
  assert(stream->queued == 1);
883
884
0
  urgency = nghttp2_extpri_uint8_urgency(stream->extpri);
885
886
0
  assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS);
887
888
0
  nghttp2_pq_remove(&session->sched[urgency].ob_data, &stream->pq_entry);
889
890
0
  stream->queued = 0;
891
0
}
892
893
static int session_attach_stream_item(nghttp2_session *session,
894
                                      nghttp2_stream *stream,
895
0
                                      nghttp2_outbound_item *item) {
896
0
  int rv;
897
898
0
  nghttp2_stream_attach_item(stream, item);
899
900
0
  rv = session_ob_data_push(session, stream);
901
0
  if (rv != 0) {
902
0
    nghttp2_stream_detach_item(stream);
903
904
0
    return rv;
905
0
  }
906
907
0
  return 0;
908
0
}
909
910
static void session_detach_stream_item(nghttp2_session *session,
911
0
                                       nghttp2_stream *stream) {
912
0
  nghttp2_stream_detach_item(stream);
913
914
0
  if (!stream->queued) {
915
0
    return;
916
0
  }
917
918
0
  session_ob_data_remove(session, stream);
919
0
}
920
921
static void session_defer_stream_item(nghttp2_session *session,
922
0
                                      nghttp2_stream *stream, uint8_t flags) {
923
0
  nghttp2_stream_defer_item(stream, flags);
924
925
0
  if (!stream->queued) {
926
0
    return;
927
0
  }
928
929
0
  session_ob_data_remove(session, stream);
930
0
}
931
932
static int session_resume_deferred_stream_item(nghttp2_session *session,
933
                                               nghttp2_stream *stream,
934
0
                                               uint8_t flags) {
935
0
  nghttp2_stream_resume_deferred_item(stream, flags);
936
937
0
  if (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) {
938
0
    return 0;
939
0
  }
940
941
0
  return session_ob_data_push(session, stream);
942
0
}
943
944
static nghttp2_outbound_item *
945
37.3k
session_sched_get_next_outbound_item(nghttp2_session *session) {
946
37.3k
  size_t i;
947
37.3k
  nghttp2_pq_entry *ent;
948
37.3k
  nghttp2_stream *stream;
949
950
335k
  for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) {
951
298k
    ent = nghttp2_pq_top(&session->sched[i].ob_data);
952
298k
    if (!ent) {
953
298k
      continue;
954
298k
    }
955
956
0
    stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry);
957
0
    return stream->item;
958
298k
  }
959
960
37.3k
  return NULL;
961
37.3k
}
962
963
197
static int session_sched_empty(nghttp2_session *session) {
964
197
  size_t i;
965
966
1.77k
  for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) {
967
1.57k
    if (!nghttp2_pq_empty(&session->sched[i].ob_data)) {
968
0
      return 0;
969
0
    }
970
1.57k
  }
971
972
197
  return 1;
973
197
}
974
975
static void session_sched_reschedule_stream(nghttp2_session *session,
976
0
                                            nghttp2_stream *stream) {
977
0
  nghttp2_pq *pq;
978
0
  uint32_t urgency = nghttp2_extpri_uint8_urgency(stream->extpri);
979
0
  int inc = nghttp2_extpri_uint8_inc(stream->extpri);
980
0
  uint64_t penalty = (uint64_t)stream->last_writelen;
981
0
  int rv;
982
983
0
  (void)rv;
984
985
0
  assert(urgency < NGHTTP2_EXTPRI_URGENCY_LEVELS);
986
987
0
  pq = &session->sched[urgency].ob_data;
988
989
0
  if (!inc || nghttp2_pq_size(pq) == 1) {
990
0
    return;
991
0
  }
992
993
0
  nghttp2_pq_remove(pq, &stream->pq_entry);
994
995
0
  stream->cycle += penalty;
996
997
0
  rv = nghttp2_pq_push(pq, &stream->pq_entry);
998
999
0
  assert(0 == rv);
1000
0
}
1001
1002
static int session_update_stream_priority(nghttp2_session *session,
1003
                                          nghttp2_stream *stream,
1004
105
                                          uint8_t u8extpri) {
1005
105
  if (stream->extpri == u8extpri) {
1006
52
    return 0;
1007
52
  }
1008
1009
53
  if (stream->queued) {
1010
0
    session_ob_data_remove(session, stream);
1011
1012
0
    stream->extpri = u8extpri;
1013
1014
0
    return session_ob_data_push(session, stream);
1015
0
  }
1016
1017
53
  stream->extpri = u8extpri;
1018
1019
53
  return 0;
1020
53
}
1021
1022
int nghttp2_session_add_item(nghttp2_session *session,
1023
82.6k
                             nghttp2_outbound_item *item) {
1024
  /* TODO Return error if stream is not found for the frame requiring
1025
     stream presence. */
1026
82.6k
  int rv = 0;
1027
82.6k
  nghttp2_stream *stream;
1028
82.6k
  nghttp2_frame *frame;
1029
1030
82.6k
  frame = &item->frame;
1031
82.6k
  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
1032
1033
82.6k
  switch (frame->hd.type) {
1034
0
  case NGHTTP2_DATA:
1035
0
    if (!stream) {
1036
0
      return NGHTTP2_ERR_STREAM_CLOSED;
1037
0
    }
1038
1039
0
    if (stream->item) {
1040
0
      return NGHTTP2_ERR_DATA_EXIST;
1041
0
    }
1042
1043
0
    rv = session_attach_stream_item(session, stream, item);
1044
1045
0
    if (rv != 0) {
1046
0
      return rv;
1047
0
    }
1048
1049
0
    return 0;
1050
0
  case NGHTTP2_HEADERS:
1051
    /* We push request HEADERS and push response HEADERS to
1052
       dedicated queue because their transmission is affected by
1053
       SETTINGS_MAX_CONCURRENT_STREAMS */
1054
    /* TODO If 2 HEADERS are submitted for reserved stream, then
1055
       both of them are queued into ob_syn, which is not
1056
       desirable. */
1057
0
    if (frame->headers.cat == NGHTTP2_HCAT_REQUEST ||
1058
0
        (stream && stream->state == NGHTTP2_STREAM_RESERVED)) {
1059
0
      nghttp2_outbound_queue_push(&session->ob_syn, item);
1060
0
      item->queued = 1;
1061
0
      return 0;
1062
0
    }
1063
1064
0
    nghttp2_outbound_queue_push(&session->ob_reg, item);
1065
0
    item->queued = 1;
1066
0
    return 0;
1067
52.5k
  case NGHTTP2_SETTINGS:
1068
53.9k
  case NGHTTP2_PING:
1069
53.9k
    nghttp2_outbound_queue_push(&session->ob_urgent, item);
1070
53.9k
    item->queued = 1;
1071
53.9k
    return 0;
1072
24.1k
  case NGHTTP2_RST_STREAM:
1073
24.1k
    if (stream) {
1074
23.6k
      stream->state = NGHTTP2_STREAM_CLOSING;
1075
23.6k
    }
1076
24.1k
    nghttp2_outbound_queue_push(&session->ob_reg, item);
1077
24.1k
    item->queued = 1;
1078
24.1k
    return 0;
1079
0
  case NGHTTP2_PUSH_PROMISE: {
1080
0
    nghttp2_headers_aux_data *aux_data;
1081
1082
0
    aux_data = &item->aux_data.headers;
1083
1084
0
    if (!stream) {
1085
0
      return NGHTTP2_ERR_STREAM_CLOSED;
1086
0
    }
1087
1088
0
    if (!nghttp2_session_open_stream(
1089
0
          session, frame->push_promise.promised_stream_id,
1090
0
          NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_RESERVED,
1091
0
          aux_data->stream_user_data)) {
1092
0
      return NGHTTP2_ERR_NOMEM;
1093
0
    }
1094
1095
0
    nghttp2_outbound_queue_push(&session->ob_reg, item);
1096
0
    item->queued = 1;
1097
1098
0
    return 0;
1099
0
  }
1100
165
  case NGHTTP2_WINDOW_UPDATE:
1101
165
    if (stream) {
1102
132
      stream->window_update_queued = 1;
1103
132
    } else if (frame->hd.stream_id == 0) {
1104
33
      session->window_update_queued = 1;
1105
33
    }
1106
165
    nghttp2_outbound_queue_push(&session->ob_reg, item);
1107
165
    item->queued = 1;
1108
165
    return 0;
1109
4.42k
  default:
1110
4.42k
    nghttp2_outbound_queue_push(&session->ob_reg, item);
1111
4.42k
    item->queued = 1;
1112
4.42k
    return 0;
1113
82.6k
  }
1114
82.6k
}
1115
1116
int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
1117
31.9k
                                   uint32_t error_code) {
1118
31.9k
  return nghttp2_session_add_rst_stream_continue(
1119
31.9k
    session, stream_id, error_code,
1120
31.9k
    /* continue_without_stream = */ 1);
1121
31.9k
}
1122
1123
int nghttp2_session_add_rst_stream_continue(nghttp2_session *session,
1124
                                            int32_t stream_id,
1125
                                            uint32_t error_code,
1126
31.9k
                                            int continue_without_stream) {
1127
31.9k
  int rv;
1128
31.9k
  nghttp2_outbound_item *item;
1129
31.9k
  nghttp2_frame *frame;
1130
31.9k
  nghttp2_stream *stream;
1131
31.9k
  nghttp2_mem *mem;
1132
1133
31.9k
  mem = &session->mem;
1134
31.9k
  stream = nghttp2_session_get_stream(session, stream_id);
1135
31.9k
  if (stream && stream->state == NGHTTP2_STREAM_CLOSING) {
1136
7.79k
    return 0;
1137
7.79k
  }
1138
1139
  /* Sending RST_STREAM to an idle stream is subject to protocol
1140
     violation.  Historically, nghttp2 allows this.  In order not to
1141
     disrupt the existing applications, we don't error out this case
1142
     and simply ignore it. */
1143
24.1k
  if (nghttp2_session_is_my_stream_id(session, stream_id)) {
1144
0
    if ((uint32_t)stream_id >= session->next_stream_id) {
1145
0
      return 0;
1146
0
    }
1147
24.1k
  } else if (session->last_recv_stream_id < stream_id) {
1148
0
    return 0;
1149
0
  }
1150
1151
  /* Cancel pending request HEADERS in ob_syn if this RST_STREAM
1152
     refers to that stream. */
1153
24.1k
  if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) &&
1154
0
      nghttp2_outbound_queue_top(&session->ob_syn)) {
1155
0
    nghttp2_headers_aux_data *aux_data;
1156
0
    nghttp2_frame *headers_frame;
1157
1158
0
    headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame;
1159
0
    assert(headers_frame->hd.type == NGHTTP2_HEADERS);
1160
1161
0
    if (headers_frame->hd.stream_id <= stream_id) {
1162
0
      for (item = session->ob_syn.head; item; item = item->qnext) {
1163
0
        aux_data = &item->aux_data.headers;
1164
1165
0
        if (item->frame.hd.stream_id < stream_id) {
1166
0
          continue;
1167
0
        }
1168
1169
        /* stream_id in ob_syn queue must be strictly increasing.  If
1170
           we found larger ID, then we can break here. */
1171
0
        if (item->frame.hd.stream_id > stream_id || aux_data->canceled) {
1172
0
          break;
1173
0
        }
1174
1175
0
        aux_data->error_code = error_code;
1176
0
        aux_data->canceled = 1;
1177
1178
0
        return 0;
1179
0
      }
1180
0
    }
1181
0
  }
1182
1183
  /* To keep the old behaviour, do not fail if stream was not
1184
     found. */
1185
24.1k
  if (!continue_without_stream && !stream) {
1186
0
    return 0;
1187
0
  }
1188
1189
24.1k
  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
1190
24.1k
  if (item == NULL) {
1191
0
    return NGHTTP2_ERR_NOMEM;
1192
0
  }
1193
1194
24.1k
  nghttp2_outbound_item_init(item);
1195
1196
24.1k
  frame = &item->frame;
1197
1198
24.1k
  nghttp2_frame_rst_stream_init(&frame->rst_stream, stream_id, error_code);
1199
1200
24.1k
  item->aux_data.rst_stream.continue_without_stream =
1201
24.1k
    (uint8_t)(continue_without_stream != 0);
1202
1203
24.1k
  rv = nghttp2_session_add_item(session, item);
1204
24.1k
  if (rv != 0) {
1205
0
    nghttp2_frame_rst_stream_free(&frame->rst_stream);
1206
0
    nghttp2_mem_free(mem, item);
1207
0
    return rv;
1208
0
  }
1209
24.1k
  return 0;
1210
24.1k
}
1211
1212
nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
1213
                                            int32_t stream_id, uint8_t flags,
1214
                                            nghttp2_stream_state initial_state,
1215
31.7k
                                            void *stream_user_data) {
1216
31.7k
  int rv;
1217
31.7k
  nghttp2_stream *stream;
1218
31.7k
  int stream_alloc = 0;
1219
31.7k
  nghttp2_mem *mem;
1220
1221
31.7k
  mem = &session->mem;
1222
31.7k
  stream = nghttp2_session_get_stream_raw(session, stream_id);
1223
1224
31.7k
  if (session->opt_flags &
1225
31.7k
      NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
1226
0
    flags |= NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION;
1227
0
  }
1228
1229
31.7k
  if (stream) {
1230
0
    assert(stream->state == NGHTTP2_STREAM_IDLE);
1231
0
    assert(initial_state != NGHTTP2_STREAM_IDLE);
1232
1233
0
    --session->num_idle_streams;
1234
31.7k
  } else {
1235
31.7k
    stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream));
1236
31.7k
    if (stream == NULL) {
1237
0
      return NULL;
1238
0
    }
1239
1240
31.7k
    stream_alloc = 1;
1241
31.7k
  }
1242
1243
31.7k
  if (initial_state == NGHTTP2_STREAM_RESERVED) {
1244
0
    flags |= NGHTTP2_STREAM_FLAG_PUSH;
1245
0
  }
1246
1247
31.7k
  if (stream_alloc) {
1248
31.7k
    nghttp2_stream_init(stream, stream_id, flags, initial_state,
1249
31.7k
                        (int32_t)session->remote_settings.initial_window_size,
1250
31.7k
                        (int32_t)session->local_settings.initial_window_size,
1251
31.7k
                        stream_user_data);
1252
31.7k
    stream->seq = session->stream_seq++;
1253
1254
31.7k
    rv = nghttp2_map_insert(&session->streams, stream_id, stream);
1255
31.7k
    if (rv != 0) {
1256
0
      nghttp2_stream_free(stream);
1257
0
      nghttp2_mem_free(mem, stream);
1258
0
      return NULL;
1259
0
    }
1260
31.7k
  } else {
1261
0
    stream->flags = flags;
1262
0
    stream->state = initial_state;
1263
0
    stream->stream_user_data = stream_user_data;
1264
0
  }
1265
1266
31.7k
  switch (initial_state) {
1267
0
  case NGHTTP2_STREAM_RESERVED:
1268
0
    if (nghttp2_session_is_my_stream_id(session, stream_id)) {
1269
      /* reserved (local) */
1270
0
      nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
1271
0
    } else {
1272
      /* reserved (remote) */
1273
0
      nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
1274
0
      ++session->num_incoming_reserved_streams;
1275
0
    }
1276
    /* Reserved stream does not count in the concurrent streams
1277
       limit. That is one of the DOS vector. */
1278
0
    break;
1279
0
  case NGHTTP2_STREAM_IDLE:
1280
0
    ++session->num_idle_streams;
1281
0
    break;
1282
31.7k
  default:
1283
31.7k
    if (nghttp2_session_is_my_stream_id(session, stream_id)) {
1284
0
      ++session->num_outgoing_streams;
1285
31.7k
    } else {
1286
31.7k
      ++session->num_incoming_streams;
1287
31.7k
    }
1288
31.7k
  }
1289
1290
31.7k
  return stream;
1291
31.7k
}
1292
1293
int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
1294
86.8k
                                 uint32_t error_code) {
1295
86.8k
  nghttp2_stream *stream;
1296
86.8k
  nghttp2_mem *mem;
1297
86.8k
  int is_my_stream_id;
1298
1299
86.8k
  mem = &session->mem;
1300
86.8k
  stream = nghttp2_session_get_stream(session, stream_id);
1301
1302
86.8k
  if (!stream) {
1303
66.5k
    return NGHTTP2_ERR_INVALID_ARGUMENT;
1304
66.5k
  }
1305
1306
20.3k
  DEBUGF("stream: stream(%p)=%d close\n", stream, stream->stream_id);
1307
1308
  /* We call on_stream_close_callback even if stream->state is
1309
     NGHTTP2_STREAM_INITIAL. This will happen while sending request
1310
     HEADERS, a local endpoint receives RST_STREAM for that stream. It
1311
     may be PROTOCOL_ERROR, but without notifying stream closure will
1312
     hang the stream in a local endpoint.
1313
  */
1314
1315
20.3k
  if (session->callbacks.on_stream_close_callback) {
1316
0
    if (session->callbacks.on_stream_close_callback(
1317
0
          session, stream_id, error_code, session->user_data) != 0) {
1318
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
1319
0
    }
1320
0
  }
1321
1322
20.3k
  if (stream->item) {
1323
0
    nghttp2_outbound_item *item;
1324
1325
0
    item = stream->item;
1326
1327
0
    session_detach_stream_item(session, stream);
1328
1329
    /* If item is queued, it will be deleted when it is popped
1330
       (nghttp2_session_prep_frame() will fail).  If session->aob.item
1331
       points to this item, let active_outbound_item_reset()
1332
       free the item. */
1333
0
    if (!item->queued && item != session->aob.item) {
1334
0
      nghttp2_outbound_item_free(item, mem);
1335
0
      nghttp2_mem_free(mem, item);
1336
0
    }
1337
0
  }
1338
1339
20.3k
  is_my_stream_id = nghttp2_session_is_my_stream_id(session, stream_id);
1340
1341
  /* pushed streams which is not opened yet is not counted toward max
1342
     concurrent limits */
1343
20.3k
  if ((stream->flags & NGHTTP2_STREAM_FLAG_PUSH)) {
1344
0
    if (!is_my_stream_id) {
1345
0
      --session->num_incoming_reserved_streams;
1346
0
    }
1347
20.3k
  } else {
1348
20.3k
    if (is_my_stream_id) {
1349
0
      --session->num_outgoing_streams;
1350
20.3k
    } else {
1351
20.3k
      --session->num_incoming_streams;
1352
20.3k
    }
1353
20.3k
  }
1354
1355
  /* Closes both directions just in case they are not closed yet */
1356
20.3k
  stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED;
1357
1358
20.3k
  nghttp2_session_destroy_stream(session, stream);
1359
1360
20.3k
  return 0;
1361
20.3k
}
1362
1363
void nghttp2_session_destroy_stream(nghttp2_session *session,
1364
20.3k
                                    nghttp2_stream *stream) {
1365
20.3k
  nghttp2_mem *mem;
1366
1367
20.3k
  DEBUGF("stream: destroy closed stream(%p)=%d\n", stream, stream->stream_id);
1368
1369
20.3k
  mem = &session->mem;
1370
1371
20.3k
  if (stream->queued) {
1372
0
    session_ob_data_remove(session, stream);
1373
0
  }
1374
1375
20.3k
  nghttp2_map_remove(&session->streams, stream->stream_id);
1376
20.3k
  nghttp2_stream_free(stream);
1377
20.3k
  nghttp2_mem_free(mem, stream);
1378
20.3k
}
1379
1380
/*
1381
 * Closes stream with stream ID |stream_id| if both transmission and
1382
 * reception of the stream were disallowed. The |error_code| indicates
1383
 * the reason of the closure.
1384
 *
1385
 * This function returns 0 if it succeeds, or one of the following
1386
 * negative error codes:
1387
 *
1388
 * NGHTTP2_ERR_INVALID_ARGUMENT
1389
 *   The stream is not found.
1390
 * NGHTTP2_ERR_CALLBACK_FAILURE
1391
 *   The callback function failed.
1392
 */
1393
int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session,
1394
465
                                              nghttp2_stream *stream) {
1395
465
  if ((stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR) {
1396
0
    return nghttp2_session_close_stream(session, stream->stream_id,
1397
0
                                        NGHTTP2_NO_ERROR);
1398
0
  }
1399
465
  return 0;
1400
465
}
1401
1402
/*
1403
 * Returns nonzero if local endpoint allows reception of new stream
1404
 * from remote.
1405
 */
1406
32.2k
static int session_allow_incoming_new_stream(nghttp2_session *session) {
1407
32.2k
  return (session->goaway_flags &
1408
32.2k
          (NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_SENT)) == 0;
1409
32.2k
}
1410
1411
/*
1412
 * This function returns nonzero if session is closing.
1413
 */
1414
93.6k
static int session_is_closing(nghttp2_session *session) {
1415
93.6k
  return (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) != 0 ||
1416
78.8k
         (nghttp2_session_want_read(session) == 0 &&
1417
7.57k
          nghttp2_session_want_write(session) == 0);
1418
93.6k
}
1419
1420
/*
1421
 * Check that we can send a frame to the |stream|. This function
1422
 * returns 0 if we can send a frame to the |frame|, or one of the
1423
 * following negative error codes:
1424
 *
1425
 * NGHTTP2_ERR_STREAM_CLOSED
1426
 *   The stream is already closed.
1427
 * NGHTTP2_ERR_STREAM_SHUT_WR
1428
 *   The stream is half-closed for transmission.
1429
 * NGHTTP2_ERR_SESSION_CLOSING
1430
 *   This session is closing.
1431
 */
1432
static int session_predicate_for_stream_send(nghttp2_session *session,
1433
0
                                             nghttp2_stream *stream) {
1434
0
  if (stream == NULL) {
1435
0
    return NGHTTP2_ERR_STREAM_CLOSED;
1436
0
  }
1437
0
  if (session_is_closing(session)) {
1438
0
    return NGHTTP2_ERR_SESSION_CLOSING;
1439
0
  }
1440
0
  if (stream->shut_flags & NGHTTP2_SHUT_WR) {
1441
0
    return NGHTTP2_ERR_STREAM_SHUT_WR;
1442
0
  }
1443
0
  return 0;
1444
0
}
1445
1446
0
int nghttp2_session_check_request_allowed(nghttp2_session *session) {
1447
0
  return !session->server && session->next_stream_id <= INT32_MAX &&
1448
0
         (session->goaway_flags & NGHTTP2_GOAWAY_RECV) == 0 &&
1449
0
         !session_is_closing(session);
1450
0
}
1451
1452
/*
1453
 * This function checks request HEADERS frame, which opens stream, can
1454
 * be sent at this time.
1455
 *
1456
 * This function returns 0 if it succeeds, or one of the following
1457
 * negative error codes:
1458
 *
1459
 * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED
1460
 *     New stream cannot be created because of GOAWAY: session is
1461
 *     going down or received last_stream_id is strictly less than
1462
 *     frame->hd.stream_id.
1463
 * NGHTTP2_ERR_STREAM_CLOSING
1464
 *     request HEADERS was canceled by RST_STREAM while it is in queue.
1465
 */
1466
static int session_predicate_request_headers_send(nghttp2_session *session,
1467
0
                                                  nghttp2_outbound_item *item) {
1468
0
  if (item->aux_data.headers.canceled) {
1469
0
    return NGHTTP2_ERR_STREAM_CLOSING;
1470
0
  }
1471
  /* If we are terminating session (NGHTTP2_GOAWAY_TERM_ON_SEND),
1472
     GOAWAY was received from peer, or session is about to close, new
1473
     request is not allowed. */
1474
0
  if ((session->goaway_flags & NGHTTP2_GOAWAY_RECV) ||
1475
0
      session_is_closing(session)) {
1476
0
    return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
1477
0
  }
1478
0
  return 0;
1479
0
}
1480
1481
/*
1482
 * This function checks HEADERS, which is the first frame from the
1483
 * server, with the |stream| can be sent at this time.  The |stream|
1484
 * can be NULL.
1485
 *
1486
 * This function returns 0 if it succeeds, or one of the following
1487
 * negative error codes:
1488
 *
1489
 * NGHTTP2_ERR_STREAM_CLOSED
1490
 *     The stream is already closed or does not exist.
1491
 * NGHTTP2_ERR_STREAM_SHUT_WR
1492
 *     The transmission is not allowed for this stream (e.g., a frame
1493
 *     with END_STREAM flag set has already sent)
1494
 * NGHTTP2_ERR_INVALID_STREAM_ID
1495
 *     The stream ID is invalid.
1496
 * NGHTTP2_ERR_STREAM_CLOSING
1497
 *     RST_STREAM was queued for this stream.
1498
 * NGHTTP2_ERR_INVALID_STREAM_STATE
1499
 *     The state of the stream is not valid.
1500
 * NGHTTP2_ERR_SESSION_CLOSING
1501
 *     This session is closing.
1502
 * NGHTTP2_ERR_PROTO
1503
 *     Client side attempted to send response.
1504
 */
1505
static int session_predicate_response_headers_send(nghttp2_session *session,
1506
0
                                                   nghttp2_stream *stream) {
1507
0
  int rv;
1508
0
  rv = session_predicate_for_stream_send(session, stream);
1509
0
  if (rv != 0) {
1510
0
    return rv;
1511
0
  }
1512
0
  assert(stream);
1513
0
  if (!session->server) {
1514
0
    return NGHTTP2_ERR_PROTO;
1515
0
  }
1516
0
  if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
1517
0
    return NGHTTP2_ERR_INVALID_STREAM_ID;
1518
0
  }
1519
0
  switch (stream->state) {
1520
0
  case NGHTTP2_STREAM_OPENING:
1521
0
    return 0;
1522
0
  case NGHTTP2_STREAM_CLOSING:
1523
0
    return NGHTTP2_ERR_STREAM_CLOSING;
1524
0
  default:
1525
0
    return NGHTTP2_ERR_INVALID_STREAM_STATE;
1526
0
  }
1527
0
}
1528
1529
/*
1530
 * This function checks HEADERS for reserved stream can be sent. The
1531
 * |stream| must be reserved state and the |session| is server side.
1532
 * The |stream| can be NULL.
1533
 *
1534
 * This function returns 0 if it succeeds, or one of the following
1535
 * error codes:
1536
 *
1537
 * NGHTTP2_ERR_STREAM_CLOSED
1538
 *   The stream is already closed.
1539
 * NGHTTP2_ERR_STREAM_SHUT_WR
1540
 *   The stream is half-closed for transmission.
1541
 * NGHTTP2_ERR_PROTO
1542
 *   The stream is not reserved state
1543
 * NGHTTP2_ERR_STREAM_CLOSED
1544
 *   RST_STREAM was queued for this stream.
1545
 * NGHTTP2_ERR_SESSION_CLOSING
1546
 *   This session is closing.
1547
 * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED
1548
 *   New stream cannot be created because GOAWAY is already sent or
1549
 *   received.
1550
 * NGHTTP2_ERR_PROTO
1551
 *   Client side attempted to send push response.
1552
 */
1553
static int
1554
session_predicate_push_response_headers_send(nghttp2_session *session,
1555
0
                                             nghttp2_stream *stream) {
1556
0
  int rv;
1557
  /* TODO Should disallow HEADERS if GOAWAY has already been issued? */
1558
0
  rv = session_predicate_for_stream_send(session, stream);
1559
0
  if (rv != 0) {
1560
0
    return rv;
1561
0
  }
1562
0
  assert(stream);
1563
0
  if (!session->server) {
1564
0
    return NGHTTP2_ERR_PROTO;
1565
0
  }
1566
0
  if (stream->state != NGHTTP2_STREAM_RESERVED) {
1567
0
    return NGHTTP2_ERR_PROTO;
1568
0
  }
1569
0
  if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) {
1570
0
    return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
1571
0
  }
1572
0
  return 0;
1573
0
}
1574
1575
/*
1576
 * This function checks HEADERS, which is neither stream-opening nor
1577
 * first response header, with the |stream| can be sent at this time.
1578
 * The |stream| can be NULL.
1579
 *
1580
 * This function returns 0 if it succeeds, or one of the following
1581
 * negative error codes:
1582
 *
1583
 * NGHTTP2_ERR_STREAM_CLOSED
1584
 *     The stream is already closed or does not exist.
1585
 * NGHTTP2_ERR_STREAM_SHUT_WR
1586
 *     The transmission is not allowed for this stream (e.g., a frame
1587
 *     with END_STREAM flag set has already sent)
1588
 * NGHTTP2_ERR_STREAM_CLOSING
1589
 *     RST_STREAM was queued for this stream.
1590
 * NGHTTP2_ERR_INVALID_STREAM_STATE
1591
 *     The state of the stream is not valid.
1592
 * NGHTTP2_ERR_SESSION_CLOSING
1593
 *   This session is closing.
1594
 */
1595
static int session_predicate_headers_send(nghttp2_session *session,
1596
0
                                          nghttp2_stream *stream) {
1597
0
  int rv;
1598
0
  rv = session_predicate_for_stream_send(session, stream);
1599
0
  if (rv != 0) {
1600
0
    return rv;
1601
0
  }
1602
0
  assert(stream);
1603
1604
0
  switch (stream->state) {
1605
0
  case NGHTTP2_STREAM_OPENED:
1606
0
    return 0;
1607
0
  case NGHTTP2_STREAM_CLOSING:
1608
0
    return NGHTTP2_ERR_STREAM_CLOSING;
1609
0
  default:
1610
0
    if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
1611
0
      return 0;
1612
0
    }
1613
0
    return NGHTTP2_ERR_INVALID_STREAM_STATE;
1614
0
  }
1615
0
}
1616
1617
/*
1618
 * This function checks PUSH_PROMISE frame |frame| with the |stream|
1619
 * can be sent at this time.  The |stream| can be NULL.
1620
 *
1621
 * This function returns 0 if it succeeds, or one of the following
1622
 * negative error codes:
1623
 *
1624
 * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED
1625
 *     New stream cannot be created because GOAWAY is already sent or
1626
 *     received.
1627
 * NGHTTP2_ERR_PROTO
1628
 *     The client side attempts to send PUSH_PROMISE, or the server
1629
 *     sends PUSH_PROMISE for the stream not initiated by the client.
1630
 * NGHTTP2_ERR_STREAM_CLOSED
1631
 *     The stream is already closed or does not exist.
1632
 * NGHTTP2_ERR_STREAM_CLOSING
1633
 *     RST_STREAM was queued for this stream.
1634
 * NGHTTP2_ERR_STREAM_SHUT_WR
1635
 *     The transmission is not allowed for this stream (e.g., a frame
1636
 *     with END_STREAM flag set has already sent)
1637
 * NGHTTP2_ERR_PUSH_DISABLED
1638
 *     The remote peer disabled reception of PUSH_PROMISE.
1639
 * NGHTTP2_ERR_SESSION_CLOSING
1640
 *   This session is closing.
1641
 */
1642
static int session_predicate_push_promise_send(nghttp2_session *session,
1643
0
                                               nghttp2_stream *stream) {
1644
0
  int rv;
1645
1646
0
  if (!session->server) {
1647
0
    return NGHTTP2_ERR_PROTO;
1648
0
  }
1649
1650
0
  rv = session_predicate_for_stream_send(session, stream);
1651
0
  if (rv != 0) {
1652
0
    return rv;
1653
0
  }
1654
1655
0
  assert(stream);
1656
1657
0
  if (session->remote_settings.enable_push == 0) {
1658
0
    return NGHTTP2_ERR_PUSH_DISABLED;
1659
0
  }
1660
0
  if (stream->state == NGHTTP2_STREAM_CLOSING) {
1661
0
    return NGHTTP2_ERR_STREAM_CLOSING;
1662
0
  }
1663
0
  if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) {
1664
0
    return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
1665
0
  }
1666
0
  return 0;
1667
0
}
1668
1669
/*
1670
 * This function checks WINDOW_UPDATE with the stream ID |stream_id|
1671
 * can be sent at this time. Note that END_STREAM flag of the previous
1672
 * frame does not affect the transmission of the WINDOW_UPDATE frame.
1673
 *
1674
 * This function returns 0 if it succeeds, or one of the following
1675
 * negative error codes:
1676
 *
1677
 * NGHTTP2_ERR_STREAM_CLOSED
1678
 *     The stream is already closed or does not exist.
1679
 * NGHTTP2_ERR_STREAM_CLOSING
1680
 *     RST_STREAM was queued for this stream.
1681
 * NGHTTP2_ERR_INVALID_STREAM_STATE
1682
 *     The state of the stream is not valid.
1683
 * NGHTTP2_ERR_SESSION_CLOSING
1684
 *   This session is closing.
1685
 */
1686
static int session_predicate_window_update_send(nghttp2_session *session,
1687
165
                                                int32_t stream_id) {
1688
165
  nghttp2_stream *stream;
1689
1690
165
  if (session_is_closing(session)) {
1691
40
    return NGHTTP2_ERR_SESSION_CLOSING;
1692
40
  }
1693
1694
125
  if (stream_id == 0) {
1695
    /* Connection-level window update */
1696
13
    return 0;
1697
13
  }
1698
112
  stream = nghttp2_session_get_stream(session, stream_id);
1699
112
  if (stream == NULL) {
1700
21
    return NGHTTP2_ERR_STREAM_CLOSED;
1701
21
  }
1702
91
  if (stream->state == NGHTTP2_STREAM_CLOSING) {
1703
10
    return NGHTTP2_ERR_STREAM_CLOSING;
1704
10
  }
1705
81
  if (state_reserved_local(session, stream)) {
1706
0
    return NGHTTP2_ERR_INVALID_STREAM_STATE;
1707
0
  }
1708
81
  return 0;
1709
81
}
1710
1711
static int session_predicate_altsvc_send(nghttp2_session *session,
1712
0
                                         int32_t stream_id) {
1713
0
  nghttp2_stream *stream;
1714
1715
0
  if (session_is_closing(session)) {
1716
0
    return NGHTTP2_ERR_SESSION_CLOSING;
1717
0
  }
1718
1719
0
  if (stream_id == 0) {
1720
0
    return 0;
1721
0
  }
1722
1723
0
  stream = nghttp2_session_get_stream(session, stream_id);
1724
0
  if (stream == NULL) {
1725
0
    return NGHTTP2_ERR_STREAM_CLOSED;
1726
0
  }
1727
0
  if (stream->state == NGHTTP2_STREAM_CLOSING) {
1728
0
    return NGHTTP2_ERR_STREAM_CLOSING;
1729
0
  }
1730
1731
0
  return 0;
1732
0
}
1733
1734
0
static int session_predicate_origin_send(nghttp2_session *session) {
1735
0
  if (session_is_closing(session)) {
1736
0
    return NGHTTP2_ERR_SESSION_CLOSING;
1737
0
  }
1738
0
  return 0;
1739
0
}
1740
1741
static int session_predicate_priority_update_send(nghttp2_session *session,
1742
0
                                                  int32_t stream_id) {
1743
0
  nghttp2_stream *stream;
1744
1745
0
  if (session_is_closing(session)) {
1746
0
    return NGHTTP2_ERR_SESSION_CLOSING;
1747
0
  }
1748
1749
0
  stream = nghttp2_session_get_stream(session, stream_id);
1750
0
  if (stream == NULL) {
1751
0
    return 0;
1752
0
  }
1753
0
  if (stream->state == NGHTTP2_STREAM_CLOSING) {
1754
0
    return NGHTTP2_ERR_STREAM_CLOSING;
1755
0
  }
1756
0
  if (stream->shut_flags & NGHTTP2_SHUT_RD) {
1757
0
    return NGHTTP2_ERR_INVALID_STREAM_STATE;
1758
0
  }
1759
1760
0
  return 0;
1761
0
}
1762
1763
/* Take into account settings max frame size and both connection-level
1764
   flow control here */
1765
static nghttp2_ssize nghttp2_session_enforce_flow_control_limits(
1766
  nghttp2_session *session, nghttp2_stream *stream,
1767
0
  nghttp2_ssize requested_window_size) {
1768
0
  DEBUGF("send: remote windowsize connection=%d, remote maxframsize=%u, "
1769
0
         "stream(id %d)=%d\n",
1770
0
         session->remote_window_size, session->remote_settings.max_frame_size,
1771
0
         stream->stream_id, stream->remote_window_size);
1772
1773
0
  return nghttp2_min_int32(
1774
0
    nghttp2_min_int32(nghttp2_min_int32((int32_t)requested_window_size,
1775
0
                                        stream->remote_window_size),
1776
0
                      session->remote_window_size),
1777
0
    (int32_t)session->remote_settings.max_frame_size);
1778
0
}
1779
1780
/*
1781
 * Returns the maximum length of next data read. If the
1782
 * connection-level and/or stream-wise flow control are enabled, the
1783
 * return value takes into account those current window sizes. The remote
1784
 * settings for max frame size is also taken into account.
1785
 */
1786
static size_t nghttp2_session_next_data_read(nghttp2_session *session,
1787
0
                                             nghttp2_stream *stream) {
1788
0
  nghttp2_ssize window_size;
1789
1790
0
  window_size = nghttp2_session_enforce_flow_control_limits(
1791
0
    session, stream, NGHTTP2_DATA_PAYLOADLEN);
1792
1793
0
  DEBUGF("send: available window=%td\n", window_size);
1794
1795
0
  return window_size > 0 ? (size_t)window_size : 0;
1796
0
}
1797
1798
/*
1799
 * This function checks DATA with the |stream| can be sent at this
1800
 * time.  The |stream| can be NULL.
1801
 *
1802
 * This function returns 0 if it succeeds, or one of the following
1803
 * negative error codes:
1804
 *
1805
 * NGHTTP2_ERR_STREAM_CLOSED
1806
 *     The stream is already closed or does not exist.
1807
 * NGHTTP2_ERR_STREAM_SHUT_WR
1808
 *     The transmission is not allowed for this stream (e.g., a frame
1809
 *     with END_STREAM flag set has already sent)
1810
 * NGHTTP2_ERR_STREAM_CLOSING
1811
 *     RST_STREAM was queued for this stream.
1812
 * NGHTTP2_ERR_INVALID_STREAM_STATE
1813
 *     The state of the stream is not valid.
1814
 * NGHTTP2_ERR_SESSION_CLOSING
1815
 *   This session is closing.
1816
 */
1817
static int nghttp2_session_predicate_data_send(nghttp2_session *session,
1818
0
                                               nghttp2_stream *stream) {
1819
0
  int rv;
1820
0
  rv = session_predicate_for_stream_send(session, stream);
1821
0
  if (rv != 0) {
1822
0
    return rv;
1823
0
  }
1824
0
  assert(stream);
1825
0
  if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
1826
    /* Request body data */
1827
    /* If stream->state is NGHTTP2_STREAM_CLOSING, RST_STREAM was
1828
       queued but not yet sent. In this case, we won't send DATA
1829
       frames. */
1830
0
    if (stream->state == NGHTTP2_STREAM_CLOSING) {
1831
0
      return NGHTTP2_ERR_STREAM_CLOSING;
1832
0
    }
1833
0
    if (stream->state == NGHTTP2_STREAM_RESERVED) {
1834
0
      return NGHTTP2_ERR_INVALID_STREAM_STATE;
1835
0
    }
1836
0
    return 0;
1837
0
  }
1838
  /* Response body data */
1839
0
  if (stream->state == NGHTTP2_STREAM_OPENED) {
1840
0
    return 0;
1841
0
  }
1842
0
  if (stream->state == NGHTTP2_STREAM_CLOSING) {
1843
0
    return NGHTTP2_ERR_STREAM_CLOSING;
1844
0
  }
1845
0
  return NGHTTP2_ERR_INVALID_STREAM_STATE;
1846
0
}
1847
1848
static nghttp2_ssize session_call_select_padding(nghttp2_session *session,
1849
                                                 const nghttp2_frame *frame,
1850
0
                                                 size_t max_payloadlen) {
1851
0
  nghttp2_ssize rv;
1852
0
  size_t max_paddedlen;
1853
1854
0
  if (frame->hd.length >= max_payloadlen ||
1855
0
      (!session->callbacks.select_padding_callback2 &&
1856
0
       !session->callbacks.select_padding_callback)) {
1857
0
    return (nghttp2_ssize)frame->hd.length;
1858
0
  }
1859
1860
0
  max_paddedlen =
1861
0
    nghttp2_min_size(frame->hd.length + NGHTTP2_MAX_PADLEN, max_payloadlen);
1862
1863
0
  if (session->callbacks.select_padding_callback2) {
1864
0
    rv = session->callbacks.select_padding_callback2(
1865
0
      session, frame, max_paddedlen, session->user_data);
1866
0
  } else {
1867
0
    rv = (nghttp2_ssize)session->callbacks.select_padding_callback(
1868
0
      session, frame, max_paddedlen, session->user_data);
1869
0
  }
1870
0
  if (rv < (nghttp2_ssize)frame->hd.length ||
1871
0
      rv > (nghttp2_ssize)max_paddedlen) {
1872
0
    return NGHTTP2_ERR_CALLBACK_FAILURE;
1873
0
  }
1874
0
  return rv;
1875
0
}
1876
1877
/* Add padding to HEADERS or PUSH_PROMISE. We use
1878
   frame->headers.padlen in this function to use the fact that
1879
   frame->push_promise has also padlen in the same position. */
1880
static int session_headers_add_pad(nghttp2_session *session,
1881
0
                                   nghttp2_frame *frame) {
1882
0
  nghttp2_ssize padded_payloadlen;
1883
0
  nghttp2_active_outbound_item *aob;
1884
0
  nghttp2_bufs *framebufs;
1885
0
  size_t padlen;
1886
0
  size_t max_payloadlen;
1887
1888
0
  aob = &session->aob;
1889
0
  framebufs = &aob->framebufs;
1890
1891
0
  max_payloadlen = nghttp2_min_size(NGHTTP2_MAX_PAYLOADLEN,
1892
0
                                    frame->hd.length + NGHTTP2_MAX_PADLEN);
1893
1894
0
  padded_payloadlen =
1895
0
    session_call_select_padding(session, frame, max_payloadlen);
1896
1897
0
  if (nghttp2_is_fatal((int)padded_payloadlen)) {
1898
0
    return (int)padded_payloadlen;
1899
0
  }
1900
1901
0
  padlen = (size_t)padded_payloadlen - frame->hd.length;
1902
1903
0
  DEBUGF("send: padding selected: payloadlen=%td, padlen=%zu\n",
1904
0
         padded_payloadlen, padlen);
1905
1906
0
  nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, 0);
1907
1908
0
  frame->headers.padlen = padlen;
1909
1910
0
  return 0;
1911
0
}
1912
1913
static size_t session_estimate_headers_payload(nghttp2_session *session,
1914
                                               const nghttp2_nv *nva,
1915
                                               size_t nvlen,
1916
0
                                               size_t additional) {
1917
0
  return nghttp2_hd_deflate_bound(&session->hd_deflater, nva, nvlen) +
1918
0
         additional;
1919
0
}
1920
1921
static int session_pack_extension(nghttp2_session *session, nghttp2_bufs *bufs,
1922
0
                                  nghttp2_frame *frame) {
1923
0
  nghttp2_ssize rv;
1924
0
  nghttp2_buf *buf;
1925
0
  size_t buflen;
1926
0
  size_t framelen;
1927
1928
0
  assert(session->callbacks.pack_extension_callback2 ||
1929
0
         session->callbacks.pack_extension_callback);
1930
1931
0
  buf = &bufs->head->buf;
1932
0
  buflen = nghttp2_min_size(nghttp2_buf_avail(buf), NGHTTP2_MAX_PAYLOADLEN);
1933
1934
0
  if (session->callbacks.pack_extension_callback2) {
1935
0
    rv = session->callbacks.pack_extension_callback2(session, buf->last, buflen,
1936
0
                                                     frame, session->user_data);
1937
0
  } else {
1938
0
    rv = (nghttp2_ssize)session->callbacks.pack_extension_callback(
1939
0
      session, buf->last, buflen, frame, session->user_data);
1940
0
  }
1941
0
  if (rv == NGHTTP2_ERR_CANCEL) {
1942
0
    return (int)rv;
1943
0
  }
1944
1945
0
  if (rv < 0 || (size_t)rv > buflen) {
1946
0
    return NGHTTP2_ERR_CALLBACK_FAILURE;
1947
0
  }
1948
1949
0
  framelen = (size_t)rv;
1950
1951
0
  frame->hd.length = framelen;
1952
1953
0
  assert(buf->pos == buf->last);
1954
0
  buf->last += framelen;
1955
0
  buf->pos -= NGHTTP2_FRAME_HDLEN;
1956
1957
0
  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
1958
1959
0
  return 0;
1960
0
}
1961
1962
/*
1963
 * This function serializes frame for transmission.
1964
 *
1965
 * This function returns 0 if it succeeds, or one of negative error
1966
 * codes, including both fatal and non-fatal ones.
1967
 */
1968
static int session_prep_frame(nghttp2_session *session,
1969
82.6k
                              nghttp2_outbound_item *item) {
1970
82.6k
  int rv;
1971
82.6k
  nghttp2_frame *frame;
1972
82.6k
  nghttp2_mem *mem;
1973
1974
82.6k
  mem = &session->mem;
1975
82.6k
  frame = &item->frame;
1976
1977
82.6k
  switch (frame->hd.type) {
1978
0
  case NGHTTP2_DATA: {
1979
0
    size_t next_readmax;
1980
0
    nghttp2_stream *stream;
1981
1982
0
    stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
1983
1984
0
    if (stream) {
1985
0
      assert(stream->item == item);
1986
0
    }
1987
1988
0
    rv = nghttp2_session_predicate_data_send(session, stream);
1989
0
    if (rv != 0) {
1990
      /* If stream was already closed, nghttp2_session_get_stream()
1991
         returns NULL, but item is still attached to the stream.
1992
         Search stream including closed again. */
1993
0
      stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);
1994
0
      if (stream) {
1995
0
        session_detach_stream_item(session, stream);
1996
0
      }
1997
1998
0
      return rv;
1999
0
    }
2000
    /* Assuming stream is not NULL */
2001
0
    assert(stream);
2002
0
    next_readmax = nghttp2_session_next_data_read(session, stream);
2003
2004
0
    if (next_readmax == 0) {
2005
      /* This must be true since we only pop DATA frame item from
2006
         queue when session->remote_window_size > 0 */
2007
0
      assert(session->remote_window_size > 0);
2008
2009
0
      session_defer_stream_item(session, stream,
2010
0
                                NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
2011
2012
0
      session->aob.item = NULL;
2013
0
      active_outbound_item_reset(&session->aob, mem);
2014
0
      return NGHTTP2_ERR_DEFERRED;
2015
0
    }
2016
2017
0
    rv =
2018
0
      nghttp2_session_pack_data(session, &session->aob.framebufs, next_readmax,
2019
0
                                frame, &item->aux_data.data, stream);
2020
0
    if (rv == NGHTTP2_ERR_PAUSE) {
2021
0
      return rv;
2022
0
    }
2023
0
    if (rv == NGHTTP2_ERR_DEFERRED) {
2024
0
      session_defer_stream_item(session, stream,
2025
0
                                NGHTTP2_STREAM_FLAG_DEFERRED_USER);
2026
2027
0
      session->aob.item = NULL;
2028
0
      active_outbound_item_reset(&session->aob, mem);
2029
0
      return NGHTTP2_ERR_DEFERRED;
2030
0
    }
2031
0
    if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
2032
0
      session_detach_stream_item(session, stream);
2033
2034
0
      rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,
2035
0
                                          NGHTTP2_INTERNAL_ERROR);
2036
0
      if (nghttp2_is_fatal(rv)) {
2037
0
        return rv;
2038
0
      }
2039
0
      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
2040
0
    }
2041
0
    if (rv != 0) {
2042
0
      session_detach_stream_item(session, stream);
2043
2044
0
      return rv;
2045
0
    }
2046
0
    return 0;
2047
0
  }
2048
0
  case NGHTTP2_HEADERS: {
2049
0
    nghttp2_headers_aux_data *aux_data;
2050
0
    size_t estimated_payloadlen;
2051
2052
0
    aux_data = &item->aux_data.headers;
2053
2054
0
    if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
2055
      /* initial HEADERS, which opens stream */
2056
0
      nghttp2_stream *stream;
2057
2058
0
      stream = nghttp2_session_open_stream(
2059
0
        session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE,
2060
0
        NGHTTP2_STREAM_INITIAL, aux_data->stream_user_data);
2061
2062
0
      if (stream == NULL) {
2063
0
        return NGHTTP2_ERR_NOMEM;
2064
0
      }
2065
2066
0
      rv = session_predicate_request_headers_send(session, item);
2067
0
      if (rv != 0) {
2068
0
        return rv;
2069
0
      }
2070
2071
0
      if (session_enforce_http_messaging(session)) {
2072
0
        nghttp2_http_record_request_method(stream, frame);
2073
0
      }
2074
0
    } else {
2075
0
      nghttp2_stream *stream;
2076
2077
0
      stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2078
2079
0
      if (stream && stream->state == NGHTTP2_STREAM_RESERVED) {
2080
0
        rv = session_predicate_push_response_headers_send(session, stream);
2081
0
        if (rv == 0) {
2082
0
          frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE;
2083
2084
0
          if (aux_data->stream_user_data) {
2085
0
            stream->stream_user_data = aux_data->stream_user_data;
2086
0
          }
2087
0
        }
2088
0
      } else if (session_predicate_response_headers_send(session, stream) ==
2089
0
                 0) {
2090
0
        frame->headers.cat = NGHTTP2_HCAT_RESPONSE;
2091
0
        rv = 0;
2092
0
      } else {
2093
0
        frame->headers.cat = NGHTTP2_HCAT_HEADERS;
2094
2095
0
        rv = session_predicate_headers_send(session, stream);
2096
0
      }
2097
2098
0
      if (rv != 0) {
2099
0
        return rv;
2100
0
      }
2101
0
    }
2102
2103
0
    estimated_payloadlen = session_estimate_headers_payload(
2104
0
      session, frame->headers.nva, frame->headers.nvlen,
2105
0
      NGHTTP2_PRIORITY_SPECLEN);
2106
2107
0
    if (estimated_payloadlen > session->max_send_header_block_length) {
2108
0
      return NGHTTP2_ERR_FRAME_SIZE_ERROR;
2109
0
    }
2110
2111
0
    rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers,
2112
0
                                    &session->hd_deflater);
2113
2114
0
    if (rv != 0) {
2115
0
      return rv;
2116
0
    }
2117
2118
0
    DEBUGF("send: before padding, HEADERS serialized in %zu bytes\n",
2119
0
           nghttp2_bufs_len(&session->aob.framebufs));
2120
2121
0
    rv = session_headers_add_pad(session, frame);
2122
2123
0
    if (rv != 0) {
2124
0
      return rv;
2125
0
    }
2126
2127
0
    DEBUGF("send: HEADERS finally serialized in %zu bytes\n",
2128
0
           nghttp2_bufs_len(&session->aob.framebufs));
2129
2130
0
    if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
2131
0
      assert(session->last_sent_stream_id < frame->hd.stream_id);
2132
0
      session->last_sent_stream_id = frame->hd.stream_id;
2133
0
    }
2134
2135
0
    return 0;
2136
0
  }
2137
0
  case NGHTTP2_PRIORITY: {
2138
0
    if (session_is_closing(session)) {
2139
0
      return NGHTTP2_ERR_SESSION_CLOSING;
2140
0
    }
2141
    /* PRIORITY frame can be sent at any time and to any stream
2142
       ID. */
2143
0
    nghttp2_frame_pack_priority(&session->aob.framebufs, &frame->priority);
2144
2145
    /* Peer can send PRIORITY frame against idle stream to create
2146
       "anchor" in dependency tree.  Only client can do this in
2147
       nghttp2.  In nghttp2, only server retains non-active (closed
2148
       or idle) streams in memory, so we don't open stream here. */
2149
0
    return 0;
2150
0
  }
2151
24.1k
  case NGHTTP2_RST_STREAM:
2152
24.1k
    if (session_is_closing(session)) {
2153
3.48k
      return NGHTTP2_ERR_SESSION_CLOSING;
2154
3.48k
    }
2155
2156
20.6k
    if (!item->aux_data.rst_stream.continue_without_stream &&
2157
0
        !nghttp2_session_get_stream(session, frame->rst_stream.hd.stream_id)) {
2158
0
      return NGHTTP2_ERR_STREAM_CLOSED;
2159
0
    }
2160
2161
20.6k
    nghttp2_frame_pack_rst_stream(&session->aob.framebufs, &frame->rst_stream);
2162
20.6k
    return 0;
2163
52.5k
  case NGHTTP2_SETTINGS: {
2164
52.5k
    if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
2165
34.0k
      assert(session->obq_flood_counter_ > 0);
2166
34.0k
      --session->obq_flood_counter_;
2167
      /* When session is about to close, don't send SETTINGS ACK.
2168
         We are required to send SETTINGS without ACK though; for
2169
         example, we have to send SETTINGS as a part of connection
2170
         preface. */
2171
34.0k
      if (session_is_closing(session)) {
2172
11.5k
        return NGHTTP2_ERR_SESSION_CLOSING;
2173
11.5k
      }
2174
34.0k
    }
2175
2176
40.9k
    rv = nghttp2_frame_pack_settings(&session->aob.framebufs, &frame->settings);
2177
40.9k
    if (rv != 0) {
2178
0
      return rv;
2179
0
    }
2180
40.9k
    return 0;
2181
40.9k
  }
2182
0
  case NGHTTP2_PUSH_PROMISE: {
2183
0
    nghttp2_stream *stream;
2184
0
    size_t estimated_payloadlen;
2185
2186
    /* stream could be NULL if associated stream was already
2187
       closed. */
2188
0
    stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2189
2190
    /* predicate should fail if stream is NULL. */
2191
0
    rv = session_predicate_push_promise_send(session, stream);
2192
0
    if (rv != 0) {
2193
0
      return rv;
2194
0
    }
2195
2196
0
    assert(stream);
2197
2198
0
    estimated_payloadlen = session_estimate_headers_payload(
2199
0
      session, frame->push_promise.nva, frame->push_promise.nvlen, 0);
2200
2201
0
    if (estimated_payloadlen > session->max_send_header_block_length) {
2202
0
      return NGHTTP2_ERR_FRAME_SIZE_ERROR;
2203
0
    }
2204
2205
0
    rv = nghttp2_frame_pack_push_promise(
2206
0
      &session->aob.framebufs, &frame->push_promise, &session->hd_deflater);
2207
0
    if (rv != 0) {
2208
0
      return rv;
2209
0
    }
2210
0
    rv = session_headers_add_pad(session, frame);
2211
0
    if (rv != 0) {
2212
0
      return rv;
2213
0
    }
2214
2215
0
    assert(session->last_sent_stream_id + 2 <=
2216
0
           frame->push_promise.promised_stream_id);
2217
0
    session->last_sent_stream_id = frame->push_promise.promised_stream_id;
2218
2219
0
    return 0;
2220
0
  }
2221
1.38k
  case NGHTTP2_PING:
2222
1.38k
    if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
2223
1.38k
      assert(session->obq_flood_counter_ > 0);
2224
1.38k
      --session->obq_flood_counter_;
2225
1.38k
    }
2226
    /* PING frame is allowed to be sent unless termination GOAWAY is
2227
       sent */
2228
1.38k
    if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
2229
322
      return NGHTTP2_ERR_SESSION_CLOSING;
2230
322
    }
2231
1.06k
    nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping);
2232
1.06k
    return 0;
2233
4.42k
  case NGHTTP2_GOAWAY:
2234
4.42k
    rv = nghttp2_frame_pack_goaway(&session->aob.framebufs, &frame->goaway);
2235
4.42k
    if (rv != 0) {
2236
0
      return rv;
2237
0
    }
2238
4.42k
    session->local_last_stream_id = frame->goaway.last_stream_id;
2239
2240
4.42k
    return 0;
2241
165
  case NGHTTP2_WINDOW_UPDATE:
2242
165
    rv = session_predicate_window_update_send(session, frame->hd.stream_id);
2243
165
    if (rv != 0) {
2244
71
      return rv;
2245
71
    }
2246
94
    nghttp2_frame_pack_window_update(&session->aob.framebufs,
2247
94
                                     &frame->window_update);
2248
94
    return 0;
2249
0
  case NGHTTP2_CONTINUATION:
2250
    /* We never handle CONTINUATION here. */
2251
0
    assert(0);
2252
0
    return 0;
2253
0
  default: {
2254
0
    nghttp2_ext_aux_data *aux_data;
2255
2256
    /* extension frame */
2257
2258
0
    aux_data = &item->aux_data.ext;
2259
2260
0
    if (aux_data->builtin == 0) {
2261
0
      if (session_is_closing(session)) {
2262
0
        return NGHTTP2_ERR_SESSION_CLOSING;
2263
0
      }
2264
2265
0
      return session_pack_extension(session, &session->aob.framebufs, frame);
2266
0
    }
2267
2268
0
    switch (frame->hd.type) {
2269
0
    case NGHTTP2_ALTSVC:
2270
0
      rv = session_predicate_altsvc_send(session, frame->hd.stream_id);
2271
0
      if (rv != 0) {
2272
0
        return rv;
2273
0
      }
2274
2275
0
      nghttp2_frame_pack_altsvc(&session->aob.framebufs, &frame->ext);
2276
2277
0
      return 0;
2278
0
    case NGHTTP2_ORIGIN:
2279
0
      rv = session_predicate_origin_send(session);
2280
0
      if (rv != 0) {
2281
0
        return rv;
2282
0
      }
2283
2284
0
      rv = nghttp2_frame_pack_origin(&session->aob.framebufs, &frame->ext);
2285
0
      if (rv != 0) {
2286
0
        return rv;
2287
0
      }
2288
2289
0
      return 0;
2290
0
    case NGHTTP2_PRIORITY_UPDATE: {
2291
0
      nghttp2_ext_priority_update *priority_update = frame->ext.payload;
2292
0
      rv = session_predicate_priority_update_send(session,
2293
0
                                                  priority_update->stream_id);
2294
0
      if (rv != 0) {
2295
0
        return rv;
2296
0
      }
2297
2298
0
      nghttp2_frame_pack_priority_update(&session->aob.framebufs, &frame->ext);
2299
2300
0
      return 0;
2301
0
    }
2302
0
    default:
2303
      /* Unreachable here */
2304
0
      assert(0);
2305
0
      return 0;
2306
0
    }
2307
0
  }
2308
82.6k
  }
2309
82.6k
}
2310
2311
nghttp2_outbound_item *
2312
0
nghttp2_session_get_next_ob_item(nghttp2_session *session) {
2313
0
  if (nghttp2_outbound_queue_top(&session->ob_urgent)) {
2314
0
    return nghttp2_outbound_queue_top(&session->ob_urgent);
2315
0
  }
2316
2317
0
  if (nghttp2_outbound_queue_top(&session->ob_reg)) {
2318
0
    return nghttp2_outbound_queue_top(&session->ob_reg);
2319
0
  }
2320
2321
0
  if (!session_is_outgoing_concurrent_streams_max(session)) {
2322
0
    if (nghttp2_outbound_queue_top(&session->ob_syn)) {
2323
0
      return nghttp2_outbound_queue_top(&session->ob_syn);
2324
0
    }
2325
0
  }
2326
2327
0
  if (session->remote_window_size > 0) {
2328
0
    return session_sched_get_next_outbound_item(session);
2329
0
  }
2330
2331
0
  return NULL;
2332
0
}
2333
2334
nghttp2_outbound_item *
2335
119k
nghttp2_session_pop_next_ob_item(nghttp2_session *session) {
2336
119k
  nghttp2_outbound_item *item;
2337
2338
119k
  item = nghttp2_outbound_queue_top(&session->ob_urgent);
2339
119k
  if (item) {
2340
53.9k
    nghttp2_outbound_queue_pop(&session->ob_urgent);
2341
53.9k
    item->queued = 0;
2342
53.9k
    return item;
2343
53.9k
  }
2344
2345
66.0k
  item = nghttp2_outbound_queue_top(&session->ob_reg);
2346
66.0k
  if (item) {
2347
28.6k
    nghttp2_outbound_queue_pop(&session->ob_reg);
2348
28.6k
    item->queued = 0;
2349
28.6k
    return item;
2350
28.6k
  }
2351
2352
37.3k
  if (!session_is_outgoing_concurrent_streams_max(session)) {
2353
37.3k
    item = nghttp2_outbound_queue_top(&session->ob_syn);
2354
37.3k
    if (item) {
2355
0
      nghttp2_outbound_queue_pop(&session->ob_syn);
2356
0
      item->queued = 0;
2357
0
      return item;
2358
0
    }
2359
37.3k
  }
2360
2361
37.3k
  if (session->remote_window_size > 0) {
2362
37.3k
    return session_sched_get_next_outbound_item(session);
2363
37.3k
  }
2364
2365
0
  return NULL;
2366
37.3k
}
2367
2368
static int session_call_before_frame_send(nghttp2_session *session,
2369
67.1k
                                          nghttp2_frame *frame) {
2370
67.1k
  int rv;
2371
67.1k
  if (session->callbacks.before_frame_send_callback) {
2372
67.1k
    rv = session->callbacks.before_frame_send_callback(session, frame,
2373
67.1k
                                                       session->user_data);
2374
67.1k
    if (rv == NGHTTP2_ERR_CANCEL) {
2375
0
      return rv;
2376
0
    }
2377
2378
67.1k
    if (rv != 0) {
2379
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
2380
0
    }
2381
67.1k
  }
2382
67.1k
  return 0;
2383
67.1k
}
2384
2385
static int session_call_on_frame_send(nghttp2_session *session,
2386
67.1k
                                      nghttp2_frame *frame) {
2387
67.1k
  int rv;
2388
67.1k
  if (session->callbacks.on_frame_send_callback) {
2389
67.1k
    rv = session->callbacks.on_frame_send_callback(session, frame,
2390
67.1k
                                                   session->user_data);
2391
67.1k
    if (rv != 0) {
2392
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
2393
0
    }
2394
67.1k
  }
2395
67.1k
  return 0;
2396
67.1k
}
2397
2398
4.84k
static int find_stream_on_goaway_func(void *entry, void *ptr) {
2399
4.84k
  nghttp2_close_stream_on_goaway_arg *arg;
2400
4.84k
  nghttp2_stream *stream;
2401
2402
4.84k
  arg = (nghttp2_close_stream_on_goaway_arg *)ptr;
2403
4.84k
  stream = (nghttp2_stream *)entry;
2404
2405
4.84k
  if (nghttp2_session_is_my_stream_id(arg->session, stream->stream_id)) {
2406
0
    if (arg->incoming) {
2407
0
      return 0;
2408
0
    }
2409
4.84k
  } else if (!arg->incoming) {
2410
989
    return 0;
2411
989
  }
2412
2413
3.85k
  if (stream->state != NGHTTP2_STREAM_IDLE &&
2414
3.85k
      (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) == 0 &&
2415
3.85k
      stream->stream_id > arg->last_stream_id) {
2416
    /* We are collecting streams to close because we cannot call
2417
       nghttp2_session_close_stream() inside nghttp2_map_each().
2418
       Reuse closed_next member.. bad choice? */
2419
52
    assert(stream->closed_next == NULL);
2420
2421
52
    if (arg->head) {
2422
21
      stream->closed_next = arg->head;
2423
21
      arg->head = stream;
2424
31
    } else {
2425
31
      arg->head = stream;
2426
31
    }
2427
52
  }
2428
2429
3.85k
  return 0;
2430
3.85k
}
2431
2432
/* Closes non-idle and non-closed streams whose stream ID >
2433
   last_stream_id.  If incoming is nonzero, we are going to close
2434
   incoming streams.  Otherwise, close outgoing streams. */
2435
static int session_close_stream_on_goaway(nghttp2_session *session,
2436
                                          int32_t last_stream_id,
2437
6.21k
                                          int incoming) {
2438
6.21k
  int rv;
2439
6.21k
  nghttp2_stream *stream, *next_stream;
2440
6.21k
  nghttp2_close_stream_on_goaway_arg arg = {session, NULL, last_stream_id,
2441
6.21k
                                            incoming};
2442
2443
6.21k
  rv = nghttp2_map_each(&session->streams, find_stream_on_goaway_func, &arg);
2444
6.21k
  assert(rv == 0);
2445
2446
6.21k
  stream = arg.head;
2447
6.26k
  while (stream) {
2448
52
    next_stream = stream->closed_next;
2449
52
    stream->closed_next = NULL;
2450
52
    rv = nghttp2_session_close_stream(session, stream->stream_id,
2451
52
                                      NGHTTP2_REFUSED_STREAM);
2452
2453
    /* stream may be deleted here */
2454
2455
52
    stream = next_stream;
2456
2457
52
    if (nghttp2_is_fatal(rv)) {
2458
      /* Clean up closed_next member just in case */
2459
0
      while (stream) {
2460
0
        next_stream = stream->closed_next;
2461
0
        stream->closed_next = NULL;
2462
0
        stream = next_stream;
2463
0
      }
2464
0
      return rv;
2465
0
    }
2466
52
  }
2467
2468
6.21k
  return 0;
2469
6.21k
}
2470
2471
static void session_reschedule_stream(nghttp2_session *session,
2472
0
                                      nghttp2_stream *stream) {
2473
0
  stream->last_writelen = stream->item->frame.hd.length;
2474
2475
0
  if (!session->server) {
2476
0
    return;
2477
0
  }
2478
2479
0
  session_sched_reschedule_stream(session, stream);
2480
0
}
2481
2482
static int session_update_stream_consumed_size(nghttp2_session *session,
2483
                                               nghttp2_stream *stream,
2484
                                               size_t delta_size);
2485
2486
static int session_update_connection_consumed_size(nghttp2_session *session,
2487
                                                   size_t delta_size);
2488
2489
/*
2490
 * Called after a frame is sent.  This function runs
2491
 * on_frame_send_callback and handles stream closure upon END_STREAM
2492
 * or RST_STREAM.  This function does not reset session->aob.  It is a
2493
 * responsibility of session_after_frame_sent2.
2494
 *
2495
 * This function returns 0 if it succeeds, or one of the following
2496
 * negative error codes:
2497
 *
2498
 * NGHTTP2_ERR_NOMEM
2499
 *     Out of memory.
2500
 * NGHTTP2_ERR_CALLBACK_FAILURE
2501
 *     The callback function failed.
2502
 */
2503
67.1k
static int session_after_frame_sent1(nghttp2_session *session) {
2504
67.1k
  int rv;
2505
67.1k
  nghttp2_active_outbound_item *aob = &session->aob;
2506
67.1k
  nghttp2_outbound_item *item = aob->item;
2507
67.1k
  nghttp2_bufs *framebufs = &aob->framebufs;
2508
67.1k
  nghttp2_frame *frame;
2509
67.1k
  nghttp2_stream *stream;
2510
2511
67.1k
  frame = &item->frame;
2512
2513
67.1k
  if (frame->hd.type == NGHTTP2_DATA) {
2514
0
    nghttp2_data_aux_data *aux_data;
2515
2516
0
    aux_data = &item->aux_data.data;
2517
2518
0
    stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2519
    /* We update flow control window after a frame was completely
2520
       sent. This is possible because we choose payload length not to
2521
       exceed the window */
2522
0
    session->remote_window_size -= (int32_t)frame->hd.length;
2523
0
    if (stream) {
2524
0
      stream->remote_window_size -= (int32_t)frame->hd.length;
2525
0
    }
2526
2527
0
    if (stream && aux_data->eof) {
2528
0
      session_detach_stream_item(session, stream);
2529
2530
      /* Call on_frame_send_callback after
2531
         nghttp2_stream_detach_item(), so that application can issue
2532
         nghttp2_submit_data2() in the callback. */
2533
0
      if (session->callbacks.on_frame_send_callback) {
2534
0
        rv = session_call_on_frame_send(session, frame);
2535
0
        if (nghttp2_is_fatal(rv)) {
2536
0
          return rv;
2537
0
        }
2538
0
      }
2539
2540
0
      if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
2541
0
        int stream_closed;
2542
2543
0
        stream_closed =
2544
0
          (stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR;
2545
2546
0
        nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
2547
2548
0
        rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
2549
0
        if (nghttp2_is_fatal(rv)) {
2550
0
          return rv;
2551
0
        }
2552
        /* stream may be NULL if it was closed */
2553
0
        if (stream_closed) {
2554
0
          stream = NULL;
2555
0
        }
2556
0
      }
2557
0
      return 0;
2558
0
    }
2559
2560
0
    if (session->callbacks.on_frame_send_callback) {
2561
0
      rv = session_call_on_frame_send(session, frame);
2562
0
      if (nghttp2_is_fatal(rv)) {
2563
0
        return rv;
2564
0
      }
2565
0
    }
2566
2567
0
    return 0;
2568
0
  }
2569
2570
  /* non-DATA frame */
2571
2572
67.1k
  if (frame->hd.type == NGHTTP2_HEADERS ||
2573
67.1k
      frame->hd.type == NGHTTP2_PUSH_PROMISE) {
2574
0
    if (nghttp2_bufs_next_present(framebufs)) {
2575
0
      DEBUGF("send: CONTINUATION exists, just return\n");
2576
0
      return 0;
2577
0
    }
2578
0
  }
2579
67.1k
  rv = session_call_on_frame_send(session, frame);
2580
67.1k
  if (nghttp2_is_fatal(rv)) {
2581
0
    return rv;
2582
0
  }
2583
67.1k
  switch (frame->hd.type) {
2584
0
  case NGHTTP2_HEADERS: {
2585
0
    nghttp2_headers_aux_data *aux_data;
2586
2587
0
    stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2588
0
    if (!stream) {
2589
0
      return 0;
2590
0
    }
2591
2592
0
    switch (frame->headers.cat) {
2593
0
    case NGHTTP2_HCAT_REQUEST: {
2594
0
      stream->state = NGHTTP2_STREAM_OPENING;
2595
0
      if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
2596
0
        nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
2597
0
      }
2598
0
      rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
2599
0
      if (nghttp2_is_fatal(rv)) {
2600
0
        return rv;
2601
0
      }
2602
      /* We assume aux_data is a pointer to nghttp2_headers_aux_data */
2603
0
      aux_data = &item->aux_data.headers;
2604
0
      if (nghttp2_data_provider_wrap_contains_read_callback(&aux_data->dpw)) {
2605
        /* nghttp2_submit_data_shared() makes a copy of
2606
           aux_data->dpw */
2607
0
        rv = nghttp2_submit_data_shared(session, NGHTTP2_FLAG_END_STREAM,
2608
0
                                        frame->hd.stream_id, &aux_data->dpw);
2609
0
        if (nghttp2_is_fatal(rv)) {
2610
0
          return rv;
2611
0
        }
2612
        /* TODO nghttp2_submit_data_shared() may fail if stream has
2613
           already DATA frame item.  We might have to handle it
2614
           here. */
2615
0
      }
2616
0
      return 0;
2617
0
    }
2618
0
    case NGHTTP2_HCAT_PUSH_RESPONSE:
2619
0
      stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH);
2620
0
      ++session->num_outgoing_streams;
2621
    /* Fall through */
2622
0
    case NGHTTP2_HCAT_RESPONSE:
2623
0
      stream->state = NGHTTP2_STREAM_OPENED;
2624
    /* Fall through */
2625
0
    case NGHTTP2_HCAT_HEADERS:
2626
0
      if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
2627
0
        nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
2628
0
      }
2629
0
      rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
2630
0
      if (nghttp2_is_fatal(rv)) {
2631
0
        return rv;
2632
0
      }
2633
      /* We assume aux_data is a pointer to nghttp2_headers_aux_data */
2634
0
      aux_data = &item->aux_data.headers;
2635
0
      if (nghttp2_data_provider_wrap_contains_read_callback(&aux_data->dpw)) {
2636
0
        rv = nghttp2_submit_data_shared(session, NGHTTP2_FLAG_END_STREAM,
2637
0
                                        frame->hd.stream_id, &aux_data->dpw);
2638
0
        if (nghttp2_is_fatal(rv)) {
2639
0
          return rv;
2640
0
        }
2641
        /* TODO nghttp2_submit_data_shared() may fail if stream has
2642
           already DATA frame item.  We might have to handle it
2643
           here. */
2644
0
      }
2645
0
      return 0;
2646
0
    default:
2647
      /* Unreachable */
2648
0
      assert(0);
2649
0
      return 0;
2650
0
    }
2651
0
  }
2652
0
  case NGHTTP2_PRIORITY:
2653
0
    return 0;
2654
20.6k
  case NGHTTP2_RST_STREAM:
2655
20.6k
    rv = nghttp2_session_close_stream(session, frame->hd.stream_id,
2656
20.6k
                                      frame->rst_stream.error_code);
2657
20.6k
    if (nghttp2_is_fatal(rv)) {
2658
0
      return rv;
2659
0
    }
2660
20.6k
    return 0;
2661
4.42k
  case NGHTTP2_GOAWAY: {
2662
4.42k
    nghttp2_goaway_aux_data *aux_data;
2663
2664
4.42k
    aux_data = &item->aux_data.goaway;
2665
2666
4.42k
    if ((aux_data->flags & NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE) == 0) {
2667
4.42k
      if (aux_data->flags & NGHTTP2_GOAWAY_AUX_TERM_ON_SEND) {
2668
4.37k
        session->goaway_flags |= NGHTTP2_GOAWAY_TERM_SENT;
2669
4.37k
      }
2670
2671
4.42k
      session->goaway_flags |= NGHTTP2_GOAWAY_SENT;
2672
2673
4.42k
      rv = session_close_stream_on_goaway(session, frame->goaway.last_stream_id,
2674
4.42k
                                          1);
2675
2676
4.42k
      if (nghttp2_is_fatal(rv)) {
2677
0
        return rv;
2678
0
      }
2679
4.42k
    }
2680
2681
4.42k
    return 0;
2682
4.42k
  }
2683
94
  case NGHTTP2_WINDOW_UPDATE:
2684
94
    if (frame->hd.stream_id == 0) {
2685
13
      session->window_update_queued = 0;
2686
13
      if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
2687
0
        rv = session_update_connection_consumed_size(session, 0);
2688
13
      } else {
2689
13
        rv = nghttp2_session_update_recv_connection_window_size(session, 0);
2690
13
      }
2691
2692
13
      if (nghttp2_is_fatal(rv)) {
2693
0
        return rv;
2694
0
      }
2695
2696
13
      return 0;
2697
13
    }
2698
2699
81
    stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2700
81
    if (!stream) {
2701
0
      return 0;
2702
0
    }
2703
2704
81
    stream->window_update_queued = 0;
2705
2706
    /* We don't have to send WINDOW_UPDATE if END_STREAM from peer
2707
       is seen. */
2708
81
    if (stream->shut_flags & NGHTTP2_SHUT_RD) {
2709
12
      return 0;
2710
12
    }
2711
2712
69
    if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
2713
0
      rv = session_update_stream_consumed_size(session, stream, 0);
2714
69
    } else {
2715
69
      rv =
2716
69
        nghttp2_session_update_recv_stream_window_size(session, stream, 0, 1);
2717
69
    }
2718
2719
69
    if (nghttp2_is_fatal(rv)) {
2720
0
      return rv;
2721
0
    }
2722
2723
69
    return 0;
2724
42.0k
  default:
2725
42.0k
    return 0;
2726
67.1k
  }
2727
67.1k
}
2728
2729
/*
2730
 * Called after a frame is sent and session_after_frame_sent1.  This
2731
 * function is responsible to reset session->aob.
2732
 */
2733
67.1k
static void session_after_frame_sent2(nghttp2_session *session) {
2734
67.1k
  nghttp2_active_outbound_item *aob = &session->aob;
2735
67.1k
  nghttp2_outbound_item *item = aob->item;
2736
67.1k
  nghttp2_bufs *framebufs = &aob->framebufs;
2737
67.1k
  nghttp2_frame *frame;
2738
67.1k
  nghttp2_mem *mem;
2739
67.1k
  nghttp2_stream *stream;
2740
67.1k
  nghttp2_data_aux_data *aux_data;
2741
2742
67.1k
  mem = &session->mem;
2743
67.1k
  frame = &item->frame;
2744
2745
67.1k
  if (frame->hd.type != NGHTTP2_DATA) {
2746
67.1k
    if (frame->hd.type == NGHTTP2_HEADERS ||
2747
67.1k
        frame->hd.type == NGHTTP2_PUSH_PROMISE) {
2748
0
      if (nghttp2_bufs_next_present(framebufs)) {
2749
0
        framebufs->cur = framebufs->cur->next;
2750
2751
0
        DEBUGF("send: next CONTINUATION frame, %zu bytes\n",
2752
0
               nghttp2_buf_len(&framebufs->cur->buf));
2753
2754
0
        return;
2755
0
      }
2756
0
    }
2757
2758
67.1k
    active_outbound_item_reset(&session->aob, mem);
2759
2760
67.1k
    return;
2761
67.1k
  }
2762
2763
  /* DATA frame */
2764
2765
0
  aux_data = &item->aux_data.data;
2766
2767
  /* On EOF, we have already detached data.  Please note that
2768
     application may issue nghttp2_submit_data2() in
2769
     on_frame_send_callback (call from session_after_frame_sent1),
2770
     which attach data to stream.  We don't want to detach it. */
2771
0
  if (aux_data->eof) {
2772
0
    active_outbound_item_reset(aob, mem);
2773
2774
0
    return;
2775
0
  }
2776
2777
  /* Reset no_copy here because next write may not use this. */
2778
0
  aux_data->no_copy = 0;
2779
2780
0
  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2781
2782
  /* If session is closed or RST_STREAM was queued, we won't send
2783
     further data. */
2784
0
  if (nghttp2_session_predicate_data_send(session, stream) != 0) {
2785
0
    if (stream) {
2786
0
      session_detach_stream_item(session, stream);
2787
0
    }
2788
2789
0
    active_outbound_item_reset(aob, mem);
2790
2791
0
    return;
2792
0
  }
2793
2794
0
  aob->item = NULL;
2795
0
  active_outbound_item_reset(&session->aob, mem);
2796
2797
0
  return;
2798
0
}
2799
2800
static int session_call_send_data(nghttp2_session *session,
2801
                                  nghttp2_outbound_item *item,
2802
0
                                  nghttp2_bufs *framebufs) {
2803
0
  int rv;
2804
0
  nghttp2_buf *buf;
2805
0
  size_t length;
2806
0
  nghttp2_frame *frame;
2807
0
  nghttp2_data_aux_data *aux_data;
2808
2809
0
  buf = &framebufs->cur->buf;
2810
0
  frame = &item->frame;
2811
0
  length = frame->hd.length - frame->data.padlen;
2812
0
  aux_data = &item->aux_data.data;
2813
2814
0
  rv = session->callbacks.send_data_callback(session, frame, buf->pos, length,
2815
                                             /* This is fine because
2816
                                                of Common Initial
2817
                                                Sequence rule. */
2818
0
                                             &aux_data->dpw.data_prd.v2.source,
2819
0
                                             session->user_data);
2820
2821
0
  switch (rv) {
2822
0
  case 0:
2823
0
  case NGHTTP2_ERR_WOULDBLOCK:
2824
0
  case NGHTTP2_ERR_PAUSE:
2825
0
  case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE:
2826
0
    return rv;
2827
0
  default:
2828
0
    return NGHTTP2_ERR_CALLBACK_FAILURE;
2829
0
  }
2830
0
}
2831
2832
static nghttp2_ssize nghttp2_session_mem_send_internal(nghttp2_session *session,
2833
                                                       const uint8_t **data_ptr,
2834
104k
                                                       int fast_cb) {
2835
104k
  int rv;
2836
104k
  nghttp2_active_outbound_item *aob;
2837
104k
  nghttp2_bufs *framebufs;
2838
104k
  nghttp2_mem *mem;
2839
2840
104k
  mem = &session->mem;
2841
104k
  aob = &session->aob;
2842
104k
  framebufs = &aob->framebufs;
2843
2844
254k
  for (;;) {
2845
254k
    switch (aob->state) {
2846
119k
    case NGHTTP2_OB_POP_ITEM: {
2847
119k
      nghttp2_outbound_item *item;
2848
2849
119k
      item = nghttp2_session_pop_next_ob_item(session);
2850
119k
      if (item == NULL) {
2851
37.3k
        return 0;
2852
37.3k
      }
2853
2854
82.6k
      rv = session_prep_frame(session, item);
2855
82.6k
      if (rv == NGHTTP2_ERR_PAUSE) {
2856
0
        return 0;
2857
0
      }
2858
82.6k
      if (rv == NGHTTP2_ERR_DEFERRED) {
2859
0
        DEBUGF("send: frame transmission deferred\n");
2860
0
        break;
2861
0
      }
2862
82.6k
      if (rv < 0) {
2863
15.4k
        int32_t opened_stream_id = 0;
2864
15.4k
        uint32_t error_code = NGHTTP2_INTERNAL_ERROR;
2865
15.4k
        int rv2 = 0;
2866
2867
15.4k
        DEBUGF("send: frame preparation failed with %s\n",
2868
15.4k
               nghttp2_strerror(rv));
2869
        /* TODO If the error comes from compressor, the connection
2870
           must be closed. */
2871
15.4k
        if (item->frame.hd.type != NGHTTP2_DATA &&
2872
15.4k
            session->callbacks.on_frame_not_send_callback && is_non_fatal(rv)) {
2873
0
          nghttp2_frame *frame = &item->frame;
2874
          /* The library is responsible for the transmission of
2875
             WINDOW_UPDATE frame, so we don't call error callback for
2876
             it.  As for RST_STREAM, if it is not sent due to missing
2877
             stream, we also do not call error callback because it may
2878
             cause a lot of noises.*/
2879
0
          if (frame->hd.type != NGHTTP2_WINDOW_UPDATE &&
2880
0
              (frame->hd.type != NGHTTP2_RST_STREAM ||
2881
0
               rv != NGHTTP2_ERR_STREAM_CLOSED) &&
2882
0
              session->callbacks.on_frame_not_send_callback(
2883
0
                session, frame, rv, session->user_data) != 0) {
2884
0
            nghttp2_outbound_item_free(item, mem);
2885
0
            nghttp2_mem_free(mem, item);
2886
2887
0
            return NGHTTP2_ERR_CALLBACK_FAILURE;
2888
0
          }
2889
0
        }
2890
        /* We have to close stream opened by failed request HEADERS
2891
           or PUSH_PROMISE. */
2892
15.4k
        switch (item->frame.hd.type) {
2893
0
        case NGHTTP2_HEADERS:
2894
0
          if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) {
2895
0
            opened_stream_id = item->frame.hd.stream_id;
2896
0
            if (item->aux_data.headers.canceled) {
2897
0
              error_code = item->aux_data.headers.error_code;
2898
0
            } else {
2899
              /* Set error_code to REFUSED_STREAM so that application
2900
                 can send request again. */
2901
0
              error_code = NGHTTP2_REFUSED_STREAM;
2902
0
            }
2903
0
          }
2904
0
          break;
2905
0
        case NGHTTP2_PUSH_PROMISE:
2906
0
          opened_stream_id = item->frame.push_promise.promised_stream_id;
2907
0
          break;
2908
15.4k
        }
2909
15.4k
        if (opened_stream_id) {
2910
          /* careful not to override rv */
2911
0
          rv2 =
2912
0
            nghttp2_session_close_stream(session, opened_stream_id, error_code);
2913
0
        }
2914
2915
15.4k
        nghttp2_outbound_item_free(item, mem);
2916
15.4k
        nghttp2_mem_free(mem, item);
2917
15.4k
        active_outbound_item_reset(aob, mem);
2918
2919
15.4k
        if (nghttp2_is_fatal(rv2)) {
2920
0
          return rv2;
2921
0
        }
2922
2923
15.4k
        if (rv == NGHTTP2_ERR_HEADER_COMP) {
2924
          /* If header compression error occurred, should terminate
2925
             connection. */
2926
0
          rv =
2927
0
            nghttp2_session_terminate_session(session, NGHTTP2_INTERNAL_ERROR);
2928
0
        }
2929
15.4k
        if (nghttp2_is_fatal(rv)) {
2930
0
          return rv;
2931
0
        }
2932
15.4k
        break;
2933
15.4k
      }
2934
2935
67.1k
      aob->item = item;
2936
2937
67.1k
      nghttp2_bufs_rewind(framebufs);
2938
2939
67.1k
      if (item->frame.hd.type != NGHTTP2_DATA) {
2940
67.1k
        nghttp2_frame *frame;
2941
2942
67.1k
        frame = &item->frame;
2943
2944
67.1k
        DEBUGF("send: next frame: payloadlen=%zu, type=%u, flags=0x%02x, "
2945
67.1k
               "stream_id=%d\n",
2946
67.1k
               frame->hd.length, frame->hd.type, frame->hd.flags,
2947
67.1k
               frame->hd.stream_id);
2948
2949
67.1k
        rv = session_call_before_frame_send(session, frame);
2950
67.1k
        if (nghttp2_is_fatal(rv)) {
2951
0
          return rv;
2952
0
        }
2953
2954
67.1k
        if (rv == NGHTTP2_ERR_CANCEL) {
2955
0
          int32_t opened_stream_id = 0;
2956
0
          uint32_t error_code = NGHTTP2_INTERNAL_ERROR;
2957
2958
0
          if (session->callbacks.on_frame_not_send_callback) {
2959
0
            if (session->callbacks.on_frame_not_send_callback(
2960
0
                  session, frame, rv, session->user_data) != 0) {
2961
0
              return NGHTTP2_ERR_CALLBACK_FAILURE;
2962
0
            }
2963
0
          }
2964
2965
          /* We have to close stream opened by canceled request
2966
             HEADERS or PUSH_PROMISE. */
2967
0
          switch (item->frame.hd.type) {
2968
0
          case NGHTTP2_HEADERS:
2969
0
            if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) {
2970
0
              opened_stream_id = item->frame.hd.stream_id;
2971
              /* We don't have to check
2972
                 item->aux_data.headers.canceled since it has already
2973
                 been checked. */
2974
              /* Set error_code to REFUSED_STREAM so that application
2975
                 can send request again. */
2976
0
              error_code = NGHTTP2_REFUSED_STREAM;
2977
0
            }
2978
0
            break;
2979
0
          case NGHTTP2_PUSH_PROMISE:
2980
0
            opened_stream_id = item->frame.push_promise.promised_stream_id;
2981
0
            break;
2982
0
          }
2983
0
          if (opened_stream_id) {
2984
            /* careful not to override rv */
2985
0
            int rv2;
2986
0
            rv2 = nghttp2_session_close_stream(session, opened_stream_id,
2987
0
                                               error_code);
2988
2989
0
            if (nghttp2_is_fatal(rv2)) {
2990
0
              return rv2;
2991
0
            }
2992
0
          }
2993
2994
0
          active_outbound_item_reset(aob, mem);
2995
2996
0
          break;
2997
0
        }
2998
67.1k
      } else {
2999
0
        DEBUGF("send: next frame: DATA\n");
3000
3001
0
        if (item->aux_data.data.no_copy) {
3002
0
          aob->state = NGHTTP2_OB_SEND_NO_COPY;
3003
0
          break;
3004
0
        }
3005
0
      }
3006
3007
67.1k
      DEBUGF("send: start transmitting frame type=%u, length=%td\n",
3008
67.1k
             framebufs->cur->buf.pos[3],
3009
67.1k
             framebufs->cur->buf.last - framebufs->cur->buf.pos);
3010
3011
67.1k
      aob->state = NGHTTP2_OB_SEND_DATA;
3012
3013
67.1k
      break;
3014
67.1k
    }
3015
134k
    case NGHTTP2_OB_SEND_DATA: {
3016
134k
      size_t datalen;
3017
134k
      nghttp2_buf *buf;
3018
3019
134k
      buf = &framebufs->cur->buf;
3020
3021
134k
      if (buf->pos == buf->last) {
3022
67.1k
        DEBUGF("send: end transmission of a frame\n");
3023
3024
        /* Frame has completely sent */
3025
67.1k
        if (fast_cb) {
3026
67.1k
          session_after_frame_sent2(session);
3027
67.1k
        } else {
3028
0
          rv = session_after_frame_sent1(session);
3029
0
          if (rv < 0) {
3030
            /* FATAL */
3031
0
            assert(nghttp2_is_fatal(rv));
3032
0
            return rv;
3033
0
          }
3034
0
          session_after_frame_sent2(session);
3035
0
        }
3036
        /* We have already adjusted the next state */
3037
67.1k
        break;
3038
67.1k
      }
3039
3040
67.1k
      *data_ptr = buf->pos;
3041
67.1k
      datalen = nghttp2_buf_len(buf);
3042
3043
      /* We increment the offset here. If send_callback does not send
3044
         everything, we will adjust it. */
3045
67.1k
      buf->pos += datalen;
3046
3047
67.1k
      return (nghttp2_ssize)datalen;
3048
134k
    }
3049
0
    case NGHTTP2_OB_SEND_NO_COPY: {
3050
0
      nghttp2_stream *stream;
3051
0
      nghttp2_frame *frame;
3052
0
      int pause;
3053
3054
0
      DEBUGF("send: no copy DATA\n");
3055
3056
0
      frame = &aob->item->frame;
3057
3058
0
      stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
3059
0
      if (stream == NULL) {
3060
0
        DEBUGF("send: no copy DATA cancelled because stream was closed\n");
3061
3062
0
        active_outbound_item_reset(aob, mem);
3063
3064
0
        break;
3065
0
      }
3066
3067
0
      rv = session_call_send_data(session, aob->item, framebufs);
3068
0
      if (nghttp2_is_fatal(rv)) {
3069
0
        return rv;
3070
0
      }
3071
3072
0
      if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
3073
0
        session_detach_stream_item(session, stream);
3074
3075
0
        rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id,
3076
0
                                            NGHTTP2_INTERNAL_ERROR);
3077
0
        if (nghttp2_is_fatal(rv)) {
3078
0
          return rv;
3079
0
        }
3080
3081
0
        active_outbound_item_reset(aob, mem);
3082
3083
0
        break;
3084
0
      }
3085
3086
0
      if (rv == NGHTTP2_ERR_WOULDBLOCK) {
3087
0
        return 0;
3088
0
      }
3089
3090
0
      pause = (rv == NGHTTP2_ERR_PAUSE);
3091
3092
0
      rv = session_after_frame_sent1(session);
3093
0
      if (rv < 0) {
3094
0
        assert(nghttp2_is_fatal(rv));
3095
0
        return rv;
3096
0
      }
3097
0
      session_after_frame_sent2(session);
3098
3099
      /* We have already adjusted the next state */
3100
3101
0
      if (pause) {
3102
0
        return 0;
3103
0
      }
3104
3105
0
      break;
3106
0
    }
3107
0
    case NGHTTP2_OB_SEND_CLIENT_MAGIC: {
3108
0
      size_t datalen;
3109
0
      nghttp2_buf *buf;
3110
3111
0
      buf = &framebufs->cur->buf;
3112
3113
0
      if (buf->pos == buf->last) {
3114
0
        DEBUGF("send: end transmission of client magic\n");
3115
0
        active_outbound_item_reset(aob, mem);
3116
0
        break;
3117
0
      }
3118
3119
0
      *data_ptr = buf->pos;
3120
0
      datalen = nghttp2_buf_len(buf);
3121
3122
0
      buf->pos += datalen;
3123
3124
0
      return (nghttp2_ssize)datalen;
3125
0
    }
3126
254k
    }
3127
254k
  }
3128
104k
}
3129
3130
ssize_t nghttp2_session_mem_send(nghttp2_session *session,
3131
0
                                 const uint8_t **data_ptr) {
3132
0
  return (ssize_t)nghttp2_session_mem_send2(session, data_ptr);
3133
0
}
3134
3135
nghttp2_ssize nghttp2_session_mem_send2(nghttp2_session *session,
3136
104k
                                        const uint8_t **data_ptr) {
3137
104k
  int rv;
3138
104k
  nghttp2_ssize len;
3139
3140
104k
  *data_ptr = NULL;
3141
3142
104k
  len = nghttp2_session_mem_send_internal(session, data_ptr, 1);
3143
104k
  if (len <= 0) {
3144
37.3k
    return len;
3145
37.3k
  }
3146
3147
67.1k
  if (session->aob.item) {
3148
    /* We have to call session_after_frame_sent1 here to handle stream
3149
       closure upon transmission of frames.  Otherwise, END_STREAM may
3150
       be reached to client before we call nghttp2_session_mem_send
3151
       again and we may get exceeding number of incoming streams. */
3152
67.1k
    rv = session_after_frame_sent1(session);
3153
67.1k
    if (rv < 0) {
3154
0
      assert(nghttp2_is_fatal(rv));
3155
0
      return (nghttp2_ssize)rv;
3156
0
    }
3157
67.1k
  }
3158
3159
67.1k
  return len;
3160
67.1k
}
3161
3162
0
int nghttp2_session_send(nghttp2_session *session) {
3163
0
  const uint8_t *data = NULL;
3164
0
  nghttp2_ssize datalen;
3165
0
  nghttp2_ssize sentlen;
3166
0
  nghttp2_bufs *framebufs;
3167
3168
0
  framebufs = &session->aob.framebufs;
3169
3170
0
  for (;;) {
3171
0
    datalen = nghttp2_session_mem_send_internal(session, &data, 0);
3172
0
    if (datalen <= 0) {
3173
0
      return (int)datalen;
3174
0
    }
3175
0
    if (session->callbacks.send_callback2) {
3176
0
      sentlen = session->callbacks.send_callback2(
3177
0
        session, data, (size_t)datalen, 0, session->user_data);
3178
0
    } else {
3179
0
      sentlen = (nghttp2_ssize)session->callbacks.send_callback(
3180
0
        session, data, (size_t)datalen, 0, session->user_data);
3181
0
    }
3182
0
    if (sentlen < 0) {
3183
0
      if (sentlen == NGHTTP2_ERR_WOULDBLOCK) {
3184
        /* Transmission canceled. Rewind the offset */
3185
0
        framebufs->cur->buf.pos -= datalen;
3186
3187
0
        return 0;
3188
0
      }
3189
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
3190
0
    }
3191
    /* Rewind the offset to the amount of unsent bytes */
3192
0
    framebufs->cur->buf.pos -= datalen - sentlen;
3193
0
  }
3194
0
}
3195
3196
static nghttp2_ssize session_recv(nghttp2_session *session, uint8_t *buf,
3197
0
                                  size_t len) {
3198
0
  nghttp2_ssize rv;
3199
3200
0
  if (session->callbacks.recv_callback2) {
3201
0
    rv = session->callbacks.recv_callback2(session, buf, len, 0,
3202
0
                                           session->user_data);
3203
0
  } else {
3204
0
    rv = (nghttp2_ssize)session->callbacks.recv_callback(session, buf, len, 0,
3205
0
                                                         session->user_data);
3206
0
  }
3207
0
  if (rv > 0) {
3208
0
    if ((size_t)rv > len) {
3209
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
3210
0
    }
3211
0
  } else if (rv < 0 && rv != NGHTTP2_ERR_WOULDBLOCK && rv != NGHTTP2_ERR_EOF) {
3212
0
    return NGHTTP2_ERR_CALLBACK_FAILURE;
3213
0
  }
3214
0
  return rv;
3215
0
}
3216
3217
static int session_call_on_begin_frame(nghttp2_session *session,
3218
259k
                                       const nghttp2_frame_hd *hd) {
3219
259k
  int rv;
3220
3221
259k
  if (session->callbacks.on_begin_frame_callback) {
3222
0
    rv = session->callbacks.on_begin_frame_callback(session, hd,
3223
0
                                                    session->user_data);
3224
3225
0
    if (rv != 0) {
3226
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
3227
0
    }
3228
0
  }
3229
3230
259k
  return 0;
3231
259k
}
3232
3233
static int session_call_on_frame_received(nghttp2_session *session,
3234
113k
                                          nghttp2_frame *frame) {
3235
113k
  int rv;
3236
113k
  if (session->callbacks.on_frame_recv_callback) {
3237
113k
    rv = session->callbacks.on_frame_recv_callback(session, frame,
3238
113k
                                                   session->user_data);
3239
113k
    if (rv != 0) {
3240
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
3241
0
    }
3242
113k
  }
3243
113k
  return 0;
3244
113k
}
3245
3246
static int session_call_on_begin_headers(nghttp2_session *session,
3247
32.1k
                                         nghttp2_frame *frame) {
3248
32.1k
  int rv;
3249
32.1k
  DEBUGF("recv: call on_begin_headers callback stream_id=%d\n",
3250
32.1k
         frame->hd.stream_id);
3251
32.1k
  if (session->callbacks.on_begin_headers_callback) {
3252
32.1k
    rv = session->callbacks.on_begin_headers_callback(session, frame,
3253
32.1k
                                                      session->user_data);
3254
32.1k
    if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
3255
0
      return rv;
3256
0
    }
3257
32.1k
    if (rv != 0) {
3258
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
3259
0
    }
3260
32.1k
  }
3261
32.1k
  return 0;
3262
32.1k
}
3263
3264
static int session_call_on_header(nghttp2_session *session,
3265
                                  const nghttp2_frame *frame,
3266
59.9k
                                  const nghttp2_hd_nv *nv) {
3267
59.9k
  int rv = 0;
3268
59.9k
  if (session->callbacks.on_header_callback2) {
3269
59.9k
    rv = session->callbacks.on_header_callback2(
3270
59.9k
      session, frame, nv->name, nv->value, nv->flags, session->user_data);
3271
59.9k
  } else if (session->callbacks.on_header_callback) {
3272
0
    rv = session->callbacks.on_header_callback(
3273
0
      session, frame, nv->name->base, nv->name->len, nv->value->base,
3274
0
      nv->value->len, nv->flags, session->user_data);
3275
0
  }
3276
3277
59.9k
  if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
3278
0
    return rv;
3279
0
  }
3280
59.9k
  if (rv != 0) {
3281
0
    return NGHTTP2_ERR_CALLBACK_FAILURE;
3282
0
  }
3283
3284
59.9k
  return 0;
3285
59.9k
}
3286
3287
static int session_call_on_invalid_header(nghttp2_session *session,
3288
                                          const nghttp2_frame *frame,
3289
20.3k
                                          const nghttp2_hd_nv *nv) {
3290
20.3k
  int rv;
3291
20.3k
  if (session->callbacks.on_invalid_header_callback2) {
3292
0
    rv = session->callbacks.on_invalid_header_callback2(
3293
0
      session, frame, nv->name, nv->value, nv->flags, session->user_data);
3294
20.3k
  } else if (session->callbacks.on_invalid_header_callback) {
3295
0
    rv = session->callbacks.on_invalid_header_callback(
3296
0
      session, frame, nv->name->base, nv->name->len, nv->value->base,
3297
0
      nv->value->len, nv->flags, session->user_data);
3298
20.3k
  } else {
3299
    /* If both callbacks are not set, the invalid field nv is
3300
       ignored. */
3301
20.3k
    return 0;
3302
20.3k
  }
3303
3304
0
  if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
3305
0
    return rv;
3306
0
  }
3307
0
  if (rv != 0) {
3308
0
    return NGHTTP2_ERR_CALLBACK_FAILURE;
3309
0
  }
3310
3311
0
  return 0;
3312
0
}
3313
3314
static int
3315
session_call_on_extension_chunk_recv_callback(nghttp2_session *session,
3316
0
                                              const uint8_t *data, size_t len) {
3317
0
  int rv;
3318
0
  nghttp2_inbound_frame *iframe = &session->iframe;
3319
0
  nghttp2_frame *frame = &iframe->frame;
3320
3321
0
  if (session->callbacks.on_extension_chunk_recv_callback) {
3322
0
    rv = session->callbacks.on_extension_chunk_recv_callback(
3323
0
      session, &frame->hd, data, len, session->user_data);
3324
0
    if (rv == NGHTTP2_ERR_CANCEL) {
3325
0
      return rv;
3326
0
    }
3327
0
    if (rv != 0) {
3328
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
3329
0
    }
3330
0
  }
3331
3332
0
  return 0;
3333
0
}
3334
3335
0
static int session_call_unpack_extension_callback(nghttp2_session *session) {
3336
0
  int rv;
3337
0
  nghttp2_inbound_frame *iframe = &session->iframe;
3338
0
  nghttp2_frame *frame = &iframe->frame;
3339
0
  void *payload = NULL;
3340
3341
0
  rv = session->callbacks.unpack_extension_callback(
3342
0
    session, &payload, &frame->hd, session->user_data);
3343
0
  if (rv == NGHTTP2_ERR_CANCEL) {
3344
0
    return rv;
3345
0
  }
3346
0
  if (rv != 0) {
3347
0
    return NGHTTP2_ERR_CALLBACK_FAILURE;
3348
0
  }
3349
3350
0
  frame->ext.payload = payload;
3351
3352
0
  return 0;
3353
0
}
3354
3355
/*
3356
 * Handles frame size error.
3357
 *
3358
 * This function returns 0 if it succeeds, or one of the following
3359
 * negative error codes:
3360
 *
3361
 * NGHTTP2_ERR_NOMEM
3362
 *   Out of memory.
3363
 */
3364
172
static int session_handle_frame_size_error(nghttp2_session *session) {
3365
  /* TODO Currently no callback is called for this error, because we
3366
     call this callback before reading any payload */
3367
172
  return nghttp2_session_terminate_session(session, NGHTTP2_FRAME_SIZE_ERROR);
3368
172
}
3369
3370
30.8k
static uint32_t get_error_code_from_lib_error_code(int lib_error_code) {
3371
30.8k
  switch (lib_error_code) {
3372
11
  case NGHTTP2_ERR_STREAM_CLOSED:
3373
11
    return NGHTTP2_STREAM_CLOSED;
3374
0
  case NGHTTP2_ERR_HEADER_COMP:
3375
0
    return NGHTTP2_COMPRESSION_ERROR;
3376
0
  case NGHTTP2_ERR_FRAME_SIZE_ERROR:
3377
0
    return NGHTTP2_FRAME_SIZE_ERROR;
3378
101
  case NGHTTP2_ERR_FLOW_CONTROL:
3379
101
    return NGHTTP2_FLOW_CONTROL_ERROR;
3380
460
  case NGHTTP2_ERR_REFUSED_STREAM:
3381
460
    return NGHTTP2_REFUSED_STREAM;
3382
1.06k
  case NGHTTP2_ERR_PROTO:
3383
8.85k
  case NGHTTP2_ERR_HTTP_HEADER:
3384
22.5k
  case NGHTTP2_ERR_HTTP_MESSAGING:
3385
22.5k
    return NGHTTP2_PROTOCOL_ERROR;
3386
7.79k
  case NGHTTP2_ERR_INTERNAL:
3387
7.79k
    return NGHTTP2_INTERNAL_ERROR;
3388
0
  case NGHTTP2_ERR_PUSH_CANCEL:
3389
0
    return NGHTTP2_CANCEL;
3390
0
  default:
3391
0
    return NGHTTP2_INTERNAL_ERROR;
3392
30.8k
  }
3393
30.8k
}
3394
3395
/*
3396
 * Calls on_invalid_frame_recv_callback if it is set to |session|.
3397
 *
3398
 * This function returns 0 if it succeeds, or one of the following
3399
 * negative error codes:
3400
 *
3401
 * NGHTTP2_ERR_CALLBACK_FAILURE
3402
 *   User defined callback function fails.
3403
 */
3404
static int session_call_on_invalid_frame_recv_callback(nghttp2_session *session,
3405
                                                       nghttp2_frame *frame,
3406
0
                                                       int lib_error_code) {
3407
0
  if (session->callbacks.on_invalid_frame_recv_callback) {
3408
0
    if (session->callbacks.on_invalid_frame_recv_callback(
3409
0
          session, frame, lib_error_code, session->user_data) != 0) {
3410
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
3411
0
    }
3412
0
  }
3413
0
  return 0;
3414
0
}
3415
3416
214k
static int session_update_glitch_ratelim(nghttp2_session *session) {
3417
214k
  if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
3418
0
    return 0;
3419
0
  }
3420
3421
214k
  nghttp2_ratelim_update(&session->glitch_ratelim, nghttp2_time_now_sec());
3422
3423
214k
  if (nghttp2_ratelim_drain(&session->glitch_ratelim, 1) == 0) {
3424
214k
    return 0;
3425
214k
  }
3426
3427
13
  return nghttp2_session_terminate_session(session, NGHTTP2_ENHANCE_YOUR_CALM);
3428
214k
}
3429
3430
static int session_handle_invalid_stream2(nghttp2_session *session,
3431
                                          int32_t stream_id,
3432
                                          nghttp2_frame *frame,
3433
29.7k
                                          int lib_error_code) {
3434
29.7k
  int rv;
3435
3436
29.7k
  rv = nghttp2_session_add_rst_stream(
3437
29.7k
    session, stream_id, get_error_code_from_lib_error_code(lib_error_code));
3438
29.7k
  if (rv != 0) {
3439
0
    return rv;
3440
0
  }
3441
29.7k
  if (frame && session->callbacks.on_invalid_frame_recv_callback) {
3442
0
    if (session->callbacks.on_invalid_frame_recv_callback(
3443
0
          session, frame, lib_error_code, session->user_data) != 0) {
3444
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
3445
0
    }
3446
0
  }
3447
29.7k
  return 0;
3448
29.7k
}
3449
3450
static int session_handle_invalid_stream(nghttp2_session *session,
3451
                                         nghttp2_frame *frame,
3452
460
                                         int lib_error_code) {
3453
460
  return session_handle_invalid_stream2(session, frame->hd.stream_id, frame,
3454
460
                                        lib_error_code);
3455
460
}
3456
3457
static int session_inflate_handle_invalid_stream(nghttp2_session *session,
3458
                                                 nghttp2_frame *frame,
3459
460
                                                 int lib_error_code) {
3460
460
  int rv;
3461
460
  rv = session_handle_invalid_stream(session, frame, lib_error_code);
3462
460
  if (nghttp2_is_fatal(rv)) {
3463
0
    return rv;
3464
0
  }
3465
460
  return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3466
460
}
3467
3468
/*
3469
 * Handles invalid frame which causes connection error.
3470
 */
3471
static int session_handle_invalid_connection(nghttp2_session *session,
3472
                                             nghttp2_frame *frame,
3473
                                             int lib_error_code,
3474
1.13k
                                             const char *reason) {
3475
1.13k
  if (session->callbacks.on_invalid_frame_recv_callback) {
3476
0
    if (session->callbacks.on_invalid_frame_recv_callback(
3477
0
          session, frame, lib_error_code, session->user_data) != 0) {
3478
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
3479
0
    }
3480
0
  }
3481
1.13k
  return nghttp2_session_terminate_session_with_reason(
3482
1.13k
    session, get_error_code_from_lib_error_code(lib_error_code), reason);
3483
1.13k
}
3484
3485
static int session_inflate_handle_invalid_connection(nghttp2_session *session,
3486
                                                     nghttp2_frame *frame,
3487
                                                     int lib_error_code,
3488
100
                                                     const char *reason) {
3489
100
  int rv;
3490
100
  rv =
3491
100
    session_handle_invalid_connection(session, frame, lib_error_code, reason);
3492
100
  if (nghttp2_is_fatal(rv)) {
3493
0
    return rv;
3494
0
  }
3495
100
  return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3496
100
}
3497
3498
/*
3499
 * Inflates header block in the memory pointed by |in| with |inlen|
3500
 * bytes. If this function returns NGHTTP2_ERR_PAUSE, the caller must
3501
 * call this function again, until it returns 0 or one of negative
3502
 * error code.  If |call_header_cb| is zero, the on_header_callback
3503
 * are not invoked and the function never return NGHTTP2_ERR_PAUSE. If
3504
 * the given |in| is the last chunk of header block, the |final| must
3505
 * be nonzero. If header block is successfully processed (which is
3506
 * indicated by the return value 0, NGHTTP2_ERR_PAUSE or
3507
 * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE), the number of processed
3508
 * input bytes is assigned to the |*readlen_ptr|.
3509
 *
3510
 * This function return 0 if it succeeds, or one of the negative error
3511
 * codes:
3512
 *
3513
 * NGHTTP2_ERR_CALLBACK_FAILURE
3514
 *     The callback function failed.
3515
 * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
3516
 *     The callback returns this error code, indicating that this
3517
 *     stream should be RST_STREAMed.
3518
 * NGHTTP2_ERR_NOMEM
3519
 *     Out of memory.
3520
 * NGHTTP2_ERR_PAUSE
3521
 *     The callback function returned NGHTTP2_ERR_PAUSE
3522
 * NGHTTP2_ERR_HEADER_COMP
3523
 *     Header decompression failed
3524
 */
3525
static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
3526
                                size_t *readlen_ptr, uint8_t *in, size_t inlen,
3527
152k
                                int final, int call_header_cb) {
3528
152k
  nghttp2_inbound_frame *iframe = &session->iframe;
3529
152k
  nghttp2_ssize proclen;
3530
152k
  int rv;
3531
152k
  int inflate_flags;
3532
152k
  nghttp2_hd_nv nv;
3533
152k
  nghttp2_stream *stream;
3534
152k
  nghttp2_stream *subject_stream;
3535
152k
  int trailer = 0;
3536
3537
152k
  *readlen_ptr = 0;
3538
152k
  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
3539
3540
152k
  if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
3541
0
    subject_stream = nghttp2_session_get_stream(
3542
0
      session, frame->push_promise.promised_stream_id);
3543
152k
  } else {
3544
152k
    subject_stream = stream;
3545
152k
    trailer = session_trailer_headers(session, stream, frame);
3546
152k
  }
3547
3548
152k
  DEBUGF("recv: decoding header block %zu bytes\n", inlen);
3549
373k
  for (;;) {
3550
373k
    inflate_flags = 0;
3551
373k
    proclen = nghttp2_hd_inflate_hd_nv(&session->hd_inflater, &nv,
3552
373k
                                       &inflate_flags, in, inlen, final);
3553
373k
    if (nghttp2_is_fatal((int)proclen)) {
3554
0
      return (int)proclen;
3555
0
    }
3556
373k
    if (proclen < 0) {
3557
2.26k
      if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) {
3558
2.14k
        if (subject_stream && subject_stream->state != NGHTTP2_STREAM_CLOSING) {
3559
          /* Adding RST_STREAM here is very important. It prevents
3560
             from invoking subsequent callbacks for the same stream
3561
             ID. */
3562
2.14k
          rv = nghttp2_session_add_rst_stream(
3563
2.14k
            session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR);
3564
3565
2.14k
          if (nghttp2_is_fatal(rv)) {
3566
0
            return rv;
3567
0
          }
3568
2.14k
        }
3569
2.14k
      }
3570
2.26k
      rv =
3571
2.26k
        nghttp2_session_terminate_session(session, NGHTTP2_COMPRESSION_ERROR);
3572
2.26k
      if (nghttp2_is_fatal(rv)) {
3573
0
        return rv;
3574
0
      }
3575
3576
2.26k
      return NGHTTP2_ERR_HEADER_COMP;
3577
2.26k
    }
3578
371k
    in += proclen;
3579
371k
    inlen -= (size_t)proclen;
3580
371k
    *readlen_ptr += (size_t)proclen;
3581
3582
371k
    DEBUGF("recv: proclen=%td\n", proclen);
3583
3584
371k
    if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) {
3585
88.0k
      rv = 0;
3586
88.0k
      if (subject_stream) {
3587
88.0k
        if (session_enforce_http_messaging(session)) {
3588
88.0k
          rv = nghttp2_http_on_header(session, subject_stream, frame, &nv,
3589
88.0k
                                      trailer);
3590
3591
88.0k
          if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) {
3592
            /* Don't overwrite rv here */
3593
20.3k
            int rv2;
3594
3595
20.3k
            rv2 = session_call_on_invalid_header(session, frame, &nv);
3596
20.3k
            if (rv2 == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
3597
0
              DEBUGF("recv: HTTP error: type=%u, id=%d, header %.*s: %.*s\n",
3598
0
                     frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
3599
0
                     nv.name->base, (int)nv.value->len, nv.value->base);
3600
3601
0
              rv = session_call_error_callback(
3602
0
                session, NGHTTP2_ERR_HTTP_HEADER,
3603
0
                "Invalid HTTP header field was received: frame type: "
3604
0
                "%u, stream: %d, name: [%.*s], value: [%.*s]",
3605
0
                frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
3606
0
                nv.name->base, (int)nv.value->len, nv.value->base);
3607
3608
0
              if (nghttp2_is_fatal(rv)) {
3609
0
                return rv;
3610
0
              }
3611
3612
0
              rv = session_handle_invalid_stream2(
3613
0
                session, subject_stream->stream_id, frame,
3614
0
                NGHTTP2_ERR_HTTP_HEADER);
3615
0
              if (nghttp2_is_fatal(rv)) {
3616
0
                return rv;
3617
0
              }
3618
3619
0
              return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
3620
20.3k
            } else {
3621
20.3k
              if (rv2 != 0) {
3622
0
                return rv2;
3623
0
              }
3624
3625
              /* header is ignored */
3626
20.3k
              DEBUGF("recv: HTTP ignored: type=%u, id=%d, header %.*s: %.*s\n",
3627
20.3k
                     frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
3628
20.3k
                     nv.name->base, (int)nv.value->len, nv.value->base);
3629
3630
20.3k
              rv2 = session_call_error_callback(
3631
20.3k
                session, NGHTTP2_ERR_HTTP_HEADER,
3632
20.3k
                "Ignoring received invalid HTTP header field: frame type: "
3633
20.3k
                "%u, stream: %d, name: [%.*s], value: [%.*s]",
3634
20.3k
                frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
3635
20.3k
                nv.name->base, (int)nv.value->len, nv.value->base);
3636
3637
20.3k
              if (nghttp2_is_fatal(rv2)) {
3638
0
                return rv2;
3639
0
              }
3640
20.3k
            }
3641
20.3k
          }
3642
3643
88.0k
          if (rv == NGHTTP2_ERR_HTTP_HEADER) {
3644
7.79k
            DEBUGF("recv: HTTP error: type=%u, id=%d, header %.*s: %.*s\n",
3645
7.79k
                   frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
3646
7.79k
                   nv.name->base, (int)nv.value->len, nv.value->base);
3647
3648
7.79k
            rv = session_call_error_callback(
3649
7.79k
              session, NGHTTP2_ERR_HTTP_HEADER,
3650
7.79k
              "Invalid HTTP header field was received: frame type: "
3651
7.79k
              "%u, stream: %d, name: [%.*s], value: [%.*s]",
3652
7.79k
              frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
3653
7.79k
              nv.name->base, (int)nv.value->len, nv.value->base);
3654
3655
7.79k
            if (nghttp2_is_fatal(rv)) {
3656
0
              return rv;
3657
0
            }
3658
3659
7.79k
            rv =
3660
7.79k
              session_handle_invalid_stream2(session, subject_stream->stream_id,
3661
7.79k
                                             frame, NGHTTP2_ERR_HTTP_HEADER);
3662
7.79k
            if (nghttp2_is_fatal(rv)) {
3663
0
              return rv;
3664
0
            }
3665
3666
7.79k
            rv = session_update_glitch_ratelim(session);
3667
7.79k
            if (rv != 0) {
3668
0
              return rv;
3669
0
            }
3670
3671
7.79k
            if (iframe->state == NGHTTP2_IB_IGN_ALL) {
3672
1
              return 0;
3673
1
            }
3674
3675
7.79k
            return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
3676
7.79k
          }
3677
88.0k
        }
3678
80.2k
        if (rv == 0) {
3679
59.9k
          rv = session_call_on_header(session, frame, &nv);
3680
          /* This handles NGHTTP2_ERR_PAUSE and
3681
             NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE as well */
3682
59.9k
          if (rv != 0) {
3683
0
            return rv;
3684
0
          }
3685
59.9k
        }
3686
80.2k
      }
3687
88.0k
    }
3688
363k
    if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
3689
132k
      nghttp2_hd_inflate_end_headers(&session->hd_inflater);
3690
132k
      break;
3691
132k
    }
3692
231k
    if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) {
3693
9.65k
      break;
3694
9.65k
    }
3695
231k
  }
3696
142k
  return 0;
3697
152k
}
3698
3699
/*
3700
 * Call this function when HEADERS frame was completely received.
3701
 *
3702
 * This function returns 0 if it succeeds, or one of negative error
3703
 * codes:
3704
 *
3705
 * NGHTTP2_ERR_CALLBACK_FAILURE
3706
 *     The callback function failed.
3707
 * NGHTTP2_ERR_NOMEM
3708
 *     Out of memory.
3709
 */
3710
static int session_end_stream_headers_received(nghttp2_session *session,
3711
                                               nghttp2_frame *frame,
3712
1.91k
                                               nghttp2_stream *stream) {
3713
1.91k
  int rv;
3714
3715
1.91k
  assert(frame->hd.type == NGHTTP2_HEADERS);
3716
3717
1.91k
  if (session->server && session_enforce_http_messaging(session) &&
3718
1.91k
      frame->headers.cat == NGHTTP2_HCAT_REQUEST &&
3719
1.87k
      !(stream->flags & NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES) &&
3720
1.87k
      (stream->http_flags & NGHTTP2_HTTP_FLAG_PRIORITY)) {
3721
105
    rv = session_update_stream_priority(session, stream, stream->http_extpri);
3722
105
    if (rv != 0) {
3723
0
      assert(nghttp2_is_fatal(rv));
3724
0
      return rv;
3725
0
    }
3726
105
  }
3727
3728
1.91k
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
3729
1.51k
    return 0;
3730
1.51k
  }
3731
3732
401
  nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
3733
401
  rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
3734
401
  if (nghttp2_is_fatal(rv)) {
3735
0
    return rv;
3736
0
  }
3737
3738
401
  return 0;
3739
401
}
3740
3741
15.5k
static int session_after_header_block_received(nghttp2_session *session) {
3742
15.5k
  int rv = 0;
3743
15.5k
  nghttp2_frame *frame = &session->iframe.frame;
3744
15.5k
  nghttp2_inbound_frame *iframe = &session->iframe;
3745
15.5k
  nghttp2_stream *stream;
3746
3747
  /* We don't call on_frame_recv_callback if stream has been closed
3748
     already or being closed. */
3749
15.5k
  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
3750
15.5k
  if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) {
3751
0
    return 0;
3752
0
  }
3753
3754
15.5k
  if (session_enforce_http_messaging(session)) {
3755
15.5k
    if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
3756
0
      nghttp2_stream *subject_stream;
3757
3758
0
      subject_stream = nghttp2_session_get_stream(
3759
0
        session, frame->push_promise.promised_stream_id);
3760
0
      if (subject_stream) {
3761
0
        rv = nghttp2_http_on_request_headers(subject_stream, frame);
3762
0
      }
3763
15.5k
    } else {
3764
15.5k
      assert(frame->hd.type == NGHTTP2_HEADERS);
3765
15.5k
      switch (frame->headers.cat) {
3766
15.3k
      case NGHTTP2_HCAT_REQUEST:
3767
15.3k
        rv = nghttp2_http_on_request_headers(stream, frame);
3768
15.3k
        break;
3769
0
      case NGHTTP2_HCAT_RESPONSE:
3770
0
      case NGHTTP2_HCAT_PUSH_RESPONSE:
3771
0
        rv = nghttp2_http_on_response_headers(stream);
3772
0
        break;
3773
125
      case NGHTTP2_HCAT_HEADERS:
3774
125
        if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
3775
0
          assert(!session->server);
3776
0
          rv = nghttp2_http_on_response_headers(stream);
3777
125
        } else {
3778
125
          rv = nghttp2_http_on_trailer_headers(stream, frame);
3779
125
        }
3780
125
        break;
3781
125
      default:
3782
0
        assert(0);
3783
15.5k
      }
3784
15.5k
      if (rv == 0 && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
3785
622
        rv = nghttp2_http_on_remote_end_stream(stream);
3786
622
      }
3787
15.5k
    }
3788
15.5k
    if (rv != 0) {
3789
13.6k
      int32_t stream_id;
3790
3791
13.6k
      if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
3792
0
        stream_id = frame->push_promise.promised_stream_id;
3793
13.6k
      } else {
3794
13.6k
        stream_id = frame->hd.stream_id;
3795
13.6k
      }
3796
3797
13.6k
      rv = session_handle_invalid_stream2(session, stream_id, frame,
3798
13.6k
                                          NGHTTP2_ERR_HTTP_MESSAGING);
3799
13.6k
      if (nghttp2_is_fatal(rv)) {
3800
0
        return rv;
3801
0
      }
3802
3803
13.6k
      rv = session_update_glitch_ratelim(session);
3804
13.6k
      if (rv != 0) {
3805
0
        return rv;
3806
0
      }
3807
3808
13.6k
      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
3809
1
        return 0;
3810
1
      }
3811
3812
13.6k
      if (frame->hd.type == NGHTTP2_HEADERS &&
3813
13.6k
          (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
3814
5.17k
        nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
3815
        /* Don't call nghttp2_session_close_stream_if_shut_rdwr
3816
           because RST_STREAM has been submitted. */
3817
5.17k
      }
3818
3819
13.6k
      return 0;
3820
13.6k
    }
3821
15.5k
  }
3822
3823
1.91k
  rv = session_call_on_frame_received(session, frame);
3824
1.91k
  if (nghttp2_is_fatal(rv)) {
3825
0
    return rv;
3826
0
  }
3827
3828
1.91k
  if (frame->hd.type != NGHTTP2_HEADERS) {
3829
0
    return 0;
3830
0
  }
3831
3832
1.91k
  return session_end_stream_headers_received(session, frame, stream);
3833
1.91k
}
3834
3835
int nghttp2_session_on_request_headers_received(nghttp2_session *session,
3836
104k
                                                nghttp2_frame *frame) {
3837
104k
  int rv = 0;
3838
104k
  nghttp2_stream *stream;
3839
104k
  if (frame->hd.stream_id == 0) {
3840
6
    return session_inflate_handle_invalid_connection(
3841
6
      session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0");
3842
6
  }
3843
3844
  /* If client receives idle stream from server, it is invalid
3845
     regardless stream ID is even or odd.  This is because client is
3846
     not expected to receive request from server. */
3847
104k
  if (!session->server) {
3848
0
    if (session_detect_idle_stream(session, frame->hd.stream_id)) {
3849
0
      return session_inflate_handle_invalid_connection(
3850
0
        session, frame, NGHTTP2_ERR_PROTO,
3851
0
        "request HEADERS: client received request");
3852
0
    }
3853
3854
0
    return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3855
0
  }
3856
3857
104k
  assert(session->server);
3858
3859
104k
  if (!session_is_new_peer_stream_id(session, frame->hd.stream_id)) {
3860
72.5k
    if (frame->hd.stream_id == 0 ||
3861
72.5k
        nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
3862
52
      return session_inflate_handle_invalid_connection(
3863
52
        session, frame, NGHTTP2_ERR_PROTO,
3864
52
        "request HEADERS: invalid stream_id");
3865
52
    }
3866
3867
    /* RFC 7540 says if an endpoint receives a HEADERS with invalid
3868
     * stream ID (e.g, numerically smaller than previous), it MUST
3869
     * issue connection error with error code PROTOCOL_ERROR.  It is a
3870
     * bit hard to detect this, since we cannot remember all streams
3871
     * we observed so far.
3872
     *
3873
     * You might imagine this is really easy.  But no.  HTTP/2 is
3874
     * asynchronous protocol, and usually client and server do not
3875
     * share the complete picture of open/closed stream status.  For
3876
     * example, after server sends RST_STREAM for a stream, client may
3877
     * send trailer HEADERS for that stream.  If naive server detects
3878
     * that, and issued connection error, then it is a bug of server
3879
     * implementation since client is not wrong if it did not get
3880
     * RST_STREAM when it issued trailer HEADERS.
3881
     *
3882
     * At the moment, we are very conservative here.  We only use
3883
     * connection error if stream ID refers idle stream, or we are
3884
     * sure that stream is half-closed(remote) or closed.  Otherwise
3885
     * we just ignore HEADERS for now.
3886
     */
3887
72.5k
    stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);
3888
72.5k
    if (stream && (stream->shut_flags & NGHTTP2_SHUT_RD)) {
3889
0
      return session_inflate_handle_invalid_connection(
3890
0
        session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed");
3891
0
    }
3892
3893
72.5k
    return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3894
72.5k
  }
3895
32.2k
  session->last_recv_stream_id = frame->hd.stream_id;
3896
3897
32.2k
  if (session_is_incoming_concurrent_streams_max(session)) {
3898
1
    return session_inflate_handle_invalid_connection(
3899
1
      session, frame, NGHTTP2_ERR_PROTO,
3900
1
      "request HEADERS: max concurrent streams exceeded");
3901
1
  }
3902
3903
32.2k
  if (!session_allow_incoming_new_stream(session)) {
3904
    /* We just ignore stream after GOAWAY was sent */
3905
0
    return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3906
0
  }
3907
3908
32.2k
  if (frame->headers.pri_spec.stream_id == frame->hd.stream_id) {
3909
6
    return session_inflate_handle_invalid_connection(
3910
6
      session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: depend on itself");
3911
6
  }
3912
3913
32.2k
  if (session_is_incoming_concurrent_streams_pending_max(session)) {
3914
460
    return session_inflate_handle_invalid_stream(session, frame,
3915
460
                                                 NGHTTP2_ERR_REFUSED_STREAM);
3916
460
  }
3917
3918
31.7k
  stream = nghttp2_session_open_stream(session, frame->hd.stream_id,
3919
31.7k
                                       NGHTTP2_STREAM_FLAG_NONE,
3920
31.7k
                                       NGHTTP2_STREAM_OPENING, NULL);
3921
31.7k
  if (!stream) {
3922
0
    return NGHTTP2_ERR_NOMEM;
3923
0
  }
3924
3925
31.7k
  session->last_proc_stream_id = session->last_recv_stream_id;
3926
3927
31.7k
  rv = session_call_on_begin_headers(session, frame);
3928
31.7k
  if (rv != 0) {
3929
0
    return rv;
3930
0
  }
3931
31.7k
  return 0;
3932
31.7k
}
3933
3934
int nghttp2_session_on_response_headers_received(nghttp2_session *session,
3935
                                                 nghttp2_frame *frame,
3936
0
                                                 nghttp2_stream *stream) {
3937
0
  int rv;
3938
  /* This function is only called if stream->state ==
3939
     NGHTTP2_STREAM_OPENING and stream_id is local side initiated. */
3940
0
  assert(stream->state == NGHTTP2_STREAM_OPENING &&
3941
0
         nghttp2_session_is_my_stream_id(session, frame->hd.stream_id));
3942
0
  if (frame->hd.stream_id == 0) {
3943
0
    return session_inflate_handle_invalid_connection(
3944
0
      session, frame, NGHTTP2_ERR_PROTO, "response HEADERS: stream_id == 0");
3945
0
  }
3946
0
  if (stream->shut_flags & NGHTTP2_SHUT_RD) {
3947
    /* half closed (remote): from the spec:
3948
3949
       If an endpoint receives additional frames for a stream that is
3950
       in this state it MUST respond with a stream error (Section
3951
       5.4.2) of type STREAM_CLOSED.
3952
3953
       We go further, and make it connection error.
3954
    */
3955
0
    return session_inflate_handle_invalid_connection(
3956
0
      session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed");
3957
0
  }
3958
0
  stream->state = NGHTTP2_STREAM_OPENED;
3959
0
  rv = session_call_on_begin_headers(session, frame);
3960
0
  if (rv != 0) {
3961
0
    return rv;
3962
0
  }
3963
0
  return 0;
3964
0
}
3965
3966
int nghttp2_session_on_push_response_headers_received(nghttp2_session *session,
3967
                                                      nghttp2_frame *frame,
3968
0
                                                      nghttp2_stream *stream) {
3969
0
  int rv = 0;
3970
0
  assert(stream->state == NGHTTP2_STREAM_RESERVED);
3971
0
  if (frame->hd.stream_id == 0) {
3972
0
    return session_inflate_handle_invalid_connection(
3973
0
      session, frame, NGHTTP2_ERR_PROTO,
3974
0
      "push response HEADERS: stream_id == 0");
3975
0
  }
3976
3977
0
  if (session->server) {
3978
0
    return session_inflate_handle_invalid_connection(
3979
0
      session, frame, NGHTTP2_ERR_PROTO,
3980
0
      "HEADERS: no HEADERS allowed from client in reserved state");
3981
0
  }
3982
3983
0
  if (session_is_incoming_concurrent_streams_max(session)) {
3984
0
    return session_inflate_handle_invalid_connection(
3985
0
      session, frame, NGHTTP2_ERR_PROTO,
3986
0
      "push response HEADERS: max concurrent streams exceeded");
3987
0
  }
3988
3989
0
  if (!session_allow_incoming_new_stream(session)) {
3990
    /* We don't accept new stream after GOAWAY was sent. */
3991
0
    return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3992
0
  }
3993
3994
0
  if (session_is_incoming_concurrent_streams_pending_max(session)) {
3995
0
    return session_inflate_handle_invalid_stream(session, frame,
3996
0
                                                 NGHTTP2_ERR_REFUSED_STREAM);
3997
0
  }
3998
3999
0
  nghttp2_stream_promise_fulfilled(stream);
4000
0
  if (!nghttp2_session_is_my_stream_id(session, stream->stream_id)) {
4001
0
    --session->num_incoming_reserved_streams;
4002
0
  }
4003
0
  ++session->num_incoming_streams;
4004
0
  rv = session_call_on_begin_headers(session, frame);
4005
0
  if (rv != 0) {
4006
0
    return rv;
4007
0
  }
4008
0
  return 0;
4009
0
}
4010
4011
int nghttp2_session_on_headers_received(nghttp2_session *session,
4012
                                        nghttp2_frame *frame,
4013
38.6k
                                        nghttp2_stream *stream) {
4014
38.6k
  int rv = 0;
4015
38.6k
  if (frame->hd.stream_id == 0) {
4016
0
    return session_inflate_handle_invalid_connection(
4017
0
      session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream_id == 0");
4018
0
  }
4019
38.6k
  if ((stream->shut_flags & NGHTTP2_SHUT_RD)) {
4020
    /* half closed (remote): from the spec:
4021
4022
       If an endpoint receives additional frames for a stream that is
4023
       in this state it MUST respond with a stream error (Section
4024
       5.4.2) of type STREAM_CLOSED.
4025
4026
       we go further, and make it connection error.
4027
    */
4028
11
    return session_inflate_handle_invalid_connection(
4029
11
      session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed");
4030
11
  }
4031
38.6k
  if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
4032
0
    if (stream->state == NGHTTP2_STREAM_OPENED) {
4033
0
      rv = session_call_on_begin_headers(session, frame);
4034
0
      if (rv != 0) {
4035
0
        return rv;
4036
0
      }
4037
0
      return 0;
4038
0
    }
4039
4040
0
    return NGHTTP2_ERR_IGN_HEADER_BLOCK;
4041
0
  }
4042
  /* If this is remote peer initiated stream, it is OK unless it
4043
     has sent END_STREAM frame already. But if stream is in
4044
     NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race
4045
     condition. */
4046
38.6k
  if (stream->state != NGHTTP2_STREAM_CLOSING) {
4047
355
    rv = session_call_on_begin_headers(session, frame);
4048
355
    if (rv != 0) {
4049
0
      return rv;
4050
0
    }
4051
355
    return 0;
4052
355
  }
4053
38.3k
  return NGHTTP2_ERR_IGN_HEADER_BLOCK;
4054
38.6k
}
4055
4056
143k
static int session_process_headers_frame(nghttp2_session *session) {
4057
143k
  nghttp2_inbound_frame *iframe = &session->iframe;
4058
143k
  nghttp2_frame *frame = &iframe->frame;
4059
143k
  nghttp2_stream *stream;
4060
4061
143k
  nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos);
4062
4063
143k
  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
4064
143k
  if (!stream) {
4065
104k
    frame->headers.cat = NGHTTP2_HCAT_REQUEST;
4066
104k
    return nghttp2_session_on_request_headers_received(session, frame);
4067
104k
  }
4068
4069
38.6k
  if (stream->state == NGHTTP2_STREAM_RESERVED) {
4070
0
    frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE;
4071
0
    return nghttp2_session_on_push_response_headers_received(session, frame,
4072
0
                                                             stream);
4073
0
  }
4074
4075
38.6k
  if (stream->state == NGHTTP2_STREAM_OPENING &&
4076
363
      nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
4077
0
    frame->headers.cat = NGHTTP2_HCAT_RESPONSE;
4078
0
    return nghttp2_session_on_response_headers_received(session, frame, stream);
4079
0
  }
4080
4081
38.6k
  frame->headers.cat = NGHTTP2_HCAT_HEADERS;
4082
38.6k
  return nghttp2_session_on_headers_received(session, frame, stream);
4083
38.6k
}
4084
4085
66.1k
static int session_update_stream_reset_ratelim(nghttp2_session *session) {
4086
66.1k
  if (!session->server || (session->goaway_flags & NGHTTP2_GOAWAY_SUBMITTED)) {
4087
11.2k
    return 0;
4088
11.2k
  }
4089
4090
54.9k
  nghttp2_ratelim_update(&session->stream_reset_ratelim,
4091
54.9k
                         nghttp2_time_now_sec());
4092
4093
54.9k
  if (nghttp2_ratelim_drain(&session->stream_reset_ratelim, 1) == 0) {
4094
54.8k
    return 0;
4095
54.8k
  }
4096
4097
49
  return nghttp2_session_add_goaway(session, session->last_recv_stream_id,
4098
49
                                    NGHTTP2_INTERNAL_ERROR, NULL, 0,
4099
49
                                    NGHTTP2_GOAWAY_AUX_NONE);
4100
54.9k
}
4101
4102
int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
4103
66.2k
                                           nghttp2_frame *frame) {
4104
66.2k
  int rv;
4105
66.2k
  nghttp2_stream *stream;
4106
66.2k
  if (frame->hd.stream_id == 0) {
4107
4
    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4108
4
                                             "RST_STREAM: stream_id == 0");
4109
4
  }
4110
4111
66.2k
  if (session_detect_idle_stream(session, frame->hd.stream_id)) {
4112
112
    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4113
112
                                             "RST_STREAM: stream in idle");
4114
112
  }
4115
4116
66.1k
  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
4117
66.1k
  if (stream) {
4118
    /* We may use stream->shut_flags for strict error checking. */
4119
55
    nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
4120
55
  }
4121
4122
66.1k
  rv = session_call_on_frame_received(session, frame);
4123
66.1k
  if (rv != 0) {
4124
0
    return rv;
4125
0
  }
4126
66.1k
  rv = nghttp2_session_close_stream(session, frame->hd.stream_id,
4127
66.1k
                                    frame->rst_stream.error_code);
4128
66.1k
  if (nghttp2_is_fatal(rv)) {
4129
0
    return rv;
4130
0
  }
4131
4132
66.1k
  return session_update_stream_reset_ratelim(session);
4133
66.1k
}
4134
4135
66.2k
static int session_process_rst_stream_frame(nghttp2_session *session) {
4136
66.2k
  nghttp2_inbound_frame *iframe = &session->iframe;
4137
66.2k
  nghttp2_frame *frame = &iframe->frame;
4138
4139
66.2k
  nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, iframe->sbuf.pos);
4140
4141
66.2k
  return nghttp2_session_on_rst_stream_received(session, frame);
4142
66.2k
}
4143
4144
2.62k
static int update_remote_initial_window_size_func(void *entry, void *ptr) {
4145
2.62k
  int rv;
4146
2.62k
  nghttp2_update_window_size_arg *arg;
4147
2.62k
  nghttp2_stream *stream;
4148
4149
2.62k
  arg = (nghttp2_update_window_size_arg *)ptr;
4150
2.62k
  stream = (nghttp2_stream *)entry;
4151
4152
2.62k
  rv = nghttp2_stream_update_remote_initial_window_size(
4153
2.62k
    stream, arg->new_window_size, arg->old_window_size);
4154
2.62k
  if (rv != 0) {
4155
8
    return NGHTTP2_ERR_FLOW_CONTROL;
4156
8
  }
4157
4158
  /* If window size gets positive, push deferred DATA frame to
4159
     outbound queue. */
4160
2.61k
  if (stream->remote_window_size > 0 &&
4161
1.81k
      nghttp2_stream_check_deferred_by_flow_control(stream)) {
4162
0
    rv = session_resume_deferred_stream_item(
4163
0
      arg->session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
4164
4165
0
    if (nghttp2_is_fatal(rv)) {
4166
0
      return rv;
4167
0
    }
4168
0
  }
4169
2.61k
  return 0;
4170
2.61k
}
4171
4172
/*
4173
 * Updates the remote initial window size of all active streams.  If
4174
 * error occurs, all streams may not be updated.
4175
 *
4176
 * This function returns 0 if it succeeds, or one of the following
4177
 * negative error codes:
4178
 *
4179
 * NGHTTP2_ERR_NOMEM
4180
 *     Out of memory.
4181
 * NGHTTP2_ERR_FLOW_CONTROL
4182
 *     Window size gets out of range.
4183
 */
4184
static int
4185
session_update_remote_initial_window_size(nghttp2_session *session,
4186
1.49k
                                          int32_t new_initial_window_size) {
4187
1.49k
  return nghttp2_map_each(
4188
1.49k
    &session->streams, update_remote_initial_window_size_func,
4189
1.49k
    &(nghttp2_update_window_size_arg){
4190
1.49k
      .session = session,
4191
1.49k
      .new_window_size = new_initial_window_size,
4192
1.49k
      .old_window_size = (int32_t)session->remote_settings.initial_window_size,
4193
1.49k
    });
4194
1.49k
}
4195
4196
217
static int update_local_initial_window_size_func(void *entry, void *ptr) {
4197
217
  int rv;
4198
217
  nghttp2_update_window_size_arg *arg;
4199
217
  nghttp2_stream *stream;
4200
217
  arg = (nghttp2_update_window_size_arg *)ptr;
4201
217
  stream = (nghttp2_stream *)entry;
4202
217
  rv = nghttp2_stream_update_local_initial_window_size(
4203
217
    stream, arg->new_window_size, arg->old_window_size);
4204
217
  if (rv != 0) {
4205
0
    return NGHTTP2_ERR_FLOW_CONTROL;
4206
0
  }
4207
4208
217
  if (stream->window_update_queued) {
4209
3
    return 0;
4210
3
  }
4211
4212
214
  if (arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
4213
0
    return session_update_stream_consumed_size(arg->session, stream, 0);
4214
0
  }
4215
4216
214
  if (nghttp2_should_send_window_update(stream->local_window_size,
4217
214
                                        stream->recv_window_size)) {
4218
57
    rv = nghttp2_session_add_window_update(arg->session, NGHTTP2_FLAG_NONE,
4219
57
                                           stream->stream_id,
4220
57
                                           stream->recv_window_size);
4221
57
    if (rv != 0) {
4222
0
      return rv;
4223
0
    }
4224
4225
57
    stream->recv_window_size = 0;
4226
57
  }
4227
214
  return 0;
4228
214
}
4229
4230
/*
4231
 * Updates the local initial window size of all active streams.  If
4232
 * error occurs, all streams may not be updated.
4233
 *
4234
 * This function returns 0 if it succeeds, or one of the following
4235
 * negative error codes:
4236
 *
4237
 * NGHTTP2_ERR_NOMEM
4238
 *     Out of memory.
4239
 * NGHTTP2_ERR_FLOW_CONTROL
4240
 *     Window size gets out of range.
4241
 */
4242
static int
4243
session_update_local_initial_window_size(nghttp2_session *session,
4244
                                         int32_t new_initial_window_size,
4245
121
                                         int32_t old_initial_window_size) {
4246
121
  return nghttp2_map_each(&session->streams,
4247
121
                          update_local_initial_window_size_func,
4248
121
                          &(nghttp2_update_window_size_arg){
4249
121
                            .session = session,
4250
121
                            .new_window_size = new_initial_window_size,
4251
121
                            .old_window_size = old_initial_window_size,
4252
121
                          });
4253
121
}
4254
4255
/*
4256
 * Apply SETTINGS values |iv| having |niv| elements to the local
4257
 * settings.  We assumes that all values in |iv| is correct, since we
4258
 * validated them in nghttp2_session_add_settings() already.
4259
 *
4260
 * This function returns 0 if it succeeds, or one of the following
4261
 * negative error codes:
4262
 *
4263
 * NGHTTP2_ERR_HEADER_COMP
4264
 *     The header table size is out of range
4265
 * NGHTTP2_ERR_NOMEM
4266
 *     Out of memory
4267
 */
4268
int nghttp2_session_update_local_settings(nghttp2_session *session,
4269
                                          nghttp2_settings_entry *iv,
4270
315
                                          size_t niv) {
4271
315
  int rv;
4272
315
  size_t i;
4273
315
  int32_t new_initial_window_size = -1;
4274
315
  uint32_t header_table_size = 0;
4275
315
  uint32_t min_header_table_size = UINT32_MAX;
4276
315
  uint8_t header_table_size_seen = 0;
4277
  /* For NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, use the value last
4278
     seen.  For NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, use both minimum
4279
     value and last seen value. */
4280
1.15k
  for (i = 0; i < niv; ++i) {
4281
835
    switch (iv[i].settings_id) {
4282
225
    case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
4283
225
      header_table_size_seen = 1;
4284
225
      header_table_size = iv[i].value;
4285
225
      min_header_table_size =
4286
225
        nghttp2_min_uint32(min_header_table_size, iv[i].value);
4287
225
      break;
4288
141
    case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
4289
141
      new_initial_window_size = (int32_t)iv[i].value;
4290
141
      break;
4291
835
    }
4292
835
  }
4293
315
  if (header_table_size_seen) {
4294
113
    if (min_header_table_size < header_table_size) {
4295
31
      rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater,
4296
31
                                                min_header_table_size);
4297
31
      if (rv != 0) {
4298
0
        return rv;
4299
0
      }
4300
31
    }
4301
4302
113
    rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater,
4303
113
                                              header_table_size);
4304
113
    if (rv != 0) {
4305
0
      return rv;
4306
0
    }
4307
113
  }
4308
315
  if (new_initial_window_size != -1) {
4309
121
    rv = session_update_local_initial_window_size(
4310
121
      session, new_initial_window_size,
4311
121
      (int32_t)session->local_settings.initial_window_size);
4312
121
    if (rv != 0) {
4313
0
      return rv;
4314
0
    }
4315
121
  }
4316
4317
1.15k
  for (i = 0; i < niv; ++i) {
4318
835
    switch (iv[i].settings_id) {
4319
225
    case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
4320
225
      session->local_settings.header_table_size = iv[i].value;
4321
225
      break;
4322
23
    case NGHTTP2_SETTINGS_ENABLE_PUSH:
4323
23
      session->local_settings.enable_push = iv[i].value;
4324
23
      break;
4325
44
    case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
4326
44
      session->local_settings.max_concurrent_streams = iv[i].value;
4327
44
      break;
4328
141
    case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
4329
141
      session->local_settings.initial_window_size = iv[i].value;
4330
141
      break;
4331
0
    case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
4332
0
      session->local_settings.max_frame_size = iv[i].value;
4333
0
      break;
4334
20
    case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
4335
20
      session->local_settings.max_header_list_size = iv[i].value;
4336
20
      break;
4337
18
    case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
4338
18
      session->local_settings.enable_connect_protocol = iv[i].value;
4339
18
      break;
4340
20
    case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
4341
20
      session->local_settings.no_rfc7540_priorities = iv[i].value;
4342
20
      break;
4343
835
    }
4344
835
  }
4345
4346
315
  return 0;
4347
315
}
4348
4349
int nghttp2_session_on_settings_received(nghttp2_session *session,
4350
35.0k
                                         nghttp2_frame *frame, int noack) {
4351
35.0k
  int rv;
4352
35.0k
  size_t i;
4353
35.0k
  nghttp2_mem *mem;
4354
35.0k
  nghttp2_inflight_settings *settings;
4355
4356
35.0k
  mem = &session->mem;
4357
4358
35.0k
  if (frame->hd.stream_id != 0) {
4359
133
    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4360
133
                                             "SETTINGS: stream_id != 0");
4361
133
  }
4362
34.8k
  if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
4363
319
    if (frame->settings.niv != 0) {
4364
0
      return session_handle_invalid_connection(
4365
0
        session, frame, NGHTTP2_ERR_FRAME_SIZE_ERROR,
4366
0
        "SETTINGS: ACK and payload != 0");
4367
0
    }
4368
4369
319
    settings = session->inflight_settings_head;
4370
4371
319
    if (!settings) {
4372
4
      return session_handle_invalid_connection(
4373
4
        session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: unexpected ACK");
4374
4
    }
4375
4376
315
    rv = nghttp2_session_update_local_settings(session, settings->iv,
4377
315
                                               settings->niv);
4378
4379
315
    session->inflight_settings_head = settings->next;
4380
4381
315
    inflight_settings_del(settings, mem);
4382
4383
315
    if (rv != 0) {
4384
0
      if (nghttp2_is_fatal(rv)) {
4385
0
        return rv;
4386
0
      }
4387
0
      return session_handle_invalid_connection(session, frame, rv, NULL);
4388
0
    }
4389
315
    return session_call_on_frame_received(session, frame);
4390
315
  }
4391
4392
34.5k
  if (!session->remote_settings_received) {
4393
17.7k
    session->remote_settings.max_concurrent_streams =
4394
17.7k
      NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;
4395
17.7k
    session->remote_settings_received = 1;
4396
17.7k
  }
4397
4398
54.1k
  for (i = 0; i < frame->settings.niv; ++i) {
4399
20.0k
    nghttp2_settings_entry *entry = &frame->settings.iv[i];
4400
4401
20.0k
    switch (entry->settings_id) {
4402
3.52k
    case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
4403
4404
3.52k
      rv = nghttp2_hd_deflate_change_table_size(&session->hd_deflater,
4405
3.52k
                                                entry->value);
4406
3.52k
      if (rv != 0) {
4407
0
        if (nghttp2_is_fatal(rv)) {
4408
0
          return rv;
4409
0
        } else {
4410
0
          return session_handle_invalid_connection(
4411
0
            session, frame, NGHTTP2_ERR_HEADER_COMP, NULL);
4412
0
        }
4413
0
      }
4414
4415
3.52k
      session->remote_settings.header_table_size = entry->value;
4416
4417
3.52k
      break;
4418
1.15k
    case NGHTTP2_SETTINGS_ENABLE_PUSH:
4419
4420
1.15k
      if (entry->value != 0 && entry->value != 1) {
4421
118
        return session_handle_invalid_connection(
4422
118
          session, frame, NGHTTP2_ERR_PROTO,
4423
118
          "SETTINGS: invalid SETTINGS_ENBLE_PUSH");
4424
118
      }
4425
4426
1.03k
      if (!session->server && entry->value != 0) {
4427
0
        return session_handle_invalid_connection(
4428
0
          session, frame, NGHTTP2_ERR_PROTO,
4429
0
          "SETTINGS: server attempted to enable push");
4430
0
      }
4431
4432
1.03k
      session->remote_settings.enable_push = entry->value;
4433
4434
1.03k
      break;
4435
386
    case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
4436
4437
386
      session->remote_settings.max_concurrent_streams = entry->value;
4438
4439
386
      break;
4440
1.57k
    case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
4441
4442
      /* Update the initial window size of the all active streams */
4443
      /* Check that initial_window_size < (1u << 31) */
4444
1.57k
      if (entry->value > NGHTTP2_MAX_WINDOW_SIZE) {
4445
76
        return session_handle_invalid_connection(
4446
76
          session, frame, NGHTTP2_ERR_FLOW_CONTROL,
4447
76
          "SETTINGS: too large SETTINGS_INITIAL_WINDOW_SIZE");
4448
76
      }
4449
4450
1.49k
      rv = session_update_remote_initial_window_size(session,
4451
1.49k
                                                     (int32_t)entry->value);
4452
4453
1.49k
      if (nghttp2_is_fatal(rv)) {
4454
0
        return rv;
4455
0
      }
4456
4457
1.49k
      if (rv != 0) {
4458
8
        return session_handle_invalid_connection(
4459
8
          session, frame, NGHTTP2_ERR_FLOW_CONTROL, NULL);
4460
8
      }
4461
4462
1.48k
      session->remote_settings.initial_window_size = entry->value;
4463
4464
1.48k
      break;
4465
535
    case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
4466
4467
535
      if (entry->value < NGHTTP2_MAX_FRAME_SIZE_MIN ||
4468
503
          entry->value > NGHTTP2_MAX_FRAME_SIZE_MAX) {
4469
108
        return session_handle_invalid_connection(
4470
108
          session, frame, NGHTTP2_ERR_PROTO,
4471
108
          "SETTINGS: invalid SETTINGS_MAX_FRAME_SIZE");
4472
108
      }
4473
4474
427
      session->remote_settings.max_frame_size = entry->value;
4475
4476
427
      break;
4477
1.33k
    case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
4478
4479
1.33k
      session->remote_settings.max_header_list_size = entry->value;
4480
4481
1.33k
      break;
4482
1.18k
    case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
4483
4484
1.18k
      if (entry->value != 0 && entry->value != 1) {
4485
114
        return session_handle_invalid_connection(
4486
114
          session, frame, NGHTTP2_ERR_PROTO,
4487
114
          "SETTINGS: invalid SETTINGS_ENABLE_CONNECT_PROTOCOL");
4488
114
      }
4489
4490
1.06k
      if (!session->server &&
4491
0
          session->remote_settings.enable_connect_protocol &&
4492
0
          entry->value == 0) {
4493
0
        return session_handle_invalid_connection(
4494
0
          session, frame, NGHTTP2_ERR_PROTO,
4495
0
          "SETTINGS: server attempted to disable "
4496
0
          "SETTINGS_ENABLE_CONNECT_PROTOCOL");
4497
0
      }
4498
4499
1.06k
      session->remote_settings.enable_connect_protocol = entry->value;
4500
4501
1.06k
      break;
4502
687
    case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
4503
4504
687
      if (entry->value != 0 && entry->value != 1) {
4505
116
        return session_handle_invalid_connection(
4506
116
          session, frame, NGHTTP2_ERR_PROTO,
4507
116
          "SETTINGS: invalid SETTINGS_NO_RFC7540_PRIORITIES");
4508
116
      }
4509
4510
571
      if (session->remote_settings.no_rfc7540_priorities != UINT32_MAX &&
4511
522
          session->remote_settings.no_rfc7540_priorities != entry->value) {
4512
4
        return session_handle_invalid_connection(
4513
4
          session, frame, NGHTTP2_ERR_PROTO,
4514
4
          "SETTINGS: SETTINGS_NO_RFC7540_PRIORITIES cannot be changed");
4515
4
      }
4516
4517
567
      session->remote_settings.no_rfc7540_priorities = entry->value;
4518
4519
567
      break;
4520
20.0k
    }
4521
20.0k
  }
4522
4523
34.0k
  if (session->remote_settings.no_rfc7540_priorities == UINT32_MAX) {
4524
17.1k
    session->remote_settings.no_rfc7540_priorities = 0;
4525
17.1k
  }
4526
4527
34.0k
  if (!noack && !session_is_closing(session)) {
4528
34.0k
    rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0);
4529
4530
34.0k
    if (rv != 0) {
4531
0
      if (nghttp2_is_fatal(rv)) {
4532
0
        return rv;
4533
0
      }
4534
4535
0
      return session_handle_invalid_connection(session, frame,
4536
0
                                               NGHTTP2_ERR_INTERNAL, NULL);
4537
0
    }
4538
34.0k
  }
4539
4540
34.0k
  return session_call_on_frame_received(session, frame);
4541
34.0k
}
4542
4543
35.0k
static int session_process_settings_frame(nghttp2_session *session) {
4544
35.0k
  nghttp2_inbound_frame *iframe = &session->iframe;
4545
35.0k
  nghttp2_frame *frame = &iframe->frame;
4546
35.0k
  size_t i;
4547
35.0k
  nghttp2_settings_entry min_header_size_entry;
4548
4549
35.0k
  if (iframe->max_niv) {
4550
7.05k
    min_header_size_entry = iframe->iv[iframe->max_niv - 1];
4551
4552
7.05k
    if (min_header_size_entry.value < UINT32_MAX) {
4553
      /* If we have less value, then we must have
4554
         SETTINGS_HEADER_TABLE_SIZE in i < iframe->niv */
4555
9.20k
      for (i = 0; i < iframe->niv; ++i) {
4556
9.20k
        if (iframe->iv[i].settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) {
4557
2.69k
          break;
4558
2.69k
        }
4559
9.20k
      }
4560
4561
2.69k
      assert(i < iframe->niv);
4562
4563
2.69k
      if (min_header_size_entry.value != iframe->iv[i].value) {
4564
882
        iframe->iv[iframe->niv++] = iframe->iv[i];
4565
882
        iframe->iv[i] = min_header_size_entry;
4566
882
      }
4567
2.69k
    }
4568
7.05k
  }
4569
4570
35.0k
  nghttp2_frame_unpack_settings_payload(&frame->settings, iframe->iv,
4571
35.0k
                                        iframe->niv);
4572
4573
35.0k
  iframe->iv = NULL;
4574
35.0k
  iframe->niv = 0;
4575
35.0k
  iframe->max_niv = 0;
4576
4577
35.0k
  return nghttp2_session_on_settings_received(session, frame, 0 /* ACK */);
4578
35.0k
}
4579
4580
int nghttp2_session_on_push_promise_received(nghttp2_session *session,
4581
24
                                             nghttp2_frame *frame) {
4582
24
  int rv;
4583
24
  nghttp2_stream *stream;
4584
24
  nghttp2_stream *promised_stream;
4585
4586
24
  if (frame->hd.stream_id == 0) {
4587
3
    return session_inflate_handle_invalid_connection(
4588
3
      session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream_id == 0");
4589
3
  }
4590
21
  if (session->server || session->local_settings.enable_push == 0) {
4591
21
    return session_inflate_handle_invalid_connection(
4592
21
      session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: push disabled");
4593
21
  }
4594
4595
0
  if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
4596
0
    return session_inflate_handle_invalid_connection(
4597
0
      session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: invalid stream_id");
4598
0
  }
4599
4600
0
  if (!session_allow_incoming_new_stream(session)) {
4601
    /* We just discard PUSH_PROMISE after GOAWAY was sent */
4602
0
    return NGHTTP2_ERR_IGN_HEADER_BLOCK;
4603
0
  }
4604
4605
0
  if (!session_is_new_peer_stream_id(session,
4606
0
                                     frame->push_promise.promised_stream_id)) {
4607
    /* The spec says if an endpoint receives a PUSH_PROMISE with
4608
       illegal stream ID is subject to a connection error of type
4609
       PROTOCOL_ERROR. */
4610
0
    return session_inflate_handle_invalid_connection(
4611
0
      session, frame, NGHTTP2_ERR_PROTO,
4612
0
      "PUSH_PROMISE: invalid promised_stream_id");
4613
0
  }
4614
4615
0
  if (session_detect_idle_stream(session, frame->hd.stream_id)) {
4616
0
    return session_inflate_handle_invalid_connection(
4617
0
      session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream in idle");
4618
0
  }
4619
4620
0
  session->last_recv_stream_id = frame->push_promise.promised_stream_id;
4621
0
  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
4622
0
  if (!stream || stream->state == NGHTTP2_STREAM_CLOSING ||
4623
0
      !session->pending_enable_push ||
4624
0
      session->num_incoming_reserved_streams >=
4625
0
        session->max_incoming_reserved_streams) {
4626
    /* Currently, client does not retain closed stream, so we don't
4627
       check NGHTTP2_SHUT_RD condition here. */
4628
0
    rv = session_handle_invalid_stream2(session,
4629
0
                                        frame->push_promise.promised_stream_id,
4630
0
                                        NULL, NGHTTP2_ERR_PUSH_CANCEL);
4631
0
    if (rv != 0) {
4632
0
      return rv;
4633
0
    }
4634
0
    return NGHTTP2_ERR_IGN_HEADER_BLOCK;
4635
0
  }
4636
4637
0
  if (stream->shut_flags & NGHTTP2_SHUT_RD) {
4638
0
    return session_inflate_handle_invalid_connection(
4639
0
      session, frame, NGHTTP2_ERR_STREAM_CLOSED, "PUSH_PROMISE: stream closed");
4640
0
  }
4641
4642
0
  promised_stream = nghttp2_session_open_stream(
4643
0
    session, frame->push_promise.promised_stream_id, NGHTTP2_STREAM_FLAG_NONE,
4644
0
    NGHTTP2_STREAM_RESERVED, NULL);
4645
4646
0
  if (!promised_stream) {
4647
0
    return NGHTTP2_ERR_NOMEM;
4648
0
  }
4649
4650
0
  session->last_proc_stream_id = session->last_recv_stream_id;
4651
0
  rv = session_call_on_begin_headers(session, frame);
4652
0
  if (rv != 0) {
4653
0
    return rv;
4654
0
  }
4655
0
  return 0;
4656
0
}
4657
4658
24
static int session_process_push_promise_frame(nghttp2_session *session) {
4659
24
  nghttp2_inbound_frame *iframe = &session->iframe;
4660
24
  nghttp2_frame *frame = &iframe->frame;
4661
4662
24
  nghttp2_frame_unpack_push_promise_payload(&frame->push_promise,
4663
24
                                            iframe->sbuf.pos);
4664
4665
24
  return nghttp2_session_on_push_promise_received(session, frame);
4666
24
}
4667
4668
int nghttp2_session_on_ping_received(nghttp2_session *session,
4669
1.78k
                                     nghttp2_frame *frame) {
4670
1.78k
  int rv = 0;
4671
1.78k
  if (frame->hd.stream_id != 0) {
4672
53
    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4673
53
                                             "PING: stream_id != 0");
4674
53
  }
4675
1.73k
  if ((session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_PING_ACK) == 0 &&
4676
1.73k
      (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 &&
4677
1.38k
      !session_is_closing(session)) {
4678
    /* Peer sent ping, so ping it back */
4679
1.38k
    rv = nghttp2_session_add_ping(session, NGHTTP2_FLAG_ACK,
4680
1.38k
                                  frame->ping.opaque_data);
4681
1.38k
    if (rv != 0) {
4682
1
      return rv;
4683
1
    }
4684
1.38k
  }
4685
1.73k
  return session_call_on_frame_received(session, frame);
4686
1.73k
}
4687
4688
1.78k
static int session_process_ping_frame(nghttp2_session *session) {
4689
1.78k
  nghttp2_inbound_frame *iframe = &session->iframe;
4690
1.78k
  nghttp2_frame *frame = &iframe->frame;
4691
4692
1.78k
  nghttp2_frame_unpack_ping_payload(&frame->ping, iframe->sbuf.pos);
4693
4694
1.78k
  return nghttp2_session_on_ping_received(session, frame);
4695
1.78k
}
4696
4697
int nghttp2_session_on_goaway_received(nghttp2_session *session,
4698
1.84k
                                       nghttp2_frame *frame) {
4699
1.84k
  int rv;
4700
4701
1.84k
  if (frame->hd.stream_id != 0) {
4702
24
    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4703
24
                                             "GOAWAY: stream_id != 0");
4704
24
  }
4705
  /* Spec says Endpoints MUST NOT increase the value they send in the
4706
     last stream identifier. */
4707
1.81k
  if ((frame->goaway.last_stream_id > 0 &&
4708
1.10k
       !nghttp2_session_is_my_stream_id(session,
4709
1.10k
                                        frame->goaway.last_stream_id)) ||
4710
1.80k
      session->remote_last_stream_id < frame->goaway.last_stream_id) {
4711
20
    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4712
20
                                             "GOAWAY: invalid last_stream_id");
4713
20
  }
4714
4715
1.79k
  session->goaway_flags |= NGHTTP2_GOAWAY_RECV;
4716
4717
1.79k
  session->remote_last_stream_id = frame->goaway.last_stream_id;
4718
4719
1.79k
  rv = session_call_on_frame_received(session, frame);
4720
4721
1.79k
  if (nghttp2_is_fatal(rv)) {
4722
0
    return rv;
4723
0
  }
4724
4725
1.79k
  return session_close_stream_on_goaway(session, frame->goaway.last_stream_id,
4726
1.79k
                                        0);
4727
1.79k
}
4728
4729
1.84k
static int session_process_goaway_frame(nghttp2_session *session) {
4730
1.84k
  nghttp2_inbound_frame *iframe = &session->iframe;
4731
1.84k
  nghttp2_frame *frame = &iframe->frame;
4732
4733
1.84k
  nghttp2_frame_unpack_goaway_payload(&frame->goaway, iframe->sbuf.pos,
4734
1.84k
                                      iframe->lbuf.pos,
4735
1.84k
                                      nghttp2_buf_len(&iframe->lbuf));
4736
4737
1.84k
  nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
4738
4739
1.84k
  return nghttp2_session_on_goaway_received(session, frame);
4740
1.84k
}
4741
4742
static int
4743
session_on_connection_window_update_received(nghttp2_session *session,
4744
539
                                             nghttp2_frame *frame) {
4745
  /* Handle connection-level flow control */
4746
539
  if (frame->window_update.window_size_increment == 0) {
4747
2
    return session_handle_invalid_connection(
4748
2
      session, frame, NGHTTP2_ERR_PROTO,
4749
2
      "WINDOW_UPDATE: window_size_increment == 0");
4750
2
  }
4751
4752
537
  if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
4753
537
      session->remote_window_size) {
4754
9
    return session_handle_invalid_connection(session, frame,
4755
9
                                             NGHTTP2_ERR_FLOW_CONTROL, NULL);
4756
9
  }
4757
528
  session->remote_window_size += frame->window_update.window_size_increment;
4758
4759
528
  return session_call_on_frame_received(session, frame);
4760
537
}
4761
4762
static int session_on_stream_window_update_received(nghttp2_session *session,
4763
894
                                                    nghttp2_frame *frame) {
4764
894
  int rv;
4765
894
  nghttp2_stream *stream;
4766
4767
894
  if (session_detect_idle_stream(session, frame->hd.stream_id)) {
4768
120
    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4769
120
                                             "WINDOW_UPDATE to idle stream");
4770
120
  }
4771
4772
774
  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
4773
774
  if (!stream) {
4774
348
    return 0;
4775
348
  }
4776
426
  if (state_reserved_remote(session, stream)) {
4777
0
    return session_handle_invalid_connection(
4778
0
      session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPADATE to reserved stream");
4779
0
  }
4780
426
  if (frame->window_update.window_size_increment == 0) {
4781
3
    return session_handle_invalid_connection(
4782
3
      session, frame, NGHTTP2_ERR_PROTO,
4783
3
      "WINDOW_UPDATE: window_size_increment == 0");
4784
3
  }
4785
423
  if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
4786
423
      stream->remote_window_size) {
4787
8
    return session_handle_invalid_connection(
4788
8
      session, frame, NGHTTP2_ERR_FLOW_CONTROL,
4789
8
      "WINDOW_UPDATE: window size overflow");
4790
8
  }
4791
415
  stream->remote_window_size += frame->window_update.window_size_increment;
4792
4793
415
  if (stream->remote_window_size > 0 &&
4794
415
      nghttp2_stream_check_deferred_by_flow_control(stream)) {
4795
0
    rv = session_resume_deferred_stream_item(
4796
0
      session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
4797
4798
0
    if (nghttp2_is_fatal(rv)) {
4799
0
      return rv;
4800
0
    }
4801
0
  }
4802
415
  return session_call_on_frame_received(session, frame);
4803
415
}
4804
4805
int nghttp2_session_on_window_update_received(nghttp2_session *session,
4806
1.43k
                                              nghttp2_frame *frame) {
4807
1.43k
  if (frame->hd.stream_id == 0) {
4808
539
    return session_on_connection_window_update_received(session, frame);
4809
894
  } else {
4810
894
    return session_on_stream_window_update_received(session, frame);
4811
894
  }
4812
1.43k
}
4813
4814
1.43k
static int session_process_window_update_frame(nghttp2_session *session) {
4815
1.43k
  nghttp2_inbound_frame *iframe = &session->iframe;
4816
1.43k
  nghttp2_frame *frame = &iframe->frame;
4817
4818
1.43k
  nghttp2_frame_unpack_window_update_payload(&frame->window_update,
4819
1.43k
                                             iframe->sbuf.pos);
4820
4821
1.43k
  return nghttp2_session_on_window_update_received(session, frame);
4822
1.43k
}
4823
4824
int nghttp2_session_on_altsvc_received(nghttp2_session *session,
4825
0
                                       nghttp2_frame *frame) {
4826
0
  nghttp2_ext_altsvc *altsvc;
4827
0
  nghttp2_stream *stream;
4828
4829
0
  altsvc = frame->ext.payload;
4830
4831
  /* session->server case has been excluded */
4832
4833
0
  if (frame->hd.stream_id == 0) {
4834
0
    if (altsvc->origin_len == 0) {
4835
0
      return session_call_on_invalid_frame_recv_callback(session, frame,
4836
0
                                                         NGHTTP2_ERR_PROTO);
4837
0
    }
4838
0
  } else {
4839
0
    if (altsvc->origin_len > 0) {
4840
0
      return session_call_on_invalid_frame_recv_callback(session, frame,
4841
0
                                                         NGHTTP2_ERR_PROTO);
4842
0
    }
4843
4844
0
    stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
4845
0
    if (!stream) {
4846
0
      return 0;
4847
0
    }
4848
4849
0
    if (stream->state == NGHTTP2_STREAM_CLOSING) {
4850
0
      return 0;
4851
0
    }
4852
0
  }
4853
4854
0
  if (altsvc->field_value_len == 0) {
4855
0
    return session_call_on_invalid_frame_recv_callback(session, frame,
4856
0
                                                       NGHTTP2_ERR_PROTO);
4857
0
  }
4858
4859
0
  return session_call_on_frame_received(session, frame);
4860
0
}
4861
4862
int nghttp2_session_on_origin_received(nghttp2_session *session,
4863
0
                                       nghttp2_frame *frame) {
4864
0
  return session_call_on_frame_received(session, frame);
4865
0
}
4866
4867
int nghttp2_session_on_priority_update_received(nghttp2_session *session,
4868
0
                                                nghttp2_frame *frame) {
4869
0
  nghttp2_ext_priority_update *priority_update;
4870
0
  nghttp2_stream *stream;
4871
0
  nghttp2_extpri extpri;
4872
0
  int rv;
4873
4874
0
  assert(session->server);
4875
4876
0
  priority_update = frame->ext.payload;
4877
4878
0
  if (frame->hd.stream_id != 0) {
4879
0
    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4880
0
                                             "PRIORITY_UPDATE: stream_id == 0");
4881
0
  }
4882
4883
0
  if (nghttp2_session_is_my_stream_id(session, priority_update->stream_id)) {
4884
0
    if (session_detect_idle_stream(session, priority_update->stream_id)) {
4885
0
      return session_handle_invalid_connection(
4886
0
        session, frame, NGHTTP2_ERR_PROTO,
4887
0
        "PRIORITY_UPDATE: prioritizing idle push is not allowed");
4888
0
    }
4889
4890
    /* TODO Ignore priority signal to a push stream for now */
4891
0
    return session_call_on_frame_received(session, frame);
4892
0
  }
4893
4894
0
  stream = nghttp2_session_get_stream_raw(session, priority_update->stream_id);
4895
0
  if (stream) {
4896
    /* Stream already exists. */
4897
0
    if (stream->flags & NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES) {
4898
0
      return session_call_on_frame_received(session, frame);
4899
0
    }
4900
0
  } else if (session_detect_idle_stream(session, priority_update->stream_id)) {
4901
0
    if (session->num_idle_streams + session->num_incoming_streams >=
4902
0
        session->local_settings.max_concurrent_streams) {
4903
0
      return session_handle_invalid_connection(
4904
0
        session, frame, NGHTTP2_ERR_PROTO,
4905
0
        "PRIORITY_UPDATE: max concurrent streams exceeded");
4906
0
    }
4907
4908
0
    stream =
4909
0
      nghttp2_session_open_stream(session, priority_update->stream_id,
4910
0
                                  NGHTTP2_FLAG_NONE, NGHTTP2_STREAM_IDLE, NULL);
4911
0
    if (!stream) {
4912
0
      return NGHTTP2_ERR_NOMEM;
4913
0
    }
4914
0
  } else {
4915
0
    return session_call_on_frame_received(session, frame);
4916
0
  }
4917
4918
0
  extpri.urgency = NGHTTP2_EXTPRI_DEFAULT_URGENCY;
4919
0
  extpri.inc = 0;
4920
4921
0
  rv = nghttp2_http_parse_priority(&extpri, priority_update->field_value,
4922
0
                                   priority_update->field_value_len);
4923
0
  if (rv != 0) {
4924
    /* Just ignore field_value if it cannot be parsed. */
4925
0
    return session_call_on_frame_received(session, frame);
4926
0
  }
4927
4928
0
  rv = session_update_stream_priority(session, stream,
4929
0
                                      nghttp2_extpri_to_uint8(&extpri));
4930
0
  if (rv != 0) {
4931
0
    if (nghttp2_is_fatal(rv)) {
4932
0
      return rv;
4933
0
    }
4934
0
  }
4935
4936
0
  return session_call_on_frame_received(session, frame);
4937
0
}
4938
4939
0
static int session_process_altsvc_frame(nghttp2_session *session) {
4940
0
  nghttp2_inbound_frame *iframe = &session->iframe;
4941
0
  nghttp2_frame *frame = &iframe->frame;
4942
4943
0
  nghttp2_frame_unpack_altsvc_payload(
4944
0
    &frame->ext, nghttp2_get_uint16(iframe->sbuf.pos), iframe->lbuf.pos,
4945
0
    nghttp2_buf_len(&iframe->lbuf));
4946
4947
  /* nghttp2_frame_unpack_altsvc_payload steals buffer from
4948
     iframe->lbuf */
4949
0
  nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
4950
4951
0
  return nghttp2_session_on_altsvc_received(session, frame);
4952
0
}
4953
4954
0
static int session_process_origin_frame(nghttp2_session *session) {
4955
0
  nghttp2_inbound_frame *iframe = &session->iframe;
4956
0
  nghttp2_frame *frame = &iframe->frame;
4957
0
  nghttp2_mem *mem = &session->mem;
4958
0
  int rv;
4959
4960
0
  rv = nghttp2_frame_unpack_origin_payload(&frame->ext, iframe->lbuf.pos,
4961
0
                                           nghttp2_buf_len(&iframe->lbuf), mem);
4962
0
  if (rv != 0) {
4963
0
    if (nghttp2_is_fatal(rv)) {
4964
0
      return rv;
4965
0
    }
4966
    /* Ignore ORIGIN frame which cannot be parsed. */
4967
0
    return 0;
4968
0
  }
4969
4970
0
  return nghttp2_session_on_origin_received(session, frame);
4971
0
}
4972
4973
0
static int session_process_priority_update_frame(nghttp2_session *session) {
4974
0
  nghttp2_inbound_frame *iframe = &session->iframe;
4975
0
  nghttp2_frame *frame = &iframe->frame;
4976
4977
0
  nghttp2_frame_unpack_priority_update_payload(&frame->ext, iframe->sbuf.pos,
4978
0
                                               nghttp2_buf_len(&iframe->sbuf));
4979
4980
0
  return nghttp2_session_on_priority_update_received(session, frame);
4981
0
}
4982
4983
0
static int session_process_extension_frame(nghttp2_session *session) {
4984
0
  int rv;
4985
0
  nghttp2_inbound_frame *iframe = &session->iframe;
4986
0
  nghttp2_frame *frame = &iframe->frame;
4987
4988
0
  rv = session_call_unpack_extension_callback(session);
4989
0
  if (nghttp2_is_fatal(rv)) {
4990
0
    return rv;
4991
0
  }
4992
4993
  /* This handles the case where rv == NGHTTP2_ERR_CANCEL as well */
4994
0
  if (rv != 0) {
4995
0
    return 0;
4996
0
  }
4997
4998
0
  return session_call_on_frame_received(session, frame);
4999
0
}
5000
5001
int nghttp2_session_on_data_received(nghttp2_session *session,
5002
6.70k
                                     nghttp2_frame *frame) {
5003
6.70k
  int rv = 0;
5004
6.70k
  nghttp2_stream *stream;
5005
6.70k
  nghttp2_inbound_frame *iframe = &session->iframe;
5006
5007
  /* We don't call on_frame_recv_callback if stream has been closed
5008
     already or being closed. */
5009
6.70k
  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
5010
6.70k
  if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) {
5011
    /* This should be treated as stream error, but it results in lots
5012
       of RST_STREAM. So just ignore frame against nonexistent stream
5013
       for now. */
5014
0
    return 0;
5015
0
  }
5016
5017
6.70k
  if (session_enforce_http_messaging(session) &&
5018
6.70k
      (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
5019
132
    if (nghttp2_http_on_remote_end_stream(stream) != 0) {
5020
68
      rv = session_handle_invalid_stream2(session, stream->stream_id, frame,
5021
68
                                          NGHTTP2_ERR_HTTP_MESSAGING);
5022
68
      if (nghttp2_is_fatal(rv)) {
5023
0
        return rv;
5024
0
      }
5025
5026
68
      rv = session_update_glitch_ratelim(session);
5027
68
      if (rv != 0) {
5028
0
        return rv;
5029
0
      }
5030
5031
68
      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
5032
0
        return 0;
5033
0
      }
5034
5035
68
      nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
5036
5037
      /* Don't call nghttp2_session_close_stream_if_shut_rdwr because
5038
         RST_STREAM has been submitted. */
5039
5040
68
      return 0;
5041
68
    }
5042
132
  }
5043
5044
6.63k
  rv = session_call_on_frame_received(session, frame);
5045
6.63k
  if (nghttp2_is_fatal(rv)) {
5046
0
    return rv;
5047
0
  }
5048
5049
6.63k
  if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
5050
64
    nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
5051
64
    rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
5052
64
    if (nghttp2_is_fatal(rv)) {
5053
0
      return rv;
5054
0
    }
5055
64
  }
5056
6.63k
  return 0;
5057
6.63k
}
5058
5059
/* For errors, this function only returns FATAL error. */
5060
6.70k
static int session_process_data_frame(nghttp2_session *session) {
5061
6.70k
  int rv;
5062
6.70k
  nghttp2_frame *public_data_frame = &session->iframe.frame;
5063
6.70k
  rv = nghttp2_session_on_data_received(session, public_data_frame);
5064
6.70k
  if (nghttp2_is_fatal(rv)) {
5065
0
    return rv;
5066
0
  }
5067
6.70k
  return 0;
5068
6.70k
}
5069
5070
/*
5071
 * Now we have SETTINGS synchronization, flow control error can be
5072
 * detected strictly. If DATA frame is received with length > 0 and
5073
 * current received window size + delta length is strictly larger than
5074
 * local window size, it is subject to FLOW_CONTROL_ERROR, so return
5075
 * -1. Note that local_window_size is calculated after SETTINGS ACK is
5076
 * received from peer, so peer must honor this limit. If the resulting
5077
 * recv_window_size is strictly larger than NGHTTP2_MAX_WINDOW_SIZE,
5078
 * return -1 too.
5079
 */
5080
static int adjust_recv_window_size(int32_t *recv_window_size_ptr, size_t delta,
5081
26.5k
                                   int32_t local_window_size) {
5082
26.5k
  if (*recv_window_size_ptr > local_window_size - (int32_t)delta ||
5083
26.4k
      *recv_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - (int32_t)delta) {
5084
26
    return -1;
5085
26
  }
5086
26.4k
  *recv_window_size_ptr += (int32_t)delta;
5087
26.4k
  return 0;
5088
26.5k
}
5089
5090
int nghttp2_session_update_recv_stream_window_size(nghttp2_session *session,
5091
                                                   nghttp2_stream *stream,
5092
                                                   size_t delta_size,
5093
8.86k
                                                   int send_window_update) {
5094
8.86k
  int rv;
5095
8.86k
  rv = adjust_recv_window_size(&stream->recv_window_size, delta_size,
5096
8.86k
                               stream->local_window_size);
5097
8.86k
  if (rv != 0) {
5098
11
    return nghttp2_session_terminate_session(session,
5099
11
                                             NGHTTP2_FLOW_CONTROL_ERROR);
5100
11
  }
5101
  /* We don't have to send WINDOW_UPDATE if the data received is the
5102
     last chunk in the incoming stream. */
5103
  /* We have to use local_settings here because it is the constraint
5104
     the remote endpoint should honor. */
5105
8.85k
  if (send_window_update &&
5106
8.74k
      !(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) &&
5107
8.74k
      stream->window_update_queued == 0 &&
5108
4.86k
      nghttp2_should_send_window_update(stream->local_window_size,
5109
4.86k
                                        stream->recv_window_size)) {
5110
75
    rv = nghttp2_session_add_window_update(
5111
75
      session, NGHTTP2_FLAG_NONE, stream->stream_id, stream->recv_window_size);
5112
75
    if (rv != 0) {
5113
0
      return rv;
5114
0
    }
5115
5116
75
    stream->recv_window_size = 0;
5117
75
  }
5118
8.85k
  return 0;
5119
8.85k
}
5120
5121
int nghttp2_session_update_recv_connection_window_size(nghttp2_session *session,
5122
17.6k
                                                       size_t delta_size) {
5123
17.6k
  int rv;
5124
17.6k
  rv = adjust_recv_window_size(&session->recv_window_size, delta_size,
5125
17.6k
                               session->local_window_size);
5126
17.6k
  if (rv != 0) {
5127
15
    return nghttp2_session_terminate_session(session,
5128
15
                                             NGHTTP2_FLOW_CONTROL_ERROR);
5129
15
  }
5130
17.6k
  if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) &&
5131
17.6k
      session->window_update_queued == 0 &&
5132
12.5k
      nghttp2_should_send_window_update(session->local_window_size,
5133
12.5k
                                        session->recv_window_size)) {
5134
    /* Use stream ID 0 to update connection-level flow control
5135
       window */
5136
33
    rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, 0,
5137
33
                                           session->recv_window_size);
5138
33
    if (rv != 0) {
5139
0
      return rv;
5140
0
    }
5141
5142
33
    session->recv_window_size = 0;
5143
33
  }
5144
17.6k
  return 0;
5145
17.6k
}
5146
5147
static int session_update_consumed_size(nghttp2_session *session,
5148
                                        int32_t *consumed_size_ptr,
5149
                                        int32_t *recv_window_size_ptr,
5150
                                        uint8_t window_update_queued,
5151
                                        int32_t stream_id, size_t delta_size,
5152
0
                                        int32_t local_window_size) {
5153
0
  int32_t recv_size;
5154
0
  int rv;
5155
5156
0
  if ((size_t)*consumed_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta_size) {
5157
0
    return nghttp2_session_terminate_session(session,
5158
0
                                             NGHTTP2_FLOW_CONTROL_ERROR);
5159
0
  }
5160
5161
0
  *consumed_size_ptr += (int32_t)delta_size;
5162
5163
0
  if (window_update_queued == 0) {
5164
    /* recv_window_size may be smaller than consumed_size, because it
5165
       may be decreased by negative value with
5166
       nghttp2_submit_window_update(). */
5167
0
    recv_size = nghttp2_min_int32(*consumed_size_ptr, *recv_window_size_ptr);
5168
5169
0
    if (nghttp2_should_send_window_update(local_window_size, recv_size)) {
5170
0
      rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE,
5171
0
                                             stream_id, recv_size);
5172
5173
0
      if (rv != 0) {
5174
0
        return rv;
5175
0
      }
5176
5177
0
      *recv_window_size_ptr -= recv_size;
5178
0
      *consumed_size_ptr -= recv_size;
5179
0
    }
5180
0
  }
5181
5182
0
  return 0;
5183
0
}
5184
5185
static int session_update_stream_consumed_size(nghttp2_session *session,
5186
                                               nghttp2_stream *stream,
5187
0
                                               size_t delta_size) {
5188
0
  return session_update_consumed_size(
5189
0
    session, &stream->consumed_size, &stream->recv_window_size,
5190
0
    stream->window_update_queued, stream->stream_id, delta_size,
5191
0
    stream->local_window_size);
5192
0
}
5193
5194
static int session_update_connection_consumed_size(nghttp2_session *session,
5195
0
                                                   size_t delta_size) {
5196
0
  return session_update_consumed_size(
5197
0
    session, &session->consumed_size, &session->recv_window_size,
5198
0
    session->window_update_queued, 0, delta_size, session->local_window_size);
5199
0
}
5200
5201
/*
5202
 * Checks that we can receive the DATA frame for stream, which is
5203
 * indicated by |session->iframe.frame.hd.stream_id|. If it is a
5204
 * connection error situation, GOAWAY frame will be issued by this
5205
 * function.
5206
 *
5207
 * If the DATA frame is allowed, returns 0.
5208
 *
5209
 * This function returns 0 if it succeeds, or one of the following
5210
 * negative error codes:
5211
 *
5212
 * NGHTTP2_ERR_IGN_PAYLOAD
5213
 *   The reception of DATA frame is connection error; or should be
5214
 *   ignored.
5215
 * NGHTTP2_ERR_NOMEM
5216
 *   Out of memory.
5217
 */
5218
22.1k
static int session_on_data_received_fail_fast(nghttp2_session *session) {
5219
22.1k
  int rv;
5220
22.1k
  nghttp2_stream *stream;
5221
22.1k
  nghttp2_inbound_frame *iframe;
5222
22.1k
  int32_t stream_id;
5223
22.1k
  const char *failure_reason;
5224
22.1k
  uint32_t error_code = NGHTTP2_PROTOCOL_ERROR;
5225
5226
22.1k
  iframe = &session->iframe;
5227
22.1k
  stream_id = iframe->frame.hd.stream_id;
5228
5229
22.1k
  if (stream_id == 0) {
5230
    /* The spec says that if a DATA frame is received whose stream ID
5231
       is 0, the recipient MUST respond with a connection error of
5232
       type PROTOCOL_ERROR. */
5233
21
    failure_reason = "DATA: stream_id == 0";
5234
21
    goto fail;
5235
21
  }
5236
5237
22.1k
  if (session_detect_idle_stream(session, stream_id)) {
5238
176
    failure_reason = "DATA: stream in idle";
5239
176
    error_code = NGHTTP2_PROTOCOL_ERROR;
5240
176
    goto fail;
5241
176
  }
5242
5243
21.9k
  stream = nghttp2_session_get_stream(session, stream_id);
5244
21.9k
  if (!stream) {
5245
11.1k
    stream = nghttp2_session_get_stream_raw(session, stream_id);
5246
11.1k
    if (stream && (stream->shut_flags & NGHTTP2_SHUT_RD)) {
5247
0
      failure_reason = "DATA: stream closed";
5248
0
      error_code = NGHTTP2_STREAM_CLOSED;
5249
0
      goto fail;
5250
0
    }
5251
5252
11.1k
    return NGHTTP2_ERR_IGN_PAYLOAD;
5253
11.1k
  }
5254
10.8k
  if (stream->shut_flags & NGHTTP2_SHUT_RD) {
5255
2
    failure_reason = "DATA: stream in half-closed(remote)";
5256
2
    error_code = NGHTTP2_STREAM_CLOSED;
5257
2
    goto fail;
5258
2
  }
5259
5260
10.8k
  if (nghttp2_session_is_my_stream_id(session, stream_id)) {
5261
0
    if (stream->state == NGHTTP2_STREAM_CLOSING) {
5262
0
      return NGHTTP2_ERR_IGN_PAYLOAD;
5263
0
    }
5264
0
    if (stream->state != NGHTTP2_STREAM_OPENED) {
5265
0
      failure_reason = "DATA: stream not opened";
5266
0
      goto fail;
5267
0
    }
5268
0
    return 0;
5269
0
  }
5270
10.8k
  if (stream->state == NGHTTP2_STREAM_RESERVED) {
5271
0
    failure_reason = "DATA: stream in reserved";
5272
0
    goto fail;
5273
0
  }
5274
10.8k
  if (stream->state == NGHTTP2_STREAM_CLOSING) {
5275
3.73k
    return NGHTTP2_ERR_IGN_PAYLOAD;
5276
3.73k
  }
5277
7.11k
  return 0;
5278
199
fail:
5279
199
  rv = nghttp2_session_terminate_session_with_reason(session, error_code,
5280
199
                                                     failure_reason);
5281
199
  if (nghttp2_is_fatal(rv)) {
5282
0
    return rv;
5283
0
  }
5284
199
  return NGHTTP2_ERR_IGN_PAYLOAD;
5285
199
}
5286
5287
static size_t inbound_frame_payload_readlen(nghttp2_inbound_frame *iframe,
5288
                                            const uint8_t *in,
5289
256k
                                            const uint8_t *last) {
5290
256k
  return nghttp2_min_size((size_t)(last - in), iframe->payloadleft);
5291
256k
}
5292
5293
/*
5294
 * Resets iframe->sbuf and advance its mark pointer by |left| bytes.
5295
 */
5296
140k
static void inbound_frame_set_mark(nghttp2_inbound_frame *iframe, size_t left) {
5297
140k
  nghttp2_buf_reset(&iframe->sbuf);
5298
140k
  iframe->sbuf.mark += left;
5299
140k
}
5300
5301
static size_t inbound_frame_buf_read(nghttp2_inbound_frame *iframe,
5302
507k
                                     const uint8_t *in, const uint8_t *last) {
5303
507k
  size_t readlen;
5304
5305
507k
  readlen = nghttp2_min_size((size_t)(last - in),
5306
507k
                             nghttp2_buf_mark_avail(&iframe->sbuf));
5307
5308
507k
  iframe->sbuf.last = nghttp2_cpymem(iframe->sbuf.last, in, readlen);
5309
5310
507k
  return readlen;
5311
507k
}
5312
5313
/*
5314
 * Unpacks SETTINGS entry in iframe->sbuf.
5315
 */
5316
23.0k
static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) {
5317
23.0k
  nghttp2_settings_entry iv;
5318
23.0k
  nghttp2_settings_entry *min_header_table_size_entry;
5319
23.0k
  size_t i;
5320
5321
23.0k
  nghttp2_frame_unpack_settings_entry(&iv, iframe->sbuf.pos);
5322
5323
23.0k
  switch (iv.settings_id) {
5324
4.71k
  case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
5325
5.95k
  case NGHTTP2_SETTINGS_ENABLE_PUSH:
5326
6.48k
  case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
5327
8.24k
  case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
5328
8.90k
  case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
5329
10.4k
  case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
5330
12.1k
  case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
5331
12.9k
  case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
5332
12.9k
    break;
5333
10.0k
  default:
5334
10.0k
    DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id);
5335
5336
10.0k
    iframe->iv[iframe->niv++] = iv;
5337
5338
10.0k
    return;
5339
23.0k
  }
5340
5341
28.9k
  for (i = 0; i < iframe->niv; ++i) {
5342
19.0k
    if (iframe->iv[i].settings_id == iv.settings_id) {
5343
3.12k
      iframe->iv[i] = iv;
5344
3.12k
      break;
5345
3.12k
    }
5346
19.0k
  }
5347
5348
12.9k
  if (i == iframe->niv) {
5349
9.84k
    iframe->iv[iframe->niv++] = iv;
5350
9.84k
  }
5351
5352
12.9k
  if (iv.settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) {
5353
    /* Keep track of minimum value of SETTINGS_HEADER_TABLE_SIZE */
5354
4.71k
    min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1];
5355
5356
4.71k
    if (iv.value < min_header_table_size_entry->value) {
5357
3.05k
      min_header_table_size_entry->value = iv.value;
5358
3.05k
    }
5359
4.71k
  }
5360
12.9k
}
5361
5362
/*
5363
 * Checks PADDED flags and set iframe->sbuf to read them accordingly.
5364
 * If padding is set, this function returns 1.  If no padding is set,
5365
 * this function returns 0.  On error, returns -1.
5366
 */
5367
static int inbound_frame_handle_pad(nghttp2_inbound_frame *iframe,
5368
150k
                                    nghttp2_frame_hd *hd) {
5369
150k
  if (hd->flags & NGHTTP2_FLAG_PADDED) {
5370
7.00k
    if (hd->length < 1) {
5371
7
      return -1;
5372
7
    }
5373
7.00k
    inbound_frame_set_mark(iframe, 1);
5374
7.00k
    return 1;
5375
7.00k
  }
5376
143k
  DEBUGF("recv: no padding in payload\n");
5377
143k
  return 0;
5378
150k
}
5379
5380
/*
5381
 * Computes number of padding based on flags. This function returns
5382
 * the calculated length if it succeeds, or -1.
5383
 */
5384
6.92k
static nghttp2_ssize inbound_frame_compute_pad(nghttp2_inbound_frame *iframe) {
5385
6.92k
  size_t padlen;
5386
5387
  /* 1 for Pad Length field */
5388
6.92k
  padlen = (size_t)(iframe->sbuf.pos[0] + 1);
5389
5390
6.92k
  DEBUGF("recv: padlen=%zu\n", padlen);
5391
5392
  /* We cannot use iframe->frame.hd.length because of CONTINUATION */
5393
6.92k
  if (padlen - 1 > iframe->payloadleft) {
5394
25
    return -1;
5395
25
  }
5396
5397
6.90k
  iframe->padlen = padlen;
5398
5399
6.90k
  return (nghttp2_ssize)padlen;
5400
6.92k
}
5401
5402
/*
5403
 * This function returns the effective payload length in the data of
5404
 * length |readlen| when the remaining payload is |payloadleft|. The
5405
 * |payloadleft| does not include |readlen|. If padding was started
5406
 * strictly before this data chunk, this function returns -1.
5407
 */
5408
static nghttp2_ssize
5409
inbound_frame_effective_readlen(nghttp2_inbound_frame *iframe,
5410
158k
                                size_t payloadleft, size_t readlen) {
5411
158k
  size_t trail_padlen =
5412
158k
    nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen);
5413
5414
158k
  if (trail_padlen > payloadleft) {
5415
2.47k
    size_t padlen;
5416
2.47k
    padlen = trail_padlen - payloadleft;
5417
2.47k
    if (readlen < padlen) {
5418
0
      return -1;
5419
0
    }
5420
2.47k
    return (nghttp2_ssize)(readlen - padlen);
5421
2.47k
  }
5422
156k
  return (nghttp2_ssize)(readlen);
5423
158k
}
5424
5425
static const uint8_t static_in[] = {0};
5426
5427
ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
5428
0
                                 size_t inlen) {
5429
0
  return (ssize_t)nghttp2_session_mem_recv2(session, in, inlen);
5430
0
}
5431
5432
static nghttp2_ssize session_mem_recv(nghttp2_session *session,
5433
18.6k
                                      const uint8_t *in, size_t inlen) {
5434
18.6k
  const uint8_t *first, *last;
5435
18.6k
  nghttp2_inbound_frame *iframe = &session->iframe;
5436
18.6k
  size_t readlen;
5437
18.6k
  nghttp2_ssize padlen;
5438
18.6k
  int rv;
5439
18.6k
  int busy = 0;
5440
18.6k
  nghttp2_frame_hd cont_hd;
5441
18.6k
  nghttp2_stream *stream;
5442
18.6k
  size_t pri_fieldlen;
5443
18.6k
  nghttp2_mem *mem;
5444
5445
18.6k
  if (in == NULL) {
5446
107
    assert(inlen == 0);
5447
107
    in = static_in;
5448
107
  }
5449
5450
18.6k
  first = in;
5451
18.6k
  last = in + inlen;
5452
5453
18.6k
  DEBUGF("recv: connection recv_window_size=%d, local_window=%d\n",
5454
18.6k
         session->recv_window_size, session->local_window_size);
5455
5456
18.6k
  mem = &session->mem;
5457
5458
18.6k
  if (!nghttp2_session_want_read(session)) {
5459
0
    return (nghttp2_ssize)inlen;
5460
0
  }
5461
5462
765k
  for (;;) {
5463
765k
    switch (iframe->state) {
5464
18.6k
    case NGHTTP2_IB_READ_CLIENT_MAGIC:
5465
18.6k
      readlen = nghttp2_min_size(inlen, iframe->payloadleft);
5466
5467
18.6k
      if (memcmp(&NGHTTP2_CLIENT_MAGIC[NGHTTP2_CLIENT_MAGIC_LEN -
5468
18.6k
                                       iframe->payloadleft],
5469
18.6k
                 in, readlen) != 0) {
5470
344
        return NGHTTP2_ERR_BAD_CLIENT_MAGIC;
5471
344
      }
5472
5473
18.3k
      iframe->payloadleft -= readlen;
5474
18.3k
      in += readlen;
5475
5476
18.3k
      if (iframe->payloadleft == 0) {
5477
18.1k
        session_inbound_frame_reset(session);
5478
18.1k
        iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS;
5479
18.1k
      }
5480
5481
18.3k
      break;
5482
18.1k
    case NGHTTP2_IB_READ_FIRST_SETTINGS:
5483
18.1k
      DEBUGF("recv: [IB_READ_FIRST_SETTINGS]\n");
5484
5485
18.1k
      readlen = inbound_frame_buf_read(iframe, in, last);
5486
18.1k
      in += readlen;
5487
5488
18.1k
      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
5489
10
        return (nghttp2_ssize)(in - first);
5490
10
      }
5491
5492
18.1k
      if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS ||
5493
18.1k
          (iframe->sbuf.pos[4] & NGHTTP2_FLAG_ACK)) {
5494
22
        rv = session_call_error_callback(
5495
22
          session, NGHTTP2_ERR_SETTINGS_EXPECTED,
5496
22
          "Remote peer returned unexpected data while we expected "
5497
22
          "SETTINGS frame.  Perhaps, peer does not support HTTP/2 "
5498
22
          "properly.");
5499
5500
22
        if (nghttp2_is_fatal(rv)) {
5501
0
          return rv;
5502
0
        }
5503
5504
22
        rv = nghttp2_session_terminate_session_with_reason(
5505
22
          session, NGHTTP2_PROTOCOL_ERROR, "SETTINGS expected");
5506
5507
22
        if (nghttp2_is_fatal(rv)) {
5508
0
          return rv;
5509
0
        }
5510
5511
22
        return (nghttp2_ssize)inlen;
5512
22
      }
5513
5514
18.1k
      iframe->state = NGHTTP2_IB_READ_HEAD;
5515
5516
    /* Fall through */
5517
351k
    case NGHTTP2_IB_READ_HEAD: {
5518
351k
      int on_begin_frame_called = 0;
5519
5520
351k
      DEBUGF("recv: [IB_READ_HEAD]\n");
5521
5522
351k
      readlen = inbound_frame_buf_read(iframe, in, last);
5523
351k
      in += readlen;
5524
5525
351k
      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
5526
200
        return (nghttp2_ssize)(in - first);
5527
200
      }
5528
5529
351k
      nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->sbuf.pos);
5530
351k
      iframe->payloadleft = iframe->frame.hd.length;
5531
5532
351k
      DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n",
5533
351k
             iframe->frame.hd.length, iframe->frame.hd.type,
5534
351k
             iframe->frame.hd.flags, iframe->frame.hd.stream_id);
5535
5536
351k
      if (iframe->frame.hd.length > session->local_settings.max_frame_size) {
5537
216
        DEBUGF("recv: length is too large %zu > %u\n", iframe->frame.hd.length,
5538
216
               session->local_settings.max_frame_size);
5539
5540
216
        rv = nghttp2_session_terminate_session_with_reason(
5541
216
          session, NGHTTP2_FRAME_SIZE_ERROR, "too large frame size");
5542
5543
216
        if (nghttp2_is_fatal(rv)) {
5544
0
          return rv;
5545
0
        }
5546
5547
216
        return (nghttp2_ssize)inlen;
5548
216
      }
5549
5550
351k
      switch (iframe->frame.hd.type) {
5551
22.1k
      case NGHTTP2_DATA: {
5552
22.1k
        DEBUGF("recv: DATA\n");
5553
5554
22.1k
        iframe->frame.hd.flags &=
5555
22.1k
          (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PADDED);
5556
        /* Check stream is open. If it is not open or closing,
5557
           ignore payload. */
5558
22.1k
        busy = 1;
5559
5560
22.1k
        rv = session_on_data_received_fail_fast(session);
5561
22.1k
        if (nghttp2_is_fatal(rv)) {
5562
0
          return rv;
5563
0
        }
5564
5565
22.1k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
5566
199
          return (nghttp2_ssize)inlen;
5567
199
        }
5568
21.9k
        if (rv == NGHTTP2_ERR_IGN_PAYLOAD) {
5569
14.8k
          DEBUGF("recv: DATA not allowed stream_id=%d\n",
5570
14.8k
                 iframe->frame.hd.stream_id);
5571
5572
14.8k
          iframe->state = NGHTTP2_IB_IGN_DATA;
5573
14.8k
          break;
5574
14.8k
        }
5575
5576
7.11k
        rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
5577
7.11k
        if (rv < 0) {
5578
2
          rv = nghttp2_session_terminate_session_with_reason(
5579
2
            session, NGHTTP2_PROTOCOL_ERROR,
5580
2
            "DATA: insufficient padding space");
5581
5582
2
          if (nghttp2_is_fatal(rv)) {
5583
0
            return rv;
5584
0
          }
5585
2
          return (nghttp2_ssize)inlen;
5586
2
        }
5587
5588
7.11k
        if (rv == 1) {
5589
4.80k
          iframe->state = NGHTTP2_IB_READ_PAD_DATA;
5590
4.80k
          break;
5591
4.80k
        }
5592
5593
        /* Empty DATA frame without END_STREAM flag set is
5594
           suspicious. */
5595
2.30k
        if (iframe->payloadleft == 0 &&
5596
385
            (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
5597
360
          rv = session_update_glitch_ratelim(session);
5598
360
          if (rv != 0) {
5599
0
            return rv;
5600
0
          }
5601
5602
360
          if (iframe->state == NGHTTP2_IB_IGN_ALL) {
5603
0
            return (nghttp2_ssize)inlen;
5604
0
          }
5605
360
        }
5606
5607
2.30k
        iframe->state = NGHTTP2_IB_READ_DATA;
5608
2.30k
        break;
5609
2.30k
      }
5610
143k
      case NGHTTP2_HEADERS:
5611
5612
143k
        DEBUGF("recv: HEADERS\n");
5613
5614
143k
        iframe->frame.hd.flags &=
5615
143k
          (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS |
5616
143k
           NGHTTP2_FLAG_PADDED | NGHTTP2_FLAG_PRIORITY);
5617
5618
143k
        rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
5619
143k
        if (rv < 0) {
5620
3
          rv = nghttp2_session_terminate_session_with_reason(
5621
3
            session, NGHTTP2_PROTOCOL_ERROR,
5622
3
            "HEADERS: insufficient padding space");
5623
3
          if (nghttp2_is_fatal(rv)) {
5624
0
            return rv;
5625
0
          }
5626
3
          return (nghttp2_ssize)inlen;
5627
3
        }
5628
5629
143k
        if (rv == 1) {
5630
2.09k
          iframe->state = NGHTTP2_IB_READ_NBYTE;
5631
2.09k
          break;
5632
2.09k
        }
5633
5634
141k
        pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags);
5635
5636
141k
        if (pri_fieldlen > 0) {
5637
4.03k
          if (iframe->payloadleft < pri_fieldlen) {
5638
5
            busy = 1;
5639
5
            iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5640
5
            break;
5641
5
          }
5642
5643
4.02k
          iframe->state = NGHTTP2_IB_READ_NBYTE;
5644
5645
4.02k
          inbound_frame_set_mark(iframe, pri_fieldlen);
5646
5647
4.02k
          break;
5648
4.03k
        }
5649
5650
        /* Call on_begin_frame_callback here because
5651
           session_process_headers_frame() may call
5652
           on_begin_headers_callback */
5653
137k
        rv = session_call_on_begin_frame(session, &iframe->frame.hd);
5654
5655
137k
        if (nghttp2_is_fatal(rv)) {
5656
0
          return rv;
5657
0
        }
5658
5659
137k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
5660
0
          return (nghttp2_ssize)inlen;
5661
0
        }
5662
5663
137k
        on_begin_frame_called = 1;
5664
5665
137k
        rv = session_process_headers_frame(session);
5666
137k
        if (nghttp2_is_fatal(rv)) {
5667
0
          return rv;
5668
0
        }
5669
5670
137k
        busy = 1;
5671
5672
137k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
5673
41
          return (nghttp2_ssize)inlen;
5674
41
        }
5675
5676
137k
        if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
5677
0
          rv = session_handle_invalid_stream2(
5678
0
            session, iframe->frame.hd.stream_id, NULL, NGHTTP2_ERR_INTERNAL);
5679
0
          if (nghttp2_is_fatal(rv)) {
5680
0
            return rv;
5681
0
          }
5682
0
          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
5683
0
          break;
5684
0
        }
5685
5686
137k
        if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
5687
107k
          rv = session_update_glitch_ratelim(session);
5688
107k
          if (rv != 0) {
5689
0
            return rv;
5690
0
          }
5691
5692
107k
          if (iframe->state == NGHTTP2_IB_IGN_ALL) {
5693
1
            return (nghttp2_ssize)inlen;
5694
1
          }
5695
5696
107k
          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
5697
107k
          break;
5698
107k
        }
5699
5700
29.9k
        iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
5701
5702
29.9k
        break;
5703
407
      case NGHTTP2_PRIORITY:
5704
407
        DEBUGF("recv: PRIORITY\n");
5705
5706
407
        iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
5707
5708
407
        if (iframe->payloadleft != NGHTTP2_PRIORITY_SPECLEN) {
5709
32
          busy = 1;
5710
5711
32
          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5712
5713
32
          break;
5714
32
        }
5715
5716
        /* This is deprecated RFC 7540 priorities mechanism which is
5717
           very unpopular.  We do not expect it is received so
5718
           frequently. */
5719
375
        rv = session_update_glitch_ratelim(session);
5720
375
        if (rv != 0) {
5721
0
          return rv;
5722
0
        }
5723
5724
375
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
5725
1
          return (nghttp2_ssize)inlen;
5726
1
        }
5727
5728
374
        iframe->state = NGHTTP2_IB_READ_NBYTE;
5729
5730
374
        inbound_frame_set_mark(iframe, NGHTTP2_PRIORITY_SPECLEN);
5731
5732
374
        break;
5733
66.3k
      case NGHTTP2_RST_STREAM:
5734
67.7k
      case NGHTTP2_WINDOW_UPDATE:
5735
#ifdef DEBUGBUILD
5736
        switch (iframe->frame.hd.type) {
5737
        case NGHTTP2_RST_STREAM:
5738
          DEBUGF("recv: RST_STREAM\n");
5739
          break;
5740
        case NGHTTP2_WINDOW_UPDATE:
5741
          DEBUGF("recv: WINDOW_UPDATE\n");
5742
          break;
5743
        }
5744
#endif /* defined(DEBUGBUILD) */
5745
5746
67.7k
        iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
5747
5748
67.7k
        if (iframe->payloadleft != 4) {
5749
51
          busy = 1;
5750
51
          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5751
51
          break;
5752
51
        }
5753
5754
67.7k
        iframe->state = NGHTTP2_IB_READ_NBYTE;
5755
5756
67.7k
        inbound_frame_set_mark(iframe, 4);
5757
5758
67.7k
        break;
5759
35.3k
      case NGHTTP2_SETTINGS:
5760
35.3k
        DEBUGF("recv: SETTINGS\n");
5761
5762
35.3k
        iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK;
5763
5764
35.3k
        if ((iframe->frame.hd.length % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) ||
5765
35.3k
            ((iframe->frame.hd.flags & NGHTTP2_FLAG_ACK) &&
5766
344
             iframe->payloadleft > 0)) {
5767
34
          busy = 1;
5768
34
          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5769
34
          break;
5770
34
        }
5771
5772
        /* Check the settings flood counter early to be safe */
5773
35.2k
        if (session->obq_flood_counter_ >= session->max_outbound_ack &&
5774
1
            !(iframe->frame.hd.flags & NGHTTP2_FLAG_ACK)) {
5775
1
          return NGHTTP2_ERR_FLOODED;
5776
1
        }
5777
5778
35.2k
        iframe->state = NGHTTP2_IB_READ_SETTINGS;
5779
5780
35.2k
        if (iframe->payloadleft) {
5781
7.32k
          nghttp2_settings_entry *min_header_table_size_entry;
5782
5783
          /* We allocate iv with additional one entry, to store the
5784
             minimum header table size. */
5785
7.32k
          iframe->max_niv =
5786
7.32k
            iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1;
5787
5788
7.32k
          if (iframe->max_niv - 1 > session->max_settings) {
5789
25
            rv = nghttp2_session_terminate_session_with_reason(
5790
25
              session, NGHTTP2_ENHANCE_YOUR_CALM,
5791
25
              "SETTINGS: too many setting entries");
5792
25
            if (nghttp2_is_fatal(rv)) {
5793
0
              return rv;
5794
0
            }
5795
25
            return (nghttp2_ssize)inlen;
5796
25
          }
5797
5798
7.30k
          iframe->iv = nghttp2_mem_malloc(mem, sizeof(nghttp2_settings_entry) *
5799
7.30k
                                                 iframe->max_niv);
5800
5801
7.30k
          if (!iframe->iv) {
5802
0
            return NGHTTP2_ERR_NOMEM;
5803
0
          }
5804
5805
7.30k
          min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1];
5806
7.30k
          min_header_table_size_entry->settings_id =
5807
7.30k
            NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
5808
7.30k
          min_header_table_size_entry->value = UINT32_MAX;
5809
5810
7.30k
          inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH);
5811
7.30k
          break;
5812
7.30k
        }
5813
5814
27.9k
        busy = 1;
5815
5816
27.9k
        inbound_frame_set_mark(iframe, 0);
5817
5818
27.9k
        break;
5819
141
      case NGHTTP2_PUSH_PROMISE:
5820
141
        DEBUGF("recv: PUSH_PROMISE\n");
5821
5822
141
        iframe->frame.hd.flags &=
5823
141
          (NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PADDED);
5824
5825
141
        rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
5826
141
        if (rv < 0) {
5827
2
          rv = nghttp2_session_terminate_session_with_reason(
5828
2
            session, NGHTTP2_PROTOCOL_ERROR,
5829
2
            "PUSH_PROMISE: insufficient padding space");
5830
2
          if (nghttp2_is_fatal(rv)) {
5831
0
            return rv;
5832
0
          }
5833
2
          return (nghttp2_ssize)inlen;
5834
2
        }
5835
5836
139
        if (rv == 1) {
5837
103
          iframe->state = NGHTTP2_IB_READ_NBYTE;
5838
103
          break;
5839
103
        }
5840
5841
36
        if (iframe->payloadleft < 4) {
5842
7
          busy = 1;
5843
7
          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5844
7
          break;
5845
7
        }
5846
5847
29
        iframe->state = NGHTTP2_IB_READ_NBYTE;
5848
5849
29
        inbound_frame_set_mark(iframe, 4);
5850
5851
29
        break;
5852
1.83k
      case NGHTTP2_PING:
5853
1.83k
        DEBUGF("recv: PING\n");
5854
5855
1.83k
        iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK;
5856
5857
1.83k
        if (iframe->payloadleft != 8) {
5858
34
          busy = 1;
5859
34
          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5860
34
          break;
5861
34
        }
5862
5863
1.80k
        iframe->state = NGHTTP2_IB_READ_NBYTE;
5864
1.80k
        inbound_frame_set_mark(iframe, 8);
5865
5866
1.80k
        break;
5867
1.93k
      case NGHTTP2_GOAWAY:
5868
1.93k
        DEBUGF("recv: GOAWAY\n");
5869
5870
1.93k
        iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
5871
5872
1.93k
        if (iframe->payloadleft < 8) {
5873
9
          busy = 1;
5874
9
          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5875
9
          break;
5876
9
        }
5877
5878
1.92k
        iframe->state = NGHTTP2_IB_READ_NBYTE;
5879
1.92k
        inbound_frame_set_mark(iframe, 8);
5880
5881
1.92k
        break;
5882
3
      case NGHTTP2_CONTINUATION:
5883
3
        DEBUGF("recv: unexpected CONTINUATION\n");
5884
5885
        /* Receiving CONTINUATION in this state are subject to
5886
           connection error of type PROTOCOL_ERROR */
5887
3
        rv = nghttp2_session_terminate_session_with_reason(
5888
3
          session, NGHTTP2_PROTOCOL_ERROR, "CONTINUATION: unexpected");
5889
3
        if (nghttp2_is_fatal(rv)) {
5890
0
          return rv;
5891
0
        }
5892
5893
3
        return (nghttp2_ssize)inlen;
5894
77.9k
      default:
5895
77.9k
        DEBUGF("recv: extension frame\n");
5896
5897
77.9k
        if (check_ext_type_set(session->user_recv_ext_types,
5898
77.9k
                               iframe->frame.hd.type)) {
5899
0
          if (!session->callbacks.unpack_extension_callback) {
5900
            /* Receiving too frequent unknown frames is suspicious. */
5901
0
            rv = session_update_glitch_ratelim(session);
5902
0
            if (rv != 0) {
5903
0
              return rv;
5904
0
            }
5905
5906
0
            if (iframe->state == NGHTTP2_IB_IGN_ALL) {
5907
0
              return (nghttp2_ssize)inlen;
5908
0
            }
5909
5910
            /* Silently ignore unknown frame type. */
5911
0
            busy = 1;
5912
5913
0
            iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5914
5915
0
            break;
5916
0
          }
5917
5918
0
          busy = 1;
5919
5920
0
          iframe->state = NGHTTP2_IB_READ_EXTENSION_PAYLOAD;
5921
5922
0
          break;
5923
77.9k
        } else {
5924
77.9k
          switch (iframe->frame.hd.type) {
5925
2.19k
          case NGHTTP2_ALTSVC:
5926
2.19k
            if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) ==
5927
2.19k
                0) {
5928
              /* Receiving too frequent unknown frames is suspicious. */
5929
2.19k
              rv = session_update_glitch_ratelim(session);
5930
2.19k
              if (rv != 0) {
5931
0
                return rv;
5932
0
              }
5933
5934
2.19k
              if (iframe->state == NGHTTP2_IB_IGN_ALL) {
5935
1
                return (nghttp2_ssize)inlen;
5936
1
              }
5937
5938
2.18k
              busy = 1;
5939
2.18k
              iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5940
2.18k
              break;
5941
2.19k
            }
5942
5943
0
            DEBUGF("recv: ALTSVC\n");
5944
5945
0
            iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
5946
0
            iframe->frame.ext.payload = &iframe->ext_frame_payload.altsvc;
5947
5948
0
            if (session->server) {
5949
              /* Receiving too frequent ALTSVC from client is
5950
                 suspicious. */
5951
0
              rv = session_update_glitch_ratelim(session);
5952
0
              if (rv != 0) {
5953
0
                return rv;
5954
0
              }
5955
5956
0
              if (iframe->state == NGHTTP2_IB_IGN_ALL) {
5957
0
                return (nghttp2_ssize)inlen;
5958
0
              }
5959
5960
0
              busy = 1;
5961
0
              iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5962
0
              break;
5963
0
            }
5964
5965
0
            if (iframe->payloadleft < 2) {
5966
0
              busy = 1;
5967
0
              iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
5968
0
              break;
5969
0
            }
5970
5971
0
            busy = 1;
5972
5973
0
            iframe->state = NGHTTP2_IB_READ_NBYTE;
5974
0
            inbound_frame_set_mark(iframe, 2);
5975
5976
0
            break;
5977
142
          case NGHTTP2_ORIGIN:
5978
142
            if (!(session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN)) {
5979
              /* Receiving too frequent unknown frames is suspicious. */
5980
142
              rv = session_update_glitch_ratelim(session);
5981
142
              if (rv != 0) {
5982
0
                return rv;
5983
0
              }
5984
5985
142
              if (iframe->state == NGHTTP2_IB_IGN_ALL) {
5986
4
                return (nghttp2_ssize)inlen;
5987
4
              }
5988
5989
138
              busy = 1;
5990
138
              iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
5991
138
              break;
5992
142
            }
5993
5994
0
            DEBUGF("recv: ORIGIN\n");
5995
5996
0
            iframe->frame.ext.payload = &iframe->ext_frame_payload.origin;
5997
5998
0
            if (session->server || iframe->frame.hd.stream_id ||
5999
0
                (iframe->frame.hd.flags & 0xF0)) {
6000
              /* Receiving too frequent invalid frames is
6001
                 suspicious. */
6002
0
              rv = session_update_glitch_ratelim(session);
6003
0
              if (rv != 0) {
6004
0
                return rv;
6005
0
              }
6006
6007
0
              if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6008
0
                return (nghttp2_ssize)inlen;
6009
0
              }
6010
6011
0
              busy = 1;
6012
0
              iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
6013
0
              break;
6014
0
            }
6015
6016
0
            iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
6017
6018
0
            if (iframe->payloadleft) {
6019
0
              iframe->raw_lbuf = nghttp2_mem_malloc(mem, iframe->payloadleft);
6020
6021
0
              if (iframe->raw_lbuf == NULL) {
6022
0
                return NGHTTP2_ERR_NOMEM;
6023
0
              }
6024
6025
0
              nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf,
6026
0
                                    iframe->payloadleft);
6027
0
            } else {
6028
0
              busy = 1;
6029
0
            }
6030
6031
0
            iframe->state = NGHTTP2_IB_READ_ORIGIN_PAYLOAD;
6032
6033
0
            break;
6034
65.1k
          case NGHTTP2_PRIORITY_UPDATE:
6035
65.1k
            if ((session->builtin_recv_ext_types &
6036
65.1k
                 NGHTTP2_TYPEMASK_PRIORITY_UPDATE) == 0) {
6037
              /* Receiving too frequent unknown frames is suspicious. */
6038
65.1k
              rv = session_update_glitch_ratelim(session);
6039
65.1k
              if (rv != 0) {
6040
0
                return rv;
6041
0
              }
6042
6043
65.1k
              if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6044
2
                return (nghttp2_ssize)inlen;
6045
2
              }
6046
6047
65.1k
              busy = 1;
6048
65.1k
              iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
6049
65.1k
              break;
6050
65.1k
            }
6051
6052
0
            DEBUGF("recv: PRIORITY_UPDATE\n");
6053
6054
0
            iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
6055
0
            iframe->frame.ext.payload =
6056
0
              &iframe->ext_frame_payload.priority_update;
6057
6058
0
            if (!session->server) {
6059
0
              rv = nghttp2_session_terminate_session_with_reason(
6060
0
                session, NGHTTP2_PROTOCOL_ERROR,
6061
0
                "PRIORITY_UPDATE is received from server");
6062
0
              if (nghttp2_is_fatal(rv)) {
6063
0
                return rv;
6064
0
              }
6065
0
              return (nghttp2_ssize)inlen;
6066
0
            }
6067
6068
0
            if (iframe->payloadleft < 4) {
6069
0
              busy = 1;
6070
0
              iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
6071
0
              break;
6072
0
            }
6073
6074
            /* Receiving too frequent PRIORITY_UPDATE is
6075
               suspicious. */
6076
0
            rv = session_update_glitch_ratelim(session);
6077
0
            if (rv != 0) {
6078
0
              return rv;
6079
0
            }
6080
6081
0
            if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6082
0
              return (nghttp2_ssize)inlen;
6083
0
            }
6084
6085
0
            if (iframe->payloadleft > sizeof(iframe->raw_sbuf)) {
6086
0
              busy = 1;
6087
0
              iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
6088
0
              break;
6089
0
            }
6090
6091
0
            busy = 1;
6092
6093
0
            iframe->state = NGHTTP2_IB_READ_NBYTE;
6094
0
            inbound_frame_set_mark(iframe, iframe->payloadleft);
6095
6096
0
            break;
6097
10.4k
          default:
6098
            /* Receiving too frequent unknown frames is suspicious. */
6099
10.4k
            rv = session_update_glitch_ratelim(session);
6100
10.4k
            if (rv != 0) {
6101
0
              return rv;
6102
0
            }
6103
6104
10.4k
            if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6105
1
              return (nghttp2_ssize)inlen;
6106
1
            }
6107
6108
10.4k
            busy = 1;
6109
6110
10.4k
            iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
6111
6112
10.4k
            break;
6113
77.9k
          }
6114
77.9k
        }
6115
351k
      }
6116
6117
350k
      if (!on_begin_frame_called) {
6118
213k
        switch (iframe->state) {
6119
0
        case NGHTTP2_IB_IGN_HEADER_BLOCK:
6120
77.9k
        case NGHTTP2_IB_IGN_PAYLOAD:
6121
78.1k
        case NGHTTP2_IB_FRAME_SIZE_ERROR:
6122
92.9k
        case NGHTTP2_IB_IGN_DATA:
6123
92.9k
        case NGHTTP2_IB_IGN_ALL:
6124
92.9k
          break;
6125
120k
        default:
6126
120k
          rv = session_call_on_begin_frame(session, &iframe->frame.hd);
6127
6128
120k
          if (nghttp2_is_fatal(rv)) {
6129
0
            return rv;
6130
0
          }
6131
6132
120k
          if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6133
0
            return (nghttp2_ssize)inlen;
6134
0
          }
6135
213k
        }
6136
213k
      }
6137
6138
350k
      break;
6139
350k
    }
6140
350k
    case NGHTTP2_IB_READ_NBYTE:
6141
78.6k
      DEBUGF("recv: [IB_READ_NBYTE]\n");
6142
6143
78.6k
      readlen = inbound_frame_buf_read(iframe, in, last);
6144
78.6k
      in += readlen;
6145
78.6k
      iframe->payloadleft -= readlen;
6146
6147
78.6k
      DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zu\n", readlen,
6148
78.6k
             iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf));
6149
6150
78.6k
      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
6151
10
        return (nghttp2_ssize)(in - first);
6152
10
      }
6153
6154
78.6k
      switch (iframe->frame.hd.type) {
6155
6.82k
      case NGHTTP2_HEADERS:
6156
6.82k
        if (iframe->padlen == 0 &&
6157
6.03k
            (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) {
6158
2.06k
          pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags);
6159
2.06k
          padlen = inbound_frame_compute_pad(iframe);
6160
2.06k
          if (padlen < 0 ||
6161
2.05k
              (size_t)padlen + pri_fieldlen > 1 + iframe->payloadleft) {
6162
9
            rv = nghttp2_session_terminate_session_with_reason(
6163
9
              session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: invalid padding");
6164
9
            if (nghttp2_is_fatal(rv)) {
6165
0
              return rv;
6166
0
            }
6167
9
            return (nghttp2_ssize)inlen;
6168
9
          }
6169
2.05k
          iframe->frame.headers.padlen = (size_t)padlen;
6170
6171
2.05k
          if (pri_fieldlen > 0) {
6172
854
            if (iframe->payloadleft < pri_fieldlen) {
6173
0
              busy = 1;
6174
0
              iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
6175
0
              break;
6176
0
            }
6177
854
            iframe->state = NGHTTP2_IB_READ_NBYTE;
6178
854
            inbound_frame_set_mark(iframe, pri_fieldlen);
6179
854
            break;
6180
1.19k
          } else {
6181
            /* Truncate buffers used for padding spec */
6182
1.19k
            inbound_frame_set_mark(iframe, 0);
6183
1.19k
          }
6184
2.05k
        }
6185
6186
5.96k
        rv = session_process_headers_frame(session);
6187
5.96k
        if (nghttp2_is_fatal(rv)) {
6188
0
          return rv;
6189
0
        }
6190
6191
5.96k
        busy = 1;
6192
6193
5.96k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6194
35
          return (nghttp2_ssize)inlen;
6195
35
        }
6196
6197
5.92k
        if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
6198
0
          rv = session_handle_invalid_stream2(
6199
0
            session, iframe->frame.hd.stream_id, NULL, NGHTTP2_ERR_INTERNAL);
6200
0
          if (nghttp2_is_fatal(rv)) {
6201
0
            return rv;
6202
0
          }
6203
0
          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
6204
0
          break;
6205
0
        }
6206
6207
5.92k
        if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
6208
3.69k
          rv = session_update_glitch_ratelim(session);
6209
3.69k
          if (rv != 0) {
6210
0
            return rv;
6211
0
          }
6212
6213
3.69k
          if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6214
1
            return (nghttp2_ssize)inlen;
6215
1
          }
6216
6217
3.69k
          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
6218
3.69k
          break;
6219
3.69k
        }
6220
6221
2.23k
        iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
6222
6223
2.23k
        break;
6224
362
      case NGHTTP2_PRIORITY:
6225
362
        session_inbound_frame_reset(session);
6226
6227
362
        break;
6228
66.2k
      case NGHTTP2_RST_STREAM:
6229
66.2k
        rv = session_process_rst_stream_frame(session);
6230
66.2k
        if (nghttp2_is_fatal(rv)) {
6231
0
          return rv;
6232
0
        }
6233
6234
66.2k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6235
116
          return (nghttp2_ssize)inlen;
6236
116
        }
6237
6238
66.1k
        session_inbound_frame_reset(session);
6239
6240
66.1k
        break;
6241
103
      case NGHTTP2_PUSH_PROMISE:
6242
103
        if (iframe->padlen == 0 &&
6243
84
            (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) {
6244
79
          padlen = inbound_frame_compute_pad(iframe);
6245
79
          if (padlen < 0 || (size_t)padlen + 4 /* promised stream id */
6246
75
                              > 1 + iframe->payloadleft) {
6247
14
            rv = nghttp2_session_terminate_session_with_reason(
6248
14
              session, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: invalid padding");
6249
14
            if (nghttp2_is_fatal(rv)) {
6250
0
              return rv;
6251
0
            }
6252
14
            return (nghttp2_ssize)inlen;
6253
14
          }
6254
6255
65
          iframe->frame.push_promise.padlen = (size_t)padlen;
6256
6257
65
          if (iframe->payloadleft < 4) {
6258
0
            busy = 1;
6259
0
            iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
6260
0
            break;
6261
0
          }
6262
6263
65
          iframe->state = NGHTTP2_IB_READ_NBYTE;
6264
6265
65
          inbound_frame_set_mark(iframe, 4);
6266
6267
65
          break;
6268
65
        }
6269
6270
24
        rv = session_process_push_promise_frame(session);
6271
24
        if (nghttp2_is_fatal(rv)) {
6272
0
          return rv;
6273
0
        }
6274
6275
24
        busy = 1;
6276
6277
24
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6278
24
          return (nghttp2_ssize)inlen;
6279
24
        }
6280
6281
0
        if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
6282
0
          rv = session_handle_invalid_stream2(
6283
0
            session, iframe->frame.push_promise.promised_stream_id, NULL,
6284
0
            NGHTTP2_ERR_INTERNAL);
6285
0
          if (nghttp2_is_fatal(rv)) {
6286
0
            return rv;
6287
0
          }
6288
0
          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
6289
0
          break;
6290
0
        }
6291
6292
0
        if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
6293
0
          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
6294
0
          break;
6295
0
        }
6296
6297
0
        iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
6298
6299
0
        break;
6300
1.78k
      case NGHTTP2_PING:
6301
1.78k
        rv = session_process_ping_frame(session);
6302
1.78k
        if (nghttp2_is_fatal(rv)) {
6303
1
          return rv;
6304
1
        }
6305
6306
1.78k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6307
53
          return (nghttp2_ssize)inlen;
6308
53
        }
6309
6310
1.73k
        session_inbound_frame_reset(session);
6311
6312
1.73k
        break;
6313
1.89k
      case NGHTTP2_GOAWAY: {
6314
1.89k
        size_t debuglen;
6315
6316
        /* 8 is Last-stream-ID + Error Code */
6317
1.89k
        debuglen = iframe->frame.hd.length - 8;
6318
6319
1.89k
        if (debuglen > 0) {
6320
595
          iframe->raw_lbuf = nghttp2_mem_malloc(mem, debuglen);
6321
6322
595
          if (iframe->raw_lbuf == NULL) {
6323
0
            return NGHTTP2_ERR_NOMEM;
6324
0
          }
6325
6326
595
          nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, debuglen);
6327
595
        }
6328
6329
1.89k
        busy = 1;
6330
6331
1.89k
        iframe->state = NGHTTP2_IB_READ_GOAWAY_DEBUG;
6332
6333
1.89k
        break;
6334
1.89k
      }
6335
1.43k
      case NGHTTP2_WINDOW_UPDATE:
6336
1.43k
        rv = session_process_window_update_frame(session);
6337
1.43k
        if (nghttp2_is_fatal(rv)) {
6338
0
          return rv;
6339
0
        }
6340
6341
1.43k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6342
142
          return (nghttp2_ssize)inlen;
6343
142
        }
6344
6345
1.29k
        session_inbound_frame_reset(session);
6346
6347
1.29k
        break;
6348
0
      case NGHTTP2_ALTSVC: {
6349
0
        size_t origin_len;
6350
6351
0
        origin_len = nghttp2_get_uint16(iframe->sbuf.pos);
6352
6353
0
        DEBUGF("recv: origin_len=%zu\n", origin_len);
6354
6355
0
        if (origin_len > iframe->payloadleft) {
6356
0
          busy = 1;
6357
0
          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
6358
0
          break;
6359
0
        }
6360
6361
0
        if (iframe->frame.hd.length > 2) {
6362
0
          iframe->raw_lbuf =
6363
0
            nghttp2_mem_malloc(mem, iframe->frame.hd.length - 2);
6364
6365
0
          if (iframe->raw_lbuf == NULL) {
6366
0
            return NGHTTP2_ERR_NOMEM;
6367
0
          }
6368
6369
0
          nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf,
6370
0
                                iframe->frame.hd.length);
6371
0
        }
6372
6373
0
        busy = 1;
6374
6375
0
        iframe->state = NGHTTP2_IB_READ_ALTSVC_PAYLOAD;
6376
6377
0
        break;
6378
0
      case NGHTTP2_PRIORITY_UPDATE:
6379
0
        DEBUGF("recv: prioritized_stream_id=%d\n",
6380
0
               nghttp2_get_uint32(iframe->sbuf.pos) & NGHTTP2_STREAM_ID_MASK);
6381
6382
0
        rv = session_process_priority_update_frame(session);
6383
0
        if (nghttp2_is_fatal(rv)) {
6384
0
          return rv;
6385
0
        }
6386
6387
0
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6388
0
          return (nghttp2_ssize)inlen;
6389
0
        }
6390
6391
0
        session_inbound_frame_reset(session);
6392
6393
0
        break;
6394
0
      }
6395
0
      default:
6396
        /* This is unknown frame */
6397
0
        session_inbound_frame_reset(session);
6398
6399
0
        break;
6400
78.6k
      }
6401
78.2k
      break;
6402
78.2k
    case NGHTTP2_IB_READ_HEADER_BLOCK:
6403
154k
    case NGHTTP2_IB_IGN_HEADER_BLOCK: {
6404
154k
      nghttp2_ssize data_readlen;
6405
154k
      size_t trail_padlen;
6406
154k
      int final;
6407
#ifdef DEBUGBUILD
6408
      if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {
6409
        DEBUGF("recv: [IB_READ_HEADER_BLOCK]\n");
6410
      } else {
6411
        DEBUGF("recv: [IB_IGN_HEADER_BLOCK]\n");
6412
      }
6413
#endif /* defined(DEBUGBUILD) */
6414
6415
154k
      readlen = inbound_frame_payload_readlen(iframe, in, last);
6416
6417
154k
      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6418
154k
             iframe->payloadleft - readlen);
6419
6420
154k
      data_readlen = inbound_frame_effective_readlen(
6421
154k
        iframe, iframe->payloadleft - readlen, readlen);
6422
6423
154k
      if (data_readlen == -1) {
6424
        /* everything is padding */
6425
0
        data_readlen = 0;
6426
0
      }
6427
6428
154k
      trail_padlen = nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen);
6429
6430
154k
      final = (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) &&
6431
143k
              iframe->payloadleft - (size_t)data_readlen == trail_padlen;
6432
6433
154k
      if (data_readlen > 0 || (data_readlen == 0 && final)) {
6434
152k
        size_t hd_proclen = 0;
6435
6436
152k
        DEBUGF("recv: block final=%d\n", final);
6437
6438
152k
        rv =
6439
152k
          inflate_header_block(session, &iframe->frame, &hd_proclen,
6440
152k
                               (uint8_t *)in, (size_t)data_readlen, final,
6441
152k
                               iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK);
6442
6443
152k
        if (nghttp2_is_fatal(rv)) {
6444
0
          return rv;
6445
0
        }
6446
6447
152k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6448
2.27k
          return (nghttp2_ssize)inlen;
6449
2.27k
        }
6450
6451
149k
        if (rv == NGHTTP2_ERR_PAUSE) {
6452
0
          in += hd_proclen;
6453
0
          iframe->payloadleft -= hd_proclen;
6454
6455
0
          return (nghttp2_ssize)(in - first);
6456
0
        }
6457
6458
149k
        if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
6459
          /* The application says no more headers. We decompress the
6460
             rest of the header block but not invoke on_header_callback
6461
             and on_frame_recv_callback. */
6462
7.79k
          in += hd_proclen;
6463
7.79k
          iframe->payloadleft -= hd_proclen;
6464
6465
          /* Use promised stream ID for PUSH_PROMISE */
6466
7.79k
          rv = session_handle_invalid_stream2(
6467
7.79k
            session,
6468
7.79k
            iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE
6469
7.79k
              ? iframe->frame.push_promise.promised_stream_id
6470
7.79k
              : iframe->frame.hd.stream_id,
6471
7.79k
            NULL, NGHTTP2_ERR_INTERNAL);
6472
7.79k
          if (nghttp2_is_fatal(rv)) {
6473
0
            return rv;
6474
0
          }
6475
7.79k
          busy = 1;
6476
7.79k
          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
6477
7.79k
          break;
6478
7.79k
        }
6479
6480
142k
        in += readlen;
6481
142k
        iframe->payloadleft -= readlen;
6482
6483
142k
        if (rv == NGHTTP2_ERR_HEADER_COMP) {
6484
          /* GOAWAY is already issued */
6485
0
          if (iframe->payloadleft == 0) {
6486
0
            session_inbound_frame_reset(session);
6487
0
          } else {
6488
0
            busy = 1;
6489
0
            iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
6490
0
          }
6491
0
          break;
6492
0
        }
6493
142k
      } else {
6494
2.40k
        in += readlen;
6495
2.40k
        iframe->payloadleft -= readlen;
6496
2.40k
      }
6497
6498
144k
      if (iframe->payloadleft) {
6499
8.17k
        break;
6500
8.17k
      }
6501
6502
136k
      if ((iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) {
6503
3.89k
        inbound_frame_set_mark(iframe, NGHTTP2_FRAME_HDLEN);
6504
6505
3.89k
        iframe->padlen = 0;
6506
6507
3.89k
        if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {
6508
2.00k
          iframe->state = NGHTTP2_IB_EXPECT_CONTINUATION;
6509
2.00k
        } else {
6510
1.88k
          iframe->state = NGHTTP2_IB_IGN_CONTINUATION;
6511
1.88k
        }
6512
132k
      } else {
6513
132k
        if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {
6514
15.5k
          rv = session_after_header_block_received(session);
6515
15.5k
          if (nghttp2_is_fatal(rv)) {
6516
0
            return rv;
6517
0
          }
6518
6519
15.5k
          if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6520
1
            return (nghttp2_ssize)inlen;
6521
1
          }
6522
15.5k
        }
6523
132k
        session_inbound_frame_reset(session);
6524
6525
132k
        session->num_continuations = 0;
6526
132k
      }
6527
136k
      break;
6528
136k
    }
6529
136k
    case NGHTTP2_IB_IGN_PAYLOAD:
6530
77.9k
      DEBUGF("recv: [IB_IGN_PAYLOAD]\n");
6531
6532
77.9k
      readlen = inbound_frame_payload_readlen(iframe, in, last);
6533
77.9k
      iframe->payloadleft -= readlen;
6534
77.9k
      in += readlen;
6535
6536
77.9k
      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6537
77.9k
             iframe->payloadleft);
6538
6539
77.9k
      if (iframe->payloadleft) {
6540
71
        break;
6541
71
      }
6542
6543
77.8k
      switch (iframe->frame.hd.type) {
6544
0
      case NGHTTP2_HEADERS:
6545
0
      case NGHTTP2_PUSH_PROMISE:
6546
0
      case NGHTTP2_CONTINUATION:
6547
        /* Mark inflater bad so that we won't perform further decoding */
6548
0
        session->hd_inflater.ctx.bad = 1;
6549
0
        break;
6550
77.8k
      default:
6551
77.8k
        break;
6552
77.8k
      }
6553
6554
77.8k
      session_inbound_frame_reset(session);
6555
6556
77.8k
      break;
6557
172
    case NGHTTP2_IB_FRAME_SIZE_ERROR:
6558
172
      DEBUGF("recv: [IB_FRAME_SIZE_ERROR]\n");
6559
6560
172
      rv = session_handle_frame_size_error(session);
6561
172
      if (nghttp2_is_fatal(rv)) {
6562
0
        return rv;
6563
0
      }
6564
6565
172
      assert(iframe->state == NGHTTP2_IB_IGN_ALL);
6566
6567
172
      return (nghttp2_ssize)inlen;
6568
51.0k
    case NGHTTP2_IB_READ_SETTINGS:
6569
51.0k
      DEBUGF("recv: [IB_READ_SETTINGS]\n");
6570
6571
51.0k
      readlen = inbound_frame_buf_read(iframe, in, last);
6572
51.0k
      iframe->payloadleft -= readlen;
6573
51.0k
      in += readlen;
6574
6575
51.0k
      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6576
51.0k
             iframe->payloadleft);
6577
6578
51.0k
      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
6579
12
        break;
6580
12
      }
6581
6582
50.9k
      if (readlen > 0) {
6583
23.0k
        inbound_frame_set_settings_entry(iframe);
6584
23.0k
      }
6585
50.9k
      if (iframe->payloadleft) {
6586
15.9k
        inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH);
6587
15.9k
        break;
6588
15.9k
      }
6589
6590
35.0k
      rv = session_process_settings_frame(session);
6591
6592
35.0k
      if (nghttp2_is_fatal(rv)) {
6593
0
        return rv;
6594
0
      }
6595
6596
35.0k
      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6597
681
        return (nghttp2_ssize)inlen;
6598
681
      }
6599
6600
34.3k
      session_inbound_frame_reset(session);
6601
6602
34.3k
      break;
6603
1.89k
    case NGHTTP2_IB_READ_GOAWAY_DEBUG:
6604
1.89k
      DEBUGF("recv: [IB_READ_GOAWAY_DEBUG]\n");
6605
6606
1.89k
      readlen = inbound_frame_payload_readlen(iframe, in, last);
6607
6608
1.89k
      if (readlen > 0) {
6609
567
        iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
6610
6611
567
        iframe->payloadleft -= readlen;
6612
567
        in += readlen;
6613
567
      }
6614
6615
1.89k
      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6616
1.89k
             iframe->payloadleft);
6617
6618
1.89k
      if (iframe->payloadleft) {
6619
51
        assert(nghttp2_buf_avail(&iframe->lbuf) > 0);
6620
6621
51
        break;
6622
51
      }
6623
6624
1.84k
      rv = session_process_goaway_frame(session);
6625
6626
1.84k
      if (nghttp2_is_fatal(rv)) {
6627
0
        return rv;
6628
0
      }
6629
6630
1.84k
      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6631
44
        return (nghttp2_ssize)inlen;
6632
44
      }
6633
6634
1.79k
      session_inbound_frame_reset(session);
6635
6636
1.79k
      break;
6637
1.79k
    case NGHTTP2_IB_EXPECT_CONTINUATION:
6638
3.65k
    case NGHTTP2_IB_IGN_CONTINUATION:
6639
#ifdef DEBUGBUILD
6640
      if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) {
6641
        fprintf(stderr, "recv: [IB_EXPECT_CONTINUATION]\n");
6642
      } else {
6643
        fprintf(stderr, "recv: [IB_IGN_CONTINUATION]\n");
6644
      }
6645
#endif /* defined(DEBUGBUILD) */
6646
6647
3.65k
      readlen = inbound_frame_buf_read(iframe, in, last);
6648
3.65k
      in += readlen;
6649
6650
3.65k
      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
6651
42
        return (nghttp2_ssize)(in - first);
6652
42
      }
6653
6654
3.61k
      nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->sbuf.pos);
6655
3.61k
      iframe->payloadleft = cont_hd.length;
6656
6657
3.61k
      DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n",
6658
3.61k
             cont_hd.length, cont_hd.type, cont_hd.flags, cont_hd.stream_id);
6659
6660
3.61k
      if (cont_hd.type != NGHTTP2_CONTINUATION ||
6661
3.56k
          cont_hd.stream_id != iframe->frame.hd.stream_id) {
6662
143
        DEBUGF("recv: expected stream_id=%d, type=%d, but got stream_id=%d, "
6663
143
               "type=%u\n",
6664
143
               iframe->frame.hd.stream_id, NGHTTP2_CONTINUATION,
6665
143
               cont_hd.stream_id, cont_hd.type);
6666
143
        rv = nghttp2_session_terminate_session_with_reason(
6667
143
          session, NGHTTP2_PROTOCOL_ERROR,
6668
143
          "unexpected non-CONTINUATION frame or stream_id is invalid");
6669
143
        if (nghttp2_is_fatal(rv)) {
6670
0
          return rv;
6671
0
        }
6672
6673
143
        return (nghttp2_ssize)inlen;
6674
143
      }
6675
6676
3.46k
      if (++session->num_continuations > session->max_continuations) {
6677
2
        return NGHTTP2_ERR_TOO_MANY_CONTINUATIONS;
6678
2
      }
6679
6680
3.46k
      if (cont_hd.length > session->local_settings.max_frame_size) {
6681
99
        DEBUGF("recv: length is too large %zu > %u\n", cont_hd.length,
6682
99
               session->local_settings.max_frame_size);
6683
99
        rv = nghttp2_session_terminate_session_with_reason(
6684
99
          session, NGHTTP2_FRAME_SIZE_ERROR, "too large frame size");
6685
6686
99
        if (nghttp2_is_fatal(rv)) {
6687
0
          return rv;
6688
0
        }
6689
6690
99
        return (nghttp2_ssize)inlen;
6691
99
      }
6692
6693
      /* CONTINUATION won't bear NGHTTP2_PADDED flag */
6694
6695
3.36k
      iframe->frame.hd.flags =
6696
3.36k
        (uint8_t)(iframe->frame.hd.flags |
6697
3.36k
                  (cont_hd.flags & NGHTTP2_FLAG_END_HEADERS));
6698
3.36k
      iframe->frame.hd.length += cont_hd.length;
6699
6700
3.36k
      busy = 1;
6701
6702
3.36k
      if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) {
6703
1.55k
        iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
6704
6705
1.55k
        rv = session_call_on_begin_frame(session, &cont_hd);
6706
6707
1.55k
        if (nghttp2_is_fatal(rv)) {
6708
0
          return rv;
6709
0
        }
6710
6711
1.55k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6712
0
          return (nghttp2_ssize)inlen;
6713
0
        }
6714
1.81k
      } else {
6715
1.81k
        iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
6716
1.81k
      }
6717
6718
3.36k
      break;
6719
4.80k
    case NGHTTP2_IB_READ_PAD_DATA:
6720
4.80k
      DEBUGF("recv: [IB_READ_PAD_DATA]\n");
6721
6722
4.80k
      readlen = inbound_frame_buf_read(iframe, in, last);
6723
4.80k
      in += readlen;
6724
4.80k
      iframe->payloadleft -= readlen;
6725
6726
4.80k
      DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zu\n", readlen,
6727
4.80k
             iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf));
6728
6729
4.80k
      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
6730
12
        return (nghttp2_ssize)(in - first);
6731
12
      }
6732
6733
      /* Pad Length field is subject to flow control */
6734
4.79k
      rv = nghttp2_session_update_recv_connection_window_size(session, readlen);
6735
4.79k
      if (nghttp2_is_fatal(rv)) {
6736
0
        return rv;
6737
0
      }
6738
6739
4.79k
      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6740
1
        return (nghttp2_ssize)inlen;
6741
1
      }
6742
6743
      /* Pad Length field is consumed immediately */
6744
4.79k
      rv =
6745
4.79k
        nghttp2_session_consume(session, iframe->frame.hd.stream_id, readlen);
6746
6747
4.79k
      if (nghttp2_is_fatal(rv)) {
6748
0
        return rv;
6749
0
      }
6750
6751
4.79k
      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6752
0
        return (nghttp2_ssize)inlen;
6753
0
      }
6754
6755
4.79k
      stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id);
6756
4.79k
      if (stream) {
6757
4.79k
        rv = nghttp2_session_update_recv_stream_window_size(
6758
4.79k
          session, stream, readlen,
6759
4.79k
          iframe->payloadleft ||
6760
2.59k
            (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0);
6761
4.79k
        if (nghttp2_is_fatal(rv)) {
6762
0
          return rv;
6763
0
        }
6764
6765
4.79k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6766
1
          return (nghttp2_ssize)inlen;
6767
1
        }
6768
4.79k
      }
6769
6770
4.78k
      busy = 1;
6771
6772
4.78k
      padlen = inbound_frame_compute_pad(iframe);
6773
4.78k
      if (padlen < 0) {
6774
18
        rv = nghttp2_session_terminate_session_with_reason(
6775
18
          session, NGHTTP2_PROTOCOL_ERROR, "DATA: invalid padding");
6776
18
        if (nghttp2_is_fatal(rv)) {
6777
0
          return rv;
6778
0
        }
6779
18
        return (nghttp2_ssize)inlen;
6780
18
      }
6781
6782
4.77k
      iframe->frame.data.padlen = (size_t)padlen;
6783
6784
      /* Empty DATA frame without END_STREAM flag set is
6785
         suspicious. */
6786
4.77k
      if (iframe->payloadleft == 0 &&
6787
2.58k
          (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
6788
2.57k
        rv = session_update_glitch_ratelim(session);
6789
2.57k
        if (rv != 0) {
6790
0
          return rv;
6791
0
        }
6792
6793
2.57k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6794
0
          return (nghttp2_ssize)inlen;
6795
0
        }
6796
2.57k
      }
6797
6798
4.77k
      iframe->state = NGHTTP2_IB_READ_DATA;
6799
6800
4.77k
      break;
6801
7.08k
    case NGHTTP2_IB_READ_DATA:
6802
7.08k
      stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id);
6803
6804
7.08k
      if (!stream) {
6805
0
        busy = 1;
6806
0
        iframe->state = NGHTTP2_IB_IGN_DATA;
6807
0
        break;
6808
0
      }
6809
6810
7.08k
      DEBUGF("recv: [IB_READ_DATA]\n");
6811
6812
7.08k
      readlen = inbound_frame_payload_readlen(iframe, in, last);
6813
7.08k
      iframe->payloadleft -= readlen;
6814
7.08k
      in += readlen;
6815
6816
7.08k
      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6817
7.08k
             iframe->payloadleft);
6818
6819
7.08k
      if (readlen > 0) {
6820
4.01k
        nghttp2_ssize data_readlen;
6821
6822
4.01k
        rv =
6823
4.01k
          nghttp2_session_update_recv_connection_window_size(session, readlen);
6824
4.01k
        if (nghttp2_is_fatal(rv)) {
6825
0
          return rv;
6826
0
        }
6827
6828
4.01k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6829
3
          return (nghttp2_ssize)inlen;
6830
3
        }
6831
6832
4.00k
        rv = nghttp2_session_update_recv_stream_window_size(
6833
4.00k
          session, stream, readlen,
6834
4.00k
          iframe->payloadleft ||
6835
3.74k
            (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0);
6836
4.00k
        if (nghttp2_is_fatal(rv)) {
6837
0
          return rv;
6838
0
        }
6839
6840
4.00k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6841
8
          return (nghttp2_ssize)inlen;
6842
8
        }
6843
6844
3.99k
        data_readlen =
6845
3.99k
          inbound_frame_effective_readlen(iframe, iframe->payloadleft, readlen);
6846
6847
3.99k
        if (data_readlen == -1) {
6848
          /* everything is padding */
6849
0
          data_readlen = 0;
6850
0
        }
6851
6852
3.99k
        padlen = (nghttp2_ssize)readlen - data_readlen;
6853
6854
3.99k
        if (padlen > 0) {
6855
          /* Padding is considered as "consumed" immediately */
6856
1.96k
          rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id,
6857
1.96k
                                       (size_t)padlen);
6858
6859
1.96k
          if (nghttp2_is_fatal(rv)) {
6860
0
            return rv;
6861
0
          }
6862
6863
1.96k
          if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6864
0
            return (nghttp2_ssize)inlen;
6865
0
          }
6866
1.96k
        }
6867
6868
3.99k
        DEBUGF("recv: data_readlen=%td\n", data_readlen);
6869
6870
3.99k
        if (data_readlen > 0) {
6871
3.39k
          if (session_enforce_http_messaging(session)) {
6872
3.39k
            if (nghttp2_http_on_data_chunk(stream, (size_t)data_readlen) != 0) {
6873
37
              if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
6874
                /* Consume all data for connection immediately here */
6875
0
                rv = session_update_connection_consumed_size(
6876
0
                  session, (size_t)data_readlen);
6877
6878
0
                if (nghttp2_is_fatal(rv)) {
6879
0
                  return rv;
6880
0
                }
6881
6882
0
                if (iframe->state == NGHTTP2_IB_IGN_DATA) {
6883
0
                  return (nghttp2_ssize)inlen;
6884
0
                }
6885
0
              }
6886
6887
37
              rv = session_handle_invalid_stream2(
6888
37
                session, iframe->frame.hd.stream_id, &iframe->frame,
6889
37
                NGHTTP2_ERR_PROTO);
6890
37
              if (nghttp2_is_fatal(rv)) {
6891
0
                return rv;
6892
0
              }
6893
6894
37
              rv = session_update_glitch_ratelim(session);
6895
37
              if (rv != 0) {
6896
0
                return rv;
6897
0
              }
6898
6899
37
              if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6900
0
                return (nghttp2_ssize)inlen;
6901
0
              }
6902
6903
37
              busy = 1;
6904
37
              iframe->state = NGHTTP2_IB_IGN_DATA;
6905
6906
37
              break;
6907
37
            }
6908
3.39k
          }
6909
3.36k
          if (session->callbacks.on_data_chunk_recv_callback) {
6910
0
            rv = session->callbacks.on_data_chunk_recv_callback(
6911
0
              session, iframe->frame.hd.flags, iframe->frame.hd.stream_id,
6912
0
              in - readlen, (size_t)data_readlen, session->user_data);
6913
0
            if (nghttp2_is_fatal(rv)) {
6914
0
              return NGHTTP2_ERR_CALLBACK_FAILURE;
6915
0
            }
6916
6917
0
            if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6918
0
              return (nghttp2_ssize)inlen;
6919
0
            }
6920
6921
0
            if (rv == NGHTTP2_ERR_PAUSE) {
6922
0
              return (nghttp2_ssize)(in - first);
6923
0
            }
6924
0
          }
6925
3.36k
        }
6926
3.99k
      }
6927
6928
7.03k
      if (iframe->payloadleft) {
6929
327
        break;
6930
327
      }
6931
6932
6.70k
      rv = session_process_data_frame(session);
6933
6.70k
      if (nghttp2_is_fatal(rv)) {
6934
0
        return rv;
6935
0
      }
6936
6937
6.70k
      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6938
0
        return (nghttp2_ssize)inlen;
6939
0
      }
6940
6941
6.70k
      session_inbound_frame_reset(session);
6942
6943
6.70k
      break;
6944
14.9k
    case NGHTTP2_IB_IGN_DATA:
6945
14.9k
      DEBUGF("recv: [IB_IGN_DATA]\n");
6946
6947
14.9k
      readlen = inbound_frame_payload_readlen(iframe, in, last);
6948
14.9k
      iframe->payloadleft -= readlen;
6949
14.9k
      in += readlen;
6950
6951
14.9k
      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6952
14.9k
             iframe->payloadleft);
6953
6954
14.9k
      if (readlen > 0) {
6955
        /* Update connection-level flow control window for ignored
6956
           DATA frame too */
6957
8.84k
        rv =
6958
8.84k
          nghttp2_session_update_recv_connection_window_size(session, readlen);
6959
8.84k
        if (nghttp2_is_fatal(rv)) {
6960
0
          return rv;
6961
0
        }
6962
6963
8.84k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6964
11
          return (nghttp2_ssize)inlen;
6965
11
        }
6966
6967
8.83k
        if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
6968
          /* Ignored DATA is considered as "consumed" immediately. */
6969
0
          rv = session_update_connection_consumed_size(session, readlen);
6970
6971
0
          if (nghttp2_is_fatal(rv)) {
6972
0
            return rv;
6973
0
          }
6974
6975
0
          if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6976
0
            return (nghttp2_ssize)inlen;
6977
0
          }
6978
0
        }
6979
8.83k
      }
6980
6981
14.9k
      if (iframe->payloadleft) {
6982
121
        break;
6983
121
      }
6984
6985
14.7k
      session_inbound_frame_reset(session);
6986
6987
14.7k
      break;
6988
0
    case NGHTTP2_IB_IGN_ALL:
6989
0
      return (nghttp2_ssize)inlen;
6990
0
    case NGHTTP2_IB_READ_EXTENSION_PAYLOAD:
6991
0
      DEBUGF("recv: [IB_READ_EXTENSION_PAYLOAD]\n");
6992
6993
0
      readlen = inbound_frame_payload_readlen(iframe, in, last);
6994
0
      iframe->payloadleft -= readlen;
6995
0
      in += readlen;
6996
6997
0
      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6998
0
             iframe->payloadleft);
6999
7000
0
      if (readlen > 0) {
7001
0
        rv = session_call_on_extension_chunk_recv_callback(
7002
0
          session, in - readlen, readlen);
7003
0
        if (nghttp2_is_fatal(rv)) {
7004
0
          return rv;
7005
0
        }
7006
7007
0
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
7008
0
          return (nghttp2_ssize)inlen;
7009
0
        }
7010
7011
0
        if (rv != 0) {
7012
0
          busy = 1;
7013
7014
0
          iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
7015
7016
0
          break;
7017
0
        }
7018
0
      }
7019
7020
0
      if (iframe->payloadleft > 0) {
7021
0
        break;
7022
0
      }
7023
7024
0
      rv = session_process_extension_frame(session);
7025
0
      if (nghttp2_is_fatal(rv)) {
7026
0
        return rv;
7027
0
      }
7028
7029
0
      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
7030
0
        return (nghttp2_ssize)inlen;
7031
0
      }
7032
7033
0
      session_inbound_frame_reset(session);
7034
7035
0
      break;
7036
0
    case NGHTTP2_IB_READ_ALTSVC_PAYLOAD:
7037
0
      DEBUGF("recv: [IB_READ_ALTSVC_PAYLOAD]\n");
7038
7039
0
      readlen = inbound_frame_payload_readlen(iframe, in, last);
7040
0
      if (readlen > 0) {
7041
0
        iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
7042
7043
0
        iframe->payloadleft -= readlen;
7044
0
        in += readlen;
7045
0
      }
7046
7047
0
      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
7048
0
             iframe->payloadleft);
7049
7050
0
      if (iframe->payloadleft) {
7051
0
        assert(nghttp2_buf_avail(&iframe->lbuf) > 0);
7052
7053
0
        break;
7054
0
      }
7055
7056
0
      rv = session_process_altsvc_frame(session);
7057
0
      if (nghttp2_is_fatal(rv)) {
7058
0
        return rv;
7059
0
      }
7060
7061
0
      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
7062
0
        return (nghttp2_ssize)inlen;
7063
0
      }
7064
7065
0
      session_inbound_frame_reset(session);
7066
7067
0
      break;
7068
0
    case NGHTTP2_IB_READ_ORIGIN_PAYLOAD:
7069
0
      DEBUGF("recv: [IB_READ_ORIGIN_PAYLOAD]\n");
7070
7071
0
      readlen = inbound_frame_payload_readlen(iframe, in, last);
7072
7073
0
      if (readlen > 0) {
7074
0
        iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
7075
7076
0
        iframe->payloadleft -= readlen;
7077
0
        in += readlen;
7078
0
      }
7079
7080
0
      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
7081
0
             iframe->payloadleft);
7082
7083
0
      if (iframe->payloadleft) {
7084
0
        assert(nghttp2_buf_avail(&iframe->lbuf) > 0);
7085
7086
0
        break;
7087
0
      }
7088
7089
0
      rv = session_process_origin_frame(session);
7090
7091
0
      if (nghttp2_is_fatal(rv)) {
7092
0
        return rv;
7093
0
      }
7094
7095
0
      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
7096
0
        return (nghttp2_ssize)inlen;
7097
0
      }
7098
7099
0
      session_inbound_frame_reset(session);
7100
7101
0
      break;
7102
765k
    }
7103
7104
760k
    if (!busy && in == last) {
7105
13.6k
      break;
7106
13.6k
    }
7107
7108
746k
    busy = 0;
7109
746k
  }
7110
7111
18.6k
  assert(in == last);
7112
7113
13.6k
  return (nghttp2_ssize)(in - first);
7114
13.6k
}
7115
7116
nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session,
7117
18.6k
                                        const uint8_t *in, size_t inlen) {
7118
18.6k
  nghttp2_ssize nread;
7119
7120
18.6k
  nread = session_mem_recv(session, in, inlen);
7121
18.6k
  if (nread < 0 && nghttp2_is_fatal((int)nread)) {
7122
348
    return nread;
7123
348
  }
7124
7125
18.3k
  if (nghttp2_session_get_outbound_queue_size(session) >
7126
18.3k
      session->max_outbound_queue_size) {
7127
0
    return NGHTTP2_ERR_FLOODED;
7128
0
  }
7129
7130
18.3k
  return nread;
7131
18.3k
}
7132
7133
0
int nghttp2_session_recv(nghttp2_session *session) {
7134
0
  uint8_t buf[NGHTTP2_INBOUND_BUFFER_LENGTH];
7135
0
  while (1) {
7136
0
    nghttp2_ssize readlen;
7137
0
    readlen = session_recv(session, buf, sizeof(buf));
7138
0
    if (readlen > 0) {
7139
0
      nghttp2_ssize proclen =
7140
0
        nghttp2_session_mem_recv2(session, buf, (size_t)readlen);
7141
0
      if (proclen < 0) {
7142
0
        return (int)proclen;
7143
0
      }
7144
0
      assert(proclen == readlen);
7145
0
    } else if (readlen == 0 || readlen == NGHTTP2_ERR_WOULDBLOCK) {
7146
0
      return 0;
7147
0
    } else if (readlen == NGHTTP2_ERR_EOF) {
7148
0
      return NGHTTP2_ERR_EOF;
7149
0
    } else if (readlen < 0) {
7150
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
7151
0
    }
7152
0
  }
7153
0
}
7154
7155
/*
7156
 * Returns the number of active streams, which includes streams in
7157
 * reserved state.
7158
 */
7159
97.4k
static size_t session_get_num_active_streams(nghttp2_session *session) {
7160
97.4k
  return nghttp2_map_size(&session->streams) - session->num_closed_streams -
7161
97.4k
         session->num_idle_streams;
7162
97.4k
}
7163
7164
97.4k
int nghttp2_session_want_read(nghttp2_session *session) {
7165
97.4k
  size_t num_active_streams;
7166
7167
  /* If this flag is set, we don't want to read. The application
7168
     should drop the connection. */
7169
97.4k
  if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) {
7170
0
    return 0;
7171
0
  }
7172
7173
97.4k
  num_active_streams = session_get_num_active_streams(session);
7174
7175
  /* Unless termination GOAWAY is sent or received, we always want to
7176
     read incoming frames. */
7177
7178
97.4k
  if (num_active_streams > 0) {
7179
37.9k
    return 1;
7180
37.9k
  }
7181
7182
  /* If there is no active streams and GOAWAY has been sent or
7183
     received, we are done with this session. */
7184
59.5k
  return (session->goaway_flags &
7185
59.5k
          (NGHTTP2_GOAWAY_SENT | NGHTTP2_GOAWAY_RECV)) == 0;
7186
97.4k
}
7187
7188
7.57k
int nghttp2_session_want_write(nghttp2_session *session) {
7189
  /* If these flag is set, we don't want to write any data. The
7190
     application should drop the connection. */
7191
7.57k
  if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) {
7192
0
    return 0;
7193
0
  }
7194
7195
  /*
7196
   * Unless termination GOAWAY is sent or received, we want to write
7197
   * frames if there is pending ones. If pending frame is request/push
7198
   * response HEADERS and concurrent stream limit is reached, we don't
7199
   * want to write them.
7200
   */
7201
7.57k
  return session->aob.item || nghttp2_outbound_queue_top(&session->ob_urgent) ||
7202
364
         nghttp2_outbound_queue_top(&session->ob_reg) ||
7203
197
         (!session_sched_empty(session) && session->remote_window_size > 0) ||
7204
197
         (nghttp2_outbound_queue_top(&session->ob_syn) &&
7205
0
          !session_is_outgoing_concurrent_streams_max(session));
7206
7.57k
}
7207
7208
int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
7209
1.38k
                             const uint8_t *opaque_data) {
7210
1.38k
  int rv;
7211
1.38k
  nghttp2_outbound_item *item;
7212
1.38k
  nghttp2_frame *frame;
7213
1.38k
  nghttp2_mem *mem;
7214
7215
1.38k
  mem = &session->mem;
7216
7217
1.38k
  if ((flags & NGHTTP2_FLAG_ACK) &&
7218
1.38k
      session->obq_flood_counter_ >= session->max_outbound_ack) {
7219
1
    return NGHTTP2_ERR_FLOODED;
7220
1
  }
7221
7222
1.38k
  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
7223
1.38k
  if (item == NULL) {
7224
0
    return NGHTTP2_ERR_NOMEM;
7225
0
  }
7226
7227
1.38k
  nghttp2_outbound_item_init(item);
7228
7229
1.38k
  frame = &item->frame;
7230
7231
1.38k
  nghttp2_frame_ping_init(&frame->ping, flags, opaque_data);
7232
7233
1.38k
  rv = nghttp2_session_add_item(session, item);
7234
7235
1.38k
  if (rv != 0) {
7236
0
    nghttp2_frame_ping_free(&frame->ping);
7237
0
    nghttp2_mem_free(mem, item);
7238
0
    return rv;
7239
0
  }
7240
7241
1.38k
  if (flags & NGHTTP2_FLAG_ACK) {
7242
1.38k
    ++session->obq_flood_counter_;
7243
1.38k
  }
7244
7245
1.38k
  return 0;
7246
1.38k
}
7247
7248
int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id,
7249
                               uint32_t error_code, const uint8_t *opaque_data,
7250
4.42k
                               size_t opaque_data_len, uint8_t aux_flags) {
7251
4.42k
  int rv;
7252
4.42k
  nghttp2_outbound_item *item;
7253
4.42k
  nghttp2_frame *frame;
7254
4.42k
  uint8_t *opaque_data_copy = NULL;
7255
4.42k
  nghttp2_goaway_aux_data *aux_data;
7256
4.42k
  nghttp2_mem *mem;
7257
7258
4.42k
  mem = &session->mem;
7259
7260
4.42k
  if (nghttp2_session_is_my_stream_id(session, last_stream_id)) {
7261
0
    return NGHTTP2_ERR_INVALID_ARGUMENT;
7262
0
  }
7263
7264
4.42k
  if (opaque_data_len) {
7265
1.87k
    if (opaque_data_len + 8 > NGHTTP2_MAX_PAYLOADLEN) {
7266
0
      return NGHTTP2_ERR_INVALID_ARGUMENT;
7267
0
    }
7268
1.87k
    opaque_data_copy = nghttp2_mem_malloc(mem, opaque_data_len);
7269
1.87k
    if (opaque_data_copy == NULL) {
7270
0
      return NGHTTP2_ERR_NOMEM;
7271
0
    }
7272
1.87k
    memcpy(opaque_data_copy, opaque_data, opaque_data_len);
7273
1.87k
  }
7274
7275
4.42k
  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
7276
4.42k
  if (item == NULL) {
7277
0
    nghttp2_mem_free(mem, opaque_data_copy);
7278
0
    return NGHTTP2_ERR_NOMEM;
7279
0
  }
7280
7281
4.42k
  nghttp2_outbound_item_init(item);
7282
7283
4.42k
  frame = &item->frame;
7284
7285
  /* last_stream_id must not be increased from the value previously
7286
     sent */
7287
4.42k
  last_stream_id =
7288
4.42k
    nghttp2_min_int32(last_stream_id, session->local_last_stream_id);
7289
7290
4.42k
  nghttp2_frame_goaway_init(&frame->goaway, last_stream_id, error_code,
7291
4.42k
                            opaque_data_copy, opaque_data_len);
7292
7293
4.42k
  aux_data = &item->aux_data.goaway;
7294
4.42k
  aux_data->flags = aux_flags;
7295
7296
4.42k
  rv = nghttp2_session_add_item(session, item);
7297
4.42k
  if (rv != 0) {
7298
0
    nghttp2_frame_goaway_free(&frame->goaway, mem);
7299
0
    nghttp2_mem_free(mem, item);
7300
0
    return rv;
7301
0
  }
7302
7303
4.42k
  session->goaway_flags |= NGHTTP2_GOAWAY_SUBMITTED;
7304
7305
4.42k
  return 0;
7306
4.42k
}
7307
7308
int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags,
7309
                                      int32_t stream_id,
7310
165
                                      int32_t window_size_increment) {
7311
165
  int rv;
7312
165
  nghttp2_outbound_item *item;
7313
165
  nghttp2_frame *frame;
7314
165
  nghttp2_mem *mem;
7315
7316
165
  mem = &session->mem;
7317
165
  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
7318
165
  if (item == NULL) {
7319
0
    return NGHTTP2_ERR_NOMEM;
7320
0
  }
7321
7322
165
  nghttp2_outbound_item_init(item);
7323
7324
165
  frame = &item->frame;
7325
7326
165
  nghttp2_frame_window_update_init(&frame->window_update, flags, stream_id,
7327
165
                                   window_size_increment);
7328
7329
165
  rv = nghttp2_session_add_item(session, item);
7330
7331
165
  if (rv != 0) {
7332
0
    nghttp2_frame_window_update_free(&frame->window_update);
7333
0
    nghttp2_mem_free(mem, item);
7334
0
    return rv;
7335
0
  }
7336
165
  return 0;
7337
165
}
7338
7339
static void
7340
session_append_inflight_settings(nghttp2_session *session,
7341
18.5k
                                 nghttp2_inflight_settings *settings) {
7342
18.5k
  nghttp2_inflight_settings **i;
7343
7344
18.5k
  for (i = &session->inflight_settings_head; *i; i = &(*i)->next)
7345
0
    ;
7346
7347
18.5k
  *i = settings;
7348
18.5k
}
7349
7350
int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
7351
52.6k
                                 const nghttp2_settings_entry *iv, size_t niv) {
7352
52.6k
  nghttp2_outbound_item *item;
7353
52.6k
  nghttp2_frame *frame;
7354
52.6k
  nghttp2_settings_entry *iv_copy;
7355
52.6k
  size_t i;
7356
52.6k
  int rv;
7357
52.6k
  nghttp2_mem *mem;
7358
52.6k
  nghttp2_inflight_settings *inflight_settings = NULL;
7359
52.6k
  uint8_t no_rfc7540_pri = session->pending_no_rfc7540_priorities;
7360
7361
52.6k
  mem = &session->mem;
7362
7363
52.6k
  if (flags & NGHTTP2_FLAG_ACK) {
7364
34.0k
    if (niv != 0) {
7365
0
      return NGHTTP2_ERR_INVALID_ARGUMENT;
7366
0
    }
7367
7368
34.0k
    if (session->obq_flood_counter_ >= session->max_outbound_ack) {
7369
0
      return NGHTTP2_ERR_FLOODED;
7370
0
    }
7371
34.0k
  }
7372
7373
52.6k
  if (!nghttp2_iv_check(iv, niv)) {
7374
138
    return NGHTTP2_ERR_INVALID_ARGUMENT;
7375
138
  }
7376
7377
72.4k
  for (i = 0; i < niv; ++i) {
7378
19.8k
    if (iv[i].settings_id != NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES) {
7379
19.7k
      continue;
7380
19.7k
    }
7381
7382
88
    if (no_rfc7540_pri == UINT8_MAX) {
7383
40
      no_rfc7540_pri = (uint8_t)iv[i].value;
7384
40
      continue;
7385
40
    }
7386
7387
48
    if (iv[i].value != (uint32_t)no_rfc7540_pri) {
7388
4
      return NGHTTP2_ERR_INVALID_ARGUMENT;
7389
4
    }
7390
48
  }
7391
7392
52.5k
  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
7393
52.5k
  if (item == NULL) {
7394
0
    return NGHTTP2_ERR_NOMEM;
7395
0
  }
7396
7397
52.5k
  if (niv > 0) {
7398
18.5k
    iv_copy = nghttp2_frame_iv_copy(iv, niv, mem);
7399
18.5k
    if (iv_copy == NULL) {
7400
0
      nghttp2_mem_free(mem, item);
7401
0
      return NGHTTP2_ERR_NOMEM;
7402
0
    }
7403
34.0k
  } else {
7404
34.0k
    iv_copy = NULL;
7405
34.0k
  }
7406
7407
52.5k
  if ((flags & NGHTTP2_FLAG_ACK) == 0) {
7408
18.5k
    rv = inflight_settings_new(&inflight_settings, iv, niv, mem);
7409
18.5k
    if (rv != 0) {
7410
0
      assert(nghttp2_is_fatal(rv));
7411
0
      nghttp2_mem_free(mem, iv_copy);
7412
0
      nghttp2_mem_free(mem, item);
7413
0
      return rv;
7414
0
    }
7415
18.5k
  }
7416
7417
52.5k
  nghttp2_outbound_item_init(item);
7418
7419
52.5k
  frame = &item->frame;
7420
7421
52.5k
  nghttp2_frame_settings_init(&frame->settings, flags, iv_copy, niv);
7422
52.5k
  rv = nghttp2_session_add_item(session, item);
7423
52.5k
  if (rv != 0) {
7424
    /* The only expected error is fatal one */
7425
0
    assert(nghttp2_is_fatal(rv));
7426
7427
0
    inflight_settings_del(inflight_settings, mem);
7428
7429
0
    nghttp2_frame_settings_free(&frame->settings, mem);
7430
0
    nghttp2_mem_free(mem, item);
7431
7432
0
    return rv;
7433
0
  }
7434
7435
52.5k
  if (flags & NGHTTP2_FLAG_ACK) {
7436
34.0k
    ++session->obq_flood_counter_;
7437
34.0k
  } else {
7438
18.5k
    session_append_inflight_settings(session, inflight_settings);
7439
18.5k
  }
7440
7441
  /* Extract NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS and ENABLE_PUSH
7442
     here.  We use it to refuse the incoming stream and PUSH_PROMISE
7443
     with RST_STREAM. */
7444
7445
63.1k
  for (i = niv; i > 0; --i) {
7446
19.8k
    if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) {
7447
9.22k
      session->pending_local_max_concurrent_stream = iv[i - 1].value;
7448
9.22k
      break;
7449
9.22k
    }
7450
19.8k
  }
7451
7452
72.3k
  for (i = niv; i > 0; --i) {
7453
19.8k
    if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_PUSH) {
7454
19
      session->pending_enable_push = (uint8_t)iv[i - 1].value;
7455
19
      break;
7456
19
    }
7457
19.8k
  }
7458
7459
72.2k
  for (i = niv; i > 0; --i) {
7460
19.8k
    if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL) {
7461
76
      session->pending_enable_connect_protocol = (uint8_t)iv[i - 1].value;
7462
76
      break;
7463
76
    }
7464
19.8k
  }
7465
7466
52.5k
  if (no_rfc7540_pri == UINT8_MAX) {
7467
18.5k
    session->pending_no_rfc7540_priorities = 0;
7468
33.9k
  } else {
7469
33.9k
    session->pending_no_rfc7540_priorities = no_rfc7540_pri;
7470
33.9k
  }
7471
7472
52.5k
  return 0;
7473
52.5k
}
7474
7475
int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
7476
                              size_t datamax, nghttp2_frame *frame,
7477
                              nghttp2_data_aux_data *aux_data,
7478
0
                              nghttp2_stream *stream) {
7479
0
  int rv;
7480
0
  uint32_t data_flags;
7481
0
  nghttp2_ssize payloadlen;
7482
0
  nghttp2_ssize padded_payloadlen;
7483
0
  nghttp2_buf *buf;
7484
0
  size_t max_payloadlen;
7485
7486
0
  assert(bufs->head == bufs->cur);
7487
7488
0
  buf = &bufs->cur->buf;
7489
7490
0
  if (session->callbacks.read_length_callback2 ||
7491
0
      session->callbacks.read_length_callback) {
7492
0
    if (session->callbacks.read_length_callback2) {
7493
0
      payloadlen = session->callbacks.read_length_callback2(
7494
0
        session, frame->hd.type, stream->stream_id, session->remote_window_size,
7495
0
        stream->remote_window_size, session->remote_settings.max_frame_size,
7496
0
        session->user_data);
7497
0
    } else {
7498
0
      payloadlen = (nghttp2_ssize)session->callbacks.read_length_callback(
7499
0
        session, frame->hd.type, stream->stream_id, session->remote_window_size,
7500
0
        stream->remote_window_size, session->remote_settings.max_frame_size,
7501
0
        session->user_data);
7502
0
    }
7503
7504
0
    DEBUGF("send: read_length_callback=%td\n", payloadlen);
7505
7506
0
    payloadlen =
7507
0
      nghttp2_session_enforce_flow_control_limits(session, stream, payloadlen);
7508
7509
0
    DEBUGF("send: read_length_callback after flow control=%td\n", payloadlen);
7510
7511
0
    if (payloadlen <= 0) {
7512
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
7513
0
    }
7514
7515
0
    if ((size_t)payloadlen > nghttp2_buf_avail(buf)) {
7516
      /* Resize the current buffer(s).  The reason why we do +1 for
7517
         buffer size is for possible padding field. */
7518
0
      rv = nghttp2_bufs_realloc(&session->aob.framebufs,
7519
0
                                (size_t)(NGHTTP2_FRAME_HDLEN + 1 + payloadlen));
7520
7521
0
      if (rv != 0) {
7522
0
        DEBUGF("send: realloc buffer failed rv=%d", rv);
7523
        /* If reallocation failed, old buffers are still in tact.  So
7524
           use safe limit. */
7525
0
        payloadlen = (nghttp2_ssize)datamax;
7526
7527
0
        DEBUGF("send: use safe limit payloadlen=%td", payloadlen);
7528
0
      } else {
7529
0
        assert(&session->aob.framebufs == bufs);
7530
7531
0
        buf = &bufs->cur->buf;
7532
0
      }
7533
0
    }
7534
0
    datamax = (size_t)payloadlen;
7535
0
  }
7536
7537
  /* Current max DATA length is less then buffer chunk size */
7538
0
  assert(nghttp2_buf_avail(buf) >= datamax);
7539
7540
0
  data_flags = NGHTTP2_DATA_FLAG_NONE;
7541
0
  switch (aux_data->dpw.version) {
7542
0
  case NGHTTP2_DATA_PROVIDER_V1:
7543
0
    payloadlen = (nghttp2_ssize)aux_data->dpw.data_prd.v1.read_callback(
7544
0
      session, frame->hd.stream_id, buf->pos, datamax, &data_flags,
7545
0
      &aux_data->dpw.data_prd.v1.source, session->user_data);
7546
7547
0
    break;
7548
0
  case NGHTTP2_DATA_PROVIDER_V2:
7549
0
    payloadlen = aux_data->dpw.data_prd.v2.read_callback(
7550
0
      session, frame->hd.stream_id, buf->pos, datamax, &data_flags,
7551
0
      &aux_data->dpw.data_prd.v2.source, session->user_data);
7552
7553
0
    break;
7554
0
  default:
7555
0
    assert(0);
7556
0
    abort();
7557
0
  }
7558
7559
0
  if (payloadlen == NGHTTP2_ERR_DEFERRED ||
7560
0
      payloadlen == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE ||
7561
0
      payloadlen == NGHTTP2_ERR_PAUSE) {
7562
0
    DEBUGF("send: DATA postponed due to %s\n",
7563
0
           nghttp2_strerror((int)payloadlen));
7564
7565
0
    return (int)payloadlen;
7566
0
  }
7567
7568
0
  if (payloadlen < 0 || datamax < (size_t)payloadlen) {
7569
    /* This is the error code when callback is failed. */
7570
0
    return NGHTTP2_ERR_CALLBACK_FAILURE;
7571
0
  }
7572
7573
0
  buf->last = buf->pos + payloadlen;
7574
0
  buf->pos -= NGHTTP2_FRAME_HDLEN;
7575
7576
  /* Clear flags, because this may contain previous flags of previous
7577
     DATA */
7578
0
  frame->hd.flags = NGHTTP2_FLAG_NONE;
7579
7580
0
  if (data_flags & NGHTTP2_DATA_FLAG_EOF) {
7581
0
    aux_data->eof = 1;
7582
    /* If NGHTTP2_DATA_FLAG_NO_END_STREAM is set, don't set
7583
       NGHTTP2_FLAG_END_STREAM */
7584
0
    if ((aux_data->flags & NGHTTP2_FLAG_END_STREAM) &&
7585
0
        (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) {
7586
0
      frame->hd.flags |= NGHTTP2_FLAG_END_STREAM;
7587
0
    }
7588
0
  }
7589
7590
0
  if (data_flags & NGHTTP2_DATA_FLAG_NO_COPY) {
7591
0
    if (session->callbacks.send_data_callback == NULL) {
7592
0
      DEBUGF("NGHTTP2_DATA_FLAG_NO_COPY requires send_data_callback set\n");
7593
7594
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
7595
0
    }
7596
0
    aux_data->no_copy = 1;
7597
0
  }
7598
7599
0
  frame->hd.length = (size_t)payloadlen;
7600
0
  frame->data.padlen = 0;
7601
7602
0
  max_payloadlen =
7603
0
    nghttp2_min_size(datamax, frame->hd.length + NGHTTP2_MAX_PADLEN);
7604
7605
0
  padded_payloadlen =
7606
0
    session_call_select_padding(session, frame, max_payloadlen);
7607
7608
0
  if (nghttp2_is_fatal((int)padded_payloadlen)) {
7609
0
    return (int)padded_payloadlen;
7610
0
  }
7611
7612
0
  frame->data.padlen = (size_t)(padded_payloadlen - payloadlen);
7613
7614
0
  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
7615
7616
0
  nghttp2_frame_add_pad(bufs, &frame->hd, frame->data.padlen,
7617
0
                        aux_data->no_copy);
7618
7619
0
  session_reschedule_stream(session, stream);
7620
7621
0
  if (frame->hd.length == 0 && (data_flags & NGHTTP2_DATA_FLAG_EOF) &&
7622
0
      (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM)) {
7623
    /* DATA payload length is 0, and DATA frame does not bear
7624
       END_STREAM.  In this case, there is no point to send 0 length
7625
       DATA frame. */
7626
0
    return NGHTTP2_ERR_CANCEL;
7627
0
  }
7628
7629
0
  return 0;
7630
0
}
7631
7632
void *nghttp2_session_get_stream_user_data(nghttp2_session *session,
7633
0
                                           int32_t stream_id) {
7634
0
  nghttp2_stream *stream;
7635
0
  stream = nghttp2_session_get_stream(session, stream_id);
7636
0
  if (stream) {
7637
0
    return stream->stream_user_data;
7638
0
  } else {
7639
0
    return NULL;
7640
0
  }
7641
0
}
7642
7643
int nghttp2_session_set_stream_user_data(nghttp2_session *session,
7644
                                         int32_t stream_id,
7645
0
                                         void *stream_user_data) {
7646
0
  nghttp2_stream *stream;
7647
0
  nghttp2_frame *frame;
7648
0
  nghttp2_outbound_item *item;
7649
7650
0
  stream = nghttp2_session_get_stream(session, stream_id);
7651
0
  if (stream) {
7652
0
    stream->stream_user_data = stream_user_data;
7653
0
    return 0;
7654
0
  }
7655
7656
0
  if (session->server || !nghttp2_session_is_my_stream_id(session, stream_id) ||
7657
0
      !nghttp2_outbound_queue_top(&session->ob_syn)) {
7658
0
    return NGHTTP2_ERR_INVALID_ARGUMENT;
7659
0
  }
7660
7661
0
  frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame;
7662
0
  assert(frame->hd.type == NGHTTP2_HEADERS);
7663
7664
0
  if (frame->hd.stream_id > stream_id ||
7665
0
      (uint32_t)stream_id >= session->next_stream_id) {
7666
0
    return NGHTTP2_ERR_INVALID_ARGUMENT;
7667
0
  }
7668
7669
0
  for (item = session->ob_syn.head; item; item = item->qnext) {
7670
0
    if (item->frame.hd.stream_id < stream_id) {
7671
0
      continue;
7672
0
    }
7673
7674
0
    if (item->frame.hd.stream_id > stream_id) {
7675
0
      break;
7676
0
    }
7677
7678
0
    item->aux_data.headers.stream_user_data = stream_user_data;
7679
0
    return 0;
7680
0
  }
7681
7682
0
  return NGHTTP2_ERR_INVALID_ARGUMENT;
7683
0
}
7684
7685
0
int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) {
7686
0
  int rv;
7687
0
  nghttp2_stream *stream;
7688
0
  stream = nghttp2_session_get_stream(session, stream_id);
7689
0
  if (stream == NULL || !nghttp2_stream_check_deferred_item(stream)) {
7690
0
    return NGHTTP2_ERR_INVALID_ARGUMENT;
7691
0
  }
7692
7693
0
  rv = session_resume_deferred_stream_item(session, stream,
7694
0
                                           NGHTTP2_STREAM_FLAG_DEFERRED_USER);
7695
7696
0
  if (nghttp2_is_fatal(rv)) {
7697
0
    return rv;
7698
0
  }
7699
7700
0
  return 0;
7701
0
}
7702
7703
18.3k
size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) {
7704
18.3k
  return nghttp2_outbound_queue_size(&session->ob_urgent) +
7705
18.3k
         nghttp2_outbound_queue_size(&session->ob_reg) +
7706
18.3k
         nghttp2_outbound_queue_size(&session->ob_syn);
7707
  /* TODO account for item attached to stream */
7708
18.3k
}
7709
7710
int32_t
7711
nghttp2_session_get_stream_effective_recv_data_length(nghttp2_session *session,
7712
0
                                                      int32_t stream_id) {
7713
0
  nghttp2_stream *stream;
7714
0
  stream = nghttp2_session_get_stream(session, stream_id);
7715
0
  if (stream == NULL) {
7716
0
    return -1;
7717
0
  }
7718
0
  return stream->recv_window_size < 0 ? 0 : stream->recv_window_size;
7719
0
}
7720
7721
int32_t
7722
nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session,
7723
0
                                                       int32_t stream_id) {
7724
0
  nghttp2_stream *stream;
7725
0
  stream = nghttp2_session_get_stream(session, stream_id);
7726
0
  if (stream == NULL) {
7727
0
    return -1;
7728
0
  }
7729
0
  return stream->local_window_size;
7730
0
}
7731
7732
int32_t nghttp2_session_get_stream_local_window_size(nghttp2_session *session,
7733
0
                                                     int32_t stream_id) {
7734
0
  nghttp2_stream *stream;
7735
0
  int32_t size;
7736
0
  stream = nghttp2_session_get_stream(session, stream_id);
7737
0
  if (stream == NULL) {
7738
0
    return -1;
7739
0
  }
7740
7741
0
  size = stream->local_window_size - stream->recv_window_size;
7742
7743
  /* size could be negative if local endpoint reduced
7744
     SETTINGS_INITIAL_WINDOW_SIZE */
7745
0
  if (size < 0) {
7746
0
    return 0;
7747
0
  }
7748
7749
0
  return size;
7750
0
}
7751
7752
int32_t
7753
0
nghttp2_session_get_effective_recv_data_length(nghttp2_session *session) {
7754
0
  return session->recv_window_size < 0 ? 0 : session->recv_window_size;
7755
0
}
7756
7757
int32_t
7758
0
nghttp2_session_get_effective_local_window_size(nghttp2_session *session) {
7759
0
  return session->local_window_size;
7760
0
}
7761
7762
0
int32_t nghttp2_session_get_local_window_size(nghttp2_session *session) {
7763
0
  return session->local_window_size - session->recv_window_size;
7764
0
}
7765
7766
int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session,
7767
0
                                                      int32_t stream_id) {
7768
0
  nghttp2_stream *stream;
7769
7770
0
  stream = nghttp2_session_get_stream(session, stream_id);
7771
0
  if (stream == NULL) {
7772
0
    return -1;
7773
0
  }
7774
7775
  /* stream->remote_window_size can be negative when
7776
     SETTINGS_INITIAL_WINDOW_SIZE is changed. */
7777
0
  return nghttp2_max_int32(0, stream->remote_window_size);
7778
0
}
7779
7780
0
int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session) {
7781
0
  return session->remote_window_size;
7782
0
}
7783
7784
uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,
7785
0
                                             nghttp2_settings_id id) {
7786
0
  switch (id) {
7787
0
  case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
7788
0
    return session->remote_settings.header_table_size;
7789
0
  case NGHTTP2_SETTINGS_ENABLE_PUSH:
7790
0
    return session->remote_settings.enable_push;
7791
0
  case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
7792
0
    return session->remote_settings.max_concurrent_streams;
7793
0
  case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
7794
0
    return session->remote_settings.initial_window_size;
7795
0
  case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
7796
0
    return session->remote_settings.max_frame_size;
7797
0
  case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
7798
0
    return session->remote_settings.max_header_list_size;
7799
0
  case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
7800
0
    return session->remote_settings.enable_connect_protocol;
7801
0
  case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
7802
0
    return session->remote_settings.no_rfc7540_priorities;
7803
0
  }
7804
7805
0
  assert(0);
7806
0
  abort(); /* if NDEBUG is set */
7807
0
}
7808
7809
uint32_t nghttp2_session_get_local_settings(nghttp2_session *session,
7810
0
                                            nghttp2_settings_id id) {
7811
0
  switch (id) {
7812
0
  case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
7813
0
    return session->local_settings.header_table_size;
7814
0
  case NGHTTP2_SETTINGS_ENABLE_PUSH:
7815
0
    return session->local_settings.enable_push;
7816
0
  case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
7817
0
    return session->local_settings.max_concurrent_streams;
7818
0
  case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
7819
0
    return session->local_settings.initial_window_size;
7820
0
  case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
7821
0
    return session->local_settings.max_frame_size;
7822
0
  case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
7823
0
    return session->local_settings.max_header_list_size;
7824
0
  case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
7825
0
    return session->local_settings.enable_connect_protocol;
7826
0
  case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
7827
0
    return session->local_settings.no_rfc7540_priorities;
7828
0
  }
7829
7830
0
  assert(0);
7831
0
  abort(); /* if NDEBUG is set */
7832
0
}
7833
7834
static int nghttp2_session_upgrade_internal(nghttp2_session *session,
7835
                                            const uint8_t *settings_payload,
7836
                                            size_t settings_payloadlen,
7837
0
                                            void *stream_user_data) {
7838
0
  nghttp2_stream *stream;
7839
0
  nghttp2_frame frame;
7840
0
  nghttp2_settings_entry *iv;
7841
0
  size_t niv;
7842
0
  int rv;
7843
0
  nghttp2_mem *mem;
7844
7845
0
  mem = &session->mem;
7846
7847
0
  if ((!session->server && session->next_stream_id != 1) ||
7848
0
      (session->server && session->last_recv_stream_id >= 1)) {
7849
0
    return NGHTTP2_ERR_PROTO;
7850
0
  }
7851
0
  if (settings_payloadlen % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) {
7852
0
    return NGHTTP2_ERR_INVALID_ARGUMENT;
7853
0
  }
7854
  /* SETTINGS frame contains too many settings */
7855
0
  if (settings_payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH >
7856
0
      session->max_settings) {
7857
0
    return NGHTTP2_ERR_TOO_MANY_SETTINGS;
7858
0
  }
7859
0
  rv = nghttp2_frame_unpack_settings_payload2(&iv, &niv, settings_payload,
7860
0
                                              settings_payloadlen, mem);
7861
0
  if (rv != 0) {
7862
0
    return rv;
7863
0
  }
7864
7865
0
  if (session->server) {
7866
0
    nghttp2_frame_hd_init(&frame.hd, settings_payloadlen, NGHTTP2_SETTINGS,
7867
0
                          NGHTTP2_FLAG_NONE, 0);
7868
0
    frame.settings.iv = iv;
7869
0
    frame.settings.niv = niv;
7870
0
    rv = nghttp2_session_on_settings_received(session, &frame, 1 /* No ACK */);
7871
0
  } else {
7872
0
    rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, niv);
7873
0
  }
7874
0
  nghttp2_mem_free(mem, iv);
7875
0
  if (rv != 0) {
7876
0
    return rv;
7877
0
  }
7878
7879
0
  stream = nghttp2_session_open_stream(
7880
0
    session, 1, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_STREAM_OPENING,
7881
0
    session->server ? NULL : stream_user_data);
7882
0
  if (stream == NULL) {
7883
0
    return NGHTTP2_ERR_NOMEM;
7884
0
  }
7885
7886
0
  if (session->server) {
7887
0
    nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
7888
0
    session->last_recv_stream_id = 1;
7889
0
    session->last_proc_stream_id = 1;
7890
0
  } else {
7891
0
    nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
7892
0
    session->last_sent_stream_id = 1;
7893
0
    session->next_stream_id += 2;
7894
0
  }
7895
0
  return 0;
7896
0
}
7897
7898
int nghttp2_session_upgrade(nghttp2_session *session,
7899
                            const uint8_t *settings_payload,
7900
                            size_t settings_payloadlen,
7901
0
                            void *stream_user_data) {
7902
0
  int rv;
7903
0
  nghttp2_stream *stream;
7904
7905
0
  rv = nghttp2_session_upgrade_internal(session, settings_payload,
7906
0
                                        settings_payloadlen, stream_user_data);
7907
0
  if (rv != 0) {
7908
0
    return rv;
7909
0
  }
7910
7911
0
  stream = nghttp2_session_get_stream(session, 1);
7912
0
  assert(stream);
7913
7914
  /* We have no information about request header fields when Upgrade
7915
     was happened.  So we don't know the request method here.  If
7916
     request method is HEAD, we have a trouble because we may have
7917
     nonzero content-length header field in response headers, and we
7918
     will going to check it against the actual DATA frames, but we may
7919
     get mismatch because HEAD response body must be empty.  Because
7920
     of this reason, nghttp2_session_upgrade() was deprecated in favor
7921
     of nghttp2_session_upgrade2(), which has |head_request| parameter
7922
     to indicate that request method is HEAD or not. */
7923
0
  stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND;
7924
0
  return 0;
7925
0
}
7926
7927
int nghttp2_session_upgrade2(nghttp2_session *session,
7928
                             const uint8_t *settings_payload,
7929
                             size_t settings_payloadlen, int head_request,
7930
0
                             void *stream_user_data) {
7931
0
  int rv;
7932
0
  nghttp2_stream *stream;
7933
7934
0
  rv = nghttp2_session_upgrade_internal(session, settings_payload,
7935
0
                                        settings_payloadlen, stream_user_data);
7936
0
  if (rv != 0) {
7937
0
    return rv;
7938
0
  }
7939
7940
0
  stream = nghttp2_session_get_stream(session, 1);
7941
0
  assert(stream);
7942
7943
0
  if (head_request) {
7944
0
    stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
7945
0
  }
7946
7947
0
  return 0;
7948
0
}
7949
7950
int nghttp2_session_get_stream_local_close(nghttp2_session *session,
7951
0
                                           int32_t stream_id) {
7952
0
  nghttp2_stream *stream;
7953
7954
0
  stream = nghttp2_session_get_stream(session, stream_id);
7955
7956
0
  if (!stream) {
7957
0
    return -1;
7958
0
  }
7959
7960
0
  return (stream->shut_flags & NGHTTP2_SHUT_WR) != 0;
7961
0
}
7962
7963
int nghttp2_session_get_stream_remote_close(nghttp2_session *session,
7964
0
                                            int32_t stream_id) {
7965
0
  nghttp2_stream *stream;
7966
7967
0
  stream = nghttp2_session_get_stream(session, stream_id);
7968
7969
0
  if (!stream) {
7970
0
    return -1;
7971
0
  }
7972
7973
0
  return (stream->shut_flags & NGHTTP2_SHUT_RD) != 0;
7974
0
}
7975
7976
int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id,
7977
6.75k
                            size_t size) {
7978
6.75k
  int rv;
7979
6.75k
  nghttp2_stream *stream;
7980
7981
6.75k
  if (stream_id == 0) {
7982
0
    return NGHTTP2_ERR_INVALID_ARGUMENT;
7983
0
  }
7984
7985
6.75k
  if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
7986
6.75k
    return NGHTTP2_ERR_INVALID_STATE;
7987
6.75k
  }
7988
7989
0
  rv = session_update_connection_consumed_size(session, size);
7990
7991
0
  if (nghttp2_is_fatal(rv)) {
7992
0
    return rv;
7993
0
  }
7994
7995
0
  stream = nghttp2_session_get_stream(session, stream_id);
7996
7997
0
  if (!stream) {
7998
0
    return 0;
7999
0
  }
8000
8001
0
  rv = session_update_stream_consumed_size(session, stream, size);
8002
8003
0
  if (nghttp2_is_fatal(rv)) {
8004
0
    return rv;
8005
0
  }
8006
8007
0
  return 0;
8008
0
}
8009
8010
0
int nghttp2_session_consume_connection(nghttp2_session *session, size_t size) {
8011
0
  int rv;
8012
8013
0
  if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
8014
0
    return NGHTTP2_ERR_INVALID_STATE;
8015
0
  }
8016
8017
0
  rv = session_update_connection_consumed_size(session, size);
8018
8019
0
  if (nghttp2_is_fatal(rv)) {
8020
0
    return rv;
8021
0
  }
8022
8023
0
  return 0;
8024
0
}
8025
8026
int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id,
8027
0
                                   size_t size) {
8028
0
  int rv;
8029
0
  nghttp2_stream *stream;
8030
8031
0
  if (stream_id == 0) {
8032
0
    return NGHTTP2_ERR_INVALID_ARGUMENT;
8033
0
  }
8034
8035
0
  if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
8036
0
    return NGHTTP2_ERR_INVALID_STATE;
8037
0
  }
8038
8039
0
  stream = nghttp2_session_get_stream(session, stream_id);
8040
8041
0
  if (!stream) {
8042
0
    return 0;
8043
0
  }
8044
8045
0
  rv = session_update_stream_consumed_size(session, stream, size);
8046
8047
0
  if (nghttp2_is_fatal(rv)) {
8048
0
    return rv;
8049
0
  }
8050
8051
0
  return 0;
8052
0
}
8053
8054
int nghttp2_session_set_next_stream_id(nghttp2_session *session,
8055
0
                                       int32_t next_stream_id) {
8056
0
  if (next_stream_id <= 0 ||
8057
0
      session->next_stream_id > (uint32_t)next_stream_id) {
8058
0
    return NGHTTP2_ERR_INVALID_ARGUMENT;
8059
0
  }
8060
8061
0
  if (session->server) {
8062
0
    if (next_stream_id % 2) {
8063
0
      return NGHTTP2_ERR_INVALID_ARGUMENT;
8064
0
    }
8065
0
  } else if (next_stream_id % 2 == 0) {
8066
0
    return NGHTTP2_ERR_INVALID_ARGUMENT;
8067
0
  }
8068
8069
0
  session->next_stream_id = (uint32_t)next_stream_id;
8070
0
  return 0;
8071
0
}
8072
8073
0
uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session) {
8074
0
  return session->next_stream_id;
8075
0
}
8076
8077
0
int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session) {
8078
0
  return session->last_proc_stream_id;
8079
0
}
8080
8081
nghttp2_stream *nghttp2_session_find_stream(nghttp2_session *session,
8082
0
                                            int32_t stream_id) {
8083
0
  if (stream_id == 0) {
8084
0
    return &nghttp2_stream_root;
8085
0
  }
8086
8087
0
  return nghttp2_session_get_stream_raw(session, stream_id);
8088
0
}
8089
8090
0
nghttp2_stream *nghttp2_session_get_root_stream(nghttp2_session *session) {
8091
0
  (void)session;
8092
8093
0
  return &nghttp2_stream_root;
8094
0
}
8095
8096
0
int nghttp2_session_check_server_session(nghttp2_session *session) {
8097
0
  return session->server;
8098
0
}
8099
8100
int nghttp2_session_change_stream_priority(
8101
  nghttp2_session *session, int32_t stream_id,
8102
0
  const nghttp2_priority_spec *pri_spec) {
8103
0
  (void)session;
8104
0
  (void)stream_id;
8105
0
  (void)pri_spec;
8106
8107
0
  return 0;
8108
0
}
8109
8110
int nghttp2_session_create_idle_stream(nghttp2_session *session,
8111
                                       int32_t stream_id,
8112
0
                                       const nghttp2_priority_spec *pri_spec) {
8113
0
  (void)session;
8114
0
  (void)stream_id;
8115
0
  (void)pri_spec;
8116
8117
0
  return 0;
8118
0
}
8119
8120
size_t
8121
0
nghttp2_session_get_hd_inflate_dynamic_table_size(nghttp2_session *session) {
8122
0
  return nghttp2_hd_inflate_get_dynamic_table_size(&session->hd_inflater);
8123
0
}
8124
8125
size_t
8126
0
nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session) {
8127
0
  return nghttp2_hd_deflate_get_dynamic_table_size(&session->hd_deflater);
8128
0
}
8129
8130
0
void nghttp2_session_set_user_data(nghttp2_session *session, void *user_data) {
8131
0
  session->user_data = user_data;
8132
0
}
8133
8134
int nghttp2_session_change_extpri_stream_priority(
8135
  nghttp2_session *session, int32_t stream_id, const nghttp2_extpri *extpri_in,
8136
0
  int ignore_client_signal) {
8137
0
  nghttp2_stream *stream;
8138
0
  nghttp2_extpri extpri = *extpri_in;
8139
8140
0
  if (!session->server) {
8141
0
    return NGHTTP2_ERR_INVALID_STATE;
8142
0
  }
8143
8144
0
  if (session->pending_no_rfc7540_priorities != 1) {
8145
0
    return 0;
8146
0
  }
8147
8148
0
  if (stream_id == 0) {
8149
0
    return NGHTTP2_ERR_INVALID_ARGUMENT;
8150
0
  }
8151
8152
0
  stream = nghttp2_session_get_stream_raw(session, stream_id);
8153
0
  if (!stream) {
8154
0
    return NGHTTP2_ERR_INVALID_ARGUMENT;
8155
0
  }
8156
8157
0
  if (extpri.urgency > NGHTTP2_EXTPRI_URGENCY_LOW) {
8158
0
    extpri.urgency = NGHTTP2_EXTPRI_URGENCY_LOW;
8159
0
  }
8160
8161
0
  if (ignore_client_signal) {
8162
0
    stream->flags |= NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES;
8163
0
  }
8164
8165
0
  return session_update_stream_priority(session, stream,
8166
0
                                        nghttp2_extpri_to_uint8(&extpri));
8167
0
}
8168
8169
int nghttp2_session_get_extpri_stream_priority(nghttp2_session *session,
8170
                                               nghttp2_extpri *extpri,
8171
0
                                               int32_t stream_id) {
8172
0
  nghttp2_stream *stream;
8173
8174
0
  if (!session->server) {
8175
0
    return NGHTTP2_ERR_INVALID_STATE;
8176
0
  }
8177
8178
0
  if (session->pending_no_rfc7540_priorities != 1) {
8179
0
    return 0;
8180
0
  }
8181
8182
0
  if (stream_id == 0) {
8183
0
    return NGHTTP2_ERR_INVALID_ARGUMENT;
8184
0
  }
8185
8186
0
  stream = nghttp2_session_get_stream_raw(session, stream_id);
8187
0
  if (!stream) {
8188
0
    return NGHTTP2_ERR_INVALID_ARGUMENT;
8189
0
  }
8190
8191
0
  nghttp2_extpri_from_uint8(extpri, stream->extpri);
8192
8193
0
  return 0;
8194
0
}