Coverage Report

Created: 2025-07-23 06:43

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