From eb9632f7c6ae675bdee4c82eb0d298ba7f37fc52 Mon Sep 17 00:00:00 2001 From: Willy Tarreau <w@1wt.eu> Date: Wed, 12 Sep 2012 08:43:15 +0200 Subject: [PATCH 10/10] MEDIUM: http: add "redirect scheme" to ease HTTP to HTTPS redirection For instance : redirect scheme https if !{ is_ssl } Backport-suggested-by: Russell Geldmacher <russell.geldmacher@gmail.com> (cherry picked from commit 2e1dca8f5238155cbc52d37316fe858c4f61cf34) --- 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 56438dd..f2043a1 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -4039,8 +4039,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 @@ -4049,14 +4050,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, 303, 307 and 308 are supported, @@ -4117,6 +4129,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 09d4dd8..0e2b14f 100644 --- a/include/types/proto_http.h +++ b/include/types/proto_http.h @@ -224,6 +224,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 cecec03..09ffcd3 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -2182,6 +2182,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", @@ -2240,7 +2252,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 7fd1fe6..ed35795 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -3390,6 +3390,71 @@ int http_process_req_common(struct session *s, struct buffer *req, int an_bit, s 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, msg->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 + (txn->req.sol + 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.8.1.5