masque_uri_udp_bind (masque v0.7.0)
View SourceConnect-UDP-Bind URI and header helpers.
Sibling of masque_uri that adds bind-specific bits the existing UDP matcher must not learn about:
- The percent-encoded
*wildcard fortarget_host/target_port, meaning "unscoped" - the bind socket can talk to any peer the proxy's policy allows. The standardmasque_uri:match/2rejects*as a host/port, so the dispatch path uses this matcher instead when theConnect-UDP-Bindrequest header is present. - Parse and format the two HTTP fields the draft adds:
Connect-UDP-Bind- RFC 9651 Boolean. Both endpoints send?1to indicate support; bind is only enabled once each side has both sent and received it.Proxy-Public-Address- RFC 9651 List of String items, each a"ip:port"tuple (IPv6 literals bracketed). Required on a successful bind response; absent / malformed / empty must fail the handshake.
This module is pure - no I/O, no state. It is deliberately additive: nothing here changes the behaviour of masque_uri for legacy CONNECT-UDP requests.
Summary
Functions
Classify a bind target without doing any URI work. Useful for callers that already hold the parsed values.
Expand a CONNECT-UDP URI template for a bind handshake. Accepts the typed bind_target() (unscoped or {Host, Port}) and a raw vars() map; the unscoped form encodes both target variables as the wildcard *.
The header pair to emit for Connect-UDP-Bind: ?1 on a request or response.
Render a list of {ip, port} tuples for emission on a response. Bracket IPv6 literals; quote each entry as a Structured Field String. Crashes on an empty list - draft-11 requires at least one valid entry on a successful bind 2xx.
Match a request path against a CONNECT-UDP URI template, accepting * (or its percent-encoded form %2A) for both target_host and target_port to mean "unscoped bind". Returns a map carrying the literal target plus a bind classification so the caller can dispatch accordingly. Falls back to the legacy masque_uri:match/2 when neither variable is the wildcard.
Read the Connect-UDP-Bind field from a Headers list. Per draft-11, invalid value types are treated as absent (the library returns invalid so the caller can choose to log or reject; the dispatch path treats invalid the same as absent).
Parse the Proxy-Public-Address field. Distinguishes absent, malformed (the field is present but does not parse as a list of "ip:port" strings), and empty (the list parses but has zero usable entries) so the bind client can fail the handshake on any of those.
Types
-type bind_header_value() :: bind | absent | invalid.
-type bind_match() :: #{target_host := binary() | '*', target_port := 1..65535 | '*', bind := unscoped | scoped}.
-type bind_target() :: unscoped | {Host :: binary(), Port :: 1..65535}.
-type proxy_public_address_error() :: absent | malformed | empty.
Functions
-spec classify(bind_match() | bind_target()) -> unscoped | scoped.
Classify a bind target without doing any URI work. Useful for callers that already hold the parsed values.
-spec expand(binary(), bind_target() | masque_uri_template:vars()) -> binary().
Expand a CONNECT-UDP URI template for a bind handshake. Accepts the typed bind_target() (unscoped or {Host, Port}) and a raw vars() map; the unscoped form encodes both target variables as the wildcard *.
The unscoped path goes directly through masque_uri_template:expand/2 rather than masque_uri:expand/2 because the latter's vars() typespec narrows target_port to an integer port number, while we need a wildcard string for an unscoped bind.
The header pair to emit for Connect-UDP-Bind: ?1 on a request or response.
-spec format_proxy_public_address([{inet:ip_address(), inet:port_number()}]) -> binary().
Render a list of {ip, port} tuples for emission on a response. Bracket IPv6 literals; quote each entry as a Structured Field String. Crashes on an empty list - draft-11 requires at least one valid entry on a successful bind 2xx.
-spec match(binary(), binary()) -> {ok, bind_match()} | {error, no_match | bad_port | bad_host | bad_template}.
Match a request path against a CONNECT-UDP URI template, accepting * (or its percent-encoded form %2A) for both target_host and target_port to mean "unscoped bind". Returns a map carrying the literal target plus a bind classification so the caller can dispatch accordingly. Falls back to the legacy masque_uri:match/2 when neither variable is the wildcard.
-spec parse_bind_header([{binary(), binary()}]) -> bind_header_value().
Read the Connect-UDP-Bind field from a Headers list. Per draft-11, invalid value types are treated as absent (the library returns invalid so the caller can choose to log or reject; the dispatch path treats invalid the same as absent).
-spec parse_proxy_public_address([{binary(), binary()}]) -> {ok, [{inet:ip_address(), inet:port_number()}]} | {error, proxy_public_address_error()}.
Parse the Proxy-Public-Address field. Distinguishes absent, malformed (the field is present but does not parse as a list of "ip:port" strings), and empty (the list parses but has zero usable entries) so the bind client can fail the handshake on any of those.