package websocket
import (
"fmt"
"net"
"net/url"
"strconv"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
)
type Addr struct {
*url .URL
}
var _ net .Addr = (*Addr )(nil )
func (addr *Addr ) Network () string {
return "websocket"
}
func NewAddr (host string ) *Addr {
return NewAddrWithScheme (host , false )
}
func NewAddrWithScheme (host string , isSecure bool ) *Addr {
scheme := "ws"
if isSecure {
scheme = "wss"
}
return &Addr {
URL : &url .URL {
Scheme : scheme ,
Host : host ,
},
}
}
func ConvertWebsocketMultiaddrToNetAddr (maddr ma .Multiaddr ) (net .Addr , error ) {
url , err := parseMultiaddr (maddr )
if err != nil {
return nil , err
}
return &Addr {URL : url }, nil
}
func ParseWebsocketNetAddr (a net .Addr ) (ma .Multiaddr , error ) {
wsa , ok := a .(*Addr )
if !ok {
return nil , fmt .Errorf ("not a websocket address" )
}
var (
tcpma ma .Multiaddr
err error
port int
host = wsa .Hostname ()
)
if portStr := wsa .Port (); portStr != "" {
port , err = strconv .Atoi (portStr )
if err != nil {
return nil , fmt .Errorf ("failed to parse port '%q': %s" , portStr , err )
}
} else {
return nil , fmt .Errorf ("invalid port in url: '%q'" , wsa .URL )
}
if ip := net .ParseIP (host ); ip != nil {
tcpma , err = manet .FromNetAddr (&net .TCPAddr {
IP : ip ,
Port : port ,
})
if err != nil {
return nil , err
}
} else {
tcpma , err = ma .NewMultiaddr (fmt .Sprintf ("/dns/%s/tcp/%d" , host , port ))
if err != nil {
return nil , err
}
}
wsma , err := ma .NewMultiaddr ("/" + wsa .Scheme )
if err != nil {
return nil , err
}
return tcpma .Encapsulate (wsma ), nil
}
func parseMultiaddr(maddr ma .Multiaddr ) (*url .URL , error ) {
parsed , err := parseWebsocketMultiaddr (maddr )
if err != nil {
return nil , err
}
scheme := "ws"
if parsed .isWSS {
scheme = "wss"
}
network , host , err := manet .DialArgs (parsed .restMultiaddr )
if err != nil {
return nil , err
}
switch network {
case "tcp" , "tcp4" , "tcp6" :
default :
return nil , fmt .Errorf ("unsupported websocket network %s" , network )
}
return &url .URL {
Scheme : scheme ,
Host : host ,
}, nil
}
type parsedWebsocketMultiaddr struct {
isWSS bool
sni *ma .Component
restMultiaddr ma .Multiaddr
}
func parseWebsocketMultiaddr(a ma .Multiaddr ) (parsedWebsocketMultiaddr , error ) {
out := parsedWebsocketMultiaddr {}
withoutWss := a .Decapsulate (wssComponent .Multiaddr ())
if !withoutWss .Equal (a ) {
a = withoutWss .Encapsulate (tlsWsAddr )
}
withoutWs := a .Decapsulate (wsComponent .Multiaddr ())
if withoutWs .Equal (a ) {
return out , fmt .Errorf ("not a websocket multiaddr" )
}
rest := withoutWs
out .restMultiaddr = withoutWs
for {
var head *ma .Component
rest , head = ma .SplitLast (rest )
if head == nil || len (rest ) == 0 {
break
}
if head .Protocol ().Code == ma .P_SNI {
out .sni = head
} else if head .Protocol ().Code == ma .P_TLS {
out .isWSS = true
out .restMultiaddr = rest
break
}
}
return out , nil
}
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 .