Coverage Report

Created: 2026-01-25 06:21

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