Coverage Report

Created: 2026-03-22 07:09

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