Coverage Report

Created: 2026-05-30 06:40

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