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