mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-02-16 10:36:55 +00:00
MINOR: http_ext: implement rfc7239_{nn,np} converters
"option forwarded" provides a convenient way to automatically insert rfc7239 forwarded header to requests sent to servers. On the other hand, manually crafting the header is quite complicated due to specific formatting rules that must be followed as per rfc7239. However, sometimes it may be necessary to craft the header manually, for instance if it has to be conditional or based on parameters that "option forwarded" doesn't provide. To ease this task, in this patch we implement rfc7239_nn and rfc7239_np which are respectively meant to craft nodename: nodeport values, specifically intended to manually build rfc7239 'for' and 'by' header fields while ensuring rfc7239 compliancy. Example: # build RFC-compliant 7239 header: http-request set-var-fmt(txn.forwarded) "for=\"%[ipv6(::1),rfc7239_nn]:%[str(8888),rfc7239_np]\";host=\"haproxy.org\";proto=http" # check RFC-compliancy: http-request set-var(txn.test) "var(txn.forwarded),debug(ok,stderr),rfc7239_is_valid,debug(ok,stderr)" # stderr output: # [debug] ok: type=str <for="[::1]:_8888";host="haproxy.org";proto=http> # [debug] ok: type=bool <1> See documentation for more info and examples.
This commit is contained in:
parent
45cbbdc845
commit
d28d016f43
@ -19760,6 +19760,8 @@ rfc7239_field(field) string string
|
||||
rfc7239_is_valid string boolean
|
||||
rfc7239_n2nn string address / str
|
||||
rfc7239_n2np string integer / str
|
||||
rfc7239_nn address/str string
|
||||
rfc7239_np integer/str string
|
||||
rtrim(chars) string string
|
||||
sdbm([avalanche]) binary integer
|
||||
secure_memcmp(var) string boolean
|
||||
@ -20838,6 +20840,52 @@ rfc7239_n2np
|
||||
#input: "_name:_port"
|
||||
# output: "_port" (string)
|
||||
|
||||
rfc7239_nn
|
||||
Converts provided address / string input into RFC7239-compliant node name.
|
||||
It may be used to manually build 'for' or 'by' 7239 header fields.
|
||||
|
||||
When provided input is string, it will be automatically prefixed with '_'
|
||||
char to represent obfuscated identifier. String must comply with RFC7239
|
||||
charset. If string is empty, it will be converter to "unknown" identifier.
|
||||
|
||||
Example:
|
||||
#input: ipv6(ab:cd:ff:ff:ff:ff:ff:ff)
|
||||
# output: "[ab:cd:ff:ff:ff:ff:ff:ff]"
|
||||
#input: str(test)
|
||||
# output: "_test"
|
||||
#input: str()
|
||||
# output: "unknown"
|
||||
|
||||
See also: "rfc7239_np"
|
||||
|
||||
rfc7239_np
|
||||
Converts provided unsigned integer / string input into RFC7239-compliant node
|
||||
port. It may be used to manually build 'for' or 'by' 7239 header fields.
|
||||
|
||||
When provided input is string, it will be automatically prefixed with '_'
|
||||
char to represent obfuscated identifier. String must comply with RFC7239
|
||||
charset and cannot be empty.
|
||||
|
||||
Example:
|
||||
#input: int(12)
|
||||
# output: "12"
|
||||
#input: str(test)
|
||||
# output: "_test"
|
||||
|
||||
# build 'for' forwarded header field
|
||||
http-request set-var-fmt(txn.test) "for=\"%[ipv6(::1),rfc7239_nn]:%[int(8080),rfc7239_np]\";"
|
||||
# output: "for=\"[::1]:8080\";"
|
||||
|
||||
# build RFC-compliant 7239 header:
|
||||
http-request set-var-fmt(txn.forwarded) "for=\"%[ipv6(::1),rfc7239_nn]:%[str(8888),rfc7239_np]\";host=\"haproxy.org\";proto=http"
|
||||
# check RFC-compliancy:
|
||||
http-request set-var(txn.test) "var(txn.forwarded),debug(test,stderr),rfc7239_is_valid,debug(test,stderr)"
|
||||
# stderr output:
|
||||
# [debug] test: type=str <for="[::1]:_8888";host="haproxy.org";proto=http>
|
||||
# [debug] test: type=bool <1>
|
||||
|
||||
See also: "rfc7239_nn"
|
||||
|
||||
rtrim(<chars>)
|
||||
Skips any characters from <chars> from the end of the string representation
|
||||
of the input sample.
|
||||
|
106
src/http_ext.c
106
src/http_ext.c
@ -1881,12 +1881,118 @@ static int sample_conv_7239_n2np(const struct arg *args, struct sample *smp, voi
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* input: ipv4 address, ipv6 address or str (empty string will result in
|
||||
* "unknown" indentifier, else string will be translated to _obfs
|
||||
* indentifier, prefixed by '_'. Must comply with RFC7239 charset)
|
||||
*
|
||||
* output: rfc7239-compliant forwarded header nodename
|
||||
*/
|
||||
static int sample_conv_7239_nn(const struct arg *args, struct sample *smp, void *private)
|
||||
{
|
||||
struct buffer *trash = get_trash_chunk();
|
||||
|
||||
switch (smp->data.type) {
|
||||
case SMP_T_IPV4:
|
||||
{
|
||||
unsigned char *pn = (unsigned char *)&(smp->data.u.ipv4);
|
||||
|
||||
chunk_printf(trash, "%d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]);
|
||||
break;
|
||||
}
|
||||
case SMP_T_IPV6:
|
||||
_7239_print_ip6(trash, &smp->data.u.ipv6, 1);
|
||||
break;
|
||||
case SMP_T_STR:
|
||||
case_str:
|
||||
{
|
||||
struct ist validate_n = ist2(smp->data.u.str.area, smp->data.u.str.data);
|
||||
|
||||
if (!istlen(validate_n)) {
|
||||
// empty -> unknown
|
||||
chunk_printf(trash, "unknown");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(http_7239_extract_obfs(&validate_n, NULL) && !istlen(validate_n)))
|
||||
return 0; /* invalid input */
|
||||
// output with '_' prefix
|
||||
chunk_printf(trash, "_%.*s", (int)smp->data.u.str.data, smp->data.u.str.area);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (sample_casts[smp->data.type][SMP_T_STR] &&
|
||||
sample_casts[smp->data.type][SMP_T_STR](smp))
|
||||
goto case_str;
|
||||
return 0; /* unexpected */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
smp->data.u.str = *trash;
|
||||
smp->data.type = SMP_T_STR;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* input: unsigned integer or str (string will be translated to _obfs
|
||||
* indentifier, prefixed by '_'. Must comply with RFC7239 charset)
|
||||
*
|
||||
* output: rfc7239-compliant forwarded header nodeport
|
||||
*/
|
||||
static int sample_conv_7239_np(const struct arg *args, struct sample *smp, void *private)
|
||||
{
|
||||
struct buffer *trash = get_trash_chunk();
|
||||
|
||||
switch (smp->data.type) {
|
||||
case SMP_T_SINT:
|
||||
{
|
||||
chunk_printf(trash, "%u", (unsigned int)smp->data.u.sint);
|
||||
break;
|
||||
}
|
||||
case SMP_T_STR:
|
||||
case_str:
|
||||
{
|
||||
struct ist validate_n = ist2(smp->data.u.str.area, smp->data.u.str.data);
|
||||
|
||||
if (!istlen(validate_n))
|
||||
return 0;
|
||||
|
||||
if (!(http_7239_extract_obfs(&validate_n, NULL) && !istlen(validate_n)))
|
||||
return 0; /* invalid input */
|
||||
// output with '_' prefix
|
||||
chunk_printf(trash, "_%.*s", (int)smp->data.u.str.data, smp->data.u.str.area);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (sample_casts[smp->data.type][SMP_T_STR] &&
|
||||
sample_casts[smp->data.type][SMP_T_STR](smp))
|
||||
goto case_str;
|
||||
return 0; /* unexpected */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
smp->data.u.str = *trash;
|
||||
smp->data.type = SMP_T_STR;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/* Note: must not be declared <const> as its list will be overwritten */
|
||||
static struct sample_conv_kw_list sample_conv_kws = {ILH, {
|
||||
{ "rfc7239_is_valid", sample_conv_7239_valid, 0, NULL, SMP_T_STR, SMP_T_BOOL},
|
||||
{ "rfc7239_field", sample_conv_7239_field, ARG1(1,STR), NULL, SMP_T_STR, SMP_T_STR},
|
||||
{ "rfc7239_n2nn", sample_conv_7239_n2nn, 0, NULL, SMP_T_STR, SMP_T_ANY},
|
||||
{ "rfc7239_n2np", sample_conv_7239_n2np, 0, NULL, SMP_T_STR, SMP_T_ANY},
|
||||
{ "rfc7239_nn", sample_conv_7239_nn, 0, NULL, SMP_T_ANY, SMP_T_STR},
|
||||
{ "rfc7239_np", sample_conv_7239_np, 0, NULL, SMP_T_ANY, SMP_T_STR},
|
||||
{ NULL, NULL, 0, 0, 0 },
|
||||
}};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user