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