websocket: add support for websocket urls without a port specified

Some servers return a websocket url without a port. This patch
automatically adds :80 for ws and :443 for wss.

Change-Id: Ifdcdbda8f87c994a5f351234c83bf4a07be34ea2
Reviewed-on: https://go-review.googlesource.com/2210
Reviewed-by: Mikio Hara <mikioh.mikioh@gmail.com>
diff --git a/websocket/client.go b/websocket/client.go
index a861bb9..ef11a51 100644
--- a/websocket/client.go
+++ b/websocket/client.go
@@ -64,6 +64,20 @@
 	return DialConfig(config)
 }
 
+var portMap = map[string]string{
+	"ws":  "80",
+	"wss": "443",
+}
+
+func parseAuthority(location *url.URL) string {
+	if _, ok := portMap[location.Scheme]; ok {
+		if _, _, err := net.SplitHostPort(location.Host); err != nil {
+			return net.JoinHostPort(location.Host, portMap[location.Scheme])
+		}
+	}
+	return location.Host
+}
+
 // DialConfig opens a new client connection to a WebSocket with a config.
 func DialConfig(config *Config) (ws *Conn, err error) {
 	var client net.Conn
@@ -75,10 +89,10 @@
 	}
 	switch config.Location.Scheme {
 	case "ws":
-		client, err = net.Dial("tcp", config.Location.Host)
+		client, err = net.Dial("tcp", parseAuthority(config.Location))
 
 	case "wss":
-		client, err = tls.Dial("tcp", config.Location.Host, config.TlsConfig)
+		client, err = tls.Dial("tcp", parseAuthority(config.Location), config.TlsConfig)
 
 	default:
 		err = ErrBadScheme
diff --git a/websocket/websocket_test.go b/websocket/websocket_test.go
index 48f14b6..a376aba 100644
--- a/websocket/websocket_test.go
+++ b/websocket/websocket_test.go
@@ -339,3 +339,76 @@
 	}
 	conn.Close()
 }
+
+var parseAuthorityTests = []struct {
+	in  *url.URL
+	out string
+}{
+	{
+		&url.URL{
+			Scheme: "ws",
+			Host:   "www.google.com",
+		},
+		"www.google.com:80",
+	},
+	{
+		&url.URL{
+			Scheme: "wss",
+			Host:   "www.google.com",
+		},
+		"www.google.com:443",
+	},
+	{
+		&url.URL{
+			Scheme: "ws",
+			Host:   "www.google.com:80",
+		},
+		"www.google.com:80",
+	},
+	{
+		&url.URL{
+			Scheme: "wss",
+			Host:   "www.google.com:443",
+		},
+		"www.google.com:443",
+	},
+	// some invalid ones for parseAuthority. parseAuthority doesn't
+	// concern itself with the scheme unless it actually knows about it
+	{
+		&url.URL{
+			Scheme: "http",
+			Host:   "www.google.com",
+		},
+		"www.google.com",
+	},
+	{
+		&url.URL{
+			Scheme: "http",
+			Host:   "www.google.com:80",
+		},
+		"www.google.com:80",
+	},
+	{
+		&url.URL{
+			Scheme: "asdf",
+			Host:   "127.0.0.1",
+		},
+		"127.0.0.1",
+	},
+	{
+		&url.URL{
+			Scheme: "asdf",
+			Host:   "www.google.com",
+		},
+		"www.google.com",
+	},
+}
+
+func TestParseAuthority(t *testing.T) {
+	for _, tt := range parseAuthorityTests {
+		out := parseAuthority(tt.in)
+		if out != tt.out {
+			t.Errorf("got %v; want %v", out, tt.out)
+		}
+	}
+}