Coverage Report

Created: 2026-10-28 06:08

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