mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-02-09 06:46:55 +00:00
MINOR: http: Action for manipulating the returned status code.
This patch is inspired by Bowen Ni's proposal and it is based on his first implementation: With Lua integration in HAProxy 1.6, one can change the request method, path, uri, header, response header etc except response line. I'd like to contribute the following methods to allow modification of the response line. [...] There are two new keywords in 'http-response' that allows you to rewrite them in the native HAProxy config. There are also two new APIs in Lua that allows you to do the same rewriting in your Lua script. Example: Use it in HAProxy config: *http-response set-code 404* Or use it in Lua script: *txn.http:res_set_reason("Redirect")* I dont take the full patch because the manipulation of the "reason" is useless. standard reason are associated with each returned code, and unknown code can take generic reason. So, this patch can set the status code, and the reason is automatically adapted.
This commit is contained in:
parent
3f4bc65a22
commit
35d70efc33
@ -3745,6 +3745,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-log-level <level> | set-mark <mark> | set-tos <tos> |
|
||||
add-acl(<file name>) <key fmt> |
|
||||
del-acl(<file name>) <key fmt> |
|
||||
@ -3834,6 +3835,15 @@ 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.
|
||||
|
||||
Example:
|
||||
|
||||
# return "431 Request Header Fields Too Large"
|
||||
http-response set-status 431
|
||||
|
||||
- "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
|
||||
time. The default value is 0, unless altered by the "nice" setting on the
|
||||
|
@ -757,6 +757,14 @@ HTTP class
|
||||
:param class_http http: The related http object.
|
||||
:param string uri: The new uri.
|
||||
|
||||
.. js:function:: HTTP.res_set_status(http, status)
|
||||
|
||||
Rewrites the response status code with the parameter "code". Note that the
|
||||
reason is automatically adapted to the new code.
|
||||
|
||||
:param class_http http: The related http object.
|
||||
:param integer status: The new response status code.
|
||||
|
||||
TXN class
|
||||
=========
|
||||
|
||||
|
@ -98,6 +98,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);
|
||||
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);
|
||||
|
@ -120,6 +120,9 @@ struct act_rule {
|
||||
struct sample_expr *expr; /* expression used as the key */
|
||||
struct cap_hdr *hdr; /* the capture storage */
|
||||
} cap;
|
||||
struct {
|
||||
unsigned int code; /* HTTP status code */
|
||||
} status;
|
||||
struct {
|
||||
struct sample_expr *expr;
|
||||
int idx;
|
||||
|
11
src/hlua.c
11
src/hlua.c
@ -3322,6 +3322,16 @@ static int hlua_http_req_set_uri(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* This function set the response code. */
|
||||
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));
|
||||
|
||||
http_set_status(code, htxn->s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
@ -4931,6 +4941,7 @@ void hlua_init(void)
|
||||
hlua_class_function(gL.T, "res_rep_value", hlua_http_res_rep_val);
|
||||
hlua_class_function(gL.T, "res_add_header", hlua_http_res_add_hdr);
|
||||
hlua_class_function(gL.T, "res_set_header", hlua_http_res_set_hdr);
|
||||
hlua_class_function(gL.T, "res_set_status", hlua_http_res_set_status);
|
||||
|
||||
lua_settable(gL.T, -3);
|
||||
|
||||
|
168
src/proto_http.c
168
src/proto_http.c
@ -273,6 +273,91 @@ fd_set http_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set
|
||||
|
||||
static int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s, struct http_txn *txn);
|
||||
|
||||
/* This function returns a reason associated with the HTTP status.
|
||||
* This function never fails, a message is always returned.
|
||||
*/
|
||||
const char *get_reason(unsigned int status)
|
||||
{
|
||||
switch (status) {
|
||||
case 100: return "Continue";
|
||||
case 101: return "Switching Protocols";
|
||||
case 102: return "Processing";
|
||||
case 200: return "OK";
|
||||
case 201: return "Created";
|
||||
case 202: return "Accepted";
|
||||
case 203: return "Non-Authoritative Information";
|
||||
case 204: return "No Content";
|
||||
case 205: return "Reset Content";
|
||||
case 206: return "Partial Content";
|
||||
case 207: return "Multi-Status";
|
||||
case 210: return "Content Different";
|
||||
case 226: return "IM Used";
|
||||
case 300: return "Multiple Choices";
|
||||
case 301: return "Moved Permanently";
|
||||
case 302: return "Moved Temporarily";
|
||||
case 303: return "See Other";
|
||||
case 304: return "Not Modified";
|
||||
case 305: return "Use Proxy";
|
||||
case 307: return "Temporary Redirect";
|
||||
case 308: return "Permanent Redirect";
|
||||
case 310: return "Too many Redirects";
|
||||
case 400: return "Bad Request";
|
||||
case 401: return "Unauthorized";
|
||||
case 402: return "Payment Required";
|
||||
case 403: return "Forbidden";
|
||||
case 404: return "Not Found";
|
||||
case 405: return "Method Not Allowed";
|
||||
case 406: return "Not Acceptable";
|
||||
case 407: return "Proxy Authentication Required";
|
||||
case 408: return "Request Time-out";
|
||||
case 409: return "Conflict";
|
||||
case 410: return "Gone";
|
||||
case 411: return "Length Required";
|
||||
case 412: return "Precondition Failed";
|
||||
case 413: return "Request Entity Too Large";
|
||||
case 414: return "Request-URI Too Long";
|
||||
case 415: return "Unsupported Media Type";
|
||||
case 416: return "Requested range unsatisfiable";
|
||||
case 417: return "Expectation failed";
|
||||
case 418: return "I'm a teapot";
|
||||
case 422: return "Unprocessable entity";
|
||||
case 423: return "Locked";
|
||||
case 424: return "Method failure";
|
||||
case 425: return "Unordered Collection";
|
||||
case 426: return "Upgrade Required";
|
||||
case 428: return "Precondition Required";
|
||||
case 429: return "Too Many Requests";
|
||||
case 431: return "Request Header Fields Too Large";
|
||||
case 449: return "Retry With";
|
||||
case 450: return "Blocked by Windows Parental Controls";
|
||||
case 451: return "Unavailable For Legal Reasons";
|
||||
case 456: return "Unrecoverable Error";
|
||||
case 499: return "client has closed connection";
|
||||
case 500: return "Internal Server Error";
|
||||
case 501: return "Not Implemented";
|
||||
case 502: return "Bad Gateway ou Proxy Error";
|
||||
case 503: return "Service Unavailable";
|
||||
case 504: return "Gateway Time-out";
|
||||
case 505: return "HTTP Version not supported";
|
||||
case 506: return "Variant also negociate";
|
||||
case 507: return "Insufficient storage";
|
||||
case 508: return "Loop detected";
|
||||
case 509: return "Bandwidth Limit Exceeded";
|
||||
case 510: return "Not extended";
|
||||
case 511: return "Network authentication required";
|
||||
case 520: return "Web server is returning an unknown error";
|
||||
default:
|
||||
switch (status) {
|
||||
case 100 ... 199: return "Informational";
|
||||
case 200 ... 299: return "Success";
|
||||
case 300 ... 399: return "Redirection";
|
||||
case 400 ... 499: return "Client Error";
|
||||
case 500 ... 599: return "Server Error";
|
||||
default: return "Other";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_proto_http()
|
||||
{
|
||||
int i;
|
||||
@ -12253,6 +12338,50 @@ int http_replace_req_line(int action, const char *replace, int len,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
struct http_txn *txn = s->txn;
|
||||
char *cur_ptr, *cur_end;
|
||||
int delta;
|
||||
char *res;
|
||||
int c_l;
|
||||
const char *msg;
|
||||
int msg_len;
|
||||
|
||||
chunk_reset(&trash);
|
||||
|
||||
res = ultoa_o(status, trash.str, trash.size);
|
||||
c_l = res - trash.str;
|
||||
|
||||
trash.str[c_l] = ' ';
|
||||
trash.len = c_l + 1;
|
||||
|
||||
msg = get_reason(status);
|
||||
msg_len = strlen(msg);
|
||||
|
||||
strncpy(&trash.str[trash.len], msg, trash.size - trash.len);
|
||||
trash.len += msg_len;
|
||||
|
||||
cur_ptr = s->res.buf->p + txn->rsp.sl.st.c;
|
||||
cur_end = s->res.buf->p + txn->rsp.sl.st.r + txn->rsp.sl.st.r_l;
|
||||
|
||||
/* commit changes and adjust message */
|
||||
delta = buffer_replace2(s->res.buf, cur_ptr, cur_end, trash.str, trash.len);
|
||||
|
||||
/* adjust res line offsets and lengths */
|
||||
txn->rsp.sl.st.r += c_l - txn->rsp.sl.st.c_l;
|
||||
txn->rsp.sl.st.c_l = c_l;
|
||||
txn->rsp.sl.st.r_l = msg_len;
|
||||
|
||||
delta = trash.len - (cur_end - cur_ptr);
|
||||
txn->rsp.sl.st.l += delta;
|
||||
txn->hdr_idx.v[0].len += delta;
|
||||
http_msg_move_end(&txn->rsp, delta);
|
||||
}
|
||||
|
||||
/* This function executes one of the set-{method,path,query,uri} actions. It
|
||||
* builds a string in the trash from the specified format string. It finds
|
||||
* the action to be performed in <http.action>, previously filled by function
|
||||
@ -12274,6 +12403,14 @@ enum act_return http_action_set_req_line(struct act_rule *rule, struct proxy *px
|
||||
return ACT_RET_CONT;
|
||||
}
|
||||
|
||||
/* This function is just a compliant action wrapper for "set-status". */
|
||||
enum act_return action_http_set_status(struct act_rule *rule, struct proxy *px,
|
||||
struct session *sess, struct stream *s)
|
||||
{
|
||||
http_set_status(rule->arg.status.code, s);
|
||||
return ACT_RET_CONT;
|
||||
}
|
||||
|
||||
/* parse an http-request action among :
|
||||
* set-method
|
||||
* set-path
|
||||
@ -12330,6 +12467,36 @@ enum act_parse_ret parse_set_req_line(const char **args, int *orig_arg, struct p
|
||||
return ACT_RET_PRS_OK;
|
||||
}
|
||||
|
||||
/* parse set-status action:
|
||||
* This action accepts a single argument of type int representing
|
||||
* an http status code. It returns ACT_RET_PRS_OK on success,
|
||||
* ACT_RET_PRS_ERR on error.
|
||||
*/
|
||||
enum act_parse_ret parse_http_set_status(const char **args, int *orig_arg, struct proxy *px,
|
||||
struct act_rule *rule, char **err)
|
||||
{
|
||||
char *error;
|
||||
|
||||
rule->action = ACT_ACTION_CONT;
|
||||
rule->action_ptr = action_http_set_status;
|
||||
|
||||
/* Check if an argument is available */
|
||||
if (!*args[*orig_arg]) {
|
||||
memprintf(err, "expects exactly 1 argument <status>");
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
/* convert status code as integer */
|
||||
rule->arg.status.code = strtol(args[*orig_arg], &error, 10);
|
||||
if (*error != '\0' || rule->arg.status.code < 100 || rule->arg.status.code > 999) {
|
||||
memprintf(err, "expects an integer status code between 100 and 999");
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
(*orig_arg)++;
|
||||
return ACT_RET_PRS_OK;
|
||||
}
|
||||
|
||||
/* This function executes the "capture" action. It executes a fetch expression,
|
||||
* turns the result into a string and puts it in a capture slot. It always
|
||||
* returns 1. If an error occurs the action is cancelled, but the rule
|
||||
@ -12901,6 +13068,7 @@ struct action_kw_list http_req_actions = {
|
||||
struct action_kw_list http_res_actions = {
|
||||
.kw = {
|
||||
{ "capture", parse_http_res_capture },
|
||||
{ "set-status", parse_http_set_status },
|
||||
{ NULL, NULL }
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user