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