MINOR: http-ana: Use proxy's error replies to emit 401/407 responses
There is no reason to not use proxy's error replies to emit 401/407
responses. The function http_reply_40x_unauthorized(), responsible to emit those
responses, is not really complex. It only adds a
WWW-Authenticate/Proxy-Authenticate header to a generic message.
So now, error replies can be defined for 401 and 407 status codes, using
errorfile or http-error directives. When an http-request auth rule is evaluated,
the corresponding error reply is used. For 401 responses, all occurrences of the
WWW-Authenticate header are removed and replaced by a new one with a basic
authentication challenge for the configured realm. For 407 responses, the same
is done on the Proxy-Authenticate header. If the error reply must not be
altered, "http-request return" rule must be used instead.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 674acd2..8a67f4d 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -2522,8 +2522,8 @@
Arguments :
<code> is the HTTP status code. Currently, HAProxy is capable of
- generating codes 200, 400, 403, 404, 405, 408, 410, 425, 429,
- 500, 502, 503, and 504.
+ generating codes 200, 400, 401, 403, 404, 405, 407, 408, 410,
+ 425, 429, 500, 502, 503, and 504.
<file> designates a file containing the full HTTP response. It is
recommended to follow the common practice of appending ".http" to
@@ -3859,8 +3859,8 @@
yes | yes | yes | yes
Arguments :
<code> is the HTTP status code. Currently, HAProxy is capable of
- generating codes 200, 400, 403, 404, 405, 408, 410, 425, 429, 500,
- 502, 503, and 504.
+ generating codes 200, 400, 401, 403, 404, 405, 407, 408, 410,
+ 425, 429, 500, 502, 503, and 504.
<file> designates a file containing the full HTTP response. It is
recommended to follow the common practice of appending ".http" to
@@ -3908,8 +3908,8 @@
<name> is the name of an existing http-errors section.
<code> is a HTTP status code. Several status code may be listed.
- Currently, HAProxy is capable of generating codes 200, 400, 403,
- 404, 405, 408, 410, 425, 429, 500, 502, 503, and 504.
+ Currently, HAProxy is capable of generating codes 200, 400, 401,
+ 403, 404, 405, 407, 408, 410, 425, 429, 500, 502, 503, and 504.
Errors defined in the http-errors section with the name <name> are imported
in the current proxy. If no status code is specified, all error files of the
@@ -3934,8 +3934,8 @@
yes | yes | yes | yes
Arguments :
<code> is the HTTP status code. Currently, HAProxy is capable of
- generating codes 200, 400, 403, 404, 405, 408, 410, 425, 429, 500,
- 502, 503, and 504.
+ generating codes 200, 400, 401, 403, 404, 405, 407, 408, 410,
+ 425, 429, 500, 502, 503, and 504.
<url> it is the exact contents of the "Location" header. It may contain
either a relative URI to an error page hosted on the same site,
@@ -3966,8 +3966,8 @@
yes | yes | yes | yes
Arguments :
<code> is the HTTP status code. Currently, HAProxy is capable of
- generating codes 200, 400, 403, 404, 405, 408, 410, 425, 429, 500,
- 502, 503, and 504.
+ generating codes 200, 400, 401, 403, 404, 405, 407, 408, 410,
+ 425, 429, 500, 502, 503, and 504.
<url> it is the exact contents of the "Location" header. It may contain
either a relative URI to an error page hosted on the same site,
@@ -4942,8 +4942,8 @@
Arguments :
staus <code> is the HTTP status code. It must be specified.
Currently, HAProxy is capable of generating codes
- 200, 400, 403, 404, 405, 408, 410, 425, 429, 500,
- 502, 503, and 504.
+ 200, 400, 401, 403, 404, 405, 407, 408, 410, 425, 429,
+ 500, 502, 503, and 504.
content-type <type> is the response content type, for instance
"text/plain". This parameter is ignored and should be
@@ -5095,6 +5095,14 @@
"realm" parameter is supported, it sets the authentication realm that is
returned with the response (typically the application's name).
+ The corresponding proxy's error message is used. It may be customized using
+ an "errorfile" or an "http-error" directive. For 401 responses, all
+ occurrences of the WWW-Authenticate header are removed and replaced by a new
+ one with a basic authentication challenge for realm "<realm>". For 407
+ responses, the same is done on the Proxy-Authenticate header. If the error
+ message must not be altered, consider to use "http-request return" rule
+ instead.
+
Example:
acl auth_ok http_auth_group(L1) G1
http-request auth unless auth_ok
diff --git a/include/common/http.h b/include/common/http.h
index 6f083d4..d31f5e9 100644
--- a/include/common/http.h
+++ b/include/common/http.h
@@ -82,9 +82,11 @@
enum {
HTTP_ERR_200 = 0,
HTTP_ERR_400,
+ HTTP_ERR_401,
HTTP_ERR_403,
HTTP_ERR_404,
HTTP_ERR_405,
+ HTTP_ERR_407,
HTTP_ERR_408,
HTTP_ERR_410,
HTTP_ERR_421,
diff --git a/src/http.c b/src/http.c
index ec6b4a2..a1e5197 100644
--- a/src/http.c
+++ b/src/http.c
@@ -215,9 +215,11 @@
const int http_err_codes[HTTP_ERR_SIZE] = {
[HTTP_ERR_200] = 200, /* used by "monitor-uri" */
[HTTP_ERR_400] = 400,
+ [HTTP_ERR_401] = 401,
[HTTP_ERR_403] = 403,
[HTTP_ERR_404] = 404,
[HTTP_ERR_405] = 405,
+ [HTTP_ERR_407] = 407,
[HTTP_ERR_408] = 408,
[HTTP_ERR_410] = 410,
[HTTP_ERR_421] = 421,
@@ -248,6 +250,15 @@
"\r\n"
"<html><body><h1>400 Bad request</h1>\nYour browser sent an invalid request.\n</body></html>\n",
+ [HTTP_ERR_401] =
+ "HTTP/1.1 401 Unauthorized\r\n"
+ "Content-length: 112\r\n"
+ "Cache-Control: no-cache\r\n"
+ "Connection: close\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n"
+ "<html><body><h1>401 Unauthorized</h1>\nYou need a valid user and password to access this content.\n</body></html>\n",
+
[HTTP_ERR_403] =
"HTTP/1.1 403 Forbidden\r\n"
"Content-length: 93\r\n"
@@ -275,6 +286,15 @@
"\r\n"
"<html><body><h1>405 Method Not Allowed</h1>\nA request was made of a resource using a request method not supported by that resource\n</body></html>\n",
+ [HTTP_ERR_407] =
+ "HTTP/1.1 407 Unauthorized\r\n"
+ "Content-length: 112\r\n"
+ "Cache-Control: no-cache\r\n"
+ "Connection: close\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n"
+ "<html><body><h1>407 Unauthorized</h1>\nYou need a valid user and password to access this content.\n</body></html>\n",
+
[HTTP_ERR_408] =
"HTTP/1.1 408 Request Time-out\r\n"
"Content-length: 110\r\n"
@@ -396,9 +416,11 @@
switch (status) {
case 200: return HTTP_ERR_200;
case 400: return HTTP_ERR_400;
+ case 401: return HTTP_ERR_401;
case 403: return HTTP_ERR_403;
case 404: return HTTP_ERR_404;
case 405: return HTTP_ERR_405;
+ case 407: return HTTP_ERR_407;
case 408: return HTTP_ERR_408;
case 410: return HTTP_ERR_410;
case 421: return HTTP_ERR_421;
diff --git a/src/http_ana.c b/src/http_ana.c
index f7da268..e239f7c 100644
--- a/src/http_ana.c
+++ b/src/http_ana.c
@@ -4910,61 +4910,54 @@
{
struct channel *res = &s->res;
struct htx *htx = htx_from_buf(&res->buf);
- struct htx_sl *sl;
- struct ist code, body;
- int status;
- unsigned int flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11);
+ struct http_reply *reply;
+ struct http_hdr_ctx ctx;
+ struct ist hdr;
if (!(s->txn->flags & TX_USE_PX_CONN)) {
- status = 401;
- code = ist("401");
- body = ist("<html><body><h1>401 Unauthorized</h1>\n"
- "You need a valid user and password to access this content.\n"
- "</body></html>\n");
+ s->txn->status = 401;
+ hdr = ist("WWW-Authenticate");
}
else {
- status = 407;
- code = ist("407");
- body = ist("<html><body><h1>407 Unauthorized</h1>\n"
- "You need a valid user and password to access this content.\n"
- "</body></html>\n");
+ s->txn->status = 407;
+ hdr = ist("Proxy-Authenticate");
}
-
- sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags,
- ist("HTTP/1.1"), code, ist("Unauthorized"));
- if (!sl)
- goto fail;
- sl->info.res.status = status;
- s->txn->status = status;
+ reply = http_error_message(s);
+ channel_htx_truncate(res, htx);
if (chunk_printf(&trash, "Basic realm=\"%s\"", auth_realm) == -1)
goto fail;
- if (!htx_add_header(htx, ist("Content-length"), ist("112")) ||
- !htx_add_header(htx, ist("Cache-Control"), ist("no-cache")) ||
- !htx_add_header(htx, ist("Connection"), ist("close")) ||
- !htx_add_header(htx, ist("Content-Type"), ist("text/html")))
- goto fail;
- if (status == 401 && !htx_add_header(htx, ist("WWW-Authenticate"), ist2(trash.area, trash.data)))
- goto fail;
- if (status == 407 && !htx_add_header(htx, ist("Proxy-Authenticate"), ist2(trash.area, trash.data)))
- goto fail;
- if (!htx_add_endof(htx, HTX_BLK_EOH))
+ /* Write the generic 40x message */
+ if (http_reply_to_htx(s, htx, reply) == -1)
goto fail;
- while (body.len) {
- size_t sent = htx_add_data(htx, body);
- if (!sent)
- goto fail;
- body.ptr += sent;
- body.len -= sent;
- }
+ /* Remove all existing occurrences of the XXX-Authenticate header */
+ ctx.blk = NULL;
+ while (http_find_header(htx, hdr, &ctx, 1))
+ http_remove_header(htx, &ctx);
- if (!htx_add_endof(htx, HTX_BLK_EOM))
+ /* Now a the right XXX-Authenticate header */
+ if (!http_add_header(htx, hdr, ist2(b_orig(&trash), b_data(&trash))))
goto fail;
+ /* Finally forward the reply */
+ htx_to_buf(htx, &res->buf);
if (!http_forward_proxy_resp(s, 1))
goto fail;
+
+ /* Note: Only eval on the request */
+ s->logs.tv_request = now;
+ s->req.analysers &= AN_REQ_FLT_END;
+
+ if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
+ _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
+
+ if (!(s->flags & SF_ERR_MASK))
+ s->flags |= SF_ERR_LOCAL;
+ if (!(s->flags & SF_FINST_MASK))
+ s->flags |= SF_FINST_R;
+
return 0;
fail: