Coverage Report

Created: 2026-01-25 06:21

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