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