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