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