rse 98/02/23 00:27:41
Modified: src CHANGES
htdocs/manual new_features_1_3.html
htdocs/manual/mod mod_rewrite.html
src/modules/standard mod_rewrite.c mod_rewrite.h
. STATUS
Log:
Add the new RewriteMap types `rnd' and `int' to mod_rewrite to allow Apache to
be used as a Reverse Proxy (where the backend servers are choosen via a `rnd'
map) and to allow mass virtual hosting without <VirtualHost> sections (where
you have to fix the case of server names when translating the Host-Header to a
directory structure).
Together with the comitted ProxyPassReverse directive we now have solved
two things the users have asked in the past:
1. The ability to use Apache as a full-featured Reverse Proxy
2. The ability to do mass virtual hosting without <VirtualHost> sections.
For both topics we should write stand-alone documents (perhaps inside
htdocs/manual/misc/) because they are not trivial to do, even when we now have
the functionality ;-)
Submitted by: Ralf S. Engelschall
Reviewed by: Dean Gaudet, Ralf S. Engelschall
Revision Changes Path
1.659 +12 -0 apache-1.3/src/CHANGES
Index: CHANGES
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/CHANGES,v
retrieving revision 1.658
retrieving revision 1.659
diff -u -r1.658 -r1.659
--- CHANGES 1998/02/23 08:04:05 1.658
+++ CHANGES 1998/02/23 08:27:32 1.659
@@ -1,5 +1,17 @@
Changes with Apache 1.3b6
+ *) Add new RewriteMap types: First, `rnd' which is equivalent to the `txt'
+ type but with a special post-processing for the looked-up value: It
+ parses it into alternatives according to `|' chars and then only one
+ particular alternative is choosen randomly (this is an essential
+ functionality needed for balancing between backend-servers when using
+ Apache as a Reverse Proxy. The looked up value here is a list of
+ servers). Second, `int' with the built-in maps named `tolower' and
+ `toupper' which can be used to map URL parts to a fixed case (this is an
+ essential feature to fix the case of server names when doing mass
+ virtual-hosting with the help of mod_rewrite instead of using
+ <VirtualHost> sections).
+
*) Add a new directive to mod_proxy similar to ProxyPass:
`ProxyPassReverse'.
This directive lets Apache adjust the URL in Location-headers on HTTP
redirect responses sent by the remote server. This way the virtually
1.46 +11 -0 apache-1.3/htdocs/manual/new_features_1_3.html
Index: new_features_1_3.html
===================================================================
RCS file: /export/home/cvs/apache-1.3/htdocs/manual/new_features_1_3.html,v
retrieving revision 1.45
retrieving revision 1.46
diff -u -r1.45 -r1.46
--- new_features_1_3.html 1998/02/23 08:04:08 1.45
+++ new_features_1_3.html 1998/02/23 08:27:35 1.46
@@ -562,6 +562,17 @@
as a reverse proxy to avoid by-passing the reverse proxy because of HTTP
redirects on the backend servers which stay behind the reverse proxy.
</LI>
+
+ <LI><STRONG>New map types for <SAMP><A
HREF="mod/mod_rewrite.html#RewriteMap">RewriteMap</A></SAMP> directive</STRONG>
+ <BR>
+ The new map types `Randomized Plain Text' and `Internal Function' were
added
+ to the <SAMP>RewriteMap</SAMP> directive of mod_rewrite. They provide two
+ new features: First, you now can randomly choose a sub-value from a value
+ which was looked-up in a rewriting map (which is useful when choosing
+ between backend servers in a Reverse Proxy situation). Second, you now can
+ translate URL parts to fixed (upper or lower) case (which is useful when
+ doing mass virtual hosting by the help of mod_rewrite).
+ </LI>
</UL>
<!--#include virtual="footer.html" -->
1.23 +103 -42 apache-1.3/htdocs/manual/mod/mod_rewrite.html
Index: mod_rewrite.html
===================================================================
RCS file: /export/home/cvs/apache-1.3/htdocs/manual/mod/mod_rewrite.html,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -r1.22 -r1.23
--- mod_rewrite.html 1998/02/05 21:20:01 1.22
+++ mod_rewrite.html 1998/02/23 08:27:36 1.23
@@ -256,7 +256,7 @@
<A
HREF="directive-dict.html#Syntax"
REL="Help"
-><STRONG>Syntax:</STRONG></A> <CODE>RewriteMap</CODE> <EM>Mapname</EM>
<CODE>{txt,dbm,prg}:</CODE><EM>Filename</EM><BR>
+><STRONG>Syntax:</STRONG></A> <CODE>RewriteMap</CODE> <EM>MapName</EM>
<EM>MapType</EM><CODE>:</CODE><EM>MapSource</EM><BR>
<A
HREF="directive-dict.html#Default"
REL="Help"
@@ -267,82 +267,146 @@
><STRONG>Context:</STRONG></A> server config, virtual host<BR>
<P>
-The <TT>RewriteMap</TT> directive defines an external <EM>Rewriting Map</EM>
+The <TT>RewriteMap</TT> directive defines a <EM>Rewriting Map</EM>
which can be used inside rule substitution strings by the mapping-functions
-to insert/substitute fields through a key lookup.
+to insert/substitute fields through a key lookup. The source of this
+lookup can be of various types.
<P>
-The <A NAME="mapfunc"><EM>Mapname</EM></A> is the name of the map and will
+The <A NAME="mapfunc"><EM>MapName</EM></A> is the name of the map and will
be used to specify a mapping-function for the substitution strings of a
-rewriting rule via
+rewriting rule via one of the following constructs:
<BLOCKQUOTE><STRONG>
-<CODE>${</CODE> <EM>Mapname</EM> <CODE>:</CODE> <EM>LookupKey</EM>
+<CODE>${</CODE> <EM>MapName</EM> <CODE>:</CODE> <EM>LookupKey</EM>
+<CODE>}</CODE><BR>
+<CODE>${</CODE> <EM>MapName</EM> <CODE>:</CODE> <EM>LookupKey</EM>
<CODE>|</CODE> <EM>DefaultValue</EM> <CODE>}</CODE>
</STRONG></BLOCKQUOTE>
-When such a directive occurs the map <EM>Mapname</EM>
+When such a construct occurs the map <EM>MapName</EM>
is consulted and the key <EM>LookupKey</EM> is looked-up. If the key is
-found, the map-function directive is substituted by <EM>SubstValue</EM>. If
-the key is not found then it is substituted by <EM>DefaultValue</EM>.
+found, the map-function construct is substituted by <EM>SubstValue</EM>. If
+the key is not found then it is substituted by <EM>DefaultValue</EM> or
+the empty string if no <EM>DefaultValue</EM> was specified.
<P>
-The <EM>Filename</EM> must be a valid Unix filepath, containing one
-of the following formats:
+The following combinations for <EM>MapType</EM> and <EM>MapSource</EM>
+can be used:
-<OL>
-<LI><STRONG>Plain Text Format</STRONG>
+<UL>
+<LI><STRONG>Standard Plain Text</STRONG><BR>
+ MapType: <CODE>txt</CODE>, MapSource: Unix filesystem path to valid
regular file
<P>
- This is a ASCII file which contains either blank lines, comment lines
- (starting with a '#' character) or
+ This is the standard rewriting map feature where the <EM>MapSource</EM>
is
+ a plain ASCII file containing either blank lines, comment lines
(starting
+ with a '#' character) or pairs like the following - one per line.
<BLOCKQUOTE><STRONG>
<EM>MatchingKey</EM> <EM>SubstValue</EM>
</STRONG></BLOCKQUOTE>
- pairs - one per line. You can create such files either manually,
- using your favorite editor, or by using the programs
- <TT>mapcollect</TT> and <TT>mapmerge</TT> from the <TT>support</TT>
- directory of the <STRONG>mod_rewrite</STRONG> distribution.
<P>
- To declare such a map prefix, <EM>Filename</EM> with a <CODE>txt:</CODE>
- string as in the following example:
-
+ Example:
<P>
<table border=0 cellspacing=1 cellpadding=5 bgcolor="#f0f0f0">
<TR><TD><PRE>
-#
-# map.real-to-user -- maps realnames to usernames
-#
+##
+## map.txt -- rewriting map
+##
Ralf.S.Engelschall rse # Bastard Operator From Hell
-Dr.Fred.Klabuster fred # Mr. DAU
+Mr.Joe.Average joe # Mr. Average
</PRE></TD></TR>
</TABLE>
<P>
<table border=0 cellspacing=1 cellpadding=5 bgcolor="#f0f0f0">
<TR><TD><PRE>
-RewriteMap real-to-host txt:/path/to/file/map.real-to-user
+RewriteMap real-to-host txt:/path/to/file/map.txt
</PRE></TD></TR>
</TABLE>
<P>
-<LI><STRONG>DBM Hashfile Format</STRONG>
+<LI><STRONG>Randomized Plain Text</STRONG><BR>
+ MapType: <CODE>rnd</CODE>, MapSource: Unix filesystem path to valid
regular file
<P>
- This is a binary NDBM format file containing the
- same contents as the <EM>Plain Text Format</EM> files. You can create
- such a file with any NDBM tool or with the <TT>dbmmanage</TT> program
- from the <TT>support</TT> directory of the Apache distribution.
+ This is identical to the Standard Plain Text variant above but with a
special
+ post-processing feature: After looking up a value it is parsed according
+ to contained ``<TT>|</TT>'' characters which have the meaning of ``or''.
Or
+ in other words: they indicate a set of alternatives from which the actual
+ returned value is choosen randomly. Although this sounds crazy and
useless, it
+ was actually designed for load balancing in a reverse proxy situation
where
+ the looked up values are server names.
+ Example:
+<P>
+<table border=0 cellspacing=1 cellpadding=5 bgcolor="#f0f0f0">
+<TR><TD><PRE>
+##
+## map.txt -- rewriting map
+##
+
+static www1|www2|www3|www4
+dynamic www5|www6
+</PRE></TD></TR>
+</TABLE>
+
+<P>
+<table border=0 cellspacing=1 cellpadding=5 bgcolor="#f0f0f0">
+<TR><TD><PRE>
+RewriteMap servers rnd:/path/to/file/map.txt
+</PRE></TD></TR>
+</TABLE>
+
+<P>
+<LI><STRONG>Hash File</STRONG><BR>
+ MapType: <CODE>dbm</CODE>, MapSource: Unix filesystem path to valid
regular file
<P>
- To declare such a map prefix <EM>Filename</EM> with a <CODE>dbm:</CODE>
- string.
+ Here the source is a binary NDBM format file containing the same
contents
+ as a <EM>Plain Text</EM> format file, but in a special representation
+ which is optimized for really fast lookups. You can create such a file
with
+ any NDBM tool or with the following Perl script:
+ <P>
+ <table border=0 cellspacing=1 cellpadding=5 bgcolor="#f0f0f0">
+ <TR><TD><PRE>
+#!/path/to/bin/perl
+##
+## txt2dbm -- convert txt map to dbm format
+##
+
+($txtmap, $dbmmap) = @ARGV;
+open(TXT, "<$txtmap");
+dbmopen(%DB, $dbmmap, 0644);
+while (<TXT>) {
+ next if (m|^s*#.*| or m|^s*$|);
+ $DB{$1} = $2 if (m|^\s*(\S+)\s+(\S+)$|);
+}
+dbmclose(%DB);
+close(TXT)</PRE></TD></TR>
+ </TABLE>
+ <P>
+ <table border=0 cellspacing=1 cellpadding=5 bgcolor="#f0f0f0">
+ <TR><TD><PRE>$ txt2dbm map.txt map.db </PRE></TD></TR>
+ </TABLE>
+<P>
+<LI><STRONG>Internal Function</STRONG><BR>
+ MapType: <CODE>int</CODE>, MapSource: Internal Apache function
+ <P>
+ Here the source is an internal Apache function. Currently you cannot
+ create your own, but the following functions already exists:
+ <UL>
+ <LI><STRONG>toupper</STRONG>:<BR>
+ Converts the looked up key to all upper case.
+ <LI><STRONG>tolower</STRONG>:<BR>
+ Converts the looked up key to all lower case.
+ </UL>
<P>
-<LI><STRONG>Program Format</STRONG>
+<LI><STRONG>External Rewriting Program</STRONG><BR>
+ MapType: <CODE>prg</CODE>, MapSource: Unix filesystem path to valid
regular file
<P>
- This is a Unix executable, not a lookup file. To create it you can use
+ Here the source is a Unix program, not a map file. To create it you can
use
the language of your choice, but the result has to be a run-able Unix
- binary (i.e. either object-code or a script with the
+ executable (i.e. either object-code or a script with the
magic cookie trick '<TT>#!/path/to/interpreter</TT>' as the first line).
<P>
This program gets started once at startup of the Apache servers and then
@@ -367,7 +431,7 @@
</PRE></TD></TR>
</TABLE>
<P>
- <STRONG>But be very careful:</STRONG><BR>
+ But be very careful:<BR>
<OL>
<LI>``<EM>Keep the program simple, stupid</EM>'' (KISS), because
if this program hangs it will lead to a hang of the Apache server
@@ -376,10 +440,7 @@
This will cause a deadloop! Hence the ``<TT>$|=1</TT>'' in the above
example...
</OL>
- <P>
- To declare such a map prefix <EM>Filename</EM> with a <CODE>prg:</CODE>
- string.
-</OL>
+</UL>
The <TT>RewriteMap</TT> directive can occur more than once. For each
mapping-function use one <TT>RewriteMap</TT> directive to declare its
1.67 +147 -2 apache-1.3/src/modules/standard/mod_rewrite.c
Index: mod_rewrite.c
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/modules/standard/mod_rewrite.c,v
retrieving revision 1.66
retrieving revision 1.67
diff -u -r1.66 -r1.67
--- mod_rewrite.c 1998/02/02 22:33:40 1.66
+++ mod_rewrite.c 1998/02/23 08:27:38 1.67
@@ -93,6 +93,7 @@
#include <time.h>
#include <signal.h>
#include <errno.h>
+#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef WIN32
@@ -435,11 +436,17 @@
new = push_array(sconf->rewritemaps);
new->name = a1;
+ new->func = NULL;
if (strncmp(a2, "txt:", 4) == 0) {
new->type = MAPTYPE_TXT;
new->datafile = a2+4;
new->checkfile = a2+4;
}
+ else if (strncmp(a2, "rnd:", 4) == 0) {
+ new->type = MAPTYPE_RND;
+ new->datafile = a2+4;
+ new->checkfile = a2+4;
+ }
else if (strncmp(a2, "dbm:", 4) == 0) {
#ifdef HAS_NDBM_LIB
new->type = MAPTYPE_DBM;
@@ -455,6 +462,18 @@
new->datafile = a2+4;
new->checkfile = a2+4;
}
+ else if (strncmp(a2, "int:", 4) == 0) {
+ new->type = MAPTYPE_INT;
+ new->datafile = NULL;
+ new->checkfile = NULL;
+ if (strcmp(a2+4, "tolower") == 0)
+ new->func = rewrite_mapfunc_tolower;
+ else if (strcmp(a2+4, "toupper") == 0)
+ new->func = rewrite_mapfunc_toupper;
+ else if (sconf->state == ENGINE_ENABLED)
+ return pstrcat(cmd->pool, "RewriteMap: internal map not found:",
+ a2+4, NULL);
+ }
else {
new->type = MAPTYPE_TXT;
new->datafile = a2;
@@ -2499,6 +2518,52 @@
s->name, key);
}
}
+ else if (s->type == MAPTYPE_INT) {
+ if ((value = lookup_map_internal(r, s->func, key)) != NULL) {
+ rewritelog(r, 5, "map lookup OK: map=%s key=%s ->
val=%s",
+ s->name, key, value);
+ return value;
+ }
+ else {
+ rewritelog(r, 5, "map lookup FAILED: map=%s key=%s",
+ s->name, key);
+ }
+ }
+ else if (s->type == MAPTYPE_RND) {
+ if (stat(s->checkfile, &st) == -1) {
+ aplog_error(APLOG_MARK, APLOG_ERR, r->server,
+ "mod_rewrite: can't access text RewriteMap "
+ "file %s", s->checkfile);
+ rewritelog(r, 1,
+ "can't open RewriteMap file, see error log");
+ return NULL;
+ }
+ value = get_cache_string(cachep, s->name, CACHEMODE_TS,
+ st.st_mtime, key);
+ if (value == NULL) {
+ rewritelog(r, 6, "cache lookup FAILED, forcing new "
+ "map lookup");
+ if ((value =
+ lookup_map_txtfile(r, s->datafile, key)) != NULL) {
+ rewritelog(r, 5, "map lookup OK: map=%s key=%s[txt] "
+ "-> val=%s", s->name, key, value);
+ set_cache_string(cachep, s->name, CACHEMODE_TS,
+ st.st_mtime, key, value);
+ }
+ else {
+ rewritelog(r, 5, "map lookup FAILED: map=%s[txt] "
+ "key=%s", s->name, key);
+ return NULL;
+ }
+ }
+ else {
+ rewritelog(r, 5, "cache lookup OK: map=%s[txt] key=%s "
+ "-> val=%s", s->name, key, value);
+ }
+ value = select_random_value_part(r, value);
+ rewritelog(r, 5, "randomly choosen the subvalue `%s'",
value);
+ return value;
+ }
}
}
return NULL;
@@ -2605,7 +2670,87 @@
return pstrdup(r->pool, buf);
}
+static char *lookup_map_internal(request_rec *r,
+ char *(*func)(request_rec *, char *),
+ char *key)
+{
+ /* currently we just let the function convert
+ the key to a corresponding value */
+ return func(r, key);
+}
+
+static char *rewrite_mapfunc_toupper(request_rec *r, char *key)
+{
+ char *value, *cp;
+
+ for (cp = value = pstrdup(r->pool, key); cp != NULL && *cp != '\0'; cp++)
+ *cp = toupper(*cp);
+ return value;
+}
+
+static char *rewrite_mapfunc_tolower(request_rec *r, char *key)
+{
+ char *value, *cp;
+
+ for (cp = value = pstrdup(r->pool, key); cp != NULL && *cp != '\0'; cp++)
+ *cp = tolower(*cp);
+ return value;
+}
+
+static int rewrite_rand_init_done = 0;
+void rewrite_rand_init(void)
+{
+ if (!rewrite_rand_init_done) {
+ srand((unsigned)(getpid()));
+ rewrite_rand_init_done = 1;
+ }
+ return;
+}
+
+int rewrite_rand(int l, int h)
+{
+ int i;
+ char buf[50];
+
+ rewrite_rand_init();
+ sprintf(buf, "%.0f", (((double)rand()/RAND_MAX)*(h-l)));
+ i = atoi(buf)+1;
+ if (i < l) i = l;
+ if (i > h) i = h;
+ return i;
+}
+
+static char *select_random_value_part(request_rec *r, char *value)
+{
+ char *buf;
+ int n, i, k;
+
+ /* count number of distinct values */
+ for (n = 1, i = 0; value[i] != '\0'; i++)
+ if (value[i] == '|')
+ n++;
+
+ /* when only one value we have no option to choose */
+ if (n == 1)
+ return value;
+
+ /* else randomly select one */
+ k = rewrite_rand(1, n);
+
+ /* and grep it out */
+ for (n = 1, i = 0; value[i] != '\0'; i++) {
+ if (n == k)
+ break;
+ if (value[i] == '|')
+ n++;
+ }
+ buf = pstrdup(r->pool, &value[i]);
+ for (i = 0; buf[i] != '\0' && buf[i] != '|'; i++)
+ ;
+ buf[i] = '\0';
+ return buf;
+}
/*
@@ -2912,7 +3057,7 @@
continue;
}
}
- outp = ap_cpystrn(outp, cp, endp - outp);
+ outp = ap_cpystrn(outp, cp, endp - outp);
break;
}
return expanded ? pstrdup(r->pool, output) : str;
@@ -3010,7 +3155,7 @@
result = r->server->server_admin;
}
else if (strcasecmp(var, "SERVER_NAME") == 0) {
- result = get_server_name(r);
+ result = get_server_name(r);
}
else if (strcasecmp(var, "SERVER_PORT") == 0) {
ap_snprintf(resultbuf, sizeof(resultbuf), "%u", get_server_port(r));
1.37 +13 -3 apache-1.3/src/modules/standard/mod_rewrite.h
Index: mod_rewrite.h
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/modules/standard/mod_rewrite.h,v
retrieving revision 1.36
retrieving revision 1.37
diff -u -r1.36 -r1.37
--- mod_rewrite.h 1998/01/07 16:46:56 1.36
+++ mod_rewrite.h 1998/02/23 08:27:39 1.37
@@ -184,6 +184,8 @@
#define MAPTYPE_TXT 1<<0
#define MAPTYPE_DBM 1<<1
#define MAPTYPE_PRG 1<<2
+#define MAPTYPE_INT 1<<3
+#define MAPTYPE_RND 1<<4
#define ENGINE_DISABLED 1<<0
#define ENGINE_ENABLED 1<<1
@@ -228,8 +230,10 @@
char *datafile; /* filename for map data files */
char *checkfile; /* filename to check for map existence */
int type; /* the type of the map */
- int fpin; /* in filepointer for program maps */
- int fpout; /* out filepointer for program maps */
+ int fpin; /* in file pointer for program maps */
+ int fpout; /* out file pointer for program maps */
+ char *(*func)(request_rec *, /* function pointer for internal maps */
+ char *);
} rewritemap_entry;
typedef struct {
@@ -385,13 +389,19 @@
static char *expand_tildepaths(request_rec *r, char *uri);
static void expand_map_lookups(request_rec *r, char *uri, int uri_len);
- /* DBM hashfile support functions */
+ /* rewrite map support functions */
static char *lookup_map(request_rec *r, char *name, char *key);
static char *lookup_map_txtfile(request_rec *r, char *file, char *key);
#if HAS_NDBM_LIB
static char *lookup_map_dbmfile(request_rec *r, char *file, char *key);
#endif
static char *lookup_map_program(request_rec *r, int fpin, int fpout, char
*key);
+static char *lookup_map_internal(request_rec *r, char *(*func)(request_rec
*r, char *key), char *key);
+static char *rewrite_mapfunc_toupper(request_rec *r, char *key);
+static char *rewrite_mapfunc_tolower(request_rec *r, char *key);
+static char *select_random_value_part(request_rec *r, char *value);
+static void rewrite_rand_init(void);
+static int rewrite_rand(int l, int h);
/* rewriting logfile support */
static void open_rewritelog(server_rec *s, pool *p);
1.162 +0 -4 apache-1.3/STATUS
Index: STATUS
===================================================================
RCS file: /export/home/cvs/apache-1.3/STATUS,v
retrieving revision 1.161
retrieving revision 1.162
diff -u -r1.161 -r1.162
--- STATUS 1998/02/23 08:04:13 1.161
+++ STATUS 1998/02/23 08:27:40 1.162
@@ -30,10 +30,6 @@
Status: Dean +1, Martin +0 (duplicates /server-info?server),
Alexei -1 (shared lib concerns)
- * Ralf's [PATCH] New RewriteMap types for mod_rewrite
- <[EMAIL PROTECTED]>
- Status: Ralf +1
-
Concepts:
* Dean's [PRE-PATCH] expanding ap_snprintf()