diff --git a/doc/configuration.txt b/doc/configuration.txt index 2a5f7dc06..132873d7d 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -4122,7 +4122,7 @@ http-response { allow | deny | add-header | set-nice | set-header | del-header | replace-header | replace-value | - set-status | + set-status [reason ] | set-log-level | set-mark | set-tos | add-acl() | del-acl() | @@ -4215,13 +4215,16 @@ http-response { allow | deny | add-header | set-nice | Cache-Control: max-age=3600, private - "set-status" replaces the response status code with 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 , 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 diff --git a/doc/lua-api/index.rst b/doc/lua-api/index.rst index f930277df..e04c5b5a7 100644 --- a/doc/lua-api/index.rst +++ b/doc/lua-api/index.rst @@ -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) diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h index 5ee2e2fa4..6c81766aa 100644 --- a/include/proto/proto_http.h +++ b/include/proto/proto_http.h @@ -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); diff --git a/include/types/action.h b/include/types/action.h index 5a70db06c..1c1715492 100644 --- a/include/types/action.h +++ b/include/types/action.h @@ -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; diff --git a/include/types/applet.h b/include/types/applet.h index 759b905b3..642c7931b 100644 --- a/include/types/applet.h +++ b/include/types/applet.h @@ -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 { diff --git a/src/hlua.c b/src/hlua.c index c343a7b39..48fcf1af0 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -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; } diff --git a/src/proto_http.c b/src/proto_http.c index 15070012a..f7f754510 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -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 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 "); + memprintf(err, "expects 1 argument: ; or 3 arguments: reason "); 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; }