]> granicus.if.org Git - apache/commitdiff
For backends which are HTTP/1.1, do a quick test (ping)
authorJim Jagielski <jim@apache.org>
Mon, 16 Aug 2010 18:36:19 +0000 (18:36 +0000)
committerJim Jagielski <jim@apache.org>
Mon, 16 Aug 2010 18:36:19 +0000 (18:36 +0000)
of the "connection" via 100-Continue for reverse
proxies...

ACO and Filip Hanik also helped out with the idea...

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@986090 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
docs/manual/mod/mod_proxy.xml
modules/proxy/mod_proxy_http.c

diff --git a/CHANGES b/CHANGES
index 1a223076f71e90e3659def88e49885226c8b468d..b18b82e7339ab73df6976e0fb56b84854907c18d 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -6,6 +6,10 @@ Changes with Apache 2.3.7
      mod_dav, mod_cache, mod_session: Fix Handling of requests without a path 
      segment. PR: 49246 [Mark Drayton, Jeff Trawick]
 
+  *) mod_proxy_http: Support the 'ping' property for backend HTTP/1.1 servers
+     via leveraging 100-Continue as the initial "request".
+     [Jim Jagielski]
+
   *) core/mod_authz_core: Introduce new access_checker_ex hook that enables
      mod_authz_core to bypass authentication if access should be allowed by
      IP address/env var/... [Stefan Fritsch]
index b32d13a8e734d57c635bcac9b2805df59eb854bc..abdb2ce66794111e14fc32ee8959aea556256310 100644 (file)
@@ -743,17 +743,20 @@ expressions</description>
     </td></tr>
     <tr><td>ping</td>
         <td>0</td>
-        <td>Ping property tells webserver to send a <code>CPING</code>
-        request on ajp13 connection before forwarding a request.
-        The parameter is the delay in seconds to wait for the
-        <code>CPONG</code> reply.
-        This features has been added to avoid problem with hung and
-        busy Tomcat's and require ajp13 ping/pong support which has
-        been implemented on Tomcat 3.3.2+, 4.1.28+ and 5.0.13+.
+        <td>Ping property tells the webserver to "test" the connection to
+        the backend before forwarding the request. For AJP, it causes
+        <module>mod_proxy_ajp</module>to send a <code>CPING</code>
+        request on the ajp13 connection (implemented on Tomcat 3.3.2+, 4.1.28+
+        and 5.0.13+). For HTTP, it causes <module>mod_proxy_http</module>
+        to send a <code>100-Continue</code> to the backend (only valid for
+        HTTP/1.1 - for non HTTP/1.1 backends, this property has no
+        effect). In both cases the parameter is the delay in seconds to wait
+        for the reply.
+        This feature has been added to avoid problems with hung and
+        busy backends.
         This will increase the network traffic during the normal operation
         which could be an issue, but it will lower the
         traffic in case some of the cluster nodes are down or busy.
-        Currently this has an effect only for AJP.
         By adding a postfix of ms the delay can be also set in
         milliseconds.
     </td></tr>
index 44def30543e668d6a86a19c337c27ff87392c866..b668fbe00d0cada4995953641495696338b14db5 100644 (file)
@@ -669,7 +669,7 @@ static int spool_reqbody_cl(apr_pool_t *p,
 
 static
 int ap_proxy_http_request(apr_pool_t *p, request_rec *r,
-                                   proxy_conn_rec *p_conn, conn_rec *origin,
+                                   proxy_conn_rec *p_conn, proxy_worker *worker,
                                    proxy_server_conf *conf,
                                    apr_uri_t *uri,
                                    char *url, char *server_portstr)
@@ -694,6 +694,8 @@ int ap_proxy_http_request(apr_pool_t *p, request_rec *r,
     int force10, rv;
     apr_table_t *headers_in_copy;
     proxy_dir_conf *dconf;
+    conn_rec *origin = p_conn->connection;
+    int do_100_continue;
 
     dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
     header_brigade = apr_brigade_create(p, origin->bucket_alloc);
@@ -702,6 +704,11 @@ int ap_proxy_http_request(apr_pool_t *p, request_rec *r,
      * Send the HTTP/1.1 request to the remote server
      */
 
+    do_100_continue = (worker->ping_timeout_set
+                       && !r->header_only
+                       && (PROXYREQ_REVERSE == r->proxyreq)
+                       && !(apr_table_get(r->subprocess_env, "force-proxy-request-1.0")));
+    
     if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) {
         /*
          * According to RFC 2616 8.2.3 we are not allowed to forward an
@@ -791,6 +798,14 @@ int ap_proxy_http_request(apr_pool_t *p, request_rec *r,
         );
     }
 
+    /* Use HTTP/1.1 100-Continue as quick "HTTP ping" test
+     * to backend
+     */
+    if (do_100_continue) {
+               apr_table_mergen(r->headers_in, "Expect", "100-Continue");
+        r->expecting_100 = 1;
+    }
+
     /* X-Forwarded-*: handling
      *
      * XXX Privacy Note:
@@ -1350,7 +1365,7 @@ apr_status_t ap_proxygetline(apr_bucket_brigade *bb, char *s, int n, request_rec
 static
 apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
                                             proxy_conn_rec *backend,
-                                            conn_rec *origin,
+                                            proxy_worker *worker,
                                             proxy_server_conf *conf,
                                             char *server_portstr) {
     conn_rec *c = r->connection;
@@ -1375,9 +1390,32 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
     int proxy_status = OK;
     const char *original_status_line = r->status_line;
     const char *proxy_status_line = NULL;
-
+    conn_rec *origin = backend->connection;
+    apr_interval_time_t old_timeout = 0;
+
+    int do_100_continue;
+    
+    do_100_continue = (worker->ping_timeout_set
+                       && !r->header_only
+                       && (PROXYREQ_REVERSE == r->proxyreq)
+                       && !(apr_table_get(r->subprocess_env, "force-proxy-request-1.0")));
+    
+    
     bb = apr_brigade_create(p, c->bucket_alloc);
     pass_bb = apr_brigade_create(p, c->bucket_alloc);
+    
+    /* Setup for 100-Continue timeout if appropriate */
+    if (do_100_continue) {
+        apr_socket_timeout_get(backend->sock, &old_timeout);
+        if (worker->ping_timeout != old_timeout) {
+            apr_status_t rc;
+               rc = apr_socket_timeout_set(backend->sock, worker->ping_timeout);
+               if (rc != APR_SUCCESS) {
+               ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,
+                          "proxy: could not set 100-Continue timeout");
+               }
+        }
+    }
 
     /* Get response from the remote server, and pass it up the
      * filter chain
@@ -1406,6 +1444,9 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
             if (APR_STATUS_IS_TIMEUP(rc)) {
                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
                               "proxy: read timeout");
+                if (do_100_continue) {
+                    return ap_proxyerror(r, HTTP_SERVICE_UNAVAILABLE, "Timeout on 100-Continue");
+                }
             }
             /*
              * If we are a reverse proxy request shutdown the connection
@@ -1641,6 +1682,12 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
 
         if (ap_is_HTTP_INFO(proxy_status)) {
             interim_response++;
+            /* Reset to old timeout iff we've adjusted it */
+            if (do_100_continue
+                && (r->status == HTTP_CONTINUE)
+                && (worker->ping_timeout != old_timeout)) {
+                    apr_socket_timeout_set(backend->sock, old_timeout);
+            }
         }
         else {
             interim_response = 0;
@@ -1923,6 +1970,7 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker,
     proxy_conn_rec *backend = NULL;
     int is_ssl = 0;
     conn_rec *c = r->connection;
+    int retry = 0;
     /*
      * Use a shorter-lived pool to reduce memory usage
      * and avoid a memory leak
@@ -1990,48 +2038,68 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker,
         backend->close = 1;
     }
 
-    /* Step One: Determine Who To Connect To */
-    if ((status = ap_proxy_determine_connection(p, r, conf, worker, backend,
-                                                uri, &url, proxyname,
+    while (retry < 2) {
+        char *locurl = url;
+
+       /* Step One: Determine Who To Connect To */
+               if ((status = ap_proxy_determine_connection(p, r, conf, worker, backend,
+                                                uri, &locurl, proxyname,
                                                 proxyport, server_portstr,
                                                 sizeof(server_portstr))) != OK)
-        goto cleanup;
-
-    /* Step Two: Make the Connection */
-    if (ap_proxy_connect_backend(proxy_function, backend, worker, r->server)) {
-        status = HTTP_SERVICE_UNAVAILABLE;
-        goto cleanup;
-    }
-
-    /* Step Three: Create conn_rec */
-    if (!backend->connection) {
-        if ((status = ap_proxy_connection_create(proxy_function, backend,
-                                                 c, r->server)) != OK)
-            goto cleanup;
-        /*
-         * On SSL connections set a note on the connection what CN is
-         * requested, such that mod_ssl can check if it is requested to do
-         * so.
+               break;
+
+       /* Step Two: Make the Connection */
+       if (ap_proxy_connect_backend(proxy_function, backend, worker, r->server)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                         "proxy: HTTP: failed to make connection to backend: %s",
+                         backend->hostname);
+               status = HTTP_SERVICE_UNAVAILABLE;
+               break;
+       }
+
+       /* Step Three: Create conn_rec */
+       if (!backend->connection) {
+                       if ((status = ap_proxy_connection_create(proxy_function, backend,
+                                                     c, r->server)) != OK)
+                               break;
+                       /*
+             * On SSL connections set a note on the connection what CN is
+                * requested, such that mod_ssl can check if it is requested to do
+                * so.
+                */
+                       if (is_ssl) {
+                               apr_table_set(backend->connection->notes, "proxy-request-hostname",
+                                 uri->hostname);
+                       }
+       }
+
+       /* Step Four: Send the Request
+         * On the off-chance that we forced a 100-Continue as a
+         * kinda HTTP ping test, allow for retries
          */
-        if (is_ssl) {
-            apr_table_set(backend->connection->notes, "proxy-request-hostname",
-                          uri->hostname);
+       if ((status = ap_proxy_http_request(p, r, backend, worker,
+                                        conf, uri, locurl, server_portstr)) != OK) {
+            if ((status == HTTP_SERVICE_UNAVAILABLE) && worker->ping_timeout_set) {
+               backend->close = 1;
+                       ap_log_error(APLOG_MARK, APLOG_INFO, status, r->server,
+                            "proxy: HTTP: 100-Continue failed to %pI (%s)",
+                            worker->cp->addr, worker->hostname);
+                       retry++;
+                       continue;
+            } else {
+                break;
+            }
+
         }
-    }
 
-    /* Step Four: Send the Request */
-    if ((status = ap_proxy_http_request(p, r, backend, backend->connection,
-                                        conf, uri, url, server_portstr)) != OK)
-        goto cleanup;
+       /* Step Five: Receive the Response... Fall thru to cleanup */
+       status = ap_proxy_http_process_response(p, r, backend, worker,
+                                                conf, server_portstr);
 
-    /* Step Five: Receive the Response */
-    if ((status = ap_proxy_http_process_response(p, r, backend,
-                                                 backend->connection,
-                                                 conf, server_portstr)) != OK)
-        goto cleanup;
+        break;
+    }
 
     /* Step Six: Clean Up */
-
 cleanup:
     if (backend) {
         if (status != OK)