On Wed, Sep 12, 2012 at 08:25:01AM +0200, Willy Tarreau wrote:
> I think it's time to add "redirect scheme" which would recompose the Location
> header from this scheme, the Host header and the URI. I'm going to look into
> this.
OK, finally here it is. Tested and works OK. Use it this way :
redirect scheme https if !{ is_ssl }
BTW, without this patch and with most recent browsers, you can also use
the trick which consists in not returning any host part in the Location :
redirect prefix https:// if !{ is_ssl }
This results in :
Location: https:///path
which most browsers interprete as reconnecting over https to the same Host
header. This obviously works only if there is no port in the Host header.
Regards,
Willy
>From 2e1dca8f5238155cbc52d37316fe858c4f61cf34 Mon Sep 17 00:00:00 2001
From: Willy Tarreau <[email protected]>
Date: Wed, 12 Sep 2012 08:43:15 +0200
Subject: MEDIUM: http: add "redirect scheme" to ease HTTP to HTTPS redirection
For instance :
redirect scheme https if !{ is_ssl }
---
doc/configuration.txt | 35 +++++++++++++++++-------
include/types/proto_http.h | 1 +
src/cfgparse.c | 14 +++++++++-
src/proto_http.c | 65 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 104 insertions(+), 11 deletions(-)
diff --git a/doc/configuration.txt b/doc/configuration.txt
index b7aa690..7be3335 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -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.
diff --git a/include/types/proto_http.h b/include/types/proto_http.h
index 665ec75..3e807fd 100644
--- a/include/types/proto_http.h
+++ b/include/types/proto_http.h
@@ -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) */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 282c128..c91e996 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -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;
diff --git a/src/proto_http.c b/src/proto_http.c
index d7208d2..a2030f6 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -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;
--
1.7.2.1.45.g54fbc