]> granicus.if.org Git - apache/blob - modules/http2/h2_session.c
a little love for timneouts in the sync mpm setups
[apache] / modules / http2 / h2_session.c
1 /* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <assert.h>
17 #include <apr_thread_cond.h>
18 #include <apr_base64.h>
19 #include <apr_strings.h>
20
21 #include <ap_mpm.h>
22
23 #include <httpd.h>
24 #include <http_core.h>
25 #include <http_config.h>
26 #include <http_log.h>
27 #include <scoreboard.h>
28
29 #include "h2_private.h"
30 #include "h2_bucket_eoc.h"
31 #include "h2_bucket_eos.h"
32 #include "h2_config.h"
33 #include "h2_ctx.h"
34 #include "h2_filter.h"
35 #include "h2_h2.h"
36 #include "h2_mplx.h"
37 #include "h2_push.h"
38 #include "h2_request.h"
39 #include "h2_response.h"
40 #include "h2_stream.h"
41 #include "h2_stream_set.h"
42 #include "h2_from_h1.h"
43 #include "h2_task.h"
44 #include "h2_session.h"
45 #include "h2_util.h"
46 #include "h2_version.h"
47 #include "h2_workers.h"
48
49
50 static int frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen);
51
52 static int h2_session_status_from_apr_status(apr_status_t rv)
53 {
54     if (rv == APR_SUCCESS) {
55         return NGHTTP2_NO_ERROR;
56     }
57     else if (APR_STATUS_IS_EAGAIN(rv)) {
58         return NGHTTP2_ERR_WOULDBLOCK;
59     }
60     else if (APR_STATUS_IS_EOF(rv)) {
61             return NGHTTP2_ERR_EOF;
62     }
63     return NGHTTP2_ERR_PROTO;
64 }
65
66 static void update_window(void *ctx, int stream_id, apr_off_t bytes_read)
67 {
68     h2_session *session = (h2_session*)ctx;
69     nghttp2_session_consume(session->ngh2, stream_id, bytes_read);
70     ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
71                   "h2_session(%ld-%d): consumed %ld bytes",
72                   session->id, stream_id, (long)bytes_read);
73 }
74
75 static apr_status_t h2_session_receive(void *ctx, 
76                                        const char *data, apr_size_t len,
77                                        apr_size_t *readlen);
78
79 static int is_accepting_streams(h2_session *session); 
80 static void dispatch_event(h2_session *session, h2_session_event_t ev, 
81                              int err, const char *msg);
82
83 h2_stream *h2_session_open_stream(h2_session *session, int stream_id)
84 {
85     h2_stream * stream;
86     apr_pool_t *stream_pool;
87     
88     if (session->spare) {
89         stream_pool = session->spare;
90         session->spare = NULL;
91     }
92     else {
93         apr_pool_create(&stream_pool, session->pool);
94     }
95     
96     stream = h2_stream_open(stream_id, stream_pool, session);
97     
98     h2_stream_set_add(session->streams, stream);
99     if (H2_STREAM_CLIENT_INITIATED(stream_id)
100         && stream_id > session->max_stream_received) {
101         ++session->requests_received;
102         session->max_stream_received = stream->id;
103     }
104     
105     return stream;
106 }
107
108 #ifdef H2_NG2_STREAM_API
109
110 /**
111  * Determine the importance of streams when scheduling tasks.
112  * - if both stream depend on the same one, compare weights
113  * - if one stream is closer to the root, prioritize that one
114  * - if both are on the same level, use the weight of their root
115  *   level ancestors
116  */
117 static int spri_cmp(int sid1, nghttp2_stream *s1, 
118                     int sid2, nghttp2_stream *s2, h2_session *session)
119 {
120     nghttp2_stream *p1, *p2;
121     
122     p1 = nghttp2_stream_get_parent(s1);
123     p2 = nghttp2_stream_get_parent(s2);
124     
125     if (p1 == p2) {
126         int32_t w1, w2;
127         
128         w1 = nghttp2_stream_get_weight(s1);
129         w2 = nghttp2_stream_get_weight(s2);
130         return w2 - w1;
131     }
132     else if (!p1) {
133         /* stream 1 closer to root */
134         return -1;
135     }
136     else if (!p2) {
137         /* stream 2 closer to root */
138         return 1;
139     }
140     return spri_cmp(sid1, p1, sid2, p2, session);
141 }
142
143 static int stream_pri_cmp(int sid1, int sid2, void *ctx)
144 {
145     h2_session *session = ctx;
146     nghttp2_stream *s1, *s2;
147     
148     s1 = nghttp2_session_find_stream(session->ngh2, sid1);
149     s2 = nghttp2_session_find_stream(session->ngh2, sid2);
150
151     if (s1 == s2) {
152         return 0;
153     }
154     else if (!s1) {
155         return 1;
156     }
157     else if (!s2) {
158         return -1;
159     }
160     return spri_cmp(sid1, s1, sid2, s2, session);
161 }
162
163 #else /* ifdef H2_NG2_STREAM_API */
164
165 /* In absence of nghttp2_stream API, which gives information about
166  * priorities since nghttp2 1.3.x, we just sort the streams by
167  * their identifier, aka. order of arrival.
168  */
169 static int stream_pri_cmp(int sid1, int sid2, void *ctx)
170 {
171     (void)ctx;
172     return sid1 - sid2;
173 }
174
175 #endif /* (ifdef else) H2_NG2_STREAM_API */
176
177 static apr_status_t stream_schedule(h2_session *session,
178                                     h2_stream *stream, int eos)
179 {
180     (void)session;
181     return h2_stream_schedule(stream, eos, h2_session_push_enabled(session), 
182                               stream_pri_cmp, session);
183 }
184
185 /*
186  * Callback when nghttp2 wants to send bytes back to the client.
187  */
188 static ssize_t send_cb(nghttp2_session *ngh2,
189                        const uint8_t *data, size_t length,
190                        int flags, void *userp)
191 {
192     h2_session *session = (h2_session *)userp;
193     apr_status_t status;
194     
195     (void)ngh2;
196     (void)flags;
197     status = h2_conn_io_write(&session->io, (const char *)data, length);
198     if (status == APR_SUCCESS) {
199         return length;
200     }
201     if (APR_STATUS_IS_EAGAIN(status)) {
202         return NGHTTP2_ERR_WOULDBLOCK;
203     }
204     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, APLOGNO(03062)
205                   "h2_session: send error");
206     return h2_session_status_from_apr_status(status);
207 }
208
209 static int on_invalid_frame_recv_cb(nghttp2_session *ngh2,
210                                     const nghttp2_frame *frame,
211                                     int error, void *userp)
212 {
213     h2_session *session = (h2_session *)userp;
214     (void)ngh2;
215     
216     if (APLOGcdebug(session->c)) {
217         char buffer[256];
218         
219         frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
220         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03063)
221                       "h2_session(%ld): recv unknown FRAME[%s], frames=%ld/%ld (r/s)",
222                       session->id, buffer, (long)session->frames_received,
223                      (long)session->frames_sent);
224     }
225     return 0;
226 }
227
228 static int on_data_chunk_recv_cb(nghttp2_session *ngh2, uint8_t flags,
229                                  int32_t stream_id,
230                                  const uint8_t *data, size_t len, void *userp)
231 {
232     h2_session *session = (h2_session *)userp;
233     apr_status_t status = APR_SUCCESS;
234     h2_stream * stream;
235     int rv;
236     
237     (void)flags;
238     if (!is_accepting_streams(session)) {
239         /* ignore */
240         return 0;
241     }
242     
243     stream = h2_session_get_stream(session, stream_id);
244     if (!stream) {
245         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03064)
246                       "h2_stream(%ld-%d): on_data_chunk for unknown stream",
247                       session->id, (int)stream_id);
248         rv = nghttp2_submit_rst_stream(ngh2, NGHTTP2_FLAG_NONE, stream_id,
249                                        NGHTTP2_INTERNAL_ERROR);
250         if (nghttp2_is_fatal(rv)) {
251             return NGHTTP2_ERR_CALLBACK_FAILURE;
252         }
253         return 0;
254     }
255     
256     status = h2_stream_write_data(stream, (const char *)data, len);
257     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, session->c,
258                   "h2_stream(%ld-%d): data_chunk_recv, written %ld bytes",
259                   session->id, stream_id, (long)len);
260     if (status != APR_SUCCESS) {
261         update_window(session, stream_id, len);
262         rv = nghttp2_submit_rst_stream(ngh2, NGHTTP2_FLAG_NONE, stream_id,
263                                        H2_STREAM_RST(stream, H2_ERR_INTERNAL_ERROR));
264         if (nghttp2_is_fatal(rv)) {
265             return NGHTTP2_ERR_CALLBACK_FAILURE;
266         }
267     }
268     return 0;
269 }
270
271 static apr_status_t stream_release(h2_session *session, 
272                                    h2_stream *stream,
273                                    uint32_t error_code) 
274 {
275     if (!error_code) {
276         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
277                       "h2_stream(%ld-%d): handled, closing", 
278                       session->id, (int)stream->id);
279         if (stream->id > session->max_stream_handled) {
280             session->max_stream_handled = stream->id;
281         }
282     }
283     else {
284         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03065)
285                       "h2_stream(%ld-%d): closing with err=%d %s", 
286                       session->id, (int)stream->id, (int)error_code,
287                       h2_h2_err_description(error_code));
288         h2_stream_rst(stream, error_code);
289     }
290     
291     return h2_conn_io_writeb(&session->io,
292                              h2_bucket_eos_create(session->c->bucket_alloc, 
293                                                   stream));
294 }
295
296 static int on_stream_close_cb(nghttp2_session *ngh2, int32_t stream_id,
297                               uint32_t error_code, void *userp)
298 {
299     h2_session *session = (h2_session *)userp;
300     h2_stream *stream;
301     
302     (void)ngh2;
303     stream = h2_session_get_stream(session, stream_id);
304     if (stream) {
305         stream_release(session, stream, error_code);
306     }
307     return 0;
308 }
309
310 static int on_begin_headers_cb(nghttp2_session *ngh2,
311                                const nghttp2_frame *frame, void *userp)
312 {
313     h2_session *session = (h2_session *)userp;
314     h2_stream *s;
315     
316     /* We may see HEADERs at the start of a stream or after all DATA
317      * streams to carry trailers. */
318     (void)ngh2;
319     s = h2_session_get_stream(session, frame->hd.stream_id);
320     if (s) {
321         /* nop */
322     }
323     else {
324         s = h2_session_open_stream((h2_session *)userp, frame->hd.stream_id);
325     }
326     return s? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
327 }
328
329 static int on_header_cb(nghttp2_session *ngh2, const nghttp2_frame *frame,
330                         const uint8_t *name, size_t namelen,
331                         const uint8_t *value, size_t valuelen,
332                         uint8_t flags,
333                         void *userp)
334 {
335     h2_session *session = (h2_session *)userp;
336     h2_stream * stream;
337     apr_status_t status;
338     
339     (void)ngh2;
340     (void)flags;
341     if (!is_accepting_streams(session)) {
342         /* just ignore */
343         return 0;
344     }
345     
346     stream = h2_session_get_stream(session, frame->hd.stream_id);
347     if (!stream) {
348         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
349                       APLOGNO(02920) 
350                       "h2_session:  stream(%ld-%d): on_header for unknown stream",
351                       session->id, (int)frame->hd.stream_id);
352         return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
353     }
354     
355     status = h2_stream_add_header(stream, (const char *)name, namelen,
356                                   (const char *)value, valuelen);
357                                     
358     if (status != APR_SUCCESS) {
359         return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
360     }
361     return 0;
362 }
363
364 /**
365  * nghttp2 session has received a complete frame. Most, it uses
366  * for processing of internal state. HEADER and DATA frames however
367  * we need to handle ourself.
368  */
369 static int on_frame_recv_cb(nghttp2_session *ng2s,
370                             const nghttp2_frame *frame,
371                             void *userp)
372 {
373     h2_session *session = (h2_session *)userp;
374     apr_status_t status = APR_SUCCESS;
375     h2_stream *stream;
376     
377     if (APLOGcdebug(session->c)) {
378         char buffer[256];
379         
380         frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
381         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03066)
382                       "h2_session(%ld): recv FRAME[%s], frames=%ld/%ld (r/s)",
383                       session->id, buffer, (long)session->frames_received,
384                      (long)session->frames_sent);
385     }
386
387     ++session->frames_received;
388     switch (frame->hd.type) {
389         case NGHTTP2_HEADERS:
390             /* This can be HEADERS for a new stream, defining the request,
391              * or HEADER may come after DATA at the end of a stream as in
392              * trailers */
393             stream = h2_session_get_stream(session, frame->hd.stream_id);
394             if (stream) {
395                 int eos = (frame->hd.flags & NGHTTP2_FLAG_END_STREAM);
396                 
397                 if (h2_stream_is_scheduled(stream)) {
398                     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
399                                   "h2_stream(%ld-%d): TRAILER, eos=%d", 
400                                   session->id, frame->hd.stream_id, eos);
401                     if (eos) {
402                         status = h2_stream_close_input(stream);
403                     }
404                 }
405                 else {
406                     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
407                                   "h2_stream(%ld-%d): HEADER, eos=%d", 
408                                   session->id, frame->hd.stream_id, eos);
409                     status = stream_schedule(session, stream, eos);
410                 }
411             }
412             else {
413                 status = APR_EINVAL;
414             }
415             break;
416         case NGHTTP2_DATA:
417             stream = h2_session_get_stream(session, frame->hd.stream_id);
418             if (stream) {
419                 int eos = (frame->hd.flags & NGHTTP2_FLAG_END_STREAM);
420                 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
421                               "h2_stream(%ld-%d): DATA, len=%ld, eos=%d", 
422                               session->id, frame->hd.stream_id, 
423                               (long)frame->hd.length, eos);
424                 if (eos) {
425                     status = h2_stream_close_input(stream);
426                 }
427             }
428             else {
429                 status = APR_EINVAL;
430             }
431             break;
432         case NGHTTP2_PRIORITY:
433             session->reprioritize = 1;
434             ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
435                           "h2_session:  stream(%ld-%d): PRIORITY frame "
436                           " weight=%d, dependsOn=%d, exclusive=%d", 
437                           session->id, (int)frame->hd.stream_id,
438                           frame->priority.pri_spec.weight,
439                           frame->priority.pri_spec.stream_id,
440                           frame->priority.pri_spec.exclusive);
441             break;
442         case NGHTTP2_WINDOW_UPDATE:
443             ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
444                           "h2_session:  stream(%ld-%d): WINDOW_UPDATE "
445                           "incr=%d", 
446                           session->id, (int)frame->hd.stream_id,
447                           frame->window_update.window_size_increment);
448             break;
449         case NGHTTP2_RST_STREAM:
450             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03067)
451                           "h2_session(%ld-%d): RST_STREAM by client, errror=%d",
452                           session->id, (int)frame->hd.stream_id,
453                           (int)frame->rst_stream.error_code);
454             stream = h2_session_get_stream(session, frame->hd.stream_id);
455             if (stream && stream->initiated_on) {
456                 ++session->pushes_reset;
457             }
458             else {
459                 ++session->streams_reset;
460             }
461             break;
462         case NGHTTP2_GOAWAY:
463             dispatch_event(session, H2_SESSION_EV_REMOTE_GOAWAY, 0, NULL);
464             break;
465         default:
466             if (APLOGctrace2(session->c)) {
467                 char buffer[256];
468                 
469                 frame_print(frame, buffer,
470                             sizeof(buffer)/sizeof(buffer[0]));
471                 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
472                               "h2_session: on_frame_rcv %s", buffer);
473             }
474             break;
475     }
476
477     if (status != APR_SUCCESS) {
478         int rv;
479         
480         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
481                       APLOGNO(02923) 
482                       "h2_session: stream(%ld-%d): error handling frame",
483                       session->id, (int)frame->hd.stream_id);
484         rv = nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE,
485                                        frame->hd.stream_id,
486                                        NGHTTP2_INTERNAL_ERROR);
487         if (nghttp2_is_fatal(rv)) {
488             return NGHTTP2_ERR_CALLBACK_FAILURE;
489         }
490     }
491     
492     return 0;
493 }
494
495 static apr_status_t pass_data(void *ctx, 
496                               const char *data, apr_off_t length)
497 {
498     return h2_conn_io_write(&((h2_session*)ctx)->io, data, length);
499 }
500
501
502 static char immortal_zeros[H2_MAX_PADLEN];
503
504 static int on_send_data_cb(nghttp2_session *ngh2, 
505                            nghttp2_frame *frame, 
506                            const uint8_t *framehd, 
507                            size_t length, 
508                            nghttp2_data_source *source, 
509                            void *userp)
510 {
511     apr_status_t status = APR_SUCCESS;
512     h2_session *session = (h2_session *)userp;
513     int stream_id = (int)frame->hd.stream_id;
514     unsigned char padlen;
515     int eos;
516     h2_stream *stream;
517     
518     (void)ngh2;
519     (void)source;
520     if (frame->data.padlen > H2_MAX_PADLEN) {
521         return NGHTTP2_ERR_PROTO;
522     }
523     padlen = (unsigned char)frame->data.padlen;
524     
525     stream = h2_session_get_stream(session, stream_id);
526     if (!stream) {
527         ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_NOTFOUND, session->c,
528                       APLOGNO(02924) 
529                       "h2_stream(%ld-%d): send_data",
530                       session->id, (int)stream_id);
531         return NGHTTP2_ERR_CALLBACK_FAILURE;
532     }
533     
534     ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
535                   "h2_stream(%ld-%d): send_data_cb for %ld bytes",
536                   session->id, (int)stream_id, (long)length);
537                   
538     if (h2_conn_io_is_buffered(&session->io)) {
539         status = h2_conn_io_write(&session->io, (const char *)framehd, 9);
540         if (status == APR_SUCCESS) {
541             if (padlen) {
542                 status = h2_conn_io_write(&session->io, (const char *)&padlen, 1);
543             }
544             
545             if (status == APR_SUCCESS) {
546                 apr_off_t len = length;
547                 status = h2_stream_readx(stream, pass_data, session, &len, &eos);
548                 if (status == APR_SUCCESS && len != length) {
549                     status = APR_EINVAL;
550                 }
551             }
552             
553             if (status == APR_SUCCESS && padlen) {
554                 if (padlen) {
555                     status = h2_conn_io_write(&session->io, immortal_zeros, padlen);
556                 }
557             }
558         }
559     }
560     else {
561         apr_bucket *b;
562         char *header = apr_pcalloc(stream->pool, 10);
563         memcpy(header, (const char *)framehd, 9);
564         if (padlen) {
565             header[9] = (char)padlen;
566         }
567         b = apr_bucket_pool_create(header, padlen? 10 : 9, 
568                                    stream->pool, session->c->bucket_alloc);
569         status = h2_conn_io_writeb(&session->io, b);
570         
571         if (status == APR_SUCCESS) {
572             apr_off_t len = length;
573             status = h2_stream_read_to(stream, session->io.output, &len, &eos);
574             if (status == APR_SUCCESS && len != length) {
575                 status = APR_EINVAL;
576             }
577         }
578             
579         if (status == APR_SUCCESS && padlen) {
580             b = apr_bucket_immortal_create(immortal_zeros, padlen, 
581                                            session->c->bucket_alloc);
582             status = h2_conn_io_writeb(&session->io, b);
583         }
584     }
585     
586     
587     if (status == APR_SUCCESS) {
588         stream->data_frames_sent++;
589         h2_conn_io_consider_flush(&session->io);
590         return 0;
591     }
592     else {
593         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
594                       APLOGNO(02925) 
595                       "h2_stream(%ld-%d): failed send_data_cb",
596                       session->id, (int)stream_id);
597     }
598     
599     return h2_session_status_from_apr_status(status);
600 }
601
602 static int on_frame_send_cb(nghttp2_session *ngh2, 
603                             const nghttp2_frame *frame,
604                             void *user_data)
605 {
606     h2_session *session = user_data;
607     if (APLOGcdebug(session->c)) {
608         char buffer[256];
609         
610         frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
611         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03068)
612                       "h2_session(%ld): sent FRAME[%s], frames=%ld/%ld (r/s)",
613                       session->id, buffer, (long)session->frames_received,
614                      (long)session->frames_sent);
615     }
616     ++session->frames_sent;
617     return 0;
618 }
619
620 #define NGH2_SET_CALLBACK(callbacks, name, fn)\
621 nghttp2_session_callbacks_set_##name##_callback(callbacks, fn)
622
623 static apr_status_t init_callbacks(conn_rec *c, nghttp2_session_callbacks **pcb)
624 {
625     int rv = nghttp2_session_callbacks_new(pcb);
626     if (rv != 0) {
627         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
628                       APLOGNO(02926) "nghttp2_session_callbacks_new: %s",
629                       nghttp2_strerror(rv));
630         return APR_EGENERAL;
631     }
632     
633     NGH2_SET_CALLBACK(*pcb, send, send_cb);
634     NGH2_SET_CALLBACK(*pcb, on_frame_recv, on_frame_recv_cb);
635     NGH2_SET_CALLBACK(*pcb, on_invalid_frame_recv, on_invalid_frame_recv_cb);
636     NGH2_SET_CALLBACK(*pcb, on_data_chunk_recv, on_data_chunk_recv_cb);
637     NGH2_SET_CALLBACK(*pcb, on_stream_close, on_stream_close_cb);
638     NGH2_SET_CALLBACK(*pcb, on_begin_headers, on_begin_headers_cb);
639     NGH2_SET_CALLBACK(*pcb, on_header, on_header_cb);
640     NGH2_SET_CALLBACK(*pcb, send_data, on_send_data_cb);
641     NGH2_SET_CALLBACK(*pcb, on_frame_send, on_frame_send_cb);
642
643     return APR_SUCCESS;
644 }
645
646 static void h2_session_cleanup(h2_session *session)
647 {
648     AP_DEBUG_ASSERT(session);
649     /* This is an early cleanup of the session that may
650      * discard what is no longer necessary for *new* streams
651      * and general HTTP/2 processing.
652      * At this point, all frames are in transit or somehwere in
653      * our buffers or passed down output filters.
654      * h2 streams might still being written out.
655      */
656     if (session->c) {
657         h2_ctx_clear(session->c);
658     }
659     if (session->ngh2) {
660         nghttp2_session_del(session->ngh2);
661         session->ngh2 = NULL;
662     }
663     if (session->spare) {
664         apr_pool_destroy(session->spare);
665         session->spare = NULL;
666     }
667 }
668
669 static void h2_session_destroy(h2_session *session)
670 {
671     AP_DEBUG_ASSERT(session);
672     h2_session_cleanup(session);
673
674     if (APLOGctrace1(session->c)) {
675         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
676                       "h2_session(%ld): destroy, %d streams open",
677                       session->id, (int)h2_stream_set_size(session->streams));
678     }
679     if (session->mplx) {
680         h2_mplx_set_consumed_cb(session->mplx, NULL, NULL);
681         h2_mplx_release_and_join(session->mplx, session->iowait);
682         session->mplx = NULL;
683     }
684     if (session->streams) {
685         h2_stream_set_destroy(session->streams);
686         session->streams = NULL;
687     }
688     if (session->pool) {
689         apr_pool_destroy(session->pool);
690     }
691 }
692
693 static apr_status_t h2_session_shutdown(h2_session *session, int reason, const char *msg)
694 {
695     apr_status_t status = APR_SUCCESS;
696     const char *err = msg;
697     
698     AP_DEBUG_ASSERT(session);
699     if (!err && reason) {
700         err = nghttp2_strerror(reason);
701     }
702     nghttp2_submit_goaway(session->ngh2, NGHTTP2_FLAG_NONE, 
703                           h2_mplx_get_max_stream_started(session->mplx), 
704                           reason, (uint8_t*)err, err? strlen(err):0);
705     status = nghttp2_session_send(session->ngh2);
706     h2_conn_io_flush(&session->io);
707     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03069)
708                   "session(%ld): sent GOAWAY, err=%d, msg=%s", 
709                   session->id, reason, err? err : "");
710     dispatch_event(session, H2_SESSION_EV_LOCAL_GOAWAY, reason, err);
711     return status;
712 }
713
714 static apr_status_t session_pool_cleanup(void *data)
715 {
716     h2_session *session = data;
717     /* On a controlled connection shutdown, this gets never
718      * called as we deregister and destroy our pool manually.
719      * However when we have an async mpm, and handed it our idle
720      * connection, it will just cleanup once the connection is closed
721      * from the other side (and sometimes even from out side) and
722      * here we arrive then.
723      */
724     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
725                   "session(%ld): pool_cleanup", session->id);
726     
727     if (session->state != H2_SESSION_ST_DONE 
728         && session->state != H2_SESSION_ST_LOCAL_SHUTDOWN) {
729         /* Not good. The connection is being torn down and we have
730          * not sent a goaway. This is considered a protocol error and
731          * the client has to assume that any streams "in flight" may have
732          * been processed and are not safe to retry.
733          * As clients with idle connection may only learn about a closed
734          * connection when sending the next request, this has the effect
735          * that at least this one request will fail.
736          */
737         ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, session->c, APLOGNO(03199)
738                       "session(%ld): connection disappeared without proper "
739                       "goodbye, clients will be confused, should not happen", 
740                       session->id);
741     }
742     /* keep us from destroying the pool, since that is already ongoing. */
743     session->pool = NULL;
744     h2_session_destroy(session);
745     return APR_SUCCESS;
746 }
747
748 static void *session_malloc(size_t size, void *ctx)
749 {
750     h2_session *session = ctx;
751     ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, session->c,
752                   "h2_session(%ld): malloc(%ld)",
753                   session->id, (long)size);
754     return malloc(size);
755 }
756
757 static void session_free(void *p, void *ctx)
758 {
759     h2_session *session = ctx;
760
761     ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, session->c,
762                   "h2_session(%ld): free()",
763                   session->id);
764     free(p);
765 }
766
767 static void *session_calloc(size_t n, size_t size, void *ctx)
768 {
769     h2_session *session = ctx;
770
771     ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, session->c,
772                   "h2_session(%ld): calloc(%ld, %ld)",
773                   session->id, (long)n, (long)size);
774     return calloc(n, size);
775 }
776
777 static void *session_realloc(void *p, size_t size, void *ctx)
778 {
779     h2_session *session = ctx;
780     ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, session->c,
781                   "h2_session(%ld): realloc(%ld)",
782                   session->id, (long)size);
783     return realloc(p, size);
784 }
785
786 static h2_session *h2_session_create_int(conn_rec *c,
787                                          request_rec *r,
788                                          h2_ctx *ctx, 
789                                          h2_workers *workers)
790 {
791     nghttp2_session_callbacks *callbacks = NULL;
792     nghttp2_option *options = NULL;
793     uint32_t n;
794
795     apr_pool_t *pool = NULL;
796     apr_status_t status = apr_pool_create(&pool, c->pool);
797     h2_session *session;
798     if (status != APR_SUCCESS) {
799         return NULL;
800     }
801
802     session = apr_pcalloc(pool, sizeof(h2_session));
803     if (session) {
804         int rv;
805         nghttp2_mem *mem;
806         
807         session->id = c->id;
808         session->c = c;
809         session->r = r;
810         session->s = h2_ctx_server_get(ctx);
811         session->config = h2_config_sget(session->s);
812         
813         session->state = H2_SESSION_ST_INIT;
814         
815         session->pool = pool;
816         apr_pool_pre_cleanup_register(pool, session, session_pool_cleanup);
817         
818         session->max_stream_count = h2_config_geti(session->config, H2_CONF_MAX_STREAMS);
819         session->max_stream_mem = h2_config_geti(session->config, H2_CONF_STREAM_MAX_MEM);
820
821         status = apr_thread_cond_create(&session->iowait, session->pool);
822         if (status != APR_SUCCESS) {
823             return NULL;
824         }
825         
826         session->streams = h2_stream_set_create(session->pool, session->max_stream_count);
827         
828         session->workers = workers;
829         session->mplx = h2_mplx_create(c, session->pool, session->config, 
830                                        session->s->timeout, workers);
831         
832         h2_mplx_set_consumed_cb(session->mplx, update_window, session);
833         
834         /* Install the connection input filter that feeds the session */
835         session->cin = h2_filter_cin_create(session->pool, h2_session_receive, session);
836         ap_add_input_filter("H2_IN", session->cin, r, c);
837
838         h2_conn_io_init(&session->io, c, session->config, session->pool);
839         session->bbtmp = apr_brigade_create(session->pool, c->bucket_alloc);
840         
841         status = init_callbacks(c, &callbacks);
842         if (status != APR_SUCCESS) {
843             ap_log_cerror(APLOG_MARK, APLOG_ERR, status, c, APLOGNO(02927) 
844                           "nghttp2: error in init_callbacks");
845             h2_session_destroy(session);
846             return NULL;
847         }
848         
849         rv = nghttp2_option_new(&options);
850         if (rv != 0) {
851             ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
852                           APLOGNO(02928) "nghttp2_option_new: %s", 
853                           nghttp2_strerror(rv));
854             h2_session_destroy(session);
855             return NULL;
856         }
857         nghttp2_option_set_peer_max_concurrent_streams(options, 
858                                                        (uint32_t)session->max_stream_count);
859         /* We need to handle window updates ourself, otherwise we
860          * get flooded by nghttp2. */
861         nghttp2_option_set_no_auto_window_update(options, 1);
862         
863         if (APLOGctrace6(c)) {
864             mem = apr_pcalloc(session->pool, sizeof(nghttp2_mem));
865             mem->mem_user_data = session;
866             mem->malloc    = session_malloc;
867             mem->free      = session_free;
868             mem->calloc    = session_calloc;
869             mem->realloc   = session_realloc;
870             
871             rv = nghttp2_session_server_new3(&session->ngh2, callbacks,
872                                              session, options, mem);
873         }
874         else {
875             rv = nghttp2_session_server_new2(&session->ngh2, callbacks,
876                                              session, options);
877         }
878         nghttp2_session_callbacks_del(callbacks);
879         nghttp2_option_del(options);
880         
881         if (rv != 0) {
882             ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
883                           APLOGNO(02929) "nghttp2_session_server_new: %s",
884                           nghttp2_strerror(rv));
885             h2_session_destroy(session);
886             return NULL;
887         }
888          
889         n = h2_config_geti(session->config, H2_CONF_PUSH_DIARY_SIZE);
890         session->push_diary = h2_push_diary_create(session->pool, n);
891         
892         if (APLOGcdebug(c)) {
893             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03200)
894                           "session(%ld) created, max_streams=%d, stream_mem=%d, push_diary(type=%d,N=%d)",
895                           session->id, (int)session->max_stream_count, (int)session->max_stream_mem,
896                           session->push_diary->dtype, (int)session->push_diary->N);
897         }
898     }
899     return session;
900 }
901
902 h2_session *h2_session_create(conn_rec *c, h2_ctx *ctx, h2_workers *workers)
903 {
904     return h2_session_create_int(c, NULL, ctx, workers);
905 }
906
907 h2_session *h2_session_rcreate(request_rec *r, h2_ctx *ctx, h2_workers *workers)
908 {
909     return h2_session_create_int(r->connection, r, ctx, workers);
910 }
911
912 void h2_session_eoc_callback(h2_session *session)
913 {
914     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
915                   "session(%ld): cleanup and destroy", session->id);
916     apr_pool_cleanup_kill(session->pool, session, session_pool_cleanup);
917     h2_session_destroy(session);
918 }
919
920 static apr_status_t h2_session_start(h2_session *session, int *rv)
921 {
922     apr_status_t status = APR_SUCCESS;
923     nghttp2_settings_entry settings[3];
924     size_t slen;
925     int win_size;
926     
927     AP_DEBUG_ASSERT(session);
928     /* Start the conversation by submitting our SETTINGS frame */
929     *rv = 0;
930     if (session->r) {
931         const char *s, *cs;
932         apr_size_t dlen; 
933         h2_stream * stream;
934
935         /* 'h2c' mode: we should have a 'HTTP2-Settings' header with
936          * base64 encoded client settings. */
937         s = apr_table_get(session->r->headers_in, "HTTP2-Settings");
938         if (!s) {
939             ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EINVAL, session->r,
940                           APLOGNO(02931) 
941                           "HTTP2-Settings header missing in request");
942             return APR_EINVAL;
943         }
944         cs = NULL;
945         dlen = h2_util_base64url_decode(&cs, s, session->pool);
946         
947         if (APLOGrdebug(session->r)) {
948             char buffer[128];
949             h2_util_hex_dump(buffer, 128, (char*)cs, dlen);
950             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, session->r, APLOGNO(03070)
951                           "upgrading h2c session with HTTP2-Settings: %s -> %s (%d)",
952                           s, buffer, (int)dlen);
953         }
954         
955         *rv = nghttp2_session_upgrade(session->ngh2, (uint8_t*)cs, dlen, NULL);
956         if (*rv != 0) {
957             status = APR_EINVAL;
958             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
959                           APLOGNO(02932) "nghttp2_session_upgrade: %s", 
960                           nghttp2_strerror(*rv));
961             return status;
962         }
963         
964         /* Now we need to auto-open stream 1 for the request we got. */
965         stream = h2_session_open_stream(session, 1);
966         if (!stream) {
967             status = APR_EGENERAL;
968             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
969                           APLOGNO(02933) "open stream 1: %s", 
970                           nghttp2_strerror(*rv));
971             return status;
972         }
973         
974         status = h2_stream_set_request(stream, session->r);
975         if (status != APR_SUCCESS) {
976             return status;
977         }
978         status = stream_schedule(session, stream, 1);
979         if (status != APR_SUCCESS) {
980             return status;
981         }
982     }
983
984     slen = 0;
985     settings[slen].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
986     settings[slen].value = (uint32_t)session->max_stream_count;
987     ++slen;
988     win_size = h2_config_geti(session->config, H2_CONF_WIN_SIZE);
989     if (win_size != H2_INITIAL_WINDOW_SIZE) {
990         settings[slen].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
991         settings[slen].value = win_size;
992         ++slen;
993     }
994     
995     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, APLOGNO(03201)
996                   "h2_session(%ld): start, INITIAL_WINDOW_SIZE=%ld, "
997                   "MAX_CONCURRENT_STREAMS=%d", 
998                   session->id, (long)win_size, (int)session->max_stream_count);
999     *rv = nghttp2_submit_settings(session->ngh2, NGHTTP2_FLAG_NONE,
1000                                   settings, slen);
1001     if (*rv != 0) {
1002         status = APR_EGENERAL;
1003         ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
1004                       APLOGNO(02935) "nghttp2_submit_settings: %s", 
1005                       nghttp2_strerror(*rv));
1006     }
1007     else {
1008         /* use maximum possible value for connection window size. We are only
1009          * interested in per stream flow control. which have the initial window
1010          * size configured above.
1011          * Therefore, for our use, the connection window can only get in the
1012          * way. Example: if we allow 100 streams with a 32KB window each, we
1013          * buffer up to 3.2 MB of data. Unless we do separate connection window
1014          * interim updates, any smaller connection window will lead to blocking
1015          * in DATA flow.
1016          */
1017         *rv = nghttp2_submit_window_update(session->ngh2, NGHTTP2_FLAG_NONE,
1018                                            0, NGHTTP2_MAX_WINDOW_SIZE - win_size);
1019         if (*rv != 0) {
1020             status = APR_EGENERAL;
1021             ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
1022                           APLOGNO(02970) "nghttp2_submit_window_update: %s", 
1023                           nghttp2_strerror(*rv));        
1024         }
1025     }
1026     return status;
1027 }
1028
1029 typedef struct {
1030     h2_session *session;
1031     int resume_count;
1032 } resume_ctx;
1033
1034 static int resume_on_data(void *ctx, h2_stream *stream)
1035 {
1036     resume_ctx *rctx = (resume_ctx*)ctx;
1037     h2_session *session = rctx->session;
1038     AP_DEBUG_ASSERT(session);
1039     AP_DEBUG_ASSERT(stream);
1040     
1041     if (h2_stream_is_suspended(stream)) {
1042         if (h2_mplx_out_has_data_for(stream->session->mplx, stream->id)) {
1043             int rv;
1044             h2_stream_set_suspended(stream, 0);
1045             ++rctx->resume_count;
1046             
1047             rv = nghttp2_session_resume_data(session->ngh2, stream->id);
1048             ap_log_cerror(APLOG_MARK, nghttp2_is_fatal(rv)?
1049                           APLOG_ERR : APLOG_DEBUG, 0, session->c,
1050                           APLOGNO(02936) 
1051                           "h2_stream(%ld-%d): resuming %s",
1052                           session->id, stream->id, rv? nghttp2_strerror(rv) : "");
1053         }
1054     }
1055     return 1;
1056 }
1057
1058 static int h2_session_resume_streams_with_data(h2_session *session)
1059 {
1060     AP_DEBUG_ASSERT(session);
1061     if (!h2_stream_set_is_empty(session->streams)
1062         && session->mplx && !session->mplx->aborted) {
1063         resume_ctx ctx;
1064         
1065         ctx.session      = session;
1066         ctx.resume_count = 0;
1067
1068         /* Resume all streams where we have data in the out queue and
1069          * which had been suspended before. */
1070         h2_stream_set_iter(session->streams, resume_on_data, &ctx);
1071         return ctx.resume_count;
1072     }
1073     return 0;
1074 }
1075
1076 h2_stream *h2_session_get_stream(h2_session *session, int stream_id)
1077 {
1078     if (!session->last_stream || stream_id != session->last_stream->id) {
1079         session->last_stream = h2_stream_set_get(session->streams, stream_id);
1080     }
1081     return session->last_stream;
1082 }
1083
1084 static ssize_t stream_data_cb(nghttp2_session *ng2s,
1085                               int32_t stream_id,
1086                               uint8_t *buf,
1087                               size_t length,
1088                               uint32_t *data_flags,
1089                               nghttp2_data_source *source,
1090                               void *puser)
1091 {
1092     h2_session *session = (h2_session *)puser;
1093     apr_off_t nread = length;
1094     int eos = 0;
1095     apr_status_t status;
1096     h2_stream *stream;
1097     AP_DEBUG_ASSERT(session);
1098     
1099     /* The session wants to send more DATA for the stream. We need
1100      * to find out how much of the requested length we can send without
1101      * blocking.
1102      * Indicate EOS when we encounter it or DEFERRED if the stream
1103      * should be suspended. Beware of trailers.
1104      */
1105  
1106     (void)ng2s;
1107     (void)buf;
1108     (void)source;
1109     stream = h2_session_get_stream(session, stream_id);
1110     if (!stream) {
1111         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
1112                       APLOGNO(02937) 
1113                       "h2_stream(%ld-%d): data requested but stream not found",
1114                       session->id, (int)stream_id);
1115         return NGHTTP2_ERR_CALLBACK_FAILURE;
1116     }
1117     
1118     AP_DEBUG_ASSERT(!h2_stream_is_suspended(stream));
1119     
1120     status = h2_stream_prep_read(stream, &nread, &eos);
1121     if (nread) {
1122         *data_flags |=  NGHTTP2_DATA_FLAG_NO_COPY;
1123     }
1124     
1125     switch (status) {
1126         case APR_SUCCESS:
1127             break;
1128             
1129         case APR_ECONNRESET:
1130             return nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE,
1131                 stream->id, stream->rst_error);
1132             
1133         case APR_EAGAIN:
1134             /* If there is no data available, our session will automatically
1135              * suspend this stream and not ask for more data until we resume
1136              * it. Remember at our h2_stream that we need to do this.
1137              */
1138             nread = 0;
1139             h2_stream_set_suspended(stream, 1);
1140             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03071)
1141                           "h2_stream(%ld-%d): suspending",
1142                           session->id, (int)stream_id);
1143             return NGHTTP2_ERR_DEFERRED;
1144             
1145         case APR_EOF:
1146             nread = 0;
1147             eos = 1;
1148             break;
1149             
1150         default:
1151             nread = 0;
1152             ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
1153                           APLOGNO(02938) "h2_stream(%ld-%d): reading data",
1154                           session->id, (int)stream_id);
1155             return NGHTTP2_ERR_CALLBACK_FAILURE;
1156     }
1157     
1158     if (eos) {
1159         apr_table_t *trailers = h2_stream_get_trailers(stream);
1160         if (trailers && !apr_is_empty_table(trailers)) {
1161             h2_ngheader *nh;
1162             int rv;
1163             
1164             nh = h2_util_ngheader_make(stream->pool, trailers);
1165             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03072)
1166                           "h2_stream(%ld-%d): submit %d trailers",
1167                           session->id, (int)stream_id,(int) nh->nvlen);
1168             rv = nghttp2_submit_trailer(ng2s, stream->id, nh->nv, nh->nvlen);
1169             if (rv < 0) {
1170                 nread = rv;
1171             }
1172             *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
1173         }
1174         
1175         *data_flags |= NGHTTP2_DATA_FLAG_EOF;
1176     }
1177     
1178     return (ssize_t)nread;
1179 }
1180
1181 typedef struct {
1182     nghttp2_nv *nv;
1183     size_t nvlen;
1184     size_t offset;
1185 } nvctx_t;
1186
1187 /**
1188  * Start submitting the response to a stream request. This is possible
1189  * once we have all the response headers. The response body will be
1190  * read by the session using the callback we supply.
1191  */
1192 static apr_status_t submit_response(h2_session *session, h2_stream *stream)
1193 {
1194     apr_status_t status = APR_SUCCESS;
1195     h2_response *response = h2_stream_get_response(stream);
1196     int rv = 0;
1197     AP_DEBUG_ASSERT(session);
1198     AP_DEBUG_ASSERT(stream);
1199     AP_DEBUG_ASSERT(response || stream->rst_error);
1200     
1201     if (stream->submitted) {
1202         rv = NGHTTP2_PROTOCOL_ERROR;
1203     }
1204     else if (response && response->headers) {
1205         nghttp2_data_provider provider;
1206         h2_ngheader *ngh;
1207         const h2_priority *prio;
1208         
1209         memset(&provider, 0, sizeof(provider));
1210         provider.source.fd = stream->id;
1211         provider.read_callback = stream_data_cb;
1212         
1213         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03073)
1214                       "h2_stream(%ld-%d): submit response %d",
1215                       session->id, stream->id, response->http_status);
1216         
1217         /* If this stream is not a pushed one itself,
1218          * and HTTP/2 server push is enabled here,
1219          * and the response is in the range 200-299 *),
1220          * and the remote side has pushing enabled,
1221          * -> find and perform any pushes on this stream
1222          *    *before* we submit the stream response itself.
1223          *    This helps clients avoid opening new streams on Link
1224          *    headers that get pushed right afterwards.
1225          * 
1226          * *) the response code is relevant, as we do not want to 
1227          *    make pushes on 401 or 403 codes, neiterh on 301/302
1228          *    and friends. And if we see a 304, we do not push either
1229          *    as the client, having this resource in its cache, might
1230          *    also have the pushed ones as well.
1231          */
1232         if (!stream->initiated_on
1233             && H2_HTTP_2XX(response->http_status)
1234             && h2_session_push_enabled(session)) {
1235             
1236             h2_stream_submit_pushes(stream);
1237         }
1238         
1239         prio = h2_stream_get_priority(stream);
1240         if (prio) {
1241             h2_session_set_prio(session, stream, prio);
1242             /* no showstopper if that fails for some reason */
1243         }
1244         
1245         ngh = h2_util_ngheader_make_res(stream->pool, response->http_status, 
1246                                         response->headers);
1247         rv = nghttp2_submit_response(session->ngh2, response->stream_id,
1248                                      ngh->nv, ngh->nvlen, &provider);
1249     }
1250     else {
1251         int err = H2_STREAM_RST(stream, H2_ERR_PROTOCOL_ERROR);
1252         
1253         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03074)
1254                       "h2_stream(%ld-%d): RST_STREAM, err=%d",
1255                       session->id, stream->id, err);
1256
1257         rv = nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE,
1258                                        stream->id, err);
1259     }
1260     
1261     stream->submitted = 1;
1262     if (stream->initiated_on) {
1263         ++session->pushes_submitted;
1264     }
1265     else {
1266         ++session->responses_submitted;
1267     }
1268
1269     if (nghttp2_is_fatal(rv)) {
1270         status = APR_EGENERAL;
1271         dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, rv, nghttp2_strerror(rv));
1272         ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
1273                       APLOGNO(02940) "submit_response: %s", 
1274                       nghttp2_strerror(rv));
1275     }
1276     
1277     return status;
1278 }
1279
1280 struct h2_stream *h2_session_push(h2_session *session, h2_stream *is,
1281                                   h2_push *push)
1282 {
1283     apr_status_t status;
1284     h2_stream *stream;
1285     h2_ngheader *ngh;
1286     int nid;
1287     
1288     ngh = h2_util_ngheader_make_req(is->pool, push->req);
1289     nid = nghttp2_submit_push_promise(session->ngh2, 0, is->id, 
1290                                       ngh->nv, ngh->nvlen, NULL);
1291     if (nid <= 0) {
1292         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03075)
1293                       "h2_stream(%ld-%d): submitting push promise fail: %s",
1294                       session->id, is->id, nghttp2_strerror(nid));
1295         return NULL;
1296     }
1297     ++session->pushes_promised;
1298     
1299     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03076)
1300                   "h2_stream(%ld-%d): SERVER_PUSH %d for %s %s on %d",
1301                   session->id, is->id, nid,
1302                   push->req->method, push->req->path, is->id);
1303                   
1304     stream = h2_session_open_stream(session, nid);
1305     if (stream) {
1306         h2_stream_set_h2_request(stream, is->id, push->req);
1307         status = stream_schedule(session, stream, 1);
1308         if (status != APR_SUCCESS) {
1309             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, session->c,
1310                           "h2_stream(%ld-%d): scheduling push stream",
1311                           session->id, stream->id);
1312             h2_stream_cleanup(stream);
1313             stream = NULL;
1314         }
1315         ++session->unsent_promises;
1316     }
1317     else {
1318         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03077)
1319                       "h2_stream(%ld-%d): failed to create stream obj %d",
1320                       session->id, is->id, nid);
1321     }
1322
1323     if (!stream) {
1324         /* try to tell the client that it should not wait. */
1325         nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE, nid,
1326                                   NGHTTP2_INTERNAL_ERROR);
1327     }
1328     
1329     return stream;
1330 }
1331
1332 static int valid_weight(float f) 
1333 {
1334     int w = (int)f;
1335     return (w < NGHTTP2_MIN_WEIGHT? NGHTTP2_MIN_WEIGHT : 
1336             (w > NGHTTP2_MAX_WEIGHT)? NGHTTP2_MAX_WEIGHT : w);
1337 }
1338
1339 apr_status_t h2_session_set_prio(h2_session *session, h2_stream *stream, 
1340                                  const h2_priority *prio)
1341 {
1342     apr_status_t status = APR_SUCCESS;
1343 #ifdef H2_NG2_CHANGE_PRIO
1344     nghttp2_stream *s_grandpa, *s_parent, *s;
1345     
1346     s = nghttp2_session_find_stream(session->ngh2, stream->id);
1347     if (!s) {
1348         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
1349                       "h2_stream(%ld-%d): lookup of nghttp2_stream failed",
1350                       session->id, stream->id);
1351         return APR_EINVAL;
1352     }
1353     
1354     s_parent = nghttp2_stream_get_parent(s);
1355     if (s_parent) {
1356         nghttp2_priority_spec ps;
1357         int id_parent, id_grandpa, w_parent, w, rv = 0;
1358         char *ptype = "AFTER";
1359         h2_dependency dep = prio->dependency;
1360         
1361         id_parent = nghttp2_stream_get_stream_id(s_parent);
1362         s_grandpa = nghttp2_stream_get_parent(s_parent);
1363         if (s_grandpa) {
1364             id_grandpa = nghttp2_stream_get_stream_id(s_grandpa);
1365         }
1366         else {
1367             /* parent of parent does not exist, 
1368              * only possible if parent == root */
1369             dep = H2_DEPENDANT_AFTER;
1370         }
1371         
1372         switch (dep) {
1373             case H2_DEPENDANT_INTERLEAVED:
1374                 /* PUSHed stream is to be interleaved with initiating stream.
1375                  * It is made a sibling of the initiating stream and gets a
1376                  * proportional weight [1, MAX_WEIGHT] of the initiaing
1377                  * stream weight.
1378                  */
1379                 ptype = "INTERLEAVED";
1380                 w_parent = nghttp2_stream_get_weight(s_parent);
1381                 w = valid_weight(w_parent * ((float)prio->weight / NGHTTP2_MAX_WEIGHT));
1382                 nghttp2_priority_spec_init(&ps, id_grandpa, w, 0);
1383                 break;
1384                 
1385             case H2_DEPENDANT_BEFORE:
1386                 /* PUSHed stream os to be sent BEFORE the initiating stream.
1387                  * It gets the same weight as the initiating stream, replaces
1388                  * that stream in the dependency tree and has the initiating
1389                  * stream as child.
1390                  */
1391                 ptype = "BEFORE";
1392                 w = w_parent = nghttp2_stream_get_weight(s_parent);
1393                 nghttp2_priority_spec_init(&ps, stream->id, w_parent, 0);
1394                 id_grandpa = nghttp2_stream_get_stream_id(s_grandpa);
1395                 rv = nghttp2_session_change_stream_priority(session->ngh2, id_parent, &ps);
1396                 if (rv < 0) {
1397                     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03202)
1398                                   "h2_stream(%ld-%d): PUSH BEFORE, weight=%d, "
1399                                   "depends=%d, returned=%d",
1400                                   session->id, id_parent, ps.weight, ps.stream_id, rv);
1401                     return APR_EGENERAL;
1402                 }
1403                 nghttp2_priority_spec_init(&ps, id_grandpa, w, 0);
1404                 break;
1405                 
1406             case H2_DEPENDANT_AFTER:
1407                 /* The PUSHed stream is to be sent after the initiating stream.
1408                  * Give if the specified weight and let it depend on the intiating
1409                  * stream.
1410                  */
1411                 /* fall through, it's the default */
1412             default:
1413                 nghttp2_priority_spec_init(&ps, id_parent, valid_weight(prio->weight), 0);
1414                 break;
1415         }
1416
1417
1418         rv = nghttp2_session_change_stream_priority(session->ngh2, stream->id, &ps);
1419         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03203)
1420                       "h2_stream(%ld-%d): PUSH %s, weight=%d, "
1421                       "depends=%d, returned=%d",
1422                       session->id, stream->id, ptype, 
1423                       ps.weight, ps.stream_id, rv);
1424         status = (rv < 0)? APR_EGENERAL : APR_SUCCESS;
1425     }
1426 #else
1427     (void)session;
1428     (void)stream;
1429     (void)prio;
1430     (void)valid_weight;
1431 #endif
1432     return status;
1433 }
1434
1435 apr_status_t h2_session_stream_destroy(h2_session *session, h2_stream *stream)
1436 {
1437     apr_pool_t *pool = h2_stream_detach_pool(stream);
1438
1439     /* this may be called while the session has already freed
1440      * some internal structures. */
1441     if (session->mplx) {
1442         h2_mplx_stream_done(session->mplx, stream->id, stream->rst_error);
1443         if (session->last_stream == stream) {
1444             session->last_stream = NULL;
1445         }
1446     }
1447     
1448     if (session->streams) {
1449         h2_stream_set_remove(session->streams, stream->id);
1450     }
1451     h2_stream_destroy(stream);
1452     
1453     if (pool) {
1454         apr_pool_clear(pool);
1455         if (session->spare) {
1456             apr_pool_destroy(session->spare);
1457         }
1458         session->spare = pool;
1459     }
1460     return APR_SUCCESS;
1461 }
1462
1463 static int frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen)
1464 {
1465     char scratch[128];
1466     size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
1467     
1468     switch (frame->hd.type) {
1469         case NGHTTP2_DATA: {
1470             return apr_snprintf(buffer, maxlen,
1471                                 "DATA[length=%d, flags=%d, stream=%d, padlen=%d]",
1472                                 (int)frame->hd.length, frame->hd.flags,
1473                                 frame->hd.stream_id, (int)frame->data.padlen);
1474         }
1475         case NGHTTP2_HEADERS: {
1476             return apr_snprintf(buffer, maxlen,
1477                                 "HEADERS[length=%d, hend=%d, stream=%d, eos=%d]",
1478                                 (int)frame->hd.length,
1479                                 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
1480                                 frame->hd.stream_id,
1481                                 !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
1482         }
1483         case NGHTTP2_PRIORITY: {
1484             return apr_snprintf(buffer, maxlen,
1485                                 "PRIORITY[length=%d, flags=%d, stream=%d]",
1486                                 (int)frame->hd.length,
1487                                 frame->hd.flags, frame->hd.stream_id);
1488         }
1489         case NGHTTP2_RST_STREAM: {
1490             return apr_snprintf(buffer, maxlen,
1491                                 "RST_STREAM[length=%d, flags=%d, stream=%d]",
1492                                 (int)frame->hd.length,
1493                                 frame->hd.flags, frame->hd.stream_id);
1494         }
1495         case NGHTTP2_SETTINGS: {
1496             if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
1497                 return apr_snprintf(buffer, maxlen,
1498                                     "SETTINGS[ack=1, stream=%d]",
1499                                     frame->hd.stream_id);
1500             }
1501             return apr_snprintf(buffer, maxlen,
1502                                 "SETTINGS[length=%d, stream=%d]",
1503                                 (int)frame->hd.length, frame->hd.stream_id);
1504         }
1505         case NGHTTP2_PUSH_PROMISE: {
1506             return apr_snprintf(buffer, maxlen,
1507                                 "PUSH_PROMISE[length=%d, hend=%d, stream=%d]",
1508                                 (int)frame->hd.length,
1509                                 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
1510                                 frame->hd.stream_id);
1511         }
1512         case NGHTTP2_PING: {
1513             return apr_snprintf(buffer, maxlen,
1514                                 "PING[length=%d, ack=%d, stream=%d]",
1515                                 (int)frame->hd.length,
1516                                 frame->hd.flags&NGHTTP2_FLAG_ACK,
1517                                 frame->hd.stream_id);
1518         }
1519         case NGHTTP2_GOAWAY: {
1520             size_t len = (frame->goaway.opaque_data_len < s_len)?
1521             frame->goaway.opaque_data_len : s_len-1;
1522             memcpy(scratch, frame->goaway.opaque_data, len);
1523             scratch[len+1] = '\0';
1524             return apr_snprintf(buffer, maxlen, "GOAWAY[error=%d, reason='%s']",
1525                                 frame->goaway.error_code, scratch);
1526         }
1527         case NGHTTP2_WINDOW_UPDATE: {
1528             return apr_snprintf(buffer, maxlen,
1529                                 "WINDOW_UPDATE[stream=%d, incr=%d]",
1530                                 frame->hd.stream_id, 
1531                                 frame->window_update.window_size_increment);
1532         }
1533         default:
1534             return apr_snprintf(buffer, maxlen,
1535                                 "type=%d[length=%d, flags=%d, stream=%d]",
1536                                 frame->hd.type, (int)frame->hd.length,
1537                                 frame->hd.flags, frame->hd.stream_id);
1538     }
1539 }
1540
1541 int h2_session_push_enabled(h2_session *session)
1542 {
1543     /* iff we can and they can */
1544     return (h2_config_geti(session->config, H2_CONF_PUSH)
1545             && nghttp2_session_get_remote_settings(session->ngh2, 
1546                                                    NGHTTP2_SETTINGS_ENABLE_PUSH));
1547 }
1548
1549 static apr_status_t h2_session_send(h2_session *session)
1550 {
1551     int rv = nghttp2_session_send(session->ngh2);
1552     if (rv != 0) {
1553         if (nghttp2_is_fatal(rv)) {
1554             dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, rv, nghttp2_strerror(rv));
1555             return APR_EGENERAL;
1556         }
1557     }
1558     
1559     session->unsent_promises = 0;
1560     session->unsent_submits = 0;
1561     
1562     return APR_SUCCESS;
1563 }
1564
1565 static apr_status_t h2_session_receive(void *ctx, const char *data, 
1566                                        apr_size_t len, apr_size_t *readlen)
1567 {
1568     h2_session *session = ctx;
1569     ssize_t n;
1570     
1571     if (len > 0) {
1572         ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
1573                       "h2_session(%ld): feeding %ld bytes to nghttp2",
1574                       session->id, (long)len);
1575         n = nghttp2_session_mem_recv(session->ngh2, (const uint8_t *)data, len);
1576         if (n < 0) {
1577             if (nghttp2_is_fatal((int)n)) {
1578                 dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, (int)n, nghttp2_strerror(n));
1579                 return APR_EGENERAL;
1580             }
1581         }
1582         else {
1583             *readlen = n;
1584             session->io.bytes_read += n;
1585         }
1586     }
1587     return APR_SUCCESS;
1588 }
1589
1590 static apr_status_t h2_session_read(h2_session *session, int block, int loops)
1591 {
1592     apr_status_t status, rstatus = APR_EAGAIN;
1593     conn_rec *c = session->c;
1594     int i;
1595     
1596     for (i = 0; i < loops; ++i) {
1597         /* H2_IN filter handles all incoming data against the session.
1598          * We just pull at the filter chain to make it happen */
1599         status = ap_get_brigade(c->input_filters,
1600                                 session->bbtmp, AP_MODE_READBYTES,
1601                                 block? APR_BLOCK_READ : APR_NONBLOCK_READ,
1602                                 APR_BUCKET_BUFF_SIZE);
1603         /* get rid of any possible data we do not expect to get */
1604         apr_brigade_cleanup(session->bbtmp); 
1605
1606         switch (status) {
1607             case APR_SUCCESS:
1608                 /* successful read, reset our idle timers */
1609                 rstatus = APR_SUCCESS;
1610                 if (block) {
1611                     /* successfull blocked read, try unblocked to
1612                      * get more. */
1613                     block = 0;
1614                 }
1615                 break;
1616             case APR_EAGAIN:
1617                 return rstatus;
1618             case APR_TIMEUP:
1619                 return status;
1620             default:
1621                 if (!i) {
1622                     /* first attempt failed */
1623                     if (APR_STATUS_IS_ETIMEDOUT(status)
1624                         || APR_STATUS_IS_ECONNABORTED(status)
1625                         || APR_STATUS_IS_ECONNRESET(status)
1626                         || APR_STATUS_IS_EOF(status)
1627                         || APR_STATUS_IS_EBADF(status)) {
1628                         /* common status for a client that has left */
1629                         ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
1630                                       "h2_session(%ld): input gone", session->id);
1631                     }
1632                     else {
1633                         /* uncommon status, log on INFO so that we see this */
1634                         ap_log_cerror( APLOG_MARK, APLOG_INFO, status, c,
1635                                       APLOGNO(02950) 
1636                                       "h2_session(%ld): error reading, terminating",
1637                                       session->id);
1638                     }
1639                     return status;
1640                 }
1641                 /* subsequent failure after success(es), return initial
1642                  * status. */
1643                 return rstatus;
1644         }
1645         if (!is_accepting_streams(session)) {
1646             break;
1647         }
1648     }
1649     return rstatus;
1650 }
1651
1652 static apr_status_t h2_session_submit(h2_session *session)
1653 {
1654     apr_status_t status = APR_EAGAIN;
1655     h2_stream *stream;
1656     
1657     if (h2_stream_set_has_unsubmitted(session->streams)) {
1658         /* If we have responses ready, submit them now. */
1659         while ((stream = h2_mplx_next_submit(session->mplx, session->streams))) {
1660             status = submit_response(session, stream);
1661             ++session->unsent_submits;
1662             
1663             /* Unsent push promises are written immediately, as nghttp2
1664              * 1.5.0 realizes internal stream data structures only on 
1665              * send and we might need them for other submits. 
1666              * Also, to conserve memory, we send at least every 10 submits
1667              * so that nghttp2 does not buffer all outbound items too 
1668              * long.
1669              */
1670             if (status == APR_SUCCESS 
1671                 && (session->unsent_promises || session->unsent_submits > 10)) {
1672                 status = h2_session_send(session);
1673                 if (status != APR_SUCCESS) {
1674                     break;
1675                 }
1676             }
1677         }
1678     }
1679     return status;
1680 }
1681
1682 static const char *StateNames[] = {
1683     "INIT",      /* H2_SESSION_ST_INIT */
1684     "DONE",      /* H2_SESSION_ST_DONE */
1685     "IDLE",      /* H2_SESSION_ST_IDLE */
1686     "BUSY",      /* H2_SESSION_ST_BUSY */
1687     "WAIT",      /* H2_SESSION_ST_WAIT */
1688     "LSHUTDOWN", /* H2_SESSION_ST_LOCAL_SHUTDOWN */
1689     "RSHUTDOWN", /* H2_SESSION_ST_REMOTE_SHUTDOWN */
1690 };
1691
1692 static const char *state_name(h2_session_state state)
1693 {
1694     if (state >= (sizeof(StateNames)/sizeof(StateNames[0]))) {
1695         return "unknown";
1696     }
1697     return StateNames[state];
1698 }
1699
1700 static int is_accepting_streams(h2_session *session)
1701 {
1702     switch (session->state) {
1703         case H2_SESSION_ST_IDLE:
1704         case H2_SESSION_ST_BUSY:
1705         case H2_SESSION_ST_WAIT:
1706             return 1;
1707         default:
1708             return 0;
1709     }
1710 }
1711
1712 static void transit(h2_session *session, const char *action, h2_session_state nstate)
1713 {
1714     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03078)
1715                   "h2_session(%ld): transit [%s] -- %s --> [%s]", session->id,
1716                   state_name(session->state), action, state_name(nstate));
1717     session->state = nstate;
1718 }
1719
1720 static void h2_session_ev_init(h2_session *session, int arg, const char *msg)
1721 {
1722     switch (session->state) {
1723         case H2_SESSION_ST_INIT:
1724             transit(session, "init", H2_SESSION_ST_BUSY);
1725             break;
1726
1727         default:
1728             /* nop */
1729             break;
1730     }
1731 }
1732
1733 static void h2_session_ev_local_goaway(h2_session *session, int arg, const char *msg)
1734 {
1735     switch (session->state) {
1736         case H2_SESSION_ST_LOCAL_SHUTDOWN:
1737             /* already did that? */
1738             break;
1739         case H2_SESSION_ST_IDLE:
1740         case H2_SESSION_ST_REMOTE_SHUTDOWN:
1741             /* all done */
1742             transit(session, "local goaway", H2_SESSION_ST_DONE);
1743             break;
1744         default:
1745             transit(session, "local goaway", H2_SESSION_ST_LOCAL_SHUTDOWN);
1746             break;
1747     }
1748 }
1749
1750 static void h2_session_ev_remote_goaway(h2_session *session, int arg, const char *msg)
1751 {
1752     switch (session->state) {
1753         case H2_SESSION_ST_REMOTE_SHUTDOWN:
1754             /* already received that? */
1755             break;
1756         case H2_SESSION_ST_IDLE:
1757         case H2_SESSION_ST_LOCAL_SHUTDOWN:
1758             /* all done */
1759             transit(session, "remote goaway", H2_SESSION_ST_DONE);
1760             break;
1761         default:
1762             transit(session, "remote goaway", H2_SESSION_ST_REMOTE_SHUTDOWN);
1763             break;
1764     }
1765 }
1766
1767 static void h2_session_ev_conn_error(h2_session *session, int arg, const char *msg)
1768 {
1769     switch (session->state) {
1770         case H2_SESSION_ST_INIT:
1771         case H2_SESSION_ST_DONE:
1772         case H2_SESSION_ST_LOCAL_SHUTDOWN:
1773             /* just leave */
1774             transit(session, "conn error", H2_SESSION_ST_DONE);
1775             break;
1776         
1777         default:
1778             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
1779                           "h2_session(%ld): conn error -> shutdown", session->id);
1780             h2_session_shutdown(session, arg, msg);
1781             break;
1782     }
1783 }
1784
1785 static void h2_session_ev_proto_error(h2_session *session, int arg, const char *msg)
1786 {
1787     switch (session->state) {
1788         case H2_SESSION_ST_DONE:
1789         case H2_SESSION_ST_LOCAL_SHUTDOWN:
1790             /* just leave */
1791             transit(session, "proto error", H2_SESSION_ST_DONE);
1792             break;
1793         
1794         default:
1795             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
1796                           "h2_session(%ld): proto error -> shutdown", session->id);
1797             h2_session_shutdown(session, arg, msg);
1798             break;
1799     }
1800 }
1801
1802 static void h2_session_ev_conn_timeout(h2_session *session, int arg, const char *msg)
1803 {
1804     switch (session->state) {
1805         case H2_SESSION_ST_LOCAL_SHUTDOWN:
1806             transit(session, "conn timeout", H2_SESSION_ST_DONE);
1807             break;
1808         default:
1809             h2_session_shutdown(session, arg, msg);
1810             transit(session, "conn timeout", H2_SESSION_ST_DONE);
1811             break;
1812     }
1813 }
1814
1815 static void h2_session_ev_no_io(h2_session *session, int arg, const char *msg)
1816 {
1817     switch (session->state) {
1818         case H2_SESSION_ST_BUSY:
1819         case H2_SESSION_ST_LOCAL_SHUTDOWN:
1820         case H2_SESSION_ST_REMOTE_SHUTDOWN:
1821             /* nothing for input and output to do. If we remain
1822              * in this state, we go into a tight loop and suck up
1823              * CPU cycles. Ideally, we'd like to do a blocking read, but that
1824              * is not possible if we have scheduled tasks and wait
1825              * for them to produce something. */
1826             if (h2_stream_set_is_empty(session->streams)) {
1827                 if (!is_accepting_streams(session)) {
1828                     /* We are no longer accepting new streams and have
1829                      * finished processing existing ones. Time to leave. */
1830                     h2_session_shutdown(session, arg, msg);
1831                     transit(session, "no io", H2_SESSION_ST_DONE);
1832                 }
1833                 else {
1834                     /* When we have no streams, no task event are possible,
1835                      * switch to blocking reads */
1836                     transit(session, "no io", H2_SESSION_ST_IDLE);
1837                     session->idle_until = apr_time_now() + session->s->keep_alive_timeout;
1838                 }
1839             }
1840             else if (!h2_stream_set_has_unsubmitted(session->streams)
1841                      && !h2_stream_set_has_suspended(session->streams)) {
1842                 /* none of our streams is waiting for a response or
1843                  * new output data from task processing, 
1844                  * switch to blocking reads. We are probably waiting on
1845                  * window updates. */
1846                 transit(session, "no io", H2_SESSION_ST_IDLE);
1847                 session->idle_until = apr_time_now() + session->s->timeout;
1848             }
1849             else {
1850                 /* Unable to do blocking reads, as we wait on events from
1851                  * task processing in other threads. Do a busy wait with
1852                  * backoff timer. */
1853                 transit(session, "no io", H2_SESSION_ST_WAIT);
1854             }
1855             break;
1856         default:
1857             /* nop */
1858             break;
1859     }
1860 }
1861
1862 static void h2_session_ev_stream_ready(h2_session *session, int arg, const char *msg)
1863 {
1864     switch (session->state) {
1865         case H2_SESSION_ST_WAIT:
1866             transit(session, "stream ready", H2_SESSION_ST_BUSY);
1867             break;
1868         default:
1869             /* nop */
1870             break;
1871     }
1872 }
1873
1874 static void h2_session_ev_data_read(h2_session *session, int arg, const char *msg)
1875 {
1876     switch (session->state) {
1877         case H2_SESSION_ST_IDLE:
1878         case H2_SESSION_ST_WAIT:
1879             transit(session, "data read", H2_SESSION_ST_BUSY);
1880             break;
1881             /* fall through */
1882         default:
1883             /* nop */
1884             break;
1885     }
1886 }
1887
1888 static void h2_session_ev_ngh2_done(h2_session *session, int arg, const char *msg)
1889 {
1890     switch (session->state) {
1891         case H2_SESSION_ST_DONE:
1892             /* nop */
1893             break;
1894         default:
1895             transit(session, "nghttp2 done", H2_SESSION_ST_DONE);
1896             break;
1897     }
1898 }
1899
1900 static void h2_session_ev_mpm_stopping(h2_session *session, int arg, const char *msg)
1901 {
1902     switch (session->state) {
1903         case H2_SESSION_ST_DONE:
1904         case H2_SESSION_ST_LOCAL_SHUTDOWN:
1905             /* nop */
1906             break;
1907         default:
1908             h2_session_shutdown(session, arg, msg);
1909             break;
1910     }
1911 }
1912
1913 static void h2_session_ev_pre_close(h2_session *session, int arg, const char *msg)
1914 {
1915     switch (session->state) {
1916         case H2_SESSION_ST_DONE:
1917         case H2_SESSION_ST_LOCAL_SHUTDOWN:
1918             /* nop */
1919             break;
1920         default:
1921             h2_session_shutdown(session, arg, msg);
1922             h2_conn_io_flush(&session->io);
1923             break;
1924     }
1925 }
1926
1927 static void dispatch_event(h2_session *session, h2_session_event_t ev, 
1928                       int arg, const char *msg)
1929 {
1930     switch (ev) {
1931         case H2_SESSION_EV_INIT:
1932             h2_session_ev_init(session, arg, msg);
1933             break;            
1934         case H2_SESSION_EV_LOCAL_GOAWAY:
1935             h2_session_ev_local_goaway(session, arg, msg);
1936             break;
1937         case H2_SESSION_EV_REMOTE_GOAWAY:
1938             h2_session_ev_remote_goaway(session, arg, msg);
1939             break;
1940         case H2_SESSION_EV_CONN_ERROR:
1941             h2_session_ev_conn_error(session, arg, msg);
1942             break;
1943         case H2_SESSION_EV_PROTO_ERROR:
1944             h2_session_ev_proto_error(session, arg, msg);
1945             break;
1946         case H2_SESSION_EV_CONN_TIMEOUT:
1947             h2_session_ev_conn_timeout(session, arg, msg);
1948             break;
1949         case H2_SESSION_EV_NO_IO:
1950             h2_session_ev_no_io(session, arg, msg);
1951             break;
1952         case H2_SESSION_EV_STREAM_READY:
1953             h2_session_ev_stream_ready(session, arg, msg);
1954             break;
1955         case H2_SESSION_EV_DATA_READ:
1956             h2_session_ev_data_read(session, arg, msg);
1957             break;
1958         case H2_SESSION_EV_NGH2_DONE:
1959             h2_session_ev_ngh2_done(session, arg, msg);
1960             break;
1961         case H2_SESSION_EV_MPM_STOPPING:
1962             h2_session_ev_mpm_stopping(session, arg, msg);
1963             break;
1964         case H2_SESSION_EV_PRE_CLOSE:
1965             h2_session_ev_pre_close(session, arg, msg);
1966             break;
1967         default:
1968             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
1969                           "h2_session(%ld): unknown event %d", 
1970                           session->id, ev);
1971             break;
1972     }
1973     
1974     if (session->state == H2_SESSION_ST_DONE) {
1975         h2_mplx_abort(session->mplx);
1976     }
1977 }
1978
1979 static const int MAX_WAIT_MICROS = 200 * 1000;
1980
1981 static void update_child_status(h2_session *session, int status, const char *msg)
1982 {
1983     apr_snprintf(session->status, sizeof(session->status),
1984                  "%s, streams: %d/%d/%d/%d/%d (open/recv/resp/push/rst)", 
1985                  msg? msg : "-",
1986                  (int)h2_stream_set_size(session->streams), 
1987                  (int)session->requests_received,
1988                  (int)session->responses_submitted,
1989                  (int)session->pushes_submitted,
1990                  (int)session->pushes_reset + session->streams_reset);
1991     ap_update_child_status_descr(session->c->sbh, status, session->status);
1992 }
1993
1994 apr_status_t h2_session_process(h2_session *session, int async)
1995 {
1996     apr_status_t status = APR_SUCCESS;
1997     conn_rec *c = session->c;
1998     int rv, have_written, have_read, mpm_state;
1999
2000     ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
2001                   "h2_session(%ld): process start, async=%d", session->id, async);
2002                   
2003     if (c->cs) {
2004         c->cs->state = CONN_STATE_WRITE_COMPLETION;
2005     }
2006     
2007     while (1) {
2008         have_read = have_written = 0;
2009
2010         if (!ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
2011             if (mpm_state == AP_MPMQ_STOPPING) {
2012                 dispatch_event(session, H2_SESSION_EV_MPM_STOPPING, 0, NULL);
2013                 break;
2014             }
2015         }
2016         
2017         session->status[0] = '\0';
2018         
2019         switch (session->state) {
2020             case H2_SESSION_ST_INIT:
2021                 ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_READ, c);
2022                 if (!h2_is_acceptable_connection(c, 1)) {
2023                     update_child_status(session, SERVER_BUSY_READ, "inadequate security");
2024                     h2_session_shutdown(session, NGHTTP2_INADEQUATE_SECURITY, NULL);
2025                 } 
2026                 else {
2027                     update_child_status(session, SERVER_BUSY_READ, "init");
2028                     status = h2_session_start(session, &rv);
2029                     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, APLOGNO(03079)
2030                                   "h2_session(%ld): started on %s:%d", session->id,
2031                                   session->s->server_hostname,
2032                                   c->local_addr->port);
2033                     if (status != APR_SUCCESS) {
2034                         dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
2035                     }
2036                     dispatch_event(session, H2_SESSION_EV_INIT, 0, NULL);
2037                 }
2038                 break;
2039                 
2040             case H2_SESSION_ST_IDLE:
2041                 update_child_status(session, (h2_stream_set_is_empty(session->streams)?
2042                                               SERVER_BUSY_KEEPALIVE : SERVER_BUSY_READ), 
2043                                               "idle");
2044                 if (async && !session->r) {
2045                     ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
2046                                   "h2_session(%ld): async idle, nonblock read", session->id);
2047                     if (c->cs) {
2048                         c->cs->sense = CONN_SENSE_WANT_READ;
2049                     }
2050                     status = h2_session_read(session, 0, 1);
2051                     if (status == APR_SUCCESS) {
2052                         have_read = 1;
2053                         dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
2054                     }
2055                     else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) {
2056                         if (apr_time_now() > session->idle_until) {
2057                             dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
2058                         }
2059                         else {
2060                             status = APR_EAGAIN;
2061                             goto out;
2062                         }
2063                     }
2064                     else {
2065                         ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, c,
2066                                       "h2_session(%ld): idle, no data, error", 
2067                                       session->id);
2068                         dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
2069                     }
2070                 }
2071                 else {
2072                     /* We wait in smaller increments, using a 1 second timeout.
2073                      * That gives us the chance to check for MPMQ_STOPPING often. */
2074                     h2_filter_cin_timeout_set(session->cin, 
2075                                               (h2_stream_set_is_empty(session->streams)?
2076                                               session->s->keep_alive_timeout :
2077                                               session->s->timeout));
2078                     status = h2_session_read(session, 1, 10);
2079                     if (status == APR_SUCCESS) {
2080                         have_read = 1;
2081                         dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
2082                     }
2083                     else if (status == APR_EAGAIN) {
2084                         /* nothing to read */
2085                     }
2086                     else if (APR_STATUS_IS_TIMEUP(status)) {
2087                         if (apr_time_now() > session->idle_until) {
2088                             dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, "timeout");
2089                         }
2090                         /* continue reading handling */
2091                     }
2092                     else {
2093                         dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
2094                     }
2095                 }
2096                 
2097                 break;
2098                 
2099             case H2_SESSION_ST_BUSY:
2100             case H2_SESSION_ST_LOCAL_SHUTDOWN:
2101             case H2_SESSION_ST_REMOTE_SHUTDOWN:
2102                 if (nghttp2_session_want_read(session->ngh2)) {
2103                     h2_filter_cin_timeout_set(session->cin, session->s->timeout);
2104                     status = h2_session_read(session, 0, 10);
2105                     if (status == APR_SUCCESS) {
2106                         have_read = 1;
2107                         dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
2108                     }
2109                     else if (status == APR_EAGAIN) {
2110                         /* nothing to read */
2111                     }
2112                     else if (APR_STATUS_IS_TIMEUP(status)) {
2113                         dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
2114                         break;
2115                     }
2116                     else {
2117                         dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
2118                     }
2119                 }
2120                 
2121                 if (!h2_stream_set_is_empty(session->streams)) {
2122                     /* resume any streams for which data is available again */
2123                     h2_session_resume_streams_with_data(session);
2124                     /* Submit any responses/push_promises that are ready */
2125                     status = h2_session_submit(session);
2126                     if (status == APR_SUCCESS) {
2127                         have_written = 1;
2128                     }
2129                     else if (status != APR_EAGAIN) {
2130                         dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 
2131                                          H2_ERR_INTERNAL_ERROR, "submit error");
2132                         break;
2133                     }
2134                     /* send out window updates for our inputs */
2135                     status = h2_mplx_in_update_windows(session->mplx);
2136                     if (status != APR_SUCCESS && status != APR_EAGAIN) {
2137                         dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 
2138                                          H2_ERR_INTERNAL_ERROR, "window update error");
2139                         break;
2140                     }
2141                 }
2142                 
2143                 if (nghttp2_session_want_write(session->ngh2)) {
2144                     ap_update_child_status(session->c->sbh, SERVER_BUSY_WRITE, NULL);
2145                     status = h2_session_send(session);
2146                     if (status == APR_SUCCESS) {
2147                         have_written = 1;
2148                     }
2149                     else {
2150                         dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 
2151                                          H2_ERR_INTERNAL_ERROR, "writing");
2152                         break;
2153                     }
2154                 }
2155                 
2156                 if (have_read || have_written) {
2157                     if (session->wait_us) {
2158                         session->wait_us = 0;
2159                         update_child_status(session, SERVER_BUSY_READ, "busy");
2160                     }
2161                 }
2162                 else if (!nghttp2_session_want_write(session->ngh2)) {
2163                     dispatch_event(session, H2_SESSION_EV_NO_IO, 0, NULL);
2164                 }
2165                 break;
2166                 
2167             case H2_SESSION_ST_WAIT:
2168                 if (session->wait_us <= 0) {
2169                     session->wait_us = 10;
2170                     session->start_wait = apr_time_now();
2171                     update_child_status(session, SERVER_BUSY_READ, "wait");
2172                 }
2173                 else if ((apr_time_now() - session->start_wait) >= session->s->timeout) {
2174                     /* waited long enough */
2175                     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, APR_TIMEUP, c,
2176                                   "h2_session: wait for data");
2177                     dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
2178                 }
2179                 else {
2180                     /* repeating, increase timer for graceful backoff */
2181                     session->wait_us = H2MIN(session->wait_us*2, MAX_WAIT_MICROS);
2182                 }
2183
2184                 if (APLOGctrace1(c)) {
2185                     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
2186                                   "h2_session: wait for data, %ld micros", 
2187                                   (long)session->wait_us);
2188                 }
2189                 status = h2_mplx_out_trywait(session->mplx, session->wait_us, 
2190                                              session->iowait);
2191                 if (status == APR_SUCCESS) {
2192                     session->wait_us = 0;
2193                     dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
2194                 }
2195                 else if (status == APR_TIMEUP) {
2196                     /* go back to checking all inputs again */
2197                     transit(session, "wait cycle", H2_SESSION_ST_BUSY);
2198                 }
2199                 else {
2200                     h2_session_shutdown(session, H2_ERR_INTERNAL_ERROR, "cond wait error");
2201                 }
2202                 break;
2203                 
2204             case H2_SESSION_ST_DONE:
2205                 update_child_status(session, SERVER_CLOSING, "done");
2206                 status = APR_EOF;
2207                 goto out;
2208                 
2209             default:
2210                 ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
2211                               APLOGNO(03080)
2212                               "h2_session(%ld): unknown state %d", session->id, session->state);
2213                 dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, 0, NULL);
2214                 break;
2215         }
2216
2217         if (have_written) {
2218             h2_conn_io_flush(&session->io);
2219         }
2220         else if (!nghttp2_session_want_read(session->ngh2) 
2221                  && !nghttp2_session_want_write(session->ngh2)) {
2222             dispatch_event(session, H2_SESSION_EV_NGH2_DONE, 0, NULL); 
2223         }
2224     }
2225     
2226 out:
2227     if (have_written) {
2228         h2_conn_io_flush(&session->io);
2229     }
2230     
2231     ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
2232                   "h2_session(%ld): [%s] process returns", 
2233                   session->id, state_name(session->state));
2234
2235     if ((session->state != H2_SESSION_ST_DONE)
2236         && (APR_STATUS_IS_EOF(status)
2237             || APR_STATUS_IS_ECONNRESET(status) 
2238             || APR_STATUS_IS_ECONNABORTED(status))) {
2239             dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
2240         }
2241
2242     status = (session->state == H2_SESSION_ST_DONE)? APR_EOF : APR_SUCCESS;
2243     if (session->state == H2_SESSION_ST_DONE) {
2244         if (!session->eoc_written) {
2245             session->eoc_written = 1;
2246             h2_conn_io_write_eoc(&session->io, 
2247                                  h2_bucket_eoc_create(session->c->bucket_alloc, session));
2248         }
2249     }
2250     
2251     return status;
2252 }
2253
2254 apr_status_t h2_session_pre_close(h2_session *session, int async)
2255 {
2256     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, 
2257                   "h2_session(%ld): pre_close", session->id);
2258     dispatch_event(session, H2_SESSION_EV_PRE_CLOSE, 0, "timeout");
2259     return APR_SUCCESS;
2260 }