Coverage Report

Created: 2026-02-09 06:05

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