Compare commits
33 Commits
master
...
20250526-q
Author | SHA1 | Date | |
---|---|---|---|
|
8f79ac238c | ||
|
c6d79ebede | ||
|
b57eea3bf3 | ||
|
979b41fe5f | ||
|
f363190bd7 | ||
|
21e2c2e9dc | ||
|
ad5b52e07a | ||
|
acbb56eac6 | ||
|
cfa82a8606 | ||
|
7fc69b202a | ||
|
a0d2a4fcfd | ||
|
74a35d94ea | ||
|
c7accba1db | ||
|
9d515eb7f3 | ||
|
fb5ce7fd02 | ||
|
1f4cb27157 | ||
|
bfa6885c2f | ||
|
7d3f60f079 | ||
|
73054af3fc | ||
|
d5a2e9b44e | ||
|
5517b9c521 | ||
|
e4f7a1fcde | ||
|
7f89f1e451 | ||
|
b99ce3c985 | ||
|
abd35e9b17 | ||
|
d5669868e6 | ||
|
7265955aa4 | ||
|
cc5ae9a29b | ||
|
e46e8e2bd3 | ||
|
efbac62ded | ||
|
02f85dd6af | ||
|
e124a66c90 | ||
|
438bb236cf |
41
CHANGELOG
41
CHANGELOG
@ -1,47 +1,6 @@
|
|||||||
ChangeLog :
|
ChangeLog :
|
||||||
===========
|
===========
|
||||||
|
|
||||||
2025/06/11 : 3.3-dev1
|
|
||||||
- BUILD: tools: properly define ha_dump_backtrace() to avoid a build warning
|
|
||||||
- DOC: config: Fix a typo in 2.7 (Name format for maps and ACLs)
|
|
||||||
- REGTESTS: Do not use REQUIRE_VERSION for HAProxy 2.5+ (5)
|
|
||||||
- REGTESTS: Remove REQUIRE_VERSION=2.3 from all tests
|
|
||||||
- REGTESTS: Remove REQUIRE_VERSION=2.4 from all tests
|
|
||||||
- REGTESTS: Remove tests with REQUIRE_VERSION_BELOW=2.4
|
|
||||||
- REGTESTS: Remove support for REQUIRE_VERSION and REQUIRE_VERSION_BELOW
|
|
||||||
- MINOR: server: group postinit server tasks under _srv_postparse()
|
|
||||||
- MINOR: stats: add stat_col flags
|
|
||||||
- MINOR: stats: add ME_NEW_COMMON() helper
|
|
||||||
- MINOR: proxy: collect per-capability stat in proxy_cond_disable()
|
|
||||||
- MINOR: proxy: add a true list containing all proxies
|
|
||||||
- MINOR: log: only run postcheck_log_backend() checks on backend
|
|
||||||
- MEDIUM: proxy: use global proxy list for REGISTER_POST_PROXY_CHECK() hook
|
|
||||||
- MEDIUM: server: automatically add server to proxy list in new_server()
|
|
||||||
- MEDIUM: server: add and use srv_init() function
|
|
||||||
- BUG/MAJOR: leastconn: Protect tree_elt with the lbprm lock
|
|
||||||
- BUG/MEDIUM: check: Requeue healthchecks on I/O events to handle check timeout
|
|
||||||
- CLEANUP: applet: Update comment for applet_put* functions
|
|
||||||
- DEBUG: check: Add the healthcheck's expiration date in the trace messags
|
|
||||||
- BUG/MINOR: mux-spop: Fix null-pointer deref on SPOP stream allocation failure
|
|
||||||
- CLEANUP: sink: remove useless cleanup in sink_new_from_logger()
|
|
||||||
- MAJOR: counters: add shared counters base infrastructure
|
|
||||||
- MINOR: counters: add shared counters helpers to get and drop shared pointers
|
|
||||||
- MINOR: counters: add common struct and flags to {fe,be}_counters_shared
|
|
||||||
- MEDIUM: counters: manage shared counters using dedicated helpers
|
|
||||||
- CLEANUP: counters: merge some common counters between {fe,be}_counters_shared
|
|
||||||
- MINOR: counters: add local-only internal rates to compute some maxes
|
|
||||||
- MAJOR: counters: dispatch counters over thread groups
|
|
||||||
- BUG/MEDIUM: cli: Properly parse empty lines and avoid crashed
|
|
||||||
- BUG/MINOR: config: emit warning for empty args only in discovery mode
|
|
||||||
- BUG/MINOR: config: fix arg number reported on empty arg warning
|
|
||||||
- BUG/MINOR: quic: Missing SSL session object freeing
|
|
||||||
- MINOR: applet: Add API functions to manipulate input and output buffers
|
|
||||||
- MINOR: applet: Add API functions to get data from the input buffer
|
|
||||||
- CLEANUP: applet: Simplify a bit comments for applet_put* functions
|
|
||||||
- MEDIUM: hlua: Update TCP applet functions to use the new applet API
|
|
||||||
- BUG/MEDIUM: fd: Use the provided tgid in fd_insert() to get tgroup_info
|
|
||||||
- BUG/MINIR: h1: Fix doc of 'accept-unsafe-...-request' about URI parsing
|
|
||||||
|
|
||||||
2025/05/28 : 3.3-dev0
|
2025/05/28 : 3.3-dev0
|
||||||
- MINOR: version: mention that it's development again
|
- MINOR: version: mention that it's development again
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
Configuration Manual
|
Configuration Manual
|
||||||
----------------------
|
----------------------
|
||||||
version 3.3
|
version 3.3
|
||||||
2025/06/11
|
2025/05/28
|
||||||
|
|
||||||
|
|
||||||
This document covers the configuration language as implemented in the version
|
This document covers the configuration language as implemented in the version
|
||||||
@ -9077,14 +9077,11 @@ no option accept-unsafe-violations-in-http-request
|
|||||||
|
|
||||||
* In H1 only, NULL character in header value will be accepted;
|
* In H1 only, NULL character in header value will be accepted;
|
||||||
|
|
||||||
* In H1 only, characters above 127 in the URI will be accepted. The list of
|
* The list of characters allowed to appear in a URI is well defined by
|
||||||
characters allowed to appear in a URI is well defined by RFC3986, and
|
RFC3986, and chars 0-31, 32 (space), 34 ('"'), 60 ('<'), 62 ('>'), 92
|
||||||
chars 0-31, 32 (space), 34 ('"'), 60 ('<'), 62 ('>'), 92 ('\'), 94 ('^'),
|
('\'), 94 ('^'), 96 ('`'), 123 ('{'), 124 ('|'), 125 ('}'), 127 (delete)
|
||||||
96 ('`'), 123 ('{'), 124 ('|'), 125 ('}'), 127 (delete) and anything
|
and anything above are normally not allowed. But here, in H1 only,
|
||||||
above are normally not allowed. In H1, all character between (0..32) and
|
HAProxy will only block a number of them (0..32, 127);
|
||||||
127 will always be blocked. All characters above 127 (excluded) will also
|
|
||||||
be blocked, except when this option is enabled. Other characters
|
|
||||||
(33..126) will not be checked at all.
|
|
||||||
|
|
||||||
* In H1 and H2, URLs containing fragment references ('#' after the path)
|
* In H1 and H2, URLs containing fragment references ('#' after the path)
|
||||||
will be accepted;
|
will be accepted;
|
||||||
|
@ -282,92 +282,6 @@ static inline void applet_expect_data(struct appctx *appctx)
|
|||||||
se_fl_clr(appctx->sedesc, SE_FL_EXP_NO_DATA);
|
se_fl_clr(appctx->sedesc, SE_FL_EXP_NO_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the buffer containing data pushed to the applet by the stream. For
|
|
||||||
* applets using its own buffers it is the appctx input buffer. For legacy
|
|
||||||
* applet, it is the output channel buffer.
|
|
||||||
*/
|
|
||||||
static inline struct buffer *applet_get_inbuf(struct appctx *appctx)
|
|
||||||
{
|
|
||||||
if (appctx->flags & APPCTX_FL_INOUT_BUFS)
|
|
||||||
return &appctx->inbuf;
|
|
||||||
else
|
|
||||||
return sc_ob(appctx_sc(appctx));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the buffer containing data pushed by the applets to the stream. For
|
|
||||||
* applets using its own buffer it is the appctx output buffer. For legacy
|
|
||||||
* applet, it is the input channel buffer.
|
|
||||||
*/
|
|
||||||
static inline struct buffer *applet_get_outbuf(struct appctx *appctx)
|
|
||||||
{
|
|
||||||
if (appctx->flags & APPCTX_FL_INOUT_BUFS)
|
|
||||||
return &appctx->outbuf;
|
|
||||||
else
|
|
||||||
return sc_ib(appctx_sc(appctx));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the amount of data in the input buffer (see applet_get_inbuf) */
|
|
||||||
static inline size_t applet_input_data(const struct appctx *appctx)
|
|
||||||
{
|
|
||||||
if (appctx->flags & APPCTX_FL_INOUT_BUFS)
|
|
||||||
return b_data(&appctx->inbuf);
|
|
||||||
else
|
|
||||||
return co_data(sc_oc(appctx_sc(appctx)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Skips <len> bytes from the input buffer (see applet_get_inbuf).
|
|
||||||
*
|
|
||||||
* This is useful when data have been read directly from the buffer. It is
|
|
||||||
* illegal to call this function with <len> causing a wrapping at the end of the
|
|
||||||
* buffer. It's the caller's responsibility to ensure that <len> is never larger
|
|
||||||
* than available ouput data.
|
|
||||||
*/
|
|
||||||
static inline void applet_skip_input(struct appctx *appctx, size_t len)
|
|
||||||
{
|
|
||||||
if (appctx->flags & APPCTX_FL_INOUT_BUFS)
|
|
||||||
b_del(&appctx->inbuf, len);
|
|
||||||
else
|
|
||||||
co_skip(sc_oc(appctx_sc(appctx)), len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Removes all bytes from the input buffer (see applet_get_inbuf).
|
|
||||||
*/
|
|
||||||
static inline void applet_reset_input(struct appctx *appctx)
|
|
||||||
{
|
|
||||||
if (appctx->flags & APPCTX_FL_INOUT_BUFS) {
|
|
||||||
b_reset(&appctx->inbuf);
|
|
||||||
applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
co_skip(sc_oc(appctx_sc(appctx)), co_data(sc_oc(appctx_sc(appctx))));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the amout of space available at the output buffer (see applet_get_outbuf).
|
|
||||||
*/
|
|
||||||
static inline size_t applet_output_room(const struct appctx *appctx)
|
|
||||||
{
|
|
||||||
if (appctx->flags & APPCTX_FL_INOUT_BUFS)
|
|
||||||
return b_room(&appctx->outbuf);
|
|
||||||
else
|
|
||||||
return channel_recv_max(sc_ic(appctx_sc(appctx)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Indicates that the applet have more data to deliver and it needs more room in
|
|
||||||
* the output buffer to do so (see applet_get_outbuf).
|
|
||||||
*
|
|
||||||
* For applets using its own buffers, <room_needed> is not used and only
|
|
||||||
* <appctx> flags are updated. For legacy applets, the amount of free space
|
|
||||||
* required must be specified. In this last case, it is the caller
|
|
||||||
* responsibility to be sure <room_needed> is valid.
|
|
||||||
*/
|
|
||||||
static inline void applet_need_room(struct appctx *appctx, size_t room_needed)
|
|
||||||
{
|
|
||||||
if (appctx->flags & APPCTX_FL_INOUT_BUFS)
|
|
||||||
applet_have_more_data(appctx);
|
|
||||||
else
|
|
||||||
sc_need_room(appctx_sc(appctx), room_needed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Should only be used via wrappers applet_putchk() / applet_putchk_stress(). */
|
/* Should only be used via wrappers applet_putchk() / applet_putchk_stress(). */
|
||||||
static inline int _applet_putchk(struct appctx *appctx, struct buffer *chunk,
|
static inline int _applet_putchk(struct appctx *appctx, struct buffer *chunk,
|
||||||
int stress)
|
int stress)
|
||||||
@ -404,7 +318,8 @@ static inline int _applet_putchk(struct appctx *appctx, struct buffer *chunk,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* writes chunk <chunk> into the applet output buffer (see applet_get_outbuf).
|
/* writes chunk <chunk> into the applet's output buffer if it uses its own
|
||||||
|
* bufferx or into the input channel of the stream attached to this applet.
|
||||||
*
|
*
|
||||||
* Returns the number of written bytes on success or -1 on error (lake of space,
|
* Returns the number of written bytes on success or -1 on error (lake of space,
|
||||||
* shutdown, invalid call...)
|
* shutdown, invalid call...)
|
||||||
@ -420,7 +335,8 @@ static inline int applet_putchk_stress(struct appctx *appctx, struct buffer *chu
|
|||||||
return _applet_putchk(appctx, chunk, 1);
|
return _applet_putchk(appctx, chunk, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* writes <len> chars from <blk> into the applet output buffer (see applet_get_outbuf).
|
/* writes <len> chars from <blk> into the applet's output buffer if it uses its own
|
||||||
|
* bufferx or into the input channel of the stream attached to this applet.
|
||||||
*
|
*
|
||||||
* Returns the number of written bytes on success or -1 on error (lake of space,
|
* Returns the number of written bytes on success or -1 on error (lake of space,
|
||||||
* shutdown, invalid call...)
|
* shutdown, invalid call...)
|
||||||
@ -455,8 +371,9 @@ static inline int applet_putblk(struct appctx *appctx, const char *blk, int len)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* writes chars from <str> up to the trailing zero (excluded) into the applet
|
/* writes chars from <str> up to the trailing zero (excluded) into the applet's
|
||||||
* output buffer (see applet_get_outbuf).
|
* output buffer if it uses its own bufferx or into the input channel of the
|
||||||
|
* stream attached to this applet.
|
||||||
*
|
*
|
||||||
* Returns the number of written bytes on success or -1 on error (lake of space,
|
* Returns the number of written bytes on success or -1 on error (lake of space,
|
||||||
* shutdown, invalid call...)
|
* shutdown, invalid call...)
|
||||||
@ -492,7 +409,8 @@ static inline int applet_putstr(struct appctx *appctx, const char *str)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* writes character <chr> into the applet's output buffer (see applet_get_outbuf).
|
/* writes character <chr> into the applet's output buffer if it uses its own
|
||||||
|
* bufferx or into the input channel of the stream attached to this applet.
|
||||||
*
|
*
|
||||||
* Returns the number of written bytes on success or -1 on error (lake of space,
|
* Returns the number of written bytes on success or -1 on error (lake of space,
|
||||||
* shutdown, invalid call...)
|
* shutdown, invalid call...)
|
||||||
@ -528,283 +446,6 @@ static inline int applet_putchr(struct appctx *appctx, char chr)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int applet_may_get(const struct appctx *appctx, size_t len)
|
|
||||||
{
|
|
||||||
if (appctx->flags & APPCTX_FL_INOUT_BUFS) {
|
|
||||||
if (len > b_data(&appctx->inbuf)) {
|
|
||||||
if (se_fl_test(appctx->sedesc, SE_FL_SHW))
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const struct stconn *sc = appctx_sc(appctx);
|
|
||||||
|
|
||||||
if ((sc->flags & SC_FL_SHUT_DONE) || len > co_data(sc_oc(sc))) {
|
|
||||||
if (sc->flags & (SC_FL_SHUT_DONE|SC_FL_SHUT_WANTED))
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
/* Gets one char from the applet input buffer (see appet_get_inbuf),
|
|
||||||
*
|
|
||||||
* Return values :
|
|
||||||
* 1 : number of bytes read, equal to requested size.
|
|
||||||
* =0 : not enough data available. <c> is left undefined.
|
|
||||||
* <0 : no more bytes readable because output is shut.
|
|
||||||
*
|
|
||||||
* The status of the corresponding buffer is not changed. The caller must call
|
|
||||||
* applet_skip_input() to update it.
|
|
||||||
*/
|
|
||||||
static inline int applet_getchar(const struct appctx *appctx, char *c)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = applet_may_get(appctx, 1);
|
|
||||||
if (ret <= 0)
|
|
||||||
return ret;
|
|
||||||
*c = ((appctx->flags & APPCTX_FL_INOUT_BUFS)
|
|
||||||
? *(b_head(&appctx->inbuf))
|
|
||||||
: *(co_head(sc_oc(appctx_sc(appctx)))));
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copies one full block of data from the applet input buffer (see
|
|
||||||
* appet_get_inbuf).
|
|
||||||
*
|
|
||||||
* <len> bytes are capied, starting at the offset <offset>.
|
|
||||||
*
|
|
||||||
* Return values :
|
|
||||||
* >0 : number of bytes read, equal to requested size.
|
|
||||||
* =0 : not enough data available. <blk> is left undefined.
|
|
||||||
* <0 : no more bytes readable because output is shut.
|
|
||||||
*
|
|
||||||
* The status of the corresponding buffer is not changed. The caller must call
|
|
||||||
* applet_skip_input() to update it.
|
|
||||||
*/
|
|
||||||
static inline int applet_getblk(const struct appctx *appctx, char *blk, int len, int offset)
|
|
||||||
{
|
|
||||||
const struct buffer *buf;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = applet_may_get(appctx, len+offset);
|
|
||||||
if (ret <= 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
buf = ((appctx->flags & APPCTX_FL_INOUT_BUFS)
|
|
||||||
? &appctx->inbuf
|
|
||||||
: sc_ob(appctx_sc(appctx)));
|
|
||||||
return b_getblk(buf, blk, len, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Gets one text block representing a word from the applet input buffer (see
|
|
||||||
* appet_get_inbuf).
|
|
||||||
*
|
|
||||||
* The separator is waited for as long as some data can still be received and the
|
|
||||||
* destination is not full. Otherwise, the string may be returned as is, without
|
|
||||||
* the separator.
|
|
||||||
*
|
|
||||||
* Return values :
|
|
||||||
* >0 : number of bytes read. Includes the separator if present before len or end.
|
|
||||||
* =0 : no separator before end found. <str> is left undefined.
|
|
||||||
* <0 : no more bytes readable because output is shut.
|
|
||||||
*
|
|
||||||
* The status of the corresponding buffer is not changed. The caller must call
|
|
||||||
* applet_skip_input() to update it.
|
|
||||||
*/
|
|
||||||
static inline int applet_getword(const struct appctx *appctx, char *str, int len, char sep)
|
|
||||||
{
|
|
||||||
const struct buffer *buf;
|
|
||||||
char *p;
|
|
||||||
size_t input, max = len;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
ret = applet_may_get(appctx, 1);
|
|
||||||
if (ret <= 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (appctx->flags & APPCTX_FL_INOUT_BUFS) {
|
|
||||||
buf = &appctx->inbuf;
|
|
||||||
input = b_data(buf);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
struct stconn *sc = appctx_sc(appctx);
|
|
||||||
|
|
||||||
buf = sc_ob(sc);
|
|
||||||
input = co_data(sc_oc(sc));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (max > input) {
|
|
||||||
max = input;
|
|
||||||
str[max-1] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p = b_head(buf);
|
|
||||||
|
|
||||||
while (max) {
|
|
||||||
*str++ = *p;
|
|
||||||
ret++;
|
|
||||||
max--;
|
|
||||||
if (*p == sep)
|
|
||||||
goto out;
|
|
||||||
p = b_next(buf, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appctx->flags & APPCTX_FL_INOUT_BUFS) {
|
|
||||||
if (ret < len && (ret < input || b_room(buf)) &&
|
|
||||||
!se_fl_test(appctx->sedesc, SE_FL_SHW))
|
|
||||||
ret = 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
struct stconn *sc = appctx_sc(appctx);
|
|
||||||
|
|
||||||
if (ret < len && (ret < input || channel_may_recv(sc_oc(sc))) &&
|
|
||||||
!(sc->flags & (SC_FL_SHUT_DONE|SC_FL_SHUT_WANTED)))
|
|
||||||
ret = 0;
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
if (max)
|
|
||||||
*str = 0;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Gets one text block representing a line from the applet input buffer (see
|
|
||||||
* appet_get_inbuf).
|
|
||||||
*
|
|
||||||
* The '\n' is waited for as long as some data can still be received and the
|
|
||||||
* destination is not full. Otherwise, the string may be returned as is, without
|
|
||||||
* the '\n'.
|
|
||||||
*
|
|
||||||
* Return values :
|
|
||||||
* >0 : number of bytes read. Includes the \n if present before len or end.
|
|
||||||
* =0 : no '\n' before end found. <str> is left undefined.
|
|
||||||
* <0 : no more bytes readable because output is shut.
|
|
||||||
*
|
|
||||||
* The status of the corresponding buffer is not changed. The caller must call
|
|
||||||
* applet_skip_input() to update it.
|
|
||||||
*/
|
|
||||||
static inline int applet_getline(const struct appctx *appctx, char *str, int len)
|
|
||||||
{
|
|
||||||
return applet_getword(appctx, str, len, '\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Gets one or two blocks of data at once from the applet input buffer (see appet_get_inbuf),
|
|
||||||
*
|
|
||||||
* Data are not copied.
|
|
||||||
*
|
|
||||||
* Return values :
|
|
||||||
* >0 : number of blocks filled (1 or 2). blk1 is always filled before blk2.
|
|
||||||
* =0 : not enough data available. <blk*> are left undefined.
|
|
||||||
* <0 : no more bytes readable because output is shut.
|
|
||||||
*
|
|
||||||
* The status of the corresponding buffer is not changed. The caller must call
|
|
||||||
* applet_skip_input() to update it.
|
|
||||||
*/
|
|
||||||
static inline int applet_getblk_nc(const struct appctx *appctx, const char **blk1, size_t *len1, const char **blk2, size_t *len2)
|
|
||||||
{
|
|
||||||
const struct buffer *buf;
|
|
||||||
size_t max;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = applet_may_get(appctx, 1);
|
|
||||||
if (ret <= 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (appctx->flags & APPCTX_FL_INOUT_BUFS) {
|
|
||||||
buf = &appctx->inbuf;
|
|
||||||
max = b_data(buf);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
struct stconn *sc = appctx_sc(appctx);
|
|
||||||
|
|
||||||
buf = sc_ob(sc);
|
|
||||||
max = co_data(sc_oc(sc));
|
|
||||||
}
|
|
||||||
|
|
||||||
return b_getblk_nc(buf, blk1, len1, blk2, len2, 0, max);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Gets one or two blocks of text representing a word from the applet input
|
|
||||||
* buffer (see appet_get_inbuf).
|
|
||||||
*
|
|
||||||
* Data are not copied. The separator is waited for as long as some data can
|
|
||||||
* still be received and the destination is not full. Otherwise, the string may
|
|
||||||
* be returned as is, without the separator.
|
|
||||||
*
|
|
||||||
* Return values :
|
|
||||||
* >0 : number of bytes read. Includes the separator if present before len or end.
|
|
||||||
* =0 : no separator before end found. <str> is left undefined.
|
|
||||||
* <0 : no more bytes readable because output is shut.
|
|
||||||
*
|
|
||||||
* The status of the corresponding buffer is not changed. The caller must call
|
|
||||||
* applet_skip_input() to update it.
|
|
||||||
*/
|
|
||||||
static inline int applet_getword_nc(const struct appctx *appctx, const char **blk1, size_t *len1, const char **blk2, size_t *len2, char sep)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
size_t l;
|
|
||||||
|
|
||||||
ret = applet_getblk_nc(appctx, blk1, len1, blk2, len2);
|
|
||||||
if (unlikely(ret <= 0))
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
for (l = 0; l < *len1 && (*blk1)[l] != sep; l++);
|
|
||||||
if (l < *len1 && (*blk1)[l] == sep) {
|
|
||||||
*len1 = l + 1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret >= 2) {
|
|
||||||
for (l = 0; l < *len2 && (*blk2)[l] != sep; l++);
|
|
||||||
if (l < *len2 && (*blk2)[l] == sep) {
|
|
||||||
*len2 = l + 1;
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we have found no LF and the buffer is full or the SC is shut, then
|
|
||||||
* the resulting string is made of the concatenation of the pending
|
|
||||||
* blocks (1 or 2).
|
|
||||||
*/
|
|
||||||
if (appctx->flags & APPCTX_FL_INOUT_BUFS) {
|
|
||||||
if (b_full(&appctx->inbuf) || se_fl_test(appctx->sedesc, SE_FL_SHW))
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
struct stconn *sc = appctx_sc(appctx);
|
|
||||||
|
|
||||||
if (!channel_may_recv(sc_oc(sc)) || sc->flags & (SC_FL_SHUT_DONE|SC_FL_SHUT_WANTED))
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No LF yet and not shut yet */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Gets one or two blocks of text representing a line from the applet input
|
|
||||||
* buffer (see appet_get_inbuf).
|
|
||||||
*
|
|
||||||
* Data are not copied. The '\n' is waited for as long as some data can still be
|
|
||||||
* received and the destination is not full. Otherwise, the string may be
|
|
||||||
* returned as is, without the '\n'.
|
|
||||||
*
|
|
||||||
* Return values :
|
|
||||||
* >0 : number of bytes read. Includes the \n if present before len or end.
|
|
||||||
* =0 : no '\n' before end found. <str> is left undefined.
|
|
||||||
* <0 : no more bytes readable because output is shut.
|
|
||||||
*
|
|
||||||
* The status of the corresponding buffer is not changed. The caller must call
|
|
||||||
* applet_skip_input() to update it.
|
|
||||||
*/
|
|
||||||
static inline int applet_getline_nc(const struct appctx *appctx, const char **blk1, size_t *len1, const char **blk2, size_t *len2)
|
|
||||||
{
|
|
||||||
return applet_getword_nc(appctx, blk1, len1, blk2, len2, '\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* _HAPROXY_APPLET_H */
|
#endif /* _HAPROXY_APPLET_H */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -499,7 +499,6 @@ static inline long fd_clr_running(int fd)
|
|||||||
static inline void fd_insert(int fd, void *owner, void (*iocb)(int fd), int tgid, unsigned long thread_mask)
|
static inline void fd_insert(int fd, void *owner, void (*iocb)(int fd), int tgid, unsigned long thread_mask)
|
||||||
{
|
{
|
||||||
extern void sock_conn_iocb(int);
|
extern void sock_conn_iocb(int);
|
||||||
struct tgroup_info *tginfo = &ha_tgroup_info[tgid - 1];
|
|
||||||
int newstate;
|
int newstate;
|
||||||
|
|
||||||
/* conn_fd_handler should support edge-triggered FDs */
|
/* conn_fd_handler should support edge-triggered FDs */
|
||||||
@ -529,7 +528,7 @@ static inline void fd_insert(int fd, void *owner, void (*iocb)(int fd), int tgid
|
|||||||
BUG_ON(fdtab[fd].state != 0);
|
BUG_ON(fdtab[fd].state != 0);
|
||||||
BUG_ON(tgid < 1 || tgid > MAX_TGROUPS);
|
BUG_ON(tgid < 1 || tgid > MAX_TGROUPS);
|
||||||
|
|
||||||
thread_mask &= tginfo->threads_enabled;
|
thread_mask &= tg->threads_enabled;
|
||||||
BUG_ON(thread_mask == 0);
|
BUG_ON(thread_mask == 0);
|
||||||
|
|
||||||
fd_claim_tgid(fd, tgid);
|
fd_claim_tgid(fd, tgid);
|
||||||
|
@ -144,6 +144,7 @@ struct qc_stream_rxbuf {
|
|||||||
struct qcs {
|
struct qcs {
|
||||||
struct qcc *qcc;
|
struct qcc *qcc;
|
||||||
struct sedesc *sd;
|
struct sedesc *sd;
|
||||||
|
struct session *sess;
|
||||||
uint32_t flags; /* QC_SF_* */
|
uint32_t flags; /* QC_SF_* */
|
||||||
enum qcs_state st; /* QC_SS_* state */
|
enum qcs_state st; /* QC_SS_* state */
|
||||||
void *ctx; /* app-ops context */
|
void *ctx; /* app-ops context */
|
||||||
@ -202,7 +203,7 @@ struct qcc_app_ops {
|
|||||||
/* Initialize <qcs> stream app context or leave it to NULL if rejected. */
|
/* Initialize <qcs> stream app context or leave it to NULL if rejected. */
|
||||||
int (*attach)(struct qcs *qcs, void *conn_ctx);
|
int (*attach)(struct qcs *qcs, void *conn_ctx);
|
||||||
|
|
||||||
/* Convert received HTTP payload to HTX. Returns amount of decoded bytes from <b> or a negative error code. */
|
/* Convert received HTTP payload to HTX. */
|
||||||
ssize_t (*rcv_buf)(struct qcs *qcs, struct buffer *b, int fin);
|
ssize_t (*rcv_buf)(struct qcs *qcs, struct buffer *b, int fin);
|
||||||
|
|
||||||
/* Convert HTX to HTTP payload for sending. */
|
/* Convert HTX to HTTP payload for sending. */
|
||||||
@ -232,7 +233,7 @@ struct qcc_app_ops {
|
|||||||
|
|
||||||
#define QC_CF_ERRL 0x00000001 /* fatal error detected locally, connection should be closed soon */
|
#define QC_CF_ERRL 0x00000001 /* fatal error detected locally, connection should be closed soon */
|
||||||
#define QC_CF_ERRL_DONE 0x00000002 /* local error properly handled, connection can be released */
|
#define QC_CF_ERRL_DONE 0x00000002 /* local error properly handled, connection can be released */
|
||||||
#define QC_CF_IS_BACK 0x00000004 /* backend side */
|
/* unused 0x00000004 */
|
||||||
#define QC_CF_CONN_FULL 0x00000008 /* no stream buffers available on connection */
|
#define QC_CF_CONN_FULL 0x00000008 /* no stream buffers available on connection */
|
||||||
/* unused 0x00000010 */
|
/* unused 0x00000010 */
|
||||||
#define QC_CF_ERR_CONN 0x00000020 /* fatal error reported by transport layer */
|
#define QC_CF_ERR_CONN 0x00000020 /* fatal error reported by transport layer */
|
||||||
|
@ -2020,13 +2020,9 @@ int connect_server(struct stream *s)
|
|||||||
srv_conn->ctx = s->scb;
|
srv_conn->ctx = s->scb;
|
||||||
|
|
||||||
#if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
|
#if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
|
||||||
/* Delay mux initialization if SSL and ALPN/NPN is set. Note
|
|
||||||
* that this is skipped in TCP mode as we only want mux-pt
|
|
||||||
* anyway.
|
|
||||||
*/
|
|
||||||
if (!srv ||
|
if (!srv ||
|
||||||
(srv->use_ssl != 1 || (!(srv->ssl_ctx.alpn_str) && !(srv->ssl_ctx.npn_str)) ||
|
(srv->use_ssl != 1 || (!(srv->ssl_ctx.alpn_str) && !(srv->ssl_ctx.npn_str)) ||
|
||||||
!IS_HTX_STRM(s)))
|
srv->mux_proto || !IS_HTX_STRM(s)))
|
||||||
#endif
|
#endif
|
||||||
init_mux = 1;
|
init_mux = 1;
|
||||||
|
|
||||||
|
@ -4115,13 +4115,6 @@ out_uri_auth_compat:
|
|||||||
int mode = conn_pr_mode_to_proto_mode(curproxy->mode);
|
int mode = conn_pr_mode_to_proto_mode(curproxy->mode);
|
||||||
const struct mux_proto_list *mux_ent;
|
const struct mux_proto_list *mux_ent;
|
||||||
|
|
||||||
if (srv_is_quic(newsrv)) {
|
|
||||||
if (!newsrv->mux_proto) {
|
|
||||||
/* Force QUIC as mux-proto on server with quic addresses, similarly to bind on FE side. */
|
|
||||||
newsrv->mux_proto = get_mux_proto(ist("quic"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!newsrv->mux_proto)
|
if (!newsrv->mux_proto)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
39
src/h3.c
39
src/h3.c
@ -1385,13 +1385,9 @@ static ssize_t h3_parse_settings_frm(struct h3c *h3c, const struct buffer *buf,
|
|||||||
/* Transcode HTTP/3 payload received in buffer <b> to HTX data for stream
|
/* Transcode HTTP/3 payload received in buffer <b> to HTX data for stream
|
||||||
* <qcs>. If <fin> is set, it indicates that no more data will arrive after.
|
* <qcs>. If <fin> is set, it indicates that no more data will arrive after.
|
||||||
*
|
*
|
||||||
* It may be necessary to call this function with an empty input buffer to
|
* Returns the count of consumed bytes or a negative error code. If 0 is
|
||||||
* signal a standalone FIN.
|
* returned, stream data is incomplete, decoding should be call again later
|
||||||
*
|
* with more content.
|
||||||
* Returns the amount of decoded bytes from <b> or a negative error code. A
|
|
||||||
* null value can be returned even if input buffer is not empty, meaning that
|
|
||||||
* transcoding cannot be performed due to partial HTTP/3 content. Receive
|
|
||||||
* operation may be called later with newer data available.
|
|
||||||
*/
|
*/
|
||||||
static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
||||||
{
|
{
|
||||||
@ -1691,13 +1687,6 @@ static int h3_encode_header(struct buffer *buf,
|
|||||||
return qpack_encode_header(buf, n, v_strip);
|
return qpack_encode_header(buf, n, v_strip);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convert a HTX status-line and associated headers stored into <htx> into a
|
|
||||||
* HTTP/3 HEADERS response frame. HTX blocks are removed up to end-of-trailer
|
|
||||||
* included.
|
|
||||||
*
|
|
||||||
* Returns the amount of consumed bytes from <htx> buffer or a negative error
|
|
||||||
* code.
|
|
||||||
*/
|
|
||||||
static int h3_resp_headers_send(struct qcs *qcs, struct htx *htx)
|
static int h3_resp_headers_send(struct qcs *qcs, struct htx *htx)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
@ -1852,15 +1841,15 @@ static int h3_resp_headers_send(struct qcs *qcs, struct htx *htx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Convert a series of HTX trailer blocks from <htx> buffer into <qcs> buffer
|
/* Convert a series of HTX trailer blocks from <htx> buffer into <qcs> buffer
|
||||||
* as a HTTP/3 HEADERS frame. Forbidden trailers are skipped. HTX trailer
|
* as a H3 HEADERS frame. H3 forbidden trailers are skipped. HTX trailer blocks
|
||||||
* blocks are removed from <htx> up to end-of-trailer included.
|
* are removed from <htx> until EOT is found and itself removed.
|
||||||
*
|
*
|
||||||
* If only a EOT HTX block is present without trailer, no H3 frame is produced.
|
* If only a EOT HTX block is present without trailer, no H3 frame is produced.
|
||||||
* Caller is responsible to emit an empty QUIC STREAM frame to signal the end
|
* Caller is responsible to emit an empty QUIC STREAM frame to signal the end
|
||||||
* of the stream.
|
* of the stream.
|
||||||
*
|
*
|
||||||
* Returns the amount of consumed bytes from <htx> buffer or a negative error
|
* Returns the size of HTX blocks removed. A negative error code is returned in
|
||||||
* code.
|
* case of a fatal error which should caused a connection closure.
|
||||||
*/
|
*/
|
||||||
static int h3_resp_trailers_send(struct qcs *qcs, struct htx *htx)
|
static int h3_resp_trailers_send(struct qcs *qcs, struct htx *htx)
|
||||||
{
|
{
|
||||||
@ -2032,9 +2021,9 @@ static int h3_resp_trailers_send(struct qcs *qcs, struct htx *htx)
|
|||||||
* underlying HTX area via <buf> as this will be used if zero-copy can be
|
* underlying HTX area via <buf> as this will be used if zero-copy can be
|
||||||
* performed.
|
* performed.
|
||||||
*
|
*
|
||||||
* Returns the amount of consumed bytes from <htx> buffer, which corresponds to
|
* Returns the total bytes of encoded HTTP/3 payload. This corresponds to the
|
||||||
* the length sum of encoded frames payload. A negative error code is returned
|
* total bytes of HTX block removed. A negative error code is returned in case
|
||||||
* in case of a fatal error which should caused a connection closure.
|
* of a fatal error which should caused a connection closure.
|
||||||
*/
|
*/
|
||||||
static int h3_resp_data_send(struct qcs *qcs, struct htx *htx,
|
static int h3_resp_data_send(struct qcs *qcs, struct htx *htx,
|
||||||
struct buffer *buf, size_t count)
|
struct buffer *buf, size_t count)
|
||||||
@ -2159,14 +2148,6 @@ static int h3_resp_data_send(struct qcs *qcs, struct htx *htx,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Transcode HTX data from <buf> of length <count> into HTTP/3 frame for <qcs>
|
|
||||||
* stream instance. Successfully transcoded HTX blocks are removed from input
|
|
||||||
* buffer.
|
|
||||||
*
|
|
||||||
* Returns the amount of consumed bytes from <buf>. In case of error,
|
|
||||||
* connection is flagged and transcoding is interrupted. The returned value is
|
|
||||||
* unchanged though.
|
|
||||||
*/
|
|
||||||
static size_t h3_snd_buf(struct qcs *qcs, struct buffer *buf, size_t count)
|
static size_t h3_snd_buf(struct qcs *qcs, struct buffer *buf, size_t count)
|
||||||
{
|
{
|
||||||
size_t total = 0;
|
size_t total = 0;
|
||||||
|
106
src/hlua.c
106
src/hlua.c
@ -5299,20 +5299,48 @@ __LJMP static int hlua_applet_tcp_get_priv(lua_State *L)
|
|||||||
__LJMP static int hlua_applet_tcp_getline_yield(lua_State *L, int status, lua_KContext ctx)
|
__LJMP static int hlua_applet_tcp_getline_yield(lua_State *L, int status, lua_KContext ctx)
|
||||||
{
|
{
|
||||||
struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
|
struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
|
||||||
|
struct stconn *sc = appctx_sc(luactx->appctx);
|
||||||
int ret;
|
int ret;
|
||||||
const char *blk1 = NULL;
|
const char *blk1;
|
||||||
size_t len1 = 0;
|
size_t len1;
|
||||||
const char *blk2 = NULL;
|
const char *blk2;
|
||||||
size_t len2 = 0;
|
size_t len2;
|
||||||
|
|
||||||
/* Read the maximum amount of data available. */
|
/* Read the maximum amount of data available. */
|
||||||
ret = applet_getline_nc(luactx->appctx, &blk1, &len1, &blk2, &len2);
|
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS) {
|
||||||
|
size_t l;
|
||||||
|
int line_found = 0;
|
||||||
|
|
||||||
|
ret = b_getblk_nc(&luactx->appctx->inbuf, &blk1, &len1, &blk2, &len2, 0, b_data(&luactx->appctx->inbuf));
|
||||||
|
if (ret == 0 && se_fl_test(luactx->appctx->sedesc, SE_FL_SHW))
|
||||||
|
ret = -1;
|
||||||
|
|
||||||
|
if (ret >= 1) {
|
||||||
|
for (l = 0; l < len1 && blk1[l] != '\n'; l++);
|
||||||
|
if (l < len1 && blk1[l] == '\n') {
|
||||||
|
len1 = l + 1;
|
||||||
|
line_found = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!line_found && ret >= 2) {
|
||||||
|
for (l = 0; l < len2 && blk2[l] != '\n'; l++);
|
||||||
|
if (l < len2 && blk2[l] == '\n') {
|
||||||
|
len2 = l + 1;
|
||||||
|
line_found = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!line_found && !se_fl_test(luactx->appctx->sedesc, SE_FL_SHW))
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = co_getline_nc(sc_oc(sc), &blk1, &len1, &blk2, &len2);
|
||||||
|
|
||||||
/* Data not yet available. return yield. */
|
/* Data not yet available. return yield. */
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
applet_need_more_data(luactx->appctx);
|
applet_need_more_data(luactx->appctx);
|
||||||
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_tcp_getline_yield, TICK_ETERNITY, 0));
|
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_tcp_getline_yield, TICK_ETERNITY, 0));
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* End of data: commit the total strings and return. */
|
/* End of data: commit the total strings and return. */
|
||||||
@ -5327,10 +5355,13 @@ __LJMP static int hlua_applet_tcp_getline_yield(lua_State *L, int status, lua_KC
|
|||||||
|
|
||||||
/* don't check the max length read and don't check. */
|
/* don't check the max length read and don't check. */
|
||||||
luaL_addlstring(&luactx->b, blk1, len1);
|
luaL_addlstring(&luactx->b, blk1, len1);
|
||||||
if (len2)
|
luaL_addlstring(&luactx->b, blk2, len2);
|
||||||
luaL_addlstring(&luactx->b, blk2, len2);
|
|
||||||
|
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS)
|
||||||
|
b_del(&luactx->appctx->inbuf, len1 + len2);
|
||||||
|
else
|
||||||
|
co_skip(sc_oc(sc), len1 + len2);
|
||||||
|
|
||||||
applet_skip_input(luactx->appctx, len1+len2);
|
|
||||||
luaL_pushresult(&luactx->b);
|
luaL_pushresult(&luactx->b);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -5352,16 +5383,23 @@ __LJMP static int hlua_applet_tcp_getline(lua_State *L)
|
|||||||
__LJMP static int hlua_applet_tcp_recv_try(lua_State *L)
|
__LJMP static int hlua_applet_tcp_recv_try(lua_State *L)
|
||||||
{
|
{
|
||||||
struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
|
struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
|
||||||
|
struct stconn *sc = appctx_sc(luactx->appctx);
|
||||||
size_t len = MAY_LJMP(luaL_checkinteger(L, 2));
|
size_t len = MAY_LJMP(luaL_checkinteger(L, 2));
|
||||||
int exp_date = MAY_LJMP(luaL_checkinteger(L, 3));
|
int exp_date = MAY_LJMP(luaL_checkinteger(L, 3));
|
||||||
int ret;
|
int ret;
|
||||||
const char *blk1 = NULL;
|
const char *blk1;
|
||||||
size_t len1 = 0;
|
size_t len1;
|
||||||
const char *blk2 = NULL;
|
const char *blk2;
|
||||||
size_t len2 = 0;
|
size_t len2;
|
||||||
|
|
||||||
/* Read the maximum amount of data available. */
|
/* Read the maximum amount of data available. */
|
||||||
ret = applet_getblk_nc(luactx->appctx, &blk1, &len1, &blk2, &len2);
|
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS) {
|
||||||
|
ret = b_getblk_nc(&luactx->appctx->inbuf, &blk1, &len1, &blk2, &len2, 0, b_data(&luactx->appctx->inbuf));
|
||||||
|
if (ret == 0 && se_fl_test(luactx->appctx->sedesc, SE_FL_SHW))
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = co_getblk_nc(sc_oc(sc), &blk1, &len1, &blk2, &len2);
|
||||||
|
|
||||||
/* Data not yet available. return yield. */
|
/* Data not yet available. return yield. */
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
@ -5392,9 +5430,11 @@ __LJMP static int hlua_applet_tcp_recv_try(lua_State *L)
|
|||||||
* the end of data stream.
|
* the end of data stream.
|
||||||
*/
|
*/
|
||||||
luaL_addlstring(&luactx->b, blk1, len1);
|
luaL_addlstring(&luactx->b, blk1, len1);
|
||||||
if (len2)
|
luaL_addlstring(&luactx->b, blk2, len2);
|
||||||
luaL_addlstring(&luactx->b, blk2, len2);
|
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS)
|
||||||
applet_skip_input(luactx->appctx, len1+len2);
|
b_del(&luactx->appctx->inbuf, len1 + len2);
|
||||||
|
else
|
||||||
|
co_skip(sc_oc(sc), len1 + len2);
|
||||||
|
|
||||||
if (tick_is_expired(exp_date, now_ms)) {
|
if (tick_is_expired(exp_date, now_ms)) {
|
||||||
/* return the result. */
|
/* return the result. */
|
||||||
@ -5414,14 +5454,15 @@ __LJMP static int hlua_applet_tcp_recv_try(lua_State *L)
|
|||||||
len -= len1;
|
len -= len1;
|
||||||
|
|
||||||
/* Copy the second block. */
|
/* Copy the second block. */
|
||||||
if (len2) {
|
if (len2 > len)
|
||||||
if (len2 > len)
|
len2 = len;
|
||||||
len2 = len;
|
luaL_addlstring(&luactx->b, blk2, len2);
|
||||||
luaL_addlstring(&luactx->b, blk2, len2);
|
len -= len2;
|
||||||
len -= len2;
|
|
||||||
}
|
|
||||||
|
|
||||||
applet_skip_input(luactx->appctx, len1+len2);
|
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS)
|
||||||
|
b_del(&luactx->appctx->inbuf, len1 + len2);
|
||||||
|
else
|
||||||
|
co_skip(sc_oc(sc), len1 + len2);
|
||||||
|
|
||||||
/* If there is no other data available, yield waiting for new data. */
|
/* If there is no other data available, yield waiting for new data. */
|
||||||
if (len > 0 && !tick_is_expired(exp_date, now_ms)) {
|
if (len > 0 && !tick_is_expired(exp_date, now_ms)) {
|
||||||
@ -5530,10 +5571,15 @@ __LJMP static int hlua_applet_tcp_send_yield(lua_State *L, int status, lua_KCont
|
|||||||
struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
|
struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
|
||||||
const char *str = MAY_LJMP(luaL_checklstring(L, 2, &len));
|
const char *str = MAY_LJMP(luaL_checklstring(L, 2, &len));
|
||||||
int l = MAY_LJMP(luaL_checkinteger(L, 3));
|
int l = MAY_LJMP(luaL_checkinteger(L, 3));
|
||||||
|
struct stconn *sc = appctx_sc(luactx->appctx);
|
||||||
|
struct channel *chn = sc_ic(sc);
|
||||||
int max;
|
int max;
|
||||||
|
|
||||||
/* Get the max amount of data which can be written */
|
/* Get the max amount of data which can be written */
|
||||||
max = applet_output_room(luactx->appctx);
|
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS)
|
||||||
|
max = b_room(&luactx->appctx->outbuf);
|
||||||
|
else
|
||||||
|
max = channel_recv_max(chn);
|
||||||
|
|
||||||
if (max > (len - l))
|
if (max > (len - l))
|
||||||
max = len - l;
|
max = len - l;
|
||||||
@ -5550,7 +5596,10 @@ __LJMP static int hlua_applet_tcp_send_yield(lua_State *L, int status, lua_KCont
|
|||||||
* applet, and returns a yield.
|
* applet, and returns a yield.
|
||||||
*/
|
*/
|
||||||
if (l < len) {
|
if (l < len) {
|
||||||
applet_need_room(luactx->appctx, applet_output_room(luactx->appctx) + 1);
|
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS)
|
||||||
|
applet_have_more_data(luactx->appctx);
|
||||||
|
else
|
||||||
|
sc_need_room(sc, channel_recv_max(chn) + 1);
|
||||||
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_tcp_send_yield, TICK_ETERNITY, 0));
|
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_tcp_send_yield, TICK_ETERNITY, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11212,7 +11261,10 @@ out:
|
|||||||
if (yield)
|
if (yield)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
applet_reset_input(ctx);
|
if (ctx->flags & APPCTX_FL_INOUT_BUFS)
|
||||||
|
b_reset(&ctx->inbuf);
|
||||||
|
else
|
||||||
|
co_skip(sc_oc(sc), co_data(sc_oc(sc)));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
@ -6,15 +6,13 @@
|
|||||||
#include <haproxy/dynbuf.h>
|
#include <haproxy/dynbuf.h>
|
||||||
#include <haproxy/htx.h>
|
#include <haproxy/htx.h>
|
||||||
#include <haproxy/http.h>
|
#include <haproxy/http.h>
|
||||||
#include <haproxy/istbuf.h>
|
|
||||||
#include <haproxy/mux_quic.h>
|
#include <haproxy/mux_quic.h>
|
||||||
#include <haproxy/qmux_http.h>
|
#include <haproxy/qmux_http.h>
|
||||||
#include <haproxy/qmux_trace.h>
|
#include <haproxy/qmux_trace.h>
|
||||||
#include <haproxy/quic_utils.h>
|
#include <haproxy/quic_utils.h>
|
||||||
#include <haproxy/trace.h>
|
#include <haproxy/trace.h>
|
||||||
|
|
||||||
/* HTTP/0.9 request -> HTX. */
|
static ssize_t hq_interop_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
||||||
static ssize_t hq_interop_rcv_buf_req(struct qcs *qcs, struct buffer *b, int fin)
|
|
||||||
{
|
{
|
||||||
struct htx *htx;
|
struct htx *htx;
|
||||||
struct htx_sl *sl;
|
struct htx_sl *sl;
|
||||||
@ -94,74 +92,12 @@ static ssize_t hq_interop_rcv_buf_req(struct qcs *qcs, struct buffer *b, int fin
|
|||||||
return b_data(b);
|
return b_data(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* HTTP/0.9 response -> HTX. */
|
|
||||||
static ssize_t hq_interop_rcv_buf_res(struct qcs *qcs, struct buffer *b, int fin)
|
|
||||||
{
|
|
||||||
ssize_t ret = 0;
|
|
||||||
struct htx *htx;
|
|
||||||
struct htx_sl *sl;
|
|
||||||
struct buffer *htx_buf;
|
|
||||||
const struct stream *strm = __sc_strm(qcs->sd->sc);
|
|
||||||
const unsigned int flags = HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN;
|
|
||||||
size_t htx_sent;
|
|
||||||
|
|
||||||
htx_buf = qcc_get_stream_rxbuf(qcs);
|
|
||||||
BUG_ON(!htx_buf);
|
|
||||||
htx = htx_from_buf(htx_buf);
|
|
||||||
|
|
||||||
if (htx_is_empty(htx) && !strm->res.total) {
|
|
||||||
/* First data transfer, add HTX response start-line first. */
|
|
||||||
sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags,
|
|
||||||
ist("HTTP/1.0"), ist("200"), ist(""));
|
|
||||||
BUG_ON(!sl);
|
|
||||||
if (fin && !b_data(b))
|
|
||||||
sl->flags |= HTX_SL_F_BODYLESS;
|
|
||||||
htx_add_endof(htx, HTX_BLK_EOH);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!b_data(b)) {
|
|
||||||
if (fin && quic_stream_is_bidi(qcs->id)) {
|
|
||||||
if (qcs_http_handle_standalone_fin(qcs)) {
|
|
||||||
htx_to_buf(htx, htx_buf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
BUG_ON(b_data(b) > htx_free_data_space(htx)); /* TODO */
|
|
||||||
BUG_ON(b_head(b) + b_data(b) > b_wrap(b)); /* TODO */
|
|
||||||
|
|
||||||
htx_sent = htx_add_data(htx, ist2(b_head(b), b_data(b)));
|
|
||||||
BUG_ON(htx_sent < b_data(b)); /* TODO */
|
|
||||||
ret = htx_sent;
|
|
||||||
|
|
||||||
if (fin && b_data(b) == htx_sent)
|
|
||||||
htx->flags |= HTX_FL_EOM;
|
|
||||||
}
|
|
||||||
|
|
||||||
htx_to_buf(htx, htx_buf);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the amount of decoded bytes from <b> or a negative error code. */
|
|
||||||
static ssize_t hq_interop_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
|
||||||
{
|
|
||||||
/* hq-interop parser does not support buffer wrapping. */
|
|
||||||
BUG_ON(b_data(b) != b_contig_data(b, 0));
|
|
||||||
|
|
||||||
return !(qcs->qcc->flags & QC_CF_IS_BACK) ?
|
|
||||||
hq_interop_rcv_buf_req(qcs, b, fin) :
|
|
||||||
hq_interop_rcv_buf_res(qcs, b, fin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the amount of consumed bytes from <buf>. */
|
|
||||||
static size_t hq_interop_snd_buf(struct qcs *qcs, struct buffer *buf,
|
static size_t hq_interop_snd_buf(struct qcs *qcs, struct buffer *buf,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
enum htx_blk_type btype;
|
enum htx_blk_type btype;
|
||||||
struct htx *htx = NULL;
|
struct htx *htx = NULL;
|
||||||
struct htx_blk *blk;
|
struct htx_blk *blk;
|
||||||
struct htx_sl *sl = NULL;
|
|
||||||
int32_t idx;
|
int32_t idx;
|
||||||
uint32_t bsize, fsize;
|
uint32_t bsize, fsize;
|
||||||
struct buffer *res = NULL;
|
struct buffer *res = NULL;
|
||||||
@ -177,25 +113,9 @@ static size_t hq_interop_snd_buf(struct qcs *qcs, struct buffer *buf,
|
|||||||
btype = htx_get_blk_type(blk);
|
btype = htx_get_blk_type(blk);
|
||||||
fsize = bsize = htx_get_blksz(blk);
|
fsize = bsize = htx_get_blksz(blk);
|
||||||
|
|
||||||
|
BUG_ON(btype == HTX_BLK_REQ_SL);
|
||||||
|
|
||||||
switch (btype) {
|
switch (btype) {
|
||||||
case HTX_BLK_REQ_SL:
|
|
||||||
res = qcc_get_stream_txbuf(qcs, &err, 0);
|
|
||||||
if (!res) {
|
|
||||||
BUG_ON(err); /* TODO */
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
BUG_ON_HOT(sl); /* Only one start-line expected */
|
|
||||||
sl = htx_get_blk_ptr(htx, blk);
|
|
||||||
|
|
||||||
/* Only GET supported for HTTP/0.9. */
|
|
||||||
b_putist(res, ist("GET "));
|
|
||||||
b_putist(res, htx_sl_req_uri(sl));
|
|
||||||
b_putist(res, ist(" HTTP/0.9\r\n"));
|
|
||||||
htx_remove_blk(htx, blk);
|
|
||||||
total += fsize;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HTX_BLK_DATA:
|
case HTX_BLK_DATA:
|
||||||
res = qcc_get_stream_txbuf(qcs, &err, 0);
|
res = qcc_get_stream_txbuf(qcs, &err, 0);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
|
@ -1091,15 +1091,14 @@ void listener_accept(struct listener *l)
|
|||||||
#endif
|
#endif
|
||||||
if (p && p->fe_sps_lim) {
|
if (p && p->fe_sps_lim) {
|
||||||
int max = 0;
|
int max = 0;
|
||||||
int it;
|
|
||||||
|
|
||||||
for (it = 0; it < global.nbtgroups; it++)
|
for (int it = 0; it < global.nbtgroups; it++)
|
||||||
max += freq_ctr_remain(&p->fe_counters.shared->tg[it]->sess_per_sec, p->fe_sps_lim, 0);
|
max += freq_ctr_remain(&p->fe_counters.shared->tg[it]->sess_per_sec, p->fe_sps_lim, 0);
|
||||||
|
|
||||||
if (unlikely(!max)) {
|
if (unlikely(!max)) {
|
||||||
unsigned int min_wait = 0;
|
unsigned int min_wait = 0;
|
||||||
|
|
||||||
for (it = 0; it < global.nbtgroups; it++) {
|
for (int it = 0; it < global.nbtgroups; it++) {
|
||||||
unsigned int cur_wait = next_event_delay(&p->fe_counters.shared->tg[it]->sess_per_sec, p->fe_sps_lim, 0);
|
unsigned int cur_wait = next_event_delay(&p->fe_counters.shared->tg[it]->sess_per_sec, p->fe_sps_lim, 0);
|
||||||
if (!it || cur_wait < min_wait)
|
if (!it || cur_wait < min_wait)
|
||||||
min_wait = cur_wait;
|
min_wait = cur_wait;
|
||||||
|
@ -135,6 +135,7 @@ static struct qcs *qcs_new(struct qcc *qcc, uint64_t id, enum qcs_type type)
|
|||||||
qcs->stream = NULL;
|
qcs->stream = NULL;
|
||||||
qcs->qcc = qcc;
|
qcs->qcc = qcc;
|
||||||
qcs->sd = NULL;
|
qcs->sd = NULL;
|
||||||
|
qcs->sess = NULL;
|
||||||
qcs->flags = QC_SF_NONE;
|
qcs->flags = QC_SF_NONE;
|
||||||
qcs->st = QC_SS_IDLE;
|
qcs->st = QC_SS_IDLE;
|
||||||
qcs->ctx = NULL;
|
qcs->ctx = NULL;
|
||||||
@ -3115,10 +3116,10 @@ static void qcc_shutdown(struct qcc *qcc)
|
|||||||
TRACE_LEAVE(QMUX_EV_QCC_END, qcc->conn);
|
TRACE_LEAVE(QMUX_EV_QCC_END, qcc->conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Loop through all qcs from <qcc> and wake their associated data layer if
|
/* Loop through all qcs from <qcc>. Report error on stream endpoint if
|
||||||
* still active. Also report error on it if connection is already in error.
|
* connection on error and wake them.
|
||||||
*/
|
*/
|
||||||
static void qcc_wake_streams(struct qcc *qcc)
|
static int qcc_wake_some_streams(struct qcc *qcc)
|
||||||
{
|
{
|
||||||
struct qcs *qcs;
|
struct qcs *qcs;
|
||||||
struct eb64_node *node;
|
struct eb64_node *node;
|
||||||
@ -3135,10 +3136,11 @@ static void qcc_wake_streams(struct qcc *qcc)
|
|||||||
if (qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL)) {
|
if (qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL)) {
|
||||||
TRACE_POINT(QMUX_EV_QCC_WAKE, qcc->conn, qcs);
|
TRACE_POINT(QMUX_EV_QCC_WAKE, qcc->conn, qcs);
|
||||||
se_fl_set_error(qcs->sd);
|
se_fl_set_error(qcs->sd);
|
||||||
|
qcs_alert(qcs);
|
||||||
}
|
}
|
||||||
|
|
||||||
qcs_alert(qcs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Conduct operations which should be made for <qcc> connection after
|
/* Conduct operations which should be made for <qcc> connection after
|
||||||
@ -3193,7 +3195,7 @@ static int qcc_io_process(struct qcc *qcc)
|
|||||||
|
|
||||||
/* Report error if set on stream endpoint layer. */
|
/* Report error if set on stream endpoint layer. */
|
||||||
if (qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL))
|
if (qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL))
|
||||||
qcc_wake_streams(qcc);
|
qcc_wake_some_streams(qcc);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (qcc_is_dead(qcc))
|
if (qcc_is_dead(qcc))
|
||||||
@ -3419,7 +3421,7 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
|||||||
_qcc_init(qcc);
|
_qcc_init(qcc);
|
||||||
conn->ctx = qcc;
|
conn->ctx = qcc;
|
||||||
qcc->nb_hreq = qcc->nb_sc = 0;
|
qcc->nb_hreq = qcc->nb_sc = 0;
|
||||||
qcc->flags = conn_is_back(conn) ? QC_CF_IS_BACK : 0;
|
qcc->flags = 0;
|
||||||
qcc->app_st = QCC_APP_ST_NULL;
|
qcc->app_st = QCC_APP_ST_NULL;
|
||||||
qcc->glitches = 0;
|
qcc->glitches = 0;
|
||||||
qcc->err = quic_err_transport(QC_ERR_NO_ERROR);
|
qcc->err = quic_err_transport(QC_ERR_NO_ERROR);
|
||||||
@ -3532,34 +3534,10 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
|||||||
if (qcc->app_ops == &h3_ops && !conn_is_back(conn))
|
if (qcc->app_ops == &h3_ops && !conn_is_back(conn))
|
||||||
proxy_inc_fe_cum_sess_ver_ctr(sess->listener, prx, 3);
|
proxy_inc_fe_cum_sess_ver_ctr(sess->listener, prx, 3);
|
||||||
|
|
||||||
if (!conn_is_back(conn)) {
|
/* Register conn for idle front closing. This is done once everything is allocated. */
|
||||||
/* Register conn for idle front closing. */
|
if (!conn_is_back(conn))
|
||||||
LIST_APPEND(&mux_stopping_data[tid].list, &conn->stopping_list);
|
LIST_APPEND(&mux_stopping_data[tid].list, &conn->stopping_list);
|
||||||
|
|
||||||
/* init read cycle */
|
|
||||||
tasklet_wakeup(qcc->wait_event.tasklet);
|
|
||||||
|
|
||||||
/* MUX is initialized before QUIC handshake completion if early data
|
|
||||||
* received. Flag connection to delay stream processing if
|
|
||||||
* wait-for-handshake is active.
|
|
||||||
*/
|
|
||||||
if (conn->handle.qc->state < QUIC_HS_ST_COMPLETE) {
|
|
||||||
if (!(conn->flags & CO_FL_EARLY_SSL_HS)) {
|
|
||||||
TRACE_STATE("flag connection with early data", QMUX_EV_QCC_WAKE, conn);
|
|
||||||
conn->flags |= CO_FL_EARLY_SSL_HS;
|
|
||||||
/* subscribe for handshake completion */
|
|
||||||
conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV,
|
|
||||||
&qcc->wait_event);
|
|
||||||
qcc->flags |= QC_CF_WAIT_HS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
/* Initiate backend side transfer by creating the first
|
|
||||||
* bidirectional stream. MUX will then be woken up on QUIC
|
|
||||||
* handshake completion so that stream layer can start the
|
|
||||||
* transfer itself.
|
|
||||||
*/
|
|
||||||
struct qcs *qcs;
|
struct qcs *qcs;
|
||||||
struct stconn *sc = conn_ctx;
|
struct stconn *sc = conn_ctx;
|
||||||
|
|
||||||
@ -3572,9 +3550,28 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
|||||||
|
|
||||||
sc_attach_mux(sc, qcs, conn);
|
sc_attach_mux(sc, qcs, conn);
|
||||||
qcs->sd = sc->sedesc;
|
qcs->sd = sc->sedesc;
|
||||||
|
qcs->sess = sess;
|
||||||
qcc->nb_sc++;
|
qcc->nb_sc++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* init read cycle */
|
||||||
|
tasklet_wakeup(qcc->wait_event.tasklet);
|
||||||
|
|
||||||
|
/* MUX is initialized before QUIC handshake completion if early data
|
||||||
|
* received. Flag connection to delay stream processing if
|
||||||
|
* wait-for-handshake is active.
|
||||||
|
*/
|
||||||
|
if (conn->handle.qc->state < QUIC_HS_ST_COMPLETE) {
|
||||||
|
if (!(conn->flags & CO_FL_EARLY_SSL_HS)) {
|
||||||
|
TRACE_STATE("flag connection with early data", QMUX_EV_QCC_WAKE, conn);
|
||||||
|
conn->flags |= CO_FL_EARLY_SSL_HS;
|
||||||
|
/* subscribe for handshake completion */
|
||||||
|
conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV,
|
||||||
|
&qcc->wait_event);
|
||||||
|
qcc->flags |= QC_CF_WAIT_HS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TRACE_LEAVE(QMUX_EV_QCC_NEW, conn);
|
TRACE_LEAVE(QMUX_EV_QCC_NEW, conn);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -3679,12 +3676,10 @@ static size_t qmux_strm_rcv_buf(struct stconn *sc, struct buffer *buf,
|
|||||||
TRACE_STATE("report end-of-input", QMUX_EV_STRM_RECV, qcc->conn, qcs);
|
TRACE_STATE("report end-of-input", QMUX_EV_STRM_RECV, qcc->conn, qcs);
|
||||||
se_fl_set(qcs->sd, SE_FL_EOI);
|
se_fl_set(qcs->sd, SE_FL_EOI);
|
||||||
|
|
||||||
if (!(qcc->flags & QC_CF_IS_BACK)) {
|
/* If request EOM is reported to the upper layer, it means the
|
||||||
/* If request EOM is reported to the upper layer, it means the
|
* QCS now expects data from the opposite side.
|
||||||
* QCS now expects data from the opposite side.
|
*/
|
||||||
*/
|
se_expect_data(qcs->sd);
|
||||||
se_expect_data(qcs->sd);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set end-of-stream on read closed. */
|
/* Set end-of-stream on read closed. */
|
||||||
@ -3966,11 +3961,7 @@ static int qmux_wake(struct connection *conn)
|
|||||||
goto release;
|
goto release;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wake all streams, unless an error is set as qcc_io_process() has
|
qcc_wake_some_streams(qcc);
|
||||||
* already woken them in this case.
|
|
||||||
*/
|
|
||||||
if (!(qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL)))
|
|
||||||
qcc_wake_streams(qcc);
|
|
||||||
|
|
||||||
qcc_refresh_timeout(qcc);
|
qcc_refresh_timeout(qcc);
|
||||||
|
|
||||||
|
@ -431,7 +431,6 @@ int quic_connect_server(struct connection *conn, int flags)
|
|||||||
fd_insert(fd, qc, quic_conn_sock_fd_iocb, tgid, ti->ltid_bit);
|
fd_insert(fd, qc, quic_conn_sock_fd_iocb, tgid, ti->ltid_bit);
|
||||||
fd_want_recv(fd);
|
fd_want_recv(fd);
|
||||||
|
|
||||||
conn_ctrl_init(conn);
|
|
||||||
return SF_ERR_NONE; /* connection is OK */
|
return SF_ERR_NONE; /* connection is OK */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +145,7 @@ void qmux_dump_qcc_info(struct buffer *msg, const struct qcc *qcc)
|
|||||||
{
|
{
|
||||||
const struct quic_conn *qc = qcc->conn->handle.qc;
|
const struct quic_conn *qc = qcc->conn->handle.qc;
|
||||||
|
|
||||||
chunk_appendf(msg, " qcc=%p(%c)", qcc, (qcc->flags & QC_CF_IS_BACK) ? 'B' : 'F');
|
chunk_appendf(msg, " qcc=%p(F)", qcc);
|
||||||
if (qcc->conn->handle.qc)
|
if (qcc->conn->handle.qc)
|
||||||
chunk_appendf(msg, " qc=%p", qcc->conn->handle.qc);
|
chunk_appendf(msg, " qc=%p", qcc->conn->handle.qc);
|
||||||
chunk_appendf(msg, " .st=%s .sc=%llu .hreq=%llu .flg=0x%04x",
|
chunk_appendf(msg, " .st=%s .sc=%llu .hreq=%llu .flg=0x%04x",
|
||||||
|
@ -1047,7 +1047,7 @@ struct task *qc_process_timer(struct task *task, void *ctx, unsigned int state)
|
|||||||
|
|
||||||
/* Allocate a new QUIC connection with <version> as QUIC version. <ipv4>
|
/* Allocate a new QUIC connection with <version> as QUIC version. <ipv4>
|
||||||
* boolean is set to 1 for IPv4 connection, 0 for IPv6. <server> is set to 1
|
* boolean is set to 1 for IPv4 connection, 0 for IPv6. <server> is set to 1
|
||||||
* for QUIC servers (or haproxy listeners), 0 for QUIC clients.
|
* for QUIC servers (or haproxy listeners).
|
||||||
* <dcid> is the destination connection ID, <scid> is the source connection ID.
|
* <dcid> is the destination connection ID, <scid> is the source connection ID.
|
||||||
* This latter <scid> CID as the same value on the wire as the one for <conn_id>
|
* This latter <scid> CID as the same value on the wire as the one for <conn_id>
|
||||||
* which is the first CID of this connection but a different internal
|
* which is the first CID of this connection but a different internal
|
||||||
@ -1060,9 +1060,6 @@ struct task *qc_process_timer(struct task *task, void *ctx, unsigned int state)
|
|||||||
* into the Retry token sent to the client before instantiated this connection.
|
* into the Retry token sent to the client before instantiated this connection.
|
||||||
* Endpoints addresses are specified via <local_addr> and <peer_addr>.
|
* Endpoints addresses are specified via <local_addr> and <peer_addr>.
|
||||||
* Returns the connection if succeeded, NULL if not.
|
* Returns the connection if succeeded, NULL if not.
|
||||||
* For QUIC clients, <dcid>, <scid>, <token_odcid>, <conn_id> must be null,
|
|
||||||
* and <token> value must be 0. This is the responsability of the caller to ensure
|
|
||||||
* this is the case.
|
|
||||||
*/
|
*/
|
||||||
struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||||
struct quic_cid *dcid, struct quic_cid *scid,
|
struct quic_cid *dcid, struct quic_cid *scid,
|
||||||
@ -1351,15 +1348,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
return qc;
|
return qc;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
if (!l && !conn_id) {
|
pool_free(pool_head_quic_connection_id, conn_id);
|
||||||
/* For QUIC clients, <conn_id> is locally used and initialized to <conn_cid>
|
|
||||||
* value as soon as this latter is attached to the CIDs tree. It must
|
|
||||||
* be freed only if it has not been attached to this tree. This is
|
|
||||||
* quic_conn_release() which free this CID when it is attached to the tree.
|
|
||||||
*/
|
|
||||||
pool_free(pool_head_quic_connection_id, conn_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
quic_conn_release(qc);
|
quic_conn_release(qc);
|
||||||
|
|
||||||
/* Decrement global counters. Done only for errors happening before or
|
/* Decrement global counters. Done only for errors happening before or
|
||||||
|
@ -500,19 +500,11 @@ static int quic_parse_new_token_frame(struct quic_frame *frm, struct quic_conn *
|
|||||||
{
|
{
|
||||||
struct qf_new_token *new_token_frm = &frm->new_token;
|
struct qf_new_token *new_token_frm = &frm->new_token;
|
||||||
|
|
||||||
if (!quic_dec_int(&new_token_frm->len, pos, end) || end - *pos < new_token_frm->len)
|
if (!quic_dec_int(&new_token_frm->len, pos, end) || end - *pos < new_token_frm->len ||
|
||||||
|
sizeof(new_token_frm->data) < new_token_frm->len)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* TODO token length is unknown as it is dependent from the peer. Hence
|
|
||||||
* dynamic allocation should be implemented for token storage, albeit
|
|
||||||
* with constraint to ensure memory usage remains reasonable.
|
|
||||||
*/
|
|
||||||
#if 0
|
|
||||||
if (sizeof(new_token_frm->data) < new_token_frm->len)
|
|
||||||
return 0;
|
|
||||||
memcpy(new_token_frm->data, *pos, new_token_frm->len);
|
memcpy(new_token_frm->data, *pos, new_token_frm->len);
|
||||||
#endif
|
|
||||||
|
|
||||||
*pos += new_token_frm->len;
|
*pos += new_token_frm->len;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -949,11 +949,7 @@ static int qc_parse_pkt_frms(struct quic_conn *qc, struct quic_rx_packet *pkt,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* TODO NEW_TOKEN not implemented on client side.
|
/* TODO */
|
||||||
* Note that for now token is not copied into <data> field
|
|
||||||
* of qf_new_token frame. See quic_parse_new_token_frame()
|
|
||||||
* for further explanations.
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case QUIC_FT_STREAM_8 ... QUIC_FT_STREAM_F:
|
case QUIC_FT_STREAM_8 ... QUIC_FT_STREAM_F:
|
||||||
|
@ -780,9 +780,13 @@ SSL_CTX *ssl_quic_srv_new_ssl_ctx(void)
|
|||||||
SSL_OP_SINGLE_ECDH_USE |
|
SSL_OP_SINGLE_ECDH_USE |
|
||||||
SSL_OP_CIPHER_SERVER_PREFERENCE;
|
SSL_OP_CIPHER_SERVER_PREFERENCE;
|
||||||
|
|
||||||
|
TRACE_ENTER(QUIC_EV_CONN_NEW);
|
||||||
|
|
||||||
ctx = SSL_CTX_new(TLS_client_method());
|
ctx = SSL_CTX_new(TLS_client_method());
|
||||||
if (!ctx)
|
if (!ctx) {
|
||||||
goto err;
|
TRACE_ERROR("Could not allocate a new TLS context", QUIC_EV_CONN_NEW);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
SSL_CTX_set_options(ctx, options);
|
SSL_CTX_set_options(ctx, options);
|
||||||
SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION);
|
SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION);
|
||||||
@ -793,10 +797,12 @@ SSL_CTX *ssl_quic_srv_new_ssl_ctx(void)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
leave:
|
leave:
|
||||||
|
TRACE_LEAVE(QUIC_EV_CONN_NEW);
|
||||||
return ctx;
|
return ctx;
|
||||||
err:
|
err:
|
||||||
SSL_CTX_free(ctx);
|
SSL_CTX_free(ctx);
|
||||||
ctx = NULL;
|
ctx = NULL;
|
||||||
|
TRACE_DEVEL("leaving on error", QUIC_EV_CONN_NEW);
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -955,6 +961,7 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
|
|||||||
else {
|
else {
|
||||||
const unsigned char *alpn;
|
const unsigned char *alpn;
|
||||||
size_t alpn_len;
|
size_t alpn_len;
|
||||||
|
struct server *s = objt_server(ctx->conn->target);
|
||||||
|
|
||||||
ctx->conn->flags &= ~(CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN);
|
ctx->conn->flags &= ~(CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN);
|
||||||
if (!ssl_sock_get_alpn(ctx->conn, ctx, (const char **)&alpn, (int *)&alpn_len) ||
|
if (!ssl_sock_get_alpn(ctx->conn, ctx, (const char **)&alpn, (int *)&alpn_len) ||
|
||||||
@ -964,13 +971,12 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
|
|||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s->mux_proto = get_mux_proto(ist("quic"));
|
||||||
if (conn_create_mux(ctx->conn, NULL) < 0) {
|
if (conn_create_mux(ctx->conn, NULL) < 0) {
|
||||||
TRACE_ERROR("mux creation failed", QUIC_EV_CONN_IO_CB, qc, &state);
|
TRACE_ERROR("mux creation failed", QUIC_EV_CONN_IO_CB, qc, &state);
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wake up MUX after its creation. Operation similar to TLS+ALPN on TCP stack. */
|
|
||||||
ctx->conn->mux->wake(ctx->conn);
|
|
||||||
qc->mux_state = QC_MUX_READY;
|
qc->mux_state = QC_MUX_READY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3836,15 +3836,6 @@ static int _srv_parse_finalize(char **args, int cur_arg,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_QUIC
|
|
||||||
if (srv_is_quic(srv)) {
|
|
||||||
if (!srv->use_ssl) {
|
|
||||||
ha_alert("QUIC protocol detected without explicit SSL requirement. Use 'ssl' to fix this.\n");
|
|
||||||
return ERR_ALERT | ERR_FATAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
srv_lb_commit_status(srv);
|
srv_lb_commit_status(srv);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user