Coverage Report

Created: 2025-07-12 06:50

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