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