To allow negotiation of a tunnel for UDP over HTTP, this document defines the "connect-udp" HTTP upgrade token. The resulting UDP tunnels use the Capsule Protocol (see
Section 3.2 of [
HTTP-DGRAM]) with HTTP Datagrams in the format defined in
Section 5.
To initiate a UDP tunnel associated with a single HTTP stream, a client issues a request containing the "connect-udp" upgrade token. The target of the tunnel is indicated by the client to the UDP proxy via the "target_host" and "target_port" variables of the URI Template; see
Section 2.
"target_host" supports using DNS names, IPv6 literals and IPv4 literals. Note that IPv6 scoped addressing zone identifiers are not supported. Using the terms IPv6address, IPv4address, reg-name, and port from [
URI], the "target_host" and "target_port" variables
MUST adhere to the format in
Figure 2, using notation from [
ABNF]. Additionally:
-
both the "target_host" and "target_port" variables MUST NOT be empty.
-
if "target_host" contains an IPv6 literal, the colons (":") MUST be percent-encoded. For example, if the target host is "2001:db8::42", it will be encoded in the URI as "2001%3Adb8%3A%3A42".
-
"target_port" MUST represent an integer between 1 and 65535 inclusive.
target_host = IPv6address / IPv4address / reg-name
target_port = port
When sending its UDP proxying request, the client
SHALL perform URI Template expansion to determine the path and query of its request.
If the request is successful, the UDP proxy commits to converting received HTTP Datagrams into UDP packets, and vice versa, until the tunnel is closed.
By virtue of the definition of the Capsule Protocol (see
Section 3.2 of [
HTTP-DGRAM]), UDP proxying requests do not carry any message content. Similarly, successful UDP proxying responses also do not carry any message content.
Upon receiving a UDP proxying request:
-
if the recipient is configured to use another HTTP proxy, it will act as an intermediary by forwarding the request to another HTTP server. Note that such intermediaries may need to re-encode the request if they forward it using a version of HTTP that is different from the one used to receive it, as the request encoding differs by version (see below).
-
otherwise, the recipient will act as a UDP proxy. It extracts the "target_host" and "target_port" variables from the URI it has reconstructed from the request headers, decodes their percent-encoding, and establishes a tunnel by directly opening a UDP socket to the requested target.
Unlike TCP, UDP is connectionless. The UDP proxy that opens the UDP socket has no way of knowing whether the destination is reachable. Therefore, it needs to respond to the request without waiting for a packet from the target. However, if the "target_host" is a DNS name, the UDP proxy
MUST perform DNS resolution before replying to the HTTP request. If errors occur during this process, the UDP proxy
MUST reject the request and
SHOULD send details using an appropriate Proxy-Status header field [
PROXY-STATUS]. For example, if DNS resolution returns an error, the proxy can use the dns_error Proxy Error Type from
Section 2.3.2 of [
PROXY-STATUS].
UDP proxies can use connected UDP sockets if their operating system supports them, as that allows the UDP proxy to rely on the kernel to only send it UDP packets that match the correct 5-tuple. If the UDP proxy uses a non-connected socket, it
MUST validate the IP source address and UDP source port on received packets to ensure they match the client's request. Packets that do not match
MUST be discarded by the UDP proxy.
The lifetime of the socket is tied to the request stream. The UDP proxy
MUST keep the socket open while the request stream is open. If a UDP proxy is notified by its operating system that its socket is no longer usable, it
MUST close the request stream. For example, this can happen when an ICMP Destination Unreachable message is received; see
Section 3.1 of [
ICMP6]. UDP proxies
MAY choose to close sockets due to a period of inactivity, but they
MUST close the request stream when closing the socket. UDP proxies that close sockets after a period of inactivity
SHOULD NOT use a period lower than two minutes; see
Section 4.3 of [
BEHAVE].
A successful response (as defined in Sections [
3.3] and [
3.5]) indicates that the UDP proxy has opened a socket to the requested target and is willing to proxy UDP payloads. Any response other than a successful response indicates that the request has failed; thus, the client
MUST abort the request.
UDP proxies
MUST NOT introduce fragmentation at the IP layer when forwarding HTTP Datagrams onto a UDP socket; overly large datagrams are silently dropped. In IPv4, the Don't Fragment (DF) bit
MUST be set, if possible, to prevent fragmentation on the path. Future extensions
MAY remove these requirements.
Implementers of UDP proxies will benefit from reading the guidance in [
UDP-USAGE].
When using HTTP/1.1 [
HTTP/1.1], a UDP proxying request will meet the following requirements:
-
the method SHALL be "GET".
-
the request SHALL include a single Host header field containing the origin of the UDP proxy.
-
the request SHALL include a Connection header field with value "Upgrade" (note that this requirement is case-insensitive as per Section 7.6.1 of [HTTP]).
-
the request SHALL include an Upgrade header field with value "connect-udp".
A UDP proxying request that does not conform to these restrictions is malformed. The recipient of such a malformed request
MUST respond with an error and
SHOULD use the 400 (Bad Request) status code.
For example, if the client is configured with URI Template "https://example.org/.well-known/masque/udp/{target_host}/{target_port}/" and wishes to open a UDP proxying tunnel to target 192.0.2.6:443, it could send the following request:
GET https://example.org/.well-known/masque/udp/192.0.2.6/443/ HTTP/1.1
Host: example.org
Connection: Upgrade
Upgrade: connect-udp
Capsule-Protocol: ?1
In HTTP/1.1, this protocol uses the GET method to mimic the design of the WebSocket Protocol [
WEBSOCKET].
The UDP proxy
SHALL indicate a successful response by replying with the following requirements:
-
the HTTP status code on the response SHALL be 101 (Switching Protocols).
-
the response SHALL include a Connection header field with value "Upgrade" (note that this requirement is case-insensitive as per Section 7.6.1 of [HTTP]).
-
the response SHALL include a single Upgrade header field with value "connect-udp".
-
the response SHALL meet the requirements of HTTP responses that start the Capsule Protocol; see Section 3.2 of [HTTP-DGRAM].
If any of these requirements are not met, the client
MUST treat this proxying attempt as failed and abort the connection.
For example, the UDP proxy could respond with:
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: connect-udp
Capsule-Protocol: ?1
When using HTTP/2 [
HTTP/2] or HTTP/3 [
HTTP/3], UDP proxying requests use HTTP Extended CONNECT. This requires that servers send an HTTP Setting as specified in [
EXT-CONNECT2] and [
EXT-CONNECT3] and that requests use HTTP pseudo-header fields with the following requirements:
-
The :method pseudo-header field SHALL be "CONNECT".
-
The :protocol pseudo-header field SHALL be "connect-udp".
-
The :authority pseudo-header field SHALL contain the authority of the UDP proxy.
-
The :path and :scheme pseudo-header fields SHALL NOT be empty. Their values SHALL contain the scheme and path from the URI Template after the URI Template expansion process has been completed.
A UDP proxying request that does not conform to these restrictions is malformed (see
Section 8.1.1 of [
HTTP/2] and
Section 4.1.2 of [
HTTP/3]).
For example, if the client is configured with URI Template "https://example.org/.well-known/masque/udp/{target_host}/{target_port}/" and wishes to open a UDP proxying tunnel to target 192.0.2.6:443, it could send the following request:
HEADERS
:method = CONNECT
:protocol = connect-udp
:scheme = https
:path = /.well-known/masque/udp/192.0.2.6/443/
:authority = example.org
capsule-protocol = ?1
The UDP proxy
SHALL indicate a successful response by replying with the following requirements:
-
the HTTP status code on the response SHALL be in the 2xx (Successful) range.
-
the response SHALL meet the requirements of HTTP responses that start the Capsule Protocol; see Section 3.2 of [HTTP-DGRAM].
If any of these requirements are not met, the client
MUST treat this proxying attempt as failed and abort the request.
For example, the UDP proxy could respond with:
HEADERS
:status = 200
capsule-protocol = ?1