Coverage Report

Created: 2025-08-26 06:12

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