-
Notifications
You must be signed in to change notification settings - Fork 619
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue #271: Support websocket for HTTPS upstream
This patch adds support for websockets on HTTPS upstream servers. Fixes #271
- Loading branch information
1 parent
77a489c
commit 94beb91
Showing
4 changed files
with
149 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package proxy | ||
|
||
import ( | ||
"bytes" | ||
"io" | ||
"net/http" | ||
"net/http/httptest" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/fabiolb/fabio/config" | ||
"github.com/fabiolb/fabio/route" | ||
|
||
"golang.org/x/net/websocket" | ||
) | ||
|
||
func TestProxyWSUpstream(t *testing.T) { | ||
wsServer := httptest.NewServer(websocket.Handler(wsEchoHandler)) | ||
defer wsServer.Close() | ||
t.Log("Started WS server: ", wsServer.URL) | ||
|
||
wssServer := httptest.NewUnstartedServer(websocket.Handler(wsEchoHandler)) | ||
wssServer.TLS = tlsServerConfig() | ||
wssServer.StartTLS() | ||
defer wssServer.Close() | ||
t.Log("Started WSS server: ", wssServer.URL) | ||
|
||
routes := "route add ws /ws " + wsServer.URL + "\n" | ||
routes += "route add ws /wss " + wssServer.URL + ` opts "proto=https"` + "\n" | ||
routes += "route add ws /insecure " + wssServer.URL + ` opts "proto=https tlsskipverify=true"` | ||
|
||
httpProxy := httptest.NewServer(&HTTPProxy{ | ||
Config: config.Proxy{NoRouteStatus: 404}, | ||
Transport: &http.Transport{TLSClientConfig: tlsClientConfig()}, | ||
InsecureTransport: &http.Transport{TLSClientConfig: tlsInsecureConfig()}, | ||
Lookup: func(r *http.Request) *route.Target { | ||
tbl, _ := route.NewTable(routes) | ||
return tbl.Lookup(r, "", route.Picker["rr"], route.Matcher["prefix"]) | ||
}, | ||
}) | ||
defer httpProxy.Close() | ||
t.Log("Started HTTP proxy: ", httpProxy.URL) | ||
|
||
httpsProxy := httptest.NewUnstartedServer(&HTTPProxy{ | ||
Config: config.Proxy{NoRouteStatus: 404}, | ||
Transport: &http.Transport{TLSClientConfig: tlsClientConfig()}, | ||
InsecureTransport: &http.Transport{TLSClientConfig: tlsInsecureConfig()}, | ||
Lookup: func(r *http.Request) *route.Target { | ||
tbl, _ := route.NewTable(routes) | ||
return tbl.Lookup(r, "", route.Picker["rr"], route.Matcher["prefix"]) | ||
}, | ||
}) | ||
httpsProxy.TLS = tlsServerConfig() | ||
httpsProxy.StartTLS() | ||
defer httpsProxy.Close() | ||
t.Log("Started HTTPS proxy: ", httpsProxy.URL) | ||
|
||
wsServerURL := wsServer.URL[len("http://"):] | ||
wssServerURL := wssServer.URL[len("https://"):] | ||
httpProxyURL := httpProxy.URL[len("http://"):] | ||
httpsProxyURL := httpsProxy.URL[len("https://"):] | ||
|
||
t.Run("ws-ws direct", func(t *testing.T) { testWSEcho(t, "ws://"+wsServerURL+"/ws") }) | ||
t.Run("wss-wss direct", func(t *testing.T) { testWSEcho(t, "wss://"+wssServerURL+"/wss") }) | ||
|
||
t.Run("ws-ws via http proxy", func(t *testing.T) { testWSEcho(t, "ws://"+httpProxyURL+"/ws") }) | ||
t.Run("wss-ws via https proxy", func(t *testing.T) { testWSEcho(t, "wss://"+httpsProxyURL+"/ws") }) | ||
|
||
t.Run("ws-wss via http proxy", func(t *testing.T) { testWSEcho(t, "ws://"+httpProxyURL+"/wss") }) | ||
t.Run("wss-wss via https proxy", func(t *testing.T) { testWSEcho(t, "wss://"+httpsProxyURL+"/wss") }) | ||
|
||
t.Run("ws-wss tlsskipverify=true via http proxy", func(t *testing.T) { testWSEcho(t, "ws://"+httpProxyURL+"/insecure") }) | ||
t.Run("wss-wss tlsskipverify=true via https proxy", func(t *testing.T) { testWSEcho(t, "wss://"+httpsProxyURL+"/insecure") }) | ||
} | ||
|
||
func testWSEcho(t *testing.T, url string) { | ||
cfg, err := websocket.NewConfig(url, "http://localhost/") | ||
if err != nil { | ||
t.Fatalf("NewConfig: ", err) | ||
} | ||
if strings.HasPrefix(url, "wss://") { | ||
cfg.TlsConfig = tlsClientConfig() | ||
} | ||
ws, err := websocket.DialConfig(cfg) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
defer ws.Close() | ||
|
||
send := []byte("foo") | ||
if _, err := ws.Write([]byte("foo")); err != nil { | ||
t.Logf("ws.Write failed: %s", err) | ||
} | ||
recv := make([]byte, 100) | ||
n, err := ws.Read(recv) | ||
if err != nil { | ||
t.Logf("ws.Read failed: %s", err) | ||
} | ||
recv = recv[:n] | ||
if got, want := recv, send; !bytes.Equal(got, want) { | ||
t.Fatalf("got %q want %q", got, want) | ||
} | ||
} | ||
|
||
func wsEchoHandler(ws *websocket.Conn) { | ||
io.Copy(ws, ws) | ||
} |