MINOR: http-ana: Use a dedicated function to send a response from an http reply

The http_reply_message() function may be used to send an http reply to a
client. This function is responsile to convert the reply in HTX, to push it in
the response buffer and to forward it to the client. It is also responsible to
terminate the transaction.

This function is used during evaluation of http return rules.
This commit is contained in:
Christopher Faulet 2020-05-13 16:38:37 +02:00
parent 7eea241c39
commit 0e2ad61315
3 changed files with 116 additions and 103 deletions

View File

@ -52,6 +52,7 @@ void http_server_error(struct stream *s, struct stream_interface *si, int err, i
void http_reply_and_close(struct stream *s, short status, const struct buffer *msg);
void http_return_srv_error(struct stream *s, struct stream_interface *si);
struct buffer *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);
struct http_txn *http_alloc_txn(struct stream *s);

View File

@ -1818,92 +1818,10 @@ static enum act_return http_action_return(struct act_rule *rule, struct proxy *p
struct session *sess, struct stream *s, int flags)
{
struct channel *req = &s->req;
struct channel *res = &s->res;
struct buffer *errmsg;
struct htx *htx = htx_from_buf(&res->buf);
struct htx_sl *sl;
struct buffer *body = NULL;
const char *status, *reason, *clen, *ctype;
unsigned int slflags;
enum act_return ret = ACT_RET_ABRT;
s->txn->status = rule->arg.http_reply->status;
channel_htx_truncate(res, htx);
if (http_reply_message(s, rule->arg.http_reply) == -1)
return ACT_RET_ERR;
/* HTTP_REPLY_ERRFILES unexpected here. handled as no payload if so */
if (rule->arg.http_reply->type == HTTP_REPLY_ERRMSG) {
/* implicit or explicit error message*/
errmsg = rule->arg.http_reply->body.errmsg;
if (!errmsg) {
/* get default error message */
errmsg = http_error_message(s);
}
if (b_is_null(errmsg))
goto end;
if (!channel_htx_copy_msg(res, htx, errmsg))
goto fail;
}
else {
/* no payload, file or log-format string */
if (rule->arg.http_reply->type == HTTP_REPLY_RAW) {
/* file */
body = &rule->arg.http_reply->body.obj;
}
else if (rule->arg.http_reply->type == HTTP_REPLY_LOGFMT) {
/* log-format string */
body = alloc_trash_chunk();
if (!body)
goto fail_alloc;
body->data = build_logline(s, body->area, body->size, &rule->arg.http_reply->body.fmt);
}
/* else no payload */
status = ultoa(rule->arg.http_reply->status);
reason = http_get_reason(rule->arg.http_reply->status);
slflags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
if (!body || !b_data(body))
slflags |= HTX_SL_F_BODYLESS;
sl = htx_add_stline(htx, HTX_BLK_RES_SL, slflags, ist("HTTP/1.1"), ist(status), ist(reason));
if (!sl)
goto fail;
sl->info.res.status = rule->arg.http_reply->status;
clen = (body ? ultoa(b_data(body)) : "0");
ctype = rule->arg.http_reply->ctype;
if (!LIST_ISEMPTY(&rule->arg.http_reply->hdrs)) {
struct http_reply_hdr *hdr;
struct buffer *value = alloc_trash_chunk();
if (!value)
goto fail;
list_for_each_entry(hdr, &rule->arg.http_reply->hdrs, list) {
chunk_reset(value);
value->data = build_logline(s, value->area, value->size, &hdr->value);
if (b_data(value) && !htx_add_header(htx, hdr->name, ist2(b_head(value), b_data(value)))) {
free_trash_chunk(value);
goto fail;
}
chunk_reset(value);
}
free_trash_chunk(value);
}
if (!htx_add_header(htx, ist("content-length"), ist(clen)) ||
(body && b_data(body) && ctype && !htx_add_header(htx, ist("content-type"), ist(ctype))) ||
!htx_add_endof(htx, HTX_BLK_EOH) ||
(body && b_data(body) && !htx_add_data_atonce(htx, ist2(b_head(body), b_data(body)))) ||
!htx_add_endof(htx, HTX_BLK_EOM))
goto fail;
}
htx_to_buf(htx, &s->res.buf);
if (!http_forward_proxy_resp(s, 1))
goto fail;
end:
if (rule->from == ACT_F_HTTP_REQ) {
/* let's log the request time */
s->logs.tv_request = now;
@ -1918,25 +1836,7 @@ static enum act_return http_action_return(struct act_rule *rule, struct proxy *p
if (!(s->flags & SF_FINST_MASK))
s->flags |= ((rule->from == ACT_F_HTTP_REQ) ? SF_FINST_R : SF_FINST_H);
leave:
if (rule->arg.http_reply->type == HTTP_REPLY_LOGFMT)
free_trash_chunk(body);
return ret;
fail_alloc:
if (!(s->flags & SF_ERR_MASK))
s->flags |= SF_ERR_RESOURCE;
ret = ACT_RET_ERR;
goto leave;
fail:
/* If an error occurred, remove the incomplete HTTP response from the
* buffer */
channel_htx_truncate(res, htx);
ret = ACT_RET_ERR;
if (!(s->flags & SF_ERR_MASK))
s->flags |= SF_ERR_PRXCOND;
goto leave;
return ACT_RET_ABRT;
}
/* Check an "http-request return" action. The function returns 1 in success

View File

@ -4661,6 +4661,118 @@ struct buffer *http_error_message(struct stream *s)
return &http_err_chunks[msgnum];
}
/* Produces a response from an http reply. Depending on the http reply type, a,
* errorfile, an raw file or a log-format string is used. On success, it returns
* 0. If an error occurs -1 is returned.
*/
int http_reply_message(struct stream *s, struct http_reply *reply)
{
struct channel *res = &s->res;
struct buffer *errmsg;
struct htx *htx = htx_from_buf(&res->buf);
struct htx_sl *sl;
struct buffer *body = NULL;
const char *status, *reason, *clen, *ctype;
unsigned int slflags;
int ret = 0;
s->txn->status = reply->status;
channel_htx_truncate(res, htx);
/* HTTP_REPLY_ERRFILES unexpected here. handled as no payload if so */
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 (b_is_null(errmsg))
goto leave;
if (!channel_htx_copy_msg(res, htx, errmsg))
goto fail;
}
else {
/* no payload, file or log-format string */
if (reply->type == HTTP_REPLY_RAW) {
/* file */
body = &reply->body.obj;
}
else if (reply->type == HTTP_REPLY_LOGFMT) {
/* log-format string */
body = alloc_trash_chunk();
if (!body)
goto fail_alloc;
body->data = build_logline(s, body->area, body->size, &reply->body.fmt);
}
/* else no payload */
status = ultoa(reply->status);
reason = http_get_reason(reply->status);
slflags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
if (!body || !b_data(body))
slflags |= HTX_SL_F_BODYLESS;
sl = htx_add_stline(htx, HTX_BLK_RES_SL, slflags, ist("HTTP/1.1"), ist(status), ist(reason));
if (!sl)
goto fail;
sl->info.res.status = reply->status;
clen = (body ? ultoa(b_data(body)) : "0");
ctype = reply->ctype;
if (!LIST_ISEMPTY(&reply->hdrs)) {
struct http_reply_hdr *hdr;
struct buffer *value = alloc_trash_chunk();
if (!value)
goto fail;
list_for_each_entry(hdr, &reply->hdrs, list) {
chunk_reset(value);
value->data = build_logline(s, value->area, value->size, &hdr->value);
if (b_data(value) && !htx_add_header(htx, hdr->name, ist2(b_head(value), b_data(value)))) {
free_trash_chunk(value);
goto fail;
}
chunk_reset(value);
}
free_trash_chunk(value);
}
if (!htx_add_header(htx, ist("content-length"), ist(clen)) ||
(body && b_data(body) && ctype && !htx_add_header(htx, ist("content-type"), ist(ctype))) ||
!htx_add_endof(htx, HTX_BLK_EOH) ||
(body && b_data(body) && !htx_add_data_atonce(htx, ist2(b_head(body), b_data(body)))) ||
!htx_add_endof(htx, HTX_BLK_EOM))
goto fail;
}
htx_to_buf(htx, &s->res.buf);
if (!http_forward_proxy_resp(s, 1))
goto fail;
leave:
if (reply->type == HTTP_REPLY_LOGFMT)
free_trash_chunk(body);
return ret;
fail_alloc:
if (!(s->flags & SF_ERR_MASK))
s->flags |= SF_ERR_RESOURCE;
ret = -1;
goto leave;
fail:
/* If an error occurred, remove the incomplete HTTP response from the
* buffer */
channel_htx_truncate(res, htx);
ret = -1;
if (!(s->flags & SF_ERR_MASK))
s->flags |= SF_ERR_PRXCOND;
goto leave;
}
/* Return the error message corresponding to si->err_type. It is assumed
* that the server side is closed. Note that err_type is actually a
* bitmask, where almost only aborts may be cumulated with other