161 lines
3.5 KiB
Go
161 lines
3.5 KiB
Go
// Package websocket implements a websocket based transport for go-libp2p.
|
|
package websocket
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
|
"github.com/libp2p/go-libp2p-core/transport"
|
|
|
|
tptu "github.com/libp2p/go-libp2p-transport-upgrader"
|
|
|
|
ws "github.com/gorilla/websocket"
|
|
ma "github.com/multiformats/go-multiaddr"
|
|
manet "github.com/multiformats/go-multiaddr-net"
|
|
mafmt "github.com/whyrusleeping/mafmt"
|
|
)
|
|
|
|
// WsProtocol is the multiaddr protocol definition for this transport.
|
|
var WsProtocol = ma.Protocol{
|
|
Code: 477,
|
|
Name: "ws",
|
|
VCode: ma.CodeToVarint(477),
|
|
}
|
|
|
|
// WsFmt is multiaddr formatter for WsProtocol
|
|
var WsFmt = mafmt.And(mafmt.TCP, mafmt.Base(WsProtocol.Code))
|
|
|
|
// WsCodec is the multiaddr-net codec definition for the websocket transport
|
|
var WsCodec = &manet.NetCodec{
|
|
NetAddrNetworks: []string{"websocket"},
|
|
ProtocolName: "ws",
|
|
ConvertMultiaddr: ConvertWebsocketMultiaddrToNetAddr,
|
|
ParseNetAddr: ParseWebsocketNetAddr,
|
|
}
|
|
|
|
// Default gorilla upgrader
|
|
var upgrader = ws.Upgrader{
|
|
// Allow requests from *all* origins.
|
|
CheckOrigin: func(r *http.Request) bool {
|
|
return true
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
err := ma.AddProtocol(WsProtocol)
|
|
if err != nil {
|
|
panic(fmt.Errorf("error registering websocket protocol: %s", err))
|
|
}
|
|
|
|
manet.RegisterNetCodec(WsCodec)
|
|
}
|
|
|
|
// WebsocketTransport is the actual go-libp2p transport
|
|
type WebsocketTransport struct {
|
|
Upgrader *tptu.Upgrader
|
|
}
|
|
|
|
func New(u *tptu.Upgrader) *WebsocketTransport {
|
|
return &WebsocketTransport{u}
|
|
}
|
|
|
|
var _ transport.Transport = (*WebsocketTransport)(nil)
|
|
|
|
func (t *WebsocketTransport) CanDial(a ma.Multiaddr) bool {
|
|
return WsFmt.Matches(a)
|
|
}
|
|
|
|
func (t *WebsocketTransport) Protocols() []int {
|
|
return []int{WsProtocol.Code}
|
|
}
|
|
|
|
func (t *WebsocketTransport) Proxy() bool {
|
|
return false
|
|
}
|
|
|
|
func (t *WebsocketTransport) maDial(ctx context.Context, raddr ma.Multiaddr) (manet.Conn, error) {
|
|
wsurl, err := parseMultiaddr(raddr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
wscon, _, err := ws.DefaultDialer.Dial(wsurl, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
mnc, err := manet.WrapNetConn(NewConn(wscon))
|
|
if err != nil {
|
|
wscon.Close()
|
|
return nil, err
|
|
}
|
|
return mnc, nil
|
|
}
|
|
|
|
func (t *WebsocketTransport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (transport.CapableConn, error) {
|
|
macon, err := t.maDial(ctx, raddr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return t.Upgrader.UpgradeOutbound(ctx, t, macon, p)
|
|
}
|
|
|
|
func (t *WebsocketTransport) maListen(a ma.Multiaddr) (manet.Listener, error) {
|
|
lnet, lnaddr, err := manet.DialArgs(a)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nl, err := net.Listen(lnet, lnaddr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
u, err := url.Parse("http://" + nl.Addr().String())
|
|
if err != nil {
|
|
nl.Close()
|
|
return nil, err
|
|
}
|
|
|
|
malist, err := t.wrapListener(nl, u)
|
|
if err != nil {
|
|
nl.Close()
|
|
return nil, err
|
|
}
|
|
|
|
go malist.serve()
|
|
|
|
return malist, nil
|
|
}
|
|
|
|
func (t *WebsocketTransport) Listen(a ma.Multiaddr) (transport.Listener, error) {
|
|
malist, err := t.maListen(a)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return t.Upgrader.UpgradeListener(t, malist), nil
|
|
}
|
|
|
|
func (t *WebsocketTransport) wrapListener(l net.Listener, origin *url.URL) (*listener, error) {
|
|
laddr, err := manet.FromNetAddr(l.Addr())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
wsma, err := ma.NewMultiaddr("/ws")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
laddr = laddr.Encapsulate(wsma)
|
|
|
|
return &listener{
|
|
laddr: laddr,
|
|
Listener: l,
|
|
incoming: make(chan *Conn),
|
|
closed: make(chan struct{}),
|
|
}, nil
|
|
}
|