Coverage Report

Created: 2026-03-12 06:35

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