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