mirror of
https://github.com/haproxy/haproxy.git
synced 2026-06-17 21:40:13 -04:00
Compare commits
67 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ad74d634a | ||
|
|
5985276735 | ||
|
|
43932db851 | ||
|
|
7266ed8c30 | ||
|
|
9a6d1fe3f0 | ||
|
|
8e1c51378e | ||
|
|
33c765fc59 | ||
|
|
08aa12392d | ||
|
|
82a16a2927 | ||
|
|
292b07270c | ||
|
|
7bfa568e27 | ||
|
|
c64e242b1c | ||
|
|
5530f50e4b | ||
|
|
f371bfd608 | ||
|
|
0748799ad1 | ||
|
|
339d25636d | ||
|
|
974560128d | ||
|
|
2ff861747c | ||
|
|
98b1fd4ff9 | ||
|
|
b9aa1c0e64 | ||
|
|
aaee6c463c | ||
|
|
caa1cd0674 | ||
|
|
0988b9c773 | ||
|
|
c9f3ddcb1e | ||
|
|
95cb3251a0 | ||
|
|
74b16c5477 | ||
|
|
8b6d8f5e4f | ||
|
|
91f9e3a3dd | ||
|
|
92206fb02f | ||
|
|
d2c9bf70e5 | ||
|
|
4bb21dae2f | ||
|
|
81d7624e01 | ||
|
|
960fa1c921 | ||
|
|
7d63efa5f5 | ||
|
|
96b08e959c | ||
|
|
784f972a6f | ||
|
|
5c0733db9a | ||
|
|
d0fde90e16 | ||
|
|
3dfd86062b | ||
|
|
cc01214a67 | ||
|
|
00c081b5f3 | ||
|
|
53fe4181a5 | ||
|
|
9e60d35aaf | ||
|
|
0a90ff6b3d | ||
|
|
6c75202b48 | ||
|
|
fb38e40ad5 | ||
|
|
9bc37232f4 | ||
|
|
3c923d075c | ||
|
|
6f8dab2583 | ||
|
|
a4520229a7 | ||
|
|
3fa818c78f | ||
|
|
a7888f0373 | ||
|
|
ef191c46d7 | ||
|
|
b9fa07bd20 | ||
|
|
172306c308 | ||
|
|
e51ae5ce66 | ||
|
|
45a64123d6 | ||
|
|
ac776e3819 | ||
|
|
1e00743520 | ||
|
|
b227ad2dc7 | ||
|
|
07deafa104 | ||
|
|
a39b1a40ad | ||
|
|
83ae0c250c | ||
|
|
f7bc8246ee | ||
|
|
3daf4498f3 | ||
|
|
4b9c8b24c5 | ||
|
|
6a7b27a0a4 |
53 changed files with 2003 additions and 999 deletions
|
|
@ -32637,9 +32637,9 @@ https://github.com/haproxy/wiki/wiki/ACME:--native-haproxy
|
|||
Current limitations:
|
||||
- The feature is limited to the http-01, dns-01 or dns-persist-01 challenges
|
||||
for now. http-01 is completely handled by HAProxy, but dns-01 and
|
||||
dns-persist-01 needs either the dataplaneAPI or another 3rd party
|
||||
tool to talk to a DNS provider API. dns-persist-01 only needs the TXT entry
|
||||
to be set once, so it could be set manually without a tool.
|
||||
dns-persist-01 needs either the dataplaneAPI, a lua script using event_hdl or
|
||||
another 3rd party tool to talk to a DNS provider API. dns-persist-01 only
|
||||
needs the TXT entry to be set once, so it could be set manually without a tool.
|
||||
- It is possible to start without an existing certificate on the disk. To do
|
||||
so, the certificate must configured in a crt-store.
|
||||
When using the "acme" keyword in a crt-store, a temporary key pair will be
|
||||
|
|
@ -32710,6 +32710,8 @@ challenge-ready <value>[,<value>]*
|
|||
"acme challenge_ready <crt> domain <domain>" on the master CLI or
|
||||
the stats socket. This allows an external DNS provisioning tool to
|
||||
confirm that the TXT record has been set before HAProxy proceeds.
|
||||
It is also possible to signal the "cli" readiness using the
|
||||
ACME.challenge_ready() lua function.
|
||||
|
||||
dns - perform a DNS pre-check by resolving the TXT record for
|
||||
"_acme-challenge.<domain>" using the configured "default" resolvers
|
||||
|
|
|
|||
|
|
@ -526,12 +526,6 @@ In addition, some variables are related to the global runqueue:
|
|||
unsigned int grq_total; /* total number of entries in the global run queue, atomic */
|
||||
static unsigned int global_rqueue_ticks; /* insertion count in the grq, use rq_lock */
|
||||
|
||||
And others to the global wait queue:
|
||||
struct eb_root timers; /* sorted timers tree, global, accessed under wq_lock */
|
||||
__decl_aligned_rwlock(wq_lock); /* RW lock related to the wait queue */
|
||||
struct eb_root timers; /* sorted timers tree, global, accessed under wq_lock */
|
||||
|
||||
|
||||
2022-06-14 - progress on task affinity
|
||||
==========
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,16 @@ make sure to respect this ordering when adding new ones.
|
|||
cast and freed. The const char* is here to leave more freedom to use consts
|
||||
when making such options lists.
|
||||
|
||||
- void hap_register_hlua_state_init(int (*fct)())
|
||||
|
||||
This adds a call to function <fct> to the list of functions to be called each
|
||||
time a new Lua state is created by hlua_init_state(). This allows source
|
||||
files other than hlua.c to register objects and functions into the Lua API
|
||||
without modifying hlua.c directly. The function <fct> must return an ERR_*
|
||||
code. If the errmsg pointer is set on return, it is printed as an alert,
|
||||
warning, or notice depending on the error code. If ERR_ABORT or ERR_FATAL is
|
||||
returned, haproxy will exit with status 1.
|
||||
|
||||
- void hap_register_per_thread_alloc(int (*fct)())
|
||||
|
||||
This adds a call to function <fct> to the list of functions to be called when
|
||||
|
|
@ -324,6 +334,18 @@ alphanumerically ordered:
|
|||
is that it allows to register multiple <post> callbacks and to register them
|
||||
elsewhere in the code.
|
||||
|
||||
- REGISTER_HLUA_STATE_INIT(fct)
|
||||
|
||||
Registers function <fct> to be called for each new Lua state created by
|
||||
hlua_init_state(). This allows source files other than hlua.c to register
|
||||
objects and functions into the Lua API without modifying hlua.c directly.
|
||||
This is done by registering a call to hap_register_hlua_state_init(fct) at
|
||||
stage STG_REGISTER. The function <fct> must be of type
|
||||
(int (*fct)(lua_State *L, char **errmsg)) and must return an ERR_* code.
|
||||
If the errmsg pointer is set on return, it is printed as an alert, warning,
|
||||
or notice depending on the error code. If ERR_ABORT or ERR_FATAL is returned,
|
||||
haproxy will exit with status 1.
|
||||
|
||||
- REGISTER_PER_THREAD_ALLOC(fct)
|
||||
|
||||
Registers a call to register_per_thread_alloc(fct) at stage STG_REGISTER.
|
||||
|
|
|
|||
|
|
@ -893,9 +893,7 @@ Core class
|
|||
|
||||
**context**: init, task, action
|
||||
|
||||
This function returns a new object of a *httpclient* class. An *httpclient*
|
||||
object must be used to process one and only one request. It must never be
|
||||
reused to process several requests.
|
||||
This function returns a new object of a *httpclient* class.
|
||||
|
||||
:returns: A :ref:`httpclient_class` object.
|
||||
|
||||
|
|
@ -1031,6 +1029,12 @@ Core class
|
|||
Be careful when subscribing to this type since many events might be
|
||||
generated.
|
||||
|
||||
**ACME** Family:
|
||||
|
||||
* **ACME_DEPLOY**: when a dns-01 challenge TXT record must be deployed
|
||||
externally before HAProxy can proceed with the ACME challenge
|
||||
* **ACME_NEWCERT**: when a new certificate is successfully installed
|
||||
|
||||
.. Note::
|
||||
Use **SERVER** in **event_types** to subscribe to all server events types
|
||||
at once. Note that this should only be used for testing purposes since a
|
||||
|
|
@ -1048,7 +1052,8 @@ Core class
|
|||
* **event** (*string*): the event type (one of the **event_types** specified
|
||||
when subscribing)
|
||||
* **event_data**: specific to each event family (For **SERVER** family,
|
||||
a :ref:`server_event_class` object)
|
||||
a :ref:`server_event_class` object; for **ACME** family,
|
||||
a :ref:`acme_event_class` object)
|
||||
* **sub**: class to manage the subscription from within the event
|
||||
(a :ref:`event_sub_class` object)
|
||||
* **when**: timestamp corresponding to the date when the event was generated.
|
||||
|
|
@ -2580,10 +2585,7 @@ HTTPClient class
|
|||
.. js:class:: HTTPClient
|
||||
|
||||
The httpclient class allows issue of outbound HTTP requests through a simple
|
||||
API without the knowledge of HAProxy internals. Any instance must be used to
|
||||
process one and only one request. It must never be reused to process several
|
||||
requests.
|
||||
|
||||
API without the knowledge of HAProxy internals.
|
||||
.. js:function:: HTTPClient.get(httpclient, request)
|
||||
.. js:function:: HTTPClient.head(httpclient, request)
|
||||
.. js:function:: HTTPClient.put(httpclient, request)
|
||||
|
|
@ -2612,7 +2614,8 @@ HTTPClient class
|
|||
haproxy address format.
|
||||
:param integer request.timeout: Optional timeout parameter, set a
|
||||
"timeout server" on the connections.
|
||||
:returns: Lua table containing the response
|
||||
:returns: Lua table containing the response. If an internal error occurs (e.g.
|
||||
connection failure, timeout, etc.), the ``status`` field will be set to 0.
|
||||
|
||||
|
||||
.. code-block:: lua
|
||||
|
|
@ -4739,6 +4742,75 @@ CertCache class
|
|||
CertCache.set{filename="certs/localhost9994.pem.rsa", crt=crt}
|
||||
|
||||
|
||||
ACME class
|
||||
==========
|
||||
|
||||
.. js:class:: ACME
|
||||
|
||||
This class provides access to the ACME (Automatic Certificate Management
|
||||
Environment) subsystem. It allows Lua scripts to interact with ongoing ACME
|
||||
certificate challenges.
|
||||
|
||||
.. js:function:: ACME.challenge_ready(crt, dns)
|
||||
|
||||
Marks the ACME challenge for domain <dns> in certificate <crt> as ready.
|
||||
Returns the number of remaining challenges, or 0 if all challenges are ready
|
||||
and validation has been triggered. Raises a Lua error if the certificate or
|
||||
domain is not found.
|
||||
|
||||
:param string crt: The filename of the certificate.
|
||||
:param string dns: The domain name for which the challenge is ready.
|
||||
:returns: The number of remaining challenges (integer), or 0 when all
|
||||
challenges are done and validation has been triggered.
|
||||
|
||||
.. _acme_event_class:
|
||||
|
||||
AcmeEvent class
|
||||
===============
|
||||
|
||||
.. js:class:: AcmeEvent
|
||||
|
||||
This class is provided with every **ACME** event.
|
||||
|
||||
See :js:func:`core.event_sub()` for more info.
|
||||
|
||||
.. js:attribute:: AcmeEvent.crtname
|
||||
|
||||
Contains the certificate store name.
|
||||
|
||||
.. js:attribute:: AcmeEvent.domain
|
||||
|
||||
Contains the domain being challenged.
|
||||
|
||||
Only available for **ACME_DEPLOY** events.
|
||||
|
||||
.. js:attribute:: AcmeEvent.thumbprint
|
||||
|
||||
Contains the account key JWK thumbprint.
|
||||
|
||||
Only available for **ACME_DEPLOY** events.
|
||||
|
||||
.. js:attribute:: AcmeEvent.dns_record
|
||||
|
||||
Contains the DNS TXT record value that must be set at
|
||||
``_acme-challenge.<domain>``.
|
||||
|
||||
Only available for **ACME_DEPLOY** events.
|
||||
|
||||
.. js:attribute:: AcmeEvent.provider
|
||||
|
||||
Contains the DNS provider name configured in the ACME section.
|
||||
Only set if a provider was configured.
|
||||
|
||||
Only available for **ACME_DEPLOY** events.
|
||||
|
||||
.. js:attribute:: AcmeEvent.vars
|
||||
|
||||
Contains the ACME vars string configured in the ACME section.
|
||||
Only set if vars were configured.
|
||||
|
||||
Only available for **ACME_DEPLOY** events.
|
||||
|
||||
External Lua libraries
|
||||
======================
|
||||
|
||||
|
|
|
|||
|
|
@ -2469,7 +2469,8 @@ httpclient [--htx] <method> <URI>
|
|||
name in the URL using the "default" resolvers section, which is populated
|
||||
with the DNS servers of your /etc/resolv.conf by default. However it won't be
|
||||
able to resolve an host from /etc/hosts if you don't use a local dns daemon
|
||||
which can resolve those.
|
||||
which can resolve those. If an internal error occurs (e.g. connection failure,
|
||||
timeout, etc.), the status code will be set to 0.
|
||||
|
||||
The --htx option allow to use the haproxy internal htx representation using
|
||||
the htx_dump() function, mainly used for debugging.
|
||||
|
|
|
|||
162
examples/lua/acme-gandi-livedns.lua
Normal file
162
examples/lua/acme-gandi-livedns.lua
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
-- ACME dns-01 automation via event_hdl callbacks using the Gandi LiveDNS API v5
|
||||
--
|
||||
-- HAProxy Configuration:
|
||||
--
|
||||
-- global
|
||||
-- expose-experimental-directives
|
||||
-- tune.lua.bool-sample-conversion normal
|
||||
-- lua-load examples/lua/acme-gandi-livedns.lua
|
||||
-- log stderr local0
|
||||
--
|
||||
-- acme LE
|
||||
-- directory https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
-- contact foobar@example.com
|
||||
-- challenge dns-01
|
||||
-- challenge-ready cli,dns
|
||||
--
|
||||
-- crt-store
|
||||
-- load crt foobar.pem acme LE domains *.foobar.example.com
|
||||
--
|
||||
-- Start HAProxy with the GANDI_API_KEY variable:
|
||||
--
|
||||
-- GANDI_API_KEY=fer89wf498w4f98we74f98wwiw787f8we4f8 ./haproxy -W -f haproxy.cfg
|
||||
--
|
||||
-- Gandi Personal Access Token (https://account.gandi.net -> Security -> Personal Access Tokens).
|
||||
-- Set the GANDI_API_KEY environment variable before starting HAProxy.
|
||||
local GANDI_API_KEY = os.getenv("GANDI_API_KEY") or error("GANDI_API_KEY environment variable is not set")
|
||||
|
||||
-- Gandi LiveDNS API base URL.
|
||||
local GANDI_API_URL = "https://api.gandi.net/v5/livedns"
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Gandi LiveDNS helpers
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
-- Try to set the _acme-challenge TXT record for <domain> to <txt_value>.
|
||||
-- Probes each possible parent zone (longest first) until Gandi accepts one.
|
||||
-- Returns the zone and record name on success, or nil on failure.
|
||||
local function dns_set_txt(domain, txt_value)
|
||||
local labels = {}
|
||||
for label in domain:gmatch("[^.]+") do
|
||||
labels[#labels + 1] = label
|
||||
end
|
||||
|
||||
for i = 1, #labels - 1 do
|
||||
local zone = table.concat(labels, ".", i + 1)
|
||||
local name = "_acme-challenge." .. table.concat(labels, ".", 1, i)
|
||||
local url = string.format("%s/domains/%s/records/%s/TXT", GANDI_API_URL, zone, name)
|
||||
local body = string.format('{"rrset_values":["%s"],"rrset_ttl":300}', txt_value)
|
||||
|
||||
core.log(core.debug, string.format("acme: trying PUT %s", url))
|
||||
|
||||
-- Remove any stale TXT record first so the new value propagates cleanly.
|
||||
local hc_del = core.httpclient()
|
||||
hc_del:delete({
|
||||
url = url,
|
||||
headers = { ["Authorization"] = { "Bearer " .. GANDI_API_KEY } },
|
||||
})
|
||||
|
||||
local hc = core.httpclient()
|
||||
local res = hc:put({
|
||||
url = url,
|
||||
headers = {
|
||||
["Authorization"] = { "Bearer " .. GANDI_API_KEY },
|
||||
["Content-Type"] = { "application/json" },
|
||||
},
|
||||
body = body,
|
||||
})
|
||||
|
||||
if res and (res.status == 200 or res.status == 201) then
|
||||
core.log(core.notice, string.format(
|
||||
"acme: TXT record set: %s in zone %s", name, zone))
|
||||
return zone, name
|
||||
end
|
||||
end
|
||||
|
||||
core.log(core.alert, string.format(
|
||||
"acme: failed to set TXT record for _acme-challenge.%s: no valid zone found", domain))
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
-- Deletes the TXT record identified by <zone> and <name>.
|
||||
local function dns_del_txt(zone, name)
|
||||
local url = string.format("%s/domains/%s/records/%s/TXT", GANDI_API_URL, zone, name)
|
||||
|
||||
core.log(core.notice, string.format("acme: DELETE %s", url))
|
||||
|
||||
local hc = core.httpclient()
|
||||
local res = hc:delete({
|
||||
url = url,
|
||||
headers = {
|
||||
["Authorization"] = { "Bearer " .. GANDI_API_KEY },
|
||||
},
|
||||
})
|
||||
|
||||
if not res or res.status ~= 204 then
|
||||
local status = res and res.status or "nil"
|
||||
core.log(core.alert, string.format(
|
||||
"acme: Gandi DELETE failed for %s/%s (status=%s)", zone, name, status))
|
||||
return false
|
||||
end
|
||||
|
||||
core.log(core.notice, string.format(
|
||||
"acme: TXT record deleted: %s in zone %s", name, zone))
|
||||
return true
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Tasks
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
-- Track deployed TXT records per cert path so they can be cleaned up.
|
||||
-- deployed[crt][domain] = { zone = ..., name = ... }
|
||||
local deployed = {}
|
||||
|
||||
-- Spawn a background task per ACME_DEPLOY event to set the TXT record and
|
||||
-- signal challenge readiness. Using register_task keeps HTTP calls in a
|
||||
-- plain task context.
|
||||
core.event_sub({"ACME_DEPLOY"}, function(event, data, sub, when)
|
||||
local crt = data.crtname
|
||||
local domain = data.domain
|
||||
local record = data.dns_record
|
||||
|
||||
core.register_task(function()
|
||||
local zone, name = dns_set_txt(domain, record)
|
||||
if not zone then
|
||||
core.log(core.alert, string.format(
|
||||
"acme: aborting challenge for crt=%s domain=%s", crt, domain))
|
||||
return
|
||||
end
|
||||
|
||||
-- Remember this record for cleanup on ACME_NEWCERT.
|
||||
if not deployed[crt] then deployed[crt] = {} end
|
||||
deployed[crt][domain] = { zone = zone, name = name }
|
||||
|
||||
-- Signal HAProxy that the dns-01 challenge for this domain is ready.
|
||||
local ok, ret = pcall(ACME.challenge_ready, crt, domain)
|
||||
if not ok then
|
||||
core.log(core.alert, string.format(
|
||||
"acme: challenge_ready error for crt=%s domain=%s: %s", crt, domain, ret))
|
||||
elseif ret == 0 then
|
||||
core.log(core.notice, string.format(
|
||||
"acme: all challenges ready for crt=%s, validation starting", crt))
|
||||
else
|
||||
core.log(core.info, string.format(
|
||||
"acme: crt=%s domain=%s ready, %d challenge(s) still pending",
|
||||
crt, domain, ret))
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
-- ACME_NEWCERT: remove the TXT records that were set for this certificate.
|
||||
core.event_sub({"ACME_NEWCERT"}, function(event, data, sub, when)
|
||||
local crt = data.crtname
|
||||
if not deployed[crt] then return end
|
||||
|
||||
core.register_task(function()
|
||||
for _, rec in pairs(deployed[crt]) do
|
||||
dns_del_txt(rec.zone, rec.name)
|
||||
end
|
||||
deployed[crt] = nil
|
||||
end)
|
||||
end)
|
||||
|
|
@ -135,6 +135,29 @@ struct acme_ctx {
|
|||
#define ACME_VERB_ADVANCED 4
|
||||
#define ACME_VERB_COMPLETE 5
|
||||
|
||||
/* event data for EVENT_HDL_SUB_ACME_DEPLOY:
|
||||
* published when a dns-01 challenge TXT record must be deployed externally.
|
||||
*/
|
||||
struct event_hdl_cb_data_acme_deploy {
|
||||
struct {
|
||||
char *crtname; /* certificate store name (heap-allocated) */
|
||||
char *domain; /* domain being challenged (heap-allocated) */
|
||||
char *thumbprint; /* account key JWK thumbprint (heap-allocated) */
|
||||
char *dns_record; /* DNS TXT record value to set (heap-allocated) */
|
||||
char *provider; /* DNS provider name (heap-allocated, may be NULL) */
|
||||
char *vars; /* ACME vars string (heap-allocated, may be NULL) */
|
||||
} safe;
|
||||
};
|
||||
|
||||
/* event data for EVENT_HDL_SUB_ACME_NEWCERT:
|
||||
* published when a new certificate was successfully installed.
|
||||
*/
|
||||
struct event_hdl_cb_data_acme_newcert {
|
||||
struct {
|
||||
char *crtname; /* certificate store name (heap-allocated) */
|
||||
} safe;
|
||||
};
|
||||
|
||||
#endif /* ! HAVE_ACME */
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,8 +5,14 @@
|
|||
#include <haproxy/ssl_ckch-t.h>
|
||||
|
||||
int ckch_conf_acme_init(void *value, char *buf, struct ckch_store *s, int cli, const char *filename, int linenum, char **err);
|
||||
int acme_challenge_ready(const char *crt, const char *dns);
|
||||
EVP_PKEY *acme_gen_tmp_pkey();
|
||||
X509 *acme_gen_tmp_x509();
|
||||
|
||||
#if defined(USE_LUA)
|
||||
#include <haproxy/hlua-t.h>
|
||||
#include <haproxy/event_hdl-t.h>
|
||||
void acme_hlua_event_push_args(struct hlua *hlua, struct event_hdl_sub_type event, void *data);
|
||||
#endif /* USE_LUA */
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -101,6 +101,8 @@ void ha_warning(const char *fmt, ...)
|
|||
* These functions are reserved to output diagnostics on MODE_DIAG.
|
||||
* Use the underscore variants only if MODE_DIAG has already been checked.
|
||||
*/
|
||||
void ha_diag_notice(const char *fmt, ...)
|
||||
__attribute__ ((format(printf, 1 ,2)));
|
||||
void _ha_vdiag_warning(const char *fmt, va_list argp);
|
||||
void _ha_diag_warning(const char *fmt, ...);
|
||||
void ha_diag_warning(const char *fmt, ...)
|
||||
|
|
|
|||
|
|
@ -290,6 +290,16 @@ struct event_hdl_sub {
|
|||
#define EVENT_HDL_SUB_PAT_REF_COMMIT EVENT_HDL_SUB_TYPE(2,4)
|
||||
#define EVENT_HDL_SUB_PAT_REF_CLEAR EVENT_HDL_SUB_TYPE(2,5)
|
||||
|
||||
/* ACME family, published in global subscription list.
|
||||
* Provides event_hdl_cb_data_acme_deploy and event_hdl_cb_data_acme_newcert
|
||||
* structs (defined in haproxy/acme-t.h).
|
||||
*/
|
||||
#define EVENT_HDL_SUB_ACME EVENT_HDL_SUB_FAMILY(3)
|
||||
/* a new certificate was successfully installed */
|
||||
#define EVENT_HDL_SUB_ACME_NEWCERT EVENT_HDL_SUB_TYPE(3,1)
|
||||
/* dns-01 challenge must be deployed externally */
|
||||
#define EVENT_HDL_SUB_ACME_DEPLOY EVENT_HDL_SUB_TYPE(3,2)
|
||||
|
||||
/* --------------------------------------- */
|
||||
|
||||
/* Please reflect changes above in event_hdl_sub_type_map defined
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
#define CLASS_HTTP "HTTP"
|
||||
#define CLASS_HTTP_MSG "HTTPMessage"
|
||||
#define CLASS_HTTPCLIENT "HTTPClient"
|
||||
#define CLASS_HTTPCLIENT_REQ "HTTPClientRequest"
|
||||
#define CLASS_MAP "Map"
|
||||
#define CLASS_APPLET_TCP "AppletTCP"
|
||||
#define CLASS_APPLET_HTTP "AppletHTTP"
|
||||
|
|
@ -259,6 +260,11 @@ struct hlua_patref_iterator_context {
|
|||
struct pat_ref_gen *gen; /* the generation we are iterating over */
|
||||
};
|
||||
|
||||
struct hlua_state_init_fct {
|
||||
struct list list;
|
||||
int (*fct)(lua_State *L, char **errmsg);
|
||||
};
|
||||
|
||||
#else /* USE_LUA */
|
||||
/************************ For use when Lua is disabled ********************/
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,17 @@
|
|||
|
||||
#ifdef USE_LUA
|
||||
|
||||
/* Lua uses longjmp to perform yield or throwing errors. This
|
||||
* macro is used only for identifying the function that can
|
||||
* not return because a longjmp is executed.
|
||||
* __LJMP marks a prototype of hlua file that can use longjmp.
|
||||
* WILL_LJMP() marks an lua function that will use longjmp.
|
||||
* MAY_LJMP() marks an lua function that may use longjmp.
|
||||
*/
|
||||
#define __LJMP
|
||||
#define WILL_LJMP(func) do { func; my_unreachable(); } while(0)
|
||||
#define MAY_LJMP(func) func
|
||||
|
||||
/* The following macros are used to set flags. */
|
||||
#define HLUA_SET_RUN(__hlua) do {(__hlua)->flags |= HLUA_RUN;} while(0)
|
||||
#define HLUA_CLR_RUN(__hlua) do {(__hlua)->flags &= ~HLUA_RUN;} while(0)
|
||||
|
|
@ -51,6 +62,11 @@
|
|||
|
||||
/* Lua HAProxy integration functions. */
|
||||
void hlua_yield_asap(lua_State *L);
|
||||
void hap_register_hlua_state_init(int (*fct)(lua_State *L, char **errmsg));
|
||||
|
||||
/* simplified way to register a lua_State init callback from any file */
|
||||
#define REGISTER_HLUA_STATE_INIT(fct) \
|
||||
INITCALL1(STG_REGISTER, hap_register_hlua_state_init, (fct))
|
||||
const char *hlua_traceback(lua_State *L, const char* sep);
|
||||
void hlua_ctx_destroy(struct hlua *lua);
|
||||
void hlua_init();
|
||||
|
|
@ -65,6 +81,15 @@ void hlua_pushref(lua_State *L, int ref);
|
|||
void hlua_unref(lua_State *L, int ref);
|
||||
struct hlua *hlua_gethlua(lua_State *L);
|
||||
void hlua_yieldk(lua_State *L, int nresults, lua_KContext ctx, lua_KFunction k, int timeout, unsigned int flags);
|
||||
int hlua_pusherror(lua_State *L, const char *fmt, ...);
|
||||
|
||||
__LJMP static inline void hlua_check_args(lua_State *L, int nb, char *fcn)
|
||||
{
|
||||
if (lua_gettop(L) == nb)
|
||||
return;
|
||||
WILL_LJMP(luaL_error(L, "'%s' needs %d arguments", fcn, nb));
|
||||
}
|
||||
#define check_args hlua_check_args
|
||||
|
||||
#else /* USE_LUA */
|
||||
|
||||
|
|
|
|||
|
|
@ -38,4 +38,9 @@ static inline int httpclient_started(struct httpclient *hc)
|
|||
return !!(hc->flags & HTTPCLIENT_FS_STARTED);
|
||||
}
|
||||
|
||||
#ifdef USE_LUA
|
||||
#include <haproxy/hlua-t.h>
|
||||
void hlua_httpclient_destroy_all(struct hlua *hlua);
|
||||
#endif
|
||||
|
||||
#endif /* !_HAPROXY_HTTPCLIENT_H */
|
||||
|
|
|
|||
|
|
@ -279,6 +279,8 @@ struct srv_per_thread {
|
|||
struct ceb_root *idle_conns; /* Shareable idle connections */
|
||||
struct ceb_root *safe_conns; /* Safe idle connections */
|
||||
struct ceb_root *avail_conns; /* Connections in use, but with still new streams available */
|
||||
struct server *srv; /* Back-pointer to the server */
|
||||
struct eb32_node idle_node; /* When to next do cleanup in the idle connections */
|
||||
#ifdef USE_QUIC
|
||||
struct ist quic_retry_token;
|
||||
#endif
|
||||
|
|
@ -401,7 +403,6 @@ struct server {
|
|||
* thread, and generally at the same time.
|
||||
*/
|
||||
THREAD_ALIGN();
|
||||
struct eb32_node idle_node; /* When to next do cleanup in the idle connections */
|
||||
unsigned int curr_idle_conns; /* Current number of orphan idling connections, both the idle and the safe lists */
|
||||
unsigned int curr_idle_nb; /* Current number of connections in the idle list */
|
||||
unsigned int curr_safe_nb; /* Current number of connections in the safe list */
|
||||
|
|
|
|||
|
|
@ -41,9 +41,9 @@
|
|||
#include <haproxy/tools.h>
|
||||
|
||||
|
||||
__decl_thread(extern HA_SPINLOCK_T idle_conn_srv_lock);
|
||||
extern struct idle_conns idle_conns[MAX_THREADS];
|
||||
extern struct task *idle_conn_task;
|
||||
extern struct task *idle_conn_task[MAX_THREADS];
|
||||
extern struct eb_root idle_conn_srv[MAX_THREADS];
|
||||
extern struct mt_list servers_list;
|
||||
extern struct dict server_key_dict;
|
||||
|
||||
|
|
|
|||
|
|
@ -255,6 +255,7 @@ struct ssl_keylog {
|
|||
#define SSL_SOCK_F_KTLS_RECV (1 << 3) /* kTLS receive is configure on that socket */
|
||||
#define SSL_SOCK_F_CTRL_SEND (1 << 4) /* We want to send a kTLS control message for that socket */
|
||||
#define SSL_SOCK_F_HAS_ALPN (1 << 5) /* An ALPN has been negotiated */
|
||||
#define SSL_SOCK_F_KTLS_ULP (1 << 6) /* TLS ULP is enabled on that socket */
|
||||
|
||||
struct ssl_sock_ctx {
|
||||
struct connection *conn;
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@
|
|||
/* use this to check a task state or to clean it up before queueing */
|
||||
#define TASK_WOKEN_ANY (TASK_WOKEN_OTHER|TASK_WOKEN_INIT|TASK_WOKEN_TIMER| \
|
||||
TASK_WOKEN_IO|TASK_WOKEN_SIGNAL|TASK_WOKEN_MSG| \
|
||||
TASK_WOKEN_RES)
|
||||
TASK_WOKEN_RES|TASK_WOKEN_WQ)
|
||||
|
||||
#define TASK_F_TASKLET 0x00008000 /* nature of this task: 0=task 1=tasklet */
|
||||
#define TASK_F_USR1 0x00010000 /* preserved user flag 1, application-specific, def:0 */
|
||||
|
|
@ -61,7 +61,8 @@
|
|||
#define TASK_F_UEVT2 0x00040000 /* one-shot user event type 2, application specific, def:0 */
|
||||
#define TASK_F_WANTS_TIME 0x00080000 /* task/tasklet wants th_ctx->sched_call_date to be set */
|
||||
#define TASK_F_UEVT3 0x00100000 /* one-shot user event type 3, application specific, def:0 */
|
||||
/* unused: 0x200000..0x80000000 */
|
||||
#define TASK_WOKEN_WQ 0x00200000 /* The task has been waken up only to be put in the wait queue, because its expire changed */
|
||||
/* unused: 0x400000..0x80000000 */
|
||||
|
||||
/* These flags are persistent across scheduler calls */
|
||||
#define TASK_PERSISTENT (TASK_SELF_WAKING | TASK_KILLED | \
|
||||
|
|
@ -82,7 +83,7 @@ static forceinline char *task_show_state(char *buf, size_t len, const char *deli
|
|||
_(TASK_KILLED, _(TASK_HEAVY, _(TASK_WOKEN_INIT,
|
||||
_(TASK_WOKEN_TIMER, _(TASK_WOKEN_IO, _(TASK_WOKEN_SIGNAL,
|
||||
_(TASK_WOKEN_MSG, _(TASK_WOKEN_RES, _(TASK_WOKEN_OTHER,
|
||||
_(TASK_F_TASKLET, _(TASK_F_USR1))))))))))))));
|
||||
_(TASK_F_TASKLET, _(TASK_F_USR1, _(TASK_WOKEN_WQ)))))))))))))));
|
||||
/* epilogue */
|
||||
_(~0U);
|
||||
return buf;
|
||||
|
|
|
|||
|
|
@ -91,14 +91,13 @@ extern struct pool_head *pool_head_task;
|
|||
extern struct pool_head *pool_head_tasklet;
|
||||
extern struct pool_head *pool_head_notification;
|
||||
|
||||
__decl_thread(extern HA_RWLOCK_T wq_lock THREAD_ALIGNED());
|
||||
|
||||
void __tasklet_wakeup_on(struct tasklet *tl, int thr);
|
||||
struct list *__tasklet_wakeup_after(struct list *head, struct tasklet *tl);
|
||||
void task_kill(struct task *t);
|
||||
void tasklet_kill(struct tasklet *t);
|
||||
void __task_wakeup(struct task *t);
|
||||
void __task_queue(struct task *task, struct eb_root *wq);
|
||||
void __task_queue(struct task *task);
|
||||
static inline void _task_queue(struct task *task, const struct ha_caller *caller);
|
||||
|
||||
unsigned int run_tasks_from_lists(unsigned int budgets[]);
|
||||
|
||||
|
|
@ -118,7 +117,7 @@ void process_runnable_tasks(void);
|
|||
void wake_expired_tasks(void);
|
||||
|
||||
/* Checks the next timer for the current thread by looking into its own timer
|
||||
* list and the global one. It may return TICK_ETERNITY if no timer is present.
|
||||
* list. It may return TICK_ETERNITY if no timer is present.
|
||||
* Note that the next timer might very well be slightly in the past.
|
||||
*/
|
||||
int next_timer_expiry(void);
|
||||
|
|
@ -205,6 +204,77 @@ static inline uint64_t task_mono_time(void)
|
|||
return th_ctx->sched_call_date;
|
||||
}
|
||||
|
||||
#if !defined(HA_CAS_IS_8B) && !defined(HA_HAVE_CAS_DW)
|
||||
__decl_thread(extern HA_SPINLOCK_T task_state_tid);
|
||||
#endif
|
||||
|
||||
static inline int __task_set_state_and_tid(struct task *t, int expected_tid, int new_tid, unsigned int current, unsigned int wanted)
|
||||
{
|
||||
#if defined(HA_CAS_IS_8B) || defined(HA_HAVE_CAS_DW)
|
||||
uint64_t expected_value;
|
||||
uint64_t new_value;
|
||||
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
expected_value = ((uint64_t)(current) << 32) | (uint32_t)expected_tid;
|
||||
#else
|
||||
expected_value = current | ((uint64_t)expected_tid << 32);
|
||||
#endif
|
||||
do {
|
||||
int tid_seen;
|
||||
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
tid_seen = (expected_value & 0xffffffff);
|
||||
if (tid_seen != expected_tid)
|
||||
return 0;
|
||||
if ((expected_value >> 32) != current)
|
||||
return 0;
|
||||
new_value = ((uint64_t)wanted << 32) | (uint32_t)new_tid;
|
||||
|
||||
#else
|
||||
tid_seen = (expected_value >> 32);
|
||||
if (tid_seen != expected_tid)
|
||||
return 0;
|
||||
if ((expected_value & 0xffffffff) != current)
|
||||
return 0;
|
||||
new_value = wanted | ((uint64_t)new_tid << 32);
|
||||
#endif
|
||||
#if defined(HA_CAS_IS_8B)
|
||||
} while (!HA_ATOMIC_CAS((uint64_t *)&t->state, &expected_value, new_value) && __ha_cpu_relax());
|
||||
#elif defined(HA_HAVE_CAS_DW)
|
||||
} while (!HA_ATOMIC_DWCAS((uint64_t *)&t->state, &expected_value, &new_value) && __ha_cpu_relax());
|
||||
#endif
|
||||
return 1;
|
||||
#else /* !HA_CAS_IS_8B && !HA_HAVE_CAS_DW */
|
||||
int old_state;
|
||||
int ret = 0;
|
||||
|
||||
HA_SPIN_LOCK(OTHER_LOCK, &task_state_tid);
|
||||
if (_HA_ATOMIC_LOAD(&t->tid) == expected_tid) {
|
||||
old_state = _HA_ATOMIC_LOAD(&t->state);
|
||||
if (old_state == current && HA_ATOMIC_CAS(&t->state, &old_state, wanted)) {
|
||||
_HA_ATOMIC_STORE(&t->tid, new_tid);
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
HA_SPIN_UNLOCK(OTHER_LOCK, &task_state_tid);
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int __task_get_new_tid_field(int curtid)
|
||||
{
|
||||
if (curtid >= 0 || curtid < -1)
|
||||
return curtid;
|
||||
return -2 - tid;
|
||||
}
|
||||
|
||||
static inline int __task_get_current_owner(int curtid)
|
||||
{
|
||||
if (curtid >= 0 || curtid == -1)
|
||||
return curtid;
|
||||
return ~(curtid + 1);
|
||||
}
|
||||
|
||||
/* puts the task <t> in run queue with reason flags <f>, and returns <t> */
|
||||
/* This will put the task in the local runqueue if the task is only runnable
|
||||
* by the current thread, in the global runqueue otherwies. With DEBUG_TASK,
|
||||
|
|
@ -220,7 +290,9 @@ static inline void _task_wakeup(struct task *t, unsigned int f, const struct ha_
|
|||
|
||||
state = _HA_ATOMIC_OR_FETCH(&t->state, f);
|
||||
while (!(state & (TASK_RUNNING | TASK_QUEUED))) {
|
||||
if (_HA_ATOMIC_CAS(&t->state, &state, state | TASK_QUEUED)) {
|
||||
int expected_tid = _HA_ATOMIC_LOAD(&t->tid);
|
||||
|
||||
if (__task_set_state_and_tid(t, expected_tid, __task_get_new_tid_field(expected_tid), state, state | TASK_QUEUED)) {
|
||||
if (likely(caller)) {
|
||||
caller = HA_ATOMIC_XCHG(&t->caller, caller);
|
||||
BUG_ON((ulong)caller & 1);
|
||||
|
|
@ -231,6 +303,7 @@ static inline void _task_wakeup(struct task *t, unsigned int f, const struct ha_
|
|||
__task_wakeup(t);
|
||||
break;
|
||||
}
|
||||
state = _HA_ATOMIC_LOAD(&t->state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -245,14 +318,28 @@ static inline void task_drop_running(struct task *t, unsigned int f)
|
|||
{
|
||||
unsigned int state, new_state;
|
||||
|
||||
state = _HA_ATOMIC_LOAD(&t->state);
|
||||
|
||||
while (1) {
|
||||
new_state = state | f;
|
||||
int cur_tid, new_tid;
|
||||
|
||||
state = _HA_ATOMIC_LOAD(&t->state);
|
||||
new_state = (state | f) &~ TASK_RUNNING;
|
||||
cur_tid = t->tid;
|
||||
if ((new_state & TASK_WOKEN_WQ) && __task_get_current_owner(cur_tid) == tid) {
|
||||
_task_queue(t, NULL);
|
||||
new_state &= ~TASK_WOKEN_WQ;
|
||||
}
|
||||
if (new_state & TASK_WOKEN_ANY)
|
||||
new_state |= TASK_QUEUED;
|
||||
|
||||
if (_HA_ATOMIC_CAS(&t->state, &state, new_state & ~TASK_RUNNING))
|
||||
|
||||
if ((new_state & TASK_QUEUED) || cur_tid >= 0 || task_in_wq(t) ||
|
||||
__task_get_current_owner(cur_tid) != tid)
|
||||
new_tid = cur_tid;
|
||||
else
|
||||
new_tid = -1;
|
||||
|
||||
if (__task_set_state_and_tid(t, cur_tid, new_tid, state, new_state))
|
||||
break;
|
||||
__ha_cpu_relax();
|
||||
}
|
||||
|
|
@ -273,31 +360,21 @@ static inline struct task *__task_unlink_wq(struct task *t)
|
|||
return t;
|
||||
}
|
||||
|
||||
/* remove a task from its wait queue. It may either be the local wait queue if
|
||||
* the task is bound to a single thread or the global queue. If the task uses a
|
||||
* shared wait queue, the global wait queue lock is used.
|
||||
/* remove a task from its wait queue, which during normal operations will be
|
||||
* the current thread's wait queue.
|
||||
*/
|
||||
static inline struct task *task_unlink_wq(struct task *t)
|
||||
{
|
||||
unsigned long locked;
|
||||
|
||||
if (likely(task_in_wq(t))) {
|
||||
locked = t->tid < 0;
|
||||
BUG_ON(t->tid >= 0 && t->tid != tid && !(global.mode & MODE_STOPPING));
|
||||
if (locked)
|
||||
HA_RWLOCK_WRLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
BUG_ON(__task_get_current_owner(t->tid) != tid && !(global.mode & MODE_STOPPING));
|
||||
__task_unlink_wq(t);
|
||||
if (locked)
|
||||
HA_RWLOCK_WRUNLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Place <task> into the wait queue, where it may already be. If the expiration
|
||||
* timer is infinite, do nothing and rely on wake_expired_task to clean up.
|
||||
* If the task uses a shared wait queue, it's queued into the global wait queue,
|
||||
* protected by the global wq_lock, otherwise by it necessarily belongs to the
|
||||
* current thread'sand is queued without locking.
|
||||
*/
|
||||
#define task_queue(t) \
|
||||
_task_queue(t, MK_CALLER(WAKEUP_TYPE_TASK_QUEUE, 0, 0))
|
||||
|
|
@ -316,34 +393,17 @@ static inline void _task_queue(struct task *task, const struct ha_caller *caller
|
|||
if (!tick_isset(task->expire))
|
||||
return;
|
||||
|
||||
#ifdef USE_THREAD
|
||||
if (task->tid < 0) {
|
||||
HA_RWLOCK_WRLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
|
||||
if (likely(caller)) {
|
||||
caller = HA_ATOMIC_XCHG(&task->caller, caller);
|
||||
BUG_ON((ulong)caller & 1);
|
||||
BUG_ON(task->tid >= 0 && task->tid != tid);
|
||||
|
||||
if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
|
||||
if (likely(caller)) {
|
||||
caller = HA_ATOMIC_XCHG(&task->caller, caller);
|
||||
BUG_ON((ulong)caller & 1);
|
||||
#ifdef DEBUG_TASK
|
||||
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
|
||||
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
|
||||
#endif
|
||||
}
|
||||
__task_queue(task, &tg_ctx->timers);
|
||||
}
|
||||
HA_RWLOCK_WRUNLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
BUG_ON(task->tid != tid);
|
||||
if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
|
||||
if (likely(caller)) {
|
||||
caller = HA_ATOMIC_XCHG(&task->caller, caller);
|
||||
BUG_ON((ulong)caller & 1);
|
||||
#ifdef DEBUG_TASK
|
||||
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
|
||||
#endif
|
||||
}
|
||||
__task_queue(task, &th_ctx->timers);
|
||||
}
|
||||
__task_queue(task);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -363,6 +423,11 @@ static inline void task_set_thread(struct task *t, int thr)
|
|||
/* no shared queue without threads */
|
||||
thr = 0;
|
||||
#endif
|
||||
/*
|
||||
* Nothing to do, the task is only temporarily owned
|
||||
*/
|
||||
if (thr == -1 && t->tid == -2 - tid)
|
||||
return;
|
||||
if (unlikely(task_in_wq(t))) {
|
||||
task_unlink_wq(t);
|
||||
t->tid = thr;
|
||||
|
|
@ -440,24 +505,30 @@ static inline void _tasklet_wakeup_on(struct tasklet *tl, int thr, uint f, const
|
|||
static inline void _task_instant_wakeup(struct task *t, unsigned int f, const struct ha_caller *caller)
|
||||
{
|
||||
int thr = t->tid;
|
||||
int newtid;
|
||||
unsigned int state;
|
||||
|
||||
if (thr < 0)
|
||||
thr = tid;
|
||||
|
||||
/* first, let's update the task's state with the wakeup condition */
|
||||
state = _HA_ATOMIC_OR_FETCH(&t->state, f);
|
||||
|
||||
/* next we need to make sure the task was not/will not be added to the
|
||||
* run queue because the tasklet list's mt_list uses the same storage
|
||||
* as the task's run_queue.
|
||||
*/
|
||||
do {
|
||||
state = _HA_ATOMIC_LOAD(&t->state);
|
||||
thr = t->tid;
|
||||
if (thr == -1)
|
||||
newtid = -2 - tid;
|
||||
else
|
||||
newtid = thr;
|
||||
|
||||
/* do nothing if someone else already added it */
|
||||
if (state & (TASK_QUEUED|TASK_RUNNING))
|
||||
return;
|
||||
} while (!_HA_ATOMIC_CAS(&t->state, &state, state | TASK_QUEUED));
|
||||
} while (!__task_set_state_and_tid(t, thr, newtid, state, state | TASK_QUEUED));
|
||||
|
||||
if (newtid < 0)
|
||||
thr = __task_get_current_owner(newtid);
|
||||
BUG_ON_HOT(task_in_rq(t));
|
||||
|
||||
/* at this point we're the first ones to add this task to the list */
|
||||
|
|
@ -707,11 +778,11 @@ static inline void tasklet_set_tid(struct tasklet *tl, int tid)
|
|||
|
||||
static inline void _task_schedule(struct task *task, int when, const struct ha_caller *caller)
|
||||
{
|
||||
int did_lock = 0;
|
||||
/* TODO: mthread, check if there is no task with this test */
|
||||
if (task_in_rq(task))
|
||||
return;
|
||||
|
||||
#ifdef USE_THREAD
|
||||
if (task->tid < 0) {
|
||||
/*
|
||||
* If the task is already running, then just wake it up, just
|
||||
|
|
@ -729,44 +800,26 @@ static inline void _task_schedule(struct task *task, int when, const struct ha_c
|
|||
task_wakeup(task, TASK_WOKEN_OTHER);
|
||||
return;
|
||||
}
|
||||
|
||||
/* FIXME: is it really needed to lock the WQ during the check ? */
|
||||
HA_RWLOCK_WRLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
if (task_in_wq(task))
|
||||
when = tick_first(when, task->expire);
|
||||
|
||||
task->expire = when;
|
||||
if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
|
||||
if (likely(caller)) {
|
||||
caller = HA_ATOMIC_XCHG(&task->caller, caller);
|
||||
BUG_ON((ulong)caller & 1);
|
||||
#ifdef DEBUG_TASK
|
||||
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
|
||||
#endif
|
||||
}
|
||||
__task_queue(task, &tg_ctx->timers);
|
||||
}
|
||||
task_drop_running(task, 0);
|
||||
HA_RWLOCK_WRUNLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
did_lock = 1;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
BUG_ON(task->tid != tid);
|
||||
if (task_in_wq(task))
|
||||
when = tick_first(when, task->expire);
|
||||
|
||||
task->expire = when;
|
||||
if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
|
||||
if (likely(caller)) {
|
||||
caller = HA_ATOMIC_XCHG(&task->caller, caller);
|
||||
BUG_ON((ulong)caller & 1);
|
||||
if (task_in_wq(task))
|
||||
when = tick_first(when, task->expire);
|
||||
|
||||
task->expire = when;
|
||||
if (!task_in_wq(task) || tick_is_lt(task->expire, task->wq.key)) {
|
||||
if (likely(caller)) {
|
||||
caller = HA_ATOMIC_XCHG(&task->caller, caller);
|
||||
BUG_ON((ulong)caller & 1);
|
||||
#ifdef DEBUG_TASK
|
||||
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
|
||||
HA_ATOMIC_STORE(&task->debug.prev_caller, caller);
|
||||
#endif
|
||||
}
|
||||
__task_queue(task, &th_ctx->timers);
|
||||
}
|
||||
__task_queue(task);
|
||||
}
|
||||
if (did_lock)
|
||||
task_drop_running(task, 0);
|
||||
}
|
||||
|
||||
/* returns the string corresponding to a task type as found in the task caller
|
||||
|
|
|
|||
|
|
@ -178,7 +178,6 @@ struct ha_rwlock {
|
|||
*/
|
||||
enum lock_label {
|
||||
TASK_RQ_LOCK,
|
||||
TASK_WQ_LOCK,
|
||||
LISTENER_LOCK,
|
||||
PROXY_LOCK,
|
||||
SERVER_LOCK,
|
||||
|
|
|
|||
|
|
@ -135,8 +135,6 @@ struct tgroup_ctx {
|
|||
ulong threads_idle; /* mask of threads idling in the poller */
|
||||
ulong stopping_threads; /* mask of threads currently stopping */
|
||||
|
||||
struct eb_root timers; /* wait queue (sorted timers tree, global, accessed under wq_lock) */
|
||||
|
||||
uint niced_tasks; /* number of niced tasks in this group's run queues */
|
||||
uint committed_extra_streams; /* sum of extra front streams committed by muxes in this group */
|
||||
|
||||
|
|
|
|||
77
reg-tests/checks/extcheck.vtc
Normal file
77
reg-tests/checks/extcheck.vtc
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
varnishtest "Health-checks: some external check tests"
|
||||
feature ignore_unknown_macro
|
||||
#REGTEST_TYPE=slow
|
||||
|
||||
server s1 {
|
||||
rxreq
|
||||
expect req.method == GET
|
||||
expect req.url == /health
|
||||
expect req.proto == HTTP/1.1
|
||||
txresp
|
||||
} -start
|
||||
|
||||
syslog S1 -level notice {
|
||||
recv
|
||||
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]/srv succeeded, reason: External check passed, code: 0"
|
||||
recv
|
||||
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]/srv succeeded, reason: External check passed, code: 0"
|
||||
} -start
|
||||
|
||||
syslog S2 -level notice {
|
||||
recv
|
||||
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]/srv succeeded.*code: 200"
|
||||
} -start
|
||||
|
||||
haproxy h1 -conf {
|
||||
global
|
||||
.if feature(THREAD)
|
||||
thread-groups 1
|
||||
.endif
|
||||
external-check
|
||||
insecure-fork-wanted
|
||||
|
||||
healthcheck http-health
|
||||
type httpchk
|
||||
http-check send meth GET uri /health ver HTTP/1.1
|
||||
|
||||
defaults
|
||||
mode http
|
||||
timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
option log-health-checks
|
||||
|
||||
backend be1
|
||||
log ${S1_addr}:${S1_port} len 2048 local0
|
||||
option external-check
|
||||
external-check command /bin/true
|
||||
server srv ${h1_li1_addr}:${h1_li1_port} check inter 100ms rise 1 fall 1
|
||||
|
||||
defaults
|
||||
mode http
|
||||
timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
|
||||
option external-check
|
||||
external-check command /bin/true
|
||||
option log-health-checks
|
||||
|
||||
backend be2
|
||||
log ${S1_addr}:${S1_port} len 2048 local0
|
||||
server srv ${h1_li1_addr}:${h1_li1_port} check inter 100ms rise 1 fall 1
|
||||
|
||||
backend be3
|
||||
log ${S2_addr}:${S2_port} len 2048 local0
|
||||
option external-check
|
||||
external-check command /bin/true
|
||||
server srv ${s1_addr}:${s1_port} check inter 100ms rise 1 fall 1 healthcheck http-health
|
||||
|
||||
listen li1
|
||||
mode http
|
||||
bind "fd@${li1}"
|
||||
http-request return status 200
|
||||
|
||||
} -start
|
||||
|
||||
syslog S1 -wait
|
||||
syslog S2 -wait
|
||||
|
|
@ -144,7 +144,7 @@ syslog S2 -level notice {
|
|||
recv
|
||||
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded"
|
||||
recv
|
||||
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9] succeeded"
|
||||
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded"
|
||||
recv
|
||||
expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv[0-9]+ succeeded"
|
||||
recv
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ core.register_service("fakeserv", "http", function(applet)
|
|||
end)
|
||||
|
||||
local function cron()
|
||||
local httpclient = core.httpclient()
|
||||
|
||||
-- wait for until the correct port is set through the c0 request..
|
||||
while vtc_port == 0 do
|
||||
core.msleep(1)
|
||||
|
|
@ -30,19 +32,16 @@ local function cron()
|
|||
body = body .. i .. ' ABCDEFGHIJKLMNOPQRSTUVWXYZ\n'
|
||||
end
|
||||
core.Info("First httpclient request")
|
||||
local httpclient = core.httpclient()
|
||||
local response = httpclient:post{url="http://127.0.0.1:" .. vtc_port, body=body}
|
||||
core.Info("Received: " .. response.body)
|
||||
|
||||
body = response.body
|
||||
|
||||
core.Info("Second httpclient request")
|
||||
local httpclient2 = core.httpclient()
|
||||
local response2 = httpclient2:post{url="http://127.0.0.1:" .. vtc_port2, body=body}
|
||||
local response2 = httpclient:post{url="http://127.0.0.1:" .. vtc_port2, body=body}
|
||||
|
||||
core.Info("Third httpclient request")
|
||||
local httpclient3 = core.httpclient()
|
||||
local response3 = httpclient3:get{url="http://127.0.0.1", dst = vtc_port3, headers={ [ "Host" ] = { "foobar.haproxy.local" } }}
|
||||
local response3 = httpclient:get{url="http://127.0.0.1", dst = vtc_port3, headers={ [ "Host" ] = { "foobar.haproxy.local" } }}
|
||||
|
||||
end
|
||||
|
||||
|
|
|
|||
273
src/acme.c
273
src/acme.c
|
|
@ -19,6 +19,7 @@
|
|||
#include <haproxy/acme-t.h>
|
||||
|
||||
#include <haproxy/acme_resolvers.h>
|
||||
#include <haproxy/event_hdl.h>
|
||||
#include <haproxy/base64.h>
|
||||
#include <haproxy/intops.h>
|
||||
#include <haproxy/cfgparse.h>
|
||||
|
|
@ -37,6 +38,12 @@
|
|||
#include <haproxy/ssl_utils.h>
|
||||
#include <haproxy/tools.h>
|
||||
#include <haproxy/trace.h>
|
||||
#ifdef USE_LUA
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <haproxy/hlua.h>
|
||||
#include <haproxy/hlua_fcn.h>
|
||||
#endif
|
||||
|
||||
#define TRACE_SOURCE &trace_acme
|
||||
|
||||
|
|
@ -1450,6 +1457,27 @@ error:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* mfree callback for EVENT_HDL_SUB_ACME_DEPLOY: frees heap-allocated fields */
|
||||
static void acme_deploy_event_mfree(const void *data)
|
||||
{
|
||||
struct event_hdl_cb_data_acme_deploy *e = (struct event_hdl_cb_data_acme_deploy *)data;
|
||||
|
||||
ha_free(&e->safe.crtname);
|
||||
ha_free(&e->safe.domain);
|
||||
ha_free(&e->safe.thumbprint);
|
||||
ha_free(&e->safe.dns_record);
|
||||
ha_free(&e->safe.provider);
|
||||
ha_free(&e->safe.vars);
|
||||
}
|
||||
|
||||
/* mfree callback for EVENT_HDL_SUB_ACME_NEWCERT: frees the heap-allocated path */
|
||||
static void acme_newcert_event_mfree(const void *data)
|
||||
{
|
||||
const struct event_hdl_cb_data_acme_newcert *e = data;
|
||||
|
||||
free(e->safe.crtname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update every certificate instances for the new store
|
||||
*
|
||||
|
|
@ -1503,6 +1531,15 @@ int acme_update_certificate(struct task *task, struct acme_ctx *ctx, char **errm
|
|||
if (dpapi)
|
||||
sink_write(dpapi, LOG_HEADER_NONE, 0, line, 3);
|
||||
|
||||
{
|
||||
struct event_hdl_cb_data_acme_newcert cb_data = { };
|
||||
|
||||
cb_data.safe.crtname = strdup(ctx->store->path);
|
||||
if (cb_data.safe.crtname)
|
||||
event_hdl_publish(NULL, EVENT_HDL_SUB_ACME_NEWCERT,
|
||||
EVENT_HDL_CB_DATA_DM(&cb_data, acme_newcert_event_mfree));
|
||||
}
|
||||
|
||||
ctx->store = NULL;
|
||||
|
||||
ret = 0;
|
||||
|
|
@ -2028,6 +2065,7 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
|
|||
/* if auth is already valid we need to skip solving challenges */
|
||||
if (strncasecmp("valid", trash.area, trash.data) == 0) {
|
||||
auth->validated = 1;
|
||||
auth->ready = ctx->cfg->cond_ready; /* no challenge needed, satisfy all readiness conditions */
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -2181,6 +2219,22 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut
|
|||
dpapi = sink_find("dpapi");
|
||||
if (dpapi)
|
||||
sink_write(dpapi, LOG_HEADER_NONE, 0, line, nmsg);
|
||||
|
||||
{
|
||||
struct event_hdl_cb_data_acme_deploy cb_data = { };
|
||||
|
||||
cb_data.safe.crtname = strdup(ctx->store->path);
|
||||
cb_data.safe.domain = isttest(auth->dns) ? strndup(auth->dns.ptr, auth->dns.len) : NULL;
|
||||
cb_data.safe.thumbprint = ctx->cfg->account.thumbprint ? strdup(ctx->cfg->account.thumbprint) : NULL;
|
||||
cb_data.safe.dns_record = strndup(dns_record->area, dns_record->data);
|
||||
cb_data.safe.provider = ctx->cfg->provider ? strdup(ctx->cfg->provider) : NULL;
|
||||
cb_data.safe.vars = ctx->cfg->vars ? strdup(ctx->cfg->vars) : NULL;
|
||||
if (cb_data.safe.crtname && cb_data.safe.dns_record)
|
||||
event_hdl_publish(NULL, EVENT_HDL_SUB_ACME_DEPLOY,
|
||||
EVENT_HDL_CB_DATA_DM(&cb_data, acme_deploy_event_mfree));
|
||||
else
|
||||
acme_deploy_event_mfree(&cb_data);
|
||||
}
|
||||
}
|
||||
else if (strcasecmp(ctx->cfg->challenge, "http-01") == 0) {
|
||||
/* only useful for http-01 */
|
||||
|
|
@ -3505,16 +3559,64 @@ err:
|
|||
return cli_dynerr(appctx, errmsg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the readiness of an ACME challenge per couple <crt>+<dns>
|
||||
* Return:
|
||||
* - -2 if the crt was not found
|
||||
* - -1 if an non-ready couple crt+dns wasn't not found
|
||||
* - 0 if the challenges are ready for the certificate
|
||||
* - > 0 with the number of remaining challenge to enable
|
||||
*/
|
||||
int acme_challenge_ready(const char *crt, const char *dns)
|
||||
{
|
||||
struct ebmb_node *node = NULL;
|
||||
struct acme_ctx *ctx = NULL;
|
||||
struct acme_auth *auth = NULL;
|
||||
int found = 0;
|
||||
int remain = 0;
|
||||
|
||||
HA_RWLOCK_WRLOCK(OTHER_LOCK, &acme_lock);
|
||||
node = ebst_lookup(&acme_tasks, crt);
|
||||
if (!node) {
|
||||
HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock);
|
||||
return -2;
|
||||
}
|
||||
|
||||
ctx = ebmb_entry(node, struct acme_ctx, node);
|
||||
if (ctx->cfg->cond_ready & ACME_RDY_CLI)
|
||||
auth = ctx->auths;
|
||||
while (auth) {
|
||||
if (strncmp(dns, auth->dns.ptr, auth->dns.len) == 0) {
|
||||
if ((auth->ready & ACME_RDY_CLI) == 0) {
|
||||
auth->ready |= ACME_RDY_CLI;
|
||||
found++;
|
||||
}
|
||||
}
|
||||
if ((auth->ready & ACME_RDY_CLI) == 0)
|
||||
remain++;
|
||||
auth = auth->next;
|
||||
}
|
||||
/* no remaining challenge to ready: wake the task only if it is
|
||||
* currently suspended in ACME_CLI_WAIT, not in the middle of an
|
||||
* HTTP exchange.
|
||||
*/
|
||||
if (!remain && ctx->state == ACME_CLI_WAIT)
|
||||
task_wakeup(ctx->task, TASK_WOKEN_MSG);
|
||||
|
||||
HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock);
|
||||
|
||||
if (!found)
|
||||
return -1;
|
||||
|
||||
return remain;
|
||||
}
|
||||
|
||||
static int cli_acme_chall_ready_parse(char **args, char *payload, struct appctx *appctx, void *private)
|
||||
{
|
||||
char *msg = NULL;
|
||||
const char *crt;
|
||||
const char *dns;
|
||||
struct acme_ctx *ctx = NULL;
|
||||
struct acme_auth *auth = NULL;
|
||||
int found = 0;
|
||||
int remain = 0;
|
||||
struct ebmb_node *node = NULL;
|
||||
int ret;
|
||||
|
||||
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
|
||||
return 1;
|
||||
|
|
@ -3527,39 +3629,13 @@ static int cli_acme_chall_ready_parse(char **args, char *payload, struct appctx
|
|||
crt = args[2];
|
||||
dns = args[4];
|
||||
|
||||
HA_RWLOCK_WRLOCK(OTHER_LOCK, &acme_lock);
|
||||
node = ebst_lookup(&acme_tasks, crt);
|
||||
if (node) {
|
||||
ctx = ebmb_entry(node, struct acme_ctx, node);
|
||||
if (ctx->cfg->cond_ready & ACME_RDY_CLI)
|
||||
auth = ctx->auths;
|
||||
while (auth) {
|
||||
if (strncmp(dns, auth->dns.ptr, auth->dns.len) == 0) {
|
||||
if (!(auth->ready & ACME_RDY_CLI)) {
|
||||
auth->ready |= ACME_RDY_CLI;
|
||||
found++;
|
||||
} else {
|
||||
memprintf(&msg, "ACME challenge for crt \"%s\" and dns \"%s\" was already READY !\n", crt, dns);
|
||||
}
|
||||
}
|
||||
if ((auth->ready & ACME_RDY_CLI) == 0)
|
||||
remain++;
|
||||
auth = auth->next;
|
||||
}
|
||||
}
|
||||
HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &acme_lock);
|
||||
if (!found) {
|
||||
if (!msg)
|
||||
memprintf(&msg, "Couldn't find an ACME task using crt \"%s\" and dns \"%s\" to set as ready!\n", crt, dns);
|
||||
goto err;
|
||||
} else {
|
||||
if (!remain) {
|
||||
if (ctx)
|
||||
task_wakeup(ctx->task, TASK_WOKEN_MSG);
|
||||
return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "%d '%s' challenge(s) ready! All challenges ready, starting challenges validation!", found, dns));
|
||||
} else {
|
||||
return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "%d '%s' challenge(s) ready! Remaining challenges to deploy: %d", found, dns, remain));
|
||||
}
|
||||
ret = acme_challenge_ready(crt, dns);
|
||||
if (ret < 0) {
|
||||
return cli_dynerr(appctx, memprintf(&msg, "Couldn't find an ACME task using crt \"%s\" and dns \"%s\" to set as ready!\n", crt, dns));
|
||||
} else if (ret == 0) {
|
||||
return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "'%s' challenges ready! All challenges ready, starting challenges validation!", crt));
|
||||
} else if (ret > 0) {
|
||||
return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "'%s' challenge(s) ready! Remaining challenges to deploy: %d", crt, ret));
|
||||
}
|
||||
|
||||
err:
|
||||
|
|
@ -3669,6 +3745,127 @@ static void __acme_init(void)
|
|||
}
|
||||
INITCALL0(STG_REGISTER, __acme_init);
|
||||
|
||||
#ifdef USE_LUA
|
||||
|
||||
#define CLASS_ACME_EVENT "AcmeEvent"
|
||||
static int class_acme_event_ref;
|
||||
|
||||
/* Push a new AcmeEvent object for an ACME_DEPLOY event onto the Lua stack.
|
||||
* The object exposes crtname, domain, thumbprint, dns_record fields, and
|
||||
* optionally provider and vars if they were configured.
|
||||
*/
|
||||
static void hlua_fcn_new_acme_event_deploy(lua_State *L, const struct event_hdl_cb_data_acme_deploy *e)
|
||||
{
|
||||
lua_newtable(L);
|
||||
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, class_acme_event_ref);
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
lua_pushstring(L, e->safe.crtname ? e->safe.crtname : "");
|
||||
lua_setfield(L, -2, "crtname");
|
||||
|
||||
lua_pushstring(L, e->safe.domain ? e->safe.domain : "");
|
||||
lua_setfield(L, -2, "domain");
|
||||
|
||||
lua_pushstring(L, e->safe.thumbprint ? e->safe.thumbprint : "");
|
||||
lua_setfield(L, -2, "thumbprint");
|
||||
|
||||
lua_pushstring(L, e->safe.dns_record ? e->safe.dns_record : "");
|
||||
lua_setfield(L, -2, "dns_record");
|
||||
|
||||
if (e->safe.provider) {
|
||||
lua_pushstring(L, e->safe.provider);
|
||||
lua_setfield(L, -2, "provider");
|
||||
}
|
||||
|
||||
if (e->safe.vars) {
|
||||
lua_pushstring(L, e->safe.vars);
|
||||
lua_setfield(L, -2, "vars");
|
||||
}
|
||||
}
|
||||
|
||||
/* Push a new AcmeEvent object for an ACME_NEWCERT event onto the Lua stack.
|
||||
* The object exposes a <crtname> field with the certificate store name.
|
||||
*/
|
||||
static void hlua_fcn_new_acme_event_newcert(lua_State *L, const char *crtname)
|
||||
{
|
||||
lua_newtable(L);
|
||||
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, class_acme_event_ref);
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
lua_pushstring(L, crtname);
|
||||
lua_setfield(L, -2, "crtname");
|
||||
}
|
||||
|
||||
/*
|
||||
* ACME.challenge_ready(crt, dns)
|
||||
*
|
||||
* Marks the ACME challenge for domain <dns> in certificate <crt> as ready.
|
||||
* Returns the number of remaining challenges, or 0 if all challenges are
|
||||
* ready and validation has been triggered.
|
||||
* Raises a Lua error if the certificate or domain is not found.
|
||||
*/
|
||||
__LJMP static int hlua_acme_challenge_ready(lua_State *L)
|
||||
{
|
||||
const char *crt;
|
||||
const char *dns;
|
||||
int ret;
|
||||
|
||||
if (lua_gettop(L) != 2)
|
||||
WILL_LJMP(luaL_error(L, "'ACME.challenge_ready' needs 2 arguments."));
|
||||
|
||||
crt = MAY_LJMP(luaL_checkstring(L, 1));
|
||||
dns = MAY_LJMP(luaL_checkstring(L, 2));
|
||||
|
||||
ret = acme_challenge_ready(crt, dns);
|
||||
if (ret == -2)
|
||||
WILL_LJMP(luaL_error(L, "ACME.challenge_ready: certificate '%s' not found", crt));
|
||||
if (ret == -1)
|
||||
WILL_LJMP(luaL_error(L, "ACME.challenge_ready: domain '%s' not found for certificate '%s'", dns, crt));
|
||||
|
||||
lua_pushinteger(L, ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int acme_hlua_init_state(lua_State *L, char **errmsg)
|
||||
{
|
||||
/* Register AcmeEvent class */
|
||||
lua_newtable(L);
|
||||
class_acme_event_ref = hlua_register_metatable(L, CLASS_ACME_EVENT);
|
||||
|
||||
lua_newtable(L);
|
||||
hlua_class_function(L, "challenge_ready", hlua_acme_challenge_ready);
|
||||
lua_setglobal(L, "ACME");
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
REGISTER_HLUA_STATE_INIT(acme_hlua_init_state);
|
||||
|
||||
/* Push ACME event data as a Lua table for core.event_sub() handlers.
|
||||
* Called from hlua_event_hdl_cb_push_args() when the event family is ACME.
|
||||
*/
|
||||
void acme_hlua_event_push_args(struct hlua *hlua, struct event_hdl_sub_type event, void *data)
|
||||
{
|
||||
if (!lua_checkstack(hlua->T, 3))
|
||||
WILL_LJMP(luaL_error(hlua->T, "Lua out of memory error."));
|
||||
|
||||
if (event_hdl_sub_type_equal(EVENT_HDL_SUB_ACME_DEPLOY, event)) {
|
||||
struct event_hdl_cb_data_acme_deploy *e_acme = data;
|
||||
|
||||
hlua->nargs += 1;
|
||||
MAY_LJMP(hlua_fcn_new_acme_event_deploy(hlua->T, e_acme));
|
||||
}
|
||||
else if (event_hdl_sub_type_equal(EVENT_HDL_SUB_ACME_NEWCERT, event)) {
|
||||
struct event_hdl_cb_data_acme_newcert *e_acme = data;
|
||||
|
||||
hlua->nargs += 1;
|
||||
MAY_LJMP(hlua_fcn_new_acme_event_newcert(hlua->T, e_acme->safe.crtname));
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* USE_LUA */
|
||||
|
||||
#endif /* ! HAVE_ACME */
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -80,7 +80,10 @@ static const char *const memprof_methods[MEMPROF_METH_METHODS] = {
|
|||
struct memprof_stats memprof_stats[MEMPROF_HASH_BUCKETS + 1] = { };
|
||||
|
||||
/* used to detect recursive calls */
|
||||
static THREAD_LOCAL int in_memprof = 0;
|
||||
#define MEMPROF_IN_INIT (1U << 0)
|
||||
#define MEMPROF_IN_HANDLER (1U << 1)
|
||||
|
||||
static THREAD_LOCAL uint in_memprof = 0; // arithmetic OR of MEMPROF_IN_*
|
||||
|
||||
/* These ones are used by glibc and will be called early. They are in charge of
|
||||
* initializing the handlers with the original functions.
|
||||
|
|
@ -137,7 +140,7 @@ static __attribute__((noreturn)) void memprof_die(const char *msg)
|
|||
*/
|
||||
static void memprof_init()
|
||||
{
|
||||
in_memprof++;
|
||||
in_memprof |= MEMPROF_IN_INIT;
|
||||
memprof_malloc_handler = get_sym_next_addr("malloc");
|
||||
if (!memprof_malloc_handler)
|
||||
memprof_die("FATAL: malloc() function not found.\n");
|
||||
|
|
@ -168,7 +171,7 @@ static void memprof_init()
|
|||
memprof_aligned_alloc_handler = get_sym_next_addr("aligned_alloc");
|
||||
memprof_posix_memalign_handler = get_sym_next_addr("posix_memalign");
|
||||
|
||||
in_memprof--;
|
||||
in_memprof &= ~MEMPROF_IN_INIT;
|
||||
}
|
||||
|
||||
/* the initial handlers will initialize all regular handlers and will call the
|
||||
|
|
@ -177,7 +180,7 @@ static void memprof_init()
|
|||
*/
|
||||
static void *memprof_malloc_initial_handler(size_t size)
|
||||
{
|
||||
if (in_memprof) {
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
/* it's likely that dlsym() needs malloc(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -188,7 +191,7 @@ static void *memprof_malloc_initial_handler(size_t size)
|
|||
|
||||
static void *memprof_calloc_initial_handler(size_t nmemb, size_t size)
|
||||
{
|
||||
if (in_memprof) {
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
/* it's likely that dlsym() needs calloc(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -198,7 +201,7 @@ static void *memprof_calloc_initial_handler(size_t nmemb, size_t size)
|
|||
|
||||
static void *memprof_realloc_initial_handler(void *ptr, size_t size)
|
||||
{
|
||||
if (in_memprof) {
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
/* it's likely that dlsym() needs realloc(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -209,7 +212,7 @@ static void *memprof_realloc_initial_handler(void *ptr, size_t size)
|
|||
|
||||
static char *memprof_strdup_initial_handler(const char *s)
|
||||
{
|
||||
if (in_memprof) {
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
/* probably that dlsym() needs strdup(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -228,7 +231,7 @@ static void memprof_free_initial_handler(void *ptr)
|
|||
|
||||
static char *memprof_strndup_initial_handler(const char *s, size_t n)
|
||||
{
|
||||
if (in_memprof) {
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
/* probably that dlsym() needs strndup(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -239,7 +242,7 @@ static char *memprof_strndup_initial_handler(const char *s, size_t n)
|
|||
|
||||
static void *memprof_valloc_initial_handler(size_t sz)
|
||||
{
|
||||
if (in_memprof) {
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
/* probably that dlsym() needs valloc(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -250,7 +253,7 @@ static void *memprof_valloc_initial_handler(size_t sz)
|
|||
|
||||
static void *memprof_pvalloc_initial_handler(size_t sz)
|
||||
{
|
||||
if (in_memprof) {
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
/* probably that dlsym() needs pvalloc(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -261,7 +264,7 @@ static void *memprof_pvalloc_initial_handler(size_t sz)
|
|||
|
||||
static void *memprof_memalign_initial_handler(size_t al, size_t sz)
|
||||
{
|
||||
if (in_memprof) {
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
/* probably that dlsym() needs memalign(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -272,7 +275,7 @@ static void *memprof_memalign_initial_handler(size_t al, size_t sz)
|
|||
|
||||
static void *memprof_aligned_alloc_initial_handler(size_t al, size_t sz)
|
||||
{
|
||||
if (in_memprof) {
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
/* probably that dlsym() needs aligned_alloc(), let's fail */
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -283,7 +286,7 @@ static void *memprof_aligned_alloc_initial_handler(size_t al, size_t sz)
|
|||
|
||||
static int memprof_posix_memalign_initial_handler(void **ptr, size_t al, size_t sz)
|
||||
{
|
||||
if (in_memprof) {
|
||||
if (in_memprof & MEMPROF_IN_INIT) {
|
||||
/* probably that dlsym() needs posix_memalign(), let's fail */
|
||||
return ENOMEM;
|
||||
}
|
||||
|
|
@ -344,11 +347,13 @@ void *malloc(size_t size)
|
|||
struct memprof_stats *bin;
|
||||
void *ret;
|
||||
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
return memprof_malloc_handler(size);
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
ret = memprof_malloc_handler(size);
|
||||
size = malloc_usable_size(ret) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_MALLOC);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
|
|
@ -371,11 +376,13 @@ void *calloc(size_t nmemb, size_t size)
|
|||
struct memprof_stats *bin;
|
||||
void *ret;
|
||||
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
return memprof_calloc_handler(nmemb, size);
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
ret = memprof_calloc_handler(nmemb, size);
|
||||
size = malloc_usable_size(ret) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_CALLOC);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
|
|
@ -401,12 +408,14 @@ void *realloc(void *ptr, size_t size)
|
|||
size_t size_before;
|
||||
void *ret;
|
||||
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
return memprof_realloc_handler(ptr, size);
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
size_before = malloc_usable_size(ptr);
|
||||
ret = memprof_realloc_handler(ptr, size);
|
||||
size = malloc_usable_size(ret);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
/* only count the extra link for new allocations */
|
||||
if (!ptr)
|
||||
|
|
@ -439,11 +448,13 @@ char *strdup(const char *s)
|
|||
size_t size;
|
||||
char *ret;
|
||||
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
return memprof_strdup_handler(s);
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
ret = memprof_strdup_handler(s);
|
||||
size = malloc_usable_size(ret) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_STRDUP);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
|
|
@ -469,13 +480,15 @@ void free(void *ptr)
|
|||
struct memprof_stats *bin;
|
||||
size_t size_before;
|
||||
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || !ptr)) {
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || !ptr || (in_memprof & MEMPROF_IN_HANDLER))) {
|
||||
memprof_free_handler(ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
size_before = malloc_usable_size(ptr) + sizeof(void *);
|
||||
memprof_free_handler(ptr);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_FREE);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
|
|
@ -495,10 +508,13 @@ char *strndup(const char *s, size_t size)
|
|||
return NULL;
|
||||
|
||||
ret = memprof_strndup_handler(s, size);
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
return ret;
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
size = malloc_usable_size(ret) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_STRNDUP);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||
|
|
@ -516,10 +532,13 @@ void *valloc(size_t size)
|
|||
return NULL;
|
||||
|
||||
ret = memprof_valloc_handler(size);
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
return ret;
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
size = malloc_usable_size(ret) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_VALLOC);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||
|
|
@ -537,10 +556,13 @@ void *pvalloc(size_t size)
|
|||
return NULL;
|
||||
|
||||
ret = memprof_pvalloc_handler(size);
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
return ret;
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
size = malloc_usable_size(ret) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_PVALLOC);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||
|
|
@ -558,10 +580,13 @@ void *memalign(size_t align, size_t size)
|
|||
return NULL;
|
||||
|
||||
ret = memprof_memalign_handler(align, size);
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
return ret;
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
size = malloc_usable_size(ret) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_MEMALIGN);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||
|
|
@ -579,10 +604,13 @@ void *aligned_alloc(size_t align, size_t size)
|
|||
return NULL;
|
||||
|
||||
ret = memprof_aligned_alloc_handler(align, size);
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
return ret;
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
size = malloc_usable_size(ret) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_ALIGNED_ALLOC);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||
|
|
@ -600,13 +628,16 @@ int posix_memalign(void **ptr, size_t align, size_t size)
|
|||
return ENOMEM;
|
||||
|
||||
ret = memprof_posix_memalign_handler(ptr, align, size);
|
||||
if (likely(!(profiling & HA_PROF_MEMORY)))
|
||||
if (likely(!(profiling & HA_PROF_MEMORY) || (in_memprof & MEMPROF_IN_HANDLER)))
|
||||
return ret;
|
||||
|
||||
if (ret != 0) // error
|
||||
return ret;
|
||||
|
||||
in_memprof |= MEMPROF_IN_HANDLER;
|
||||
size = malloc_usable_size(*ptr) + sizeof(void *);
|
||||
in_memprof &= ~MEMPROF_IN_HANDLER;
|
||||
|
||||
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_POSIX_MEMALIGN);
|
||||
if (unlikely(th_ctx->lock_level & 0x7F))
|
||||
_HA_ATOMIC_ADD(&bin->locked_calls, 1);
|
||||
|
|
|
|||
|
|
@ -2486,16 +2486,17 @@ init_proxies_list_stage1:
|
|||
/* At this point, target names have already been resolved. */
|
||||
/***********************************************************/
|
||||
|
||||
idle_conn_task = task_new_anywhere();
|
||||
if (!idle_conn_task) {
|
||||
ha_alert("parsing : failed to allocate global idle connection task.\n");
|
||||
cfgerr++;
|
||||
}
|
||||
else {
|
||||
idle_conn_task->process = srv_cleanup_idle_conns;
|
||||
idle_conn_task->context = NULL;
|
||||
for (i = 0; i < global.nbthread; i++) {
|
||||
idle_conn_srv[i] = EB_ROOT;
|
||||
idle_conn_task[i] = task_new_on(i);
|
||||
if (!idle_conn_task[i]) {
|
||||
ha_alert("parsing : failed to allocate global idle connection task.\n");
|
||||
cfgerr++;
|
||||
}
|
||||
else {
|
||||
idle_conn_task[i]->process = srv_cleanup_idle_conns;
|
||||
idle_conn_task[i]->context = NULL;
|
||||
|
||||
for (i = 0; i < global.nbthread; i++) {
|
||||
idle_conns[i].cleanup_task = task_new_on(i);
|
||||
if (!idle_conns[i].cleanup_task) {
|
||||
ha_alert("parsing : failed to allocate idle connection tasks for thread '%d'.\n", i);
|
||||
|
|
|
|||
26
src/check.c
26
src/check.c
|
|
@ -232,6 +232,9 @@ static void check_trace(enum trace_level level, uint64_t mask,
|
|||
chunk_appendf(&trace_buf, " sc=%p(0x%08x)", check->sc, check->sc->flags);
|
||||
}
|
||||
|
||||
if (check->type != PR_O2_TCPCHK_CHK)
|
||||
return;
|
||||
|
||||
if (mask & CHK_EV_TCPCHK) {
|
||||
const char *type;
|
||||
|
||||
|
|
@ -1404,7 +1407,20 @@ struct task *process_chk_conn(struct task *t, void *context, unsigned int state)
|
|||
|
||||
check_release_buf(check, &check->bi);
|
||||
check_release_buf(check, &check->bo);
|
||||
_HA_ATOMIC_DEC(&th_ctx->running_checks);
|
||||
|
||||
if (unlikely(LIST_INLIST(&check->check_queue))) {
|
||||
/*
|
||||
* If that check is still queued, and we're about to
|
||||
* purge it, then remove it from the queue, as it is
|
||||
* about to be freed.
|
||||
* This can happen if a server is deleted while the check
|
||||
* is queued.
|
||||
*/
|
||||
if (check->state & CHK_ST_PURGE)
|
||||
LIST_DEL_INIT(&check->check_queue);
|
||||
}
|
||||
else
|
||||
_HA_ATOMIC_DEC(&th_ctx->running_checks);
|
||||
_HA_ATOMIC_DEC(&th_ctx->active_checks);
|
||||
check->state &= ~(CHK_ST_INPROGRESS|CHK_ST_IN_ALLOC|CHK_ST_OUT_ALLOC);
|
||||
check->state &= ~CHK_ST_READY;
|
||||
|
|
@ -1563,6 +1579,7 @@ void free_check(struct check *check)
|
|||
ha_free(&check->tcpcheck);
|
||||
}
|
||||
|
||||
LIST_DEL_INIT(&check->check_queue);
|
||||
pool_free(pool_head_uniqueid, istptr(check->unique_id));
|
||||
check->unique_id = IST_NULL;
|
||||
ha_free(&check->pool_conn_name);
|
||||
|
|
@ -1682,7 +1699,7 @@ static int start_checks()
|
|||
for (px = proxies_list; px; px = px->next) {
|
||||
for (s = px->srv; s; s = s->next) {
|
||||
if ((px->options2 & PR_O2_USE_SBUF_CHECK) &&
|
||||
(s->check.tcpcheck->rs->flags & TCPCHK_RULES_MAY_USE_SBUF))
|
||||
(s->check.tcpcheck->rs && s->check.tcpcheck->rs->flags & TCPCHK_RULES_MAY_USE_SBUF))
|
||||
s->check.state |= CHK_ST_USE_SMALL_BUFF;
|
||||
|
||||
if (s->check.state & CHK_ST_CONFIGURED) {
|
||||
|
|
@ -1799,6 +1816,9 @@ int init_srv_check(struct server *srv)
|
|||
if (!srv->do_check || !(srv->proxy->cap & PR_CAP_BE))
|
||||
goto out;
|
||||
|
||||
if (!srv->check.type && (srv->proxy->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK)
|
||||
goto init;
|
||||
|
||||
check_type = srv->check.tcpcheck->rs->flags & TCPCHK_RULES_PROTO_CHK;
|
||||
|
||||
if (!(srv->flags & SRV_F_DYNAMIC)) {
|
||||
|
|
@ -1939,7 +1959,7 @@ int init_srv_check(struct server *srv)
|
|||
}
|
||||
|
||||
init:
|
||||
err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
|
||||
err = init_check(&srv->check, srv->check.type ? srv->check.type : (srv->proxy->options2 & PR_O2_CHK_ANY));
|
||||
if (err) {
|
||||
ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
|
||||
proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
|
||||
|
|
|
|||
|
|
@ -1279,7 +1279,7 @@ static int cpu_policy_first_usable_node(int policy, int tmin, int tmax, int gmin
|
|||
if (tmin <= thr_count && thr_count < tmax)
|
||||
tmax = thr_count;
|
||||
|
||||
ha_diag_warning("Multi-socket cpu detected, automatically binding on active CPUs of '%d' (%u active cpu(s))\n", first_node_id, cpu_count);
|
||||
ha_diag_notice("Multi-socket cpu detected, automatically binding on active CPUs of '%d' (%u active cpu(s))\n", first_node_id, cpu_count);
|
||||
|
||||
if (!global.nbthread)
|
||||
global.nbthread = tmax;
|
||||
|
|
@ -1586,9 +1586,9 @@ static int cpu_policy_group_by_cluster(int policy, int tmin, int tmax, int gmin,
|
|||
}
|
||||
|
||||
if (global.nbthread)
|
||||
ha_diag_warning("Created %d threads split into %d groups\n", global.nbthread, global.nbtgroups);
|
||||
ha_diag_notice("Created %d threads split into %d groups\n", global.nbthread, global.nbtgroups);
|
||||
else
|
||||
ha_diag_warning("Could not determine any CPU cluster\n");
|
||||
ha_diag_notice("Could not determine any CPU cluster\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1684,9 +1684,9 @@ static int cpu_policy_group_by_ccx(int policy, int tmin, int tmax, int gmin, int
|
|||
}
|
||||
|
||||
if (global.nbthread)
|
||||
ha_diag_warning("Created %d threads split into %d groups\n", global.nbthread, global.nbtgroups);
|
||||
ha_diag_notice("Created %d threads split into %d groups\n", global.nbthread, global.nbtgroups);
|
||||
else
|
||||
ha_diag_warning("Could not determine any CPU cluster\n");
|
||||
ha_diag_notice("Could not determine any CPU cluster\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
14
src/errors.c
14
src/errors.c
|
|
@ -386,6 +386,20 @@ void ha_diag_warning(const char *fmt, ...)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Displays the message on stderr with the pid if MODE_DIAG is set.
|
||||
*/
|
||||
void ha_diag_notice(const char *fmt, ...)
|
||||
{
|
||||
va_list argp;
|
||||
|
||||
if (global.mode & MODE_DIAG) {
|
||||
va_start(argp, fmt);
|
||||
print_message(1, "DIAG", fmt, argp);
|
||||
va_end(argp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Displays the message on stderr with the pid.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@ static struct event_hdl_sub_type_map event_hdl_sub_type_map[] = {
|
|||
{"PAT_REF_SET", EVENT_HDL_SUB_PAT_REF_SET},
|
||||
{"PAT_REF_COMMIT", EVENT_HDL_SUB_PAT_REF_COMMIT},
|
||||
{"PAT_REF_CLEAR", EVENT_HDL_SUB_PAT_REF_CLEAR},
|
||||
{"ACME", EVENT_HDL_SUB_ACME},
|
||||
{"ACME_NEWCERT", EVENT_HDL_SUB_ACME_NEWCERT},
|
||||
{"ACME_DEPLOY", EVENT_HDL_SUB_ACME_DEPLOY},
|
||||
};
|
||||
|
||||
/* internal types (only used in this file) */
|
||||
|
|
|
|||
93
src/h3.c
93
src/h3.c
|
|
@ -92,6 +92,10 @@ static const struct name_desc h3_trace_decoding[] = {
|
|||
{ .name="clean", .desc="only user-friendly stuff, generally suitable for level \"user\"" },
|
||||
#define H3_VERB_MINIMAL 2
|
||||
{ .name="minimal", .desc="report only qcc/qcs state and flags, no real decoding" },
|
||||
#define H3_VERB_SIMPLE 3
|
||||
{ .name="simple", .desc="add request/response status line or frame info when available" },
|
||||
#define H3_VERB_ADVANCED 4
|
||||
{ .name="advanced", .desc="add header fields or frame decoding when available" },
|
||||
{ /* end */ }
|
||||
};
|
||||
|
||||
|
|
@ -630,6 +634,51 @@ static struct ist _h3_trim_header(struct ist value)
|
|||
return v;
|
||||
}
|
||||
|
||||
static void _h3_trace_header(const struct ist n, const struct ist v,
|
||||
uint64_t mask, const struct ist trc_loc, const char *func,
|
||||
const struct qcc *qcc, const struct qcs *qcs)
|
||||
{
|
||||
struct ist n_short, v_short;
|
||||
const char *c_str __maybe_unused;
|
||||
const char *s_str __maybe_unused;
|
||||
|
||||
chunk_reset(&trash);
|
||||
c_str = chunk_newstr(&trash);
|
||||
if (qcc)
|
||||
chunk_appendf(&trash, "qcc=%p(%c)", qcc, (qcc->flags & QC_CF_IS_BACK) ? 'B' : 'F');
|
||||
|
||||
s_str = chunk_newstr(&trash);
|
||||
if (qcs)
|
||||
chunk_appendf(&trash, " qcc=%p(%llu)", qcs, (ullong)qcs->id);
|
||||
|
||||
n_short = ist2(chunk_newstr(&trash), 0);
|
||||
istscpy(&n_short, n, 256);
|
||||
trash.data += n_short.len;
|
||||
if (n_short.len != n.len)
|
||||
chunk_appendf(&trash, " (... +%ld)", (long)(n.len - n_short.len));
|
||||
|
||||
v_short = ist2(chunk_newstr(&trash), 0);
|
||||
istscpy(&v_short, v, 1024);
|
||||
trash.data += v_short.len;
|
||||
if (v_short.len != v.len)
|
||||
chunk_appendf(&trash, " (... +%ld)", (long)(v.len - v_short.len));
|
||||
|
||||
TRACE_PRINTF_LOC(TRACE_LEVEL_USER, mask, trc_loc, func,
|
||||
0, 0, 0, 0, "%s%s %s %s: %s", c_str, s_str,
|
||||
mask & H3_EV_TX_HDR ? "sndh" : "rcvh",
|
||||
istptr(n_short), istptr(v_short));
|
||||
}
|
||||
|
||||
/* Output a trace for HTTP/3 header <n>:<v> if tracing is enabled. */
|
||||
static void h3_trace_header(const struct ist n, const struct ist v,
|
||||
uint64_t mask, const struct ist trc_loc, const char *func,
|
||||
const struct qcc *qcc, const struct qcs *qcs)
|
||||
{
|
||||
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED &&
|
||||
TRACE_ENABLED(TRACE_LEVEL_USER, mask, qcc ? qcc->conn : 0, qcs, 0, 0))
|
||||
_h3_trace_header(n, v, mask, trc_loc, func, qcc, qcs);
|
||||
}
|
||||
|
||||
/* Parse from buffer <buf> a H3 HEADERS frame of length <len>. Data are copied
|
||||
* in a local HTX buffer and transfer to the stream connector layer. <fin> must be
|
||||
* set if this is the last data to transfer from this stream.
|
||||
|
|
@ -695,6 +744,12 @@ static ssize_t h3_req_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED &&
|
||||
TRACE_ENABLED(TRACE_LEVEL_USER, H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, 0, 0, 0)) {
|
||||
for (i = 0; list[i].n.len; ++i)
|
||||
h3_trace_header(list[i].n, list[i].v, H3_EV_RX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
|
||||
}
|
||||
|
||||
if (!b_alloc(&htx_buf, DB_SE_RX)) {
|
||||
TRACE_ERROR("HTX buffer alloc failure", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
|
||||
len = -1;
|
||||
|
|
@ -1183,6 +1238,13 @@ static ssize_t h3_resp_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED &&
|
||||
TRACE_ENABLED(TRACE_LEVEL_USER, H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, 0, 0, 0)) {
|
||||
int i;
|
||||
for (i = 0; list[i].n.len; ++i)
|
||||
h3_trace_header(list[i].n, list[i].v, H3_EV_RX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
|
||||
}
|
||||
|
||||
if (!(appbuf = qcc_get_stream_rxbuf(qcs))) {
|
||||
TRACE_ERROR("buffer alloc failure", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
|
||||
len = -1;
|
||||
|
|
@ -2012,8 +2074,9 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
|||
* receipt of a PUSH_PROMISE frame that contains a larger push ID than
|
||||
* the client has advertised as a connection error of H3_ID_ERROR.
|
||||
*/
|
||||
ret = H3_ERR_ID_ERROR;
|
||||
break;
|
||||
TRACE_ERROR("Received unexpected PUSH_PROMISE frame", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
|
||||
qcc_set_error(qcs->qcc, H3_ERR_ID_ERROR, 1, muxc_tevt_type_proto_err);
|
||||
goto err;
|
||||
case H3_FT_MAX_PUSH_ID:
|
||||
/* h3_check_frame_valid() must reject on client side. */
|
||||
BUG_ON(conn_is_back(qcs->qcc->conn));
|
||||
|
|
@ -2259,6 +2322,7 @@ static int h3_req_headers_send(struct qcs *qcs, struct htx *htx)
|
|||
|
||||
if (qpack_encode_method(&headers_buf, sl->info.req.meth, meth))
|
||||
goto err_full;
|
||||
h3_trace_header(ist(":method"), meth, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
|
||||
|
||||
if (uri.ptr[0] != '/' && uri.ptr[0] != '*') {
|
||||
int len = 1;
|
||||
|
|
@ -2290,13 +2354,23 @@ static int h3_req_headers_send(struct qcs *qcs, struct htx *htx)
|
|||
|
||||
if (qpack_encode_scheme(&headers_buf, scheme))
|
||||
goto err_full;
|
||||
h3_trace_header(ist(":scheme"), scheme, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
|
||||
|
||||
if (qpack_encode_path(&headers_buf, uri))
|
||||
goto err_full;
|
||||
h3_trace_header(ist(":path"), uri, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
|
||||
|
||||
if (istlen(auth)) {
|
||||
if (qpack_encode_auth(&headers_buf, auth))
|
||||
goto err_full;
|
||||
h3_trace_header(ist(":authority"), auth, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
|
||||
}
|
||||
|
||||
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED &&
|
||||
TRACE_ENABLED(TRACE_LEVEL_USER, H3_EV_TX_FRAME|H3_EV_TX_HDR, qcs->qcc->conn, 0, 0, 0)) {
|
||||
int i;
|
||||
for (i = 0; list[i].n.len; ++i)
|
||||
h3_trace_header(list[i].n, list[i].v, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
|
||||
}
|
||||
|
||||
if (!(sl->flags & HTX_SL_F_XFER_LEN)) {
|
||||
|
|
@ -2433,6 +2507,13 @@ static int h3_resp_headers_send(struct qcs *qcs, struct htx *htx)
|
|||
TRACE_USER("handling final HTX response", H3_EV_STRM_SEND, qcs->qcc->conn, qcs);
|
||||
h3s->flags &= ~H3_SF_SENT_INTERIM;
|
||||
}
|
||||
|
||||
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED) {
|
||||
char sts[4];
|
||||
h3_trace_header(ist(":status"), ist(ultoa_r(status, sts, sizeof(sts))),
|
||||
H3_EV_TX_FRAME|H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__,
|
||||
qcs->qcc, qcs);
|
||||
}
|
||||
}
|
||||
else if (type == HTX_BLK_HDR) {
|
||||
if (unlikely(hdr >= sizeof(list) / sizeof(list[0]) - 1)) {
|
||||
|
|
@ -2450,6 +2531,14 @@ static int h3_resp_headers_send(struct qcs *qcs, struct htx *htx)
|
|||
}
|
||||
}
|
||||
|
||||
if ((TRACE_SOURCE)->verbosity >= H3_VERB_ADVANCED &&
|
||||
TRACE_ENABLED(TRACE_LEVEL_USER, H3_EV_TX_FRAME|H3_EV_TX_HDR, qcs->qcc->conn, 0, 0, 0)) {
|
||||
int i;
|
||||
for (i = 0; list[i].n.len; ++i)
|
||||
h3_trace_header(list[i].n, list[i].v, H3_EV_TX_HDR, ist(TRC_LOC), __FUNCTION__, qcs->qcc, qcs);
|
||||
}
|
||||
|
||||
|
||||
/* Current function expects HTX start-line to be present. This also
|
||||
* ensures <status> conformance has been checked prior to encoding it.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2810,6 +2810,7 @@ void deinit(void)
|
|||
struct cfg_postparser *pprs, *pprsb;
|
||||
char **tmp = init_env;
|
||||
int cur_fd;
|
||||
int i;
|
||||
|
||||
/* the user may want to skip this phase */
|
||||
if (global.tune.options & GTUNE_QUICK_EXIT)
|
||||
|
|
@ -2886,8 +2887,10 @@ void deinit(void)
|
|||
ha_free(&global.server_state_base);
|
||||
ha_free(&global.server_state_file);
|
||||
ha_free(&global.stats_file);
|
||||
task_destroy(idle_conn_task);
|
||||
idle_conn_task = NULL;
|
||||
for (i = 0; i < global.nbthread; i++) {
|
||||
task_destroy(idle_conn_task[i]);
|
||||
idle_conn_task[i] = NULL;
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(log, logb, &global.loggers, list) {
|
||||
LIST_DEL_INIT(&log->list);
|
||||
|
|
|
|||
609
src/hlua.c
609
src/hlua.c
|
|
@ -73,6 +73,9 @@
|
|||
#include <haproxy/event_hdl.h>
|
||||
#include <haproxy/check.h>
|
||||
#include <haproxy/mailers.h>
|
||||
#if defined(HAVE_ACME)
|
||||
#include <haproxy/acme.h>
|
||||
#endif /* HAVE_ACME */
|
||||
|
||||
/* Global LUA flags */
|
||||
|
||||
|
|
@ -105,16 +108,6 @@ static uint8_t hlua_body = 1;
|
|||
*/
|
||||
static uint8_t hlua_bool_sample_conversion = HLUA_BOOL_SAMPLE_CONVERSION_UNK;
|
||||
|
||||
/* Lua uses longjmp to perform yield or throwing errors. This
|
||||
* macro is used only for identifying the function that can
|
||||
* not return because a longjmp is executed.
|
||||
* __LJMP marks a prototype of hlua file that can use longjmp.
|
||||
* WILL_LJMP() marks an lua function that will use longjmp.
|
||||
* MAY_LJMP() marks an lua function that may use longjmp.
|
||||
*/
|
||||
#define __LJMP
|
||||
#define WILL_LJMP(func) do { func; my_unreachable(); } while(0)
|
||||
#define MAY_LJMP(func) func
|
||||
|
||||
/* This couple of function executes securely some Lua calls outside of
|
||||
* the lua runtime environment. Each Lua call can return a longjmp
|
||||
|
|
@ -176,6 +169,24 @@ static int hlua_panic_ljmp(lua_State *L) { WILL_LJMP(longjmp(safe_ljmp_env, 1));
|
|||
*/
|
||||
static struct list referenced_functions = LIST_HEAD_INIT(referenced_functions);
|
||||
|
||||
/* List of callbacks registered via hap_register_hlua_state_init(), called
|
||||
* for each new lua_State created in hlua_init_state().
|
||||
*/
|
||||
static struct list hlua_state_init_list = LIST_HEAD_INIT(hlua_state_init_list);
|
||||
|
||||
void hap_register_hlua_state_init(int (*fct)(lua_State *L, char **errmsg))
|
||||
{
|
||||
struct hlua_state_init_fct *entry;
|
||||
|
||||
entry = calloc(1, sizeof(*entry));
|
||||
if (!entry) {
|
||||
ha_alert("hlua: out of memory registering state init callback\n");
|
||||
exit(1);
|
||||
}
|
||||
entry->fct = fct;
|
||||
LIST_APPEND(&hlua_state_init_list, &entry->list);
|
||||
}
|
||||
|
||||
/* This variable is used only during initialization to identify the Lua state
|
||||
* currently being initialized. 0 is the common lua state, 1 to n are the Lua
|
||||
* states dedicated to each thread (in this case hlua_state_id==tid+1).
|
||||
|
|
@ -499,7 +510,6 @@ static int class_fetches_ref;
|
|||
static int class_converters_ref;
|
||||
static int class_http_ref;
|
||||
static int class_http_msg_ref;
|
||||
static int class_httpclient_ref;
|
||||
static int class_map_ref;
|
||||
static int class_applet_tcp_ref;
|
||||
static int class_applet_http_ref;
|
||||
|
|
@ -988,13 +998,6 @@ const char *hlua_traceback(lua_State *L, const char* sep)
|
|||
* stack. If the number of arguments available is not the same
|
||||
* then <nb> an error is thrown.
|
||||
*/
|
||||
__LJMP static inline void check_args(lua_State *L, int nb, char *fcn)
|
||||
{
|
||||
if (lua_gettop(L) == nb)
|
||||
return;
|
||||
WILL_LJMP(luaL_error(L, "'%s' needs %d arguments", fcn, nb));
|
||||
}
|
||||
|
||||
/* This function pushes an error string prefixed by the file name
|
||||
* and the line number where the error is encountered.
|
||||
*
|
||||
|
|
@ -1011,7 +1014,7 @@ __LJMP static int _hlua_pusherror(lua_State *L)
|
|||
|
||||
return 1;
|
||||
}
|
||||
static int hlua_pusherror(lua_State *L, const char *fmt, ...)
|
||||
int hlua_pusherror(lua_State *L, const char *fmt, ...)
|
||||
{
|
||||
va_list argp;
|
||||
int ret = 1;
|
||||
|
|
@ -1783,34 +1786,6 @@ int hlua_ctx_init(struct hlua *lua, int state_id, struct task *task)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* kill all associated httpclient to this hlua task
|
||||
* We must take extra precautions as we're manipulating lua-exposed
|
||||
* objects without the main lua lock.
|
||||
*/
|
||||
static void hlua_httpclient_destroy_all(struct hlua *hlua)
|
||||
{
|
||||
struct hlua_httpclient *hlua_hc;
|
||||
|
||||
/* use thread-safe accessors for hc_list since GC cycle initiated by
|
||||
* another thread sharing the same main lua stack (lua coroutine)
|
||||
* could execute hlua_httpclient_gc() on the hlua->hc_list items
|
||||
* in parallel: Lua GC applies on the main stack, it is not limited to
|
||||
* a single coroutine stack, see Github issue #2037 for reference.
|
||||
* Remember, coroutines created using lua_newthread() are not meant to
|
||||
* be thread safe in Lua. (From lua co-author:
|
||||
* http://lua-users.org/lists/lua-l/2011-07/msg00072.html)
|
||||
*
|
||||
* This security measure is superfluous when 'lua-load-per-thread' is used
|
||||
* since in this case coroutines exclusively run on the same thread
|
||||
* (main stack is not shared between OS threads).
|
||||
*/
|
||||
while ((hlua_hc = MT_LIST_POP(&hlua->hc_list, typeof(hlua_hc), by_hlua))) {
|
||||
httpclient_stop_and_destroy(hlua_hc->hc);
|
||||
hlua_hc->hc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Used to destroy the Lua coroutine when the attached stream or task
|
||||
* is destroyed. The destroy also the memory context. The struct "lua"
|
||||
* will be freed.
|
||||
|
|
@ -7950,488 +7925,6 @@ __LJMP static int hlua_http_msg_unset_eom(lua_State *L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
* Class HTTPClient
|
||||
*
|
||||
*
|
||||
*/
|
||||
__LJMP static struct hlua_httpclient *hlua_checkhttpclient(lua_State *L, int ud)
|
||||
{
|
||||
return MAY_LJMP(hlua_checkudata(L, ud, class_httpclient_ref));
|
||||
}
|
||||
|
||||
|
||||
/* stops the httpclient and ask it to kill itself */
|
||||
__LJMP static int hlua_httpclient_gc(lua_State *L)
|
||||
{
|
||||
struct hlua_httpclient *hlua_hc;
|
||||
|
||||
MAY_LJMP(check_args(L, 1, "__gc"));
|
||||
|
||||
hlua_hc = MAY_LJMP(hlua_checkhttpclient(L, 1));
|
||||
|
||||
if (MT_LIST_DELETE(&hlua_hc->by_hlua)) {
|
||||
/* we won the race against hlua_httpclient_destroy_all() */
|
||||
httpclient_stop_and_destroy(hlua_hc->hc);
|
||||
hlua_hc->hc = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
__LJMP static int hlua_httpclient_new(lua_State *L)
|
||||
{
|
||||
struct hlua_httpclient *hlua_hc;
|
||||
struct hlua *hlua;
|
||||
|
||||
/* Get hlua struct, or NULL if we execute from main lua state */
|
||||
hlua = hlua_gethlua(L);
|
||||
if (!hlua)
|
||||
return 0;
|
||||
|
||||
/* Check stack size. */
|
||||
if (!lua_checkstack(L, 3)) {
|
||||
hlua_pusherror(L, "httpclient: full stack");
|
||||
goto err;
|
||||
}
|
||||
/* Create the object: obj[0] = userdata. */
|
||||
lua_newtable(L);
|
||||
hlua_hc = MAY_LJMP(lua_newuserdata(L, sizeof(*hlua_hc)));
|
||||
lua_rawseti(L, -2, 0);
|
||||
memset(hlua_hc, 0, sizeof(*hlua_hc));
|
||||
|
||||
hlua_hc->hc = httpclient_new(hlua, 0, IST_NULL);
|
||||
if (!hlua_hc->hc)
|
||||
goto err;
|
||||
|
||||
MT_LIST_APPEND(&hlua->hc_list, &hlua_hc->by_hlua);
|
||||
|
||||
/* Pop a class stream metatable and affect it to the userdata. */
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, class_httpclient_ref);
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
return 1;
|
||||
|
||||
err:
|
||||
WILL_LJMP(lua_error(L));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Callback of the httpclient, this callback wakes the lua task up, once the
|
||||
* httpclient receives some data
|
||||
*
|
||||
*/
|
||||
|
||||
static void hlua_httpclient_cb(struct httpclient *hc)
|
||||
{
|
||||
struct hlua *hlua = hc->caller;
|
||||
|
||||
if (!hlua || !hlua->task)
|
||||
return;
|
||||
|
||||
task_wakeup(hlua->task, TASK_WOKEN_MSG);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill the lua stack with headers from the httpclient response
|
||||
* This works the same way as the hlua_http_get_headers() function
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_get_headers(lua_State *L, struct hlua_httpclient *hlua_hc)
|
||||
{
|
||||
struct http_hdr *hdr;
|
||||
|
||||
lua_newtable(L);
|
||||
|
||||
for (hdr = hlua_hc->hc->res.hdrs; hdr && isttest(hdr->n); hdr++) {
|
||||
struct ist n, v;
|
||||
int len;
|
||||
|
||||
n = hdr->n;
|
||||
v = hdr->v;
|
||||
|
||||
/* Check for existing entry:
|
||||
* assume that the table is on the top of the stack, and
|
||||
* push the key in the stack, the function lua_gettable()
|
||||
* perform the lookup.
|
||||
*/
|
||||
|
||||
lua_pushlstring(L, n.ptr, n.len);
|
||||
lua_gettable(L, -2);
|
||||
|
||||
switch (lua_type(L, -1)) {
|
||||
case LUA_TNIL:
|
||||
/* Table not found, create it. */
|
||||
lua_pop(L, 1); /* remove the nil value. */
|
||||
lua_pushlstring(L, n.ptr, n.len); /* push the header name as key. */
|
||||
lua_newtable(L); /* create and push empty table. */
|
||||
lua_pushlstring(L, v.ptr, v.len); /* push header value. */
|
||||
lua_rawseti(L, -2, 0); /* index header value (pop it). */
|
||||
lua_rawset(L, -3); /* index new table with header name (pop the values). */
|
||||
break;
|
||||
|
||||
case LUA_TTABLE:
|
||||
/* Entry found: push the value in the table. */
|
||||
len = lua_rawlen(L, -1);
|
||||
lua_pushlstring(L, v.ptr, v.len); /* push header value. */
|
||||
lua_rawseti(L, -2, len+1); /* index header value (pop it). */
|
||||
lua_pop(L, 1); /* remove the table (it is stored in the main table). */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Other cases are errors. */
|
||||
hlua_pusherror(L, "internal error during the parsing of headers.");
|
||||
WILL_LJMP(lua_error(L));
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate and return an array of http_hdr ist extracted from the <headers> lua table
|
||||
*
|
||||
* Caller must free the result
|
||||
*/
|
||||
struct http_hdr *hlua_httpclient_table_to_hdrs(lua_State *L)
|
||||
{
|
||||
struct http_hdr hdrs[global.tune.max_http_hdr];
|
||||
struct http_hdr *result = NULL;
|
||||
uint32_t hdr_num = 0;
|
||||
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0) {
|
||||
struct ist name, value;
|
||||
const char *n, *v;
|
||||
size_t nlen, vlen;
|
||||
|
||||
if (!lua_isstring(L, -2) || !lua_istable(L, -1)) {
|
||||
/* Skip element if the key is not a string or if the value is not a table */
|
||||
goto next_hdr;
|
||||
}
|
||||
|
||||
n = lua_tolstring(L, -2, &nlen);
|
||||
name = ist2(n, nlen);
|
||||
|
||||
/* Loop on header's values */
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2)) {
|
||||
if (!lua_isstring(L, -1)) {
|
||||
/* Skip the value if it is not a string */
|
||||
goto next_value;
|
||||
}
|
||||
|
||||
if (hdr_num >= global.tune.max_http_hdr) {
|
||||
lua_pop(L, 2);
|
||||
goto skip_headers;
|
||||
}
|
||||
|
||||
v = lua_tolstring(L, -1, &vlen);
|
||||
value = ist2(v, vlen);
|
||||
name = ist2(n, nlen);
|
||||
|
||||
hdrs[hdr_num].n = istdup(name);
|
||||
hdrs[hdr_num].v = istdup(value);
|
||||
|
||||
hdr_num++;
|
||||
|
||||
next_value:
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
next_hdr:
|
||||
lua_pop(L, 1);
|
||||
|
||||
}
|
||||
|
||||
if (hdr_num) {
|
||||
/* alloc and copy the headers in the httpclient struct */
|
||||
result = calloc((hdr_num + 1), sizeof(*result));
|
||||
if (!result)
|
||||
goto skip_headers;
|
||||
memcpy(result, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
|
||||
|
||||
result[hdr_num].n = IST_NULL;
|
||||
result[hdr_num].v = IST_NULL;
|
||||
}
|
||||
|
||||
skip_headers:
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* For each yield, checks if there is some data in the httpclient and push them
|
||||
* in the lua buffer, once the httpclient finished its job, push the result on
|
||||
* the stack
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_rcv_yield(lua_State *L, int status, lua_KContext ctx)
|
||||
{
|
||||
struct buffer *tr;
|
||||
int res;
|
||||
struct hlua *hlua = hlua_gethlua(L);
|
||||
struct hlua_httpclient *hlua_hc = hlua_checkhttpclient(L, 1);
|
||||
|
||||
|
||||
tr = get_trash_chunk();
|
||||
|
||||
res = httpclient_res_xfer(hlua_hc->hc, tr);
|
||||
luaL_addlstring(&hlua_hc->b, b_orig(tr), res);
|
||||
|
||||
if (!httpclient_data(hlua_hc->hc) && httpclient_ended(hlua_hc->hc)) {
|
||||
|
||||
luaL_pushresult(&hlua_hc->b);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "status");
|
||||
lua_pushinteger(L, hlua_hc->hc->res.status);
|
||||
lua_settable(L, -3);
|
||||
|
||||
|
||||
lua_pushstring(L, "reason");
|
||||
lua_pushlstring(L, hlua_hc->hc->res.reason.ptr, hlua_hc->hc->res.reason.len);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "headers");
|
||||
hlua_httpclient_get_headers(L, hlua_hc);
|
||||
lua_settable(L, -3);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (httpclient_data(hlua_hc->hc))
|
||||
task_wakeup(hlua->task, TASK_WOKEN_MSG);
|
||||
|
||||
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_rcv_yield, TICK_ETERNITY, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call this when trying to stream a body during a request
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_snd_yield(lua_State *L, int status, lua_KContext ctx)
|
||||
{
|
||||
struct hlua *hlua;
|
||||
struct hlua_httpclient *hlua_hc = hlua_checkhttpclient(L, 1);
|
||||
const char *body_str = NULL;
|
||||
int ret;
|
||||
int end = 0;
|
||||
size_t buf_len;
|
||||
size_t to_send = 0;
|
||||
|
||||
hlua = hlua_gethlua(L);
|
||||
|
||||
if (!hlua || !hlua->task)
|
||||
WILL_LJMP(luaL_error(L, "The 'get' function is only allowed in "
|
||||
"'frontend', 'backend' or 'task'"));
|
||||
|
||||
ret = lua_getfield(L, -1, "body");
|
||||
if (ret != LUA_TSTRING)
|
||||
goto rcv;
|
||||
|
||||
body_str = lua_tolstring(L, -1, &buf_len);
|
||||
lua_pop(L, 1);
|
||||
|
||||
to_send = buf_len - hlua_hc->sent;
|
||||
|
||||
if ((hlua_hc->sent + to_send) >= buf_len)
|
||||
end = 1;
|
||||
|
||||
/* the end flag is always set since we are using the whole remaining size */
|
||||
hlua_hc->sent += httpclient_req_xfer(hlua_hc->hc, ist2(body_str + hlua_hc->sent, to_send), end);
|
||||
|
||||
if (buf_len > hlua_hc->sent) {
|
||||
/* still need to process the buffer */
|
||||
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_snd_yield, TICK_ETERNITY, 0));
|
||||
} else {
|
||||
goto rcv;
|
||||
/* we sent the whole request buffer we can recv */
|
||||
}
|
||||
return 0;
|
||||
|
||||
rcv:
|
||||
|
||||
/* we return a "res" object */
|
||||
lua_newtable(L);
|
||||
|
||||
lua_pushstring(L, "body");
|
||||
luaL_buffinit(L, &hlua_hc->b);
|
||||
|
||||
task_wakeup(hlua->task, TASK_WOKEN_MSG);
|
||||
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_rcv_yield, TICK_ETERNITY, 0));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send an HTTP request and wait for a response
|
||||
*/
|
||||
|
||||
__LJMP static int hlua_httpclient_send(lua_State *L, enum http_meth_t meth)
|
||||
{
|
||||
struct hlua_httpclient *hlua_hc;
|
||||
struct http_hdr *hdrs = NULL;
|
||||
struct http_hdr *hdrs_i = NULL;
|
||||
struct hlua *hlua;
|
||||
const char *url_str = NULL;
|
||||
const char *body_str = NULL;
|
||||
size_t buf_len = 0;
|
||||
int ret;
|
||||
|
||||
hlua = hlua_gethlua(L);
|
||||
|
||||
if (!hlua || !hlua->task)
|
||||
WILL_LJMP(luaL_error(L, "The 'get' function is only allowed in "
|
||||
"'frontend', 'backend' or 'task'"));
|
||||
|
||||
if (lua_gettop(L) != 2 || lua_type(L, -1) != LUA_TTABLE)
|
||||
WILL_LJMP(luaL_error(L, "'get' needs a table as argument"));
|
||||
|
||||
hlua_hc = hlua_checkhttpclient(L, 1);
|
||||
|
||||
/* An HTTPclient instance must never process more that one request. So
|
||||
* at this stage, it must never have been started.
|
||||
*/
|
||||
if (httpclient_started(hlua_hc->hc))
|
||||
WILL_LJMP(luaL_error(L, "httpclient instance cannot be reused. It must process at most one request"));
|
||||
|
||||
lua_pushnil(L); /* first key */
|
||||
while (lua_next(L, 2)) {
|
||||
if (strcmp(lua_tostring(L, -2), "dst") == 0) {
|
||||
if (httpclient_set_dst(hlua_hc->hc, lua_tostring(L, -1)) < 0)
|
||||
WILL_LJMP(luaL_error(L, "Can't use the 'dst' argument"));
|
||||
|
||||
} else if (strcmp(lua_tostring(L, -2), "url") == 0) {
|
||||
if (lua_type(L, -1) != LUA_TSTRING)
|
||||
WILL_LJMP(luaL_error(L, "invalid parameter in 'url', must be a string"));
|
||||
url_str = lua_tostring(L, -1);
|
||||
|
||||
} else if (strcmp(lua_tostring(L, -2), "timeout") == 0) {
|
||||
if (lua_type(L, -1) != LUA_TNUMBER)
|
||||
WILL_LJMP(luaL_error(L, "invalid parameter in 'timeout', must be a number"));
|
||||
httpclient_set_timeout(hlua_hc->hc, lua_tointeger(L, -1));
|
||||
|
||||
} else if (strcmp(lua_tostring(L, -2), "headers") == 0) {
|
||||
if (lua_type(L, -1) != LUA_TTABLE)
|
||||
WILL_LJMP(luaL_error(L, "invalid parameter in 'headers', must be a table"));
|
||||
hdrs = hlua_httpclient_table_to_hdrs(L);
|
||||
|
||||
} else if (strcmp(lua_tostring(L, -2), "body") == 0) {
|
||||
if (lua_type(L, -1) != LUA_TSTRING)
|
||||
WILL_LJMP(luaL_error(L, "invalid parameter in 'body', must be a string"));
|
||||
body_str = lua_tolstring(L, -1, &buf_len);
|
||||
|
||||
} else {
|
||||
WILL_LJMP(luaL_error(L, "'%s' invalid parameter name", lua_tostring(L, -2)));
|
||||
}
|
||||
/* removes 'value'; keeps 'key' for next iteration */
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
if (!url_str) {
|
||||
WILL_LJMP(luaL_error(L, "'get' need a 'url' argument"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
hlua_hc->sent = 0;
|
||||
|
||||
istfree(&hlua_hc->hc->req.url);
|
||||
hlua_hc->hc->req.url = istdup(ist(url_str));
|
||||
hlua_hc->hc->req.meth = meth;
|
||||
|
||||
/* update the httpclient callbacks */
|
||||
hlua_hc->hc->ops.res_stline = hlua_httpclient_cb;
|
||||
hlua_hc->hc->ops.res_headers = hlua_httpclient_cb;
|
||||
hlua_hc->hc->ops.res_payload = hlua_httpclient_cb;
|
||||
hlua_hc->hc->ops.res_end = hlua_httpclient_cb;
|
||||
|
||||
/* a body is available, it will use the request callback */
|
||||
if (body_str && buf_len) {
|
||||
hlua_hc->hc->ops.req_payload = hlua_httpclient_cb;
|
||||
}
|
||||
|
||||
ret = httpclient_req_gen(hlua_hc->hc, hlua_hc->hc->req.url, meth, hdrs, IST_NULL);
|
||||
|
||||
/* free the temporary headers array */
|
||||
hdrs_i = hdrs;
|
||||
while (hdrs_i && isttest(hdrs_i->n)) {
|
||||
istfree(&hdrs_i->n);
|
||||
istfree(&hdrs_i->v);
|
||||
hdrs_i++;
|
||||
}
|
||||
ha_free(&hdrs);
|
||||
|
||||
|
||||
if (ret != ERR_NONE) {
|
||||
WILL_LJMP(luaL_error(L, "Can't generate the HTTP request"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!httpclient_start(hlua_hc->hc))
|
||||
WILL_LJMP(luaL_error(L, "couldn't start the httpclient"));
|
||||
|
||||
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_snd_yield, TICK_ETERNITY, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends an HTTP HEAD request and wait for a response
|
||||
*
|
||||
* httpclient:head(url, headers, payload)
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_head(lua_State *L)
|
||||
{
|
||||
return hlua_httpclient_send(L, HTTP_METH_HEAD);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send an HTTP GET request and wait for a response
|
||||
*
|
||||
* httpclient:get(url, headers, payload)
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_get(lua_State *L)
|
||||
{
|
||||
return hlua_httpclient_send(L, HTTP_METH_GET);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends an HTTP PUT request and wait for a response
|
||||
*
|
||||
* httpclient:put(url, headers, payload)
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_put(lua_State *L)
|
||||
{
|
||||
return hlua_httpclient_send(L, HTTP_METH_PUT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send an HTTP POST request and wait for a response
|
||||
*
|
||||
* httpclient:post(url, headers, payload)
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_post(lua_State *L)
|
||||
{
|
||||
return hlua_httpclient_send(L, HTTP_METH_POST);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Sends an HTTP DELETE request and wait for a response
|
||||
*
|
||||
* httpclient:delete(url, headers, payload)
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_delete(lua_State *L)
|
||||
{
|
||||
return hlua_httpclient_send(L, HTTP_METH_DELETE);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
|
|
@ -10063,6 +9556,11 @@ __LJMP static void hlua_event_hdl_cb_push_args(struct hlua_event_sub *hlua_sub,
|
|||
lua_settable(hlua->T, -3);
|
||||
}
|
||||
}
|
||||
#if defined(HAVE_ACME)
|
||||
else if (event_hdl_sub_family_equal(EVENT_HDL_SUB_ACME, event)) {
|
||||
MAY_LJMP(acme_hlua_event_push_args(hlua, event, data));
|
||||
}
|
||||
#endif /* HAVE_ACME */
|
||||
/* sub mgmt */
|
||||
hlua->nargs += 1;
|
||||
hlua_fcn_new_event_sub(hlua->T, hlua_sub->sub);
|
||||
|
|
@ -14333,7 +13831,6 @@ lua_State *hlua_init_state(int thread_num)
|
|||
hlua_class_function(L, "get_patref", hlua_get_patref);
|
||||
hlua_class_function(L, "get_var", hlua_core_get_var);
|
||||
hlua_class_function(L, "tcp", hlua_socket_new);
|
||||
hlua_class_function(L, "httpclient", hlua_httpclient_new);
|
||||
hlua_class_function(L, "event_sub", hlua_event_global_sub);
|
||||
hlua_class_function(L, "log", hlua_log);
|
||||
hlua_class_function(L, "Debug", hlua_log_debug);
|
||||
|
|
@ -14655,30 +14152,6 @@ lua_State *hlua_init_state(int thread_num)
|
|||
/* Register previous table in the registry with reference and named entry. */
|
||||
class_http_msg_ref = hlua_register_metatable(L, CLASS_HTTP_MSG);
|
||||
|
||||
/*
|
||||
*
|
||||
* Register class HTTPClient
|
||||
*
|
||||
*/
|
||||
|
||||
/* Create and fill the metatable. */
|
||||
lua_newtable(L);
|
||||
lua_pushstring(L, "__index");
|
||||
lua_newtable(L);
|
||||
hlua_class_function(L, "get", hlua_httpclient_get);
|
||||
hlua_class_function(L, "head", hlua_httpclient_head);
|
||||
hlua_class_function(L, "put", hlua_httpclient_put);
|
||||
hlua_class_function(L, "post", hlua_httpclient_post);
|
||||
hlua_class_function(L, "delete", hlua_httpclient_delete);
|
||||
lua_settable(L, -3); /* Sets the __index entry. */
|
||||
/* Register the garbage collector entry. */
|
||||
lua_pushstring(L, "__gc");
|
||||
lua_pushcclosure(L, hlua_httpclient_gc, 0);
|
||||
lua_settable(L, -3); /* Push the last 2 entries in the table at index -3 */
|
||||
|
||||
|
||||
|
||||
class_httpclient_ref = hlua_register_metatable(L, CLASS_HTTPCLIENT);
|
||||
/*
|
||||
*
|
||||
* Register class AppletTCP
|
||||
|
|
@ -14830,6 +14303,28 @@ lua_State *hlua_init_state(int thread_num)
|
|||
/* Register previous table in the registry with reference and named entry. */
|
||||
class_socket_ref = hlua_register_metatable(L, CLASS_SOCKET);
|
||||
|
||||
/* Call all registered state init callbacks. */
|
||||
{
|
||||
struct hlua_state_init_fct *e;
|
||||
char *errmsg = NULL;
|
||||
int err_code;
|
||||
|
||||
list_for_each_entry(e, &hlua_state_init_list, list) {
|
||||
err_code = e->fct(L, &errmsg);
|
||||
if (errmsg) {
|
||||
if (err_code & ERR_ALERT)
|
||||
ha_alert("Lua: %s\n", errmsg);
|
||||
else if (err_code & ERR_WARN)
|
||||
ha_warning("Lua: %s\n", errmsg);
|
||||
else
|
||||
ha_notice("Lua: %s\n", errmsg);
|
||||
ha_free(&errmsg);
|
||||
}
|
||||
if (err_code & (ERR_ABORT|ERR_FATAL))
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
lua_atpanic(L, hlua_panic_safe);
|
||||
|
||||
return L;
|
||||
|
|
@ -14918,10 +14413,16 @@ static void hlua_deinit()
|
|||
{
|
||||
int thr;
|
||||
struct hlua_reg_filter *reg_flt, *reg_flt_bck;
|
||||
struct hlua_state_init_fct *e, *eb;
|
||||
|
||||
list_for_each_entry_safe(reg_flt, reg_flt_bck, &referenced_filters, l)
|
||||
release_hlua_reg_filter(reg_flt);
|
||||
|
||||
list_for_each_entry_safe(e, eb, &hlua_state_init_list, list) {
|
||||
LIST_DELETE(&e->list);
|
||||
free(e);
|
||||
}
|
||||
|
||||
for (thr = 0; thr < MAX_THREADS+1; thr++) {
|
||||
if (hlua_states[thr])
|
||||
lua_close(hlua_states[thr]);
|
||||
|
|
|
|||
|
|
@ -351,6 +351,8 @@ int hpack_dht_insert(struct hpack_dht *dht, struct ist name, struct ist value)
|
|||
else {
|
||||
/* need to defragment the table before inserting upfront */
|
||||
dht = hpack_dht_defrag(dht);
|
||||
if (!dht)
|
||||
return -1;
|
||||
wrap = dht->wrap + 1;
|
||||
head = dht->head + 1;
|
||||
dht->dte[head].addr = dht->dte[dht->front].addr - (name.len + value.len);
|
||||
|
|
|
|||
|
|
@ -1490,6 +1490,7 @@ static enum act_return http_action_set_headers_bin(struct act_rule *rule, struct
|
|||
struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn.http->req : &s->txn.http->rsp);
|
||||
struct htx *htx = htxbuf(&msg->chn->buf);
|
||||
struct sample *hdrs_bin;
|
||||
struct buffer *copy = NULL;
|
||||
char *p, *end;
|
||||
enum act_return ret = ACT_RET_CONT;
|
||||
struct http_hdr_ctx ctx;
|
||||
|
|
@ -1500,8 +1501,19 @@ static enum act_return http_action_set_headers_bin(struct act_rule *rule, struct
|
|||
if (!hdrs_bin)
|
||||
return ACT_RET_CONT;
|
||||
|
||||
p = b_orig(&hdrs_bin->data.u.str);
|
||||
end = b_tail(&hdrs_bin->data.u.str);
|
||||
/* The sample may point into the very HTX message we're about to modify
|
||||
* (e.g. req.body) or into a rotating trash chunk that http_add_header()
|
||||
* reuses internally; either way a defrag/realloc would leave our p/end/
|
||||
* n/v pointers dangling. Work on a private copy to stay safe.
|
||||
*/
|
||||
copy = alloc_trash_chunk();
|
||||
if (!copy || b_data(&hdrs_bin->data.u.str) > b_size(copy))
|
||||
goto fail_rewrite;
|
||||
memcpy(b_orig(copy), b_orig(&hdrs_bin->data.u.str), b_data(&hdrs_bin->data.u.str));
|
||||
b_set_data(copy, b_data(&hdrs_bin->data.u.str));
|
||||
|
||||
p = b_orig(copy);
|
||||
end = b_tail(copy);
|
||||
while (p < end) {
|
||||
if (decode_varint(&p, end, &sz) == -1)
|
||||
goto fail_rewrite;
|
||||
|
|
@ -1546,6 +1558,7 @@ static enum act_return http_action_set_headers_bin(struct act_rule *rule, struct
|
|||
ret = ACT_RET_ERR;
|
||||
|
||||
leave:
|
||||
free_trash_chunk(copy);
|
||||
return ret;
|
||||
|
||||
fail_rewrite:
|
||||
|
|
|
|||
|
|
@ -1265,7 +1265,7 @@ int http_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
|
|||
struct htx *htx;
|
||||
struct connection *srv_conn;
|
||||
struct htx_sl *sl;
|
||||
int n;
|
||||
int n, l7_retry_failed = 0;
|
||||
|
||||
DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn, msg);
|
||||
|
||||
|
|
@ -1306,19 +1306,22 @@ int http_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
|
|||
(!conn || conn->err_code != CO_ER_SSL_EARLY_FAILED)) {
|
||||
if (co_data(rep) || do_l7_retry(s, s->scb) == 0)
|
||||
return 0;
|
||||
l7_retry_failed = 1;
|
||||
}
|
||||
|
||||
/* Perform a L7 retry on empty response or because server refuses the early data. */
|
||||
if ((txn->flags & TX_L7_RETRY) &&
|
||||
(s->be->retry_type & PR_RE_EARLY_ERROR) &&
|
||||
conn && conn->err_code == CO_ER_SSL_EARLY_FAILED &&
|
||||
do_l7_retry(s, s->scb) == 0) {
|
||||
DBG_TRACE_DEVEL("leaving on L7 retry",
|
||||
STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn);
|
||||
return 0;
|
||||
conn && conn->err_code == CO_ER_SSL_EARLY_FAILED) {
|
||||
if (do_l7_retry(s, s->scb) == 0) {
|
||||
DBG_TRACE_DEVEL("leaving on L7 retry",
|
||||
STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn);
|
||||
return 0;
|
||||
}
|
||||
l7_retry_failed = 1;
|
||||
}
|
||||
|
||||
if (s->flags & SF_SRV_REUSED)
|
||||
if (!l7_retry_failed && (s->flags & SF_SRV_REUSED))
|
||||
goto abort_keep_alive;
|
||||
|
||||
if (s->be_tgcounters)
|
||||
|
|
@ -1416,9 +1419,10 @@ int http_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
|
|||
STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn);
|
||||
return 0;
|
||||
}
|
||||
l7_retry_failed = 1;
|
||||
}
|
||||
|
||||
if (s->flags & SF_SRV_REUSED)
|
||||
if (!l7_retry_failed && (s->flags & SF_SRV_REUSED))
|
||||
goto abort_keep_alive;
|
||||
|
||||
if (s->be_tgcounters)
|
||||
|
|
@ -2517,7 +2521,6 @@ int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s, struc
|
|||
}
|
||||
case REDIRECT_TYPE_LOCATION:
|
||||
default:
|
||||
memset(chunk->area, 0x50, chunk->size);
|
||||
if (rule->rdr_str) { /* this is an old "redirect" rule */
|
||||
/* add location */
|
||||
if (!chunk_memcat(chunk, rule->rdr_str, rule->rdr_len))
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@
|
|||
#include <haproxy/global.h>
|
||||
#include <haproxy/istbuf.h>
|
||||
#include <haproxy/h1_htx.h>
|
||||
#ifdef USE_LUA
|
||||
#include <haproxy/chunk.h>
|
||||
#include <haproxy/hlua.h>
|
||||
#include <haproxy/hlua_fcn.h>
|
||||
#include <haproxy/task.h>
|
||||
#endif
|
||||
#include <haproxy/http.h>
|
||||
#include <haproxy/http_ana-t.h>
|
||||
#include <haproxy/http_client.h>
|
||||
|
|
@ -39,6 +45,11 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
#ifdef USE_LUA
|
||||
static int class_httpclient_ref; /* httpclient LUA class */
|
||||
static int class_httpclient_request_ref; /* httpclient request LUA class */
|
||||
#endif
|
||||
|
||||
static struct proxy *httpclient_proxy;
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
|
|
@ -677,6 +688,10 @@ void httpclient_applet_io_handler(struct appctx *appctx)
|
|||
|
||||
/* copy the status line in the httpclient */
|
||||
hc->res.status = sl->info.res.status;
|
||||
|
||||
if (__sc_strm(appctx_sc(appctx))->flags & SF_ERR_MASK)
|
||||
hc->res.status = 0;
|
||||
|
||||
hc->res.vsn = istdup(htx_sl_res_vsn(sl));
|
||||
hc->res.reason = istdup(htx_sl_res_reason(sl));
|
||||
htx_remove_blk(htx, blk);
|
||||
|
|
@ -1487,3 +1502,559 @@ static struct cfg_kw_list cfg_kws = {ILH, {
|
|||
}};
|
||||
|
||||
INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
* Class HTTPClient
|
||||
*
|
||||
*
|
||||
*/
|
||||
#ifdef USE_LUA
|
||||
|
||||
/* kill all associated httpclient to this hlua task
|
||||
* We must take extra precautions as we're manipulating lua-exposed
|
||||
* objects without the main lua lock.
|
||||
*/
|
||||
void hlua_httpclient_destroy_all(struct hlua *hlua)
|
||||
{
|
||||
struct hlua_httpclient *hlua_hc;
|
||||
|
||||
/* use thread-safe accessors for hc_list since GC cycle initiated by
|
||||
* another thread sharing the same main lua stack (lua coroutine)
|
||||
* could execute hlua_httpclient_gc() on the hlua->hc_list items
|
||||
* in parallel: Lua GC applies on the main stack, it is not limited to
|
||||
* a single coroutine stack, see Github issue #2037 for reference.
|
||||
* Remember, coroutines created using lua_newthread() are not meant to
|
||||
* be thread safe in Lua. (From lua co-author:
|
||||
* http://lua-users.org/lists/lua-l/2011-07/msg00072.html)
|
||||
*
|
||||
* This security measure is superfluous when 'lua-load-per-thread' is used
|
||||
* since in this case coroutines exclusively run on the same thread
|
||||
* (main stack is not shared between OS threads).
|
||||
*/
|
||||
while ((hlua_hc = MT_LIST_POP(&hlua->hc_list, typeof(hlua_hc), by_hlua))) {
|
||||
httpclient_stop_and_destroy(hlua_hc->hc);
|
||||
hlua_hc->hc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
__LJMP static struct hlua_httpclient *hlua_checkhttpclient(lua_State *L, int ud)
|
||||
{
|
||||
return MAY_LJMP(hlua_checkudata(L, ud, class_httpclient_request_ref));
|
||||
}
|
||||
|
||||
|
||||
/* stops the httpclient and ask it to kill itself */
|
||||
__LJMP static int hlua_httpclient_gc(lua_State *L)
|
||||
{
|
||||
struct hlua_httpclient *hlua_hc;
|
||||
|
||||
MAY_LJMP(hlua_check_args(L, 1, "__gc"));
|
||||
|
||||
hlua_hc = MAY_LJMP(hlua_checkhttpclient(L, 1));
|
||||
|
||||
if (MT_LIST_DELETE(&hlua_hc->by_hlua)) {
|
||||
/* we won the race against hlua_httpclient_destroy_all() */
|
||||
httpclient_stop_and_destroy(hlua_hc->hc);
|
||||
hlua_hc->hc = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
__LJMP static int hlua_httpclient_factory_new(lua_State *L)
|
||||
{
|
||||
|
||||
lua_newtable(L);
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, class_httpclient_ref);
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
__LJMP static int hlua_httpclient_new(lua_State *L)
|
||||
{
|
||||
struct hlua_httpclient *hlua_hc;
|
||||
struct hlua *hlua;
|
||||
|
||||
/* Get hlua struct, or NULL if we execute from main lua state */
|
||||
hlua = hlua_gethlua(L);
|
||||
if (!hlua)
|
||||
return 0;
|
||||
|
||||
/* Check stack size. */
|
||||
if (!lua_checkstack(L, 3)) {
|
||||
hlua_pusherror(L, "httpclient: full stack");
|
||||
goto err;
|
||||
}
|
||||
/* Create the object: obj[0] = userdata. */
|
||||
lua_newtable(L);
|
||||
hlua_hc = MAY_LJMP(lua_newuserdata(L, sizeof(*hlua_hc)));
|
||||
lua_rawseti(L, -2, 0);
|
||||
memset(hlua_hc, 0, sizeof(*hlua_hc));
|
||||
|
||||
|
||||
MT_LIST_APPEND(&hlua->hc_list, &hlua_hc->by_hlua);
|
||||
|
||||
/* Pop a class stream metatable and affect it to the userdata. */
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, class_httpclient_request_ref);
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
return 1;
|
||||
|
||||
err:
|
||||
WILL_LJMP(lua_error(L));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Callback of the httpclient, this callback wakes the lua task up, once the
|
||||
* httpclient receives some data
|
||||
*
|
||||
*/
|
||||
|
||||
static void hlua_httpclient_cb(struct httpclient *hc)
|
||||
{
|
||||
struct hlua *hlua = hc->caller;
|
||||
|
||||
if (!hlua || !hlua->task)
|
||||
return;
|
||||
|
||||
task_wakeup(hlua->task, TASK_WOKEN_MSG);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill the lua stack with headers from the httpclient response
|
||||
* This works the same way as the hlua_http_get_headers() function
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_get_headers(lua_State *L, struct hlua_httpclient *hlua_hc)
|
||||
{
|
||||
struct http_hdr *hdr;
|
||||
|
||||
lua_newtable(L);
|
||||
|
||||
for (hdr = hlua_hc->hc->res.hdrs; hdr && isttest(hdr->n); hdr++) {
|
||||
struct ist n, v;
|
||||
int len;
|
||||
|
||||
n = hdr->n;
|
||||
v = hdr->v;
|
||||
|
||||
/* Check for existing entry:
|
||||
* assume that the table is on the top of the stack, and
|
||||
* push the key in the stack, the function lua_gettable()
|
||||
* perform the lookup.
|
||||
*/
|
||||
|
||||
lua_pushlstring(L, n.ptr, n.len);
|
||||
lua_gettable(L, -2);
|
||||
|
||||
switch (lua_type(L, -1)) {
|
||||
case LUA_TNIL:
|
||||
/* Table not found, create it. */
|
||||
lua_pop(L, 1); /* remove the nil value. */
|
||||
lua_pushlstring(L, n.ptr, n.len); /* push the header name as key. */
|
||||
lua_newtable(L); /* create and push empty table. */
|
||||
lua_pushlstring(L, v.ptr, v.len); /* push header value. */
|
||||
lua_rawseti(L, -2, 0); /* index header value (pop it). */
|
||||
lua_rawset(L, -3); /* index new table with header name (pop the values). */
|
||||
break;
|
||||
|
||||
case LUA_TTABLE:
|
||||
/* Entry found: push the value in the table. */
|
||||
len = lua_rawlen(L, -1);
|
||||
lua_pushlstring(L, v.ptr, v.len); /* push header value. */
|
||||
lua_rawseti(L, -2, len+1); /* index header value (pop it). */
|
||||
lua_pop(L, 1); /* remove the table (it is stored in the main table). */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Other cases are errors. */
|
||||
hlua_pusherror(L, "internal error during the parsing of headers.");
|
||||
WILL_LJMP(lua_error(L));
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate and return an array of http_hdr ist extracted from the <headers> lua table
|
||||
*
|
||||
* Caller must free the result
|
||||
*/
|
||||
static struct http_hdr *hlua_httpclient_table_to_hdrs(lua_State *L)
|
||||
{
|
||||
struct http_hdr hdrs[global.tune.max_http_hdr];
|
||||
struct http_hdr *result = NULL;
|
||||
uint32_t hdr_num = 0;
|
||||
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0) {
|
||||
struct ist name, value;
|
||||
const char *n, *v;
|
||||
size_t nlen, vlen;
|
||||
|
||||
if (!lua_isstring(L, -2) || !lua_istable(L, -1)) {
|
||||
/* Skip element if the key is not a string or if the value is not a table */
|
||||
goto next_hdr;
|
||||
}
|
||||
|
||||
n = lua_tolstring(L, -2, &nlen);
|
||||
name = ist2(n, nlen);
|
||||
|
||||
/* Loop on header's values */
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2)) {
|
||||
if (!lua_isstring(L, -1)) {
|
||||
/* Skip the value if it is not a string */
|
||||
goto next_value;
|
||||
}
|
||||
|
||||
if (hdr_num >= global.tune.max_http_hdr) {
|
||||
lua_pop(L, 2);
|
||||
goto skip_headers;
|
||||
}
|
||||
|
||||
v = lua_tolstring(L, -1, &vlen);
|
||||
value = ist2(v, vlen);
|
||||
name = ist2(n, nlen);
|
||||
|
||||
hdrs[hdr_num].n = istdup(name);
|
||||
hdrs[hdr_num].v = istdup(value);
|
||||
|
||||
hdr_num++;
|
||||
|
||||
next_value:
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
next_hdr:
|
||||
lua_pop(L, 1);
|
||||
|
||||
}
|
||||
|
||||
if (hdr_num) {
|
||||
/* alloc and copy the headers in the httpclient struct */
|
||||
result = calloc((hdr_num + 1), sizeof(*result));
|
||||
if (!result)
|
||||
goto skip_headers;
|
||||
memcpy(result, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
|
||||
|
||||
result[hdr_num].n = IST_NULL;
|
||||
result[hdr_num].v = IST_NULL;
|
||||
}
|
||||
|
||||
skip_headers:
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* For each yield, checks if there is some data in the httpclient and push them
|
||||
* in the lua buffer, once the httpclient finished its job, push the result on
|
||||
* the stack
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_rcv_yield(lua_State *L, int status, lua_KContext ctx)
|
||||
{
|
||||
struct buffer *tr;
|
||||
int res;
|
||||
struct hlua *hlua = hlua_gethlua(L);
|
||||
struct hlua_httpclient *hlua_hc = hlua_checkhttpclient(L, 1);
|
||||
|
||||
|
||||
tr = get_trash_chunk();
|
||||
|
||||
res = httpclient_res_xfer(hlua_hc->hc, tr);
|
||||
luaL_addlstring(&hlua_hc->b, b_orig(tr), res);
|
||||
|
||||
if (!httpclient_data(hlua_hc->hc) && httpclient_ended(hlua_hc->hc)) {
|
||||
|
||||
luaL_pushresult(&hlua_hc->b);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "status");
|
||||
lua_pushinteger(L, hlua_hc->hc->res.status);
|
||||
lua_settable(L, -3);
|
||||
|
||||
|
||||
lua_pushstring(L, "reason");
|
||||
lua_pushlstring(L, hlua_hc->hc->res.reason.ptr, hlua_hc->hc->res.reason.len);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "headers");
|
||||
hlua_httpclient_get_headers(L, hlua_hc);
|
||||
lua_settable(L, -3);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (httpclient_data(hlua_hc->hc))
|
||||
task_wakeup(hlua->task, TASK_WOKEN_MSG);
|
||||
|
||||
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_rcv_yield, TICK_ETERNITY, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call this when trying to stream a body during a request
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_snd_yield(lua_State *L, int status, lua_KContext ctx)
|
||||
{
|
||||
struct hlua *hlua;
|
||||
struct hlua_httpclient *hlua_hc = hlua_checkhttpclient(L, 1);
|
||||
const char *body_str = NULL;
|
||||
int ret;
|
||||
int end = 0;
|
||||
size_t buf_len;
|
||||
size_t to_send = 0;
|
||||
|
||||
hlua = hlua_gethlua(L);
|
||||
|
||||
if (!hlua || !hlua->task)
|
||||
WILL_LJMP(luaL_error(L, "The 'get' function is only allowed in "
|
||||
"'frontend', 'backend' or 'task'"));
|
||||
|
||||
ret = lua_getfield(L, -1, "body");
|
||||
if (ret != LUA_TSTRING)
|
||||
goto rcv;
|
||||
|
||||
body_str = lua_tolstring(L, -1, &buf_len);
|
||||
lua_pop(L, 1);
|
||||
|
||||
to_send = buf_len - hlua_hc->sent;
|
||||
|
||||
if ((hlua_hc->sent + to_send) >= buf_len)
|
||||
end = 1;
|
||||
|
||||
/* the end flag is always set since we are using the whole remaining size */
|
||||
hlua_hc->sent += httpclient_req_xfer(hlua_hc->hc, ist2(body_str + hlua_hc->sent, to_send), end);
|
||||
|
||||
if (buf_len > hlua_hc->sent) {
|
||||
/* still need to process the buffer */
|
||||
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_snd_yield, TICK_ETERNITY, 0));
|
||||
} else {
|
||||
goto rcv;
|
||||
/* we sent the whole request buffer we can recv */
|
||||
}
|
||||
return 0;
|
||||
|
||||
rcv:
|
||||
|
||||
/* we return a "res" object */
|
||||
lua_newtable(L);
|
||||
|
||||
lua_pushstring(L, "body");
|
||||
luaL_buffinit(L, &hlua_hc->b);
|
||||
|
||||
task_wakeup(hlua->task, TASK_WOKEN_MSG);
|
||||
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_rcv_yield, TICK_ETERNITY, 0));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send an HTTP request and wait for a response
|
||||
*/
|
||||
|
||||
__LJMP static int hlua_httpclient_send(lua_State *L, enum http_meth_t meth)
|
||||
{
|
||||
struct hlua_httpclient *hlua_hc;
|
||||
struct http_hdr *hdrs = NULL;
|
||||
struct http_hdr *hdrs_i = NULL;
|
||||
struct hlua *hlua;
|
||||
const char *url_str = NULL;
|
||||
const char *body_str = NULL;
|
||||
size_t buf_len = 0;
|
||||
int ret;
|
||||
|
||||
hlua = hlua_gethlua(L);
|
||||
|
||||
if (!hlua || !hlua->task)
|
||||
WILL_LJMP(luaL_error(L, "The 'get' function is only allowed in "
|
||||
"'frontend', 'backend' or 'task'"));
|
||||
|
||||
if (lua_gettop(L) != 2 || lua_type(L, -1) != LUA_TTABLE)
|
||||
WILL_LJMP(luaL_error(L, "'get' needs a table as argument"));
|
||||
|
||||
/* Create the internal httpclient request object and replace the factory at index 1 */
|
||||
hlua_httpclient_new(L);
|
||||
lua_replace(L, 1);
|
||||
|
||||
hlua_hc = hlua_checkhttpclient(L, 1);
|
||||
|
||||
hlua_hc->hc = httpclient_new(hlua, 0, IST_NULL);
|
||||
if (!hlua_hc->hc)
|
||||
WILL_LJMP(luaL_error(L, "out of memory"));
|
||||
|
||||
lua_pushnil(L); /* first key */
|
||||
while (lua_next(L, 2)) {
|
||||
if (strcmp(lua_tostring(L, -2), "dst") == 0) {
|
||||
if (httpclient_set_dst(hlua_hc->hc, lua_tostring(L, -1)) < 0)
|
||||
WILL_LJMP(luaL_error(L, "Can't use the 'dst' argument"));
|
||||
|
||||
} else if (strcmp(lua_tostring(L, -2), "url") == 0) {
|
||||
if (lua_type(L, -1) != LUA_TSTRING)
|
||||
WILL_LJMP(luaL_error(L, "invalid parameter in 'url', must be a string"));
|
||||
url_str = lua_tostring(L, -1);
|
||||
|
||||
} else if (strcmp(lua_tostring(L, -2), "timeout") == 0) {
|
||||
if (lua_type(L, -1) != LUA_TNUMBER)
|
||||
WILL_LJMP(luaL_error(L, "invalid parameter in 'timeout', must be a number"));
|
||||
httpclient_set_timeout(hlua_hc->hc, lua_tointeger(L, -1));
|
||||
|
||||
} else if (strcmp(lua_tostring(L, -2), "headers") == 0) {
|
||||
if (lua_type(L, -1) != LUA_TTABLE)
|
||||
WILL_LJMP(luaL_error(L, "invalid parameter in 'headers', must be a table"));
|
||||
hdrs = hlua_httpclient_table_to_hdrs(L);
|
||||
|
||||
} else if (strcmp(lua_tostring(L, -2), "body") == 0) {
|
||||
if (lua_type(L, -1) != LUA_TSTRING)
|
||||
WILL_LJMP(luaL_error(L, "invalid parameter in 'body', must be a string"));
|
||||
body_str = lua_tolstring(L, -1, &buf_len);
|
||||
|
||||
} else {
|
||||
WILL_LJMP(luaL_error(L, "'%s' invalid parameter name", lua_tostring(L, -2)));
|
||||
}
|
||||
/* removes 'value'; keeps 'key' for next iteration */
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
if (!url_str) {
|
||||
WILL_LJMP(luaL_error(L, "'get' need a 'url' argument"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
hlua_hc->sent = 0;
|
||||
|
||||
istfree(&hlua_hc->hc->req.url);
|
||||
hlua_hc->hc->req.url = istdup(ist(url_str));
|
||||
hlua_hc->hc->req.meth = meth;
|
||||
|
||||
/* update the httpclient callbacks */
|
||||
hlua_hc->hc->ops.res_stline = hlua_httpclient_cb;
|
||||
hlua_hc->hc->ops.res_headers = hlua_httpclient_cb;
|
||||
hlua_hc->hc->ops.res_payload = hlua_httpclient_cb;
|
||||
hlua_hc->hc->ops.res_end = hlua_httpclient_cb;
|
||||
|
||||
/* a body is available, it will use the request callback */
|
||||
if (body_str && buf_len) {
|
||||
hlua_hc->hc->ops.req_payload = hlua_httpclient_cb;
|
||||
}
|
||||
|
||||
ret = httpclient_req_gen(hlua_hc->hc, hlua_hc->hc->req.url, meth, hdrs, IST_NULL);
|
||||
|
||||
/* free the temporary headers array */
|
||||
hdrs_i = hdrs;
|
||||
while (hdrs_i && isttest(hdrs_i->n)) {
|
||||
istfree(&hdrs_i->n);
|
||||
istfree(&hdrs_i->v);
|
||||
hdrs_i++;
|
||||
}
|
||||
ha_free(&hdrs);
|
||||
|
||||
|
||||
if (ret != ERR_NONE) {
|
||||
WILL_LJMP(luaL_error(L, "Can't generate the HTTP request"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!httpclient_start(hlua_hc->hc))
|
||||
WILL_LJMP(luaL_error(L, "couldn't start the httpclient"));
|
||||
|
||||
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_snd_yield, TICK_ETERNITY, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends an HTTP HEAD request and wait for a response
|
||||
*
|
||||
* httpclient:head(url, headers, payload)
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_head(lua_State *L)
|
||||
{
|
||||
return hlua_httpclient_send(L, HTTP_METH_HEAD);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send an HTTP GET request and wait for a response
|
||||
*
|
||||
* httpclient:get(url, headers, payload)
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_get(lua_State *L)
|
||||
{
|
||||
return hlua_httpclient_send(L, HTTP_METH_GET);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends an HTTP PUT request and wait for a response
|
||||
*
|
||||
* httpclient:put(url, headers, payload)
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_put(lua_State *L)
|
||||
{
|
||||
return hlua_httpclient_send(L, HTTP_METH_PUT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send an HTTP POST request and wait for a response
|
||||
*
|
||||
* httpclient:post(url, headers, payload)
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_post(lua_State *L)
|
||||
{
|
||||
return hlua_httpclient_send(L, HTTP_METH_POST);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Sends an HTTP DELETE request and wait for a response
|
||||
*
|
||||
* httpclient:delete(url, headers, payload)
|
||||
*/
|
||||
__LJMP static int hlua_httpclient_delete(lua_State *L)
|
||||
{
|
||||
return hlua_httpclient_send(L, HTTP_METH_DELETE);
|
||||
}
|
||||
|
||||
/* Registers the HTTPClient Lua class and exposes core.httpclient constructor.
|
||||
* Called for each new lua_State created by hlua_init_state().
|
||||
*/
|
||||
static int hlua_http_client_init_state(lua_State *L, char **errmsg)
|
||||
{
|
||||
/* Register HTTPClientRequest */
|
||||
lua_newtable(L);
|
||||
/* Register the garbage collector entry. */
|
||||
lua_pushstring(L, "__gc");
|
||||
lua_pushcclosure(L, hlua_httpclient_gc, 0);
|
||||
lua_settable(L, -3);
|
||||
|
||||
class_httpclient_request_ref = hlua_register_metatable(L, CLASS_HTTPCLIENT_REQ);
|
||||
|
||||
/* Register HTTPClient */
|
||||
lua_newtable(L);
|
||||
lua_pushstring(L, "__index");
|
||||
lua_newtable(L);
|
||||
hlua_class_function(L, "get", hlua_httpclient_get);
|
||||
hlua_class_function(L, "head", hlua_httpclient_head);
|
||||
hlua_class_function(L, "put", hlua_httpclient_put);
|
||||
hlua_class_function(L, "post", hlua_httpclient_post);
|
||||
hlua_class_function(L, "delete", hlua_httpclient_delete);
|
||||
lua_settable(L, -3); /* Sets the __index entry. */
|
||||
|
||||
class_httpclient_ref = hlua_register_metatable(L, CLASS_HTTPCLIENT);
|
||||
|
||||
lua_getglobal(L, "core");
|
||||
hlua_class_function(L, "httpclient", hlua_httpclient_factory_new);
|
||||
lua_pop(L, 1);
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
REGISTER_HLUA_STATE_INIT(hlua_http_client_init_state);
|
||||
|
||||
#endif /* USE_LUA */
|
||||
|
|
|
|||
|
|
@ -998,8 +998,12 @@ static int cli_parse_del_map(char **args, char *payload, struct appctx *appctx,
|
|||
/* Lookup the reference in the maps. */
|
||||
ctx->ref = pat_ref_lookup_ref(args[2]);
|
||||
if (!ctx->ref ||
|
||||
!(ctx->ref->flags & ctx->display_flags))
|
||||
return cli_err(appctx, "Unknown map identifier. Please use #<id> or <file>.\n");
|
||||
!(ctx->ref->flags & ctx->display_flags)) {
|
||||
if (ctx->display_flags == PAT_REF_MAP)
|
||||
return cli_err(appctx, "Unknown map identifier. Please use #<id> or <file>.\n");
|
||||
else
|
||||
return cli_err(appctx, "Unknown ACL identifier. Please use #<id> or <file>.\n");
|
||||
}
|
||||
|
||||
/* If the entry identifier start with a '#', it is considered as
|
||||
* pointer id
|
||||
|
|
|
|||
|
|
@ -53,11 +53,13 @@ struct fcgi_conn {
|
|||
uint32_t streams_limit; /* maximum number of concurrent streams the peer supports */
|
||||
uint32_t flags; /* Connection flags: FCGI_CF_* */
|
||||
|
||||
uint32_t drl; /* demux record length (if dsi >= 0) */
|
||||
int16_t dsi; /* dmux stream ID (<0 = idle ) */
|
||||
uint16_t drl; /* demux record length (if dsi >= 0) */
|
||||
uint8_t drt; /* demux record type (if dsi >= 0) */
|
||||
uint8_t drp; /* demux record padding (if dsi >= 0) */
|
||||
|
||||
uint32_t term_evts_log; /* Termination events log: first 4 events reported */
|
||||
|
||||
struct buffer dbuf; /* demux buffer */
|
||||
struct buffer mbuf[FCGI_C_MBUF_CNT]; /* mux buffers (ring) */
|
||||
|
||||
|
|
@ -68,8 +70,6 @@ struct fcgi_conn {
|
|||
unsigned int nb_reserved; /* number of reserved streams */
|
||||
unsigned int stream_cnt; /* total number of streams seen */
|
||||
|
||||
uint32_t term_evts_log; /* Termination events log: first 4 events reported */
|
||||
|
||||
struct proxy *proxy; /* the proxy this connection was created for */
|
||||
struct fcgi_app *app; /* FCGI application used by this mux */
|
||||
struct task *task; /* timeout management task */
|
||||
|
|
|
|||
19
src/mux_h1.c
19
src/mux_h1.c
|
|
@ -5986,7 +5986,24 @@ static int cfg_parse_h1_headers_case_adjust_file(char **args, int section_type,
|
|||
return -1;
|
||||
}
|
||||
free(hdrs_map.name);
|
||||
hdrs_map.name = strdup(args[1]);
|
||||
if (args[1][0] != '/') {
|
||||
char *curpath;
|
||||
char *fullpath = NULL;
|
||||
|
||||
/* filename is provided using relative path, store the absolute path
|
||||
* to take current chdir into account for other threads file load
|
||||
* which occur later
|
||||
*/
|
||||
curpath = getcwd(trash.area, trash.size);
|
||||
if (!curpath) {
|
||||
memprintf(err, "failed to retrieve cur path");
|
||||
return -1;
|
||||
}
|
||||
hdrs_map.name = memprintf(&fullpath, "%s/%s", curpath, args[1]);
|
||||
}
|
||||
else
|
||||
hdrs_map.name = strdup(args[1]);
|
||||
|
||||
if (!hdrs_map.name) {
|
||||
memprintf(err, "'%s %s' : out of memory", args[0], args[1]);
|
||||
return -1;
|
||||
|
|
|
|||
|
|
@ -1268,10 +1268,11 @@ static int qcs_transfer_rx_data(struct qcs *qcs, struct qc_stream_rxbuf *rxbuf)
|
|||
rxbuf->off_end = qcs->rx.offset + b_data(&b) + to_copy;
|
||||
eb64_insert(&qcs->rx.bufs, &rxbuf->off_node);
|
||||
|
||||
/* Increment next rxbuf offset. This must not exceed off_end. */
|
||||
rxbuf_next->off_node.key += to_copy;
|
||||
BUG_ON(rxbuf_next->off_node.key > rxbuf_next->off_end);
|
||||
|
||||
if (rxbuf_next->off_node.key == rxbuf_next->off_end) {
|
||||
/* Now reinsert next rxbuf unless it has been completely truncated. */
|
||||
if (rxbuf_next->off_node.key < rxbuf_next->off_end) {
|
||||
eb64_insert(&qcs->rx.bufs, &rxbuf_next->off_node);
|
||||
}
|
||||
else {
|
||||
|
|
@ -2333,6 +2334,9 @@ int qcc_recv_reset_stream(struct qcc *qcc, uint64_t id, uint64_t err, uint64_t f
|
|||
goto err;
|
||||
}
|
||||
|
||||
qcs->flags |= QC_SF_SIZE_KNOWN|QC_SF_RECV_RESET;
|
||||
qcs_close_remote(qcs);
|
||||
|
||||
/* RFC 9000 3.2. Receiving Stream States
|
||||
*
|
||||
* An
|
||||
|
|
@ -2340,14 +2344,15 @@ int qcc_recv_reset_stream(struct qcc *qcc, uint64_t id, uint64_t err, uint64_t f
|
|||
* data that was not consumed, and signal the receipt of the
|
||||
* RESET_STREAM.
|
||||
*/
|
||||
qcs->flags |= QC_SF_SIZE_KNOWN|QC_SF_RECV_RESET;
|
||||
qcs_close_remote(qcs);
|
||||
while (!eb_is_empty(&qcs->rx.bufs)) {
|
||||
b = container_of(eb64_first(&qcs->rx.bufs),
|
||||
struct qc_stream_rxbuf, off_node);
|
||||
qcs_free_rxbuf(qcs, b);
|
||||
}
|
||||
|
||||
/* Remove stream from recv_list if present. */
|
||||
LIST_DEL_INIT(&qcs->el_recv);
|
||||
|
||||
out:
|
||||
if (qcc->glitches != prev_glitches && !(qcc->flags & QC_CF_IS_BACK))
|
||||
session_add_glitch_ctr(qcc->conn->owner, qcc->glitches - prev_glitches);
|
||||
|
|
@ -3315,6 +3320,7 @@ static int qcc_io_recv(struct qcc *qcc)
|
|||
qcc_qmux_recv(qcc);
|
||||
}
|
||||
|
||||
next_recv:
|
||||
while (!LIST_ISEMPTY(&qcc->recv_list)) {
|
||||
qcs = LIST_ELEM(qcc->recv_list.n, struct qcs *, el_recv);
|
||||
/* No need to add an uni local stream in recv_list. */
|
||||
|
|
@ -3322,12 +3328,26 @@ static int qcc_io_recv(struct qcc *qcc)
|
|||
|
||||
while (qcs_rx_avail_data(qcs) && !(qcs->flags & QC_SF_DEM_FULL)) {
|
||||
ret = qcc_decode_qcs(qcc, qcs);
|
||||
LIST_DEL_INIT(&qcs->el_recv);
|
||||
if (ret <= 0) {
|
||||
LIST_DEL_INIT(&qcs->el_recv);
|
||||
/* Interrupt all receive if connection on error. */
|
||||
if (qcc->flags & QC_CF_ERRL)
|
||||
goto done;
|
||||
/* Decode next entry if stream on error. */
|
||||
goto next_recv;
|
||||
}
|
||||
|
||||
if (ret <= 0)
|
||||
goto done;
|
||||
total += ret;
|
||||
}
|
||||
|
||||
/* Always remove QCS from recv_list to prevent infinite loop.
|
||||
* This is performed even if inner loop was not executed : QCS
|
||||
* has nothing to do in recv_list if no avail Rx data or demux
|
||||
* is blocked. Next decoding will be performed on new data read
|
||||
* unless demux is blocked. In this case QCS will be reinserted
|
||||
* in recv_list on unblocking to execute decode here again.
|
||||
*/
|
||||
LIST_DEL_INIT(&qcs->el_recv);
|
||||
}
|
||||
|
||||
done:
|
||||
|
|
|
|||
|
|
@ -370,6 +370,13 @@ struct pool_head *create_pool_from_reg(const char *name, struct pool_registratio
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (invalid_char(name)) {
|
||||
ha_alert("Pool '%s' declared at %s:%u contains invalid chars in its name and "
|
||||
"cannot be registered. Please report to developers. Aborting.\n",
|
||||
name, reg->file, reg->line);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
extra_mark = (pool_debugging & POOL_DBG_TAG) ? POOL_EXTRA_MARK : 0;
|
||||
extra_caller = (pool_debugging & POOL_DBG_CALLER) ? POOL_EXTRA_CALLER : 0;
|
||||
extra = extra_mark + extra_caller;
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ void quic_transport_params_init(struct quic_transport_params *p, int server)
|
|||
server ? quic_tune.fe.stream_max_concurrent : quic_tune.be.stream_max_concurrent;
|
||||
/* TODO value used to conform with HTTP/3, should be derived from app_ops */
|
||||
const int max_streams_uni = 3;
|
||||
uint64_t max_stream_data_bidi;
|
||||
|
||||
/* Set RFC default values for unspecified parameters. */
|
||||
quic_dflt_transport_params_cpy(p);
|
||||
|
|
@ -81,21 +82,30 @@ void quic_transport_params_init(struct quic_transport_params *p, int server)
|
|||
p->initial_max_data = stream_rxbuf ?
|
||||
stream_rxbuf : max_streams_bidi * stream_rx_bufsz;
|
||||
|
||||
/* Set remote streams flow-control data limit. This is calculated as a
|
||||
* ratio from max-data, then rounded up to bufsize.
|
||||
/* Calculate the limit for the Rx capability of bidirectional streams.
|
||||
* This is a ratio from max-data rounded up to bufsize.
|
||||
*/
|
||||
p->initial_max_stream_data_bidi_remote = server ?
|
||||
max_stream_data_bidi = server ?
|
||||
p->initial_max_data * quic_tune.fe.stream_data_ratio / 100 :
|
||||
p->initial_max_data * quic_tune.be.stream_data_ratio / 100;
|
||||
p->initial_max_stream_data_bidi_remote =
|
||||
stream_rx_bufsz * ((p->initial_max_stream_data_bidi_remote + (stream_rx_bufsz - 1)) / stream_rx_bufsz);
|
||||
max_stream_data_bidi =
|
||||
stream_rx_bufsz * ((max_stream_data_bidi + (stream_rx_bufsz - 1)) / stream_rx_bufsz);
|
||||
|
||||
/* Set remaining flow-control data limit. Local bidi streams are unused
|
||||
* on server side. Uni streams are only used for control exchange, so
|
||||
* only a single buffer for in flight data should be enough.
|
||||
/* Apply bidirectional streams Rx cap. This depends on the connection
|
||||
* side. On FE side, exchange will occur on remote streams; on BE side,
|
||||
* exchange will occur on local streams.
|
||||
*/
|
||||
p->initial_max_stream_data_bidi_local = stream_rx_bufsz;
|
||||
p->initial_max_stream_data_uni = stream_rx_bufsz;
|
||||
if (server) {
|
||||
p->initial_max_stream_data_bidi_remote = max_stream_data_bidi;
|
||||
p->initial_max_stream_data_bidi_local = stream_rx_bufsz;
|
||||
}
|
||||
else {
|
||||
p->initial_max_stream_data_bidi_remote = stream_rx_bufsz;
|
||||
p->initial_max_stream_data_bidi_local = max_stream_data_bidi;
|
||||
}
|
||||
|
||||
/* Unidirectional streams exchange should be minimal. */
|
||||
p->initial_max_stream_data_uni = stream_rx_bufsz;
|
||||
|
||||
if (server) {
|
||||
p->with_stateless_reset_token = 1;
|
||||
|
|
|
|||
|
|
@ -2029,8 +2029,8 @@ static int qc_do_build_pkt(unsigned char *pos, const unsigned char *end,
|
|||
/* Handle Initial packet padding if necessary. */
|
||||
if (padding && dglen < QUIC_INITIAL_PACKET_MINLEN) {
|
||||
padding_len = QUIC_INITIAL_PACKET_MINLEN - dglen;
|
||||
|
||||
len += padding_len;
|
||||
|
||||
/* Update size of packet length field with new PADDING data. */
|
||||
if (pkt->type != QUIC_PACKET_TYPE_SHORT) {
|
||||
size_t len_sz_diff = quic_int_getsize(len) - len_sz;
|
||||
|
|
@ -2038,6 +2038,7 @@ static int qc_do_build_pkt(unsigned char *pos, const unsigned char *end,
|
|||
padding_len -= len_sz_diff;
|
||||
len_sz += len_sz_diff;
|
||||
dglen += len_sz_diff;
|
||||
len -= len_sz_diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2074,6 +2075,7 @@ static int qc_do_build_pkt(unsigned char *pos, const unsigned char *end,
|
|||
len += padding_len;
|
||||
}
|
||||
|
||||
/* Encode length field : length of PN and payload (frames + TLS AEAD tag). */
|
||||
if (pkt->type != QUIC_PACKET_TYPE_SHORT && !quic_enc_int(&pos, end, len))
|
||||
goto no_room;
|
||||
|
||||
|
|
|
|||
21
src/regex.c
21
src/regex.c
|
|
@ -442,7 +442,7 @@ static void regex_register_build_options(void)
|
|||
INITCALL0(STG_REGISTER, regex_register_build_options);
|
||||
|
||||
#ifdef USE_PCRE2
|
||||
static int init_pcre2_per_thread(void)
|
||||
static int init_pcre2_one_thread(void)
|
||||
{
|
||||
local_pcre2_match = pcre2_match_data_create(MAX_MATCH, NULL);
|
||||
if (!local_pcre2_match) {
|
||||
|
|
@ -452,13 +452,32 @@ static int init_pcre2_per_thread(void)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* per-thread init for the next threads (first one already done) */
|
||||
static int init_pcre2_per_thread(void)
|
||||
{
|
||||
if (!tid)
|
||||
return 1;
|
||||
return init_pcre2_one_thread();
|
||||
}
|
||||
|
||||
/* per-thread deinit for the next threads */
|
||||
static void deinit_pcre2_per_thread(void)
|
||||
{
|
||||
if (tid)
|
||||
pcre2_match_data_free(local_pcre2_match);
|
||||
}
|
||||
|
||||
/* late deinit for the first thread */
|
||||
static void deinit_pcre2_first_thread(void)
|
||||
{
|
||||
pcre2_match_data_free(local_pcre2_match);
|
||||
}
|
||||
|
||||
/* early init for the first thread */
|
||||
INITCALL0(STG_INIT, init_pcre2_one_thread);
|
||||
REGISTER_PER_THREAD_INIT(init_pcre2_per_thread);
|
||||
REGISTER_PER_THREAD_DEINIT(deinit_pcre2_per_thread);
|
||||
REGISTER_POST_DEINIT(deinit_pcre2_first_thread);
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
|
|||
126
src/server.c
126
src/server.c
|
|
@ -76,9 +76,8 @@ struct srv_kw_list srv_keywords = {
|
|||
.list = LIST_HEAD_INIT(srv_keywords.list)
|
||||
};
|
||||
|
||||
__decl_thread(HA_SPINLOCK_T idle_conn_srv_lock);
|
||||
struct eb_root idle_conn_srv = EB_ROOT;
|
||||
struct task *idle_conn_task __read_mostly = NULL;
|
||||
struct eb_root idle_conn_srv[MAX_THREADS];
|
||||
struct task *idle_conn_task[MAX_THREADS] __read_mostly = {};
|
||||
struct mt_list servers_list = MT_LIST_HEAD_INIT(servers_list);
|
||||
static struct task *server_atomic_sync_task = NULL;
|
||||
static event_hdl_async_equeue server_atomic_sync_queue;
|
||||
|
|
@ -2978,6 +2977,19 @@ void srv_settings_cpy(struct server *srv, const struct server *src, int srv_tmpl
|
|||
srv->check.alpn_len = src->check.alpn_len;
|
||||
}
|
||||
}
|
||||
if (src->check.tcpcheck && src->check.tcpcheck->healthcheck) {
|
||||
struct tcpcheck *tcpcheck = NULL;
|
||||
|
||||
tcpcheck = calloc(1, sizeof(*tcpcheck));
|
||||
if (tcpcheck) {
|
||||
LIST_INIT(&tcpcheck->preset_vars);
|
||||
tcpcheck->healthcheck = strdup(src->check.tcpcheck->healthcheck);
|
||||
if (tcpcheck->healthcheck == NULL)
|
||||
ha_free(&tcpcheck);
|
||||
}
|
||||
if (tcpcheck)
|
||||
srv->check.tcpcheck = tcpcheck;
|
||||
}
|
||||
|
||||
if (!(srv->flags & SRV_F_RHTTP))
|
||||
srv->check.reuse_pool = src->check.reuse_pool;
|
||||
|
|
@ -3268,6 +3280,8 @@ struct server *srv_drop(struct server *srv)
|
|||
|
||||
/* This BUG_ON() is invalid for now as server released on deinit will
|
||||
* trigger it as they are not properly removed from their tree.
|
||||
* This is even more relevant now, as we would need to check the
|
||||
* idle_node for each thread
|
||||
*/
|
||||
//BUG_ON(ceb_intree(&srv->addr_node) ||
|
||||
// srv->idle_node.node.leaf_p ||
|
||||
|
|
@ -6115,6 +6129,7 @@ static int srv_init_per_thr(struct server *srv)
|
|||
srv->per_thr[i].idle_conns = NULL;
|
||||
srv->per_thr[i].safe_conns = NULL;
|
||||
srv->per_thr[i].avail_conns = NULL;
|
||||
srv->per_thr[i].srv = srv;
|
||||
MT_LIST_INIT(&srv->per_thr[i].sess_conns);
|
||||
MT_LIST_INIT(&srv->per_thr[i].streams);
|
||||
|
||||
|
|
@ -6206,7 +6221,6 @@ static int cli_parse_add_server(char **args, char *payload, struct appctx *appct
|
|||
struct server *srv;
|
||||
char *be_name, *sv_name, *errmsg;
|
||||
int errcode, argc;
|
||||
int next_id;
|
||||
const int parse_flags = SRV_PARSE_DYNAMIC|SRV_PARSE_PARSE_ADDR;
|
||||
|
||||
usermsgs_clr("CLI");
|
||||
|
|
@ -6373,6 +6387,19 @@ static int cli_parse_add_server(char **args, char *payload, struct appctx *appct
|
|||
if (errcode)
|
||||
goto out;
|
||||
|
||||
/* Generate the server ID if not manually specified. This must be
|
||||
* performed before the server queuing in LB tree (srv_alloc_lb()).
|
||||
* Proxy tree ID insertion though is only done when all fallible
|
||||
* operation are completed.
|
||||
*/
|
||||
if (!srv->puid) {
|
||||
srv->puid = server_get_next_id(be, 1);
|
||||
if (!srv->puid) {
|
||||
ha_alert("Cannot attach server : no id left in proxy\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!srv_alloc_lb(srv, be)) {
|
||||
ha_alert("Failed to initialize load-balancing data.\n");
|
||||
goto out;
|
||||
|
|
@ -6392,16 +6419,7 @@ static int cli_parse_add_server(char **args, char *payload, struct appctx *appct
|
|||
if (errcode)
|
||||
goto out;
|
||||
|
||||
/* generate the server id if not manually specified */
|
||||
if (!srv->puid) {
|
||||
next_id = server_get_next_id(be, 1);
|
||||
if (!next_id) {
|
||||
ha_alert("Cannot attach server : no id left in proxy\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
srv->puid = next_id;
|
||||
}
|
||||
/* All fallible operations completed, the server can now be made visible. */
|
||||
|
||||
/* insert the server in the backend trees */
|
||||
server_index_id(be, srv);
|
||||
|
|
@ -6638,7 +6656,8 @@ static int cli_parse_delete_server(char **args, char *payload, struct appctx *ap
|
|||
cebuis_item_delete(&be->used_server_addr, addr_node, addr_key, srv);
|
||||
|
||||
/* remove srv from idle_node tree for idle conn cleanup */
|
||||
eb32_delete(&srv->idle_node);
|
||||
for (ret = 0; ret < global.nbthread; ret++)
|
||||
eb32_delete(&srv->per_thr[ret].idle_node);
|
||||
|
||||
/* set LSB bit (odd bit) for reuse_cnt */
|
||||
srv_id_reuse_cnt |= 1;
|
||||
|
|
@ -7531,20 +7550,16 @@ int srv_add_to_idle_list(struct server *srv, struct connection *conn, int is_saf
|
|||
HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
|
||||
_HA_ATOMIC_INC(&srv->curr_idle_thr[tid]);
|
||||
|
||||
if (HA_ATOMIC_LOAD(&srv->idle_node.node.leaf_p) == NULL) {
|
||||
HA_SPIN_LOCK(OTHER_LOCK, &idle_conn_srv_lock);
|
||||
if (_HA_ATOMIC_LOAD(&srv->idle_node.node.leaf_p) == NULL) {
|
||||
srv->idle_node.key = tick_add(srv->pool_purge_delay,
|
||||
if (srv->per_thr[tid].idle_node.node.leaf_p == NULL) {
|
||||
srv->per_thr[tid].idle_node.key = tick_add(srv->pool_purge_delay,
|
||||
now_ms);
|
||||
eb32_insert(&idle_conn_srv, &srv->idle_node);
|
||||
if (!task_in_wq(idle_conn_task) && !
|
||||
task_in_rq(idle_conn_task)) {
|
||||
task_schedule(idle_conn_task,
|
||||
srv->idle_node.key);
|
||||
}
|
||||
BUG_ON_STRESS(!mt_list_isempty(&conn->toremove_list));
|
||||
eb32_insert(&idle_conn_srv[tid], &srv->per_thr[tid].idle_node);
|
||||
if (!task_in_wq(idle_conn_task[tid]) &&
|
||||
!task_in_rq(idle_conn_task[tid])) {
|
||||
task_schedule(idle_conn_task[tid],
|
||||
srv->per_thr[tid].idle_node.key);
|
||||
}
|
||||
HA_SPIN_UNLOCK(OTHER_LOCK, &idle_conn_srv_lock);
|
||||
BUG_ON_STRESS(!mt_list_isempty(&conn->toremove_list));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -7566,24 +7581,26 @@ struct task *srv_cleanup_idle_conns(struct task *task, void *context, unsigned i
|
|||
{
|
||||
struct server *srv;
|
||||
struct eb32_node *eb;
|
||||
int i;
|
||||
unsigned int next_wakeup;
|
||||
int mytid = tid;
|
||||
|
||||
next_wakeup = TICK_ETERNITY;
|
||||
HA_SPIN_LOCK(OTHER_LOCK, &idle_conn_srv_lock);
|
||||
while (1) {
|
||||
struct srv_per_thread *per_thr;
|
||||
int exceed_conns;
|
||||
int to_kill;
|
||||
int curr_idle;
|
||||
int max_conn;
|
||||
int removed;
|
||||
|
||||
eb = eb32_lookup_ge(&idle_conn_srv, now_ms - TIMER_LOOK_BACK);
|
||||
eb = eb32_lookup_ge(&idle_conn_srv[mytid], now_ms - TIMER_LOOK_BACK);
|
||||
if (!eb) {
|
||||
/* we might have reached the end of the tree, typically because
|
||||
* <now_ms> is in the first half and we're first scanning the last
|
||||
* half. Let's loop back to the beginning of the tree now.
|
||||
*/
|
||||
|
||||
eb = eb32_first(&idle_conn_srv);
|
||||
eb = eb32_first(&idle_conn_srv[mytid]);
|
||||
if (likely(!eb))
|
||||
break;
|
||||
}
|
||||
|
|
@ -7592,7 +7609,8 @@ struct task *srv_cleanup_idle_conns(struct task *task, void *context, unsigned i
|
|||
next_wakeup = eb->key;
|
||||
break;
|
||||
}
|
||||
srv = eb32_entry(eb, struct server, idle_node);
|
||||
per_thr = eb32_entry(eb, struct srv_per_thread, idle_node);
|
||||
srv = per_thr->srv;
|
||||
|
||||
/* Calculate how many idle connections we want to kill :
|
||||
* we want to remove half the difference between the total
|
||||
|
|
@ -7605,47 +7623,41 @@ struct task *srv_cleanup_idle_conns(struct task *task, void *context, unsigned i
|
|||
exceed_conns = srv->curr_used_conns + curr_idle - MAX(srv->max_used_conns, srv->est_need_conns);
|
||||
exceed_conns = to_kill = exceed_conns / 2 + (exceed_conns & 1);
|
||||
|
||||
srv->est_need_conns = (srv->est_need_conns + srv->max_used_conns) / 2;
|
||||
/*
|
||||
* It is acceptable not to lock anything before modifying
|
||||
* est_need_conns and max_used_conns, even if multiple threads
|
||||
* are running that task at the same time, we don't need a
|
||||
* very high precision here, it will converge over time.
|
||||
*/
|
||||
HA_ATOMIC_STORE(&srv->est_need_conns, (srv->est_need_conns + srv->max_used_conns) / 2);
|
||||
if (srv->est_need_conns < srv->max_used_conns)
|
||||
srv->est_need_conns = srv->max_used_conns;
|
||||
HA_ATOMIC_STORE(&srv->est_need_conns, srv->max_used_conns);
|
||||
|
||||
HA_ATOMIC_STORE(&srv->max_used_conns, srv->curr_used_conns);
|
||||
|
||||
if (exceed_conns <= 0)
|
||||
goto remove;
|
||||
|
||||
/* check all threads starting with ours */
|
||||
for (i = tid;;) {
|
||||
int max_conn;
|
||||
int removed;
|
||||
max_conn = (exceed_conns * srv->curr_idle_thr[mytid]) / curr_idle + 1;
|
||||
|
||||
max_conn = (exceed_conns * srv->curr_idle_thr[i]) /
|
||||
curr_idle + 1;
|
||||
HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[mytid].idle_conns_lock);
|
||||
removed = srv_migrate_conns_to_remove(srv, mytid, max_conn);
|
||||
HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[mytid].idle_conns_lock);
|
||||
|
||||
HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock);
|
||||
removed = srv_migrate_conns_to_remove(srv, i, max_conn);
|
||||
HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock);
|
||||
|
||||
if (removed)
|
||||
task_wakeup(idle_conns[i].cleanup_task, TASK_WOKEN_OTHER);
|
||||
|
||||
if ((i = ((i + 1 == global.nbthread) ? 0 : i + 1)) == tid)
|
||||
break;
|
||||
}
|
||||
if (removed)
|
||||
task_wakeup(idle_conns[mytid].cleanup_task, TASK_WOKEN_OTHER);
|
||||
remove:
|
||||
eb32_delete(&srv->idle_node);
|
||||
eb32_delete(&srv->per_thr[mytid].idle_node);
|
||||
|
||||
if (srv->curr_idle_conns) {
|
||||
if (!LIST_ISEMPTY(&srv->per_thr[mytid].idle_conn_list)) {
|
||||
/* There are still more idle connections, add the
|
||||
* server back in the tree.
|
||||
*/
|
||||
srv->idle_node.key = tick_add(srv->pool_purge_delay, now_ms);
|
||||
eb32_insert(&idle_conn_srv, &srv->idle_node);
|
||||
next_wakeup = tick_first(next_wakeup, srv->idle_node.key);
|
||||
srv->per_thr[mytid].idle_node.key = tick_add(srv->pool_purge_delay, now_ms);
|
||||
eb32_insert(&idle_conn_srv[mytid], &srv->per_thr[mytid].idle_node);
|
||||
next_wakeup = tick_first(next_wakeup, srv->per_thr[mytid].idle_node.key);
|
||||
}
|
||||
}
|
||||
HA_SPIN_UNLOCK(OTHER_LOCK, &idle_conn_srv_lock);
|
||||
|
||||
task->expire = next_wakeup;
|
||||
return task;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
|
||||
DECLARE_TYPED_POOL(pool_head_session, "session", struct session);
|
||||
DECLARE_TYPED_POOL(pool_head_sess_priv_conns, "session priv conns list", struct sess_priv_conns);
|
||||
DECLARE_TYPED_POOL(pool_head_sess_priv_conns, "sess_priv_conns", struct sess_priv_conns);
|
||||
|
||||
int conn_complete_session(struct connection *conn);
|
||||
|
||||
|
|
|
|||
|
|
@ -439,6 +439,22 @@ static int ha_ssl_read(BIO *h, char *buf, int size)
|
|||
}
|
||||
|
||||
#ifdef HA_USE_KTLS
|
||||
static int ktls_enable_ulp(struct ssl_sock_ctx *ctx)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!(ctx->flags & SSL_SOCK_F_KTLS_ULP)) {
|
||||
ret = setsockopt(ctx->conn->handle.fd, SOL_TCP, TCP_ULP, "tls",
|
||||
sizeof("tls"));
|
||||
if (ret == 0)
|
||||
ctx->flags |= SSL_SOCK_F_KTLS_ULP;
|
||||
else
|
||||
ctx->flags &= ~SSL_SOCK_F_KTLS_ENABLED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Returns 0 on success, -1 on failure */
|
||||
static int ktls_set_key(struct ssl_sock_ctx *ctx, void *info, size_t info_len, int is_tx)
|
||||
{
|
||||
|
|
@ -485,6 +501,9 @@ static long ha_ssl_ctrl(BIO *h, int cmd, long arg1, void *arg2)
|
|||
|
||||
if (!(ctx->flags & SSL_SOCK_F_KTLS_ENABLED))
|
||||
return 0;
|
||||
|
||||
if (ktls_enable_ulp(ctx) == -1)
|
||||
return 0;
|
||||
/*
|
||||
* As OpenSSL doesn't export struct tls_crypto_info_all,
|
||||
* and it puts the size at the end of the struct,
|
||||
|
|
@ -5670,14 +5689,6 @@ static int ssl_sock_start(struct connection *conn, void *xprt_ctx)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
#ifdef HA_USE_KTLS
|
||||
/*
|
||||
* Make the socket usable for kTLS. That does not mean that we will
|
||||
* use kTLS, though, just that the socket will be able to do it.
|
||||
*/
|
||||
if ((ctx->flags & SSL_SOCK_F_KTLS_ENABLED) && setsockopt(conn->handle.fd, SOL_TCP, TCP_ULP, "tls", sizeof("tls")) != 0)
|
||||
ctx->flags &= ~SSL_SOCK_F_KTLS_ENABLED;
|
||||
#endif
|
||||
tasklet_wakeup(ctx->wait_event.tasklet);
|
||||
|
||||
return 0;
|
||||
|
|
@ -6617,6 +6628,9 @@ static void ssl_sock_setup_ktls(struct ssl_sock_ctx *ctx)
|
|||
if (!(ctx->flags & SSL_SOCK_F_KTLS_ENABLED))
|
||||
return;
|
||||
|
||||
if (ktls_enable_ulp(ctx) == -1)
|
||||
return;
|
||||
|
||||
switch (SSL_version(ctx->ssl)) {
|
||||
case TLS_1_2_VERSION:
|
||||
is_tls_12 = 1;
|
||||
|
|
@ -7078,8 +7092,12 @@ static size_t ssl_sock_to_buf(struct connection *conn, void *xprt_ctx, struct bu
|
|||
memcpy(b_tail(buf), b_head(&ctx->early_buf), try);
|
||||
b_add(buf, try);
|
||||
b_del(&ctx->early_buf, try);
|
||||
if (b_data(&ctx->early_buf) == 0)
|
||||
b_free(&ctx->early_buf);
|
||||
if (b_data(&ctx->early_buf) == 0) {
|
||||
if (!(ctx->conn->flags & CO_FL_EARLY_SSL_HS))
|
||||
b_free(&ctx->early_buf);
|
||||
else
|
||||
b_reset(&ctx->early_buf);
|
||||
}
|
||||
TRACE_STATE("read early data", SSL_EV_CONN_RECV|SSL_EV_CONN_RECV_EARLY, conn, &try);
|
||||
return try;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,9 @@ void se_shutdown(struct sedesc *sedesc, enum se_shut_mode mode)
|
|||
struct se_abort_info *reason = NULL;
|
||||
unsigned int flags = 0;
|
||||
|
||||
/* Should never happen, placed here to be sure we forgot nothing */
|
||||
BUG_ON(!(mode & (SE_SHW_SILENT|SE_SHW_NORMAL)));
|
||||
|
||||
if ((mode & (SE_SHW_SILENT|SE_SHW_NORMAL)) && !se_fl_test(sedesc, SE_FL_SHW)) {
|
||||
se_report_term_evt(sedesc, se_tevt_type_shutw);
|
||||
flags |= (mode & SE_SHW_NORMAL) ? SE_FL_SHWN : SE_FL_SHWS;
|
||||
|
|
|
|||
241
src/task.c
241
src/task.c
|
|
@ -35,18 +35,15 @@ DECLARE_TYPED_POOL(pool_head_tasklet, "tasklet", struct tasklet, 0, 64);
|
|||
*/
|
||||
DECLARE_TYPED_POOL(pool_head_notification, "notification", struct notification);
|
||||
|
||||
/* The lock protecting all wait queues at once. For now we have no better
|
||||
* alternative since a task may have to be removed from a queue and placed
|
||||
* into another one. Storing the WQ index into the task doesn't seem to be
|
||||
* sufficient either.
|
||||
*/
|
||||
__decl_aligned_rwlock(wq_lock);
|
||||
|
||||
/* used to detect if the scheduler looks stuck (for warnings) */
|
||||
static struct {
|
||||
int sched_stuck THREAD_ALIGNED();
|
||||
} sched_ctx[MAX_THREADS];
|
||||
|
||||
#if !defined(HA_CAS_IS_8B) && !defined(HA_HAVE_CAS_DW)
|
||||
__decl_thread(HA_SPINLOCK_T task_state_tid);
|
||||
#endif
|
||||
|
||||
/* Flags the task <t> for immediate destruction and puts it into its first
|
||||
* thread's shared tasklet list if not yet queued/running. This will bypass
|
||||
* the priority scheduling and make the task show up as fast as possible in
|
||||
|
|
@ -82,7 +79,9 @@ void task_kill(struct task *t)
|
|||
* Note: that's a task so it must be accounted for as such. Pick
|
||||
* the task's first thread for the job.
|
||||
*/
|
||||
thr = t->tid >= 0 ? t->tid : tid;
|
||||
thr = __task_get_current_owner(t->tid);
|
||||
if (thr == -1)
|
||||
thr = tid;
|
||||
|
||||
/* Beware: tasks that have never run don't have their ->list empty yet! */
|
||||
MT_LIST_APPEND(&ha_thread_ctx[thr].shared_tasklet_list,
|
||||
|
|
@ -216,7 +215,13 @@ struct list *__tasklet_wakeup_after(struct list *head, struct tasklet *tl)
|
|||
void __task_wakeup(struct task *t)
|
||||
{
|
||||
struct eb_root *root = &th_ctx->rqueue;
|
||||
int thr __maybe_unused = t->tid >= 0 ? t->tid : tid;
|
||||
/*
|
||||
* At this point the task tid should always be set to the relevant
|
||||
* thread, so we can just use __task_get_current_owner();
|
||||
*/
|
||||
int thr __maybe_unused = __task_get_current_owner(t->tid);
|
||||
|
||||
BUG_ON(t->tid == -1);
|
||||
|
||||
#ifdef USE_THREAD
|
||||
if (thr != tid) {
|
||||
|
|
@ -275,18 +280,38 @@ void __task_wakeup(struct task *t)
|
|||
* at all about locking so the caller must be careful when deciding whether to
|
||||
* lock or not around this call.
|
||||
*/
|
||||
void __task_queue(struct task *task, struct eb_root *wq)
|
||||
void __task_queue(struct task *task)
|
||||
{
|
||||
#ifdef USE_THREAD
|
||||
BUG_ON((wq == &tg_ctx->timers && task->tid >= 0) ||
|
||||
(wq == &th_ctx->timers && task->tid < 0) ||
|
||||
(wq != &tg_ctx->timers && wq != &th_ctx->timers));
|
||||
#endif
|
||||
int old_state, new_state;
|
||||
int old_tid;
|
||||
int cur_owner;
|
||||
|
||||
/* if this happens the process is doomed anyway, so better catch it now
|
||||
* so that we have the caller in the stack.
|
||||
*/
|
||||
BUG_ON(task->expire == TICK_ETERNITY);
|
||||
|
||||
do {
|
||||
new_state = old_state = _HA_ATOMIC_LOAD(&task->state);
|
||||
if (old_state & TASK_KILLED)
|
||||
return;
|
||||
old_tid = _HA_ATOMIC_LOAD(&task->tid);
|
||||
cur_owner = __task_get_current_owner(old_tid);
|
||||
if (old_tid != -1 && cur_owner != tid)
|
||||
new_state |= TASK_WOKEN_WQ;
|
||||
} while (!(__task_set_state_and_tid(task, old_tid, __task_get_new_tid_field(old_tid), old_state, new_state)));
|
||||
|
||||
if (cur_owner != tid && cur_owner != -1) {
|
||||
/*
|
||||
* If the task has already been woken up to be added in the
|
||||
* wait queue, nothing left to do, the target thread will
|
||||
* eventually do the right thing.
|
||||
*/
|
||||
if (!(old_state & TASK_WOKEN_WQ))
|
||||
_task_wakeup(task, 0, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (likely(task_in_wq(task)))
|
||||
__task_unlink_wq(task);
|
||||
|
||||
|
|
@ -298,7 +323,7 @@ void __task_queue(struct task *task, struct eb_root *wq)
|
|||
return;
|
||||
#endif
|
||||
|
||||
eb32_insert(wq, &task->wq);
|
||||
eb32_insert(&th_ctx->timers, &task->wq);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -311,7 +336,6 @@ void wake_expired_tasks()
|
|||
int max_processed = global.tune.runqueue_depth;
|
||||
struct task *task;
|
||||
struct eb32_node *eb;
|
||||
__decl_thread(int key);
|
||||
|
||||
while (1) {
|
||||
if (max_processed-- <= 0)
|
||||
|
|
@ -348,9 +372,53 @@ void wake_expired_tasks()
|
|||
|
||||
task = eb32_entry(eb, struct task, wq);
|
||||
if (tick_is_expired(task->expire, now_ms)) {
|
||||
int set_running = 0;
|
||||
|
||||
/* expired task, wake it up */
|
||||
__task_unlink_wq(task);
|
||||
_task_wakeup(task, TASK_WOKEN_TIMER, 0);
|
||||
/*
|
||||
* If it's a shared task, see whether we should hand it
|
||||
* to a less loaded thread.
|
||||
*/
|
||||
if (task->tid < 0) {
|
||||
int attempts = MIN(global.nbthread, 3);
|
||||
while (attempts-- > 0) {
|
||||
uint new_tid = statistical_prng_range(global.nbthread);
|
||||
|
||||
if (new_tid == tid)
|
||||
continue;
|
||||
if (ha_thread_ctx[new_tid].rq_total * 2 < th_ctx->rq_total) {
|
||||
int cur_state;
|
||||
do {
|
||||
cur_state = _HA_ATOMIC_LOAD(&task->state);
|
||||
/*
|
||||
* Okay the task is already in our runqueue,
|
||||
* or somebody owns the
|
||||
* TASK_RUNNING flag because
|
||||
* it is calling task_schedule(), give up.
|
||||
*/
|
||||
if (cur_state & (TASK_QUEUED | TASK_RUNNING))
|
||||
break;
|
||||
/*
|
||||
* Make sure we have TASK_RUNNING set
|
||||
* so that the task don't
|
||||
* immediately run on the
|
||||
* new thread and gets
|
||||
* freed.
|
||||
*/
|
||||
if (__task_set_state_and_tid(task, task->tid, -2 - new_tid, cur_state, cur_state | TASK_RUNNING)) {
|
||||
set_running = 1;
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (set_running)
|
||||
task_drop_running(task, TASK_WOKEN_TIMER);
|
||||
else
|
||||
_task_wakeup(task, TASK_WOKEN_TIMER, 0);
|
||||
}
|
||||
else if (task->expire != eb->key) {
|
||||
/* task is not expired but its key doesn't match so let's
|
||||
|
|
@ -358,7 +426,7 @@ void wake_expired_tasks()
|
|||
*/
|
||||
__task_unlink_wq(task);
|
||||
if (tick_isset(task->expire))
|
||||
__task_queue(task, &tt->timers);
|
||||
__task_queue(task);
|
||||
}
|
||||
else {
|
||||
/* task not expired and correctly placed. It may not be eternal. */
|
||||
|
|
@ -366,105 +434,12 @@ void wake_expired_tasks()
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_THREAD
|
||||
if (eb_is_empty(&tg_ctx->timers))
|
||||
goto leave;
|
||||
|
||||
HA_RWLOCK_RDLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
eb = eb32_lookup_ge(&tg_ctx->timers, now_ms - TIMER_LOOK_BACK);
|
||||
if (!eb) {
|
||||
eb = eb32_first(&tg_ctx->timers);
|
||||
if (likely(!eb)) {
|
||||
HA_RWLOCK_RDUNLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
key = eb->key;
|
||||
|
||||
if (tick_is_lt(now_ms, key)) {
|
||||
HA_RWLOCK_RDUNLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* There's really something of interest here, let's visit the queue */
|
||||
|
||||
if (HA_RWLOCK_TRYRDTOSK(TASK_WQ_LOCK, &wq_lock)) {
|
||||
/* if we failed to grab the lock it means another thread is
|
||||
* already doing the same here, so let it do the job.
|
||||
*/
|
||||
HA_RWLOCK_RDUNLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
lookup_next:
|
||||
if (max_processed-- <= 0)
|
||||
break;
|
||||
eb = eb32_lookup_ge(&tg_ctx->timers, now_ms - TIMER_LOOK_BACK);
|
||||
if (!eb) {
|
||||
/* we might have reached the end of the tree, typically because
|
||||
* <now_ms> is in the first half and we're first scanning the last
|
||||
* half. Let's loop back to the beginning of the tree now.
|
||||
*/
|
||||
eb = eb32_first(&tg_ctx->timers);
|
||||
if (likely(!eb))
|
||||
break;
|
||||
}
|
||||
|
||||
task = eb32_entry(eb, struct task, wq);
|
||||
|
||||
/* Check for any competing run of the task (quite rare but may
|
||||
* involve a dangerous concurrent access on task->expire). In
|
||||
* order to protect against this, we'll take an exclusive access
|
||||
* on TASK_RUNNING before checking/touching task->expire. If the
|
||||
* task is already RUNNING on another thread, it will deal by
|
||||
* itself with the requeuing so we must not do anything and
|
||||
* simply quit the loop for now, because we cannot wait with the
|
||||
* WQ lock held as this would prevent the running thread from
|
||||
* requeuing the task. One annoying effect of holding RUNNING
|
||||
* here is that a concurrent task_wakeup() will refrain from
|
||||
* waking it up. This forces us to check for a wakeup after
|
||||
* releasing the flag.
|
||||
*/
|
||||
if (HA_ATOMIC_FETCH_OR(&task->state, TASK_RUNNING) & TASK_RUNNING)
|
||||
break;
|
||||
|
||||
if (tick_is_expired(task->expire, now_ms)) {
|
||||
/* expired task, wake it up */
|
||||
HA_RWLOCK_SKTOWR(TASK_WQ_LOCK, &wq_lock);
|
||||
__task_unlink_wq(task);
|
||||
HA_RWLOCK_WRTOSK(TASK_WQ_LOCK, &wq_lock);
|
||||
task_drop_running(task, TASK_WOKEN_TIMER);
|
||||
}
|
||||
else if (task->expire != eb->key) {
|
||||
/* task is not expired but its key doesn't match so let's
|
||||
* update it and skip to next apparently expired task.
|
||||
*/
|
||||
HA_RWLOCK_SKTOWR(TASK_WQ_LOCK, &wq_lock);
|
||||
__task_unlink_wq(task);
|
||||
if (tick_isset(task->expire))
|
||||
__task_queue(task, &tg_ctx->timers);
|
||||
HA_RWLOCK_WRTOSK(TASK_WQ_LOCK, &wq_lock);
|
||||
task_drop_running(task, 0);
|
||||
goto lookup_next;
|
||||
}
|
||||
else {
|
||||
/* task not expired and correctly placed. It may not be eternal. */
|
||||
BUG_ON(task->expire == TICK_ETERNITY);
|
||||
task_drop_running(task, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
HA_RWLOCK_SKUNLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
#endif
|
||||
leave:
|
||||
return;
|
||||
}
|
||||
|
||||
/* Checks the next timer for the current thread by looking into its own timer
|
||||
* list and the global one. It may return TICK_ETERNITY if no timer is present.
|
||||
* list. It may return TICK_ETERNITY if no timer is present.
|
||||
* Note that the next timer might very well be slightly in the past.
|
||||
*/
|
||||
int next_timer_expiry()
|
||||
|
|
@ -472,7 +447,6 @@ int next_timer_expiry()
|
|||
struct thread_ctx * const tt = th_ctx; // thread's tasks
|
||||
struct eb32_node *eb;
|
||||
int ret = TICK_ETERNITY;
|
||||
__decl_thread(int key = TICK_ETERNITY);
|
||||
|
||||
/* first check in the thread-local timers */
|
||||
eb = eb32_lookup_ge(&tt->timers, now_ms - TIMER_LOOK_BACK);
|
||||
|
|
@ -487,19 +461,6 @@ int next_timer_expiry()
|
|||
if (eb)
|
||||
ret = eb->key;
|
||||
|
||||
#ifdef USE_THREAD
|
||||
if (!eb_is_empty(&tg_ctx->timers)) {
|
||||
HA_RWLOCK_RDLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
eb = eb32_lookup_ge(&tg_ctx->timers, now_ms - TIMER_LOOK_BACK);
|
||||
if (!eb)
|
||||
eb = eb32_first(&tg_ctx->timers);
|
||||
if (eb)
|
||||
key = eb->key;
|
||||
HA_RWLOCK_RDUNLOCK(TASK_WQ_LOCK, &wq_lock);
|
||||
if (eb)
|
||||
ret = tick_first(ret, key);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -652,6 +613,19 @@ unsigned int run_tasks_from_lists(unsigned int budgets[])
|
|||
goto next;
|
||||
}
|
||||
|
||||
if (state & TASK_WOKEN_WQ) {
|
||||
/* We should add this task to our wait queue */
|
||||
task_queue(t);
|
||||
/*
|
||||
* If this is the only reason the task got scheduled,
|
||||
* then we don't actually have ot run it.
|
||||
*/
|
||||
if ((state & TASK_WOKEN_ANY) == TASK_WOKEN_WQ) {
|
||||
task_drop_running(t, 0);
|
||||
goto next;
|
||||
}
|
||||
state &= ~TASK_WOKEN_WQ;
|
||||
}
|
||||
/* OK now the task or tasklet is well alive and is going to be run */
|
||||
if (state & TASK_F_TASKLET) {
|
||||
/* this is a tasklet */
|
||||
|
|
@ -680,7 +654,8 @@ unsigned int run_tasks_from_lists(unsigned int budgets[])
|
|||
__task_free(t);
|
||||
}
|
||||
else {
|
||||
task_queue(t);
|
||||
if (__task_get_current_owner(t->tid) == tid)
|
||||
task_queue(t);
|
||||
task_drop_running(t, 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -949,13 +924,6 @@ void mworker_cleantasks()
|
|||
tmp_rq = eb32_next(tmp_rq);
|
||||
task_destroy(t);
|
||||
}
|
||||
/* cleanup the timers queue */
|
||||
tmp_wq = eb32_first(&tg_ctx->timers);
|
||||
while (tmp_wq) {
|
||||
t = eb32_entry(tmp_wq, struct task, wq);
|
||||
tmp_wq = eb32_next(tmp_wq);
|
||||
task_destroy(t);
|
||||
}
|
||||
#endif
|
||||
/* clean the per thread run queue */
|
||||
for (i = 0; i < global.nbthread; i++) {
|
||||
|
|
@ -980,9 +948,6 @@ static void init_task()
|
|||
{
|
||||
int i, q;
|
||||
|
||||
for (i = 0; i < MAX_TGROUPS; i++)
|
||||
memset(&ha_tgroup_ctx[i].timers, 0, sizeof(ha_tgroup_ctx[i].timers));
|
||||
|
||||
for (i = 0; i < MAX_THREADS; i++) {
|
||||
for (q = 0; q < TL_CLASSES; q++)
|
||||
LIST_INIT(&ha_thread_ctx[i].tasklets[q]);
|
||||
|
|
|
|||
|
|
@ -4198,7 +4198,7 @@ int check_server_tcpcheck(struct server *srv)
|
|||
}
|
||||
srv->check.tcpcheck->rs = rs;
|
||||
srv->check.tcpcheck->flags = rs->conf.flags;
|
||||
|
||||
srv->check.type = PR_O2_TCPCHK_CHK;
|
||||
err_code = check_tcpcheck_ruleset(srv->proxy, rs);
|
||||
}
|
||||
|
||||
|
|
@ -5970,7 +5970,7 @@ static int srv_parse_healthcheck(char **args, int *cur_arg, struct proxy *curpx,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (srv->check.tcpcheck->healthcheck) {
|
||||
if (srv->check.tcpcheck && srv->check.tcpcheck->healthcheck) {
|
||||
/* a healthcheck section was already defined. Replace it */
|
||||
ha_free(&srv->check.tcpcheck->healthcheck);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -434,7 +434,6 @@ const char *lock_label(enum lock_label label)
|
|||
{
|
||||
switch (label) {
|
||||
case TASK_RQ_LOCK: return "TASK_RQ";
|
||||
case TASK_WQ_LOCK: return "TASK_WQ";
|
||||
case LISTENER_LOCK: return "LISTENER";
|
||||
case PROXY_LOCK: return "PROXY";
|
||||
case SERVER_LOCK: return "SERVER";
|
||||
|
|
@ -1667,7 +1666,9 @@ void thread_detect_count(void)
|
|||
char *err __maybe_unused;
|
||||
int thr_forced = 0;
|
||||
int tgrp_forced = 0;
|
||||
#ifdef USE_THREAD
|
||||
int cpus_detected = 0;
|
||||
#endif
|
||||
|
||||
thr_min = 1; thr_max = MAX_THREADS;
|
||||
grp_min = 1; grp_max = MAX_TGROUPS;
|
||||
|
|
@ -1810,6 +1811,7 @@ void thread_detect_count(void)
|
|||
if (!global.nbtgroups)
|
||||
global.nbtgroups = grp_min;
|
||||
|
||||
#ifdef USE_THREAD
|
||||
if (tgrp_forced && !thr_forced && !cpu_map_in_conf &&
|
||||
(!global.thread_limit || (global.nbthread < global.thread_limit)) &&
|
||||
global.nbthread < MIN(cpus_detected, thread_cpus_enabled_at_boot) &&
|
||||
|
|
@ -1828,6 +1830,7 @@ void thread_detect_count(void)
|
|||
ha_notice("%d usable CPUs detected but 'nbthread' forced to %d (%d%%). Remove 'nbthread' for optimal tuning.\n",
|
||||
nbcpus, global.nbthread, global.nbthread * 100 / nbcpus);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (global.nbthread > global.maxthrpertgroup * global.nbtgroups) {
|
||||
ha_diag_warning("nbthread too large or not set, found %d CPUs, limiting to %d threads (maximum is %d per thread group and %d groups). Please set nbthreads and/or increase thread-groups in the global section to silence this warning.\n",
|
||||
|
|
|
|||
|
|
@ -350,6 +350,22 @@ static void xprt_qmux_close(struct connection *conn, void *xprt_ctx)
|
|||
pool_free(xprt_qmux_ctx_pool, ctx);
|
||||
}
|
||||
|
||||
/* Retrieve the ssl_sock_ctx of the lower layer. Contrary to most XPRTs, QMux
|
||||
* is stacked on top of the SSL layer (and not the other way around), so during
|
||||
* the QMux handshake conn->xprt points to xprt_qmux. Without this delegation,
|
||||
* conn_get_ssl_sock_ctx() would return NULL for any code inspecting the SSL
|
||||
* layer of the connection (sample fetches, logging, info callback, ...) while
|
||||
* the QMux handshake is in progress.
|
||||
*/
|
||||
static struct ssl_sock_ctx *xprt_qmux_get_ssl_sock_ctx(struct connection *conn)
|
||||
{
|
||||
struct xprt_qmux_ctx *ctx = conn->xprt_ctx;
|
||||
|
||||
if (ctx && ctx->ops_lower == xprt_get(XPRT_SSL))
|
||||
return ctx->ctx_lower;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int xprt_qmux_get_alpn(const struct connection *conn, void *xprt_ctx,
|
||||
const char **str, int *len)
|
||||
{
|
||||
|
|
@ -371,6 +387,7 @@ struct xprt_ops xprt_qmux = {
|
|||
.start = xprt_qmux_start,
|
||||
.close = xprt_qmux_close,
|
||||
.get_alpn = xprt_qmux_get_alpn,
|
||||
.get_ssl_sock_ctx = xprt_qmux_get_ssl_sock_ctx,
|
||||
.name = "qmux",
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue