MEDIUM: http-ana: Use http replies for HTTP error messages

When HAProxy returns an http error message, the corresponding http reply is now
used instead of the buffer containing the corresponding HTX message. So,
http_error_message() function now returns the http reply to use for a given
stream. And the http_reply_and_close() function now relies on
http_reply_message() to send the response to the client.
diff --git a/include/proto/http_ana.h b/include/proto/http_ana.h
index f021cb0..c226b12 100644
--- a/include/proto/http_ana.h
+++ b/include/proto/http_ana.h
@@ -48,10 +48,10 @@
 void http_check_request_for_cacheability(struct stream *s, struct channel *req);
 void http_check_response_for_cacheability(struct stream *s, struct channel *res);
 void http_perform_server_redirect(struct stream *s, struct stream_interface *si);
-void http_server_error(struct stream *s, struct stream_interface *si, int err, int finst, const struct buffer *msg);
-void http_reply_and_close(struct stream *s, short status, const struct buffer *msg);
+void http_server_error(struct stream *s, struct stream_interface *si, int err, int finst, struct http_reply *msg);
+void http_reply_and_close(struct stream *s, short status, struct http_reply *msg);
 void http_return_srv_error(struct stream *s, struct stream_interface *si);
-struct buffer *http_error_message(struct stream *s);
+struct http_reply *http_error_message(struct stream *s);
 int http_reply_message(struct stream *s, struct http_reply *reply);
 int http_forward_proxy_resp(struct stream *s, int final);
 
diff --git a/src/http_ana.c b/src/http_ana.c
index e6a43ea..35ea8e9 100644
--- a/src/http_ana.c
+++ b/src/http_ana.c
@@ -672,12 +672,6 @@
 		_HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1);
 	if (sess->listener->counters)
 		_HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
-
-	if (txn->http_reply) {
-		if (http_reply_message(s, txn->http_reply) == -1)
-			goto return_int_err;
-		goto return_prx_cond;
-	}
 	goto return_prx_err;
 
  return_int_err:
@@ -1008,26 +1002,7 @@
 	 */
 	s->logs.t_queue = tv_ms_elapsed(&s->logs.tv_accept, &now);
 
-	if (req->flags & CF_READ_ERROR) {
-		http_reply_and_close(s, txn->status, NULL);
-		goto end;
-	}
-
-	if (txn->http_reply) {
-		if (!http_reply_message(s, txn->http_reply))
-			goto end;
-
-		txn->status = 500;
-		if (!(s->flags & SF_ERR_MASK))
-			s->flags |= SF_ERR_INTERNAL;
-		_HA_ATOMIC_ADD(&s->sess->fe->fe_counters.internal_errors, 1);
-		if (s->flags & SF_BE_ASSIGNED)
-			_HA_ATOMIC_ADD(&s->be->be_counters.internal_errors, 1);
-		if (s->sess->listener->counters)
-			_HA_ATOMIC_ADD(&s->sess->listener->counters->internal_errors, 1);
-	}
-
-	http_reply_and_close(s, txn->status, http_error_message(s));
+	http_reply_and_close(s, txn->status, (!(req->flags & CF_READ_ERROR) ? http_error_message(s) : NULL));
 
   end:
 	req->analysers &= AN_REQ_FLT_END;
@@ -2185,12 +2160,6 @@
 		_HA_ATOMIC_ADD(&sess->listener->counters->denied_resp, 1);
 	if (objt_server(s->target))
 		_HA_ATOMIC_ADD(&__objt_server(s->target)->counters.denied_resp, 1);
-
-	if (txn->http_reply) {
-		if (http_reply_message(s, txn->http_reply) == -1)
-			goto return_int_err;
-		goto return_prx_cond;
-	}
 	goto return_prx_err;
 
  return_int_err:
@@ -4631,7 +4600,7 @@
 }
 
 void http_server_error(struct stream *s, struct stream_interface *si, int err,
-		       int finst, const struct buffer *msg)
+		       int finst, struct http_reply *msg)
 {
 	http_reply_and_close(s, s->txn->status, msg);
 	if (!(s->flags & SF_ERR_MASK))
@@ -4640,56 +4609,49 @@
 		s->flags |= finst;
 }
 
-void http_reply_and_close(struct stream *s, short status, const struct buffer *msg)
+void http_reply_and_close(struct stream *s, short status, struct http_reply *msg)
 {
+	if (!msg) {
+		channel_htx_truncate(&s->res, htxbuf(&s->res.buf));
+		goto end;
+	}
+
+	if (http_reply_message(s, msg) == -1) {
+		/* On error, return a 500 error message, but don't rewrite it if
+		 * it is already an internal error.
+		 */
+		if (s->txn->status == 500)
+			s->txn->flags |= TX_CONST_REPLY;
+		s->txn->status = 500;
+		s->txn->http_reply = NULL;
+		return http_reply_and_close(s, s->txn->status, http_error_message(s));
+	}
+
+end:
+	s->res.wex = tick_add_ifset(now_ms, s->res.wto);
+	s->txn->flags &= ~TX_WAIT_NEXT_RQ;
+
 	channel_auto_read(&s->req);
 	channel_abort(&s->req);
 	channel_auto_close(&s->req);
 	channel_htx_erase(&s->req, htxbuf(&s->req.buf));
-	channel_htx_truncate(&s->res, htxbuf(&s->res.buf));
 	channel_auto_read(&s->res);
 	channel_auto_close(&s->res);
 	channel_shutr_now(&s->res);
-
-	s->res.wex = tick_add_ifset(now_ms, s->res.wto);
-	s->txn->flags &= ~TX_WAIT_NEXT_RQ;
-
-	/* <msg> is an HTX structure. So we copy it in the response's
-	 * channel */
-	if (msg && !b_is_null(msg)) {
-		struct channel *chn = &s->res;
-		struct htx *htx;
-
-		FLT_STRM_CB(s, flt_http_reply(s, s->txn->status, msg));
-		htx = htx_from_buf(&chn->buf);
-		if (channel_htx_copy_msg(chn, htx, msg)) {
-			if (!http_forward_proxy_resp(s,  1)) {
-				/* On error, return a 500 error message, but
-				 * don't rewrite it if it is already an internal
-				 * error.
-				 */
-				if (s->txn->status == 500)
-					s->txn->flags |= TX_CONST_REPLY;
-				s->txn->status = 500;
-				s->txn->errmsg = NULL;
-				return http_reply_and_close(s, s->txn->status, http_error_message(s));
-                       }
-		}
-	}
 }
 
-struct buffer *http_error_message(struct stream *s)
+struct http_reply *http_error_message(struct stream *s)
 {
 	const int msgnum = http_get_status_idx(s->txn->status);
 
-	if (s->txn->errmsg)
-		return s->txn->errmsg;
-	else if (s->be->errmsg[msgnum])
-		return s->be->errmsg[msgnum];
-	else if (strm_fe(s)->errmsg[msgnum])
-		return strm_fe(s)->errmsg[msgnum];
+	if (s->txn->http_reply)
+		return s->txn->http_reply;
+	else if (s->be->replies[msgnum])
+		return s->be->replies[msgnum];
+	else if (strm_fe(s)->replies[msgnum])
+		return strm_fe(s)->replies[msgnum];
 	else
-		return &http_err_chunks[msgnum];
+		return &http_err_replies[msgnum];
 }
 
 /* Produces a response from an http reply. Depending on the http reply type, a,
@@ -4707,7 +4669,8 @@
 	unsigned int slflags;
 	int ret = 0;
 
-	s->txn->status = reply->status;
+	if (s->txn->status == -1)
+		s->txn->status = reply->status;
 	channel_htx_truncate(res, htx);
 
 	/*
@@ -4722,16 +4685,24 @@
 		if (reply->body.reply)
 			reply = reply->body.reply;
 	}
+	if (reply->type == HTTP_REPLY_ERRMSG && !reply->body.errmsg)  {
+		/* get default error message */
+		if (reply == s->txn->http_reply)
+			s->txn->http_reply = NULL;
+		reply = http_error_message(s);
+		if (reply->type == HTTP_REPLY_INDIRECT) {
+			if (reply->body.reply)
+				reply = reply->body.reply;
+		}
+	}
 
 	if (reply->type == HTTP_REPLY_ERRMSG) {
 		/* implicit or explicit error message*/
 		errmsg = reply->body.errmsg;
-		if (!errmsg) {
-			/* get default error message */
-			errmsg = http_error_message(s);
+		if (errmsg && !b_is_null(errmsg)) {
+			if (!channel_htx_copy_msg(res, htx, errmsg))
+				goto fail;
 		}
-		if (!b_is_null(errmsg) && !channel_htx_copy_msg(res, htx, errmsg))
-			goto fail;
 	}
 	else {
 		/* no payload, file or log-format string */