Coverage Report

Created: 2025-12-11 07:13

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