diff --git a/doc/configuration.txt b/doc/configuration.txt index 1a89ad0a9..2242c7100 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -2628,7 +2628,8 @@ errorfile yes | yes | yes | yes Arguments : is the HTTP status code. Currently, HAProxy is capable of - generating codes 200, 400, 403, 408, 500, 502, 503, and 504. + generating codes 200, 400, 403, 405, 408, 429, 500, 502, 503, and + 504. designates a file containing the full HTTP response. It is recommended to follow the common practice of appending ".http" to diff --git a/include/types/proto_http.h b/include/types/proto_http.h index 611cafed2..91245e991 100644 --- a/include/types/proto_http.h +++ b/include/types/proto_http.h @@ -309,7 +309,9 @@ enum { HTTP_ERR_200 = 0, HTTP_ERR_400, HTTP_ERR_403, + HTTP_ERR_405, HTTP_ERR_408, + HTTP_ERR_429, HTTP_ERR_500, HTTP_ERR_502, HTTP_ERR_503, @@ -416,6 +418,7 @@ struct http_req_rule { struct list list; struct acl_cond *cond; /* acl condition to meet */ unsigned int action; /* HTTP_REQ_* */ + short deny_status; /* HTTP status to return to user when denying */ int (*action_ptr)(struct http_req_rule *rule, struct proxy *px, struct stream *s); /* ptr to custom action */ union { struct { @@ -483,6 +486,7 @@ struct http_txn { unsigned int flags; /* transaction flags */ enum http_meth_t meth; /* HTTP method */ /* 1 unused byte here */ + short rule_deny_status; /* HTTP status from rule when denying */ short status; /* HTTP status from the server, negative if from proxy */ char *uri; /* first line if log needed, NULL otherwise */ diff --git a/src/proto_http.c b/src/proto_http.c index 338ae9e73..4db982aee 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -131,7 +131,9 @@ const int http_err_codes[HTTP_ERR_SIZE] = { [HTTP_ERR_200] = 200, /* used by "monitor-uri" */ [HTTP_ERR_400] = 400, [HTTP_ERR_403] = 403, + [HTTP_ERR_405] = 405, [HTTP_ERR_408] = 408, + [HTTP_ERR_429] = 429, [HTTP_ERR_500] = 500, [HTTP_ERR_502] = 502, [HTTP_ERR_503] = 503, @@ -163,6 +165,14 @@ static const char *http_err_msgs[HTTP_ERR_SIZE] = { "\r\n" "

403 Forbidden

\nRequest forbidden by administrative rules.\n\n", + [HTTP_ERR_405] = + "HTTP/1.0 405 Method Not Allowed\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "

405 Method Not Allowed

\nA request was made of a resource using a request method not supported by that resource\n\n", + [HTTP_ERR_408] = "HTTP/1.0 408 Request Time-out\r\n" "Cache-Control: no-cache\r\n" @@ -171,6 +181,14 @@ static const char *http_err_msgs[HTTP_ERR_SIZE] = { "\r\n" "

408 Request Time-out

\nYour browser didn't send a complete request in time.\n\n", + [HTTP_ERR_429] = + "HTTP/1.0 429 Too Many Requests\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "

429 Too Many Requests

\nYou have sent too many requests in a given amount of time.\n\n", + [HTTP_ERR_500] = "HTTP/1.0 500 Server Error\r\n" "Cache-Control: no-cache\r\n" @@ -3371,10 +3389,12 @@ resume_execution: return HTTP_RULE_RES_STOP; case HTTP_REQ_ACT_DENY: + txn->rule_deny_status = rule->deny_status; return HTTP_RULE_RES_DENY; case HTTP_REQ_ACT_TARPIT: txn->flags |= TX_CLTARPIT; + txn->rule_deny_status = rule->deny_status; return HTTP_RULE_RES_DENY; case HTTP_REQ_ACT_AUTH: @@ -4266,9 +4286,9 @@ int http_process_req_common(struct stream *s, struct channel *req, int an_bit, s deny: /* this request was blocked (denied) */ txn->flags |= TX_CLDENY; - txn->status = 403; + txn->status = http_err_codes[txn->rule_deny_status]; s->logs.tv_request = now; - stream_int_retnclose(&s->si[0], http_error_message(s, HTTP_ERR_403)); + stream_int_retnclose(&s->si[0], http_error_message(s, txn->rule_deny_status)); stream_inc_http_err_ctr(s); sess->fe->fe_counters.denied_req++; if (sess->fe != s->be) @@ -8997,12 +9017,38 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i goto out_err; } + rule->deny_status = HTTP_ERR_403; if (!strcmp(args[0], "allow")) { rule->action = HTTP_REQ_ACT_ALLOW; cur_arg = 1; } else if (!strcmp(args[0], "deny") || !strcmp(args[0], "block")) { + int code; + int hc; + rule->action = HTTP_REQ_ACT_DENY; cur_arg = 1; + if (strcmp(args[cur_arg], "deny_status") == 0) { + cur_arg++; + if (!args[cur_arg]) { + Alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : missing status code.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0]); + goto out_err; + } + + code = atol(args[cur_arg]); + cur_arg++; + for (hc = 0; hc < HTTP_ERR_SIZE; hc++) { + if (http_err_codes[hc] == code) { + rule->deny_status = hc; + break; + } + } + + if (hc >= HTTP_ERR_SIZE) { + Warning("parsing [%s:%d] : status code %d not handled, using default code 403.\n", + file, linenum, code); + } + } } else if (!strcmp(args[0], "tarpit")) { rule->action = HTTP_REQ_ACT_TARPIT; cur_arg = 1;