Mailing List Archive

cvs commit: apachen/src/modules/standard mod_rewrite.c mod_rewrite.h
rse 97/11/12 02:46:57

Modified: src CHANGES
src/modules/standard mod_rewrite.c mod_rewrite.h
Log:
The mod_rewrite ``Break Its Heart'' patch:

- the rewriting engine now is really very well documented, so
all Apache developers should be able to understand it now ;_) Try it
out: look at the new apply_rewrite_list and apply_rewrite_rule functions
and try to understand it...

- the rewriting engine no longer contains any redundant stuff for the
proxy-throughput, redirection and per-dir cases. This makes it even more
clear to understand and avoid future bugs like the following:

- fixed the query string bug recently discovered by Mark:
| RewriteRule ^foo /bar?query [R]
| RewriteRule ^foo http://host/bar?query [R]
where the second rule's query string was escaped.
(fixed by avoiding this as an extra case ;_)

- fixed the nasty redirection bug recently discovered in c.i.w.s.u
| RewriteRule ^(.*[^/])(.*) ${vhost:$1|$1}$2 [R,L]
where the expansion of ${vhost} to http://... was too
late for the rewriting engine to accept it as a redirect.
(fixed by new and more accurate evaluation order)

- split out the fully-qualification of URLs into own named function
fully_qualify_uri(r). This avoid redundancy, too. Also incorporate
updated APACHE_SSL #ifdef'edchanges to mod_rewrite.c from Ben's latest
1.2.4+ssl_1.11 patch

- make cmd_rewriterule static (this was left over from old days of the
mod_rewrite_compat which never found its way into the core distribution)

Submitted by: Ralf S. Engelschall
Reviewed by: Roy T. Fielding, Jim Jagielski, Ralf S. Engelschall

Revision Changes Path
1.504 +12 -0 apachen/src/CHANGES

Index: CHANGES
===================================================================
RCS file: /export/home/cvs/apachen/src/CHANGES,v
retrieving revision 1.503
retrieving revision 1.504
diff -u -r1.503 -r1.504
--- CHANGES 1997/11/12 00:05:55 1.503
+++ CHANGES 1997/11/12 10:46:52 1.504
@@ -1,5 +1,17 @@
Changes with Apache 1.3b3

+ *) Complete rewrite ;_) of mod_rewrite's URL rewriting engine:
+ Now the rewriting engine (the heart of mod_rewrite) is organized more
+ straight-forward, first time well documented and reduced to the really
+ essential parts. All redundant cases were stripped off and processing now
+ is the same for both per-server and per-directory context with only a
+ minimum difference (the prefix stripping in per-dir context). As a
+ side-effect some subtle restrictions and two recently discovered problems
+ are gone: Wrong escaping of QUERY_STRING on redirects in per-directory
+ context and restrictions on the substitution URL on redirects.
+ Additionally some minor source cleanups were done.
+ [Ralf S. Engelschall]
+
*) Lars Eilebrecht wrote a whole new set of Apache Vhost Internals
documentation, examples, explanations and caveats. They live in a new
subdirectory htdocs/manual/vhost/. [Lars Eilebrecht <sfx@unix-ag.org>]



1.56 +380 -244 apachen/src/modules/standard/mod_rewrite.c

Index: mod_rewrite.c
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_rewrite.c,v
retrieving revision 1.55
retrieving revision 1.56
diff -u -r1.55 -r1.56
--- mod_rewrite.c 1997/10/27 19:15:35 1.55
+++ mod_rewrite.c 1997/11/12 10:46:55 1.56
@@ -235,7 +235,7 @@
static int proxy_available;

/* maximum nmatch parameter for regexec */
-#define MAX_NMATCH (10)
+#define MAX_NMATCH (10)

/* the txt mapfile parsing stuff */
#define MAPFILE_PATTERN "^([^ \t]+)[ \t]+([^ \t]+).*$"
@@ -637,9 +637,8 @@
return NULL;
}

-/* NON static */
-const char *cmd_rewriterule(cmd_parms *cmd, rewrite_perdir_conf *dconf,
- char *str)
+static const char *cmd_rewriterule(cmd_parms *cmd, rewrite_perdir_conf *dconf,
+ char *str)
{
rewrite_server_conf *sconf;
rewriterule_entry *new;
@@ -1447,11 +1446,15 @@
/*
** +-------------------------------------------------------+
** | |
-** | rewriting engine
+** | the rewriting engine
** | |
** +-------------------------------------------------------+
*/

+/*
+ * Apply a complete rule set,
+ * i.e. a list of rewrite rules
+ */
static int apply_rewrite_list(request_rec *r, array_header *rewriterules,
char *perdir)
{
@@ -1462,14 +1465,19 @@
int rc;
int s;

+ /*
+ * Iterate over all existing rules
+ */
entries = (rewriterule_entry *)rewriterules->elts;
changed = 0;
loop:
for (i = 0; i < rewriterules->nelts; i++) {
p = &entries[i];

- /* ignore this rule on subrequests if we are explicitly asked to do so
- * or this is a proxy throughput or a forced redirect rule
+ /*
+ * Ignore this rule on subrequests if we are explicitly
+ * asked to do so or this is a proxy-throughput or a
+ * forced redirect rule.
*/
if (r->main != NULL &&
(p->flags & RULEFLAG_IGNOREONSUBREQ ||
@@ -1477,40 +1485,73 @@
p->flags & RULEFLAG_FORCEREDIRECT ))
continue;

- /* apply the current rule */
+ /*
+ * Apply the current rule.
+ */
rc = apply_rewrite_rule(r, p, perdir);
if (rc) {
- if (rc != 2) /* not a match-only rule */
+ /*
+ * Indicate a change if this was not a match-only rule.
+ */
+ if (rc != 2)
changed = 1;
+
+ /*
+ * Pass-Through Feature (`RewriteRule .. .. [PT]'):
+ * Because the Apache 1.x API is very limited we
+ * need this hack to pass the rewritten URL to other
+ * modules like mod_alias, mod_userdir, etc.
+ */
if (p->flags & RULEFLAG_PASSTHROUGH) {
- rewritelog(r, 2,
- "forcing '%s' to get passed through to next URI-to-filename handler",
- r->filename);
- r->filename = pstrcat(r->pool, "passthrough:", r->filename,
- NULL);
+ rewritelog(r, 2, "forcing '%s' to get passed through "
+ "to next API URI-to-filename handler", r->filename);
+ r->filename = pstrcat(r->pool, "passthrough:",
+ r->filename, NULL);
changed = 1;
break;
}
+
+ /*
+ * Rule has the "forbidden" flag set which means that
+ * we stop processing and indicate this to the caller.
+ */
if (p->flags & RULEFLAG_FORBIDDEN) {
rewritelog(r, 2, "forcing '%s' to be forbidden", r->filename);
r->filename = pstrcat(r->pool, "forbidden:", r->filename, NULL);
changed = 1;
break;
}
+
+ /*
+ * Rule has the "gone" flag set which means that
+ * we stop processing and indicate this to the caller.
+ */
if (p->flags & RULEFLAG_GONE) {
rewritelog(r, 2, "forcing '%s' to be gone", r->filename);
r->filename = pstrcat(r->pool, "gone:", r->filename, NULL);
changed = 1;
break;
}
+
+ /*
+ * Stop processing also on proxy pass-through and
+ * last-rule and new-round flags.
+ */
if (p->flags & RULEFLAG_PROXY)
break;
if (p->flags & RULEFLAG_LASTRULE)
break;
+
+ /*
+ * On "new-round" flag we just start from the top of
+ * the rewriting ruleset again.
+ */
if (p->flags & RULEFLAG_NEWROUND)
goto loop;

- /* if we are forced to skip N next rules, do it now */
+ /*
+ * If we are forced to skip N next rules, do it now.
+ */
if (p->skip > 0) {
s = p->skip;
while ( i < rewriterules->nelts
@@ -1522,8 +1563,9 @@
}
}
else {
- /* if current rule is chained with next rule(s),
- * skip all this next rule(s)
+ /*
+ * If current rule is chained with next rule(s),
+ * skip all this next rule(s)
*/
while ( i < rewriterules->nelts
&& p->flags & RULEFLAG_CHAIN) {
@@ -1535,6 +1577,9 @@
return changed;
}

+/*
+ * Apply a single(!) rewrite rule
+ */
static int apply_rewrite_rule(request_rec *r, rewriterule_entry *p,
char *perdir)
{
@@ -1543,7 +1588,6 @@
int flags;
char newuri[MAX_STRING_LEN];
char env[MAX_STRING_LEN];
- char port[32];
regex_t *regexp;
regmatch_t regmatch[MAX_NMATCH];
backrefinfo *briRR = NULL;
@@ -1556,20 +1600,32 @@
int i;
int rc;

+ /*
+ * Initialisation
+ */
uri = r->filename;
regexp = p->regexp;
output = p->output;
flags = p->flags;

+ /*
+ * Add (perhaps splitted away) PATH_INFO postfix to URL to
+ * make sure we really match against the complete URL.
+ */
if (perdir != NULL && r->path_info != NULL && r->path_info[0] != '\0') {
rewritelog(r, 3, "[per-dir %s] add path-info postfix: %s -> %s%s",
perdir, uri, uri, r->path_info);
uri = pstrcat(r->pool, uri, r->path_info, NULL);
}

+ /*
+ * On per-directory context (.htaccess) strip the location
+ * prefix from the URL to make sure patterns apply only to
+ * the local part. Additionally indicate this special
+ * threatment in the logfile.
+ */
prefixstrip = 0;
if (perdir != NULL) {
- /* this is a per-directory match */
if ( strlen(uri) >= strlen(perdir)
&& strncmp(uri, perdir, strlen(perdir)) == 0) {
rewritelog(r, 3, "[per-dir %s] strip per-dir prefix: %s -> %s",
@@ -1579,162 +1635,105 @@
}
}

- if (perdir != NULL)
+ /*
+ * Try to match the URI against the RewriteRule pattern
+ * and exit immeddiately if it didn't apply.
+ */
+ if (perdir == NULL)
+ rewritelog(r, 3, "applying pattern '%s' to uri '%s'",
+ p->pattern, uri);
+ else
rewritelog(r, 3, "[per-dir %s] applying pattern '%s' to uri '%s'",
perdir, p->pattern, uri);
-
- /* try to match the pattern */
rc = (regexec(regexp, uri, regexp->re_nsub+1, regmatch, 0) == 0);
- if (( rc && !(p->flags & RULEFLAG_NOTMATCH)) ||
- (!rc && (p->flags & RULEFLAG_NOTMATCH)) ) {
-
- /* create the RewriteRule regsubinfo */
- briRR = (backrefinfo *)palloc(r->pool, sizeof(backrefinfo));
- if (!rc && (p->flags & RULEFLAG_NOTMATCH)) {
- briRR->source = "";
- briRR->nsub = 0;
- }
- else {
- briRR->source = pstrdup(r->pool, uri);
- briRR->nsub = regexp->re_nsub;
- memcpy((void *)(briRR->regmatch), (void *)(regmatch),
- sizeof(regmatch));
- }
+ if (! (( rc && !(p->flags & RULEFLAG_NOTMATCH)) ||
+ (!rc && (p->flags & RULEFLAG_NOTMATCH)) ) )
+ return 0;
+
+ /*
+ * Else create the RewriteRule `regsubinfo' structure which
+ * holds the substitution information.
+ */
+ briRR = (backrefinfo *)palloc(r->pool, sizeof(backrefinfo));
+ if (!rc && (p->flags & RULEFLAG_NOTMATCH)) {
+ /* empty info on negative patterns */
+ briRR->source = "";
+ briRR->nsub = 0;
+ }
+ else {
+ briRR->source = pstrdup(r->pool, uri);
+ briRR->nsub = regexp->re_nsub;
+ memcpy((void *)(briRR->regmatch), (void *)(regmatch),
+ sizeof(regmatch));
+ }

- /* create the RewriteCond backrefinfo, but
- * initialized as empty backrefinfo, i.e. not subst
- */
- briRC = (backrefinfo *)pcalloc(r->pool, sizeof(backrefinfo));
- briRC->source = "";
- briRC->nsub = 0;
-
- /* ok, the pattern matched, but we now additionally have to check
- * for any preconditions which have to be also true. We do this
- * at this very late stage to avoid unnessesary checks which
- * slow down the rewriting engine!!
- */
- rewriteconds = p->rewriteconds;
- conds = (rewritecond_entry *)rewriteconds->elts;
- failed = 0;
- for (i = 0; i < rewriteconds->nelts; i++) {
- c = &conds[i];
- rc = apply_rewrite_cond(r, c, perdir, briRR, briRC);
- if (c->flags & CONDFLAG_ORNEXT) {
- /* there is a "or" flag */
- if (rc == 0) {
- /* one cond is false, but another can be true... */
- continue;
- }
- else {
- /* one true cond is enough, so skip the other conds
- * of the "ornext" chained conds
- */
- while ( i < rewriteconds->nelts
- && c->flags & CONDFLAG_ORNEXT) {
- i++;
- c = &conds[i];
- }
- continue;
- }
+ /*
+ * Initiallally create the RewriteCond backrefinfo with
+ * empty backrefinfo, i.e. not subst parts
+ * (this one is adjusted inside apply_rewrite_cond() later!!)
+ */
+ briRC = (backrefinfo *)pcalloc(r->pool, sizeof(backrefinfo));
+ briRC->source = "";
+ briRC->nsub = 0;
+
+ /*
+ * Ok, we already know the pattern has matched, but we now
+ * additionally have to check for all existing preconditions
+ * (RewriteCond) which have to be also true. We do this at
+ * this very late stage to avoid unnessesary checks which
+ * would slow down the rewriting engine!!
+ */
+ rewriteconds = p->rewriteconds;
+ conds = (rewritecond_entry *)rewriteconds->elts;
+ failed = 0;
+ for (i = 0; i < rewriteconds->nelts; i++) {
+ c = &conds[i];
+ rc = apply_rewrite_cond(r, c, perdir, briRR, briRC);
+ if (c->flags & CONDFLAG_ORNEXT) {
+ /*
+ * The "OR" case
+ */
+ if (rc == 0) {
+ /* One condition is false, but another can be
+ * still true, so we have to continue...
+ */
+ continue;
}
else {
- /* no "or" flag, so a single fail means total fail */
- if (rc == 0) { /* failed */
- failed = 1;
- break;
+ /* One true condition is enough in "or" case, so
+ * skip the other conditions which are "ornext"
+ * chained
+ */
+ while ( i < rewriteconds->nelts
+ && c->flags & CONDFLAG_ORNEXT) {
+ i++;
+ c = &conds[i];
}
+ continue;
}
}
- if (failed)
- return 0; /* if any condition fails this complete rule fails */
-
- /* if this is a pure matching rule we return immediately */
- if (strcmp(output, "-") == 0) {
- /* but before we set the env variables... */
- for (i = 0; p->env[i] != NULL; i++) {
- strncpy(env, p->env[i], sizeof(env)-1);
- EOS_PARANOIA(env);
- expand_backref_inbuffer(r->pool, env, sizeof(env), briRR, '$');
- expand_backref_inbuffer(r->pool, env, sizeof(env), briRC, '%');
- add_env_variable(r, env);
- }
- return 2;
- }
-
- /* if this is a forced proxy request ... */
- if (p->flags & RULEFLAG_PROXY) {
- output = pstrcat(r->pool, "proxy:", output, NULL);
- strncpy(newuri, output, sizeof(newuri)-1);
- EOS_PARANOIA(newuri);
- /* expand $N */
- expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRR,'$');
- /* expand %N */
- expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRC,'%');
- /* expand %{...} */
- expand_variables_inbuffer(r, newuri, sizeof(newuri));
- /* expand ${...} */
- expand_map_lookups(r, newuri, sizeof(newuri));
- if (perdir == NULL)
- rewritelog(r, 2, "rewrite %s -> %s", r->filename, newuri);
- else
- rewritelog(r, 2, "[per-dir %s] rewrite %s -> %s", perdir,
- r->filename, newuri);
- r->filename = pstrdup(r->pool, newuri);
- return 1;
- }
-
- /* if this is an implicit redirect in a per-dir rule */
- i = strlen(output);
- if (perdir != NULL
- && ( (i > 7 && strncmp(output, "http://", 7) == 0)
- || (i > 8 && strncmp(output, "https://", 8) == 0)
- || (i > 9 && strncmp(output, "gopher://", 9) == 0)
- || (i > 6 && strncmp(output, "ftp://", 6) == 0) ) ) {
- strncpy(newuri, output, sizeof(newuri)-1);
- EOS_PARANOIA(newuri);
- /* expand $N */
- expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRR,'$');
- /* expand %N */
- expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRC,'%');
- /* expand %{...} */
- expand_variables_inbuffer(r, newuri, sizeof(newuri));
- /* expand ${...} */
- expand_map_lookups(r, newuri, sizeof(newuri));
- for (i = 0; p->env[i] != NULL; i++) {
- strncpy(env, p->env[i], sizeof(env)-1);
- EOS_PARANOIA(env);
- expand_backref_inbuffer(r->pool, env, sizeof(env), briRR, '$');
- expand_backref_inbuffer(r->pool, env, sizeof(env), briRC, '%');
- add_env_variable(r, env);
- }
- rewritelog(r, 2, "[per-dir %s] redirect %s -> %s", perdir,
- r->filename, newuri);
- r->filename = pstrdup(r->pool, newuri);
- r->status = p->forced_responsecode;
- return 1;
- }
-
- /* add again the previously stripped perdir prefix if the new
- * URI is not a new one (i.e. prefixed by a slash which means
- * that it is not for this per-dir context)
- */
- if (prefixstrip && output[0] != '/') {
- rewritelog(r, 3, "[per-dir %s] add per-dir prefix: %s -> %s%s",
- perdir, output, perdir, output);
- output = pstrcat(r->pool, perdir, output, NULL);
- }
-
- /* standard case: create the substitution string */
- strncpy(newuri, output, sizeof(newuri)-1);
- EOS_PARANOIA(newuri);
- /* expand $N */
- expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRR, '$');
- /* expand %N */
- expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRC, '%');
- /* expand %{...} */
- expand_variables_inbuffer(r, newuri, sizeof(newuri));
- /* expand ${...} */
- expand_map_lookups(r, newuri, sizeof(newuri));
+ else {
+ /*
+ * The "AND" case, i.e. no "or" flag,
+ * so a single failure means total failure.
+ */
+ if (rc == 0) {
+ failed = 1;
+ break;
+ }
+ }
+ }
+ /* if any condition fails the complete rule fails */
+ if (failed)
+ return 0;
+
+ /*
+ * If this is a pure matching rule (`RewriteRule <pat> -')
+ * we stop processing and return immediately. The only thing
+ * we have not to forget are the environment variables
+ * (`RewriteRule <pat> - [E=...]')
+ */
+ if (strcmp(output, "-") == 0) {
for (i = 0; p->env[i] != NULL; i++) {
strncpy(env, p->env[i], sizeof(env)-1);
EOS_PARANOIA(env);
@@ -1742,93 +1741,180 @@
expand_backref_inbuffer(r->pool, env, sizeof(env), briRC, '%');
add_env_variable(r, env);
}
+ return 2;
+ }

- if (perdir == NULL)
- rewritelog(r, 2, "rewrite %s -> %s", uri, newuri);
- else
- rewritelog(r, 2, "[per-dir %s] rewrite %s -> %s", perdir, uri,
- newuri);
-
- r->filename = pstrdup(r->pool, newuri);
+ /*
+ * Ok, now we finally know all patterns have matched and
+ * that there is something to replace, so we create the
+ * substitution URL string in `newuri'.
+ */
+ /* 1. take the output string */
+ strncpy(newuri, output, sizeof(newuri)-1);
+ EOS_PARANOIA(newuri);
+ /* 2. expand $N (i.e. backrefs to RewriteRule pattern) */
+ expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRR, '$');
+ /* 3. expand %N (i.e. backrefs to latest RewriteCond pattern) */
+ expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRC, '%');
+ /* 4. expand %{...} (i.e. variables) */
+ expand_variables_inbuffer(r, newuri, sizeof(newuri));
+ /* 5. expand ${...} (RewriteMap lookups) */
+ expand_map_lookups(r, newuri, sizeof(newuri));
+ /* and log the result... */
+ if (perdir == NULL)
+ rewritelog(r, 2, "rewrite %s -> %s", uri, newuri);
+ else
+ rewritelog(r, 2, "[per-dir %s] rewrite %s -> %s", perdir, uri, newuri);

- /* reduce http[s]://<ourhost>[:<port>] */
- reduce_uri(r);
+ /*
+ * Additionally do expansion for the environment variable
+ * strings (`RewriteCond .. .. [E=<string>]').
+ */
+ for (i = 0; p->env[i] != NULL; i++) {
+ /* 1. take the string */
+ strncpy(env, p->env[i], sizeof(env)-1);
+ EOS_PARANOIA(env);
+ /* 2. expand $N (i.e. backrefs to RewriteRule pattern) */
+ expand_backref_inbuffer(r->pool, env, sizeof(env), briRR, '$');
+ /* 3. expand %N (i.e. backrefs to latest RewriteCond pattern) */
+ expand_backref_inbuffer(r->pool, env, sizeof(env), briRC, '%');
+ /* and add the variable to Apache's structures */
+ add_env_variable(r, env);
+ }

- /* split out on-the-fly generated QUERY_STRING '....?xxxxx&xxxx...' */
- splitout_queryargs(r, p->flags & RULEFLAG_QSAPPEND);
+ /*
+ * Now replace API's knowledge of the current URI:
+ * Replace r->filename with the new URI string and split out
+ * an on-the-fly generated QUERY_STRING part into r->args
+ */
+ r->filename = pstrdup(r->pool, newuri);
+ splitout_queryargs(r, p->flags & RULEFLAG_QSAPPEND);
+
+ /*
+ * Again add the previously stripped per-directory location
+ * prefix if the new URI is not a new one for this
+ * location, i.e. if it's not starting with either a slash
+ * or a fully qualified URL scheme.
+ */
+ i = strlen(r->filename);
+ if ( prefixstrip
+ && !( r->filename[0] == '/'
+ || ( (i > 7 && strncmp(r->filename, "http://", 7) == 0)
+ || (i > 8 && strncmp(r->filename, "https://", 8) == 0)
+ || (i > 9 && strncmp(r->filename, "gopher://", 9) == 0)
+ || (i > 6 && strncmp(r->filename, "ftp://", 6) == 0)))) {
+ rewritelog(r, 3, "[per-dir %s] add per-dir prefix: %s -> %s%s",
+ perdir, r->filename, perdir, r->filename);
+ r->filename = pstrcat(r->pool, perdir, r->filename, NULL);
+ }
+
+ /*
+ * If this rule is forced for proxy throughput
+ * (`RewriteRule ... ... [P]') then emulate mod_proxy's
+ * URL-to-filename handler to be sure mod_proxy is triggered
+ * for this URL later in the Apache API. But make sure it is
+ * a fully-qualified URL. (If not it is qualified with
+ * ourself).
+ */
+ if (p->flags & RULEFLAG_PROXY) {
+ fully_qualify_uri(r);
+ if (perdir == NULL)
+ rewritelog(r, 2, "forcing proxy-throughput with %s", r->filename);
+ else
+ rewritelog(r, 2, "[per-dir %s] forcing proxy-throughput with %s",
+ perdir, r->filename);
+ r->filename = pstrcat(r->pool, "proxy:", r->filename, NULL);
+ return 1;
+ }

- /* remember if a MIME-type should be later forced for this URL */
- if (p->forced_mimetype != NULL) {
- table_set(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR,
- p->forced_mimetype);
- if (perdir == NULL)
- rewritelog(r, 2, "remember %s to have MIME-type '%s'",
- r->filename, p->forced_mimetype);
- else
- rewritelog(r, 2,
- "[per-dir %s] remember %s to have MIME-type '%s'",
- perdir, r->filename, p->forced_mimetype);
- }
+ /*
+ * If this rule is explicitly forced for HTTP redirection
+ * (`RewriteRule .. .. [R]') then force an external HTTP
+ * redirect. But make sure it is a fully-qualified URL. (If
+ * not it is qualified with ourself).
+ */
+ if (p->flags & RULEFLAG_FORCEREDIRECT) {
+ fully_qualify_uri(r);
+ if (perdir == NULL)
+ rewritelog(r, 2,
+ "explicitly forcing redirect with %s", r->filename);
+ else
+ rewritelog(r, 2,
+ "[per-dir %s] explicitly forcing redirect with %s",
+ perdir, r->filename);
+ r->status = p->forced_responsecode;
+ return 1;
+ }

- /* if we are forced to do a explicit redirect by [R] flag
- * and the current URL still is not a fully qualified one we
- * finally prefix it with http[s]://<ourname> explicitly
- */
- if (flags & RULEFLAG_FORCEREDIRECT) {
- r->status = p->forced_responsecode;
- if ( !(strlen(r->filename) > 7 &&
- strncmp(r->filename, "http://", 7) == 0)
- && !(strlen(r->filename) > 8 &&
- strncmp(r->filename, "https://", 8) == 0)
- && !(strlen(r->filename) > 9 &&
- strncmp(r->filename, "gopher://", 9) == 0)
- && !(strlen(r->filename) > 6 &&
- strncmp(r->filename, "ftp://", 6) == 0) ) {
+ /*
+ * Special Rewriting Feature: Self-Reduction
+ * We reduce the URL by stripping a possible
+ * http[s]://<ourhost>[:<port>] prefix, i.e. a prefix which
+ * corresponds to ourself. This is to simplify rewrite maps
+ * and to avoid recursion, etc. When this prefix is not a
+ * coincidence then the user has to use [R] explicitly (see
+ * above).
+ */
+ reduce_uri(r);

-#ifdef APACHE_SSL
- if ((!r->connection->client->ssl
- && r->server->port == DEFAULT_PORT) ||
- ( r->connection->client->ssl && r->server->port == 443) )
-#else
- if (r->server->port == DEFAULT_PORT)
-#endif
- port[0] = '\0';
- else
- ap_snprintf(port, sizeof(port), ":%u", r->server->port);
- if (r->filename[0] == '/')
-#ifdef APACHE_SSL
- ap_snprintf(newuri, sizeof(newuri), "%s://%s%s%s",
- http_method(r), r->server->server_hostname,
- port, r->filename);
-#else
- ap_snprintf(newuri, sizeof(newuri), "http://%s%s%s",
- r->server->server_hostname, port, r->filename);
-#endif
- else
-#ifdef APACHE_SSL
- ap_snprintf(newuri, sizeof(newuri), "%s://%s%s/%s",
- http_method(r), r->server->server_hostname,
- port, r->filename);
-#else
- ap_snprintf(newuri, sizeof(newuri), "http://%s%s/%s",
- r->server->server_hostname, port, r->filename);
-#endif
- if (perdir == NULL)
- rewritelog(r, 2, "prepare forced redirect %s -> %s",
- r->filename, newuri);
- else
- rewritelog(r, 2,
- "[per-dir %s] prepare forced redirect %s -> %s",
- perdir, r->filename, newuri);
+ /*
+ * If this rule is still implicitly forced for HTTP
+ * redirection (`RewriteRule .. <scheme>://...') then
+ * directly force an external HTTP redirect.
+ */
+ i = strlen(r->filename);
+ if ( (i > 7 && strncmp(r->filename, "http://", 7) == 0)
+ || (i > 8 && strncmp(r->filename, "https://", 8) == 0)
+ || (i > 9 && strncmp(r->filename, "gopher://", 9) == 0)
+ || (i > 6 && strncmp(r->filename, "ftp://", 6) == 0)) {
+ if (perdir == NULL)
+ rewritelog(r, 2,
+ "implicitly forcing redirect (rc=%d) with %s",
+ p->forced_responsecode, r->filename);
+ else
+ rewritelog(r, 2, "[per-dir %s] implicitly forcing redirect "
+ "(rc=%d) with %s", perdir, p->forced_responsecode,
+ r->filename);
+ r->status = p->forced_responsecode;
+ return 1;
+ }

- r->filename = pstrdup(r->pool, newuri);
- return 1;
- }
- }
+ /*
+ * Now we are sure it is not a fully qualified URL. But
+ * there is still one special case left: A local rewrite in
+ * per-directory context, i.e. a substitution URL which does
+ * not start with a slash. Here we add again the initially
+ * stripped per-directory prefix.
+ */
+ if (prefixstrip && r->filename[0] != '/') {
+ rewritelog(r, 3, "[per-dir %s] add per-dir prefix: %s -> %s%s",
+ perdir, r->filename, perdir, r->filename);
+ r->filename = pstrcat(r->pool, perdir, r->filename, NULL);
+ }

- return 1;
+ /*
+ * Finally we had to remember if a MIME-type should be
+ * forced for this URL (`RewriteRule .. .. [T=<type>]')
+ * Later in the API processing phase this is forced by our
+ * MIME API-hook function.
+ */
+ if (p->forced_mimetype != NULL) {
+ table_set(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR,
+ p->forced_mimetype);
+ if (perdir == NULL)
+ rewritelog(r, 2, "remember %s to have MIME-type '%s'",
+ r->filename, p->forced_mimetype);
+ else
+ rewritelog(r, 2,
+ "[per-dir %s] remember %s to have MIME-type '%s'",
+ perdir, r->filename, p->forced_mimetype);
}
- return 0;
+
+ /*
+ * Puuhhhhhhhh... WHAT COMPLICATED STUFF ;_)
+ * But now we're done for this particular rule.
+ */
+ return 1;
}

static int apply_rewrite_cond(request_rec *r, rewritecond_entry *p,
@@ -2103,6 +2189,56 @@
}
}
return;
+}
+
+
+/*
+**
+** add 'http[s]://ourhost[:ourport]/' to URI
+** if URI is still not fully qualified
+**
+*/
+
+static void fully_qualify_uri(request_rec *r)
+{
+ int i;
+ char newuri[MAX_STRING_LEN];
+ char port[32];
+
+ i = strlen(r->filename);
+ if (!( (i > 7 && strncmp(r->filename, "http://", 7) == 0)
+ || (i > 8 && strncmp(r->filename, "https://", 8) == 0)
+ || (i > 9 && strncmp(r->filename, "gopher://", 9) == 0)
+ || (i > 6 && strncmp(r->filename, "ftp://", 6) == 0))) {
+#ifdef APACHE_SSL
+ if (is_default_port(r->server->port,r))
+#else
+ if (r->server->port == DEFAULT_PORT)
+#endif
+ port[0] = '\0';
+ else
+ ap_snprintf(port, sizeof(port), ":%u", r->server->port);
+ if (r->filename[0] == '/')
+#ifdef APACHE_SSL
+ ap_snprintf(newuri, sizeof(newuri), "%s://%s%s%s",
+ http_method(r), r->server->server_hostname,
+ port, r->filename);
+#else
+ ap_snprintf(newuri, sizeof(newuri), "http://%s%s%s",
+ r->server->server_hostname, port, r->filename);
+#endif
+ else
+#ifdef APACHE_SSL
+ ap_snprintf(newuri, sizeof(newuri), "%s://%s%s/%s",
+ http_method(r), r->server->server_hostname,
+ port, r->filename);
+#else
+ ap_snprintf(newuri, sizeof(newuri), "http://%s%s/%s",
+ r->server->server_hostname, port, r->filename);
+#endif
+ r->filename = pstrdup(r->pool, newuri);
+ }
+ return;
}





1.35 +2 -1 apachen/src/modules/standard/mod_rewrite.h

Index: mod_rewrite.h
===================================================================
RCS file: /export/home/cvs/apachen/src/modules/standard/mod_rewrite.h,v
retrieving revision 1.34
retrieving revision 1.35
diff -u -r1.34 -r1.35
--- mod_rewrite.h 1997/10/22 20:30:28 1.34
+++ mod_rewrite.h 1997/11/12 10:46:56 1.35
@@ -348,7 +348,7 @@
static const char *cmd_rewritecond_setflag(pool *p, rewritecond_entry *cfg,
char *key, char *val);

-extern const char *cmd_rewriterule(cmd_parms *cmd, rewrite_perdir_conf *dconf,
+static const char *cmd_rewriterule(cmd_parms *cmd, rewrite_perdir_conf *dconf,
char *str);

static const char *cmd_rewriterule_parseflagfield(pool *p,
@@ -378,6 +378,7 @@

/* URI transformation function */
static void splitout_queryargs(request_rec *r, int qsappend);
+static void fully_qualify_uri(request_rec *r);
static void reduce_uri(request_rec *r);
static void expand_backref_inbuffer(pool *p, char *buf, int nbuf,
backrefinfo *bri, char c);