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