diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c index 459d8cd734..b2c8060cb0 100644 --- a/modules/proxy/mod_proxy.c +++ b/modules/proxy/mod_proxy.c @@ -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}, diff --git a/modules/proxy/mod_proxy_hcheck.c b/modules/proxy/mod_proxy_hcheck.c index 27dcc1d90f..2ce6757d39 100644 --- a/modules/proxy/mod_proxy_hcheck.c +++ b/modules/proxy/mod_proxy_hcheck.c @@ -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) { + if ((status = hc_get_backend("HCTCP", &backend, hc, ctx)) != 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) { - 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;