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