MEDIUM: http: add "redirect scheme" to ease HTTP to HTTPS redirection

For instance :

   redirect scheme https if !{ is_ssl }
This commit is contained in:
Willy Tarreau 2012-09-12 08:43:15 +02:00
parent 16216828fc
commit 2e1dca8f52
4 changed files with 104 additions and 11 deletions

View File

@ -4393,8 +4393,9 @@ rate-limit sessions <rate>
See also : the "backlog" keyword and the "fe_sess_rate" ACL criterion.
redirect location <to> [code <code>] <option> [{if | unless} <condition>]
redirect prefix <to> [code <code>] <option> [{if | unless} <condition>]
redirect location <loc> [code <code>] <option> [{if | unless} <condition>]
redirect prefix <pfx> [code <code>] <option> [{if | unless} <condition>]
redirect scheme <sch> [code <code>] <option> [{if | unless} <condition>]
Return an HTTP redirection if/unless a condition is matched
May be used in sections : defaults | frontend | listen | backend
no | yes | yes | yes
@ -4403,14 +4404,25 @@ redirect prefix <to> [code <code>] <option> [{if | unless} <condition>]
response. If no condition is specified, the redirect applies unconditionally.
Arguments :
<to> With "redirect location", the exact value in <to> is placed into
the HTTP "Location" header. In case of "redirect prefix", the
"Location" header is built from the concatenation of <to> and the
complete URI, including the query string, unless the "drop-query"
option is specified (see below). As a special case, if <to>
equals exactly "/" in prefix mode, then nothing is inserted
before the original URI. It allows one to redirect to the same
URL.
<loc> With "redirect location", the exact value in <loc> is placed into
the HTTP "Location" header.
<pfx> With "redirect prefix", the "Location" header is built from the
concatenation of <pfx> and the complete URI path, including the
query string, unless the "drop-query" option is specified (see
below). As a special case, if <pfx> equals exactly "/", then
nothing is inserted before the original URI. It allows one to
redirect to the same URL (for instance, to insert a cookie).
<sch> With "redirect scheme", then the "Location" header is built by
concatenating <sch> with "://" then the first occurrence of the
"Host" header, and then the URI path, including the query string
unless the "drop-query" option is specified (see below). If no
path is found or if the path is "*", then "/" is used instead. If
no "Host" header is found, then an empty host component will be
returned, which most recent browsers interprete as redirecting to
the same host. This directive is mostly used to redirect HTTP to
HTTPS.
<code> The code is optional. It indicates which type of HTTP redirection
is desired. Only codes 301, 302 and 303 are supported, and 302 is
@ -4469,6 +4481,9 @@ redirect prefix <to> [code <code>] <option> [{if | unless} <condition>]
acl missing_slash path_reg ^/article/[^/]*$
redirect code 301 prefix / drop-query append-slash if missing_slash
Example: redirect all HTTP traffic to HTTPS when SSL is handled by haproxy.
redirect scheme https if !{ is_ssl }
See section 7 about ACL usage.

View File

@ -204,6 +204,7 @@ enum {
REDIRECT_TYPE_NONE = 0, /* no redirection */
REDIRECT_TYPE_LOCATION, /* location redirect */
REDIRECT_TYPE_PREFIX, /* prefix redirect */
REDIRECT_TYPE_SCHEME, /* scheme redirect (eg: switch from http to https) */
};
/* Perist types (force-persist, ignore-persist) */

View File

@ -2837,6 +2837,18 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
cur_arg++;
destination = args[cur_arg];
}
else if (!strcmp(args[cur_arg], "scheme")) {
if (!*args[cur_arg + 1]) {
Alert("parsing [%s:%d] : '%s': missing argument for '%s'.\n",
file, linenum, args[0], args[cur_arg]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
type = REDIRECT_TYPE_SCHEME;
cur_arg++;
destination = args[cur_arg];
}
else if (!strcmp(args[cur_arg], "set-cookie")) {
if (!*args[cur_arg + 1]) {
Alert("parsing [%s:%d] : '%s': missing argument for '%s'.\n",
@ -2895,7 +2907,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
break;
}
else {
Alert("parsing [%s:%d] : '%s' expects 'code', 'prefix', 'location', 'set-cookie', 'clear-cookie', 'drop-query' or 'append-slash' (was '%s').\n",
Alert("parsing [%s:%d] : '%s' expects 'code', 'prefix', 'location', 'scheme', 'set-cookie', 'clear-cookie', 'drop-query' or 'append-slash' (was '%s').\n",
file, linenum, args[0], args[cur_arg]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;

View File

@ -3072,6 +3072,71 @@ int http_process_req_common(struct session *s, struct channel *req, int an_bit,
goto return_bad_req;
switch(rule->type) {
case REDIRECT_TYPE_SCHEME: {
const char *path;
const char *host;
struct hdr_ctx ctx;
int pathlen;
int hostlen;
host = "";
hostlen = 0;
ctx.idx = 0;
if (http_find_header2("Host", 4, txn->req.buf->buf.p + txn->req.sol, &txn->hdr_idx, &ctx)) {
host = ctx.line + ctx.val;
hostlen = ctx.vlen;
}
path = http_get_path(txn);
/* build message using path */
if (path) {
pathlen = txn->req.sl.rq.u_l + (req->buf.p + txn->req.sl.rq.u) - path;
if (rule->flags & REDIRECT_FLAG_DROP_QS) {
int qs = 0;
while (qs < pathlen) {
if (path[qs] == '?') {
pathlen = qs;
break;
}
qs++;
}
}
} else {
path = "/";
pathlen = 1;
}
/* check if we can add scheme + "://" + host + path */
if (rdr.len + rule->rdr_len + 3 + hostlen + pathlen > rdr.size - 4)
goto return_bad_req;
/* add scheme */
memcpy(rdr.str + rdr.len, rule->rdr_str, rule->rdr_len);
rdr.len += rule->rdr_len;
/* add "://" */
memcpy(rdr.str + rdr.len, "://", 3);
rdr.len += 3;
/* add host */
memcpy(rdr.str + rdr.len, host, hostlen);
rdr.len += hostlen;
/* add path */
memcpy(rdr.str + rdr.len, path, pathlen);
rdr.len += pathlen;
/* append a slash at the end of the location is needed and missing */
if (rdr.len && rdr.str[rdr.len - 1] != '/' &&
(rule->flags & REDIRECT_FLAG_APPEND_SLASH)) {
if (rdr.len > rdr.size - 5)
goto return_bad_req;
rdr.str[rdr.len] = '/';
rdr.len++;
}
break;
}
case REDIRECT_TYPE_PREFIX: {
const char *path;
int pathlen;