2024-03-22 18:38:19 +00:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
2024-03-29 15:53:10 +00:00
|
|
|
"golang.org/x/sync/errgroup"
|
2024-03-29 14:05:42 +00:00
|
|
|
"math/rand"
|
2024-03-24 13:09:36 +00:00
|
|
|
"net/url"
|
2024-03-24 16:10:02 +00:00
|
|
|
"os"
|
2024-03-22 18:38:19 +00:00
|
|
|
"time"
|
|
|
|
|
2024-03-24 16:10:02 +00:00
|
|
|
"github.com/charmbracelet/log"
|
2024-03-24 13:09:36 +00:00
|
|
|
"github.com/gorilla/websocket"
|
2024-03-22 18:38:19 +00:00
|
|
|
cm "krzyzanowski.dev/p2pchat/common"
|
|
|
|
)
|
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
type Context struct {
|
|
|
|
conn *websocket.Conn
|
|
|
|
// Assumption: size of 1 is enough, because first response read will be response for the last request
|
|
|
|
// no need to buffer
|
|
|
|
resFromServer chan cm.RFrame
|
|
|
|
reqFromServer chan cm.RFrame
|
|
|
|
rToServer chan cm.RFrame
|
|
|
|
}
|
2024-03-24 16:10:02 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
func NewClientContext(conn *websocket.Conn) *Context {
|
|
|
|
return &Context{
|
|
|
|
conn: conn,
|
|
|
|
resFromServer: make(chan cm.RFrame),
|
|
|
|
reqFromServer: make(chan cm.RFrame),
|
|
|
|
rToServer: make(chan cm.RFrame),
|
2024-03-24 16:10:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
func (cliCtx *Context) serverHandler() error {
|
|
|
|
for {
|
|
|
|
reqFrame := <-cliCtx.reqFromServer
|
2024-03-22 18:38:19 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
logger.Debug("got request from server", "id", reqFrame.ID)
|
2024-03-22 18:38:19 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
if reqFrame.ID == cm.EchoReqID {
|
|
|
|
echoReq, err := cm.RequestFromFrame[cm.EchoRequest](reqFrame)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-03-22 18:38:19 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
resFrame, err := cm.ResponseFrameFrom(cm.EchoResponse(echoReq))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-03-22 18:38:19 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
cliCtx.rToServer <- resFrame
|
|
|
|
} else {
|
|
|
|
logger.Fatal("can't handle it!")
|
|
|
|
}
|
2024-03-22 18:38:19 +00:00
|
|
|
}
|
2024-03-29 14:05:42 +00:00
|
|
|
}
|
2024-03-22 18:38:19 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
func (cliCtx *Context) serverWriter() error {
|
|
|
|
for {
|
|
|
|
logger.Debug("waiting for a frame to write")
|
|
|
|
frameToWrite := <-cliCtx.rToServer
|
|
|
|
err := cliCtx.conn.WriteJSON(frameToWrite)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
logger.Debug("frame written", "id", frameToWrite.ID)
|
2024-03-22 18:38:19 +00:00
|
|
|
}
|
2024-03-29 14:05:42 +00:00
|
|
|
}
|
2024-03-22 18:38:19 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
func (cliCtx *Context) serverReader() error {
|
|
|
|
for {
|
|
|
|
logger.Debug("waiting for a frame to read")
|
|
|
|
var rFrame cm.RFrame
|
|
|
|
err := cliCtx.conn.ReadJSON(&rFrame)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-03-22 18:38:19 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
logger.Debug("frame read", "id", rFrame.ID)
|
2024-03-22 18:38:19 +00:00
|
|
|
|
2024-03-29 15:53:10 +00:00
|
|
|
if rFrame.IsResponse() {
|
2024-03-29 14:05:42 +00:00
|
|
|
cliCtx.resFromServer <- rFrame
|
|
|
|
} else {
|
|
|
|
cliCtx.reqFromServer <- rFrame
|
|
|
|
}
|
2024-03-22 18:38:19 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
logger.Debug("frame pushed", "id", rFrame.ID)
|
2024-03-24 13:09:36 +00:00
|
|
|
}
|
2024-03-29 14:05:42 +00:00
|
|
|
}
|
2024-03-22 18:38:19 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
func (cliCtx *Context) sendRequest(req cm.Request) error {
|
|
|
|
rf, err := cm.RequestFrameFrom(req)
|
2024-03-24 13:09:36 +00:00
|
|
|
if err != nil {
|
2024-03-29 14:05:42 +00:00
|
|
|
return err
|
2024-03-24 13:09:36 +00:00
|
|
|
}
|
2024-03-22 18:38:19 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
cliCtx.rToServer <- rf
|
|
|
|
return nil
|
|
|
|
}
|
2024-03-22 18:38:19 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
func (cliCtx *Context) getResponseFrame() cm.RFrame {
|
|
|
|
return <-cliCtx.resFromServer
|
|
|
|
}
|
|
|
|
|
|
|
|
var logger = log.NewWithOptions(os.Stdout, log.Options{
|
|
|
|
ReportTimestamp: true,
|
|
|
|
TimeFormat: time.TimeOnly,
|
|
|
|
Prefix: "👤 Client",
|
|
|
|
})
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
if cm.IsProd {
|
|
|
|
logger.SetLevel(log.InfoLevel)
|
|
|
|
} else {
|
|
|
|
logger.SetLevel(log.DebugLevel)
|
2024-03-24 13:09:36 +00:00
|
|
|
}
|
2024-03-29 14:05:42 +00:00
|
|
|
}
|
2024-03-22 18:38:19 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
func testAuth(ctx *Context) {
|
|
|
|
logger.Info("Trying to authenticate as krzmaciek...")
|
2024-03-29 15:53:10 +00:00
|
|
|
err := ctx.sendRequest(cm.AuthRequest{Nickname: "krzmaciek", Password: "9maciek1"})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
logger.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
logger.Debug("Request sent, waiting for response...")
|
|
|
|
arf := ctx.getResponseFrame()
|
|
|
|
ar, err := cm.ResponseFromFrame[cm.AuthResponse](arf)
|
2024-03-29 15:53:10 +00:00
|
|
|
|
2024-03-22 18:38:19 +00:00
|
|
|
if err != nil {
|
2024-03-29 14:05:42 +00:00
|
|
|
logger.Error(err)
|
2024-03-29 15:53:10 +00:00
|
|
|
return
|
2024-03-22 18:38:19 +00:00
|
|
|
}
|
2024-03-29 15:53:10 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
logger.Infof("Authenticated?: %t", ar.IsSuccess)
|
|
|
|
}
|
2024-03-22 18:38:19 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
func testEcho(ctx *Context) {
|
|
|
|
echoByte := rand.Intn(32)
|
|
|
|
logger.Info("Testing echo...", "echoByte", echoByte)
|
2024-03-29 15:53:10 +00:00
|
|
|
err := ctx.sendRequest(cm.EchoRequest{EchoByte: byte(echoByte)})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
logger.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
logger.Debug("Request sent, waiting for response...")
|
|
|
|
ereqf := ctx.getResponseFrame()
|
|
|
|
ereq, err := cm.ResponseFromFrame[cm.EchoResponse](ereqf)
|
2024-03-29 15:53:10 +00:00
|
|
|
|
2024-03-24 13:09:36 +00:00
|
|
|
if err != nil {
|
2024-03-29 14:05:42 +00:00
|
|
|
logger.Error(err)
|
2024-03-29 15:53:10 +00:00
|
|
|
return
|
2024-03-24 13:09:36 +00:00
|
|
|
}
|
2024-03-29 15:53:10 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
logger.Info("Got response", "echoByte", ereq.EchoByte)
|
|
|
|
}
|
2024-03-22 18:38:19 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
func testListPeers(ctx *Context) {
|
|
|
|
logger.Info("Trying to get list of peers...")
|
2024-03-29 15:53:10 +00:00
|
|
|
err := ctx.sendRequest(cm.ListPeersRequest{})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
logger.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
logger.Debug("Request sent, waiting for response...")
|
|
|
|
lpreqf := ctx.getResponseFrame()
|
|
|
|
lpreq, err := cm.ResponseFromFrame[cm.ListPeersResponse](lpreqf)
|
2024-03-29 15:53:10 +00:00
|
|
|
|
2024-03-24 13:09:36 +00:00
|
|
|
if err != nil {
|
2024-03-29 14:05:42 +00:00
|
|
|
logger.Error(err)
|
2024-03-29 15:53:10 +00:00
|
|
|
return
|
2024-03-24 13:09:36 +00:00
|
|
|
}
|
2024-03-29 15:53:10 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
logger.Info("Got that list", "peersList", lpreq.PeersInfo)
|
|
|
|
}
|
2024-03-22 18:38:19 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
func RunClient() {
|
|
|
|
u := url.URL{Scheme: "ws", Host: ":8080", Path: "/wsapi"}
|
|
|
|
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
|
2024-03-22 18:38:19 +00:00
|
|
|
|
2024-03-29 14:05:42 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Error("could not connect to websocket")
|
|
|
|
return
|
2024-03-22 18:38:19 +00:00
|
|
|
}
|
|
|
|
|
2024-03-29 15:53:10 +00:00
|
|
|
defer func(c *websocket.Conn) {
|
|
|
|
err := c.Close()
|
|
|
|
if err != nil {
|
|
|
|
logger.Error(err)
|
|
|
|
}
|
|
|
|
}(c)
|
2024-03-29 14:05:42 +00:00
|
|
|
|
|
|
|
ctx := NewClientContext(c)
|
2024-03-29 15:53:10 +00:00
|
|
|
errGroup := new(errgroup.Group)
|
|
|
|
errGroup.Go(ctx.serverHandler)
|
|
|
|
errGroup.Go(ctx.serverReader)
|
|
|
|
errGroup.Go(ctx.serverWriter)
|
2024-03-29 14:05:42 +00:00
|
|
|
|
|
|
|
testAuth(ctx)
|
|
|
|
testEcho(ctx)
|
|
|
|
testListPeers(ctx)
|
2024-03-29 15:53:10 +00:00
|
|
|
err = errGroup.Wait()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
logger.Error(err)
|
|
|
|
}
|
2024-03-29 14:05:42 +00:00
|
|
|
|
2024-03-24 16:10:02 +00:00
|
|
|
logger.Info("closing connection...")
|
2024-03-29 15:53:10 +00:00
|
|
|
err = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
logger.Error(err)
|
|
|
|
}
|
2024-03-22 18:38:19 +00:00
|
|
|
}
|