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