MINOR: http: custom status reason.

The older 'rsprep' directive allows modification of the status reason.

Extend 'http-response set-status' to take an optional string of the new
status reason.

  http-response set-status 418 reason "I'm a coffeepot"

Matching updates in Lua code:
- AppletHTTP.set_status
- HTTP.res_set_status

Signed-off-by: Robin H. Johnson <robbat2@gentoo.org>
This commit is contained in:
Robin H. Johnson 2017-01-01 13:10:52 -08:00 committed by Willy Tarreau
parent 2afff9c2d6
commit 52f5db2a44
7 changed files with 44 additions and 17 deletions

View File

@ -4122,7 +4122,7 @@ http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> |
set-header <name> <fmt> | del-header <name> |
replace-header <name> <regex-match> <replace-fmt> |
replace-value <name> <regex-match> <replace-fmt> |
set-status <status> |
set-status <status> [reason <str>] |
set-log-level <level> | set-mark <mark> | set-tos <tos> |
add-acl(<file name>) <key fmt> |
del-acl(<file name>) <key fmt> |
@ -4215,13 +4215,16 @@ http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> |
Cache-Control: max-age=3600, private
- "set-status" replaces the response status code with <status> which must
be an integer between 100 and 999. Note that the reason is automatically
adapted to the new code.
be an integer between 100 and 999. Optionally, a custom reason text can be
provided defined by <str>, or the default reason for the specified code
will be used as a fallback.
Example:
# return "431 Request Header Fields Too Large"
http-response set-status 431
# return "503 Slow Down", custom reason
http-response set-status 503 reason "Slow Down".
- "set-nice" sets the "nice" factor of the current request being processed.
It only has effect against the other requests being processed at the same

View File

@ -1443,13 +1443,15 @@ HTTP class
:param class_http http: The related http object.
:param string uri: The new uri.
.. js:function:: HTTP.res_set_status(http, status)
.. js:function:: HTTP.res_set_status(http, status [, reason])
Rewrites the response status code with the parameter "code". Note that the
reason is automatically adapted to the new code.
Rewrites the response status code with the parameter "code".
If no custom reason is provided, it will be generated from the status.
:param class_http http: The related http object.
:param integer status: The new response status code.
:param string reason: The new response reason (optional).
.. _txn_class:
@ -2080,13 +2082,14 @@ AppletHTTP class
AppletHTTP.headers["accept"][2] = "*/*, q=0.1"
..
.. js:function:: AppletHTTP.set_status(applet, code)
.. js:function:: AppletHTTP.set_status(applet, code [, reason])
This function sets the HTTP status code for the response. The allowed code are
from 100 to 599.
:param class_AppletHTTP applet: An :ref:`applethttp_class`
:param integer code: the status code returned to the client.
:param string reason: the status reason returned to the client (optional).
.. js:function:: AppletHTTP.add_header(applet, name, value)

View File

@ -108,7 +108,7 @@ int http_header_match2(const char *hdr, const char *end, const char *name, int l
int http_remove_header2(struct http_msg *msg, struct hdr_idx *idx, struct hdr_ctx *ctx);
int http_header_add_tail2(struct http_msg *msg, struct hdr_idx *hdr_idx, const char *text, int len);
int http_replace_req_line(int action, const char *replace, int len, struct proxy *px, struct stream *s);
void http_set_status(unsigned int status, struct stream *s);
void http_set_status(unsigned int status, const char *reason, struct stream *s);
int http_transform_header_str(struct stream* s, struct http_msg *msg, const char* name,
unsigned int name_len, const char *str, struct my_regex *re,
int action);

View File

@ -135,6 +135,7 @@ struct act_rule {
} cap;
struct {
unsigned int code; /* HTTP status code */
const char *reason; /* HTTP status reason */
} status;
struct {
struct sample_expr *expr;

View File

@ -80,6 +80,7 @@ struct appctx {
int left_bytes; /* The max amount of bytes that we can read. */
int flags;
int status;
const char *reason;
struct task *task;
} hlua_apphttp; /* used by the Lua HTTP services */
struct {

View File

@ -3591,6 +3591,7 @@ static int hlua_applet_http_new(lua_State *L, struct appctx *ctx)
lua_rawseti(L, -2, 0);
appctx->appctx = ctx;
appctx->appctx->ctx.hlua_apphttp.status = 200; /* Default status code returned. */
appctx->appctx->ctx.hlua_apphttp.reason = NULL; /* Use default reason based on status */
appctx->htxn.s = s;
appctx->htxn.p = px;
@ -4119,6 +4120,7 @@ __LJMP static int hlua_applet_http_status(lua_State *L)
{
struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1));
int status = MAY_LJMP(luaL_checkinteger(L, 2));
const char *reason = MAY_LJMP(luaL_optlstring(L, 3, NULL, NULL));
if (status < 100 || status > 599) {
lua_pushboolean(L, 0);
@ -4126,6 +4128,7 @@ __LJMP static int hlua_applet_http_status(lua_State *L)
}
appctx->appctx->ctx.hlua_apphttp.status = status;
appctx->appctx->ctx.hlua_apphttp.reason = reason;
lua_pushboolean(L, 1);
return 1;
}
@ -4178,12 +4181,16 @@ __LJMP static int hlua_applet_http_start_response(lua_State *L)
int hdr_connection = 0;
int hdr_contentlength = -1;
int hdr_chunked = 0;
const char *reason = appctx->appctx->ctx.hlua_apphttp.reason;
if (reason == NULL)
reason = get_reason(appctx->appctx->ctx.hlua_apphttp.status);
/* Use the same http version than the request. */
chunk_appendf(tmp, "HTTP/1.%c %d %s\r\n",
appctx->appctx->ctx.hlua_apphttp.flags & APPLET_HTTP11 ? '1' : '0',
appctx->appctx->ctx.hlua_apphttp.status,
get_reason(appctx->appctx->ctx.hlua_apphttp.status));
reason);
/* Get the array associated to the field "response" in the object AppletHTTP. */
lua_pushvalue(L, 0);
@ -4737,17 +4744,18 @@ static int hlua_http_req_set_uri(lua_State *L)
return 1;
}
/* This function set the response code. */
/* This function set the response code & optionally reason. */
static int hlua_http_res_set_status(lua_State *L)
{
struct hlua_txn *htxn = MAY_LJMP(hlua_checkhttp(L, 1));
unsigned int code = MAY_LJMP(luaL_checkinteger(L, 2));
const char *reason = MAY_LJMP(luaL_optlstring(L, 3, NULL, NULL));
/* Check if a valid response is parsed */
if (unlikely(htxn->s->txn->rsp.msg_state < HTTP_MSG_BODY))
return 0;
http_set_status(code, htxn->s);
http_set_status(code, reason, htxn->s);
return 0;
}

View File

@ -12454,14 +12454,14 @@ int http_replace_req_line(int action, const char *replace, int len,
/* This function replace the HTTP status code and the associated message. The
* variable <status> contains the new status code. This function never fails.
*/
void http_set_status(unsigned int status, struct stream *s)
void http_set_status(unsigned int status, const char *reason, struct stream *s)
{
struct http_txn *txn = s->txn;
char *cur_ptr, *cur_end;
int delta;
char *res;
int c_l;
const char *msg;
const char *msg = reason;
int msg_len;
chunk_reset(&trash);
@ -12472,9 +12472,10 @@ void http_set_status(unsigned int status, struct stream *s)
trash.str[c_l] = ' ';
trash.len = c_l + 1;
msg = get_reason(status);
/* Do we have a custom reason format string? */
if (msg == NULL)
msg = get_reason(status);
msg_len = strlen(msg);
strncpy(&trash.str[trash.len], msg, trash.size - trash.len);
trash.len += msg_len;
@ -12520,7 +12521,7 @@ enum act_return http_action_set_req_line(struct act_rule *rule, struct proxy *px
enum act_return action_http_set_status(struct act_rule *rule, struct proxy *px,
struct session *sess, struct stream *s, int flags)
{
http_set_status(rule->arg.status.code, s);
http_set_status(rule->arg.status.code, rule->arg.status.reason, s);
return ACT_RET_CONT;
}
@ -12596,7 +12597,7 @@ enum act_parse_ret parse_http_set_status(const char **args, int *orig_arg, struc
/* Check if an argument is available */
if (!*args[*orig_arg]) {
memprintf(err, "expects exactly 1 argument <status>");
memprintf(err, "expects 1 argument: <status>; or 3 arguments: <status> reason <fmt>");
return ACT_RET_PRS_ERR;
}
@ -12608,6 +12609,16 @@ enum act_parse_ret parse_http_set_status(const char **args, int *orig_arg, struc
}
(*orig_arg)++;
/* set custom reason string */
rule->arg.status.reason = NULL; // If null, we use the default reason for the status code.
if (*args[*orig_arg] && strcmp(args[*orig_arg], "reason") == 0 &&
(*args[*orig_arg + 1] && strcmp(args[*orig_arg + 1], "if") != 0 && strcmp(args[*orig_arg + 1], "unless") != 0)) {
(*orig_arg)++;
rule->arg.status.reason = strdup(args[*orig_arg]);
(*orig_arg)++;
}
return ACT_RET_PRS_OK;
}