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: