websocket tunnel interface cleanup
This commit is contained in:
parent
39ada8ec7a
commit
0eaa1aca2a
|
@ -4,15 +4,12 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/packer"
|
"git.coolaj86.com/coolaj86/go-telebitd/mplexer/packer"
|
||||||
|
|
||||||
jwt "github.com/dgrijalva/jwt-go"
|
jwt "github.com/dgrijalva/jwt-go"
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
|
|
||||||
_ "github.com/joho/godotenv/autoload"
|
_ "github.com/joho/godotenv/autoload"
|
||||||
)
|
)
|
||||||
|
@ -20,27 +17,27 @@ import (
|
||||||
func main() {
|
func main() {
|
||||||
// TODO replace the websocket connection with a mock server
|
// TODO replace the websocket connection with a mock server
|
||||||
|
|
||||||
relay := os.Getenv("RELAY") // "wss://roottest.duckdns.org:8443"
|
relay := os.Getenv("RELAY") // "wss://example.com:443"
|
||||||
authz, err := getToken(os.Getenv("SECRET"))
|
authz, err := getToken(os.Getenv("SECRET"))
|
||||||
if nil != err {
|
if nil != err {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
wsd := websocket.Dialer{}
|
|
||||||
headers := http.Header{}
|
mux := packer.NewRouteMux()
|
||||||
headers.Set("Authorization", fmt.Sprintf("Bearer %s", authz))
|
//mux.HandleTLS("*", mux.TerminateTLS(mux))
|
||||||
// *http.Response
|
mux.ForwardTCP("*", "localhost:3000", 120*time.Second)
|
||||||
sep := "?"
|
// TODO set failure
|
||||||
if strings.Contains(relay, sep) {
|
|
||||||
sep = "&"
|
tun, err := packer.DialWebsocketTunnel(ctx, relay, authz)
|
||||||
}
|
|
||||||
wsconn, _, err := wsd.DialContext(ctx, relay+sep+"access_token="+authz, headers)
|
|
||||||
if nil != err {
|
if nil != err {
|
||||||
fmt.Println("relay:", relay)
|
fmt.Println("relay:", relay)
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.Fatal("Closed server: ", packer.ListenAndServe(tun, mux))
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// TODO for http proxy
|
// TODO for http proxy
|
||||||
|
@ -68,15 +65,6 @@ func main() {
|
||||||
t.ListenAndServe("wss://example.com", mux)
|
t.ListenAndServe("wss://example.com", mux)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
mux := packer.NewRouteMux()
|
|
||||||
//mux.HandleTLS("*", mux.TerminateTLS(mux))
|
|
||||||
mux.ForwardTCP("*", "localhost:3000", 120*time.Second)
|
|
||||||
// TODO set failure
|
|
||||||
|
|
||||||
wsw := packer.NewWSWrap(wsconn)
|
|
||||||
log.Fatal("Closed server: ", packer.ListenAndServe(wsw, mux))
|
|
||||||
}
|
|
||||||
|
|
||||||
func getToken(secret string) (token string, err error) {
|
func getToken(secret string) (token string, err error) {
|
||||||
domains := []string{"dandel.duckdns.org"}
|
domains := []string{"dandel.duckdns.org"}
|
||||||
tokenData := jwt.MapClaims{"domains": domains}
|
tokenData := jwt.MapClaims{"domains": domains}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
// A Listener transforms a multiplexed websocket connection into individual net.Conn-like connections.
|
// A Listener transforms a multiplexed websocket connection into individual net.Conn-like connections.
|
||||||
type Listener struct {
|
type Listener struct {
|
||||||
//wsconn *websocket.Conn
|
//wsconn *websocket.Conn
|
||||||
wsw *WSWrap
|
tun net.Conn
|
||||||
incoming chan *Conn
|
incoming chan *Conn
|
||||||
close chan struct{}
|
close chan struct{}
|
||||||
encoder *Encoder
|
encoder *Encoder
|
||||||
|
@ -22,15 +22,15 @@ type Listener struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen creates a new Listener and sets it up to receive and distribute connections.
|
// Listen creates a new Listener and sets it up to receive and distribute connections.
|
||||||
func Listen(wsw *WSWrap) *Listener {
|
func Listen(tun net.Conn) *Listener {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
|
|
||||||
// Feed the socket into the Encoder and Decoder
|
// Feed the socket into the Encoder and Decoder
|
||||||
listener := &Listener{
|
listener := &Listener{
|
||||||
wsw: wsw,
|
tun: tun,
|
||||||
incoming: make(chan *Conn, 1), // buffer ever so slightly
|
incoming: make(chan *Conn, 1), // buffer ever so slightly
|
||||||
close: make(chan struct{}),
|
close: make(chan struct{}),
|
||||||
encoder: NewEncoder(ctx, wsw),
|
encoder: NewEncoder(ctx, tun),
|
||||||
conns: map[string]net.Conn{},
|
conns: map[string]net.Conn{},
|
||||||
//conns: map[string]*Conn{},
|
//conns: map[string]*Conn{},
|
||||||
}
|
}
|
||||||
|
@ -40,11 +40,11 @@ func Listen(wsw *WSWrap) *Listener {
|
||||||
go func() {
|
go func() {
|
||||||
err := listener.encoder.Run()
|
err := listener.encoder.Run()
|
||||||
fmt.Printf("encoder stopped entirely: %q", err)
|
fmt.Printf("encoder stopped entirely: %q", err)
|
||||||
wsw.Close()
|
tun.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Decode the stream as it comes in
|
// Decode the stream as it comes in
|
||||||
decoder := NewDecoder(wsw)
|
decoder := NewDecoder(tun)
|
||||||
go func() {
|
go func() {
|
||||||
// TODO pass error to Accept()
|
// TODO pass error to Accept()
|
||||||
err := decoder.Decode(listener)
|
err := decoder.Decode(listener)
|
||||||
|
@ -60,8 +60,8 @@ func Listen(wsw *WSWrap) *Listener {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServe listens on a websocket and handles the incomming net.Conn-like connections with a Handler
|
// ListenAndServe listens on a websocket and handles the incomming net.Conn-like connections with a Handler
|
||||||
func ListenAndServe(wsw *WSWrap, mux Handler) error {
|
func ListenAndServe(tun net.Conn, mux Handler) error {
|
||||||
listener := Listen(wsw)
|
listener := Listen(tun)
|
||||||
return Serve(listener, mux)
|
return Serve(listener, mux)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ func (l *Listener) Accept() (*Conn, error) {
|
||||||
// Close stops accepting new connections and closes the underlying websocket.
|
// Close stops accepting new connections and closes the underlying websocket.
|
||||||
// TODO return errors.
|
// TODO return errors.
|
||||||
func (l *Listener) Close() error {
|
func (l *Listener) Close() error {
|
||||||
l.wsw.Close()
|
l.tun.Close()
|
||||||
close(l.incoming)
|
close(l.incoming)
|
||||||
l.close <- struct{}{}
|
l.close <- struct{}{}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -10,10 +10,10 @@ func TestDialServer(t *testing.T) {
|
||||||
// TODO replace the websocket connection with a mock server
|
// TODO replace the websocket connection with a mock server
|
||||||
|
|
||||||
//ctx := context.Background()
|
//ctx := context.Background()
|
||||||
wsw := &WSWrap{}
|
tun := &WebsocketTunnel{}
|
||||||
|
|
||||||
mux := NewRouteMux()
|
mux := NewRouteMux()
|
||||||
t.Fatal(ListenAndServe(wsw, mux))
|
t.Fatal(ListenAndServe(tun, mux))
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrNoImpl error = errors.New("not implemented")
|
var ErrNoImpl error = errors.New("not implemented")
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
package packer
|
package packer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WSWrap wraps a websocket.Conn instance to behave like net.Conn.
|
// WebsocketTunnel wraps a websocket.Conn instance to behave like net.Conn.
|
||||||
// TODO make conform.
|
// TODO make conform.
|
||||||
type WSWrap struct {
|
type WebsocketTunnel struct {
|
||||||
wsconn WSConn
|
wsconn WSConn
|
||||||
tmpr io.Reader
|
tmpr io.Reader
|
||||||
//w io.WriteCloser
|
//w io.WriteCloser
|
||||||
|
@ -26,20 +29,35 @@ type WSConn interface {
|
||||||
WriteControl(messageType int, data []byte, deadline time.Time) error
|
WriteControl(messageType int, data []byte, deadline time.Time) error
|
||||||
WriteMessage(messageType int, data []byte) error
|
WriteMessage(messageType int, data []byte) error
|
||||||
SetReadDeadline(t time.Time) error
|
SetReadDeadline(t time.Time) error
|
||||||
|
SetWriteDeadline(t time.Time) error
|
||||||
Close() error
|
Close() error
|
||||||
RemoteAddr() net.Addr
|
RemoteAddr() net.Addr
|
||||||
// LocalAddr() net.Addr
|
// LocalAddr() net.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWSWrap allocates a new websocket connection wrapper
|
// NewWebsocketTunnel allocates a new websocket connection wrapper
|
||||||
func NewWSWrap(wsconn WSConn) *WSWrap {
|
func NewWebsocketTunnel(wsconn WSConn) net.Conn {
|
||||||
return &WSWrap{
|
return &WebsocketTunnel{
|
||||||
wsconn: wsconn,
|
wsconn: wsconn,
|
||||||
tmpr: nil,
|
tmpr: nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wsw *WSWrap) Read(b []byte) (int, error) {
|
// DialWebsocketTunnel connects to the given websocket relay as wraps it as net.Conn
|
||||||
|
func DialWebsocketTunnel(ctx context.Context, relay, authz string) (net.Conn, error) {
|
||||||
|
wsd := websocket.Dialer{}
|
||||||
|
headers := http.Header{}
|
||||||
|
headers.Set("Authorization", fmt.Sprintf("Bearer %s", authz))
|
||||||
|
// *http.Response
|
||||||
|
sep := "?"
|
||||||
|
if strings.Contains(relay, sep) {
|
||||||
|
sep = "&"
|
||||||
|
}
|
||||||
|
wsconn, _, err := wsd.DialContext(ctx, relay+sep+"access_token="+authz+"&versions=v1", headers)
|
||||||
|
return NewWebsocketTunnel(wsconn), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wsw *WebsocketTunnel) Read(b []byte) (int, error) {
|
||||||
if nil == wsw.tmpr {
|
if nil == wsw.tmpr {
|
||||||
_, msgr, err := wsw.wsconn.NextReader()
|
_, msgr, err := wsw.wsconn.NextReader()
|
||||||
if nil != err {
|
if nil != err {
|
||||||
|
@ -61,7 +79,7 @@ func (wsw *WSWrap) Read(b []byte) (int, error) {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wsw *WSWrap) Write(b []byte) (int, error) {
|
func (wsw *WebsocketTunnel) Write(b []byte) (int, error) {
|
||||||
// TODO create or reset ping deadline
|
// TODO create or reset ping deadline
|
||||||
// TODO document that more complete writes are preferred?
|
// TODO document that more complete writes are preferred?
|
||||||
|
|
||||||
|
@ -81,7 +99,7 @@ func (wsw *WSWrap) Write(b []byte) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close will close the websocket with a control message
|
// Close will close the websocket with a control message
|
||||||
func (wsw *WSWrap) Close() error {
|
func (wsw *WebsocketTunnel) Close() error {
|
||||||
fmt.Println("[debug] closing the websocket.Conn")
|
fmt.Println("[debug] closing the websocket.Conn")
|
||||||
|
|
||||||
// TODO handle EOF as websocket.CloseNormal?
|
// TODO handle EOF as websocket.CloseNormal?
|
||||||
|
@ -95,27 +113,35 @@ func (wsw *WSWrap) Close() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalAddr returns the local network address.
|
// LocalAddr is not implemented and will panic
|
||||||
func (wsw *WSWrap) LocalAddr() *Addr {
|
func (wsw *WebsocketTunnel) LocalAddr() net.Addr {
|
||||||
panic("not implemented")
|
// TODO do we reverse this since the "local" address is that of the relay?
|
||||||
|
// return wsw.wsconn.RemoteAddr()
|
||||||
|
panic("LocalAddr() not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoteAddr returns the remote network address.
|
// RemoteAddr is not implemented and will panic. Additionally, it wouldn't mean anything useful anyway.
|
||||||
func (wsw *WSWrap) RemoteAddr() *Addr {
|
func (wsw *WebsocketTunnel) RemoteAddr() net.Addr {
|
||||||
panic("not implemented")
|
// TODO do we reverse this since the "remote" address means nothing / is that of one of the clients?
|
||||||
|
// return wsw.wsconn.LocalAddr()
|
||||||
|
panic("RemoteAddr() not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDeadline sets the read and write deadlines associated
|
// SetDeadline sets the read and write deadlines associated
|
||||||
func (wsw *WSWrap) SetDeadline(t time.Time) error {
|
func (wsw *WebsocketTunnel) SetDeadline(t time.Time) error {
|
||||||
panic("not implemented")
|
err := wsw.SetReadDeadline(t)
|
||||||
|
if nil == err {
|
||||||
|
err = wsw.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetReadDeadline sets the deadline for future Read calls
|
// SetReadDeadline sets the deadline for future Read calls
|
||||||
func (wsw *WSWrap) SetReadDeadline(t time.Time) error {
|
func (wsw *WebsocketTunnel) SetReadDeadline(t time.Time) error {
|
||||||
panic("not implemented")
|
return wsw.wsconn.SetReadDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetWriteDeadline sets the deadline for future Write calls
|
// SetWriteDeadline sets the deadline for future Write calls
|
||||||
func (wsw *WSWrap) SetWriteDeadline(t time.Time) error {
|
func (wsw *WebsocketTunnel) SetWriteDeadline(t time.Time) error {
|
||||||
panic("not implemented")
|
return wsw.wsconn.SetWriteDeadline(t)
|
||||||
}
|
}
|
Loading…
Reference in New Issue