package websocket
import (
"errors"
"io"
"net"
"sync"
"time"
"github.com/libp2p/go-libp2p/core/network"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
ws "github.com/gorilla/websocket"
)
var GracefulCloseTimeout = 100 * time .Millisecond
type Conn struct {
*ws .Conn
Scope network .ConnManagementScope
secure bool
DefaultMessageType int
reader io .Reader
closeOnceVal func () error
laddr ma .Multiaddr
raddr ma .Multiaddr
readLock, writeLock sync .Mutex
}
var _ net .Conn = (*Conn )(nil )
var _ manet .Conn = (*Conn )(nil )
func newConn(raw *ws .Conn , secure bool , scope network .ConnManagementScope ) *Conn {
lna := NewAddrWithScheme (raw .LocalAddr ().String (), secure )
laddr , err := manet .FromNetAddr (lna )
if err != nil {
log .Errorf ("BUG: invalid localaddr on websocket conn" , raw .LocalAddr ())
return nil
}
rna := NewAddrWithScheme (raw .RemoteAddr ().String (), secure )
raddr , err := manet .FromNetAddr (rna )
if err != nil {
log .Errorf ("BUG: invalid remoteaddr on websocket conn" , raw .RemoteAddr ())
return nil
}
c := &Conn {
Conn : raw ,
Scope : scope ,
secure : secure ,
DefaultMessageType : ws .BinaryMessage ,
laddr : laddr ,
raddr : raddr ,
}
c .closeOnceVal = sync .OnceValue (c .closeOnceFn )
return c
}
func (c *Conn ) LocalMultiaddr () ma .Multiaddr {
return c .laddr
}
func (c *Conn ) RemoteMultiaddr () ma .Multiaddr {
return c .raddr
}
func (c *Conn ) Read (b []byte ) (int , error ) {
c .readLock .Lock ()
defer c .readLock .Unlock ()
if c .reader == nil {
if err := c .prepNextReader (); err != nil {
return 0 , err
}
}
for {
n , err := c .reader .Read (b )
switch err {
case io .EOF :
c .reader = nil
if n > 0 {
return n , nil
}
if err := c .prepNextReader (); err != nil {
return 0 , err
}
default :
return n , err
}
}
}
func (c *Conn ) prepNextReader () error {
t , r , err := c .Conn .NextReader ()
if err != nil {
if wserr , ok := err .(*ws .CloseError ); ok {
if wserr .Code == 1000 || wserr .Code == 1005 {
return io .EOF
}
}
return err
}
if t == ws .CloseMessage {
return io .EOF
}
c .reader = r
return nil
}
func (c *Conn ) Write (b []byte ) (n int , err error ) {
c .writeLock .Lock ()
defer c .writeLock .Unlock ()
if err := c .Conn .WriteMessage (c .DefaultMessageType , b ); err != nil {
return 0 , err
}
return len (b ), nil
}
func (c *Conn ) Close () error {
return c .closeOnceVal ()
}
func (c *Conn ) closeOnceFn () error {
err1 := c .Conn .WriteControl (
ws .CloseMessage ,
ws .FormatCloseMessage (ws .CloseNormalClosure , "closed" ),
time .Now ().Add (GracefulCloseTimeout ),
)
err2 := c .Conn .Close ()
return errors .Join (err1 , err2 )
}
func (c *Conn ) LocalAddr () net .Addr {
return NewAddrWithScheme (c .Conn .LocalAddr ().String (), c .secure )
}
func (c *Conn ) RemoteAddr () net .Addr {
return NewAddrWithScheme (c .Conn .RemoteAddr ().String (), c .secure )
}
func (c *Conn ) SetDeadline (t time .Time ) error {
if err := c .SetReadDeadline (t ); err != nil {
return err
}
return c .SetWriteDeadline (t )
}
func (c *Conn ) SetReadDeadline (t time .Time ) error {
return c .Conn .SetReadDeadline (t )
}
func (c *Conn ) SetWriteDeadline (t time .Time ) error {
c .writeLock .Lock ()
defer c .writeLock .Unlock ()
return c .Conn .SetWriteDeadline (t )
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .