summaryrefslogtreecommitdiffstats
path: root/src/http.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/http.c')
-rw-r--r--src/http.c212
1 files changed, 181 insertions, 31 deletions
diff --git a/src/http.c b/src/http.c
index 9599e0e..8c94dfb 100644
--- a/src/http.c
+++ b/src/http.c
@@ -12,6 +12,7 @@
#include <ctype.h>
#include <haproxy/api.h>
+#include <haproxy/cfgparse.h>
#include <haproxy/http.h>
#include <haproxy/tools.h>
@@ -344,6 +345,14 @@ const struct ist http_known_methods[HTTP_METH_OTHER] = {
[HTTP_METH_CONNECT] = IST("CONNECT"),
};
+/* 500 bits to indicate for each status code from 100 to 599 if it participates
+ * to the error or failure class. The last 12 bits are not assigned for now.
+ * Not initialized, has to be done at boot. This is manipulated using
+ * http_status_{add,del}_range().
+ */
+long http_err_status_codes[512 / sizeof(long)] = { };
+long http_fail_status_codes[512 / sizeof(long)] = { };
+
/*
* returns a known method among HTTP_METH_* or HTTP_METH_OTHER for all unknown
* ones.
@@ -352,15 +361,15 @@ enum http_meth_t find_http_meth(const char *str, const int len)
{
const struct ist m = ist2(str, len);
- if (isteq(m, ist("GET"))) return HTTP_METH_GET;
- else if (isteq(m, ist("HEAD"))) return HTTP_METH_HEAD;
- else if (isteq(m, ist("POST"))) return HTTP_METH_POST;
- else if (isteq(m, ist("CONNECT"))) return HTTP_METH_CONNECT;
- else if (isteq(m, ist("PUT"))) return HTTP_METH_PUT;
- else if (isteq(m, ist("OPTIONS"))) return HTTP_METH_OPTIONS;
- else if (isteq(m, ist("DELETE"))) return HTTP_METH_DELETE;
- else if (isteq(m, ist("TRACE"))) return HTTP_METH_TRACE;
- else return HTTP_METH_OTHER;
+ if (isteq(m, http_known_methods[HTTP_METH_GET])) return HTTP_METH_GET;
+ else if (isteq(m, http_known_methods[HTTP_METH_PUT])) return HTTP_METH_PUT;
+ else if (isteq(m, http_known_methods[HTTP_METH_HEAD])) return HTTP_METH_HEAD;
+ else if (isteq(m, http_known_methods[HTTP_METH_POST])) return HTTP_METH_POST;
+ else if (isteq(m, http_known_methods[HTTP_METH_TRACE])) return HTTP_METH_TRACE;
+ else if (isteq(m, http_known_methods[HTTP_METH_DELETE])) return HTTP_METH_DELETE;
+ else if (isteq(m, http_known_methods[HTTP_METH_CONNECT])) return HTTP_METH_CONNECT;
+ else if (isteq(m, http_known_methods[HTTP_METH_OPTIONS])) return HTTP_METH_OPTIONS;
+ else return HTTP_METH_OTHER;
}
/* This function returns HTTP_ERR_<num> (enum) matching http status code.
@@ -368,28 +377,27 @@ enum http_meth_t find_http_meth(const char *str, const int len)
*/
int http_get_status_idx(unsigned int status)
{
- switch (status) {
- case 200: return HTTP_ERR_200;
- case 400: return HTTP_ERR_400;
- case 401: return HTTP_ERR_401;
- case 403: return HTTP_ERR_403;
- case 404: return HTTP_ERR_404;
- case 405: return HTTP_ERR_405;
- case 407: return HTTP_ERR_407;
- case 408: return HTTP_ERR_408;
- case 410: return HTTP_ERR_410;
- case 413: return HTTP_ERR_413;
- case 421: return HTTP_ERR_421;
- case 422: return HTTP_ERR_422;
- case 425: return HTTP_ERR_425;
- case 429: return HTTP_ERR_429;
- case 500: return HTTP_ERR_500;
- case 501: return HTTP_ERR_501;
- case 502: return HTTP_ERR_502;
- case 503: return HTTP_ERR_503;
- case 504: return HTTP_ERR_504;
- default: return HTTP_ERR_500;
- }
+ /* This table was built using dev/phash and easily finds solutions up
+ * to 21 different entries and produces much better code with 32
+ * (padded with err 500 below as it's the default, though only [19] is
+ * the real one).
+ */
+ const uchar codes[32] = {
+ HTTP_ERR_408, HTTP_ERR_200, HTTP_ERR_504, HTTP_ERR_400,
+ HTTP_ERR_500, HTTP_ERR_500, HTTP_ERR_401, HTTP_ERR_410,
+ HTTP_ERR_500, HTTP_ERR_500, HTTP_ERR_500, HTTP_ERR_500,
+ HTTP_ERR_500, HTTP_ERR_429, HTTP_ERR_403, HTTP_ERR_500,
+ HTTP_ERR_421, HTTP_ERR_404, HTTP_ERR_413, HTTP_ERR_500,
+ HTTP_ERR_422, HTTP_ERR_405, HTTP_ERR_500, HTTP_ERR_501,
+ HTTP_ERR_500, HTTP_ERR_500, HTTP_ERR_500, HTTP_ERR_502,
+ HTTP_ERR_407, HTTP_ERR_500, HTTP_ERR_503, HTTP_ERR_425,
+ };
+ uint hash = ((status * 118) >> 5) % 32;
+ uint ret = codes[hash];
+
+ if (http_err_codes[ret] == status)
+ return ret;
+ return HTTP_ERR_500;
}
/* This function returns a reason associated with the HTTP status.
@@ -478,6 +486,40 @@ const char *http_get_reason(unsigned int status)
}
}
+/* add status codes from low to high included to status codes array <array>
+ * which must be compatible with http_err_codes and http_fail_codes (i.e. 512
+ * bits each). This is not thread save and is meant for being called during
+ * boot only. Only status codes 100-599 are permitted.
+ */
+void http_status_add_range(long *array, uint low, uint high)
+{
+ low -= 100;
+ high -= 100;
+
+ BUG_ON(low > 499);
+ BUG_ON(high > 499);
+
+ while (low <= high)
+ ha_bit_set(low++, array);
+}
+
+/* remove status codes from low to high included to status codes array <array>
+ * which must be compatible with http_err_codes and http_fail_codes (i.e. 512
+ * bits each). This is not thread save and is meant for being called during
+ * boot only. Only status codes 100-599 are permitted.
+ */
+void http_status_del_range(long *array, uint low, uint high)
+{
+ low -= 100;
+ high -= 100;
+
+ BUG_ON(low > 499);
+ BUG_ON(high > 499);
+
+ while (low <= high)
+ ha_bit_clr(low++, array);
+}
+
/* Returns the ist string corresponding to port part (without ':') in the host
* <host>, IST_NULL if no ':' is found or an empty IST if there is no digit. In
* the last case, the result is the original ist trimmed to 0. So be sure to test
@@ -1431,3 +1473,111 @@ struct ist http_trim_trailing_spht(struct ist value)
return ret;
}
+
+/* initialize the required structures and arrays */
+static void _http_init()
+{
+ /* preset the default status codes that count as errors and failures */
+ http_status_add_range(http_err_status_codes, 400, 499);
+ http_status_add_range(http_fail_status_codes, 500, 599);
+ http_status_del_range(http_fail_status_codes, 501, 501);
+ http_status_del_range(http_fail_status_codes, 505, 505);
+}
+INITCALL0(STG_INIT, _http_init);
+
+/*
+ * registered keywords below
+ */
+
+/* parses a global "http-err-codes" and "http-fail-codes" directive. */
+static int http_parse_http_err_fail_codes(char **args, int section_type, struct proxy *curpx,
+ const struct proxy *defpx, const char *file, int line,
+ char **err)
+{
+ const char *cmd = args[0];
+ const char *p, *b, *e;
+ int op, low, high;
+ long *bitfield;
+ int ret = -1;
+
+ if (strcmp(cmd, "http-err-codes") == 0)
+ bitfield = http_err_status_codes;
+ else if (strcmp(cmd, "http-fail-codes") == 0)
+ bitfield = http_fail_status_codes;
+ else
+ ABORT_NOW();
+
+ if (!*args[1]) {
+ memprintf(err, "Missing status codes range for '%s'.", cmd);
+ goto end;
+ }
+
+ /* operation: <0 = remove, 0 = replace, >0 = add. The operation is only
+ * reset for each new arg so that we can do +200,300,400 without
+ * changing the operation.
+ */
+ for (; *(p = *(++args)); ) {
+ switch (*p) {
+ case '+': op = 1; p++; break;
+ case '-': op = -1; p++; break;
+ default: op = 0; break;
+ }
+
+ if (!*p)
+ goto inval;
+
+ while (1) {
+ b = p;
+ e = p + strlen(p);
+ low = read_uint(&p, e);
+ if (b == e || p == b)
+ goto inval;
+
+ high = low;
+ if (*p == '-') {
+ p++;
+ b = p;
+ high = read_uint(&p, e);
+ if (b == e || p == b || (*p && *p != ','))
+ goto inval;
+ }
+ else if (*p && *p != ',')
+ goto inval;
+
+ if (high < low || low < 100 || high > 599) {
+ memprintf(err, "Invalid status codes range '%s' in '%s'.\n"
+ " Codes must be between 100 and 599 and ranges in ascending order.",
+ *args, cmd);
+ goto end;
+ }
+
+ if (!op)
+ memset(bitfield, 0, sizeof(http_err_status_codes));
+ if (op >= 0)
+ http_status_add_range(bitfield, low, high);
+ if (op < 0)
+ http_status_del_range(bitfield, low, high);
+
+ if (!*p)
+ break;
+ /* skip ',' */
+ p++;
+ }
+ }
+ ret = 0;
+ end:
+ return ret;
+ inval:
+ memprintf(err, "Invalid status codes range '%s' in '%s' at position %lu. Ranges must be in the form [+-]{low[-{high}]}[,...].",
+ *args, cmd, (ulong)(p - *args));
+ goto end;
+
+}
+
+static struct cfg_kw_list cfg_kws = {{ },{
+ { CFG_GLOBAL, "http-err-codes", http_parse_http_err_fail_codes },
+ { CFG_GLOBAL, "http-fail-codes", http_parse_http_err_fail_codes },
+ { /* END */ }
+}};
+
+INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);