]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
And we now allow for health checks via OPTIONS *
authorJim Jagielski <jim@apache.org>
Mon, 18 Jan 2016 19:44:20 +0000 (19:44 +0000)
committerJim Jagielski <jim@apache.org>
Mon, 18 Jan 2016 19:44:20 +0000 (19:44 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1725328 13f79535-47bb-0310-9956-ffa450edef68

modules/proxy/mod_proxy.c
modules/proxy/mod_proxy_hcheck.c

index 459d8cd7343624dca7bb33a49cd9ef906abd9b8d..b2c8060cb0414e58dcad314f0f0e3d8179ba7c82 100644 (file)
@@ -46,7 +46,7 @@ static APR_OPTIONAL_FN_TYPE(set_worker_hc_param) *set_worker_hc_param_f = NULL;
 hcmethods_t hcmethods[] = {
         {NONE, "NONE", 1},
         {TCP, "TCP", 1},
-        {OPTIONS, "OPTIONS", 0},
+        {OPTIONS, "OPTIONS", 1},
         {HEAD, "HEAD", 0},
         {GET, "GET", 0},
         {CPING, "CPING", 0},
index 27dcc1d90faa7596ca13303aef5168d7524ca1fc..2ce6757d39df80d202375879c454475c59672529 100644 (file)
@@ -39,6 +39,7 @@ typedef struct {
 
 typedef struct {
     apr_pool_t *p;
+    apr_bucket_alloc_t *ba;
     apr_array_header_t *templates;
     apr_array_header_t *conditions;
     ap_watchdog_t *watchdog;
@@ -51,6 +52,7 @@ static void *hc_create_config(apr_pool_t *p, server_rec *s)
 {
     sctx_t *ctx = (sctx_t *) apr_palloc(p, sizeof(sctx_t));
     apr_pool_create(&ctx->p, p);
+    ctx->ba = apr_bucket_alloc_create(p);
     ctx->templates = apr_array_make(ctx->p, 10, sizeof(hc_template_t));
     ctx->conditions = apr_array_make(ctx->p, 10, sizeof(hc_condition_t));
     ctx->hcworkers = apr_hash_make(ctx->p);
@@ -59,75 +61,6 @@ static void *hc_create_config(apr_pool_t *p, server_rec *s)
     return ctx;
 }
 
-static proxy_worker *hc_get_hcworker(sctx_t *ctx, proxy_worker *worker)
-{
-    proxy_worker *hc = NULL;
-    const char* wptr;
-
-    wptr = apr_psprintf(ctx->p, "%pp", worker);
-    hc = (proxy_worker *)apr_hash_get(ctx->hcworkers, wptr, APR_HASH_KEY_STRING);
-    if (!hc) {
-        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO()
-                     "Creating hc worker %s for %s://%s:%d",
-                     wptr, worker->s->scheme, worker->s->hostname,
-                     (int)worker->s->port);
-
-        ap_proxy_define_worker(ctx->p, &hc, NULL, NULL, worker->s->name, 0);
-        PROXY_STRNCPY(hc->s->name,     wptr);
-        PROXY_STRNCPY(hc->s->hostname, worker->s->hostname);
-        PROXY_STRNCPY(hc->s->scheme,   worker->s->scheme);
-        hc->hash.def = hc->s->hash.def = ap_proxy_hashfunc(hc->s->name, PROXY_HASHFUNC_DEFAULT);
-        hc->hash.fnv = hc->s->hash.fnv = ap_proxy_hashfunc(hc->s->name, PROXY_HASHFUNC_FNV);
-        hc->s->port = worker->s->port;
-        /* Do not disable worker in case of errors */
-        hc->s->status |= PROXY_WORKER_IGNORE_ERRORS;
-        /* Mark as the "generic" worker */
-        hc->s->status |= PROXY_WORKER_GENERIC;
-        ap_proxy_initialize_worker(hc, ctx->s, ctx->p);
-        /* Enable address cache for generic reverse worker */
-        hc->s->is_address_reusable = 1;
-        /* tuck away since we need the preparsed address */
-        hc->cp->addr = worker->cp->addr;
-        hc->s->method = worker->s->method;
-        apr_hash_set(ctx->hcworkers, wptr, APR_HASH_KEY_STRING, hc);
-    }
-    return hc;
-}
-
-static apr_status_t hc_init_worker(sctx_t *ctx, proxy_worker *worker) {
-    /*
-     * Since this is the watchdog, workers never actually handle a
-     * request here, and so the local data isn't initialized (of
-     * course, the shared memory is). So we need to bootstrap
-     * worker->cp. Note, we only need do this once.
-     */
-    if (!worker->cp) {
-        apr_status_t rv;
-        apr_status_t err = APR_SUCCESS;
-        rv = ap_proxy_initialize_worker(worker, ctx->s, ctx->p);
-        if (rv != APR_SUCCESS) {
-            ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ctx->s, APLOGNO() "Cannot init worker");
-            return rv;
-        }
-        /*
-         * normally, this is done in ap_proxy_determine_connection().
-         * TODO: Look at using ap_proxy_determine_connection() with a
-         * fake request_rec
-         */
-        err = apr_sockaddr_info_get(&(worker->cp->addr), worker->s->hostname, APR_UNSPEC,
-                                    worker->s->port, 0, ctx->p);
-
-        if (err != APR_SUCCESS) {
-            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO()
-                         "DNS lookup failure for: %s:%d",
-                         worker->s->hostname, (int)worker->s->port);
-            return err;
-        }
-    }
-    return APR_SUCCESS;
-}
-
-
 /*
  * This serves double duty by not only validating (and creating)
  * the health-check template, but also ties into set_worker_param()
@@ -325,6 +258,143 @@ static const char *set_hc_template(cmd_parms *cmd, void *dummy, const char *arg)
 
     return NULL;
 }
+
+static request_rec *create_request_rec(conn_rec *conn)
+{
+    request_rec *r;
+    apr_pool_t *p;
+
+    apr_pool_create(&p, conn->pool);
+    apr_pool_tag(p, "request");
+    r = apr_pcalloc(p, sizeof(request_rec));
+    r->pool            = p;
+    r->connection      = conn;
+    r->server          = conn->base_server;
+
+    r->user            = NULL;
+    r->ap_auth_type    = NULL;
+
+    r->allowed_methods = ap_make_method_list(p, 2);
+
+    r->headers_in      = apr_table_make(r->pool, 25);
+    r->trailers_in     = apr_table_make(r->pool, 5);
+    r->subprocess_env  = apr_table_make(r->pool, 25);
+    r->headers_out     = apr_table_make(r->pool, 12);
+    r->err_headers_out = apr_table_make(r->pool, 5);
+    r->trailers_out    = apr_table_make(r->pool, 5);
+    r->notes           = apr_table_make(r->pool, 5);
+
+    r->request_config  = ap_create_request_config(r->pool);
+    /* Must be set before we run create request hook */
+
+    r->proto_output_filters = conn->output_filters;
+    r->output_filters  = r->proto_output_filters;
+    r->proto_input_filters = conn->input_filters;
+    r->input_filters   = r->proto_input_filters;
+    r->per_dir_config  = r->server->lookup_defaults;
+
+    r->sent_bodyct     = 0;                      /* bytect isn't for body */
+
+    r->read_length     = 0;
+    r->read_body       = REQUEST_NO_BODY;
+
+    r->status          = HTTP_OK;  /* Until further notice */
+    r->header_only     = 0;
+    r->the_request     = NULL;
+
+    /* Begin by presuming any module can make its own path_info assumptions,
+     * until some module interjects and changes the value.
+     */
+    r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
+
+    r->useragent_addr = conn->client_addr;
+    r->useragent_ip = conn->client_ip;
+
+
+    /* Time to populate r with the data we have. */
+    r->method = "OPTIONS";
+    /* Provide quick information about the request method as soon as known */
+    r->method_number = ap_method_number_of(r->method);
+    if (r->method_number == M_GET && r->method[0] == 'H') {
+        r->header_only = 1;
+    }
+
+    r->protocol = (char*)"HTTP/1.1";
+    r->proto_num = HTTP_VERSION(1, 1);
+
+    r->hostname = NULL;
+
+    return r;
+}
+
+static proxy_worker *hc_get_hcworker(sctx_t *ctx, proxy_worker *worker)
+{
+    proxy_worker *hc = NULL;
+    const char* wptr;
+
+    wptr = apr_psprintf(ctx->p, "%pp", worker);
+    hc = (proxy_worker *)apr_hash_get(ctx->hcworkers, wptr, APR_HASH_KEY_STRING);
+    if (!hc) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO()
+                     "Creating hc worker %s for %s://%s:%d",
+                     wptr, worker->s->scheme, worker->s->hostname,
+                     (int)worker->s->port);
+
+        ap_proxy_define_worker(ctx->p, &hc, NULL, NULL, worker->s->name, 0);
+        PROXY_STRNCPY(hc->s->name,     wptr);
+        PROXY_STRNCPY(hc->s->hostname, worker->s->hostname);
+        PROXY_STRNCPY(hc->s->scheme,   worker->s->scheme);
+        hc->hash.def = hc->s->hash.def = ap_proxy_hashfunc(hc->s->name, PROXY_HASHFUNC_DEFAULT);
+        hc->hash.fnv = hc->s->hash.fnv = ap_proxy_hashfunc(hc->s->name, PROXY_HASHFUNC_FNV);
+        hc->s->port = worker->s->port;
+        /* Do not disable worker in case of errors */
+        hc->s->status |= PROXY_WORKER_IGNORE_ERRORS;
+        /* Mark as the "generic" worker */
+        hc->s->status |= PROXY_WORKER_GENERIC;
+        ap_proxy_initialize_worker(hc, ctx->s, ctx->p);
+        /* Enable address cache for generic reverse worker */
+        hc->s->is_address_reusable = 1;
+        /* tuck away since we need the preparsed address */
+        hc->cp->addr = worker->cp->addr;
+        hc->s->method = worker->s->method;
+        apr_hash_set(ctx->hcworkers, wptr, APR_HASH_KEY_STRING, hc);
+    }
+    return hc;
+}
+
+static apr_status_t hc_init_worker(sctx_t *ctx, proxy_worker *worker) {
+    /*
+     * Since this is the watchdog, workers never actually handle a
+     * request here, and so the local data isn't initialized (of
+     * course, the shared memory is). So we need to bootstrap
+     * worker->cp. Note, we only need do this once.
+     */
+    if (!worker->cp) {
+        apr_status_t rv;
+        apr_status_t err = APR_SUCCESS;
+        rv = ap_proxy_initialize_worker(worker, ctx->s, ctx->p);
+        if (rv != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ctx->s, APLOGNO() "Cannot init worker");
+            return rv;
+        }
+        /*
+         * normally, this is done in ap_proxy_determine_connection().
+         * TODO: Look at using ap_proxy_determine_connection() with a
+         * fake request_rec
+         */
+        err = apr_sockaddr_info_get(&(worker->cp->addr), worker->s->hostname, APR_UNSPEC,
+                                    worker->s->port, 0, ctx->p);
+
+        if (err != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO()
+                         "DNS lookup failure for: %s:%d",
+                         worker->s->hostname, (int)worker->s->port);
+            return err;
+        }
+    }
+    return APR_SUCCESS;
+}
+
 static apr_status_t backend_cleanup(const char *proxy_function, proxy_conn_rec *backend,
                                     server_rec *s, int status)
 {
@@ -345,6 +415,28 @@ static apr_status_t backend_cleanup(const char *proxy_function, proxy_conn_rec *
 
 }
 
+static int hc_get_backend(const char *proxy_function, proxy_conn_rec **backend,
+                          proxy_worker *hc, sctx_t *ctx)
+{
+    int status;
+    status = ap_proxy_acquire_connection("HCTCP", backend, hc, ctx->s);
+    if (status == OK) {
+        (*backend)->addr = hc->cp->addr;
+        (*backend)->pool = ctx->p;
+        (*backend)->hostname = hc->s->hostname;
+        if (strcmp(hc->s->scheme, "https") == 0) {
+            if (!ap_proxy_ssl_enable(NULL)) {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO()
+                              "mod_ssl not configured?");
+                return !OK;
+            }
+            (*backend)->is_ssl = 1;
+        }
+
+    }
+    return status;
+}
+
 static apr_status_t hc_check_tcp(sctx_t *ctx, apr_pool_t *p, proxy_worker *worker)
 {
     int status;
@@ -353,7 +445,7 @@ static apr_status_t hc_check_tcp(sctx_t *ctx, apr_pool_t *p, proxy_worker *worke
 
     hc = hc_get_hcworker(ctx, worker);
 
-    status = ap_proxy_acquire_connection("HCTCP", &backend, hc, ctx->s);
+    status = hc_get_backend("HCTCP", &backend, hc, ctx);
     if (status == OK) {
         backend->addr = hc->cp->addr;
         status = ap_proxy_connect_backend("HCTCP", backend, hc, ctx->s);
@@ -364,7 +456,74 @@ static apr_status_t hc_check_tcp(sctx_t *ctx, apr_pool_t *p, proxy_worker *worke
     return backend_cleanup("HCTCP", backend, ctx->s, status);
 }
 
-#if 0
+static void hc_send(sctx_t *ctx, apr_pool_t *p, const char *out, proxy_conn_rec *backend)
+{
+    apr_bucket_brigade *tmp_bb = apr_brigade_create(p, ctx->ba);
+    APR_BRIGADE_INSERT_TAIL(tmp_bb, apr_bucket_pool_create(out, strlen(out), p,
+                            ctx->ba));
+    APR_BRIGADE_INSERT_TAIL(tmp_bb, apr_bucket_flush_create(ctx->ba));
+    ap_pass_brigade(backend->connection->output_filters, tmp_bb);
+    apr_brigade_destroy(tmp_bb);
+}
+
+static int hc_read_headers(sctx_t *ctx, request_rec *r)
+{
+    char buffer[HUGE_STRING_LEN];
+    int len;
+
+    len = ap_getline(buffer, sizeof(buffer), r, 1);
+    if (len <= 0) {
+        return !OK;
+    }
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO()
+            "%s", buffer);
+    /* for the below, see ap_proxy_http_process_response() */
+    if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
+        int major, minor;
+        char keepchar;
+        int proxy_status = OK;
+        const char *proxy_status_line = NULL;
+
+        major = buffer[5] - '0';
+        minor = buffer[7] - '0';
+        if ((major != 1) || (len >= sizeof(buffer)-1)) {
+            return !OK;
+        }
+
+        keepchar = buffer[12];
+        buffer[12] = '\0';
+        proxy_status = atoi(&buffer[9]);
+        if (keepchar != '\0') {
+            buffer[12] = keepchar;
+        } else {
+            buffer[12] = ' ';
+            buffer[13] = '\0';
+        }
+        proxy_status_line = apr_pstrdup(r->pool, &buffer[9]);
+        r->status = proxy_status;
+        r->status_line = proxy_status_line;
+    } else {
+        return !OK;
+    }
+    /* OK, 1st line is OK... scarf in the headers */
+    while ((len = ap_getline(buffer, sizeof(buffer), r, 1)) > 0) {
+        char *value, *end;
+        if (!(value = strchr(buffer, ':'))) {
+            return !OK;
+        }
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO()
+                "%s", buffer);
+        *value = '\0';
+        ++value;
+        while (apr_isspace(*value))
+            ++value;            /* Skip to start of value   */
+        for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --end)
+            *end = '\0';
+        apr_table_add(r->headers_out, buffer, value);
+    }
+    return OK;
+}
+
 static apr_status_t hc_check_options(sctx_t *ctx, apr_pool_t *p, proxy_worker *worker)
 {
     int status;
@@ -372,37 +531,32 @@ static apr_status_t hc_check_options(sctx_t *ctx, apr_pool_t *p, proxy_worker *w
     proxy_worker *hc;
     conn_rec c;
     request_rec *r;
-
-    proxy_server_conf *conf = (proxy_server_conf *)ap_get_module_config(ctx->s->module_config,
-                                                                        &proxy_module);
+    const char *req;
 
     hc = hc_get_hcworker(ctx, worker);
 
-    if ((status = ap_proxy_acquire_connection("HCTCP", &backend, hc, ctx->s)) != OK) {
-        return backend_cleanup("HCTCP", backend, ctx->s, status);
-    }
-/*
-    if ((status = ap_proxy_determine_connection(p, r, conf, hc,
-                    backend, uri, &newurl, proxyname, proxyport,
-                    server_portstr, sizeof(server_portstr))) != OK) {
+    if ((status = hc_get_backend("HCTCP", &backend, hc, ctx)) != OK) {
         return backend_cleanup("HCTCP", backend, ctx->s, status);
     }
-*/
     if ((status = ap_proxy_connect_backend("HCTCP", backend, hc, ctx->s)) != OK) {
         return backend_cleanup("HCTCP", backend, ctx->s, status);
     }
 
     if (!backend->connection) {
-        status = ap_proxy_connection_create("HCTCP", backend, &c, ctx->s);
-        if (status != OK) {
+        if ((status = ap_proxy_connection_create("HCTCP", backend, &c, ctx->s)) != OK) {
             return backend_cleanup("HCTCP", backend, ctx->s, status);
         }
     }
+    req = apr_psprintf(p, "OPTIONS * HTTP/1.1\r\nHost: %s://%s:%d\r\n\r\n",
+            hc->s->scheme, hc->s->hostname, (int)hc->s->port);
+    hc_send(ctx, p, req, backend);
 
-    return APR_SUCCESS;
-}
+    r = create_request_rec(backend->connection);
+    r->pool = p;
+    status = hc_read_headers(ctx, r);
+
+    return backend_cleanup("HCTCP", backend, ctx->s, status);}
 
-#endif
 
 static void hc_check(sctx_t *ctx, apr_pool_t *p, apr_time_t now,
                      proxy_worker *worker)
@@ -418,6 +572,10 @@ static void hc_check(sctx_t *ctx, apr_pool_t *p, apr_time_t now,
             rv = hc_check_tcp(ctx, p, worker);
             break;
 
+        case OPTIONS:
+             rv = hc_check_options(ctx, p, worker);
+             break;
+
         default:
             rv = APR_ENOTIMPL;
             break;
@@ -474,9 +632,8 @@ static apr_status_t hc_watchdog_callback(int state, void *data,
                         worker = *workers;
                         /* TODO: REMOVE ap_log_error call */
                         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO()
-                                     "Checking %s worker: %s  [%d] (%lu %lu %lu)", balancer->s->name,
-                                     worker->s->name, worker->s->method, (unsigned long)now,
-                                     (unsigned long)worker->s->updated, (unsigned long)worker->s->interval);
+                                     "Checking %s worker: %s  [%d] (%pp)", balancer->s->name,
+                                     worker->s->name, worker->s->method, worker);
                         if ((worker->s->method != NONE) && (now > worker->s->updated + worker->s->interval)) {
                             if ((rv = hc_init_worker(ctx, worker)) != APR_SUCCESS) {
                                 return rv;