Coverage Report

Created: 2026-10-28 06:13

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