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