Coverage Report

Created: 2026-09-14 06:50

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