1
0
mirror of https://github.com/apache/httpd.git synced 2025-08-08 15:02:10 +03:00

New mod_proxy/mod_cache (file cache) for 2.0. This uses a caching API so

that shared mem, LDAP servers, DBs, etc. can also be used for proxy
caching. The caching API is very young, and subject to change as APR changes.

proxy_cache.c from the proxy subdir is no longer used.

Build with --enable-modules=proxy,cache

Lightly tested on Linux, no warranties expressed or implied yet.

Still to do:

strerror(), waitpid(), stat() cleanups
address issues marked by @@@FIXME
add HTTP/1.1

This should be considered a *reference* proxy implementation for 2.0. What
actually ends up shipping with 2.0 is likely going to be rather different as
the redesign evolves. This may end up being the 2.0 backwards compatibility
workalike.

Modified to work with today's conf/build/layout scheme

PR:
Obtained from:
Submitted by:	Sam Magnuson
Reviewed by:	Chuck Murcko


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@85552 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Chuck Murcko
2000-06-12 21:41:58 +00:00
parent bfbb1c736d
commit db096af6a5
8 changed files with 1855 additions and 1992 deletions

View File

@@ -0,0 +1,5 @@
LTLIBRARY_NAME = libapachemod_proxy.la
LTLIBRARY_SOURCES = mod_proxy.lo proxy_connect.lo proxy_ftp.lo proxy_http.lo proxy_util.lo
include $(top_srcdir)/build/ltlib.mk

32
modules/proxy/config.m4 Normal file
View File

@@ -0,0 +1,32 @@
dnl modules enabled in this directory by default
dnl AC_DEFUN(modulename, modulestructname, defaultonoroff, configmacros)
dnl XXX - Need to add help text to --enable-module flags
dnl XXX - Need to allow --enable-module to fail if optional config fails
AC_DEFUN(APACHE_CHECK_PROXY_MODULE, [
APACHE_MODULE($1,,,$2,$3,$4)
])
APACHE_MODPATH_INIT(proxy)
APACHE_CHECK_PROXY_MODULE(proxy, , yes)
dnl APACHE_CHECK_STANDARD_MODULE(auth_db, , no, [
dnl AC_CHECK_HEADERS(db.h)
dnl AC_CHECK_LIB(db,main)
dnl ])
dnl APACHE_CHECK_STANDARD_MODULE(usertrack, , no, [
dnl AC_CHECK_HEADERS(sys/times.h)
dnl AC_CHECK_FUNCS(times)
dnl ])
APACHE_MODPATH_FINISH
if test "$sharedobjs" = "yes"; then
LIBS="$LIBS -ldl"
LTFLAGS="$LTFLAGS -export-dynamic"
fi
APACHE_SUBST(STANDARD_LIBS)

View File

@@ -63,6 +63,7 @@
#include "http_log.h" #include "http_log.h"
#include "http_vhost.h" #include "http_vhost.h"
#include "http_request.h" #include "http_request.h"
#include "util_date.h"
/* Some WWW schemes and their default ports; this is basically /etc/services */ /* Some WWW schemes and their default ports; this is basically /etc/services */
/* This will become global when the protocol abstraction comes */ /* This will become global when the protocol abstraction comes */
@@ -76,7 +77,7 @@ static struct proxy_services defports[] =
{"wais", DEFAULT_WAIS_PORT}, {"wais", DEFAULT_WAIS_PORT},
{"snews", DEFAULT_SNEWS_PORT}, {"snews", DEFAULT_SNEWS_PORT},
{"prospero", DEFAULT_PROSPERO_PORT}, {"prospero", DEFAULT_PROSPERO_PORT},
{NULL, -1} /* unknown port */ {NULL, -1} /* unknown port */
}; };
/* /*
@@ -98,29 +99,29 @@ static int alias_match(const char *uri, const char *alias_fakename)
const char *aliasp = alias_fakename, *urip = uri; const char *aliasp = alias_fakename, *urip = uri;
while (aliasp < end_fakename) { while (aliasp < end_fakename) {
if (*aliasp == '/') { if (*aliasp == '/') {
/* any number of '/' in the alias matches any number in /* any number of '/' in the alias matches any number in
* the supplied URI, but there must be at least one... * the supplied URI, but there must be at least one...
*/ */
if (*urip != '/') if (*urip != '/')
return 0; return 0;
while (*aliasp == '/') while (*aliasp == '/')
++aliasp; ++aliasp;
while (*urip == '/') while (*urip == '/')
++urip; ++urip;
} }
else { else {
/* Other characters are compared literally */ /* Other characters are compared literally */
if (*urip++ != *aliasp++) if (*urip++ != *aliasp++)
return 0; return 0;
} }
} }
/* Check last alias path component matched all the way */ /* Check last alias path component matched all the way */
if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/') if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
return 0; return 0;
/* Return number of characters from URI which matched (may be /* Return number of characters from URI which matched (may be
* greater than length of alias, since we may have matched * greater than length of alias, since we may have matched
@@ -149,25 +150,25 @@ static int proxy_detect(request_rec *r)
conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
if (conf->req && r->parsed_uri.scheme) { if (conf->req && r->parsed_uri.scheme) {
/* but it might be something vhosted */ /* but it might be something vhosted */
if (!(r->parsed_uri.hostname if (!(r->parsed_uri.hostname
&& !strcasecmp(r->parsed_uri.scheme, ap_http_method(r)) && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))
&& ap_matches_request_vhost(r, r->parsed_uri.hostname, && ap_matches_request_vhost(r, r->parsed_uri.hostname,
r->parsed_uri.port_str ? r->parsed_uri.port : ap_default_port(r)))) { r->parsed_uri.port_str ? r->parsed_uri.port : ap_default_port(r)))) {
r->proxyreq = 1; r->proxyreq = 1;
r->uri = r->unparsed_uri; r->uri = r->unparsed_uri;
r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL); r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
r->handler = "proxy-server"; r->handler = "proxy-server";
} }
} }
/* We need special treatment for CONNECT proxying: it has no scheme part */ /* We need special treatment for CONNECT proxying: it has no scheme part */
else if (conf->req && r->method_number == M_CONNECT else if (conf->req && r->method_number == M_CONNECT
&& r->parsed_uri.hostname && r->parsed_uri.hostname
&& r->parsed_uri.port_str) { && r->parsed_uri.port_str) {
r->proxyreq = 1; r->proxyreq = 1;
r->uri = r->unparsed_uri; r->uri = r->unparsed_uri;
r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL); r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
r->handler = "proxy-server"; r->handler = "proxy-server";
} }
return DECLINED; return DECLINED;
} }
@@ -181,10 +182,10 @@ static int proxy_trans(request_rec *r)
struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts; struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts;
if (r->proxyreq) { if (r->proxyreq) {
/* someone has already set up the proxy, it was possibly ourselves /* someone has already set up the proxy, it was possibly ourselves
* in proxy_detect * in proxy_detect
*/ */
return OK; return OK;
} }
/* XXX: since r->uri has been manipulated already we're not really /* XXX: since r->uri has been manipulated already we're not really
@@ -195,13 +196,13 @@ static int proxy_trans(request_rec *r)
for (i = 0; i < conf->aliases->nelts; i++) { for (i = 0; i < conf->aliases->nelts; i++) {
len = alias_match(r->uri, ent[i].fake); len = alias_match(r->uri, ent[i].fake);
if (len > 0) { if (len > 0) {
r->filename = ap_pstrcat(r->pool, "proxy:", ent[i].real, r->filename = ap_pstrcat(r->pool, "proxy:", ent[i].real,
r->uri + len, NULL); r->uri + len, NULL);
r->handler = "proxy-server"; r->handler = "proxy-server";
r->proxyreq = 1; r->proxyreq = 1;
return OK; return OK;
} }
} }
return DECLINED; return DECLINED;
} }
@@ -217,36 +218,30 @@ static int proxy_fixup(request_rec *r)
char *url, *p; char *url, *p;
if (!r->proxyreq || strncmp(r->filename, "proxy:", 6) != 0) if (!r->proxyreq || strncmp(r->filename, "proxy:", 6) != 0)
return DECLINED; return DECLINED;
url = &r->filename[6]; url = &r->filename[6];
/* canonicalise each specific scheme */ /* canonicalise each specific scheme */
if (strncasecmp(url, "http:", 5) == 0) if (strncasecmp(url, "http:", 5) == 0)
return ap_proxy_http_canon(r, url + 5, "http", DEFAULT_HTTP_PORT); return ap_proxy_http_canon(r, url + 5, "http", DEFAULT_HTTP_PORT);
else if (strncasecmp(url, "ftp:", 4) == 0) else if (strncasecmp(url, "ftp:", 4) == 0)
return ap_proxy_ftp_canon(r, url + 4); return ap_proxy_ftp_canon(r, url + 4);
p = strchr(url, ':'); p = strchr(url, ':');
if (p == NULL || p == url) if (p == NULL || p == url)
return HTTP_BAD_REQUEST; return HTTP_BAD_REQUEST;
return OK; /* otherwise; we've done the best we can */ return OK; /* otherwise; we've done the best we can */
} }
static void proxy_init(server_rec *r, ap_pool_t *p) /* Send a redirection if the request contains a hostname which is not
{ * fully qualified, i.e. doesn't have a domain name appended. Some proxy
ap_proxy_garbage_init(r, p); * servers like Netscape's allow this and access hosts from the local
} * domain in this case. I think it is better to redirect to a FQDN, since
* these will later be found in the bookmarks files.
* The "ProxyDomain" directive determines what domain will be appended
*/
/* Send a redirection if the request contains a hostname which is not */
/* fully qualified, i.e. doesn't have a domain name appended. Some proxy */
/* servers like Netscape's allow this and access hosts from the local */
/* domain in this case. I think it is better to redirect to a FQDN, since */
/* these will later be found in the bookmarks files. */
/* The "ProxyDomain" directive determines what domain will be appended */
static int proxy_needsdomain(request_rec *r, const char *url, const char *domain) static int proxy_needsdomain(request_rec *r, const char *url, const char *domain)
{ {
char *nuri; char *nuri;
@@ -254,29 +249,29 @@ static int proxy_needsdomain(request_rec *r, const char *url, const char *domain
/* We only want to worry about GETs */ /* We only want to worry about GETs */
if (!r->proxyreq || r->method_number != M_GET || !r->parsed_uri.hostname) if (!r->proxyreq || r->method_number != M_GET || !r->parsed_uri.hostname)
return DECLINED; return DECLINED;
/* If host does contain a dot already, or it is "localhost", decline */ /* If host does contain a dot already, or it is "localhost", decline */
if (strchr(r->parsed_uri.hostname, '.') != NULL if (strchr(r->parsed_uri.hostname, '.') != NULL
|| strcasecmp(r->parsed_uri.hostname, "localhost") == 0) || strcasecmp(r->parsed_uri.hostname, "localhost") == 0)
return DECLINED; /* host name has a dot already */ return DECLINED; /* host name has a dot already */
ref = ap_table_get(r->headers_in, "Referer"); ref = ap_table_get(r->headers_in, "Referer");
/* Reassemble the request, but insert the domain after the host name */ /* Reassemble the request, but insert the domain after the host name */
/* Note that the domain name always starts with a dot */ /* Note that the domain name always starts with a dot */
r->parsed_uri.hostname = ap_pstrcat(r->pool, r->parsed_uri.hostname, r->parsed_uri.hostname = ap_pstrcat(r->pool, r->parsed_uri.hostname,
domain, NULL); domain, NULL);
nuri = ap_unparse_uri_components(r->pool, nuri = ap_unparse_uri_components(r->pool,
&r->parsed_uri, &r->parsed_uri,
UNP_REVEALPASSWORD); UNP_REVEALPASSWORD);
ap_table_set(r->headers_out, "Location", nuri); ap_table_set(r->headers_out, "Location", nuri);
ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r, ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, 0, r,
"Domain missing: %s sent to %s%s%s", r->uri, "Domain missing: %s sent to %s%s%s", r->uri,
ap_unparse_uri_components(r->pool, &r->parsed_uri, ap_unparse_uri_components(r->pool, &r->parsed_uri,
UNP_OMITUSERINFO), UNP_OMITUSERINFO),
ref ? " from " : "", ref ? ref : ""); ref ? " from " : "", ref ? ref : "");
return HTTP_MOVED_PERMANENTLY; return HTTP_MOVED_PERMANENTLY;
} }
@@ -288,51 +283,99 @@ static int proxy_handler(request_rec *r)
{ {
char *url, *scheme, *p; char *url, *scheme, *p;
void *sconf = r->server->module_config; void *sconf = r->server->module_config;
proxy_server_conf *conf = proxy_server_conf *conf = (proxy_server_conf *)
(proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); ap_get_module_config(sconf, &proxy_module);
ap_array_header_t *proxies = conf->proxies; ap_array_header_t *proxies = conf->proxies;
struct proxy_remote *ents = (struct proxy_remote *) proxies->elts; struct proxy_remote *ents = (struct proxy_remote *) proxies->elts;
int i, rc; int i, rc;
cache_req *cr; ap_cache_el *cr=NULL;
int direct_connect = 0; int direct_connect = 0;
const char *maxfwd_str; const char *maxfwd_str;
const char *pragma, *auth, *imstr;
if (!r->proxyreq || strncmp(r->filename, "proxy:", 6) != 0) if (!r->proxyreq || strncmp(r->filename, "proxy:", 6) != 0)
return DECLINED; return DECLINED;
if (r->method_number == M_TRACE && if (r->method_number == M_TRACE && (maxfwd_str =
(maxfwd_str = ap_table_get(r->headers_in, "Max-Forwards")) != NULL) { ap_table_get(r->headers_in, "Max-Forwards")) != NULL) {
int maxfwd = strtol(maxfwd_str, NULL, 10); int maxfwd = strtol(maxfwd_str, NULL, 10);
if (maxfwd < 1) { if (maxfwd < 1) {
int access_status; int access_status;
r->proxyreq = 0; r->proxyreq = 0;
if ((access_status = ap_send_http_trace(r))) if ((access_status = ap_send_http_trace(r)))
ap_die(access_status, r); ap_die(access_status, r);
else else
ap_finalize_request_protocol(r); ap_finalize_request_protocol(r);
return OK; return OK;
} }
ap_table_setn(r->headers_in, "Max-Forwards", ap_table_setn(r->headers_in, "Max-Forwards",
ap_psprintf(r->pool, "%d", (maxfwd > 0) ? maxfwd-1 : 0)); ap_psprintf(r->pool, "%d", (maxfwd > 0) ? maxfwd-1 : 0));
} }
if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)))
return rc; return rc;
url = r->filename + 6; url = r->filename + 6;
p = strchr(url, ':'); p = strchr(url, ':');
if (p == NULL) if (p == NULL)
return HTTP_BAD_REQUEST; return HTTP_BAD_REQUEST;
rc = ap_proxy_cache_check(r, url, &conf->cache, &cr); pragma = ap_table_get(r->headers_in, "Pragma");
if (rc != DECLINED) auth = ap_table_get(r->headers_in, "Authorization");
return rc; imstr = ap_table_get(r->headers_in, "If-Modified-Since");
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL,
"Request for %s, pragma=%s, auth=%s, imstr=%s", url,
pragma, auth, imstr);
/* can this request be cached at all? */
if (r->method_number == M_GET && strlen(url) < 1024 &&
!ap_proxy_liststr(pragma, "no-cache") && auth == NULL)
{
if(ap_cache_seek(conf->cache, url, &cr) == APR_SUCCESS)
{
int has_m = 0;
/* now we need to check if the last modified date is write if */
if(imstr)
{
time_t ims = (time_t)ap_parseHTTPdate(ap_proxy_date_canon(r->pool, imstr));
if(ims == BAD_DATE)
ap_table_unset(r->headers_in, "If-Modified-Since");
else
{
/* ok we were asked to check, so let's do that */
if(ap_cache_el_header(cr, "Last-Modified",
(char **)&imstr) == APR_SUCCESS)
{
time_t lm =
ap_parseHTTPdate(ap_proxy_date_canon(r->pool, imstr));
if(lm != BAD_DATE)
{
if(ims < lm)
ap_table_set(r->headers_in,
"If-Modified-Since", imstr);
else
{
has_m = 1;
}
}
}
}
}
return has_m ? HTTP_NOT_MODIFIED : ap_proxy_cache_send(r, cr);
}
/* if there wasn't an entry in the cache we get here,
we need to create one */
ap_cache_create(conf->cache, url, &cr);
}
/* If the host doesn't have a domain name, add one and redirect. */ /* If the host doesn't have a domain name, add one and redirect. */
if (conf->domain != NULL) { if (conf->domain != NULL) {
rc = proxy_needsdomain(r, url, conf->domain); rc = proxy_needsdomain(r, url, conf->domain);
if (ap_is_HTTP_REDIRECT(rc)) if (ap_is_HTTP_REDIRECT(rc))
return HTTP_MOVED_PERMANENTLY; return HTTP_MOVED_PERMANENTLY;
} }
*p = '\0'; *p = '\0';
@@ -344,47 +387,47 @@ static int proxy_handler(request_rec *r)
/* we only know how to handle communication to a proxy via http */ /* we only know how to handle communication to a proxy via http */
/*if (strcasecmp(scheme, "http") == 0) */ /*if (strcasecmp(scheme, "http") == 0) */
{ {
int ii; int ii;
struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts; struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
for (direct_connect = ii = 0; ii < conf->dirconn->nelts && !direct_connect; ii++) { for (direct_connect = ii = 0; ii < conf->dirconn->nelts && !direct_connect; ii++) {
direct_connect = list[ii].matcher(&list[ii], r); direct_connect = list[ii].matcher(&list[ii], r);
} }
#if DEBUGGING #if DEBUGGING
ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r, ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
(direct_connect) ? "NoProxy for %s" : "UseProxy for %s", (direct_connect) ? "NoProxy for %s" : "UseProxy for %s",
r->uri); r->uri);
#endif #endif
} }
/* firstly, try a proxy, unless a NoProxy directive is active */ /* firstly, try a proxy, unless a NoProxy directive is active */
if (!direct_connect) if (!direct_connect)
for (i = 0; i < proxies->nelts; i++) { for (i = 0; i < proxies->nelts; i++) {
p = strchr(ents[i].scheme, ':'); /* is it a partial URL? */ p = strchr(ents[i].scheme, ':'); /* is it a partial URL? */
if (strcmp(ents[i].scheme, "*") == 0 || if (strcmp(ents[i].scheme, "*") == 0 ||
(p == NULL && strcasecmp(scheme, ents[i].scheme) == 0) || (p == NULL && strcasecmp(scheme, ents[i].scheme) == 0) ||
(p != NULL && (p != NULL &&
strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) { strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) {
/* CONNECT is a special method that bypasses the normal /* CONNECT is a special method that bypasses the normal
* proxy code. * proxy code.
*/ */
if (r->method_number == M_CONNECT) if (r->method_number == M_CONNECT)
rc = ap_proxy_connect_handler(r, cr, url, ents[i].hostname, rc = ap_proxy_connect_handler(r, cr, url, ents[i].hostname,
ents[i].port); ents[i].port);
/* we only know how to handle communication to a proxy via http */ /* we only know how to handle communication to a proxy via http */
else if (strcasecmp(ents[i].protocol, "http") == 0) else if (strcasecmp(ents[i].protocol, "http") == 0)
rc = ap_proxy_http_handler(r, cr, url, ents[i].hostname, rc = ap_proxy_http_handler(r, cr, url, ents[i].hostname,
ents[i].port); ents[i].port);
else else
rc = DECLINED; rc = DECLINED;
/* an error or success */ /* an error or success */
if (rc != DECLINED && rc != HTTP_BAD_GATEWAY) if (rc != DECLINED && rc != HTTP_BAD_GATEWAY)
return rc; return rc;
/* we failed to talk to the upstream proxy */ /* we failed to talk to the upstream proxy */
} }
} }
/* otherwise, try it direct */ /* otherwise, try it direct */
/* N.B. what if we're behind a firewall, where we must use a proxy or /* N.B. what if we're behind a firewall, where we must use a proxy or
@@ -392,20 +435,19 @@ static int proxy_handler(request_rec *r)
*/ */
/* handle the scheme */ /* handle the scheme */
if (r->method_number == M_CONNECT) if (r->method_number == M_CONNECT)
return ap_proxy_connect_handler(r, cr, url, NULL, 0); return ap_proxy_connect_handler(r, cr, url, NULL, 0);
if (strcasecmp(scheme, "http") == 0) if (strcasecmp(scheme, "http") == 0)
return ap_proxy_http_handler(r, cr, url, NULL, 0); return ap_proxy_http_handler(r, cr, url, NULL, 0);
if (strcasecmp(scheme, "ftp") == 0) if (strcasecmp(scheme, "ftp") == 0)
return ap_proxy_ftp_handler(r, cr, url); return ap_proxy_ftp_handler(r, cr, url);
else else
return HTTP_FORBIDDEN; return HTTP_FORBIDDEN;
} }
/* -------------------------------------------------------------- */ /* -------------------------------------------------------------- */
/* Setup configurable data */ /* Setup configurable data */
static void * static void *create_proxy_config(ap_pool_t *p, server_rec *s)
create_proxy_config(ap_pool_t *p, server_rec *s)
{ {
proxy_server_conf *ps = ap_pcalloc(p, sizeof(proxy_server_conf)); proxy_server_conf *ps = ap_pcalloc(p, sizeof(proxy_server_conf));
@@ -416,21 +458,12 @@ static void *
ps->dirconn = ap_make_array(p, 10, sizeof(struct dirconn_entry)); ps->dirconn = ap_make_array(p, 10, sizeof(struct dirconn_entry));
ps->nocaches = ap_make_array(p, 10, sizeof(struct nocache_entry)); ps->nocaches = ap_make_array(p, 10, sizeof(struct nocache_entry));
ps->allowed_connect_ports = ap_make_array(p, 10, sizeof(int)); ps->allowed_connect_ports = ap_make_array(p, 10, sizeof(int));
ps->cache_completion = DEFAULT_CACHE_COMPLETION;
ps->domain = NULL; ps->domain = NULL;
ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */ ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */
ps->req = 0; ps->req = 0;
ps->cache.root = NULL; ap_cache_init(&ps->cache, "mod_proxy cache", s);
ps->cache.space = DEFAULT_CACHE_SPACE;
ps->cache.maxexpire = DEFAULT_CACHE_MAXEXPIRE;
ps->cache.defaultexpire = DEFAULT_CACHE_EXPIRE;
ps->cache.lmfactor = DEFAULT_CACHE_LMFACTOR;
ps->cache.gcinterval = -1;
/* at these levels, the cache can have 2^18 directories (256,000) */
ps->cache.dirlevels = 3;
ps->cache.dirlength = 1;
ps->cache.cache_completion = DEFAULT_CACHE_COMPLETION;
return ps; return ps;
} }
@@ -446,26 +479,26 @@ static const char *
p = strchr(r, ':'); p = strchr(r, ':');
if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0') if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0')
return "ProxyRemote: Bad syntax for a remote proxy server"; return "ProxyRemote: Bad syntax for a remote proxy server";
q = strchr(p + 3, ':'); q = strchr(p + 3, ':');
if (q != NULL) { if (q != NULL) {
if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) if (sscanf(q + 1, "%u", &port) != 1 || port > 65535)
return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)"; return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)";
*q = '\0'; *q = '\0';
} }
else else
port = -1; port = -1;
*p = '\0'; *p = '\0';
if (strchr(f, ':') == NULL) if (strchr(f, ':') == NULL)
ap_str_tolower(f); /* lowercase scheme */ ap_str_tolower(f); /* lowercase scheme */
ap_str_tolower(p + 3); /* lowercase hostname */ ap_str_tolower(p + 3); /* lowercase hostname */
if (port == -1) { if (port == -1) {
int i; int i;
for (i = 0; defports[i].scheme != NULL; i++) for (i = 0; defports[i].scheme != NULL; i++)
if (strcasecmp(defports[i].scheme, r) == 0) if (strcasecmp(defports[i].scheme, r) == 0)
break; break;
port = defports[i].port; port = defports[i].port;
} }
new = ap_push_array(conf->proxies); new = ap_push_array(conf->proxies);
@@ -476,6 +509,36 @@ static const char *
return NULL; return NULL;
} }
static const char *
set_cache_exclude(cmd_parms *cmd, void *dummy, char *arg)
{
server_rec *s = cmd->server;
proxy_server_conf *psf = (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
struct nocache_entry *new;
struct nocache_entry *list = (struct nocache_entry *) psf->nocaches->elts;
struct hostent hp;
int found = 0;
int i;
/* Don't duplicate entries */
for (i = 0; i < psf->nocaches->nelts; i++) {
if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */
found = 1;
}
if (!found) {
new = ap_push_array(psf->nocaches);
new->name = arg;
/* Don't do name lookups on things that aren't dotted */
if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL)
/*@@@FIXME: This copies only the first of (possibly many) IP addrs */
memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr));
else
new->addr.s_addr = 0;
}
return NULL;
}
static const char * static const char *
add_pass(cmd_parms *cmd, void *dummy, char *f, char *r) add_pass(cmd_parms *cmd, void *dummy, char *f, char *r)
{ {
@@ -505,8 +568,7 @@ static const char *
return NULL; return NULL;
} }
static const char * static const char *set_proxy_exclude(cmd_parms *parms, void *dummy, char *arg)
set_proxy_exclude(cmd_parms *parms, void *dummy, char *arg)
{ {
server_rec *s = parms->server; server_rec *s = parms->server;
proxy_server_conf *conf = proxy_server_conf *conf =
@@ -519,19 +581,19 @@ static const char *
/* Don't duplicate entries */ /* Don't duplicate entries */
for (i = 0; i < conf->noproxies->nelts; i++) { for (i = 0; i < conf->noproxies->nelts; i++) {
if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */ if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */
found = 1; found = 1;
} }
if (!found) { if (!found) {
new = ap_push_array(conf->noproxies); new = ap_push_array(conf->noproxies);
new->name = arg; new->name = arg;
/* Don't do name lookups on things that aren't dotted */ /* Don't do name lookups on things that aren't dotted */
if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL) if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL)
/*@@@FIXME: This copies only the first of (possibly many) IP addrs */ /*@@@FIXME: This copies only the first of (possibly many) IP addrs */
memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr)); memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr));
else else
new->addr.s_addr = 0; new->addr.s_addr = 0;
} }
return NULL; return NULL;
} }
@@ -548,7 +610,7 @@ static const char *
int *New; int *New;
if (!ap_isdigit(arg[0])) if (!ap_isdigit(arg[0]))
return "AllowCONNECT: port number must be numeric"; return "AllowCONNECT: port number must be numeric";
New = ap_push_array(conf->allowed_connect_ports); New = ap_push_array(conf->allowed_connect_ports);
*New = atoi(arg); *New = atoi(arg);
@@ -571,44 +633,44 @@ static const char *
/* Don't duplicate entries */ /* Don't duplicate entries */
for (i = 0; i < conf->dirconn->nelts; i++) { for (i = 0; i < conf->dirconn->nelts; i++) {
if (strcasecmp(arg, list[i].name) == 0) if (strcasecmp(arg, list[i].name) == 0)
found = 1; found = 1;
} }
if (!found) { if (!found) {
New = ap_push_array(conf->dirconn); New = ap_push_array(conf->dirconn);
New->name = arg; New->name = arg;
New->hostentry = NULL; New->hostentry = NULL;
if (ap_proxy_is_ipaddr(New, parms->pool)) { if (ap_proxy_is_ipaddr(New, parms->pool)) {
#if DEBUGGING #if DEBUGGING
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, 0, NULL,
"Parsed addr %s", inet_ntoa(New->addr)); "Parsed addr %s", inet_ntoa(New->addr));
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, 0, NULL,
"Parsed mask %s", inet_ntoa(New->mask)); "Parsed mask %s", inet_ntoa(New->mask));
#endif #endif
} }
else if (ap_proxy_is_domainname(New, parms->pool)) { else if (ap_proxy_is_domainname(New, parms->pool)) {
ap_str_tolower(New->name); ap_str_tolower(New->name);
#if DEBUGGING #if DEBUGGING
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, 0, NULL,
"Parsed domain %s", New->name); "Parsed domain %s", New->name);
#endif #endif
} }
else if (ap_proxy_is_hostname(New, parms->pool)) { else if (ap_proxy_is_hostname(New, parms->pool)) {
ap_str_tolower(New->name); ap_str_tolower(New->name);
#if DEBUGGING #if DEBUGGING
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, 0, NULL,
"Parsed host %s", New->name); "Parsed host %s", New->name);
#endif #endif
} }
else { else {
ap_proxy_is_word(New, parms->pool); ap_proxy_is_word(New, parms->pool);
#if DEBUGGING #if DEBUGGING
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, 0, NULL,
"Parsed word %s", New->name); "Parsed word %s", New->name);
#endif #endif
} }
} }
return NULL; return NULL;
} }
@@ -620,7 +682,7 @@ static const char *
ap_get_module_config(parms->server->module_config, &proxy_module); ap_get_module_config(parms->server->module_config, &proxy_module);
if (arg[0] != '.') if (arg[0] != '.')
return "ProxyDomain: domain name must start with a dot."; return "ProxyDomain: domain name must start with a dot.";
psf->domain = arg; psf->domain = arg;
return NULL; return NULL;
@@ -637,146 +699,6 @@ static const char *
} }
static const char *
set_cache_size(cmd_parms *parms, char *struct_ptr, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
int val;
if (sscanf(arg, "%d", &val) != 1)
return "CacheSize value must be an integer (kBytes)";
psf->cache.space = val;
return NULL;
}
static const char *
set_cache_root(cmd_parms *parms, void *dummy, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
psf->cache.root = arg;
return NULL;
}
static const char *
set_cache_factor(cmd_parms *parms, void *dummy, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
double val;
if (sscanf(arg, "%lg", &val) != 1)
return "CacheLastModifiedFactor value must be a float";
psf->cache.lmfactor = val;
return NULL;
}
static const char *
set_cache_maxex(cmd_parms *parms, void *dummy, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
double val;
if (sscanf(arg, "%lg", &val) != 1)
return "CacheMaxExpire value must be a float";
psf->cache.maxexpire = (int) (val * (double) SEC_ONE_HR);
return NULL;
}
static const char *
set_cache_defex(cmd_parms *parms, void *dummy, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
double val;
if (sscanf(arg, "%lg", &val) != 1)
return "CacheDefaultExpire value must be a float";
psf->cache.defaultexpire = (int) (val * (double) SEC_ONE_HR);
return NULL;
}
static const char *
set_cache_gcint(cmd_parms *parms, void *dummy, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
double val;
if (sscanf(arg, "%lg", &val) != 1)
return "CacheGcInterval value must be a float";
psf->cache.gcinterval = (int) (val * (double) SEC_ONE_HR);
return NULL;
}
static const char *
set_cache_dirlevels(cmd_parms *parms, char *struct_ptr, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
int val;
val = atoi(arg);
if (val < 1)
return "CacheDirLevels value must be an integer greater than 0";
if (val * psf->cache.dirlength > CACHEFILE_LEN)
return "CacheDirLevels*CacheDirLength value must not be higher than 20";
psf->cache.dirlevels = val;
return NULL;
}
static const char *
set_cache_dirlength(cmd_parms *parms, char *struct_ptr, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
int val;
val = atoi(arg);
if (val < 1)
return "CacheDirLength value must be an integer greater than 0";
if (val * psf->cache.dirlevels > CACHEFILE_LEN)
return "CacheDirLevels*CacheDirLength value must not be higher than 20";
psf->cache.dirlength = val;
return NULL;
}
static const char *
set_cache_exclude(cmd_parms *parms, void *dummy, char *arg)
{
server_rec *s = parms->server;
proxy_server_conf *conf =
ap_get_module_config(s->module_config, &proxy_module);
struct nocache_entry *new;
struct nocache_entry *list = (struct nocache_entry *) conf->nocaches->elts;
struct hostent hp;
int found = 0;
int i;
/* Don't duplicate entries */
for (i = 0; i < conf->nocaches->nelts; i++) {
if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */
found = 1;
}
if (!found) {
new = ap_push_array(conf->nocaches);
new->name = arg;
/* Don't do name lookups on things that aren't dotted */
if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL)
/*@@@FIXME: This copies only the first of (possibly many) IP addrs */
memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr));
else
new->addr.s_addr = 0;
}
return NULL;
}
static const char * static const char *
set_recv_buffer_size(cmd_parms *parms, void *dummy, char *arg) set_recv_buffer_size(cmd_parms *parms, void *dummy, char *arg)
{ {
@@ -784,34 +706,17 @@ static const char *
ap_get_module_config(parms->server->module_config, &proxy_module); ap_get_module_config(parms->server->module_config, &proxy_module);
int s = atoi(arg); int s = atoi(arg);
if (s < 512 && s != 0) { if (s < 512 && s != 0) {
return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default."; return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
} }
psf->recv_buffer_size = s; psf->recv_buffer_size = s;
return NULL; return NULL;
} }
static const char*
set_cache_completion(cmd_parms *parms, void *dummy, char *arg)
{
proxy_server_conf *psf =
ap_get_module_config(parms->server->module_config, &proxy_module);
int s = atoi(arg);
if (s > 100 || s < 0) {
return "CacheForceCompletion must be <= 100 percent, "
"or 0 for system default.";
}
if (s > 0)
psf->cache.cache_completion = ((float)s / 100);
return NULL;
}
static const char* static const char*
set_via_opt(cmd_parms *parms, void *dummy, char *arg) set_via_opt(cmd_parms *parms, void *dummy, char *arg)
{ {
proxy_server_conf *psf = proxy_server_conf *psf = ap_get_module_config(parms->server->module_config, &proxy_module);
ap_get_module_config(parms->server->module_config, &proxy_module);
if (strcasecmp(arg, "Off") == 0) if (strcasecmp(arg, "Off") == 0)
psf->viaopt = via_off; psf->viaopt = via_off;
@@ -822,13 +727,28 @@ static const char*
else if (strcasecmp(arg, "Full") == 0) else if (strcasecmp(arg, "Full") == 0)
psf->viaopt = via_full; psf->viaopt = via_full;
else { else {
return "ProxyVia must be one of: " return "ProxyVia must be one of: "
"off | on | full | block"; "off | on | full | block";
} }
return NULL; return NULL;
} }
static const char*
set_cache_completion(cmd_parms *parms, void *dummy, char *arg)
{
proxy_server_conf *psf = ap_get_module_config(parms->server->module_config, &proxy_module);
int s = atoi(arg);
if (s > 100 || s < 0) {
return "CacheForceCompletion must be <= 100 percent, "
"or 0 for system default.";
}
if (s > 0)
psf->cache_completion = ((float)s / 100);
return NULL;
}
static const handler_rec proxy_handlers[] = static const handler_rec proxy_handlers[] =
{ {
{"proxy-server", proxy_handler}, {"proxy-server", proxy_handler},
@@ -855,50 +775,34 @@ static const command_rec proxy_cmds[] =
"The default intranet domain name (in absence of a domain in the URL)"}, "The default intranet domain name (in absence of a domain in the URL)"},
{"AllowCONNECT", set_allowed_ports, NULL, RSRC_CONF, ITERATE, {"AllowCONNECT", set_allowed_ports, NULL, RSRC_CONF, ITERATE,
"A list of ports which CONNECT may connect to"}, "A list of ports which CONNECT may connect to"},
{"CacheRoot", set_cache_root, NULL, RSRC_CONF, TAKE1,
"The directory to store cache files"},
{"CacheSize", set_cache_size, NULL, RSRC_CONF, TAKE1,
"The maximum disk space used by the cache in Kb"},
{"CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF, TAKE1,
"The maximum time in hours to cache a document"},
{"CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF, TAKE1,
"The default time in hours to cache a document"},
{"CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF, TAKE1,
"The factor used to estimate Expires date from LastModified date"},
{"CacheGcInterval", set_cache_gcint, NULL, RSRC_CONF, TAKE1,
"The interval between garbage collections, in hours"},
{"CacheDirLevels", set_cache_dirlevels, NULL, RSRC_CONF, TAKE1,
"The number of levels of subdirectories in the cache"},
{"CacheDirLength", set_cache_dirlength, NULL, RSRC_CONF, TAKE1,
"The number of characters in subdirectory names"},
{"NoCache", set_cache_exclude, NULL, RSRC_CONF, ITERATE,
"A list of names, hosts or domains for which caching is *not* provided"},
{"CacheForceCompletion", set_cache_completion, NULL, RSRC_CONF, TAKE1,
"Force a http cache completion after this percentage is loaded"},
{"ProxyVia", set_via_opt, NULL, RSRC_CONF, TAKE1, {"ProxyVia", set_via_opt, NULL, RSRC_CONF, TAKE1,
"Configure Via: proxy header header to one of: on | off | block | full"}, "Configure Via: proxy header header to one of: on | off | block | full"},
{"ProxyNoCache", set_cache_exclude, NULL, RSRC_CONF, ITERATE,
"A list of names, hosts or domains for which caching is *not* provided"},
{"ProxyForceCacheCompletion", set_cache_completion, NULL, RSRC_CONF, TAKE1,
"Force a http cache completion after this percentage is loaded"},
{NULL} {NULL}
}; };
static void register_hooks(void)
{
/* [2] filename-to-URI translation */
ap_hook_translate_name(proxy_trans, NULL, NULL, AP_HOOK_FIRST);
/* [8] fixups */
ap_hook_fixups(proxy_fixup, NULL, NULL, AP_HOOK_FIRST);
/* [1] post read_request handling */
ap_hook_post_read_request(proxy_detect, NULL, NULL, AP_HOOK_FIRST);
}
module MODULE_VAR_EXPORT proxy_module = module MODULE_VAR_EXPORT proxy_module =
{ {
STANDARD_MODULE_STUFF, STANDARD20_MODULE_STUFF,
proxy_init, /* initializer */ NULL, /* create per-directory config structure */
NULL, /* create per-directory config structure */ NULL, /* merge per-directory config structures */
NULL, /* merge per-directory config structures */ create_proxy_config, /* create per-server config structure */
create_proxy_config, /* create per-server config structure */ NULL, /* merge per-server config structures */
NULL, /* merge per-server config structures */ proxy_cmds, /* command ap_table_t */
proxy_cmds, /* command ap_table_t */ proxy_handlers, /* handlers */
proxy_handlers, /* handlers */ register_hooks
proxy_trans, /* translate_handler */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
proxy_fixup, /* pre-run fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
proxy_detect /* post read-request */
}; };

View File

@@ -75,13 +75,13 @@
Things to do: Things to do:
1. Make it garbage collect in the background, not while someone is waiting for 1. Make it garbage collect in the background, not while someone is
a response! waiting for a response!
2. Check the logic thoroughly. 2. Check the logic thoroughly.
3. Empty directories are only removed the next time round (but this does avoid 3. Empty directories are only removed the next time round (but this does
two passes). Consider doing them the first time round. avoid two passes). Consider doing them the first time round.
Ben Laurie <ben@algroup.co.uk> 30 Mar 96 Ben Laurie <ben@algroup.co.uk> 30 Mar 96
@@ -103,12 +103,13 @@
*/ */
#define TESTING 0 #define TESTING 0
#undef EXPLAIN #undef EXPLAIN
#include "httpd.h" #include "httpd.h"
#include "http_config.h" #include "http_config.h"
#include "http_protocol.h" #include "http_protocol.h"
#include "ap_cache.h"
#include "explain.h" #include "explain.h"
@@ -120,27 +121,25 @@ enum enctype {
enc_path, enc_search, enc_user, enc_fpath, enc_parm enc_path, enc_search, enc_user, enc_fpath, enc_parm
}; };
#define HDR_APP (0) /* append header, for proxy_add_header() */ #define HDR_APP (0) /* append header, for proxy_add_header() */
#define HDR_REP (1) /* replace header, for proxy_add_header() */ #define HDR_REP (1) /* replace header, for proxy_add_header() */
/* number of characters in the hash */ #ifdef CHARSET_EBCDIC
#define HASH_LEN (22*2) #define CRLF "\r\n"
#else /*CHARSET_EBCDIC*/
#define CRLF "\015\012"
#endif /*CHARSET_EBCDIC*/
/* maximum 'CacheDirLevels*CacheDirLength' value */ #define DEFAULT_FTP_DATA_PORT 20
#define CACHEFILE_LEN 20 /* must be less than HASH_LEN/2 */ #define DEFAULT_FTP_PORT 21
#define DEFAULT_GOPHER_PORT 70
#define SEC_ONE_DAY 86400 /* one day, in seconds */ #define DEFAULT_NNTP_PORT 119
#define SEC_ONE_HR 3600 /* one hour, in seconds */ #define DEFAULT_WAIS_PORT 210
#define DEFAULT_HTTPS_PORT 443
#define DEFAULT_FTP_DATA_PORT 20 #define DEFAULT_SNEWS_PORT 563
#define DEFAULT_FTP_PORT 21 #define DEFAULT_PROSPERO_PORT 1525 /* WARNING: conflict w/Oracle */
#define DEFAULT_GOPHER_PORT 70
#define DEFAULT_NNTP_PORT 119
#define DEFAULT_WAIS_PORT 210
#define DEFAULT_HTTPS_PORT 443
#define DEFAULT_SNEWS_PORT 563
#define DEFAULT_PROSPERO_PORT 1525 /* WARNING: conflict w/Oracle */
#define DEFAULT_CACHE_COMPLETION (0.9)
/* Some WWW schemes and their default ports; this is basically /etc/services */ /* Some WWW schemes and their default ports; this is basically /etc/services */
struct proxy_services { struct proxy_services {
const char *scheme; const char *scheme;
@@ -149,10 +148,10 @@ struct proxy_services {
/* static information about a remote proxy */ /* static information about a remote proxy */
struct proxy_remote { struct proxy_remote {
const char *scheme; /* the schemes handled by this proxy, or '*' */ const char *scheme; /* the schemes handled by this proxy, or '*' */
const char *protocol; /* the scheme used to talk to this proxy */ const char *protocol; /* the scheme used to talk to this proxy */
const char *hostname; /* the hostname of this proxy */ const char *hostname; /* the hostname of this proxy */
int port; /* the port for this proxy */ int port; /* the port for this proxy */
}; };
struct proxy_alias { struct proxy_alias {
@@ -177,27 +176,7 @@ struct nocache_entry {
struct in_addr addr; struct in_addr addr;
}; };
#define DEFAULT_CACHE_SPACE 5
#define DEFAULT_CACHE_MAXEXPIRE SEC_ONE_DAY
#define DEFAULT_CACHE_EXPIRE SEC_ONE_HR
#define DEFAULT_CACHE_LMFACTOR (0.1)
#define DEFAULT_CACHE_COMPLETION (0.9)
/* static information about the local cache */
struct cache_conf {
const char *root; /* the location of the cache directory */
off_t space; /* Maximum cache size (in 1024 bytes) */
time_t maxexpire; /* Maximum time to keep cached files in secs */
time_t defaultexpire; /* default time to keep cached file in secs */
double lmfactor; /* factor for estimating expires date */
time_t gcinterval; /* garbage collection interval, in seconds */
int dirlevels; /* Number of levels of subdirectories */
int dirlength; /* Length of subdirectory names */
float cache_completion; /* Force cache completion after this point */
};
typedef struct { typedef struct {
struct cache_conf cache; /* cache configuration */
ap_array_header_t *proxies; ap_array_header_t *proxies;
ap_array_header_t *aliases; ap_array_header_t *aliases;
ap_array_header_t *raliases; ap_array_header_t *raliases;
@@ -205,8 +184,10 @@ typedef struct {
ap_array_header_t *dirconn; ap_array_header_t *dirconn;
ap_array_header_t *nocaches; ap_array_header_t *nocaches;
ap_array_header_t *allowed_connect_ports; ap_array_header_t *allowed_connect_ports;
char *domain; /* domain name to use in absence of a domain name in the request */ char *domain; /* domain name to use in absence of
int req; /* true if proxy requests are enabled */ a domain name in the request */
int req; /* true if proxy requests are enabled */
float cache_completion; /* Force cache completion after this point */
enum { enum {
via_off, via_off,
via_on, via_on,
@@ -214,97 +195,60 @@ typedef struct {
via_full via_full
} viaopt; /* how to deal with proxy Via: headers */ } viaopt; /* how to deal with proxy Via: headers */
size_t recv_buffer_size; size_t recv_buffer_size;
ap_cache_handle_t *cache;
} proxy_server_conf; } proxy_server_conf;
struct hdr_entry {
const char *field;
const char *value;
};
/* caching information about a request */
typedef struct { typedef struct {
request_rec *req; /* the request */ float cache_completion; /* completion percentage */
char *url; /* the URL requested */ int content_length; /* length of the content */
char *filename; /* name of the cache file, or NULL if no cache */ } proxy_completion;
char *tempfile; /* name of the temporary file, of NULL if not caching */
time_t ims; /* if-modified-since date of request; -1 if no header */
BUFF *fp; /* the cache file descriptor if the file is cached
and may be returned, or NULL if the file is
not cached (or must be reloaded) */
time_t expire; /* calculated expire date of cached entity */
time_t lmod; /* last-modified date of cached entity */
time_t date; /* the date the cached file was last touched */
int version; /* update count of the file */
off_t len; /* content length */
char *protocol; /* Protocol, and major/minor number, e.g. HTTP/1.1 */
int status; /* the status of the cached file */
unsigned int written; /* total *content* bytes written to cache */
float cache_completion; /* specific to this request */
char *resp_line; /* the whole status like (protocol, code + message) */
ap_table_t *hdrs; /* the HTTP headers of the file */
} cache_req;
/* Additional information passed to the function called by ap_table_do() */
struct tbl_do_args {
request_rec *req;
cache_req *cache;
};
/* Function prototypes */ /* Function prototypes */
/* proxy_cache.c */
void ap_proxy_cache_tidy(cache_req *c);
int ap_proxy_cache_check(request_rec *r, char *url, struct cache_conf *conf,
cache_req **cr);
int ap_proxy_cache_update(cache_req *c, ap_table_t *resp_hdrs,
const int is_HTTP1, int nocache);
void ap_proxy_garbage_coll(request_rec *r);
/* proxy_connect.c */ /* proxy_connect.c */
int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url, int ap_proxy_connect_handler(request_rec *r, ap_cache_el *c, char *url,
const char *proxyhost, int proxyport); const char *proxyhost, int proxyport);
/* proxy_ftp.c */ /* proxy_ftp.c */
int ap_proxy_ftp_canon(request_rec *r, char *url); int ap_proxy_ftp_canon(request_rec *r, char *url);
int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url); int ap_proxy_ftp_handler(request_rec *r, ap_cache_el *c, char *url);
/* proxy_http.c */ /* proxy_http.c */
int ap_proxy_http_canon(request_rec *r, char *url, const char *scheme, int ap_proxy_http_canon(request_rec *r, char *url, const char *scheme,
int def_port); int def_port);
int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, int ap_proxy_http_handler(request_rec *r, ap_cache_el *c, char *url,
const char *proxyhost, int proxyport); const char *proxyhost, int proxyport);
/* proxy_util.c */ /* proxy_util.c */
int ap_proxy_hex2c(const char *x); int ap_proxy_hex2c(const char *x);
void ap_proxy_c2hex(int ch, char *x); void ap_proxy_c2hex(int ch, char *x);
char *ap_proxy_canonenc(ap_pool_t *p, const char *x, int len, enum enctype t, char *ap_proxy_canonenc(ap_pool_t *p, const char *x, int len, enum enctype t,
int isenc); int isenc);
char *ap_proxy_canon_netloc(ap_pool_t *p, char **const urlp, char **userp, char *ap_proxy_canon_netloc(ap_pool_t *p, char **const urlp, char **userp,
char **passwordp, char **hostp, int *port); char **passwordp, char **hostp, int *port);
const char *ap_proxy_date_canon(ap_pool_t *p, const char *x); const char *ap_proxy_date_canon(ap_pool_t *p, const char *x);
table *ap_proxy_read_headers(request_rec *r, char *buffer, int size, BUFF *f); ap_table_t *ap_proxy_read_headers(request_rec *r, char *buffer, int size, BUFF *f);
long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c); long int ap_proxy_send_fb(proxy_completion *, BUFF *f, request_rec *r, ap_cache_el *c);
void ap_proxy_send_headers(request_rec *r, const char *respline, ap_table_t *hdrs); void ap_proxy_send_headers(request_rec *r, const char *respline, ap_table_t *hdrs);
int ap_proxy_liststr(const char *list, const char *val); int ap_proxy_liststr(const char *list, const char *val);
void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength); void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength);
int ap_proxy_hex2sec(const char *x); int ap_proxy_hex2sec(const char *x);
void ap_proxy_sec2hex(int t, char *y); void ap_proxy_sec2hex(int t, char *y);
cache_req *ap_proxy_cache_error(cache_req *r);
int ap_proxyerror(request_rec *r, int statuscode, const char *message);
const char *ap_proxy_host2addr(const char *host, struct hostent *reqhp); const char *ap_proxy_host2addr(const char *host, struct hostent *reqhp);
void ap_proxy_cache_error(ap_cache_el **r);
int ap_proxyerror(request_rec *r, int statuscode, const char *message);
int ap_proxy_is_ipaddr(struct dirconn_entry *This, ap_pool_t *p); int ap_proxy_is_ipaddr(struct dirconn_entry *This, ap_pool_t *p);
int ap_proxy_is_domainname(struct dirconn_entry *This, ap_pool_t *p); int ap_proxy_is_domainname(struct dirconn_entry *This, ap_pool_t *p);
int ap_proxy_is_hostname(struct dirconn_entry *This, ap_pool_t *p); int ap_proxy_is_hostname(struct dirconn_entry *This, ap_pool_t *p);
int ap_proxy_is_word(struct dirconn_entry *This, ap_pool_t *p); int ap_proxy_is_word(struct dirconn_entry *This, ap_pool_t *p);
int ap_proxy_doconnect(int sock, struct sockaddr_in *addr, request_rec *r); int ap_proxy_doconnect(ap_socket_t *sock, char *host, ap_uint32_t port, request_rec *r);
int ap_proxy_garbage_init(server_rec *, ap_pool_t *); int ap_proxy_garbage_init(server_rec *, ap_pool_t *);
/* This function is called by ap_table_do() for all header lines */ /* This function is called by ap_table_do() for all header lines */
int ap_proxy_send_hdr_line(void *p, const char *key, const char *value); int ap_proxy_send_hdr_line(void *p, const char *key, const char *value);
unsigned ap_proxy_bputs2(const char *data, BUFF *client, cache_req *cache); unsigned ap_proxy_bputs2(const char *data, BUFF *client, ap_cache_el *cache);
#endif /*MOD_PROXY_H*/ #endif /*MOD_PROXY_H*/

View File

@@ -61,13 +61,12 @@
#include "mod_proxy.h" #include "mod_proxy.h"
#include "http_log.h" #include "http_log.h"
#include "http_main.h" #include "http_main.h"
#include "iol_socket.h"
#ifdef HAVE_BSTRING_H #ifdef HAVE_BSTRING_H
#include <bstring.h> /* for IRIX, FD_SET calls bzero() */ #include <bstring.h> /* for IRIX, FD_SET calls bzero() */
#endif #endif
DEF_Explain
/* /*
* This handles Netscape CONNECT method secure proxy requests. * This handles Netscape CONNECT method secure proxy requests.
* A connection is opened to the specified host and data is * A connection is opened to the specified host and data is
@@ -105,187 +104,195 @@ allowed_port(proxy_server_conf *conf, int port)
int *list = (int *) conf->allowed_connect_ports->elts; int *list = (int *) conf->allowed_connect_ports->elts;
for(i = 0; i < conf->allowed_connect_ports->nelts; i++) { for(i = 0; i < conf->allowed_connect_ports->nelts; i++) {
if(port == list[i]) if(port == list[i])
return 1; return 1;
} }
return 0; return 0;
} }
int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url, int ap_proxy_connect_handler(request_rec *r, ap_cache_el *c, char *url,
const char *proxyhost, int proxyport) const char *proxyhost, int proxyport)
{ {
struct sockaddr_in server;
struct in_addr destaddr; struct in_addr destaddr;
struct hostent server_hp;
const char *host, *err; const char *host, *err;
char *p; char *p;
int port, sock; int port;
ap_socket_t *sock;
char buffer[HUGE_STRING_LEN]; char buffer[HUGE_STRING_LEN];
int nbytes, i, j; int nbytes, i, j;
fd_set fds;
BUFF *sock_buff;
ap_socket_t *client_sock;
ap_pollfd_t *pollfd;
ap_int32_t pollcnt;
ap_int16_t pollevent;
void *sconf = r->server->module_config; void *sconf = r->server->module_config;
proxy_server_conf *conf = proxy_server_conf *conf =
(proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts; struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
memset(&server, '\0', sizeof(server));
server.sin_family = AF_INET;
/* Break the URL into host:port pairs */ /* Break the URL into host:port pairs */
host = url; host = url;
p = strchr(url, ':'); p = strchr(url, ':');
if (p == NULL) if (p == NULL)
port = DEFAULT_HTTPS_PORT; port = DEFAULT_HTTPS_PORT;
else { else {
port = atoi(p + 1); port = atoi(p + 1);
*p = '\0'; *p = '\0';
} }
/* check if ProxyBlock directive on this host */ /* check if ProxyBlock directive on this host */
destaddr.s_addr = ap_inet_addr(host); destaddr.s_addr = ap_inet_addr(host);
for (i = 0; i < conf->noproxies->nelts; i++) { for (i = 0; i < conf->noproxies->nelts; i++) {
if ((npent[i].name != NULL && strstr(host, npent[i].name) != NULL) if ((npent[i].name != NULL && strstr(host, npent[i].name) != NULL)
|| destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*') || destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*')
return ap_proxyerror(r, HTTP_FORBIDDEN, return ap_proxyerror(r, HTTP_FORBIDDEN,
"Connect to remote machine blocked"); "Connect to remote machine blocked");
} }
/* Check if it is an allowed port */ /* Check if it is an allowed port */
if (conf->allowed_connect_ports->nelts == 0) { if (conf->allowed_connect_ports->nelts == 0) {
/* Default setting if not overridden by AllowCONNECT */ /* Default setting if not overridden by AllowCONNECT */
switch (port) { switch (port) {
case DEFAULT_HTTPS_PORT: case DEFAULT_HTTPS_PORT:
case DEFAULT_SNEWS_PORT: case DEFAULT_SNEWS_PORT:
break; break;
default: default:
return HTTP_FORBIDDEN; return HTTP_FORBIDDEN;
} }
} else if(!allowed_port(conf, port)) } else if(!allowed_port(conf, port))
return HTTP_FORBIDDEN; return HTTP_FORBIDDEN;
if (proxyhost) { if (proxyhost) {
Explain2("CONNECT to remote proxy %s on port %d", proxyhost, proxyport); ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL,
"CONNECT to remote proxy %s on port %d", proxyhost, proxyport);
} }
else { else {
Explain2("CONNECT to %s on port %d", host, port); ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL,
"CONNECT to %s on port %d", host, port);
} }
server.sin_port = (proxyport ? htons(proxyport) : htons(port)); if ((ap_create_tcp_socket(&sock, r->pool)) != APR_SUCCESS) {
err = ap_proxy_host2addr(proxyhost ? proxyhost : host, &server_hp); ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"proxy: error creating socket");
if (err != NULL) return HTTP_INTERNAL_SERVER_ERROR;
return ap_proxyerror(r,
proxyhost ? HTTP_BAD_GATEWAY : HTTP_INTERNAL_SERVER_ERROR,
err);
sock = ap_psocket(r->pool, PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == -1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"proxy: error creating socket");
return HTTP_INTERNAL_SERVER_ERROR;
} }
#ifdef CHECK_FD_SETSIZE if (ap_proxy_doconnect(sock, (char *)(proxyhost ? proxyhost : host), proxyport ? proxyport : port, r) == -1) {
if (sock >= FD_SETSIZE) { ap_close_socket(sock);
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR,
"proxy_connect_handler: filedescriptor (%u) " ap_pstrcat(r->pool, "Could not connect to remote machine:<br>",
"larger than FD_SETSIZE (%u) " strerror(errno), NULL));
"found, you probably need to rebuild Apache with a "
"larger FD_SETSIZE", sock, FD_SETSIZE);
ap_pclosesocket(r->pool, sock);
return HTTP_INTERNAL_SERVER_ERROR;
}
#endif
j = 0;
while (server_hp.h_addr_list[j] != NULL) {
memcpy(&server.sin_addr, server_hp.h_addr_list[j],
sizeof(struct in_addr));
i = ap_proxy_doconnect(sock, &server, r);
if (i == 0)
break;
j++;
}
if (i == -1) {
char buf[120];
ap_pclosesocket(r->pool, sock);
return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, ap_pstrcat(r->pool,
"Could not connect to remote machine:<br>",
ap_strerror(errno, buf, sizeof(buf)),
NULL));
} }
/* If we are connecting through a remote proxy, we need to pass /* If we are connecting through a remote proxy, we need to pass
* the CONNECT request on to it. * the CONNECT request on to it.
*/ */
if (proxyport) { if (proxyport) {
/* FIXME: We should not be calling write() directly, but we currently /* FIXME: We should not be calling write() directly, but we currently
* have no alternative. Error checking ignored. Also, we force * have no alternative. Error checking ignored. Also, we force
* a HTTP/1.0 request to keep things simple. * a HTTP/1.0 request to keep things simple.
*/ */
Explain0("Sending the CONNECT request to the remote proxy"); ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL,
ap_snprintf(buffer, sizeof(buffer), "CONNECT %s HTTP/1.0" CRLF, "Sending the CONNECT request to the remote proxy");
r->uri); nbytes = ap_snprintf(buffer, sizeof(buffer), "CONNECT %s HTTP/1.0" CRLF, r->uri);
write(sock, buffer, strlen(buffer)); ap_send(sock, buffer, &nbytes);
ap_snprintf(buffer, sizeof(buffer), nbytes = ap_snprintf(buffer, sizeof(buffer),"Proxy-agent: %s" CRLF CRLF, ap_get_server_version());
"Proxy-agent: %s" CRLF CRLF, ap_get_server_version()); ap_send(sock, buffer, &nbytes);
write(sock, buffer, strlen(buffer));
} }
else { else {
Explain0("Returning 200 OK Status"); ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL,
ap_rvputs(r, "HTTP/1.0 200 Connection established" CRLF, NULL); "Returning 200 OK Status");
ap_rvputs(r, "Proxy-agent: ", ap_get_server_version(), CRLF CRLF, NULL); ap_rvputs(r, "HTTP/1.0 200 Connection established" CRLF, NULL);
ap_bflush(r->connection->client); ap_rvputs(r, "Proxy-agent: ", ap_get_server_version(), CRLF CRLF, NULL);
ap_bflush(r->connection->client);
} }
while (1) { /* Infinite loop until error (one side closes the connection) */ sock_buff = ap_bcreate(r->pool, B_RDWR);
FD_ZERO(&fds); ap_bpush_iol(sock_buff, unix_attach_socket(sock));
FD_SET(sock, &fds);
FD_SET(r->connection->client->fd, &fds);
Explain0("Going to sleep (select)"); if(ap_setup_poll(&pollfd, 2, r->pool) != APR_SUCCESS)
i = ap_select((r->connection->client->fd > sock ? {
r->connection->client->fd + 1 : ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "proxy: error ap_setup_poll()");
sock + 1), &fds, NULL, NULL, NULL); return HTTP_INTERNAL_SERVER_ERROR;
Explain1("Woke from select(), i=%d", i);
if (i) {
if (FD_ISSET(sock, &fds)) {
Explain0("sock was set");
if ((nbytes = read(sock, buffer, HUGE_STRING_LEN)) != 0) {
if (nbytes == -1)
break;
if (write(r->connection->client->fd, buffer, nbytes) == EOF)
break;
Explain1("Wrote %d bytes to client", nbytes);
}
else
break;
}
else if (FD_ISSET(r->connection->client->fd, &fds)) {
Explain0("client->fd was set");
if ((nbytes = read(r->connection->client->fd, buffer,
HUGE_STRING_LEN)) != 0) {
if (nbytes == -1)
break;
if (write(sock, buffer, nbytes) == EOF)
break;
Explain1("Wrote %d bytes to server", nbytes);
}
else
break;
}
else
break; /* Must be done waiting */
}
else
break;
} }
ap_pclosesocket(r->pool, sock); /* Add client side to the poll */
#if 0
/* FIXME !!!! SDM !!! If someone can figure out how to turn a conn_rec into a ap_sock_t or something
this code might work. However if we must we can change r->connection->client to non-blocking and
just see if a recv gives us anything and do the same to sock (server) side, I'll leave this as TBD so
one can decide the best path to take
*/
if(ap_put_os_sock(&client_sock, (ap_os_sock_t *)get_socket(r->connection->client),
r->pool) != APR_SUCCESS)
{
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "proxy: error creating client ap_socket_t");
return HTTP_INTERNAL_SERVER_ERROR;
}
ap_add_poll_socket(pollfd, client_sock, APR_POLLIN);
#endif
/* Add the server side to the poll */
ap_add_poll_socket(pollfd, sock, APR_POLLIN);
while (1) { /* Infinite loop until error (one side closes the connection) */
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, "Going to sleep (poll)");
if(ap_poll(pollfd, &pollcnt, -1) != APR_SUCCESS)
{
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "proxy: error ap_poll()");
return HTTP_INTERNAL_SERVER_ERROR;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL,
"Woke from select(), i=%d", pollcnt);
if (pollcnt) {
ap_get_revents(&pollevent, sock, pollfd);
if (pollevent & APR_POLLIN) {
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL,
"sock was set");
if(ap_bread(sock_buff, buffer, HUGE_STRING_LEN, &nbytes) == APR_SUCCESS) {
int o = 0;
while(nbytes)
{
ap_bwrite(r->connection->client, buffer + o, nbytes, &i);
o += i;
nbytes -= i;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL,
"Wrote %d bytes to client", nbytes);
}
else
break;
}
ap_get_revents(&pollevent, client_sock, pollfd);
if (pollevent & APR_POLLIN) {
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL,
"client was set");
if(ap_bread(r->connection->client, buffer, HUGE_STRING_LEN, &nbytes) == APR_SUCCESS) {
int o = 0;
while(nbytes)
{
ap_bwrite(sock_buff, buffer + o, nbytes, &i);
o += i;
nbytes -= i;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL,
"Wrote %d bytes to server", nbytes);
}
else
break;
}
}
else
break;
}
ap_close_socket(sock);
return OK; return OK;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -63,6 +63,7 @@
#include "http_main.h" #include "http_main.h"
#include "http_core.h" #include "http_core.h"
#include "util_date.h" #include "util_date.h"
#include "iol_socket.h"
/* /*
* Canonicalise http-like URLs. * Canonicalise http-like URLs.
@@ -82,7 +83,7 @@ int ap_proxy_http_canon(request_rec *r, char *url, const char *scheme, int def_p
port = def_port; port = def_port;
err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
if (err) if (err)
return HTTP_BAD_REQUEST; return HTTP_BAD_REQUEST;
/* now parse path/search args, according to rfc1738 */ /* now parse path/search args, according to rfc1738 */
/* N.B. if this isn't a true proxy request, then the URL _path_ /* N.B. if this isn't a true proxy request, then the URL _path_
@@ -90,25 +91,25 @@ int ap_proxy_http_canon(request_rec *r, char *url, const char *scheme, int def_p
* == r->unparsed_uri, and no others have that property. * == r->unparsed_uri, and no others have that property.
*/ */
if (r->uri == r->unparsed_uri) { if (r->uri == r->unparsed_uri) {
search = strchr(url, '?'); search = strchr(url, '?');
if (search != NULL) if (search != NULL)
*(search++) = '\0'; *(search++) = '\0';
} }
else else
search = r->args; search = r->args;
/* process path */ /* process path */
path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq); path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq);
if (path == NULL) if (path == NULL)
return HTTP_BAD_REQUEST; return HTTP_BAD_REQUEST;
if (port != def_port) if (port != def_port)
ap_snprintf(sport, sizeof(sport), ":%d", port); ap_snprintf(sport, sizeof(sport), ":%d", port);
else else
sport[0] = '\0'; sport[0] = '\0';
r->filename = ap_pstrcat(r->pool, "proxy:", scheme, "://", host, sport, "/", r->filename = ap_pstrcat(r->pool, "proxy:", scheme, "://", host, sport, "/",
path, (search) ? "?" : "", (search) ? search : "", NULL); path, (search) ? "?" : "", (search) ? search : "", NULL);
return OK; return OK;
} }
@@ -142,17 +143,17 @@ static void clear_connection(ap_pool_t *p, ap_table_t *headers)
ap_table_unset(headers, "Proxy-Connection"); ap_table_unset(headers, "Proxy-Connection");
if (!next) if (!next)
return; return;
while (*next) { while (*next) {
name = next; name = next;
while (*next && !ap_isspace(*next) && (*next != ',')) while (*next && !ap_isspace(*next) && (*next != ','))
++next; ++next;
while (*next && (ap_isspace(*next) || (*next == ','))) { while (*next && (ap_isspace(*next) || (*next == ','))) {
*next = '\0'; *next = '\0';
++next; ++next;
} }
ap_table_unset(headers, name); ap_table_unset(headers, name);
} }
ap_table_unset(headers, "Connection"); ap_table_unset(headers, "Connection");
} }
@@ -166,33 +167,34 @@ static void clear_connection(ap_pool_t *p, ap_table_t *headers)
* we return DECLINED so that we can try another proxy. (Or the direct * we return DECLINED so that we can try another proxy. (Or the direct
* route.) * route.)
*/ */
int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, int ap_proxy_http_handler(request_rec *r, ap_cache_el *c, char *url,
const char *proxyhost, int proxyport) const char *proxyhost, int proxyport)
{ {
const char *strp; const char *strp;
char *strp2; char *strp2;
const char *err, *desthost; const char *err, *desthost;
int i, j, sock, len, backasswards; ap_socket_t *sock;
int i, j, len, backasswards, content_length=-1;
ap_array_header_t *reqhdrs_arr; ap_array_header_t *reqhdrs_arr;
ap_table_t *resp_hdrs; ap_table_t *resp_hdrs;
table_entry *reqhdrs; ap_table_entry_t *reqhdrs;
struct sockaddr_in server; struct sockaddr_in server;
struct in_addr destaddr; struct in_addr destaddr;
struct hostent server_hp; struct hostent server_hp;
BUFF *f; BUFF *f, *cachefp=NULL;
char buffer[HUGE_STRING_LEN]; char buffer[HUGE_STRING_LEN];
char portstr[32]; char portstr[32];
ap_pool_t *p = r->pool; ap_pool_t *p = r->pool;
const long int zero = 0L; const long int zero = 0L;
int destport = 0; int destport = 0;
ap_ssize_t cntr;
char *destportstr = NULL; char *destportstr = NULL;
const char *urlptr = NULL; const char *urlptr = NULL;
const char *datestr; char *datestr, *clen;
struct tbl_do_args tdo;
void *sconf = r->server->module_config; void *sconf = r->server->module_config;
proxy_server_conf *conf = proxy_server_conf *conf =
(proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts; struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
struct nocache_entry *ncent = (struct nocache_entry *) conf->nocaches->elts; struct nocache_entry *ncent = (struct nocache_entry *) conf->nocaches->elts;
int nocache = 0; int nocache = 0;
@@ -204,308 +206,259 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url,
urlptr = strstr(url, "://"); urlptr = strstr(url, "://");
if (urlptr == NULL) if (urlptr == NULL)
return HTTP_BAD_REQUEST; return HTTP_BAD_REQUEST;
urlptr += 3; urlptr += 3;
destport = DEFAULT_HTTP_PORT; destport = DEFAULT_HTTP_PORT;
strp = strchr(urlptr, '/'); strp = strchr(urlptr, '/');
if (strp == NULL) { if (strp == NULL) {
desthost = ap_pstrdup(p, urlptr); desthost = ap_pstrdup(p, urlptr);
urlptr = "/"; urlptr = "/";
} }
else { else {
char *q = ap_palloc(p, strp - urlptr + 1); char *q = ap_palloc(p, strp - urlptr + 1);
memcpy(q, urlptr, strp - urlptr); memcpy(q, urlptr, strp - urlptr);
q[strp - urlptr] = '\0'; q[strp - urlptr] = '\0';
urlptr = strp; urlptr = strp;
desthost = q; desthost = q;
} }
strp2 = strchr(desthost, ':'); strp2 = strchr(desthost, ':');
if (strp2 != NULL) { if (strp2 != NULL) {
*(strp2++) = '\0'; *(strp2++) = '\0';
if (ap_isdigit(*strp2)) { if (ap_isdigit(*strp2)) {
destport = atoi(strp2); destport = atoi(strp2);
destportstr = strp2; destportstr = strp2;
} }
} }
/* check if ProxyBlock directive on this host */ /* check if ProxyBlock directive on this host */
destaddr.s_addr = ap_inet_addr(desthost); destaddr.s_addr = ap_inet_addr(desthost);
for (i = 0; i < conf->noproxies->nelts; i++) { for (i = 0; i < conf->noproxies->nelts; i++) {
if ((npent[i].name != NULL && strstr(desthost, npent[i].name) != NULL) if ((npent[i].name != NULL && strstr(desthost, npent[i].name) != NULL)
|| destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*') || destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*')
return ap_proxyerror(r, HTTP_FORBIDDEN, return ap_proxyerror(r, HTTP_FORBIDDEN,
"Connect to remote machine blocked"); "Connect to remote machine blocked");
}
if ((ap_create_tcp_socket(&sock, r->pool)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"proxy: error creating socket");
return HTTP_INTERNAL_SERVER_ERROR;
}
if (conf->recv_buffer_size > 0 && ap_setsocketopt(sock, APR_SO_RCVBUF,conf->recv_buffer_size)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
} }
if (proxyhost != NULL) { if (proxyhost != NULL) {
server.sin_port = htons(proxyport); i = ap_proxy_doconnect(sock, (char *)proxyhost, proxyport, r);
err = ap_proxy_host2addr(proxyhost, &server_hp);
if (err != NULL)
return DECLINED; /* try another */
} }
else { else {
server.sin_port = htons(destport); i = ap_proxy_doconnect(sock, (char *)desthost, destport, r);
err = ap_proxy_host2addr(desthost, &server_hp);
if (err != NULL)
return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err);
} }
sock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == -1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"proxy: error creating socket");
return HTTP_INTERNAL_SERVER_ERROR;
}
if (conf->recv_buffer_size) {
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
(const char *) &conf->recv_buffer_size, sizeof(int))
== -1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
}
}
#ifdef SINIX_D_RESOLVER_BUG
{
struct in_addr *ip_addr = (struct in_addr *) *server_hp.h_addr_list;
for (; ip_addr->s_addr != 0; ++ip_addr) {
memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr));
i = ap_proxy_doconnect(sock, &server, r);
if (i == 0)
break;
}
}
#else
j = 0;
while (server_hp.h_addr_list[j] != NULL) {
memcpy(&server.sin_addr, server_hp.h_addr_list[j],
sizeof(struct in_addr));
i = ap_proxy_doconnect(sock, &server, r);
if (i == 0)
break;
j++;
}
#endif
if (i == -1) { if (i == -1) {
if (proxyhost != NULL) if (proxyhost != NULL)
return DECLINED; /* try again another way */ return DECLINED; /* try again another way */
else else
char buf[120]; return ap_proxyerror(r, HTTP_BAD_GATEWAY,
return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool, ap_pstrcat(r->pool, "Could not connect to remote machine: ",
"Could not connect to remote machine: ", strerror(errno), NULL));
ap_strerror(errno, buf, sizeof(buf)), NULL));
} }
clear_connection(r->pool, r->headers_in); /* Strip connection-based headers */ clear_connection(r->pool, r->headers_in); /* Strip connection-based headers */
f = ap_bcreate(p, B_RDWR | B_SOCKET); f = ap_bcreate(p, B_RDWR);
ap_bpushfd(f, sock); ap_bpush_iol(f, unix_attach_socket(sock));
ap_bvputs(f, r->method, " ", proxyhost ? url : urlptr, " HTTP/1.0" CRLF, ap_bvputs(f, r->method, " ", proxyhost ? url : urlptr, " HTTP/1.0" CRLF,
NULL); NULL);
if (destportstr != NULL && destport != DEFAULT_HTTP_PORT) if (destportstr != NULL && destport != DEFAULT_HTTP_PORT)
ap_bvputs(f, "Host: ", desthost, ":", destportstr, CRLF, NULL); ap_bvputs(f, "Host: ", desthost, ":", destportstr, CRLF, NULL);
else else
ap_bvputs(f, "Host: ", desthost, CRLF, NULL); ap_bvputs(f, "Host: ", desthost, CRLF, NULL);
if (conf->viaopt == via_block) { if (conf->viaopt == via_block) {
/* Block all outgoing Via: headers */ /* Block all outgoing Via: headers */
ap_table_unset(r->headers_in, "Via"); ap_table_unset(r->headers_in, "Via");
} else if (conf->viaopt != via_off) { } else if (conf->viaopt != via_off) {
/* Create a "Via:" request header entry and merge it */ /* Create a "Via:" request header entry and merge it */
i = ap_get_server_port(r); i = ap_get_server_port(r);
if (ap_is_default_port(i,r)) { if (ap_is_default_port(i,r)) {
strcpy(portstr,""); strcpy(portstr,"");
} else { } else {
ap_snprintf(portstr, sizeof portstr, ":%d", i); ap_snprintf(portstr, sizeof portstr, ":%d", i);
} }
/* Generate outgoing Via: header with/without server comment: */ /* Generate outgoing Via: header with/without server comment: */
ap_table_mergen(r->headers_in, "Via", ap_table_mergen(r->headers_in, "Via",
(conf->viaopt == via_full) (conf->viaopt == via_full)
? ap_psprintf(p, "%d.%d %s%s (%s)", ? ap_psprintf(p, "%d.%d %s%s (%s)",
HTTP_VERSION_MAJOR(r->proto_num), HTTP_VERSION_MAJOR(r->proto_num),
HTTP_VERSION_MINOR(r->proto_num), HTTP_VERSION_MINOR(r->proto_num),
ap_get_server_name(r), portstr, ap_get_server_name(r), portstr,
SERVER_BASEVERSION) AP_SERVER_BASEVERSION)
: ap_psprintf(p, "%d.%d %s%s", : ap_psprintf(p, "%d.%d %s%s",
HTTP_VERSION_MAJOR(r->proto_num), HTTP_VERSION_MAJOR(r->proto_num),
HTTP_VERSION_MINOR(r->proto_num), HTTP_VERSION_MINOR(r->proto_num),
ap_get_server_name(r), portstr) ap_get_server_name(r), portstr)
); );
} }
reqhdrs_arr = ap_table_elts(r->headers_in); reqhdrs_arr = ap_table_elts(r->headers_in);
reqhdrs = (table_entry *) reqhdrs_arr->elts; reqhdrs = (ap_table_entry_t *) reqhdrs_arr->elts;
for (i = 0; i < reqhdrs_arr->nelts; i++) { for (i = 0; i < reqhdrs_arr->nelts; i++) {
if (reqhdrs[i].key == NULL || reqhdrs[i].val == NULL if (reqhdrs[i].key == NULL || reqhdrs[i].val == NULL
/* Clear out headers not to send */ /* Clear out headers not to send */
|| !strcasecmp(reqhdrs[i].key, "Host") /* Already sent */ || !strcasecmp(reqhdrs[i].key, "Host") /* Already sent */
/* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be /* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be
* suppressed if THIS server requested the authentication, * suppressed if THIS server requested the authentication,
* not when a frontend proxy requested it! * not when a frontend proxy requested it!
*/ */
|| !strcasecmp(reqhdrs[i].key, "Proxy-Authorization")) || !strcasecmp(reqhdrs[i].key, "Proxy-Authorization"))
continue; continue;
ap_bvputs(f, reqhdrs[i].key, ": ", reqhdrs[i].val, CRLF, NULL); ap_bvputs(f, reqhdrs[i].key, ": ", reqhdrs[i].val, CRLF, NULL);
} }
ap_bputs(CRLF, f); ap_bputs(CRLF, f);
/* send the request data, if any. */ /* send the request data, if any. */
if (ap_should_client_block(r)) { if (ap_should_client_block(r)) {
while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0) while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0)
ap_bwrite(f, buffer, i); ap_bwrite(f, buffer, i, &cntr);
} }
ap_bflush(f); ap_bflush(f);
len = ap_bgets(buffer, sizeof buffer - 1, f); len = ap_bgets(buffer, sizeof buffer - 1, f);
if (len == -1) { if (len == -1) {
ap_bclose(f); ap_bclose(f);
ap_log_rerror(APLOG_MARK, APLOG_ERR, r, ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"ap_bgets() - proxy receive - Error reading from remote server %s (length %d)", "ap_bgets() - proxy receive - Error reading from remote server %s (length %d)",
proxyhost ? proxyhost : desthost, len); proxyhost ? proxyhost : desthost, len);
return ap_proxyerror(r, HTTP_BAD_GATEWAY, return ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server"); "Error reading from remote server");
} else if (len == 0) { } else if (len == 0) {
ap_bclose(f); ap_bclose(f);
return ap_proxyerror(r, HTTP_BAD_GATEWAY, return ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Document contains no data"); "Document contains no data");
} }
/* Is it an HTTP/1 response? This is buggy if we ever see an HTTP/1.10 */ /* Is it an HTTP/1 response? This is buggy if we ever see an HTTP/1.10 */
if (ap_checkmask(buffer, "HTTP/#.# ###*")) { if (ap_checkmask(buffer, "HTTP/#.# ###*")) {
int major, minor; int major, minor;
if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) { if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) {
major = 1; major = 1;
minor = 0; minor = 0;
} }
/* If not an HTTP/1 message or if the status line was > 8192 bytes */ /* If not an HTTP/1 message or if the status line was > 8192 bytes */
if (buffer[5] != '1' || buffer[len - 1] != '\n') { if (buffer[5] != '1' || buffer[len - 1] != '\n') {
ap_bclose(f); ap_bclose(f);
return HTTP_BAD_GATEWAY; return HTTP_BAD_GATEWAY;
} }
backasswards = 0; backasswards = 0;
buffer[--len] = '\0'; buffer[--len] = '\0';
buffer[12] = '\0'; buffer[12] = '\0';
r->status = atoi(&buffer[9]); r->status = atoi(&buffer[9]);
buffer[12] = ' '; buffer[12] = ' ';
r->status_line = ap_pstrdup(p, &buffer[9]); r->status_line = ap_pstrdup(p, &buffer[9]);
/* read the headers. */ /* read the headers. */
/* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */ /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */
/* Also, take care with headers with multiple occurences. */ /* Also, take care with headers with multiple occurences. */
resp_hdrs = ap_proxy_read_headers(r, buffer, HUGE_STRING_LEN, f);
if (resp_hdrs == NULL) {
ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r->server,
"proxy: Bad HTTP/%d.%d header returned by %s (%s)",
major, minor, r->uri, r->method);
nocache = 1; /* do not cache this broken file */
}
else
{
clear_connection(p, resp_hdrs); /* Strip Connection hdrs */
ap_cache_el_header_merge(c, resp_hdrs);
}
resp_hdrs = ap_proxy_read_headers(r, buffer, HUGE_STRING_LEN, f); if (conf->viaopt != via_off && conf->viaopt != via_block) {
if (resp_hdrs == NULL) { /* Create a "Via:" response header entry and merge it */
ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server, i = ap_get_server_port(r);
"proxy: Bad HTTP/%d.%d header returned by %s (%s)", if (ap_is_default_port(i,r)) {
major, minor, r->uri, r->method); strcpy(portstr,"");
resp_hdrs = ap_make_table(p, 20); } else {
nocache = 1; /* do not cache this broken file */ ap_snprintf(portstr, sizeof portstr, ":%d", i);
} }
ap_cache_el_header_add(c, "Via", (conf->viaopt == via_full)
if (conf->viaopt != via_off && conf->viaopt != via_block) { ? ap_psprintf(p, "%d.%d %s%s (%s)", major, minor,
/* Create a "Via:" response header entry and merge it */ ap_get_server_name(r), portstr, AP_SERVER_BASEVERSION)
i = ap_get_server_port(r); : ap_psprintf(p, "%d.%d %s%s", major, minor, ap_get_server_name(r), portstr)
if (ap_is_default_port(i,r)) { );
strcpy(portstr,""); }
} else {
ap_snprintf(portstr, sizeof portstr, ":%d", i);
}
ap_table_mergen((table *)resp_hdrs, "Via",
(conf->viaopt == via_full)
? ap_psprintf(p, "%d.%d %s%s (%s)",
major, minor,
ap_get_server_name(r), portstr,
SERVER_BASEVERSION)
: ap_psprintf(p, "%d.%d %s%s",
major, minor,
ap_get_server_name(r), portstr)
);
}
clear_connection(p, resp_hdrs); /* Strip Connection hdrs */
} }
else { else {
/* an http/0.9 response */ /* an http/0.9 response */
backasswards = 1; backasswards = 1;
r->status = 200; r->status = 200;
r->status_line = "200 OK"; r->status_line = "200 OK";
/* no headers */
resp_hdrs = ap_make_table(p, 20);
} }
c->hdrs = resp_hdrs;
/* /*
* HTTP/1.0 requires us to accept 3 types of dates, but only generate * HTTP/1.0 requires us to accept 3 types of dates, but only generate
* one type * one type
*/ */
if ((datestr = ap_table_get(resp_hdrs, "Date")) != NULL) if (ap_cache_el_header(c, "Date", &datestr) == APR_SUCCESS)
ap_table_set(resp_hdrs, "Date", ap_proxy_date_canon(p, datestr)); ap_cache_el_header_set(c, "Date", ap_proxy_date_canon(p, datestr));
if ((datestr = ap_table_get(resp_hdrs, "Last-Modified")) != NULL) if (ap_cache_el_header(c, "Last-Modified", &datestr) == APR_SUCCESS)
ap_table_set(resp_hdrs, "Last-Modified", ap_proxy_date_canon(p, datestr)); ap_cache_el_header_set(c, "Last-Modified", ap_proxy_date_canon(p, datestr));
if ((datestr = ap_table_get(resp_hdrs, "Expires")) != NULL) if (ap_cache_el_header(c, "Expires", &datestr) == APR_SUCCESS)
ap_table_set(resp_hdrs, "Expires", ap_proxy_date_canon(p, datestr)); ap_cache_el_header_set(c, "Expires", ap_proxy_date_canon(p, datestr));
if ((datestr = ap_table_get(resp_hdrs, "Location")) != NULL) if (ap_cache_el_header(c, "Location", &datestr) == APR_SUCCESS)
ap_table_set(resp_hdrs, "Location", proxy_location_reverse_map(r, datestr)); ap_cache_el_header_set(c, "Location", proxy_location_reverse_map(r, datestr));
if ((datestr = ap_table_get(resp_hdrs, "URI")) != NULL) if (ap_cache_el_header(c, "URI", &datestr) == APR_SUCCESS)
ap_table_set(resp_hdrs, "URI", proxy_location_reverse_map(r, datestr)); ap_cache_el_header_set(c, "URI", proxy_location_reverse_map(r, datestr));
/* check if NoCache directive on this host */ /* check if NoCache directive on this host */
if (ap_cache_el_header(c, "Content-Length", &clen) == APR_SUCCESS)
content_length = atoi(clen ? clen : "-1");
for (i = 0; i < conf->nocaches->nelts; i++) { for (i = 0; i < conf->nocaches->nelts; i++) {
if ((ncent[i].name != NULL && strstr(desthost, ncent[i].name) != NULL) if ((ncent[i].name != NULL && strstr(desthost, ncent[i].name) != NULL)
|| destaddr.s_addr == ncent[i].addr.s_addr || ncent[i].name[0] == '*') || destaddr.s_addr == ncent[i].addr.s_addr || ncent[i].name[0] == '*')
nocache = 1; nocache = 1;
} }
i = ap_proxy_cache_update(c, resp_hdrs, !backasswards, nocache); if(nocache || !ap_proxy_cache_should_cache(r, resp_hdrs, !backasswards))
if (i != DECLINED) { ap_proxy_cache_error(&c);
ap_bclose(f); else
return i; ap_cache_el_data(c, &cachefp);
}
/* write status line */ /* write status line */
if (!r->assbackwards) if (!r->assbackwards)
ap_rvputs(r, "HTTP/1.0 ", r->status_line, CRLF, NULL); ap_rvputs(r, "HTTP/1.0 ", r->status_line, CRLF, NULL);
if (c != NULL && c->fp != NULL && if (cachefp && ap_bvputs(cachefp, "HTTP/1.0 ", r->status_line, CRLF, NULL) == -1) {
ap_bvputs(c->fp, "HTTP/1.0 ", r->status_line, CRLF, NULL) == -1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, "proxy: error writing status line to cache");
"proxy: error writing status line to %s", c->tempfile); ap_proxy_cache_error(&c);
c = ap_proxy_cache_error(c); cachefp = NULL;
} }
/* send headers */ /* send headers */
tdo.req = r; ap_cache_el_header_walk(c, ap_proxy_send_hdr_line, r, NULL);
tdo.cache = c;
ap_table_do(ap_proxy_send_hdr_line, &tdo, resp_hdrs, NULL);
if (!r->assbackwards) if (!r->assbackwards)
ap_rputs(CRLF, r); ap_rputs(CRLF, r);
if (c != NULL && c->fp != NULL && ap_bputs(CRLF, c->fp) == -1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
"proxy: error writing CRLF to %s", c->tempfile);
c = ap_proxy_cache_error(c);
}
ap_bsetopt(r->connection->client, BO_BYTECT, &zero); ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
r->sent_bodyct = 1; r->sent_bodyct = 1;
/* Is it an HTTP/0.9 respose? If so, send the extra data */ /* Is it an HTTP/0.9 respose? If so, send the extra data */
if (backasswards) { if (backasswards) {
ap_bwrite(r->connection->client, buffer, len); ap_bwrite(r->connection->client, buffer, len, &cntr);
if (c != NULL && c->fp != NULL && ap_bwrite(c->fp, buffer, len) != len) { if (cachefp && ap_bwrite(cachefp, buffer, len, &cntr) != len) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"proxy: error writing extra data to %s", c->tempfile); "proxy: error writing extra data to cache", cachefp);
c = ap_proxy_cache_error(c); ap_proxy_cache_error(&c);
} }
} }
#ifdef CHARSET_EBCDIC #ifdef CHARSET_EBCDIC
@@ -516,19 +469,17 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url,
ap_bsetflag(r->connection->client, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0); ap_bsetflag(r->connection->client, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0);
#endif #endif
/* send body */ /* send body */
/* if header only, then cache will be NULL */ /* if header only, then cache will be NULL */
/* HTTP/1.0 tells us to read to EOF, rather than content-length bytes */ /* HTTP/1.0 tells us to read to EOF, rather than content-length bytes */
if (!r->header_only) { if (!r->header_only) {
/* we need to set this for ap_proxy_send_fb()... */ proxy_completion pc;
c->cache_completion = conf->cache.cache_completion; pc.content_length = content_length;
ap_proxy_send_fb(f, r, c); pc.cache_completion = conf->cache_completion;
ap_proxy_send_fb(&pc, f, r, c);
} }
ap_proxy_cache_tidy(c);
ap_bclose(f); ap_bclose(f);
if(c) ap_proxy_cache_update(c);
ap_proxy_garbage_coll(r);
return OK; return OK;
} }

File diff suppressed because it is too large Load Diff