Coverage Report

Created: 2025-06-07 06:02

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