You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by rp...@apache.org on 2020/06/08 07:30:49 UTC
svn commit: r1878579 - in /httpd/httpd/branches/2.4.x: ./ CHANGES
modules/http2/h2_proxy_session.c modules/http2/h2_proxy_session.h
modules/http2/h2_version.h
Author: rpluem
Date: Mon Jun 8 07:30:49 2020
New Revision: 1878579
URL: http://svn.apache.org/viewvc?rev=1878579&view=rev
Log:
Merge r1878433 from trunk:
*) mod_proxy_http2: the "ping" proxy parameter
(see <https://httpd.apache.org/docs/2.4/mod/mod_proxy.html>) is now used
when checking the liveliness of a new or reused h2 connection to the backend.
With short durations, this makes load-balancing more responsive. The module
will hold back requests until ping conditions are met, using features of the
HTTP/2 protocol alone. [Ruediger Pluem, Stefan Eissing]
Note: mod_proxy_http2 is currently CTR on 2.4.x.
Submitted by: icing
Reviewed by: rpluem
Modified:
httpd/httpd/branches/2.4.x/ (props changed)
httpd/httpd/branches/2.4.x/CHANGES
httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_session.c
httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_session.h
httpd/httpd/branches/2.4.x/modules/http2/h2_version.h
Propchange: httpd/httpd/branches/2.4.x/
------------------------------------------------------------------------------
Merged /httpd/httpd/trunk:r1878433
Modified: httpd/httpd/branches/2.4.x/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/CHANGES?rev=1878579&r1=1878578&r2=1878579&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/CHANGES [utf-8] (original)
+++ httpd/httpd/branches/2.4.x/CHANGES [utf-8] Mon Jun 8 07:30:49 2020
@@ -1,6 +1,14 @@
-*- coding: utf-8 -*-
Changes with Apache 2.4.44
+ *) mod_proxy_http2: the "ping" proxy parameter·
+ (see <https://httpd.apache.org/docs/2.4/mod/mod_proxy.html>) is now used
+ when checking the liveliness of a new or reused h2 connection to the
+ backend.
+ With short durations, this makes load-balancing more responsive. The module
+ will hold back requests until ping conditions are met, using features of
+ the HTTP/2 protocol alone. [Ruediger Pluem, Stefan Eissing]
+
*) core: httpd is no longer linked against -lsystemd if mod_systemd
is enabled (and built as a DSO). [Rainer Jung]
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_session.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_session.c?rev=1878579&r1=1878578&r2=1878579&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_session.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_session.c Mon Jun 8 07:30:49 2020
@@ -64,6 +64,121 @@ static apr_status_t check_suspended(h2_p
static void stream_resume(h2_proxy_stream *stream);
static apr_status_t submit_trailers(h2_proxy_stream *stream);
+/*
+ * The H2_PING connection sub-state: a state independant of the H2_SESSION state
+ * of the connection:
+ * - H2_PING_ST_NONE: no interference with request handling, ProxyTimeout in effect.
+ * When entered, all suspended streams are unsuspended again.
+ * - H2_PING_ST_AWAIT_ANY: new requests are suspended, a possibly configured "ping"
+ * timeout is in effect. Any frame received transits to H2_PING_ST_NONE.
+ * - H2_PING_ST_AWAIT_PING: same as above, but only a PING frame transits
+ * to H2_PING_ST_NONE.
+ *
+ * An AWAIT state is entered on a new connection or when re-using a connection and
+ * the last frame received has been some time ago. The latter sends a PING frame
+ * and insists on an answer, the former is satisfied by any frame received from the
+ * backend.
+ *
+ * This works for new connections as there is always at least one SETTINGS frame
+ * that the backend sends. When re-using connection, we send a PING and insist on
+ * receiving one back, as there might be frames in our connection buffers from
+ * some time ago. Since some servers have protections against PING flooding, we
+ * only ever have one PING unanswered.
+ *
+ * Requests are suspended while in a PING state, as we do not want to send data
+ * before we can be reasonably sure that the connection is working (at least on
+ * the h2 protocol level). This also means that the session can do blocking reads
+ * when expecting PING answers.
+ */
+static void set_ping_timeout(h2_proxy_session *session)
+{
+ if (session->ping_timeout != -1 && session->save_timeout == -1) {
+ apr_socket_t *socket = NULL;
+
+ socket = ap_get_conn_socket(session->c);
+ if (socket) {
+ apr_socket_timeout_get(socket, &session->save_timeout);
+ apr_socket_timeout_set(socket, session->ping_timeout);
+ }
+ }
+}
+
+static void unset_ping_timeout(h2_proxy_session *session)
+{
+ if (session->save_timeout != -1) {
+ apr_socket_t *socket = NULL;
+
+ socket = ap_get_conn_socket(session->c);
+ if (socket) {
+ apr_socket_timeout_set(socket, session->save_timeout);
+ session->save_timeout = -1;
+ }
+ }
+}
+
+static void enter_ping_state(h2_proxy_session *session, h2_ping_state_t state)
+{
+ if (session->ping_state == state) return;
+ switch (session->ping_state) {
+ case H2_PING_ST_NONE:
+ /* leaving NONE, enforce timeout, send frame maybe */
+ if (H2_PING_ST_AWAIT_PING == state) {
+ unset_ping_timeout(session);
+ nghttp2_submit_ping(session->ngh2, 0, (const uint8_t *)"nevergonnagiveyouup");
+ }
+ set_ping_timeout(session);
+ session->ping_state = state;
+ break;
+ default:
+ /* no switching between the != NONE states */
+ if (H2_PING_ST_NONE == state) {
+ session->ping_state = state;
+ unset_ping_timeout(session);
+ ping_arrived(session);
+ }
+ break;
+ }
+}
+
+static void ping_new_session(h2_proxy_session *session, proxy_conn_rec *p_conn)
+{
+ session->save_timeout = -1;
+ session->ping_timeout = (p_conn->worker->s->ping_timeout_set?
+ p_conn->worker->s->ping_timeout : -1);
+ session->ping_state = H2_PING_ST_NONE;
+ enter_ping_state(session, H2_PING_ST_AWAIT_ANY);
+}
+
+static void ping_reuse_session(h2_proxy_session *session)
+{
+ if (H2_PING_ST_NONE == session->ping_state) {
+ apr_interval_time_t age = apr_time_now() - session->last_frame_received;
+ if (age > apr_time_from_sec(1)) {
+ enter_ping_state(session, H2_PING_ST_AWAIT_PING);
+ }
+ }
+}
+
+static void ping_ev_frame_received(h2_proxy_session *session, const nghttp2_frame *frame)
+{
+ session->last_frame_received = apr_time_now();
+ switch (session->ping_state) {
+ case H2_PING_ST_NONE:
+ /* nop */
+ break;
+ case H2_PING_ST_AWAIT_ANY:
+ enter_ping_state(session, H2_PING_ST_NONE);
+ break;
+ case H2_PING_ST_AWAIT_PING:
+ if (NGHTTP2_PING == frame->hd.type) {
+ enter_ping_state(session, H2_PING_ST_NONE);
+ }
+ /* we may receive many other frames while we are waiting for the
+ * PING answer. They may come all from our connection buffers and
+ * say nothing about the current state of the backend. */
+ break;
+ }
+}
static apr_status_t proxy_session_pre_close(void *theconn)
{
@@ -154,7 +269,8 @@ static int on_frame_recv(nghttp2_session
session->id, buffer);
}
- session->last_frame_received = apr_time_now();
+ ping_ev_frame_received(session, frame);
+ /* Action for frame types: */
switch (frame->hd.type) {
case NGHTTP2_HEADERS:
stream = nghttp2_session_get_stream_user_data(ngh2, frame->hd.stream_id);
@@ -195,10 +311,6 @@ static int on_frame_recv(nghttp2_session
stream_resume(stream);
break;
case NGHTTP2_PING:
- if (session->check_ping) {
- session->check_ping = 0;
- ping_arrived(session);
- }
break;
case NGHTTP2_PUSH_PROMISE:
break;
@@ -499,7 +611,7 @@ static ssize_t stream_request_data(nghtt
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
- if (stream->session->check_ping) {
+ if (stream->session->ping_state != H2_PING_ST_NONE) {
/* suspend until we hear from the other side */
stream->waiting_on_ping = 1;
status = APR_EAGAIN;
@@ -654,18 +766,13 @@ h2_proxy_session *h2_proxy_session_setup
nghttp2_option_del(option);
nghttp2_session_callbacks_del(cbs);
+ ping_new_session(session, p_conn);
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03362)
"setup session for %s", p_conn->hostname);
}
else {
h2_proxy_session *session = p_conn->data;
- if (!session->check_ping) {
- apr_interval_time_t age = apr_time_now() - session->last_frame_received;
- if (age > apr_time_from_sec(1)) {
- session->check_ping = 1;
- nghttp2_submit_ping(session->ngh2, 0, (const uint8_t *)"nevergonnagiveyouup");
- }
- }
+ ping_reuse_session(session);
}
return p_conn->data;
}
@@ -976,7 +1083,7 @@ static void stream_resume(h2_proxy_strea
static int is_waiting_for_backend(h2_proxy_session *session)
{
- return (session->check_ping
+ return ((session->ping_state != H2_PING_ST_NONE)
|| ((session->suspended->nelts <= 0)
&& !nghttp2_session_want_write(session->ngh2)
&& nghttp2_session_want_read(session->ngh2)));
@@ -1399,7 +1506,6 @@ apr_status_t h2_proxy_session_process(h2
{
apr_status_t status;
int have_written = 0, have_read = 0;
- apr_interval_time_t timeout;
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
"h2_proxy_session(%s): process", session->id);
@@ -1438,25 +1544,11 @@ run_loop:
case H2_PROXYS_ST_WAIT:
if (is_waiting_for_backend(session)) {
- /*
- * We can do a blocking read. There is nothing we want to
- * send or check until we get more data from the backend.
- * The timeout used is either the one currently on the socket
- * as indicated by a passed value of 0 or the ping timeout
- * set via the ping parameter on the worker if set and if
- * we are waiting for a ping.
- * The timeout on the socket is configured via
- * Timeout -> ProxyTimeout -> timeout parameter on the used
- * worker with the later ones taking precedence.
- */
- if (session->check_ping
- && session->p_conn->worker->s->ping_timeout_set) {
- timeout = session->p_conn->worker->s->ping_timeout;
- }
- else {
- timeout = 0;
- }
- status = h2_proxy_session_read(session, 1, timeout);
+ /* we can do a blocking read with the default timeout (as
+ * configured via ProxyTimeout in our socket. There is
+ * nothing we want to send or check until we get more data
+ * from the backend. */
+ status = h2_proxy_session_read(session, 1, 0);
if (status == APR_SUCCESS) {
have_read = 1;
dispatch_event(session, H2_PROXYS_EV_DATA_READ, 0, NULL);
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_session.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_session.h?rev=1878579&r1=1878578&r2=1878579&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_session.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_proxy_session.h Mon Jun 8 07:30:49 2020
@@ -60,6 +60,11 @@ typedef enum {
H2_PROXYS_EV_PRE_CLOSE, /* connection will close after this */
} h2_proxys_event_t;
+typedef enum {
+ H2_PING_ST_NONE, /* normal connection mode, ProxyTimeout rules */
+ H2_PING_ST_AWAIT_ANY, /* waiting for any frame from backend */
+ H2_PING_ST_AWAIT_PING, /* waiting for PING frame from backend */
+} h2_ping_state_t;
typedef struct h2_proxy_session h2_proxy_session;
typedef void h2_proxy_request_done(h2_proxy_session *s, request_rec *r,
@@ -74,7 +79,6 @@ struct h2_proxy_session {
nghttp2_session *ngh2; /* the nghttp2 session itself */
unsigned int aborted : 1;
- unsigned int check_ping : 1;
unsigned int h2_front : 1; /* if front-end connection is HTTP/2 */
h2_proxy_request_done *done;
@@ -94,6 +98,10 @@ struct h2_proxy_session {
apr_bucket_brigade *input;
apr_bucket_brigade *output;
+
+ h2_ping_state_t ping_state;
+ apr_time_t ping_timeout;
+ apr_time_t save_timeout;
};
h2_proxy_session *h2_proxy_session_setup(const char *id, proxy_conn_rec *p_conn,
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_version.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_version.h?rev=1878579&r1=1878578&r2=1878579&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_version.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_version.h Mon Jun 8 07:30:49 2020
@@ -27,7 +27,7 @@
* @macro
* Version number of the http2 module as c string
*/
-#define MOD_HTTP2_VERSION "1.15.8"
+#define MOD_HTTP2_VERSION "1.15.11"
/**
* @macro
@@ -35,6 +35,6 @@
* release. This is a 24 bit number with 8 bits for major number, 8 bits
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
*/
-#define MOD_HTTP2_VERSION_NUM 0x010f08
+#define MOD_HTTP2_VERSION_NUM 0x010f0b
#endif /* mod_h2_h2_version_h */