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