mirror of
https://github.com/originalmk/archat-server.git
synced 2025-01-18 16:29:17 +00:00
Working on initating connections
This commit is contained in:
parent
b9fe4a7094
commit
4a38b6e28a
160
client/client.go
160
client/client.go
@ -1,15 +1,19 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
"math/rand"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/charmbracelet/log"
|
"github.com/charmbracelet/log"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
cm "krzyzanowski.dev/p2pchat/common"
|
cm "krzyzanowski.dev/archat/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
@ -30,10 +34,14 @@ func NewClientContext(conn *websocket.Conn) *Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cliCtx *Context) serverHandler() error {
|
func (cliCtx *Context) serverHandler(syncCtx context.Context) error {
|
||||||
for {
|
defer logger.Debug("server handler last line...")
|
||||||
reqFrame := <-cliCtx.reqFromServer
|
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-syncCtx.Done():
|
||||||
|
return nil
|
||||||
|
case reqFrame := <-cliCtx.reqFromServer:
|
||||||
logger.Debug("got request from server", "id", reqFrame.ID)
|
logger.Debug("got request from server", "id", reqFrame.ID)
|
||||||
|
|
||||||
if reqFrame.ID == cm.EchoReqID {
|
if reqFrame.ID == cm.EchoReqID {
|
||||||
@ -49,24 +57,33 @@ func (cliCtx *Context) serverHandler() error {
|
|||||||
|
|
||||||
cliCtx.rToServer <- resFrame
|
cliCtx.rToServer <- resFrame
|
||||||
} else {
|
} else {
|
||||||
logger.Fatal("can't handle it!")
|
logger.Warn("can't handle it!")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cliCtx *Context) serverWriter() error {
|
func (cliCtx *Context) serverWriter(syncCtx context.Context) error {
|
||||||
|
defer logger.Debug("server writer last line...")
|
||||||
|
|
||||||
for {
|
for {
|
||||||
logger.Debug("waiting for a frame to write")
|
logger.Debug("waiting for a frame to write")
|
||||||
frameToWrite := <-cliCtx.rToServer
|
select {
|
||||||
|
case <-syncCtx.Done():
|
||||||
|
return nil
|
||||||
|
case frameToWrite := <-cliCtx.rToServer:
|
||||||
err := cliCtx.conn.WriteJSON(frameToWrite)
|
err := cliCtx.conn.WriteJSON(frameToWrite)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Debug("frame written", "id", frameToWrite.ID)
|
logger.Debug("frame written", "id", frameToWrite.ID)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cliCtx *Context) serverReader() error {
|
func (cliCtx *Context) serverReader(syncCtx context.Context) error {
|
||||||
|
defer logger.Debug("server reader last line...")
|
||||||
|
|
||||||
for {
|
for {
|
||||||
logger.Debug("waiting for a frame to read")
|
logger.Debug("waiting for a frame to read")
|
||||||
var rFrame cm.RFrame
|
var rFrame cm.RFrame
|
||||||
@ -115,9 +132,9 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAuth(ctx *Context) {
|
func sendAuth(ctx *Context, nick, pass string) {
|
||||||
logger.Info("Trying to authenticate as krzmaciek...")
|
logger.Info("Trying to authenticate as krzmaciek...")
|
||||||
err := ctx.sendRequest(cm.AuthRequest{Nickname: "krzmaciek", Password: "9maciek1"})
|
err := ctx.sendRequest(cm.AuthRequest{Nickname: nick, Password: pass})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
@ -136,10 +153,9 @@ func testAuth(ctx *Context) {
|
|||||||
logger.Infof("Authenticated?: %t", ar.IsSuccess)
|
logger.Infof("Authenticated?: %t", ar.IsSuccess)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEcho(ctx *Context) {
|
func sendEcho(ctx *Context, echoByte byte) {
|
||||||
echoByte := rand.Intn(32)
|
|
||||||
logger.Info("Testing echo...", "echoByte", echoByte)
|
logger.Info("Testing echo...", "echoByte", echoByte)
|
||||||
err := ctx.sendRequest(cm.EchoRequest{EchoByte: byte(echoByte)})
|
err := ctx.sendRequest(cm.EchoRequest{EchoByte: echoByte})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
@ -158,7 +174,7 @@ func testEcho(ctx *Context) {
|
|||||||
logger.Info("Got response", "echoByte", ereq.EchoByte)
|
logger.Info("Got response", "echoByte", ereq.EchoByte)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testListPeers(ctx *Context) {
|
func sendListPeers(ctx *Context) {
|
||||||
logger.Info("Trying to get list of peers...")
|
logger.Info("Trying to get list of peers...")
|
||||||
err := ctx.sendRequest(cm.ListPeersRequest{})
|
err := ctx.sendRequest(cm.ListPeersRequest{})
|
||||||
|
|
||||||
@ -179,6 +195,18 @@ func testListPeers(ctx *Context) {
|
|||||||
logger.Info("Got that list", "peersList", lpreq.PeersInfo)
|
logger.Info("Got that list", "peersList", lpreq.PeersInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sendStartChatA(ctx *Context, nick string) {
|
||||||
|
logger.Info("Doing chat start A...")
|
||||||
|
err := ctx.sendRequest(cm.StartChatARequest{Nickname: nick})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug("Request sent, no wait for response")
|
||||||
|
}
|
||||||
|
|
||||||
func RunClient() {
|
func RunClient() {
|
||||||
u := url.URL{Scheme: "ws", Host: ":8080", Path: "/wsapi"}
|
u := url.URL{Scheme: "ws", Host: ":8080", Path: "/wsapi"}
|
||||||
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
|
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
|
||||||
@ -188,32 +216,94 @@ func RunClient() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func(c *websocket.Conn) {
|
cliCtx := NewClientContext(c)
|
||||||
err := c.Close()
|
errGroup, syncCtx := errgroup.WithContext(context.Background())
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
errGroup.Go(func() error {
|
||||||
|
return cliCtx.serverHandler(syncCtx)
|
||||||
|
})
|
||||||
|
|
||||||
|
errGroup.Go(func() error {
|
||||||
|
return cliCtx.serverReader(syncCtx)
|
||||||
|
})
|
||||||
|
|
||||||
|
errGroup.Go(func() error {
|
||||||
|
return cliCtx.serverWriter(syncCtx)
|
||||||
|
})
|
||||||
|
|
||||||
|
errGroup.Go(func() error {
|
||||||
|
<-syncCtx.Done()
|
||||||
|
logger.Info("closing client...")
|
||||||
|
time.Sleep(time.Second * 3)
|
||||||
|
close(cliCtx.rToServer)
|
||||||
|
close(cliCtx.resFromServer)
|
||||||
|
close(cliCtx.reqFromServer)
|
||||||
|
_ = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||||
|
return c.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
closer := make(chan int)
|
||||||
|
errGroup.Go(func() error {
|
||||||
|
select {
|
||||||
|
case <-closer:
|
||||||
|
return errors.New("close")
|
||||||
|
case <-syncCtx.Done():
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}(c)
|
})
|
||||||
|
|
||||||
ctx := NewClientContext(c)
|
go func() {
|
||||||
errGroup := new(errgroup.Group)
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
errGroup.Go(ctx.serverHandler)
|
|
||||||
errGroup.Go(ctx.serverReader)
|
for scanner.Scan() {
|
||||||
errGroup.Go(ctx.serverWriter)
|
cmd := strings.TrimRight(scanner.Text(), " \n\t")
|
||||||
|
cmdElements := strings.Split(cmd, " ")
|
||||||
|
cmdName := cmdElements[0]
|
||||||
|
cmdArgs := cmdElements[1:]
|
||||||
|
|
||||||
|
if cmdName == "exit" {
|
||||||
|
logger.Info("closing...")
|
||||||
|
closer <- 1
|
||||||
|
} else if cmdName == "echo" {
|
||||||
|
if len(cmdArgs) != 1 {
|
||||||
|
logger.Errorf("echo command requires 1 argument, but %d was provided", len(cmdArgs))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
num, err := strconv.Atoi(cmdArgs[0])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("%s is not a number", cmdArgs[0])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
sendEcho(cliCtx, byte(num))
|
||||||
|
} else if cmdName == "list" {
|
||||||
|
sendListPeers(cliCtx)
|
||||||
|
} else if cmdName == "auth" {
|
||||||
|
if len(cmdArgs) != 2 {
|
||||||
|
logger.Errorf("auth command requires 2 argument, but %d was provided", len(cmdArgs))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
nick := cmdArgs[0]
|
||||||
|
pass := cmdArgs[1]
|
||||||
|
|
||||||
|
sendAuth(cliCtx, nick, pass)
|
||||||
|
} else if cmdName == "startchata" {
|
||||||
|
if len(cmdArgs) != 1 {
|
||||||
|
logger.Errorf("startchata command requires 1 argument, but %d was provided", len(cmdArgs))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
sendStartChatA(cliCtx, cmdArgs[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
testAuth(ctx)
|
|
||||||
testEcho(ctx)
|
|
||||||
testListPeers(ctx)
|
|
||||||
err = errGroup.Wait()
|
err = errGroup.Wait()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info("closing connection...")
|
|
||||||
err = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,10 @@ func (rf RFrame) IsResponse() bool {
|
|||||||
return rf.ID > 128
|
return rf.ID > 128
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rf RFrame) IsError() bool {
|
||||||
|
return rf.ID > 256
|
||||||
|
}
|
||||||
|
|
||||||
func RequestFrameFrom(req Request) (RFrame, error) {
|
func RequestFrameFrom(req Request) (RFrame, error) {
|
||||||
jsonBytes, err := json.Marshal(req)
|
jsonBytes, err := json.Marshal(req)
|
||||||
|
|
||||||
@ -139,8 +143,10 @@ func (AuthResponse) ID() int {
|
|||||||
return AuthResID
|
return AuthResID
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Stateful" requests like these need to have some information identifying what operation they are linked to
|
// "Stateful" requests like these need to have some information identifying
|
||||||
// There may be some errors if two requests are sent by one host to the same other host... may they?
|
// what operation they are linked to
|
||||||
|
// There may be some errors if two requests are sent by one host to the same
|
||||||
|
// other host... may they?
|
||||||
|
|
||||||
type StartChatARequest struct {
|
type StartChatARequest struct {
|
||||||
Nickname string `json:"nickname"`
|
Nickname string `json:"nickname"`
|
||||||
|
2
go.mod
2
go.mod
@ -1,4 +1,4 @@
|
|||||||
module krzyzanowski.dev/p2pchat
|
module krzyzanowski.dev/archat
|
||||||
|
|
||||||
go 1.21.7
|
go 1.21.7
|
||||||
|
|
||||||
|
4
main.go
4
main.go
@ -4,8 +4,8 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"krzyzanowski.dev/p2pchat/client"
|
"krzyzanowski.dev/archat/client"
|
||||||
"krzyzanowski.dev/p2pchat/server"
|
"krzyzanowski.dev/archat/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
313
server/server.go
313
server/server.go
@ -1,25 +1,57 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"golang.org/x/sync/errgroup"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
"github.com/charmbracelet/log"
|
"github.com/charmbracelet/log"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"krzyzanowski.dev/p2pchat/common"
|
"krzyzanowski.dev/archat/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Peer struct {
|
||||||
|
id int
|
||||||
|
conn *websocket.Conn
|
||||||
|
hasAccount bool
|
||||||
|
account *Account
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPeer(conn *websocket.Conn) *Peer {
|
||||||
|
return &Peer{-1, conn, false, nil}
|
||||||
|
}
|
||||||
|
|
||||||
type Account struct {
|
type Account struct {
|
||||||
nickname string
|
nickname string
|
||||||
passHash []byte
|
passHash []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
InitiationStageA = 1
|
||||||
|
InitiationStageB = 2
|
||||||
|
InitiationStageC = 3
|
||||||
|
InitiationStageD = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
type Initiation struct {
|
||||||
|
abANick string
|
||||||
|
abBNick string
|
||||||
|
stage int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInitiation(abA string, abB string) *Initiation {
|
||||||
|
return &Initiation{abA, abB, InitiationStageA}
|
||||||
|
}
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
idCounter int
|
idCounter int
|
||||||
idCounterLock sync.RWMutex
|
idCounterLock sync.RWMutex
|
||||||
@ -27,6 +59,43 @@ type Context struct {
|
|||||||
peersListLock sync.RWMutex
|
peersListLock sync.RWMutex
|
||||||
accounts map[string]*Account
|
accounts map[string]*Account
|
||||||
accountsLock sync.RWMutex
|
accountsLock sync.RWMutex
|
||||||
|
initiations []*Initiation
|
||||||
|
initiationsLock sync.RWMutex
|
||||||
|
handlerContexts []*HandlerContext
|
||||||
|
handlerContextsLock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewContext() *Context {
|
||||||
|
return &Context{
|
||||||
|
peersList: make([]*Peer, 0),
|
||||||
|
accounts: make(map[string]*Account),
|
||||||
|
initiations: make([]*Initiation, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remember to lock before calling
|
||||||
|
func (ctx *Context) getPeerByNick(nick string) (*Peer, error) {
|
||||||
|
for _, peer := range ctx.peersList {
|
||||||
|
if peer.hasAccount && peer.account.nickname == nick {
|
||||||
|
return peer, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, errors.New("peer not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remember to lock before calling
|
||||||
|
func (ctx *Context) getCtxByNick(nick string) (*HandlerContext, error) {
|
||||||
|
idx := slices.IndexFunc[[]*HandlerContext, *HandlerContext](
|
||||||
|
ctx.handlerContexts,
|
||||||
|
func(handlerContext *HandlerContext) bool {
|
||||||
|
return handlerContext.peer.hasAccount && handlerContext.peer.account.nickname == nick
|
||||||
|
})
|
||||||
|
|
||||||
|
if idx != -1 {
|
||||||
|
return ctx.handlerContexts[idx], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
type HandlerContext struct {
|
type HandlerContext struct {
|
||||||
@ -47,9 +116,13 @@ func NewHandlerContext(peer *Peer, srvCtx *Context) *HandlerContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hdlCtx *HandlerContext) clientHandler() error {
|
func (hdlCtx *HandlerContext) clientHandler(syncCtx context.Context) error {
|
||||||
|
handleNext:
|
||||||
for {
|
for {
|
||||||
reqFrame := <-hdlCtx.reqFromClient
|
select {
|
||||||
|
case <-syncCtx.Done():
|
||||||
|
return nil
|
||||||
|
case reqFrame := <-hdlCtx.reqFromClient:
|
||||||
var res common.Response
|
var res common.Response
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@ -59,6 +132,8 @@ func (hdlCtx *HandlerContext) clientHandler() error {
|
|||||||
res, err = hdlCtx.handleListPeers(&reqFrame)
|
res, err = hdlCtx.handleListPeers(&reqFrame)
|
||||||
} else if reqFrame.ID == common.EchoReqID {
|
} else if reqFrame.ID == common.EchoReqID {
|
||||||
res, err = hdlCtx.handleEcho(&reqFrame)
|
res, err = hdlCtx.handleEcho(&reqFrame)
|
||||||
|
} else if reqFrame.ID == common.StartChatAReqID {
|
||||||
|
res, err = hdlCtx.handleChatStartA(&reqFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -66,6 +141,11 @@ func (hdlCtx *HandlerContext) clientHandler() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if res == nil {
|
||||||
|
logger.Debugf("request without response ID=%d", reqFrame.ID)
|
||||||
|
continue handleNext
|
||||||
|
}
|
||||||
|
|
||||||
resFrame, err := common.ResponseFrameFrom(res)
|
resFrame, err := common.ResponseFrameFrom(res)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -75,11 +155,15 @@ func (hdlCtx *HandlerContext) clientHandler() error {
|
|||||||
|
|
||||||
hdlCtx.rToClient <- resFrame
|
hdlCtx.rToClient <- resFrame
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hdlCtx *HandlerContext) clientWriter() error {
|
func (hdlCtx *HandlerContext) clientWriter(syncCtx context.Context) error {
|
||||||
for {
|
for {
|
||||||
rFrame := <-hdlCtx.rToClient
|
select {
|
||||||
|
case <-syncCtx.Done():
|
||||||
|
return nil
|
||||||
|
case rFrame := <-hdlCtx.rToClient:
|
||||||
resJsonBytes, err := json.Marshal(rFrame)
|
resJsonBytes, err := json.Marshal(rFrame)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -95,10 +179,15 @@ func (hdlCtx *HandlerContext) clientWriter() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hdlCtx *HandlerContext) clientReader() error {
|
func (hdlCtx *HandlerContext) clientReader(syncCtx context.Context) error {
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-syncCtx.Done():
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
messType, messBytes, err := hdlCtx.peer.conn.ReadMessage()
|
messType, messBytes, err := hdlCtx.peer.conn.ReadMessage()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -131,6 +220,7 @@ func (hdlCtx *HandlerContext) clientReader() error {
|
|||||||
hdlCtx.reqFromClient <- rFrame
|
hdlCtx.reqFromClient <- rFrame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hdlCtx *HandlerContext) sendRequest(req common.Request) error {
|
func (hdlCtx *HandlerContext) sendRequest(req common.Request) error {
|
||||||
@ -148,13 +238,6 @@ func (hdlCtx *HandlerContext) getResponseFrame() common.RFrame {
|
|||||||
return <-hdlCtx.resFromClient
|
return <-hdlCtx.resFromClient
|
||||||
}
|
}
|
||||||
|
|
||||||
type Peer struct {
|
|
||||||
id int
|
|
||||||
conn *websocket.Conn
|
|
||||||
hasAccount bool
|
|
||||||
account *Account
|
|
||||||
}
|
|
||||||
|
|
||||||
var logger = log.NewWithOptions(os.Stdout, log.Options{
|
var logger = log.NewWithOptions(os.Stdout, log.Options{
|
||||||
ReportTimestamp: true,
|
ReportTimestamp: true,
|
||||||
TimeFormat: time.TimeOnly,
|
TimeFormat: time.TimeOnly,
|
||||||
@ -169,32 +252,36 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPeer(conn *websocket.Conn) *Peer {
|
type Matcher[T any] func(*T) bool
|
||||||
return &Peer{-1, conn, false, nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
func peerSliceIndexOf(s []*Peer, id int) int {
|
func (ctx *Context) removePeer(peer *Peer) {
|
||||||
i := 0
|
ctx.handlerContextsLock.Lock()
|
||||||
var p *Peer
|
ctx.peersListLock.Lock()
|
||||||
|
ctx.initiationsLock.Lock()
|
||||||
|
|
||||||
for i, p = range s {
|
ctx.handlerContexts = slices.DeleteFunc[[]*HandlerContext, *HandlerContext](
|
||||||
if p.id == id {
|
ctx.handlerContexts,
|
||||||
break
|
func(h *HandlerContext) bool {
|
||||||
}
|
return h.peer.id == peer.id
|
||||||
}
|
})
|
||||||
|
|
||||||
return i
|
ctx.peersList = slices.DeleteFunc[[]*Peer, *Peer](
|
||||||
}
|
ctx.peersList,
|
||||||
|
func(p *Peer) bool {
|
||||||
|
return p.id == peer.id
|
||||||
|
})
|
||||||
|
|
||||||
func peerSliceRemove(s *[]*Peer, i int) {
|
ctx.initiations = slices.DeleteFunc[[]*Initiation, *Initiation](
|
||||||
(*s)[i] = (*s)[len(*s)-1]
|
ctx.initiations,
|
||||||
*s = (*s)[:len(*s)-1]
|
func(i *Initiation) bool {
|
||||||
}
|
return peer.hasAccount && (peer.account.nickname == i.abANick || peer.account.nickname == i.abBNick)
|
||||||
|
})
|
||||||
|
|
||||||
func (srvCtx *Context) removePeer(peer *Peer) {
|
// TODO: Inform the other side about peer leaving
|
||||||
srvCtx.peersListLock.Lock()
|
|
||||||
peerSliceRemove(&srvCtx.peersList, peerSliceIndexOf(srvCtx.peersList, peer.id))
|
ctx.handlerContextsLock.Unlock()
|
||||||
srvCtx.peersListLock.Unlock()
|
ctx.peersListLock.Unlock()
|
||||||
|
ctx.initiationsLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleDisconnection(handlerCtx *HandlerContext) {
|
func handleDisconnection(handlerCtx *HandlerContext) {
|
||||||
@ -230,13 +317,21 @@ func (hdlCtx *HandlerContext) handleListPeers(reqFrame *common.RFrame) (res comm
|
|||||||
listPeersRes := common.ListPeersResponse{PeersInfo: make([]common.PeerInfo, 0)}
|
listPeersRes := common.ListPeersResponse{PeersInfo: make([]common.PeerInfo, 0)}
|
||||||
|
|
||||||
for _, peer := range peersFreeze {
|
for _, peer := range peersFreeze {
|
||||||
|
var nickname string
|
||||||
|
|
||||||
|
if peer.hasAccount {
|
||||||
|
nickname = peer.account.nickname
|
||||||
|
} else {
|
||||||
|
nickname = ""
|
||||||
|
}
|
||||||
|
|
||||||
listPeersRes.PeersInfo = append(
|
listPeersRes.PeersInfo = append(
|
||||||
listPeersRes.PeersInfo,
|
listPeersRes.PeersInfo,
|
||||||
common.PeerInfo{
|
common.PeerInfo{
|
||||||
ID: peer.id,
|
ID: peer.id,
|
||||||
Addr: peer.conn.RemoteAddr().String(),
|
Addr: peer.conn.RemoteAddr().String(),
|
||||||
HasNickname: peer.hasAccount,
|
HasNickname: peer.hasAccount,
|
||||||
Nickname: peer.account.nickname,
|
Nickname: nickname,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -290,11 +385,86 @@ func (hdlCtx *HandlerContext) handleAuth(reqFrame *common.RFrame) (res common.Re
|
|||||||
return authRes, nil
|
return authRes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srvCtx *Context) printConnectedPeers() {
|
func (hdlCtx *HandlerContext) handleChatStartA(reqFrame *common.RFrame) (res common.Response, err error) {
|
||||||
srvCtx.peersListLock.RLock()
|
startChatAReq, err := common.RequestFromFrame[common.StartChatARequest](*reqFrame)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
receiverPeerCtx, err := hdlCtx.getCtxByNick(startChatAReq.Nickname)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug("receiver peer not found")
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// initation started
|
||||||
|
hdlCtx.initiationsLock.Lock()
|
||||||
|
hdlCtx.initiations = append(hdlCtx.initiations, NewInitiation(hdlCtx.peer.account.nickname, startChatAReq.Nickname))
|
||||||
|
hdlCtx.initiationsLock.Unlock()
|
||||||
|
|
||||||
|
chatStartB := common.StartChatBRequest{
|
||||||
|
Nickname: hdlCtx.peer.account.nickname,
|
||||||
|
}
|
||||||
|
|
||||||
|
chatStartBReqF, err := common.RequestFrameFrom(chatStartB)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug("chat start B req frame creation failed")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
receiverPeerCtx.rToClient <- chatStartBReqF
|
||||||
|
|
||||||
|
hdlCtx.initiationsLock.Lock()
|
||||||
|
//
|
||||||
|
hdlCtx.initiationsLock.Unlock()
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hdlCtx *HandlerContext) handleChatStartC(reqFrame *common.RFrame) (res common.Response, err error) {
|
||||||
|
startChatAReq, err := common.RequestFromFrame[common.StartChatARequest](*reqFrame)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
receiverPeerCtx, err := hdlCtx.getCtxByNick(startChatAReq.Nickname)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug("receiver peer not found")
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// initation started
|
||||||
|
hdlCtx.initiationsLock.Lock()
|
||||||
|
hdlCtx.initiations = append(hdlCtx.initiations, NewInitiation(hdlCtx.peer.account.nickname, startChatAReq.Nickname))
|
||||||
|
hdlCtx.initiationsLock.Unlock()
|
||||||
|
|
||||||
|
chatStartB := common.StartChatBRequest{
|
||||||
|
Nickname: hdlCtx.peer.account.nickname,
|
||||||
|
}
|
||||||
|
|
||||||
|
chatStartBReqF, err := common.RequestFrameFrom(chatStartB)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug("chat start B req frame creation failed")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
receiverPeerCtx.rToClient <- chatStartBReqF
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *Context) printDebugInfo() {
|
||||||
|
ctx.peersListLock.RLock()
|
||||||
|
logger.Debug("================================ server state")
|
||||||
logger.Debug("displaying all connections:")
|
logger.Debug("displaying all connections:")
|
||||||
|
|
||||||
for _, p := range srvCtx.peersList {
|
for _, p := range ctx.peersList {
|
||||||
nick := "-"
|
nick := "-"
|
||||||
|
|
||||||
if p.hasAccount {
|
if p.hasAccount {
|
||||||
@ -304,17 +474,23 @@ func (srvCtx *Context) printConnectedPeers() {
|
|||||||
logger.Debugf("ID#%d, Addr:%s, Auth:%t, Nick:%s", p.id, p.conn.RemoteAddr(), p.hasAccount, nick)
|
logger.Debugf("ID#%d, Addr:%s, Auth:%t, Nick:%s", p.id, p.conn.RemoteAddr(), p.hasAccount, nick)
|
||||||
}
|
}
|
||||||
|
|
||||||
srvCtx.peersListLock.RUnlock()
|
logger.Debug("displaying all initiations:")
|
||||||
|
|
||||||
|
for _, i := range ctx.initiations {
|
||||||
|
logger.Debugf("from %s to %s, stage: %d", i.abBNick, i.abBNick, i.stage)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.peersListLock.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srvCtx *Context) addPeer(peer *Peer) {
|
func (ctx *Context) addPeer(peer *Peer) {
|
||||||
srvCtx.idCounterLock.Lock()
|
ctx.idCounterLock.Lock()
|
||||||
srvCtx.idCounter++
|
ctx.idCounter++
|
||||||
peer.id = srvCtx.idCounter
|
peer.id = ctx.idCounter
|
||||||
srvCtx.idCounterLock.Unlock()
|
ctx.idCounterLock.Unlock()
|
||||||
srvCtx.peersListLock.Lock()
|
ctx.peersListLock.Lock()
|
||||||
srvCtx.peersList = append(srvCtx.peersList, peer)
|
ctx.peersList = append(ctx.peersList, peer)
|
||||||
srvCtx.peersListLock.Unlock()
|
ctx.peersListLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEcho(hdlCtx *HandlerContext) {
|
func testEcho(hdlCtx *HandlerContext) {
|
||||||
@ -333,7 +509,7 @@ func testEcho(hdlCtx *HandlerContext) {
|
|||||||
logger.Debug("test echo done", "byteSent", 123, "byteReceived", echoRes.EchoByte)
|
logger.Debug("test echo done", "byteSent", 123, "byteReceived", echoRes.EchoByte)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srvCtx *Context) wsapiHandler(w http.ResponseWriter, r *http.Request) {
|
func (ctx *Context) wsapiHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
upgrader := websocket.Upgrader{}
|
upgrader := websocket.Upgrader{}
|
||||||
conn, err := upgrader.Upgrade(w, r, nil)
|
conn, err := upgrader.Upgrade(w, r, nil)
|
||||||
|
|
||||||
@ -343,8 +519,11 @@ func (srvCtx *Context) wsapiHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
peer := NewPeer(conn)
|
peer := NewPeer(conn)
|
||||||
srvCtx.addPeer(peer)
|
ctx.addPeer(peer)
|
||||||
handlerCtx := NewHandlerContext(peer, srvCtx)
|
handlerCtx := NewHandlerContext(peer, ctx)
|
||||||
|
ctx.handlerContextsLock.Lock()
|
||||||
|
ctx.handlerContexts = append(ctx.handlerContexts, handlerCtx)
|
||||||
|
ctx.handlerContextsLock.Unlock()
|
||||||
defer handleDisconnection(handlerCtx)
|
defer handleDisconnection(handlerCtx)
|
||||||
|
|
||||||
defer func(conn *websocket.Conn) {
|
defer func(conn *websocket.Conn) {
|
||||||
@ -355,10 +534,30 @@ func (srvCtx *Context) wsapiHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}(conn)
|
}(conn)
|
||||||
|
|
||||||
logger.Infof("%s connected", conn.RemoteAddr())
|
logger.Infof("%s connected", conn.RemoteAddr())
|
||||||
errGroup := new(errgroup.Group)
|
|
||||||
errGroup.Go(handlerCtx.clientHandler)
|
errGroup, syncCtx := errgroup.WithContext(context.Background())
|
||||||
errGroup.Go(handlerCtx.clientWriter)
|
|
||||||
errGroup.Go(handlerCtx.clientReader)
|
errGroup.Go(func() error {
|
||||||
|
return handlerCtx.clientHandler(syncCtx)
|
||||||
|
})
|
||||||
|
|
||||||
|
errGroup.Go(func() error {
|
||||||
|
return handlerCtx.clientWriter(syncCtx)
|
||||||
|
})
|
||||||
|
|
||||||
|
errGroup.Go(func() error {
|
||||||
|
return handlerCtx.clientReader(syncCtx)
|
||||||
|
})
|
||||||
|
|
||||||
|
errGroup.Go(func() error {
|
||||||
|
<-syncCtx.Done()
|
||||||
|
time.Sleep(time.Second * 3)
|
||||||
|
close(handlerCtx.rToClient)
|
||||||
|
close(handlerCtx.resFromClient)
|
||||||
|
close(handlerCtx.reqFromClient)
|
||||||
|
return conn.Close()
|
||||||
|
})
|
||||||
|
|
||||||
testEcho(handlerCtx)
|
testEcho(handlerCtx)
|
||||||
err = errGroup.Wait()
|
err = errGroup.Wait()
|
||||||
|
|
||||||
@ -369,11 +568,11 @@ func (srvCtx *Context) wsapiHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RunServer() {
|
func RunServer() {
|
||||||
srvCtx := &Context{peersList: make([]*Peer, 0), accounts: make(map[string]*Account)}
|
srvCtx := NewContext()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
srvCtx.printConnectedPeers()
|
srvCtx.printDebugInfo()
|
||||||
time.Sleep(time.Second * 5)
|
time.Sleep(time.Second * 5)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user