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