proxy_ajp.c revision adc4cf1a176a83f86547b1c5f47067199e3609a6
/* Copyright 1999-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* HTTP routines for Apache proxy */
#include "mod_proxy.h"
typedef struct {
const char *name;
int close;
void *data; /* To store ajp data */
/*
* Canonicalise http-like URLs.
* scheme is the scheme for the URL
* url is the URL starting with the first '/'
* def_port is the default port for this scheme.
*/
{
const char *err;
const char *scheme;
"proxy: HTTP: canonicalising URL %s", url);
/* ap_port_of_scheme() */
url += 5;
scheme = "http";
}
url += 6;
scheme = "https";
}
else {
return DECLINED;
}
"proxy: HTTP: canonicalising URL %s", url);
/* do syntatic check.
* We break the URL into host, port, path, search
*/
if (err) {
"error parsing URL %s: %s",
return HTTP_BAD_REQUEST;
}
/* N.B. if this isn't a true proxy request, then the URL _path_
* has already been decoded. True proxy requests have r->uri
* == r->unparsed_uri, and no others have that property.
*/
if (r->uri == r->unparsed_uri) {
*(search++) = '\0';
}
else
/* process path */
return HTTP_BAD_REQUEST;
else
sport[0] = '\0';
}
return OK;
}
static const char *ap_proxy_location_reverse_map(request_rec *r, proxy_server_conf *conf, const char *url)
{
struct proxy_alias *ent;
char *u;
/* XXX FIXME: Make sure this handled the ambiguous case of the :80
* after the hostname */
return ap_construct_url(r->pool, u, r);
}
}
return url;
}
/* cookies are a bit trickier to match: we've got two substrings to worry
* about, and we can't just find them with strstr 'cos of case. Regexp
* matching would be an easy fix, but for better consistency with all the
* other matches we'll refrain and use apr_strmatch to find path=/domain=
* and stick to plain strings for the config values.
*/
static const char *proxy_cookie_reverse_map(request_rec *r,
{
struct proxy_alias *ent;
const char* pathp ;
const char* domainp ;
int ddiff = 0 ;
int pdiff = 0 ;
char* ret ;
/* find the match and replacement, but save replacing until we've done
both path and domain so we know the new strlen
*/
pathp += 5 ;
break ;
}
}
}
domainp += 7 ;
break ;
}
}
}
if ( newpath ) {
if ( newdomain ) {
} else {
}
} else {
}
} else {
if ( newdomain ) {
} else {
}
}
return ret ;
}
/* Clear all connection-based headers from the incoming headers table */
{
const char *name;
if (!next)
return;
while (*next) {
++next;
}
*next = '\0';
++next;
}
}
}
static
conn_rec *c,
char **url,
const char *proxyname,
char *server_portstr,
int server_portstr_size) {
int server_port;
/*
* Break up the URL to determine the host to connect to
*/
/* we break the URL into host, port, uri */
return ap_proxyerror(r, HTTP_BAD_REQUEST,
NULL));
}
}
/* do a DNS lookup for the destination host */
/* see memory note above */
/* 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) {
/* see memory note above */
} else {
}
if (err != APR_SUCCESS) {
return ap_proxyerror(r, HTTP_BAD_GATEWAY,
apr_pstrcat(p, "DNS lookup failure for: ",
}
/* Get the server port for the Via headers */
{
if (ap_is_default_port(server_port, r)) {
} else {
}
}
/* check if ProxyBlock directive on this host */
return ap_proxyerror(r, HTTP_FORBIDDEN,
"Connect to remote machine blocked");
}
return OK;
}
static
const char *proxyname) {
/* 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) {
"proxy: keepalive address match (keep original socket)");
} else {
"proxy: keepalive address mismatch / connection has"
" changed (close old socket (%s/%s, %d/%d))",
}
}
/* get a socket - either a keepalive one, or a new one */
new = 1;
char test_buffer[1];
/* use previous keepalive socket */
new = 0;
/* save timeout */
/* set no timeout */
/* put back old timeout */
if ( APR_STATUS_IS_EOF(socket_status) ) {
"proxy: HTTP: previous connection is closed");
new = 1;
}
}
if (new) {
/* create a new socket */
/*
* 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
*/
/* handle a permanent error on the connect */
if (failed) {
if (proxyname) {
return DECLINED;
} else {
return HTTP_BAD_GATEWAY;
}
}
"proxy: socket is connected");
/* the socket is now open, create a new backend server connection */
r->connection->id,
if (!*origin) {
/* the peer reset the connection already; ap_run_create_connection()
* closed the socket
*/
r->server, "proxy: an error occurred creating a "
return HTTP_INTERNAL_SERVER_ERROR;
}
r->server, "proxy: failed to enable ssl support "
return HTTP_INTERNAL_SERVER_ERROR;
}
}
else {
}
"proxy: connection complete to %pI (%s)",
/* set up the connection filters */
}
return OK;
}
static
char *url, char *server_portstr)
{
int result;
/*
* Send the AJP request to the remote server
*/
/* send request headers */
if (status != APR_SUCCESS) {
"proxy: request failed to %pI (%s)",
return status;
}
/* read the response */
if (status != APR_SUCCESS) {
"proxy: request failed to %pI (%s)",
return status;
}
/* parse the reponse */
if (result == 4) {
"proxy: got response from %pI (%s)",
return APR_SUCCESS;
}
/* send data via brigade or not??? */
/*
status = ajp_send_data(p_conn->sock,r);
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);
return status;
}
*/
return APR_SUCCESS;
}
{
static const char* date_hdrs[]
static const struct {
const char* name ;
} transform_hdrs[] = {
{ "Location", ap_proxy_location_reverse_map } ,
{ "Content-Location", ap_proxy_location_reverse_map } ,
{ "URI", ap_proxy_location_reverse_map } ,
{ "Set-Cookie", proxy_cookie_reverse_map } ,
} ;
int i ;
for ( i = 0 ; date_hdrs[i] ; ++i ) {
return ;
}
}
for ( i = 0 ; transform_hdrs[i].name ; ++i ) {
return ;
}
}
return ;
}
static void ap_proxy_read_headers(request_rec *r, request_rec *rr, char *buffer, int size, conn_rec *c)
{
int len;
char field[MAX_STRING_LEN];
int saw_headers = 0;
/*
* Read header lines until we get the empty separator line, a read error,
* the connection closes (EOF), or we timeout.
*/
/* We may encounter invalid headers, usually from buggy
* MS IIS servers, so we need to determine just how to handle
* them. We can either ignore them, assume that they mark the
* start-of-body (eg: a missing CRLF) or (the default) mark
* the headers as totally bogus and return a 500. The sole
* exception is an extra "HTTP/1.0 200, OK" line sprinkled
* in between the usual MIME headers, which is a favorite
* IIS bug.
*/
/* XXX: The mask check is buggy if we ever see an HTTP/1.10 */
/* Nope, it wasn't even an extra HTTP header. Give up. */
return ;
}
/* if we've already started loading headers_out, then
* return what we've accumulated so far, in the hopes
* that they are useful. Otherwise, we completely bail.
*/
/* FIXME: We've already scarfed the supposed 1st line of
* the body, so the actual content may end up being bogus
* as well. If the content is HTML, we may be lucky.
*/
if (saw_headers) {
"proxy: Starting body due to bogus non-header in headers "
return ;
} else {
"proxy: No HTTP headers "
return ;
}
}
}
/* this is the psc->badopt == bad_ignore case */
"proxy: Ignoring bogus HTTP header "
continue;
}
*value = '\0';
++value;
/* XXX: RFC2068 defines only SP and HT as whitespace, this test is
* wrong... and so are many others probably.
*/
while (apr_isspace(*value))
++value; /* Skip to start of value */
/* should strip trailing whitespace as well */
end)
*end = '\0';
/* make sure we add so as not to destroy duplicated headers
* by ProxyPassReverse and family with process_proxy_header
*/
saw_headers = 1;
/* the header was too long; at the least we should skip extra data */
>= MAX_STRING_LEN - 1) {
/* soak up the extra data */
}
if (len == 0) /* time to exit the larger loop as well */
break;
}
}
}
{
return 1;
}
/*
* Process the AJP response, data already contains the first part of it.
*/
static
char *server_portstr) {
conn_rec *c = r->connection;
apr_bucket *e;
int type;
// bb = apr_brigade_create(p, c->bucket_alloc);
while (type != 5) {
if (type == 4) {
/* AJP13_SEND_HEADERS: process them */
if (status != APR_SUCCESS) {
break;
}
} else if (type == 3) {
/* AJP13_SEND_BODY_CHUNK: piece of data */
char *buff;
ap_rflush(r);
// e = apr_bucket_transient_create(buff, size, c->bucket_alloc);
// APR_BRIGADE_INSERT_TAIL(bb, e);
} else {
break;
}
/* Read the next message */
if (status != APR_SUCCESS) {
break;
}
}
if (status != APR_SUCCESS) {
"proxy: error reading headers from remote "
return ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server");
}
return ap_rflush(r);
/* The page is ready give it to the rest of the logic */
e = apr_bucket_eos_create(c->bucket_alloc);
"proxy: error processing body");
return ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server");
}
return OK;
}
static
/* If there are no KeepAlives, or 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
*/
"ap_proxy_http_cleanup closing %d %d %d %s",
}
}
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
* go via the proxy.
* Note that if a proxy is used, then URLs other than http: can be accessed,
* also, if we have trouble which is clearly specific to the proxy, then
* we return DECLINED so that we can try another proxy. (Or the direct
* route.)
*/
{
int status;
char server_portstr[32];
int is_ssl = 0;
/* Note: Memory pool allocation.
* A downstream keepalive connection is always connected to the existence
* (or not) of an upstream keepalive connection. If this is not done then
* load balancing against multiple backend servers breaks (one backend
* server ends up taking 100% of the load), and the risk is run of
* downstream keepalive connections being kept open unnecessarily. This
* keeps webservers busy and ties up resources.
*
* As a result, we allocate all sockets out of the upstream connection
* pool, and when we want to reuse a socket, we check first whether the
* connection ID of the current upstream connection is the same as that
* of the connection when the socket was opened.
*/
conn_rec *c = r->connection;
sizeof(*p_conn));
/* only use stored info for top-level pages. Sub requests don't share
* in keepalives
*/
if (!r->main) {
}
/* create space for state information */
if (!backend) {
if (!r->main) {
}
}
"proxy: AJP: declining URL %s", url);
return DECLINED;
}
"proxy: AJP: serving URL %s", url);
/* only use stored info for top-level pages. Sub requests don't share
* in keepalives
*/
if (!r->main) {
}
/* create space for state information */
if (!backend) {
if (!r->main) {
}
}
/* Step One: Determine Who To Connect To */
sizeof(server_portstr));
return status;
}
/* Step Two: Make the Connection */
return status;
}
/* Step Three: Send the Request */
return status;
}
/* Step Four: Receive the Response */
/* clean up even if there is an error */
return status;
}
/* Step Five: Clean Up */
return status;
}
return OK;
}
static void ap_proxy_http_register_hook(apr_pool_t *p)
{
}
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
NULL, /* create per-server config structure */
NULL, /* merge per-server config structures */
NULL, /* command apr_table_t */
ap_proxy_http_register_hook/* register hooks */
};