From 8dcf78d9e6b6c5f5d0a255dec07b4dfa5dfc4c9d Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Thu, 19 Sep 2024 18:44:47 +0800 Subject: [PATCH] improve the proxy section --- content/docs/masque/proxy.md | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/content/docs/masque/proxy.md b/content/docs/masque/proxy.md index 3b6e4bc..6d89bfc 100644 --- a/content/docs/masque/proxy.md +++ b/content/docs/masque/proxy.md @@ -8,11 +8,11 @@ To create a MASQUE proxy server, the following steps are necessary: 1. Set up an HTTP/3 server that defines an `http.Handler` for the URI template. 2. Decode the client's request and create a socket to the target. -3. Use the `masque.Proxy` to handle proxying of the UDP packet flow. +3. Use the `masque.Proxy` to handle proxying UDP of the UDP packet flow. ## URI Templates -HTTP clients are configured to use a UDP proxy with a URI Template ([RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570)). +HTTP clients are configured to use a CONNECT-UDP proxy with a URI Template ([RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570)). This URI template encodes the target host and port number. For example, for a proxy running on `https://proxy.example.com`, these are possible URI templates: @@ -44,7 +44,9 @@ http.Handle("/masque", func(w http.ResponseWriter, r *http.Request) { return } - // Optional: Whitelisting / blacklisting logic. + // optional: whitelisting / blacklisting logic + + // start proxying UDP datagrams back and forth err = proxy.Proxy(w, mreq) // ... error handling } @@ -54,24 +56,23 @@ s := http3.Server{Addr: ":4443"} s.ListenAndServeTLS(, ) ``` -The error returned from `masque ParseRequest` is a `masque.RequestParseError`, which contains a field 'HTTPStatus'. This allows the proxy to reject -invalid requests with the correct HTTP status code. +`masque.ParseRequest` parses the Extended CONNECT request, and extracts the target host and port from the URI template. If parsing of the request fails, it returns a `masque.RequestParseError`. This struct contains a field 'HTTPStatus', allowing the application to reject invalid requests with the correct HTTP status code. -The `masque Request.Target` contains the requested target as `{target_host}:{target_port}`. Proxies can implement custom logic to decide which proxying requests are permissible. +The `masque Request.Target` contains the requested target encoded as `{target_host}:{target_port}`. Applications can implement custom logic to decide which proxying requests are permissible. {{< callout type="warning" >}} Applications may add custom header fields to the response header, but must not call `WriteHeader` on the `http.ResponseWriter` The header is sent when `Proxy.Proxy` is called. -{{< / callout >}} +{{< /callout >}} For more details on how to set up and configure an HTTP/3 server, see [Serving HTTP/3]({{< relref "../http3/server.md" >}}). -## Controlling the Socket +## Managing UDP Sockets -`proxy.Proxy` creates a new connected UDP socket on `:0` to send UDP datagrams to the target. +The `proxy.Proxy` function used above creates a new connected UDP socket on `:0` to send UDP datagrams to the target. -An application that wishes a more fine-grained control over the socket can use `Proxy.ProxyConnectedSocket` instead of `Proxy.Proxy`: +An application that wishes a more fine-grained control over the socket can instead use `Proxy.ProxyConnectedSocket`: ```go http.Handle("/masque", func(w http.ResponseWriter, r *http.Request) { // parse the UDP proxying request @@ -89,6 +90,14 @@ http.Handle("/masque", func(w http.ResponseWriter, r *http.Request) { } ``` +The `net.UDPConn` passed to `ProxyConnectedSocket` is closed by the proxy after proxying is done. + +{{< callout type="warning" >}} + Note that it is currently not possible to use unconnected UDP sockets (issue [#3]((https://github.com/quic-go/masque-go/issues/3))). + It is invalid to pass an unconnected socket to `ProxyConnectedSocket`. +{{< /callout >}} + + ## 📝 Future Work