Repository: nhooyr/websocket
Pull request #299 (2021-04-24 03:24:44)

Returns real remote and local address instead mocked
Currently, RemoteAddr() and LocalAddr() are mocked. This change allow the real RemoteAddr() and LocalAddr() to be used.

ZackaryWelch: I would like to get this in if possible. It's the only thing this library lacks from gorilla/websocket.
nhooyr: Going to close as I'm assuming this approach won't work. Leaving the issue open at #327 though so I can figure something out here.
nhooyr: Ah I understand, it works server side but would not work with connections from `websocket.Dial` because there we only get a `io.ReadWriteCloser` from `net/http`.
photostorm: What is being store with io.ReadWriterClose when using the websocket.Dial? io.ReadWriterClose is just common abstraction. If the io.ReadWriterClose is being used with net.Conn then we know the local address and remote address methods exists. Just because we see io.ReadWriterClose when looking at the code does not hide the ability to access other methods if what being stored there during runtime supports these methods. Can you provide any screenshots of what is being stored there?
nhooyr: I don't fully remember the type but it's not a net.Conn. Grep the net/http.Transport code for protocol upgrade and you'll find it. I'm heading out right now otherwise I'd link you.
photostorm: I will do exploring with that when I get some time. 

Thanks for your responses.
nhooyr: See https://github.com/nhooyr/websocket/blob/14fb98eba64eeb5e9d06a88b98c47ae924ac82b4/dial.go#L128

We don't get a `net.Conn` from `net/http` when dialing, we only get a `io.ReadWriteCloser`.

Patch:
diff --git a/netconn.go b/netconn.go
index 4af6c20..74000c9 100644
--- a/netconn.go
+++ b/netconn.go
@@ -33,8 +33,13 @@ import (
 // where only the reading/writing goroutines are interrupted but the connection
 // is kept alive.
 //
-// The Addr methods will return a mock net.Addr that returns "websocket" for Network
-// and "websocket/unknown-addr" for String.
+// The Addr methods will return the real addresses for connections obtained
+// from websocket.Accept. But for connections obtained from websocket.Dial, a mock net.Addr
+// will be returned that gives "websocket" for Network() and "websocket/unknown-addr" for
+// String(). This is because websocket.Dial only exposes a io.ReadWriteCloser instead of the
+// full net.Conn to us.
+//
+// When running as WASM, the Addr methods will always return the mock address described above.
 //
 // A received StatusNormalClosure or StatusGoingAway close frame will be translated to
 // io.EOF when reading.
@@ -181,14 +186,6 @@ func (a websocketAddr) String() string {
 	return "websocket/unknown-addr"
 }
 
-func (nc *netConn) RemoteAddr() net.Addr {
-	return websocketAddr{}
-}
-
-func (nc *netConn) LocalAddr() net.Addr {
-	return websocketAddr{}
-}
-
 func (nc *netConn) SetDeadline(t time.Time) error {
 	nc.SetWriteDeadline(t)
 	nc.SetReadDeadline(t)
diff --git a/netconn_js.go b/netconn_js.go
new file mode 100644
index 0000000..ccc8c89
--- /dev/null
+++ b/netconn_js.go
@@ -0,0 +1,11 @@
+package websocket
+
+import "net"
+
+func (nc *netConn) RemoteAddr() net.Addr {
+	return websocketAddr{}
+}
+
+func (nc *netConn) LocalAddr() net.Addr {
+	return websocketAddr{}
+}
diff --git a/netconn_notjs.go b/netconn_notjs.go
new file mode 100644
index 0000000..f3eb0d6
--- /dev/null
+++ b/netconn_notjs.go
@@ -0,0 +1,20 @@
+//go:build !js
+// +build !js
+
+package websocket
+
+import "net"
+
+func (nc *netConn) RemoteAddr() net.Addr {
+	if unc, ok := nc.c.rwc.(net.Conn); ok {
+		return unc.RemoteAddr()
+	}
+	return websocketAddr{}
+}
+
+func (nc *netConn) LocalAddr() net.Addr {
+	if unc, ok := nc.c.rwc.(net.Conn); ok {
+		return unc.LocalAddr()
+	}
+	return websocketAddr{}
+}


Test patch:
diff --git a/conn_test.go b/conn_test.go
index a3f3d78..b9e2063 100644
--- a/conn_test.go
+++ b/conn_test.go
@@ -155,8 +155,8 @@ func TestConn(t *testing.T) {
 		n1.SetDeadline(time.Time{})
 
 		assert.Equal(t, "remote addr", n1.RemoteAddr(), n1.LocalAddr())
-		assert.Equal(t, "remote addr string", "websocket/unknown-addr", n1.RemoteAddr().String())
-		assert.Equal(t, "remote addr network", "websocket", n1.RemoteAddr().Network())
+		assert.Equal(t, "remote addr string", "pipe", n1.RemoteAddr().String())
+		assert.Equal(t, "remote addr network", "pipe", n1.RemoteAddr().Network())
 
 		errs := xsync.Go(func() error {
 			_, err := n2.Write([]byte("hello"))