theunknownport
theunknownport

Reputation: 1

How do I implement a wss reverse proxy as a gin route?

I just recently started coding in go and thus I am not so skilled yet.

I have a use case where I want to implement a reverse proxy as a gin route. So my route looks like this:

server.router.POST("/console", server.proxyConsoleUrl)

And my handler function something like this:

func (server *Server) proxyConsoleUrl(ctx *gin.Context) {
    director := func(req *http.Request) {
        r := ctx.Request
        // This is not working, scheme wss is not supported
        req.URL.Scheme = "wss"
        req.URL.Host = "192.168.******:8006"
        // The path which gets proxied should be overriden
        req.URL.RawPath = "/api2/json/nodes/something/qemu/123/vncwebsocket?port=5900&vncticket=something"
        req.Header["my-header"] = []string{r.Header.Get("my-header")}
        // Golang camelcases headers
        delete(req.Header, "My-Header")
        // This header has to be added to every request which gets proxied
        req.Header["Authorization"] = []string{"MYCUSTOMHEADER"}
    }
    proxy := &httputil.ReverseProxy{Director: director, Transport: &http.Transport{
        Proxy: http.ProxyFromEnvironment,
        Dial: (&net.Dialer{
            Timeout:   30 * time.Second,
            KeepAlive: 30 * time.Second,
        }).Dial,
        TLSHandshakeTimeout: 10 * time.Second,
        TLSClientConfig:     &tls.Config{InsecureSkipVerify: true},
    }}
    proxy.ServeHTTP(ctx.Writer, ctx.Request)
}

So my first problem is, that httputil.ReverseProxy doesn't support web socket as far as I know and noticed when running my code:

httputil: unsupported protocol scheme "wss"

The Second problem is, that I want to override the backend url as well as add custom headers which are added by the proxy.

Maybe someone has an idea hot to implement this, if it's even possible. -Thanks in advance

Upvotes: 0

Views: 1741

Answers (1)

Thundercat
Thundercat

Reputation: 120999

WebSocket support was added to httputil.ReverseProxy in Go version 1.12.

Use the result of url.Parse("https://192.168.******:8006/api2/json/nodes/something/qemu/123/vncwebsocket?port=5900&vncticket=something") to set the target URL. This fixes the following issues:

  • The WebSocket protocol uses "http" or "https" on the wire, not "wss".
  • RawPath is ignored when RawPath is a not a valid escaping of Path. See EscapedPath for the details. Because the RawPath in the question includes a query string, it will never be a valid escaping of Path. The client's path is alway used as is. Ditto for the client's query string.

Create the proxy once and reuse it. The important point is to create an reuse a single transport per the Transport documentation. Reusing the proxy accomplishes that goal.

func createProxy() *httputil.ReverseProxy {
    target, _ := url.Parse("https://192.168.******:8006/api2/json/nodes/something/qemu/123/vncwebsocket?port=5900&vncticket=something")
    director := func(req *http.Request) {
        req.URL = target
        req.Header["my-header"] = []string{req.Header.Get("my-header")}
        delete(req.Header, "My-Header")
        req.Header["Authorization"] = []string{"MYCUSTOMHEADER"}
    }
    return &httputil.ReverseProxy{Director: director, Transport: &http.Transport{
        Proxy: http.ProxyFromEnvironment,
        Dial: (&net.Dialer{
            Timeout:   30 * time.Second,
            KeepAlive: 30 * time.Second,
        }).Dial,
        TLSHandshakeTimeout: 10 * time.Second,
        TLSClientConfig:     &tls.Config{InsecureSkipVerify: true},
    }}
}

var proxy = createProxy()

func (server *Server) proxyConsoleUrl(ctx *gin.Context) {
    proxy.ServeHTTP(ctx.Writer, ctx.Request)
}

Upvotes: 1

Related Questions