Coverage Report

Created: 2024-09-08 06:32

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