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