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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <apr_thread_cond.h>
20 #include <apr_base64.h>
21 #include <apr_strings.h>
26 #include <http_core.h>
27 #include <http_config.h>
29 #include <scoreboard.h>
31 #include <mpm_common.h>
33 #include "h2_private.h"
35 #include "h2_bucket_beam.h"
36 #include "h2_bucket_eos.h"
37 #include "h2_config.h"
39 #include "h2_filter.h"
43 #include "h2_request.h"
44 #include "h2_headers.h"
45 #include "h2_stream.h"
47 #include "h2_session.h"
49 #include "h2_version.h"
50 #include "h2_workers.h"
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);
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);
62 static int h2_session_status_from_apr_status(apr_status_t rv)
64 if (rv == APR_SUCCESS) {
65 return NGHTTP2_NO_ERROR;
67 else if (APR_STATUS_IS_EAGAIN(rv)) {
68 return NGHTTP2_ERR_WOULDBLOCK;
70 else if (APR_STATUS_IS_EOF(rv)) {
71 return NGHTTP2_ERR_EOF;
73 return NGHTTP2_ERR_PROTO;
76 static h2_stream *get_stream(h2_session *session, int stream_id)
80 if (stream_id <= 0) return NULL;
81 stream = h2_mplx_stream_get(session->mplx, stream_id);
83 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
84 "session_stream_get(%d) == NULL", stream_id);
89 static void dispatch_event(h2_session *session, h2_session_event_t ev,
90 int err, const char *msg);
92 void h2_session_event(h2_session *session, h2_session_event_t ev,
93 int err, const char *msg)
95 dispatch_event(session, ev, err, msg);
98 static int rst_unprocessed_stream(h2_stream *stream, void *ctx)
100 int unprocessed = (!h2_stream_was_closed(stream)
101 && (H2_STREAM_CLIENT_INITIATED(stream->id)?
102 (!stream->session->local.accepting
103 && stream->id > stream->session->local.accepted_max)
105 (!stream->session->remote.accepting
106 && stream->id > stream->session->remote.accepted_max))
109 h2_stream_rst(stream, H2_ERR_NO_ERROR);
115 static void cleanup_unprocessed_streams(h2_session *session)
117 h2_mplx_stream_do(session->mplx, rst_unprocessed_stream, session);
120 static apr_pool_t *session_stream_pool_create(h2_session *session)
124 apr_pool_create(&pool, session->pool);
125 apr_pool_tag(pool, "h2_stream");
129 static h2_stream *session_stream_pcreate(h2_session *session, int stream_id,
130 apr_pool_t *pool, int initiated_on)
132 return h2_stream_create(stream_id, pool, session, session->monitor, initiated_on);
135 static h2_stream *session_stream_create(h2_session *session, int stream_id)
137 return session_stream_pcreate(session, stream_id, session_stream_pool_create(session), 0);
141 * Determine the importance of streams when scheduling tasks.
142 * - if both stream depend on the same one, compare weights
143 * - if one stream is closer to the root, prioritize that one
144 * - if both are on the same level, use the weight of their root
147 static int spri_cmp(int sid1, nghttp2_stream *s1,
148 int sid2, nghttp2_stream *s2, h2_session *session)
150 nghttp2_stream *p1, *p2;
152 p1 = nghttp2_stream_get_parent(s1);
153 p2 = nghttp2_stream_get_parent(s2);
158 w1 = nghttp2_stream_get_weight(s1);
159 w2 = nghttp2_stream_get_weight(s2);
163 /* stream 1 closer to root */
167 /* stream 2 closer to root */
170 return spri_cmp(sid1, p1, sid2, p2, session);
173 static int stream_pri_cmp(int sid1, int sid2, void *ctx)
175 h2_session *session = ctx;
176 nghttp2_stream *s1, *s2;
178 s1 = nghttp2_session_find_stream(session->ngh2, sid1);
179 s2 = nghttp2_session_find_stream(session->ngh2, sid2);
190 return spri_cmp(sid1, s1, sid2, s2, session);
194 * Callback when nghttp2 wants to send bytes back to the client.
196 static ssize_t send_cb(nghttp2_session *ngh2,
197 const uint8_t *data, size_t length,
198 int flags, void *userp)
200 h2_session *session = (h2_session *)userp;
205 status = h2_conn_io_write(&session->io, (const char *)data, length);
206 if (status == APR_SUCCESS) {
209 if (APR_STATUS_IS_EAGAIN(status)) {
210 return NGHTTP2_ERR_WOULDBLOCK;
212 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, APLOGNO(03062)
213 "h2_session: send error");
214 return h2_session_status_from_apr_status(status);
217 static int on_invalid_frame_recv_cb(nghttp2_session *ngh2,
218 const nghttp2_frame *frame,
219 int error, void *userp)
221 h2_session *session = (h2_session *)userp;
224 if (APLOGcdebug(session->c)) {
227 h2_util_frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
228 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
229 H2_SSSN_LOG(APLOGNO(03063), session,
230 "recv invalid FRAME[%s], frames=%ld/%ld (r/s)"),
231 buffer, (long)session->frames_received,
232 (long)session->frames_sent);
237 static int on_data_chunk_recv_cb(nghttp2_session *ngh2, uint8_t flags,
239 const uint8_t *data, size_t len, void *userp)
241 h2_session *session = (h2_session *)userp;
242 apr_status_t status = APR_EINVAL;
246 stream = get_stream(session, stream_id);
248 status = h2_stream_recv_DATA(stream, flags, data, len);
249 dispatch_event(session, H2_SESSION_EV_STREAM_CHANGE, 0, "stream data rcvd");
252 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03064)
253 "h2_stream(%ld-%d): on_data_chunk for unknown stream",
254 session->id, (int)stream_id);
255 rv = NGHTTP2_ERR_CALLBACK_FAILURE;
258 if (status != APR_SUCCESS) {
259 /* count this as consumed explicitly as no one will read it */
260 nghttp2_session_consume(session->ngh2, stream_id, len);
265 static int on_stream_close_cb(nghttp2_session *ngh2, int32_t stream_id,
266 uint32_t error_code, void *userp)
268 h2_session *session = (h2_session *)userp;
272 stream = get_stream(session, stream_id);
275 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
276 H2_STRM_LOG(APLOGNO(03065), stream,
277 "closing with err=%d %s"),
278 (int)error_code, h2_h2_err_description(error_code));
279 h2_stream_rst(stream, error_code);
285 static int on_begin_headers_cb(nghttp2_session *ngh2,
286 const nghttp2_frame *frame, void *userp)
288 h2_session *session = (h2_session *)userp;
291 /* We may see HEADERs at the start of a stream or after all DATA
292 * streams to carry trailers. */
294 stream = get_stream(session, frame->hd.stream_id);
296 stream = session_stream_create(session, frame->hd.stream_id);
297 if (!stream) return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
298 h2_mplx_stream_register(session->mplx, stream);
303 static int on_header_cb(nghttp2_session *ngh2, const nghttp2_frame *frame,
304 const uint8_t *name, size_t namelen,
305 const uint8_t *value, size_t valuelen,
309 h2_session *session = (h2_session *)userp;
314 stream = get_stream(session, frame->hd.stream_id);
316 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(02920)
317 "h2_stream(%ld-%d): on_header unknown stream",
318 session->id, (int)frame->hd.stream_id);
319 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
322 status = h2_stream_add_header(stream, (const char *)name, namelen,
323 (const char *)value, valuelen);
324 if (status != APR_SUCCESS && !h2_stream_is_ready(stream)) {
325 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
331 * nghttp2 session has received a complete frame. Most are used by nghttp2
332 * for processing of internal state. Some, like HEADER and DATA frames,
335 static int on_frame_recv_cb(nghttp2_session *ng2s,
336 const nghttp2_frame *frame,
339 h2_session *session = (h2_session *)userp;
341 apr_status_t rv = APR_SUCCESS;
343 if (APLOGcdebug(session->c)) {
346 h2_util_frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
347 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
348 H2_SSSN_LOG(APLOGNO(03066), session,
349 "recv FRAME[%s], frames=%ld/%ld (r/s)"),
350 buffer, (long)session->frames_received,
351 (long)session->frames_sent);
354 ++session->frames_received;
355 switch (frame->hd.type) {
356 case NGHTTP2_HEADERS:
357 /* This can be HEADERS for a new stream, defining the request,
358 * or HEADER may come after DATA at the end of a stream as in
360 stream = get_stream(session, frame->hd.stream_id);
362 rv = h2_stream_recv_frame(stream, NGHTTP2_HEADERS, frame->hd.flags,
363 frame->hd.length + H2_FRAME_HDR_LEN);
367 stream = get_stream(session, frame->hd.stream_id);
369 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
370 H2_STRM_LOG(APLOGNO(02923), stream,
371 "DATA, len=%ld, flags=%d"),
372 (long)frame->hd.length, frame->hd.flags);
373 rv = h2_stream_recv_frame(stream, NGHTTP2_DATA, frame->hd.flags,
374 frame->hd.length + H2_FRAME_HDR_LEN);
377 case NGHTTP2_PRIORITY:
378 session->reprioritize = 1;
379 if (APLOGctrace2(session->c)) {
380 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
381 "h2_stream(%ld-%d): PRIORITY frame "
382 " weight=%d, dependsOn=%d, exclusive=%d",
383 session->id, (int)frame->hd.stream_id,
384 frame->priority.pri_spec.weight,
385 frame->priority.pri_spec.stream_id,
386 frame->priority.pri_spec.exclusive);
389 case NGHTTP2_WINDOW_UPDATE:
390 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
391 "h2_stream(%ld-%d): WINDOW_UPDATE incr=%d",
392 session->id, (int)frame->hd.stream_id,
393 frame->window_update.window_size_increment);
394 if (nghttp2_session_want_write(session->ngh2)) {
395 dispatch_event(session, H2_SESSION_EV_FRAME_RCVD, 0, "window update");
398 case NGHTTP2_RST_STREAM:
399 if (APLOGcdebug(session->c)) {
400 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03067)
401 "h2_stream(%ld-%d): RST_STREAM by client, errror=%d",
402 session->id, (int)frame->hd.stream_id,
403 (int)frame->rst_stream.error_code);
405 stream = get_stream(session, frame->hd.stream_id);
407 stream->initiated_on? ++session->pushes_reset : ++session->streams_reset;
411 if (frame->goaway.error_code == 0
412 && frame->goaway.last_stream_id == ((1u << 31) - 1)) {
413 /* shutdown notice. Should not come from a client... */
414 session->remote.accepting = 0;
417 session->remote.accepted_max = frame->goaway.last_stream_id;
418 dispatch_event(session, H2_SESSION_EV_REMOTE_GOAWAY,
419 frame->goaway.error_code, NULL);
422 case NGHTTP2_SETTINGS:
423 if (APLOGctrace2(session->c)) {
424 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
425 H2_SSSN_MSG(session, "SETTINGS, len=%ld"), (long)frame->hd.length);
429 if (APLOGctrace2(session->c)) {
432 h2_util_frame_print(frame, buffer,
433 sizeof(buffer)/sizeof(buffer[0]));
434 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
435 H2_SSSN_MSG(session, "on_frame_rcv %s"), buffer);
440 if (session->state == H2_SESSION_ST_IDLE) {
441 /* We received a frame, but session is in state IDLE. That means the frame
442 * did not really progress any of the (possibly) open streams. It was a meta
443 * frame, e.g. SETTINGS/WINDOW_UPDATE/unknown/etc.
444 * Remember: IDLE means we cannot send because either there are no streams open or
445 * all open streams are blocked on exhausted WINDOWs for outgoing data.
446 * The more frames we receive that do not change this, the less interested we
447 * become in serving this connection. This is expressed in increasing "idle_delays".
448 * Eventually, the connection will timeout and we'll close it. */
449 session->idle_frames = H2MIN(session->idle_frames + 1, session->frames_received);
450 ap_log_cerror( APLOG_MARK, APLOG_TRACE2, 0, session->c,
451 H2_SSSN_MSG(session, "session has %ld idle frames"),
452 (long)session->idle_frames);
453 if (session->idle_frames > 10) {
454 apr_size_t busy_frames = H2MAX(session->frames_received - session->idle_frames, 1);
455 int idle_ratio = (int)(session->idle_frames / busy_frames);
456 if (idle_ratio > 100) {
457 session->idle_delay = apr_time_from_msec(H2MIN(1000, idle_ratio));
459 else if (idle_ratio > 10) {
460 session->idle_delay = apr_time_from_msec(10);
462 else if (idle_ratio > 1) {
463 session->idle_delay = apr_time_from_msec(1);
466 session->idle_delay = 0;
471 return (APR_SUCCESS != rv)? NGHTTP2_ERR_PROTO : 0;
474 static char immortal_zeros[H2_MAX_PADLEN];
476 static int on_send_data_cb(nghttp2_session *ngh2,
477 nghttp2_frame *frame,
478 const uint8_t *framehd,
480 nghttp2_data_source *source,
483 apr_status_t status = APR_SUCCESS;
484 h2_session *session = (h2_session *)userp;
485 int stream_id = (int)frame->hd.stream_id;
486 unsigned char padlen;
490 apr_off_t len = length;
494 /* Be nimble, react to events from your tasks and do not buffer more than we need */
495 if (h2_mplx_has_master_events(session->mplx) ||h2_conn_io_needs_flush(&session->io)) {
496 return NGHTTP2_ERR_WOULDBLOCK;
499 ap_assert(frame->data.padlen <= (H2_MAX_PADLEN+1));
500 padlen = (unsigned char)frame->data.padlen;
502 stream = get_stream(session, stream_id);
504 ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_NOTFOUND, session->c,
506 "h2_stream(%ld-%d): send_data, stream not found",
507 session->id, (int)stream_id);
508 return NGHTTP2_ERR_CALLBACK_FAILURE;
511 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
512 H2_STRM_MSG(stream, "send_data_cb for %ld bytes"),
515 status = h2_conn_io_write(&session->io, (const char *)framehd, H2_FRAME_HDR_LEN);
516 if (padlen && status == APR_SUCCESS) {
518 status = h2_conn_io_write(&session->io, (const char *)&padlen, 1);
521 if (status != APR_SUCCESS) {
522 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, session->c,
523 H2_STRM_MSG(stream, "writing frame header"));
524 return NGHTTP2_ERR_CALLBACK_FAILURE;
527 status = h2_stream_read_to(stream, session->bbtmp, &len, &eos);
528 if (status != APR_SUCCESS) {
529 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, session->c,
530 H2_STRM_MSG(stream, "send_data_cb, reading stream"));
531 apr_brigade_cleanup(session->bbtmp);
532 return NGHTTP2_ERR_CALLBACK_FAILURE;
534 else if (len != length) {
535 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, session->c,
536 H2_STRM_MSG(stream, "send_data_cb, wanted %ld bytes, "
537 "got %ld from stream"), (long)length, (long)len);
538 apr_brigade_cleanup(session->bbtmp);
539 return NGHTTP2_ERR_CALLBACK_FAILURE;
543 b = apr_bucket_immortal_create(immortal_zeros, padlen,
544 session->c->bucket_alloc);
545 APR_BRIGADE_INSERT_TAIL(session->bbtmp, b);
548 status = h2_conn_io_pass(&session->io, session->bbtmp);
549 apr_brigade_cleanup(session->bbtmp);
551 if (status == APR_SUCCESS) {
552 stream->out_data_frames++;
553 stream->out_data_octets += length;
557 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
558 H2_STRM_LOG(APLOGNO(02925), stream, "failed send_data_cb"));
559 return NGHTTP2_ERR_CALLBACK_FAILURE;
563 static int on_frame_send_cb(nghttp2_session *ngh2,
564 const nghttp2_frame *frame,
567 h2_session *session = user_data;
569 int stream_id = frame->hd.stream_id;
571 ++session->frames_sent;
572 switch (frame->hd.type) {
573 case NGHTTP2_PUSH_PROMISE:
574 /* PUSH_PROMISE we report on the promised stream */
575 stream_id = frame->push_promise.promised_stream_id;
581 if (APLOGcdebug(session->c)) {
584 h2_util_frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
585 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
586 H2_SSSN_LOG(APLOGNO(03068), session,
587 "sent FRAME[%s], frames=%ld/%ld (r/s)"),
588 buffer, (long)session->frames_received,
589 (long)session->frames_sent);
592 stream = get_stream(session, stream_id);
594 h2_stream_send_frame(stream, frame->hd.type, frame->hd.flags,
595 frame->hd.length + H2_FRAME_HDR_LEN);
600 #ifdef H2_NG2_INVALID_HEADER_CB
601 static int on_invalid_header_cb(nghttp2_session *ngh2,
602 const nghttp2_frame *frame,
603 const uint8_t *name, size_t namelen,
604 const uint8_t *value, size_t valuelen,
605 uint8_t flags, void *user_data)
607 h2_session *session = user_data;
610 if (APLOGcdebug(session->c)) {
611 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03456)
612 "h2_stream(%ld-%d): invalid header '%s: %s'",
613 session->id, (int)frame->hd.stream_id,
614 apr_pstrndup(session->pool, (const char *)name, namelen),
615 apr_pstrndup(session->pool, (const char *)value, valuelen));
617 stream = get_stream(session, frame->hd.stream_id);
619 h2_stream_rst(stream, NGHTTP2_PROTOCOL_ERROR);
625 static ssize_t select_padding_cb(nghttp2_session *ngh2,
626 const nghttp2_frame *frame,
627 size_t max_payloadlen, void *user_data)
629 h2_session *session = user_data;
630 ssize_t frame_len = frame->hd.length + H2_FRAME_HDR_LEN; /* the total length without padding */
631 ssize_t padded_len = frame_len;
633 /* Determine # of padding bytes to append to frame. Unless session->padding_always
634 * the number my be capped by the ui.write_size that currently applies.
636 if (session->padding_max) {
637 int n = ap_random_pick(0, session->padding_max);
638 padded_len = H2MIN(max_payloadlen + H2_FRAME_HDR_LEN, frame_len + n);
641 if (padded_len != frame_len) {
642 if (!session->padding_always && session->io.write_size
643 && (padded_len > session->io.write_size)
644 && (frame_len <= session->io.write_size)) {
645 padded_len = session->io.write_size;
647 if (APLOGctrace2(session->c)) {
648 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
649 "select padding from [%d, %d]: %d (frame length: 0x%04x, write size: %d)",
650 (int)frame_len, (int)max_payloadlen+H2_FRAME_HDR_LEN,
651 (int)(padded_len - frame_len), (int)padded_len, (int)session->io.write_size);
653 return padded_len - H2_FRAME_HDR_LEN;
655 return frame->hd.length;
658 #define NGH2_SET_CALLBACK(callbacks, name, fn)\
659 nghttp2_session_callbacks_set_##name##_callback(callbacks, fn)
661 static apr_status_t init_callbacks(conn_rec *c, nghttp2_session_callbacks **pcb)
663 int rv = nghttp2_session_callbacks_new(pcb);
665 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
666 APLOGNO(02926) "nghttp2_session_callbacks_new: %s",
667 nghttp2_strerror(rv));
671 NGH2_SET_CALLBACK(*pcb, send, send_cb);
672 NGH2_SET_CALLBACK(*pcb, on_frame_recv, on_frame_recv_cb);
673 NGH2_SET_CALLBACK(*pcb, on_invalid_frame_recv, on_invalid_frame_recv_cb);
674 NGH2_SET_CALLBACK(*pcb, on_data_chunk_recv, on_data_chunk_recv_cb);
675 NGH2_SET_CALLBACK(*pcb, on_stream_close, on_stream_close_cb);
676 NGH2_SET_CALLBACK(*pcb, on_begin_headers, on_begin_headers_cb);
677 NGH2_SET_CALLBACK(*pcb, on_header, on_header_cb);
678 NGH2_SET_CALLBACK(*pcb, send_data, on_send_data_cb);
679 NGH2_SET_CALLBACK(*pcb, on_frame_send, on_frame_send_cb);
680 #ifdef H2_NG2_INVALID_HEADER_CB
681 NGH2_SET_CALLBACK(*pcb, on_invalid_header, on_invalid_header_cb);
683 NGH2_SET_CALLBACK(*pcb, select_padding, select_padding_cb);
687 static apr_status_t h2_session_shutdown_notice(h2_session *session)
692 if (!session->local.accepting) {
696 nghttp2_submit_shutdown_notice(session->ngh2);
697 session->local.accepting = 0;
698 status = nghttp2_session_send(session->ngh2);
699 if (status == APR_SUCCESS) {
700 status = h2_conn_io_flush(&session->io);
702 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
703 H2_SSSN_LOG(APLOGNO(03457), session, "sent shutdown notice"));
707 static apr_status_t h2_session_shutdown(h2_session *session, int error,
708 const char *msg, int force_close)
710 apr_status_t status = APR_SUCCESS;
713 if (session->local.shutdown) {
717 msg = nghttp2_strerror(error);
720 if (error || force_close) {
721 /* not a graceful shutdown, we want to leave...
722 * Do not start further streams that are waiting to be scheduled.
723 * Find out the max stream id that we habe been processed or
724 * are still actively working on.
725 * Remove all streams greater than this number without submitting
726 * a RST_STREAM frame, since that should be clear from the GOAWAY
728 session->local.accepted_max = h2_mplx_shutdown(session->mplx);
729 session->local.error = error;
732 /* graceful shutdown. we will continue processing all streams
733 * we have, but no longer accept new ones. Report the max stream
734 * we have received and discard all new ones. */
737 session->local.accepting = 0;
738 session->local.shutdown = 1;
739 if (!session->c->aborted) {
740 nghttp2_submit_goaway(session->ngh2, NGHTTP2_FLAG_NONE,
741 session->local.accepted_max,
742 error, (uint8_t*)msg, msg? strlen(msg):0);
743 status = nghttp2_session_send(session->ngh2);
744 if (status == APR_SUCCESS) {
745 status = h2_conn_io_flush(&session->io);
747 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
748 H2_SSSN_LOG(APLOGNO(03069), session,
749 "sent GOAWAY, err=%d, msg=%s"), error, msg? msg : "");
751 dispatch_event(session, H2_SESSION_EV_LOCAL_GOAWAY, error, msg);
755 static apr_status_t session_cleanup(h2_session *session, const char *trigger)
757 conn_rec *c = session->c;
758 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
759 H2_SSSN_MSG(session, "pool_cleanup"));
761 if (session->state != H2_SESSION_ST_DONE
762 && session->state != H2_SESSION_ST_INIT) {
763 /* Not good. The connection is being torn down and we have
764 * not sent a goaway. This is considered a protocol error and
765 * the client has to assume that any streams "in flight" may have
766 * been processed and are not safe to retry.
767 * As clients with idle connection may only learn about a closed
768 * connection when sending the next request, this has the effect
769 * that at least this one request will fail.
771 ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c,
772 H2_SSSN_LOG(APLOGNO(03199), session,
773 "connection disappeared without proper "
774 "goodbye, clients will be confused, should not happen"));
777 transit(session, trigger, H2_SESSION_ST_CLEANUP);
778 h2_mplx_release_and_join(session->mplx, session->iowait);
779 session->mplx = NULL;
781 ap_assert(session->ngh2);
782 nghttp2_session_del(session->ngh2);
783 session->ngh2 = NULL;
790 static apr_status_t session_pool_cleanup(void *data)
795 if ((session = h2_ctx_get_session(c))) {
796 /* if the session is still there, now is the last chance
797 * to perform cleanup. Normally, cleanup should have happened
798 * earlier in the connection pre_close. Main reason is that
799 * any ongoing requests on slave connections might still access
800 * data which has, at this time, already been freed. An example
801 * is mod_ssl that uses request hooks. */
802 ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c,
803 H2_SSSN_LOG(APLOGNO(10020), session,
804 "session cleanup triggered by pool cleanup. "
805 "this should have happened earlier already."));
806 return session_cleanup(session, "pool cleanup");
811 apr_status_t h2_session_create(h2_session **psession, conn_rec *c, request_rec *r,
812 server_rec *s, h2_workers *workers)
814 nghttp2_session_callbacks *callbacks = NULL;
815 nghttp2_option *options = NULL;
816 apr_allocator_t *allocator;
817 apr_thread_mutex_t *mutex;
819 apr_pool_t *pool = NULL;
825 status = apr_allocator_create(&allocator);
826 if (status != APR_SUCCESS) {
829 apr_allocator_max_free_set(allocator, ap_max_mem_free);
830 apr_pool_create_ex(&pool, c->pool, NULL, allocator);
832 apr_allocator_destroy(allocator);
835 apr_pool_tag(pool, "h2_session");
836 apr_allocator_owner_set(allocator, pool);
837 status = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, pool);
838 if (status != APR_SUCCESS) {
841 apr_allocator_mutex_set(allocator, mutex);
843 session = apr_pcalloc(pool, sizeof(h2_session));
853 session->pool = pool;
854 session->workers = workers;
856 session->state = H2_SESSION_ST_INIT;
857 session->local.accepting = 1;
858 session->remote.accepting = 1;
860 session->max_stream_count = h2_config_sgeti(s, H2_CONF_MAX_STREAMS);
861 session->max_stream_mem = h2_config_sgeti(s, H2_CONF_STREAM_MAX_MEM);
863 status = apr_thread_cond_create(&session->iowait, session->pool);
864 if (status != APR_SUCCESS) {
868 session->in_pending = h2_iq_create(session->pool, (int)session->max_stream_count);
869 if (session->in_pending == NULL) {
874 session->in_process = h2_iq_create(session->pool, (int)session->max_stream_count);
875 if (session->in_process == NULL) {
880 session->monitor = apr_pcalloc(pool, sizeof(h2_stream_monitor));
881 if (session->monitor == NULL) {
885 session->monitor->ctx = session;
886 session->monitor->on_state_enter = on_stream_state_enter;
887 session->monitor->on_state_event = on_stream_state_event;
888 session->monitor->on_event = on_stream_event;
890 session->mplx = h2_mplx_create(c, s, session->pool, workers);
892 /* connection input filter that feeds the session */
893 session->cin = h2_filter_cin_create(session);
894 ap_add_input_filter("H2_IN", session->cin, r, c);
896 h2_conn_io_init(&session->io, c, s);
897 session->padding_max = h2_config_sgeti(s, H2_CONF_PADDING_BITS);
898 if (session->padding_max) {
899 session->padding_max = (0x01 << session->padding_max) - 1;
901 session->padding_always = h2_config_sgeti(s, H2_CONF_PADDING_ALWAYS);
902 session->bbtmp = apr_brigade_create(session->pool, c->bucket_alloc);
904 status = init_callbacks(c, &callbacks);
905 if (status != APR_SUCCESS) {
906 ap_log_cerror(APLOG_MARK, APLOG_ERR, status, c, APLOGNO(02927)
907 "nghttp2: error in init_callbacks");
912 rv = nghttp2_option_new(&options);
914 ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
915 APLOGNO(02928) "nghttp2_option_new: %s",
916 nghttp2_strerror(rv));
920 nghttp2_option_set_peer_max_concurrent_streams(options, (uint32_t)session->max_stream_count);
921 /* We need to handle window updates ourself, otherwise we
922 * get flooded by nghttp2. */
923 nghttp2_option_set_no_auto_window_update(options, 1);
925 rv = nghttp2_session_server_new2(&session->ngh2, callbacks,
927 nghttp2_session_callbacks_del(callbacks);
928 nghttp2_option_del(options);
931 ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
932 APLOGNO(02929) "nghttp2_session_server_new: %s",
933 nghttp2_strerror(rv));
938 n = h2_config_sgeti(s, H2_CONF_PUSH_DIARY_SIZE);
939 session->push_diary = h2_push_diary_create(session->pool, n);
941 if (APLOGcdebug(c)) {
942 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
943 H2_SSSN_LOG(APLOGNO(03200), session,
944 "created, max_streams=%d, stream_mem=%d, "
945 "workers_limit=%d, workers_max=%d, "
946 "push_diary(type=%d,N=%d)"),
947 (int)session->max_stream_count,
948 (int)session->max_stream_mem,
949 session->mplx->limit_active,
950 session->mplx->max_active,
951 session->push_diary->dtype,
952 (int)session->push_diary->N);
955 apr_pool_pre_cleanup_register(pool, c, session_pool_cleanup);
959 apr_pool_destroy(pool);
963 static apr_status_t h2_session_start(h2_session *session, int *rv)
965 apr_status_t status = APR_SUCCESS;
966 nghttp2_settings_entry settings[3];
971 /* Start the conversation by submitting our SETTINGS frame */
978 /* 'h2c' mode: we should have a 'HTTP2-Settings' header with
979 * base64 encoded client settings. */
980 s = apr_table_get(session->r->headers_in, "HTTP2-Settings");
982 ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EINVAL, session->r,
984 "HTTP2-Settings header missing in request");
988 dlen = h2_util_base64url_decode(&cs, s, session->pool);
990 if (APLOGrdebug(session->r)) {
992 h2_util_hex_dump(buffer, 128, (char*)cs, dlen);
993 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, session->r, APLOGNO(03070)
994 "upgrading h2c session with HTTP2-Settings: %s -> %s (%d)",
995 s, buffer, (int)dlen);
998 *rv = nghttp2_session_upgrade(session->ngh2, (uint8_t*)cs, dlen, NULL);
1000 status = APR_EINVAL;
1001 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
1002 APLOGNO(02932) "nghttp2_session_upgrade: %s",
1003 nghttp2_strerror(*rv));
1007 /* Now we need to auto-open stream 1 for the request we got. */
1008 stream = session_stream_create(session, 1);
1010 status = APR_EGENERAL;
1011 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
1012 APLOGNO(02933) "open stream 1: %s",
1013 nghttp2_strerror(*rv));
1016 status = h2_stream_set_request_rec(stream, session->r, 1);
1017 if (status != APR_SUCCESS) {
1020 h2_mplx_stream_register(session->mplx, stream);
1024 settings[slen].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
1025 settings[slen].value = (uint32_t)session->max_stream_count;
1027 win_size = h2_config_sgeti(session->s, H2_CONF_WIN_SIZE);
1028 if (win_size != H2_INITIAL_WINDOW_SIZE) {
1029 settings[slen].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
1030 settings[slen].value = win_size;
1034 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
1035 H2_SSSN_LOG(APLOGNO(03201), session,
1036 "start, INITIAL_WINDOW_SIZE=%ld, MAX_CONCURRENT_STREAMS=%d"),
1037 (long)win_size, (int)session->max_stream_count);
1038 *rv = nghttp2_submit_settings(session->ngh2, NGHTTP2_FLAG_NONE,
1041 status = APR_EGENERAL;
1042 ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
1043 H2_SSSN_LOG(APLOGNO(02935), session,
1044 "nghttp2_submit_settings: %s"), nghttp2_strerror(*rv));
1047 /* use maximum possible value for connection window size. We are only
1048 * interested in per stream flow control. which have the initial window
1049 * size configured above.
1050 * Therefore, for our use, the connection window can only get in the
1051 * way. Example: if we allow 100 streams with a 32KB window each, we
1052 * buffer up to 3.2 MB of data. Unless we do separate connection window
1053 * interim updates, any smaller connection window will lead to blocking
1056 *rv = nghttp2_submit_window_update(session->ngh2, NGHTTP2_FLAG_NONE,
1057 0, NGHTTP2_MAX_WINDOW_SIZE - win_size);
1059 status = APR_EGENERAL;
1060 ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
1061 H2_SSSN_LOG(APLOGNO(02970), session,
1062 "nghttp2_submit_window_update: %s"),
1063 nghttp2_strerror(*rv));
1070 static apr_status_t on_stream_headers(h2_session *session, h2_stream *stream,
1071 h2_headers *headers, apr_off_t len,
1074 static ssize_t stream_data_cb(nghttp2_session *ng2s,
1078 uint32_t *data_flags,
1079 nghttp2_data_source *source,
1082 h2_session *session = (h2_session *)puser;
1083 apr_off_t nread = length;
1085 apr_status_t status;
1089 /* The session wants to send more DATA for the stream. We need
1090 * to find out how much of the requested length we can send without
1092 * Indicate EOS when we encounter it or DEFERRED if the stream
1093 * should be suspended. Beware of trailers.
1099 stream = get_stream(session, stream_id);
1101 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
1103 "h2_stream(%ld-%d): data_cb, stream not found",
1104 session->id, (int)stream_id);
1105 return NGHTTP2_ERR_CALLBACK_FAILURE;
1108 status = h2_stream_out_prepare(stream, &nread, &eos, NULL);
1110 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
1111 H2_STRM_MSG(stream, "prepared no_copy, len=%ld, eos=%d"),
1113 *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
1124 case APR_ECONNRESET:
1125 case APR_ECONNABORTED:
1126 return NGHTTP2_ERR_CALLBACK_FAILURE;
1129 /* If there is no data available, our session will automatically
1130 * suspend this stream and not ask for more data until we resume
1131 * it. Remember at our h2_stream that we need to do this.
1134 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
1135 H2_STRM_LOG(APLOGNO(03071), stream, "suspending"));
1136 return NGHTTP2_ERR_DEFERRED;
1140 ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
1141 H2_STRM_LOG(APLOGNO(02938), stream, "reading data"));
1142 return NGHTTP2_ERR_CALLBACK_FAILURE;
1146 *data_flags |= NGHTTP2_DATA_FLAG_EOF;
1148 return (ssize_t)nread;
1151 static int valid_weight(float f)
1154 return (w < NGHTTP2_MIN_WEIGHT? NGHTTP2_MIN_WEIGHT :
1155 (w > NGHTTP2_MAX_WEIGHT)? NGHTTP2_MAX_WEIGHT : w);
1158 static apr_status_t session_stream_priority_set(h2_session *session, h2_stream *stream,
1159 const h2_priority *prio)
1161 apr_status_t status = APR_SUCCESS;
1162 #ifdef H2_NG2_CHANGE_PRIO
1163 nghttp2_stream *s_grandpa, *s_parent, *s;
1166 /* we treat this as a NOP */
1169 s = nghttp2_session_find_stream(session->ngh2, stream->id);
1171 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
1172 H2_STRM_MSG(stream, "lookup of nghttp2_stream failed"));
1176 s_parent = nghttp2_stream_get_parent(s);
1178 nghttp2_priority_spec ps;
1179 int id_parent, id_grandpa, w_parent, w;
1181 const char *ptype = "AFTER";
1182 h2_dependency dep = prio->dependency;
1184 id_parent = nghttp2_stream_get_stream_id(s_parent);
1185 s_grandpa = nghttp2_stream_get_parent(s_parent);
1187 id_grandpa = nghttp2_stream_get_stream_id(s_grandpa);
1190 /* parent of parent does not exist,
1191 * only possible if parent == root */
1192 dep = H2_DEPENDANT_AFTER;
1196 case H2_DEPENDANT_INTERLEAVED:
1197 /* PUSHed stream is to be interleaved with initiating stream.
1198 * It is made a sibling of the initiating stream and gets a
1199 * proportional weight [1, MAX_WEIGHT] of the initiaing
1202 ptype = "INTERLEAVED";
1203 w_parent = nghttp2_stream_get_weight(s_parent);
1204 w = valid_weight(w_parent * ((float)prio->weight / NGHTTP2_MAX_WEIGHT));
1205 nghttp2_priority_spec_init(&ps, id_grandpa, w, 0);
1208 case H2_DEPENDANT_BEFORE:
1209 /* PUSHed stream os to be sent BEFORE the initiating stream.
1210 * It gets the same weight as the initiating stream, replaces
1211 * that stream in the dependency tree and has the initiating
1215 w = w_parent = nghttp2_stream_get_weight(s_parent);
1216 nghttp2_priority_spec_init(&ps, stream->id, w_parent, 0);
1217 id_grandpa = nghttp2_stream_get_stream_id(s_grandpa);
1218 rv = nghttp2_session_change_stream_priority(session->ngh2, id_parent, &ps);
1220 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03202)
1221 "h2_stream(%ld-%d): PUSH BEFORE, weight=%d, "
1222 "depends=%d, returned=%d",
1223 session->id, id_parent, ps.weight, ps.stream_id, rv);
1224 return APR_EGENERAL;
1226 nghttp2_priority_spec_init(&ps, id_grandpa, w, 0);
1229 case H2_DEPENDANT_AFTER:
1230 /* The PUSHed stream is to be sent after the initiating stream.
1231 * Give if the specified weight and let it depend on the intiating
1234 /* fall through, it's the default */
1236 nghttp2_priority_spec_init(&ps, id_parent, valid_weight(prio->weight), 0);
1241 rv = nghttp2_session_change_stream_priority(session->ngh2, stream->id, &ps);
1242 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
1243 H2_STRM_LOG(APLOGNO(03203), stream,
1244 "PUSH %s, weight=%d, depends=%d, returned=%d"),
1245 ptype, ps.weight, ps.stream_id, rv);
1246 status = (rv < 0)? APR_EGENERAL : APR_SUCCESS;
1257 apr_status_t h2_session_push(h2_session *session, int initiating_stream_id, h2_push *push)
1264 pool = session_stream_pool_create(session);
1265 if (APR_SUCCESS != h2_req_create_ngheader(&ngh, pool, push->req)) goto fail;
1267 nid = nghttp2_submit_push_promise(session->ngh2, 0, initiating_stream_id,
1268 ngh->nv, ngh->nvlen, NULL);
1270 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
1271 APLOGNO(03075) "submitting push promise fail: %s", nghttp2_strerror(nid));
1275 ++session->pushes_promised;
1276 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
1277 APLOGNO(03076) "SERVER_PUSH %d for %s %s on %d",
1278 nid, push->req->method, push->req->path, initiating_stream_id);
1280 stream = session_stream_pcreate(session, nid, pool, initiating_stream_id);
1282 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
1283 H2_STRM_LOG(APLOGNO(03077), stream,
1284 "failed to create stream obj %d"), nid);
1288 session_stream_priority_set(session, stream, push->priority);
1289 h2_stream_request_set(stream, push->req);
1290 ++session->unsent_promises;
1291 h2_mplx_stream_register(session->mplx, stream);
1296 nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE, nid, NGHTTP2_INTERNAL_ERROR);
1298 if (pool) apr_pool_destroy(pool);
1302 int h2_session_push_enabled(h2_session *session)
1304 /* iff we can and they can and want */
1305 return (session->remote.accepting /* remote GOAWAY received */
1306 && h2_config_sgeti(session->s, H2_CONF_PUSH)
1307 && nghttp2_session_get_remote_settings(session->ngh2,
1308 NGHTTP2_SETTINGS_ENABLE_PUSH));
1311 static apr_status_t h2_session_send(h2_session *session)
1313 apr_interval_time_t saved_timeout;
1315 apr_socket_t *socket;
1317 socket = ap_get_conn_socket(session->c);
1319 apr_socket_timeout_get(socket, &saved_timeout);
1320 apr_socket_timeout_set(socket, session->s->timeout);
1323 rv = nghttp2_session_send(session->ngh2);
1326 apr_socket_timeout_set(socket, saved_timeout);
1328 session->have_written = 1;
1329 if (rv != 0 && rv != NGHTTP2_ERR_WOULDBLOCK) {
1330 if (nghttp2_is_fatal(rv)) {
1331 dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, rv, nghttp2_strerror(rv));
1332 return APR_EGENERAL;
1336 session->unsent_promises = 0;
1337 session->unsent_submits = 0;
1343 * headers for the stream are ready.
1345 static apr_status_t on_stream_headers(h2_session *session, h2_stream *stream,
1346 h2_headers *headers, apr_off_t len,
1349 apr_status_t status = APR_SUCCESS;
1354 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
1355 H2_STRM_MSG(stream, "on_headers"));
1356 if (headers->status < 100) {
1357 h2_stream_rst(stream, headers->status);
1360 else if (stream->has_response) {
1363 status = h2_res_create_ngtrailer(&nh, stream->pool, headers);
1365 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
1366 H2_STRM_LOG(APLOGNO(03072), stream, "submit %d trailers"),
1368 if (status == APR_SUCCESS) {
1369 rv = nghttp2_submit_trailer(session->ngh2, stream->id,
1373 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
1374 H2_STRM_LOG(APLOGNO(10024), stream, "invalid trailers"));
1375 h2_stream_rst(stream, NGHTTP2_PROTOCOL_ERROR);
1380 nghttp2_data_provider provider, *pprovider = NULL;
1384 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
1385 H2_STRM_LOG(APLOGNO(03073), stream, "submit response %d, REMOTE_WINDOW_SIZE=%u"),
1387 (unsigned int)nghttp2_session_get_stream_remote_window_size(session->ngh2, stream->id));
1389 if (!eos || len > 0) {
1390 memset(&provider, 0, sizeof(provider));
1391 provider.source.fd = stream->id;
1392 provider.read_callback = stream_data_cb;
1393 pprovider = &provider;
1396 /* If this stream is not a pushed one itself,
1397 * and HTTP/2 server push is enabled here,
1398 * and the response HTTP status is not sth >= 400,
1399 * and the remote side has pushing enabled,
1400 * -> find and perform any pushes on this stream
1401 * *before* we submit the stream response itself.
1402 * This helps clients avoid opening new streams on Link
1403 * headers that get pushed right afterwards.
1405 * *) the response code is relevant, as we do not want to
1406 * make pushes on 401 or 403 codes and friends.
1407 * And if we see a 304, we do not push either
1408 * as the client, having this resource in its cache, might
1409 * also have the pushed ones as well.
1411 if (!stream->initiated_on
1412 && !stream->has_response
1413 && stream->request && stream->request->method
1414 && !strcmp("GET", stream->request->method)
1415 && (headers->status < 400)
1416 && (headers->status != 304)
1417 && h2_session_push_enabled(session)) {
1418 /* PUSH is possibe and enabled on server, unless the request
1419 * denies it, submit resources to push */
1420 s = apr_table_get(headers->notes, H2_PUSH_MODE_NOTE);
1421 if (!s || strcmp(s, "0")) {
1422 h2_stream_submit_pushes(stream, headers);
1426 if (!stream->pref_priority) {
1427 stream->pref_priority = h2_stream_get_priority(stream, headers);
1429 session_stream_priority_set(session, stream, stream->pref_priority);
1431 note = apr_table_get(headers->notes, H2_FILTER_DEBUG_NOTE);
1432 if (note && !strcmp("on", note)) {
1433 int32_t connFlowIn, connFlowOut;
1435 connFlowIn = nghttp2_session_get_effective_local_window_size(session->ngh2);
1436 connFlowOut = nghttp2_session_get_remote_window_size(session->ngh2);
1437 headers = h2_headers_copy(stream->pool, headers);
1438 apr_table_setn(headers->headers, "conn-flow-in",
1439 apr_itoa(stream->pool, connFlowIn));
1440 apr_table_setn(headers->headers, "conn-flow-out",
1441 apr_itoa(stream->pool, connFlowOut));
1444 if (headers->status == 103
1445 && !h2_config_sgeti(session->s, H2_CONF_EARLY_HINTS)) {
1446 /* suppress sending this to the client, it might have triggered
1447 * pushes and served its purpose nevertheless */
1452 status = h2_res_create_ngheader(&ngh, stream->pool, headers);
1453 if (status == APR_SUCCESS) {
1454 rv = nghttp2_submit_response(session->ngh2, stream->id,
1455 ngh->nv, ngh->nvlen, pprovider);
1456 stream->has_response = h2_headers_are_response(headers);
1457 session->have_written = 1;
1459 if (stream->initiated_on) {
1460 ++session->pushes_submitted;
1463 ++session->responses_submitted;
1467 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
1468 H2_STRM_LOG(APLOGNO(10025), stream, "invalid response"));
1469 h2_stream_rst(stream, NGHTTP2_PROTOCOL_ERROR);
1474 if (nghttp2_is_fatal(rv)) {
1475 status = APR_EGENERAL;
1476 dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, rv, nghttp2_strerror(rv));
1477 ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
1478 APLOGNO(02940) "submit_response: %s",
1479 nghttp2_strerror(rv));
1482 ++session->unsent_submits;
1484 /* Unsent push promises are written immediately, as nghttp2
1485 * 1.5.0 realizes internal stream data structures only on
1486 * send and we might need them for other submits.
1487 * Also, to conserve memory, we send at least every 10 submits
1488 * so that nghttp2 does not buffer all outbound items too
1491 if (status == APR_SUCCESS
1492 && (session->unsent_promises || session->unsent_submits > 10)) {
1493 status = h2_session_send(session);
1499 * A stream was resumed as new response/output data arrived.
1501 static apr_status_t on_stream_resume(void *ctx, h2_stream *stream)
1503 h2_session *session = ctx;
1504 apr_status_t status = APR_EAGAIN;
1508 h2_headers *headers;
1511 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
1512 H2_STRM_MSG(stream, "on_resume"));
1516 status = h2_stream_out_prepare(stream, &len, &eos, &headers);
1517 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, session->c,
1518 H2_STRM_MSG(stream, "prepared len=%ld, eos=%d"),
1521 status = on_stream_headers(session, stream, headers, len, eos);
1522 if (status != APR_SUCCESS || stream->rst_error) {
1527 else if (status != APR_EAGAIN) {
1528 /* we have DATA to send */
1529 if (!stream->has_response) {
1530 /* but no response */
1531 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
1532 H2_STRM_LOG(APLOGNO(03466), stream,
1533 "no response, RST_STREAM"));
1534 h2_stream_rst(stream, H2_ERR_PROTOCOL_ERROR);
1537 rv = nghttp2_session_resume_data(session->ngh2, stream->id);
1538 session->have_written = 1;
1539 ap_log_cerror(APLOG_MARK, nghttp2_is_fatal(rv)?
1540 APLOG_ERR : APLOG_DEBUG, 0, session->c,
1541 H2_STRM_LOG(APLOGNO(02936), stream, "resumed"));
1546 static void h2_session_in_flush(h2_session *session)
1550 while ((id = h2_iq_shift(session->in_process)) > 0) {
1551 h2_stream *stream = get_stream(session, id);
1553 if (h2_stream_prep_processing(stream) == APR_SUCCESS) {
1554 h2_mplx_process(session->mplx, id, stream_pri_cmp, session);
1557 h2_stream_rst(stream, H2_ERR_INTERNAL_ERROR);
1562 while ((id = h2_iq_shift(session->in_pending)) > 0) {
1563 h2_stream *stream = get_stream(session, id);
1565 h2_stream_flush_input(stream);
1570 static apr_status_t session_read(h2_session *session, apr_size_t readlen, int block)
1572 apr_status_t status, rstatus = APR_EAGAIN;
1573 conn_rec *c = session->c;
1574 apr_off_t read_start = session->io.bytes_read;
1577 /* H2_IN filter handles all incoming data against the session.
1578 * We just pull at the filter chain to make it happen */
1579 status = ap_get_brigade(c->input_filters,
1580 session->bbtmp, AP_MODE_READBYTES,
1581 block? APR_BLOCK_READ : APR_NONBLOCK_READ,
1582 H2MAX(APR_BUCKET_BUFF_SIZE, readlen));
1583 /* get rid of any possible data we do not expect to get */
1584 apr_brigade_cleanup(session->bbtmp);
1588 /* successful read, reset our idle timers */
1589 rstatus = APR_SUCCESS;
1591 /* successful blocked read, try unblocked to
1601 if (session->io.bytes_read == read_start) {
1602 /* first attempt failed */
1603 if (APR_STATUS_IS_ETIMEDOUT(status)
1604 || APR_STATUS_IS_ECONNABORTED(status)
1605 || APR_STATUS_IS_ECONNRESET(status)
1606 || APR_STATUS_IS_EOF(status)
1607 || APR_STATUS_IS_EBADF(status)) {
1608 /* common status for a client that has left */
1609 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, c,
1610 H2_SSSN_MSG(session, "input gone"));
1613 /* uncommon status, log on INFO so that we see this */
1614 ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, c,
1615 H2_SSSN_LOG(APLOGNO(02950), session,
1616 "error reading, terminating"));
1620 /* subsequent failure after success(es), return initial
1624 if ((session->io.bytes_read - read_start) > readlen) {
1625 /* read enough in one go, give write a chance */
1626 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, c,
1627 H2_SSSN_MSG(session, "read enough, returning"));
1634 static apr_status_t h2_session_read(h2_session *session, int block)
1636 apr_status_t status = session_read(session, session->max_stream_mem
1637 * H2MAX(2, session->open_streams),
1639 h2_session_in_flush(session);
1643 static const char *StateNames[] = {
1644 "INIT", /* H2_SESSION_ST_INIT */
1645 "DONE", /* H2_SESSION_ST_DONE */
1646 "IDLE", /* H2_SESSION_ST_IDLE */
1647 "BUSY", /* H2_SESSION_ST_BUSY */
1648 "WAIT", /* H2_SESSION_ST_WAIT */
1649 "CLEANUP", /* H2_SESSION_ST_CLEANUP */
1652 const char *h2_session_state_str(h2_session_state state)
1654 if (state >= (sizeof(StateNames)/sizeof(StateNames[0]))) {
1657 return StateNames[state];
1660 static void update_child_status(h2_session *session, int status, const char *msg)
1662 /* Assume that we also change code/msg when something really happened and
1663 * avoid updating the scoreboard in between */
1664 if (session->last_status_code != status
1665 || session->last_status_msg != msg) {
1666 apr_snprintf(session->status, sizeof(session->status),
1667 "%s, streams: %d/%d/%d/%d/%d (open/recv/resp/push/rst)",
1669 (int)session->open_streams,
1670 (int)session->remote.emitted_count,
1671 (int)session->responses_submitted,
1672 (int)session->pushes_submitted,
1673 (int)session->pushes_reset + session->streams_reset);
1674 ap_update_child_status_descr(session->c->sbh, status, session->status);
1678 static void transit(h2_session *session, const char *action, h2_session_state nstate)
1684 if (session->state != nstate) {
1685 ostate = session->state;
1686 session->state = nstate;
1688 loglvl = APLOG_DEBUG;
1689 if ((ostate == H2_SESSION_ST_BUSY && nstate == H2_SESSION_ST_WAIT)
1690 || (ostate == H2_SESSION_ST_WAIT && nstate == H2_SESSION_ST_BUSY)){
1691 loglvl = APLOG_TRACE1;
1693 ap_log_cerror(APLOG_MARK, loglvl, 0, session->c,
1694 H2_SSSN_LOG(APLOGNO(03078), session,
1695 "transit [%s] -- %s --> [%s]"),
1696 h2_session_state_str(ostate), action,
1697 h2_session_state_str(nstate));
1699 switch (session->state) {
1700 case H2_SESSION_ST_IDLE:
1701 if (!session->remote.emitted_count) {
1702 /* on fresh connections, with async mpm, do not return
1703 * to mpm for a second. This gives the first request a better
1704 * chance to arrive (und connection leaving IDLE state).
1705 * If we return to mpm right away, this connection has the
1706 * same chance of being cleaned up by the mpm as connections
1707 * that already served requests - not fair. */
1708 session->idle_sync_until = apr_time_now() + apr_time_from_sec(1);
1710 timeout = H2MAX(session->s->timeout, session->s->keep_alive_timeout);
1711 update_child_status(session, SERVER_BUSY_READ, "idle");
1712 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
1713 H2_SSSN_LOG("", session, "enter idle, timeout = %d sec"),
1714 (int)apr_time_sec(H2MAX(session->s->timeout, session->s->keep_alive_timeout)));
1716 else if (session->open_streams) {
1718 timeout = session->s->keep_alive_timeout;
1719 update_child_status(session, SERVER_BUSY_KEEPALIVE, "idle");
1722 /* normal keepalive setup */
1724 timeout = session->s->keep_alive_timeout;
1725 update_child_status(session, SERVER_BUSY_KEEPALIVE, "idle");
1727 session->idle_until = apr_time_now() + timeout;
1728 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
1729 H2_SSSN_LOG("", session, "enter idle, %s = %d sec"),
1730 s, (int)apr_time_sec(timeout));
1732 case H2_SESSION_ST_DONE:
1733 update_child_status(session, SERVER_CLOSING, "done");
1742 static void h2_session_ev_init(h2_session *session, int arg, const char *msg)
1744 switch (session->state) {
1745 case H2_SESSION_ST_INIT:
1746 transit(session, "init", H2_SESSION_ST_BUSY);
1754 static void h2_session_ev_local_goaway(h2_session *session, int arg, const char *msg)
1756 cleanup_unprocessed_streams(session);
1757 if (!session->remote.shutdown) {
1758 update_child_status(session, SERVER_CLOSING, "local goaway");
1760 transit(session, "local goaway", H2_SESSION_ST_DONE);
1763 static void h2_session_ev_remote_goaway(h2_session *session, int arg, const char *msg)
1765 if (!session->remote.shutdown) {
1766 session->remote.error = arg;
1767 session->remote.accepting = 0;
1768 session->remote.shutdown = 1;
1769 cleanup_unprocessed_streams(session);
1770 update_child_status(session, SERVER_CLOSING, "remote goaway");
1771 transit(session, "remote goaway", H2_SESSION_ST_DONE);
1775 static void h2_session_ev_conn_error(h2_session *session, int arg, const char *msg)
1777 switch (session->state) {
1778 case H2_SESSION_ST_INIT:
1779 case H2_SESSION_ST_DONE:
1781 transit(session, "conn error", H2_SESSION_ST_DONE);
1785 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
1786 H2_SSSN_LOG(APLOGNO(03401), session,
1787 "conn error -> shutdown"));
1788 h2_session_shutdown(session, arg, msg, 0);
1793 static void h2_session_ev_proto_error(h2_session *session, int arg, const char *msg)
1795 if (!session->local.shutdown) {
1796 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
1797 H2_SSSN_LOG(APLOGNO(03402), session,
1798 "proto error -> shutdown"));
1799 h2_session_shutdown(session, arg, msg, 0);
1803 static void h2_session_ev_conn_timeout(h2_session *session, int arg, const char *msg)
1805 transit(session, msg, H2_SESSION_ST_DONE);
1806 if (!session->local.shutdown) {
1807 h2_session_shutdown(session, arg, msg, 1);
1811 static void h2_session_ev_no_io(h2_session *session, int arg, const char *msg)
1813 switch (session->state) {
1814 case H2_SESSION_ST_BUSY:
1815 /* Nothing to READ, nothing to WRITE on the master connection.
1817 * - we wait for the client to send us sth
1818 * - we wait for started tasks to produce output
1819 * - we have finished all streams and the client has sent GO_AWAY
1821 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
1822 H2_SSSN_MSG(session, "NO_IO event, %d streams open"),
1823 session->open_streams);
1824 h2_conn_io_flush(&session->io);
1825 if (session->open_streams > 0) {
1826 if (h2_mplx_awaits_data(session->mplx)) {
1827 /* waiting for at least one stream to produce data */
1828 transit(session, "no io", H2_SESSION_ST_WAIT);
1831 /* we have streams open, and all are submitted and none
1832 * is suspended. The only thing keeping us from WRITEing
1833 * more must be the flow control.
1834 * This means we only wait for WINDOW_UPDATE from the
1835 * client and can block on READ. */
1836 transit(session, "no io (flow wait)", H2_SESSION_ST_IDLE);
1837 /* Make sure we have flushed all previously written output
1838 * so that the client will react. */
1839 if (h2_conn_io_flush(&session->io) != APR_SUCCESS) {
1840 dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
1845 else if (session->local.accepting) {
1846 /* When we have no streams, but accept new, switch to idle */
1847 transit(session, "no io (keepalive)", H2_SESSION_ST_IDLE);
1850 /* We are no longer accepting new streams and there are
1851 * none left. Time to leave. */
1852 h2_session_shutdown(session, arg, msg, 0);
1853 transit(session, "no io", H2_SESSION_ST_DONE);
1862 static void h2_session_ev_frame_rcvd(h2_session *session, int arg, const char *msg)
1864 switch (session->state) {
1865 case H2_SESSION_ST_IDLE:
1866 case H2_SESSION_ST_WAIT:
1867 transit(session, "frame received", H2_SESSION_ST_BUSY);
1875 static void h2_session_ev_stream_change(h2_session *session, int arg, const char *msg)
1877 switch (session->state) {
1878 case H2_SESSION_ST_IDLE:
1879 case H2_SESSION_ST_WAIT:
1880 transit(session, "stream change", H2_SESSION_ST_BUSY);
1888 static void h2_session_ev_ngh2_done(h2_session *session, int arg, const char *msg)
1890 switch (session->state) {
1891 case H2_SESSION_ST_DONE:
1895 transit(session, "nghttp2 done", H2_SESSION_ST_DONE);
1900 static void h2_session_ev_mpm_stopping(h2_session *session, int arg, const char *msg)
1902 switch (session->state) {
1903 case H2_SESSION_ST_DONE:
1907 h2_session_shutdown_notice(session);
1912 static void h2_session_ev_pre_close(h2_session *session, int arg, const char *msg)
1914 h2_session_shutdown(session, arg, msg, 1);
1917 static void ev_stream_open(h2_session *session, h2_stream *stream)
1919 h2_iq_append(session->in_process, stream->id);
1922 void h2_session_eos_sent(h2_session *session, int stream_id)
1924 /* stream may no longer be known by nghttp2, but still kept in mplx */
1925 h2_stream *stream = h2_mplx_stream_get(session->mplx, stream_id);
1927 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
1928 H2_STRM_MSG(stream, "eos sent"));
1929 h2_stream_dispatch(stream, H2_SEV_EOS_SENT);
1932 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
1933 "eos sent for unknown stream %d", stream_id);
1937 static void ev_stream_closed(h2_session *session, h2_stream *stream)
1941 if (H2_STREAM_CLIENT_INITIATED(stream->id)
1942 && (stream->id > session->local.completed_max)) {
1943 session->local.completed_max = stream->id;
1945 switch (session->state) {
1946 case H2_SESSION_ST_IDLE:
1952 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
1953 H2_STRM_MSG(stream, "sending eos"));
1954 /* The stream might have data in the buffers of the main connection.
1955 * We can only free the allocated resources once all had been written.
1956 * Send a special buckets on the connection that gets destroyed when
1957 * all preceding data has been handled. On its destruction, it is safe
1958 * to purge all resources of the stream. */
1959 b = h2_bucket_eos_create(session->c->bucket_alloc, session->c, stream->id);
1960 APR_BRIGADE_INSERT_TAIL(session->bbtmp, b);
1961 h2_conn_io_pass(&session->io, session->bbtmp);
1962 apr_brigade_cleanup(session->bbtmp);
1965 static void on_stream_state_enter(void *ctx, h2_stream *stream)
1967 h2_session *session = ctx;
1968 /* stream entered a new state */
1969 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
1970 H2_STRM_MSG(stream, "entered state"));
1971 switch (stream->state) {
1972 case H2_SS_IDLE: /* stream was created */
1973 ++session->open_streams;
1974 if (H2_STREAM_CLIENT_INITIATED(stream->id)) {
1975 ++session->remote.emitted_count;
1976 if (stream->id > session->remote.emitted_max) {
1977 session->remote.emitted_max = stream->id;
1978 session->local.accepted_max = stream->id;
1982 if (stream->id > session->local.emitted_max) {
1983 ++session->local.emitted_count;
1984 session->remote.emitted_max = stream->id;
1988 case H2_SS_OPEN: /* stream has request headers */
1989 case H2_SS_RSVD_L: /* stream has request headers */
1990 ev_stream_open(session, stream);
1992 case H2_SS_CLOSED_L: /* stream output was closed */
1994 case H2_SS_CLOSED_R: /* stream input was closed */
1996 case H2_SS_CLOSED: /* stream in+out were closed */
1997 --session->open_streams;
1998 ev_stream_closed(session, stream);
2001 h2_mplx_stream_discard(session->mplx, stream->id);
2006 dispatch_event(session, H2_SESSION_EV_STREAM_CHANGE, 0, "stream state change");
2009 static void on_stream_event(void *ctx, h2_stream *stream,
2010 h2_stream_event_t ev)
2012 h2_session *session = ctx;
2014 case H2_SEV_IN_DATA_PENDING:
2015 h2_iq_append(session->in_pending, stream->id);
2023 static void on_stream_state_event(void *ctx, h2_stream *stream,
2024 h2_stream_event_t ev)
2026 h2_session *session = ctx;
2028 case H2_SEV_CANCELLED:
2029 if (session->state != H2_SESSION_ST_DONE) {
2030 nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE,
2031 stream->id, stream->rst_error);
2040 static void dispatch_event(h2_session *session, h2_session_event_t ev,
2041 int arg, const char *msg)
2044 case H2_SESSION_EV_INIT:
2045 h2_session_ev_init(session, arg, msg);
2047 case H2_SESSION_EV_LOCAL_GOAWAY:
2048 h2_session_ev_local_goaway(session, arg, msg);
2050 case H2_SESSION_EV_REMOTE_GOAWAY:
2051 h2_session_ev_remote_goaway(session, arg, msg);
2053 case H2_SESSION_EV_CONN_ERROR:
2054 h2_session_ev_conn_error(session, arg, msg);
2056 case H2_SESSION_EV_PROTO_ERROR:
2057 h2_session_ev_proto_error(session, arg, msg);
2059 case H2_SESSION_EV_CONN_TIMEOUT:
2060 h2_session_ev_conn_timeout(session, arg, msg);
2062 case H2_SESSION_EV_NO_IO:
2063 h2_session_ev_no_io(session, arg, msg);
2065 case H2_SESSION_EV_FRAME_RCVD:
2066 h2_session_ev_frame_rcvd(session, arg, msg);
2068 case H2_SESSION_EV_NGH2_DONE:
2069 h2_session_ev_ngh2_done(session, arg, msg);
2071 case H2_SESSION_EV_MPM_STOPPING:
2072 h2_session_ev_mpm_stopping(session, arg, msg);
2074 case H2_SESSION_EV_PRE_CLOSE:
2075 h2_session_ev_pre_close(session, arg, msg);
2077 case H2_SESSION_EV_STREAM_CHANGE:
2078 h2_session_ev_stream_change(session, arg, msg);
2081 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
2082 H2_SSSN_MSG(session, "unknown event %d"), ev);
2087 /* trigger window updates, stream resumes and submits */
2088 static apr_status_t dispatch_master(h2_session *session) {
2089 apr_status_t status;
2091 status = h2_mplx_dispatch_master_events(session->mplx,
2092 on_stream_resume, session);
2093 if (status == APR_EAGAIN) {
2094 ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, session->c,
2095 H2_SSSN_MSG(session, "no master event available"));
2097 else if (status != APR_SUCCESS) {
2098 ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, session->c,
2099 H2_SSSN_MSG(session, "dispatch error"));
2100 dispatch_event(session, H2_SESSION_EV_CONN_ERROR,
2101 H2_ERR_INTERNAL_ERROR, "dispatch error");
2106 static const int MAX_WAIT_MICROS = 200 * 1000;
2108 apr_status_t h2_session_process(h2_session *session, int async)
2110 apr_status_t status = APR_SUCCESS;
2111 conn_rec *c = session->c;
2112 int rv, mpm_state, trace = APLOGctrace3(c);
2116 ap_log_cerror( APLOG_MARK, APLOG_TRACE3, status, c,
2117 H2_SSSN_MSG(session, "process start, async=%d"), async);
2120 while (session->state != H2_SESSION_ST_DONE) {
2121 now = apr_time_now();
2122 session->have_read = session->have_written = 0;
2124 if (session->local.accepting
2125 && !ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
2126 if (mpm_state == AP_MPMQ_STOPPING) {
2127 dispatch_event(session, H2_SESSION_EV_MPM_STOPPING, 0, NULL);
2131 session->status[0] = '\0';
2133 switch (session->state) {
2134 case H2_SESSION_ST_INIT:
2135 ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_READ, c);
2136 if (!h2_is_acceptable_connection(c, session->r, 1)) {
2137 update_child_status(session, SERVER_BUSY_READ,
2138 "inadequate security");
2139 h2_session_shutdown(session,
2140 NGHTTP2_INADEQUATE_SECURITY, NULL, 1);
2143 update_child_status(session, SERVER_BUSY_READ, "init");
2144 status = h2_session_start(session, &rv);
2145 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
2146 H2_SSSN_LOG(APLOGNO(03079), session,
2147 "started on %s:%d"),
2148 session->s->server_hostname,
2149 c->local_addr->port);
2150 if (status != APR_SUCCESS) {
2151 dispatch_event(session,
2152 H2_SESSION_EV_CONN_ERROR, 0, NULL);
2154 dispatch_event(session, H2_SESSION_EV_INIT, 0, NULL);
2158 case H2_SESSION_ST_IDLE:
2159 if (session->idle_until && (apr_time_now() + session->idle_delay) > session->idle_until) {
2160 ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
2161 H2_SSSN_MSG(session, "idle, timeout reached, closing"));
2162 if (session->idle_delay) {
2163 apr_table_setn(session->c->notes, "short-lingering-close", "1");
2165 dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, "timeout");
2169 if (session->idle_delay) {
2170 /* we are less interested in spending time on this connection */
2171 ap_log_cerror( APLOG_MARK, APLOG_TRACE2, status, c,
2172 H2_SSSN_MSG(session, "session is idle (%ld ms), idle wait %ld sec left"),
2173 (long)apr_time_as_msec(session->idle_delay),
2174 (long)apr_time_sec(session->idle_until - now));
2175 apr_sleep(session->idle_delay);
2176 session->idle_delay = 0;
2179 h2_conn_io_flush(&session->io);
2180 if (async && !session->r && (now > session->idle_sync_until)) {
2182 ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, c,
2183 H2_SSSN_MSG(session,
2184 "nonblock read, %d streams open"),
2185 session->open_streams);
2187 status = h2_session_read(session, 0);
2189 if (status == APR_SUCCESS) {
2190 session->have_read = 1;
2192 else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) {
2193 status = APR_EAGAIN;
2197 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
2198 H2_SSSN_LOG(APLOGNO(03403), session,
2200 dispatch_event(session,
2201 H2_SESSION_EV_CONN_ERROR, 0, "timeout");
2205 /* make certain, we send everything before we idle */
2207 ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, c,
2208 H2_SSSN_MSG(session,
2209 "sync, stutter 1-sec, %d streams open"),
2210 session->open_streams);
2212 /* We wait in smaller increments, using a 1 second timeout.
2213 * That gives us the chance to check for MPMQ_STOPPING often.
2215 status = h2_mplx_idle(session->mplx);
2216 if (status == APR_EAGAIN) {
2219 else if (status != APR_SUCCESS) {
2220 dispatch_event(session, H2_SESSION_EV_CONN_ERROR,
2221 H2_ERR_ENHANCE_YOUR_CALM, "less is more");
2223 h2_filter_cin_timeout_set(session->cin, apr_time_from_sec(1));
2224 status = h2_session_read(session, 1);
2225 if (status == APR_SUCCESS) {
2226 session->have_read = 1;
2228 else if (status == APR_EAGAIN) {
2229 /* nothing to read */
2231 else if (APR_STATUS_IS_TIMEUP(status)) {
2232 /* continue reading handling */
2234 else if (APR_STATUS_IS_ECONNABORTED(status)
2235 || APR_STATUS_IS_ECONNRESET(status)
2236 || APR_STATUS_IS_EOF(status)
2237 || APR_STATUS_IS_EBADF(status)) {
2238 ap_log_cerror( APLOG_MARK, APLOG_TRACE3, status, c,
2239 H2_SSSN_MSG(session, "input gone"));
2240 dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
2243 ap_log_cerror( APLOG_MARK, APLOG_TRACE3, status, c,
2244 H2_SSSN_MSG(session,
2245 "(1 sec timeout) read failed"));
2246 dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, "error");
2249 if (nghttp2_session_want_write(session->ngh2)) {
2250 ap_update_child_status(session->c->sbh, SERVER_BUSY_WRITE, NULL);
2251 status = h2_session_send(session);
2252 if (status == APR_SUCCESS) {
2253 status = h2_conn_io_flush(&session->io);
2255 if (status != APR_SUCCESS) {
2256 dispatch_event(session, H2_SESSION_EV_CONN_ERROR,
2257 H2_ERR_INTERNAL_ERROR, "writing");
2263 case H2_SESSION_ST_BUSY:
2264 if (nghttp2_session_want_read(session->ngh2)) {
2265 ap_update_child_status(session->c->sbh, SERVER_BUSY_READ, NULL);
2266 h2_filter_cin_timeout_set(session->cin, session->s->timeout);
2267 status = h2_session_read(session, 0);
2268 if (status == APR_SUCCESS) {
2269 session->have_read = 1;
2271 else if (status == APR_EAGAIN) {
2272 /* nothing to read */
2274 else if (APR_STATUS_IS_TIMEUP(status)) {
2275 dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
2279 dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
2283 status = dispatch_master(session);
2284 if (status != APR_SUCCESS && status != APR_EAGAIN) {
2288 if (nghttp2_session_want_write(session->ngh2)) {
2289 ap_update_child_status(session->c->sbh, SERVER_BUSY_WRITE, NULL);
2290 status = h2_session_send(session);
2291 if (status == APR_SUCCESS) {
2292 status = h2_conn_io_flush(&session->io);
2294 if (status != APR_SUCCESS) {
2295 dispatch_event(session, H2_SESSION_EV_CONN_ERROR,
2296 H2_ERR_INTERNAL_ERROR, "writing");
2301 if (session->have_read || session->have_written) {
2302 if (session->wait_us) {
2303 session->wait_us = 0;
2306 else if (!nghttp2_session_want_write(session->ngh2)) {
2307 dispatch_event(session, H2_SESSION_EV_NO_IO, 0, NULL);
2311 case H2_SESSION_ST_WAIT:
2312 if (session->wait_us <= 0) {
2313 session->wait_us = 10;
2314 if (h2_conn_io_flush(&session->io) != APR_SUCCESS) {
2315 dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
2320 /* repeating, increase timer for graceful backoff */
2321 session->wait_us = H2MIN(session->wait_us*2, MAX_WAIT_MICROS);
2325 ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c,
2326 "h2_session: wait for data, %ld micros",
2327 (long)session->wait_us);
2329 status = h2_mplx_out_trywait(session->mplx, session->wait_us,
2331 if (status == APR_SUCCESS) {
2332 session->wait_us = 0;
2333 dispatch_event(session, H2_SESSION_EV_STREAM_CHANGE, 0, NULL);
2335 else if (APR_STATUS_IS_TIMEUP(status)) {
2336 /* go back to checking all inputs again */
2337 transit(session, "wait cycle", session->local.shutdown?
2338 H2_SESSION_ST_DONE : H2_SESSION_ST_BUSY);
2340 else if (APR_STATUS_IS_ECONNRESET(status)
2341 || APR_STATUS_IS_ECONNABORTED(status)) {
2342 dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
2345 ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, c,
2346 H2_SSSN_LOG(APLOGNO(03404), session,
2347 "waiting on conditional"));
2348 h2_session_shutdown(session, H2_ERR_INTERNAL_ERROR,
2349 "cond wait error", 0);
2354 ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
2355 H2_SSSN_LOG(APLOGNO(03080), session,
2357 dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, 0, NULL);
2361 if (!nghttp2_session_want_read(session->ngh2)
2362 && !nghttp2_session_want_write(session->ngh2)) {
2363 dispatch_event(session, H2_SESSION_EV_NGH2_DONE, 0, NULL);
2365 if (session->reprioritize) {
2366 h2_mplx_reprioritize(session->mplx, stream_pri_cmp, session);
2367 session->reprioritize = 0;
2373 ap_log_cerror( APLOG_MARK, APLOG_TRACE3, status, c,
2374 H2_SSSN_MSG(session, "process returns"));
2377 if ((session->state != H2_SESSION_ST_DONE)
2378 && (APR_STATUS_IS_EOF(status)
2379 || APR_STATUS_IS_ECONNRESET(status)
2380 || APR_STATUS_IS_ECONNABORTED(status))) {
2381 dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
2384 return (session->state == H2_SESSION_ST_DONE)? APR_EOF : APR_SUCCESS;
2387 apr_status_t h2_session_pre_close(h2_session *session, int async)
2389 apr_status_t status;
2391 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
2392 H2_SSSN_MSG(session, "pre_close"));
2393 dispatch_event(session, H2_SESSION_EV_PRE_CLOSE, 0,
2394 (session->state == H2_SESSION_ST_IDLE)? "timeout" : NULL);
2395 status = session_cleanup(session, "pre_close");
2396 if (status == APR_SUCCESS) {
2397 /* no one should hold a reference to this session any longer and
2398 * the h2_ctx was removed from the connection.
2399 * Take the pool (and thus all subpools etc. down now, instead of
2400 * during cleanup of main connection pool. */
2401 apr_pool_destroy(session->pool);