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