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