mirror of
https://github.com/haproxy/haproxy.git
synced 2026-03-28 13:24:28 -04:00
Compare commits
63 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
44 changed files with 1389 additions and 504 deletions
|
|
@ -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
|
||||||
|
|
@ -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 -
|
||||||
|
|
@ -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 diffent 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 automtically 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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 Transfert 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 */
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
99
src/chunk.c
99
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
|
||||||
|
|
@ -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. Becasuse 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
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 transfromed 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 patially 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, transfert 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, trasnfer 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, trasnfer 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);
|
||||||
|
}
|
||||||
|
|
|
||||||
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 prioritary 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 },
|
||||||
|
|
|
||||||
16
src/proxy.c
16
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",
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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)) {
|
||||||
|
|
|
||||||
27
src/stream.c
27
src/stream.c
|
|
@ -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 stirngs 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…
Reference in a new issue