1 /* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
17 #include <apr_thread_cond.h>
18 #include <apr_base64.h>
19 #include <apr_strings.h>
24 #include <http_core.h>
25 #include <http_config.h>
27 #include <scoreboard.h>
29 #include "h2_private.h"
30 #include "h2_bucket_eoc.h"
31 #include "h2_bucket_eos.h"
32 #include "h2_config.h"
34 #include "h2_filter.h"
38 #include "h2_request.h"
39 #include "h2_response.h"
40 #include "h2_stream.h"
41 #include "h2_stream_set.h"
42 #include "h2_from_h1.h"
44 #include "h2_session.h"
46 #include "h2_version.h"
47 #include "h2_workers.h"
50 static int frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen);
52 static int h2_session_status_from_apr_status(apr_status_t rv)
54 if (rv == APR_SUCCESS) {
55 return NGHTTP2_NO_ERROR;
57 else if (APR_STATUS_IS_EAGAIN(rv)) {
58 return NGHTTP2_ERR_WOULDBLOCK;
60 else if (APR_STATUS_IS_EOF(rv)) {
61 return NGHTTP2_ERR_EOF;
63 return NGHTTP2_ERR_PROTO;
66 static void update_window(void *ctx, int stream_id, apr_off_t bytes_read)
68 h2_session *session = (h2_session*)ctx;
69 nghttp2_session_consume(session->ngh2, stream_id, bytes_read);
70 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
71 "h2_session(%ld-%d): consumed %ld bytes",
72 session->id, stream_id, (long)bytes_read);
75 static apr_status_t h2_session_receive(void *ctx,
76 const char *data, apr_size_t len,
79 static int is_accepting_streams(h2_session *session);
80 static void dispatch_event(h2_session *session, h2_session_event_t ev,
81 int err, const char *msg);
83 h2_stream *h2_session_open_stream(h2_session *session, int stream_id)
86 apr_pool_t *stream_pool;
89 stream_pool = session->spare;
90 session->spare = NULL;
93 apr_pool_create(&stream_pool, session->pool);
96 stream = h2_stream_open(stream_id, stream_pool, session);
98 h2_stream_set_add(session->streams, stream);
99 if (H2_STREAM_CLIENT_INITIATED(stream_id)
100 && stream_id > session->max_stream_received) {
101 ++session->requests_received;
102 session->max_stream_received = stream->id;
108 #ifdef H2_NG2_STREAM_API
111 * Determine the importance of streams when scheduling tasks.
112 * - if both stream depend on the same one, compare weights
113 * - if one stream is closer to the root, prioritize that one
114 * - if both are on the same level, use the weight of their root
117 static int spri_cmp(int sid1, nghttp2_stream *s1,
118 int sid2, nghttp2_stream *s2, h2_session *session)
120 nghttp2_stream *p1, *p2;
122 p1 = nghttp2_stream_get_parent(s1);
123 p2 = nghttp2_stream_get_parent(s2);
128 w1 = nghttp2_stream_get_weight(s1);
129 w2 = nghttp2_stream_get_weight(s2);
133 /* stream 1 closer to root */
137 /* stream 2 closer to root */
140 return spri_cmp(sid1, p1, sid2, p2, session);
143 static int stream_pri_cmp(int sid1, int sid2, void *ctx)
145 h2_session *session = ctx;
146 nghttp2_stream *s1, *s2;
148 s1 = nghttp2_session_find_stream(session->ngh2, sid1);
149 s2 = nghttp2_session_find_stream(session->ngh2, sid2);
160 return spri_cmp(sid1, s1, sid2, s2, session);
163 #else /* ifdef H2_NG2_STREAM_API */
165 /* In absence of nghttp2_stream API, which gives information about
166 * priorities since nghttp2 1.3.x, we just sort the streams by
167 * their identifier, aka. order of arrival.
169 static int stream_pri_cmp(int sid1, int sid2, void *ctx)
175 #endif /* (ifdef else) H2_NG2_STREAM_API */
177 static apr_status_t stream_schedule(h2_session *session,
178 h2_stream *stream, int eos)
181 return h2_stream_schedule(stream, eos, h2_session_push_enabled(session),
182 stream_pri_cmp, session);
186 * Callback when nghttp2 wants to send bytes back to the client.
188 static ssize_t send_cb(nghttp2_session *ngh2,
189 const uint8_t *data, size_t length,
190 int flags, void *userp)
192 h2_session *session = (h2_session *)userp;
197 status = h2_conn_io_write(&session->io, (const char *)data, length);
198 if (status == APR_SUCCESS) {
201 if (APR_STATUS_IS_EAGAIN(status)) {
202 return NGHTTP2_ERR_WOULDBLOCK;
204 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, APLOGNO(03062)
205 "h2_session: send error");
206 return h2_session_status_from_apr_status(status);
209 static int on_invalid_frame_recv_cb(nghttp2_session *ngh2,
210 const nghttp2_frame *frame,
211 int error, void *userp)
213 h2_session *session = (h2_session *)userp;
216 if (APLOGcdebug(session->c)) {
219 frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
220 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03063)
221 "h2_session(%ld): recv unknown FRAME[%s], frames=%ld/%ld (r/s)",
222 session->id, buffer, (long)session->frames_received,
223 (long)session->frames_sent);
228 static int on_data_chunk_recv_cb(nghttp2_session *ngh2, uint8_t flags,
230 const uint8_t *data, size_t len, void *userp)
232 h2_session *session = (h2_session *)userp;
233 apr_status_t status = APR_SUCCESS;
238 if (!is_accepting_streams(session)) {
243 stream = h2_session_get_stream(session, stream_id);
245 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03064)
246 "h2_stream(%ld-%d): on_data_chunk for unknown stream",
247 session->id, (int)stream_id);
248 rv = nghttp2_submit_rst_stream(ngh2, NGHTTP2_FLAG_NONE, stream_id,
249 NGHTTP2_INTERNAL_ERROR);
250 if (nghttp2_is_fatal(rv)) {
251 return NGHTTP2_ERR_CALLBACK_FAILURE;
256 status = h2_stream_write_data(stream, (const char *)data, len);
257 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, session->c,
258 "h2_stream(%ld-%d): data_chunk_recv, written %ld bytes",
259 session->id, stream_id, (long)len);
260 if (status != APR_SUCCESS) {
261 update_window(session, stream_id, len);
262 rv = nghttp2_submit_rst_stream(ngh2, NGHTTP2_FLAG_NONE, stream_id,
263 H2_STREAM_RST(stream, H2_ERR_INTERNAL_ERROR));
264 if (nghttp2_is_fatal(rv)) {
265 return NGHTTP2_ERR_CALLBACK_FAILURE;
271 static apr_status_t stream_release(h2_session *session,
276 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
277 "h2_stream(%ld-%d): handled, closing",
278 session->id, (int)stream->id);
279 if (stream->id > session->max_stream_handled) {
280 session->max_stream_handled = stream->id;
284 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03065)
285 "h2_stream(%ld-%d): closing with err=%d %s",
286 session->id, (int)stream->id, (int)error_code,
287 h2_h2_err_description(error_code));
288 h2_stream_rst(stream, error_code);
291 return h2_conn_io_writeb(&session->io,
292 h2_bucket_eos_create(session->c->bucket_alloc,
296 static int on_stream_close_cb(nghttp2_session *ngh2, int32_t stream_id,
297 uint32_t error_code, void *userp)
299 h2_session *session = (h2_session *)userp;
303 stream = h2_session_get_stream(session, stream_id);
305 stream_release(session, stream, error_code);
310 static int on_begin_headers_cb(nghttp2_session *ngh2,
311 const nghttp2_frame *frame, void *userp)
313 h2_session *session = (h2_session *)userp;
316 /* We may see HEADERs at the start of a stream or after all DATA
317 * streams to carry trailers. */
319 s = h2_session_get_stream(session, frame->hd.stream_id);
324 s = h2_session_open_stream((h2_session *)userp, frame->hd.stream_id);
326 return s? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
329 static int on_header_cb(nghttp2_session *ngh2, const nghttp2_frame *frame,
330 const uint8_t *name, size_t namelen,
331 const uint8_t *value, size_t valuelen,
335 h2_session *session = (h2_session *)userp;
341 if (!is_accepting_streams(session)) {
346 stream = h2_session_get_stream(session, frame->hd.stream_id);
348 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
350 "h2_session: stream(%ld-%d): on_header for unknown stream",
351 session->id, (int)frame->hd.stream_id);
352 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
355 status = h2_stream_add_header(stream, (const char *)name, namelen,
356 (const char *)value, valuelen);
358 if (status != APR_SUCCESS) {
359 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
365 * nghttp2 session has received a complete frame. Most, it uses
366 * for processing of internal state. HEADER and DATA frames however
367 * we need to handle ourself.
369 static int on_frame_recv_cb(nghttp2_session *ng2s,
370 const nghttp2_frame *frame,
373 h2_session *session = (h2_session *)userp;
374 apr_status_t status = APR_SUCCESS;
377 if (APLOGcdebug(session->c)) {
380 frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
381 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03066)
382 "h2_session(%ld): recv FRAME[%s], frames=%ld/%ld (r/s)",
383 session->id, buffer, (long)session->frames_received,
384 (long)session->frames_sent);
387 ++session->frames_received;
388 switch (frame->hd.type) {
389 case NGHTTP2_HEADERS:
390 /* This can be HEADERS for a new stream, defining the request,
391 * or HEADER may come after DATA at the end of a stream as in
393 stream = h2_session_get_stream(session, frame->hd.stream_id);
395 int eos = (frame->hd.flags & NGHTTP2_FLAG_END_STREAM);
397 if (h2_stream_is_scheduled(stream)) {
398 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
399 "h2_stream(%ld-%d): TRAILER, eos=%d",
400 session->id, frame->hd.stream_id, eos);
402 status = h2_stream_close_input(stream);
406 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
407 "h2_stream(%ld-%d): HEADER, eos=%d",
408 session->id, frame->hd.stream_id, eos);
409 status = stream_schedule(session, stream, eos);
417 stream = h2_session_get_stream(session, frame->hd.stream_id);
419 int eos = (frame->hd.flags & NGHTTP2_FLAG_END_STREAM);
420 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
421 "h2_stream(%ld-%d): DATA, len=%ld, eos=%d",
422 session->id, frame->hd.stream_id,
423 (long)frame->hd.length, eos);
425 status = h2_stream_close_input(stream);
432 case NGHTTP2_PRIORITY:
433 session->reprioritize = 1;
434 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
435 "h2_session: stream(%ld-%d): PRIORITY frame "
436 " weight=%d, dependsOn=%d, exclusive=%d",
437 session->id, (int)frame->hd.stream_id,
438 frame->priority.pri_spec.weight,
439 frame->priority.pri_spec.stream_id,
440 frame->priority.pri_spec.exclusive);
442 case NGHTTP2_WINDOW_UPDATE:
443 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
444 "h2_session: stream(%ld-%d): WINDOW_UPDATE "
446 session->id, (int)frame->hd.stream_id,
447 frame->window_update.window_size_increment);
449 case NGHTTP2_RST_STREAM:
450 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03067)
451 "h2_session(%ld-%d): RST_STREAM by client, errror=%d",
452 session->id, (int)frame->hd.stream_id,
453 (int)frame->rst_stream.error_code);
454 stream = h2_session_get_stream(session, frame->hd.stream_id);
455 if (stream && stream->initiated_on) {
456 ++session->pushes_reset;
459 ++session->streams_reset;
463 dispatch_event(session, H2_SESSION_EV_REMOTE_GOAWAY, 0, NULL);
466 if (APLOGctrace2(session->c)) {
469 frame_print(frame, buffer,
470 sizeof(buffer)/sizeof(buffer[0]));
471 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
472 "h2_session: on_frame_rcv %s", buffer);
477 if (status != APR_SUCCESS) {
480 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
482 "h2_session: stream(%ld-%d): error handling frame",
483 session->id, (int)frame->hd.stream_id);
484 rv = nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE,
486 NGHTTP2_INTERNAL_ERROR);
487 if (nghttp2_is_fatal(rv)) {
488 return NGHTTP2_ERR_CALLBACK_FAILURE;
495 static apr_status_t pass_data(void *ctx,
496 const char *data, apr_off_t length)
498 return h2_conn_io_write(&((h2_session*)ctx)->io, data, length);
502 static char immortal_zeros[H2_MAX_PADLEN];
504 static int on_send_data_cb(nghttp2_session *ngh2,
505 nghttp2_frame *frame,
506 const uint8_t *framehd,
508 nghttp2_data_source *source,
511 apr_status_t status = APR_SUCCESS;
512 h2_session *session = (h2_session *)userp;
513 int stream_id = (int)frame->hd.stream_id;
514 unsigned char padlen;
520 if (frame->data.padlen > H2_MAX_PADLEN) {
521 return NGHTTP2_ERR_PROTO;
523 padlen = (unsigned char)frame->data.padlen;
525 stream = h2_session_get_stream(session, stream_id);
527 ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_NOTFOUND, session->c,
529 "h2_stream(%ld-%d): send_data",
530 session->id, (int)stream_id);
531 return NGHTTP2_ERR_CALLBACK_FAILURE;
534 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
535 "h2_stream(%ld-%d): send_data_cb for %ld bytes",
536 session->id, (int)stream_id, (long)length);
538 if (h2_conn_io_is_buffered(&session->io)) {
539 status = h2_conn_io_write(&session->io, (const char *)framehd, 9);
540 if (status == APR_SUCCESS) {
542 status = h2_conn_io_write(&session->io, (const char *)&padlen, 1);
545 if (status == APR_SUCCESS) {
546 apr_off_t len = length;
547 status = h2_stream_readx(stream, pass_data, session, &len, &eos);
548 if (status == APR_SUCCESS && len != length) {
553 if (status == APR_SUCCESS && padlen) {
555 status = h2_conn_io_write(&session->io, immortal_zeros, padlen);
562 char *header = apr_pcalloc(stream->pool, 10);
563 memcpy(header, (const char *)framehd, 9);
565 header[9] = (char)padlen;
567 b = apr_bucket_pool_create(header, padlen? 10 : 9,
568 stream->pool, session->c->bucket_alloc);
569 status = h2_conn_io_writeb(&session->io, b);
571 if (status == APR_SUCCESS) {
572 apr_off_t len = length;
573 status = h2_stream_read_to(stream, session->io.output, &len, &eos);
574 if (status == APR_SUCCESS && len != length) {
579 if (status == APR_SUCCESS && padlen) {
580 b = apr_bucket_immortal_create(immortal_zeros, padlen,
581 session->c->bucket_alloc);
582 status = h2_conn_io_writeb(&session->io, b);
587 if (status == APR_SUCCESS) {
588 stream->data_frames_sent++;
589 h2_conn_io_consider_flush(&session->io);
593 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
595 "h2_stream(%ld-%d): failed send_data_cb",
596 session->id, (int)stream_id);
599 return h2_session_status_from_apr_status(status);
602 static int on_frame_send_cb(nghttp2_session *ngh2,
603 const nghttp2_frame *frame,
606 h2_session *session = user_data;
607 if (APLOGcdebug(session->c)) {
610 frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
611 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03068)
612 "h2_session(%ld): sent FRAME[%s], frames=%ld/%ld (r/s)",
613 session->id, buffer, (long)session->frames_received,
614 (long)session->frames_sent);
616 ++session->frames_sent;
620 #define NGH2_SET_CALLBACK(callbacks, name, fn)\
621 nghttp2_session_callbacks_set_##name##_callback(callbacks, fn)
623 static apr_status_t init_callbacks(conn_rec *c, nghttp2_session_callbacks **pcb)
625 int rv = nghttp2_session_callbacks_new(pcb);
627 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
628 APLOGNO(02926) "nghttp2_session_callbacks_new: %s",
629 nghttp2_strerror(rv));
633 NGH2_SET_CALLBACK(*pcb, send, send_cb);
634 NGH2_SET_CALLBACK(*pcb, on_frame_recv, on_frame_recv_cb);
635 NGH2_SET_CALLBACK(*pcb, on_invalid_frame_recv, on_invalid_frame_recv_cb);
636 NGH2_SET_CALLBACK(*pcb, on_data_chunk_recv, on_data_chunk_recv_cb);
637 NGH2_SET_CALLBACK(*pcb, on_stream_close, on_stream_close_cb);
638 NGH2_SET_CALLBACK(*pcb, on_begin_headers, on_begin_headers_cb);
639 NGH2_SET_CALLBACK(*pcb, on_header, on_header_cb);
640 NGH2_SET_CALLBACK(*pcb, send_data, on_send_data_cb);
641 NGH2_SET_CALLBACK(*pcb, on_frame_send, on_frame_send_cb);
646 static void h2_session_cleanup(h2_session *session)
648 AP_DEBUG_ASSERT(session);
649 /* This is an early cleanup of the session that may
650 * discard what is no longer necessary for *new* streams
651 * and general HTTP/2 processing.
652 * At this point, all frames are in transit or somehwere in
653 * our buffers or passed down output filters.
654 * h2 streams might still being written out.
657 h2_ctx_clear(session->c);
660 nghttp2_session_del(session->ngh2);
661 session->ngh2 = NULL;
663 if (session->spare) {
664 apr_pool_destroy(session->spare);
665 session->spare = NULL;
669 static void h2_session_destroy(h2_session *session)
671 AP_DEBUG_ASSERT(session);
672 h2_session_cleanup(session);
674 if (APLOGctrace1(session->c)) {
675 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
676 "h2_session(%ld): destroy, %d streams open",
677 session->id, (int)h2_stream_set_size(session->streams));
680 h2_mplx_set_consumed_cb(session->mplx, NULL, NULL);
681 h2_mplx_release_and_join(session->mplx, session->iowait);
682 session->mplx = NULL;
684 if (session->streams) {
685 h2_stream_set_destroy(session->streams);
686 session->streams = NULL;
689 apr_pool_destroy(session->pool);
693 static apr_status_t h2_session_shutdown(h2_session *session, int reason, const char *msg)
695 apr_status_t status = APR_SUCCESS;
696 const char *err = msg;
698 AP_DEBUG_ASSERT(session);
699 if (!err && reason) {
700 err = nghttp2_strerror(reason);
702 nghttp2_submit_goaway(session->ngh2, NGHTTP2_FLAG_NONE,
703 h2_mplx_get_max_stream_started(session->mplx),
704 reason, (uint8_t*)err, err? strlen(err):0);
705 status = nghttp2_session_send(session->ngh2);
706 h2_conn_io_flush(&session->io);
707 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03069)
708 "session(%ld): sent GOAWAY, err=%d, msg=%s",
709 session->id, reason, err? err : "");
710 dispatch_event(session, H2_SESSION_EV_LOCAL_GOAWAY, reason, err);
714 static apr_status_t session_pool_cleanup(void *data)
716 h2_session *session = data;
717 /* On a controlled connection shutdown, this gets never
718 * called as we deregister and destroy our pool manually.
719 * However when we have an async mpm, and handed it our idle
720 * connection, it will just cleanup once the connection is closed
721 * from the other side (and sometimes even from out side) and
722 * here we arrive then.
724 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
725 "session(%ld): pool_cleanup", session->id);
727 if (session->state != H2_SESSION_ST_DONE
728 && session->state != H2_SESSION_ST_LOCAL_SHUTDOWN) {
729 /* Not good. The connection is being torn down and we have
730 * not sent a goaway. This is considered a protocol error and
731 * the client has to assume that any streams "in flight" may have
732 * been processed and are not safe to retry.
733 * As clients with idle connection may only learn about a closed
734 * connection when sending the next request, this has the effect
735 * that at least this one request will fail.
737 ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, session->c, APLOGNO(03199)
738 "session(%ld): connection disappeared without proper "
739 "goodbye, clients will be confused, should not happen",
742 /* keep us from destroying the pool, since that is already ongoing. */
743 session->pool = NULL;
744 h2_session_destroy(session);
748 static void *session_malloc(size_t size, void *ctx)
750 h2_session *session = ctx;
751 ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, session->c,
752 "h2_session(%ld): malloc(%ld)",
753 session->id, (long)size);
757 static void session_free(void *p, void *ctx)
759 h2_session *session = ctx;
761 ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, session->c,
762 "h2_session(%ld): free()",
767 static void *session_calloc(size_t n, size_t size, void *ctx)
769 h2_session *session = ctx;
771 ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, session->c,
772 "h2_session(%ld): calloc(%ld, %ld)",
773 session->id, (long)n, (long)size);
774 return calloc(n, size);
777 static void *session_realloc(void *p, size_t size, void *ctx)
779 h2_session *session = ctx;
780 ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, session->c,
781 "h2_session(%ld): realloc(%ld)",
782 session->id, (long)size);
783 return realloc(p, size);
786 static h2_session *h2_session_create_int(conn_rec *c,
791 nghttp2_session_callbacks *callbacks = NULL;
792 nghttp2_option *options = NULL;
795 apr_pool_t *pool = NULL;
796 apr_status_t status = apr_pool_create(&pool, c->pool);
798 if (status != APR_SUCCESS) {
802 session = apr_pcalloc(pool, sizeof(h2_session));
810 session->s = h2_ctx_server_get(ctx);
811 session->config = h2_config_sget(session->s);
813 session->state = H2_SESSION_ST_INIT;
815 session->pool = pool;
816 apr_pool_pre_cleanup_register(pool, session, session_pool_cleanup);
818 session->max_stream_count = h2_config_geti(session->config, H2_CONF_MAX_STREAMS);
819 session->max_stream_mem = h2_config_geti(session->config, H2_CONF_STREAM_MAX_MEM);
821 status = apr_thread_cond_create(&session->iowait, session->pool);
822 if (status != APR_SUCCESS) {
826 session->streams = h2_stream_set_create(session->pool, session->max_stream_count);
828 session->workers = workers;
829 session->mplx = h2_mplx_create(c, session->pool, session->config,
830 session->s->timeout, workers);
832 h2_mplx_set_consumed_cb(session->mplx, update_window, session);
834 /* Install the connection input filter that feeds the session */
835 session->cin = h2_filter_cin_create(session->pool, h2_session_receive, session);
836 ap_add_input_filter("H2_IN", session->cin, r, c);
838 h2_conn_io_init(&session->io, c, session->config, session->pool);
839 session->bbtmp = apr_brigade_create(session->pool, c->bucket_alloc);
841 status = init_callbacks(c, &callbacks);
842 if (status != APR_SUCCESS) {
843 ap_log_cerror(APLOG_MARK, APLOG_ERR, status, c, APLOGNO(02927)
844 "nghttp2: error in init_callbacks");
845 h2_session_destroy(session);
849 rv = nghttp2_option_new(&options);
851 ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
852 APLOGNO(02928) "nghttp2_option_new: %s",
853 nghttp2_strerror(rv));
854 h2_session_destroy(session);
857 nghttp2_option_set_peer_max_concurrent_streams(options,
858 (uint32_t)session->max_stream_count);
859 /* We need to handle window updates ourself, otherwise we
860 * get flooded by nghttp2. */
861 nghttp2_option_set_no_auto_window_update(options, 1);
863 if (APLOGctrace6(c)) {
864 mem = apr_pcalloc(session->pool, sizeof(nghttp2_mem));
865 mem->mem_user_data = session;
866 mem->malloc = session_malloc;
867 mem->free = session_free;
868 mem->calloc = session_calloc;
869 mem->realloc = session_realloc;
871 rv = nghttp2_session_server_new3(&session->ngh2, callbacks,
872 session, options, mem);
875 rv = nghttp2_session_server_new2(&session->ngh2, callbacks,
878 nghttp2_session_callbacks_del(callbacks);
879 nghttp2_option_del(options);
882 ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
883 APLOGNO(02929) "nghttp2_session_server_new: %s",
884 nghttp2_strerror(rv));
885 h2_session_destroy(session);
889 n = h2_config_geti(session->config, H2_CONF_PUSH_DIARY_SIZE);
890 session->push_diary = h2_push_diary_create(session->pool, n);
892 if (APLOGcdebug(c)) {
893 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03200)
894 "session(%ld) created, max_streams=%d, stream_mem=%d, push_diary(type=%d,N=%d)",
895 session->id, (int)session->max_stream_count, (int)session->max_stream_mem,
896 session->push_diary->dtype, (int)session->push_diary->N);
902 h2_session *h2_session_create(conn_rec *c, h2_ctx *ctx, h2_workers *workers)
904 return h2_session_create_int(c, NULL, ctx, workers);
907 h2_session *h2_session_rcreate(request_rec *r, h2_ctx *ctx, h2_workers *workers)
909 return h2_session_create_int(r->connection, r, ctx, workers);
912 void h2_session_eoc_callback(h2_session *session)
914 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
915 "session(%ld): cleanup and destroy", session->id);
916 apr_pool_cleanup_kill(session->pool, session, session_pool_cleanup);
917 h2_session_destroy(session);
920 static apr_status_t h2_session_start(h2_session *session, int *rv)
922 apr_status_t status = APR_SUCCESS;
923 nghttp2_settings_entry settings[3];
927 AP_DEBUG_ASSERT(session);
928 /* Start the conversation by submitting our SETTINGS frame */
935 /* 'h2c' mode: we should have a 'HTTP2-Settings' header with
936 * base64 encoded client settings. */
937 s = apr_table_get(session->r->headers_in, "HTTP2-Settings");
939 ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EINVAL, session->r,
941 "HTTP2-Settings header missing in request");
945 dlen = h2_util_base64url_decode(&cs, s, session->pool);
947 if (APLOGrdebug(session->r)) {
949 h2_util_hex_dump(buffer, 128, (char*)cs, dlen);
950 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, session->r, APLOGNO(03070)
951 "upgrading h2c session with HTTP2-Settings: %s -> %s (%d)",
952 s, buffer, (int)dlen);
955 *rv = nghttp2_session_upgrade(session->ngh2, (uint8_t*)cs, dlen, NULL);
958 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
959 APLOGNO(02932) "nghttp2_session_upgrade: %s",
960 nghttp2_strerror(*rv));
964 /* Now we need to auto-open stream 1 for the request we got. */
965 stream = h2_session_open_stream(session, 1);
967 status = APR_EGENERAL;
968 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r,
969 APLOGNO(02933) "open stream 1: %s",
970 nghttp2_strerror(*rv));
974 status = h2_stream_set_request(stream, session->r);
975 if (status != APR_SUCCESS) {
978 status = stream_schedule(session, stream, 1);
979 if (status != APR_SUCCESS) {
985 settings[slen].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
986 settings[slen].value = (uint32_t)session->max_stream_count;
988 win_size = h2_config_geti(session->config, H2_CONF_WIN_SIZE);
989 if (win_size != H2_INITIAL_WINDOW_SIZE) {
990 settings[slen].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
991 settings[slen].value = win_size;
995 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, APLOGNO(03201)
996 "h2_session(%ld): start, INITIAL_WINDOW_SIZE=%ld, "
997 "MAX_CONCURRENT_STREAMS=%d",
998 session->id, (long)win_size, (int)session->max_stream_count);
999 *rv = nghttp2_submit_settings(session->ngh2, NGHTTP2_FLAG_NONE,
1002 status = APR_EGENERAL;
1003 ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
1004 APLOGNO(02935) "nghttp2_submit_settings: %s",
1005 nghttp2_strerror(*rv));
1008 /* use maximum possible value for connection window size. We are only
1009 * interested in per stream flow control. which have the initial window
1010 * size configured above.
1011 * Therefore, for our use, the connection window can only get in the
1012 * way. Example: if we allow 100 streams with a 32KB window each, we
1013 * buffer up to 3.2 MB of data. Unless we do separate connection window
1014 * interim updates, any smaller connection window will lead to blocking
1017 *rv = nghttp2_submit_window_update(session->ngh2, NGHTTP2_FLAG_NONE,
1018 0, NGHTTP2_MAX_WINDOW_SIZE - win_size);
1020 status = APR_EGENERAL;
1021 ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
1022 APLOGNO(02970) "nghttp2_submit_window_update: %s",
1023 nghttp2_strerror(*rv));
1030 h2_session *session;
1034 static int resume_on_data(void *ctx, h2_stream *stream)
1036 resume_ctx *rctx = (resume_ctx*)ctx;
1037 h2_session *session = rctx->session;
1038 AP_DEBUG_ASSERT(session);
1039 AP_DEBUG_ASSERT(stream);
1041 if (h2_stream_is_suspended(stream)) {
1042 if (h2_mplx_out_has_data_for(stream->session->mplx, stream->id)) {
1044 h2_stream_set_suspended(stream, 0);
1045 ++rctx->resume_count;
1047 rv = nghttp2_session_resume_data(session->ngh2, stream->id);
1048 ap_log_cerror(APLOG_MARK, nghttp2_is_fatal(rv)?
1049 APLOG_ERR : APLOG_DEBUG, 0, session->c,
1051 "h2_stream(%ld-%d): resuming %s",
1052 session->id, stream->id, rv? nghttp2_strerror(rv) : "");
1058 static int h2_session_resume_streams_with_data(h2_session *session)
1060 AP_DEBUG_ASSERT(session);
1061 if (!h2_stream_set_is_empty(session->streams)
1062 && session->mplx && !session->mplx->aborted) {
1065 ctx.session = session;
1066 ctx.resume_count = 0;
1068 /* Resume all streams where we have data in the out queue and
1069 * which had been suspended before. */
1070 h2_stream_set_iter(session->streams, resume_on_data, &ctx);
1071 return ctx.resume_count;
1076 h2_stream *h2_session_get_stream(h2_session *session, int stream_id)
1078 if (!session->last_stream || stream_id != session->last_stream->id) {
1079 session->last_stream = h2_stream_set_get(session->streams, stream_id);
1081 return session->last_stream;
1084 static ssize_t stream_data_cb(nghttp2_session *ng2s,
1088 uint32_t *data_flags,
1089 nghttp2_data_source *source,
1092 h2_session *session = (h2_session *)puser;
1093 apr_off_t nread = length;
1095 apr_status_t status;
1097 AP_DEBUG_ASSERT(session);
1099 /* The session wants to send more DATA for the stream. We need
1100 * to find out how much of the requested length we can send without
1102 * Indicate EOS when we encounter it or DEFERRED if the stream
1103 * should be suspended. Beware of trailers.
1109 stream = h2_session_get_stream(session, stream_id);
1111 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
1113 "h2_stream(%ld-%d): data requested but stream not found",
1114 session->id, (int)stream_id);
1115 return NGHTTP2_ERR_CALLBACK_FAILURE;
1118 AP_DEBUG_ASSERT(!h2_stream_is_suspended(stream));
1120 status = h2_stream_prep_read(stream, &nread, &eos);
1122 *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
1129 case APR_ECONNRESET:
1130 return nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE,
1131 stream->id, stream->rst_error);
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.
1139 h2_stream_set_suspended(stream, 1);
1140 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03071)
1141 "h2_stream(%ld-%d): suspending",
1142 session->id, (int)stream_id);
1143 return NGHTTP2_ERR_DEFERRED;
1152 ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
1153 APLOGNO(02938) "h2_stream(%ld-%d): reading data",
1154 session->id, (int)stream_id);
1155 return NGHTTP2_ERR_CALLBACK_FAILURE;
1159 apr_table_t *trailers = h2_stream_get_trailers(stream);
1160 if (trailers && !apr_is_empty_table(trailers)) {
1164 nh = h2_util_ngheader_make(stream->pool, trailers);
1165 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03072)
1166 "h2_stream(%ld-%d): submit %d trailers",
1167 session->id, (int)stream_id,(int) nh->nvlen);
1168 rv = nghttp2_submit_trailer(ng2s, stream->id, nh->nv, nh->nvlen);
1172 *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
1175 *data_flags |= NGHTTP2_DATA_FLAG_EOF;
1178 return (ssize_t)nread;
1188 * Start submitting the response to a stream request. This is possible
1189 * once we have all the response headers. The response body will be
1190 * read by the session using the callback we supply.
1192 static apr_status_t submit_response(h2_session *session, h2_stream *stream)
1194 apr_status_t status = APR_SUCCESS;
1195 h2_response *response = h2_stream_get_response(stream);
1197 AP_DEBUG_ASSERT(session);
1198 AP_DEBUG_ASSERT(stream);
1199 AP_DEBUG_ASSERT(response || stream->rst_error);
1201 if (stream->submitted) {
1202 rv = NGHTTP2_PROTOCOL_ERROR;
1204 else if (response && response->headers) {
1205 nghttp2_data_provider provider;
1207 const h2_priority *prio;
1209 memset(&provider, 0, sizeof(provider));
1210 provider.source.fd = stream->id;
1211 provider.read_callback = stream_data_cb;
1213 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03073)
1214 "h2_stream(%ld-%d): submit response %d",
1215 session->id, stream->id, response->http_status);
1217 /* If this stream is not a pushed one itself,
1218 * and HTTP/2 server push is enabled here,
1219 * and the response is in the range 200-299 *),
1220 * and the remote side has pushing enabled,
1221 * -> find and perform any pushes on this stream
1222 * *before* we submit the stream response itself.
1223 * This helps clients avoid opening new streams on Link
1224 * headers that get pushed right afterwards.
1226 * *) the response code is relevant, as we do not want to
1227 * make pushes on 401 or 403 codes, neiterh on 301/302
1228 * and friends. And if we see a 304, we do not push either
1229 * as the client, having this resource in its cache, might
1230 * also have the pushed ones as well.
1232 if (!stream->initiated_on
1233 && H2_HTTP_2XX(response->http_status)
1234 && h2_session_push_enabled(session)) {
1236 h2_stream_submit_pushes(stream);
1239 prio = h2_stream_get_priority(stream);
1241 h2_session_set_prio(session, stream, prio);
1242 /* no showstopper if that fails for some reason */
1245 ngh = h2_util_ngheader_make_res(stream->pool, response->http_status,
1247 rv = nghttp2_submit_response(session->ngh2, response->stream_id,
1248 ngh->nv, ngh->nvlen, &provider);
1251 int err = H2_STREAM_RST(stream, H2_ERR_PROTOCOL_ERROR);
1253 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03074)
1254 "h2_stream(%ld-%d): RST_STREAM, err=%d",
1255 session->id, stream->id, err);
1257 rv = nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE,
1261 stream->submitted = 1;
1262 if (stream->initiated_on) {
1263 ++session->pushes_submitted;
1266 ++session->responses_submitted;
1269 if (nghttp2_is_fatal(rv)) {
1270 status = APR_EGENERAL;
1271 dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, rv, nghttp2_strerror(rv));
1272 ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
1273 APLOGNO(02940) "submit_response: %s",
1274 nghttp2_strerror(rv));
1280 struct h2_stream *h2_session_push(h2_session *session, h2_stream *is,
1283 apr_status_t status;
1288 ngh = h2_util_ngheader_make_req(is->pool, push->req);
1289 nid = nghttp2_submit_push_promise(session->ngh2, 0, is->id,
1290 ngh->nv, ngh->nvlen, NULL);
1292 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03075)
1293 "h2_stream(%ld-%d): submitting push promise fail: %s",
1294 session->id, is->id, nghttp2_strerror(nid));
1297 ++session->pushes_promised;
1299 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03076)
1300 "h2_stream(%ld-%d): SERVER_PUSH %d for %s %s on %d",
1301 session->id, is->id, nid,
1302 push->req->method, push->req->path, is->id);
1304 stream = h2_session_open_stream(session, nid);
1306 h2_stream_set_h2_request(stream, is->id, push->req);
1307 status = stream_schedule(session, stream, 1);
1308 if (status != APR_SUCCESS) {
1309 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, session->c,
1310 "h2_stream(%ld-%d): scheduling push stream",
1311 session->id, stream->id);
1312 h2_stream_cleanup(stream);
1315 ++session->unsent_promises;
1318 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03077)
1319 "h2_stream(%ld-%d): failed to create stream obj %d",
1320 session->id, is->id, nid);
1324 /* try to tell the client that it should not wait. */
1325 nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE, nid,
1326 NGHTTP2_INTERNAL_ERROR);
1332 static int valid_weight(float f)
1335 return (w < NGHTTP2_MIN_WEIGHT? NGHTTP2_MIN_WEIGHT :
1336 (w > NGHTTP2_MAX_WEIGHT)? NGHTTP2_MAX_WEIGHT : w);
1339 apr_status_t h2_session_set_prio(h2_session *session, h2_stream *stream,
1340 const h2_priority *prio)
1342 apr_status_t status = APR_SUCCESS;
1343 #ifdef H2_NG2_CHANGE_PRIO
1344 nghttp2_stream *s_grandpa, *s_parent, *s;
1346 s = nghttp2_session_find_stream(session->ngh2, stream->id);
1348 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
1349 "h2_stream(%ld-%d): lookup of nghttp2_stream failed",
1350 session->id, stream->id);
1354 s_parent = nghttp2_stream_get_parent(s);
1356 nghttp2_priority_spec ps;
1357 int id_parent, id_grandpa, w_parent, w, rv = 0;
1358 char *ptype = "AFTER";
1359 h2_dependency dep = prio->dependency;
1361 id_parent = nghttp2_stream_get_stream_id(s_parent);
1362 s_grandpa = nghttp2_stream_get_parent(s_parent);
1364 id_grandpa = nghttp2_stream_get_stream_id(s_grandpa);
1367 /* parent of parent does not exist,
1368 * only possible if parent == root */
1369 dep = H2_DEPENDANT_AFTER;
1373 case H2_DEPENDANT_INTERLEAVED:
1374 /* PUSHed stream is to be interleaved with initiating stream.
1375 * It is made a sibling of the initiating stream and gets a
1376 * proportional weight [1, MAX_WEIGHT] of the initiaing
1379 ptype = "INTERLEAVED";
1380 w_parent = nghttp2_stream_get_weight(s_parent);
1381 w = valid_weight(w_parent * ((float)prio->weight / NGHTTP2_MAX_WEIGHT));
1382 nghttp2_priority_spec_init(&ps, id_grandpa, w, 0);
1385 case H2_DEPENDANT_BEFORE:
1386 /* PUSHed stream os to be sent BEFORE the initiating stream.
1387 * It gets the same weight as the initiating stream, replaces
1388 * that stream in the dependency tree and has the initiating
1392 w = w_parent = nghttp2_stream_get_weight(s_parent);
1393 nghttp2_priority_spec_init(&ps, stream->id, w_parent, 0);
1394 id_grandpa = nghttp2_stream_get_stream_id(s_grandpa);
1395 rv = nghttp2_session_change_stream_priority(session->ngh2, id_parent, &ps);
1397 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03202)
1398 "h2_stream(%ld-%d): PUSH BEFORE, weight=%d, "
1399 "depends=%d, returned=%d",
1400 session->id, id_parent, ps.weight, ps.stream_id, rv);
1401 return APR_EGENERAL;
1403 nghttp2_priority_spec_init(&ps, id_grandpa, w, 0);
1406 case H2_DEPENDANT_AFTER:
1407 /* The PUSHed stream is to be sent after the initiating stream.
1408 * Give if the specified weight and let it depend on the intiating
1411 /* fall through, it's the default */
1413 nghttp2_priority_spec_init(&ps, id_parent, valid_weight(prio->weight), 0);
1418 rv = nghttp2_session_change_stream_priority(session->ngh2, stream->id, &ps);
1419 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03203)
1420 "h2_stream(%ld-%d): PUSH %s, weight=%d, "
1421 "depends=%d, returned=%d",
1422 session->id, stream->id, ptype,
1423 ps.weight, ps.stream_id, rv);
1424 status = (rv < 0)? APR_EGENERAL : APR_SUCCESS;
1435 apr_status_t h2_session_stream_destroy(h2_session *session, h2_stream *stream)
1437 apr_pool_t *pool = h2_stream_detach_pool(stream);
1439 /* this may be called while the session has already freed
1440 * some internal structures. */
1441 if (session->mplx) {
1442 h2_mplx_stream_done(session->mplx, stream->id, stream->rst_error);
1443 if (session->last_stream == stream) {
1444 session->last_stream = NULL;
1448 if (session->streams) {
1449 h2_stream_set_remove(session->streams, stream->id);
1451 h2_stream_destroy(stream);
1454 apr_pool_clear(pool);
1455 if (session->spare) {
1456 apr_pool_destroy(session->spare);
1458 session->spare = pool;
1463 static int frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen)
1466 size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
1468 switch (frame->hd.type) {
1469 case NGHTTP2_DATA: {
1470 return apr_snprintf(buffer, maxlen,
1471 "DATA[length=%d, flags=%d, stream=%d, padlen=%d]",
1472 (int)frame->hd.length, frame->hd.flags,
1473 frame->hd.stream_id, (int)frame->data.padlen);
1475 case NGHTTP2_HEADERS: {
1476 return apr_snprintf(buffer, maxlen,
1477 "HEADERS[length=%d, hend=%d, stream=%d, eos=%d]",
1478 (int)frame->hd.length,
1479 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
1480 frame->hd.stream_id,
1481 !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
1483 case NGHTTP2_PRIORITY: {
1484 return apr_snprintf(buffer, maxlen,
1485 "PRIORITY[length=%d, flags=%d, stream=%d]",
1486 (int)frame->hd.length,
1487 frame->hd.flags, frame->hd.stream_id);
1489 case NGHTTP2_RST_STREAM: {
1490 return apr_snprintf(buffer, maxlen,
1491 "RST_STREAM[length=%d, flags=%d, stream=%d]",
1492 (int)frame->hd.length,
1493 frame->hd.flags, frame->hd.stream_id);
1495 case NGHTTP2_SETTINGS: {
1496 if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
1497 return apr_snprintf(buffer, maxlen,
1498 "SETTINGS[ack=1, stream=%d]",
1499 frame->hd.stream_id);
1501 return apr_snprintf(buffer, maxlen,
1502 "SETTINGS[length=%d, stream=%d]",
1503 (int)frame->hd.length, frame->hd.stream_id);
1505 case NGHTTP2_PUSH_PROMISE: {
1506 return apr_snprintf(buffer, maxlen,
1507 "PUSH_PROMISE[length=%d, hend=%d, stream=%d]",
1508 (int)frame->hd.length,
1509 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
1510 frame->hd.stream_id);
1512 case NGHTTP2_PING: {
1513 return apr_snprintf(buffer, maxlen,
1514 "PING[length=%d, ack=%d, stream=%d]",
1515 (int)frame->hd.length,
1516 frame->hd.flags&NGHTTP2_FLAG_ACK,
1517 frame->hd.stream_id);
1519 case NGHTTP2_GOAWAY: {
1520 size_t len = (frame->goaway.opaque_data_len < s_len)?
1521 frame->goaway.opaque_data_len : s_len-1;
1522 memcpy(scratch, frame->goaway.opaque_data, len);
1523 scratch[len+1] = '\0';
1524 return apr_snprintf(buffer, maxlen, "GOAWAY[error=%d, reason='%s']",
1525 frame->goaway.error_code, scratch);
1527 case NGHTTP2_WINDOW_UPDATE: {
1528 return apr_snprintf(buffer, maxlen,
1529 "WINDOW_UPDATE[stream=%d, incr=%d]",
1530 frame->hd.stream_id,
1531 frame->window_update.window_size_increment);
1534 return apr_snprintf(buffer, maxlen,
1535 "type=%d[length=%d, flags=%d, stream=%d]",
1536 frame->hd.type, (int)frame->hd.length,
1537 frame->hd.flags, frame->hd.stream_id);
1541 int h2_session_push_enabled(h2_session *session)
1543 /* iff we can and they can */
1544 return (h2_config_geti(session->config, H2_CONF_PUSH)
1545 && nghttp2_session_get_remote_settings(session->ngh2,
1546 NGHTTP2_SETTINGS_ENABLE_PUSH));
1549 static apr_status_t h2_session_send(h2_session *session)
1551 int rv = nghttp2_session_send(session->ngh2);
1553 if (nghttp2_is_fatal(rv)) {
1554 dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, rv, nghttp2_strerror(rv));
1555 return APR_EGENERAL;
1559 session->unsent_promises = 0;
1560 session->unsent_submits = 0;
1565 static apr_status_t h2_session_receive(void *ctx, const char *data,
1566 apr_size_t len, apr_size_t *readlen)
1568 h2_session *session = ctx;
1572 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
1573 "h2_session(%ld): feeding %ld bytes to nghttp2",
1574 session->id, (long)len);
1575 n = nghttp2_session_mem_recv(session->ngh2, (const uint8_t *)data, len);
1577 if (nghttp2_is_fatal((int)n)) {
1578 dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, (int)n, nghttp2_strerror(n));
1579 return APR_EGENERAL;
1584 session->io.bytes_read += n;
1590 static apr_status_t h2_session_read(h2_session *session, int block, int loops)
1592 apr_status_t status, rstatus = APR_EAGAIN;
1593 conn_rec *c = session->c;
1596 for (i = 0; i < loops; ++i) {
1597 /* H2_IN filter handles all incoming data against the session.
1598 * We just pull at the filter chain to make it happen */
1599 status = ap_get_brigade(c->input_filters,
1600 session->bbtmp, AP_MODE_READBYTES,
1601 block? APR_BLOCK_READ : APR_NONBLOCK_READ,
1602 APR_BUCKET_BUFF_SIZE);
1603 /* get rid of any possible data we do not expect to get */
1604 apr_brigade_cleanup(session->bbtmp);
1608 /* successful read, reset our idle timers */
1609 rstatus = APR_SUCCESS;
1611 /* successfull blocked read, try unblocked to
1622 /* first attempt failed */
1623 if (APR_STATUS_IS_ETIMEDOUT(status)
1624 || APR_STATUS_IS_ECONNABORTED(status)
1625 || APR_STATUS_IS_ECONNRESET(status)
1626 || APR_STATUS_IS_EOF(status)
1627 || APR_STATUS_IS_EBADF(status)) {
1628 /* common status for a client that has left */
1629 ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
1630 "h2_session(%ld): input gone", session->id);
1633 /* uncommon status, log on INFO so that we see this */
1634 ap_log_cerror( APLOG_MARK, APLOG_INFO, status, c,
1636 "h2_session(%ld): error reading, terminating",
1641 /* subsequent failure after success(es), return initial
1645 if (!is_accepting_streams(session)) {
1652 static apr_status_t h2_session_submit(h2_session *session)
1654 apr_status_t status = APR_EAGAIN;
1657 if (h2_stream_set_has_unsubmitted(session->streams)) {
1658 /* If we have responses ready, submit them now. */
1659 while ((stream = h2_mplx_next_submit(session->mplx, session->streams))) {
1660 status = submit_response(session, stream);
1661 ++session->unsent_submits;
1663 /* Unsent push promises are written immediately, as nghttp2
1664 * 1.5.0 realizes internal stream data structures only on
1665 * send and we might need them for other submits.
1666 * Also, to conserve memory, we send at least every 10 submits
1667 * so that nghttp2 does not buffer all outbound items too
1670 if (status == APR_SUCCESS
1671 && (session->unsent_promises || session->unsent_submits > 10)) {
1672 status = h2_session_send(session);
1673 if (status != APR_SUCCESS) {
1682 static const char *StateNames[] = {
1683 "INIT", /* H2_SESSION_ST_INIT */
1684 "DONE", /* H2_SESSION_ST_DONE */
1685 "IDLE", /* H2_SESSION_ST_IDLE */
1686 "BUSY", /* H2_SESSION_ST_BUSY */
1687 "WAIT", /* H2_SESSION_ST_WAIT */
1688 "LSHUTDOWN", /* H2_SESSION_ST_LOCAL_SHUTDOWN */
1689 "RSHUTDOWN", /* H2_SESSION_ST_REMOTE_SHUTDOWN */
1692 static const char *state_name(h2_session_state state)
1694 if (state >= (sizeof(StateNames)/sizeof(StateNames[0]))) {
1697 return StateNames[state];
1700 static int is_accepting_streams(h2_session *session)
1702 switch (session->state) {
1703 case H2_SESSION_ST_IDLE:
1704 case H2_SESSION_ST_BUSY:
1705 case H2_SESSION_ST_WAIT:
1712 static void transit(h2_session *session, const char *action, h2_session_state nstate)
1714 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03078)
1715 "h2_session(%ld): transit [%s] -- %s --> [%s]", session->id,
1716 state_name(session->state), action, state_name(nstate));
1717 session->state = nstate;
1720 static void h2_session_ev_init(h2_session *session, int arg, const char *msg)
1722 switch (session->state) {
1723 case H2_SESSION_ST_INIT:
1724 transit(session, "init", H2_SESSION_ST_BUSY);
1733 static void h2_session_ev_local_goaway(h2_session *session, int arg, const char *msg)
1735 switch (session->state) {
1736 case H2_SESSION_ST_LOCAL_SHUTDOWN:
1737 /* already did that? */
1739 case H2_SESSION_ST_IDLE:
1740 case H2_SESSION_ST_REMOTE_SHUTDOWN:
1742 transit(session, "local goaway", H2_SESSION_ST_DONE);
1745 transit(session, "local goaway", H2_SESSION_ST_LOCAL_SHUTDOWN);
1750 static void h2_session_ev_remote_goaway(h2_session *session, int arg, const char *msg)
1752 switch (session->state) {
1753 case H2_SESSION_ST_REMOTE_SHUTDOWN:
1754 /* already received that? */
1756 case H2_SESSION_ST_IDLE:
1757 case H2_SESSION_ST_LOCAL_SHUTDOWN:
1759 transit(session, "remote goaway", H2_SESSION_ST_DONE);
1762 transit(session, "remote goaway", H2_SESSION_ST_REMOTE_SHUTDOWN);
1767 static void h2_session_ev_conn_error(h2_session *session, int arg, const char *msg)
1769 switch (session->state) {
1770 case H2_SESSION_ST_INIT:
1771 case H2_SESSION_ST_DONE:
1772 case H2_SESSION_ST_LOCAL_SHUTDOWN:
1774 transit(session, "conn error", H2_SESSION_ST_DONE);
1778 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
1779 "h2_session(%ld): conn error -> shutdown", session->id);
1780 h2_session_shutdown(session, arg, msg);
1785 static void h2_session_ev_proto_error(h2_session *session, int arg, const char *msg)
1787 switch (session->state) {
1788 case H2_SESSION_ST_DONE:
1789 case H2_SESSION_ST_LOCAL_SHUTDOWN:
1791 transit(session, "proto error", H2_SESSION_ST_DONE);
1795 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
1796 "h2_session(%ld): proto error -> shutdown", session->id);
1797 h2_session_shutdown(session, arg, msg);
1802 static void h2_session_ev_conn_timeout(h2_session *session, int arg, const char *msg)
1804 switch (session->state) {
1805 case H2_SESSION_ST_LOCAL_SHUTDOWN:
1806 transit(session, "conn timeout", H2_SESSION_ST_DONE);
1809 h2_session_shutdown(session, arg, msg);
1810 transit(session, "conn timeout", H2_SESSION_ST_DONE);
1815 static void h2_session_ev_no_io(h2_session *session, int arg, const char *msg)
1817 switch (session->state) {
1818 case H2_SESSION_ST_BUSY:
1819 case H2_SESSION_ST_LOCAL_SHUTDOWN:
1820 case H2_SESSION_ST_REMOTE_SHUTDOWN:
1821 /* nothing for input and output to do. If we remain
1822 * in this state, we go into a tight loop and suck up
1823 * CPU cycles. Ideally, we'd like to do a blocking read, but that
1824 * is not possible if we have scheduled tasks and wait
1825 * for them to produce something. */
1826 if (h2_stream_set_is_empty(session->streams)) {
1827 if (!is_accepting_streams(session)) {
1828 /* We are no longer accepting new streams and have
1829 * finished processing existing ones. Time to leave. */
1830 h2_session_shutdown(session, arg, msg);
1831 transit(session, "no io", H2_SESSION_ST_DONE);
1834 /* When we have no streams, no task event are possible,
1835 * switch to blocking reads */
1836 transit(session, "no io", H2_SESSION_ST_IDLE);
1837 session->idle_until = apr_time_now() + session->s->keep_alive_timeout;
1840 else if (!h2_stream_set_has_unsubmitted(session->streams)
1841 && !h2_stream_set_has_suspended(session->streams)) {
1842 /* none of our streams is waiting for a response or
1843 * new output data from task processing,
1844 * switch to blocking reads. We are probably waiting on
1845 * window updates. */
1846 transit(session, "no io", H2_SESSION_ST_IDLE);
1847 session->idle_until = apr_time_now() + session->s->timeout;
1850 /* Unable to do blocking reads, as we wait on events from
1851 * task processing in other threads. Do a busy wait with
1853 transit(session, "no io", H2_SESSION_ST_WAIT);
1862 static void h2_session_ev_stream_ready(h2_session *session, int arg, const char *msg)
1864 switch (session->state) {
1865 case H2_SESSION_ST_WAIT:
1866 transit(session, "stream ready", H2_SESSION_ST_BUSY);
1874 static void h2_session_ev_data_read(h2_session *session, int arg, const char *msg)
1876 switch (session->state) {
1877 case H2_SESSION_ST_IDLE:
1878 case H2_SESSION_ST_WAIT:
1879 transit(session, "data read", 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:
1904 case H2_SESSION_ST_LOCAL_SHUTDOWN:
1908 h2_session_shutdown(session, arg, msg);
1913 static void h2_session_ev_pre_close(h2_session *session, int arg, const char *msg)
1915 switch (session->state) {
1916 case H2_SESSION_ST_DONE:
1917 case H2_SESSION_ST_LOCAL_SHUTDOWN:
1921 h2_session_shutdown(session, arg, msg);
1922 h2_conn_io_flush(&session->io);
1927 static void dispatch_event(h2_session *session, h2_session_event_t ev,
1928 int arg, const char *msg)
1931 case H2_SESSION_EV_INIT:
1932 h2_session_ev_init(session, arg, msg);
1934 case H2_SESSION_EV_LOCAL_GOAWAY:
1935 h2_session_ev_local_goaway(session, arg, msg);
1937 case H2_SESSION_EV_REMOTE_GOAWAY:
1938 h2_session_ev_remote_goaway(session, arg, msg);
1940 case H2_SESSION_EV_CONN_ERROR:
1941 h2_session_ev_conn_error(session, arg, msg);
1943 case H2_SESSION_EV_PROTO_ERROR:
1944 h2_session_ev_proto_error(session, arg, msg);
1946 case H2_SESSION_EV_CONN_TIMEOUT:
1947 h2_session_ev_conn_timeout(session, arg, msg);
1949 case H2_SESSION_EV_NO_IO:
1950 h2_session_ev_no_io(session, arg, msg);
1952 case H2_SESSION_EV_STREAM_READY:
1953 h2_session_ev_stream_ready(session, arg, msg);
1955 case H2_SESSION_EV_DATA_READ:
1956 h2_session_ev_data_read(session, arg, msg);
1958 case H2_SESSION_EV_NGH2_DONE:
1959 h2_session_ev_ngh2_done(session, arg, msg);
1961 case H2_SESSION_EV_MPM_STOPPING:
1962 h2_session_ev_mpm_stopping(session, arg, msg);
1964 case H2_SESSION_EV_PRE_CLOSE:
1965 h2_session_ev_pre_close(session, arg, msg);
1968 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
1969 "h2_session(%ld): unknown event %d",
1974 if (session->state == H2_SESSION_ST_DONE) {
1975 h2_mplx_abort(session->mplx);
1979 static const int MAX_WAIT_MICROS = 200 * 1000;
1981 static void update_child_status(h2_session *session, int status, const char *msg)
1983 apr_snprintf(session->status, sizeof(session->status),
1984 "%s, streams: %d/%d/%d/%d/%d (open/recv/resp/push/rst)",
1986 (int)h2_stream_set_size(session->streams),
1987 (int)session->requests_received,
1988 (int)session->responses_submitted,
1989 (int)session->pushes_submitted,
1990 (int)session->pushes_reset + session->streams_reset);
1991 ap_update_child_status_descr(session->c->sbh, status, session->status);
1994 apr_status_t h2_session_process(h2_session *session, int async)
1996 apr_status_t status = APR_SUCCESS;
1997 conn_rec *c = session->c;
1998 int rv, have_written, have_read, mpm_state;
2000 ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
2001 "h2_session(%ld): process start, async=%d", session->id, async);
2004 c->cs->state = CONN_STATE_WRITE_COMPLETION;
2008 have_read = have_written = 0;
2010 if (!ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
2011 if (mpm_state == AP_MPMQ_STOPPING) {
2012 dispatch_event(session, H2_SESSION_EV_MPM_STOPPING, 0, NULL);
2017 session->status[0] = '\0';
2019 switch (session->state) {
2020 case H2_SESSION_ST_INIT:
2021 ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_READ, c);
2022 if (!h2_is_acceptable_connection(c, 1)) {
2023 update_child_status(session, SERVER_BUSY_READ, "inadequate security");
2024 h2_session_shutdown(session, NGHTTP2_INADEQUATE_SECURITY, NULL);
2027 update_child_status(session, SERVER_BUSY_READ, "init");
2028 status = h2_session_start(session, &rv);
2029 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, APLOGNO(03079)
2030 "h2_session(%ld): started on %s:%d", session->id,
2031 session->s->server_hostname,
2032 c->local_addr->port);
2033 if (status != APR_SUCCESS) {
2034 dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
2036 dispatch_event(session, H2_SESSION_EV_INIT, 0, NULL);
2040 case H2_SESSION_ST_IDLE:
2041 update_child_status(session, (h2_stream_set_is_empty(session->streams)?
2042 SERVER_BUSY_KEEPALIVE : SERVER_BUSY_READ),
2044 if (async && !session->r) {
2045 ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
2046 "h2_session(%ld): async idle, nonblock read", session->id);
2048 c->cs->sense = CONN_SENSE_WANT_READ;
2050 status = h2_session_read(session, 0, 1);
2051 if (status == APR_SUCCESS) {
2053 dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
2055 else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) {
2056 if (apr_time_now() > session->idle_until) {
2057 dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
2060 status = APR_EAGAIN;
2065 ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, c,
2066 "h2_session(%ld): idle, no data, error",
2068 dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
2072 /* We wait in smaller increments, using a 1 second timeout.
2073 * That gives us the chance to check for MPMQ_STOPPING often. */
2074 h2_filter_cin_timeout_set(session->cin,
2075 (h2_stream_set_is_empty(session->streams)?
2076 session->s->keep_alive_timeout :
2077 session->s->timeout));
2078 status = h2_session_read(session, 1, 10);
2079 if (status == APR_SUCCESS) {
2081 dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
2083 else if (status == APR_EAGAIN) {
2084 /* nothing to read */
2086 else if (APR_STATUS_IS_TIMEUP(status)) {
2087 if (apr_time_now() > session->idle_until) {
2088 dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, "timeout");
2090 /* continue reading handling */
2093 dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
2099 case H2_SESSION_ST_BUSY:
2100 case H2_SESSION_ST_LOCAL_SHUTDOWN:
2101 case H2_SESSION_ST_REMOTE_SHUTDOWN:
2102 if (nghttp2_session_want_read(session->ngh2)) {
2103 h2_filter_cin_timeout_set(session->cin, session->s->timeout);
2104 status = h2_session_read(session, 0, 10);
2105 if (status == APR_SUCCESS) {
2107 dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
2109 else if (status == APR_EAGAIN) {
2110 /* nothing to read */
2112 else if (APR_STATUS_IS_TIMEUP(status)) {
2113 dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
2117 dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
2121 if (!h2_stream_set_is_empty(session->streams)) {
2122 /* resume any streams for which data is available again */
2123 h2_session_resume_streams_with_data(session);
2124 /* Submit any responses/push_promises that are ready */
2125 status = h2_session_submit(session);
2126 if (status == APR_SUCCESS) {
2129 else if (status != APR_EAGAIN) {
2130 dispatch_event(session, H2_SESSION_EV_CONN_ERROR,
2131 H2_ERR_INTERNAL_ERROR, "submit error");
2134 /* send out window updates for our inputs */
2135 status = h2_mplx_in_update_windows(session->mplx);
2136 if (status != APR_SUCCESS && status != APR_EAGAIN) {
2137 dispatch_event(session, H2_SESSION_EV_CONN_ERROR,
2138 H2_ERR_INTERNAL_ERROR, "window update error");
2143 if (nghttp2_session_want_write(session->ngh2)) {
2144 ap_update_child_status(session->c->sbh, SERVER_BUSY_WRITE, NULL);
2145 status = h2_session_send(session);
2146 if (status == APR_SUCCESS) {
2150 dispatch_event(session, H2_SESSION_EV_CONN_ERROR,
2151 H2_ERR_INTERNAL_ERROR, "writing");
2156 if (have_read || have_written) {
2157 if (session->wait_us) {
2158 session->wait_us = 0;
2159 update_child_status(session, SERVER_BUSY_READ, "busy");
2162 else if (!nghttp2_session_want_write(session->ngh2)) {
2163 dispatch_event(session, H2_SESSION_EV_NO_IO, 0, NULL);
2167 case H2_SESSION_ST_WAIT:
2168 if (session->wait_us <= 0) {
2169 session->wait_us = 10;
2170 session->start_wait = apr_time_now();
2171 update_child_status(session, SERVER_BUSY_READ, "wait");
2173 else if ((apr_time_now() - session->start_wait) >= session->s->timeout) {
2174 /* waited long enough */
2175 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, APR_TIMEUP, c,
2176 "h2_session: wait for data");
2177 dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
2180 /* repeating, increase timer for graceful backoff */
2181 session->wait_us = H2MIN(session->wait_us*2, MAX_WAIT_MICROS);
2184 if (APLOGctrace1(c)) {
2185 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
2186 "h2_session: wait for data, %ld micros",
2187 (long)session->wait_us);
2189 status = h2_mplx_out_trywait(session->mplx, session->wait_us,
2191 if (status == APR_SUCCESS) {
2192 session->wait_us = 0;
2193 dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
2195 else if (status == APR_TIMEUP) {
2196 /* go back to checking all inputs again */
2197 transit(session, "wait cycle", H2_SESSION_ST_BUSY);
2200 h2_session_shutdown(session, H2_ERR_INTERNAL_ERROR, "cond wait error");
2204 case H2_SESSION_ST_DONE:
2205 update_child_status(session, SERVER_CLOSING, "done");
2210 ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
2212 "h2_session(%ld): unknown state %d", session->id, session->state);
2213 dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, 0, NULL);
2218 h2_conn_io_flush(&session->io);
2220 else if (!nghttp2_session_want_read(session->ngh2)
2221 && !nghttp2_session_want_write(session->ngh2)) {
2222 dispatch_event(session, H2_SESSION_EV_NGH2_DONE, 0, NULL);
2228 h2_conn_io_flush(&session->io);
2231 ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
2232 "h2_session(%ld): [%s] process returns",
2233 session->id, state_name(session->state));
2235 if ((session->state != H2_SESSION_ST_DONE)
2236 && (APR_STATUS_IS_EOF(status)
2237 || APR_STATUS_IS_ECONNRESET(status)
2238 || APR_STATUS_IS_ECONNABORTED(status))) {
2239 dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
2242 status = (session->state == H2_SESSION_ST_DONE)? APR_EOF : APR_SUCCESS;
2243 if (session->state == H2_SESSION_ST_DONE) {
2244 if (!session->eoc_written) {
2245 session->eoc_written = 1;
2246 h2_conn_io_write_eoc(&session->io,
2247 h2_bucket_eoc_create(session->c->bucket_alloc, session));
2254 apr_status_t h2_session_pre_close(h2_session *session, int async)
2256 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
2257 "h2_session(%ld): pre_close", session->id);
2258 dispatch_event(session, H2_SESSION_EV_PRE_CLOSE, 0, "timeout");