module AP_MODULE_DECLARE_DATA proxy_ajp_module;
-int ap_proxy_ajp_canon(request_rec *r, char *url);
-int ap_proxy_ajp_handler(request_rec *r, proxy_server_conf *conf,
- char *url, const char *proxyname,
- apr_port_t proxyport);
-
-typedef struct {
- const char *name;
- apr_port_t port;
- apr_sockaddr_t *addr;
- apr_socket_t *sock;
- int close;
- void *data; /* To store ajp data */
-} proxy_ajp_conn_t;
-
-static apr_status_t ap_proxy_ajp_cleanup(request_rec *r,
- proxy_ajp_conn_t *p_conn,
- proxy_conn_rec *backend);
-
/*
* Canonicalise http-like URLs.
* scheme is the scheme for the URL
return OK;
}
-static
-apr_status_t ap_proxy_http_determine_connection(apr_pool_t *p, request_rec *r,
- proxy_ajp_conn_t *p_conn,
- conn_rec *c,
- proxy_server_conf *conf,
- apr_uri_t *uri,
- char **url,
- const char *proxyname,
- apr_port_t proxyport,
- char *server_portstr,
- int server_portstr_size) {
- int server_port;
- apr_status_t err;
- apr_sockaddr_t *uri_addr;
- /*
- * Break up the URL to determine the host to connect to
- */
-
- /* we break the URL into host, port, uri */
- if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {
- return ap_proxyerror(r, HTTP_BAD_REQUEST,
- apr_pstrcat(p,"URI cannot be parsed: ", *url,
- NULL));
- }
- if (!uri->port) {
- uri->port = apr_uri_port_of_scheme(uri->scheme);
- }
-
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "proxy: AJP connecting %s to %s:%d", *url, uri->hostname,
- uri->port);
-
- /* do a DNS lookup for the destination host */
- /* see memory note above */
- err = apr_sockaddr_info_get(&uri_addr, apr_pstrdup(c->pool, uri->hostname),
- APR_UNSPEC, uri->port, 0, c->pool);
-
- /* allocate these out of the connection pool - the check on
- * r->connection->id makes sure that this string does not get accessed
- * past the connection lifetime */
- /* are we connecting directly, or via a proxy? */
- if (proxyname) {
- p_conn->name = apr_pstrdup(c->pool, proxyname);
- p_conn->port = proxyport;
- /* see memory note above */
- err = apr_sockaddr_info_get(&p_conn->addr, p_conn->name, APR_UNSPEC,
- p_conn->port, 0, c->pool);
- } else {
- p_conn->name = apr_pstrdup(c->pool, uri->hostname);
- p_conn->port = uri->port;
- p_conn->addr = uri_addr;
- *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
- uri->query ? uri->query : "",
- uri->fragment ? "#" : "",
- uri->fragment ? uri->fragment : "", NULL);
- }
-
- if (err != APR_SUCCESS) {
- return ap_proxyerror(r, HTTP_BAD_GATEWAY,
- apr_pstrcat(p, "DNS lookup failure for: ",
- p_conn->name, NULL));
- }
-
- /* Get the server port for the Via headers */
- {
- server_port = ap_get_server_port(r);
- if (ap_is_default_port(server_port, r)) {
- strcpy(server_portstr,"");
- } else {
- apr_snprintf(server_portstr, server_portstr_size, ":%d",
- server_port);
- }
- }
-
- /* check if ProxyBlock directive on this host */
- if (OK != ap_proxy_checkproxyblock(r, conf, uri_addr)) {
- return ap_proxyerror(r, HTTP_FORBIDDEN,
- "Connect to remote machine blocked");
- }
- return OK;
-}
-
-static
-apr_status_t ap_proxy_http_create_connection(apr_pool_t *p, request_rec *r,
- proxy_ajp_conn_t *p_conn,
- conn_rec *c, conn_rec **origin,
- proxy_conn_rec *backend,
- proxy_server_conf *conf,
- const char *proxyname) {
- int failed = 0, new_conn = 0;
- apr_socket_t *client_socket = NULL;
-
- /* We have determined who to connect to. Now make the connection, supporting
- * a KeepAlive connection.
- */
-
- /* get all the possible IP addresses for the destname and loop through them
- * until we get a successful connection
- */
-
- /* if a keepalive socket is already open, check whether it must stay
- * open, or whether it should be closed and a new socket created.
- */
- /* see memory note above */
- if (backend->connection) {
- client_socket = ap_get_module_config(backend->connection->conn_config, &core_module);
- if ((backend->connection->id == c->id) &&
- (backend->port == p_conn->port) &&
- (backend->hostname) &&
- (!apr_strnatcasecmp(backend->hostname, p_conn->name))) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "proxy: keepalive address match (keep original socket)");
- } else {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "proxy: keepalive address mismatch / connection has"
- " changed (close old socket (%s/%s, %d/%d))",
- p_conn->name, backend->hostname, p_conn->port,
- backend->port);
- apr_socket_close(client_socket);
- backend->connection = NULL;
- }
- }
-
- /* get a socket - either a keepalive one, or a new one */
- new_conn = 1;
- if ((backend->connection) && (backend->connection->id == c->id)) {
- apr_size_t buffer_len = 1;
- char test_buffer[1];
- apr_status_t socket_status;
- apr_interval_time_t current_timeout;
-
- /* use previous keepalive socket */
- *origin = backend->connection;
- p_conn->sock = client_socket;
- new_conn = 0;
-
- /* save timeout */
- apr_socket_timeout_get(p_conn->sock, ¤t_timeout);
- /* set no timeout */
- apr_socket_timeout_set(p_conn->sock, 0);
- socket_status = apr_socket_recv(p_conn->sock, test_buffer, &buffer_len);
- /* put back old timeout */
- apr_socket_timeout_set(p_conn->sock, current_timeout);
- if ( APR_STATUS_IS_EOF(socket_status) ) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
- "proxy: AJP: previous connection is closed");
- new_conn = 1;
- }
- }
- if (new_conn) {
-
- /* create a new socket */
- backend->connection = NULL;
-
- /*
- * At this point we have a list of one or more IP addresses of
- * the machine to connect to. If configured, reorder this
- * list so that the "best candidate" is first try. "best
- * candidate" could mean the least loaded server, the fastest
- * responding server, whatever.
- *
- * For now we do nothing, ie we get DNS round robin.
- * XXX FIXME
- */
- failed = ap_proxy_connect_to_backend(&p_conn->sock, "AJP",
- p_conn->addr, p_conn->name,
- conf, r->server, c->pool);
-
- /* handle a permanent error on the connect */
- if (failed) {
- if (proxyname) {
- return DECLINED;
- } else {
- return HTTP_BAD_GATEWAY;
- }
- }
-
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "proxy: socket is connected");
-
- /* the socket is now open, create a new backend server connection */
- *origin = ap_run_create_connection(c->pool, r->server, p_conn->sock,
- r->connection->id,
- r->connection->sbh, c->bucket_alloc);
- if (!*origin) {
- /* the peer reset the connection already; ap_run_create_connection()
- * closed the socket
- */
- ap_log_error(APLOG_MARK, APLOG_ERR, 0,
- r->server, "proxy: an error occurred creating a "
- "new connection to %pI (%s)", p_conn->addr,
- p_conn->name);
- apr_socket_close(p_conn->sock);
- return HTTP_INTERNAL_SERVER_ERROR;
- }
- backend->connection = *origin;
- backend->hostname = apr_pstrdup(c->pool, p_conn->name);
- backend->port = p_conn->port;
-
- if (backend->is_ssl) {
- if (!ap_proxy_ssl_enable(backend->connection)) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0,
- r->server, "proxy: failed to enable ssl support "
- "for %pI (%s)", p_conn->addr, p_conn->name);
- return HTTP_INTERNAL_SERVER_ERROR;
- }
- }
- else {
- ap_proxy_ssl_disable(backend->connection);
- }
-
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
- "proxy: connection complete to %pI (%s)",
- p_conn->addr, p_conn->name);
-
- /* set up the connection filters */
- ap_run_pre_connection(*origin, p_conn->sock);
- }
- return OK;
-}
-
-
static
apr_status_t ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
- proxy_ajp_conn_t *p_conn, conn_rec *origin,
+ proxy_conn_rec *conn,
+ conn_rec *origin,
proxy_server_conf *conf,
apr_uri_t *uri,
char *url, char *server_portstr)
*/
/* send request headers */
- status = ajp_send_header(p_conn->sock,r);
+ status = ajp_send_header(conn->sock, r);
if (status != APR_SUCCESS) {
+ conn->close++;
ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
"proxy: request failed to %pI (%s)",
- p_conn->addr, p_conn->name);
+ conn->worker->cp->addr,
+ conn->worker->hostname);
return status;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"proxy: got %d byte of data", bufsiz);
if (bufsiz > 0) {
- status = ajp_send_data_msg(p_conn->sock, r, msg, bufsiz);
+ status = ajp_send_data_msg(conn->sock, r, msg, bufsiz);
if (status != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
"proxy: request failed to %pI (%s)",
- p_conn->addr, p_conn->name);
+ conn->worker->cp->addr,
+ conn->worker->hostname);
return status;
}
}
}
/* read the response */
- status = ajp_read_header(p_conn->sock, r,
- (ajp_msg_t **)&(p_conn->data));
+ status = ajp_read_header(conn->sock, r,
+ (ajp_msg_t **)&(conn->data));
if (status != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
"proxy: request failed to %pI (%s)",
- p_conn->addr, p_conn->name);
+ conn->worker->cp->addr,
+ conn->worker->hostname);
return status;
}
/* parse the reponse */
- result = ajp_parse_type(r, p_conn->data);
+ result = ajp_parse_type(r, conn->data);
if (result == CMD_AJP13_SEND_HEADERS) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"proxy: got response from %pI (%s)",
- p_conn->addr, p_conn->name);
+ conn->worker->cp->addr,
+ conn->worker->hostname);
return APR_SUCCESS;
}
*/
static
apr_status_t ap_proxy_ajp_process_response(apr_pool_t * p, request_rec *r,
- proxy_ajp_conn_t *p_conn,
conn_rec *origin,
proxy_conn_rec *backend,
proxy_server_conf *conf,
bb = apr_brigade_create(p, c->bucket_alloc);
- type = ajp_parse_type(r, p_conn->data);
+ type = ajp_parse_type(r, backend->data);
status = APR_SUCCESS;
while (type != CMD_AJP13_END_RESPONSE) {
if (type == CMD_AJP13_SEND_HEADERS) {
/* AJP13_SEND_HEADERS: process them */
- status = ajp_parse_header(r, p_conn->data);
+ status = ajp_parse_header(r, backend->data);
if (status != APR_SUCCESS) {
break;
}
- } else if (type == CMD_AJP13_SEND_BODY_CHUNK) {
+ }
+ else if (type == CMD_AJP13_SEND_BODY_CHUNK) {
/* AJP13_SEND_BODY_CHUNK: piece of data */
apr_uint16_t size;
char *buff;
- status = ajp_parse_data(r, p_conn->data, &size, &buff);
+ status = ajp_parse_data(r, backend->data, &size, &buff);
e = apr_bucket_transient_create(buff, size, c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, e);
- } else {
+ }
+ else {
status = APR_EGENERAL;
break;
}
/* Read the next message */
- status = ajp_read_header(p_conn->sock, r,
- (ajp_msg_t **)&(p_conn->data));
+ status = ajp_read_header(backend->sock, r,
+ (ajp_msg_t **)&(backend->data));
if (status != APR_SUCCESS) {
break;
}
- type = ajp_parse_type(r, p_conn->data);
+ type = ajp_parse_type(r, backend->data);
}
if (status != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"proxy: error reading headers from remote "
- "server %s:%d", p_conn->name, p_conn->port);
+ "server %s:%d",
+ backend->worker->cp->addr,
+ backend->worker->hostname);
return ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server");
}
return OK;
}
-static
-apr_status_t ap_proxy_ajp_cleanup(request_rec *r, proxy_ajp_conn_t *p_conn,
- proxy_conn_rec *backend) {
- /* If the connection has been signalled
- * to close, close the socket and clean up
- */
-
- /* if the connection is < HTTP/1.1, or Connection: close,
- * we close the socket, otherwise we leave it open for KeepAlive support
- */
- if (p_conn->close) {
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
- "ap_proxy_ajp_cleanup closing");
- if (p_conn->sock) {
- apr_socket_close(p_conn->sock);
- p_conn->sock = NULL;
- backend->connection = NULL;
- }
- }
- return OK;
-}
-
/*
* This handles http:// URLs, and other URLs using a remote proxy over http
* If proxyhost is NULL, then contact the server directly, otherwise
* we return DECLINED so that we can try another proxy. (Or the direct
* route.)
*/
-int ap_proxy_ajp_handler(request_rec *r, proxy_server_conf *conf,
- char *url, const char *proxyname,
- apr_port_t proxyport)
+int ap_proxy_ajp_handler(request_rec *r, proxy_worker *worker,
+ proxy_server_conf *conf,
+ char *url, const char *proxyname,
+ apr_port_t proxyport)
{
int status;
char server_portstr[32];
conn_rec *origin = NULL;
proxy_conn_rec *backend = NULL;
int is_ssl = 0;
+ const char *scheme = "ajp";
/* Note: Memory pool allocation.
* A downstream keepalive connection is always connected to the existence
apr_pool_t *p = r->connection->pool;
conn_rec *c = r->connection;
apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri));
- proxy_ajp_conn_t *p_conn = apr_pcalloc(r->connection->pool,
- sizeof(*p_conn));
- /* only use stored info for top-level pages. Sub requests don't share
- * in keepalives
- */
- if (!r->main) {
- backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config,
- &proxy_ajp_module);
- }
- /* create space for state information */
- if (!backend) {
- backend = apr_pcalloc(c->pool, sizeof(proxy_conn_rec));
- backend->connection = NULL;
- backend->hostname = NULL;
- backend->port = 0;
- if (!r->main) {
- ap_set_module_config(c->conn_config, &proxy_ajp_module, backend);
- }
- }
-
if (strncasecmp(url, "ajp:", 4) != 0) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"proxy: AJP: declining URL %s", url);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"proxy: AJP: serving URL %s", url);
-
+
/* only use stored info for top-level pages. Sub requests don't share
* in keepalives
*/
}
/* create space for state information */
if (!backend) {
- backend = apr_pcalloc(c->pool, sizeof(proxy_conn_rec));
- backend->connection = NULL;
- backend->hostname = NULL;
- backend->port = 0;
+ status = ap_proxy_acquire_connection(scheme, &backend, worker, r->server);
+ if (status != OK) {
+ if (backend) {
+ backend->close_on_recycle = 1;
+ ap_proxy_release_connection(scheme, backend, r->server);
+ }
+ return status;
+ }
if (!r->main) {
ap_set_module_config(c->conn_config, &proxy_ajp_module, backend);
}
}
- backend->is_ssl = is_ssl;
+ backend->is_ssl = 0;
+ backend->close_on_recycle = 0;
/* Step One: Determine Who To Connect To */
- status = ap_proxy_http_determine_connection(p, r, p_conn, c, conf, uri,
- &url, proxyname, proxyport,
- server_portstr,
- sizeof(server_portstr));
- if ( status != OK ) {
- return status;
- }
+ status = ap_proxy_determine_connection(p, r, conf, worker, backend, c->pool,
+ uri, &url, proxyname, proxyport,
+ server_portstr,
+ sizeof(server_portstr));
+
+ if (status != OK)
+ goto cleanup;
/* Step Two: Make the Connection */
- status = ap_proxy_http_create_connection(p, r, p_conn, c, &origin, backend,
- conf, proxyname);
- if ( status != OK ) {
- return status;
+ status = ap_proxy_connect_backend(scheme, backend, worker, r->server);
+ if (status != OK)
+ goto cleanup;
+
+ /* Step Three: Create conn_rec */
+ if (!backend->connection) {
+ status = ap_proxy_connection_create(scheme, backend, c, r->server);
+ if (status != OK)
+ goto cleanup;
}
- /* Step Three: Send the Request */
- status = ap_proxy_ajp_request(p, r, p_conn, origin, conf, uri, url,
- server_portstr);
- if ( status != OK ) {
- p_conn->close++;
- ap_proxy_ajp_cleanup(r, p_conn, backend);
- return status;
- }
-
- /* Step Four: Receive the Response */
- status = ap_proxy_ajp_process_response(p, r, p_conn, origin, backend,
+
+ /* Step Four: Send the Request */
+ status = ap_proxy_ajp_request(p, r, backend, origin, conf, uri, url,
+ server_portstr);
+ if (status != OK)
+ goto cleanup;
+
+ /* Step Five: Receive the Response */
+ status = ap_proxy_ajp_process_response(p, r, origin, backend,
conf, server_portstr);
- if ( status != OK ) {
- /* clean up even if there is an error */
- p_conn->close++;
- ap_proxy_ajp_cleanup(r, p_conn, backend);
- return status;
- }
-
- /* Step Five: Clean Up */
- status = ap_proxy_ajp_cleanup(r, p_conn, backend);
- if ( status != OK ) {
- return status;
- }
-
- return OK;
+cleanup:
+ /* Clear the module config */
+ ap_set_module_config(c->conn_config, &proxy_ajp_module, NULL);
+ /* Do not close the socket */
+ ap_proxy_release_connection(scheme, backend, r->server);
+ return status;
}
static void ap_proxy_http_register_hook(apr_pool_t *p)