2018-07-04 10:51:47 +00:00
|
|
|
// Package websocket implements a websocket based transport for go-libp2p.
|
|
|
|
package websocket
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-04-01 16:16:46 +00:00
|
|
|
"crypto/tls"
|
|
|
|
"net/http"
|
|
|
|
"time"
|
2018-07-04 10:51:47 +00:00
|
|
|
|
2022-04-01 16:16:46 +00:00
|
|
|
"github.com/libp2p/go-libp2p-core/network"
|
2019-06-09 07:24:20 +00:00
|
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
|
|
|
"github.com/libp2p/go-libp2p-core/transport"
|
2022-04-01 16:16:46 +00:00
|
|
|
|
2018-07-04 10:51:47 +00:00
|
|
|
ma "github.com/multiformats/go-multiaddr"
|
2019-10-04 15:21:24 +00:00
|
|
|
mafmt "github.com/multiformats/go-multiaddr-fmt"
|
2021-06-16 20:19:45 +00:00
|
|
|
manet "github.com/multiformats/go-multiaddr/net"
|
2022-04-01 16:16:46 +00:00
|
|
|
|
|
|
|
ws "github.com/gorilla/websocket"
|
2018-07-04 10:51:47 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// WsFmt is multiaddr formatter for WsProtocol
|
2021-06-16 20:19:45 +00:00
|
|
|
var WsFmt = mafmt.And(mafmt.TCP, mafmt.Base(ma.P_WS))
|
2018-07-04 10:51:47 +00:00
|
|
|
|
2021-06-16 20:19:45 +00:00
|
|
|
// This is _not_ WsFmt because we want the transport to stick to dialing fully
|
|
|
|
// resolved addresses.
|
2022-04-01 16:16:46 +00:00
|
|
|
var dialMatcher = mafmt.And(mafmt.IP, mafmt.Base(ma.P_TCP), mafmt.Or(mafmt.Base(ma.P_WS), mafmt.Base(ma.P_WSS)))
|
2018-07-04 10:51:47 +00:00
|
|
|
|
|
|
|
func init() {
|
2021-10-19 13:43:41 +00:00
|
|
|
manet.RegisterFromNetAddr(ParseWebsocketNetAddr, "websocket")
|
|
|
|
manet.RegisterToNetAddr(ConvertWebsocketMultiaddrToNetAddr, "ws")
|
2022-04-01 16:16:46 +00:00
|
|
|
manet.RegisterToNetAddr(ConvertWebsocketMultiaddrToNetAddr, "wss")
|
2018-07-04 10:51:47 +00:00
|
|
|
}
|
|
|
|
|
2022-04-01 16:16:46 +00:00
|
|
|
// Default gorilla upgrader
|
|
|
|
var upgrader = ws.Upgrader{
|
|
|
|
// Allow requests from *all* origins.
|
|
|
|
CheckOrigin: func(r *http.Request) bool {
|
|
|
|
return true
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
type Option func(*WebsocketTransport) error
|
|
|
|
|
|
|
|
// WithTLSClientConfig sets a TLS client configuration on the WebSocket Dialer. Only
|
|
|
|
// relevant for non-browser usages.
|
|
|
|
//
|
|
|
|
// Some useful use cases include setting InsecureSkipVerify to `true`, or
|
|
|
|
// setting user-defined trusted CA certificates.
|
|
|
|
func WithTLSClientConfig(c *tls.Config) Option {
|
|
|
|
return func(t *WebsocketTransport) error {
|
|
|
|
t.tlsClientConf = c
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithTLSConfig sets a TLS configuration for the WebSocket listener.
|
|
|
|
func WithTLSConfig(conf *tls.Config) Option {
|
|
|
|
return func(t *WebsocketTransport) error {
|
|
|
|
t.tlsConf = conf
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2021-06-16 20:19:45 +00:00
|
|
|
|
2018-07-04 10:51:47 +00:00
|
|
|
// WebsocketTransport is the actual go-libp2p transport
|
|
|
|
type WebsocketTransport struct {
|
2022-04-01 16:16:46 +00:00
|
|
|
upgrader transport.Upgrader
|
|
|
|
rcmgr network.ResourceManager
|
|
|
|
|
|
|
|
tlsClientConf *tls.Config
|
|
|
|
tlsConf *tls.Config
|
2018-07-04 10:51:47 +00:00
|
|
|
}
|
|
|
|
|
2022-04-01 16:16:46 +00:00
|
|
|
var _ transport.Transport = (*WebsocketTransport)(nil)
|
|
|
|
|
|
|
|
func New(u transport.Upgrader, rcmgr network.ResourceManager, opts ...Option) (*WebsocketTransport, error) {
|
|
|
|
if rcmgr == nil {
|
|
|
|
rcmgr = network.NullResourceManager
|
|
|
|
}
|
|
|
|
t := &WebsocketTransport{
|
|
|
|
upgrader: u,
|
|
|
|
rcmgr: rcmgr,
|
|
|
|
}
|
|
|
|
for _, opt := range opts {
|
|
|
|
if err := opt(t); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return t, nil
|
2018-07-04 10:51:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *WebsocketTransport) CanDial(a ma.Multiaddr) bool {
|
2021-06-16 20:19:45 +00:00
|
|
|
return dialMatcher.Matches(a)
|
2018-07-04 10:51:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *WebsocketTransport) Protocols() []int {
|
2022-04-01 16:16:46 +00:00
|
|
|
return []int{ma.P_WS, ma.P_WSS}
|
2018-07-04 10:51:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *WebsocketTransport) Proxy() bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-06-09 07:24:20 +00:00
|
|
|
func (t *WebsocketTransport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (transport.CapableConn, error) {
|
2022-04-01 16:16:46 +00:00
|
|
|
connScope, err := t.rcmgr.OpenConnection(network.DirOutbound, true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-07-04 10:51:47 +00:00
|
|
|
macon, err := t.maDial(ctx, raddr)
|
2022-04-01 16:16:46 +00:00
|
|
|
if err != nil {
|
|
|
|
connScope.Done()
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return t.upgrader.Upgrade(ctx, t, macon, network.DirOutbound, p, connScope)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *WebsocketTransport) maDial(ctx context.Context, raddr ma.Multiaddr) (manet.Conn, error) {
|
|
|
|
wsurl, err := parseMultiaddr(raddr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
isWss := wsurl.Scheme == "wss"
|
|
|
|
dialer := ws.Dialer{HandshakeTimeout: 30 * time.Second}
|
|
|
|
if isWss {
|
|
|
|
dialer.TLSClientConfig = t.tlsClientConf
|
|
|
|
|
|
|
|
}
|
|
|
|
wscon, _, err := dialer.DialContext(ctx, wsurl.String(), nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
mnc, err := manet.WrapNetConn(NewConn(wscon, isWss))
|
|
|
|
if err != nil {
|
|
|
|
wscon.Close()
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return mnc, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *WebsocketTransport) maListen(a ma.Multiaddr) (manet.Listener, error) {
|
|
|
|
l, err := newListener(a, t.tlsConf)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
go l.serve()
|
|
|
|
return l, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *WebsocketTransport) Listen(a ma.Multiaddr) (transport.Listener, error) {
|
|
|
|
malist, err := t.maListen(a)
|
2018-07-04 10:51:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-04-01 16:16:46 +00:00
|
|
|
return t.upgrader.UpgradeListener(t, malist), nil
|
2018-07-04 10:51:47 +00:00
|
|
|
}
|