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