Coverage Report

Created: 2025-12-03 06:25

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