"Fossies" - the Fresh Open Source Software Archive

Member "mod_http2-1.15.17/mod_http2/h2_session.c" (22 Feb 2021, 92135 Bytes) of package /linux/www/apache_httpd_modules/mod_http2-1.15.17.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "h2_session.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.15.16_vs_1.15.17.

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