Coverage Report

Created: 2024-05-21 06:52

/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
586k
session_is_outgoing_concurrent_streams_max(nghttp2_session *session) {
51
586k
  return session->remote_settings.max_concurrent_streams <=
52
586k
         session->num_outgoing_streams;
53
586k
}
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
12.0M
int nghttp2_is_fatal(int lib_error_code) {
85
12.0M
  return lib_error_code < NGHTTP2_ERR_FATAL;
86
12.0M
}
87
88
148k
static int session_enforce_http_messaging(nghttp2_session *session) {
89
148k
  return (session->opt_flags & NGHTTP2_OPTMASK_NO_HTTP_MESSAGING) == 0;
90
148k
}
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
6.73k
                                   nghttp2_frame *frame) {
98
6.73k
  if (!stream || frame->hd.type != NGHTTP2_HEADERS) {
99
135
    return 0;
100
135
  }
101
6.59k
  if (session->server) {
102
0
    return frame->headers.cat == NGHTTP2_HCAT_HEADERS;
103
0
  }
104
105
6.59k
  return frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
106
6.59k
         (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) == 0;
107
6.59k
}
108
109
/* Returns nonzero if the |stream| is in reserved(remote) state */
110
static int state_reserved_remote(nghttp2_session *session,
111
965
                                 nghttp2_stream *stream) {
112
965
  return stream->state == NGHTTP2_STREAM_RESERVED &&
113
965
         !nghttp2_session_is_my_stream_id(session, stream->stream_id);
114
965
}
115
116
/* Returns nonzero if the |stream| is in reserved(local) state */
117
static int state_reserved_local(nghttp2_session *session,
118
0
                                nghttp2_stream *stream) {
119
0
  return stream->state == NGHTTP2_STREAM_RESERVED &&
120
0
         nghttp2_session_is_my_stream_id(session, stream->stream_id);
121
0
}
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.73k
                                         int32_t stream_id) {
129
1.73k
  return stream_id != 0 &&
130
1.73k
         !nghttp2_session_is_my_stream_id(session, stream_id) &&
131
1.73k
         session->last_recv_stream_id < stream_id;
132
1.73k
}
133
134
static int session_detect_idle_stream(nghttp2_session *session,
135
3.83k
                                      int32_t stream_id) {
136
  /* Assume that stream object with stream_id does not exist */
137
3.83k
  if (nghttp2_session_is_my_stream_id(session, stream_id)) {
138
2.69k
    if (session->last_sent_stream_id < stream_id) {
139
343
      return 1;
140
343
    }
141
2.34k
    return 0;
142
2.69k
  }
143
1.14k
  if (session_is_new_peer_stream_id(session, stream_id)) {
144
240
    return 1;
145
240
  }
146
908
  return 0;
147
1.14k
}
148
149
27.0k
static int session_no_rfc7540_pri_no_fallback(nghttp2_session *session) {
150
27.0k
  return session->pending_no_rfc7540_priorities == 1 &&
151
27.0k
         !session->fallback_rfc7540_priorities;
152
27.0k
}
153
154
9.75k
static int check_ext_type_set(const uint8_t *ext_types, uint8_t type) {
155
9.75k
  return (ext_types[type / 8] & (1 << (type & 0x7))) > 0;
156
9.75k
}
157
158
static int session_call_error_callback(nghttp2_session *session,
159
                                       int lib_error_code, const char *fmt,
160
1.49k
                                       ...) {
161
1.49k
  size_t bufsize;
162
1.49k
  va_list ap;
163
1.49k
  char *buf;
164
1.49k
  int rv;
165
1.49k
  nghttp2_mem *mem;
166
167
1.49k
  if (!session->callbacks.error_callback &&
168
1.49k
      !session->callbacks.error_callback2) {
169
0
    return 0;
170
0
  }
171
172
1.49k
  mem = &session->mem;
173
174
1.49k
  va_start(ap, fmt);
175
1.49k
  rv = vsnprintf(NULL, 0, fmt, ap);
176
1.49k
  va_end(ap);
177
178
1.49k
  if (rv < 0) {
179
0
    return NGHTTP2_ERR_NOMEM;
180
0
  }
181
182
1.49k
  bufsize = (size_t)(rv + 1);
183
184
1.49k
  buf = nghttp2_mem_malloc(mem, bufsize);
185
1.49k
  if (buf == NULL) {
186
0
    return NGHTTP2_ERR_NOMEM;
187
0
  }
188
189
1.49k
  va_start(ap, fmt);
190
1.49k
  rv = vsnprintf(buf, bufsize, fmt, ap);
191
1.49k
  va_end(ap);
192
193
1.49k
  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
1.49k
  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
1.49k
  } else {
206
1.49k
    rv = session->callbacks.error_callback(session, buf, (size_t)rv,
207
1.49k
                                           session->user_data);
208
1.49k
  }
209
210
1.49k
  nghttp2_mem_free(mem, buf);
211
212
1.49k
  if (rv != 0) {
213
0
    return NGHTTP2_ERR_CALLBACK_FAILURE;
214
0
  }
215
216
1.49k
  return 0;
217
1.49k
}
218
219
static int session_terminate_session(nghttp2_session *session,
220
                                     int32_t last_stream_id,
221
4.46k
                                     uint32_t error_code, const char *reason) {
222
4.46k
  int rv;
223
4.46k
  const uint8_t *debug_data;
224
4.46k
  size_t debug_datalen;
225
226
4.46k
  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
4.46k
  session->iframe.state = NGHTTP2_IB_IGN_ALL;
233
234
4.46k
  if (reason == NULL) {
235
1.65k
    debug_data = NULL;
236
1.65k
    debug_datalen = 0;
237
2.81k
  } else {
238
2.81k
    debug_data = (const uint8_t *)reason;
239
2.81k
    debug_datalen = strlen(reason);
240
2.81k
  }
241
242
4.46k
  rv = nghttp2_session_add_goaway(session, last_stream_id, error_code,
243
4.46k
                                  debug_data, debug_datalen,
244
4.46k
                                  NGHTTP2_GOAWAY_AUX_TERM_ON_SEND);
245
246
4.46k
  if (rv != 0) {
247
0
    return rv;
248
0
  }
249
250
4.46k
  session->goaway_flags |= NGHTTP2_GOAWAY_TERM_ON_SEND;
251
252
4.46k
  return 0;
253
4.46k
}
254
255
int nghttp2_session_terminate_session(nghttp2_session *session,
256
1.64k
                                      uint32_t error_code) {
257
1.64k
  return session_terminate_session(session, session->last_proc_stream_id,
258
1.64k
                                   error_code, NULL);
259
1.64k
}
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
2.82k
                                                  const char *reason) {
270
2.82k
  return session_terminate_session(session, session->last_proc_stream_id,
271
2.82k
                                   error_code, reason);
272
2.82k
}
273
274
int nghttp2_session_is_my_stream_id(nghttp2_session *session,
275
82.6k
                                    int32_t stream_id) {
276
82.6k
  int rem;
277
82.6k
  if (stream_id == 0) {
278
4.46k
    return 0;
279
4.46k
  }
280
78.2k
  rem = stream_id & 0x1;
281
78.2k
  if (session->server) {
282
0
    return rem == 0;
283
0
  }
284
78.2k
  return rem == 1;
285
78.2k
}
286
287
nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session,
288
17.4M
                                           int32_t stream_id) {
289
17.4M
  nghttp2_stream *stream;
290
291
17.4M
  stream = (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id);
292
293
17.4M
  if (stream == NULL || (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) ||
294
17.4M
      stream->state == NGHTTP2_STREAM_IDLE) {
295
7.21M
    return NULL;
296
7.21M
  }
297
298
10.2M
  return stream;
299
17.4M
}
300
301
nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session,
302
14.0k
                                               int32_t stream_id) {
303
14.0k
  return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id);
304
14.0k
}
305
306
47.4k
static void session_inbound_frame_reset(nghttp2_session *session) {
307
47.4k
  nghttp2_inbound_frame *iframe = &session->iframe;
308
47.4k
  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
47.4k
  switch (iframe->frame.hd.type) {
313
19.9k
  case NGHTTP2_DATA:
314
19.9k
    break;
315
5.61k
  case NGHTTP2_HEADERS:
316
5.61k
    nghttp2_frame_headers_free(&iframe->frame.headers, mem);
317
5.61k
    break;
318
149
  case NGHTTP2_PRIORITY:
319
149
    nghttp2_frame_priority_free(&iframe->frame.priority);
320
149
    break;
321
552
  case NGHTTP2_RST_STREAM:
322
552
    nghttp2_frame_rst_stream_free(&iframe->frame.rst_stream);
323
552
    break;
324
11.8k
  case NGHTTP2_SETTINGS:
325
11.8k
    nghttp2_frame_settings_free(&iframe->frame.settings, mem);
326
327
11.8k
    nghttp2_mem_free(mem, iframe->iv);
328
329
11.8k
    iframe->iv = NULL;
330
11.8k
    iframe->niv = 0;
331
11.8k
    iframe->max_niv = 0;
332
333
11.8k
    break;
334
669
  case NGHTTP2_PUSH_PROMISE:
335
669
    nghttp2_frame_push_promise_free(&iframe->frame.push_promise, mem);
336
669
    break;
337
233
  case NGHTTP2_PING:
338
233
    nghttp2_frame_ping_free(&iframe->frame.ping);
339
233
    break;
340
1.34k
  case NGHTTP2_GOAWAY:
341
1.34k
    nghttp2_frame_goaway_free(&iframe->frame.goaway, mem);
342
1.34k
    break;
343
1.84k
  case NGHTTP2_WINDOW_UPDATE:
344
1.84k
    nghttp2_frame_window_update_free(&iframe->frame.window_update);
345
1.84k
    break;
346
5.24k
  default:
347
    /* extension frame */
348
5.24k
    if (check_ext_type_set(session->user_recv_ext_types,
349
5.24k
                           iframe->frame.hd.type)) {
350
0
      nghttp2_frame_extension_free(&iframe->frame.ext);
351
5.24k
    } else {
352
5.24k
      switch (iframe->frame.hd.type) {
353
203
      case NGHTTP2_ALTSVC:
354
203
        if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) == 0) {
355
203
          break;
356
203
        }
357
0
        nghttp2_frame_altsvc_free(&iframe->frame.ext, mem);
358
0
        break;
359
62
      case NGHTTP2_ORIGIN:
360
62
        if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN) == 0) {
361
62
          break;
362
62
        }
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
5.24k
      }
374
5.24k
    }
375
376
5.24k
    break;
377
47.4k
  }
378
379
47.4k
  memset(&iframe->frame, 0, sizeof(nghttp2_frame));
380
47.4k
  memset(&iframe->ext_frame_payload, 0, sizeof(nghttp2_ext_frame_payload));
381
382
47.4k
  iframe->state = NGHTTP2_IB_READ_HEAD;
383
384
47.4k
  nghttp2_buf_wrap_init(&iframe->sbuf, iframe->raw_sbuf,
385
47.4k
                        sizeof(iframe->raw_sbuf));
386
47.4k
  iframe->sbuf.mark += NGHTTP2_FRAME_HDLEN;
387
388
47.4k
  nghttp2_buf_free(&iframe->lbuf, mem);
389
47.4k
  nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
390
391
47.4k
  iframe->raw_lbuf = NULL;
392
393
47.4k
  iframe->payloadleft = 0;
394
47.4k
  iframe->padlen = 0;
395
47.4k
}
396
397
26.1k
static void init_settings(nghttp2_settings_storage *settings) {
398
26.1k
  settings->header_table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
399
26.1k
  settings->enable_push = 1;
400
26.1k
  settings->max_concurrent_streams = NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;
401
26.1k
  settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE;
402
26.1k
  settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN;
403
26.1k
  settings->max_header_list_size = UINT32_MAX;
404
26.1k
  settings->no_rfc7540_priorities = UINT32_MAX;
405
26.1k
}
406
407
static void active_outbound_item_reset(nghttp2_active_outbound_item *aob,
408
109k
                                       nghttp2_mem *mem) {
409
109k
  DEBUGF("send: reset nghttp2_active_outbound_item\n");
410
109k
  DEBUGF("send: aob->item = %p\n", aob->item);
411
109k
  nghttp2_outbound_item_free(aob->item, mem);
412
109k
  nghttp2_mem_free(mem, aob->item);
413
109k
  aob->item = NULL;
414
109k
  nghttp2_bufs_reset(&aob->framebufs);
415
109k
  aob->state = NGHTTP2_OB_POP_ITEM;
416
109k
}
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
13.0k
                       const nghttp2_option *option, nghttp2_mem *mem) {
439
13.0k
  int rv;
440
13.0k
  size_t nbuffer;
441
13.0k
  size_t max_deflate_dynamic_table_size =
442
13.0k
      NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE;
443
13.0k
  size_t i;
444
445
13.0k
  if (mem == NULL) {
446
13.0k
    mem = nghttp2_mem_default();
447
13.0k
  }
448
449
13.0k
  *session_ptr = nghttp2_mem_calloc(mem, 1, sizeof(nghttp2_session));
450
13.0k
  if (*session_ptr == NULL) {
451
0
    rv = NGHTTP2_ERR_NOMEM;
452
0
    goto fail_session;
453
0
  }
454
455
13.0k
  (*session_ptr)->mem = *mem;
456
13.0k
  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
13.0k
  nghttp2_stream_init(&(*session_ptr)->root, 0, NGHTTP2_STREAM_FLAG_NONE,
462
13.0k
                      NGHTTP2_STREAM_IDLE, NGHTTP2_DEFAULT_WEIGHT, 0, 0, NULL,
463
13.0k
                      mem);
464
465
13.0k
  (*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
466
13.0k
  (*session_ptr)->recv_window_size = 0;
467
13.0k
  (*session_ptr)->consumed_size = 0;
468
13.0k
  (*session_ptr)->recv_reduction = 0;
469
13.0k
  (*session_ptr)->local_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
470
471
13.0k
  (*session_ptr)->goaway_flags = NGHTTP2_GOAWAY_NONE;
472
13.0k
  (*session_ptr)->local_last_stream_id = (1u << 31) - 1;
473
13.0k
  (*session_ptr)->remote_last_stream_id = (1u << 31) - 1;
474
475
13.0k
  (*session_ptr)->pending_local_max_concurrent_stream =
476
13.0k
      NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;
477
13.0k
  (*session_ptr)->pending_enable_push = 1;
478
13.0k
  (*session_ptr)->pending_no_rfc7540_priorities = UINT8_MAX;
479
480
13.0k
  nghttp2_ratelim_init(&(*session_ptr)->stream_reset_ratelim,
481
13.0k
                       NGHTTP2_DEFAULT_STREAM_RESET_BURST,
482
13.0k
                       NGHTTP2_DEFAULT_STREAM_RESET_RATE);
483
484
13.0k
  if (server) {
485
0
    (*session_ptr)->server = 1;
486
0
  }
487
488
13.0k
  init_settings(&(*session_ptr)->remote_settings);
489
13.0k
  init_settings(&(*session_ptr)->local_settings);
490
491
13.0k
  (*session_ptr)->max_incoming_reserved_streams =
492
13.0k
      NGHTTP2_MAX_INCOMING_RESERVED_STREAMS;
493
494
  /* Limit max outgoing concurrent streams to sensible value */
495
13.0k
  (*session_ptr)->remote_settings.max_concurrent_streams = 100;
496
497
13.0k
  (*session_ptr)->max_send_header_block_length = NGHTTP2_MAX_HEADERSLEN;
498
13.0k
  (*session_ptr)->max_outbound_ack = NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM;
499
13.0k
  (*session_ptr)->max_settings = NGHTTP2_DEFAULT_MAX_SETTINGS;
500
13.0k
  (*session_ptr)->max_continuations = NGHTTP2_DEFAULT_MAX_CONTINUATIONS;
501
502
13.0k
  if (option) {
503
13.0k
    if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) &&
504
13.0k
        option->no_auto_window_update) {
505
506
13.0k
      (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE;
507
13.0k
    }
508
509
13.0k
    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
13.0k
    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
13.0k
    if ((option->opt_set_mask & NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC) &&
522
13.0k
        option->no_recv_client_magic) {
523
524
0
      (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC;
525
0
    }
526
527
13.0k
    if ((option->opt_set_mask & NGHTTP2_OPT_NO_HTTP_MESSAGING) &&
528
13.0k
        option->no_http_messaging) {
529
530
0
      (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING;
531
0
    }
532
533
13.0k
    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
13.0k
    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
13.0k
    if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_PING_ACK) &&
543
13.0k
        option->no_auto_ping_ack) {
544
0
      (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_PING_ACK;
545
0
    }
546
547
13.0k
    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
13.0k
    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
13.0k
    if ((option->opt_set_mask & NGHTTP2_OPT_NO_CLOSED_STREAMS) &&
557
13.0k
        option->no_closed_streams) {
558
0
      (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_CLOSED_STREAMS;
559
0
    }
560
561
13.0k
    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
13.0k
    if ((option->opt_set_mask & NGHTTP2_OPT_MAX_SETTINGS) &&
566
13.0k
        option->max_settings) {
567
0
      (*session_ptr)->max_settings = option->max_settings;
568
0
    }
569
570
13.0k
    if ((option->opt_set_mask &
571
13.0k
         NGHTTP2_OPT_SERVER_FALLBACK_RFC7540_PRIORITIES) &&
572
13.0k
        option->server_fallback_rfc7540_priorities) {
573
0
      (*session_ptr)->opt_flags |=
574
0
          NGHTTP2_OPTMASK_SERVER_FALLBACK_RFC7540_PRIORITIES;
575
0
    }
576
577
13.0k
    if ((option->opt_set_mask &
578
13.0k
         NGHTTP2_OPT_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) &&
579
13.0k
        option->no_rfc9113_leading_and_trailing_ws_validation) {
580
13.0k
      (*session_ptr)->opt_flags |=
581
13.0k
          NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION;
582
13.0k
    }
583
584
13.0k
    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
13.0k
    if (option->opt_set_mask & NGHTTP2_OPT_MAX_CONTINUATIONS) {
591
0
      (*session_ptr)->max_continuations = option->max_continuations;
592
0
    }
593
13.0k
  }
594
595
13.0k
  rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater,
596
13.0k
                                max_deflate_dynamic_table_size, mem);
597
13.0k
  if (rv != 0) {
598
0
    goto fail_hd_deflater;
599
0
  }
600
13.0k
  rv = nghttp2_hd_inflate_init(&(*session_ptr)->hd_inflater, mem);
601
13.0k
  if (rv != 0) {
602
0
    goto fail_hd_inflater;
603
0
  }
604
605
13.0k
  nbuffer = ((*session_ptr)->max_send_header_block_length +
606
13.0k
             NGHTTP2_FRAMEBUF_CHUNKLEN - 1) /
607
13.0k
            NGHTTP2_FRAMEBUF_CHUNKLEN;
608
609
13.0k
  if (nbuffer == 0) {
610
0
    nbuffer = 1;
611
0
  }
612
613
  /* 1 for Pad Field. */
614
13.0k
  rv = nghttp2_bufs_init3(&(*session_ptr)->aob.framebufs,
615
13.0k
                          NGHTTP2_FRAMEBUF_CHUNKLEN, nbuffer, 1,
616
13.0k
                          NGHTTP2_FRAME_HDLEN + 1, mem);
617
13.0k
  if (rv != 0) {
618
0
    goto fail_aob_framebuf;
619
0
  }
620
621
13.0k
  nghttp2_map_init(&(*session_ptr)->streams, mem);
622
623
13.0k
  active_outbound_item_reset(&(*session_ptr)->aob, mem);
624
625
13.0k
  (*session_ptr)->callbacks = *callbacks;
626
13.0k
  (*session_ptr)->user_data = user_data;
627
628
13.0k
  session_inbound_frame_reset(*session_ptr);
629
630
13.0k
  if (nghttp2_enable_strict_preface) {
631
13.0k
    nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe;
632
633
13.0k
    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
13.0k
    } else {
638
13.0k
      iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS;
639
13.0k
    }
640
641
13.0k
    if (!server) {
642
13.0k
      (*session_ptr)->aob.state = NGHTTP2_OB_SEND_CLIENT_MAGIC;
643
13.0k
      nghttp2_bufs_add(&(*session_ptr)->aob.framebufs, NGHTTP2_CLIENT_MAGIC,
644
13.0k
                       NGHTTP2_CLIENT_MAGIC_LEN);
645
13.0k
    }
646
13.0k
  }
647
648
117k
  for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) {
649
104k
    nghttp2_pq_init(&(*session_ptr)->sched[i].ob_data, stream_less, mem);
650
104k
  }
651
652
13.0k
  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
13.0k
                                void *user_data, const nghttp2_option *option) {
674
13.0k
  return nghttp2_session_client_new3(session_ptr, callbacks, user_data, option,
675
13.0k
                                     NULL);
676
13.0k
}
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
13.0k
                                nghttp2_mem *mem) {
682
13.0k
  int rv;
683
13.0k
  nghttp2_session *session;
684
685
13.0k
  rv = session_new(&session, callbacks, user_data, 0, option, mem);
686
687
13.0k
  if (rv != 0) {
688
0
    return rv;
689
0
  }
690
  /* IDs for use in client */
691
13.0k
  session->next_stream_id = 1;
692
693
13.0k
  *session_ptr = session;
694
695
13.0k
  return 0;
696
13.0k
}
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.94k
static int free_streams(void *entry, void *ptr) {
733
2.94k
  nghttp2_session *session;
734
2.94k
  nghttp2_stream *stream;
735
2.94k
  nghttp2_outbound_item *item;
736
2.94k
  nghttp2_mem *mem;
737
738
2.94k
  session = (nghttp2_session *)ptr;
739
2.94k
  mem = &session->mem;
740
2.94k
  stream = (nghttp2_stream *)entry;
741
2.94k
  item = stream->item;
742
743
2.94k
  if (item && !item->queued && item != session->aob.item) {
744
31
    nghttp2_outbound_item_free(item, mem);
745
31
    nghttp2_mem_free(mem, item);
746
31
  }
747
748
2.94k
  nghttp2_stream_free(stream);
749
2.94k
  nghttp2_mem_free(mem, stream);
750
751
2.94k
  return 0;
752
2.94k
}
753
754
39.1k
static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) {
755
39.1k
  nghttp2_outbound_item *item, *next;
756
39.2k
  for (item = q->head; item;) {
757
34
    next = item->qnext;
758
34
    nghttp2_outbound_item_free(item, mem);
759
34
    nghttp2_mem_free(mem, item);
760
34
    item = next;
761
34
  }
762
39.1k
}
763
764
static int inflight_settings_new(nghttp2_inflight_settings **settings_ptr,
765
                                 const nghttp2_settings_entry *iv, size_t niv,
766
13.0k
                                 nghttp2_mem *mem) {
767
13.0k
  *settings_ptr = nghttp2_mem_malloc(mem, sizeof(nghttp2_inflight_settings));
768
13.0k
  if (!*settings_ptr) {
769
0
    return NGHTTP2_ERR_NOMEM;
770
0
  }
771
772
13.0k
  if (niv > 0) {
773
13.0k
    (*settings_ptr)->iv = nghttp2_frame_iv_copy(iv, niv, mem);
774
13.0k
    if (!(*settings_ptr)->iv) {
775
0
      nghttp2_mem_free(mem, *settings_ptr);
776
0
      return NGHTTP2_ERR_NOMEM;
777
0
    }
778
13.0k
  } else {
779
0
    (*settings_ptr)->iv = NULL;
780
0
  }
781
782
13.0k
  (*settings_ptr)->niv = niv;
783
13.0k
  (*settings_ptr)->next = NULL;
784
785
13.0k
  return 0;
786
13.0k
}
787
788
static void inflight_settings_del(nghttp2_inflight_settings *settings,
789
13.0k
                                  nghttp2_mem *mem) {
790
13.0k
  if (!settings) {
791
0
    return;
792
0
  }
793
794
13.0k
  nghttp2_mem_free(mem, settings->iv);
795
13.0k
  nghttp2_mem_free(mem, settings);
796
13.0k
}
797
798
13.0k
void nghttp2_session_del(nghttp2_session *session) {
799
13.0k
  nghttp2_mem *mem;
800
13.0k
  nghttp2_inflight_settings *settings;
801
13.0k
  size_t i;
802
803
13.0k
  if (session == NULL) {
804
0
    return;
805
0
  }
806
807
13.0k
  mem = &session->mem;
808
809
26.0k
  for (settings = session->inflight_settings_head; settings;) {
810
12.9k
    nghttp2_inflight_settings *next = settings->next;
811
12.9k
    inflight_settings_del(settings, mem);
812
12.9k
    settings = next;
813
12.9k
  }
814
815
117k
  for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) {
816
104k
    nghttp2_pq_free(&session->sched[i].ob_data);
817
104k
  }
818
13.0k
  nghttp2_stream_free(&session->root);
819
820
  /* Have to free streams first, so that we can check
821
     stream->item->queued */
822
13.0k
  nghttp2_map_each_free(&session->streams, free_streams, session);
823
13.0k
  nghttp2_map_free(&session->streams);
824
825
13.0k
  ob_q_free(&session->ob_urgent, mem);
826
13.0k
  ob_q_free(&session->ob_reg, mem);
827
13.0k
  ob_q_free(&session->ob_syn, mem);
828
829
13.0k
  active_outbound_item_reset(&session->aob, mem);
830
13.0k
  session_inbound_frame_reset(session);
831
13.0k
  nghttp2_hd_deflate_free(&session->hd_deflater);
832
13.0k
  nghttp2_hd_inflate_free(&session->hd_inflater);
833
13.0k
  nghttp2_bufs_free(&session->aob.framebufs);
834
13.0k
  nghttp2_mem_free(mem, session);
835
13.0k
}
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
1.34k
                                      nghttp2_outbound_item *item) {
976
1.34k
  int rv;
977
978
1.34k
  rv = nghttp2_stream_attach_item(stream, item);
979
1.34k
  if (rv != 0) {
980
0
    return rv;
981
0
  }
982
983
1.34k
  if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) {
984
1.34k
    return 0;
985
1.34k
  }
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
1.31k
                                       nghttp2_stream *stream) {
999
1.31k
  nghttp2_stream_detach_item(stream);
1000
1001
1.31k
  if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) ||
1002
1.31k
      !stream->queued) {
1003
1.31k
    return;
1004
1.31k
  }
1005
1006
0
  session_ob_data_remove(session, stream);
1007
0
}
1008
1009
static void session_defer_stream_item(nghttp2_session *session,
1010
807
                                      nghttp2_stream *stream, uint8_t flags) {
1011
807
  nghttp2_stream_defer_item(stream, flags);
1012
1013
807
  if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) ||
1014
807
      !stream->queued) {
1015
807
    return;
1016
807
  }
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
747
                                               uint8_t flags) {
1024
747
  int rv;
1025
1026
747
  rv = nghttp2_stream_resume_deferred_item(stream, flags);
1027
747
  if (rv != 0) {
1028
0
    return rv;
1029
0
  }
1030
1031
747
  if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) ||
1032
747
      (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL)) {
1033
747
    return 0;
1034
747
  }
1035
1036
0
  return session_ob_data_push(session, stream);
1037
747
}
1038
1039
static nghttp2_outbound_item *
1040
45.1k
session_sched_get_next_outbound_item(nghttp2_session *session) {
1041
45.1k
  size_t i;
1042
45.1k
  nghttp2_pq_entry *ent;
1043
45.1k
  nghttp2_stream *stream;
1044
1045
406k
  for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) {
1046
361k
    ent = nghttp2_pq_top(&session->sched[i].ob_data);
1047
361k
    if (!ent) {
1048
361k
      continue;
1049
361k
    }
1050
1051
0
    stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry);
1052
0
    return stream->item;
1053
361k
  }
1054
1055
45.1k
  return NULL;
1056
45.1k
}
1057
1058
2.41M
static int session_sched_empty(nghttp2_session *session) {
1059
2.41M
  size_t i;
1060
1061
21.7M
  for (i = 0; i < NGHTTP2_EXTPRI_URGENCY_LEVELS; ++i) {
1062
19.3M
    if (!nghttp2_pq_empty(&session->sched[i].ob_data)) {
1063
0
      return 0;
1064
0
    }
1065
19.3M
  }
1066
1067
2.41M
  return 1;
1068
2.41M
}
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
67.6k
                             nghttp2_outbound_item *item) {
1119
  /* TODO Return error if stream is not found for the frame requiring
1120
     stream presence. */
1121
67.6k
  int rv = 0;
1122
67.6k
  nghttp2_stream *stream;
1123
67.6k
  nghttp2_frame *frame;
1124
1125
67.6k
  frame = &item->frame;
1126
67.6k
  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
1127
1128
67.6k
  switch (frame->hd.type) {
1129
1.34k
  case NGHTTP2_DATA:
1130
1.34k
    if (!stream) {
1131
0
      return NGHTTP2_ERR_STREAM_CLOSED;
1132
0
    }
1133
1134
1.34k
    if (stream->item) {
1135
0
      return NGHTTP2_ERR_DATA_EXIST;
1136
0
    }
1137
1138
1.34k
    rv = session_attach_stream_item(session, stream, item);
1139
1140
1.34k
    if (rv != 0) {
1141
0
      return rv;
1142
0
    }
1143
1144
1.34k
    return 0;
1145
13.3k
  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
13.3k
    if (frame->headers.cat == NGHTTP2_HCAT_REQUEST ||
1153
13.3k
        (stream && stream->state == NGHTTP2_STREAM_RESERVED)) {
1154
13.3k
      nghttp2_outbound_queue_push(&session->ob_syn, item);
1155
13.3k
      item->queued = 1;
1156
13.3k
      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
24.0k
  case NGHTTP2_SETTINGS:
1164
24.1k
  case NGHTTP2_PING:
1165
24.1k
    nghttp2_outbound_queue_push(&session->ob_urgent, item);
1166
24.1k
    item->queued = 1;
1167
24.1k
    return 0;
1168
11.2k
  case NGHTTP2_RST_STREAM:
1169
11.2k
    if (stream) {
1170
10.7k
      stream->state = NGHTTP2_STREAM_CLOSING;
1171
10.7k
    }
1172
11.2k
    nghttp2_outbound_queue_push(&session->ob_reg, item);
1173
11.2k
    item->queued = 1;
1174
11.2k
    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
13.0k
  case NGHTTP2_WINDOW_UPDATE:
1205
13.0k
    if (stream) {
1206
0
      stream->window_update_queued = 1;
1207
13.0k
    } else if (frame->hd.stream_id == 0) {
1208
13.0k
      session->window_update_queued = 1;
1209
13.0k
    }
1210
13.0k
    nghttp2_outbound_queue_push(&session->ob_reg, item);
1211
13.0k
    item->queued = 1;
1212
13.0k
    return 0;
1213
4.46k
  default:
1214
4.46k
    nghttp2_outbound_queue_push(&session->ob_reg, item);
1215
4.46k
    item->queued = 1;
1216
4.46k
    return 0;
1217
67.6k
  }
1218
67.6k
}
1219
1220
int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id,
1221
15.2k
                                   uint32_t error_code) {
1222
15.2k
  int rv;
1223
15.2k
  nghttp2_outbound_item *item;
1224
15.2k
  nghttp2_frame *frame;
1225
15.2k
  nghttp2_stream *stream;
1226
15.2k
  nghttp2_mem *mem;
1227
1228
15.2k
  mem = &session->mem;
1229
15.2k
  stream = nghttp2_session_get_stream(session, stream_id);
1230
15.2k
  if (stream && stream->state == NGHTTP2_STREAM_CLOSING) {
1231
3.27k
    return 0;
1232
3.27k
  }
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
11.9k
  if (nghttp2_session_is_my_stream_id(session, stream_id)) {
1239
11.5k
    if ((uint32_t)stream_id >= session->next_stream_id) {
1240
0
      return 0;
1241
0
    }
1242
11.5k
  } 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
11.9k
  if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) &&
1249
11.9k
      nghttp2_outbound_queue_top(&session->ob_syn)) {
1250
719
    nghttp2_headers_aux_data *aux_data;
1251
719
    nghttp2_frame *headers_frame;
1252
1253
719
    headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame;
1254
719
    assert(headers_frame->hd.type == NGHTTP2_HEADERS);
1255
1256
719
    if (headers_frame->hd.stream_id <= stream_id) {
1257
1258
719
      for (item = session->ob_syn.head; item; item = item->qnext) {
1259
719
        aux_data = &item->aux_data.headers;
1260
1261
719
        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
719
        if (item->frame.hd.stream_id > stream_id || aux_data->canceled) {
1268
0
          break;
1269
0
        }
1270
1271
719
        aux_data->error_code = error_code;
1272
719
        aux_data->canceled = 1;
1273
1274
719
        return 0;
1275
719
      }
1276
719
    }
1277
719
  }
1278
1279
11.2k
  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
1280
11.2k
  if (item == NULL) {
1281
0
    return NGHTTP2_ERR_NOMEM;
1282
0
  }
1283
1284
11.2k
  nghttp2_outbound_item_init(item);
1285
1286
11.2k
  frame = &item->frame;
1287
1288
11.2k
  nghttp2_frame_rst_stream_init(&frame->rst_stream, stream_id, error_code);
1289
11.2k
  rv = nghttp2_session_add_item(session, item);
1290
11.2k
  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
11.2k
  return 0;
1296
11.2k
}
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
13.3k
                                            void *stream_user_data) {
1303
13.3k
  int rv;
1304
13.3k
  nghttp2_stream *stream;
1305
13.3k
  nghttp2_stream *dep_stream = NULL;
1306
13.3k
  int stream_alloc = 0;
1307
13.3k
  nghttp2_priority_spec pri_spec_default;
1308
13.3k
  nghttp2_priority_spec *pri_spec = pri_spec_in;
1309
13.3k
  nghttp2_mem *mem;
1310
1311
13.3k
  mem = &session->mem;
1312
13.3k
  stream = nghttp2_session_get_stream_raw(session, stream_id);
1313
1314
13.3k
  if (session->opt_flags &
1315
13.3k
      NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION) {
1316
13.3k
    flags |= NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION;
1317
13.3k
  }
1318
1319
13.3k
  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
13.3k
  } else {
1339
13.3k
    stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream));
1340
13.3k
    if (stream == NULL) {
1341
0
      return NULL;
1342
0
    }
1343
1344
13.3k
    stream_alloc = 1;
1345
13.3k
  }
1346
1347
13.3k
  if (session_no_rfc7540_pri_no_fallback(session) ||
1348
13.3k
      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
13.3k
  } 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
13.3k
  if (initial_state == NGHTTP2_STREAM_RESERVED) {
1397
0
    flags |= NGHTTP2_STREAM_FLAG_PUSH;
1398
0
  }
1399
1400
13.3k
  if (stream_alloc) {
1401
13.3k
    nghttp2_stream_init(stream, stream_id, flags, initial_state,
1402
13.3k
                        pri_spec->weight,
1403
13.3k
                        (int32_t)session->remote_settings.initial_window_size,
1404
13.3k
                        (int32_t)session->local_settings.initial_window_size,
1405
13.3k
                        stream_user_data, mem);
1406
1407
13.3k
    if (session_no_rfc7540_pri_no_fallback(session)) {
1408
0
      stream->seq = session->stream_seq++;
1409
0
    }
1410
1411
13.3k
    rv = nghttp2_map_insert(&session->streams, stream_id, stream);
1412
13.3k
    if (rv != 0) {
1413
0
      nghttp2_stream_free(stream);
1414
0
      nghttp2_mem_free(mem, stream);
1415
0
      return NULL;
1416
0
    }
1417
13.3k
  } 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
13.3k
  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
13.3k
  default:
1443
13.3k
    if (nghttp2_session_is_my_stream_id(session, stream_id)) {
1444
13.3k
      ++session->num_outgoing_streams;
1445
13.3k
    } else {
1446
0
      ++session->num_incoming_streams;
1447
0
    }
1448
13.3k
  }
1449
1450
13.3k
  if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
1451
0
    return stream;
1452
0
  }
1453
1454
13.3k
  if (pri_spec->stream_id == 0) {
1455
13.3k
    dep_stream = &session->root;
1456
13.3k
  }
1457
1458
13.3k
  assert(dep_stream);
1459
1460
13.3k
  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
13.3k
  } else {
1466
13.3k
    nghttp2_stream_dep_add(dep_stream, stream);
1467
13.3k
  }
1468
1469
13.3k
  return stream;
1470
13.3k
}
1471
1472
int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
1473
10.8k
                                 uint32_t error_code) {
1474
10.8k
  int rv;
1475
10.8k
  nghttp2_stream *stream;
1476
10.8k
  nghttp2_mem *mem;
1477
10.8k
  int is_my_stream_id;
1478
1479
10.8k
  mem = &session->mem;
1480
10.8k
  stream = nghttp2_session_get_stream(session, stream_id);
1481
1482
10.8k
  if (!stream) {
1483
473
    return NGHTTP2_ERR_INVALID_ARGUMENT;
1484
473
  }
1485
1486
10.3k
  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
10.3k
  if (session->callbacks.on_stream_close_callback) {
1496
10.3k
    if (session->callbacks.on_stream_close_callback(
1497
10.3k
            session, stream_id, error_code, session->user_data) != 0) {
1498
1499
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
1500
0
    }
1501
10.3k
  }
1502
1503
10.3k
  if (stream->item) {
1504
187
    nghttp2_outbound_item *item;
1505
1506
187
    item = stream->item;
1507
1508
187
    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
187
    if (!item->queued && item != session->aob.item) {
1515
187
      nghttp2_outbound_item_free(item, mem);
1516
187
      nghttp2_mem_free(mem, item);
1517
187
    }
1518
187
  }
1519
1520
10.3k
  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
10.3k
  if ((stream->flags & NGHTTP2_STREAM_FLAG_PUSH)) {
1525
0
    if (!is_my_stream_id) {
1526
0
      --session->num_incoming_reserved_streams;
1527
0
    }
1528
10.3k
  } else {
1529
10.3k
    if (is_my_stream_id) {
1530
10.3k
      --session->num_outgoing_streams;
1531
10.3k
    } else {
1532
0
      --session->num_incoming_streams;
1533
0
    }
1534
10.3k
  }
1535
1536
  /* Closes both directions just in case they are not closed yet */
1537
10.3k
  stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED;
1538
1539
10.3k
  if (session->pending_no_rfc7540_priorities == 1) {
1540
0
    return nghttp2_session_destroy_stream(session, stream);
1541
0
  }
1542
1543
10.3k
  if ((session->opt_flags & NGHTTP2_OPTMASK_NO_CLOSED_STREAMS) == 0 &&
1544
10.3k
      session->server && !is_my_stream_id &&
1545
10.3k
      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
10.3k
  } else {
1551
10.3k
    rv = nghttp2_session_destroy_stream(session, stream);
1552
10.3k
    if (rv != 0) {
1553
0
      return rv;
1554
0
    }
1555
10.3k
  }
1556
1557
10.3k
  return 0;
1558
10.3k
}
1559
1560
int nghttp2_session_destroy_stream(nghttp2_session *session,
1561
10.3k
                                   nghttp2_stream *stream) {
1562
10.3k
  nghttp2_mem *mem;
1563
10.3k
  int rv;
1564
1565
10.3k
  DEBUGF("stream: destroy closed stream(%p)=%d\n", stream, stream->stream_id);
1566
1567
10.3k
  mem = &session->mem;
1568
1569
10.3k
  if (nghttp2_stream_in_dep_tree(stream)) {
1570
10.3k
    rv = nghttp2_stream_dep_remove(stream);
1571
10.3k
    if (rv != 0) {
1572
0
      return rv;
1573
0
    }
1574
10.3k
  }
1575
1576
10.3k
  if (stream->queued &&
1577
10.3k
      (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) {
1578
0
    session_ob_data_remove(session, stream);
1579
0
  }
1580
1581
10.3k
  nghttp2_map_remove(&session->streams, stream->stream_id);
1582
10.3k
  nghttp2_stream_free(stream);
1583
10.3k
  nghttp2_mem_free(mem, stream);
1584
1585
10.3k
  return 0;
1586
10.3k
}
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
127k
int nghttp2_session_adjust_idle_stream(nghttp2_session *session) {
1698
127k
  size_t max;
1699
127k
  int rv;
1700
1701
  /* Make minimum number of idle streams 16, and maximum 100, which
1702
     are arbitrary chosen numbers. */
1703
127k
  max = nghttp2_min_uint32(
1704
127k
      100, nghttp2_max_uint32(
1705
127k
               16, nghttp2_min_uint32(
1706
127k
                       session->local_settings.max_concurrent_streams,
1707
127k
                       session->pending_local_max_concurrent_stream)));
1708
1709
127k
  DEBUGF("stream: adjusting kept idle streams num_idle_streams=%zu, max=%zu\n",
1710
127k
         session->num_idle_streams, max);
1711
1712
127k
  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
127k
  return 0;
1740
127k
}
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
14.3k
                                              nghttp2_stream *stream) {
1757
14.3k
  if ((stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR) {
1758
709
    return nghttp2_session_close_stream(session, stream->stream_id,
1759
709
                                        NGHTTP2_NO_ERROR);
1760
709
  }
1761
13.6k
  return 0;
1762
14.3k
}
1763
1764
/*
1765
 * Returns nonzero if local endpoint allows reception of new stream
1766
 * from remote.
1767
 */
1768
589
static int session_allow_incoming_new_stream(nghttp2_session *session) {
1769
589
  return (session->goaway_flags &
1770
589
          (NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_SENT)) == 0;
1771
589
}
1772
1773
/*
1774
 * This function returns nonzero if session is closing.
1775
 */
1776
87.3k
static int session_is_closing(nghttp2_session *session) {
1777
87.3k
  return (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) != 0 ||
1778
87.3k
         (nghttp2_session_want_read(session) == 0 &&
1779
73.8k
          nghttp2_session_want_write(session) == 0);
1780
87.3k
}
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
5.80k
                                             nghttp2_stream *stream) {
1796
5.80k
  if (stream == NULL) {
1797
0
    return NGHTTP2_ERR_STREAM_CLOSED;
1798
0
  }
1799
5.80k
  if (session_is_closing(session)) {
1800
64
    return NGHTTP2_ERR_SESSION_CLOSING;
1801
64
  }
1802
5.74k
  if (stream->shut_flags & NGHTTP2_SHUT_WR) {
1803
0
    return NGHTTP2_ERR_STREAM_SHUT_WR;
1804
0
  }
1805
5.74k
  return 0;
1806
5.74k
}
1807
1808
23.5k
int nghttp2_session_check_request_allowed(nghttp2_session *session) {
1809
23.5k
  return !session->server && session->next_stream_id <= INT32_MAX &&
1810
23.5k
         (session->goaway_flags & NGHTTP2_GOAWAY_RECV) == 0 &&
1811
23.5k
         !session_is_closing(session);
1812
23.5k
}
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
13.3k
                                                  nghttp2_outbound_item *item) {
1830
13.3k
  if (item->aux_data.headers.canceled) {
1831
685
    return NGHTTP2_ERR_STREAM_CLOSING;
1832
685
  }
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
12.6k
  if ((session->goaway_flags & NGHTTP2_GOAWAY_RECV) ||
1837
12.6k
      session_is_closing(session)) {
1838
30
    return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
1839
30
  }
1840
12.6k
  return 0;
1841
12.6k
}
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
13.0k
                                                int32_t stream_id) {
2050
13.0k
  nghttp2_stream *stream;
2051
2052
13.0k
  if (session_is_closing(session)) {
2053
651
    return NGHTTP2_ERR_SESSION_CLOSING;
2054
651
  }
2055
2056
12.4k
  if (stream_id == 0) {
2057
    /* Connection-level window update */
2058
12.4k
    return 0;
2059
12.4k
  }
2060
0
  stream = nghttp2_session_get_stream(session, stream_id);
2061
0
  if (stream == NULL) {
2062
0
    return NGHTTP2_ERR_STREAM_CLOSED;
2063
0
  }
2064
0
  if (stream->state == NGHTTP2_STREAM_CLOSING) {
2065
0
    return NGHTTP2_ERR_STREAM_CLOSING;
2066
0
  }
2067
0
  if (state_reserved_local(session, stream)) {
2068
0
    return NGHTTP2_ERR_INVALID_STREAM_STATE;
2069
0
  }
2070
0
  return 0;
2071
0
}
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
3.80k
    nghttp2_ssize requested_window_size) {
2130
3.80k
  DEBUGF("send: remote windowsize connection=%d, remote maxframsize=%u, "
2131
3.80k
         "stream(id %d)=%d\n",
2132
3.80k
         session->remote_window_size, session->remote_settings.max_frame_size,
2133
3.80k
         stream->stream_id, stream->remote_window_size);
2134
2135
3.80k
  return nghttp2_min_int32(
2136
3.80k
      nghttp2_min_int32(nghttp2_min_int32((int32_t)requested_window_size,
2137
3.80k
                                          stream->remote_window_size),
2138
3.80k
                        session->remote_window_size),
2139
3.80k
      (int32_t)session->remote_settings.max_frame_size);
2140
3.80k
}
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
3.80k
                                             nghttp2_stream *stream) {
2150
3.80k
  nghttp2_ssize window_size;
2151
2152
3.80k
  window_size = nghttp2_session_enforce_flow_control_limits(
2153
3.80k
      session, stream, NGHTTP2_DATA_PAYLOADLEN);
2154
2155
3.80k
  DEBUGF("send: available window=%td\n", window_size);
2156
2157
3.80k
  return window_size > 0 ? (size_t)window_size : 0;
2158
3.80k
}
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
5.80k
                                               nghttp2_stream *stream) {
2181
5.80k
  int rv;
2182
5.80k
  rv = session_predicate_for_stream_send(session, stream);
2183
5.80k
  if (rv != 0) {
2184
64
    return rv;
2185
64
  }
2186
5.74k
  assert(stream);
2187
5.74k
  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
5.74k
    if (stream->state == NGHTTP2_STREAM_CLOSING) {
2193
0
      return NGHTTP2_ERR_STREAM_CLOSING;
2194
0
    }
2195
5.74k
    if (stream->state == NGHTTP2_STREAM_RESERVED) {
2196
0
      return NGHTTP2_ERR_INVALID_STREAM_STATE;
2197
0
    }
2198
5.74k
    return 0;
2199
5.74k
  }
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
15.5k
                                                 size_t max_payloadlen) {
2213
15.5k
  nghttp2_ssize rv;
2214
15.5k
  size_t max_paddedlen;
2215
2216
15.5k
  if (frame->hd.length >= max_payloadlen ||
2217
15.5k
      (!session->callbacks.select_padding_callback2 &&
2218
15.5k
       !session->callbacks.select_padding_callback)) {
2219
15.5k
    return (nghttp2_ssize)frame->hd.length;
2220
15.5k
  }
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
12.5k
                                   nghttp2_frame *frame) {
2244
12.5k
  nghttp2_ssize padded_payloadlen;
2245
12.5k
  nghttp2_active_outbound_item *aob;
2246
12.5k
  nghttp2_bufs *framebufs;
2247
12.5k
  size_t padlen;
2248
12.5k
  size_t max_payloadlen;
2249
2250
12.5k
  aob = &session->aob;
2251
12.5k
  framebufs = &aob->framebufs;
2252
2253
12.5k
  max_payloadlen = nghttp2_min_size(NGHTTP2_MAX_PAYLOADLEN,
2254
12.5k
                                    frame->hd.length + NGHTTP2_MAX_PADLEN);
2255
2256
12.5k
  padded_payloadlen =
2257
12.5k
      session_call_select_padding(session, frame, max_payloadlen);
2258
2259
12.5k
  if (nghttp2_is_fatal((int)padded_payloadlen)) {
2260
0
    return (int)padded_payloadlen;
2261
0
  }
2262
2263
12.5k
  padlen = (size_t)padded_payloadlen - frame->hd.length;
2264
2265
12.5k
  DEBUGF("send: padding selected: payloadlen=%td, padlen=%zu\n",
2266
12.5k
         padded_payloadlen, padlen);
2267
2268
12.5k
  nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, 0);
2269
2270
12.5k
  frame->headers.padlen = padlen;
2271
2272
12.5k
  return 0;
2273
12.5k
}
2274
2275
static size_t session_estimate_headers_payload(nghttp2_session *session,
2276
                                               const nghttp2_nv *nva,
2277
                                               size_t nvlen,
2278
12.6k
                                               size_t additional) {
2279
12.6k
  return nghttp2_hd_deflate_bound(&session->hd_deflater, nva, nvlen) +
2280
12.6k
         additional;
2281
12.6k
}
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
70.1k
                              nghttp2_outbound_item *item) {
2332
70.1k
  int rv;
2333
70.1k
  nghttp2_frame *frame;
2334
70.1k
  nghttp2_mem *mem;
2335
2336
70.1k
  mem = &session->mem;
2337
70.1k
  frame = &item->frame;
2338
2339
70.1k
  switch (frame->hd.type) {
2340
3.87k
  case NGHTTP2_DATA: {
2341
3.87k
    size_t next_readmax;
2342
3.87k
    nghttp2_stream *stream;
2343
2344
3.87k
    stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2345
2346
3.87k
    if (stream) {
2347
3.87k
      assert(stream->item == item);
2348
3.87k
    }
2349
2350
3.87k
    rv = nghttp2_session_predicate_data_send(session, stream);
2351
3.87k
    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
64
      stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);
2356
64
      if (stream) {
2357
64
        session_detach_stream_item(session, stream);
2358
64
      }
2359
2360
64
      return rv;
2361
64
    }
2362
    /* Assuming stream is not NULL */
2363
3.80k
    assert(stream);
2364
3.80k
    next_readmax = nghttp2_session_next_data_read(session, stream);
2365
2366
3.80k
    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
83
      assert(session->remote_window_size > 0);
2371
2372
83
      session_defer_stream_item(session, stream,
2373
83
                                NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
2374
2375
83
      session->aob.item = NULL;
2376
83
      active_outbound_item_reset(&session->aob, mem);
2377
83
      return NGHTTP2_ERR_DEFERRED;
2378
83
    }
2379
2380
3.72k
    rv = nghttp2_session_pack_data(session, &session->aob.framebufs,
2381
3.72k
                                   next_readmax, frame, &item->aux_data.data,
2382
3.72k
                                   stream);
2383
3.72k
    if (rv == NGHTTP2_ERR_PAUSE) {
2384
0
      return rv;
2385
0
    }
2386
3.72k
    if (rv == NGHTTP2_ERR_DEFERRED) {
2387
724
      session_defer_stream_item(session, stream,
2388
724
                                NGHTTP2_STREAM_FLAG_DEFERRED_USER);
2389
2390
724
      session->aob.item = NULL;
2391
724
      active_outbound_item_reset(&session->aob, mem);
2392
724
      return NGHTTP2_ERR_DEFERRED;
2393
724
    }
2394
2.99k
    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
2.99k
    if (rv != 0) {
2405
0
      session_detach_stream_item(session, stream);
2406
2407
0
      return rv;
2408
0
    }
2409
2.99k
    return 0;
2410
2.99k
  }
2411
13.3k
  case NGHTTP2_HEADERS: {
2412
13.3k
    nghttp2_headers_aux_data *aux_data;
2413
13.3k
    size_t estimated_payloadlen;
2414
2415
13.3k
    aux_data = &item->aux_data.headers;
2416
2417
13.3k
    if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
2418
      /* initial HEADERS, which opens stream */
2419
13.3k
      nghttp2_stream *stream;
2420
2421
13.3k
      stream = nghttp2_session_open_stream(
2422
13.3k
          session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE,
2423
13.3k
          &frame->headers.pri_spec, NGHTTP2_STREAM_INITIAL,
2424
13.3k
          aux_data->stream_user_data);
2425
2426
13.3k
      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
13.3k
      rv = session_predicate_request_headers_send(session, item);
2434
13.3k
      if (rv != 0) {
2435
715
        return rv;
2436
715
      }
2437
2438
12.6k
      if (session_enforce_http_messaging(session)) {
2439
12.6k
        nghttp2_http_record_request_method(stream, frame);
2440
12.6k
      }
2441
12.6k
    } 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
12.6k
    estimated_payloadlen = session_estimate_headers_payload(
2471
12.6k
        session, frame->headers.nva, frame->headers.nvlen,
2472
12.6k
        NGHTTP2_PRIORITY_SPECLEN);
2473
2474
12.6k
    if (estimated_payloadlen > session->max_send_header_block_length) {
2475
48
      return NGHTTP2_ERR_FRAME_SIZE_ERROR;
2476
48
    }
2477
2478
12.5k
    rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers,
2479
12.5k
                                    &session->hd_deflater);
2480
2481
12.5k
    if (rv != 0) {
2482
0
      return rv;
2483
0
    }
2484
2485
12.5k
    DEBUGF("send: before padding, HEADERS serialized in %zu bytes\n",
2486
12.5k
           nghttp2_bufs_len(&session->aob.framebufs));
2487
2488
12.5k
    rv = session_headers_add_pad(session, frame);
2489
2490
12.5k
    if (rv != 0) {
2491
0
      return rv;
2492
0
    }
2493
2494
12.5k
    DEBUGF("send: HEADERS finally serialized in %zu bytes\n",
2495
12.5k
           nghttp2_bufs_len(&session->aob.framebufs));
2496
2497
12.5k
    if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
2498
12.5k
      assert(session->last_sent_stream_id < frame->hd.stream_id);
2499
12.5k
      session->last_sent_stream_id = frame->hd.stream_id;
2500
12.5k
    }
2501
2502
12.5k
    return 0;
2503
12.5k
  }
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
11.2k
  case NGHTTP2_RST_STREAM:
2519
11.2k
    if (session_is_closing(session)) {
2520
3.13k
      return NGHTTP2_ERR_SESSION_CLOSING;
2521
3.13k
    }
2522
8.10k
    nghttp2_frame_pack_rst_stream(&session->aob.framebufs, &frame->rst_stream);
2523
8.10k
    return 0;
2524
24.0k
  case NGHTTP2_SETTINGS: {
2525
24.0k
    if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
2526
10.9k
      assert(session->obq_flood_counter_ > 0);
2527
10.9k
      --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
10.9k
      if (session_is_closing(session)) {
2533
5.27k
        return NGHTTP2_ERR_SESSION_CLOSING;
2534
5.27k
      }
2535
10.9k
    }
2536
2537
18.7k
    rv = nghttp2_frame_pack_settings(&session->aob.framebufs, &frame->settings);
2538
18.7k
    if (rv != 0) {
2539
0
      return rv;
2540
0
    }
2541
18.7k
    return 0;
2542
18.7k
  }
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
136
  case NGHTTP2_PING:
2583
136
    if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
2584
136
      assert(session->obq_flood_counter_ > 0);
2585
136
      --session->obq_flood_counter_;
2586
136
    }
2587
    /* PING frame is allowed to be sent unless termination GOAWAY is
2588
       sent */
2589
136
    if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
2590
61
      return NGHTTP2_ERR_SESSION_CLOSING;
2591
61
    }
2592
75
    nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping);
2593
75
    return 0;
2594
4.46k
  case NGHTTP2_GOAWAY:
2595
4.46k
    rv = nghttp2_frame_pack_goaway(&session->aob.framebufs, &frame->goaway);
2596
4.46k
    if (rv != 0) {
2597
0
      return rv;
2598
0
    }
2599
4.46k
    session->local_last_stream_id = frame->goaway.last_stream_id;
2600
2601
4.46k
    return 0;
2602
13.0k
  case NGHTTP2_WINDOW_UPDATE:
2603
13.0k
    rv = session_predicate_window_update_send(session, frame->hd.stream_id);
2604
13.0k
    if (rv != 0) {
2605
651
      return rv;
2606
651
    }
2607
12.4k
    nghttp2_frame_pack_window_update(&session->aob.framebufs,
2608
12.4k
                                     &frame->window_update);
2609
12.4k
    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
70.1k
  }
2670
70.1k
}
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
115k
nghttp2_session_pop_next_ob_item(nghttp2_session *session) {
2704
115k
  nghttp2_outbound_item *item;
2705
2706
115k
  item = nghttp2_outbound_queue_top(&session->ob_urgent);
2707
115k
  if (item) {
2708
24.1k
    nghttp2_outbound_queue_pop(&session->ob_urgent);
2709
24.1k
    item->queued = 0;
2710
24.1k
    return item;
2711
24.1k
  }
2712
2713
91.2k
  item = nghttp2_outbound_queue_top(&session->ob_reg);
2714
91.2k
  if (item) {
2715
28.7k
    nghttp2_outbound_queue_pop(&session->ob_reg);
2716
28.7k
    item->queued = 0;
2717
28.7k
    return item;
2718
28.7k
  }
2719
2720
62.4k
  if (!session_is_outgoing_concurrent_streams_max(session)) {
2721
61.9k
    item = nghttp2_outbound_queue_top(&session->ob_syn);
2722
61.9k
    if (item) {
2723
13.3k
      nghttp2_outbound_queue_pop(&session->ob_syn);
2724
13.3k
      item->queued = 0;
2725
13.3k
      return item;
2726
13.3k
    }
2727
61.9k
  }
2728
2729
49.1k
  if (session->remote_window_size > 0) {
2730
48.9k
    item = nghttp2_stream_next_outbound_item(&session->root);
2731
48.9k
    if (item) {
2732
3.87k
      return item;
2733
3.87k
    }
2734
2735
45.1k
    return session_sched_get_next_outbound_item(session);
2736
48.9k
  }
2737
2738
120
  return NULL;
2739
49.1k
}
2740
2741
static int session_call_before_frame_send(nghttp2_session *session,
2742
56.3k
                                          nghttp2_frame *frame) {
2743
56.3k
  int rv;
2744
56.3k
  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
56.3k
  return 0;
2756
56.3k
}
2757
2758
static int session_call_on_frame_send(nghttp2_session *session,
2759
59.3k
                                      nghttp2_frame *frame) {
2760
59.3k
  int rv;
2761
59.3k
  if (session->callbacks.on_frame_send_callback) {
2762
59.3k
    rv = session->callbacks.on_frame_send_callback(session, frame,
2763
59.3k
                                                   session->user_data);
2764
59.3k
    if (rv != 0) {
2765
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
2766
0
    }
2767
59.3k
  }
2768
59.3k
  return 0;
2769
59.3k
}
2770
2771
4.00k
static int find_stream_on_goaway_func(void *entry, void *ptr) {
2772
4.00k
  nghttp2_close_stream_on_goaway_arg *arg;
2773
4.00k
  nghttp2_stream *stream;
2774
2775
4.00k
  arg = (nghttp2_close_stream_on_goaway_arg *)ptr;
2776
4.00k
  stream = (nghttp2_stream *)entry;
2777
2778
4.00k
  if (nghttp2_session_is_my_stream_id(arg->session, stream->stream_id)) {
2779
4.00k
    if (arg->incoming) {
2780
2.94k
      return 0;
2781
2.94k
    }
2782
4.00k
  } else if (!arg->incoming) {
2783
0
    return 0;
2784
0
  }
2785
2786
1.06k
  if (stream->state != NGHTTP2_STREAM_IDLE &&
2787
1.06k
      (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) == 0 &&
2788
1.06k
      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
851
    assert(stream->closed_next == NULL);
2793
851
    assert(stream->closed_prev == NULL);
2794
2795
851
    if (arg->head) {
2796
0
      stream->closed_next = arg->head;
2797
0
      arg->head = stream;
2798
851
    } else {
2799
851
      arg->head = stream;
2800
851
    }
2801
851
  }
2802
2803
1.06k
  return 0;
2804
1.06k
}
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
5.65k
                                          int incoming) {
2812
5.65k
  int rv;
2813
5.65k
  nghttp2_stream *stream, *next_stream;
2814
5.65k
  nghttp2_close_stream_on_goaway_arg arg = {session, NULL, last_stream_id,
2815
5.65k
                                            incoming};
2816
2817
5.65k
  rv = nghttp2_map_each(&session->streams, find_stream_on_goaway_func, &arg);
2818
5.65k
  assert(rv == 0);
2819
2820
5.65k
  stream = arg.head;
2821
6.50k
  while (stream) {
2822
851
    next_stream = stream->closed_next;
2823
851
    stream->closed_next = NULL;
2824
851
    rv = nghttp2_session_close_stream(session, stream->stream_id,
2825
851
                                      NGHTTP2_REFUSED_STREAM);
2826
2827
    /* stream may be deleted here */
2828
2829
851
    stream = next_stream;
2830
2831
851
    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
851
  }
2841
2842
5.65k
  return 0;
2843
5.65k
}
2844
2845
static void session_reschedule_stream(nghttp2_session *session,
2846
2.99k
                                      nghttp2_stream *stream) {
2847
2.99k
  stream->last_writelen = stream->item->frame.hd.length;
2848
2849
2.99k
  if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) {
2850
2.99k
    nghttp2_stream_reschedule(stream);
2851
2.99k
    return;
2852
2.99k
  }
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
59.4k
static int session_after_frame_sent1(nghttp2_session *session) {
2883
59.4k
  int rv;
2884
59.4k
  nghttp2_active_outbound_item *aob = &session->aob;
2885
59.4k
  nghttp2_outbound_item *item = aob->item;
2886
59.4k
  nghttp2_bufs *framebufs = &aob->framebufs;
2887
59.4k
  nghttp2_frame *frame;
2888
59.4k
  nghttp2_stream *stream;
2889
2890
59.4k
  frame = &item->frame;
2891
2892
59.4k
  if (frame->hd.type == NGHTTP2_DATA) {
2893
2.99k
    nghttp2_data_aux_data *aux_data;
2894
2895
2.99k
    aux_data = &item->aux_data.data;
2896
2897
2.99k
    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
2.99k
    session->remote_window_size -= (int32_t)frame->hd.length;
2902
2.99k
    if (stream) {
2903
2.99k
      stream->remote_window_size -= (int32_t)frame->hd.length;
2904
2.99k
    }
2905
2906
2.99k
    if (stream && aux_data->eof) {
2907
1.06k
      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
1.06k
      if (session->callbacks.on_frame_send_callback) {
2913
1.06k
        rv = session_call_on_frame_send(session, frame);
2914
1.06k
        if (nghttp2_is_fatal(rv)) {
2915
0
          return rv;
2916
0
        }
2917
1.06k
      }
2918
2919
1.06k
      if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
2920
1.06k
        int stream_closed;
2921
2922
1.06k
        stream_closed =
2923
1.06k
            (stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR;
2924
2925
1.06k
        nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
2926
2927
1.06k
        rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
2928
1.06k
        if (nghttp2_is_fatal(rv)) {
2929
0
          return rv;
2930
0
        }
2931
        /* stream may be NULL if it was closed */
2932
1.06k
        if (stream_closed) {
2933
0
          stream = NULL;
2934
0
        }
2935
1.06k
      }
2936
1.06k
      return 0;
2937
1.06k
    }
2938
2939
1.93k
    if (session->callbacks.on_frame_send_callback) {
2940
1.93k
      rv = session_call_on_frame_send(session, frame);
2941
1.93k
      if (nghttp2_is_fatal(rv)) {
2942
0
        return rv;
2943
0
      }
2944
1.93k
    }
2945
2946
1.93k
    return 0;
2947
1.93k
  }
2948
2949
  /* non-DATA frame */
2950
2951
56.4k
  if (frame->hd.type == NGHTTP2_HEADERS ||
2952
56.4k
      frame->hd.type == NGHTTP2_PUSH_PROMISE) {
2953
12.6k
    if (nghttp2_bufs_next_present(framebufs)) {
2954
123
      DEBUGF("send: CONTINUATION exists, just return\n");
2955
123
      return 0;
2956
123
    }
2957
12.6k
  }
2958
56.3k
  rv = session_call_on_frame_send(session, frame);
2959
56.3k
  if (nghttp2_is_fatal(rv)) {
2960
0
    return rv;
2961
0
  }
2962
56.3k
  switch (frame->hd.type) {
2963
12.5k
  case NGHTTP2_HEADERS: {
2964
12.5k
    nghttp2_headers_aux_data *aux_data;
2965
2966
12.5k
    stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
2967
12.5k
    if (!stream) {
2968
0
      return 0;
2969
0
    }
2970
2971
12.5k
    switch (frame->headers.cat) {
2972
12.5k
    case NGHTTP2_HCAT_REQUEST: {
2973
12.5k
      stream->state = NGHTTP2_STREAM_OPENING;
2974
12.5k
      if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
2975
11.2k
        nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
2976
11.2k
      }
2977
12.5k
      rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
2978
12.5k
      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
12.5k
      aux_data = &item->aux_data.headers;
2983
12.5k
      if (aux_data->dpw.data_prd.read_callback) {
2984
        /* nghttp2_submit_data_shared() makes a copy of
2985
           aux_data->dpw */
2986
1.34k
        rv = nghttp2_submit_data_shared(session, NGHTTP2_FLAG_END_STREAM,
2987
1.34k
                                        frame->hd.stream_id, &aux_data->dpw);
2988
1.34k
        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
1.34k
      }
2995
12.5k
      return 0;
2996
12.5k
    }
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
12.5k
    }
3030
12.5k
  }
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
8.10k
  case NGHTTP2_RST_STREAM:
3065
8.10k
    rv = nghttp2_session_close_stream(session, frame->hd.stream_id,
3066
8.10k
                                      frame->rst_stream.error_code);
3067
8.10k
    if (nghttp2_is_fatal(rv)) {
3068
0
      return rv;
3069
0
    }
3070
8.10k
    return 0;
3071
4.46k
  case NGHTTP2_GOAWAY: {
3072
4.46k
    nghttp2_goaway_aux_data *aux_data;
3073
3074
4.46k
    aux_data = &item->aux_data.goaway;
3075
3076
4.46k
    if ((aux_data->flags & NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE) == 0) {
3077
3078
4.46k
      if (aux_data->flags & NGHTTP2_GOAWAY_AUX_TERM_ON_SEND) {
3079
4.46k
        session->goaway_flags |= NGHTTP2_GOAWAY_TERM_SENT;
3080
4.46k
      }
3081
3082
4.46k
      session->goaway_flags |= NGHTTP2_GOAWAY_SENT;
3083
3084
4.46k
      rv = session_close_stream_on_goaway(session, frame->goaway.last_stream_id,
3085
4.46k
                                          1);
3086
3087
4.46k
      if (nghttp2_is_fatal(rv)) {
3088
0
        return rv;
3089
0
      }
3090
4.46k
    }
3091
3092
4.46k
    return 0;
3093
4.46k
  }
3094
12.4k
  case NGHTTP2_WINDOW_UPDATE:
3095
12.4k
    if (frame->hd.stream_id == 0) {
3096
12.4k
      session->window_update_queued = 0;
3097
12.4k
      if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
3098
12.4k
        rv = session_update_connection_consumed_size(session, 0);
3099
12.4k
      } else {
3100
0
        rv = nghttp2_session_update_recv_connection_window_size(session, 0);
3101
0
      }
3102
3103
12.4k
      if (nghttp2_is_fatal(rv)) {
3104
0
        return rv;
3105
0
      }
3106
3107
12.4k
      return 0;
3108
12.4k
    }
3109
3110
0
    stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
3111
0
    if (!stream) {
3112
0
      return 0;
3113
0
    }
3114
3115
0
    stream->window_update_queued = 0;
3116
3117
    /* We don't have to send WINDOW_UPDATE if END_STREAM from peer
3118
       is seen. */
3119
0
    if (stream->shut_flags & NGHTTP2_SHUT_RD) {
3120
0
      return 0;
3121
0
    }
3122
3123
0
    if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
3124
0
      rv = session_update_stream_consumed_size(session, stream, 0);
3125
0
    } else {
3126
0
      rv =
3127
0
          nghttp2_session_update_recv_stream_window_size(session, stream, 0, 1);
3128
0
    }
3129
3130
0
    if (nghttp2_is_fatal(rv)) {
3131
0
      return rv;
3132
0
    }
3133
3134
0
    return 0;
3135
18.8k
  default:
3136
18.8k
    return 0;
3137
56.3k
  }
3138
56.3k
}
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
59.4k
static void session_after_frame_sent2(nghttp2_session *session) {
3145
59.4k
  nghttp2_active_outbound_item *aob = &session->aob;
3146
59.4k
  nghttp2_outbound_item *item = aob->item;
3147
59.4k
  nghttp2_bufs *framebufs = &aob->framebufs;
3148
59.4k
  nghttp2_frame *frame;
3149
59.4k
  nghttp2_mem *mem;
3150
59.4k
  nghttp2_stream *stream;
3151
59.4k
  nghttp2_data_aux_data *aux_data;
3152
3153
59.4k
  mem = &session->mem;
3154
59.4k
  frame = &item->frame;
3155
3156
59.4k
  if (frame->hd.type != NGHTTP2_DATA) {
3157
3158
56.4k
    if (frame->hd.type == NGHTTP2_HEADERS ||
3159
56.4k
        frame->hd.type == NGHTTP2_PUSH_PROMISE) {
3160
3161
12.6k
      if (nghttp2_bufs_next_present(framebufs)) {
3162
123
        framebufs->cur = framebufs->cur->next;
3163
3164
123
        DEBUGF("send: next CONTINUATION frame, %zu bytes\n",
3165
123
               nghttp2_buf_len(&framebufs->cur->buf));
3166
3167
123
        return;
3168
123
      }
3169
12.6k
    }
3170
3171
56.3k
    active_outbound_item_reset(&session->aob, mem);
3172
3173
56.3k
    return;
3174
56.4k
  }
3175
3176
  /* DATA frame */
3177
3178
2.99k
  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
2.99k
  if (aux_data->eof) {
3185
1.06k
    active_outbound_item_reset(aob, mem);
3186
3187
1.06k
    return;
3188
1.06k
  }
3189
3190
  /* Reset no_copy here because next write may not use this. */
3191
1.93k
  aux_data->no_copy = 0;
3192
3193
1.93k
  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
1.93k
  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
1.93k
  aob->item = NULL;
3208
1.93k
  active_outbound_item_reset(&session->aob, mem);
3209
3210
1.93k
  return;
3211
1.93k
}
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
117k
                                                       int fast_cb) {
3245
117k
  int rv;
3246
117k
  nghttp2_active_outbound_item *aob;
3247
117k
  nghttp2_bufs *framebufs;
3248
117k
  nghttp2_mem *mem;
3249
3250
117k
  mem = &session->mem;
3251
117k
  aob = &session->aob;
3252
117k
  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
117k
  rv = nghttp2_session_adjust_idle_stream(session);
3258
117k
  if (nghttp2_is_fatal(rv)) {
3259
0
    return rv;
3260
0
  }
3261
3262
260k
  for (;;) {
3263
260k
    switch (aob->state) {
3264
115k
    case NGHTTP2_OB_POP_ITEM: {
3265
115k
      nghttp2_outbound_item *item;
3266
3267
115k
      item = nghttp2_session_pop_next_ob_item(session);
3268
115k
      if (item == NULL) {
3269
45.2k
        return 0;
3270
45.2k
      }
3271
3272
70.1k
      rv = session_prep_frame(session, item);
3273
70.1k
      if (rv == NGHTTP2_ERR_PAUSE) {
3274
0
        return 0;
3275
0
      }
3276
70.1k
      if (rv == NGHTTP2_ERR_DEFERRED) {
3277
807
        DEBUGF("send: frame transmission deferred\n");
3278
807
        break;
3279
807
      }
3280
69.3k
      if (rv < 0) {
3281
9.95k
        int32_t opened_stream_id = 0;
3282
9.95k
        uint32_t error_code = NGHTTP2_INTERNAL_ERROR;
3283
9.95k
        int rv2 = 0;
3284
3285
9.95k
        DEBUGF("send: frame preparation failed with %s\n",
3286
9.95k
               nghttp2_strerror(rv));
3287
        /* TODO If the error comes from compressor, the connection
3288
           must be closed. */
3289
9.95k
        if (item->frame.hd.type != NGHTTP2_DATA &&
3290
9.95k
            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
9.95k
        switch (item->frame.hd.type) {
3308
763
        case NGHTTP2_HEADERS:
3309
763
          if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) {
3310
763
            opened_stream_id = item->frame.hd.stream_id;
3311
763
            if (item->aux_data.headers.canceled) {
3312
685
              error_code = item->aux_data.headers.error_code;
3313
685
            } else {
3314
              /* Set error_code to REFUSED_STREAM so that application
3315
                 can send request again. */
3316
78
              error_code = NGHTTP2_REFUSED_STREAM;
3317
78
            }
3318
763
          }
3319
763
          break;
3320
0
        case NGHTTP2_PUSH_PROMISE:
3321
0
          opened_stream_id = item->frame.push_promise.promised_stream_id;
3322
0
          break;
3323
9.95k
        }
3324
9.95k
        if (opened_stream_id) {
3325
          /* careful not to override rv */
3326
763
          rv2 = nghttp2_session_close_stream(session, opened_stream_id,
3327
763
                                             error_code);
3328
763
        }
3329
3330
9.95k
        nghttp2_outbound_item_free(item, mem);
3331
9.95k
        nghttp2_mem_free(mem, item);
3332
9.95k
        active_outbound_item_reset(aob, mem);
3333
3334
9.95k
        if (nghttp2_is_fatal(rv2)) {
3335
0
          return rv2;
3336
0
        }
3337
3338
9.95k
        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
9.95k
        if (nghttp2_is_fatal(rv)) {
3345
0
          return rv;
3346
0
        }
3347
9.95k
        break;
3348
9.95k
      }
3349
3350
59.3k
      aob->item = item;
3351
3352
59.3k
      nghttp2_bufs_rewind(framebufs);
3353
3354
59.3k
      if (item->frame.hd.type != NGHTTP2_DATA) {
3355
56.3k
        nghttp2_frame *frame;
3356
3357
56.3k
        frame = &item->frame;
3358
3359
56.3k
        DEBUGF("send: next frame: payloadlen=%zu, type=%u, flags=0x%02x, "
3360
56.3k
               "stream_id=%d\n",
3361
56.3k
               frame->hd.length, frame->hd.type, frame->hd.flags,
3362
56.3k
               frame->hd.stream_id);
3363
3364
56.3k
        rv = session_call_before_frame_send(session, frame);
3365
56.3k
        if (nghttp2_is_fatal(rv)) {
3366
0
          return rv;
3367
0
        }
3368
3369
56.3k
        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
56.3k
      } else {
3414
2.99k
        DEBUGF("send: next frame: DATA\n");
3415
3416
2.99k
        if (item->aux_data.data.no_copy) {
3417
0
          aob->state = NGHTTP2_OB_SEND_NO_COPY;
3418
0
          break;
3419
0
        }
3420
2.99k
      }
3421
3422
59.3k
      DEBUGF("send: start transmitting frame type=%u, length=%td\n",
3423
59.3k
             framebufs->cur->buf.pos[3],
3424
59.3k
             framebufs->cur->buf.last - framebufs->cur->buf.pos);
3425
3426
59.3k
      aob->state = NGHTTP2_OB_SEND_DATA;
3427
3428
59.3k
      break;
3429
59.3k
    }
3430
118k
    case NGHTTP2_OB_SEND_DATA: {
3431
118k
      size_t datalen;
3432
118k
      nghttp2_buf *buf;
3433
3434
118k
      buf = &framebufs->cur->buf;
3435
3436
118k
      if (buf->pos == buf->last) {
3437
59.4k
        DEBUGF("send: end transmission of a frame\n");
3438
3439
        /* Frame has completely sent */
3440
59.4k
        if (fast_cb) {
3441
0
          session_after_frame_sent2(session);
3442
59.4k
        } else {
3443
59.4k
          rv = session_after_frame_sent1(session);
3444
59.4k
          if (rv < 0) {
3445
            /* FATAL */
3446
0
            assert(nghttp2_is_fatal(rv));
3447
0
            return rv;
3448
0
          }
3449
59.4k
          session_after_frame_sent2(session);
3450
59.4k
        }
3451
        /* We have already adjusted the next state */
3452
59.4k
        break;
3453
59.4k
      }
3454
3455
59.4k
      *data_ptr = buf->pos;
3456
59.4k
      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
59.4k
      buf->pos += datalen;
3461
3462
59.4k
      return (nghttp2_ssize)datalen;
3463
118k
    }
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
26.1k
    case NGHTTP2_OB_SEND_CLIENT_MAGIC: {
3523
26.1k
      size_t datalen;
3524
26.1k
      nghttp2_buf *buf;
3525
3526
26.1k
      buf = &framebufs->cur->buf;
3527
3528
26.1k
      if (buf->pos == buf->last) {
3529
13.0k
        DEBUGF("send: end transmission of client magic\n");
3530
13.0k
        active_outbound_item_reset(aob, mem);
3531
13.0k
        break;
3532
13.0k
      }
3533
3534
13.0k
      *data_ptr = buf->pos;
3535
13.0k
      datalen = nghttp2_buf_len(buf);
3536
3537
13.0k
      buf->pos += datalen;
3538
3539
13.0k
      return (nghttp2_ssize)datalen;
3540
26.1k
    }
3541
260k
    }
3542
260k
  }
3543
117k
}
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
45.2k
int nghttp2_session_send(nghttp2_session *session) {
3578
45.2k
  const uint8_t *data = NULL;
3579
45.2k
  nghttp2_ssize datalen;
3580
45.2k
  nghttp2_ssize sentlen;
3581
45.2k
  nghttp2_bufs *framebufs;
3582
3583
45.2k
  framebufs = &session->aob.framebufs;
3584
3585
117k
  for (;;) {
3586
117k
    datalen = nghttp2_session_mem_send_internal(session, &data, 0);
3587
117k
    if (datalen <= 0) {
3588
45.2k
      return (int)datalen;
3589
45.2k
    }
3590
72.5k
    if (session->callbacks.send_callback2) {
3591
0
      sentlen = session->callbacks.send_callback2(
3592
0
          session, data, (size_t)datalen, 0, session->user_data);
3593
72.5k
    } else {
3594
72.5k
      sentlen = (nghttp2_ssize)session->callbacks.send_callback(
3595
72.5k
          session, data, (size_t)datalen, 0, session->user_data);
3596
72.5k
    }
3597
72.5k
    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
72.5k
    framebufs->cur->buf.pos -= datalen - sentlen;
3608
72.5k
  }
3609
45.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
22.3k
                                       const nghttp2_frame_hd *hd) {
3634
22.3k
  int rv;
3635
3636
22.3k
  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
22.3k
  return 0;
3647
22.3k
}
3648
3649
static int session_call_on_frame_received(nghttp2_session *session,
3650
15.2k
                                          nghttp2_frame *frame) {
3651
15.2k
  int rv;
3652
15.2k
  if (session->callbacks.on_frame_recv_callback) {
3653
15.2k
    rv = session->callbacks.on_frame_recv_callback(session, frame,
3654
15.2k
                                                   session->user_data);
3655
15.2k
    if (rv != 0) {
3656
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
3657
0
    }
3658
15.2k
  }
3659
15.2k
  return 0;
3660
15.2k
}
3661
3662
static int session_call_on_begin_headers(nghttp2_session *session,
3663
5.11k
                                         nghttp2_frame *frame) {
3664
5.11k
  int rv;
3665
5.11k
  DEBUGF("recv: call on_begin_headers callback stream_id=%d\n",
3666
5.11k
         frame->hd.stream_id);
3667
5.11k
  if (session->callbacks.on_begin_headers_callback) {
3668
5.11k
    rv = session->callbacks.on_begin_headers_callback(session, frame,
3669
5.11k
                                                      session->user_data);
3670
5.11k
    if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
3671
0
      return rv;
3672
0
    }
3673
5.11k
    if (rv != 0) {
3674
0
      return NGHTTP2_ERR_CALLBACK_FAILURE;
3675
0
    }
3676
5.11k
  }
3677
5.11k
  return 0;
3678
5.11k
}
3679
3680
static int session_call_on_header(nghttp2_session *session,
3681
                                  const nghttp2_frame *frame,
3682
132k
                                  const nghttp2_hd_nv *nv) {
3683
132k
  int rv = 0;
3684
132k
  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
132k
  } else if (session->callbacks.on_header_callback) {
3688
132k
    rv = session->callbacks.on_header_callback(
3689
132k
        session, frame, nv->name->base, nv->name->len, nv->value->base,
3690
132k
        nv->value->len, nv->flags, session->user_data);
3691
132k
  }
3692
3693
132k
  if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
3694
0
    return rv;
3695
0
  }
3696
132k
  if (rv != 0) {
3697
0
    return NGHTTP2_ERR_CALLBACK_FAILURE;
3698
0
  }
3699
3700
132k
  return 0;
3701
132k
}
3702
3703
static int session_call_on_invalid_header(nghttp2_session *session,
3704
                                          const nghttp2_frame *frame,
3705
453
                                          const nghttp2_hd_nv *nv) {
3706
453
  int rv;
3707
453
  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
453
  } 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
453
  } else {
3715
453
    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
3716
453
  }
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
89
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
89
  return nghttp2_session_terminate_session(session, NGHTTP2_FRAME_SIZE_ERROR);
3782
89
}
3783
3784
2.83k
static uint32_t get_error_code_from_lib_error_code(int lib_error_code) {
3785
2.83k
  switch (lib_error_code) {
3786
2
  case NGHTTP2_ERR_STREAM_CLOSED:
3787
2
    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
315
  case NGHTTP2_ERR_FLOW_CONTROL:
3793
315
    return NGHTTP2_FLOW_CONTROL_ERROR;
3794
0
  case NGHTTP2_ERR_REFUSED_STREAM:
3795
0
    return NGHTTP2_REFUSED_STREAM;
3796
1.10k
  case NGHTTP2_ERR_PROTO:
3797
2.46k
  case NGHTTP2_ERR_HTTP_HEADER:
3798
2.51k
  case NGHTTP2_ERR_HTTP_MESSAGING:
3799
2.51k
    return NGHTTP2_PROTOCOL_ERROR;
3800
0
  default:
3801
0
    return NGHTTP2_INTERNAL_ERROR;
3802
2.83k
  }
3803
2.83k
}
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.67k
                                          int lib_error_code) {
3830
1.67k
  int rv;
3831
1.67k
  rv = nghttp2_session_add_rst_stream(
3832
1.67k
      session, stream_id, get_error_code_from_lib_error_code(lib_error_code));
3833
1.67k
  if (rv != 0) {
3834
0
    return rv;
3835
0
  }
3836
1.67k
  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.67k
  return 0;
3843
1.67k
}
3844
3845
static int session_handle_invalid_stream(nghttp2_session *session,
3846
                                         nghttp2_frame *frame,
3847
263
                                         int lib_error_code) {
3848
263
  return session_handle_invalid_stream2(session, frame->hd.stream_id, frame,
3849
263
                                        lib_error_code);
3850
263
}
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
1.15k
                                             const char *reason) {
3870
1.15k
  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
1.15k
  return nghttp2_session_terminate_session_with_reason(
3877
1.15k
      session, get_error_code_from_lib_error_code(lib_error_code), reason);
3878
1.15k
}
3879
3880
static int session_inflate_handle_invalid_connection(nghttp2_session *session,
3881
                                                     nghttp2_frame *frame,
3882
                                                     int lib_error_code,
3883
320
                                                     const char *reason) {
3884
320
  int rv;
3885
320
  rv =
3886
320
      session_handle_invalid_connection(session, frame, lib_error_code, reason);
3887
320
  if (nghttp2_is_fatal(rv)) {
3888
0
    return rv;
3889
0
  }
3890
320
  return NGHTTP2_ERR_IGN_HEADER_BLOCK;
3891
320
}
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
7.21k
                                int final, int call_header_cb) {
3923
7.21k
  nghttp2_ssize proclen;
3924
7.21k
  int rv;
3925
7.21k
  int inflate_flags;
3926
7.21k
  nghttp2_hd_nv nv;
3927
7.21k
  nghttp2_stream *stream;
3928
7.21k
  nghttp2_stream *subject_stream;
3929
7.21k
  int trailer = 0;
3930
3931
7.21k
  *readlen_ptr = 0;
3932
7.21k
  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
3933
3934
7.21k
  if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
3935
485
    subject_stream = nghttp2_session_get_stream(
3936
485
        session, frame->push_promise.promised_stream_id);
3937
6.73k
  } else {
3938
6.73k
    subject_stream = stream;
3939
6.73k
    trailer = session_trailer_headers(session, stream, frame);
3940
6.73k
  }
3941
3942
7.21k
  DEBUGF("recv: decoding header block %zu bytes\n", inlen);
3943
178k
  for (;;) {
3944
178k
    inflate_flags = 0;
3945
178k
    proclen = nghttp2_hd_inflate_hd_nv(&session->hd_inflater, &nv,
3946
178k
                                       &inflate_flags, in, inlen, final);
3947
178k
    if (nghttp2_is_fatal((int)proclen)) {
3948
0
      return (int)proclen;
3949
0
    }
3950
178k
    if (proclen < 0) {
3951
1.55k
      if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) {
3952
960
        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
960
          rv = nghttp2_session_add_rst_stream(
3957
960
              session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR);
3958
3959
960
          if (nghttp2_is_fatal(rv)) {
3960
0
            return rv;
3961
0
          }
3962
960
        }
3963
960
      }
3964
1.55k
      rv =
3965
1.55k
          nghttp2_session_terminate_session(session, NGHTTP2_COMPRESSION_ERROR);
3966
1.55k
      if (nghttp2_is_fatal(rv)) {
3967
0
        return rv;
3968
0
      }
3969
3970
1.55k
      return NGHTTP2_ERR_HEADER_COMP;
3971
1.55k
    }
3972
176k
    in += proclen;
3973
176k
    inlen -= (size_t)proclen;
3974
176k
    *readlen_ptr += (size_t)proclen;
3975
3976
176k
    DEBUGF("recv: proclen=%td\n", proclen);
3977
3978
176k
    if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) {
3979
134k
      rv = 0;
3980
134k
      if (subject_stream) {
3981
134k
        if (session_enforce_http_messaging(session)) {
3982
134k
          rv = nghttp2_http_on_header(session, subject_stream, frame, &nv,
3983
134k
                                      trailer);
3984
3985
134k
          if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) {
3986
            /* Don't overwrite rv here */
3987
453
            int rv2;
3988
3989
453
            rv2 = session_call_on_invalid_header(session, frame, &nv);
3990
453
            if (rv2 == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
3991
453
              rv = NGHTTP2_ERR_HTTP_HEADER;
3992
453
            } 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
453
          }
4014
4015
134k
          if (rv == NGHTTP2_ERR_HTTP_HEADER) {
4016
1.36k
            DEBUGF("recv: HTTP error: type=%u, id=%d, header %.*s: %.*s\n",
4017
1.36k
                   frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
4018
1.36k
                   nv.name->base, (int)nv.value->len, nv.value->base);
4019
4020
1.36k
            rv = session_call_error_callback(
4021
1.36k
                session, NGHTTP2_ERR_HTTP_HEADER,
4022
1.36k
                "Invalid HTTP header field was received: frame type: "
4023
1.36k
                "%u, stream: %d, name: [%.*s], value: [%.*s]",
4024
1.36k
                frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
4025
1.36k
                nv.name->base, (int)nv.value->len, nv.value->base);
4026
4027
1.36k
            if (nghttp2_is_fatal(rv)) {
4028
0
              return rv;
4029
0
            }
4030
4031
1.36k
            rv = session_handle_invalid_stream2(session,
4032
1.36k
                                                subject_stream->stream_id,
4033
1.36k
                                                frame, NGHTTP2_ERR_HTTP_HEADER);
4034
1.36k
            if (nghttp2_is_fatal(rv)) {
4035
0
              return rv;
4036
0
            }
4037
1.36k
            return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
4038
1.36k
          }
4039
134k
        }
4040
133k
        if (rv == 0) {
4041
132k
          rv = session_call_on_header(session, frame, &nv);
4042
          /* This handles NGHTTP2_ERR_PAUSE and
4043
             NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE as well */
4044
132k
          if (rv != 0) {
4045
0
            return rv;
4046
0
          }
4047
132k
        }
4048
133k
      }
4049
134k
    }
4050
175k
    if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
4051
1.56k
      nghttp2_hd_inflate_end_headers(&session->hd_inflater);
4052
1.56k
      break;
4053
1.56k
    }
4054
173k
    if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) {
4055
2.73k
      break;
4056
2.73k
    }
4057
173k
  }
4058
4.29k
  return 0;
4059
7.21k
}
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
930
                                               nghttp2_stream *stream) {
4075
930
  int rv;
4076
4077
930
  assert(frame->hd.type == NGHTTP2_HEADERS);
4078
4079
930
  if (session->server && session_enforce_http_messaging(session) &&
4080
930
      frame->headers.cat == NGHTTP2_HCAT_REQUEST &&
4081
930
      (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) &&
4082
930
      !(stream->flags & NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES) &&
4083
930
      (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
930
  if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
4092
216
    return 0;
4093
216
  }
4094
4095
714
  nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
4096
714
  rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
4097
714
  if (nghttp2_is_fatal(rv)) {
4098
0
    return rv;
4099
0
  }
4100
4101
714
  return 0;
4102
714
}
4103
4104
978
static int session_after_header_block_received(nghttp2_session *session) {
4105
978
  int rv = 0;
4106
978
  nghttp2_frame *frame = &session->iframe.frame;
4107
978
  nghttp2_stream *stream;
4108
4109
  /* We don't call on_frame_recv_callback if stream has been closed
4110
     already or being closed. */
4111
978
  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
4112
978
  if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) {
4113
0
    return 0;
4114
0
  }
4115
4116
978
  if (session_enforce_http_messaging(session)) {
4117
978
    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
978
    } else {
4126
978
      assert(frame->hd.type == NGHTTP2_HEADERS);
4127
978
      switch (frame->headers.cat) {
4128
0
      case NGHTTP2_HCAT_REQUEST:
4129
0
        rv = nghttp2_http_on_request_headers(stream, frame);
4130
0
        break;
4131
947
      case NGHTTP2_HCAT_RESPONSE:
4132
947
      case NGHTTP2_HCAT_PUSH_RESPONSE:
4133
947
        rv = nghttp2_http_on_response_headers(stream);
4134
947
        break;
4135
31
      case NGHTTP2_HCAT_HEADERS:
4136
31
        if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
4137
14
          assert(!session->server);
4138
14
          rv = nghttp2_http_on_response_headers(stream);
4139
17
        } else {
4140
17
          rv = nghttp2_http_on_trailer_headers(stream, frame);
4141
17
        }
4142
31
        break;
4143
31
      default:
4144
0
        assert(0);
4145
978
      }
4146
978
      if (rv == 0 && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
4147
718
        rv = nghttp2_http_on_remote_end_stream(stream);
4148
718
      }
4149
978
    }
4150
978
    if (rv != 0) {
4151
48
      int32_t stream_id;
4152
4153
48
      if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
4154
0
        stream_id = frame->push_promise.promised_stream_id;
4155
48
      } else {
4156
48
        stream_id = frame->hd.stream_id;
4157
48
      }
4158
4159
48
      rv = session_handle_invalid_stream2(session, stream_id, frame,
4160
48
                                          NGHTTP2_ERR_HTTP_MESSAGING);
4161
48
      if (nghttp2_is_fatal(rv)) {
4162
0
        return rv;
4163
0
      }
4164
4165
48
      if (frame->hd.type == NGHTTP2_HEADERS &&
4166
48
          (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
4167
19
        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
19
      }
4171
48
      return 0;
4172
48
    }
4173
978
  }
4174
4175
930
  rv = session_call_on_frame_received(session, frame);
4176
930
  if (nghttp2_is_fatal(rv)) {
4177
0
    return rv;
4178
0
  }
4179
4180
930
  if (frame->hd.type != NGHTTP2_HEADERS) {
4181
0
    return 0;
4182
0
  }
4183
4184
930
  return session_end_stream_headers_received(session, frame, stream);
4185
930
}
4186
4187
int nghttp2_session_on_request_headers_received(nghttp2_session *session,
4188
298
                                                nghttp2_frame *frame) {
4189
298
  int rv = 0;
4190
298
  nghttp2_stream *stream;
4191
298
  if (frame->hd.stream_id == 0) {
4192
10
    return session_inflate_handle_invalid_connection(
4193
10
        session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0");
4194
10
  }
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
288
  if (!session->server) {
4200
288
    if (session_detect_idle_stream(session, frame->hd.stream_id)) {
4201
146
      return session_inflate_handle_invalid_connection(
4202
146
          session, frame, NGHTTP2_ERR_PROTO,
4203
146
          "request HEADERS: client received request");
4204
146
    }
4205
4206
142
    return NGHTTP2_ERR_IGN_HEADER_BLOCK;
4207
288
  }
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
5.06k
                                                 nghttp2_stream *stream) {
4294
5.06k
  int rv;
4295
  /* This function is only called if stream->state ==
4296
     NGHTTP2_STREAM_OPENING and stream_id is local side initiated. */
4297
5.06k
  assert(stream->state == NGHTTP2_STREAM_OPENING &&
4298
5.06k
         nghttp2_session_is_my_stream_id(session, frame->hd.stream_id));
4299
5.06k
  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
5.06k
  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
5.06k
  stream->state = NGHTTP2_STREAM_OPENED;
4316
5.06k
  rv = session_call_on_begin_headers(session, frame);
4317
5.06k
  if (rv != 0) {
4318
0
    return rv;
4319
0
  }
4320
5.06k
  return 0;
4321
5.06k
}
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
159
                                        nghttp2_stream *stream) {
4371
159
  int rv = 0;
4372
159
  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
159
  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
2
    return session_inflate_handle_invalid_connection(
4386
2
        session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed");
4387
2
  }
4388
157
  if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
4389
157
    if (stream->state == NGHTTP2_STREAM_OPENED) {
4390
51
      rv = session_call_on_begin_headers(session, frame);
4391
51
      if (rv != 0) {
4392
0
        return rv;
4393
0
      }
4394
51
      return 0;
4395
51
    }
4396
4397
106
    return NGHTTP2_ERR_IGN_HEADER_BLOCK;
4398
157
  }
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
5.52k
static int session_process_headers_frame(nghttp2_session *session) {
4414
5.52k
  nghttp2_inbound_frame *iframe = &session->iframe;
4415
5.52k
  nghttp2_frame *frame = &iframe->frame;
4416
5.52k
  nghttp2_stream *stream;
4417
4418
5.52k
  nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos);
4419
4420
5.52k
  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
4421
5.52k
  if (!stream) {
4422
298
    frame->headers.cat = NGHTTP2_HCAT_REQUEST;
4423
298
    return nghttp2_session_on_request_headers_received(session, frame);
4424
298
  }
4425
4426
5.22k
  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
5.22k
  if (stream->state == NGHTTP2_STREAM_OPENING &&
4433
5.22k
      nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
4434
5.06k
    frame->headers.cat = NGHTTP2_HCAT_RESPONSE;
4435
5.06k
    return nghttp2_session_on_response_headers_received(session, frame, stream);
4436
5.06k
  }
4437
4438
159
  frame->headers.cat = NGHTTP2_HCAT_HEADERS;
4439
159
  return nghttp2_session_on_headers_received(session, frame, stream);
4440
5.22k
}
4441
4442
int nghttp2_session_on_priority_received(nghttp2_session *session,
4443
113
                                         nghttp2_frame *frame) {
4444
113
  int rv;
4445
113
  nghttp2_stream *stream;
4446
4447
113
  assert(!session_no_rfc7540_pri_no_fallback(session));
4448
4449
113
  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
111
  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
109
  if (!session->server) {
4460
    /* Re-prioritization works only in server */
4461
109
    return session_call_on_frame_received(session, frame);
4462
109
  }
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
113
static int session_process_priority_frame(nghttp2_session *session) {
4503
113
  nghttp2_inbound_frame *iframe = &session->iframe;
4504
113
  nghttp2_frame *frame = &iframe->frame;
4505
4506
113
  assert(!session_no_rfc7540_pri_no_fallback(session));
4507
4508
113
  nghttp2_frame_unpack_priority_payload(&frame->priority, iframe->sbuf.pos);
4509
4510
113
  return nghttp2_session_on_priority_received(session, frame);
4511
113
}
4512
4513
440
static int session_update_stream_reset_ratelim(nghttp2_session *session) {
4514
440
  if (!session->server || (session->goaway_flags & NGHTTP2_GOAWAY_SUBMITTED)) {
4515
440
    return 0;
4516
440
  }
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
529
                                           nghttp2_frame *frame) {
4532
529
  int rv;
4533
529
  nghttp2_stream *stream;
4534
529
  if (frame->hd.stream_id == 0) {
4535
3
    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4536
3
                                             "RST_STREAM: stream_id == 0");
4537
3
  }
4538
4539
526
  if (session_detect_idle_stream(session, frame->hd.stream_id)) {
4540
86
    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4541
86
                                             "RST_STREAM: stream in idle");
4542
86
  }
4543
4544
440
  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
4545
440
  if (stream) {
4546
    /* We may use stream->shut_flags for strict error checking. */
4547
242
    nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
4548
242
  }
4549
4550
440
  rv = session_call_on_frame_received(session, frame);
4551
440
  if (rv != 0) {
4552
0
    return rv;
4553
0
  }
4554
440
  rv = nghttp2_session_close_stream(session, frame->hd.stream_id,
4555
440
                                    frame->rst_stream.error_code);
4556
440
  if (nghttp2_is_fatal(rv)) {
4557
0
    return rv;
4558
0
  }
4559
4560
440
  return session_update_stream_reset_ratelim(session);
4561
440
}
4562
4563
529
static int session_process_rst_stream_frame(nghttp2_session *session) {
4564
529
  nghttp2_inbound_frame *iframe = &session->iframe;
4565
529
  nghttp2_frame *frame = &iframe->frame;
4566
4567
529
  nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, iframe->sbuf.pos);
4568
4569
529
  return nghttp2_session_on_rst_stream_received(session, frame);
4570
529
}
4571
4572
910
static int update_remote_initial_window_size_func(void *entry, void *ptr) {
4573
910
  int rv;
4574
910
  nghttp2_update_window_size_arg *arg;
4575
910
  nghttp2_stream *stream;
4576
4577
910
  arg = (nghttp2_update_window_size_arg *)ptr;
4578
910
  stream = (nghttp2_stream *)entry;
4579
4580
910
  rv = nghttp2_stream_update_remote_initial_window_size(
4581
910
      stream, arg->new_window_size, arg->old_window_size);
4582
910
  if (rv != 0) {
4583
121
    return nghttp2_session_add_rst_stream(arg->session, stream->stream_id,
4584
121
                                          NGHTTP2_FLOW_CONTROL_ERROR);
4585
121
  }
4586
4587
  /* If window size gets positive, push deferred DATA frame to
4588
     outbound queue. */
4589
789
  if (stream->remote_window_size > 0 &&
4590
789
      nghttp2_stream_check_deferred_by_flow_control(stream)) {
4591
4592
6
    rv = session_resume_deferred_stream_item(
4593
6
        arg->session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
4594
4595
6
    if (nghttp2_is_fatal(rv)) {
4596
0
      return rv;
4597
0
    }
4598
6
  }
4599
789
  return 0;
4600
789
}
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
1.04k
                                          int32_t new_initial_window_size) {
4615
1.04k
  nghttp2_update_window_size_arg arg;
4616
4617
1.04k
  arg.session = session;
4618
1.04k
  arg.new_window_size = new_initial_window_size;
4619
1.04k
  arg.old_window_size = (int32_t)session->remote_settings.initial_window_size;
4620
4621
1.04k
  return nghttp2_map_each(&session->streams,
4622
1.04k
                          update_remote_initial_window_size_func, &arg);
4623
1.04k
}
4624
4625
51
static int update_local_initial_window_size_func(void *entry, void *ptr) {
4626
51
  int rv;
4627
51
  nghttp2_update_window_size_arg *arg;
4628
51
  nghttp2_stream *stream;
4629
51
  arg = (nghttp2_update_window_size_arg *)ptr;
4630
51
  stream = (nghttp2_stream *)entry;
4631
51
  rv = nghttp2_stream_update_local_initial_window_size(
4632
51
      stream, arg->new_window_size, arg->old_window_size);
4633
51
  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
51
  if (stream->window_update_queued) {
4639
0
    return 0;
4640
0
  }
4641
4642
51
  if (arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
4643
51
    return session_update_stream_consumed_size(arg->session, stream, 0);
4644
51
  }
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
73
                                         int32_t old_initial_window_size) {
4675
73
  nghttp2_update_window_size_arg arg;
4676
73
  arg.session = session;
4677
73
  arg.new_window_size = new_initial_window_size;
4678
73
  arg.old_window_size = old_initial_window_size;
4679
73
  return nghttp2_map_each(&session->streams,
4680
73
                          update_local_initial_window_size_func, &arg);
4681
73
}
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
73
                                          size_t niv) {
4699
73
  int rv;
4700
73
  size_t i;
4701
73
  int32_t new_initial_window_size = -1;
4702
73
  uint32_t header_table_size = 0;
4703
73
  uint32_t min_header_table_size = UINT32_MAX;
4704
73
  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
292
  for (i = 0; i < niv; ++i) {
4709
219
    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
73
    case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
4717
73
      new_initial_window_size = (int32_t)iv[i].value;
4718
73
      break;
4719
219
    }
4720
219
  }
4721
73
  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
73
  if (new_initial_window_size != -1) {
4737
73
    rv = session_update_local_initial_window_size(
4738
73
        session, new_initial_window_size,
4739
73
        (int32_t)session->local_settings.initial_window_size);
4740
73
    if (rv != 0) {
4741
0
      return rv;
4742
0
    }
4743
73
  }
4744
4745
292
  for (i = 0; i < niv; ++i) {
4746
219
    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
73
    case NGHTTP2_SETTINGS_ENABLE_PUSH:
4751
73
      session->local_settings.enable_push = iv[i].value;
4752
73
      break;
4753
73
    case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
4754
73
      session->local_settings.max_concurrent_streams = iv[i].value;
4755
73
      break;
4756
73
    case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
4757
73
      session->local_settings.initial_window_size = iv[i].value;
4758
73
      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
219
    }
4772
219
  }
4773
4774
73
  return 0;
4775
73
}
4776
4777
int nghttp2_session_on_settings_received(nghttp2_session *session,
4778
11.6k
                                         nghttp2_frame *frame, int noack) {
4779
11.6k
  int rv;
4780
11.6k
  size_t i;
4781
11.6k
  nghttp2_mem *mem;
4782
11.6k
  nghttp2_inflight_settings *settings;
4783
4784
11.6k
  mem = &session->mem;
4785
4786
11.6k
  if (frame->hd.stream_id != 0) {
4787
157
    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
4788
157
                                             "SETTINGS: stream_id != 0");
4789
157
  }
4790
11.5k
  if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
4791
78
    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
78
    settings = session->inflight_settings_head;
4798
4799
78
    if (!settings) {
4800
5
      return session_handle_invalid_connection(
4801
5
          session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: unexpected ACK");
4802
5
    }
4803
4804
73
    rv = nghttp2_session_update_local_settings(session, settings->iv,
4805
73
                                               settings->niv);
4806
4807
73
    session->inflight_settings_head = settings->next;
4808
4809
73
    inflight_settings_del(settings, mem);
4810
4811
73
    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
73
    return session_call_on_frame_received(session, frame);
4818
73
  }
4819
4820
11.4k
  if (!session->remote_settings_received) {
4821
8.98k
    session->remote_settings.max_concurrent_streams =
4822
8.98k
        NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;
4823
8.98k
    session->remote_settings_received = 1;
4824
8.98k
  }
4825
4826
35.9k
  for (i = 0; i < frame->settings.niv; ++i) {
4827
24.8k
    nghttp2_settings_entry *entry = &frame->settings.iv[i];
4828
4829
24.8k
    switch (entry->settings_id) {
4830
1.73k
    case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
4831
4832
1.73k
      rv = nghttp2_hd_deflate_change_table_size(&session->hd_deflater,
4833
1.73k
                                                entry->value);
4834
1.73k
      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.73k
      session->remote_settings.header_table_size = entry->value;
4844
4845
1.73k
      break;
4846
148
    case NGHTTP2_SETTINGS_ENABLE_PUSH:
4847
4848
148
      if (entry->value != 0 && entry->value != 1) {
4849
90
        return session_handle_invalid_connection(
4850
90
            session, frame, NGHTTP2_ERR_PROTO,
4851
90
            "SETTINGS: invalid SETTINGS_ENBLE_PUSH");
4852
90
      }
4853
4854
58
      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
56
      session->remote_settings.enable_push = entry->value;
4861
4862
56
      break;
4863
656
    case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
4864
4865
656
      session->remote_settings.max_concurrent_streams = entry->value;
4866
4867
656
      break;
4868
1.08k
    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
1.08k
      if (entry->value > NGHTTP2_MAX_WINDOW_SIZE) {
4873
43
        return session_handle_invalid_connection(
4874
43
            session, frame, NGHTTP2_ERR_FLOW_CONTROL,
4875
43
            "SETTINGS: too large SETTINGS_INITIAL_WINDOW_SIZE");
4876
43
      }
4877
4878
1.04k
      rv = session_update_remote_initial_window_size(session,
4879
1.04k
                                                     (int32_t)entry->value);
4880
4881
1.04k
      if (nghttp2_is_fatal(rv)) {
4882
0
        return rv;
4883
0
      }
4884
4885
1.04k
      if (rv != 0) {
4886
0
        return session_handle_invalid_connection(
4887
0
            session, frame, NGHTTP2_ERR_FLOW_CONTROL, NULL);
4888
0
      }
4889
4890
1.04k
      session->remote_settings.initial_window_size = entry->value;
4891
4892
1.04k
      break;
4893
216
    case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
4894
4895
216
      if (entry->value < NGHTTP2_MAX_FRAME_SIZE_MIN ||
4896
216
          entry->value > NGHTTP2_MAX_FRAME_SIZE_MAX) {
4897
78
        return session_handle_invalid_connection(
4898
78
            session, frame, NGHTTP2_ERR_PROTO,
4899
78
            "SETTINGS: invalid SETTINGS_MAX_FRAME_SIZE");
4900
78
      }
4901
4902
138
      session->remote_settings.max_frame_size = entry->value;
4903
4904
138
      break;
4905
132
    case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
4906
4907
132
      session->remote_settings.max_header_list_size = entry->value;
4908
4909
132
      break;
4910
140
    case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
4911
4912
140
      if (entry->value != 0 && entry->value != 1) {
4913
82
        return session_handle_invalid_connection(
4914
82
            session, frame, NGHTTP2_ERR_PROTO,
4915
82
            "SETTINGS: invalid SETTINGS_ENABLE_CONNECT_PROTOCOL");
4916
82
      }
4917
4918
58
      if (!session->server &&
4919
58
          session->remote_settings.enable_connect_protocol &&
4920
58
          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
56
      session->remote_settings.enable_connect_protocol = entry->value;
4928
4929
56
      break;
4930
166
    case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
4931
4932
166
      if (entry->value != 0 && entry->value != 1) {
4933
81
        return session_handle_invalid_connection(
4934
81
            session, frame, NGHTTP2_ERR_PROTO,
4935
81
            "SETTINGS: invalid SETTINGS_NO_RFC7540_PRIORITIES");
4936
81
      }
4937
4938
85
      if (session->remote_settings.no_rfc7540_priorities != UINT32_MAX &&
4939
85
          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
83
      session->remote_settings.no_rfc7540_priorities = entry->value;
4946
4947
83
      break;
4948
24.8k
    }
4949
24.8k
  }
4950
4951
11.0k
  if (session->remote_settings.no_rfc7540_priorities == UINT32_MAX) {
4952
8.61k
    session->remote_settings.no_rfc7540_priorities = 0;
4953
4954
8.61k
    if (session->server && session->pending_no_rfc7540_priorities &&
4955
8.61k
        (session->opt_flags &
4956
0
         NGHTTP2_OPTMASK_SERVER_FALLBACK_RFC7540_PRIORITIES)) {
4957
0
      session->fallback_rfc7540_priorities = 1;
4958
0
    }
4959
8.61k
  }
4960
4961
11.0k
  if (!noack && !session_is_closing(session)) {
4962
10.9k
    rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0);
4963
4964
10.9k
    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
10.9k
  }
4973
4974
11.0k
  return session_call_on_frame_received(session, frame);
4975
11.0k
}
4976
4977
11.6k
static int session_process_settings_frame(nghttp2_session *session) {
4978
11.6k
  nghttp2_inbound_frame *iframe = &session->iframe;
4979
11.6k
  nghttp2_frame *frame = &iframe->frame;
4980
11.6k
  size_t i;
4981
11.6k
  nghttp2_settings_entry min_header_size_entry;
4982
4983
11.6k
  if (iframe->max_niv) {
4984
4.46k
    min_header_size_entry = iframe->iv[iframe->max_niv - 1];
4985
4986
4.46k
    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
5.72k
      for (i = 0; i < iframe->niv; ++i) {
4990
5.72k
        if (iframe->iv[i].settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) {
4991
1.49k
          break;
4992
1.49k
        }
4993
5.72k
      }
4994
4995
1.49k
      assert(i < iframe->niv);
4996
4997
1.49k
      if (min_header_size_entry.value != iframe->iv[i].value) {
4998
345
        iframe->iv[iframe->niv++] = iframe->iv[i];
4999
345
        iframe->iv[i] = min_header_size_entry;
5000
345
      }
5001
1.49k
    }
5002
4.46k
  }
5003
5004
11.6k
  nghttp2_frame_unpack_settings_payload(&frame->settings, iframe->iv,
5005
11.6k
                                        iframe->niv);
5006
5007
11.6k
  iframe->iv = NULL;
5008
11.6k
  iframe->niv = 0;
5009
11.6k
  iframe->max_niv = 0;
5010
5011
11.6k
  return nghttp2_session_on_settings_received(session, frame, 0 /* ACK */);
5012
11.6k
}
5013
5014
int nghttp2_session_on_push_promise_received(nghttp2_session *session,
5015
620
                                             nghttp2_frame *frame) {
5016
620
  int rv;
5017
620
  nghttp2_stream *stream;
5018
620
  nghttp2_stream *promised_stream;
5019
620
  nghttp2_priority_spec pri_spec;
5020
5021
620
  if (frame->hd.stream_id == 0) {
5022
9
    return session_inflate_handle_invalid_connection(
5023
9
        session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream_id == 0");
5024
9
  }
5025
611
  if (session->server || session->local_settings.enable_push == 0) {
5026
5
    return session_inflate_handle_invalid_connection(
5027
5
        session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: push disabled");
5028
5
  }
5029
5030
606
  if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
5031
17
    return session_inflate_handle_invalid_connection(
5032
17
        session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: invalid stream_id");
5033
17
  }
5034
5035
589
  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
589
  if (!session_is_new_peer_stream_id(session,
5041
589
                                     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
66
    return session_inflate_handle_invalid_connection(
5046
66
        session, frame, NGHTTP2_ERR_PROTO,
5047
66
        "PUSH_PROMISE: invalid promised_stream_id");
5048
66
  }
5049
5050
523
  if (session_detect_idle_stream(session, frame->hd.stream_id)) {
5051
65
    return session_inflate_handle_invalid_connection(
5052
65
        session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream in idle");
5053
65
  }
5054
5055
458
  session->last_recv_stream_id = frame->push_promise.promised_stream_id;
5056
458
  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
5057
458
  if (!stream || stream->state == NGHTTP2_STREAM_CLOSING ||
5058
458
      !session->pending_enable_push ||
5059
458
      session->num_incoming_reserved_streams >=
5060
458
          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
458
    rv = nghttp2_session_add_rst_stream(
5065
458
        session, frame->push_promise.promised_stream_id, NGHTTP2_CANCEL);
5066
458
    if (rv != 0) {
5067
0
      return rv;
5068
0
    }
5069
458
    return NGHTTP2_ERR_IGN_HEADER_BLOCK;
5070
458
  }
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
620
static int session_process_push_promise_frame(nghttp2_session *session) {
5101
620
  nghttp2_inbound_frame *iframe = &session->iframe;
5102
620
  nghttp2_frame *frame = &iframe->frame;
5103
5104
620
  nghttp2_frame_unpack_push_promise_payload(&frame->push_promise,
5105
620
                                            iframe->sbuf.pos);
5106
5107
620
  return nghttp2_session_on_push_promise_received(session, frame);
5108
620
}
5109
5110
int nghttp2_session_on_ping_received(nghttp2_session *session,
5111
205
                                     nghttp2_frame *frame) {
5112
205
  int rv = 0;
5113
205
  if (frame->hd.stream_id != 0) {
5114
13
    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
5115
13
                                             "PING: stream_id != 0");
5116
13
  }
5117
192
  if ((session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_PING_ACK) == 0 &&
5118
192
      (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 &&
5119
192
      !session_is_closing(session)) {
5120
    /* Peer sent ping, so ping it back */
5121
136
    rv = nghttp2_session_add_ping(session, NGHTTP2_FLAG_ACK,
5122
136
                                  frame->ping.opaque_data);
5123
136
    if (rv != 0) {
5124
0
      return rv;
5125
0
    }
5126
136
  }
5127
192
  return session_call_on_frame_received(session, frame);
5128
192
}
5129
5130
205
static int session_process_ping_frame(nghttp2_session *session) {
5131
205
  nghttp2_inbound_frame *iframe = &session->iframe;
5132
205
  nghttp2_frame *frame = &iframe->frame;
5133
5134
205
  nghttp2_frame_unpack_ping_payload(&frame->ping, iframe->sbuf.pos);
5135
5136
205
  return nghttp2_session_on_ping_received(session, frame);
5137
205
}
5138
5139
int nghttp2_session_on_goaway_received(nghttp2_session *session,
5140
1.26k
                                       nghttp2_frame *frame) {
5141
1.26k
  int rv;
5142
5143
1.26k
  if (frame->hd.stream_id != 0) {
5144
68
    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
5145
68
                                             "GOAWAY: stream_id != 0");
5146
68
  }
5147
  /* Spec says Endpoints MUST NOT increase the value they send in the
5148
     last stream identifier. */
5149
1.19k
  if ((frame->goaway.last_stream_id > 0 &&
5150
1.19k
       !nghttp2_session_is_my_stream_id(session,
5151
324
                                        frame->goaway.last_stream_id)) ||
5152
1.19k
      session->remote_last_stream_id < frame->goaway.last_stream_id) {
5153
15
    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
5154
15
                                             "GOAWAY: invalid last_stream_id");
5155
15
  }
5156
5157
1.18k
  session->goaway_flags |= NGHTTP2_GOAWAY_RECV;
5158
5159
1.18k
  session->remote_last_stream_id = frame->goaway.last_stream_id;
5160
5161
1.18k
  rv = session_call_on_frame_received(session, frame);
5162
5163
1.18k
  if (nghttp2_is_fatal(rv)) {
5164
0
    return rv;
5165
0
  }
5166
5167
1.18k
  return session_close_stream_on_goaway(session, frame->goaway.last_stream_id,
5168
1.18k
                                        0);
5169
1.18k
}
5170
5171
1.26k
static int session_process_goaway_frame(nghttp2_session *session) {
5172
1.26k
  nghttp2_inbound_frame *iframe = &session->iframe;
5173
1.26k
  nghttp2_frame *frame = &iframe->frame;
5174
5175
1.26k
  nghttp2_frame_unpack_goaway_payload(&frame->goaway, iframe->sbuf.pos,
5176
1.26k
                                      iframe->lbuf.pos,
5177
1.26k
                                      nghttp2_buf_len(&iframe->lbuf));
5178
5179
1.26k
  nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0);
5180
5181
1.26k
  return nghttp2_session_on_goaway_received(session, frame);
5182
1.26k
}
5183
5184
static int
5185
session_on_connection_window_update_received(nghttp2_session *session,
5186
405
                                             nghttp2_frame *frame) {
5187
  /* Handle connection-level flow control */
5188
405
  if (frame->window_update.window_size_increment == 0) {
5189
3
    return session_handle_invalid_connection(
5190
3
        session, frame, NGHTTP2_ERR_PROTO,
5191
3
        "WINDOW_UPDATE: window_size_increment == 0");
5192
3
  }
5193
5194
402
  if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
5195
402
      session->remote_window_size) {
5196
9
    return session_handle_invalid_connection(session, frame,
5197
9
                                             NGHTTP2_ERR_FLOW_CONTROL, NULL);
5198
9
  }
5199
393
  session->remote_window_size += frame->window_update.window_size_increment;
5200
5201
393
  return session_call_on_frame_received(session, frame);
5202
402
}
5203
5204
static int session_on_stream_window_update_received(nghttp2_session *session,
5205
1.38k
                                                    nghttp2_frame *frame) {
5206
1.38k
  int rv;
5207
1.38k
  nghttp2_stream *stream;
5208
5209
1.38k
  if (session_detect_idle_stream(session, frame->hd.stream_id)) {
5210
93
    return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
5211
93
                                             "WINDOW_UPDATE to idle stream");
5212
93
  }
5213
5214
1.28k
  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
5215
1.28k
  if (!stream) {
5216
324
    return 0;
5217
324
  }
5218
965
  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
965
  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
963
  if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
5228
963
      stream->remote_window_size) {
5229
263
    return session_handle_invalid_stream(session, frame,
5230
263
                                         NGHTTP2_ERR_FLOW_CONTROL);
5231
263
  }
5232
700
  stream->remote_window_size += frame->window_update.window_size_increment;
5233
5234
700
  if (stream->remote_window_size > 0 &&
5235
700
      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
700
  return session_call_on_frame_received(session, frame);
5245
700
}
5246
5247
int nghttp2_session_on_window_update_received(nghttp2_session *session,
5248
1.78k
                                              nghttp2_frame *frame) {
5249
1.78k
  if (frame->hd.stream_id == 0) {
5250
405
    return session_on_connection_window_update_received(session, frame);
5251
1.38k
  } else {
5252
1.38k
    return session_on_stream_window_update_received(session, frame);
5253
1.38k
  }
5254
1.78k
}
5255
5256
1.78k
static int session_process_window_update_frame(nghttp2_session *session) {
5257
1.78k
  nghttp2_inbound_frame *iframe = &session->iframe;
5258
1.78k
  nghttp2_frame *frame = &iframe->frame;
5259
5260
1.78k
  nghttp2_frame_unpack_window_update_payload(&frame->window_update,
5261
1.78k
                                             iframe->sbuf.pos);
5262
5263
1.78k
  return nghttp2_session_on_window_update_received(session, frame);
5264
1.78k
}
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
178
                                     nghttp2_frame *frame) {
5447
178
  int rv = 0;
5448
178
  nghttp2_stream *stream;
5449
5450
  /* We don't call on_frame_recv_callback if stream has been closed
5451
     already or being closed. */
5452
178
  stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
5453
178
  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
177
  if (session_enforce_http_messaging(session) &&
5461
177
      (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
5462
15
    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
15
  }
5475
5476
176
  rv = session_call_on_frame_received(session, frame);
5477
176
  if (nghttp2_is_fatal(rv)) {
5478
0
    return rv;
5479
0
  }
5480
5481
176
  if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
5482
14
    nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
5483
14
    rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
5484
14
    if (nghttp2_is_fatal(rv)) {
5485
0
      return rv;
5486
0
    }
5487
14
  }
5488
176
  return 0;
5489
176
}
5490
5491
/* For errors, this function only returns FATAL error. */
5492
178
static int session_process_data_frame(nghttp2_session *session) {
5493
178
  int rv;
5494
178
  nghttp2_frame *public_data_frame = &session->iframe.frame;
5495
178
  rv = nghttp2_session_on_data_received(session, public_data_frame);
5496
178
  if (nghttp2_is_fatal(rv)) {
5497
0
    return rv;
5498
0
  }
5499
178
  return 0;
5500
178
}
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.15k
                                   int32_t local_window_size) {
5514
1.15k
  if (*recv_window_size_ptr > local_window_size - (int32_t)delta ||
5515
1.15k
      *recv_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - (int32_t)delta) {
5516
0
    return -1;
5517
0
  }
5518
1.15k
  *recv_window_size_ptr += (int32_t)delta;
5519
1.15k
  return 0;
5520
1.15k
}
5521
5522
int nghttp2_session_update_recv_stream_window_size(nghttp2_session *session,
5523
                                                   nghttp2_stream *stream,
5524
                                                   size_t delta_size,
5525
391
                                                   int send_window_update) {
5526
391
  int rv;
5527
391
  rv = adjust_recv_window_size(&stream->recv_window_size, delta_size,
5528
391
                               stream->local_window_size);
5529
391
  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
391
  if (send_window_update &&
5538
391
      !(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) &&
5539
391
      stream->window_update_queued == 0 &&
5540
391
      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
391
  return 0;
5552
391
}
5553
5554
int nghttp2_session_update_recv_connection_window_size(nghttp2_session *session,
5555
759
                                                       size_t delta_size) {
5556
759
  int rv;
5557
759
  rv = adjust_recv_window_size(&session->recv_window_size, delta_size,
5558
759
                               session->local_window_size);
5559
759
  if (rv != 0) {
5560
0
    return nghttp2_session_terminate_session(session,
5561
0
                                             NGHTTP2_FLOW_CONTROL_ERROR);
5562
0
  }
5563
759
  if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) &&
5564
759
      session->window_update_queued == 0 &&
5565
759
      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
759
  return 0;
5578
759
}
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
13.7k
                                        int32_t local_window_size) {
5586
13.7k
  int32_t recv_size;
5587
13.7k
  int rv;
5588
5589
13.7k
  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
13.7k
  *consumed_size_ptr += (int32_t)delta_size;
5595
5596
13.7k
  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
13.7k
    recv_size = nghttp2_min_int32(*consumed_size_ptr, *recv_window_size_ptr);
5601
5602
13.7k
    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
13.7k
  }
5614
5615
13.7k
  return 0;
5616
13.7k
}
5617
5618
static int session_update_stream_consumed_size(nghttp2_session *session,
5619
                                               nghttp2_stream *stream,
5620
486
                                               size_t delta_size) {
5621
486
  return session_update_consumed_size(
5622
486
      session, &stream->consumed_size, &stream->recv_window_size,
5623
486
      stream->window_update_queued, stream->stream_id, delta_size,
5624
486
      stream->local_window_size);
5625
486
}
5626
5627
static int session_update_connection_consumed_size(nghttp2_session *session,
5628
13.2k
                                                   size_t delta_size) {
5629
13.2k
  return session_update_consumed_size(
5630
13.2k
      session, &session->consumed_size, &session->recv_window_size,
5631
13.2k
      session->window_update_queued, 0, delta_size, session->local_window_size);
5632
13.2k
}
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.13k
static int session_on_data_received_fail_fast(nghttp2_session *session) {
5652
1.13k
  int rv;
5653
1.13k
  nghttp2_stream *stream;
5654
1.13k
  nghttp2_inbound_frame *iframe;
5655
1.13k
  int32_t stream_id;
5656
1.13k
  const char *failure_reason;
5657
1.13k
  uint32_t error_code = NGHTTP2_PROTOCOL_ERROR;
5658
5659
1.13k
  iframe = &session->iframe;
5660
1.13k
  stream_id = iframe->frame.hd.stream_id;
5661
5662
1.13k
  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
16
    failure_reason = "DATA: stream_id == 0";
5667
16
    goto fail;
5668
16
  }
5669
5670
1.12k
  if (session_detect_idle_stream(session, stream_id)) {
5671
193
    failure_reason = "DATA: stream in idle";
5672
193
    error_code = NGHTTP2_PROTOCOL_ERROR;
5673
193
    goto fail;
5674
193
  }
5675
5676
927
  stream = nghttp2_session_get_stream(session, stream_id);
5677
927
  if (!stream) {
5678
613
    stream = nghttp2_session_get_stream_raw(session, stream_id);
5679
613
    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
613
    return NGHTTP2_ERR_IGN_PAYLOAD;
5686
613
  }
5687
314
  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
312
  if (nghttp2_session_is_my_stream_id(session, stream_id)) {
5694
312
    if (stream->state == NGHTTP2_STREAM_CLOSING) {
5695
41
      return NGHTTP2_ERR_IGN_PAYLOAD;
5696
41
    }
5697
271
    if (stream->state != NGHTTP2_STREAM_OPENED) {
5698
3
      failure_reason = "DATA: stream not opened";
5699
3
      goto fail;
5700
3
    }
5701
268
    return 0;
5702
271
  }
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
214
fail:
5712
214
  rv = nghttp2_session_terminate_session_with_reason(session, error_code,
5713
214
                                                     failure_reason);
5714
214
  if (nghttp2_is_fatal(rv)) {
5715
0
    return rv;
5716
0
  }
5717
214
  return NGHTTP2_ERR_IGN_PAYLOAD;
5718
214
}
5719
5720
static size_t inbound_frame_payload_readlen(nghttp2_inbound_frame *iframe,
5721
                                            const uint8_t *in,
5722
14.4k
                                            const uint8_t *last) {
5723
14.4k
  return nghttp2_min_size((size_t)(last - in), iframe->payloadleft);
5724
14.4k
}
5725
5726
/*
5727
 * Resets iframe->sbuf and advance its mark pointer by |left| bytes.
5728
 */
5729
44.4k
static void inbound_frame_set_mark(nghttp2_inbound_frame *iframe, size_t left) {
5730
44.4k
  nghttp2_buf_reset(&iframe->sbuf);
5731
44.4k
  iframe->sbuf.mark += left;
5732
44.4k
}
5733
5734
static size_t inbound_frame_buf_read(nghttp2_inbound_frame *iframe,
5735
83.0k
                                     const uint8_t *in, const uint8_t *last) {
5736
83.0k
  size_t readlen;
5737
5738
83.0k
  readlen = nghttp2_min_size((size_t)(last - in),
5739
83.0k
                             nghttp2_buf_mark_avail(&iframe->sbuf));
5740
5741
83.0k
  iframe->sbuf.last = nghttp2_cpymem(iframe->sbuf.last, in, readlen);
5742
5743
83.0k
  return readlen;
5744
83.0k
}
5745
5746
/*
5747
 * Unpacks SETTINGS entry in iframe->sbuf.
5748
 */
5749
26.7k
static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) {
5750
26.7k
  nghttp2_settings_entry iv;
5751
26.7k
  nghttp2_settings_entry *min_header_table_size_entry;
5752
26.7k
  size_t i;
5753
5754
26.7k
  nghttp2_frame_unpack_settings_entry(&iv, iframe->sbuf.pos);
5755
5756
26.7k
  switch (iv.settings_id) {
5757
2.07k
  case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
5758
2.34k
  case NGHTTP2_SETTINGS_ENABLE_PUSH:
5759
3.07k
  case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
5760
4.35k
  case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
5761
4.60k
  case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
5762
4.82k
  case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
5763
4.99k
  case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
5764
5.20k
  case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
5765
5.20k
    break;
5766
21.5k
  default:
5767
21.5k
    DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id);
5768
5769
21.5k
    iframe->iv[iframe->niv++] = iv;
5770
5771
21.5k
    return;
5772
26.7k
  }
5773
5774
23.0k
  for (i = 0; i < iframe->niv; ++i) {
5775
18.8k
    if (iframe->iv[i].settings_id == iv.settings_id) {
5776
999
      iframe->iv[i] = iv;
5777
999
      break;
5778
999
    }
5779
18.8k
  }
5780
5781
5.20k
  if (i == iframe->niv) {
5782
4.20k
    iframe->iv[iframe->niv++] = iv;
5783
4.20k
  }
5784
5785
5.20k
  if (iv.settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) {
5786
    /* Keep track of minimum value of SETTINGS_HEADER_TABLE_SIZE */
5787
2.07k
    min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1];
5788
5789
2.07k
    if (iv.value < min_header_table_size_entry->value) {
5790
1.62k
      min_header_table_size_entry->value = iv.value;
5791
1.62k
    }
5792
2.07k
  }
5793
5.20k
}
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
6.51k
                                    nghttp2_frame_hd *hd) {
5802
6.51k
  if (hd->flags & NGHTTP2_FLAG_PADDED) {
5803
2.39k
    if (hd->length < 1) {
5804
12
      return -1;
5805
12
    }
5806
2.38k
    inbound_frame_set_mark(iframe, 1);
5807
2.38k
    return 1;
5808
2.39k
  }
5809
4.11k
  DEBUGF("recv: no padding in payload\n");
5810
4.11k
  return 0;
5811
6.51k
}
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.38k
static nghttp2_ssize inbound_frame_compute_pad(nghttp2_inbound_frame *iframe) {
5818
2.38k
  size_t padlen;
5819
5820
  /* 1 for Pad Length field */
5821
2.38k
  padlen = (size_t)(iframe->sbuf.pos[0] + 1);
5822
5823
2.38k
  DEBUGF("recv: padlen=%zu\n", padlen);
5824
5825
  /* We cannot use iframe->frame.hd.length because of CONTINUATION */
5826
2.38k
  if (padlen - 1 > iframe->payloadleft) {
5827
20
    return -1;
5828
20
  }
5829
5830
2.36k
  iframe->padlen = padlen;
5831
5832
2.36k
  return (nghttp2_ssize)padlen;
5833
2.38k
}
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
7.78k
                                size_t payloadleft, size_t readlen) {
5844
7.78k
  size_t trail_padlen =
5845
7.78k
      nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen);
5846
5847
7.78k
  if (trail_padlen > payloadleft) {
5848
838
    size_t padlen;
5849
838
    padlen = trail_padlen - payloadleft;
5850
838
    if (readlen < padlen) {
5851
9
      return -1;
5852
9
    }
5853
829
    return (nghttp2_ssize)(readlen - padlen);
5854
838
  }
5855
6.94k
  return (nghttp2_ssize)(readlen);
5856
7.78k
}
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
10.0k
                                 size_t inlen) {
5862
10.0k
  return (ssize_t)nghttp2_session_mem_recv2(session, in, inlen);
5863
10.0k
}
5864
5865
nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session,
5866
10.0k
                                        const uint8_t *in, size_t inlen) {
5867
10.0k
  const uint8_t *first, *last;
5868
10.0k
  nghttp2_inbound_frame *iframe = &session->iframe;
5869
10.0k
  size_t readlen;
5870
10.0k
  nghttp2_ssize padlen;
5871
10.0k
  int rv;
5872
10.0k
  int busy = 0;
5873
10.0k
  nghttp2_frame_hd cont_hd;
5874
10.0k
  nghttp2_stream *stream;
5875
10.0k
  size_t pri_fieldlen;
5876
10.0k
  nghttp2_mem *mem;
5877
5878
10.0k
  if (in == NULL) {
5879
0
    assert(inlen == 0);
5880
0
    in = static_in;
5881
0
  }
5882
5883
10.0k
  first = in;
5884
10.0k
  last = in + inlen;
5885
5886
10.0k
  DEBUGF("recv: connection recv_window_size=%d, local_window=%d\n",
5887
10.0k
         session->recv_window_size, session->local_window_size);
5888
5889
10.0k
  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
10.0k
  rv = nghttp2_session_adjust_idle_stream(session);
5895
10.0k
  if (nghttp2_is_fatal(rv)) {
5896
0
    return rv;
5897
0
  }
5898
5899
10.0k
  if (!nghttp2_session_want_read(session)) {
5900
147
    return (nghttp2_ssize)inlen;
5901
147
  }
5902
5903
88.3k
  for (;;) {
5904
88.3k
    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
9.46k
    case NGHTTP2_IB_READ_FIRST_SETTINGS:
5924
9.46k
      DEBUGF("recv: [IB_READ_FIRST_SETTINGS]\n");
5925
5926
9.46k
      readlen = inbound_frame_buf_read(iframe, in, last);
5927
9.46k
      in += readlen;
5928
5929
9.46k
      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
5930
87
        return (nghttp2_ssize)(in - first);
5931
87
      }
5932
5933
9.37k
      if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS ||
5934
9.37k
          (iframe->sbuf.pos[4] & NGHTTP2_FLAG_ACK)) {
5935
130
        rv = session_call_error_callback(
5936
130
            session, NGHTTP2_ERR_SETTINGS_EXPECTED,
5937
130
            "Remote peer returned unexpected data while we expected "
5938
130
            "SETTINGS frame.  Perhaps, peer does not support HTTP/2 "
5939
130
            "properly.");
5940
5941
130
        if (nghttp2_is_fatal(rv)) {
5942
0
          return rv;
5943
0
        }
5944
5945
130
        rv = nghttp2_session_terminate_session_with_reason(
5946
130
            session, NGHTTP2_PROTOCOL_ERROR, "SETTINGS expected");
5947
5948
130
        if (nghttp2_is_fatal(rv)) {
5949
0
          return rv;
5950
0
        }
5951
5952
130
        return (nghttp2_ssize)inlen;
5953
130
      }
5954
5955
9.24k
      iframe->state = NGHTTP2_IB_READ_HEAD;
5956
5957
    /* Fall through */
5958
29.9k
    case NGHTTP2_IB_READ_HEAD: {
5959
29.9k
      int on_begin_frame_called = 0;
5960
5961
29.9k
      DEBUGF("recv: [IB_READ_HEAD]\n");
5962
5963
29.9k
      readlen = inbound_frame_buf_read(iframe, in, last);
5964
29.9k
      in += readlen;
5965
5966
29.9k
      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
5967
1.10k
        return (nghttp2_ssize)(in - first);
5968
1.10k
      }
5969
5970
28.8k
      nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->sbuf.pos);
5971
28.8k
      iframe->payloadleft = iframe->frame.hd.length;
5972
5973
28.8k
      DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n",
5974
28.8k
             iframe->frame.hd.length, iframe->frame.hd.type,
5975
28.8k
             iframe->frame.hd.flags, iframe->frame.hd.stream_id);
5976
5977
28.8k
      if (iframe->frame.hd.length > session->local_settings.max_frame_size) {
5978
1.15k
        DEBUGF("recv: length is too large %zu > %u\n", iframe->frame.hd.length,
5979
1.15k
               session->local_settings.max_frame_size);
5980
5981
1.15k
        rv = nghttp2_session_terminate_session_with_reason(
5982
1.15k
            session, NGHTTP2_FRAME_SIZE_ERROR, "too large frame size");
5983
5984
1.15k
        if (nghttp2_is_fatal(rv)) {
5985
0
          return rv;
5986
0
        }
5987
5988
1.15k
        return (nghttp2_ssize)inlen;
5989
1.15k
      }
5990
5991
27.7k
      switch (iframe->frame.hd.type) {
5992
1.13k
      case NGHTTP2_DATA: {
5993
1.13k
        DEBUGF("recv: DATA\n");
5994
5995
1.13k
        iframe->frame.hd.flags &=
5996
1.13k
            (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PADDED);
5997
        /* Check stream is open. If it is not open or closing,
5998
           ignore payload. */
5999
1.13k
        busy = 1;
6000
6001
1.13k
        rv = session_on_data_received_fail_fast(session);
6002
1.13k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6003
214
          return (nghttp2_ssize)inlen;
6004
214
        }
6005
922
        if (rv == NGHTTP2_ERR_IGN_PAYLOAD) {
6006
654
          DEBUGF("recv: DATA not allowed stream_id=%d\n",
6007
654
                 iframe->frame.hd.stream_id);
6008
654
          iframe->state = NGHTTP2_IB_IGN_DATA;
6009
654
          break;
6010
654
        }
6011
6012
268
        if (nghttp2_is_fatal(rv)) {
6013
0
          return rv;
6014
0
        }
6015
6016
268
        rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
6017
268
        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
266
        if (rv == 1) {
6029
158
          iframe->state = NGHTTP2_IB_READ_PAD_DATA;
6030
158
          break;
6031
158
        }
6032
6033
108
        iframe->state = NGHTTP2_IB_READ_DATA;
6034
108
        break;
6035
266
      }
6036
5.58k
      case NGHTTP2_HEADERS:
6037
6038
5.58k
        DEBUGF("recv: HEADERS\n");
6039
6040
5.58k
        iframe->frame.hd.flags &=
6041
5.58k
            (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS |
6042
5.58k
             NGHTTP2_FLAG_PADDED | NGHTTP2_FLAG_PRIORITY);
6043
6044
5.58k
        rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
6045
5.58k
        if (rv < 0) {
6046
5
          rv = nghttp2_session_terminate_session_with_reason(
6047
5
              session, NGHTTP2_PROTOCOL_ERROR,
6048
5
              "HEADERS: insufficient padding space");
6049
5
          if (nghttp2_is_fatal(rv)) {
6050
0
            return rv;
6051
0
          }
6052
5
          return (nghttp2_ssize)inlen;
6053
5
        }
6054
6055
5.57k
        if (rv == 1) {
6056
1.94k
          iframe->state = NGHTTP2_IB_READ_NBYTE;
6057
1.94k
          break;
6058
1.94k
        }
6059
6060
3.63k
        pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags);
6061
6062
3.63k
        if (pri_fieldlen > 0) {
6063
996
          if (iframe->payloadleft < pri_fieldlen) {
6064
5
            busy = 1;
6065
5
            iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
6066
5
            break;
6067
5
          }
6068
6069
991
          iframe->state = NGHTTP2_IB_READ_NBYTE;
6070
6071
991
          inbound_frame_set_mark(iframe, pri_fieldlen);
6072
6073
991
          break;
6074
996
        }
6075
6076
        /* Call on_begin_frame_callback here because
6077
           session_process_headers_frame() may call
6078
           on_begin_headers_callback */
6079
2.63k
        rv = session_call_on_begin_frame(session, &iframe->frame.hd);
6080
6081
2.63k
        if (nghttp2_is_fatal(rv)) {
6082
0
          return rv;
6083
0
        }
6084
6085
2.63k
        on_begin_frame_called = 1;
6086
6087
2.63k
        rv = session_process_headers_frame(session);
6088
2.63k
        if (nghttp2_is_fatal(rv)) {
6089
0
          return rv;
6090
0
        }
6091
6092
2.63k
        busy = 1;
6093
6094
2.63k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6095
84
          return (nghttp2_ssize)inlen;
6096
84
        }
6097
6098
2.55k
        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
2.55k
        if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
6109
83
          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
6110
83
          break;
6111
83
        }
6112
6113
2.47k
        iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
6114
6115
2.47k
        break;
6116
142
      case NGHTTP2_PRIORITY:
6117
142
        DEBUGF("recv: PRIORITY\n");
6118
6119
142
        iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
6120
6121
142
        if (iframe->payloadleft != NGHTTP2_PRIORITY_SPECLEN) {
6122
10
          busy = 1;
6123
6124
10
          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
6125
6126
10
          break;
6127
10
        }
6128
6129
132
        iframe->state = NGHTTP2_IB_READ_NBYTE;
6130
6131
132
        inbound_frame_set_mark(iframe, NGHTTP2_PRIORITY_SPECLEN);
6132
6133
132
        break;
6134
547
      case NGHTTP2_RST_STREAM:
6135
2.34k
      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
2.34k
        iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
6148
6149
2.34k
        if (iframe->payloadleft != 4) {
6150
30
          busy = 1;
6151
30
          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
6152
30
          break;
6153
30
        }
6154
6155
2.31k
        iframe->state = NGHTTP2_IB_READ_NBYTE;
6156
6157
2.31k
        inbound_frame_set_mark(iframe, 4);
6158
6159
2.31k
        break;
6160
11.7k
      case NGHTTP2_SETTINGS:
6161
11.7k
        DEBUGF("recv: SETTINGS\n");
6162
6163
11.7k
        iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK;
6164
6165
11.7k
        if ((iframe->frame.hd.length % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) ||
6166
11.7k
            ((iframe->frame.hd.flags & NGHTTP2_FLAG_ACK) &&
6167
11.7k
             iframe->payloadleft > 0)) {
6168
18
          busy = 1;
6169
18
          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
6170
18
          break;
6171
18
        }
6172
6173
        /* Check the settings flood counter early to be safe */
6174
11.7k
        if (session->obq_flood_counter_ >= session->max_outbound_ack &&
6175
11.7k
            !(iframe->frame.hd.flags & NGHTTP2_FLAG_ACK)) {
6176
0
          return NGHTTP2_ERR_FLOODED;
6177
0
        }
6178
6179
11.7k
        iframe->state = NGHTTP2_IB_READ_SETTINGS;
6180
6181
11.7k
        if (iframe->payloadleft) {
6182
4.56k
          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
4.56k
          iframe->max_niv =
6187
4.56k
              iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1;
6188
6189
4.56k
          if (iframe->max_niv - 1 > session->max_settings) {
6190
24
            rv = nghttp2_session_terminate_session_with_reason(
6191
24
                session, NGHTTP2_ENHANCE_YOUR_CALM,
6192
24
                "SETTINGS: too many setting entries");
6193
24
            if (nghttp2_is_fatal(rv)) {
6194
0
              return rv;
6195
0
            }
6196
24
            return (nghttp2_ssize)inlen;
6197
24
          }
6198
6199
4.54k
          iframe->iv = nghttp2_mem_malloc(mem, sizeof(nghttp2_settings_entry) *
6200
4.54k
                                                   iframe->max_niv);
6201
6202
4.54k
          if (!iframe->iv) {
6203
0
            return NGHTTP2_ERR_NOMEM;
6204
0
          }
6205
6206
4.54k
          min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1];
6207
4.54k
          min_header_table_size_entry->settings_id =
6208
4.54k
              NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
6209
4.54k
          min_header_table_size_entry->value = UINT32_MAX;
6210
6211
4.54k
          inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH);
6212
4.54k
          break;
6213
4.54k
        }
6214
6215
7.20k
        busy = 1;
6216
6217
7.20k
        inbound_frame_set_mark(iframe, 0);
6218
6219
7.20k
        break;
6220
663
      case NGHTTP2_PUSH_PROMISE:
6221
663
        DEBUGF("recv: PUSH_PROMISE\n");
6222
6223
663
        iframe->frame.hd.flags &=
6224
663
            (NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PADDED);
6225
6226
663
        rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
6227
663
        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
658
        if (rv == 1) {
6238
282
          iframe->state = NGHTTP2_IB_READ_NBYTE;
6239
282
          break;
6240
282
        }
6241
6242
376
        if (iframe->payloadleft < 4) {
6243
5
          busy = 1;
6244
5
          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
6245
5
          break;
6246
5
        }
6247
6248
371
        iframe->state = NGHTTP2_IB_READ_NBYTE;
6249
6250
371
        inbound_frame_set_mark(iframe, 4);
6251
6252
371
        break;
6253
224
      case NGHTTP2_PING:
6254
224
        DEBUGF("recv: PING\n");
6255
6256
224
        iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK;
6257
6258
224
        if (iframe->payloadleft != 8) {
6259
16
          busy = 1;
6260
16
          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
6261
16
          break;
6262
16
        }
6263
6264
208
        iframe->state = NGHTTP2_IB_READ_NBYTE;
6265
208
        inbound_frame_set_mark(iframe, 8);
6266
6267
208
        break;
6268
1.32k
      case NGHTTP2_GOAWAY:
6269
1.32k
        DEBUGF("recv: GOAWAY\n");
6270
6271
1.32k
        iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
6272
6273
1.32k
        if (iframe->payloadleft < 8) {
6274
5
          busy = 1;
6275
5
          iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
6276
5
          break;
6277
5
        }
6278
6279
1.32k
        iframe->state = NGHTTP2_IB_READ_NBYTE;
6280
1.32k
        inbound_frame_set_mark(iframe, 8);
6281
6282
1.32k
        break;
6283
7
      case NGHTTP2_CONTINUATION:
6284
7
        DEBUGF("recv: unexpected CONTINUATION\n");
6285
6286
        /* Receiving CONTINUATION in this state are subject to
6287
           connection error of type PROTOCOL_ERROR */
6288
7
        rv = nghttp2_session_terminate_session_with_reason(
6289
7
            session, NGHTTP2_PROTOCOL_ERROR, "CONTINUATION: unexpected");
6290
7
        if (nghttp2_is_fatal(rv)) {
6291
0
          return rv;
6292
0
        }
6293
6294
7
        return (nghttp2_ssize)inlen;
6295
4.51k
      default:
6296
4.51k
        DEBUGF("recv: extension frame\n");
6297
6298
4.51k
        if (check_ext_type_set(session->user_recv_ext_types,
6299
4.51k
                               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
4.51k
        } else {
6316
4.51k
          switch (iframe->frame.hd.type) {
6317
161
          case NGHTTP2_ALTSVC:
6318
161
            if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) ==
6319
161
                0) {
6320
161
              busy = 1;
6321
161
              iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
6322
161
              break;
6323
161
            }
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
61
          case NGHTTP2_ORIGIN:
6349
61
            if (!(session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN)) {
6350
61
              busy = 1;
6351
61
              iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
6352
61
              break;
6353
61
            }
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
46
          case NGHTTP2_PRIORITY_UPDATE:
6385
46
            if ((session->builtin_recv_ext_types &
6386
46
                 NGHTTP2_TYPEMASK_PRIORITY_UPDATE) == 0) {
6387
46
              busy = 1;
6388
46
              iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
6389
46
              break;
6390
46
            }
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
4.24k
          default:
6428
4.24k
            busy = 1;
6429
6430
4.24k
            iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
6431
6432
4.24k
            break;
6433
4.51k
          }
6434
4.51k
        }
6435
27.7k
      }
6436
6437
27.3k
      if (!on_begin_frame_called) {
6438
24.8k
        switch (iframe->state) {
6439
0
        case NGHTTP2_IB_IGN_HEADER_BLOCK:
6440
4.51k
        case NGHTTP2_IB_IGN_PAYLOAD:
6441
4.60k
        case NGHTTP2_IB_FRAME_SIZE_ERROR:
6442
5.25k
        case NGHTTP2_IB_IGN_DATA:
6443
5.25k
        case NGHTTP2_IB_IGN_ALL:
6444
5.25k
          break;
6445
19.5k
        default:
6446
19.5k
          rv = session_call_on_begin_frame(session, &iframe->frame.hd);
6447
6448
19.5k
          if (nghttp2_is_fatal(rv)) {
6449
0
            return rv;
6450
0
          }
6451
24.8k
        }
6452
24.8k
      }
6453
6454
27.3k
      break;
6455
27.3k
    }
6456
27.3k
    case NGHTTP2_IB_READ_NBYTE:
6457
8.93k
      DEBUGF("recv: [IB_READ_NBYTE]\n");
6458
6459
8.93k
      readlen = inbound_frame_buf_read(iframe, in, last);
6460
8.93k
      in += readlen;
6461
8.93k
      iframe->payloadleft -= readlen;
6462
6463
8.93k
      DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zu\n", readlen,
6464
8.93k
             iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf));
6465
6466
8.93k
      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
6467
63
        return (nghttp2_ssize)(in - first);
6468
63
      }
6469
6470
8.86k
      switch (iframe->frame.hd.type) {
6471
4.00k
      case NGHTTP2_HEADERS:
6472
4.00k
        if (iframe->padlen == 0 &&
6473
4.00k
            (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) {
6474
1.94k
          pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags);
6475
1.94k
          padlen = inbound_frame_compute_pad(iframe);
6476
1.94k
          if (padlen < 0 ||
6477
1.94k
              (size_t)padlen + pri_fieldlen > 1 + iframe->payloadleft) {
6478
16
            rv = nghttp2_session_terminate_session_with_reason(
6479
16
                session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: invalid padding");
6480
16
            if (nghttp2_is_fatal(rv)) {
6481
0
              return rv;
6482
0
            }
6483
16
            return (nghttp2_ssize)inlen;
6484
16
          }
6485
1.92k
          iframe->frame.headers.padlen = (size_t)padlen;
6486
6487
1.92k
          if (pri_fieldlen > 0) {
6488
1.09k
            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.09k
            iframe->state = NGHTTP2_IB_READ_NBYTE;
6494
1.09k
            inbound_frame_set_mark(iframe, pri_fieldlen);
6495
1.09k
            break;
6496
1.09k
          } else {
6497
            /* Truncate buffers used for padding spec */
6498
832
            inbound_frame_set_mark(iframe, 0);
6499
832
          }
6500
1.92k
        }
6501
6502
2.88k
        rv = session_process_headers_frame(session);
6503
2.88k
        if (nghttp2_is_fatal(rv)) {
6504
0
          return rv;
6505
0
        }
6506
6507
2.88k
        busy = 1;
6508
6509
2.88k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6510
74
          return (nghttp2_ssize)inlen;
6511
74
        }
6512
6513
2.81k
        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.81k
        if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
6524
165
          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
6525
165
          break;
6526
165
        }
6527
6528
2.64k
        iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
6529
6530
2.64k
        break;
6531
129
      case NGHTTP2_PRIORITY:
6532
129
        if (!session_no_rfc7540_pri_no_fallback(session) &&
6533
129
            session->remote_settings.no_rfc7540_priorities != 1) {
6534
113
          rv = session_process_priority_frame(session);
6535
113
          if (nghttp2_is_fatal(rv)) {
6536
0
            return rv;
6537
0
          }
6538
6539
113
          if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6540
4
            return (nghttp2_ssize)inlen;
6541
4
          }
6542
113
        }
6543
6544
125
        session_inbound_frame_reset(session);
6545
6546
125
        break;
6547
529
      case NGHTTP2_RST_STREAM:
6548
529
        rv = session_process_rst_stream_frame(session);
6549
529
        if (nghttp2_is_fatal(rv)) {
6550
0
          return rv;
6551
0
        }
6552
6553
529
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6554
89
          return (nghttp2_ssize)inlen;
6555
89
        }
6556
6557
440
        session_inbound_frame_reset(session);
6558
6559
440
        break;
6560
902
      case NGHTTP2_PUSH_PROMISE:
6561
902
        if (iframe->padlen == 0 &&
6562
902
            (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) {
6563
282
          padlen = inbound_frame_compute_pad(iframe);
6564
282
          if (padlen < 0 || (size_t)padlen + 4 /* promised stream id */
6565
277
                                > 1 + iframe->payloadleft) {
6566
11
            rv = nghttp2_session_terminate_session_with_reason(
6567
11
                session, NGHTTP2_PROTOCOL_ERROR,
6568
11
                "PUSH_PROMISE: invalid padding");
6569
11
            if (nghttp2_is_fatal(rv)) {
6570
0
              return rv;
6571
0
            }
6572
11
            return (nghttp2_ssize)inlen;
6573
11
          }
6574
6575
271
          iframe->frame.push_promise.padlen = (size_t)padlen;
6576
6577
271
          if (iframe->payloadleft < 4) {
6578
0
            busy = 1;
6579
0
            iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR;
6580
0
            break;
6581
0
          }
6582
6583
271
          iframe->state = NGHTTP2_IB_READ_NBYTE;
6584
6585
271
          inbound_frame_set_mark(iframe, 4);
6586
6587
271
          break;
6588
271
        }
6589
6590
620
        rv = session_process_push_promise_frame(session);
6591
620
        if (nghttp2_is_fatal(rv)) {
6592
0
          return rv;
6593
0
        }
6594
6595
620
        busy = 1;
6596
6597
620
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6598
162
          return (nghttp2_ssize)inlen;
6599
162
        }
6600
6601
458
        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
458
        if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) {
6613
458
          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
6614
458
          break;
6615
458
        }
6616
6617
0
        iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
6618
6619
0
        break;
6620
205
      case NGHTTP2_PING:
6621
205
        rv = session_process_ping_frame(session);
6622
205
        if (nghttp2_is_fatal(rv)) {
6623
0
          return rv;
6624
0
        }
6625
6626
205
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6627
13
          return (nghttp2_ssize)inlen;
6628
13
        }
6629
6630
192
        session_inbound_frame_reset(session);
6631
6632
192
        break;
6633
1.31k
      case NGHTTP2_GOAWAY: {
6634
1.31k
        size_t debuglen;
6635
6636
        /* 8 is Last-stream-ID + Error Code */
6637
1.31k
        debuglen = iframe->frame.hd.length - 8;
6638
6639
1.31k
        if (debuglen > 0) {
6640
994
          iframe->raw_lbuf = nghttp2_mem_malloc(mem, debuglen);
6641
6642
994
          if (iframe->raw_lbuf == NULL) {
6643
0
            return NGHTTP2_ERR_NOMEM;
6644
0
          }
6645
6646
994
          nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, debuglen);
6647
994
        }
6648
6649
1.31k
        busy = 1;
6650
6651
1.31k
        iframe->state = NGHTTP2_IB_READ_GOAWAY_DEBUG;
6652
6653
1.31k
        break;
6654
1.31k
      }
6655
1.78k
      case NGHTTP2_WINDOW_UPDATE:
6656
1.78k
        rv = session_process_window_update_frame(session);
6657
1.78k
        if (nghttp2_is_fatal(rv)) {
6658
0
          return rv;
6659
0
        }
6660
6661
1.78k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6662
107
          return (nghttp2_ssize)inlen;
6663
107
        }
6664
6665
1.68k
        session_inbound_frame_reset(session);
6666
6667
1.68k
        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
8.86k
      }
6717
8.39k
      break;
6718
8.39k
    case NGHTTP2_IB_READ_HEADER_BLOCK:
6719
7.55k
    case NGHTTP2_IB_IGN_HEADER_BLOCK: {
6720
7.55k
      nghttp2_ssize data_readlen;
6721
7.55k
      size_t trail_padlen;
6722
7.55k
      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
7.55k
      readlen = inbound_frame_payload_readlen(iframe, in, last);
6732
6733
7.55k
      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6734
7.55k
             iframe->payloadleft - readlen);
6735
6736
7.55k
      data_readlen = inbound_frame_effective_readlen(
6737
7.55k
          iframe, iframe->payloadleft - readlen, readlen);
6738
6739
7.55k
      if (data_readlen == -1) {
6740
        /* everything is padding */
6741
4
        data_readlen = 0;
6742
4
      }
6743
6744
7.55k
      trail_padlen = nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen);
6745
6746
7.55k
      final = (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) &&
6747
7.55k
              iframe->payloadleft - (size_t)data_readlen == trail_padlen;
6748
6749
7.55k
      if (data_readlen > 0 || (data_readlen == 0 && final)) {
6750
7.21k
        size_t hd_proclen = 0;
6751
6752
7.21k
        DEBUGF("recv: block final=%d\n", final);
6753
6754
7.21k
        rv =
6755
7.21k
            inflate_header_block(session, &iframe->frame, &hd_proclen,
6756
7.21k
                                 (uint8_t *)in, (size_t)data_readlen, final,
6757
7.21k
                                 iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK);
6758
6759
7.21k
        if (nghttp2_is_fatal(rv)) {
6760
0
          return rv;
6761
0
        }
6762
6763
7.21k
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6764
1.55k
          return (nghttp2_ssize)inlen;
6765
1.55k
        }
6766
6767
5.66k
        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
5.66k
        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
1.36k
          in += hd_proclen;
6779
1.36k
          iframe->payloadleft -= hd_proclen;
6780
6781
          /* Use promised stream ID for PUSH_PROMISE */
6782
1.36k
          rv = nghttp2_session_add_rst_stream(
6783
1.36k
              session,
6784
1.36k
              iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE
6785
1.36k
                  ? iframe->frame.push_promise.promised_stream_id
6786
1.36k
                  : iframe->frame.hd.stream_id,
6787
1.36k
              NGHTTP2_INTERNAL_ERROR);
6788
1.36k
          if (nghttp2_is_fatal(rv)) {
6789
0
            return rv;
6790
0
          }
6791
1.36k
          busy = 1;
6792
1.36k
          iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
6793
1.36k
          break;
6794
1.36k
        }
6795
6796
4.29k
        in += readlen;
6797
4.29k
        iframe->payloadleft -= readlen;
6798
6799
4.29k
        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
4.29k
      } else {
6810
336
        in += readlen;
6811
336
        iframe->payloadleft -= readlen;
6812
336
      }
6813
6814
4.63k
      if (iframe->payloadleft) {
6815
2.59k
        break;
6816
2.59k
      }
6817
6818
2.03k
      if ((iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) {
6819
6820
483
        inbound_frame_set_mark(iframe, NGHTTP2_FRAME_HDLEN);
6821
6822
483
        iframe->padlen = 0;
6823
6824
483
        if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {
6825
244
          iframe->state = NGHTTP2_IB_EXPECT_CONTINUATION;
6826
244
        } else {
6827
239
          iframe->state = NGHTTP2_IB_IGN_CONTINUATION;
6828
239
        }
6829
1.55k
      } else {
6830
1.55k
        if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) {
6831
978
          rv = session_after_header_block_received(session);
6832
978
          if (nghttp2_is_fatal(rv)) {
6833
0
            return rv;
6834
0
          }
6835
978
        }
6836
1.55k
        session_inbound_frame_reset(session);
6837
6838
1.55k
        session->num_continuations = 0;
6839
1.55k
      }
6840
2.03k
      break;
6841
2.03k
    }
6842
4.60k
    case NGHTTP2_IB_IGN_PAYLOAD:
6843
4.60k
      DEBUGF("recv: [IB_IGN_PAYLOAD]\n");
6844
6845
4.60k
      readlen = inbound_frame_payload_readlen(iframe, in, last);
6846
4.60k
      iframe->payloadleft -= readlen;
6847
4.60k
      in += readlen;
6848
6849
4.60k
      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6850
4.60k
             iframe->payloadleft);
6851
6852
4.60k
      if (iframe->payloadleft) {
6853
387
        break;
6854
387
      }
6855
6856
4.22k
      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
4.22k
      default:
6864
4.22k
        break;
6865
4.22k
      }
6866
6867
4.22k
      session_inbound_frame_reset(session);
6868
6869
4.22k
      break;
6870
89
    case NGHTTP2_IB_FRAME_SIZE_ERROR:
6871
89
      DEBUGF("recv: [IB_FRAME_SIZE_ERROR]\n");
6872
6873
89
      rv = session_handle_frame_size_error(session);
6874
89
      if (nghttp2_is_fatal(rv)) {
6875
0
        return rv;
6876
0
      }
6877
6878
89
      assert(iframe->state == NGHTTP2_IB_IGN_ALL);
6879
6880
89
      return (nghttp2_ssize)inlen;
6881
34.0k
    case NGHTTP2_IB_READ_SETTINGS:
6882
34.0k
      DEBUGF("recv: [IB_READ_SETTINGS]\n");
6883
6884
34.0k
      readlen = inbound_frame_buf_read(iframe, in, last);
6885
34.0k
      iframe->payloadleft -= readlen;
6886
34.0k
      in += readlen;
6887
6888
34.0k
      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6889
34.0k
             iframe->payloadleft);
6890
6891
34.0k
      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
6892
83
        break;
6893
83
      }
6894
6895
33.9k
      if (readlen > 0) {
6896
26.7k
        inbound_frame_set_settings_entry(iframe);
6897
26.7k
      }
6898
33.9k
      if (iframe->payloadleft) {
6899
22.2k
        inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH);
6900
22.2k
        break;
6901
22.2k
      }
6902
6903
11.6k
      rv = session_process_settings_frame(session);
6904
6905
11.6k
      if (nghttp2_is_fatal(rv)) {
6906
0
        return rv;
6907
0
      }
6908
6909
11.6k
      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6910
542
        return (nghttp2_ssize)inlen;
6911
542
      }
6912
6913
11.1k
      session_inbound_frame_reset(session);
6914
6915
11.1k
      break;
6916
1.37k
    case NGHTTP2_IB_READ_GOAWAY_DEBUG:
6917
1.37k
      DEBUGF("recv: [IB_READ_GOAWAY_DEBUG]\n");
6918
6919
1.37k
      readlen = inbound_frame_payload_readlen(iframe, in, last);
6920
6921
1.37k
      if (readlen > 0) {
6922
1.04k
        iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
6923
6924
1.04k
        iframe->payloadleft -= readlen;
6925
1.04k
        in += readlen;
6926
1.04k
      }
6927
6928
1.37k
      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
6929
1.37k
             iframe->payloadleft);
6930
6931
1.37k
      if (iframe->payloadleft) {
6932
104
        assert(nghttp2_buf_avail(&iframe->lbuf) > 0);
6933
6934
104
        break;
6935
104
      }
6936
6937
1.26k
      rv = session_process_goaway_frame(session);
6938
6939
1.26k
      if (nghttp2_is_fatal(rv)) {
6940
0
        return rv;
6941
0
      }
6942
6943
1.26k
      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
6944
83
        return (nghttp2_ssize)inlen;
6945
83
      }
6946
6947
1.18k
      session_inbound_frame_reset(session);
6948
6949
1.18k
      break;
6950
241
    case NGHTTP2_IB_EXPECT_CONTINUATION:
6951
475
    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
475
      if (++session->num_continuations > session->max_continuations) {
6961
6
        return NGHTTP2_ERR_TOO_MANY_CONTINUATIONS;
6962
6
      }
6963
6964
469
      readlen = inbound_frame_buf_read(iframe, in, last);
6965
469
      in += readlen;
6966
6967
469
      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
6968
39
        return (nghttp2_ssize)(in - first);
6969
39
      }
6970
6971
430
      nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->sbuf.pos);
6972
430
      iframe->payloadleft = cont_hd.length;
6973
6974
430
      DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n",
6975
430
             cont_hd.length, cont_hd.type, cont_hd.flags, cont_hd.stream_id);
6976
6977
430
      if (cont_hd.type != NGHTTP2_CONTINUATION ||
6978
430
          cont_hd.stream_id != iframe->frame.hd.stream_id) {
6979
97
        DEBUGF("recv: expected stream_id=%d, type=%d, but got stream_id=%d, "
6980
97
               "type=%u\n",
6981
97
               iframe->frame.hd.stream_id, NGHTTP2_CONTINUATION,
6982
97
               cont_hd.stream_id, cont_hd.type);
6983
97
        rv = nghttp2_session_terminate_session_with_reason(
6984
97
            session, NGHTTP2_PROTOCOL_ERROR,
6985
97
            "unexpected non-CONTINUATION frame or stream_id is invalid");
6986
97
        if (nghttp2_is_fatal(rv)) {
6987
0
          return rv;
6988
0
        }
6989
6990
97
        return (nghttp2_ssize)inlen;
6991
97
      }
6992
6993
      /* CONTINUATION won't bear NGHTTP2_PADDED flag */
6994
6995
333
      iframe->frame.hd.flags =
6996
333
          (uint8_t)(iframe->frame.hd.flags |
6997
333
                    (cont_hd.flags & NGHTTP2_FLAG_END_HEADERS));
6998
333
      iframe->frame.hd.length += cont_hd.length;
6999
7000
333
      busy = 1;
7001
7002
333
      if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) {
7003
167
        iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK;
7004
7005
167
        rv = session_call_on_begin_frame(session, &cont_hd);
7006
7007
167
        if (nghttp2_is_fatal(rv)) {
7008
0
          return rv;
7009
0
        }
7010
167
      } else {
7011
166
        iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK;
7012
166
      }
7013
7014
333
      break;
7015
333
    case NGHTTP2_IB_READ_PAD_DATA:
7016
158
      DEBUGF("recv: [IB_READ_PAD_DATA]\n");
7017
7018
158
      readlen = inbound_frame_buf_read(iframe, in, last);
7019
158
      in += readlen;
7020
158
      iframe->payloadleft -= readlen;
7021
7022
158
      DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zu\n", readlen,
7023
158
             iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf));
7024
7025
158
      if (nghttp2_buf_mark_avail(&iframe->sbuf)) {
7026
1
        return (nghttp2_ssize)(in - first);
7027
1
      }
7028
7029
      /* Pad Length field is subject to flow control */
7030
157
      rv = nghttp2_session_update_recv_connection_window_size(session, readlen);
7031
157
      if (nghttp2_is_fatal(rv)) {
7032
0
        return rv;
7033
0
      }
7034
7035
157
      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
7036
0
        return (nghttp2_ssize)inlen;
7037
0
      }
7038
7039
      /* Pad Length field is consumed immediately */
7040
157
      rv =
7041
157
          nghttp2_session_consume(session, iframe->frame.hd.stream_id, readlen);
7042
7043
157
      if (nghttp2_is_fatal(rv)) {
7044
0
        return rv;
7045
0
      }
7046
7047
157
      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
7048
0
        return (nghttp2_ssize)inlen;
7049
0
      }
7050
7051
157
      stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id);
7052
157
      if (stream) {
7053
157
        rv = nghttp2_session_update_recv_stream_window_size(
7054
157
            session, stream, readlen,
7055
157
            iframe->payloadleft ||
7056
157
                (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0);
7057
157
        if (nghttp2_is_fatal(rv)) {
7058
0
          return rv;
7059
0
        }
7060
157
      }
7061
7062
157
      busy = 1;
7063
7064
157
      padlen = inbound_frame_compute_pad(iframe);
7065
157
      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
153
      iframe->frame.data.padlen = (size_t)padlen;
7075
7076
153
      iframe->state = NGHTTP2_IB_READ_DATA;
7077
7078
153
      break;
7079
278
    case NGHTTP2_IB_READ_DATA:
7080
278
      stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id);
7081
7082
278
      if (!stream) {
7083
0
        busy = 1;
7084
0
        iframe->state = NGHTTP2_IB_IGN_DATA;
7085
0
        break;
7086
0
      }
7087
7088
278
      DEBUGF("recv: [IB_READ_DATA]\n");
7089
7090
278
      readlen = inbound_frame_payload_readlen(iframe, in, last);
7091
278
      iframe->payloadleft -= readlen;
7092
278
      in += readlen;
7093
7094
278
      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
7095
278
             iframe->payloadleft);
7096
7097
278
      if (readlen > 0) {
7098
234
        nghttp2_ssize data_readlen;
7099
7100
234
        rv = nghttp2_session_update_recv_connection_window_size(session,
7101
234
                                                                readlen);
7102
234
        if (nghttp2_is_fatal(rv)) {
7103
0
          return rv;
7104
0
        }
7105
7106
234
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
7107
0
          return (nghttp2_ssize)inlen;
7108
0
        }
7109
7110
234
        rv = nghttp2_session_update_recv_stream_window_size(
7111
234
            session, stream, readlen,
7112
234
            iframe->payloadleft ||
7113
234
                (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0);
7114
234
        if (nghttp2_is_fatal(rv)) {
7115
0
          return rv;
7116
0
        }
7117
7118
234
        data_readlen = inbound_frame_effective_readlen(
7119
234
            iframe, iframe->payloadleft, readlen);
7120
7121
234
        if (data_readlen == -1) {
7122
          /* everything is padding */
7123
5
          data_readlen = 0;
7124
5
        }
7125
7126
234
        padlen = (nghttp2_ssize)readlen - data_readlen;
7127
7128
234
        if (padlen > 0) {
7129
          /* Padding is considered as "consumed" immediately */
7130
89
          rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id,
7131
89
                                       (size_t)padlen);
7132
7133
89
          if (nghttp2_is_fatal(rv)) {
7134
0
            return rv;
7135
0
          }
7136
7137
89
          if (iframe->state == NGHTTP2_IB_IGN_ALL) {
7138
0
            return (nghttp2_ssize)inlen;
7139
0
          }
7140
89
        }
7141
7142
234
        DEBUGF("recv: data_readlen=%td\n", data_readlen);
7143
7144
234
        if (data_readlen > 0) {
7145
207
          if (session_enforce_http_messaging(session)) {
7146
207
            if (nghttp2_http_on_data_chunk(stream, (size_t)data_readlen) != 0) {
7147
18
              if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
7148
                /* Consume all data for connection immediately here */
7149
18
                rv = session_update_connection_consumed_size(
7150
18
                    session, (size_t)data_readlen);
7151
7152
18
                if (nghttp2_is_fatal(rv)) {
7153
0
                  return rv;
7154
0
                }
7155
7156
18
                if (iframe->state == NGHTTP2_IB_IGN_DATA) {
7157
0
                  return (nghttp2_ssize)inlen;
7158
0
                }
7159
18
              }
7160
7161
18
              rv = nghttp2_session_add_rst_stream(
7162
18
                  session, iframe->frame.hd.stream_id, NGHTTP2_PROTOCOL_ERROR);
7163
18
              if (nghttp2_is_fatal(rv)) {
7164
0
                return rv;
7165
0
              }
7166
18
              busy = 1;
7167
18
              iframe->state = NGHTTP2_IB_IGN_DATA;
7168
18
              break;
7169
18
            }
7170
207
          }
7171
189
          if (session->callbacks.on_data_chunk_recv_callback) {
7172
189
            rv = session->callbacks.on_data_chunk_recv_callback(
7173
189
                session, iframe->frame.hd.flags, iframe->frame.hd.stream_id,
7174
189
                in - readlen, (size_t)data_readlen, session->user_data);
7175
189
            if (rv == NGHTTP2_ERR_PAUSE) {
7176
0
              return (nghttp2_ssize)(in - first);
7177
0
            }
7178
7179
189
            if (nghttp2_is_fatal(rv)) {
7180
0
              return NGHTTP2_ERR_CALLBACK_FAILURE;
7181
0
            }
7182
189
          }
7183
189
        }
7184
234
      }
7185
7186
260
      if (iframe->payloadleft) {
7187
82
        break;
7188
82
      }
7189
7190
178
      rv = session_process_data_frame(session);
7191
178
      if (nghttp2_is_fatal(rv)) {
7192
0
        return rv;
7193
0
      }
7194
7195
178
      session_inbound_frame_reset(session);
7196
7197
178
      break;
7198
684
    case NGHTTP2_IB_IGN_DATA:
7199
684
      DEBUGF("recv: [IB_IGN_DATA]\n");
7200
7201
684
      readlen = inbound_frame_payload_readlen(iframe, in, last);
7202
684
      iframe->payloadleft -= readlen;
7203
684
      in += readlen;
7204
7205
684
      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
7206
684
             iframe->payloadleft);
7207
7208
684
      if (readlen > 0) {
7209
        /* Update connection-level flow control window for ignored
7210
           DATA frame too */
7211
368
        rv = nghttp2_session_update_recv_connection_window_size(session,
7212
368
                                                                readlen);
7213
368
        if (nghttp2_is_fatal(rv)) {
7214
0
          return rv;
7215
0
        }
7216
7217
368
        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
7218
0
          return (nghttp2_ssize)inlen;
7219
0
        }
7220
7221
368
        if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
7222
7223
          /* Ignored DATA is considered as "consumed" immediately. */
7224
368
          rv = session_update_connection_consumed_size(session, readlen);
7225
7226
368
          if (nghttp2_is_fatal(rv)) {
7227
0
            return rv;
7228
0
          }
7229
7230
368
          if (iframe->state == NGHTTP2_IB_IGN_ALL) {
7231
0
            return (nghttp2_ssize)inlen;
7232
0
          }
7233
368
        }
7234
368
      }
7235
7236
684
      if (iframe->payloadleft) {
7237
63
        break;
7238
63
      }
7239
7240
621
      session_inbound_frame_reset(session);
7241
7242
621
      break;
7243
34
    case NGHTTP2_IB_IGN_ALL:
7244
34
      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
88.3k
    }
7346
7347
82.5k
    if (!busy && in == last) {
7348
4.06k
      break;
7349
4.06k
    }
7350
7351
78.5k
    busy = 0;
7352
78.5k
  }
7353
7354
4.06k
  assert(in == last);
7355
7356
4.06k
  return (nghttp2_ssize)(in - first);
7357
4.06k
}
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
96.1k
static size_t session_get_num_active_streams(nghttp2_session *session) {
7386
96.1k
  return nghttp2_map_size(&session->streams) - session->num_closed_streams -
7387
96.1k
         session->num_idle_streams;
7388
96.1k
}
7389
7390
96.8k
int nghttp2_session_want_read(nghttp2_session *session) {
7391
96.8k
  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
96.8k
  if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) {
7396
704
    return 0;
7397
704
  }
7398
7399
96.1k
  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
96.1k
  if (num_active_streams > 0) {
7405
65.3k
    return 1;
7406
65.3k
  }
7407
7408
  /* If there is no active streams and GOAWAY has been sent or
7409
     received, we are done with this session. */
7410
30.7k
  return (session->goaway_flags &
7411
30.7k
          (NGHTTP2_GOAWAY_SENT | NGHTTP2_GOAWAY_RECV)) == 0;
7412
96.1k
}
7413
7414
5.79M
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
5.79M
  if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) {
7418
2.91M
    return 0;
7419
2.91M
  }
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
2.87M
  return session->aob.item || nghttp2_outbound_queue_top(&session->ob_urgent) ||
7428
2.87M
         nghttp2_outbound_queue_top(&session->ob_reg) ||
7429
2.87M
         ((!nghttp2_pq_empty(&session->root.obq) ||
7430
2.85M
           !session_sched_empty(session)) &&
7431
2.85M
          session->remote_window_size > 0) ||
7432
2.87M
         (nghttp2_outbound_queue_top(&session->ob_syn) &&
7433
2.85M
          !session_is_outgoing_concurrent_streams_max(session));
7434
5.79M
}
7435
7436
int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
7437
136
                             const uint8_t *opaque_data) {
7438
136
  int rv;
7439
136
  nghttp2_outbound_item *item;
7440
136
  nghttp2_frame *frame;
7441
136
  nghttp2_mem *mem;
7442
7443
136
  mem = &session->mem;
7444
7445
136
  if ((flags & NGHTTP2_FLAG_ACK) &&
7446
136
      session->obq_flood_counter_ >= session->max_outbound_ack) {
7447
0
    return NGHTTP2_ERR_FLOODED;
7448
0
  }
7449
7450
136
  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
7451
136
  if (item == NULL) {
7452
0
    return NGHTTP2_ERR_NOMEM;
7453
0
  }
7454
7455
136
  nghttp2_outbound_item_init(item);
7456
7457
136
  frame = &item->frame;
7458
7459
136
  nghttp2_frame_ping_init(&frame->ping, flags, opaque_data);
7460
7461
136
  rv = nghttp2_session_add_item(session, item);
7462
7463
136
  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
136
  if (flags & NGHTTP2_FLAG_ACK) {
7470
136
    ++session->obq_flood_counter_;
7471
136
  }
7472
7473
136
  return 0;
7474
136
}
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
4.46k
                               size_t opaque_data_len, uint8_t aux_flags) {
7479
4.46k
  int rv;
7480
4.46k
  nghttp2_outbound_item *item;
7481
4.46k
  nghttp2_frame *frame;
7482
4.46k
  uint8_t *opaque_data_copy = NULL;
7483
4.46k
  nghttp2_goaway_aux_data *aux_data;
7484
4.46k
  nghttp2_mem *mem;
7485
7486
4.46k
  mem = &session->mem;
7487
7488
4.46k
  if (nghttp2_session_is_my_stream_id(session, last_stream_id)) {
7489
0
    return NGHTTP2_ERR_INVALID_ARGUMENT;
7490
0
  }
7491
7492
4.46k
  if (opaque_data_len) {
7493
2.81k
    if (opaque_data_len + 8 > NGHTTP2_MAX_PAYLOADLEN) {
7494
0
      return NGHTTP2_ERR_INVALID_ARGUMENT;
7495
0
    }
7496
2.81k
    opaque_data_copy = nghttp2_mem_malloc(mem, opaque_data_len);
7497
2.81k
    if (opaque_data_copy == NULL) {
7498
0
      return NGHTTP2_ERR_NOMEM;
7499
0
    }
7500
2.81k
    memcpy(opaque_data_copy, opaque_data, opaque_data_len);
7501
2.81k
  }
7502
7503
4.46k
  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
7504
4.46k
  if (item == NULL) {
7505
0
    nghttp2_mem_free(mem, opaque_data_copy);
7506
0
    return NGHTTP2_ERR_NOMEM;
7507
0
  }
7508
7509
4.46k
  nghttp2_outbound_item_init(item);
7510
7511
4.46k
  frame = &item->frame;
7512
7513
  /* last_stream_id must not be increased from the value previously
7514
     sent */
7515
4.46k
  last_stream_id =
7516
4.46k
      nghttp2_min_int32(last_stream_id, session->local_last_stream_id);
7517
7518
4.46k
  nghttp2_frame_goaway_init(&frame->goaway, last_stream_id, error_code,
7519
4.46k
                            opaque_data_copy, opaque_data_len);
7520
7521
4.46k
  aux_data = &item->aux_data.goaway;
7522
4.46k
  aux_data->flags = aux_flags;
7523
7524
4.46k
  rv = nghttp2_session_add_item(session, item);
7525
4.46k
  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
4.46k
  session->goaway_flags |= NGHTTP2_GOAWAY_SUBMITTED;
7532
7533
4.46k
  return 0;
7534
4.46k
}
7535
7536
int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags,
7537
                                      int32_t stream_id,
7538
13.0k
                                      int32_t window_size_increment) {
7539
13.0k
  int rv;
7540
13.0k
  nghttp2_outbound_item *item;
7541
13.0k
  nghttp2_frame *frame;
7542
13.0k
  nghttp2_mem *mem;
7543
7544
13.0k
  mem = &session->mem;
7545
13.0k
  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
7546
13.0k
  if (item == NULL) {
7547
0
    return NGHTTP2_ERR_NOMEM;
7548
0
  }
7549
7550
13.0k
  nghttp2_outbound_item_init(item);
7551
7552
13.0k
  frame = &item->frame;
7553
7554
13.0k
  nghttp2_frame_window_update_init(&frame->window_update, flags, stream_id,
7555
13.0k
                                   window_size_increment);
7556
7557
13.0k
  rv = nghttp2_session_add_item(session, item);
7558
7559
13.0k
  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
13.0k
  return 0;
7565
13.0k
}
7566
7567
static void
7568
session_append_inflight_settings(nghttp2_session *session,
7569
13.0k
                                 nghttp2_inflight_settings *settings) {
7570
13.0k
  nghttp2_inflight_settings **i;
7571
7572
13.0k
  for (i = &session->inflight_settings_head; *i; i = &(*i)->next)
7573
0
    ;
7574
7575
13.0k
  *i = settings;
7576
13.0k
}
7577
7578
int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
7579
24.0k
                                 const nghttp2_settings_entry *iv, size_t niv) {
7580
24.0k
  nghttp2_outbound_item *item;
7581
24.0k
  nghttp2_frame *frame;
7582
24.0k
  nghttp2_settings_entry *iv_copy;
7583
24.0k
  size_t i;
7584
24.0k
  int rv;
7585
24.0k
  nghttp2_mem *mem;
7586
24.0k
  nghttp2_inflight_settings *inflight_settings = NULL;
7587
24.0k
  uint8_t no_rfc7540_pri = session->pending_no_rfc7540_priorities;
7588
7589
24.0k
  mem = &session->mem;
7590
7591
24.0k
  if (flags & NGHTTP2_FLAG_ACK) {
7592
10.9k
    if (niv != 0) {
7593
0
      return NGHTTP2_ERR_INVALID_ARGUMENT;
7594
0
    }
7595
7596
10.9k
    if (session->obq_flood_counter_ >= session->max_outbound_ack) {
7597
0
      return NGHTTP2_ERR_FLOODED;
7598
0
    }
7599
10.9k
  }
7600
7601
24.0k
  if (!nghttp2_iv_check(iv, niv)) {
7602
0
    return NGHTTP2_ERR_INVALID_ARGUMENT;
7603
0
  }
7604
7605
63.1k
  for (i = 0; i < niv; ++i) {
7606
39.1k
    if (iv[i].settings_id != NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES) {
7607
39.1k
      continue;
7608
39.1k
    }
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
24.0k
  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
7621
24.0k
  if (item == NULL) {
7622
0
    return NGHTTP2_ERR_NOMEM;
7623
0
  }
7624
7625
24.0k
  if (niv > 0) {
7626
13.0k
    iv_copy = nghttp2_frame_iv_copy(iv, niv, mem);
7627
13.0k
    if (iv_copy == NULL) {
7628
0
      nghttp2_mem_free(mem, item);
7629
0
      return NGHTTP2_ERR_NOMEM;
7630
0
    }
7631
13.0k
  } else {
7632
10.9k
    iv_copy = NULL;
7633
10.9k
  }
7634
7635
24.0k
  if ((flags & NGHTTP2_FLAG_ACK) == 0) {
7636
13.0k
    rv = inflight_settings_new(&inflight_settings, iv, niv, mem);
7637
13.0k
    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
13.0k
  }
7644
7645
24.0k
  nghttp2_outbound_item_init(item);
7646
7647
24.0k
  frame = &item->frame;
7648
7649
24.0k
  nghttp2_frame_settings_init(&frame->settings, flags, iv_copy, niv);
7650
24.0k
  rv = nghttp2_session_add_item(session, item);
7651
24.0k
  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
24.0k
  if (flags & NGHTTP2_FLAG_ACK) {
7664
10.9k
    ++session->obq_flood_counter_;
7665
13.0k
  } else {
7666
13.0k
    session_append_inflight_settings(session, inflight_settings);
7667
13.0k
  }
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
50.1k
  for (i = niv; i > 0; --i) {
7674
39.1k
    if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) {
7675
13.0k
      session->pending_local_max_concurrent_stream = iv[i - 1].value;
7676
13.0k
      break;
7677
13.0k
    }
7678
39.1k
  }
7679
7680
24.0k
  for (i = niv; i > 0; --i) {
7681
13.0k
    if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_PUSH) {
7682
13.0k
      session->pending_enable_push = (uint8_t)iv[i - 1].value;
7683
13.0k
      break;
7684
13.0k
    }
7685
13.0k
  }
7686
7687
63.1k
  for (i = niv; i > 0; --i) {
7688
39.1k
    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
39.1k
  }
7693
7694
24.0k
  if (no_rfc7540_pri == UINT8_MAX) {
7695
13.0k
    session->pending_no_rfc7540_priorities = 0;
7696
13.0k
  } else {
7697
10.9k
    session->pending_no_rfc7540_priorities = no_rfc7540_pri;
7698
10.9k
  }
7699
7700
24.0k
  return 0;
7701
24.0k
}
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
3.72k
                              nghttp2_stream *stream) {
7707
3.72k
  int rv;
7708
3.72k
  uint32_t data_flags;
7709
3.72k
  nghttp2_ssize payloadlen;
7710
3.72k
  nghttp2_ssize padded_payloadlen;
7711
3.72k
  nghttp2_buf *buf;
7712
3.72k
  size_t max_payloadlen;
7713
7714
3.72k
  assert(bufs->head == bufs->cur);
7715
7716
3.72k
  buf = &bufs->cur->buf;
7717
7718
3.72k
  if (session->callbacks.read_length_callback2 ||
7719
3.72k
      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
3.72k
  assert(nghttp2_buf_avail(buf) >= datamax);
7767
7768
3.72k
  data_flags = NGHTTP2_DATA_FLAG_NONE;
7769
3.72k
  switch (aux_data->dpw.version) {
7770
3.72k
  case NGHTTP2_DATA_PROVIDER_V1:
7771
3.72k
    payloadlen = (nghttp2_ssize)aux_data->dpw.data_prd.v1.read_callback(
7772
3.72k
        session, frame->hd.stream_id, buf->pos, datamax, &data_flags,
7773
3.72k
        &aux_data->dpw.data_prd.source, session->user_data);
7774
7775
3.72k
    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
3.72k
  }
7786
7787
3.72k
  if (payloadlen == NGHTTP2_ERR_DEFERRED ||
7788
3.72k
      payloadlen == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE ||
7789
3.72k
      payloadlen == NGHTTP2_ERR_PAUSE) {
7790
724
    DEBUGF("send: DATA postponed due to %s\n",
7791
724
           nghttp2_strerror((int)payloadlen));
7792
7793
724
    return (int)payloadlen;
7794
724
  }
7795
7796
2.99k
  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
2.99k
  buf->last = buf->pos + payloadlen;
7802
2.99k
  buf->pos -= NGHTTP2_FRAME_HDLEN;
7803
7804
  /* Clear flags, because this may contain previous flags of previous
7805
     DATA */
7806
2.99k
  frame->hd.flags = NGHTTP2_FLAG_NONE;
7807
7808
2.99k
  if (data_flags & NGHTTP2_DATA_FLAG_EOF) {
7809
1.06k
    aux_data->eof = 1;
7810
    /* If NGHTTP2_DATA_FLAG_NO_END_STREAM is set, don't set
7811
       NGHTTP2_FLAG_END_STREAM */
7812
1.06k
    if ((aux_data->flags & NGHTTP2_FLAG_END_STREAM) &&
7813
1.06k
        (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) {
7814
1.06k
      frame->hd.flags |= NGHTTP2_FLAG_END_STREAM;
7815
1.06k
    }
7816
1.06k
  }
7817
7818
2.99k
  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
2.99k
  frame->hd.length = (size_t)payloadlen;
7828
2.99k
  frame->data.padlen = 0;
7829
7830
2.99k
  max_payloadlen =
7831
2.99k
      nghttp2_min_size(datamax, frame->hd.length + NGHTTP2_MAX_PADLEN);
7832
7833
2.99k
  padded_payloadlen =
7834
2.99k
      session_call_select_padding(session, frame, max_payloadlen);
7835
7836
2.99k
  if (nghttp2_is_fatal((int)padded_payloadlen)) {
7837
0
    return (int)padded_payloadlen;
7838
0
  }
7839
7840
2.99k
  frame->data.padlen = (size_t)(padded_payloadlen - payloadlen);
7841
7842
2.99k
  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
7843
7844
2.99k
  nghttp2_frame_add_pad(bufs, &frame->hd, frame->data.padlen,
7845
2.99k
                        aux_data->no_copy);
7846
7847
2.99k
  session_reschedule_stream(session, stream);
7848
7849
2.99k
  if (frame->hd.length == 0 && (data_flags & NGHTTP2_DATA_FLAG_EOF) &&
7850
2.99k
      (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
2.99k
  return 0;
7858
2.99k
}
7859
7860
void *nghttp2_session_get_stream_user_data(nghttp2_session *session,
7861
154k
                                           int32_t stream_id) {
7862
154k
  nghttp2_stream *stream;
7863
154k
  stream = nghttp2_session_get_stream(session, stream_id);
7864
154k
  if (stream) {
7865
154k
    return stream->stream_user_data;
7866
154k
  } else {
7867
298
    return NULL;
7868
298
  }
7869
154k
}
7870
7871
int nghttp2_session_set_stream_user_data(nghttp2_session *session,
7872
                                         int32_t stream_id,
7873
16.3k
                                         void *stream_user_data) {
7874
16.3k
  nghttp2_stream *stream;
7875
16.3k
  nghttp2_frame *frame;
7876
16.3k
  nghttp2_outbound_item *item;
7877
7878
16.3k
  stream = nghttp2_session_get_stream(session, stream_id);
7879
16.3k
  if (stream) {
7880
12.6k
    stream->stream_user_data = stream_user_data;
7881
12.6k
    return 0;
7882
12.6k
  }
7883
7884
3.69k
  if (session->server || !nghttp2_session_is_my_stream_id(session, stream_id) ||
7885
3.69k
      !nghttp2_outbound_queue_top(&session->ob_syn)) {
7886
2.98k
    return NGHTTP2_ERR_INVALID_ARGUMENT;
7887
2.98k
  }
7888
7889
719
  frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame;
7890
719
  assert(frame->hd.type == NGHTTP2_HEADERS);
7891
7892
719
  if (frame->hd.stream_id > stream_id ||
7893
719
      (uint32_t)stream_id >= session->next_stream_id) {
7894
0
    return NGHTTP2_ERR_INVALID_ARGUMENT;
7895
0
  }
7896
7897
719
  for (item = session->ob_syn.head; item; item = item->qnext) {
7898
719
    if (item->frame.hd.stream_id < stream_id) {
7899
0
      continue;
7900
0
    }
7901
7902
719
    if (item->frame.hd.stream_id > stream_id) {
7903
0
      break;
7904
0
    }
7905
7906
719
    item->aux_data.headers.stream_user_data = stream_user_data;
7907
719
    return 0;
7908
719
  }
7909
7910
0
  return NGHTTP2_ERR_INVALID_ARGUMENT;
7911
719
}
7912
7913
5.71M
int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) {
7914
5.71M
  int rv;
7915
5.71M
  nghttp2_stream *stream;
7916
5.71M
  stream = nghttp2_session_get_stream(session, stream_id);
7917
5.71M
  if (stream == NULL || !nghttp2_stream_check_deferred_item(stream)) {
7918
5.71M
    return NGHTTP2_ERR_INVALID_ARGUMENT;
7919
5.71M
  }
7920
7921
741
  rv = session_resume_deferred_stream_item(session, stream,
7922
741
                                           NGHTTP2_STREAM_FLAG_DEFERRED_USER);
7923
7924
741
  if (nghttp2_is_fatal(rv)) {
7925
0
    return rv;
7926
0
  }
7927
7928
741
  return 0;
7929
741
}
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
0
                                                       int32_t stream_id) {
7952
0
  nghttp2_stream *stream;
7953
0
  stream = nghttp2_session_get_stream(session, stream_id);
7954
0
  if (stream == NULL) {
7955
0
    return -1;
7956
0
  }
7957
0
  return stream->local_window_size;
7958
0
}
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
11.4M
                                                      int32_t stream_id) {
7996
11.4M
  nghttp2_stream *stream;
7997
7998
11.4M
  stream = nghttp2_session_get_stream(session, stream_id);
7999
11.4M
  if (stream == NULL) {
8000
4.76M
    return -1;
8001
4.76M
  }
8002
8003
  /* stream->remote_window_size can be negative when
8004
     SETTINGS_INITIAL_WINDOW_SIZE is changed. */
8005
6.66M
  return nghttp2_max_int32(0, stream->remote_window_size);
8006
11.4M
}
8007
8008
5.71M
int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session) {
8009
5.71M
  return session->remote_window_size;
8010
5.71M
}
8011
8012
uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,
8013
22.0k
                                             nghttp2_settings_id id) {
8014
22.0k
  switch (id) {
8015
0
  case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
8016
0
    return session->remote_settings.header_table_size;
8017
11.0k
  case NGHTTP2_SETTINGS_ENABLE_PUSH:
8018
11.0k
    return session->remote_settings.enable_push;
8019
11.0k
  case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
8020
11.0k
    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
22.0k
  }
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
14
                                            void *stream_user_data) {
8066
14
  nghttp2_stream *stream;
8067
14
  nghttp2_frame frame;
8068
14
  nghttp2_settings_entry *iv;
8069
14
  size_t niv;
8070
14
  int rv;
8071
14
  nghttp2_priority_spec pri_spec;
8072
14
  nghttp2_mem *mem;
8073
8074
14
  mem = &session->mem;
8075
8076
14
  if ((!session->server && session->next_stream_id != 1) ||
8077
14
      (session->server && session->last_recv_stream_id >= 1)) {
8078
0
    return NGHTTP2_ERR_PROTO;
8079
0
  }
8080
14
  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
14
  if (settings_payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH >
8085
14
      session->max_settings) {
8086
0
    return NGHTTP2_ERR_TOO_MANY_SETTINGS;
8087
0
  }
8088
14
  rv = nghttp2_frame_unpack_settings_payload2(&iv, &niv, settings_payload,
8089
14
                                              settings_payloadlen, mem);
8090
14
  if (rv != 0) {
8091
0
    return rv;
8092
0
  }
8093
8094
14
  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
14
  } else {
8101
14
    rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, niv);
8102
14
  }
8103
14
  nghttp2_mem_free(mem, iv);
8104
14
  if (rv != 0) {
8105
0
    return rv;
8106
0
  }
8107
8108
14
  nghttp2_priority_spec_default_init(&pri_spec);
8109
8110
14
  stream = nghttp2_session_open_stream(
8111
14
      session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_OPENING,
8112
14
      session->server ? NULL : stream_user_data);
8113
14
  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
14
  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
14
  } else {
8125
14
    nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
8126
14
    session->last_sent_stream_id = 1;
8127
14
    session->next_stream_id += 2;
8128
14
  }
8129
14
  return 0;
8130
14
}
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
14
                             void *stream_user_data) {
8165
14
  int rv;
8166
14
  nghttp2_stream *stream;
8167
8168
14
  rv = nghttp2_session_upgrade_internal(session, settings_payload,
8169
14
                                        settings_payloadlen, stream_user_data);
8170
14
  if (rv != 0) {
8171
0
    return rv;
8172
0
  }
8173
8174
14
  stream = nghttp2_session_get_stream(session, 1);
8175
14
  assert(stream);
8176
8177
14
  if (head_request) {
8178
1
    stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
8179
1
  }
8180
8181
14
  return 0;
8182
14
}
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
435
                            size_t size) {
8212
435
  int rv;
8213
435
  nghttp2_stream *stream;
8214
8215
435
  if (stream_id == 0) {
8216
0
    return NGHTTP2_ERR_INVALID_ARGUMENT;
8217
0
  }
8218
8219
435
  if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
8220
0
    return NGHTTP2_ERR_INVALID_STATE;
8221
0
  }
8222
8223
435
  rv = session_update_connection_consumed_size(session, size);
8224
8225
435
  if (nghttp2_is_fatal(rv)) {
8226
0
    return rv;
8227
0
  }
8228
8229
435
  stream = nghttp2_session_get_stream(session, stream_id);
8230
8231
435
  if (!stream) {
8232
0
    return 0;
8233
0
  }
8234
8235
435
  rv = session_update_stream_consumed_size(session, stream, size);
8236
8237
435
  if (nghttp2_is_fatal(rv)) {
8238
0
    return rv;
8239
0
  }
8240
8241
435
  return 0;
8242
435
}
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
}