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