mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-03-30 14:01:01 +02:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08cc37a554 | ||
|
|
b7d1c2f91d | ||
|
|
20ae1eb79d | ||
|
|
4fd5cafe27 | ||
|
|
d26bd9f978 | ||
|
|
506cfcb5d4 | ||
|
|
418f0c0bbe | ||
|
|
27d7c69e87 | ||
|
|
d1c7e56585 | ||
|
|
4e99cddde4 | ||
|
|
0e36267aac | ||
|
|
1b0dfff552 | ||
|
|
d3ad730d5f | ||
|
|
cca9245416 | ||
|
|
07edaed191 | ||
|
|
1c1d9d2500 | ||
|
|
47987ccbd9 | ||
|
|
33041fe91f | ||
|
|
8e250bba8f | ||
|
|
c7564c19a2 | ||
|
|
efbf0f8ed1 | ||
|
|
52d8ee85e7 | ||
|
|
38a7d8599d | ||
|
|
82afd36b6c | ||
|
|
ada33006ef | ||
|
|
163eba5c8c | ||
|
|
61d68f14b2 | ||
|
|
125cbecfa9 | ||
|
|
a61ea0f414 | ||
|
|
cd363e0246 | ||
|
|
d257dd4563 | ||
|
|
39121ceca6 | ||
|
|
c9a9fa813b | ||
|
|
181cd8ba8a | ||
|
|
5acdda4eed | ||
|
|
92a24a4e87 | ||
|
|
467f911cea | ||
|
|
0213dd70c9 | ||
|
|
5ead611cc2 | ||
|
|
41c89e4fb6 | ||
|
|
b71f70d548 | ||
|
|
01b9b67d5c | ||
|
|
f8c96bf9cb | ||
|
|
4d6cba03f2 | ||
|
|
1c379cad88 | ||
|
|
3d9865a12c | ||
|
|
d72be950bd | ||
|
|
5a0fbbf1ca | ||
|
|
c6fc53aa99 | ||
|
|
2ca7601c2d | ||
|
|
d250b381dc | ||
|
|
5b184e4178 | ||
|
|
fedaf054c4 | ||
|
|
8e469ebf2e | ||
|
|
ff7b06badb | ||
|
|
5d0f5f8168 | ||
|
|
ed6a4bc807 | ||
|
|
282b9b7d16 | ||
|
|
6982c2539f | ||
|
|
9852d5be26 | ||
|
|
7d40b3134a | ||
|
|
8f6cb8f452 | ||
|
|
60c9e2975b | ||
|
|
5617e47f91 | ||
|
|
042b7ab763 | ||
|
|
7466f64c56 |
33
.github/matrix.py
vendored
33
.github/matrix.py
vendored
@ -12,6 +12,7 @@ import functools
|
|||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import urllib.error
|
||||||
import urllib.request
|
import urllib.request
|
||||||
from os import environ
|
from os import environ
|
||||||
from packaging import version
|
from packaging import version
|
||||||
@ -33,13 +34,24 @@ def get_all_github_tags(url):
|
|||||||
headers = {}
|
headers = {}
|
||||||
if environ.get("GITHUB_TOKEN") is not None:
|
if environ.get("GITHUB_TOKEN") is not None:
|
||||||
headers["Authorization"] = "token {}".format(environ.get("GITHUB_TOKEN"))
|
headers["Authorization"] = "token {}".format(environ.get("GITHUB_TOKEN"))
|
||||||
request = urllib.request.Request(url, headers=headers)
|
all_tags = []
|
||||||
try:
|
page = 1
|
||||||
tags = urllib.request.urlopen(request)
|
sep = "&" if "?" in url else "?"
|
||||||
except:
|
while True:
|
||||||
return None
|
paginated_url = "{}{}per_page=100&page={}".format(url, sep, page)
|
||||||
tags = json.loads(tags.read().decode("utf-8"))
|
request = urllib.request.Request(paginated_url, headers=headers)
|
||||||
return [tag['name'] for tag in tags]
|
try:
|
||||||
|
response = urllib.request.urlopen(request)
|
||||||
|
except urllib.error.URLError:
|
||||||
|
return all_tags if all_tags else None
|
||||||
|
tags = json.loads(response.read().decode("utf-8"))
|
||||||
|
if not tags:
|
||||||
|
break
|
||||||
|
all_tags.extend([tag['name'] for tag in tags])
|
||||||
|
if len(tags) < 100:
|
||||||
|
break
|
||||||
|
page += 1
|
||||||
|
return all_tags if all_tags else None
|
||||||
|
|
||||||
@functools.lru_cache(5)
|
@functools.lru_cache(5)
|
||||||
def determine_latest_openssl(ssl):
|
def determine_latest_openssl(ssl):
|
||||||
@ -65,6 +77,8 @@ def determine_latest_aws_lc(ssl):
|
|||||||
if not tags:
|
if not tags:
|
||||||
return "AWS_LC_VERSION=failed_to_detect"
|
return "AWS_LC_VERSION=failed_to_detect"
|
||||||
valid_tags = list(filter(aws_lc_version_valid, tags))
|
valid_tags = list(filter(aws_lc_version_valid, tags))
|
||||||
|
if not valid_tags:
|
||||||
|
return "AWS_LC_VERSION=failed_to_detect"
|
||||||
latest_tag = max(valid_tags, key=aws_lc_version_string_to_num)
|
latest_tag = max(valid_tags, key=aws_lc_version_string_to_num)
|
||||||
return "AWS_LC_VERSION={}".format(latest_tag[1:])
|
return "AWS_LC_VERSION={}".format(latest_tag[1:])
|
||||||
|
|
||||||
@ -76,11 +90,12 @@ def aws_lc_fips_version_valid(version_string):
|
|||||||
|
|
||||||
@functools.lru_cache(5)
|
@functools.lru_cache(5)
|
||||||
def determine_latest_aws_lc_fips(ssl):
|
def determine_latest_aws_lc_fips(ssl):
|
||||||
# the AWS-LC-FIPS tags are at the end of the list, so let's get a lot
|
tags = get_all_github_tags("https://api.github.com/repos/aws/aws-lc/tags")
|
||||||
tags = get_all_github_tags("https://api.github.com/repos/aws/aws-lc/tags?per_page=200")
|
|
||||||
if not tags:
|
if not tags:
|
||||||
return "AWS_LC_FIPS_VERSION=failed_to_detect"
|
return "AWS_LC_FIPS_VERSION=failed_to_detect"
|
||||||
valid_tags = list(filter(aws_lc_fips_version_valid, tags))
|
valid_tags = list(filter(aws_lc_fips_version_valid, tags))
|
||||||
|
if not valid_tags:
|
||||||
|
return "AWS_LC_FIPS_VERSION=failed_to_detect"
|
||||||
latest_tag = max(valid_tags, key=aws_lc_fips_version_string_to_num)
|
latest_tag = max(valid_tags, key=aws_lc_fips_version_string_to_num)
|
||||||
return "AWS_LC_FIPS_VERSION={}".format(latest_tag[12:])
|
return "AWS_LC_FIPS_VERSION={}".format(latest_tag[12:])
|
||||||
|
|
||||||
|
|||||||
@ -149,7 +149,7 @@ usage() {
|
|||||||
echo "Options:"
|
echo "Options:"
|
||||||
echo " -S, --master-socket <path> Use the master socket at <path> (default: ${SOCKET})"
|
echo " -S, --master-socket <path> Use the master socket at <path> (default: ${SOCKET})"
|
||||||
echo " -s, --socket <path> Use the stats socket at <path>"
|
echo " -s, --socket <path> Use the stats socket at <path>"
|
||||||
echo " -p, --path <path> Specifiy a base path for relative files (default: ${BASEPATH})"
|
echo " -p, --path <path> Specify a base path for relative files (default: ${BASEPATH})"
|
||||||
echo " -n, --dry-run Read certificates on the socket but don't dump them"
|
echo " -n, --dry-run Read certificates on the socket but don't dump them"
|
||||||
echo " -d, --debug Debug mode, set -x"
|
echo " -d, --debug Debug mode, set -x"
|
||||||
echo " -v, --verbose Verbose mode"
|
echo " -v, --verbose Verbose mode"
|
||||||
|
|||||||
@ -86,7 +86,7 @@ maintenance model and what the user wants is passed, then the LLM is invited to
|
|||||||
provide its opinion on the need for a backport and an explanation of the reason
|
provide its opinion on the need for a backport and an explanation of the reason
|
||||||
for its choice. This often helps the user to find a quick summary about the
|
for its choice. This often helps the user to find a quick summary about the
|
||||||
patch. All these outputs are then converted to a long HTML page with colors and
|
patch. All these outputs are then converted to a long HTML page with colors and
|
||||||
radio buttons, where patches are pre-selected based on this classification,
|
radio buttons, where patches are preselected based on this classification,
|
||||||
that the user can consult and adjust, read the commits if needed, and the
|
that the user can consult and adjust, read the commits if needed, and the
|
||||||
selected patches finally provide some copy-pastable commands in a text-area to
|
selected patches finally provide some copy-pastable commands in a text-area to
|
||||||
select commit IDs to work on, typically in a form that's suitable for a simple
|
select commit IDs to work on, typically in a form that's suitable for a simple
|
||||||
|
|||||||
@ -1886,10 +1886,13 @@ The following keywords are supported in the "global" section :
|
|||||||
- tune.h2.be.glitches-threshold
|
- tune.h2.be.glitches-threshold
|
||||||
- tune.h2.be.initial-window-size
|
- tune.h2.be.initial-window-size
|
||||||
- tune.h2.be.max-concurrent-streams
|
- tune.h2.be.max-concurrent-streams
|
||||||
|
- tune.h2.be.max-frames-at-once
|
||||||
- tune.h2.be.rxbuf
|
- tune.h2.be.rxbuf
|
||||||
- tune.h2.fe.glitches-threshold
|
- tune.h2.fe.glitches-threshold
|
||||||
- tune.h2.fe.initial-window-size
|
- tune.h2.fe.initial-window-size
|
||||||
- tune.h2.fe.max-concurrent-streams
|
- tune.h2.fe.max-concurrent-streams
|
||||||
|
- tune.h2.fe.max-frames-at-once
|
||||||
|
- tune.h2.fe.max-rst-at-once
|
||||||
- tune.h2.fe.max-total-streams
|
- tune.h2.fe.max-total-streams
|
||||||
- tune.h2.fe.rxbuf
|
- tune.h2.fe.rxbuf
|
||||||
- tune.h2.header-table-size
|
- tune.h2.header-table-size
|
||||||
@ -4162,8 +4165,11 @@ tune.bufsize.small <size>
|
|||||||
If however a small buffer is not sufficient, a reallocation is automatically
|
If however a small buffer is not sufficient, a reallocation is automatically
|
||||||
done to switch to a standard size buffer.
|
done to switch to a standard size buffer.
|
||||||
|
|
||||||
For the moment, it is used only by HTTP/3 protocol to emit the response
|
For the moment, it is automatically used only by HTTP/3 protocol to emit the
|
||||||
headers.
|
response headers. Otherwise, small buffers support can be enabled for
|
||||||
|
specific proxies via the "use-small-buffers" option.
|
||||||
|
|
||||||
|
See also: option use-small-buffers
|
||||||
|
|
||||||
tune.comp.maxlevel <number>
|
tune.comp.maxlevel <number>
|
||||||
Sets the maximum compression level. The compression level affects CPU
|
Sets the maximum compression level. The compression level affects CPU
|
||||||
@ -4368,6 +4374,13 @@ tune.h2.be.max-concurrent-streams <number>
|
|||||||
case). It is highly recommended not to increase this value; some might find
|
case). It is highly recommended not to increase this value; some might find
|
||||||
it optimal to run at low values (1..5 typically).
|
it optimal to run at low values (1..5 typically).
|
||||||
|
|
||||||
|
tune.h2.be.max-frames-at-once <number>
|
||||||
|
Sets the maximum number of HTTP/2 incoming frames that will be processed at
|
||||||
|
once on a backend connection. It can be useful to set this to a low value
|
||||||
|
(a few tens to a few hundreds) when dealing with very large buffers in order
|
||||||
|
to maintain a low latency and a better fairness between multiple connections.
|
||||||
|
The default value is zero, which means that no limitation is enforced.
|
||||||
|
|
||||||
tune.h2.be.rxbuf <size>
|
tune.h2.be.rxbuf <size>
|
||||||
Sets the HTTP/2 receive buffer size for outgoing connections, in bytes. This
|
Sets the HTTP/2 receive buffer size for outgoing connections, in bytes. This
|
||||||
size will be rounded up to the next multiple of tune.bufsize and will be
|
size will be rounded up to the next multiple of tune.bufsize and will be
|
||||||
@ -4458,6 +4471,25 @@ tune.h2.fe.max-concurrent-streams <number> [args...]
|
|||||||
|
|
||||||
tune.h2.fe.max-concurrent-streams 100 rq-load auto min 15
|
tune.h2.fe.max-concurrent-streams 100 rq-load auto min 15
|
||||||
|
|
||||||
|
tune.h2.fe.max-frames-at-once <number>
|
||||||
|
Sets the maximum number of HTTP/2 incoming frames that will be processed at
|
||||||
|
once on a frontend connection. It can be useful to set this to a low value
|
||||||
|
(a few tens to a few hundreds) when dealing with very large buffers in order
|
||||||
|
to maintain a low latency and a better fairness between multiple connections.
|
||||||
|
The default value is zero, which means that no limitation is enforced.
|
||||||
|
|
||||||
|
tune.h2.fe.max-rst-at-once <number>
|
||||||
|
Sets the maximum number of HTTP/2 incoming RST_STREAM that will be processed
|
||||||
|
at once on a frontend connection. Once the specified number of RST_STREAM
|
||||||
|
frames are received, the connection handler will be placed in a low priority
|
||||||
|
queue and be processed after all other tasks. It can be useful to set this to
|
||||||
|
a very low value (1 or a few units) to significantly reduce the impacts of
|
||||||
|
RST_STREAM floods. RST_STREAM do happen when a user clicks on the Stop button
|
||||||
|
in their browser, but the few extra milliseconds caused by this requeuing are
|
||||||
|
generally unnoticeable, however they are generally effective at significantly
|
||||||
|
lowering the load caused from such floods. The default value is zero, which
|
||||||
|
means that no limitation is enforced.
|
||||||
|
|
||||||
tune.h2.fe.max-total-streams <number>
|
tune.h2.fe.max-total-streams <number>
|
||||||
Sets the HTTP/2 maximum number of total streams processed per incoming
|
Sets the HTTP/2 maximum number of total streams processed per incoming
|
||||||
connection. Once this limit is reached, HAProxy will send a graceful GOAWAY
|
connection. Once this limit is reached, HAProxy will send a graceful GOAWAY
|
||||||
@ -4604,22 +4636,22 @@ tune.http.maxhdr <number>
|
|||||||
protocols. This limit is large enough but not documented on purpose. The same
|
protocols. This limit is large enough but not documented on purpose. The same
|
||||||
limit is applied on the first steps of the decoding for the same reason.
|
limit is applied on the first steps of the decoding for the same reason.
|
||||||
|
|
||||||
tune.idle-pool.shared { on | off }
|
tune.idle-pool.shared { auto | on | off }
|
||||||
Enables ('on') or disables ('off') sharing of idle connection pools between
|
Controls sharing idle connection pools between threads for a same server.
|
||||||
threads for a same server. The default is to share them between threads in
|
It can be enabled for all threads in a same thread group ('on'), enabled for
|
||||||
order to minimize the number of persistent connections to a server, and to
|
all threads ('full') or disabled ('off'). The default is to share them
|
||||||
optimize the connection reuse rate. But to help with debugging or when
|
between threads in the same thread group ('on'), in order to minimize the
|
||||||
|
number of persistent connections to a server, and to optimize the connection
|
||||||
|
reuse rate. Sharing with threads from other thread groups can have a
|
||||||
|
performance impact, and is not enabled by default, but can be useful if
|
||||||
|
maximizing connection reuse is a priority. To help with debugging or when
|
||||||
suspecting a bug in HAProxy around connection reuse, it can be convenient to
|
suspecting a bug in HAProxy around connection reuse, it can be convenient to
|
||||||
forcefully disable this idle pool sharing between multiple threads, and force
|
forcefully disable this idle pool sharing between multiple threads,
|
||||||
this option to "off". The default is on. It is strongly recommended against
|
and force this option to "off". It is strongly recommended against disabling
|
||||||
disabling this option without setting a conservative value on "pool-low-conn"
|
this option without setting a conservative value on "pool-low-conn" for all
|
||||||
for all servers relying on connection reuse to achieve a high performance
|
servers relying on connection reuse to achieve a high performance level,
|
||||||
level, otherwise connections might be closed very often as the thread count
|
otherwise connections might be closed very often as the thread count
|
||||||
increases. Note that in any case, connections are only shared between threads
|
increases.
|
||||||
of the same thread group. This means that systems with many NUMA nodes may
|
|
||||||
show slightly more persistent connections while machines with unified caches
|
|
||||||
and many CPU cores per node may experience higher CPU usage. In the latter
|
|
||||||
case, the "max-thread-per-group" tunable may be used to improve the behavior.
|
|
||||||
|
|
||||||
tune.idletimer <timeout>
|
tune.idletimer <timeout>
|
||||||
Sets the duration after which HAProxy will consider that an empty buffer is
|
Sets the duration after which HAProxy will consider that an empty buffer is
|
||||||
@ -5409,7 +5441,7 @@ tune.ssl.certificate-compression { auto | off }
|
|||||||
|
|
||||||
When set to "auto" it uses the default value of the TLS library.
|
When set to "auto" it uses the default value of the TLS library.
|
||||||
|
|
||||||
With "off" it tries to explicitely disable the support of the feature.
|
With "off" it tries to explicitly disable the support of the feature.
|
||||||
HAProxy won't try to send compressed certificates anymore nor accept
|
HAProxy won't try to send compressed certificates anymore nor accept
|
||||||
compressed certificates.
|
compressed certificates.
|
||||||
|
|
||||||
@ -5555,6 +5587,9 @@ tune.takeover-other-tg-connections <value>
|
|||||||
connections.
|
connections.
|
||||||
Note that using connections from other thread groups can occur performance
|
Note that using connections from other thread groups can occur performance
|
||||||
penalties, so it should not be used unless really needed.
|
penalties, so it should not be used unless really needed.
|
||||||
|
Note that this behavior is now controlled by tune.idle-pool.shared, and
|
||||||
|
this keyword is just there for compatibility with older configurations, and
|
||||||
|
will be deprecated.
|
||||||
|
|
||||||
tune.vars.global-max-size <size>
|
tune.vars.global-max-size <size>
|
||||||
tune.vars.proc-max-size <size>
|
tune.vars.proc-max-size <size>
|
||||||
@ -5942,6 +5977,8 @@ errorloc302 X X X X
|
|||||||
-- keyword -------------------------- defaults - frontend - listen -- backend -
|
-- keyword -------------------------- defaults - frontend - listen -- backend -
|
||||||
errorloc303 X X X X
|
errorloc303 X X X X
|
||||||
error-log-format X X X -
|
error-log-format X X X -
|
||||||
|
external-check command X - X X
|
||||||
|
external-check path X - X X
|
||||||
force-persist - - X X
|
force-persist - - X X
|
||||||
force-be-switch - X X -
|
force-be-switch - X X -
|
||||||
filter - X X X
|
filter - X X X
|
||||||
@ -5987,6 +6024,7 @@ option disable-h2-upgrade (*) X X X -
|
|||||||
option dontlog-normal (*) X X X -
|
option dontlog-normal (*) X X X -
|
||||||
option dontlognull (*) X X X -
|
option dontlognull (*) X X X -
|
||||||
-- keyword -------------------------- defaults - frontend - listen -- backend -
|
-- keyword -------------------------- defaults - frontend - listen -- backend -
|
||||||
|
option external-check X - X X
|
||||||
option forwardfor X X X X
|
option forwardfor X X X X
|
||||||
option forwarded (*) X - X X
|
option forwarded (*) X - X X
|
||||||
option h1-case-adjust-bogus-client (*) X X X -
|
option h1-case-adjust-bogus-client (*) X X X -
|
||||||
@ -6005,9 +6043,9 @@ option httpchk X - X X
|
|||||||
option httpclose (*) X X X X
|
option httpclose (*) X X X X
|
||||||
option httplog X X X -
|
option httplog X X X -
|
||||||
option httpslog X X X -
|
option httpslog X X X -
|
||||||
|
option idle-close-on-response (*) X X X -
|
||||||
option independent-streams (*) X X X X
|
option independent-streams (*) X X X X
|
||||||
option ldap-check X - X X
|
option ldap-check X - X X
|
||||||
option external-check X - X X
|
|
||||||
option log-health-checks (*) X - X X
|
option log-health-checks (*) X - X X
|
||||||
option log-separate-errors (*) X X X -
|
option log-separate-errors (*) X X X -
|
||||||
option logasap (*) X X X -
|
option logasap (*) X X X -
|
||||||
@ -6034,9 +6072,7 @@ option tcp-smart-connect (*) X - X X
|
|||||||
option tcpka X X X X
|
option tcpka X X X X
|
||||||
option tcplog X X X -
|
option tcplog X X X -
|
||||||
option transparent (deprecated) (*) X - X X
|
option transparent (deprecated) (*) X - X X
|
||||||
option idle-close-on-response (*) X X X -
|
option use-small-buffers (*) X - X X
|
||||||
external-check command X - X X
|
|
||||||
external-check path X - X X
|
|
||||||
persist rdp-cookie X - X X
|
persist rdp-cookie X - X X
|
||||||
quic-initial X (!) X X -
|
quic-initial X (!) X X -
|
||||||
rate-limit sessions X X X -
|
rate-limit sessions X X X -
|
||||||
@ -7239,7 +7275,7 @@ default_backend <backend>
|
|||||||
used when no rule has matched. It generally is the dynamic backend which
|
used when no rule has matched. It generally is the dynamic backend which
|
||||||
will catch all undetermined requests.
|
will catch all undetermined requests.
|
||||||
|
|
||||||
If a backend is disabled or unpublished, default_backend rules targetting it
|
If a backend is disabled or unpublished, default_backend rules targeting it
|
||||||
will be ignored and stream processing will remain on the original proxy.
|
will be ignored and stream processing will remain on the original proxy.
|
||||||
|
|
||||||
Example :
|
Example :
|
||||||
@ -7699,6 +7735,96 @@ force-persist { if | unless } <condition>
|
|||||||
and section 7 about ACL usage.
|
and section 7 about ACL usage.
|
||||||
|
|
||||||
|
|
||||||
|
external-check command <command>
|
||||||
|
Executable to run when performing an external-check
|
||||||
|
|
||||||
|
May be used in the following contexts: tcp, http, log
|
||||||
|
|
||||||
|
May be used in sections : defaults | frontend | listen | backend
|
||||||
|
yes | no | yes | yes
|
||||||
|
|
||||||
|
Arguments :
|
||||||
|
<command> is the external command to run
|
||||||
|
|
||||||
|
The arguments passed to the to the command are:
|
||||||
|
|
||||||
|
<proxy_address> <proxy_port> <server_address> <server_port>
|
||||||
|
|
||||||
|
The <proxy_address> and <proxy_port> are derived from the first listener
|
||||||
|
that is either IPv4, IPv6 or a UNIX socket. In the case of a UNIX socket
|
||||||
|
listener the proxy_address will be the path of the socket and the
|
||||||
|
<proxy_port> will be the string "NOT_USED". In a backend section, it's not
|
||||||
|
possible to determine a listener, and both <proxy_address> and <proxy_port>
|
||||||
|
will have the string value "NOT_USED".
|
||||||
|
|
||||||
|
Some values are also provided through environment variables.
|
||||||
|
|
||||||
|
Environment variables :
|
||||||
|
HAPROXY_PROXY_ADDR The first bind address if available (or empty if not
|
||||||
|
applicable, for example in a "backend" section).
|
||||||
|
|
||||||
|
HAPROXY_PROXY_ID The backend id.
|
||||||
|
|
||||||
|
HAPROXY_PROXY_NAME The backend name.
|
||||||
|
|
||||||
|
HAPROXY_PROXY_PORT The first bind port if available (or empty if not
|
||||||
|
applicable, for example in a "backend" section or
|
||||||
|
for a UNIX socket).
|
||||||
|
|
||||||
|
HAPROXY_SERVER_ADDR The server address.
|
||||||
|
|
||||||
|
HAPROXY_SERVER_CURCONN The current number of connections on the server.
|
||||||
|
|
||||||
|
HAPROXY_SERVER_ID The server id.
|
||||||
|
|
||||||
|
HAPROXY_SERVER_MAXCONN The server max connections.
|
||||||
|
|
||||||
|
HAPROXY_SERVER_NAME The server name.
|
||||||
|
|
||||||
|
HAPROXY_SERVER_PORT The server port if available (or empty for a UNIX
|
||||||
|
socket).
|
||||||
|
|
||||||
|
HAPROXY_SERVER_SSL "0" when SSL is not used, "1" when it is used
|
||||||
|
|
||||||
|
HAPROXY_SERVER_PROTO The protocol used by this server, which can be one
|
||||||
|
of "cli" (the haproxy CLI), "syslog" (syslog TCP
|
||||||
|
server), "peers" (peers TCP server), "h1" (HTTP/1.x
|
||||||
|
server), "h2" (HTTP/2 server), or "tcp" (any other
|
||||||
|
TCP server).
|
||||||
|
|
||||||
|
PATH The PATH environment variable used when executing
|
||||||
|
the command may be set using "external-check path".
|
||||||
|
|
||||||
|
If the command executed and exits with a zero status then the check is
|
||||||
|
considered to have passed, otherwise the check is considered to have
|
||||||
|
failed.
|
||||||
|
|
||||||
|
Example :
|
||||||
|
external-check command /bin/true
|
||||||
|
|
||||||
|
See also : "external-check", "option external-check", "external-check path"
|
||||||
|
|
||||||
|
|
||||||
|
external-check path <path>
|
||||||
|
The value of the PATH environment variable used when running an external-check
|
||||||
|
|
||||||
|
May be used in the following contexts: tcp, http, log
|
||||||
|
|
||||||
|
May be used in sections : defaults | frontend | listen | backend
|
||||||
|
yes | no | yes | yes
|
||||||
|
|
||||||
|
Arguments :
|
||||||
|
<path> is the path used when executing external command to run
|
||||||
|
|
||||||
|
The default path is "".
|
||||||
|
|
||||||
|
Example :
|
||||||
|
external-check path "/usr/bin:/bin"
|
||||||
|
|
||||||
|
See also : "external-check", "option external-check",
|
||||||
|
"external-check command"
|
||||||
|
|
||||||
|
|
||||||
force-be-switch { if | unless } <condition>
|
force-be-switch { if | unless } <condition>
|
||||||
Allow content switching to select a backend instance even if it is disabled
|
Allow content switching to select a backend instance even if it is disabled
|
||||||
or unpublished. This rule can be used by admins to test traffic to services
|
or unpublished. This rule can be used by admins to test traffic to services
|
||||||
@ -8203,6 +8329,11 @@ http-check expect [min-recv <int>] [comment <msg>]
|
|||||||
occurred during the expect rule evaluation. <fmt> is a
|
occurred during the expect rule evaluation. <fmt> is a
|
||||||
Custom log format string (see section 8.2.6).
|
Custom log format string (see section 8.2.6).
|
||||||
|
|
||||||
|
status-code <expr> is optional and can be used to set the check status code
|
||||||
|
reported in logs, on success or on error. <expr> is a
|
||||||
|
standard HAProxy expression formed by a sample-fetch
|
||||||
|
followed by some converters.
|
||||||
|
|
||||||
<match> is a keyword indicating how to look for a specific pattern in the
|
<match> is a keyword indicating how to look for a specific pattern in the
|
||||||
response. The keyword may be one of "status", "rstatus", "hdr",
|
response. The keyword may be one of "status", "rstatus", "hdr",
|
||||||
"fhdr", "string", or "rstring". The keyword may be preceded by an
|
"fhdr", "string", or "rstring". The keyword may be preceded by an
|
||||||
@ -9920,6 +10051,24 @@ no option dontlognull
|
|||||||
See also : "log", "http-ignore-probes", "monitor-uri", and
|
See also : "log", "http-ignore-probes", "monitor-uri", and
|
||||||
section 8 about logging.
|
section 8 about logging.
|
||||||
|
|
||||||
|
|
||||||
|
option external-check
|
||||||
|
Use external processes for server health checks
|
||||||
|
|
||||||
|
May be used in the following contexts: tcp, http, log
|
||||||
|
|
||||||
|
May be used in sections : defaults | frontend | listen | backend
|
||||||
|
yes | no | yes | yes
|
||||||
|
|
||||||
|
It is possible to test the health of a server using an external command.
|
||||||
|
This is achieved by running the executable set using "external-check
|
||||||
|
command".
|
||||||
|
|
||||||
|
Requires the "external-check" global to be set.
|
||||||
|
|
||||||
|
See also : "external-check", "external-check command", "external-check path"
|
||||||
|
|
||||||
|
|
||||||
option forwarded [ proto ]
|
option forwarded [ proto ]
|
||||||
[ host | host-expr <host_expr> ]
|
[ host | host-expr <host_expr> ]
|
||||||
[ by | by-expr <by_expr> ] [ by_port | by_port-expr <by_port_expr>]
|
[ by | by-expr <by_expr> ] [ by_port | by_port-expr <by_port_expr>]
|
||||||
@ -10710,6 +10859,39 @@ option httpslog
|
|||||||
See also : section 8 about logging.
|
See also : section 8 about logging.
|
||||||
|
|
||||||
|
|
||||||
|
option idle-close-on-response
|
||||||
|
no option idle-close-on-response
|
||||||
|
Avoid closing idle frontend connections if a soft stop is in progress
|
||||||
|
|
||||||
|
May be used in the following contexts: http
|
||||||
|
|
||||||
|
May be used in sections : defaults | frontend | listen | backend
|
||||||
|
yes | yes | yes | no
|
||||||
|
|
||||||
|
Arguments : none
|
||||||
|
|
||||||
|
By default, idle connections will be closed during a soft stop. In some
|
||||||
|
environments, a client talking to the proxy may have prepared some idle
|
||||||
|
connections in order to send requests later. If there is no proper retry on
|
||||||
|
write errors, this can result in errors while haproxy is reloading. Even
|
||||||
|
though a proper implementation should retry on connection/write errors, this
|
||||||
|
option was introduced to support backwards compatibility with haproxy prior
|
||||||
|
to version 2.4. Indeed before v2.4, haproxy used to wait for a last request
|
||||||
|
and response to add a "connection: close" header before closing, thus
|
||||||
|
notifying the client that the connection would not be reusable.
|
||||||
|
|
||||||
|
In a real life example, this behavior was seen in AWS using the ALB in front
|
||||||
|
of a haproxy. The end result was ALB sending 502 during haproxy reloads.
|
||||||
|
|
||||||
|
Users are warned that using this option may increase the number of old
|
||||||
|
processes if connections remain idle for too long. Adjusting the client
|
||||||
|
timeouts and/or the "hard-stop-after" parameter accordingly might be
|
||||||
|
needed in case of frequent reloads.
|
||||||
|
|
||||||
|
See also: "timeout client", "timeout client-fin", "timeout http-request",
|
||||||
|
"hard-stop-after"
|
||||||
|
|
||||||
|
|
||||||
option independent-streams
|
option independent-streams
|
||||||
no option independent-streams
|
no option independent-streams
|
||||||
Enable or disable independent timeout processing for both directions
|
Enable or disable independent timeout processing for both directions
|
||||||
@ -10774,56 +10956,6 @@ option ldap-check
|
|||||||
See also : "option httpchk"
|
See also : "option httpchk"
|
||||||
|
|
||||||
|
|
||||||
option external-check
|
|
||||||
Use external processes for server health checks
|
|
||||||
|
|
||||||
May be used in the following contexts: tcp, http, log
|
|
||||||
|
|
||||||
May be used in sections : defaults | frontend | listen | backend
|
|
||||||
yes | no | yes | yes
|
|
||||||
|
|
||||||
It is possible to test the health of a server using an external command.
|
|
||||||
This is achieved by running the executable set using "external-check
|
|
||||||
command".
|
|
||||||
|
|
||||||
Requires the "external-check" global to be set.
|
|
||||||
|
|
||||||
See also : "external-check", "external-check command", "external-check path"
|
|
||||||
|
|
||||||
|
|
||||||
option idle-close-on-response
|
|
||||||
no option idle-close-on-response
|
|
||||||
Avoid closing idle frontend connections if a soft stop is in progress
|
|
||||||
|
|
||||||
May be used in the following contexts: http
|
|
||||||
|
|
||||||
May be used in sections : defaults | frontend | listen | backend
|
|
||||||
yes | yes | yes | no
|
|
||||||
|
|
||||||
Arguments : none
|
|
||||||
|
|
||||||
By default, idle connections will be closed during a soft stop. In some
|
|
||||||
environments, a client talking to the proxy may have prepared some idle
|
|
||||||
connections in order to send requests later. If there is no proper retry on
|
|
||||||
write errors, this can result in errors while haproxy is reloading. Even
|
|
||||||
though a proper implementation should retry on connection/write errors, this
|
|
||||||
option was introduced to support backwards compatibility with haproxy prior
|
|
||||||
to version 2.4. Indeed before v2.4, haproxy used to wait for a last request
|
|
||||||
and response to add a "connection: close" header before closing, thus
|
|
||||||
notifying the client that the connection would not be reusable.
|
|
||||||
|
|
||||||
In a real life example, this behavior was seen in AWS using the ALB in front
|
|
||||||
of a haproxy. The end result was ALB sending 502 during haproxy reloads.
|
|
||||||
|
|
||||||
Users are warned that using this option may increase the number of old
|
|
||||||
processes if connections remain idle for too long. Adjusting the client
|
|
||||||
timeouts and/or the "hard-stop-after" parameter accordingly might be
|
|
||||||
needed in case of frequent reloads.
|
|
||||||
|
|
||||||
See also: "timeout client", "timeout client-fin", "timeout http-request",
|
|
||||||
"hard-stop-after"
|
|
||||||
|
|
||||||
|
|
||||||
option log-health-checks
|
option log-health-checks
|
||||||
no option log-health-checks
|
no option log-health-checks
|
||||||
Enable or disable logging of health checks status updates
|
Enable or disable logging of health checks status updates
|
||||||
@ -11785,95 +11917,35 @@ no option transparent (deprecated)
|
|||||||
"transparent" option of the "bind" keyword.
|
"transparent" option of the "bind" keyword.
|
||||||
|
|
||||||
|
|
||||||
external-check command <command>
|
option use-small-buffers [ queue | l7-retries | check ]*
|
||||||
Executable to run when performing an external-check
|
|
||||||
|
|
||||||
May be used in the following contexts: tcp, http, log
|
Enable support for small buffers for the given categories.
|
||||||
|
|
||||||
|
May be used in the following contexts: tcp, http
|
||||||
|
|
||||||
May be used in sections : defaults | frontend | listen | backend
|
May be used in sections : defaults | frontend | listen | backend
|
||||||
yes | no | yes | yes
|
yes | no | yes | yes
|
||||||
|
|
||||||
Arguments :
|
This option can be used to enable the small buffers support at different places
|
||||||
<command> is the external command to run
|
to save memory. By default, with no parameter, small buffers are used as far
|
||||||
|
as possible at all possible places. Otherwise, it is possible to limit it to
|
||||||
|
following the places:
|
||||||
|
|
||||||
The arguments passed to the to the command are:
|
- queue: When set, small buffers will be used to store the requests, if
|
||||||
|
small enough, when the connection is queued.
|
||||||
|
- l7-retries: When set, small buffers will be used to save the requests
|
||||||
|
when L7 retries are enabled.
|
||||||
|
- check: When set, small buffers will be used for the health-checks
|
||||||
|
requests.
|
||||||
|
|
||||||
<proxy_address> <proxy_port> <server_address> <server_port>
|
When enabled, small buffers are used, but only if it is possible. Otherwise,
|
||||||
|
when data are too large, a regular buffer is automatically used. The size of
|
||||||
|
small buffers is configurable via the "tune.bufsize.small" global setting.
|
||||||
|
|
||||||
The <proxy_address> and <proxy_port> are derived from the first listener
|
If this option has been enabled in a "defaults" section, it can be disabled
|
||||||
that is either IPv4, IPv6 or a UNIX socket. In the case of a UNIX socket
|
in a specific instance by prepending the "no" keyword before it.
|
||||||
listener the proxy_address will be the path of the socket and the
|
|
||||||
<proxy_port> will be the string "NOT_USED". In a backend section, it's not
|
|
||||||
possible to determine a listener, and both <proxy_address> and <proxy_port>
|
|
||||||
will have the string value "NOT_USED".
|
|
||||||
|
|
||||||
Some values are also provided through environment variables.
|
|
||||||
|
|
||||||
Environment variables :
|
|
||||||
HAPROXY_PROXY_ADDR The first bind address if available (or empty if not
|
|
||||||
applicable, for example in a "backend" section).
|
|
||||||
|
|
||||||
HAPROXY_PROXY_ID The backend id.
|
|
||||||
|
|
||||||
HAPROXY_PROXY_NAME The backend name.
|
|
||||||
|
|
||||||
HAPROXY_PROXY_PORT The first bind port if available (or empty if not
|
|
||||||
applicable, for example in a "backend" section or
|
|
||||||
for a UNIX socket).
|
|
||||||
|
|
||||||
HAPROXY_SERVER_ADDR The server address.
|
|
||||||
|
|
||||||
HAPROXY_SERVER_CURCONN The current number of connections on the server.
|
|
||||||
|
|
||||||
HAPROXY_SERVER_ID The server id.
|
|
||||||
|
|
||||||
HAPROXY_SERVER_MAXCONN The server max connections.
|
|
||||||
|
|
||||||
HAPROXY_SERVER_NAME The server name.
|
|
||||||
|
|
||||||
HAPROXY_SERVER_PORT The server port if available (or empty for a UNIX
|
|
||||||
socket).
|
|
||||||
|
|
||||||
HAPROXY_SERVER_SSL "0" when SSL is not used, "1" when it is used
|
|
||||||
|
|
||||||
HAPROXY_SERVER_PROTO The protocol used by this server, which can be one
|
|
||||||
of "cli" (the haproxy CLI), "syslog" (syslog TCP
|
|
||||||
server), "peers" (peers TCP server), "h1" (HTTP/1.x
|
|
||||||
server), "h2" (HTTP/2 server), or "tcp" (any other
|
|
||||||
TCP server).
|
|
||||||
|
|
||||||
PATH The PATH environment variable used when executing
|
|
||||||
the command may be set using "external-check path".
|
|
||||||
|
|
||||||
If the command executed and exits with a zero status then the check is
|
|
||||||
considered to have passed, otherwise the check is considered to have
|
|
||||||
failed.
|
|
||||||
|
|
||||||
Example :
|
|
||||||
external-check command /bin/true
|
|
||||||
|
|
||||||
See also : "external-check", "option external-check", "external-check path"
|
|
||||||
|
|
||||||
|
|
||||||
external-check path <path>
|
|
||||||
The value of the PATH environment variable used when running an external-check
|
|
||||||
|
|
||||||
May be used in the following contexts: tcp, http, log
|
|
||||||
|
|
||||||
May be used in sections : defaults | frontend | listen | backend
|
|
||||||
yes | no | yes | yes
|
|
||||||
|
|
||||||
Arguments :
|
|
||||||
<path> is the path used when executing external command to run
|
|
||||||
|
|
||||||
The default path is "".
|
|
||||||
|
|
||||||
Example :
|
|
||||||
external-check path "/usr/bin:/bin"
|
|
||||||
|
|
||||||
See also : "external-check", "option external-check",
|
|
||||||
"external-check command"
|
|
||||||
|
|
||||||
|
See also: tune.bufsize.small
|
||||||
|
|
||||||
persist rdp-cookie
|
persist rdp-cookie
|
||||||
persist rdp-cookie(<name>)
|
persist rdp-cookie(<name>)
|
||||||
@ -13632,13 +13704,6 @@ tcp-check expect [min-recv <int>] [comment <msg>]
|
|||||||
does not match, the check will wait for more data. If set to 0,
|
does not match, the check will wait for more data. If set to 0,
|
||||||
the evaluation result is always conclusive.
|
the evaluation result is always conclusive.
|
||||||
|
|
||||||
<match> is a keyword indicating how to look for a specific pattern in the
|
|
||||||
response. The keyword may be one of "string", "rstring", "binary" or
|
|
||||||
"rbinary".
|
|
||||||
The keyword may be preceded by an exclamation mark ("!") to negate
|
|
||||||
the match. Spaces are allowed between the exclamation mark and the
|
|
||||||
keyword. See below for more details on the supported keywords.
|
|
||||||
|
|
||||||
ok-status <st> is optional and can be used to set the check status if
|
ok-status <st> is optional and can be used to set the check status if
|
||||||
the expect rule is successfully evaluated and if it is
|
the expect rule is successfully evaluated and if it is
|
||||||
the last rule in the tcp-check ruleset. "L7OK", "L7OKC",
|
the last rule in the tcp-check ruleset. "L7OK", "L7OKC",
|
||||||
@ -13686,6 +13751,13 @@ tcp-check expect [min-recv <int>] [comment <msg>]
|
|||||||
standard HAProxy expression formed by a sample-fetch
|
standard HAProxy expression formed by a sample-fetch
|
||||||
followed by some converters.
|
followed by some converters.
|
||||||
|
|
||||||
|
<match> is a keyword indicating how to look for a specific pattern in the
|
||||||
|
response. The keyword may be one of "string", "rstring", "binary" or
|
||||||
|
"rbinary".
|
||||||
|
The keyword may be preceded by an exclamation mark ("!") to negate
|
||||||
|
the match. Spaces are allowed between the exclamation mark and the
|
||||||
|
keyword. See below for more details on the supported keywords.
|
||||||
|
|
||||||
<pattern> is the pattern to look for. It may be a string or a regular
|
<pattern> is the pattern to look for. It may be a string or a regular
|
||||||
expression. If the pattern contains spaces, they must be escaped
|
expression. If the pattern contains spaces, they must be escaped
|
||||||
with the usual backslash ('\').
|
with the usual backslash ('\').
|
||||||
@ -15382,15 +15454,14 @@ disable-l7-retry
|
|||||||
reason than a connection failure. This can be useful for example to make
|
reason than a connection failure. This can be useful for example to make
|
||||||
sure POST requests aren't retried on failure.
|
sure POST requests aren't retried on failure.
|
||||||
|
|
||||||
do-log
|
do-log [profile <log_profile>]
|
||||||
Usable in: QUIC Ini| TCP RqCon| RqSes| RqCnt| RsCnt| HTTP Req| Res| Aft
|
Usable in: QUIC Ini| TCP RqCon| RqSes| RqCnt| RsCnt| HTTP Req| Res| Aft
|
||||||
X | X | X | X | X | X | X | X
|
X | X | X | X | X | X | X | X
|
||||||
|
|
||||||
This action manually triggers a log emission on the proxy. This means
|
This action manually triggers a log emission on the proxy. This means
|
||||||
log options on the proxy will be considered (including formatting options
|
log options on the proxy will be considered (including formatting options
|
||||||
such as "log-format"), but it will not interfere with the logs automatically
|
such as "log-format"), but it will not interfere with the logs automatically
|
||||||
generated by the proxy during transaction handling. It currently doesn't
|
generated by the proxy during transaction handling.
|
||||||
support any argument, though extensions may appear in future versions.
|
|
||||||
|
|
||||||
Using "log-profile", it is possible to precisely describe how the log should
|
Using "log-profile", it is possible to precisely describe how the log should
|
||||||
be emitted for each of the available contexts where the action may be used.
|
be emitted for each of the available contexts where the action may be used.
|
||||||
@ -15400,15 +15471,28 @@ do-log
|
|||||||
|
|
||||||
Also, they will be properly reported when using "%OG" logformat alias.
|
Also, they will be properly reported when using "%OG" logformat alias.
|
||||||
|
|
||||||
|
Optional "profile" argument may be used to specify the name of a log-profile
|
||||||
|
section that should be used for this do-log action specifically instead of
|
||||||
|
the one associated to the current logger that applies by default.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
log-profile myprof
|
log-profile my-dft-prof
|
||||||
on tcp-req-conn format "Connect: %ci"
|
on tcp-req-conn format "Connect: %ci"
|
||||||
|
|
||||||
|
log-profile my-local-prof
|
||||||
|
on tcp-req-conn format "Local Connect: %ci"
|
||||||
|
|
||||||
frontend myfront
|
frontend myfront
|
||||||
log stdout format rfc5424 profile myprof local0
|
log stdout format rfc5424 profile my-dft-prof local0
|
||||||
log-format "log generated using proxy logformat, from '%OG'"
|
log-format "log generated using proxy logformat, from '%OG'"
|
||||||
tcp-request connection do-log #uses special log-profile format
|
acl local src 127.0.0.1
|
||||||
tcp-request content do-log #uses proxy logformat
|
# on connection use either log-profile from the logger (my-dft-prof) or
|
||||||
|
# explicit my-local-prof if source ip is localhost
|
||||||
|
tcp-request connection do-log if !local
|
||||||
|
tcp-request connection do-log profile my-local-prof if local
|
||||||
|
# on content use proxy logformat, since no override was specified
|
||||||
|
# in my-dft-prof
|
||||||
|
tcp-request content do-log
|
||||||
|
|
||||||
do-resolve(<var>,<resolvers>[,ipv4|ipv6]) <expr>
|
do-resolve(<var>,<resolvers>[,ipv4|ipv6]) <expr>
|
||||||
Usable in: QUIC Ini| TCP RqCon| RqSes| RqCnt| RsCnt| HTTP Req| Res| Aft
|
Usable in: QUIC Ini| TCP RqCon| RqSes| RqCnt| RsCnt| HTTP Req| Res| Aft
|
||||||
@ -21546,7 +21630,7 @@ jwt_decrypt_cert(<cert>)
|
|||||||
decrypted thanks to the certificate provided.
|
decrypted thanks to the certificate provided.
|
||||||
The <cert> parameter must be a path to an already loaded certificate (that
|
The <cert> parameter must be a path to an already loaded certificate (that
|
||||||
can be dumped via the "dump ssl cert" CLI command). The certificate must have
|
can be dumped via the "dump ssl cert" CLI command). The certificate must have
|
||||||
its "jwt" option explicitely set to "on" (see "jwt" crt-list option). It can
|
its "jwt" option explicitly set to "on" (see "jwt" crt-list option). It can
|
||||||
be provided directly or via a variable.
|
be provided directly or via a variable.
|
||||||
The only tokens managed yet are the ones using the Compact Serialization
|
The only tokens managed yet are the ones using the Compact Serialization
|
||||||
format (five dot-separated base64-url encoded strings).
|
format (five dot-separated base64-url encoded strings).
|
||||||
@ -29976,7 +30060,7 @@ Enables filter that explicitly tries to compress HTTP responses according to
|
|||||||
|
|
||||||
filter compression (deprecated)
|
filter compression (deprecated)
|
||||||
|
|
||||||
Alias for backward compatibility purposes that is functionnally equivalent to
|
Alias for backward compatibility purposes that is functionally equivalent to
|
||||||
enabling both "comp-req" and "comp-res" filter. "compression" keyword must be
|
enabling both "comp-req" and "comp-res" filter. "compression" keyword must be
|
||||||
used to configure appropriate behavior:
|
used to configure appropriate behavior:
|
||||||
|
|
||||||
@ -31359,9 +31443,9 @@ user <username> [password|insecure-password <password>]
|
|||||||
slower than their glibc counterparts when calculating hashes, so you might
|
slower than their glibc counterparts when calculating hashes, so you might
|
||||||
want to consider this aspect too.
|
want to consider this aspect too.
|
||||||
|
|
||||||
All passwords are considered normal arguments and are therefor subject to
|
All passwords are considered normal arguments and are therefore subject to
|
||||||
regular section 2.2 Quoting and escaping. Single quoting passwords is
|
regular section 2.2 Quoting and escaping. Single quoting passwords is
|
||||||
therefor recommended.
|
therefore recommended.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
userlist L1
|
userlist L1
|
||||||
|
|||||||
@ -134,7 +134,7 @@ HATerm:
|
|||||||
- /?R=<enable> Enable sending random data if >0.
|
- /?R=<enable> Enable sending random data if >0.
|
||||||
|
|
||||||
Note that those arguments may be cumulated on one line separated by a set of
|
Note that those arguments may be cumulated on one line separated by a set of
|
||||||
delimitors among [&?,;/] :
|
delimiters among [&?,;/] :
|
||||||
- GET /?s=20k&c=1&t=700&K=30r HTTP/1.0
|
- GET /?s=20k&c=1&t=700&K=30r HTTP/1.0
|
||||||
- GET /?r=500?s=0?c=0?t=1000 HTTP/1.0
|
- GET /?r=500?s=0?c=0?t=1000 HTTP/1.0
|
||||||
|
|
||||||
|
|||||||
@ -539,10 +539,22 @@ message. These functions are used by HTX analyzers or by multiplexers.
|
|||||||
with the first block not removed, or NULL if everything was removed, and
|
with the first block not removed, or NULL if everything was removed, and
|
||||||
the amount of data drained.
|
the amount of data drained.
|
||||||
|
|
||||||
- htx_xfer_blks() transfers HTX blocks from an HTX message to another,
|
- htx_xfer() transfers HTX blocks from an HTX message to another, stopping
|
||||||
stopping after the first block of a specified type is transferred or when
|
when a specific amount of bytes, including meta-data, was copied. If the
|
||||||
a specific amount of bytes, including meta-data, was moved. If the tail
|
tail block is a DATA block, it may be partially copied. All other block
|
||||||
block is a DATA block, it may be partially moved. All other block are
|
are transferred at once. By default, copied blocks are removed from the
|
||||||
|
original HTX message and headers and trailers parts cannot be partially
|
||||||
|
copied. But flags can be set to change the default behavior:
|
||||||
|
|
||||||
|
- HTX_XFER_KEEP_SRC_BLKS: source blocks are not removed
|
||||||
|
- HTX_XFER_PARTIAL_HDRS_COPY: partial headers and trailers
|
||||||
|
part can be xferred
|
||||||
|
- HTX_XFER_HDRS_ONLY: Only the headers part is xferred
|
||||||
|
|
||||||
|
- htx_xfer_blks() [DEPRECATED] transfers HTX blocks from an HTX message to
|
||||||
|
another, stopping after the first block of a specified type is transferred
|
||||||
|
or when a specific amount of bytes, including meta-data, was moved. If the
|
||||||
|
tail block is a DATA block, it may be partially moved. All other block are
|
||||||
transferred at once or kept. This function returns a mixed value, with the
|
transferred at once or kept. This function returns a mixed value, with the
|
||||||
last block moved, or NULL if nothing was moved, and the amount of data
|
last block moved, or NULL if nothing was moved, and the amount of data
|
||||||
transferred. When HEADERS or TRAILERS blocks must be transferred, this
|
transferred. When HEADERS or TRAILERS blocks must be transferred, this
|
||||||
|
|||||||
@ -114,7 +114,7 @@ SHUT RDY ACT
|
|||||||
1 1 1 => shut pending
|
1 1 1 => shut pending
|
||||||
|
|
||||||
PB: we can land into final shut if one thread disables the FD while another
|
PB: we can land into final shut if one thread disables the FD while another
|
||||||
one that was waiting on it reports it as shut. Theorically it should be
|
one that was waiting on it reports it as shut. Theoretically it should be
|
||||||
implicitly ready though, since reported. But if no data is reported, it
|
implicitly ready though, since reported. But if no data is reported, it
|
||||||
will be reportedly shut only. And no event will be reported then. This
|
will be reportedly shut only. And no event will be reported then. This
|
||||||
might still make sense since it's not active, thus we don't want events.
|
might still make sense since it's not active, thus we don't want events.
|
||||||
|
|||||||
@ -1731,7 +1731,7 @@ add backend <name> from <defproxy> [mode <mode>] [guid <guid>] [ EXPERIMENTAL ]
|
|||||||
Only TCP or HTTP proxies can be created. All of the settings are inherited
|
Only TCP or HTTP proxies can be created. All of the settings are inherited
|
||||||
from <defproxy> default proxy instance. By default, it is mandatory to
|
from <defproxy> default proxy instance. By default, it is mandatory to
|
||||||
specify the backend mode via the argument of the same name, unless <defproxy>
|
specify the backend mode via the argument of the same name, unless <defproxy>
|
||||||
already defines it explicitely. It is also possible to use an optional GUID
|
already defines it explicitly. It is also possible to use an optional GUID
|
||||||
argument if wanted.
|
argument if wanted.
|
||||||
|
|
||||||
Servers can be added via the command "add server". The backend is initialized
|
Servers can be added via the command "add server". The backend is initialized
|
||||||
@ -1740,10 +1740,7 @@ add backend <name> from <defproxy> [mode <mode>] [guid <guid>] [ EXPERIMENTAL ]
|
|||||||
|
|
||||||
All named default proxies can be used, given that they validate the same
|
All named default proxies can be used, given that they validate the same
|
||||||
inheritance rules applied during configuration parsing. There is some
|
inheritance rules applied during configuration parsing. There is some
|
||||||
exceptions though, for example when the mode is neither TCP nor HTTP. Another
|
exceptions though, for example when the mode is neither TCP nor HTTP.
|
||||||
exception is that it is not yet possible to use a default proxies which
|
|
||||||
reference custom HTTP errors, for example via the errorfiles or http-rules
|
|
||||||
keywords.
|
|
||||||
|
|
||||||
This command is restricted and can only be issued on sockets configured for
|
This command is restricted and can only be issued on sockets configured for
|
||||||
level "admin". Moreover, this feature is still considered in development so it
|
level "admin". Moreover, this feature is still considered in development so it
|
||||||
@ -2133,7 +2130,7 @@ del backend <name>
|
|||||||
be attached to the backend instance.
|
be attached to the backend instance.
|
||||||
|
|
||||||
There is additional restrictions which prevent backend removal. First, a
|
There is additional restrictions which prevent backend removal. First, a
|
||||||
backend cannot be removed if it is explicitely referenced by config elements,
|
backend cannot be removed if it is explicitly referenced by config elements,
|
||||||
for example via a use_backend rule or in sample expressions. Some proxies
|
for example via a use_backend rule or in sample expressions. Some proxies
|
||||||
options are also incompatible with runtime deletion. Currently, this is the
|
options are also incompatible with runtime deletion. Currently, this is the
|
||||||
case when deprecated dispatch or option transparent are used. Also, a backend
|
case when deprecated dispatch or option transparent are used. Also, a backend
|
||||||
@ -2141,7 +2138,7 @@ del backend <name>
|
|||||||
impossible for now to remove a backend if QUIC servers were present in it.
|
impossible for now to remove a backend if QUIC servers were present in it.
|
||||||
|
|
||||||
It can be useful to use "wait be-removable" prior to this command to check
|
It can be useful to use "wait be-removable" prior to this command to check
|
||||||
for the aformentioned requisites. This also provides a methode to wait for
|
for the aforementioned requisites. This also provides a method to wait for
|
||||||
the final closure of the streams attached to the target backend.
|
the final closure of the streams attached to the target backend.
|
||||||
|
|
||||||
This command is restricted and can only be issued on sockets configured for
|
This command is restricted and can only be issued on sockets configured for
|
||||||
|
|||||||
@ -58,6 +58,7 @@ struct acme_auth {
|
|||||||
struct ist auth; /* auth URI */
|
struct ist auth; /* auth URI */
|
||||||
struct ist chall; /* challenge URI */
|
struct ist chall; /* challenge URI */
|
||||||
struct ist token; /* token */
|
struct ist token; /* token */
|
||||||
|
int validated; /* already validated */
|
||||||
int ready; /* is the challenge ready ? */
|
int ready; /* is the challenge ready ? */
|
||||||
void *next;
|
void *next;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -198,6 +198,11 @@ struct act_rule {
|
|||||||
struct server *srv; /* target server to attach the connection */
|
struct server *srv; /* target server to attach the connection */
|
||||||
struct sample_expr *name; /* used to differentiate idle connections */
|
struct sample_expr *name; /* used to differentiate idle connections */
|
||||||
} attach_srv; /* 'attach-srv' rule */
|
} attach_srv; /* 'attach-srv' rule */
|
||||||
|
struct {
|
||||||
|
enum log_orig_id orig;
|
||||||
|
char *profile_name;
|
||||||
|
struct log_profile *profile;
|
||||||
|
} do_log; /* 'do-log' action */
|
||||||
struct {
|
struct {
|
||||||
int value;
|
int value;
|
||||||
struct sample_expr *expr;
|
struct sample_expr *expr;
|
||||||
|
|||||||
@ -59,6 +59,7 @@ enum chk_result {
|
|||||||
#define CHK_ST_FASTINTER 0x0400 /* force fastinter check */
|
#define CHK_ST_FASTINTER 0x0400 /* force fastinter check */
|
||||||
#define CHK_ST_READY 0x0800 /* check ready to migrate or run, see below */
|
#define CHK_ST_READY 0x0800 /* check ready to migrate or run, see below */
|
||||||
#define CHK_ST_SLEEPING 0x1000 /* check was sleeping, i.e. not currently bound to a thread, see below */
|
#define CHK_ST_SLEEPING 0x1000 /* check was sleeping, i.e. not currently bound to a thread, see below */
|
||||||
|
#define CHK_ST_USE_SMALL_BUFF 0x2000 /* Use small buffers if possible for the request */
|
||||||
|
|
||||||
/* 4 possible states for CHK_ST_SLEEPING and CHK_ST_READY:
|
/* 4 possible states for CHK_ST_SLEEPING and CHK_ST_READY:
|
||||||
* SLP RDY State Description
|
* SLP RDY State Description
|
||||||
@ -188,6 +189,7 @@ struct check {
|
|||||||
char **envp; /* the environment to use if running a process-based check */
|
char **envp; /* the environment to use if running a process-based check */
|
||||||
struct pid_list *curpid; /* entry in pid_list used for current process-based test, or -1 if not in test */
|
struct pid_list *curpid; /* entry in pid_list used for current process-based test, or -1 if not in test */
|
||||||
struct sockaddr_storage addr; /* the address to check */
|
struct sockaddr_storage addr; /* the address to check */
|
||||||
|
struct protocol *proto; /* protocol used for check, may be different from the server's one */
|
||||||
char *pool_conn_name; /* conn name used on reuse */
|
char *pool_conn_name; /* conn name used on reuse */
|
||||||
char *sni; /* Server name */
|
char *sni; /* Server name */
|
||||||
char *alpn_str; /* ALPN to use for checks */
|
char *alpn_str; /* ALPN to use for checks */
|
||||||
|
|||||||
@ -78,7 +78,7 @@ struct task *process_chk(struct task *t, void *context, unsigned int state);
|
|||||||
struct task *srv_chk_io_cb(struct task *t, void *ctx, unsigned int state);
|
struct task *srv_chk_io_cb(struct task *t, void *ctx, unsigned int state);
|
||||||
|
|
||||||
int check_buf_available(void *target);
|
int check_buf_available(void *target);
|
||||||
struct buffer *check_get_buf(struct check *check, struct buffer *bptr);
|
struct buffer *check_get_buf(struct check *check, struct buffer *bptr, unsigned int small_buffer);
|
||||||
void check_release_buf(struct check *check, struct buffer *bptr);
|
void check_release_buf(struct check *check, struct buffer *bptr);
|
||||||
const char *init_check(struct check *check, int type);
|
const char *init_check(struct check *check, int type);
|
||||||
void free_check(struct check *check);
|
void free_check(struct check *check);
|
||||||
|
|||||||
@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
extern struct pool_head *pool_head_trash;
|
extern struct pool_head *pool_head_trash;
|
||||||
extern struct pool_head *pool_head_large_trash;
|
extern struct pool_head *pool_head_large_trash;
|
||||||
|
extern struct pool_head *pool_head_small_trash;
|
||||||
|
|
||||||
/* function prototypes */
|
/* function prototypes */
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ int chunk_strcmp(const struct buffer *chk, const char *str);
|
|||||||
int chunk_strcasecmp(const struct buffer *chk, const char *str);
|
int chunk_strcasecmp(const struct buffer *chk, const char *str);
|
||||||
struct buffer *get_trash_chunk(void);
|
struct buffer *get_trash_chunk(void);
|
||||||
struct buffer *get_large_trash_chunk(void);
|
struct buffer *get_large_trash_chunk(void);
|
||||||
|
struct buffer *get_small_trash_chunk(void);
|
||||||
struct buffer *get_trash_chunk_sz(size_t size);
|
struct buffer *get_trash_chunk_sz(size_t size);
|
||||||
struct buffer *get_larger_trash_chunk(struct buffer *chunk);
|
struct buffer *get_larger_trash_chunk(struct buffer *chunk);
|
||||||
int init_trash_buffers(int first);
|
int init_trash_buffers(int first);
|
||||||
@ -133,6 +135,29 @@ static forceinline struct buffer *alloc_large_trash_chunk(void)
|
|||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a small trash chunk from the reentrant pool. The buffer starts at
|
||||||
|
* the end of the chunk. This chunk must be freed using free_trash_chunk(). This
|
||||||
|
* call may fail and the caller is responsible for checking that the returned
|
||||||
|
* pointer is not NULL.
|
||||||
|
*/
|
||||||
|
static forceinline struct buffer *alloc_small_trash_chunk(void)
|
||||||
|
{
|
||||||
|
struct buffer *chunk;
|
||||||
|
|
||||||
|
if (!pool_head_small_trash)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
chunk = pool_alloc(pool_head_small_trash);
|
||||||
|
if (chunk) {
|
||||||
|
char *buf = (char *)chunk + sizeof(struct buffer);
|
||||||
|
*buf = 0;
|
||||||
|
chunk_init(chunk, buf,
|
||||||
|
pool_head_small_trash->size - sizeof(struct buffer));
|
||||||
|
}
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate a trash chunk accordingly to the requested size. This chunk must be
|
* Allocate a trash chunk accordingly to the requested size. This chunk must be
|
||||||
* freed using free_trash_chunk(). This call may fail and the caller is
|
* freed using free_trash_chunk(). This call may fail and the caller is
|
||||||
@ -140,7 +165,9 @@ static forceinline struct buffer *alloc_large_trash_chunk(void)
|
|||||||
*/
|
*/
|
||||||
static forceinline struct buffer *alloc_trash_chunk_sz(size_t size)
|
static forceinline struct buffer *alloc_trash_chunk_sz(size_t size)
|
||||||
{
|
{
|
||||||
if (likely(size <= pool_head_trash->size))
|
if (pool_head_small_trash && size <= pool_head_small_trash->size)
|
||||||
|
return alloc_small_trash_chunk();
|
||||||
|
else if (size <= pool_head_trash->size)
|
||||||
return alloc_trash_chunk();
|
return alloc_trash_chunk();
|
||||||
else if (pool_head_large_trash && size <= pool_head_large_trash->size)
|
else if (pool_head_large_trash && size <= pool_head_large_trash->size)
|
||||||
return alloc_large_trash_chunk();
|
return alloc_large_trash_chunk();
|
||||||
@ -153,10 +180,12 @@ static forceinline struct buffer *alloc_trash_chunk_sz(size_t size)
|
|||||||
*/
|
*/
|
||||||
static forceinline void free_trash_chunk(struct buffer *chunk)
|
static forceinline void free_trash_chunk(struct buffer *chunk)
|
||||||
{
|
{
|
||||||
if (likely(chunk && chunk->size == pool_head_trash->size - sizeof(struct buffer)))
|
if (pool_head_small_trash && chunk && chunk->size == pool_head_small_trash->size - sizeof(struct buffer))
|
||||||
pool_free(pool_head_trash, chunk);
|
pool_free(pool_head_small_trash, chunk);
|
||||||
else
|
else if (pool_head_large_trash && chunk && chunk->size == pool_head_large_trash->size - sizeof(struct buffer))
|
||||||
pool_free(pool_head_large_trash, chunk);
|
pool_free(pool_head_large_trash, chunk);
|
||||||
|
else
|
||||||
|
pool_free(pool_head_trash, chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* copies chunk <src> into <chk>. Returns 0 in case of failure. */
|
/* copies chunk <src> into <chk>. Returns 0 in case of failure. */
|
||||||
|
|||||||
@ -34,6 +34,7 @@
|
|||||||
#include <haproxy/listener-t.h>
|
#include <haproxy/listener-t.h>
|
||||||
#include <haproxy/obj_type.h>
|
#include <haproxy/obj_type.h>
|
||||||
#include <haproxy/pool-t.h>
|
#include <haproxy/pool-t.h>
|
||||||
|
#include <haproxy/protocol.h>
|
||||||
#include <haproxy/server.h>
|
#include <haproxy/server.h>
|
||||||
#include <haproxy/session-t.h>
|
#include <haproxy/session-t.h>
|
||||||
#include <haproxy/task-t.h>
|
#include <haproxy/task-t.h>
|
||||||
@ -609,13 +610,13 @@ void list_mux_proto(FILE *out);
|
|||||||
*/
|
*/
|
||||||
static inline const struct mux_proto_list *conn_get_best_mux_entry(
|
static inline const struct mux_proto_list *conn_get_best_mux_entry(
|
||||||
const struct ist mux_proto,
|
const struct ist mux_proto,
|
||||||
int proto_side, int proto_mode)
|
int proto_side, int proto_is_quic, int proto_mode)
|
||||||
{
|
{
|
||||||
struct mux_proto_list *item;
|
struct mux_proto_list *item;
|
||||||
struct mux_proto_list *fallback = NULL;
|
struct mux_proto_list *fallback = NULL;
|
||||||
|
|
||||||
list_for_each_entry(item, &mux_proto_list.list, list) {
|
list_for_each_entry(item, &mux_proto_list.list, list) {
|
||||||
if (!(item->side & proto_side) || !(item->mode & proto_mode))
|
if (!(item->side & proto_side) || !(item->mode & proto_mode) || (proto_is_quic && !(item->mux->flags & MX_FL_FRAMED)))
|
||||||
continue;
|
continue;
|
||||||
if (istlen(mux_proto) && isteq(mux_proto, item->token))
|
if (istlen(mux_proto) && isteq(mux_proto, item->token))
|
||||||
return item;
|
return item;
|
||||||
@ -640,7 +641,7 @@ static inline const struct mux_ops *conn_get_best_mux(struct connection *conn,
|
|||||||
{
|
{
|
||||||
const struct mux_proto_list *item;
|
const struct mux_proto_list *item;
|
||||||
|
|
||||||
item = conn_get_best_mux_entry(mux_proto, proto_side, proto_mode);
|
item = conn_get_best_mux_entry(mux_proto, proto_side, proto_is_quic(conn->ctrl), proto_mode);
|
||||||
|
|
||||||
return item ? item->mux : NULL;
|
return item ? item->mux : NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
extern struct pool_head *pool_head_buffer;
|
extern struct pool_head *pool_head_buffer;
|
||||||
extern struct pool_head *pool_head_large_buffer;
|
extern struct pool_head *pool_head_large_buffer;
|
||||||
|
extern struct pool_head *pool_head_small_buffer;
|
||||||
|
|
||||||
int init_buffer(void);
|
int init_buffer(void);
|
||||||
void buffer_dump(FILE *o, struct buffer *b, int from, int to);
|
void buffer_dump(FILE *o, struct buffer *b, int from, int to);
|
||||||
@ -66,6 +67,12 @@ static inline int b_is_large_sz(size_t sz)
|
|||||||
return (pool_head_large_buffer && sz == pool_head_large_buffer->size);
|
return (pool_head_large_buffer && sz == pool_head_large_buffer->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return 1 if <sz> is the size of a small buffer */
|
||||||
|
static inline int b_is_small_sz(size_t sz)
|
||||||
|
{
|
||||||
|
return (pool_head_small_buffer && sz == pool_head_small_buffer->size);
|
||||||
|
}
|
||||||
|
|
||||||
/* Return 1 if <bug> is a default buffer */
|
/* Return 1 if <bug> is a default buffer */
|
||||||
static inline int b_is_default(struct buffer *buf)
|
static inline int b_is_default(struct buffer *buf)
|
||||||
{
|
{
|
||||||
@ -78,6 +85,12 @@ static inline int b_is_large(struct buffer *buf)
|
|||||||
return b_is_large_sz(b_size(buf));
|
return b_is_large_sz(b_size(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return 1 if <buf> is a small buffer */
|
||||||
|
static inline int b_is_small(struct buffer *buf)
|
||||||
|
{
|
||||||
|
return b_is_small_sz(b_size(buf));
|
||||||
|
}
|
||||||
|
|
||||||
/**************************************************/
|
/**************************************************/
|
||||||
/* Functions below are used for buffer allocation */
|
/* Functions below are used for buffer allocation */
|
||||||
/**************************************************/
|
/**************************************************/
|
||||||
@ -172,6 +185,8 @@ static inline char *__b_get_emergency_buf(void)
|
|||||||
* than the default buffers */ \
|
* than the default buffers */ \
|
||||||
if (unlikely(b_is_large_sz(sz))) \
|
if (unlikely(b_is_large_sz(sz))) \
|
||||||
pool_free(pool_head_large_buffer, area); \
|
pool_free(pool_head_large_buffer, area); \
|
||||||
|
else if (unlikely(b_is_small_sz(sz))) \
|
||||||
|
pool_free(pool_head_small_buffer, area); \
|
||||||
else if (th_ctx->emergency_bufs_left < global.tune.reserved_bufs) \
|
else if (th_ctx->emergency_bufs_left < global.tune.reserved_bufs) \
|
||||||
th_ctx->emergency_bufs[th_ctx->emergency_bufs_left++] = area; \
|
th_ctx->emergency_bufs[th_ctx->emergency_bufs_left++] = area; \
|
||||||
else \
|
else \
|
||||||
@ -185,6 +200,35 @@ static inline char *__b_get_emergency_buf(void)
|
|||||||
__b_free((_buf)); \
|
__b_free((_buf)); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
static inline struct buffer *b_alloc_small(struct buffer *buf)
|
||||||
|
{
|
||||||
|
char *area = NULL;
|
||||||
|
|
||||||
|
if (!buf->size) {
|
||||||
|
area = pool_alloc(pool_head_small_buffer);
|
||||||
|
if (!area)
|
||||||
|
return NULL;
|
||||||
|
buf->area = area;
|
||||||
|
buf->size = global.tune.bufsize_small;
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct buffer *b_alloc_large(struct buffer *buf)
|
||||||
|
{
|
||||||
|
char *area = NULL;
|
||||||
|
|
||||||
|
if (!buf->size) {
|
||||||
|
area = pool_alloc(pool_head_large_buffer);
|
||||||
|
if (!area)
|
||||||
|
return NULL;
|
||||||
|
buf->area = area;
|
||||||
|
buf->size = global.tune.bufsize_large;
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
/* Offer one or multiple buffer currently belonging to target <from> to whoever
|
/* Offer one or multiple buffer currently belonging to target <from> to whoever
|
||||||
* needs one. Any pointer is valid for <from>, including NULL. Its purpose is
|
* needs one. Any pointer is valid for <from>, including NULL. Its purpose is
|
||||||
* to avoid passing a buffer to oneself in case of failed allocations (e.g.
|
* to avoid passing a buffer to oneself in case of failed allocations (e.g.
|
||||||
|
|||||||
@ -93,4 +93,22 @@ struct http_errors {
|
|||||||
struct list list; /* http-errors list */
|
struct list list; /* http-errors list */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Indicates the keyword origin of an http-error definition. This is used in
|
||||||
|
* <conf_errors> type to indicate which part of the internal union should be
|
||||||
|
* manipulated.
|
||||||
|
*/
|
||||||
|
enum http_err_directive {
|
||||||
|
HTTP_ERR_DIRECTIVE_SECTION = 0, /* "errorfiles" keyword referencing a http-errors section */
|
||||||
|
HTTP_ERR_DIRECTIVE_INLINE, /* "errorfile" keyword with inline error definition */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Used with "errorfiles" directives. It indicates for each known HTTP error
|
||||||
|
* status codes if they are defined in the target http-errors section.
|
||||||
|
*/
|
||||||
|
enum http_err_import {
|
||||||
|
HTTP_ERR_IMPORT_NO = 0,
|
||||||
|
HTTP_ERR_IMPORT_IMPLICIT, /* import every errcode defined in a section */
|
||||||
|
HTTP_ERR_IMPORT_EXPLICIT, /* import a specific errcode from a section */
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* _HAPROXY_HTTP_HTX_T_H */
|
#endif /* _HAPROXY_HTTP_HTX_T_H */
|
||||||
|
|||||||
@ -78,6 +78,7 @@ struct buffer *http_load_errorfile(const char *file, char **errmsg);
|
|||||||
struct buffer *http_load_errormsg(const char *key, const struct ist msg, char **errmsg);
|
struct buffer *http_load_errormsg(const char *key, const struct ist msg, char **errmsg);
|
||||||
struct buffer *http_parse_errorfile(int status, const char *file, char **errmsg);
|
struct buffer *http_parse_errorfile(int status, const char *file, char **errmsg);
|
||||||
struct buffer *http_parse_errorloc(int errloc, int status, const char *url, char **errmsg);
|
struct buffer *http_parse_errorloc(int errloc, int status, const char *url, char **errmsg);
|
||||||
|
int proxy_check_http_errors(struct proxy *px);
|
||||||
int proxy_dup_default_conf_errors(struct proxy *curpx, const struct proxy *defpx, char **errmsg);
|
int proxy_dup_default_conf_errors(struct proxy *curpx, const struct proxy *defpx, char **errmsg);
|
||||||
void proxy_release_conf_errors(struct proxy *px);
|
void proxy_release_conf_errors(struct proxy *px);
|
||||||
|
|
||||||
|
|||||||
@ -57,6 +57,16 @@ size_t htx_add_data(struct htx *htx, const struct ist data);
|
|||||||
struct htx_blk *htx_add_last_data(struct htx *htx, struct ist data);
|
struct htx_blk *htx_add_last_data(struct htx *htx, struct ist data);
|
||||||
void htx_move_blk_before(struct htx *htx, struct htx_blk **blk, struct htx_blk **ref);
|
void htx_move_blk_before(struct htx *htx, struct htx_blk **blk, struct htx_blk **ref);
|
||||||
int htx_append_msg(struct htx *dst, const struct htx *src);
|
int htx_append_msg(struct htx *dst, const struct htx *src);
|
||||||
|
struct buffer *htx_move_to_small_buffer(struct buffer *dst, struct buffer *src);
|
||||||
|
struct buffer *htx_move_to_large_buffer(struct buffer *dst, struct buffer *src);
|
||||||
|
struct buffer *htx_copy_to_small_buffer(struct buffer *dst, struct buffer *src);
|
||||||
|
struct buffer *htx_copy_to_large_buffer(struct buffer *dst, struct buffer *src);
|
||||||
|
|
||||||
|
#define HTX_XFER_DEFAULT 0x00000000 /* Default XFER: no partial xfer / remove blocks from source */
|
||||||
|
#define HTX_XFER_KEEP_SRC_BLKS 0x00000001 /* Don't remove xfer blocks from source messages during xfer */
|
||||||
|
#define HTX_XFER_PARTIAL_HDRS_COPY 0x00000002 /* Allow partial copy of headers and trailers part */
|
||||||
|
#define HTX_XFER_HDRS_ONLY 0x00000003 /* Only Transfer header blocks (start-line, header and EOH) */
|
||||||
|
size_t htx_xfer(struct htx *dst, struct htx *src, size_t count, unsigned int flags);
|
||||||
|
|
||||||
/* Functions and macros to get parts of the start-line or length of these
|
/* Functions and macros to get parts of the start-line or length of these
|
||||||
* parts. Request and response start-lines are both composed of 3 parts.
|
* parts. Request and response start-lines are both composed of 3 parts.
|
||||||
|
|||||||
@ -124,6 +124,12 @@ static inline int real_family(int ss_family)
|
|||||||
return fam ? fam->real_family : AF_UNSPEC;
|
return fam ? fam->real_family : AF_UNSPEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int proto_is_quic(const struct protocol *proto)
|
||||||
|
{
|
||||||
|
return (proto->proto_type == PROTO_TYPE_DGRAM &&
|
||||||
|
proto->xprt_type == PROTO_TYPE_STREAM);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _HAPROXY_PROTOCOL_H */
|
#endif /* _HAPROXY_PROTOCOL_H */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -156,14 +156,17 @@ enum PR_SRV_STATE_FILE {
|
|||||||
#define PR_O2_RSTRICT_REQ_HDR_NAMES_NOOP 0x01000000 /* preserve request header names containing chars outside of [0-9a-zA-Z-] charset */
|
#define PR_O2_RSTRICT_REQ_HDR_NAMES_NOOP 0x01000000 /* preserve request header names containing chars outside of [0-9a-zA-Z-] charset */
|
||||||
#define PR_O2_RSTRICT_REQ_HDR_NAMES_MASK 0x01c00000 /* mask for restrict-http-header-names option */
|
#define PR_O2_RSTRICT_REQ_HDR_NAMES_MASK 0x01c00000 /* mask for restrict-http-header-names option */
|
||||||
|
|
||||||
/* unused : 0x02000000 ... 0x08000000 */
|
|
||||||
|
|
||||||
/* server health checks */
|
/* server health checks */
|
||||||
#define PR_O2_CHK_NONE 0x00000000 /* no L7 health checks configured (TCP by default) */
|
#define PR_O2_CHK_NONE 0x00000000 /* no L7 health checks configured (TCP by default) */
|
||||||
#define PR_O2_TCPCHK_CHK 0x90000000 /* use TCPCHK check for server health */
|
#define PR_O2_TCPCHK_CHK 0x02000000 /* use TCPCHK check for server health */
|
||||||
#define PR_O2_EXT_CHK 0xA0000000 /* use external command for server health */
|
#define PR_O2_EXT_CHK 0x04000000 /* use external command for server health */
|
||||||
/* unused: 0xB0000000 to 0xF000000, reserved for health checks */
|
#define PR_O2_CHK_ANY 0x06000000 /* Mask to cover any check */
|
||||||
#define PR_O2_CHK_ANY 0xF0000000 /* Mask to cover any check */
|
|
||||||
|
#define PR_O2_USE_SBUF_QUEUE 0x08000000 /* use small buffer for request when stream are queued*/
|
||||||
|
#define PR_O2_USE_SBUF_L7_RETRY 0x10000000 /* use small buffer for request when L7 retires are enabled */
|
||||||
|
#define PR_O2_USE_SBUF_CHECK 0x20000000 /* use small buffer for request's healthchecks */
|
||||||
|
#define PR_O2_USE_SBUF_ALL 0x38000000 /* all flags for use-large-buffer option */
|
||||||
|
/* unused : 0x40000000 ... 0x80000000 */
|
||||||
/* end of proxy->options2 */
|
/* end of proxy->options2 */
|
||||||
|
|
||||||
/* bits for proxy->options3 */
|
/* bits for proxy->options3 */
|
||||||
@ -241,12 +244,12 @@ enum PR_SRV_STATE_FILE {
|
|||||||
/* Proxy flags */
|
/* Proxy flags */
|
||||||
#define PR_FL_DISABLED 0x00000001 /* The proxy was disabled in the configuration (not at runtime) */
|
#define PR_FL_DISABLED 0x00000001 /* The proxy was disabled in the configuration (not at runtime) */
|
||||||
#define PR_FL_STOPPED 0x00000002 /* The proxy was stopped */
|
#define PR_FL_STOPPED 0x00000002 /* The proxy was stopped */
|
||||||
#define PR_FL_DEF_EXPLICIT_MODE 0x00000004 /* Proxy mode is explicitely defined - only used for defaults instance */
|
#define PR_FL_DEF_EXPLICIT_MODE 0x00000004 /* Proxy mode is explicitly defined - only used for defaults instance */
|
||||||
#define PR_FL_EXPLICIT_REF 0x00000008 /* The default proxy is explicitly referenced by another proxy */
|
#define PR_FL_EXPLICIT_REF 0x00000008 /* The default proxy is explicitly referenced by another proxy */
|
||||||
#define PR_FL_IMPLICIT_REF 0x00000010 /* The default proxy is implicitly referenced by another proxy */
|
#define PR_FL_IMPLICIT_REF 0x00000010 /* The default proxy is implicitly referenced by another proxy */
|
||||||
#define PR_FL_PAUSED 0x00000020 /* The proxy was paused at run time (reversible) */
|
#define PR_FL_PAUSED 0x00000020 /* The proxy was paused at run time (reversible) */
|
||||||
#define PR_FL_CHECKED 0x00000040 /* The proxy configuration was fully checked (including postparsing checks) */
|
#define PR_FL_CHECKED 0x00000040 /* The proxy configuration was fully checked (including postparsing checks) */
|
||||||
#define PR_FL_BE_UNPUBLISHED 0x00000080 /* The proxy cannot be targetted by content switching rules */
|
#define PR_FL_BE_UNPUBLISHED 0x00000080 /* The proxy cannot be targeted by content switching rules */
|
||||||
#define PR_FL_DELETED 0x00000100 /* Proxy has been deleted and must be manipulated with care */
|
#define PR_FL_DELETED 0x00000100 /* Proxy has been deleted and must be manipulated with care */
|
||||||
#define PR_FL_NON_PURGEABLE 0x00000200 /* Proxy referenced by config elements which prevent its runtime removal. */
|
#define PR_FL_NON_PURGEABLE 0x00000200 /* Proxy referenced by config elements which prevent its runtime removal. */
|
||||||
|
|
||||||
|
|||||||
@ -339,7 +339,7 @@ struct global_ssl {
|
|||||||
char **passphrase_cmd;
|
char **passphrase_cmd;
|
||||||
int passphrase_cmd_args_cnt;
|
int passphrase_cmd_args_cnt;
|
||||||
|
|
||||||
unsigned int certificate_compression:1; /* allow to explicitely disable certificate compression */
|
unsigned int certificate_compression:1; /* allow to explicitly disable certificate compression */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* The order here matters for picking a default context,
|
/* The order here matters for picking a default context,
|
||||||
|
|||||||
@ -130,20 +130,22 @@ struct notification {
|
|||||||
* on return.
|
* on return.
|
||||||
*/
|
*/
|
||||||
#define TASK_COMMON \
|
#define TASK_COMMON \
|
||||||
struct { \
|
unsigned int state; /* task state : bitfield of TASK_ */ \
|
||||||
unsigned int state; /* task state : bitfield of TASK_ */ \
|
int tid; /* tid of task/tasklet. <0 = local for tasklet, unbound for task */ \
|
||||||
int tid; /* tid of task/tasklet. <0 = local for tasklet, unbound for task */ \
|
struct task *(*process)(struct task *t, void *ctx, unsigned int state); /* the function which processes the task */ \
|
||||||
struct task *(*process)(struct task *t, void *ctx, unsigned int state); /* the function which processes the task */ \
|
void *context; /* the task's context */ \
|
||||||
void *context; /* the task's context */ \
|
const struct ha_caller *caller; /* call place of last wakeup(); 0 on init, -1 on free */ \
|
||||||
const struct ha_caller *caller; /* call place of last wakeup(); 0 on init, -1 on free */ \
|
uint32_t wake_date; /* date of the last task wakeup */ \
|
||||||
uint32_t wake_date; /* date of the last task wakeup */ \
|
unsigned int calls; /* number of times process was called */ \
|
||||||
unsigned int calls; /* number of times process was called */ \
|
TASK_DEBUG_STORAGE; \
|
||||||
TASK_DEBUG_STORAGE; \
|
short last_run; /* 16-bit now_ms of last run */
|
||||||
}
|
/* a 16- or 48-bit hole remains here and is used by task */
|
||||||
|
|
||||||
/* The base for all tasks */
|
/* The base for all tasks */
|
||||||
struct task {
|
struct task {
|
||||||
TASK_COMMON; /* must be at the beginning! */
|
TASK_COMMON; /* must be at the beginning! */
|
||||||
|
short nice; /* task prio from -1024 to +1024 */
|
||||||
|
int expire; /* next expiration date for this task, in ticks */
|
||||||
struct eb32_node rq; /* ebtree node used to hold the task in the run queue */
|
struct eb32_node rq; /* ebtree node used to hold the task in the run queue */
|
||||||
/* WARNING: the struct task is often aliased as a struct tasklet when
|
/* WARNING: the struct task is often aliased as a struct tasklet when
|
||||||
* it is NOT in the run queue. The tasklet has its struct list here
|
* it is NOT in the run queue. The tasklet has its struct list here
|
||||||
@ -151,14 +153,12 @@ struct task {
|
|||||||
* ever reorder these fields without taking this into account!
|
* ever reorder these fields without taking this into account!
|
||||||
*/
|
*/
|
||||||
struct eb32_node wq; /* ebtree node used to hold the task in the wait queue */
|
struct eb32_node wq; /* ebtree node used to hold the task in the wait queue */
|
||||||
int expire; /* next expiration date for this task, in ticks */
|
|
||||||
short nice; /* task prio from -1024 to +1024 */
|
|
||||||
/* 16-bit hole here */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* lightweight tasks, without priority, mainly used for I/Os */
|
/* lightweight tasks, without priority, mainly used for I/Os */
|
||||||
struct tasklet {
|
struct tasklet {
|
||||||
TASK_COMMON; /* must be at the beginning! */
|
TASK_COMMON; /* must be at the beginning! */
|
||||||
|
/* 48-bit hole here */
|
||||||
struct list list;
|
struct list list;
|
||||||
/* WARNING: the struct task is often aliased as a struct tasklet when
|
/* WARNING: the struct task is often aliased as a struct tasklet when
|
||||||
* it is not in the run queue. The task has its struct rq here where
|
* it is not in the run queue. The task has its struct rq here where
|
||||||
|
|||||||
@ -121,6 +121,7 @@ enum tcpcheck_rule_type {
|
|||||||
/* Unused 0x000000A0..0x00000FF0 (reserved for future proto) */
|
/* Unused 0x000000A0..0x00000FF0 (reserved for future proto) */
|
||||||
#define TCPCHK_RULES_TCP_CHK 0x00000FF0
|
#define TCPCHK_RULES_TCP_CHK 0x00000FF0
|
||||||
#define TCPCHK_RULES_PROTO_CHK 0x00000FF0 /* Mask to cover protocol check */
|
#define TCPCHK_RULES_PROTO_CHK 0x00000FF0 /* Mask to cover protocol check */
|
||||||
|
#define TCPCHK_RULES_MAY_USE_SBUF 0x00001000 /* checks may try to use small buffers if possible for the request */
|
||||||
|
|
||||||
struct check;
|
struct check;
|
||||||
struct tcpcheck_connect {
|
struct tcpcheck_connect {
|
||||||
|
|||||||
@ -1587,7 +1587,7 @@ struct XXH3_state_s {
|
|||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* simple alias to pre-selected XXH3_128bits variant
|
* simple alias to preselected XXH3_128bits variant
|
||||||
*/
|
*/
|
||||||
XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH128(XXH_NOESCAPE const void* data, size_t len, XXH64_hash_t seed);
|
XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH128(XXH_NOESCAPE const void* data, size_t len, XXH64_hash_t seed);
|
||||||
|
|
||||||
|
|||||||
94
src/acme.c
94
src/acme.c
@ -15,6 +15,7 @@
|
|||||||
#include <haproxy/acme-t.h>
|
#include <haproxy/acme-t.h>
|
||||||
|
|
||||||
#include <haproxy/base64.h>
|
#include <haproxy/base64.h>
|
||||||
|
#include <haproxy/intops.h>
|
||||||
#include <haproxy/cfgparse.h>
|
#include <haproxy/cfgparse.h>
|
||||||
#include <haproxy/cli.h>
|
#include <haproxy/cli.h>
|
||||||
#include <haproxy/errors.h>
|
#include <haproxy/errors.h>
|
||||||
@ -266,7 +267,6 @@ static int cfg_parse_acme(const char *file, int linenum, char **args, int kwm)
|
|||||||
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
|
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
|
||||||
|
|
||||||
if (strcmp(args[0], "acme") == 0) {
|
if (strcmp(args[0], "acme") == 0) {
|
||||||
struct acme_cfg *tmp_acme = acme_cfgs;
|
|
||||||
|
|
||||||
if (alertif_too_many_args(1, file, linenum, args, &err_code))
|
if (alertif_too_many_args(1, file, linenum, args, &err_code))
|
||||||
goto out;
|
goto out;
|
||||||
@ -292,7 +292,7 @@ static int cfg_parse_acme(const char *file, int linenum, char **args, int kwm)
|
|||||||
* name */
|
* name */
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
ha_alert("parsing [%s:%d]: acme section '%s' already exists (%s:%d).\n",
|
ha_alert("parsing [%s:%d]: acme section '%s' already exists (%s:%d).\n",
|
||||||
file, linenum, args[1], tmp_acme->filename, tmp_acme->linenum);
|
file, linenum, args[1], cur_acme->filename, cur_acme->linenum);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1188,7 +1188,7 @@ int acme_res_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||||||
}
|
}
|
||||||
/* get the next retry timing */
|
/* get the next retry timing */
|
||||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||||
ctx->retryafter = atol(hdr->v.ptr);
|
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1261,7 +1261,7 @@ int acme_res_chkorder(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||||||
}
|
}
|
||||||
/* get the next retry timing */
|
/* get the next retry timing */
|
||||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||||
ctx->retryafter = atol(hdr->v.ptr);
|
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1344,7 +1344,6 @@ int acme_req_finalize(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||||||
csr->data = ret;
|
csr->data = ret;
|
||||||
|
|
||||||
chunk_printf(req_in, "{ \"csr\": \"%.*s\" }", (int)csr->data, csr->area);
|
chunk_printf(req_in, "{ \"csr\": \"%.*s\" }", (int)csr->data, csr->area);
|
||||||
OPENSSL_free(data);
|
|
||||||
|
|
||||||
|
|
||||||
if (acme_jws_payload(req_in, ctx->nonce, ctx->finalize, ctx->cfg->account.pkey, ctx->kid, req_out, errmsg) != 0)
|
if (acme_jws_payload(req_in, ctx->nonce, ctx->finalize, ctx->cfg->account.pkey, ctx->kid, req_out, errmsg) != 0)
|
||||||
@ -1358,6 +1357,7 @@ int acme_req_finalize(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||||||
error:
|
error:
|
||||||
memprintf(errmsg, "couldn't request the finalize URL");
|
memprintf(errmsg, "couldn't request the finalize URL");
|
||||||
out:
|
out:
|
||||||
|
OPENSSL_free(data);
|
||||||
free_trash_chunk(req_in);
|
free_trash_chunk(req_in);
|
||||||
free_trash_chunk(req_out);
|
free_trash_chunk(req_out);
|
||||||
free_trash_chunk(csr);
|
free_trash_chunk(csr);
|
||||||
@ -1391,7 +1391,7 @@ int acme_res_finalize(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||||||
}
|
}
|
||||||
/* get the next retry timing */
|
/* get the next retry timing */
|
||||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||||
ctx->retryafter = atol(hdr->v.ptr);
|
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1492,7 +1492,7 @@ enum acme_ret acme_res_challenge(struct task *task, struct acme_ctx *ctx, struct
|
|||||||
}
|
}
|
||||||
/* get the next retry timing */
|
/* get the next retry timing */
|
||||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||||
ctx->retryafter = atol(hdr->v.ptr);
|
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1618,7 +1618,7 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
|
|||||||
}
|
}
|
||||||
/* get the next retry timing */
|
/* get the next retry timing */
|
||||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||||
ctx->retryafter = atol(hdr->v.ptr);
|
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1654,6 +1654,19 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
|
|||||||
|
|
||||||
auth->dns = istdup(ist2(t2->area, t2->data));
|
auth->dns = istdup(ist2(t2->area, t2->data));
|
||||||
|
|
||||||
|
ret = mjson_get_string(hc->res.buf.area, hc->res.buf.data, "$.status", trash.area, trash.size);
|
||||||
|
if (ret == -1) {
|
||||||
|
memprintf(errmsg, "couldn't get a \"status\" from Authorization URL \"%s\"", auth->auth.ptr);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
trash.data = ret;
|
||||||
|
|
||||||
|
/* if auth is already valid we need to skip solving challenges */
|
||||||
|
if (strncasecmp("valid", trash.area, trash.data) == 0) {
|
||||||
|
auth->validated = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* get the multiple challenges and select the one from the configuration */
|
/* get the multiple challenges and select the one from the configuration */
|
||||||
for (i = 0; ; i++) {
|
for (i = 0; ; i++) {
|
||||||
int ret;
|
int ret;
|
||||||
@ -1761,6 +1774,7 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
@ -1849,7 +1863,7 @@ int acme_res_neworder(struct task *task, struct acme_ctx *ctx, char **errmsg)
|
|||||||
}
|
}
|
||||||
/* get the next retry timing */
|
/* get the next retry timing */
|
||||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||||
ctx->retryafter = atol(hdr->v.ptr);
|
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||||
}
|
}
|
||||||
/* get the order URL */
|
/* get the order URL */
|
||||||
if (isteqi(hdr->n, ist("Location"))) {
|
if (isteqi(hdr->n, ist("Location"))) {
|
||||||
@ -2009,7 +2023,7 @@ int acme_res_account(struct task *task, struct acme_ctx *ctx, int newaccount, ch
|
|||||||
}
|
}
|
||||||
/* get the next retry timing */
|
/* get the next retry timing */
|
||||||
if (isteqi(hdr->n, ist("Retry-After"))) {
|
if (isteqi(hdr->n, ist("Retry-After"))) {
|
||||||
ctx->retryafter = atol(hdr->v.ptr);
|
ctx->retryafter = __strl2uic(hdr->v.ptr, hdr->v.len);
|
||||||
}
|
}
|
||||||
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
if (isteqi(hdr->n, ist("Replay-Nonce"))) {
|
||||||
istfree(&ctx->nonce);
|
istfree(&ctx->nonce);
|
||||||
@ -2263,6 +2277,14 @@ re:
|
|||||||
break;
|
break;
|
||||||
case ACME_CHALLENGE:
|
case ACME_CHALLENGE:
|
||||||
if (http_st == ACME_HTTP_REQ) {
|
if (http_st == ACME_HTTP_REQ) {
|
||||||
|
/* if challenge is already validated we skip this stage */
|
||||||
|
if (ctx->next_auth->validated) {
|
||||||
|
if ((ctx->next_auth = ctx->next_auth->next) == NULL) {
|
||||||
|
st = ACME_CHKCHALLENGE;
|
||||||
|
ctx->next_auth = ctx->auths;
|
||||||
|
}
|
||||||
|
goto nextreq;
|
||||||
|
}
|
||||||
|
|
||||||
/* if the challenge is not ready, wait to be wakeup */
|
/* if the challenge is not ready, wait to be wakeup */
|
||||||
if (!ctx->next_auth->ready)
|
if (!ctx->next_auth->ready)
|
||||||
@ -2292,6 +2314,14 @@ re:
|
|||||||
break;
|
break;
|
||||||
case ACME_CHKCHALLENGE:
|
case ACME_CHKCHALLENGE:
|
||||||
if (http_st == ACME_HTTP_REQ) {
|
if (http_st == ACME_HTTP_REQ) {
|
||||||
|
/* if challenge is already validated we skip this stage */
|
||||||
|
if (ctx->next_auth->validated) {
|
||||||
|
if ((ctx->next_auth = ctx->next_auth->next) == NULL)
|
||||||
|
st = ACME_FINALIZE;
|
||||||
|
|
||||||
|
goto nextreq;
|
||||||
|
}
|
||||||
|
|
||||||
if (acme_post_as_get(task, ctx, ctx->next_auth->chall, &errmsg) != 0)
|
if (acme_post_as_get(task, ctx, ctx->next_auth->chall, &errmsg) != 0)
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
@ -2526,9 +2556,9 @@ X509_REQ *acme_x509_req(EVP_PKEY *pkey, char **san)
|
|||||||
{
|
{
|
||||||
struct buffer *san_trash = NULL;
|
struct buffer *san_trash = NULL;
|
||||||
X509_REQ *x = NULL;
|
X509_REQ *x = NULL;
|
||||||
X509_NAME *nm;
|
X509_NAME *nm = NULL;
|
||||||
STACK_OF(X509_EXTENSION) *exts = NULL;
|
STACK_OF(X509_EXTENSION) *exts = NULL;
|
||||||
X509_EXTENSION *ext_san;
|
X509_EXTENSION *ext_san = NULL;
|
||||||
char *str_san = NULL;
|
char *str_san = NULL;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
@ -2559,26 +2589,36 @@ X509_REQ *acme_x509_req(EVP_PKEY *pkey, char **san)
|
|||||||
for (i = 0; san[i]; i++) {
|
for (i = 0; san[i]; i++) {
|
||||||
chunk_appendf(san_trash, "%sDNS:%s", i ? "," : "", san[i]);
|
chunk_appendf(san_trash, "%sDNS:%s", i ? "," : "", san[i]);
|
||||||
}
|
}
|
||||||
str_san = my_strndup(san_trash->area, san_trash->data);
|
if ((str_san = my_strndup(san_trash->area, san_trash->data)) == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
if ((ext_san = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, str_san)) == NULL)
|
if ((ext_san = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, str_san)) == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (!sk_X509_EXTENSION_push(exts, ext_san))
|
if (!sk_X509_EXTENSION_push(exts, ext_san))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
ext_san = NULL; /* handle double-free upon error */
|
||||||
|
|
||||||
if (!X509_REQ_add_extensions(x, exts))
|
if (!X509_REQ_add_extensions(x, exts))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
|
|
||||||
|
|
||||||
if (!X509_REQ_sign(x, pkey, EVP_sha256()))
|
if (!X509_REQ_sign(x, pkey, EVP_sha256()))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
|
||||||
|
X509_NAME_free(nm);
|
||||||
|
free(str_san);
|
||||||
free_trash_chunk(san_trash);
|
free_trash_chunk(san_trash);
|
||||||
|
|
||||||
return x;
|
return x;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
X509_EXTENSION_free(ext_san);
|
||||||
|
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
|
||||||
|
X509_REQ_free(x);
|
||||||
|
X509_NAME_free(nm);
|
||||||
|
free(str_san);
|
||||||
free_trash_chunk(san_trash);
|
free_trash_chunk(san_trash);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@ -2627,7 +2667,7 @@ EVP_PKEY *acme_gen_tmp_pkey()
|
|||||||
/* start an ACME task */
|
/* start an ACME task */
|
||||||
static int acme_start_task(struct ckch_store *store, char **errmsg)
|
static int acme_start_task(struct ckch_store *store, char **errmsg)
|
||||||
{
|
{
|
||||||
struct task *task;
|
struct task *task = NULL;
|
||||||
struct acme_ctx *ctx = NULL;
|
struct acme_ctx *ctx = NULL;
|
||||||
struct acme_cfg *cfg;
|
struct acme_cfg *cfg;
|
||||||
struct ckch_store *newstore = NULL;
|
struct ckch_store *newstore = NULL;
|
||||||
@ -2712,6 +2752,8 @@ err:
|
|||||||
HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock);
|
HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock);
|
||||||
acme_ctx_destroy(ctx);
|
acme_ctx_destroy(ctx);
|
||||||
}
|
}
|
||||||
|
if (task)
|
||||||
|
task_destroy(task);
|
||||||
memprintf(errmsg, "%sCan't start the ACME client.", *errmsg ? *errmsg : "");
|
memprintf(errmsg, "%sCan't start the ACME client.", *errmsg ? *errmsg : "");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -2721,7 +2763,10 @@ static int cli_acme_renew_parse(char **args, char *payload, struct appctx *appct
|
|||||||
struct ckch_store *store = NULL;
|
struct ckch_store *store = NULL;
|
||||||
char *errmsg = NULL;
|
char *errmsg = NULL;
|
||||||
|
|
||||||
if (!*args[1]) {
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!*args[2]) {
|
||||||
memprintf(&errmsg, ": not enough parameters\n");
|
memprintf(&errmsg, ": not enough parameters\n");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
@ -2760,8 +2805,11 @@ static int cli_acme_chall_ready_parse(char **args, char *payload, struct appctx
|
|||||||
int remain = 0;
|
int remain = 0;
|
||||||
struct ebmb_node *node = NULL;
|
struct ebmb_node *node = NULL;
|
||||||
|
|
||||||
if (!*args[2] && !*args[3] && !*args[4]) {
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
memprintf(&msg, ": not enough parameters\n");
|
return 1;
|
||||||
|
|
||||||
|
if (!*args[2] || !*args[3] || !*args[4]) {
|
||||||
|
memprintf(&msg, "Not enough parameters: \"acme challenge_ready <certfile> domain <domain>\"\n");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2882,8 +2930,12 @@ end:
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cli_acme_ps(char **args, char *payload, struct appctx *appctx, void *private)
|
static int cli_acme_parse_status(char **args, char *payload, struct appctx *appctx, void *private)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2891,7 +2943,7 @@ static int cli_acme_ps(char **args, char *payload, struct appctx *appctx, void *
|
|||||||
|
|
||||||
static struct cli_kw_list cli_kws = {{ },{
|
static struct cli_kw_list cli_kws = {{ },{
|
||||||
{ { "acme", "renew", NULL }, "acme renew <certfile> : renew a certificate using the ACME protocol", cli_acme_renew_parse, NULL, NULL, NULL, 0 },
|
{ { "acme", "renew", NULL }, "acme renew <certfile> : renew a certificate using the ACME protocol", cli_acme_renew_parse, NULL, NULL, NULL, 0 },
|
||||||
{ { "acme", "status", NULL }, "acme status : show status of certificates configured with ACME", cli_acme_ps, cli_acme_status_io_handler, NULL, NULL, 0 },
|
{ { "acme", "status", NULL }, "acme status : show status of certificates configured with ACME", cli_acme_parse_status, cli_acme_status_io_handler, NULL, NULL, 0 },
|
||||||
{ { "acme", "challenge_ready", NULL }, "acme challenge_ready <certfile> domain <domain> : notify HAProxy that the ACME challenge is ready", cli_acme_chall_ready_parse, NULL, NULL, NULL, 0 },
|
{ { "acme", "challenge_ready", NULL }, "acme challenge_ready <certfile> domain <domain> : notify HAProxy that the ACME challenge is ready", cli_acme_chall_ready_parse, NULL, NULL, NULL, 0 },
|
||||||
{ { NULL }, NULL, NULL, NULL }
|
{ { NULL }, NULL, NULL, NULL }
|
||||||
}};
|
}};
|
||||||
|
|||||||
@ -511,7 +511,7 @@ size_t appctx_htx_rcv_buf(struct appctx *appctx, struct buffer *buf, size_t coun
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
htx_xfer_blks(buf_htx, appctx_htx, count, HTX_BLK_UNUSED);
|
htx_xfer(buf_htx, appctx_htx, count, HTX_XFER_DEFAULT);
|
||||||
buf_htx->flags |= (appctx_htx->flags & (HTX_FL_PARSING_ERROR|HTX_FL_PROCESSING_ERROR));
|
buf_htx->flags |= (appctx_htx->flags & (HTX_FL_PARSING_ERROR|HTX_FL_PROCESSING_ERROR));
|
||||||
if (htx_is_empty(appctx_htx)) {
|
if (htx_is_empty(appctx_htx)) {
|
||||||
buf_htx->flags |= (appctx_htx->flags & HTX_FL_EOM);
|
buf_htx->flags |= (appctx_htx->flags & HTX_FL_EOM);
|
||||||
@ -608,7 +608,7 @@ size_t appctx_htx_snd_buf(struct appctx *appctx, struct buffer *buf, size_t coun
|
|||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
htx_xfer_blks(appctx_htx, buf_htx, count, HTX_BLK_UNUSED);
|
htx_xfer(appctx_htx, buf_htx, count, HTX_XFER_DEFAULT);
|
||||||
if (htx_is_empty(buf_htx)) {
|
if (htx_is_empty(buf_htx)) {
|
||||||
appctx_htx->flags |= (buf_htx->flags & HTX_FL_EOM);
|
appctx_htx->flags |= (buf_htx->flags & HTX_FL_EOM);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2067,7 +2067,7 @@ int connect_server(struct stream *s)
|
|||||||
* available.
|
* available.
|
||||||
*
|
*
|
||||||
* This check must be performed before conn_prepare()
|
* This check must be performed before conn_prepare()
|
||||||
* to ensure consistency accross the whole stack, in
|
* to ensure consistency across the whole stack, in
|
||||||
* particular for QUIC between quic-conn and mux layer.
|
* particular for QUIC between quic-conn and mux layer.
|
||||||
*/
|
*/
|
||||||
if (IS_HTX_STRM(s) && srv->use_ssl &&
|
if (IS_HTX_STRM(s) && srv->use_ssl &&
|
||||||
@ -3063,7 +3063,7 @@ int be_downtime(struct proxy *px) {
|
|||||||
|
|
||||||
/* Checks if <px> backend supports the addition of servers at runtime. Either a
|
/* Checks if <px> backend supports the addition of servers at runtime. Either a
|
||||||
* backend or a defaults proxy are supported. If proxy is incompatible, <msg>
|
* backend or a defaults proxy are supported. If proxy is incompatible, <msg>
|
||||||
* will be allocated to contain a textual explaination.
|
* will be allocated to contain a textual explanation.
|
||||||
*/
|
*/
|
||||||
int be_supports_dynamic_srv(struct proxy *px, char **msg)
|
int be_supports_dynamic_srv(struct proxy *px, char **msg)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1358,14 +1358,15 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
err_code |= warnif_misplaced_http_req(curproxy, file, linenum, args[0], NULL);
|
if (warnif_misplaced_http_req(curproxy, file, linenum, args[0], NULL))
|
||||||
|
err_code |= ERR_WARN;
|
||||||
|
|
||||||
if (curproxy->cap & PR_CAP_FE)
|
if (curproxy->cap & PR_CAP_FE)
|
||||||
where |= SMP_VAL_FE_HRQ_HDR;
|
where |= SMP_VAL_FE_HRQ_HDR;
|
||||||
if (curproxy->cap & PR_CAP_BE)
|
if (curproxy->cap & PR_CAP_BE)
|
||||||
where |= SMP_VAL_BE_HRQ_HDR;
|
where |= SMP_VAL_BE_HRQ_HDR;
|
||||||
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
|
|
||||||
LIST_APPEND(&curproxy->http_req_rules, &rule->list);
|
LIST_APPEND(&curproxy->http_req_rules, &rule->list);
|
||||||
@ -1400,7 +1401,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
if (curproxy->cap & PR_CAP_BE)
|
if (curproxy->cap & PR_CAP_BE)
|
||||||
where |= SMP_VAL_BE_HRS_HDR;
|
where |= SMP_VAL_BE_HRS_HDR;
|
||||||
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
|
|
||||||
LIST_APPEND(&curproxy->http_res_rules, &rule->list);
|
LIST_APPEND(&curproxy->http_res_rules, &rule->list);
|
||||||
@ -1434,7 +1435,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
if (curproxy->cap & PR_CAP_BE)
|
if (curproxy->cap & PR_CAP_BE)
|
||||||
where |= SMP_VAL_BE_HRS_HDR;
|
where |= SMP_VAL_BE_HRS_HDR;
|
||||||
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
|
|
||||||
LIST_APPEND(&curproxy->http_after_res_rules, &rule->list);
|
LIST_APPEND(&curproxy->http_after_res_rules, &rule->list);
|
||||||
@ -1491,14 +1492,15 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
LIST_APPEND(&curproxy->redirect_rules, &rule->list);
|
LIST_APPEND(&curproxy->redirect_rules, &rule->list);
|
||||||
err_code |= warnif_misplaced_redirect(curproxy, file, linenum, args[0], NULL);
|
if (warnif_misplaced_redirect(curproxy, file, linenum, args[0], NULL))
|
||||||
|
err_code |= ERR_WARN;
|
||||||
|
|
||||||
if (curproxy->cap & PR_CAP_FE)
|
if (curproxy->cap & PR_CAP_FE)
|
||||||
where |= SMP_VAL_FE_HRQ_HDR;
|
where |= SMP_VAL_FE_HRQ_HDR;
|
||||||
if (curproxy->cap & PR_CAP_BE)
|
if (curproxy->cap & PR_CAP_BE)
|
||||||
where |= SMP_VAL_BE_HRQ_HDR;
|
where |= SMP_VAL_BE_HRQ_HDR;
|
||||||
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
}
|
}
|
||||||
else if (strcmp(args[0], "use_backend") == 0) {
|
else if (strcmp(args[0], "use_backend") == 0) {
|
||||||
@ -1528,7 +1530,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
err_code |= warnif_cond_conflicts(cond, SMP_VAL_FE_SET_BCK, &errmsg);
|
err_code |= warnif_cond_conflicts(cond, SMP_VAL_FE_SET_BCK, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
}
|
}
|
||||||
else if (*args[2]) {
|
else if (*args[2]) {
|
||||||
@ -1591,7 +1593,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_SET_SRV, &errmsg);
|
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_SET_SRV, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
|
|
||||||
rule = calloc(1, sizeof(*rule));
|
rule = calloc(1, sizeof(*rule));
|
||||||
@ -1646,7 +1648,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
* where force-persist is applied.
|
* where force-persist is applied.
|
||||||
*/
|
*/
|
||||||
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_REQ_CNT, &errmsg);
|
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_REQ_CNT, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
|
|
||||||
rule = calloc(1, sizeof(*rule));
|
rule = calloc(1, sizeof(*rule));
|
||||||
@ -1814,7 +1816,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_STO_RUL, &errmsg);
|
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_STO_RUL, &errmsg);
|
||||||
else
|
else
|
||||||
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_SET_SRV, &errmsg);
|
err_code |= warnif_cond_conflicts(cond, SMP_VAL_BE_SET_SRV, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
|
|
||||||
rule = calloc(1, sizeof(*rule));
|
rule = calloc(1, sizeof(*rule));
|
||||||
@ -1872,7 +1874,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
if (curproxy->cap & PR_CAP_BE)
|
if (curproxy->cap & PR_CAP_BE)
|
||||||
where |= SMP_VAL_BE_HRQ_HDR;
|
where |= SMP_VAL_BE_HRQ_HDR;
|
||||||
err_code |= warnif_cond_conflicts(cond, where, &errmsg);
|
err_code |= warnif_cond_conflicts(cond, where, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
|
|
||||||
rule = calloc(1, sizeof(*rule));
|
rule = calloc(1, sizeof(*rule));
|
||||||
@ -1952,7 +1954,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
if (curproxy->cap & PR_CAP_BE)
|
if (curproxy->cap & PR_CAP_BE)
|
||||||
where |= SMP_VAL_BE_HRQ_HDR;
|
where |= SMP_VAL_BE_HRQ_HDR;
|
||||||
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
err_code |= warnif_cond_conflicts(rule->cond, where, &errmsg);
|
||||||
if (err_code)
|
if (errmsg && *errmsg)
|
||||||
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
ha_warning("parsing [%s:%d] : '%s.\n'", file, linenum, errmsg);
|
||||||
LIST_APPEND(&curproxy->uri_auth->http_req_rules, &rule->list);
|
LIST_APPEND(&curproxy->uri_auth->http_req_rules, &rule->list);
|
||||||
|
|
||||||
@ -2200,6 +2202,42 @@ stats_error_parsing:
|
|||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
else if (strcmp(args[1], "use-small-buffers") == 0) {
|
||||||
|
unsigned int flags = PR_O2_USE_SBUF_ALL;
|
||||||
|
|
||||||
|
if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[1], NULL)) {
|
||||||
|
err_code |= ERR_WARN;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*(args[2])) {
|
||||||
|
int cur_arg;
|
||||||
|
|
||||||
|
flags = 0;
|
||||||
|
for (cur_arg = 2; *(args[cur_arg]); cur_arg++) {
|
||||||
|
if (strcmp(args[cur_arg], "queue") == 0)
|
||||||
|
flags |= PR_O2_USE_SBUF_QUEUE;
|
||||||
|
else if (strcmp(args[cur_arg], "l7-retries") == 0)
|
||||||
|
flags |= PR_O2_USE_SBUF_L7_RETRY;
|
||||||
|
else if (strcmp(args[cur_arg], "check") == 0)
|
||||||
|
flags |= PR_O2_USE_SBUF_CHECK;
|
||||||
|
else {
|
||||||
|
ha_alert("parsing [%s:%d] : invalid parameter '%s'. option '%s' expects 'queue', 'l7-retries' or 'check' value.\n",
|
||||||
|
file, linenum, args[cur_arg], args[1]);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (kwm == KWM_STD) {
|
||||||
|
curproxy->options2 &= ~PR_O2_USE_SBUF_ALL;
|
||||||
|
curproxy->options2 |= flags;
|
||||||
|
}
|
||||||
|
else if (kwm == KWM_NO) {
|
||||||
|
curproxy->options2 &= ~flags;
|
||||||
|
}
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
if (kwm != KWM_STD) {
|
if (kwm != KWM_STD) {
|
||||||
ha_alert("parsing [%s:%d]: negation/default is not supported for option '%s'.\n",
|
ha_alert("parsing [%s:%d]: negation/default is not supported for option '%s'.\n",
|
||||||
@ -2557,7 +2595,8 @@ stats_error_parsing:
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
err_code |= warnif_misplaced_monitor(curproxy, file, linenum, args[0], args[1]);
|
if (warnif_misplaced_monitor(curproxy, file, linenum, args[0], args[1]))
|
||||||
|
err_code |= ERR_WARN;
|
||||||
if ((cond = build_acl_cond(file, linenum, &curproxy->acl, curproxy, (const char **)args + 2, &errmsg)) == NULL) {
|
if ((cond = build_acl_cond(file, linenum, &curproxy->acl, curproxy, (const char **)args + 2, &errmsg)) == NULL) {
|
||||||
ha_alert("parsing [%s:%d] : error detected while parsing a '%s %s' condition : %s.\n",
|
ha_alert("parsing [%s:%d] : error detected while parsing a '%s %s' condition : %s.\n",
|
||||||
file, linenum, args[0], args[1], errmsg);
|
file, linenum, args[0], args[1], errmsg);
|
||||||
|
|||||||
@ -521,7 +521,7 @@ static int ssl_parse_global_keylog(char **args, int section_type, struct proxy *
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Allow to explicitely disable certificate compression when set to "off" */
|
/* Allow to explicitly disable certificate compression when set to "off" */
|
||||||
#ifdef SSL_OP_NO_RX_CERTIFICATE_COMPRESSION
|
#ifdef SSL_OP_NO_RX_CERTIFICATE_COMPRESSION
|
||||||
static int ssl_parse_certificate_compression(char **args, int section_type, struct proxy *curpx,
|
static int ssl_parse_certificate_compression(char **args, int section_type, struct proxy *curpx,
|
||||||
const struct proxy *defpx, const char *file, int line,
|
const struct proxy *defpx, const char *file, int line,
|
||||||
|
|||||||
@ -63,6 +63,7 @@
|
|||||||
#include <haproxy/global.h>
|
#include <haproxy/global.h>
|
||||||
#include <haproxy/http_ana.h>
|
#include <haproxy/http_ana.h>
|
||||||
#include <haproxy/http_rules.h>
|
#include <haproxy/http_rules.h>
|
||||||
|
#include <haproxy/http_htx.h>
|
||||||
#include <haproxy/lb_chash.h>
|
#include <haproxy/lb_chash.h>
|
||||||
#include <haproxy/lb_fas.h>
|
#include <haproxy/lb_fas.h>
|
||||||
#include <haproxy/lb_fwlc.h>
|
#include <haproxy/lb_fwlc.h>
|
||||||
@ -2318,6 +2319,18 @@ int check_config_validity()
|
|||||||
"Please fix either value to remove this warning.\n",
|
"Please fix either value to remove this warning.\n",
|
||||||
global.tune.bufsize_large, global.tune.bufsize);
|
global.tune.bufsize_large, global.tune.bufsize);
|
||||||
global.tune.bufsize_large = 0;
|
global.tune.bufsize_large = 0;
|
||||||
|
err_code |= ERR_WARN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (global.tune.bufsize_small > 0) {
|
||||||
|
if (global.tune.bufsize_small == global.tune.bufsize)
|
||||||
|
global.tune.bufsize_small = 0;
|
||||||
|
else if (global.tune.bufsize_small > global.tune.bufsize) {
|
||||||
|
ha_warning("invalid small buffer size %d bytes which is greater to default bufsize %d bytes.\n",
|
||||||
|
global.tune.bufsize_small, global.tune.bufsize);
|
||||||
|
global.tune.bufsize_small = 0;
|
||||||
|
err_code |= ERR_WARN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2392,6 +2405,8 @@ int check_config_validity()
|
|||||||
else {
|
else {
|
||||||
cfgerr += acl_find_targets(defpx);
|
cfgerr += acl_find_targets(defpx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err_code |= proxy_check_http_errors(defpx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* starting to initialize the main proxies list */
|
/* starting to initialize the main proxies list */
|
||||||
|
|||||||
40
src/check.c
40
src/check.c
@ -1515,13 +1515,15 @@ int check_buf_available(void *target)
|
|||||||
/*
|
/*
|
||||||
* Allocate a buffer. If it fails, it adds the check in buffer wait queue.
|
* Allocate a buffer. If it fails, it adds the check in buffer wait queue.
|
||||||
*/
|
*/
|
||||||
struct buffer *check_get_buf(struct check *check, struct buffer *bptr)
|
struct buffer *check_get_buf(struct check *check, struct buffer *bptr, unsigned int small_buffer)
|
||||||
{
|
{
|
||||||
struct buffer *buf = NULL;
|
struct buffer *buf = NULL;
|
||||||
|
|
||||||
if (likely(!LIST_INLIST(&check->buf_wait.list)) &&
|
if (small_buffer == 0 || (buf = b_alloc_small(bptr)) == NULL) {
|
||||||
unlikely((buf = b_alloc(bptr, DB_CHANNEL)) == NULL)) {
|
if (likely(!LIST_INLIST(&check->buf_wait.list)) &&
|
||||||
b_queue(DB_CHANNEL, &check->buf_wait, check, check_buf_available);
|
unlikely((buf = b_alloc(bptr, DB_CHANNEL)) == NULL)) {
|
||||||
|
b_queue(DB_CHANNEL, &check->buf_wait, check, check_buf_available);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
@ -1533,8 +1535,11 @@ struct buffer *check_get_buf(struct check *check, struct buffer *bptr)
|
|||||||
void check_release_buf(struct check *check, struct buffer *bptr)
|
void check_release_buf(struct check *check, struct buffer *bptr)
|
||||||
{
|
{
|
||||||
if (bptr->size) {
|
if (bptr->size) {
|
||||||
|
int defbuf = b_is_default(bptr);
|
||||||
|
|
||||||
b_free(bptr);
|
b_free(bptr);
|
||||||
offer_buffers(check->buf_wait.target, 1);
|
if (defbuf)
|
||||||
|
offer_buffers(check->buf_wait.target, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1654,7 +1659,6 @@ int start_check_task(struct check *check, int mininter,
|
|||||||
*/
|
*/
|
||||||
static int start_checks()
|
static int start_checks()
|
||||||
{
|
{
|
||||||
|
|
||||||
struct proxy *px;
|
struct proxy *px;
|
||||||
struct server *s;
|
struct server *s;
|
||||||
char *errmsg = NULL;
|
char *errmsg = NULL;
|
||||||
@ -1681,6 +1685,10 @@ static int start_checks()
|
|||||||
*/
|
*/
|
||||||
for (px = proxies_list; px; px = px->next) {
|
for (px = proxies_list; px; px = px->next) {
|
||||||
for (s = px->srv; s; s = s->next) {
|
for (s = px->srv; s; s = s->next) {
|
||||||
|
if ((px->options2 & PR_O2_USE_SBUF_CHECK) &&
|
||||||
|
(s->check.tcpcheck_rules->flags & TCPCHK_RULES_MAY_USE_SBUF))
|
||||||
|
s->check.state |= CHK_ST_USE_SMALL_BUFF;
|
||||||
|
|
||||||
if (s->check.state & CHK_ST_CONFIGURED) {
|
if (s->check.state & CHK_ST_CONFIGURED) {
|
||||||
nbcheck++;
|
nbcheck++;
|
||||||
if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
|
if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
|
||||||
@ -1805,7 +1813,15 @@ int init_srv_check(struct server *srv)
|
|||||||
* specified.
|
* specified.
|
||||||
*/
|
*/
|
||||||
if (!srv->check.port && !is_addr(&srv->check.addr)) {
|
if (!srv->check.port && !is_addr(&srv->check.addr)) {
|
||||||
if (!srv->check.use_ssl && srv->use_ssl != -1)
|
/*
|
||||||
|
* If any setting is set for the check, then we can't
|
||||||
|
* assume we'll use the same XPRT as the server, the
|
||||||
|
* server may be QUIC, but we want a TCP check.
|
||||||
|
*/
|
||||||
|
if (!srv->check.use_ssl && srv->use_ssl != -1 &&
|
||||||
|
!srv->check.via_socks4 && !srv->check.send_proxy &&
|
||||||
|
(!srv->check.alpn_len || (srv->check.alpn_len == srv->ssl_ctx.alpn_len && !strncmp(srv->check.alpn_str, srv->ssl_ctx.alpn_str, srv->check.alpn_len))) &&
|
||||||
|
(!srv->check.mux_proto || srv->check.mux_proto != srv->mux_proto))
|
||||||
srv->check.xprt = srv->xprt;
|
srv->check.xprt = srv->xprt;
|
||||||
else if (srv->check.use_ssl == 1)
|
else if (srv->check.use_ssl == 1)
|
||||||
srv->check.xprt = xprt_get(XPRT_SSL);
|
srv->check.xprt = xprt_get(XPRT_SSL);
|
||||||
@ -2056,6 +2072,7 @@ static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct
|
|||||||
char **errmsg)
|
char **errmsg)
|
||||||
{
|
{
|
||||||
struct sockaddr_storage *sk;
|
struct sockaddr_storage *sk;
|
||||||
|
struct protocol *proto;
|
||||||
int port1, port2, err_code = 0;
|
int port1, port2, err_code = 0;
|
||||||
|
|
||||||
|
|
||||||
@ -2064,7 +2081,7 @@ static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, NULL, NULL, NULL, errmsg, NULL, NULL, NULL,
|
sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, NULL, &proto, NULL, errmsg, NULL, NULL, NULL,
|
||||||
PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM | PA_O_CONNECT);
|
PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM | PA_O_CONNECT);
|
||||||
if (!sk) {
|
if (!sk) {
|
||||||
memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
|
memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
|
||||||
@ -2072,6 +2089,7 @@ static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct
|
|||||||
}
|
}
|
||||||
|
|
||||||
srv->check.addr = *sk;
|
srv->check.addr = *sk;
|
||||||
|
srv->check.proto = proto;
|
||||||
/* if agentaddr was never set, we can use addr */
|
/* if agentaddr was never set, we can use addr */
|
||||||
if (!(srv->flags & SRV_F_AGENTADDR))
|
if (!(srv->flags & SRV_F_AGENTADDR))
|
||||||
srv->agent.addr = *sk;
|
srv->agent.addr = *sk;
|
||||||
@ -2101,7 +2119,11 @@ static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
set_srv_agent_addr(srv, &sk);
|
set_srv_agent_addr(srv, &sk);
|
||||||
|
/* Agent currently only uses TCP */
|
||||||
|
if (sk.ss_family == AF_INET)
|
||||||
|
srv->agent.proto = &proto_tcpv4;
|
||||||
|
else
|
||||||
|
srv->agent.proto = &proto_tcpv6;
|
||||||
out:
|
out:
|
||||||
return err_code;
|
return err_code;
|
||||||
|
|
||||||
|
|||||||
101
src/chunk.c
101
src/chunk.c
@ -53,6 +53,22 @@ struct pool_head *pool_head_large_trash __read_mostly = NULL;
|
|||||||
/* this is used to drain data, and as a temporary large buffer */
|
/* this is used to drain data, and as a temporary large buffer */
|
||||||
THREAD_LOCAL struct buffer trash_large = { };
|
THREAD_LOCAL struct buffer trash_large = { };
|
||||||
|
|
||||||
|
/* small trash chunks used for various conversions */
|
||||||
|
static THREAD_LOCAL struct buffer *small_trash_chunk;
|
||||||
|
static THREAD_LOCAL struct buffer small_trash_chunk1;
|
||||||
|
static THREAD_LOCAL struct buffer small_trash_chunk2;
|
||||||
|
|
||||||
|
/* small trash buffers used for various conversions */
|
||||||
|
static int small_trash_size __read_mostly = 0;
|
||||||
|
static THREAD_LOCAL char *small_trash_buf1 = NULL;
|
||||||
|
static THREAD_LOCAL char *small_trash_buf2 = NULL;
|
||||||
|
|
||||||
|
/* the trash pool for reentrant allocations */
|
||||||
|
struct pool_head *pool_head_small_trash __read_mostly = NULL;
|
||||||
|
|
||||||
|
/* this is used to drain data, and as a temporary small buffer */
|
||||||
|
THREAD_LOCAL struct buffer trash_small = { };
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns a pre-allocated and initialized trash chunk that can be used for any
|
* Returns a pre-allocated and initialized trash chunk that can be used for any
|
||||||
* type of conversion. Two chunks and their respective buffers are alternatively
|
* type of conversion. Two chunks and their respective buffers are alternatively
|
||||||
@ -80,7 +96,7 @@ struct buffer *get_trash_chunk(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Similar to get_trash_chunk() but return a pre-allocated large chunk
|
/* Similar to get_trash_chunk() but return a pre-allocated large chunk
|
||||||
* instead. Becasuse large buffers are not enabled by default, this function may
|
* instead. Because large buffers are not enabled by default, this function may
|
||||||
* return NULL.
|
* return NULL.
|
||||||
*/
|
*/
|
||||||
struct buffer *get_large_trash_chunk(void)
|
struct buffer *get_large_trash_chunk(void)
|
||||||
@ -103,14 +119,40 @@ struct buffer *get_large_trash_chunk(void)
|
|||||||
return large_trash_chunk;
|
return large_trash_chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Similar to get_trash_chunk() but return a pre-allocated small chunk
|
||||||
|
* instead. Because small buffers are not enabled by default, this function may
|
||||||
|
* return NULL.
|
||||||
|
*/
|
||||||
|
struct buffer *get_small_trash_chunk(void)
|
||||||
|
{
|
||||||
|
char *small_trash_buf;
|
||||||
|
|
||||||
|
if (!small_trash_size)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (small_trash_chunk == &small_trash_chunk1) {
|
||||||
|
small_trash_chunk = &small_trash_chunk2;
|
||||||
|
small_trash_buf = small_trash_buf2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
small_trash_chunk = &small_trash_chunk1;
|
||||||
|
small_trash_buf = small_trash_buf1;
|
||||||
|
}
|
||||||
|
*small_trash_buf = 0;
|
||||||
|
chunk_init(small_trash_chunk, small_trash_buf, small_trash_size);
|
||||||
|
return small_trash_chunk;
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns a trash chunk accordingly to the requested size. This function may
|
/* Returns a trash chunk accordingly to the requested size. This function may
|
||||||
* fail if the requested size is too big or if the large chubks are not
|
* fail if the requested size is too big or if the large chubks are not
|
||||||
* configured.
|
* configured.
|
||||||
*/
|
*/
|
||||||
struct buffer *get_trash_chunk_sz(size_t size)
|
struct buffer *get_trash_chunk_sz(size_t size)
|
||||||
{
|
{
|
||||||
if (likely(size <= trash_size))
|
if (likely(size > small_trash_size && size <= trash_size))
|
||||||
return get_trash_chunk();
|
return get_trash_chunk();
|
||||||
|
else if (small_trash_size && size <= small_trash_size)
|
||||||
|
return get_small_trash_chunk();
|
||||||
else if (large_trash_size && size <= large_trash_size)
|
else if (large_trash_size && size <= large_trash_size)
|
||||||
return get_large_trash_chunk();
|
return get_large_trash_chunk();
|
||||||
else
|
else
|
||||||
@ -122,17 +164,20 @@ struct buffer *get_trash_chunk_sz(size_t size)
|
|||||||
*/
|
*/
|
||||||
struct buffer *get_larger_trash_chunk(struct buffer *chk)
|
struct buffer *get_larger_trash_chunk(struct buffer *chk)
|
||||||
{
|
{
|
||||||
struct buffer *chunk;
|
struct buffer *chunk = NULL;
|
||||||
|
|
||||||
if (!chk)
|
if (!chk || chk->size == small_trash_size) {
|
||||||
return get_trash_chunk();
|
/* no chunk or a small one, use a regular buffer */
|
||||||
|
chunk = get_trash_chunk();
|
||||||
|
}
|
||||||
|
else if (large_trash_size && chk->size <= large_trash_size) {
|
||||||
|
/* a regular byffer, use a large buffer if possible */
|
||||||
|
chunk = get_large_trash_chunk();
|
||||||
|
}
|
||||||
|
|
||||||
/* No large buffers or current chunk is alread a large trash chunk */
|
if (chk && chunk)
|
||||||
if (!large_trash_size || chk->size == large_trash_size)
|
b_xfer(chunk, chk, b_data(chk));
|
||||||
return NULL;
|
|
||||||
|
|
||||||
chunk = get_large_trash_chunk();
|
|
||||||
b_xfer(chunk, chk, b_data(chk));
|
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,9 +211,29 @@ static int alloc_large_trash_buffers(int bufsize)
|
|||||||
return trash_large.area && large_trash_buf1 && large_trash_buf2;
|
return trash_large.area && large_trash_buf1 && large_trash_buf2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* allocates the trash small buffers if necessary. Returns 0 in case of
|
||||||
|
* failure. Unlike alloc_trash_buffers(), It is unexpected to call this function
|
||||||
|
* multiple times. Small buffers are not used during configuration parsing.
|
||||||
|
*/
|
||||||
|
static int alloc_small_trash_buffers(int bufsize)
|
||||||
|
{
|
||||||
|
small_trash_size = bufsize;
|
||||||
|
if (!small_trash_size)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
BUG_ON(trash_small.area && small_trash_buf1 && small_trash_buf2);
|
||||||
|
|
||||||
|
chunk_init(&trash_small, my_realloc2(trash_small.area, bufsize), bufsize);
|
||||||
|
small_trash_buf1 = (char *)my_realloc2(small_trash_buf1, bufsize);
|
||||||
|
small_trash_buf2 = (char *)my_realloc2(small_trash_buf2, bufsize);
|
||||||
|
return trash_small.area && small_trash_buf1 && small_trash_buf2;
|
||||||
|
}
|
||||||
|
|
||||||
static int alloc_trash_buffers_per_thread()
|
static int alloc_trash_buffers_per_thread()
|
||||||
{
|
{
|
||||||
return alloc_trash_buffers(global.tune.bufsize) && alloc_large_trash_buffers(global.tune.bufsize_large);
|
return (alloc_trash_buffers(global.tune.bufsize) &&
|
||||||
|
alloc_large_trash_buffers(global.tune.bufsize_large) &&
|
||||||
|
alloc_small_trash_buffers(global.tune.bufsize_large));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_trash_buffers_per_thread()
|
static void free_trash_buffers_per_thread()
|
||||||
@ -180,6 +245,10 @@ static void free_trash_buffers_per_thread()
|
|||||||
chunk_destroy(&trash_large);
|
chunk_destroy(&trash_large);
|
||||||
ha_free(&large_trash_buf2);
|
ha_free(&large_trash_buf2);
|
||||||
ha_free(&large_trash_buf1);
|
ha_free(&large_trash_buf1);
|
||||||
|
|
||||||
|
chunk_destroy(&trash_small);
|
||||||
|
ha_free(&small_trash_buf2);
|
||||||
|
ha_free(&small_trash_buf1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize the trash buffers. It returns 0 if an error occurred. */
|
/* Initialize the trash buffers. It returns 0 if an error occurred. */
|
||||||
@ -207,6 +276,14 @@ int init_trash_buffers(int first)
|
|||||||
if (!pool_head_large_trash)
|
if (!pool_head_large_trash)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!first && global.tune.bufsize_small) {
|
||||||
|
pool_head_small_trash = create_pool("small_trash",
|
||||||
|
sizeof(struct buffer) + global.tune.bufsize_small,
|
||||||
|
MEM_F_EXACT);
|
||||||
|
if (!pool_head_small_trash)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2670,7 +2670,7 @@ static int _send_status(char **args, char *payload, struct appctx *appctx, void
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* the sockpair between the master and the worker is
|
/* the sockpair between the master and the worker is
|
||||||
* used temporarly as a listener to receive
|
* used temporarily as a listener to receive
|
||||||
* _send_status. Once it is received we don't want to
|
* _send_status. Once it is received we don't want to
|
||||||
* use this FD as a listener anymore, but only as a
|
* use this FD as a listener anymore, but only as a
|
||||||
* server, to allow only connections from the master to
|
* server, to allow only connections from the master to
|
||||||
|
|||||||
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
struct pool_head *pool_head_buffer __read_mostly;
|
struct pool_head *pool_head_buffer __read_mostly;
|
||||||
struct pool_head *pool_head_large_buffer __read_mostly = NULL;
|
struct pool_head *pool_head_large_buffer __read_mostly = NULL;
|
||||||
|
struct pool_head *pool_head_small_buffer __read_mostly;
|
||||||
|
|
||||||
/* perform minimal initializations, report 0 in case of error, 1 if OK. */
|
/* perform minimal initializations, report 0 in case of error, 1 if OK. */
|
||||||
int init_buffer()
|
int init_buffer()
|
||||||
@ -43,6 +44,12 @@ int init_buffer()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (global.tune.bufsize_small) {
|
||||||
|
pool_head_small_buffer = create_aligned_pool("small_buffer", global.tune.bufsize_small, 64, MEM_F_SHARED|MEM_F_EXACT);
|
||||||
|
if (!pool_head_small_buffer)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* make sure any change to the queues assignment isn't overlooked */
|
/* make sure any change to the queues assignment isn't overlooked */
|
||||||
BUG_ON(DB_PERMANENT - DB_UNLIKELY - 1 != DYNBUF_NBQ);
|
BUG_ON(DB_PERMANENT - DB_UNLIKELY - 1 != DYNBUF_NBQ);
|
||||||
BUG_ON(DB_MUX_RX_Q < DB_SE_RX_Q || DB_MUX_RX_Q >= DYNBUF_NBQ);
|
BUG_ON(DB_MUX_RX_Q < DB_SE_RX_Q || DB_MUX_RX_Q >= DYNBUF_NBQ);
|
||||||
|
|||||||
13
src/ech.c
13
src/ech.c
@ -136,6 +136,10 @@ static int cli_parse_show_ech(char **args, char *payload,
|
|||||||
{
|
{
|
||||||
struct show_ech_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
struct show_ech_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
|
||||||
/* no parameter, shows only file list */
|
/* no parameter, shows only file list */
|
||||||
if (*args[3]) {
|
if (*args[3]) {
|
||||||
SSL_CTX *sctx = NULL;
|
SSL_CTX *sctx = NULL;
|
||||||
@ -297,6 +301,9 @@ static int cli_parse_add_ech(char **args, char *payload, struct appctx *appctx,
|
|||||||
OSSL_ECHSTORE *es = NULL;
|
OSSL_ECHSTORE *es = NULL;
|
||||||
BIO *es_in = NULL;
|
BIO *es_in = NULL;
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (!*args[3] || !payload)
|
if (!*args[3] || !payload)
|
||||||
return cli_err(appctx, "syntax: add ssl ech <name> <PEM file content>");
|
return cli_err(appctx, "syntax: add ssl ech <name> <PEM file content>");
|
||||||
if (cli_find_ech_specific_ctx(args[3], &sctx) != 1)
|
if (cli_find_ech_specific_ctx(args[3], &sctx) != 1)
|
||||||
@ -324,6 +331,9 @@ static int cli_parse_set_ech(char **args, char *payload, struct appctx *appctx,
|
|||||||
OSSL_ECHSTORE *es = NULL;
|
OSSL_ECHSTORE *es = NULL;
|
||||||
BIO *es_in = NULL;
|
BIO *es_in = NULL;
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (!*args[3] || !payload)
|
if (!*args[3] || !payload)
|
||||||
return cli_err(appctx, "syntax: set ssl ech <name> <PEM file content>");
|
return cli_err(appctx, "syntax: set ssl ech <name> <PEM file content>");
|
||||||
if (cli_find_ech_specific_ctx(args[3], &sctx) != 1)
|
if (cli_find_ech_specific_ctx(args[3], &sctx) != 1)
|
||||||
@ -351,6 +361,9 @@ static int cli_parse_del_ech(char **args, char *payload, struct appctx *appctx,
|
|||||||
char success_message[ECH_SUCCESS_MSG_MAX];
|
char success_message[ECH_SUCCESS_MSG_MAX];
|
||||||
OSSL_ECHSTORE *es = NULL;
|
OSSL_ECHSTORE *es = NULL;
|
||||||
|
|
||||||
|
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (!*args[3])
|
if (!*args[3])
|
||||||
return cli_err(appctx, "syntax: del ssl ech <name>");
|
return cli_err(appctx, "syntax: del ssl ech <name>");
|
||||||
if (*args[4])
|
if (*args[4])
|
||||||
|
|||||||
@ -1114,7 +1114,7 @@ static int spoe_process_event(struct stream *s, struct spoe_context *ctx,
|
|||||||
}
|
}
|
||||||
else if (ret == 0) {
|
else if (ret == 0) {
|
||||||
if ((s->scf->flags & SC_FL_ERROR) ||
|
if ((s->scf->flags & SC_FL_ERROR) ||
|
||||||
((s->scf->flags & (SC_FL_EOS|SC_FL_ABRT_DONE)) && proxy_abrt_close_def(s->be, 1))) {
|
((s->scf->flags & SC_FL_EOS) && proxy_abrt_close_def(s->be, 1))) {
|
||||||
ctx->status_code = SPOE_CTX_ERR_INTERRUPT;
|
ctx->status_code = SPOE_CTX_ERR_INTERRUPT;
|
||||||
spoe_stop_processing(agent, ctx);
|
spoe_stop_processing(agent, ctx);
|
||||||
spoe_handle_processing_error(s, agent, ctx, dir);
|
spoe_handle_processing_error(s, agent, ctx, dir);
|
||||||
|
|||||||
@ -2822,7 +2822,7 @@ void deinit(void)
|
|||||||
* they are respectively cleaned up in sink_deinit() and deinit_log_forward()
|
* they are respectively cleaned up in sink_deinit() and deinit_log_forward()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* If named defaults were preserved, ensure <def_ref> count is resetted. */
|
/* If named defaults were preserved, ensure <def_ref> count is reset. */
|
||||||
if (!(global.tune.options & GTUNE_PURGE_DEFAULTS))
|
if (!(global.tune.options & GTUNE_PURGE_DEFAULTS))
|
||||||
defaults_px_unref_all();
|
defaults_px_unref_all();
|
||||||
|
|
||||||
|
|||||||
@ -47,7 +47,7 @@ const char *HTTP_HELP =
|
|||||||
" - /?R=<enable> Enable sending random data if >0.\n"
|
" - /?R=<enable> Enable sending random data if >0.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Note that those arguments may be cumulated on one line separated by a set of\n"
|
"Note that those arguments may be cumulated on one line separated by a set of\n"
|
||||||
"delimitors among [&?,;/] :\n"
|
"delimiters among [&?,;/] :\n"
|
||||||
" - GET /?s=20k&c=1&t=700&K=30r HTTP/1.0\n"
|
" - GET /?s=20k&c=1&t=700&K=30r HTTP/1.0\n"
|
||||||
" - GET /?r=500?s=0?c=0?t=1000 HTTP/1.0\n"
|
" - GET /?r=500?s=0?c=0?t=1000 HTTP/1.0\n"
|
||||||
"\n";
|
"\n";
|
||||||
@ -323,7 +323,7 @@ static int hstream_htx_buf_snd(struct connection *conn, struct hstream *hs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* The HTX data are not fully sent if the last HTX data
|
/* The HTX data are not fully sent if the last HTX data
|
||||||
* were not fully transfered or if there are remaining data
|
* were not fully transferred or if there are remaining data
|
||||||
* to send (->to_write > 0).
|
* to send (->to_write > 0).
|
||||||
*/
|
*/
|
||||||
if (!htx_is_empty(htxbuf(&hs->res))) {
|
if (!htx_is_empty(htxbuf(&hs->res))) {
|
||||||
@ -1025,7 +1025,7 @@ static int hstream_build_responses(void)
|
|||||||
|
|
||||||
/* original haterm chunk mode responses are made of 1-byte chunks
|
/* original haterm chunk mode responses are made of 1-byte chunks
|
||||||
* but the haproxy muxes do not support this. At this time
|
* but the haproxy muxes do not support this. At this time
|
||||||
* these reponses are handled the same way as for common
|
* these responses are handled the same way as for common
|
||||||
* responses with a pre-built buffer.
|
* responses with a pre-built buffer.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < sizeof(common_chunk_resp); i++)
|
for (i = 0; i < sizeof(common_chunk_resp); i++)
|
||||||
@ -1033,7 +1033,7 @@ static int hstream_build_responses(void)
|
|||||||
|
|
||||||
random_resp = malloc(random_resp_len);
|
random_resp = malloc(random_resp_len);
|
||||||
if (!random_resp) {
|
if (!random_resp) {
|
||||||
ha_alert("not enough memore...\n");
|
ha_alert("not enough memory...\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -106,7 +106,7 @@ static inline size_t hbuf_is_null(const struct hbuf *h)
|
|||||||
|
|
||||||
/* Simple function, to append <line> to <b> without without
|
/* Simple function, to append <line> to <b> without without
|
||||||
* trailing '\0' character.
|
* trailing '\0' character.
|
||||||
* Take into an account the '\t' and '\n' escaped sequeces.
|
* Take into an account the '\t' and '\n' escaped sequences.
|
||||||
*/
|
*/
|
||||||
static void hstream_str_buf_append(struct hbuf *h, const char *line)
|
static void hstream_str_buf_append(struct hbuf *h, const char *line)
|
||||||
{
|
{
|
||||||
@ -215,7 +215,7 @@ void haproxy_init_args(int argc, char **argv)
|
|||||||
argc--; argv++;
|
argc--; argv++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Restore the argumenst */
|
/* Restore the arguments */
|
||||||
argc = sargc; argv = sargv;
|
argc = sargc; argv = sargv;
|
||||||
while (argc > 0) {
|
while (argc > 0) {
|
||||||
char *opt;
|
char *opt;
|
||||||
|
|||||||
@ -2828,8 +2828,7 @@ static enum rule_result http_req_get_intercept_rule(struct proxy *px, struct lis
|
|||||||
int act_opts = 0;
|
int act_opts = 0;
|
||||||
|
|
||||||
if ((s->scf->flags & SC_FL_ERROR) ||
|
if ((s->scf->flags & SC_FL_ERROR) ||
|
||||||
((s->scf->flags & (SC_FL_EOS|SC_FL_ABRT_DONE)) &&
|
((s->scf->flags & SC_FL_EOS) && proxy_abrt_close_def(px, 1)))
|
||||||
proxy_abrt_close_def(px, 1)))
|
|
||||||
act_opts |= ACT_OPT_FINAL | ACT_OPT_FINAL_EARLY;
|
act_opts |= ACT_OPT_FINAL | ACT_OPT_FINAL_EARLY;
|
||||||
|
|
||||||
/* If "the current_rule_list" match the executed rule list, we are in
|
/* If "the current_rule_list" match the executed rule list, we are in
|
||||||
@ -3020,8 +3019,7 @@ static enum rule_result http_res_get_intercept_rule(struct proxy *px, struct lis
|
|||||||
if (final)
|
if (final)
|
||||||
act_opts |= ACT_OPT_FINAL;
|
act_opts |= ACT_OPT_FINAL;
|
||||||
if ((s->scf->flags & SC_FL_ERROR) ||
|
if ((s->scf->flags & SC_FL_ERROR) ||
|
||||||
((s->scf->flags & (SC_FL_EOS|SC_FL_ABRT_DONE)) &&
|
((s->scf->flags & SC_FL_EOS) && proxy_abrt_close_def(px, 1)))
|
||||||
proxy_abrt_close_def(px, 1)))
|
|
||||||
act_opts |= ACT_OPT_FINAL | ACT_OPT_FINAL_EARLY;
|
act_opts |= ACT_OPT_FINAL | ACT_OPT_FINAL_EARLY;
|
||||||
|
|
||||||
/* If "the current_rule_list" match the executed rule list, we are in
|
/* If "the current_rule_list" match the executed rule list, we are in
|
||||||
@ -4337,20 +4335,10 @@ enum rule_result http_wait_for_msg_body(struct stream *s, struct channel *chn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (channel_htx_full(chn, htx, global.tune.maxrewrite) || sc_waiting_room(chn_prod(chn))) {
|
if (channel_htx_full(chn, htx, global.tune.maxrewrite) || sc_waiting_room(chn_prod(chn))) {
|
||||||
struct buffer lbuf;
|
struct buffer lbuf = BUF_NULL;
|
||||||
char *area;
|
|
||||||
|
|
||||||
if (large_buffer == 0 || b_is_large(&chn->buf))
|
if (large_buffer == 0 || b_is_large(&chn->buf) || !htx_move_to_large_buffer(&lbuf, &chn->buf))
|
||||||
goto end; /* don't use large buffer or large buffer is full */
|
goto end; /* don't use large buffer or already a large buffer */
|
||||||
|
|
||||||
/* normal buffer is full, allocate a large one
|
|
||||||
*/
|
|
||||||
area = pool_alloc(pool_head_large_buffer);
|
|
||||||
if (!area)
|
|
||||||
goto end; /* Allocation failure: TODO must be improved to use buffer_wait */
|
|
||||||
lbuf = b_make(area, global.tune.bufsize_large, 0, 0);
|
|
||||||
htx_xfer_blks(htx_from_buf(&lbuf), htx, htx_used_space(htx), HTX_BLK_UNUSED);
|
|
||||||
htx_to_buf(htx, &chn->buf);
|
|
||||||
b_free(&chn->buf);
|
b_free(&chn->buf);
|
||||||
offer_buffers(s, 1);
|
offer_buffers(s, 1);
|
||||||
chn->buf = lbuf;
|
chn->buf = lbuf;
|
||||||
@ -4366,8 +4354,7 @@ enum rule_result http_wait_for_msg_body(struct stream *s, struct channel *chn,
|
|||||||
/* we get here if we need to wait for more data */
|
/* we get here if we need to wait for more data */
|
||||||
|
|
||||||
if ((s->scf->flags & SC_FL_ERROR) ||
|
if ((s->scf->flags & SC_FL_ERROR) ||
|
||||||
((s->scf->flags & (SC_FL_EOS|SC_FL_ABRT_DONE)) &&
|
((s->scf->flags & SC_FL_EOS) && proxy_abrt_close_def(s->be, 1)))
|
||||||
proxy_abrt_close_def(s->be, 1)))
|
|
||||||
ret = HTTP_RULE_RES_CONT;
|
ret = HTTP_RULE_RES_CONT;
|
||||||
else if (!(chn_prod(chn)->flags & (SC_FL_ERROR|SC_FL_EOS|SC_FL_ABRT_DONE))) {
|
else if (!(chn_prod(chn)->flags & (SC_FL_ERROR|SC_FL_EOS|SC_FL_ABRT_DONE))) {
|
||||||
if (!tick_isset(chn->analyse_exp))
|
if (!tick_isset(chn->analyse_exp))
|
||||||
@ -4747,7 +4734,7 @@ int http_forward_proxy_resp(struct stream *s, int final)
|
|||||||
if (s->txn->meth == HTTP_METH_HEAD)
|
if (s->txn->meth == HTTP_METH_HEAD)
|
||||||
htx_skip_msg_payload(htx);
|
htx_skip_msg_payload(htx);
|
||||||
|
|
||||||
/* Respnse from haproxy, override HTTP response verison using the request one */
|
/* Response from haproxy, override HTTP response version using the request one */
|
||||||
s->txn->rsp.vsn = s->txn->req.vsn;
|
s->txn->rsp.vsn = s->txn->req.vsn;
|
||||||
|
|
||||||
channel_auto_read(req);
|
channel_auto_read(req);
|
||||||
|
|||||||
@ -604,10 +604,7 @@ void httpclient_applet_io_handler(struct appctx *appctx)
|
|||||||
htx_to_buf(htx, outbuf);
|
htx_to_buf(htx, outbuf);
|
||||||
b_xfer(outbuf, &hc->req.buf, b_data(&hc->req.buf));
|
b_xfer(outbuf, &hc->req.buf, b_data(&hc->req.buf));
|
||||||
} else {
|
} else {
|
||||||
struct htx_ret ret;
|
if (!htx_xfer(htx, hc_htx, htx_used_space(hc_htx), HTX_XFER_DEFAULT)) {
|
||||||
|
|
||||||
ret = htx_xfer_blks(htx, hc_htx, htx_used_space(hc_htx), HTX_BLK_UNUSED);
|
|
||||||
if (!ret.ret) {
|
|
||||||
applet_have_more_data(appctx);
|
applet_have_more_data(appctx);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -711,7 +708,6 @@ void httpclient_applet_io_handler(struct appctx *appctx)
|
|||||||
if (hc->options & HTTPCLIENT_O_RES_HTX) {
|
if (hc->options & HTTPCLIENT_O_RES_HTX) {
|
||||||
/* HTX mode transfers the header to the hc buffer */
|
/* HTX mode transfers the header to the hc buffer */
|
||||||
struct htx *hc_htx;
|
struct htx *hc_htx;
|
||||||
struct htx_ret ret;
|
|
||||||
|
|
||||||
if (!b_alloc(&hc->res.buf, DB_MUX_TX)) {
|
if (!b_alloc(&hc->res.buf, DB_MUX_TX)) {
|
||||||
applet_wont_consume(appctx);
|
applet_wont_consume(appctx);
|
||||||
@ -720,8 +716,7 @@ void httpclient_applet_io_handler(struct appctx *appctx)
|
|||||||
hc_htx = htxbuf(&hc->res.buf);
|
hc_htx = htxbuf(&hc->res.buf);
|
||||||
|
|
||||||
/* xfer the headers */
|
/* xfer the headers */
|
||||||
ret = htx_xfer_blks(hc_htx, htx, htx_used_space(htx), HTX_BLK_EOH);
|
if (!htx_xfer(hc_htx, htx, htx_used_space(htx), HTX_XFER_HDRS_ONLY)) {
|
||||||
if (!ret.ret) {
|
|
||||||
applet_need_more_data(appctx);
|
applet_need_more_data(appctx);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -811,12 +806,10 @@ void httpclient_applet_io_handler(struct appctx *appctx)
|
|||||||
if (hc->options & HTTPCLIENT_O_RES_HTX) {
|
if (hc->options & HTTPCLIENT_O_RES_HTX) {
|
||||||
/* HTX mode transfers the header to the hc buffer */
|
/* HTX mode transfers the header to the hc buffer */
|
||||||
struct htx *hc_htx;
|
struct htx *hc_htx;
|
||||||
struct htx_ret ret;
|
|
||||||
|
|
||||||
hc_htx = htxbuf(&hc->res.buf);
|
hc_htx = htxbuf(&hc->res.buf);
|
||||||
|
|
||||||
ret = htx_xfer_blks(hc_htx, htx, htx_used_space(htx), HTX_BLK_UNUSED);
|
if (!htx_xfer(hc_htx, htx, htx_used_space(htx), HTX_XFER_DEFAULT))
|
||||||
if (!ret.ret)
|
|
||||||
applet_wont_consume(appctx);
|
applet_wont_consume(appctx);
|
||||||
else
|
else
|
||||||
applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
|
applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
|
||||||
|
|||||||
@ -44,7 +44,7 @@
|
|||||||
/* this struct is used between calls to smp_fetch_hdr() or smp_fetch_cookie() */
|
/* this struct is used between calls to smp_fetch_hdr() or smp_fetch_cookie() */
|
||||||
static THREAD_LOCAL struct http_hdr_ctx static_http_hdr_ctx;
|
static THREAD_LOCAL struct http_hdr_ctx static_http_hdr_ctx;
|
||||||
/* this is used to convert raw connection buffers to htx */
|
/* this is used to convert raw connection buffers to htx */
|
||||||
/* NOTE: For now, raw bufers cannot exceeds the standard size */
|
/* NOTE: For now, raw buffers cannot exceeds the standard size */
|
||||||
static THREAD_LOCAL struct buffer static_raw_htx_chunk;
|
static THREAD_LOCAL struct buffer static_raw_htx_chunk;
|
||||||
static THREAD_LOCAL char *static_raw_htx_buf;
|
static THREAD_LOCAL char *static_raw_htx_buf;
|
||||||
|
|
||||||
|
|||||||
175
src/http_htx.c
175
src/http_htx.c
@ -41,17 +41,18 @@ struct list http_replies_list = LIST_HEAD_INIT(http_replies_list);
|
|||||||
/* The declaration of an errorfiles/errorfile directives. Used during config
|
/* The declaration of an errorfiles/errorfile directives. Used during config
|
||||||
* parsing only. */
|
* parsing only. */
|
||||||
struct conf_errors {
|
struct conf_errors {
|
||||||
char type; /* directive type (0: errorfiles, 1: errorfile) */
|
enum http_err_directive directive; /* directive type: inline (errorfile <code> <file>) / section (errorfiles <section>) */
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
int status; /* the status code associated to this error */
|
int status; /* the status code associated to this error */
|
||||||
struct http_reply *reply; /* the http reply for the errorfile */
|
struct http_reply *reply; /* the http reply for the errorfile */
|
||||||
} errorfile; /* describe an "errorfile" directive */
|
} inl; /* for HTTP_ERR_DIRECTIVE_INLINE only */
|
||||||
struct {
|
struct {
|
||||||
char *name; /* the http-errors section name */
|
char *name; /* the http-errors section name */
|
||||||
char status[HTTP_ERR_SIZE]; /* list of status to import (0: ignore, 1: implicit import, 2: explicit import) */
|
struct http_errors *resolved; /* resolved section pointer set via proxy_check_http_errors() */
|
||||||
} errorfiles; /* describe an "errorfiles" directive */
|
enum http_err_import status[HTTP_ERR_SIZE]; /* list of status to import */
|
||||||
} info;
|
} section; /* for HTTP_ERR_DIRECTIVE_SECTION only */
|
||||||
|
} type;
|
||||||
|
|
||||||
char *file; /* file where the directive appears */
|
char *file; /* file where the directive appears */
|
||||||
int line; /* line where the directive appears */
|
int line; /* line where the directive appears */
|
||||||
@ -2034,9 +2035,9 @@ static int proxy_parse_errorloc(char **args, int section, struct proxy *curpx,
|
|||||||
ret = -1;
|
ret = -1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
conf_err->type = 1;
|
conf_err->directive = HTTP_ERR_DIRECTIVE_INLINE;
|
||||||
conf_err->info.errorfile.status = status;
|
conf_err->type.inl.status = status;
|
||||||
conf_err->info.errorfile.reply = reply;
|
conf_err->type.inl.reply = reply;
|
||||||
|
|
||||||
conf_err->file = strdup(file);
|
conf_err->file = strdup(file);
|
||||||
conf_err->line = line;
|
conf_err->line = line;
|
||||||
@ -2105,9 +2106,9 @@ static int proxy_parse_errorfile(char **args, int section, struct proxy *curpx,
|
|||||||
ret = -1;
|
ret = -1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
conf_err->type = 1;
|
conf_err->directive = HTTP_ERR_DIRECTIVE_INLINE;
|
||||||
conf_err->info.errorfile.status = status;
|
conf_err->type.inl.status = status;
|
||||||
conf_err->info.errorfile.reply = reply;
|
conf_err->type.inl.reply = reply;
|
||||||
conf_err->file = strdup(file);
|
conf_err->file = strdup(file);
|
||||||
conf_err->line = line;
|
conf_err->line = line;
|
||||||
LIST_APPEND(&curpx->conf.errors, &conf_err->list);
|
LIST_APPEND(&curpx->conf.errors, &conf_err->list);
|
||||||
@ -2146,12 +2147,12 @@ static int proxy_parse_errorfiles(char **args, int section, struct proxy *curpx,
|
|||||||
memprintf(err, "%s : out of memory.", args[0]);
|
memprintf(err, "%s : out of memory.", args[0]);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
conf_err->type = 0;
|
|
||||||
|
|
||||||
conf_err->info.errorfiles.name = name;
|
conf_err->directive = HTTP_ERR_DIRECTIVE_SECTION;
|
||||||
|
conf_err->type.section.name = name;
|
||||||
if (!*(args[2])) {
|
if (!*(args[2])) {
|
||||||
for (rc = 0; rc < HTTP_ERR_SIZE; rc++)
|
for (rc = 0; rc < HTTP_ERR_SIZE; rc++)
|
||||||
conf_err->info.errorfiles.status[rc] = 1;
|
conf_err->type.section.status[rc] = HTTP_ERR_IMPORT_IMPLICIT;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int cur_arg, status;
|
int cur_arg, status;
|
||||||
@ -2160,7 +2161,7 @@ static int proxy_parse_errorfiles(char **args, int section, struct proxy *curpx,
|
|||||||
|
|
||||||
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
|
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
|
||||||
if (http_err_codes[rc] == status) {
|
if (http_err_codes[rc] == status) {
|
||||||
conf_err->info.errorfiles.status[rc] = 2;
|
conf_err->type.section.status[rc] = HTTP_ERR_IMPORT_EXPLICIT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2231,16 +2232,16 @@ static int proxy_parse_http_error(char **args, int section, struct proxy *curpx,
|
|||||||
if (reply->type == HTTP_REPLY_ERRFILES) {
|
if (reply->type == HTTP_REPLY_ERRFILES) {
|
||||||
int rc = http_get_status_idx(reply->status);
|
int rc = http_get_status_idx(reply->status);
|
||||||
|
|
||||||
conf_err->type = 2;
|
conf_err->directive = HTTP_ERR_DIRECTIVE_SECTION;
|
||||||
conf_err->info.errorfiles.name = reply->body.http_errors;
|
conf_err->type.section.name = reply->body.http_errors;
|
||||||
conf_err->info.errorfiles.status[rc] = 2;
|
conf_err->type.section.status[rc] = HTTP_ERR_IMPORT_EXPLICIT;
|
||||||
reply->body.http_errors = NULL;
|
reply->body.http_errors = NULL;
|
||||||
release_http_reply(reply);
|
release_http_reply(reply);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
conf_err->type = 1;
|
conf_err->directive = HTTP_ERR_DIRECTIVE_INLINE;
|
||||||
conf_err->info.errorfile.status = reply->status;
|
conf_err->type.inl.status = reply->status;
|
||||||
conf_err->info.errorfile.reply = reply;
|
conf_err->type.inl.reply = reply;
|
||||||
LIST_APPEND(&http_replies_list, &reply->list);
|
LIST_APPEND(&http_replies_list, &reply->list);
|
||||||
}
|
}
|
||||||
conf_err->file = strdup(file);
|
conf_err->file = strdup(file);
|
||||||
@ -2260,60 +2261,46 @@ static int proxy_parse_http_error(char **args, int section, struct proxy *curpx,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check "errorfiles" proxy keyword */
|
/* Converts <conf_errors> initialized during config parsing for <px> proxy.
|
||||||
static int proxy_check_errors(struct proxy *px)
|
* Each one of them is transformed in a http_reply type, stored in proxy
|
||||||
|
* replies array member. The original <conf_errors> becomes unneeded and is
|
||||||
|
* thus removed and freed.
|
||||||
|
*/
|
||||||
|
static int proxy_finalize_http_errors(struct proxy *px)
|
||||||
{
|
{
|
||||||
struct conf_errors *conf_err, *conf_err_back;
|
struct conf_errors *conf_err, *conf_err_back;
|
||||||
struct http_errors *http_errs;
|
struct http_errors *http_errs;
|
||||||
int rc, err = ERR_NONE;
|
int rc;
|
||||||
|
|
||||||
list_for_each_entry_safe(conf_err, conf_err_back, &px->conf.errors, list) {
|
list_for_each_entry_safe(conf_err, conf_err_back, &px->conf.errors, list) {
|
||||||
if (conf_err->type == 1) {
|
switch (conf_err->directive) {
|
||||||
/* errorfile */
|
case HTTP_ERR_DIRECTIVE_INLINE:
|
||||||
rc = http_get_status_idx(conf_err->info.errorfile.status);
|
rc = http_get_status_idx(conf_err->type.inl.status);
|
||||||
px->replies[rc] = conf_err->info.errorfile.reply;
|
px->replies[rc] = conf_err->type.inl.reply;
|
||||||
|
|
||||||
/* For proxy, to rely on default replies, just don't reference a reply */
|
/* For proxy, to rely on default replies, just don't reference a reply */
|
||||||
if (px->replies[rc]->type == HTTP_REPLY_ERRMSG && !px->replies[rc]->body.errmsg)
|
if (px->replies[rc]->type == HTTP_REPLY_ERRMSG && !px->replies[rc]->body.errmsg)
|
||||||
px->replies[rc] = NULL;
|
px->replies[rc] = NULL;
|
||||||
}
|
break;
|
||||||
else {
|
|
||||||
/* errorfiles */
|
|
||||||
list_for_each_entry(http_errs, &http_errors_list, list) {
|
|
||||||
if (strcmp(http_errs->id, conf_err->info.errorfiles.name) == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* unknown http-errors section */
|
case HTTP_ERR_DIRECTIVE_SECTION:
|
||||||
if (&http_errs->list == &http_errors_list) {
|
http_errs = conf_err->type.section.resolved;
|
||||||
ha_alert("proxy '%s': unknown http-errors section '%s' (at %s:%d).\n",
|
if (http_errs) {
|
||||||
px->id, conf_err->info.errorfiles.name, conf_err->file, conf_err->line);
|
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
|
||||||
err |= ERR_ALERT | ERR_FATAL;
|
if (conf_err->type.section.status[rc] == HTTP_ERR_IMPORT_NO)
|
||||||
free(conf_err->info.errorfiles.name);
|
continue;
|
||||||
goto next;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(conf_err->info.errorfiles.name);
|
|
||||||
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
|
|
||||||
if (conf_err->info.errorfiles.status[rc] > 0) {
|
|
||||||
if (http_errs->replies[rc])
|
if (http_errs->replies[rc])
|
||||||
px->replies[rc] = http_errs->replies[rc];
|
px->replies[rc] = http_errs->replies[rc];
|
||||||
else if (conf_err->info.errorfiles.status[rc] == 2)
|
|
||||||
ha_warning("config: proxy '%s' : status '%d' not declared in"
|
|
||||||
" http-errors section '%s' (at %s:%d).\n",
|
|
||||||
px->id, http_err_codes[rc], http_errs->id,
|
|
||||||
conf_err->file, conf_err->line);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next:
|
|
||||||
LIST_DELETE(&conf_err->list);
|
LIST_DELETE(&conf_err->list);
|
||||||
free(conf_err->file);
|
free(conf_err->file);
|
||||||
free(conf_err);
|
free(conf_err);
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
return ERR_NONE;
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int post_check_errors()
|
static int post_check_errors()
|
||||||
@ -2343,6 +2330,55 @@ static int post_check_errors()
|
|||||||
return err_code;
|
return err_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Checks the validity of conf_errors stored in <px> proxy after the
|
||||||
|
* configuration is completely parsed.
|
||||||
|
*
|
||||||
|
* Returns ERR_NONE on success and a combination of ERR_CODE on failure.
|
||||||
|
*/
|
||||||
|
int proxy_check_http_errors(struct proxy *px)
|
||||||
|
{
|
||||||
|
struct http_errors *http_errs;
|
||||||
|
struct conf_errors *conf_err;
|
||||||
|
int section_found;
|
||||||
|
int rc, err = ERR_NONE;
|
||||||
|
|
||||||
|
list_for_each_entry(conf_err, &px->conf.errors, list) {
|
||||||
|
if (conf_err->directive == HTTP_ERR_DIRECTIVE_SECTION) {
|
||||||
|
section_found = 0;
|
||||||
|
list_for_each_entry(http_errs, &http_errors_list, list) {
|
||||||
|
if (strcmp(http_errs->id, conf_err->type.section.name) == 0) {
|
||||||
|
section_found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!section_found) {
|
||||||
|
ha_alert("proxy '%s': unknown http-errors section '%s' (at %s:%d).\n",
|
||||||
|
px->id, conf_err->type.section.name, conf_err->file, conf_err->line);
|
||||||
|
ha_free(&conf_err->type.section.name);
|
||||||
|
err |= ERR_ALERT | ERR_FATAL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf_err->type.section.resolved = http_errs;
|
||||||
|
ha_free(&conf_err->type.section.name);
|
||||||
|
|
||||||
|
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
|
||||||
|
if (conf_err->type.section.status[rc] == HTTP_ERR_IMPORT_EXPLICIT &&
|
||||||
|
!http_errs->replies[rc]) {
|
||||||
|
ha_warning("config: proxy '%s' : status '%d' not declared in"
|
||||||
|
" http-errors section '%s' (at %s:%d).\n",
|
||||||
|
px->id, http_err_codes[rc], http_errs->id,
|
||||||
|
conf_err->file, conf_err->line);
|
||||||
|
err |= ERR_WARN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
int proxy_dup_default_conf_errors(struct proxy *curpx, const struct proxy *defpx, char **errmsg)
|
int proxy_dup_default_conf_errors(struct proxy *curpx, const struct proxy *defpx, char **errmsg)
|
||||||
{
|
{
|
||||||
struct conf_errors *conf_err, *new_conf_err = NULL;
|
struct conf_errors *conf_err, *new_conf_err = NULL;
|
||||||
@ -2354,19 +2390,22 @@ int proxy_dup_default_conf_errors(struct proxy *curpx, const struct proxy *defpx
|
|||||||
memprintf(errmsg, "unable to duplicate default errors (out of memory).");
|
memprintf(errmsg, "unable to duplicate default errors (out of memory).");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
new_conf_err->type = conf_err->type;
|
new_conf_err->directive = conf_err->directive;
|
||||||
if (conf_err->type == 1) {
|
switch (conf_err->directive) {
|
||||||
new_conf_err->info.errorfile.status = conf_err->info.errorfile.status;
|
case HTTP_ERR_DIRECTIVE_INLINE:
|
||||||
new_conf_err->info.errorfile.reply = conf_err->info.errorfile.reply;
|
new_conf_err->type.inl.status = conf_err->type.inl.status;
|
||||||
}
|
new_conf_err->type.inl.reply = conf_err->type.inl.reply;
|
||||||
else {
|
break;
|
||||||
new_conf_err->info.errorfiles.name = strdup(conf_err->info.errorfiles.name);
|
|
||||||
if (!new_conf_err->info.errorfiles.name) {
|
case HTTP_ERR_DIRECTIVE_SECTION:
|
||||||
|
new_conf_err->type.section.name = strdup(conf_err->type.section.name);
|
||||||
|
if (!new_conf_err->type.section.name) {
|
||||||
memprintf(errmsg, "unable to duplicate default errors (out of memory).");
|
memprintf(errmsg, "unable to duplicate default errors (out of memory).");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
memcpy(&new_conf_err->info.errorfiles.status, &conf_err->info.errorfiles.status,
|
memcpy(&new_conf_err->type.section.status, &conf_err->type.section.status,
|
||||||
sizeof(conf_err->info.errorfiles.status));
|
sizeof(conf_err->type.section.status));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
new_conf_err->file = strdup(conf_err->file);
|
new_conf_err->file = strdup(conf_err->file);
|
||||||
new_conf_err->line = conf_err->line;
|
new_conf_err->line = conf_err->line;
|
||||||
@ -2385,8 +2424,8 @@ void proxy_release_conf_errors(struct proxy *px)
|
|||||||
struct conf_errors *conf_err, *conf_err_back;
|
struct conf_errors *conf_err, *conf_err_back;
|
||||||
|
|
||||||
list_for_each_entry_safe(conf_err, conf_err_back, &px->conf.errors, list) {
|
list_for_each_entry_safe(conf_err, conf_err_back, &px->conf.errors, list) {
|
||||||
if (conf_err->type == 0)
|
if (conf_err->directive == HTTP_ERR_DIRECTIVE_SECTION)
|
||||||
free(conf_err->info.errorfiles.name);
|
free(conf_err->type.section.name);
|
||||||
LIST_DELETE(&conf_err->list);
|
LIST_DELETE(&conf_err->list);
|
||||||
free(conf_err->file);
|
free(conf_err->file);
|
||||||
free(conf_err);
|
free(conf_err);
|
||||||
@ -2505,7 +2544,7 @@ static struct cfg_kw_list cfg_kws = {ILH, {
|
|||||||
}};
|
}};
|
||||||
|
|
||||||
INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
|
INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
|
||||||
REGISTER_POST_PROXY_CHECK(proxy_check_errors);
|
REGISTER_POST_PROXY_CHECK(proxy_finalize_http_errors);
|
||||||
REGISTER_POST_CHECK(post_check_errors);
|
REGISTER_POST_CHECK(post_check_errors);
|
||||||
|
|
||||||
REGISTER_CONFIG_SECTION("http-errors", cfg_parse_http_errors, NULL);
|
REGISTER_CONFIG_SECTION("http-errors", cfg_parse_http_errors, NULL);
|
||||||
|
|||||||
219
src/htx.c
219
src/htx.c
@ -11,6 +11,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <haproxy/chunk.h>
|
#include <haproxy/chunk.h>
|
||||||
|
#include <haproxy/dynbuf.h>
|
||||||
#include <haproxy/global.h>
|
#include <haproxy/global.h>
|
||||||
#include <haproxy/htx.h>
|
#include <haproxy/htx.h>
|
||||||
#include <haproxy/net_helper.h>
|
#include <haproxy/net_helper.h>
|
||||||
@ -719,10 +720,163 @@ struct htx_blk *htx_replace_blk_value(struct htx *htx, struct htx_blk *blk,
|
|||||||
return blk;
|
return blk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Transfer HTX blocks from <src> to <dst>, stopping if <count> bytes were
|
||||||
|
* transferred (including payload and meta-data). It returns the number of bytes
|
||||||
|
* copied. By default, copied blocks are removed from <src> and only full
|
||||||
|
* headers and trailers part can be moved. <flags> can be set to change the
|
||||||
|
* default behavior:
|
||||||
|
* - HTX_XFER_KEEP_SRC_BLKS: source blocks are not removed
|
||||||
|
* - HTX_XFER_PARTIAL_HDRS_COPY: partial headers and trailers part can be xferred
|
||||||
|
* - HTX_XFER_HDRS_ONLY: Only the headers part is xferred
|
||||||
|
*/
|
||||||
|
size_t htx_xfer(struct htx *dst, struct htx *src, size_t count, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct htx_blk *blk, *last_dstblk;
|
||||||
|
size_t ret = 0;
|
||||||
|
uint32_t max, last_dstblk_sz;
|
||||||
|
int dst_full = 0;
|
||||||
|
|
||||||
|
last_dstblk = NULL;
|
||||||
|
last_dstblk_sz = 0;
|
||||||
|
for (blk = htx_get_head_blk(src); blk && count; blk = htx_get_next_blk(src, blk)) {
|
||||||
|
struct ist v;
|
||||||
|
enum htx_blk_type type;
|
||||||
|
uint32_t sz;
|
||||||
|
|
||||||
|
/* Ignore unused block */
|
||||||
|
type = htx_get_blk_type(blk);
|
||||||
|
if (type == HTX_BLK_UNUSED)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((flags & HTX_XFER_HDRS_ONLY) &&
|
||||||
|
type != HTX_BLK_REQ_SL && type != HTX_BLK_RES_SL &&
|
||||||
|
type != HTX_BLK_HDR && type != HTX_BLK_EOH)
|
||||||
|
break;
|
||||||
|
|
||||||
|
max = htx_get_max_blksz(dst, count);
|
||||||
|
if (!max)
|
||||||
|
break;
|
||||||
|
|
||||||
|
sz = htx_get_blksz(blk);
|
||||||
|
switch (type) {
|
||||||
|
case HTX_BLK_DATA:
|
||||||
|
v = htx_get_blk_value(src, blk);
|
||||||
|
if (v.len > max)
|
||||||
|
v.len = max;
|
||||||
|
v.len = htx_add_data(dst, v);
|
||||||
|
if (!v.len) {
|
||||||
|
dst_full = 1;
|
||||||
|
goto stop;
|
||||||
|
}
|
||||||
|
last_dstblk = htx_get_tail_blk(dst);
|
||||||
|
last_dstblk_sz = v.len;
|
||||||
|
count -= sizeof(*blk) + v.len;
|
||||||
|
ret += sizeof(*blk) + v.len;
|
||||||
|
if (v.len != sz) {
|
||||||
|
dst_full = 1;
|
||||||
|
goto stop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (sz > max) {
|
||||||
|
dst_full = 1;
|
||||||
|
goto stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_dstblk = htx_add_blk(dst, type, sz);
|
||||||
|
if (!last_dstblk) {
|
||||||
|
dst_full = 1;
|
||||||
|
goto stop;
|
||||||
|
}
|
||||||
|
last_dstblk->info = blk->info;
|
||||||
|
htx_memcpy(htx_get_blk_ptr(dst, last_dstblk), htx_get_blk_ptr(src, blk), sz);
|
||||||
|
last_dstblk_sz = sz;
|
||||||
|
count -= sizeof(*blk) + sz;
|
||||||
|
ret += sizeof(*blk) + sz;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_dstblk = NULL; /* Reset last_dstblk because it was fully copied */
|
||||||
|
last_dstblk_sz = 0;
|
||||||
|
}
|
||||||
|
stop:
|
||||||
|
/* Here, if not NULL, <blk> point on the first not fully copied block in
|
||||||
|
* <src>. And <last_dstblk>, if defined, is the last not fully copied
|
||||||
|
* block in <dst>. So have:
|
||||||
|
* - <blk> == NULL: everything was copied. <last_dstblk> must be NULL
|
||||||
|
* - <blk> != NULL && <last_dstblk> == NULL: partial copy but the last block was fully copied
|
||||||
|
* - <blk> != NULL && <last_dstblk> != NULL: partial copy and the last block was partially copied (DATA block only)
|
||||||
|
*/
|
||||||
|
if (!(flags & HTX_XFER_PARTIAL_HDRS_COPY)) {
|
||||||
|
/* Partial headers/trailers copy is not supported */
|
||||||
|
struct htx_blk *dstblk;
|
||||||
|
enum htx_blk_type type = HTX_BLK_UNUSED;
|
||||||
|
|
||||||
|
dstblk = htx_get_tail_blk(dst);
|
||||||
|
if (dstblk)
|
||||||
|
type = htx_get_blk_type(dstblk);
|
||||||
|
|
||||||
|
/* the last copied block is a start-line, a header or a trailer */
|
||||||
|
if (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL || type == HTX_BLK_HDR || type == HTX_BLK_TLR) {
|
||||||
|
/* <src > cannot have partial headers or trailers part */
|
||||||
|
BUG_ON(blk == NULL);
|
||||||
|
|
||||||
|
/* Remove partial headers/trailers from <dst> and rollback on <str> to not remove them later */
|
||||||
|
while (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL || type == HTX_BLK_HDR || type == HTX_BLK_TLR) {
|
||||||
|
BUG_ON(type != htx_get_blk_type(blk));
|
||||||
|
ret -= sizeof(*blk) + htx_get_blksz(blk);
|
||||||
|
htx_remove_blk(dst, dstblk);
|
||||||
|
dstblk = htx_get_tail_blk(dst);
|
||||||
|
blk = htx_get_prev_blk(src, blk);
|
||||||
|
if (!dstblk)
|
||||||
|
break;
|
||||||
|
type = htx_get_blk_type(dstblk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Report if the xfer was interrupted because <dst> was
|
||||||
|
* full but is was originally empty
|
||||||
|
*/
|
||||||
|
if (dst_full && htx_is_empty(dst))
|
||||||
|
src->flags |= HTX_FL_PARSING_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(flags & HTX_XFER_KEEP_SRC_BLKS)) {
|
||||||
|
/* True xfer performed, remove copied block from <src> */
|
||||||
|
struct htx_blk *blk2;
|
||||||
|
|
||||||
|
/* Remove all fully copied blocks */
|
||||||
|
if (!blk)
|
||||||
|
htx_drain(src, src->data);
|
||||||
|
else {
|
||||||
|
for (blk2 = htx_get_head_blk(src); blk2 && blk2 != blk; blk2 = htx_remove_blk(src, blk2));
|
||||||
|
|
||||||
|
/* If copy was stopped on a DATA block and the last destination
|
||||||
|
* block is not NULL, it means a partial copy was performed. So
|
||||||
|
* cut the source block accordingly
|
||||||
|
*/
|
||||||
|
if (last_dstblk && blk2 && htx_get_blk_type(blk2) == HTX_BLK_DATA) {
|
||||||
|
htx_cut_data_blk(src, blk2, last_dstblk_sz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Everything was copied, transfer terminal HTX flags too */
|
||||||
|
if (!blk) {
|
||||||
|
dst->flags |= (src->flags & (HTX_FL_EOM|HTX_FL_PARSING_ERROR|HTX_FL_PROCESSING_ERROR));
|
||||||
|
src->flags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Transfer HTX blocks from <src> to <dst>, stopping once the first block of the
|
/* Transfer HTX blocks from <src> to <dst>, stopping once the first block of the
|
||||||
* type <mark> is transferred (typically EOH or EOT) or when <count> bytes were
|
* type <mark> is transferred (typically EOH or EOT) or when <count> bytes were
|
||||||
* moved (including payload and meta-data). It returns the number of bytes moved
|
* moved (including payload and meta-data). It returns the number of bytes moved
|
||||||
* and the last HTX block inserted in <dst>.
|
* and the last HTX block inserted in <dst>.
|
||||||
|
*
|
||||||
|
* DEPRECATED
|
||||||
*/
|
*/
|
||||||
struct htx_ret htx_xfer_blks(struct htx *dst, struct htx *src, uint32_t count,
|
struct htx_ret htx_xfer_blks(struct htx *dst, struct htx *src, uint32_t count,
|
||||||
enum htx_blk_type mark)
|
enum htx_blk_type mark)
|
||||||
@ -1181,3 +1335,68 @@ int htx_append_msg(struct htx *dst, const struct htx *src)
|
|||||||
htx_truncate(dst, offset);
|
htx_truncate(dst, offset);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* If possible, transfer HTX blocks from <src> to a small buffer. This function
|
||||||
|
* allocate the small buffer and makes <dst> point on it. If <dst> is not empty
|
||||||
|
* or if <src> contains to many data, NULL is returned. If the allocation
|
||||||
|
* failed, NULL is returned. Otherwise <dst> is returned. <flags> instructs how
|
||||||
|
* the transfer must be performed.
|
||||||
|
*/
|
||||||
|
struct buffer *__htx_xfer_to_small_buffer(struct buffer *dst, struct buffer *src, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct htx *dst_htx;
|
||||||
|
struct htx *src_htx = htxbuf(src);
|
||||||
|
size_t sz = (sizeof(struct htx) + htx_used_space(src_htx));
|
||||||
|
|
||||||
|
if (dst->size || sz > global.tune.bufsize_small || !b_alloc_small(dst))
|
||||||
|
return NULL;
|
||||||
|
dst_htx = htx_from_buf(dst);
|
||||||
|
htx_xfer(dst_htx, src_htx, src_htx->size, flags);
|
||||||
|
htx_to_buf(dst_htx, dst);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If possible, transfer HTX blocks from <src> to a large buffer. This function
|
||||||
|
* allocate the small buffer and makes <dst> point on it. If <dst> is not empty
|
||||||
|
* or if <src> contains to many data, NULL is returned. If the allocation
|
||||||
|
* failed, NULL is returned. Otherwise <dst> is returned. <flags> instructs how
|
||||||
|
* the transfer must be performed.
|
||||||
|
*/
|
||||||
|
struct buffer *__htx_xfer_to_large_buffer(struct buffer *dst, struct buffer *src, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct htx *dst_htx;
|
||||||
|
struct htx *src_htx = htxbuf(src);
|
||||||
|
size_t sz = (sizeof(struct htx) + htx_used_space(src_htx));
|
||||||
|
|
||||||
|
if (dst->size || sz > global.tune.bufsize_large || !b_alloc_large(dst))
|
||||||
|
return NULL;
|
||||||
|
dst_htx = htx_from_buf(dst);
|
||||||
|
htx_xfer(dst_htx, src_htx, src_htx->size, flags);
|
||||||
|
htx_to_buf(dst_htx, dst);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move HTX blocks from <src> to <dst>. Relies on __htx_xfer_to_small_buffer() */
|
||||||
|
struct buffer *htx_move_to_small_buffer(struct buffer *dst, struct buffer *src)
|
||||||
|
{
|
||||||
|
return __htx_xfer_to_small_buffer(dst, src, HTX_XFER_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move HTX blocks from <src> to <dst>. Relies on __htx_xfer_to_large_buffer() */
|
||||||
|
struct buffer *htx_move_to_large_buffer(struct buffer *dst, struct buffer *src)
|
||||||
|
{
|
||||||
|
return __htx_xfer_to_large_buffer(dst, src, HTX_XFER_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy HTX blocks from <src> to <dst>. Relies on __htx_xfer_to_small_buffer() */
|
||||||
|
struct buffer *htx_copy_to_small_buffer(struct buffer *dst, struct buffer *src)
|
||||||
|
{
|
||||||
|
return __htx_xfer_to_small_buffer(dst, src, HTX_XFER_KEEP_SRC_BLKS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy HTX blocks from <src> to <dst>. Relies on __htx_xfer_to_large_buffer() */
|
||||||
|
struct buffer *htx_copy_to_large_buffer(struct buffer *dst, struct buffer *src)
|
||||||
|
{
|
||||||
|
return __htx_xfer_to_large_buffer(dst, src, HTX_XFER_KEEP_SRC_BLKS);
|
||||||
|
}
|
||||||
|
|||||||
@ -424,7 +424,7 @@ end:
|
|||||||
* the one found in the JWE token.
|
* the one found in the JWE token.
|
||||||
* The tag is built out of a HMAC of some concatenated data taken from the JWE
|
* The tag is built out of a HMAC of some concatenated data taken from the JWE
|
||||||
* token (see https://datatracker.ietf.org/doc/html/rfc7518#section-5.2). The
|
* token (see https://datatracker.ietf.org/doc/html/rfc7518#section-5.2). The
|
||||||
* firest half of the previously decrypted cek is used as HMAC key.
|
* first half of the previously decrypted cek is used as HMAC key.
|
||||||
* Returns 0 in case of success, 1 otherwise.
|
* Returns 0 in case of success, 1 otherwise.
|
||||||
*/
|
*/
|
||||||
static int build_and_check_tag(jwe_enc enc, struct jwt_item items[JWE_ELT_MAX],
|
static int build_and_check_tag(jwe_enc enc, struct jwt_item items[JWE_ELT_MAX],
|
||||||
@ -602,7 +602,7 @@ static inline void clear_decoded_items(struct buffer *decoded_items[JWE_ELT_MAX]
|
|||||||
/*
|
/*
|
||||||
* Decrypt the contents of a JWE token thanks to the user-provided base64
|
* Decrypt the contents of a JWE token thanks to the user-provided base64
|
||||||
* encoded secret. This converter can only be used for tokens that have a
|
* encoded secret. This converter can only be used for tokens that have a
|
||||||
* symetric algorithm (AESKW, AESGCMKW or "dir" special case).
|
* symmetric algorithm (AESKW, AESGCMKW or "dir" special case).
|
||||||
* Returns the decrypted contents, or nothing if any error happened.
|
* Returns the decrypted contents, or nothing if any error happened.
|
||||||
*/
|
*/
|
||||||
static int sample_conv_jwt_decrypt_secret(const struct arg *args, struct sample *smp, void *private)
|
static int sample_conv_jwt_decrypt_secret(const struct arg *args, struct sample *smp, void *private)
|
||||||
@ -1096,7 +1096,7 @@ end:
|
|||||||
/*
|
/*
|
||||||
* Decrypt the contents of a JWE token thanks to the user-provided certificate
|
* Decrypt the contents of a JWE token thanks to the user-provided certificate
|
||||||
* and private key. This converter can only be used for tokens that have an
|
* and private key. This converter can only be used for tokens that have an
|
||||||
* asymetric algorithm (RSA only for now).
|
* asymmetric algorithm (RSA only for now).
|
||||||
* Returns the decrypted contents, or nothing if any error happened.
|
* Returns the decrypted contents, or nothing if any error happened.
|
||||||
*/
|
*/
|
||||||
static int sample_conv_jwt_decrypt_cert(const struct arg *args, struct sample *smp, void *private)
|
static int sample_conv_jwt_decrypt_cert(const struct arg *args, struct sample *smp, void *private)
|
||||||
@ -1173,7 +1173,7 @@ static int sample_conv_jwt_decrypt_cert(const struct arg *args, struct sample *s
|
|||||||
/* With ECDH-ES no CEK will be provided. */
|
/* With ECDH-ES no CEK will be provided. */
|
||||||
if (!ec || alg != JWE_ALG_ECDH_ES) {
|
if (!ec || alg != JWE_ALG_ECDH_ES) {
|
||||||
|
|
||||||
/* With asymetric crypto algorithms we should always have a CEK */
|
/* With asymmetric crypto algorithms we should always have a CEK */
|
||||||
if (!items[JWE_ELT_CEK].length)
|
if (!items[JWE_ELT_CEK].length)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
|
|||||||
116
src/log.c
116
src/log.c
@ -2913,6 +2913,7 @@ static inline void __send_log_set_metadata_sd(struct ist *metadata, char *sd, si
|
|||||||
struct process_send_log_ctx {
|
struct process_send_log_ctx {
|
||||||
struct session *sess;
|
struct session *sess;
|
||||||
struct stream *stream;
|
struct stream *stream;
|
||||||
|
struct log_profile *profile;
|
||||||
struct log_orig origin;
|
struct log_orig origin;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2942,6 +2943,10 @@ static inline void _process_send_log_override(struct process_send_log_ctx *ctx,
|
|||||||
enum log_orig_id orig = (ctx) ? ctx->origin.id : LOG_ORIG_UNSPEC;
|
enum log_orig_id orig = (ctx) ? ctx->origin.id : LOG_ORIG_UNSPEC;
|
||||||
uint16_t orig_fl = (ctx) ? ctx->origin.flags : LOG_ORIG_FL_NONE;
|
uint16_t orig_fl = (ctx) ? ctx->origin.flags : LOG_ORIG_FL_NONE;
|
||||||
|
|
||||||
|
/* ctx->profile gets priority over logger profile */
|
||||||
|
if (ctx && ctx->profile)
|
||||||
|
prof = ctx->profile;
|
||||||
|
|
||||||
BUG_ON(!prof);
|
BUG_ON(!prof);
|
||||||
|
|
||||||
if (!b_is_null(&prof->log_tag))
|
if (!b_is_null(&prof->log_tag))
|
||||||
@ -3095,8 +3100,8 @@ static void process_send_log(struct process_send_log_ctx *ctx,
|
|||||||
|
|
||||||
nblogger += 1;
|
nblogger += 1;
|
||||||
|
|
||||||
/* logger may use a profile to override a few things */
|
/* caller or default logger may use a profile to override a few things */
|
||||||
if (unlikely(logger->prof))
|
if (unlikely(logger->prof || (ctx && ctx->profile)))
|
||||||
_process_send_log_override(ctx, logger, hdr, message, size, nblogger);
|
_process_send_log_override(ctx, logger, hdr, message, size, nblogger);
|
||||||
else
|
else
|
||||||
_process_send_log_final(logger, hdr, message, size, nblogger);
|
_process_send_log_final(logger, hdr, message, size, nblogger);
|
||||||
@ -5200,17 +5205,11 @@ out:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void do_log_ctx(struct process_send_log_ctx *ctx)
|
||||||
* opportunistic log when at least the session is known to exist
|
|
||||||
* <s> may be NULL
|
|
||||||
*
|
|
||||||
* Will not log if the frontend has no log defined. By default it will
|
|
||||||
* try to emit the log as INFO, unless the stream already exists and
|
|
||||||
* set-log-level was used.
|
|
||||||
*/
|
|
||||||
void do_log(struct session *sess, struct stream *s, struct log_orig origin)
|
|
||||||
{
|
{
|
||||||
struct process_send_log_ctx ctx;
|
struct stream *s = ctx->stream;
|
||||||
|
struct session *sess = ctx->sess;
|
||||||
|
struct log_orig origin = ctx->origin;
|
||||||
int size;
|
int size;
|
||||||
int sd_size = 0;
|
int sd_size = 0;
|
||||||
int level = -1;
|
int level = -1;
|
||||||
@ -5242,11 +5241,27 @@ void do_log(struct session *sess, struct stream *s, struct log_orig origin)
|
|||||||
|
|
||||||
size = sess_build_logline_orig(sess, s, logline, global.max_syslog_len, &sess->fe->logformat, origin);
|
size = sess_build_logline_orig(sess, s, logline, global.max_syslog_len, &sess->fe->logformat, origin);
|
||||||
|
|
||||||
|
__send_log(ctx, &sess->fe->loggers, &sess->fe->log_tag, level,
|
||||||
|
logline, size, logline_rfc5424, sd_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* opportunistic log when at least the session is known to exist
|
||||||
|
* <s> may be NULL
|
||||||
|
*
|
||||||
|
* Will not log if the frontend has no log defined. By default it will
|
||||||
|
* try to emit the log as INFO, unless the stream already exists and
|
||||||
|
* set-log-level was used.
|
||||||
|
*/
|
||||||
|
void do_log(struct session *sess, struct stream *s, struct log_orig origin)
|
||||||
|
{
|
||||||
|
struct process_send_log_ctx ctx;
|
||||||
|
|
||||||
ctx.origin = origin;
|
ctx.origin = origin;
|
||||||
ctx.sess = sess;
|
ctx.sess = sess;
|
||||||
ctx.stream = s;
|
ctx.stream = s;
|
||||||
__send_log(&ctx, &sess->fe->loggers, &sess->fe->log_tag, level,
|
ctx.profile = NULL;
|
||||||
logline, size, logline_rfc5424, sd_size);
|
do_log_ctx(&ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -5297,6 +5312,7 @@ void strm_log(struct stream *s, struct log_orig origin)
|
|||||||
ctx.origin = origin;
|
ctx.origin = origin;
|
||||||
ctx.sess = sess;
|
ctx.sess = sess;
|
||||||
ctx.stream = s;
|
ctx.stream = s;
|
||||||
|
ctx.profile = NULL;
|
||||||
__send_log(&ctx, &sess->fe->loggers, &sess->fe->log_tag, level,
|
__send_log(&ctx, &sess->fe->loggers, &sess->fe->log_tag, level,
|
||||||
logline, size, logline_rfc5424, sd_size);
|
logline, size, logline_rfc5424, sd_size);
|
||||||
s->logs.logwait = 0;
|
s->logs.logwait = 0;
|
||||||
@ -5364,6 +5380,7 @@ void _sess_log(struct session *sess, int embryonic)
|
|||||||
ctx.origin = orig;
|
ctx.origin = orig;
|
||||||
ctx.sess = sess;
|
ctx.sess = sess;
|
||||||
ctx.stream = NULL;
|
ctx.stream = NULL;
|
||||||
|
ctx.profile = NULL;
|
||||||
__send_log(&ctx, &sess->fe->loggers,
|
__send_log(&ctx, &sess->fe->loggers,
|
||||||
&sess->fe->log_tag, level,
|
&sess->fe->log_tag, level,
|
||||||
logline, size, logline_rfc5424, sd_size);
|
logline, size, logline_rfc5424, sd_size);
|
||||||
@ -6910,24 +6927,87 @@ static int px_parse_log_steps(char **args, int section_type, struct proxy *curpx
|
|||||||
static enum act_return do_log_action(struct act_rule *rule, struct proxy *px,
|
static enum act_return do_log_action(struct act_rule *rule, struct proxy *px,
|
||||||
struct session *sess, struct stream *s, int flags)
|
struct session *sess, struct stream *s, int flags)
|
||||||
{
|
{
|
||||||
|
struct process_send_log_ctx ctx;
|
||||||
|
|
||||||
/* do_log() expects valid session pointer */
|
/* do_log() expects valid session pointer */
|
||||||
BUG_ON(sess == NULL);
|
BUG_ON(sess == NULL);
|
||||||
|
|
||||||
do_log(sess, s, log_orig(rule->arg.expr_int.value, LOG_ORIG_FL_NONE));
|
ctx.origin = log_orig(rule->arg.do_log.orig, LOG_ORIG_FL_NONE);
|
||||||
|
ctx.sess = sess;
|
||||||
|
ctx.stream = s;
|
||||||
|
ctx.profile = rule->arg.do_log.profile;
|
||||||
|
|
||||||
|
do_log_ctx(&ctx);
|
||||||
return ACT_RET_CONT;
|
return ACT_RET_CONT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse a "do_log" action. It doesn't take any argument
|
static int do_log_action_check(struct act_rule *rule, struct proxy *px, char **err)
|
||||||
|
{
|
||||||
|
if (rule->arg.do_log.profile_name) {
|
||||||
|
struct log_profile *prof;
|
||||||
|
|
||||||
|
prof = log_profile_find_by_name(rule->arg.do_log.profile_name);
|
||||||
|
if (!prof) {
|
||||||
|
memprintf(err, "do-log action: profile '%s' is invalid", rule->arg.do_log.profile_name);
|
||||||
|
ha_free(&rule->arg.do_log.profile_name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha_free(&rule->arg.do_log.profile_name);
|
||||||
|
|
||||||
|
if (!log_profile_postcheck(px, prof, err)) {
|
||||||
|
memprintf(err, "do-log action on %s %s uses incompatible log-profile '%s': %s", proxy_type_str(px), px->id, prof->id, *err);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
rule->arg.do_log.profile = prof;
|
||||||
|
}
|
||||||
|
return 1; // success
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_log_action_release(struct act_rule *rule)
|
||||||
|
{
|
||||||
|
ha_free(&rule->arg.do_log.profile_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Parse a "do_log" action. It takes optional "log-profile" argument to
|
||||||
|
* specifically use a given log-profile when generating the log message
|
||||||
|
*
|
||||||
* May be used from places where per-context actions are usually registered
|
* May be used from places where per-context actions are usually registered
|
||||||
*/
|
*/
|
||||||
enum act_parse_ret do_log_parse_act(enum log_orig_id id,
|
enum act_parse_ret do_log_parse_act(enum log_orig_id id,
|
||||||
const char **args, int *orig_arg, struct proxy *px,
|
const char **args, int *orig_arg, struct proxy *px,
|
||||||
struct act_rule *rule, char **err)
|
struct act_rule *rule, char **err)
|
||||||
{
|
{
|
||||||
|
int cur_arg = *orig_arg;
|
||||||
|
|
||||||
rule->action_ptr = do_log_action;
|
rule->action_ptr = do_log_action;
|
||||||
rule->action = ACT_CUSTOM;
|
rule->action = ACT_CUSTOM;
|
||||||
rule->release_ptr = NULL;
|
rule->check_ptr = do_log_action_check;
|
||||||
rule->arg.expr_int.value = id;
|
rule->release_ptr = do_log_action_release;
|
||||||
|
rule->arg.do_log.orig = id;
|
||||||
|
|
||||||
|
while (*args[*orig_arg]) {
|
||||||
|
if (!strcmp(args[*orig_arg], "profile")) {
|
||||||
|
if (!*args[*orig_arg + 1]) {
|
||||||
|
memprintf(err,
|
||||||
|
"action '%s': 'profile' expects argument.",
|
||||||
|
args[cur_arg-1]);
|
||||||
|
return ACT_RET_PRS_ERR;
|
||||||
|
}
|
||||||
|
rule->arg.do_log.profile_name = strdup(args[*orig_arg + 1]);
|
||||||
|
if (!rule->arg.do_log.profile_name) {
|
||||||
|
memprintf(err,
|
||||||
|
"action '%s': memory error when setting 'profile'",
|
||||||
|
args[cur_arg-1]);
|
||||||
|
return ACT_RET_PRS_ERR;
|
||||||
|
}
|
||||||
|
*orig_arg += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return ACT_RET_PRS_OK;
|
return ACT_RET_PRS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
82
src/mux_h2.c
82
src/mux_h2.c
@ -489,6 +489,9 @@ static int h2_be_glitches_threshold = 0; /* backend's max glitches
|
|||||||
static int h2_fe_glitches_threshold = 0; /* frontend's max glitches: unlimited */
|
static int h2_fe_glitches_threshold = 0; /* frontend's max glitches: unlimited */
|
||||||
static uint h2_be_rxbuf = 0; /* backend's default total rxbuf (bytes) */
|
static uint h2_be_rxbuf = 0; /* backend's default total rxbuf (bytes) */
|
||||||
static uint h2_fe_rxbuf = 0; /* frontend's default total rxbuf (bytes) */
|
static uint h2_fe_rxbuf = 0; /* frontend's default total rxbuf (bytes) */
|
||||||
|
static unsigned int h2_be_max_frames_at_once = 0; /* backend value: 0=no limit */
|
||||||
|
static unsigned int h2_fe_max_frames_at_once = 0; /* frontend value: 0=no limit */
|
||||||
|
static unsigned int h2_fe_max_rst_at_once = 0; /* frontend value: 0=no limit */
|
||||||
static unsigned int h2_settings_max_concurrent_streams = 100; /* default value */
|
static unsigned int h2_settings_max_concurrent_streams = 100; /* default value */
|
||||||
static unsigned int h2_be_settings_max_concurrent_streams = 0; /* backend value */
|
static unsigned int h2_be_settings_max_concurrent_streams = 0; /* backend value */
|
||||||
static unsigned int h2_fe_settings_max_concurrent_streams = 0; /* frontend value */
|
static unsigned int h2_fe_settings_max_concurrent_streams = 0; /* frontend value */
|
||||||
@ -4239,6 +4242,8 @@ static void h2_process_demux(struct h2c *h2c)
|
|||||||
struct h2_fh hdr;
|
struct h2_fh hdr;
|
||||||
unsigned int padlen = 0;
|
unsigned int padlen = 0;
|
||||||
int32_t old_iw = h2c->miw;
|
int32_t old_iw = h2c->miw;
|
||||||
|
uint frames_budget = 0;
|
||||||
|
uint rst_budget = 0;
|
||||||
|
|
||||||
TRACE_ENTER(H2_EV_H2C_WAKE, h2c->conn);
|
TRACE_ENTER(H2_EV_H2C_WAKE, h2c->conn);
|
||||||
|
|
||||||
@ -4327,6 +4332,14 @@ static void h2_process_demux(struct h2c *h2c)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (h2c->flags & H2_CF_IS_BACK) {
|
||||||
|
frames_budget = h2_be_max_frames_at_once;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
frames_budget = h2_fe_max_frames_at_once;
|
||||||
|
rst_budget = h2_fe_max_rst_at_once;
|
||||||
|
}
|
||||||
|
|
||||||
/* process as many incoming frames as possible below */
|
/* process as many incoming frames as possible below */
|
||||||
while (1) {
|
while (1) {
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@ -4629,6 +4642,29 @@ static void h2_process_demux(struct h2c *h2c)
|
|||||||
h2c->st0 = H2_CS_FRAME_H;
|
h2c->st0 = H2_CS_FRAME_H;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If more frames remain in the buffer, let's first check if we've
|
||||||
|
* depleted the frames processing budget. Consuming the RST budget
|
||||||
|
* makes the tasklet go to TL_BULK to make it less priority than
|
||||||
|
* other processing since it's often used by attacks, while other
|
||||||
|
* frame types just yield normally.
|
||||||
|
*/
|
||||||
|
if (b_data(&h2c->dbuf)) {
|
||||||
|
if (h2c->dft == H2_FT_RST_STREAM && (rst_budget && !--rst_budget)) {
|
||||||
|
/* we've consumed all RST frames permitted by
|
||||||
|
* the budget, we have to yield now.
|
||||||
|
*/
|
||||||
|
tasklet_wakeup(h2c->wait_event.tasklet, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if ((frames_budget && !--frames_budget)) {
|
||||||
|
/* we've consumed all frames permitted by the
|
||||||
|
* budget, we have to yield now.
|
||||||
|
*/
|
||||||
|
tasklet_wakeup(h2c->wait_event.tasklet);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (h2c_update_strm_rx_win(h2c) &&
|
if (h2c_update_strm_rx_win(h2c) &&
|
||||||
@ -7830,7 +7866,6 @@ static size_t h2_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, in
|
|||||||
struct htx *h2s_htx = NULL;
|
struct htx *h2s_htx = NULL;
|
||||||
struct htx *buf_htx = NULL;
|
struct htx *buf_htx = NULL;
|
||||||
struct buffer *rxbuf = NULL;
|
struct buffer *rxbuf = NULL;
|
||||||
struct htx_ret htxret;
|
|
||||||
size_t ret = 0;
|
size_t ret = 0;
|
||||||
uint prev_h2c_flags = h2c->flags;
|
uint prev_h2c_flags = h2c->flags;
|
||||||
unsigned long long prev_body_len = h2s->body_len;
|
unsigned long long prev_body_len = h2s->body_len;
|
||||||
@ -7865,17 +7900,7 @@ static size_t h2_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, in
|
|||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
htxret = htx_xfer_blks(buf_htx, h2s_htx, count, HTX_BLK_UNUSED);
|
count -= htx_xfer(buf_htx, h2s_htx, count, HTX_XFER_DEFAULT);
|
||||||
count -= htxret.ret;
|
|
||||||
|
|
||||||
if (h2s_htx->flags & HTX_FL_PARSING_ERROR) {
|
|
||||||
buf_htx->flags |= HTX_FL_PARSING_ERROR;
|
|
||||||
if (htx_is_empty(buf_htx))
|
|
||||||
se_fl_set(h2s->sd, SE_FL_EOI);
|
|
||||||
}
|
|
||||||
else if (htx_is_empty(h2s_htx)) {
|
|
||||||
buf_htx->flags |= (h2s_htx->flags & HTX_FL_EOM);
|
|
||||||
}
|
|
||||||
|
|
||||||
htx_to_buf(buf_htx, buf);
|
htx_to_buf(buf_htx, buf);
|
||||||
htx_to_buf(h2s_htx, rxbuf);
|
htx_to_buf(h2s_htx, rxbuf);
|
||||||
@ -7904,13 +7929,7 @@ static size_t h2_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, in
|
|||||||
|
|
||||||
/* tell the stream layer whether there are data left or not */
|
/* tell the stream layer whether there are data left or not */
|
||||||
if (h2s_rxbuf_cnt(h2s)) {
|
if (h2s_rxbuf_cnt(h2s)) {
|
||||||
/* Note that parsing errors can also arrive here, we may need
|
|
||||||
* to propagate errors upstream otherwise no new activity will
|
|
||||||
* unblock them.
|
|
||||||
*/
|
|
||||||
se_fl_set(h2s->sd, SE_FL_RCV_MORE | SE_FL_WANT_ROOM);
|
se_fl_set(h2s->sd, SE_FL_RCV_MORE | SE_FL_WANT_ROOM);
|
||||||
if (h2s_htx && h2s_htx->flags & HTX_FL_PARSING_ERROR)
|
|
||||||
h2s_propagate_term_flags(h2c, h2s);
|
|
||||||
BUG_ON_HOT(!buf->data);
|
BUG_ON_HOT(!buf->data);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -8800,6 +8819,30 @@ static int h2_parse_max_total_streams(char **args, int section_type, struct prox
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* config parser for global "tune.h2.{be.,fe.,}max-{frames,rst}-at-once" */
|
||||||
|
static int h2_parse_max_frames_at_once(char **args, int section_type, struct proxy *curpx,
|
||||||
|
const struct proxy *defpx, const char *file, int line,
|
||||||
|
char **err)
|
||||||
|
{
|
||||||
|
uint *vptr;
|
||||||
|
|
||||||
|
/* backend/frontend/default */
|
||||||
|
if (strcmp(args[0], "tune.h2.be.max-frames-at-once") == 0)
|
||||||
|
vptr = &h2_be_max_frames_at_once;
|
||||||
|
else if (strcmp(args[0], "tune.h2.fe.max-frames-at-once") == 0)
|
||||||
|
vptr = &h2_fe_max_frames_at_once;
|
||||||
|
else if (strcmp(args[0], "tune.h2.fe.max-rst-at-once") == 0)
|
||||||
|
vptr = &h2_fe_max_rst_at_once;
|
||||||
|
else
|
||||||
|
BUG_ON(1, "unhandled keyword");
|
||||||
|
|
||||||
|
if (too_many_args(1, args, err, NULL))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*vptr = atoi(args[1]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* config parser for global "tune.h2.max-frame-size" */
|
/* config parser for global "tune.h2.max-frame-size" */
|
||||||
static int h2_parse_max_frame_size(char **args, int section_type, struct proxy *curpx,
|
static int h2_parse_max_frame_size(char **args, int section_type, struct proxy *curpx,
|
||||||
const struct proxy *defpx, const char *file, int line,
|
const struct proxy *defpx, const char *file, int line,
|
||||||
@ -8898,10 +8941,13 @@ static struct cfg_kw_list cfg_kws = {ILH, {
|
|||||||
{ CFG_GLOBAL, "tune.h2.be.glitches-threshold", h2_parse_glitches_threshold },
|
{ CFG_GLOBAL, "tune.h2.be.glitches-threshold", h2_parse_glitches_threshold },
|
||||||
{ CFG_GLOBAL, "tune.h2.be.initial-window-size", h2_parse_initial_window_size },
|
{ CFG_GLOBAL, "tune.h2.be.initial-window-size", h2_parse_initial_window_size },
|
||||||
{ CFG_GLOBAL, "tune.h2.be.max-concurrent-streams", h2_parse_max_concurrent_streams },
|
{ CFG_GLOBAL, "tune.h2.be.max-concurrent-streams", h2_parse_max_concurrent_streams },
|
||||||
|
{ CFG_GLOBAL, "tune.h2.be.max-frames-at-once", h2_parse_max_frames_at_once },
|
||||||
{ CFG_GLOBAL, "tune.h2.be.rxbuf", h2_parse_rxbuf },
|
{ CFG_GLOBAL, "tune.h2.be.rxbuf", h2_parse_rxbuf },
|
||||||
{ CFG_GLOBAL, "tune.h2.fe.glitches-threshold", h2_parse_glitches_threshold },
|
{ CFG_GLOBAL, "tune.h2.fe.glitches-threshold", h2_parse_glitches_threshold },
|
||||||
{ CFG_GLOBAL, "tune.h2.fe.initial-window-size", h2_parse_initial_window_size },
|
{ CFG_GLOBAL, "tune.h2.fe.initial-window-size", h2_parse_initial_window_size },
|
||||||
{ CFG_GLOBAL, "tune.h2.fe.max-concurrent-streams", h2_parse_max_concurrent_streams },
|
{ CFG_GLOBAL, "tune.h2.fe.max-concurrent-streams", h2_parse_max_concurrent_streams },
|
||||||
|
{ CFG_GLOBAL, "tune.h2.fe.max-frames-at-once", h2_parse_max_frames_at_once },
|
||||||
|
{ CFG_GLOBAL, "tune.h2.fe.max-rst-at-once", h2_parse_max_frames_at_once },
|
||||||
{ CFG_GLOBAL, "tune.h2.fe.max-total-streams", h2_parse_max_total_streams },
|
{ CFG_GLOBAL, "tune.h2.fe.max-total-streams", h2_parse_max_total_streams },
|
||||||
{ CFG_GLOBAL, "tune.h2.fe.rxbuf", h2_parse_rxbuf },
|
{ CFG_GLOBAL, "tune.h2.fe.rxbuf", h2_parse_rxbuf },
|
||||||
{ CFG_GLOBAL, "tune.h2.header-table-size", h2_parse_header_table_size },
|
{ CFG_GLOBAL, "tune.h2.header-table-size", h2_parse_header_table_size },
|
||||||
|
|||||||
18
src/proxy.c
18
src/proxy.c
@ -1676,11 +1676,17 @@ int proxy_finalize(struct proxy *px, int *err_code)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bind_conf->mux_proto) {
|
if (bind_conf->mux_proto) {
|
||||||
|
int is_quic;
|
||||||
|
|
||||||
|
if ((bind_conf->options & (BC_O_USE_SOCK_DGRAM | BC_O_USE_XPRT_STREAM)) == (BC_O_USE_SOCK_DGRAM | BC_O_USE_XPRT_STREAM))
|
||||||
|
is_quic = 1;
|
||||||
|
else
|
||||||
|
is_quic = 0;
|
||||||
/* it is possible that an incorrect mux was referenced
|
/* it is possible that an incorrect mux was referenced
|
||||||
* due to the proxy's mode not being taken into account
|
* due to the proxy's mode not being taken into account
|
||||||
* on first pass. Let's adjust it now.
|
* on first pass. Let's adjust it now.
|
||||||
*/
|
*/
|
||||||
mux_ent = conn_get_best_mux_entry(bind_conf->mux_proto->token, PROTO_SIDE_FE, mode);
|
mux_ent = conn_get_best_mux_entry(bind_conf->mux_proto->token, PROTO_SIDE_FE, is_quic, mode);
|
||||||
|
|
||||||
if (!mux_ent || !isteq(mux_ent->token, bind_conf->mux_proto->token)) {
|
if (!mux_ent || !isteq(mux_ent->token, bind_conf->mux_proto->token)) {
|
||||||
ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for 'bind %s' at [%s:%d].\n",
|
ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for 'bind %s' at [%s:%d].\n",
|
||||||
@ -2672,6 +2678,8 @@ int proxy_finalize(struct proxy *px, int *err_code)
|
|||||||
*err_code |= ERR_WARN;
|
*err_code |= ERR_WARN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*err_code |= proxy_check_http_errors(px);
|
||||||
|
|
||||||
if (px->mode != PR_MODE_HTTP && !(px->options & PR_O_HTTP_UPG)) {
|
if (px->mode != PR_MODE_HTTP && !(px->options & PR_O_HTTP_UPG)) {
|
||||||
int optnum;
|
int optnum;
|
||||||
|
|
||||||
@ -2877,7 +2885,7 @@ int proxy_finalize(struct proxy *px, int *err_code)
|
|||||||
* due to the proxy's mode not being taken into account
|
* due to the proxy's mode not being taken into account
|
||||||
* on first pass. Let's adjust it now.
|
* on first pass. Let's adjust it now.
|
||||||
*/
|
*/
|
||||||
mux_ent = conn_get_best_mux_entry(newsrv->mux_proto->token, PROTO_SIDE_BE, mode);
|
mux_ent = conn_get_best_mux_entry(newsrv->mux_proto->token, PROTO_SIDE_BE, srv_is_quic(newsrv), mode);
|
||||||
|
|
||||||
if (!mux_ent || !isteq(mux_ent->token, newsrv->mux_proto->token)) {
|
if (!mux_ent || !isteq(mux_ent->token, newsrv->mux_proto->token)) {
|
||||||
ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for server '%s' at [%s:%d].\n",
|
ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for server '%s' at [%s:%d].\n",
|
||||||
@ -4908,7 +4916,7 @@ static int cli_parse_add_backend(char **args, char *payload, struct appctx *appc
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (!(defpx->flags & PR_FL_DEF_EXPLICIT_MODE) && !mode) {
|
if (!(defpx->flags & PR_FL_DEF_EXPLICIT_MODE) && !mode) {
|
||||||
cli_dynerr(appctx, memprintf(&msg, "Mode is required as '%s' default proxy does not explicitely defines it.\n", def_name));
|
cli_dynerr(appctx, memprintf(&msg, "Mode is required as '%s' default proxy does not explicitly defines it.\n", def_name));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (defpx->mode != PR_MODE_TCP && defpx->mode != PR_MODE_HTTP) {
|
if (defpx->mode != PR_MODE_TCP && defpx->mode != PR_MODE_HTTP) {
|
||||||
@ -4916,10 +4924,6 @@ static int cli_parse_add_backend(char **args, char *payload, struct appctx *appc
|
|||||||
def_name, proxy_mode_str(defpx->mode)));
|
def_name, proxy_mode_str(defpx->mode)));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (!LIST_ISEMPTY(&defpx->conf.errors)) {
|
|
||||||
cli_dynerr(appctx, memprintf(&msg, "Dynamic backends cannot inherit from default proxy '%s' because it references HTTP errors.\n", def_name));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
thread_isolate();
|
thread_isolate();
|
||||||
|
|
||||||
|
|||||||
@ -44,7 +44,7 @@ size_t qcs_http_rcv_buf(struct qcs *qcs, struct buffer *buf, size_t count,
|
|||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
htx_xfer_blks(cs_htx, qcs_htx, count, HTX_BLK_UNUSED);
|
htx_xfer(cs_htx, qcs_htx, count, HTX_XFER_DEFAULT);
|
||||||
BUG_ON(qcs_htx->flags & HTX_FL_PARSING_ERROR);
|
BUG_ON(qcs_htx->flags & HTX_FL_PARSING_ERROR);
|
||||||
|
|
||||||
/* Copy EOM from src to dst buffer if all data copied. */
|
/* Copy EOM from src to dst buffer if all data copied. */
|
||||||
|
|||||||
@ -61,10 +61,10 @@
|
|||||||
static uint64_t qpack_get_varint(const unsigned char **buf, uint64_t *len_in, int b)
|
static uint64_t qpack_get_varint(const unsigned char **buf, uint64_t *len_in, int b)
|
||||||
{
|
{
|
||||||
uint64_t ret = 0;
|
uint64_t ret = 0;
|
||||||
int len = *len_in;
|
uint64_t len = *len_in;
|
||||||
const uint8_t *raw = *buf;
|
const uint8_t *raw = *buf;
|
||||||
uint64_t v, max = ~0;
|
uint64_t v, limit = (1ULL << 62) - 1;
|
||||||
uint8_t shift = 0;
|
int shift = 0;
|
||||||
|
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
goto too_short;
|
goto too_short;
|
||||||
@ -77,24 +77,26 @@ static uint64_t qpack_get_varint(const unsigned char **buf, uint64_t *len_in, in
|
|||||||
do {
|
do {
|
||||||
if (!len)
|
if (!len)
|
||||||
goto too_short;
|
goto too_short;
|
||||||
|
|
||||||
v = *raw++;
|
v = *raw++;
|
||||||
len--;
|
len--;
|
||||||
if (v & 127) { // make UBSan happy
|
/* This check is sufficient to prevent any overflow
|
||||||
if ((v & 127) > max)
|
* and implicitly limits shift to 63.
|
||||||
goto too_large;
|
*/
|
||||||
ret += (v & 127) << shift;
|
if ((v & 127) > (limit - ret) >> shift)
|
||||||
}
|
goto too_large;
|
||||||
max >>= 7;
|
|
||||||
|
ret += (v & 127) << shift;
|
||||||
shift += 7;
|
shift += 7;
|
||||||
} while (v & 128);
|
} while (v & 128);
|
||||||
|
|
||||||
end:
|
end:
|
||||||
*buf = raw;
|
*buf = raw;
|
||||||
*len_in = len;
|
*len_in = len;
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
too_large:
|
too_large:
|
||||||
too_short:
|
too_short:
|
||||||
*len_in = (uint64_t)-1;
|
*len_in = (uint64_t)-1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -402,7 +404,10 @@ int qpack_decode_fs(const unsigned char *raw, uint64_t len, struct buffer *tmp,
|
|||||||
n = efl_type & 0x20;
|
n = efl_type & 0x20;
|
||||||
static_tbl = efl_type & 0x10;
|
static_tbl = efl_type & 0x10;
|
||||||
index = qpack_get_varint(&raw, &len, 4);
|
index = qpack_get_varint(&raw, &len, 4);
|
||||||
if (len == (uint64_t)-1) {
|
/* There must be at least one byte available for <h> value after this
|
||||||
|
* decoding before the next call to qpack_get_varint().
|
||||||
|
*/
|
||||||
|
if ((int64_t)len <= 0) {
|
||||||
qpack_debug_printf(stderr, "##ERR@%d\n", __LINE__);
|
qpack_debug_printf(stderr, "##ERR@%d\n", __LINE__);
|
||||||
ret = -QPACK_RET_TRUNCATED;
|
ret = -QPACK_RET_TRUNCATED;
|
||||||
goto out;
|
goto out;
|
||||||
@ -474,7 +479,10 @@ int qpack_decode_fs(const unsigned char *raw, uint64_t len, struct buffer *tmp,
|
|||||||
n = *raw & 0x10;
|
n = *raw & 0x10;
|
||||||
hname = *raw & 0x08;
|
hname = *raw & 0x08;
|
||||||
name_len = qpack_get_varint(&raw, &len, 3);
|
name_len = qpack_get_varint(&raw, &len, 3);
|
||||||
if (len == (uint64_t)-1 || len < name_len) {
|
/* There must be at least one byte available for <hvalue> after this
|
||||||
|
* decoding before the next call to qpack_get_varint().
|
||||||
|
*/
|
||||||
|
if ((int64_t)len < (int64_t)name_len + 1) {
|
||||||
qpack_debug_printf(stderr, "##ERR@%d\n", __LINE__);
|
qpack_debug_printf(stderr, "##ERR@%d\n", __LINE__);
|
||||||
ret = -QPACK_RET_TRUNCATED;
|
ret = -QPACK_RET_TRUNCATED;
|
||||||
goto out;
|
goto out;
|
||||||
|
|||||||
@ -1185,7 +1185,14 @@ int qc_parse_frm(struct quic_frame *frm, struct quic_rx_packet *pkt,
|
|||||||
|
|
||||||
parser = qf_parser(frm->type);
|
parser = qf_parser(frm->type);
|
||||||
if (!(parser->mask & (1U << pkt->type))) {
|
if (!(parser->mask & (1U << pkt->type))) {
|
||||||
|
/* RFC 9000 12.4. Frames and Frame Types
|
||||||
|
*
|
||||||
|
* An endpoint MUST treat
|
||||||
|
* receipt of a frame in a packet type that is not permitted as a
|
||||||
|
* connection error of type PROTOCOL_VIOLATION.
|
||||||
|
*/
|
||||||
TRACE_DEVEL("unauthorized frame", QUIC_EV_CONN_PRSFRM, qc, frm);
|
TRACE_DEVEL("unauthorized frame", QUIC_EV_CONN_PRSFRM, qc, frm);
|
||||||
|
quic_set_connection_close(qc, quic_err_transport(QC_ERR_PROTOCOL_VIOLATION));
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1363,7 +1363,7 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc, void *target)
|
|||||||
/* This code is called by connect_server() by way of
|
/* This code is called by connect_server() by way of
|
||||||
* conn_prepare().
|
* conn_prepare().
|
||||||
* XXX TODO XXX: there is a remaining race condition where
|
* XXX TODO XXX: there is a remaining race condition where
|
||||||
* the negotiated alpn could be resetted before running this code
|
* the negotiated alpn could be reset before running this code
|
||||||
* here. In this case the app_ops for the mux will not be
|
* here. In this case the app_ops for the mux will not be
|
||||||
* set by quic_reuse_srv_params().
|
* set by quic_reuse_srv_params().
|
||||||
*
|
*
|
||||||
|
|||||||
@ -16,8 +16,6 @@ DECLARE_STATIC_TYPED_POOL(pool_head_quic_stream_desc, "qc_stream_desc", struct q
|
|||||||
DECLARE_STATIC_TYPED_POOL(pool_head_quic_stream_buf, "qc_stream_buf", struct qc_stream_buf);
|
DECLARE_STATIC_TYPED_POOL(pool_head_quic_stream_buf, "qc_stream_buf", struct qc_stream_buf);
|
||||||
DECLARE_STATIC_TYPED_POOL(pool_head_quic_stream_ack, "qc_stream_ack", struct qc_stream_ack);
|
DECLARE_STATIC_TYPED_POOL(pool_head_quic_stream_ack, "qc_stream_ack", struct qc_stream_ack);
|
||||||
|
|
||||||
static struct pool_head *pool_head_sbuf;
|
|
||||||
|
|
||||||
static void qc_stream_buf_free(struct qc_stream_desc *stream,
|
static void qc_stream_buf_free(struct qc_stream_desc *stream,
|
||||||
struct qc_stream_buf **stream_buf)
|
struct qc_stream_buf **stream_buf)
|
||||||
{
|
{
|
||||||
@ -39,13 +37,10 @@ static void qc_stream_buf_free(struct qc_stream_desc *stream,
|
|||||||
room = b_data(buf);
|
room = b_data(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((*stream_buf)->sbuf) {
|
b_free(buf);
|
||||||
pool_free(pool_head_sbuf, buf->area);
|
if (!(*stream_buf)->sbuf) {
|
||||||
}
|
|
||||||
else {
|
|
||||||
bdata_ctr_del(&stream->data, b_data(buf));
|
bdata_ctr_del(&stream->data, b_data(buf));
|
||||||
bdata_ctr_bdec(&stream->data);
|
bdata_ctr_bdec(&stream->data);
|
||||||
b_free(buf);
|
|
||||||
offer_buffers(NULL, 1);
|
offer_buffers(NULL, 1);
|
||||||
}
|
}
|
||||||
pool_free(pool_head_quic_stream_buf, *stream_buf);
|
pool_free(pool_head_quic_stream_buf, *stream_buf);
|
||||||
@ -412,10 +407,7 @@ void qc_stream_desc_free(struct qc_stream_desc *stream, int closing)
|
|||||||
pool_free(pool_head_quic_stream_ack, ack);
|
pool_free(pool_head_quic_stream_ack, ack);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf->sbuf)
|
b_free(&buf->buf);
|
||||||
pool_free(pool_head_sbuf, buf->buf.area);
|
|
||||||
else
|
|
||||||
b_free(&buf->buf);
|
|
||||||
|
|
||||||
eb64_delete(&buf->offset_node);
|
eb64_delete(&buf->offset_node);
|
||||||
pool_free(pool_head_quic_stream_buf, buf);
|
pool_free(pool_head_quic_stream_buf, buf);
|
||||||
@ -461,7 +453,7 @@ struct buffer *qc_stream_buf_alloc(struct qc_stream_desc *stream,
|
|||||||
stream->buf->buf = BUF_NULL;
|
stream->buf->buf = BUF_NULL;
|
||||||
stream->buf->offset_node.key = offset;
|
stream->buf->offset_node.key = offset;
|
||||||
|
|
||||||
if (!small) {
|
if (!small || !global.tune.bufsize_small) {
|
||||||
stream->buf->sbuf = 0;
|
stream->buf->sbuf = 0;
|
||||||
if (!b_alloc(&stream->buf->buf, DB_MUX_TX)) {
|
if (!b_alloc(&stream->buf->buf, DB_MUX_TX)) {
|
||||||
pool_free(pool_head_quic_stream_buf, stream->buf);
|
pool_free(pool_head_quic_stream_buf, stream->buf);
|
||||||
@ -470,16 +462,12 @@ struct buffer *qc_stream_buf_alloc(struct qc_stream_desc *stream,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
char *area;
|
if (!b_alloc_small(&stream->buf->buf)) {
|
||||||
|
|
||||||
if (!(area = pool_alloc(pool_head_sbuf))) {
|
|
||||||
pool_free(pool_head_quic_stream_buf, stream->buf);
|
pool_free(pool_head_quic_stream_buf, stream->buf);
|
||||||
stream->buf = NULL;
|
stream->buf = NULL;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream->buf->sbuf = 1;
|
stream->buf->sbuf = 1;
|
||||||
stream->buf->buf = b_make(area, global.tune.bufsize_small, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
eb64_insert(&stream->buf_tree, &stream->buf->offset_node);
|
eb64_insert(&stream->buf_tree, &stream->buf->offset_node);
|
||||||
@ -502,7 +490,7 @@ struct buffer *qc_stream_buf_realloc(struct qc_stream_desc *stream)
|
|||||||
BUG_ON(b_data(&stream->buf->buf));
|
BUG_ON(b_data(&stream->buf->buf));
|
||||||
|
|
||||||
/* Release buffer */
|
/* Release buffer */
|
||||||
pool_free(pool_head_sbuf, stream->buf->buf.area);
|
b_free(&stream->buf->buf);
|
||||||
stream->buf->buf = BUF_NULL;
|
stream->buf->buf = BUF_NULL;
|
||||||
stream->buf->sbuf = 0;
|
stream->buf->sbuf = 0;
|
||||||
|
|
||||||
@ -536,23 +524,3 @@ void qc_stream_buf_release(struct qc_stream_desc *stream)
|
|||||||
if (stream->notify_room && room)
|
if (stream->notify_room && room)
|
||||||
stream->notify_room(stream, room);
|
stream->notify_room(stream, room);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int create_sbuf_pool(void)
|
|
||||||
{
|
|
||||||
if (global.tune.bufsize_small > global.tune.bufsize) {
|
|
||||||
ha_warning("invalid small buffer size %d bytes which is greater to default bufsize %d bytes.\n",
|
|
||||||
global.tune.bufsize_small, global.tune.bufsize);
|
|
||||||
return ERR_FATAL|ERR_ABORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
pool_head_sbuf = create_pool("sbuf", global.tune.bufsize_small,
|
|
||||||
MEM_F_SHARED|MEM_F_EXACT);
|
|
||||||
if (!pool_head_sbuf) {
|
|
||||||
ha_warning("error on small buffer pool allocation.\n");
|
|
||||||
return ERR_FATAL|ERR_ABORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ERR_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
REGISTER_POST_CHECK(create_sbuf_pool);
|
|
||||||
|
|||||||
38
src/server.c
38
src/server.c
@ -37,6 +37,7 @@
|
|||||||
#include <haproxy/namespace.h>
|
#include <haproxy/namespace.h>
|
||||||
#include <haproxy/port_range.h>
|
#include <haproxy/port_range.h>
|
||||||
#include <haproxy/protocol.h>
|
#include <haproxy/protocol.h>
|
||||||
|
#include <haproxy/proto_tcp.h>
|
||||||
#include <haproxy/proxy.h>
|
#include <haproxy/proxy.h>
|
||||||
#include <haproxy/queue.h>
|
#include <haproxy/queue.h>
|
||||||
#include <haproxy/quic_tp.h>
|
#include <haproxy/quic_tp.h>
|
||||||
@ -2938,7 +2939,9 @@ void srv_settings_cpy(struct server *srv, const struct server *src, int srv_tmpl
|
|||||||
}
|
}
|
||||||
srv->use_ssl = src->use_ssl;
|
srv->use_ssl = src->use_ssl;
|
||||||
srv->check.addr = src->check.addr;
|
srv->check.addr = src->check.addr;
|
||||||
|
srv->check.proto = src->check.proto;
|
||||||
srv->agent.addr = src->agent.addr;
|
srv->agent.addr = src->agent.addr;
|
||||||
|
srv->agent.proto = src->agent.proto;
|
||||||
srv->check.use_ssl = src->check.use_ssl;
|
srv->check.use_ssl = src->check.use_ssl;
|
||||||
srv->check.port = src->check.port;
|
srv->check.port = src->check.port;
|
||||||
if (src->check.sni != NULL)
|
if (src->check.sni != NULL)
|
||||||
@ -4635,6 +4638,11 @@ out:
|
|||||||
set_srv_agent_addr(s, &sk);
|
set_srv_agent_addr(s, &sk);
|
||||||
if (port)
|
if (port)
|
||||||
set_srv_agent_port(s, new_port);
|
set_srv_agent_port(s, new_port);
|
||||||
|
/* Agent currently only uses TCP */
|
||||||
|
if (sk.ss_family == AF_INET)
|
||||||
|
s->agent.proto = &proto_tcpv4;
|
||||||
|
else
|
||||||
|
s->agent.proto = &proto_tcpv6;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -4646,7 +4654,8 @@ out:
|
|||||||
*/
|
*/
|
||||||
const char *srv_update_check_addr_port(struct server *s, const char *addr, const char *port)
|
const char *srv_update_check_addr_port(struct server *s, const char *addr, const char *port)
|
||||||
{
|
{
|
||||||
struct sockaddr_storage sk;
|
struct sockaddr_storage *sk = NULL;
|
||||||
|
struct protocol *proto = NULL;
|
||||||
struct buffer *msg;
|
struct buffer *msg;
|
||||||
int new_port;
|
int new_port;
|
||||||
|
|
||||||
@ -4658,8 +4667,8 @@ const char *srv_update_check_addr_port(struct server *s, const char *addr, const
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (addr) {
|
if (addr) {
|
||||||
memset(&sk, 0, sizeof(struct sockaddr_storage));
|
sk = str2sa_range(addr, NULL, NULL, NULL, NULL, &proto, NULL, NULL, NULL, NULL, NULL, 0);
|
||||||
if (str2ip2(addr, &sk, 0) == NULL) {
|
if (sk == NULL) {
|
||||||
chunk_appendf(msg, "invalid addr '%s'", addr);
|
chunk_appendf(msg, "invalid addr '%s'", addr);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -4683,8 +4692,10 @@ out:
|
|||||||
if (msg->data)
|
if (msg->data)
|
||||||
return msg->area;
|
return msg->area;
|
||||||
else {
|
else {
|
||||||
if (addr)
|
if (sk) {
|
||||||
s->check.addr = sk;
|
s->check.addr = *sk;
|
||||||
|
s->check.proto = proto;
|
||||||
|
}
|
||||||
if (port)
|
if (port)
|
||||||
s->check.port = new_port;
|
s->check.port = new_port;
|
||||||
|
|
||||||
@ -6230,7 +6241,7 @@ static int cli_parse_add_server(char **args, char *payload, struct appctx *appct
|
|||||||
int proto_mode = conn_pr_mode_to_proto_mode(be->mode);
|
int proto_mode = conn_pr_mode_to_proto_mode(be->mode);
|
||||||
const struct mux_proto_list *mux_ent;
|
const struct mux_proto_list *mux_ent;
|
||||||
|
|
||||||
mux_ent = conn_get_best_mux_entry(srv->mux_proto->token, PROTO_SIDE_BE, proto_mode);
|
mux_ent = conn_get_best_mux_entry(srv->mux_proto->token, PROTO_SIDE_BE, srv_is_quic(srv), proto_mode);
|
||||||
|
|
||||||
if (!mux_ent || !isteq(mux_ent->token, srv->mux_proto->token)) {
|
if (!mux_ent || !isteq(mux_ent->token, srv->mux_proto->token)) {
|
||||||
ha_alert("MUX protocol is not usable for server.\n");
|
ha_alert("MUX protocol is not usable for server.\n");
|
||||||
@ -7612,7 +7623,7 @@ static void srv_close_idle_conns(struct server *srv)
|
|||||||
|
|
||||||
REGISTER_SERVER_DEINIT(srv_close_idle_conns);
|
REGISTER_SERVER_DEINIT(srv_close_idle_conns);
|
||||||
|
|
||||||
/* config parser for global "tune.idle-pool.shared", accepts "on" or "off" */
|
/* config parser for global "tune.idle-pool.shared", accepts "full", "on" or "off" */
|
||||||
static int cfg_parse_idle_pool_shared(char **args, int section_type, struct proxy *curpx,
|
static int cfg_parse_idle_pool_shared(char **args, int section_type, struct proxy *curpx,
|
||||||
const struct proxy *defpx, const char *file, int line,
|
const struct proxy *defpx, const char *file, int line,
|
||||||
char **err)
|
char **err)
|
||||||
@ -7620,12 +7631,17 @@ static int cfg_parse_idle_pool_shared(char **args, int section_type, struct prox
|
|||||||
if (too_many_args(1, args, err, NULL))
|
if (too_many_args(1, args, err, NULL))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (strcmp(args[1], "on") == 0)
|
if (strcmp(args[1], "full") == 0) {
|
||||||
global.tune.options |= GTUNE_IDLE_POOL_SHARED;
|
global.tune.options |= GTUNE_IDLE_POOL_SHARED;
|
||||||
else if (strcmp(args[1], "off") == 0)
|
global.tune.tg_takeover = FULL_THREADGROUP_TAKEOVER;
|
||||||
|
} else if (strcmp(args[1], "on") == 0) {
|
||||||
|
global.tune.options |= GTUNE_IDLE_POOL_SHARED;
|
||||||
|
global.tune.tg_takeover = RESTRICTED_THREADGROUP_TAKEOVER;
|
||||||
|
} else if (strcmp(args[1], "off") == 0) {
|
||||||
global.tune.options &= ~GTUNE_IDLE_POOL_SHARED;
|
global.tune.options &= ~GTUNE_IDLE_POOL_SHARED;
|
||||||
else {
|
global.tune.tg_takeover = NO_THREADGROUP_TAKEOVER;
|
||||||
memprintf(err, "'%s' expects either 'on' or 'off' but got '%s'.", args[0], args[1]);
|
} else {
|
||||||
|
memprintf(err, "'%s' expects 'auto', 'on' or 'off' but got '%s'.", args[0], args[1]);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -162,8 +162,8 @@ struct connection *sock_accept_conn(struct listener *l, int *status)
|
|||||||
case ENFILE:
|
case ENFILE:
|
||||||
if (p)
|
if (p)
|
||||||
send_log(p, LOG_EMERG,
|
send_log(p, LOG_EMERG,
|
||||||
"Proxy %s reached system FD limit (maxsock=%d). Please check system tunables.\n",
|
"Proxy %s reached system FD limit (actconn=%d). Please check system tunables.\n",
|
||||||
p->id, global.maxsock);
|
p->id, actconn);
|
||||||
ret = CO_AC_PAUSE;
|
ret = CO_AC_PAUSE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -179,8 +179,8 @@ struct connection *sock_accept_conn(struct listener *l, int *status)
|
|||||||
case ENOMEM:
|
case ENOMEM:
|
||||||
if (p)
|
if (p)
|
||||||
send_log(p, LOG_EMERG,
|
send_log(p, LOG_EMERG,
|
||||||
"Proxy %s reached system memory limit (maxsock=%d). Please check system tunables.\n",
|
"Proxy %s reached system memory limit (actconn=%d). Please check system tunables.\n",
|
||||||
p->id, global.maxsock);
|
p->id, actconn);
|
||||||
ret = CO_AC_PAUSE;
|
ret = CO_AC_PAUSE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
25
src/stconn.c
25
src/stconn.c
@ -1190,7 +1190,7 @@ int sc_conn_recv(struct stconn *sc)
|
|||||||
* SE_FL_RCV_MORE on the SC if more space is needed.
|
* SE_FL_RCV_MORE on the SC if more space is needed.
|
||||||
*/
|
*/
|
||||||
max = channel_recv_max(ic);
|
max = channel_recv_max(ic);
|
||||||
if ((ic->flags & CF_WROTE_DATA) && b_is_large(sc_ib(sc)))
|
if (b_is_small(sc_ib(sc)) || ((ic->flags & CF_WROTE_DATA) && b_is_large(sc_ib(sc))))
|
||||||
max = 0;
|
max = 0;
|
||||||
ret = CALL_MUX_WITH_RET(conn->mux, rcv_buf(sc, &ic->buf, max, cur_flags));
|
ret = CALL_MUX_WITH_RET(conn->mux, rcv_buf(sc, &ic->buf, max, cur_flags));
|
||||||
|
|
||||||
@ -1496,16 +1496,21 @@ int sc_conn_send(struct stconn *sc)
|
|||||||
if (s->txn->req.msg_state != HTTP_MSG_DONE || b_is_large(&oc->buf))
|
if (s->txn->req.msg_state != HTTP_MSG_DONE || b_is_large(&oc->buf))
|
||||||
s->txn->flags &= ~TX_L7_RETRY;
|
s->txn->flags &= ~TX_L7_RETRY;
|
||||||
else {
|
else {
|
||||||
if (b_alloc(&s->txn->l7_buffer, DB_UNLIKELY) == NULL)
|
if (!(s->be->options2 & PR_O2_USE_SBUF_L7_RETRY) ||
|
||||||
s->txn->flags &= ~TX_L7_RETRY;
|
!htx_copy_to_small_buffer(&s->txn->l7_buffer, &oc->buf)) {
|
||||||
else {
|
if (b_alloc(&s->txn->l7_buffer, DB_UNLIKELY) == NULL)
|
||||||
memcpy(b_orig(&s->txn->l7_buffer),
|
s->txn->flags &= ~TX_L7_RETRY;
|
||||||
b_orig(&oc->buf),
|
else {
|
||||||
b_size(&oc->buf));
|
memcpy(b_orig(&s->txn->l7_buffer),
|
||||||
s->txn->l7_buffer.head = co_data(oc);
|
b_orig(&oc->buf),
|
||||||
b_add(&s->txn->l7_buffer, co_data(oc));
|
b_size(&oc->buf));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s->txn->flags & TX_L7_RETRY) {
|
||||||
|
s->txn->l7_buffer.head = co_data(oc);
|
||||||
|
b_set_data(&s->txn->l7_buffer, co_data(oc));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1861,7 +1866,7 @@ int sc_applet_recv(struct stconn *sc)
|
|||||||
* SE_FL_RCV_MORE on the SC if more space is needed.
|
* SE_FL_RCV_MORE on the SC if more space is needed.
|
||||||
*/
|
*/
|
||||||
max = channel_recv_max(ic);
|
max = channel_recv_max(ic);
|
||||||
if ((ic->flags & CF_WROTE_DATA) && b_is_large(sc_ib(sc)))
|
if (b_is_small(sc_ib(sc)) || ((ic->flags & CF_WROTE_DATA) && b_is_large(sc_ib(sc))))
|
||||||
max = 0;
|
max = 0;
|
||||||
ret = appctx_rcv_buf(sc, &ic->buf, max, flags);
|
ret = appctx_rcv_buf(sc, &ic->buf, max, flags);
|
||||||
if (sc_ep_test(sc, SE_FL_WANT_ROOM)) {
|
if (sc_ep_test(sc, SE_FL_WANT_ROOM)) {
|
||||||
|
|||||||
@ -6313,7 +6313,7 @@ static int stk_promex_metric_info(unsigned int id, struct promex_metric *metric,
|
|||||||
break;
|
break;
|
||||||
case STICKTABLE_LOCAL_UPDATES:
|
case STICKTABLE_LOCAL_UPDATES:
|
||||||
*metric = (struct promex_metric){ .n = ist("local_updates"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_MODULE_METRIC };
|
*metric = (struct promex_metric){ .n = ist("local_updates"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_MODULE_METRIC };
|
||||||
*desc = ist("Cumulative number of updates on the stick table initiated by the local process. Please note that this value will eventually wrap after 4294967295 since it is stored using unsigned int (uint32). As this metric is often used to compute the update rate of a given table between two queries, wrapping must be taken into account and the time between 2 queries must not exceed the theorical time needed for this value to wrap.");
|
*desc = ist("Cumulative number of updates on the stick table initiated by the local process. Please note that this value will eventually wrap after 4294967295 since it is stored using unsigned int (uint32). As this metric is often used to compute the update rate of a given table between two queries, wrapping must be taken into account and the time between 2 queries must not exceed the theoretical time needed for this value to wrap.");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
31
src/stream.c
31
src/stream.c
@ -1138,12 +1138,12 @@ enum act_return process_use_service(struct act_rule *rule, struct proxy *px,
|
|||||||
return ACT_RET_STOP;
|
return ACT_RET_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parses persist-rules attached to <fe> frontend and report the first macthing
|
/* Parses persist-rules attached to <fe> frontend and report the first matching
|
||||||
* entry, using <sess> session and <s> stream as sample source.
|
* entry, using <sess> session and <s> stream as sample source.
|
||||||
*
|
*
|
||||||
* As this function is called several times in the same stream context,
|
* As this function is called several times in the same stream context,
|
||||||
* <persist> will act as a caching value to avoid reprocessing of a similar
|
* <persist> will act as a caching value to avoid reprocessing of a similar
|
||||||
* ruleset. It must be set to a negative value for the first invokation.
|
* ruleset. It must be set to a negative value for the first invocation.
|
||||||
*
|
*
|
||||||
* Returns 1 if a rule matches, else 0.
|
* Returns 1 if a rule matches, else 0.
|
||||||
*/
|
*/
|
||||||
@ -2511,6 +2511,29 @@ struct task *process_stream(struct task *t, void *context, unsigned int state)
|
|||||||
srv = objt_server(s->target);
|
srv = objt_server(s->target);
|
||||||
if (scb->state == SC_ST_ASS && srv && srv->rdr_len && (s->flags & SF_REDIRECTABLE))
|
if (scb->state == SC_ST_ASS && srv && srv->rdr_len && (s->flags & SF_REDIRECTABLE))
|
||||||
http_perform_server_redirect(s, scb);
|
http_perform_server_redirect(s, scb);
|
||||||
|
|
||||||
|
if (unlikely((s->be->options2 & PR_O2_USE_SBUF_QUEUE) && scb->state == SC_ST_QUE)) {
|
||||||
|
struct buffer sbuf = BUF_NULL;
|
||||||
|
|
||||||
|
if (IS_HTX_STRM(s)) {
|
||||||
|
if (!htx_move_to_small_buffer(&sbuf, &req->buf))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (b_size(&req->buf) == global.tune.bufsize_small ||
|
||||||
|
b_data(&req->buf) > global.tune.bufsize_small)
|
||||||
|
break;
|
||||||
|
if (!b_alloc_small(&sbuf))
|
||||||
|
break;
|
||||||
|
b_xfer(&sbuf, &req->buf, b_data(&req->buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
b_free(&req->buf);
|
||||||
|
offer_buffers(s, 1);
|
||||||
|
req->buf = sbuf;
|
||||||
|
DBG_TRACE_DEVEL("request moved to a small buffer", STRM_EV_STRM_PROC, s);
|
||||||
|
}
|
||||||
|
|
||||||
} while (scb->state == SC_ST_ASS);
|
} while (scb->state == SC_ST_ASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3266,7 +3289,7 @@ static int check_tcp_switch_stream_mode(struct act_rule *rule, struct proxy *px,
|
|||||||
px->options |= PR_O_HTTP_UPG;
|
px->options |= PR_O_HTTP_UPG;
|
||||||
|
|
||||||
if (mux_proto) {
|
if (mux_proto) {
|
||||||
mux_ent = conn_get_best_mux_entry(mux_proto->token, PROTO_SIDE_FE, mode);
|
mux_ent = conn_get_best_mux_entry(mux_proto->token, PROTO_SIDE_FE, 0, mode);
|
||||||
if (!mux_ent || !isteq(mux_ent->token, mux_proto->token)) {
|
if (!mux_ent || !isteq(mux_ent->token, mux_proto->token)) {
|
||||||
memprintf(err, "MUX protocol '%.*s' is not compatible with the selected mode",
|
memprintf(err, "MUX protocol '%.*s' is not compatible with the selected mode",
|
||||||
(int)mux_proto->token.len, mux_proto->token.ptr);
|
(int)mux_proto->token.len, mux_proto->token.ptr);
|
||||||
@ -3274,7 +3297,7 @@ static int check_tcp_switch_stream_mode(struct act_rule *rule, struct proxy *px,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mux_ent = conn_get_best_mux_entry(IST_NULL, PROTO_SIDE_FE, mode);
|
mux_ent = conn_get_best_mux_entry(IST_NULL, PROTO_SIDE_FE, 0, mode);
|
||||||
if (!mux_ent) {
|
if (!mux_ent) {
|
||||||
memprintf(err, "Unable to find compatible MUX protocol with the selected mode");
|
memprintf(err, "Unable to find compatible MUX protocol with the selected mode");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
39
src/task.c
39
src/task.c
@ -147,8 +147,7 @@ void __tasklet_wakeup_on(struct tasklet *tl, int thr)
|
|||||||
LIST_APPEND(&th_ctx->tasklets[TL_BULK], &tl->list);
|
LIST_APPEND(&th_ctx->tasklets[TL_BULK], &tl->list);
|
||||||
th_ctx->tl_class_mask |= 1 << TL_BULK;
|
th_ctx->tl_class_mask |= 1 << TL_BULK;
|
||||||
}
|
}
|
||||||
else if ((struct task *)tl == th_ctx->current) {
|
else if ((struct task *)tl == th_ctx->current && !(tl->state & TASK_WOKEN_ANY)) {
|
||||||
_HA_ATOMIC_OR(&tl->state, TASK_SELF_WAKING);
|
|
||||||
LIST_APPEND(&th_ctx->tasklets[TL_BULK], &tl->list);
|
LIST_APPEND(&th_ctx->tasklets[TL_BULK], &tl->list);
|
||||||
th_ctx->tl_class_mask |= 1 << TL_BULK;
|
th_ctx->tl_class_mask |= 1 << TL_BULK;
|
||||||
}
|
}
|
||||||
@ -157,8 +156,8 @@ void __tasklet_wakeup_on(struct tasklet *tl, int thr)
|
|||||||
th_ctx->tl_class_mask |= 1 << TL_URGENT;
|
th_ctx->tl_class_mask |= 1 << TL_URGENT;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LIST_APPEND(&th_ctx->tasklets[th_ctx->current_queue], &tl->list);
|
LIST_APPEND(&th_ctx->tasklets[TL_NORMAL], &tl->list);
|
||||||
th_ctx->tl_class_mask |= 1 << th_ctx->current_queue;
|
th_ctx->tl_class_mask |= 1 << TL_NORMAL;
|
||||||
}
|
}
|
||||||
_HA_ATOMIC_INC(&th_ctx->rq_total);
|
_HA_ATOMIC_INC(&th_ctx->rq_total);
|
||||||
} else {
|
} else {
|
||||||
@ -186,8 +185,7 @@ struct list *__tasklet_wakeup_after(struct list *head, struct tasklet *tl)
|
|||||||
LIST_INSERT(&th_ctx->tasklets[TL_BULK], &tl->list);
|
LIST_INSERT(&th_ctx->tasklets[TL_BULK], &tl->list);
|
||||||
th_ctx->tl_class_mask |= 1 << TL_BULK;
|
th_ctx->tl_class_mask |= 1 << TL_BULK;
|
||||||
}
|
}
|
||||||
else if ((struct task *)tl == th_ctx->current) {
|
else if ((struct task *)tl == th_ctx->current && !(tl->state & TASK_WOKEN_ANY)) {
|
||||||
_HA_ATOMIC_OR(&tl->state, TASK_SELF_WAKING);
|
|
||||||
LIST_INSERT(&th_ctx->tasklets[TL_BULK], &tl->list);
|
LIST_INSERT(&th_ctx->tasklets[TL_BULK], &tl->list);
|
||||||
th_ctx->tl_class_mask |= 1 << TL_BULK;
|
th_ctx->tl_class_mask |= 1 << TL_BULK;
|
||||||
}
|
}
|
||||||
@ -196,8 +194,8 @@ struct list *__tasklet_wakeup_after(struct list *head, struct tasklet *tl)
|
|||||||
th_ctx->tl_class_mask |= 1 << TL_URGENT;
|
th_ctx->tl_class_mask |= 1 << TL_URGENT;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LIST_INSERT(&th_ctx->tasklets[th_ctx->current_queue], &tl->list);
|
LIST_INSERT(&th_ctx->tasklets[TL_NORMAL], &tl->list);
|
||||||
th_ctx->tl_class_mask |= 1 << th_ctx->current_queue;
|
th_ctx->tl_class_mask |= 1 << TL_NORMAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -563,14 +561,22 @@ unsigned int run_tasks_from_lists(unsigned int budgets[])
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
budgets[queue]--;
|
|
||||||
activity[tid].ctxsw++;
|
|
||||||
|
|
||||||
t = (struct task *)LIST_ELEM(tl_queues[queue].n, struct tasklet *, list);
|
t = (struct task *)LIST_ELEM(tl_queues[queue].n, struct tasklet *, list);
|
||||||
|
|
||||||
|
/* check if this task has already run during this loop */
|
||||||
|
if ((uint16_t)t->last_run == (uint16_t)activity[tid].loops) {
|
||||||
|
budget_mask &= ~(1 << queue);
|
||||||
|
queue++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
t->last_run = activity[tid].loops;
|
||||||
ctx = t->context;
|
ctx = t->context;
|
||||||
process = t->process;
|
process = t->process;
|
||||||
t->calls++;
|
t->calls++;
|
||||||
|
|
||||||
|
budgets[queue]--;
|
||||||
|
activity[tid].ctxsw++;
|
||||||
|
|
||||||
th_ctx->lock_wait_total = 0;
|
th_ctx->lock_wait_total = 0;
|
||||||
th_ctx->mem_wait_total = 0;
|
th_ctx->mem_wait_total = 0;
|
||||||
th_ctx->locked_total = 0;
|
th_ctx->locked_total = 0;
|
||||||
@ -723,8 +729,8 @@ void process_runnable_tasks()
|
|||||||
struct task *t;
|
struct task *t;
|
||||||
const unsigned int default_weights[TL_CLASSES] = {
|
const unsigned int default_weights[TL_CLASSES] = {
|
||||||
[TL_URGENT] = 64, // ~50% of CPU bandwidth for I/O
|
[TL_URGENT] = 64, // ~50% of CPU bandwidth for I/O
|
||||||
[TL_NORMAL] = 48, // ~37% of CPU bandwidth for tasks
|
[TL_NORMAL] = 60, // ~47% of CPU bandwidth for tasks
|
||||||
[TL_BULK] = 16, // ~13% of CPU bandwidth for self-wakers
|
[TL_BULK] = 4, // ~3% of CPU bandwidth for self-wakers
|
||||||
[TL_HEAVY] = 1, // never more than 1 heavy task at once
|
[TL_HEAVY] = 1, // never more than 1 heavy task at once
|
||||||
};
|
};
|
||||||
unsigned int max[TL_CLASSES]; // max to be run per class
|
unsigned int max[TL_CLASSES]; // max to be run per class
|
||||||
@ -734,7 +740,7 @@ void process_runnable_tasks()
|
|||||||
int max_processed;
|
int max_processed;
|
||||||
int lpicked, gpicked;
|
int lpicked, gpicked;
|
||||||
int heavy_queued = 0;
|
int heavy_queued = 0;
|
||||||
int budget;
|
int budget, done;
|
||||||
|
|
||||||
_HA_ATOMIC_AND(&th_ctx->flags, ~TH_FL_STUCK); // this thread is still running
|
_HA_ATOMIC_AND(&th_ctx->flags, ~TH_FL_STUCK); // this thread is still running
|
||||||
|
|
||||||
@ -904,10 +910,11 @@ void process_runnable_tasks()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* execute tasklets in each queue */
|
/* execute tasklets in each queue */
|
||||||
max_processed -= run_tasks_from_lists(max);
|
done = run_tasks_from_lists(max);
|
||||||
|
max_processed -= done;
|
||||||
|
|
||||||
/* some tasks may have woken other ones up */
|
/* some tasks may have woken other ones up */
|
||||||
if (max_processed > 0 && thread_has_tasks())
|
if (done && max_processed > 0 && thread_has_tasks())
|
||||||
goto not_done_yet;
|
goto not_done_yet;
|
||||||
|
|
||||||
leave:
|
leave:
|
||||||
|
|||||||
@ -1257,7 +1257,8 @@ static int tcp_parse_tcp_rep(char **args, int section_type, struct proxy *curpx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* the following function directly emits the warning */
|
/* the following function directly emits the warning */
|
||||||
warnif_misplaced_tcp_res_cont(curpx, file, line, args[0], args[1]);
|
if (warnif_misplaced_tcp_res_cont(curpx, file, line, args[0], args[1]))
|
||||||
|
warn++;
|
||||||
LIST_APPEND(&curpx->tcp_rep.inspect_rules, &rule->list);
|
LIST_APPEND(&curpx->tcp_rep.inspect_rules, &rule->list);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1377,7 +1378,8 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* the following function directly emits the warning */
|
/* the following function directly emits the warning */
|
||||||
warnif_misplaced_tcp_req_cont(curpx, file, line, args[0], args[1]);
|
if (warnif_misplaced_tcp_req_cont(curpx, file, line, args[0], args[1]))
|
||||||
|
warn++;
|
||||||
LIST_APPEND(&curpx->tcp_req.inspect_rules, &rule->list);
|
LIST_APPEND(&curpx->tcp_req.inspect_rules, &rule->list);
|
||||||
}
|
}
|
||||||
else if (strcmp(args[1], "connection") == 0) {
|
else if (strcmp(args[1], "connection") == 0) {
|
||||||
@ -1422,7 +1424,8 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* the following function directly emits the warning */
|
/* the following function directly emits the warning */
|
||||||
warnif_misplaced_tcp_req_conn(curpx, file, line, args[0], args[1]);
|
if (warnif_misplaced_tcp_req_conn(curpx, file, line, args[0], args[1]))
|
||||||
|
warn++;
|
||||||
LIST_APPEND(&curpx->tcp_req.l4_rules, &rule->list);
|
LIST_APPEND(&curpx->tcp_req.l4_rules, &rule->list);
|
||||||
}
|
}
|
||||||
else if (strcmp(args[1], "session") == 0) {
|
else if (strcmp(args[1], "session") == 0) {
|
||||||
@ -1466,7 +1469,8 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* the following function directly emits the warning */
|
/* the following function directly emits the warning */
|
||||||
warnif_misplaced_tcp_req_sess(curpx, file, line, args[0], args[1]);
|
if (warnif_misplaced_tcp_req_sess(curpx, file, line, args[0], args[1]))
|
||||||
|
warn++;
|
||||||
LIST_APPEND(&curpx->tcp_req.l5_rules, &rule->list);
|
LIST_APPEND(&curpx->tcp_req.l5_rules, &rule->list);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@ -40,6 +40,7 @@
|
|||||||
#include <haproxy/check.h>
|
#include <haproxy/check.h>
|
||||||
#include <haproxy/chunk.h>
|
#include <haproxy/chunk.h>
|
||||||
#include <haproxy/connection.h>
|
#include <haproxy/connection.h>
|
||||||
|
#include <haproxy/dynbuf.h>
|
||||||
#include <haproxy/errors.h>
|
#include <haproxy/errors.h>
|
||||||
#include <haproxy/global.h>
|
#include <haproxy/global.h>
|
||||||
#include <haproxy/h1.h>
|
#include <haproxy/h1.h>
|
||||||
@ -1427,9 +1428,15 @@ enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpchec
|
|||||||
check->mux_proto = NULL;
|
check->mux_proto = NULL;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
proto = s ?
|
if (check->proto)
|
||||||
protocol_lookup(conn->dst->ss_family, s->addr_type.proto_type, s->alt_proto) :
|
proto = check->proto;
|
||||||
protocol_lookup(conn->dst->ss_family, PROTO_TYPE_STREAM, 0);
|
else {
|
||||||
|
if (is_addr(&connect->addr))
|
||||||
|
proto = protocol_lookup(conn->dst->ss_family, PROTO_TYPE_STREAM, 0);
|
||||||
|
else
|
||||||
|
proto = protocol_lookup(conn->dst->ss_family, s->addr_type.proto_type, s->alt_proto);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
port = 0;
|
port = 0;
|
||||||
@ -1659,7 +1666,8 @@ enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_r
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!check_get_buf(check, &check->bo)) {
|
retry:
|
||||||
|
if (!check_get_buf(check, &check->bo, (check->state & CHK_ST_USE_SMALL_BUFF))) {
|
||||||
check->state |= CHK_ST_OUT_ALLOC;
|
check->state |= CHK_ST_OUT_ALLOC;
|
||||||
ret = TCPCHK_EVAL_WAIT;
|
ret = TCPCHK_EVAL_WAIT;
|
||||||
TRACE_STATE("waiting for output buffer allocation", CHK_EV_TCPCHK_SND|CHK_EV_TX_DATA|CHK_EV_TX_BLK, check);
|
TRACE_STATE("waiting for output buffer allocation", CHK_EV_TCPCHK_SND|CHK_EV_TX_DATA|CHK_EV_TX_BLK, check);
|
||||||
@ -1679,6 +1687,13 @@ enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_r
|
|||||||
case TCPCHK_SEND_STRING:
|
case TCPCHK_SEND_STRING:
|
||||||
case TCPCHK_SEND_BINARY:
|
case TCPCHK_SEND_BINARY:
|
||||||
if (istlen(send->data) >= b_size(&check->bo)) {
|
if (istlen(send->data) >= b_size(&check->bo)) {
|
||||||
|
if (b_is_small(&check->bo)) {
|
||||||
|
check->state &= ~CHK_ST_USE_SMALL_BUFF;
|
||||||
|
check_release_buf(check, &check->bo);
|
||||||
|
TRACE_DEVEL("Send fail with small buffer retry with default one", CHK_EV_TCPCHK_SND|CHK_EV_TX_DATA, check);
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
|
||||||
chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d",
|
chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d",
|
||||||
(unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo),
|
(unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo),
|
||||||
tcpcheck_get_step_id(check, rule));
|
tcpcheck_get_step_id(check, rule));
|
||||||
@ -1689,6 +1704,7 @@ enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_r
|
|||||||
b_putist(&check->bo, send->data);
|
b_putist(&check->bo, send->data);
|
||||||
break;
|
break;
|
||||||
case TCPCHK_SEND_STRING_LF:
|
case TCPCHK_SEND_STRING_LF:
|
||||||
|
BUG_ON(check->state & CHK_ST_USE_SMALL_BUFF);
|
||||||
check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt);
|
check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt);
|
||||||
if (!b_data(&check->bo))
|
if (!b_data(&check->bo))
|
||||||
goto out;
|
goto out;
|
||||||
@ -1696,7 +1712,8 @@ enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_r
|
|||||||
case TCPCHK_SEND_BINARY_LF: {
|
case TCPCHK_SEND_BINARY_LF: {
|
||||||
int len = b_size(&check->bo);
|
int len = b_size(&check->bo);
|
||||||
|
|
||||||
tmp = alloc_trash_chunk();
|
BUG_ON(check->state & CHK_ST_USE_SMALL_BUFF);
|
||||||
|
tmp = alloc_trash_chunk_sz(len);
|
||||||
if (!tmp)
|
if (!tmp)
|
||||||
goto error_lf;
|
goto error_lf;
|
||||||
tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt);
|
tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt);
|
||||||
@ -1713,7 +1730,7 @@ enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_r
|
|||||||
struct ist meth, uri, vsn, clen, body;
|
struct ist meth, uri, vsn, clen, body;
|
||||||
unsigned int slflags = 0;
|
unsigned int slflags = 0;
|
||||||
|
|
||||||
tmp = alloc_trash_chunk();
|
tmp = alloc_trash_chunk_sz(b_size(&check->bo));
|
||||||
if (!tmp)
|
if (!tmp)
|
||||||
goto error_htx;
|
goto error_htx;
|
||||||
|
|
||||||
@ -1838,6 +1855,12 @@ enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_r
|
|||||||
htx_reset(htx);
|
htx_reset(htx);
|
||||||
htx_to_buf(htx, &check->bo);
|
htx_to_buf(htx, &check->bo);
|
||||||
}
|
}
|
||||||
|
if (b_is_small(&check->bo)) {
|
||||||
|
check->state &= ~CHK_ST_USE_SMALL_BUFF;
|
||||||
|
check_release_buf(check, &check->bo);
|
||||||
|
TRACE_DEVEL("Send fail with small buffer retry with default one", CHK_EV_TCPCHK_SND|CHK_EV_TX_DATA, check);
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
chunk_printf(&trash, "tcp-check send : failed to build HTTP request at step %d",
|
chunk_printf(&trash, "tcp-check send : failed to build HTTP request at step %d",
|
||||||
tcpcheck_get_step_id(check, rule));
|
tcpcheck_get_step_id(check, rule));
|
||||||
TRACE_ERROR("failed to build HTTP request", CHK_EV_TCPCHK_SND|CHK_EV_TX_DATA|CHK_EV_TCPCHK_ERR, check);
|
TRACE_ERROR("failed to build HTTP request", CHK_EV_TCPCHK_SND|CHK_EV_TX_DATA|CHK_EV_TCPCHK_ERR, check);
|
||||||
@ -1884,7 +1907,7 @@ enum tcpcheck_eval_ret tcpcheck_eval_recv(struct check *check, struct tcpcheck_r
|
|||||||
goto wait_more_data;
|
goto wait_more_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!check_get_buf(check, &check->bi)) {
|
if (!check_get_buf(check, &check->bi, 0)) {
|
||||||
check->state |= CHK_ST_IN_ALLOC;
|
check->state |= CHK_ST_IN_ALLOC;
|
||||||
TRACE_STATE("waiting for input buffer allocation", CHK_EV_RX_DATA|CHK_EV_RX_BLK, check);
|
TRACE_STATE("waiting for input buffer allocation", CHK_EV_RX_DATA|CHK_EV_RX_BLK, check);
|
||||||
goto wait_more_data;
|
goto wait_more_data;
|
||||||
@ -4067,6 +4090,8 @@ static int check_proxy_tcpcheck(struct proxy *px)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Allow small buffer use by default. All send rules must be compatible */
|
||||||
|
px->tcpcheck_rules.flags |= (global.tune.bufsize_small ? TCPCHK_RULES_MAY_USE_SBUF : 0);
|
||||||
|
|
||||||
/* Remove all comment rules. To do so, when a such rule is found, the
|
/* Remove all comment rules. To do so, when a such rule is found, the
|
||||||
* comment is assigned to the following rule(s).
|
* comment is assigned to the following rule(s).
|
||||||
@ -4096,6 +4121,25 @@ static int check_proxy_tcpcheck(struct proxy *px)
|
|||||||
ha_free(&comment);
|
ha_free(&comment);
|
||||||
break;
|
break;
|
||||||
case TCPCHK_ACT_SEND:
|
case TCPCHK_ACT_SEND:
|
||||||
|
/* Disable small buffer use for rules using LF strings or too large data */
|
||||||
|
switch (chk->send.type) {
|
||||||
|
case TCPCHK_SEND_STRING:
|
||||||
|
case TCPCHK_SEND_BINARY:
|
||||||
|
if (istlen(chk->send.data) >= global.tune.bufsize_small)
|
||||||
|
px->tcpcheck_rules.flags &= ~TCPCHK_RULES_MAY_USE_SBUF;
|
||||||
|
break;
|
||||||
|
case TCPCHK_SEND_STRING_LF:
|
||||||
|
case TCPCHK_SEND_BINARY_LF:
|
||||||
|
px->tcpcheck_rules.flags &= ~TCPCHK_RULES_MAY_USE_SBUF;
|
||||||
|
break;
|
||||||
|
case TCPCHK_SEND_HTTP:
|
||||||
|
if ((chk->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) ||
|
||||||
|
(istlen(chk->send.http.body) >= global.tune.bufsize_small))
|
||||||
|
px->tcpcheck_rules.flags &= ~TCPCHK_RULES_MAY_USE_SBUF;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
__fallthrough;
|
||||||
case TCPCHK_ACT_EXPECT:
|
case TCPCHK_ACT_EXPECT:
|
||||||
if (!chk->comment && comment)
|
if (!chk->comment && comment)
|
||||||
chk->comment = strdup(comment);
|
chk->comment = strdup(comment);
|
||||||
|
|||||||
@ -6057,6 +6057,9 @@ static int dl_collect_libs_cb(struct dl_phdr_info *info, size_t size, void *data
|
|||||||
/* else it's a VDSO or similar and we're not interested */
|
/* else it's a VDSO or similar and we're not interested */
|
||||||
goto leave;
|
goto leave;
|
||||||
|
|
||||||
|
if (!fname)
|
||||||
|
goto leave;
|
||||||
|
|
||||||
load_file_into_tar(&ctx->storage, &ctx->size, ctx->prefix, fname, NULL, "haproxy-libs-dump");
|
load_file_into_tar(&ctx->storage, &ctx->size, ctx->prefix, fname, NULL, "haproxy-libs-dump");
|
||||||
|
|
||||||
/* try to load equivalent debug symbols for absolute paths */
|
/* try to load equivalent debug symbols for absolute paths */
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user