- Update haproxy download URL and hash - Add new patches - Add several CFLAGS (derived from haproxy Makefile) to make the build work with v1.9+ - Update default configuration - Add check-command (for config) to init-script - Add prometheus-service from contribs by default Signed-off-by: Christian Lachner <gladiac@gmail.com>lilik-openwrt-22.03
@ -1,7 +1,6 @@ | |||
#!/bin/sh | |||
if [ "$ACTION" = add ]; then | |||
/etc/init.d/haproxy enabled && \ | |||
/etc/init.d/haproxy start | |||
fi | |||
@ -0,0 +1,32 @@ | |||
commit 032cff38c24d8359dc575423a94d19b6ad8bf848 | |||
Author: Christopher Faulet <cfaulet@haproxy.com> | |||
Date: Mon Jun 17 11:44:47 2019 +0200 | |||
BUG/MEDIUM: h2/htx: Update data length of the HTX when the cookie list is built | |||
When an H2 request is converted into an HTX message, All cookie headers are | |||
grouped into one, each value separated by a semicolon (;). To do so, we add the | |||
header "cookie" with the first value and then we update the value by appending | |||
other cookies. But during this operation, only the size of the HTX block is | |||
updated. And not the data length of the whole HTX message. | |||
It is an old bug and it seems to work by chance till now. But it may lead to | |||
undefined behaviour by time to time. | |||
This patch must be backported to 2.0 and 1.9 | |||
(cherry picked from commit 0c6de00d7c842a682bba7586ef34fb10f69ec63c) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/src/h2.c b/src/h2.c | |||
index 9681aca5..32c1ef16 100644 | |||
--- a/src/h2.c | |||
+++ b/src/h2.c | |||
@@ -737,6 +737,7 @@ int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *ms | |||
goto fail; | |||
htx_set_blk_value_len(blk, tl); | |||
+ htx->data += vl+2; | |||
*(char *)(htx_get_blk_ptr(htx, blk) + bs + 0) = ';'; | |||
*(char *)(htx_get_blk_ptr(htx, blk) + bs + 1) = ' '; | |||
memcpy(htx_get_blk_ptr(htx, blk) + bs + 2, list[ck].v.ptr, vl); |
@ -1,40 +0,0 @@ | |||
commit cf2f1243373be97249567ffd259e975cc87068b8 | |||
Author: Christopher Faulet <cfaulet@haproxy.com> | |||
Date: Mon Apr 29 13:12:02 2019 +0200 | |||
BUG/MINOR: http: Call stream_inc_be_http_req_ctr() only one time per request | |||
The function stream_inc_be_http_req_ctr() is called at the beginning of the | |||
analysers AN_REQ_HTTP_PROCESS_FE/BE. It as an effect only on the backend. But we | |||
must be careful to call it only once. If the processing of HTTP rules is | |||
interrupted in the middle, when the analyser is resumed, we must not call it | |||
again. Otherwise, the tracked counters of the backend are incremented several | |||
times. | |||
This bug was reported in github. See issue #74. | |||
This fix should be backported as far as 1.6. | |||
(cherry picked from commit 1907ccc2f75b78ace1ee4acdfc60d48a76e3decd) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
(cherry picked from commit 319921866ea9ecc46215fea5679abc8efdfcbea5) | |||
[cf: HTX part was removed] | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/src/proto_http.c b/src/proto_http.c | |||
index ccacd6a4..556cabad 100644 | |||
--- a/src/proto_http.c | |||
+++ b/src/proto_http.c | |||
@@ -3420,8 +3420,10 @@ int http_process_req_common(struct stream *s, struct channel *req, int an_bit, s | |||
req->buf->i, | |||
req->analysers); | |||
- /* just in case we have some per-backend tracking */ | |||
- stream_inc_be_http_req_ctr(s); | |||
+ /* just in case we have some per-backend tracking. Only called the first | |||
+ * execution of the analyser. */ | |||
+ if (!s->current_rule || s->current_rule_list != &px->http_req_rules) | |||
+ stream_inc_be_http_req_ctr(s); | |||
/* evaluate http-request rules */ | |||
if (!LIST_ISEMPTY(&px->http_req_rules)) { |
@ -1,83 +0,0 @@ | |||
commit c1620a52a3def02b4837376385c416c03ca874c4 | |||
Author: Kevin Zhu <ipandtcp@gmail.com> | |||
Date: Fri Apr 26 14:00:01 2019 +0800 | |||
BUG/MEDIUM: spoe: arg len encoded in previous frag frame but len changed | |||
Fragmented arg will do fetch at every encode time, each fetch may get | |||
different result if SMP_F_MAY_CHANGE, for example res.payload, but | |||
the length already encoded in first fragment of the frame, that will | |||
cause SPOA decode failed and waste resources. | |||
This patch must be backported to 1.9 and 1.8. | |||
(cherry picked from commit f7f54280c8106e92a55243f5d60f8587e79602d1) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
(cherry picked from commit 3a838e526cdbc00ded5362e66f1ef3a441abc3c1) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/include/proto/spoe.h b/include/proto/spoe.h | |||
index 002cf7d7..2cdca10b 100644 | |||
--- a/include/proto/spoe.h | |||
+++ b/include/proto/spoe.h | |||
@@ -121,7 +121,7 @@ spoe_decode_buffer(char **buf, char *end, char **str, uint64_t *len) | |||
* many bytes has been encoded. If <*off> is zero at the end, it means that all | |||
* data has been encoded. */ | |||
static inline int | |||
-spoe_encode_data(struct sample *smp, unsigned int *off, char **buf, char *end) | |||
+spoe_encode_data(unsigned int *len, struct sample *smp, unsigned int *off, char **buf, char *end) | |||
{ | |||
char *p = *buf; | |||
int ret; | |||
@@ -183,15 +183,16 @@ spoe_encode_data(struct sample *smp, unsigned int *off, char **buf, char *end) | |||
ret = spoe_encode_frag_buffer(chk->str, chk->len, &p, end); | |||
if (ret == -1) | |||
return -1; | |||
+ *len = chk->len; | |||
} | |||
else { | |||
/* The sample has been fragmented, encode remaining data */ | |||
- ret = MIN(chk->len - *off, end - p); | |||
+ ret = MIN(*len - *off, end - p); | |||
memcpy(p, chk->str + *off, ret); | |||
p += ret; | |||
} | |||
/* Now update <*off> */ | |||
- if (ret + *off != chk->len) | |||
+ if (ret + *off != *len) | |||
*off += ret; | |||
else | |||
*off = 0; | |||
diff --git a/include/types/spoe.h b/include/types/spoe.h | |||
index 53e7200c..cfaa42f8 100644 | |||
--- a/include/types/spoe.h | |||
+++ b/include/types/spoe.h | |||
@@ -304,6 +304,7 @@ struct spoe_context { | |||
struct spoe_message *curmsg; /* SPOE message from which to resume encoding */ | |||
struct spoe_arg *curarg; /* SPOE arg in <curmsg> from which to resume encoding */ | |||
unsigned int curoff; /* offset in <curarg> from which to resume encoding */ | |||
+ unsigned int curlen; /* length of <curarg> need to be encode, for SMP_F_MAY_CHANGE data */ | |||
unsigned int flags; /* SPOE_FRM_FL_* */ | |||
} frag_ctx; /* Info about fragmented frames, valid on if SPOE_CTX_FL_FRAGMENTED is set */ | |||
}; | |||
diff --git a/src/flt_spoe.c b/src/flt_spoe.c | |||
index f6109778..0c0b3794 100644 | |||
--- a/src/flt_spoe.c | |||
+++ b/src/flt_spoe.c | |||
@@ -2172,6 +2172,7 @@ spoe_encode_message(struct stream *s, struct spoe_context *ctx, | |||
list_for_each_entry(arg, &msg->args, list) { | |||
ctx->frag_ctx.curarg = arg; | |||
ctx->frag_ctx.curoff = UINT_MAX; | |||
+ ctx->frag_ctx.curlen = 0; | |||
encode_argument: | |||
if (ctx->frag_ctx.curoff != UINT_MAX) | |||
@@ -2186,7 +2187,7 @@ spoe_encode_message(struct stream *s, struct spoe_context *ctx, | |||
/* Fetch the arguement value */ | |||
smp = sample_process(s->be, s->sess, s, dir|SMP_OPT_FINAL, arg->expr, NULL); | |||
- ret = spoe_encode_data(smp, &ctx->frag_ctx.curoff, buf, end); | |||
+ ret = spoe_encode_data(&ctx->frag_ctx.curlen, smp, &ctx->frag_ctx.curoff, buf, end); | |||
if (ret == -1 || ctx->frag_ctx.curoff) | |||
goto too_big; | |||
} |
@ -0,0 +1,32 @@ | |||
commit 5a8549e68225070d0b79cbbb9c5f791e103685e7 | |||
Author: Christopher Faulet <cfaulet@haproxy.com> | |||
Date: Mon Jun 17 13:36:06 2019 +0200 | |||
BUG/MINOR: lua/htx: Make txn.req_req_* and txn.res_rep_* HTX aware | |||
These bindings were not updated to support HTX streams. | |||
This patch must be backported to 2.0 and 1.9. It fixes the issue #124. | |||
(cherry picked from commit ea418748dd22331e9798cfd8f5e25b436cd577e3) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/src/hlua.c b/src/hlua.c | |||
index 28abd083..32f0e8db 100644 | |||
--- a/src/hlua.c | |||
+++ b/src/hlua.c | |||
@@ -5375,7 +5375,13 @@ __LJMP static inline int hlua_http_rep_hdr(lua_State *L, struct hlua_txn *htxn, | |||
if (!(re = regex_comp(reg, 1, 1, NULL))) | |||
WILL_LJMP(luaL_argerror(L, 3, "invalid regex")); | |||
- http_transform_header_str(htxn->s, msg, name, name_len, value, re, action); | |||
+ if (IS_HTX_STRM(htxn->s)) { | |||
+ struct htx *htx = htxbuf(&msg->chn->buf); | |||
+ | |||
+ htx_transform_header_str(htxn->s, msg->chn, htx, ist2(name, name_len), value, re, action); | |||
+ } | |||
+ else | |||
+ http_transform_header_str(htxn->s, msg, name, name_len, value, re, action); | |||
regex_free(re); | |||
return 0; | |||
} |
@ -0,0 +1,29 @@ | |||
commit 661bfc3d0e1b7756db59d00d86e316f694cae3c6 | |||
Author: Christopher Faulet <cfaulet@haproxy.com> | |||
Date: Mon Jun 17 14:07:46 2019 +0200 | |||
BUG/MINOR: mux-h1: Add the header connection in lower case in outgoing messages | |||
When necessary, this header is directly added in outgoing messages by the H1 | |||
multiplexer. Because there is no HTX conversion first, the header name is not | |||
converserted to its lower case version. So, it must be added in lower case by | |||
the multiplexer. | |||
This patch must be backported to 2.0 and 1.9. | |||
(cherry picked from commit a110ecbd843e156dd01c6ac581c735be5e240d5b) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/src/mux_h1.c b/src/mux_h1.c | |||
index 317f1a55..21deb354 100644 | |||
--- a/src/mux_h1.c | |||
+++ b/src/mux_h1.c | |||
@@ -1642,7 +1642,7 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun | |||
/* There is no "Connection:" header and | |||
* it the conn_mode must be | |||
* processed. So do it */ | |||
- n = ist("Connection"); | |||
+ n = ist("connection"); | |||
v = ist(""); | |||
h1_process_output_conn_mode(h1s, h1m, &v); | |||
if (v.len) { |
@ -1,71 +0,0 @@ | |||
commit 72fdff1fdb5b82685dc3d2db23ece042195a0cbd | |||
Author: Christopher Faulet <cfaulet@haproxy.com> | |||
Date: Fri Apr 26 14:30:15 2019 +0200 | |||
MINOR: spoe: Use the sample context to pass frag_ctx info during encoding | |||
This simplifies the API and hide the details in the sample. This way, only | |||
string and binary are aware of these info, because other types cannot be | |||
partially encoded. | |||
This patch may be backported to 1.9 and 1.8. | |||
(cherry picked from commit 85db3212b87b33f1a39a688546f4f53a5c4ba4da) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
(cherry picked from commit b93366e3ee44f5de93f01569fcdcd602f6d0703f) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/include/proto/spoe.h b/include/proto/spoe.h | |||
index 2cdca10b..cce13e50 100644 | |||
--- a/include/proto/spoe.h | |||
+++ b/include/proto/spoe.h | |||
@@ -117,11 +117,9 @@ spoe_decode_buffer(char **buf, char *end, char **str, uint64_t *len) | |||
* | |||
* If the value is too big to be encoded, depending on its type, then encoding | |||
* failed or the value is partially encoded. Only strings and binaries can be | |||
- * partially encoded. In this case, the offset <*off> is updated to known how | |||
- * many bytes has been encoded. If <*off> is zero at the end, it means that all | |||
- * data has been encoded. */ | |||
+ * partially encoded. */ | |||
static inline int | |||
-spoe_encode_data(unsigned int *len, struct sample *smp, unsigned int *off, char **buf, char *end) | |||
+spoe_encode_data(struct sample *smp, char **buf, char *end) | |||
{ | |||
char *p = *buf; | |||
int ret; | |||
@@ -164,12 +162,16 @@ spoe_encode_data(unsigned int *len, struct sample *smp, unsigned int *off, char | |||
case SMP_T_STR: | |||
case SMP_T_BIN: { | |||
+ /* If defined, get length and offset of the sample by reading the sample | |||
+ * context. ctx.a[0] is the pointer to the length and ctx.a[1] is the | |||
+ * pointer to the offset. If the offset is greater than 0, it means the | |||
+ * sample is partially encoded. In this case, we only need to encode the | |||
+ * reamining. When all the sample is encoded, the offset is reset to 0. | |||
+ * So the caller know it can try to encode the next sample. */ | |||
struct chunk *chk = &smp->data.u.str; | |||
+ unsigned int *len = (smp->ctx.a[0] ? smp->ctx.a[0] : 0); | |||
+ unsigned int *off = (smp->ctx.a[1] ? smp->ctx.a[1] : 0); | |||
- /* Here, we need to know if the sample has already been | |||
- * partially encoded. If yes, we only need to encode the | |||
- * remaining, <*off> reprensenting the number of bytes | |||
- * already encoded. */ | |||
if (!*off) { | |||
/* First evaluation of the sample : encode the | |||
* type (string or binary), the buffer length | |||
diff --git a/src/flt_spoe.c b/src/flt_spoe.c | |||
index 0c0b3794..66d8b045 100644 | |||
--- a/src/flt_spoe.c | |||
+++ b/src/flt_spoe.c | |||
@@ -2187,7 +2187,9 @@ spoe_encode_message(struct stream *s, struct spoe_context *ctx, | |||
/* Fetch the arguement value */ | |||
smp = sample_process(s->be, s->sess, s, dir|SMP_OPT_FINAL, arg->expr, NULL); | |||
- ret = spoe_encode_data(&ctx->frag_ctx.curlen, smp, &ctx->frag_ctx.curoff, buf, end); | |||
+ smp->ctx.a[0] = &ctx->frag_ctx.curlen; | |||
+ smp->ctx.a[1] = &ctx->frag_ctx.curoff; | |||
+ ret = spoe_encode_data(smp, buf, end); | |||
if (ret == -1 || ctx->frag_ctx.curoff) | |||
goto too_big; | |||
} |
@ -0,0 +1,253 @@ | |||
commit eaf650083924a697cde3379703984c5e7a5ebd41 | |||
Author: Tim Duesterhus <tim@bastelstu.be> | |||
Date: Mon Jun 17 16:10:07 2019 +0200 | |||
BUG/MEDIUM: compression: Set Vary: Accept-Encoding for compressed responses | |||
Make HAProxy set the `Vary: Accept-Encoding` response header if it compressed | |||
the server response. | |||
Technically the `Vary` header SHOULD also be set for responses that would | |||
normally be compressed based off the current configuration, but are not due | |||
to a missing or invalid `Accept-Encoding` request header or due to the | |||
maximum compression rate being exceeded. | |||
Not setting the header in these cases does no real harm, though: An | |||
uncompressed response might be returned by a Cache, even if a compressed | |||
one could be retrieved from HAProxy. This increases the traffic to the end | |||
user if the cache is unable to compress itself, but it saves another | |||
roundtrip to HAProxy. | |||
see the discussion on the mailing list: https://www.mail-archive.com/haproxy@formilux.org/msg34221.html | |||
Message-ID: 20190617121708.GA2964@1wt.eu | |||
A small issue remains: The User-Agent is not added to the `Vary` header, | |||
despite being relevant to the response. Adding the User-Agent header would | |||
make responses effectively uncacheable and it's unlikely to see a Mozilla/4 | |||
in the wild in 2019. | |||
Add a reg-test to ensure the behaviour as described in this commit message. | |||
see issue #121 | |||
Should be backported to all branches with compression (i.e. 1.6+). | |||
(cherry picked from commit 721d686bd10dc6993859f9026ad907753d1d2064) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/reg-tests/compression/vary.vtc b/reg-tests/compression/vary.vtc | |||
new file mode 100644 | |||
index 00000000..0a060e4b | |||
--- /dev/null | |||
+++ b/reg-tests/compression/vary.vtc | |||
@@ -0,0 +1,187 @@ | |||
+varnishtest "Compression sets Vary header" | |||
+ | |||
+#REQUIRE_VERSION=1.9 | |||
+#REQUIRE_OPTION=ZLIB|SLZ | |||
+ | |||
+feature ignore_unknown_macro | |||
+ | |||
+server s1 { | |||
+ rxreq | |||
+ expect req.url == "/plain/accept-encoding-gzip" | |||
+ expect req.http.accept-encoding == "gzip" | |||
+ txresp \ | |||
+ -hdr "Content-Type: text/plain" \ | |||
+ -bodylen 100 | |||
+ | |||
+ rxreq | |||
+ expect req.url == "/plain/accept-encoding-invalid" | |||
+ expect req.http.accept-encoding == "invalid" | |||
+ txresp \ | |||
+ -hdr "Content-Type: text/plain" \ | |||
+ -bodylen 100 | |||
+ | |||
+ rxreq | |||
+ expect req.url == "/plain/accept-encoding-null" | |||
+ expect req.http.accept-encoding == "<undef>" | |||
+ txresp \ | |||
+ -hdr "Content-Type: text/plain" \ | |||
+ -bodylen 100 | |||
+ | |||
+ rxreq | |||
+ expect req.url == "/html/accept-encoding-gzip" | |||
+ expect req.http.accept-encoding == "gzip" | |||
+ txresp \ | |||
+ -hdr "Content-Type: text/html" \ | |||
+ -bodylen 100 | |||
+ | |||
+ rxreq | |||
+ expect req.url == "/html/accept-encoding-invalid" | |||
+ expect req.http.accept-encoding == "invalid" | |||
+ txresp \ | |||
+ -hdr "Content-Type: text/html" \ | |||
+ -bodylen 100 | |||
+ | |||
+ | |||
+ rxreq | |||
+ expect req.url == "/html/accept-encoding-null" | |||
+ expect req.http.accept-encoding == "<undef>" | |||
+ txresp \ | |||
+ -hdr "Content-Type: text/html" \ | |||
+ -bodylen 100 | |||
+ | |||
+ rxreq | |||
+ expect req.url == "/dup-etag/accept-encoding-gzip" | |||
+ expect req.http.accept-encoding == "gzip" | |||
+ txresp \ | |||
+ -hdr "Content-Type: text/plain" \ | |||
+ -hdr "ETag: \"123\"" \ | |||
+ -hdr "ETag: \"123\"" \ | |||
+ -bodylen 100 | |||
+} -repeat 2 -start | |||
+ | |||
+ | |||
+haproxy h1 -conf { | |||
+ defaults | |||
+ mode http | |||
+ ${no-htx} option http-use-htx | |||
+ timeout connect 1s | |||
+ timeout client 1s | |||
+ timeout server 1s | |||
+ | |||
+ frontend fe-gzip | |||
+ bind "fd@${fe_gzip}" | |||
+ default_backend be-gzip | |||
+ | |||
+ backend be-gzip | |||
+ compression algo gzip | |||
+ compression type text/plain | |||
+ server www ${s1_addr}:${s1_port} | |||
+ | |||
+ frontend fe-nothing | |||
+ bind "fd@${fe_nothing}" | |||
+ default_backend be-nothing | |||
+ | |||
+ backend be-nothing | |||
+ server www ${s1_addr}:${s1_port} | |||
+} -start | |||
+ | |||
+client c1 -connect ${h1_fe_gzip_sock} { | |||
+ txreq -url "/plain/accept-encoding-gzip" \ | |||
+ -hdr "Accept-Encoding: gzip" | |||
+ rxresp | |||
+ expect resp.status == 200 | |||
+ expect resp.http.content-encoding == "gzip" | |||
+ expect resp.http.vary == "Accept-Encoding" | |||
+ gunzip | |||
+ expect resp.bodylen == 100 | |||
+ | |||
+ txreq -url "/plain/accept-encoding-invalid" \ | |||
+ -hdr "Accept-Encoding: invalid" | |||
+ rxresp | |||
+ expect resp.status == 200 | |||
+ expect resp.http.vary == "<undef>" | |||
+ expect resp.bodylen == 100 | |||
+ | |||
+ txreq -url "/plain/accept-encoding-null" | |||
+ rxresp | |||
+ expect resp.status == 200 | |||
+ expect resp.http.vary == "<undef>" | |||
+ expect resp.bodylen == 100 | |||
+ | |||
+ txreq -url "/html/accept-encoding-gzip" \ | |||
+ -hdr "Accept-Encoding: gzip" | |||
+ rxresp | |||
+ expect resp.status == 200 | |||
+ expect resp.http.vary == "<undef>" | |||
+ expect resp.bodylen == 100 | |||
+ | |||
+ txreq -url "/html/accept-encoding-invalid" \ | |||
+ -hdr "Accept-Encoding: invalid" | |||
+ rxresp | |||
+ expect resp.status == 200 | |||
+ expect resp.http.vary == "<undef>" | |||
+ expect resp.bodylen == 100 | |||
+ | |||
+ txreq -url "/html/accept-encoding-null" | |||
+ rxresp | |||
+ expect resp.status == 200 | |||
+ expect resp.http.vary == "<undef>" | |||
+ expect resp.bodylen == 100 | |||
+ | |||
+ txreq -url "/dup-etag/accept-encoding-gzip" \ | |||
+ -hdr "Accept-Encoding: gzip" | |||
+ rxresp | |||
+ expect resp.status == 200 | |||
+ expect resp.http.vary == "<undef>" | |||
+ expect resp.bodylen == 100 | |||
+} -run | |||
+ | |||
+# This Client duplicates c1, against the "nothing" frontend, ensuring no Vary header is ever set. | |||
+client c2 -connect ${h1_fe_nothing_sock} { | |||
+ txreq -url "/plain/accept-encoding-gzip" \ | |||
+ -hdr "Accept-Encoding: gzip" | |||
+ rxresp | |||
+ expect resp.status == 200 | |||
+ expect resp.http.vary == "<undef>" | |||
+ expect resp.bodylen == 100 | |||
+ | |||
+ txreq -url "/plain/accept-encoding-invalid" \ | |||
+ -hdr "Accept-Encoding: invalid" | |||
+ rxresp | |||
+ expect resp.status == 200 | |||
+ expect resp.http.vary == "<undef>" | |||
+ expect resp.bodylen == 100 | |||
+ | |||
+ txreq -url "/plain/accept-encoding-null" | |||
+ rxresp | |||
+ expect resp.status == 200 | |||
+ expect resp.http.vary == "<undef>" | |||
+ expect resp.bodylen == 100 | |||
+ | |||
+ txreq -url "/html/accept-encoding-gzip" \ | |||
+ -hdr "Accept-Encoding: gzip" | |||
+ rxresp | |||
+ expect resp.status == 200 | |||
+ expect resp.http.vary == "<undef>" | |||
+ expect resp.bodylen == 100 | |||
+ | |||
+ txreq -url "/html/accept-encoding-invalid" \ | |||
+ -hdr "Accept-Encoding: invalid" | |||
+ rxresp | |||
+ expect resp.status == 200 | |||
+ expect resp.http.vary == "<undef>" | |||
+ expect resp.bodylen == 100 | |||
+ | |||
+ txreq -url "/html/accept-encoding-null" | |||
+ rxresp | |||
+ expect resp.status == 200 | |||
+ expect resp.http.vary == "<undef>" | |||
+ expect resp.bodylen == 100 | |||
+ | |||
+ txreq -url "/dup-etag/accept-encoding-gzip" \ | |||
+ -hdr "Accept-Encoding: gzip" | |||
+ rxresp | |||
+ expect resp.status == 200 | |||
+ expect resp.http.vary == "<undef>" | |||
+ expect resp.bodylen == 100 | |||
+} -run | |||
diff --git a/src/flt_http_comp.c b/src/flt_http_comp.c | |||
index b04dcd14..37f237fe 100644 | |||
--- a/src/flt_http_comp.c | |||
+++ b/src/flt_http_comp.c | |||
@@ -523,6 +523,9 @@ http_set_comp_reshdr(struct comp_state *st, struct stream *s, struct http_msg *m | |||
} | |||
} | |||
+ if (http_header_add_tail2(msg, &txn->hdr_idx, "Vary: Accept-Encoding", 21) < 0) | |||
+ goto error; | |||
+ | |||
return 1; | |||
error: | |||
@@ -577,6 +580,9 @@ htx_set_comp_reshdr(struct comp_state *st, struct stream *s, struct http_msg *ms | |||
} | |||
} | |||
+ if (!http_add_header(htx, ist("Vary"), ist("Accept-Encoding"))) | |||
+ goto error; | |||
+ | |||
return 1; | |||
error: |
@ -1,38 +0,0 @@ | |||
commit dfc3718f0a302ea3deb5f1a325d35fce0e4cfa48 | |||
Author: Yann Cézard <ycezard@viareport.com> | |||
Date: Thu Apr 25 14:48:38 2019 +0200 | |||
DOC: contrib/modsecurity: Typos and fix the reject example | |||
Thanks to https://www.mail-archive.com/haproxy@formilux.org/msg30056.html | |||
This patch may be backported to 1.9 and 1.8. | |||
(cherry picked from commit 494ddbff478d880e48de490f2689607addac70bc) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
(cherry picked from commit 850896603086877641272d6e4075e66bd91f2e50) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/contrib/modsecurity/README b/contrib/modsecurity/README | |||
index e6cb305e..8031389d 100644 | |||
--- a/contrib/modsecurity/README | |||
+++ b/contrib/modsecurity/README | |||
@@ -88,15 +88,15 @@ HAProxy configuration. For example: | |||
balance roundrobin | |||
timeout connect 5s | |||
timeout server 3m | |||
- server iprep1 127.0.0.1:12345 | |||
+ server modsec1 127.0.0.1:12345 | |||
The modsecurity action is returned in a variable called txn.modsec.code. It | |||
contains the HTTP returned code. If the variable contains 0, the request is | |||
clean. | |||
- tcp-request content reject if { var(txn.modsec.code) -m int gt 0 } | |||
+ http-request deny if { var(txn.modsec.code) -m int gt 0 } | |||
-With this rule, all the request not clean are reected. | |||
+With this rule, all the request not clean are rejected. | |||
Known bugs, limitations and TODO list |
@ -1,36 +0,0 @@ | |||
commit 95cf225d099dcb49eefcf4f5b648be604414ae0c | |||
Author: Yann Cézard <ycezard@viareport.com> | |||
Date: Thu Apr 25 14:30:23 2019 +0200 | |||
BUG/MEDIUM: contrib/modsecurity: If host header is NULL, don't try to strdup it | |||
I discovered this bug when running OWASP regression tests against HAProxy + | |||
modsecurity-spoa (it's a POC to evaluate how it is working). I found out that | |||
modsecurity spoa will crash when the request doesn't have any Host header. | |||
See the pull request #86 on github for details. | |||
This patch must be backported to 1.9 and 1.8. | |||
(cherry picked from commit bf60f6b8033deddc86de5357d6099c7593fe44cc) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
(cherry picked from commit d988e3dddcbe1f48f3b24d1bb529fc9ecefde180) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/contrib/modsecurity/modsec_wrapper.c b/contrib/modsecurity/modsec_wrapper.c | |||
index 271ec15d..2f3987b4 100644 | |||
--- a/contrib/modsecurity/modsec_wrapper.c | |||
+++ b/contrib/modsecurity/modsec_wrapper.c | |||
@@ -325,7 +325,11 @@ int modsecurity_process(struct worker *worker, struct modsecurity_parameters *pa | |||
req->content_type = apr_table_get(req->headers_in, "Content-Type"); | |||
req->content_encoding = apr_table_get(req->headers_in, "Content-Encoding"); | |||
req->hostname = apr_table_get(req->headers_in, "Host"); | |||
- req->parsed_uri.hostname = chunk_strdup(req, req->hostname, strlen(req->hostname)); | |||
+ if (req->hostname != NULL) { | |||
+ req->parsed_uri.hostname = chunk_strdup(req, req->hostname, strlen(req->hostname)); | |||
+ } else { | |||
+ req->parsed_uri.hostname = NULL; | |||
+ } | |||
lang = apr_table_get(req->headers_in, "Content-Languages"); | |||
if (lang != NULL) { |
@ -0,0 +1,66 @@ | |||
commit 8d09dc21dc913d1540d07d1019a51c430317eae1 | |||
Author: Christopher Faulet <cfaulet@haproxy.com> | |||
Date: Tue Jun 18 09:37:00 2019 +0200 | |||
MINOR: htx: Add the function htx_change_blk_value_len() | |||
As its name suggest, this function change the value length of a block. But it | |||
also update the HTX message accordingly. It simplifies the HTX API. The function | |||
htx_set_blk_value_len() is still available and must be used with caution because | |||
this one does not update the HTX message. It just updates the HTX block. It | |||
should be considered as an internal function. When possible, | |||
htx_change_blk_value_len() should be used instead. | |||
This function is used to fix a bug affecting the 2.0. So, this patch must be | |||
backported to 2.0. | |||
(cherry picked from commit bb0efcdd293de33607a6eba075971b49857375e2) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/include/common/htx.h b/include/common/htx.h | |||
index 9631c618..7d15365a 100644 | |||
--- a/include/common/htx.h | |||
+++ b/include/common/htx.h | |||
@@ -470,7 +470,41 @@ static inline struct htx_blk *htx_get_next_blk(const struct htx *htx, | |||
} | |||
/* Changes the size of the value. It is the caller responsibility to change the | |||
- * value itself, make sure there is enough space and update allocated value. | |||
+ * value itself, make sure there is enough space and update allocated | |||
+ * value. This function updates the HTX message accordingly. | |||
+ */ | |||
+static inline void htx_change_blk_value_len(struct htx *htx, struct htx_blk *blk, uint32_t newlen) | |||
+{ | |||
+ enum htx_blk_type type = htx_get_blk_type(blk); | |||
+ uint32_t oldlen, sz; | |||
+ int32_t delta; | |||
+ | |||
+ sz = htx_get_blksz(blk); | |||
+ switch (type) { | |||
+ case HTX_BLK_HDR: | |||
+ case HTX_BLK_TLR: | |||
+ oldlen = (blk->info >> 8) & 0xfffff; | |||
+ blk->info = (type << 28) + (newlen << 8) + (blk->info & 0xff); | |||
+ break; | |||
+ default: | |||
+ oldlen = blk->info & 0xfffffff; | |||
+ blk->info = (type << 28) + newlen; | |||
+ break; | |||
+ } | |||
+ | |||
+ /* Update HTTP message */ | |||
+ delta = (newlen - oldlen); | |||
+ htx->data += delta; | |||
+ if (blk->addr+sz == htx->tail_addr) | |||
+ htx->tail_addr += delta; | |||
+ else if (blk->addr+sz == htx->head_addr) | |||
+ htx->head_addr += delta; | |||
+} | |||
+ | |||
+/* Changes the size of the value. It is the caller responsibility to change the | |||
+ * value itself, make sure there is enough space and update allocated | |||
+ * value. Unlike the function htx_change_blk_value_len(), this one does not | |||
+ * update the HTX message. So it should be used with caution. | |||
*/ | |||
static inline void htx_set_blk_value_len(struct htx_blk *blk, uint32_t vlen) | |||
{ |
@ -0,0 +1,133 @@ | |||
commit 41dc8432f87622145390dc1b1467a5ee14ba184c | |||
Author: Christopher Faulet <cfaulet@haproxy.com> | |||
Date: Tue Jun 18 09:49:16 2019 +0200 | |||
BUG/MEDIUM: htx: Fully update HTX message when the block value is changed | |||
Everywhere the value length of a block is changed, calling the function | |||
htx_set_blk_value_len(), the HTX message must be updated. But at many places, | |||
because of the recent changes in the HTX structure, this update was only | |||
partially done. tail_addr and head_addr values were not systematically updated. | |||
In fact, the function htx_set_blk_value_len() was designed as an internal | |||
function to the HTX API. And we used it from outside by convenience. But it is | |||
really painfull and error prone to let the caller update the HTX message. So | |||
now, we use the function htx_change_blk_value_len() wherever is possible. It | |||
changes the value length of a block and updates the HTX message accordingly. | |||
This patch must be backported to 2.0. | |||
(cherry picked from commit 3e2638ee04407a1d9e9376f0c518f67fca7deaa4) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/src/h2.c b/src/h2.c | |||
index 32c1ef16..990d602b 100644 | |||
--- a/src/h2.c | |||
+++ b/src/h2.c | |||
@@ -736,8 +736,7 @@ int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *ms | |||
if (tl > fs) | |||
goto fail; | |||
- htx_set_blk_value_len(blk, tl); | |||
- htx->data += vl+2; | |||
+ htx_change_blk_value_len(htx, blk, tl); | |||
*(char *)(htx_get_blk_ptr(htx, blk) + bs + 0) = ';'; | |||
*(char *)(htx_get_blk_ptr(htx, blk) + bs + 1) = ' '; | |||
memcpy(htx_get_blk_ptr(htx, blk) + bs + 2, list[ck].v.ptr, vl); | |||
diff --git a/src/http_htx.c b/src/http_htx.c | |||
index 7322b337..bc26e5ba 100644 | |||
--- a/src/http_htx.c | |||
+++ b/src/http_htx.c | |||
@@ -461,10 +461,7 @@ int http_remove_header(struct htx *htx, struct http_hdr_ctx *ctx) | |||
} | |||
/* Update the block content and its len */ | |||
memmove(start, start+len, v.len-len); | |||
- htx_set_blk_value_len(blk, v.len-len); | |||
- | |||
- /* Update HTX msg */ | |||
- htx->data -= len; | |||
+ htx_change_blk_value_len(htx, blk, v.len-len); | |||
/* Finally update the ctx */ | |||
ctx->value.ptr = start; | |||
diff --git a/src/htx.c b/src/htx.c | |||
index bfd136f4..81492598 100644 | |||
--- a/src/htx.c | |||
+++ b/src/htx.c | |||
@@ -406,15 +406,8 @@ void htx_truncate(struct htx *htx, uint32_t offset) | |||
offset -= sz; | |||
continue; | |||
} | |||
- if (type == HTX_BLK_DATA) { | |||
- htx_set_blk_value_len(blk, offset); | |||
- htx->data -= (sz - offset); | |||
- | |||
- if (blk->addr+sz == htx->tail_addr) | |||
- htx->tail_addr -= offset; | |||
- else if (blk->addr+sz == htx->head_addr) | |||
- htx->head_addr -= offset; | |||
- } | |||
+ if (type == HTX_BLK_DATA) | |||
+ htx_change_blk_value_len(htx, blk, offset); | |||
offset = 0; | |||
} | |||
while (blk) | |||
@@ -522,14 +515,7 @@ struct htx_blk *htx_add_data_atonce(struct htx *htx, struct ist data) | |||
/* Append data and update the block itself */ | |||
ptr = htx_get_blk_ptr(htx, tailblk); | |||
memcpy(ptr+sz, data.ptr, len); | |||
- htx_set_blk_value_len(tailblk, sz+len); | |||
- | |||
- /* Update HTTP message */ | |||
- htx->data += len; | |||
- if (tailblk->addr+sz == htx->tail_addr) | |||
- htx->tail_addr += len; | |||
- else if (tailblk->addr+sz == htx->head_addr) | |||
- htx->head_addr += len; | |||
+ htx_change_blk_value_len(htx, tailblk, sz+len); | |||
if (data.len == len) { | |||
blk = tailblk; | |||
@@ -988,14 +974,7 @@ size_t htx_add_data(struct htx *htx, const struct ist data) | |||
/* Append data and update the block itself */ | |||
ptr = htx_get_blk_ptr(htx, tailblk); | |||
memcpy(ptr + sz, data.ptr, len); | |||
- htx_set_blk_value_len(tailblk, sz + len); | |||
- | |||
- /* Update HTTP message */ | |||
- htx->data += len; | |||
- if (tailblk->addr+sz == htx->tail_addr) | |||
- htx->tail_addr += len; | |||
- else if (tailblk->addr+sz == htx->head_addr) | |||
- htx->head_addr += len; | |||
+ htx_change_blk_value_len(htx, tailblk, sz+len); | |||
BUG_ON((int32_t)htx->tail_addr < 0); | |||
BUG_ON((int32_t)htx->head_addr < 0); | |||
diff --git a/src/proto_htx.c b/src/proto_htx.c | |||
index 7f501366..d821e38c 100644 | |||
--- a/src/proto_htx.c | |||
+++ b/src/proto_htx.c | |||
@@ -4314,10 +4314,8 @@ static void htx_manage_client_side_cookies(struct stream *s, struct channel *req | |||
hdr_end = (preserve_hdr ? del_from : hdr_beg); | |||
} | |||
if ((hdr_end - hdr_beg) != ctx.value.len) { | |||
- if (hdr_beg != hdr_end) { | |||
- htx_set_blk_value_len(ctx.blk, hdr_end - hdr_beg); | |||
- htx->data -= ctx.value.len - (hdr_end - hdr_beg); | |||
- } | |||
+ if (hdr_beg != hdr_end) | |||
+ htx_change_blk_value_len(htx, ctx.blk, hdr_end - hdr_beg); | |||
else | |||
http_remove_header(htx, &ctx); | |||
} | |||
@@ -4495,8 +4493,7 @@ static void htx_manage_server_side_cookies(struct stream *s, struct channel *res | |||
next += stripped_before; | |||
hdr_end += stripped_before; | |||
- htx_set_blk_value_len(ctx.blk, hdr_end - hdr_beg); | |||
- htx->data -= ctx.value.len - (hdr_end - hdr_beg); | |||
+ htx_change_blk_value_len(htx, ctx.blk, hdr_end - hdr_beg); | |||
ctx.value.len = hdr_end - hdr_beg; | |||
} | |||
@ -1,23 +0,0 @@ | |||
commit 86860896dc1841eb59cb95832d76a8093e8dc8c5 | |||
Author: Christopher Faulet <cfaulet@haproxy.com> | |||
Date: Tue Apr 30 10:55:38 2019 +0200 | |||
MINOR: examples: Use right locale for the last changelog date in haproxy.spec | |||
The last changelog entry was stamped with the wrong locale. | |||
No need to backport, it is specific to 1.8 | |||
diff --git a/examples/haproxy.spec b/examples/haproxy.spec | |||
index f3e0c7e0..fe5215d7 100644 | |||
--- a/examples/haproxy.spec | |||
+++ b/examples/haproxy.spec | |||
@@ -74,7 +74,7 @@ fi | |||
%attr(0755,root,root) %config %{_sysconfdir}/rc.d/init.d/%{name} | |||
%changelog | |||
-* jeu. avril 25 2019 Christopher Faulet <cfaulet@haproxy.com> | |||
+* Thu Apr 25 2019 Christopher Faulet <cfaulet@haproxy.com> | |||
- updated to 1.8.20 | |||
* Mon Feb 11 2019 Willy Tarreau <w@1wt.eu> |
@ -1,67 +0,0 @@ | |||
commit 83af1f6b65806982640679823228976deebf5202 | |||
Author: Willy Tarreau <w@1wt.eu> | |||
Date: Tue Apr 30 11:43:43 2019 +0200 | |||
BUG/MAJOR: map/acl: real fix segfault during show map/acl on CLI | |||
A previous commit 8d85aa44d ("BUG/MAJOR: map: fix segfault during | |||
'show map/acl' on cli.") was provided to address a concurrency issue | |||
between "show acl" and "clear acl" on the CLI. Sadly the code placed | |||
there was copy-pasted without changing the element type (which was | |||
struct stream in the original code) and not tested since the crash | |||
is still present. | |||
The reproducer is simple : load a large ACL file (e.g. geolocation | |||
addresses), issue "show acl #0" in loops in one window and issue a | |||
"clear acl #0" in the other one, haproxy crashes. | |||
This fix was also tested with threads enabled and looks good since | |||
the locking seems to work correctly in these areas though. It will | |||
have to be backported as far as 1.6 since the commit above went | |||
that far as well... | |||
(cherry picked from commit 49ee3b2f9a9e5d0b8d394938df527aa645ce72b4) | |||
Signed-off-by: Willy Tarreau <w@1wt.eu> | |||
(cherry picked from commit ac4be10f62ef72962d9cf0e6f2619e1e1c370d62) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/src/pattern.c b/src/pattern.c | |||
index 7eea9d96..21639569 100644 | |||
--- a/src/pattern.c | |||
+++ b/src/pattern.c | |||
@@ -1651,7 +1651,7 @@ int pat_ref_delete_by_id(struct pat_ref *ref, struct pat_ref_elt *refelt) | |||
LIST_DEL(&bref->users); | |||
LIST_INIT(&bref->users); | |||
if (elt->list.n != &ref->head) | |||
- LIST_ADDQ(&LIST_ELEM(elt->list.n, struct stream *, list)->back_refs, &bref->users); | |||
+ LIST_ADDQ(&LIST_ELEM(elt->list.n, typeof(elt), list)->back_refs, &bref->users); | |||
bref->ref = elt->list.n; | |||
} | |||
list_for_each_entry(expr, &ref->pat, list) | |||
@@ -1691,7 +1691,7 @@ int pat_ref_delete(struct pat_ref *ref, const char *key) | |||
LIST_DEL(&bref->users); | |||
LIST_INIT(&bref->users); | |||
if (elt->list.n != &ref->head) | |||
- LIST_ADDQ(&LIST_ELEM(elt->list.n, struct stream *, list)->back_refs, &bref->users); | |||
+ LIST_ADDQ(&LIST_ELEM(elt->list.n, typeof(elt), list)->back_refs, &bref->users); | |||
bref->ref = elt->list.n; | |||
} | |||
list_for_each_entry(expr, &ref->pat, list) | |||
@@ -2086,7 +2086,7 @@ void pat_ref_reload(struct pat_ref *ref, struct pat_ref *replace) | |||
LIST_DEL(&bref->users); | |||
LIST_INIT(&bref->users); | |||
if (elt->list.n != &ref->head) | |||
- LIST_ADDQ(&LIST_ELEM(elt->list.n, struct stream *, list)->back_refs, &bref->users); | |||
+ LIST_ADDQ(&LIST_ELEM(elt->list.n, typeof(elt), list)->back_refs, &bref->users); | |||
bref->ref = elt->list.n; | |||
} | |||
LIST_DEL(&elt->list); | |||
@@ -2175,7 +2175,7 @@ void pat_ref_prune(struct pat_ref *ref) | |||
LIST_DEL(&bref->users); | |||
LIST_INIT(&bref->users); | |||
if (elt->list.n != &ref->head) | |||
- LIST_ADDQ(&LIST_ELEM(elt->list.n, struct stream *, list)->back_refs, &bref->users); | |||
+ LIST_ADDQ(&LIST_ELEM(elt->list.n, typeof(elt), list)->back_refs, &bref->users); | |||
bref->ref = elt->list.n; | |||
} | |||
LIST_DEL(&elt->list); |
@ -0,0 +1,27 @@ | |||
commit 3d574a587dc3704e93bcd29b16d54d96069667b4 | |||
Author: Christopher Faulet <cfaulet@haproxy.com> | |||
Date: Tue Jun 18 12:22:38 2019 +0200 | |||
BUG/MEDIUM: mux-h2: Reset padlen when several frames are demux | |||
In the function h2_process_demux(), if several frames are parsed, the padding | |||
length must be reset between each frame. Otherwise we may wrongly think a frame | |||
has a padding block because the previous one was padded. | |||
This patch must be backported to 2.0 and 1.9. | |||
(cherry picked from commit dd2a5620d594523cd515a629e105a9a2b64345bb) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/src/mux_h2.c b/src/mux_h2.c | |||
index d02168df..c06d5d68 100644 | |||
--- a/src/mux_h2.c | |||
+++ b/src/mux_h2.c | |||
@@ -2316,6 +2316,7 @@ static void h2_process_demux(struct h2c *h2c) | |||
break; | |||
} | |||
+ padlen = 0; | |||
if (h2_ft_bit(hdr.ft) & H2_FT_PADDED_MASK && hdr.ff & H2_F_PADDED) { | |||
/* If the frame is padded (HEADERS, PUSH_PROMISE or DATA), | |||
* we read the pad length and drop it from the remaining |
@ -1,79 +0,0 @@ | |||
commit 7bd7a8d2b8889f604b807c21190d2e70328d6674 | |||
Author: Christopher Faulet <cfaulet@haproxy.com> | |||
Date: Tue Apr 30 12:17:13 2019 +0200 | |||
BUG/MEDIUM: listener: Fix how unlimited number of consecutive accepts is handled | |||
There is a bug when global.tune.maxaccept is set to -1 (no limit). It is pretty | |||
visible with one process (nbproc sets to 1). The functions listener_accept() and | |||
accept_queue_process() don't expect to handle negative maxaccept values. So | |||
instead of accepting incoming connections without any limit, none are never | |||
accepted and HAProxy loop infinitly in the scheduler. | |||
When there are 2 or more processes, the bug is a bit more subtile. The limit for | |||
a listener is set to 1. So only one connection is accepted at a time by a given | |||
listener. This happens because the listener's maxaccept value is an unsigned | |||
integer. In check_config_validity(), it is first set to UINT_MAX (-1 casted in | |||
an unsigned integer), and then some calculations on it leads to an integer | |||
overflow. | |||
To fix the bug, the listener's maxaccept value is now a signed integer. So, if a | |||
negative value is set for global.tune.maxaccept, we keep it untouched for the | |||
listener and no calculation is made on it. Then, in the listener code, this | |||
signed value is casted to a unsigned one. It simplifies all tests instead of | |||
dealing with negative values. So, it limits the number of connections accepted | |||
at a time to UINT_MAX at most. But, honestly, it not an issue. | |||
This patch must be backported to 1.9 and 1.8. | |||
(cherry picked from commit 102854cbbaa4d22466dddec9035d411db244082f) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
(cherry picked from commit bca4fb2d9d7f2966d9f8270fa1796fdc0dfc866d) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/include/types/listener.h b/include/types/listener.h | |||
index ea2eadb5..16ef6d7a 100644 | |||
--- a/include/types/listener.h | |||
+++ b/include/types/listener.h | |||
@@ -196,7 +196,7 @@ struct listener { | |||
int nbconn; /* current number of connections on this listener */ | |||
int maxconn; /* maximum connections allowed on this listener */ | |||
unsigned int backlog; /* if set, listen backlog */ | |||
- unsigned int maxaccept; /* if set, max number of connections accepted at once */ | |||
+ int maxaccept; /* if set, max number of connections accepted at once (-1 when disabled) */ | |||
struct list proto_list; /* list in the protocol header */ | |||
int (*accept)(struct listener *l, int fd, struct sockaddr_storage *addr); /* upper layer's accept() */ | |||
enum obj_type *default_target; /* default target to use for accepted sessions or NULL */ | |||
diff --git a/src/listener.c b/src/listener.c | |||
index 821c931a..74990c45 100644 | |||
--- a/src/listener.c | |||
+++ b/src/listener.c | |||
@@ -406,7 +406,7 @@ void listener_accept(int fd) | |||
{ | |||
struct listener *l = fdtab[fd].owner; | |||
struct proxy *p; | |||
- int max_accept; | |||
+ unsigned int max_accept; | |||
int next_conn = 0; | |||
int next_feconn = 0; | |||
int next_actconn = 0; | |||
@@ -420,6 +420,10 @@ void listener_accept(int fd) | |||
if (!l) | |||
return; | |||
p = l->bind_conf->frontend; | |||
+ | |||
+ /* if l->maxaccept is -1, then max_accept is UINT_MAX. It is not really | |||
+ * illimited, but it is probably enough. | |||
+ */ | |||
max_accept = l->maxaccept ? l->maxaccept : 1; | |||
if (!(l->options & LI_O_UNLIMITED) && global.sps_lim) { | |||
@@ -480,7 +484,7 @@ void listener_accept(int fd) | |||
* worst case. If we fail due to system limits or temporary resource | |||
* shortage, we try again 100ms later in the worst case. | |||
*/ | |||
- for (; max_accept-- > 0; next_conn = next_feconn = next_actconn = 0) { | |||
+ for (; max_accept; next_conn = next_feconn = next_actconn = 0, max_accept--) { | |||
struct sockaddr_storage addr; | |||
socklen_t laddr = sizeof(addr); | |||
unsigned int count; |
@ -0,0 +1,30 @@ | |||
commit 4fb65f421b4d650711e5d8b9dbcbf4bf589d26cc | |||
Author: Christopher Faulet <cfaulet@haproxy.com> | |||
Date: Wed Jun 19 09:25:58 2019 +0200 | |||
BUG/MEDIUM: mux-h2: Remove the padding length when a DATA frame size is checked | |||
When a DATA frame is processed for a message with a content-length, we first | |||
take care to not have a frame size that exceeds the remaining to | |||
read. Otherwise, an error is triggered. But we must remove the padding length | |||
from the frame size because the padding is not included in the announced | |||
content-length. | |||
This patch must be backported to 2.0 and 1.9. | |||
(cherry picked from commit 4f09ec812adbd9336cddc054660a7fb5cd54b459) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/src/mux_h2.c b/src/mux_h2.c | |||
index c06d5d68..5bb85181 100644 | |||
--- a/src/mux_h2.c | |||
+++ b/src/mux_h2.c | |||
@@ -2177,7 +2177,7 @@ static int h2c_frt_handle_data(struct h2c *h2c, struct h2s *h2s) | |||
goto strm_err; | |||
} | |||
- if ((h2s->flags & H2_SF_DATA_CLEN) && h2c->dfl > h2s->body_len) { | |||
+ if ((h2s->flags & H2_SF_DATA_CLEN) && (h2c->dfl - h2c->dpl) > h2s->body_len) { | |||
/* RFC7540#8.1.2 */ | |||
error = H2_ERR_PROTOCOL_ERROR; | |||
goto strm_err; |
@ -0,0 +1,39 @@ | |||
commit 3f0b1de623d09f8668db34c1be696f7245de7d50 | |||
Author: Christopher Faulet <cfaulet@haproxy.com> | |||
Date: Wed Jun 19 10:50:38 2019 +0200 | |||
BUG/MEDIUM: lb_fwlc: Don't test the server's lb_tree from outside the lock | |||
In the function fwlc_srv_reposition(), the server's lb_tree is tested from | |||
outside the lock. So it is possible to remove it after the test and then call | |||
eb32_insert() in fwlc_queue_srv() with a NULL root pointer, which is | |||
invalid. Moving the test in the scope of the lock fixes the bug. | |||
This issue was reported on Github, issue #126. | |||
This patch must be backported to 2.0, 1.9 and 1.8. | |||
(cherry picked from commit 1ae2a8878170ded922f2c4d32b6704af7689bbfd) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/src/lb_fwlc.c b/src/lb_fwlc.c | |||
index 174dc67e..5fa81739 100644 | |||
--- a/src/lb_fwlc.c | |||
+++ b/src/lb_fwlc.c | |||
@@ -66,12 +66,11 @@ static inline void fwlc_queue_srv(struct server *s) | |||
*/ | |||
static void fwlc_srv_reposition(struct server *s) | |||
{ | |||
- if (!s->lb_tree) | |||
- return; | |||
- | |||
HA_SPIN_LOCK(LBPRM_LOCK, &s->proxy->lbprm.lock); | |||
- fwlc_dequeue_srv(s); | |||
- fwlc_queue_srv(s); | |||
+ if (s->lb_tree) { | |||
+ fwlc_dequeue_srv(s); | |||
+ fwlc_queue_srv(s); | |||
+ } | |||
HA_SPIN_UNLOCK(LBPRM_LOCK, &s->proxy->lbprm.lock); | |||
} | |||
@ -1,44 +0,0 @@ | |||
commit 6e580b6e744011e87c337ebe2c082acfd5ca835a | |||
Author: Christopher Faulet <cfaulet@haproxy.com> | |||
Date: Tue Apr 30 14:03:56 2019 +0200 | |||
MINOR: config: Test validity of tune.maxaccept during the config parsing | |||
Only -1 and positive integers from 0 to INT_MAX are accepted. An error is | |||
triggered during the config parsing for any other values. | |||
This patch may be backported to all supported versions. | |||
(cherry picked from commit 6b02ab87348090efec73b1dd24f414239669f279) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
(cherry picked from commit 2bbc40f8bc9a52ba0d03b25270ac0129cca29bba) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/src/cfgparse.c b/src/cfgparse.c | |||
index c178538b..8e325416 100644 | |||
--- a/src/cfgparse.c | |||
+++ b/src/cfgparse.c | |||
@@ -789,6 +789,8 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm) | |||
global.tune.maxpollevents = atol(args[1]); | |||
} | |||
else if (!strcmp(args[0], "tune.maxaccept")) { | |||
+ long max; | |||
+ | |||
if (alertif_too_many_args(1, file, linenum, args, &err_code)) | |||
goto out; | |||
if (global.tune.maxaccept != 0) { | |||
@@ -801,7 +803,13 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm) | |||
err_code |= ERR_ALERT | ERR_FATAL; | |||
goto out; | |||
} | |||
- global.tune.maxaccept = atol(args[1]); | |||
+ max = atol(args[1]); | |||
+ if (/*max < -1 || */max > INT_MAX) { | |||
+ ha_alert("parsing [%s:%d] : '%s' expects -1 or an integer from 0 to INT_MAX.\n", file, linenum, args[0]); | |||
+ err_code |= ERR_ALERT | ERR_FATAL; | |||
+ goto out; | |||
+ } | |||
+ global.tune.maxaccept = max; | |||
} | |||
else if (!strcmp(args[0], "tune.chksize")) { | |||
if (alertif_too_many_args(1, file, linenum, args, &err_code)) |
@ -1,34 +0,0 @@ | |||
commit c6e03c1495fa51f9a98ed0bbe3230313c7c7201c | |||
Author: Christopher Faulet <cfaulet@haproxy.com> | |||
Date: Tue Apr 30 14:08:41 2019 +0200 | |||
CLEANUP: config: Don't alter listener->maxaccept when nbproc is set to 1 | |||
This patch only removes a useless calculation on listener->maxaccept when nbproc | |||
is set to 1. Indeed, the following formula has no effet in such case: | |||
listener->maxaccept = (listener->maxaccept + nbproc - 1) / nbproc; | |||
This patch may be backported as far as 1.5. | |||
(cherry picked from commit 02f3cf19ed803d20aff9294ce7cb732489951ff5) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
(cherry picked from commit 14203e3cf9404e57de5e274b453f0fe4f2174924) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/src/cfgparse.c b/src/cfgparse.c | |||
index 8e325416..3f6ea352 100644 | |||
--- a/src/cfgparse.c | |||
+++ b/src/cfgparse.c | |||
@@ -9018,9 +9018,8 @@ out_uri_auth_compat: | |||
* is bound to. Rememeber that maxaccept = -1 must be kept as it is | |||
* used to disable the limit. | |||
*/ | |||
- if (listener->maxaccept > 0) { | |||
- if (nbproc > 1) | |||
- listener->maxaccept = (listener->maxaccept + 1) / 2; | |||
+ if (listener->maxaccept > 0 && nbproc > 1) { | |||
+ listener->maxaccept = (listener->maxaccept + 1) / 2; | |||
listener->maxaccept = (listener->maxaccept + nbproc - 1) / nbproc; | |||
} | |||
@ -1,54 +0,0 @@ | |||
commit f95cf6ad70565ee2322cf23bc519b7bb0b3831b2 | |||
Author: Olivier Houchard <ohouchard@haproxy.com> | |||
Date: Tue Apr 30 13:38:02 2019 +0200 | |||
MINOR: threads: Implement HA_ATOMIC_LOAD(). | |||
The same way we have HA_ATOMIC_STORE(), implement HA_ATOMIC_LOAD(). | |||
This should be backported to 1.8 and 1.9, as we need it for a bug fix | |||
in port ranges. | |||
(cherry picked from commit 9ce62b5498b27fbf4217d9c25779d5b2ceca23f2) | |||
Signed-off-by: Olivier Houchard <cognet@ci0.org> | |||
(cherry picked from commit 358c979611370fa2bc3b8e47ed50a325cf9126cf) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/include/common/hathreads.h b/include/common/hathreads.h | |||
index 8134839a..11d7cab6 100644 | |||
--- a/include/common/hathreads.h | |||
+++ b/include/common/hathreads.h | |||
@@ -62,6 +62,7 @@ enum { tid = 0 }; | |||
*(val) = new; \ | |||
__old_xchg; \ | |||
}) | |||
+#define HA_ATOMIC_LOAD(val) *(val) | |||
#define HA_ATOMIC_STORE(val, new) ({*(val) = new;}) | |||
#define HA_ATOMIC_UPDATE_MAX(val, new) \ | |||
({ \ | |||
@@ -203,6 +204,16 @@ static inline unsigned long thread_isolated() | |||
} while (!__sync_bool_compare_and_swap(__val_xchg, __old_xchg, __new_xchg)); \ | |||
__old_xchg; \ | |||
}) | |||
+ | |||
+#define HA_ATOMIC_LOAD(val) \ | |||
+ ({ \ | |||
+ typeof(*(val)) ret; \ | |||
+ __sync_synchronize(); \ | |||
+ ret = *(volatile typeof(val))val; \ | |||
+ __sync_synchronize(); \ | |||
+ ret; \ | |||
+ }) | |||
+ | |||
#define HA_ATOMIC_STORE(val, new) \ | |||
({ \ | |||
typeof((val)) __val_store = (val); \ | |||
@@ -221,6 +232,8 @@ static inline unsigned long thread_isolated() | |||
#define HA_ATOMIC_OR(val, flags) __atomic_or_fetch(val, flags, __ATOMIC_SEQ_CST) | |||
#define HA_ATOMIC_XCHG(val, new) __atomic_exchange_n(val, new, __ATOMIC_SEQ_CST) | |||
#define HA_ATOMIC_STORE(val, new) __atomic_store_n(val, new, __ATOMIC_SEQ_CST) | |||
+#define HA_ATOMIC_LOAD(val) __atomic_load_n(val, __ATOMIC_SEQ_CST) | |||
+ | |||
#endif | |||
#define HA_ATOMIC_UPDATE_MAX(val, new) \ |
@ -1,116 +0,0 @@ | |||
commit 31470e2ba2aabb4c6340fbc15cb5486ceb8c69c8 | |||
Author: Olivier Houchard <ohouchard@haproxy.com> | |||
Date: Mon Apr 29 18:52:06 2019 +0200 | |||
BUG/MEDIUM: port_range: Make the ring buffer lock-free. | |||
Port range uses a ring buffer, and unfortunately, when making haproxy | |||
multithreaded, it's been overlooked, and the ring buffer is not thread-safe. | |||
When specifying a source range, 2 or more threads could pick the same | |||
port, and of course only one of them could use the port, the others would | |||
always fail the connection. | |||
To fix this, make it a lock-free ring buffer. This is easier than usual | |||
because we know the ring buffer can never be full. | |||
This should be backported to 1.8 and 1.9. | |||
(cherry picked from commit 07425de71777b688e77a9c70a7088c13e66e41e9) | |||
Signed-off-by: Olivier Houchard <cognet@ci0.org> | |||
(cherry picked from commit bffb51147a4a5939e344b3c838628f9a944febef) | |||
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> | |||
diff --git a/include/proto/port_range.h b/include/proto/port_range.h | |||
index 8c63faca..f7e3f1d5 100644 | |||
--- a/include/proto/port_range.h | |||
+++ b/include/proto/port_range.h | |||
@@ -24,18 +24,22 @@ | |||
#include <types/port_range.h> | |||
+#define GET_NEXT_OFF(range, off) ((off) == (range)->size - 1 ? 0 : (off) + 1) | |||
+ | |||
/* return an available port from range <range>, or zero if none is left */ | |||
static inline int port_range_alloc_port(struct port_range *range) | |||
{ | |||
int ret; | |||
+ int get; | |||
+ int put; | |||
- if (!range->avail) | |||
- return 0; | |||
- ret = range->ports[range->get]; | |||
- range->get++; | |||
- if (range->get >= range->size) | |||
- range->get = 0; | |||
- range->avail--; | |||
+ get = HA_ATOMIC_LOAD(&range->get); | |||
+ do { | |||
+ put = HA_ATOMIC_LOAD(&range->put_t); | |||
+ if (unlikely(put == get)) | |||
+ return 0; | |||
+ ret = range->ports[get]; | |||
+ } while (!(HA_ATOMIC_CAS(&range->get, &get, GET_NEXT_OFF(range, get)))); | |||
return ret; | |||
} | |||
@@ -45,14 +49,28 @@ static inline int port_range_alloc_port(struct port_range *range) | |||
*/ | |||
static inline void port_range_release_port(struct port_range *range, int port) | |||
{ | |||
+ int put; | |||
+ | |||
if (!port || !range) | |||
return; | |||
- range->ports[range->put] = port; | |||
- range->avail++; | |||
- range->put++; | |||
- if (range->put >= range->size) | |||
- range->put = 0; | |||
+ put = range->put_h; | |||
+ /* put_h is reserved for producers, so that they can each get a | |||
+ * free slot, put_t is what is used by consumers to know if there's | |||
+ * elements available or not | |||
+ */ | |||
+ /* First reserve or slot, we know the ring buffer can't be full, | |||
+ * as we will only ever release port we allocated before | |||
+ */ | |||
+ while (!(HA_ATOMIC_CAS(&range->put_h, &put, GET_NEXT_OFF(range, put)))); | |||
+ HA_ATOMIC_STORE(&range->ports[put], port); | |||
+ /* Wait until all the threads that got a slot before us are done */ | |||
+ while ((volatile int)range->put_t != put) | |||
+ __ha_compiler_barrier(); | |||
+ /* Let the world know we're done, and any potential consumer they | |||
+ * can use that port. | |||
+ */ | |||
+ HA_ATOMIC_STORE(&range->put_t, GET_NEXT_OFF(range, put)); | |||
} | |||
/* return a new initialized port range of N ports. The ports are not | |||
@@ -62,8 +80,10 @@ static inline struct port_range *port_range_alloc_range(int n) | |||
{ | |||
struct port_range *ret; | |||
ret = calloc(1, sizeof(struct port_range) + | |||
- n * sizeof(((struct port_range *)0)->ports[0])); | |||
- ret->size = ret->avail = n; | |||
+ (n + 1) * sizeof(((struct port_range *)0)->ports[0])); | |||
+ ret->size = n + 1; | |||
+ /* Start at the first free element */ | |||
+ ret->put_h = ret->put_t = n; | |||
return ret; | |||
} | |||
diff --git a/include/types/port_range.h b/include/types/port_range.h | |||
index 1d010f77..33455d2d 100644 | |||
--- a/include/types/port_range.h | |||
+++ b/include/types/port_range.h | |||
@@ -25,8 +25,7 @@ | |||
#include <netinet/in.h> | |||
struct port_range { | |||
- int size, get, put; /* range size, and get/put positions */ | |||
- int avail; /* number of available ports left */ | |||
+ int size, get, put_h, put_t; /* range size, and get/put positions */ | |||
uint16_t ports[0]; /* array of <size> ports, in host byte order */ | |||
}; | |||
@ -1,107 +0,0 @@ | |||
--- a/src/ssl_sock.c | |||
+++ b/src/ssl_sock.c | |||
@@ -39,6 +39,7 @@ | |||
#include <netdb.h> | |||
#include <netinet/tcp.h> | |||
+#include <openssl/bn.h> | |||
#include <openssl/crypto.h> | |||
#include <openssl/ssl.h> | |||
#include <openssl/x509.h> | |||
@@ -60,6 +61,17 @@ | |||
#include <openssl/async.h> | |||
#endif | |||
+#ifndef OPENSSL_VERSION | |||
+#define OPENSSL_VERSION SSLEAY_VERSION | |||
+#define OpenSSL_version(x) SSLeay_version(x) | |||
+#define OpenSSL_version_num SSLeay | |||
+#endif | |||
+ | |||
+#if OPENSSL_VERSION_NUMBER < 0x10100000L | |||
+#define X509_getm_notBefore X509_get_notBefore | |||
+#define X509_getm_notAfter X509_get_notAfter | |||
+#endif | |||
+ | |||
#include <import/lru.h> | |||
#include <import/xxhash.h> | |||
@@ -217,7 +229,7 @@ static struct { | |||
.capture_cipherlist = 0, | |||
}; | |||
-#ifdef USE_THREAD | |||
+#if defined(USE_THREAD) && (OPENSSL_VERSION_NUMBER < 0x10100000L) | |||
static HA_RWLOCK_T *ssl_rwlocks; | |||
@@ -1716,8 +1728,8 @@ ssl_sock_do_create_cert(const char *servername, struct bind_conf *bind_conf, SSL | |||
ASN1_INTEGER_set(X509_get_serialNumber(newcrt), HA_ATOMIC_ADD(&ssl_ctx_serial, 1)); | |||
/* Set duration for the certificate */ | |||
- if (!X509_gmtime_adj(X509_get_notBefore(newcrt), (long)-60*60*24) || | |||
- !X509_gmtime_adj(X509_get_notAfter(newcrt),(long)60*60*24*365)) | |||
+ if (!X509_gmtime_adj(X509_getm_notBefore(newcrt), (long)-60*60*24) || | |||
+ !X509_gmtime_adj(X509_getm_notAfter(newcrt),(long)60*60*24*365)) | |||
goto mkcert_error; | |||
/* set public key in the certificate */ | |||
@@ -6299,7 +6311,7 @@ smp_fetch_ssl_x_notafter(const struct arg *args, struct sample *smp, const char | |||
goto out; | |||
smp_trash = get_trash_chunk(); | |||
- if (ssl_sock_get_time(X509_get_notAfter(crt), smp_trash) <= 0) | |||
+ if (ssl_sock_get_time(X509_getm_notAfter(crt), smp_trash) <= 0) | |||
goto out; | |||
smp->data.u.str = *smp_trash; | |||
@@ -6399,7 +6411,7 @@ smp_fetch_ssl_x_notbefore(const struct arg *args, struct sample *smp, const char | |||
goto out; | |||
smp_trash = get_trash_chunk(); | |||
- if (ssl_sock_get_time(X509_get_notBefore(crt), smp_trash) <= 0) | |||
+ if (ssl_sock_get_time(X509_getm_notBefore(crt), smp_trash) <= 0) | |||
goto out; | |||
smp->data.u.str = *smp_trash; | |||
@@ -8976,10 +8988,12 @@ static void __ssl_sock_init(void) | |||
#endif | |||
xprt_register(XPRT_SSL, &ssl_sock); | |||
+#if OPENSSL_VERSION_NUMBER < 0x10100000L | |||
SSL_library_init(); | |||
+#endif | |||
cm = SSL_COMP_get_compression_methods(); | |||
sk_SSL_COMP_zero(cm); | |||
-#ifdef USE_THREAD | |||
+#if defined(USE_THREAD) && (OPENSSL_VERSION_NUMBER < 0x10100000L) | |||
ssl_locking_init(); | |||
#endif | |||
#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined OPENSSL_NO_TLSEXT && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER) | |||
@@ -9008,8 +9022,8 @@ static void __ssl_sock_init(void) | |||
#else /* OPENSSL_IS_BORINGSSL */ | |||
OPENSSL_VERSION_TEXT | |||
"\nRunning on OpenSSL version : %s%s", | |||
- SSLeay_version(SSLEAY_VERSION), | |||
- ((OPENSSL_VERSION_NUMBER ^ SSLeay()) >> 8) ? " (VERSIONS DIFFER!)" : ""); | |||
+ OpenSSL_version(OPENSSL_VERSION), | |||
+ ((OPENSSL_VERSION_NUMBER ^ OpenSSL_version_num()) >> 8) ? " (VERSIONS DIFFER!)" : ""); | |||
#endif | |||
memprintf(&ptr, "%s\nOpenSSL library supports TLS extensions : " | |||
#if OPENSSL_VERSION_NUMBER < 0x00907000L | |||
@@ -9100,12 +9114,14 @@ static void __ssl_sock_deinit(void) | |||
} | |||
#endif | |||
+#if OPENSSL_VERSION_NUMBER < 0x10100000L | |||
ERR_remove_state(0); | |||
ERR_free_strings(); | |||
EVP_cleanup(); | |||
+#endif | |||
-#if OPENSSL_VERSION_NUMBER >= 0x00907000L | |||
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L && OPENSSL_VERSION_NUMBER < 0x10100000L | |||
CRYPTO_cleanup_all_ex_data(); | |||
#endif | |||
} |