Coverage Report

Created: 2026-07-30 07:04

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