mirror of
https://github.com/yggdrasil-network/yggdrasil-go
synced 2024-11-09 15:10:26 +03:00
Implement websocket (ws:// and wss://) links (#1152)
ws:// can be listened and dialed wss:// is a convenience link for ws:// that supports dialing to ws:// peer. --------- Signed-off-by: Vasyl Gello <vasek.gello@gmail.com> Co-authored-by: Neil Alexander <neilalexander@users.noreply.github.com>
This commit is contained in:
parent
da7ebde828
commit
5ea16e63a1
1
go.mod
1
go.mod
@ -19,6 +19,7 @@ require (
|
||||
golang.org/x/text v0.16.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3
|
||||
nhooyr.io/websocket v1.8.11
|
||||
)
|
||||
|
||||
require (
|
||||
|
2
go.sum
2
go.sum
@ -154,3 +154,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=
|
||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY=
|
||||
nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0=
|
||||
nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
|
||||
|
@ -37,6 +37,8 @@ type links struct {
|
||||
unix *linkUNIX // UNIX interface support
|
||||
socks *linkSOCKS // SOCKS interface support
|
||||
quic *linkQUIC // QUIC interface support
|
||||
ws *linkWS // WS interface support
|
||||
wss *linkWSS // WSS interface support
|
||||
// _links can only be modified safely from within the links actor
|
||||
_links map[linkInfo]*link // *link is nil if connection in progress
|
||||
}
|
||||
@ -97,6 +99,8 @@ func (l *links) init(c *Core) error {
|
||||
l.unix = l.newLinkUNIX()
|
||||
l.socks = l.newLinkSOCKS()
|
||||
l.quic = l.newLinkQUIC()
|
||||
l.ws = l.newLinkWS()
|
||||
l.wss = l.newLinkWSS()
|
||||
l._links = make(map[linkInfo]*link)
|
||||
|
||||
var listeners []ListenAddress
|
||||
@ -417,6 +421,10 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
|
||||
protocol = l.unix
|
||||
case "quic":
|
||||
protocol = l.quic
|
||||
case "ws":
|
||||
protocol = l.ws
|
||||
case "wss":
|
||||
protocol = l.wss
|
||||
default:
|
||||
cancel()
|
||||
return nil, ErrLinkUnrecognisedSchema
|
||||
@ -545,6 +553,10 @@ func (l *links) connect(ctx context.Context, u *url.URL, info linkInfo, options
|
||||
dialer = l.unix
|
||||
case "quic":
|
||||
dialer = l.quic
|
||||
case "ws":
|
||||
dialer = l.ws
|
||||
case "wss":
|
||||
dialer = l.wss
|
||||
default:
|
||||
return nil, ErrLinkUnrecognisedSchema
|
||||
}
|
||||
|
123
src/core/link_ws.go
Normal file
123
src/core/link_ws.go
Normal file
@ -0,0 +1,123 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/Arceliar/phony"
|
||||
"nhooyr.io/websocket"
|
||||
)
|
||||
|
||||
type linkWS struct {
|
||||
phony.Inbox
|
||||
*links
|
||||
}
|
||||
|
||||
type linkWSConn struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
type linkWSListener struct {
|
||||
ch chan *linkWSConn
|
||||
ctx context.Context
|
||||
httpServer *http.Server
|
||||
listener net.Listener
|
||||
}
|
||||
|
||||
type wsServer struct {
|
||||
ch chan *linkWSConn
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (l *linkWSListener) Accept() (net.Conn, error) {
|
||||
qs := <-l.ch
|
||||
if qs == nil {
|
||||
return nil, context.Canceled
|
||||
}
|
||||
return qs, nil
|
||||
}
|
||||
|
||||
func (l *linkWSListener) Addr() net.Addr {
|
||||
return l.listener.Addr()
|
||||
}
|
||||
|
||||
func (l *linkWSListener) Close() error {
|
||||
if err := l.httpServer.Shutdown(l.ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return l.listener.Close()
|
||||
}
|
||||
|
||||
func (s *wsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/health" || r.URL.Path == "/healthz" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("OK"))
|
||||
return
|
||||
}
|
||||
|
||||
c, err := websocket.Accept(w, r, &websocket.AcceptOptions{
|
||||
Subprotocols: []string{"ygg-ws"},
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if c.Subprotocol() != "ygg-ws" {
|
||||
c.Close(websocket.StatusPolicyViolation, "client must speak the ygg-ws subprotocol")
|
||||
return
|
||||
}
|
||||
|
||||
s.ch <- &linkWSConn{
|
||||
Conn: websocket.NetConn(s.ctx, c, websocket.MessageBinary),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *links) newLinkWS() *linkWS {
|
||||
lt := &linkWS{
|
||||
links: l,
|
||||
}
|
||||
return lt
|
||||
}
|
||||
|
||||
func (l *linkWS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
wsconn, _, err := websocket.Dial(ctx, url.String(), &websocket.DialOptions{
|
||||
Subprotocols: []string{"ygg-ws"},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &linkWSConn{
|
||||
Conn: websocket.NetConn(ctx, wsconn, websocket.MessageBinary),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *linkWS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
||||
nl, err := net.Listen("tcp", url.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ch := make(chan *linkWSConn)
|
||||
|
||||
httpServer := &http.Server{
|
||||
Handler: &wsServer{
|
||||
ch: ch,
|
||||
ctx: ctx,
|
||||
},
|
||||
BaseContext: func(_ net.Listener) context.Context { return ctx },
|
||||
ReadTimeout: time.Second * 10,
|
||||
WriteTimeout: time.Second * 10,
|
||||
}
|
||||
|
||||
lwl := &linkWSListener{
|
||||
ch: ch,
|
||||
ctx: ctx,
|
||||
httpServer: httpServer,
|
||||
listener: nl,
|
||||
}
|
||||
go lwl.httpServer.Serve(nl) // nolint:errcheck
|
||||
return lwl, nil
|
||||
}
|
43
src/core/link_wss.go
Normal file
43
src/core/link_wss.go
Normal file
@ -0,0 +1,43 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"github.com/Arceliar/phony"
|
||||
"nhooyr.io/websocket"
|
||||
)
|
||||
|
||||
type linkWSS struct {
|
||||
phony.Inbox
|
||||
*links
|
||||
}
|
||||
|
||||
type linkWSSConn struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (l *links) newLinkWSS() *linkWSS {
|
||||
lwss := &linkWSS{
|
||||
links: l,
|
||||
}
|
||||
return lwss
|
||||
}
|
||||
|
||||
func (l *linkWSS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||
wsconn, _, err := websocket.Dial(ctx, url.String(), &websocket.DialOptions{
|
||||
Subprotocols: []string{"ygg-ws"},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &linkWSSConn{
|
||||
Conn: websocket.NetConn(ctx, wsconn, websocket.MessageBinary),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *linkWSS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
||||
return nil, fmt.Errorf("WSS listener not supported, use WS listener behind reverse proxy instead")
|
||||
}
|
Loading…
Reference in New Issue
Block a user