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