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