diff --git a/api.go b/api.go index 4d6569e..e52ec25 100644 --- a/api.go +++ b/api.go @@ -16,6 +16,7 @@ import ( "github.com/gorilla/mux" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/urfave/negroni" + "github.com/yhat/wsutil" ) func main() { @@ -37,10 +38,19 @@ func main() { // Reverse proxy (needs to be the first route, to make sure it is the first thing we check) proxyHandler := handlers.NewMultipleHostReverseProxy() + websocketProxyHandler := handlers.NewMultipleHostWebsocketReverseProxy() + + proxyMultiplexer := func(rw http.ResponseWriter, r *http.Request) { + if wsutil.IsWebSocketRequest(r) { + websocketProxyHandler.ServeHTTP(rw, r) + } else { + proxyHandler.ServeHTTP(rw, r) + } + } // Specific routes - r.Host(`{node:ip[0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}}-{port:[0-9]*}.{tld:.*}`).Handler(proxyHandler) - r.Host(`{node:ip[0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}}.{tld:.*}`).Handler(proxyHandler) + r.Host(`{node:ip[0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}}-{port:[0-9]*}.{tld:.*}`).HandlerFunc(proxyMultiplexer) + r.Host(`{node:ip[0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}}.{tld:.*}`).HandlerFunc(proxyMultiplexer) r.HandleFunc("/ping", handlers.Ping).Methods("GET") r.HandleFunc("/sessions/{sessionId}", handlers.GetSession).Methods("GET") r.Handle("/sessions/{sessionId}/instances", http.HandlerFunc(handlers.NewInstance)).Methods("POST") diff --git a/handlers/websocket_reverseproxy.go b/handlers/websocket_reverseproxy.go new file mode 100644 index 0000000..c91c25e --- /dev/null +++ b/handlers/websocket_reverseproxy.go @@ -0,0 +1,54 @@ +package handlers + +import ( + "crypto/tls" + "fmt" + "net" + "net/http" + "strings" + + "github.com/franela/play-with-docker/config" + "github.com/gorilla/mux" + "github.com/yhat/wsutil" +) + +func NewMultipleHostWebsocketReverseProxy() *wsutil.ReverseProxy { + tlsConfig := &tls.Config{ + InsecureSkipVerify: true, + } + director := func(req *http.Request) { + v := mux.Vars(req) + node := v["node"] + port := v["port"] + hostPort := strings.Split(req.Host, ":") + + // give priority to the URL host port + if len(hostPort) > 1 && hostPort[1] != config.PortNumber { + port = hostPort[1] + } else if port == "" { + port = "80" + } + + if strings.HasPrefix(node, "ip") { + // Node is actually an ip, need to convert underscores by dots. + ip := strings.Replace(strings.TrimPrefix(node, "ip"), "_", ".", -1) + + if net.ParseIP(ip) == nil { + // Not a valid IP, so treat this is a hostname. + } else { + node = ip + } + } + + if port == "443" { + // Only proxy http for now + req.URL.Scheme = "wss" + } else { + // Only proxy http for now + req.URL.Scheme = "ws" + } + req.URL.Host = fmt.Sprintf("%s:%s", node, port) + } + + return &wsutil.ReverseProxy{Director: director, TLSClientConfig: tlsConfig} +}