go-ssb-room/muxrpc/test/go/websocket_test.go

141 lines
3.6 KiB
Go

// SPDX-FileCopyrightText: 2021 The NGI Pointer Secure-Scuttlebutt Team of 2020/2021
//
// SPDX-License-Identifier: MIT
package go_test
import (
"context"
"encoding/base64"
"encoding/json"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"testing"
"time"
"github.com/gorilla/websocket"
"github.com/ssbc/go-muxrpc/v2"
"github.com/ssbc/go-muxrpc/v2/debug"
"github.com/ssbc/go-secretstream"
"github.com/stretchr/testify/require"
"go.mindeco.de/encodedTime"
"github.com/ssbc/go-ssb-room/v2/internal/maybemod/keys"
"github.com/ssbc/go-ssb-room/v2/internal/network"
tunserv "github.com/ssbc/go-ssb-room/v2/muxrpc/handlers/tunnel/server"
"github.com/ssbc/go-ssb-room/v2/roomdb"
)
func TestWebsocketDialing(t *testing.T) {
r := require.New(t)
testPath := filepath.Join("testrun", t.Name())
os.RemoveAll(testPath)
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
t.Cleanup(cancel)
// create the roomsrv
session := makeNamedTestBot(t, "server", ctx, nil)
server := session.srv
// open a TCP listener for HTTP
l, err := net.Listen("tcp4", "localhost:0")
r.NoError(err)
var fh = failHandler{t: t}
// serve the websocket handler
handler := server.Network.WebsockHandler(fh)
go http.Serve(l, handler)
// create a fresh keypair for the client
client, err := keys.NewKeyPair(nil)
r.NoError(err)
// add it as a memeber
memberID, err := server.Members.Add(ctx, client.Feed, roomdb.RoleMember)
r.NoError(err)
t.Log("client member:", memberID)
// construct the websocket http address
var wsURL url.URL
wsURL.Scheme = "ws"
wsURL.Host = l.Addr().String()
wsURL.Path = "/"
// create a websocket connection
conn, resp, err := websocket.DefaultDialer.DialContext(ctx, wsURL.String(), nil)
r.NoError(err)
t.Log(resp.Status)
r.Equal(http.StatusSwitchingProtocols, resp.StatusCode)
// default app key for the secret-handshake connection
ak, err := base64.StdEncoding.DecodeString("1KHLiKZvAvjbY1ziZEHMXawbCEIM6qwjCDm3VYRan/s=")
r.NoError(err)
// create a shs client to authenticate and encrypt the connection
clientSHS, err := secretstream.NewClient(client.Pair, ak)
r.NoError(err)
// the returned wrapper: func(net.Conn) (net.Conn, error)
// returns a new connection that went through shs and does boxstream
connAuther := clientSHS.ConnWrapper(server.Whoami().PubKey())
// turn a websocket conn into a net.Conn
wrappedConn := network.NewWebsockConn(conn)
authedConn, err := connAuther(wrappedConn)
r.NoError(err)
var muxMock muxrpc.FakeHandler
debugConn := debug.Dump(filepath.Join(testPath, "client"), authedConn)
pkr := muxrpc.NewPacker(debugConn)
wsEndpoint := muxrpc.Handle(pkr, &muxMock, muxrpc.WithContext(ctx))
srv := wsEndpoint.(muxrpc.Server)
go func() {
err = srv.Serve()
r.NoError(err)
t.Log("mux server error:", err)
}()
// check we are talking to a room
var meta tunserv.MetadataReply
err = wsEndpoint.Async(ctx, &meta, muxrpc.TypeJSON, muxrpc.Method{"tunnel", "isRoom"})
r.NoError(err)
r.Equal("server", meta.Name)
r.True(meta.Membership, "not a member?")
// open the gossip.ping channel
src, snk, err := wsEndpoint.Duplex(ctx, muxrpc.TypeJSON, muxrpc.Method{"gossip", "ping"})
r.NoError(err)
pingTS := encodedTime.NewMillisecs(time.Now().Unix())
err = json.NewEncoder(snk).Encode(pingTS)
r.NoError(err)
r.True(src.Next(ctx))
pong, err := src.Bytes()
r.NoError(err)
var pongTS encodedTime.Millisecs
err = json.Unmarshal(pong, &pongTS)
r.NoError(err)
r.False(time.Time(pongTS).IsZero())
}
type failHandler struct {
t *testing.T
}
func (fh failHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
fh.t.Error("next handler called", req.URL.String())
}