Coverage Report

Created: 2022-11-30 06:20

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