mirror of
https://github.com/originalmk/archat-server.git
synced 2025-01-18 16:29:17 +00:00
Finished implementation for B, C, D stages of initiation
This commit is contained in:
parent
d5b891e4f5
commit
62e3ee780b
@ -1,5 +1,6 @@
|
||||
# 🔀 Archat
|
||||
*It is not working application yet, work in progress!*
|
||||
|
||||
Simple P2P server (and client - for testing purposes only, client for end user will be written in NodeJS using Electron, soon).
|
||||
|
||||
## Starting
|
||||
|
124
client/client.go
124
client/client.go
@ -7,8 +7,10 @@ import (
|
||||
"golang.org/x/sync/errgroup"
|
||||
"net/url"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/log"
|
||||
@ -23,6 +25,8 @@ type Context struct {
|
||||
resFromServer chan cm.RFrame
|
||||
reqFromServer chan cm.RFrame
|
||||
rToServer chan cm.RFrame
|
||||
initiations []*cm.Initiation
|
||||
initiationsLock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewClientContext(conn *websocket.Conn) *Context {
|
||||
@ -37,6 +41,7 @@ func NewClientContext(conn *websocket.Conn) *Context {
|
||||
func (cliCtx *Context) serverHandler(syncCtx context.Context) error {
|
||||
defer logger.Debug("server handler last line...")
|
||||
|
||||
handleNext:
|
||||
for {
|
||||
select {
|
||||
case <-syncCtx.Done():
|
||||
@ -44,23 +49,81 @@ func (cliCtx *Context) serverHandler(syncCtx context.Context) error {
|
||||
case reqFrame := <-cliCtx.reqFromServer:
|
||||
logger.Debug("got request from server", "id", reqFrame.ID)
|
||||
|
||||
var res cm.Response
|
||||
var err error
|
||||
|
||||
if reqFrame.ID == cm.EchoReqID {
|
||||
echoReq, err := cm.RequestFromFrame[cm.EchoRequest](reqFrame)
|
||||
res, err = cliCtx.handleEcho(reqFrame)
|
||||
} else if reqFrame.ID == cm.StartChatBReqID {
|
||||
res, err = cliCtx.handleStartChatB(reqFrame)
|
||||
} else if reqFrame.ID == cm.StartChatDReqID {
|
||||
res, err = cliCtx.handleStartChatD(reqFrame)
|
||||
} else {
|
||||
logger.Warn("can't handle it!")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("could not handle request ID=%d", reqFrame.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
resFrame, err := cm.ResponseFrameFrom(cm.EchoResponse(echoReq))
|
||||
if res == nil {
|
||||
logger.Debugf("request without response ID=%d", reqFrame.ID)
|
||||
continue handleNext
|
||||
}
|
||||
|
||||
resFrame, err := cm.ResponseFrameFrom(res)
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("could not create frame from response")
|
||||
return err
|
||||
}
|
||||
|
||||
cliCtx.rToServer <- resFrame
|
||||
} else {
|
||||
logger.Warn("can't handle it!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cliCtx *Context) handleEcho(reqFrame cm.RFrame) (res cm.Response, err error) {
|
||||
echoReq, err := cm.RequestFromFrame[cm.EchoRequest](reqFrame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cm.EchoResponse(echoReq), nil
|
||||
}
|
||||
|
||||
func (cliCtx *Context) handleStartChatB(reqFrame cm.RFrame) (res cm.Response, err error) {
|
||||
startChatBReq, err := cm.RequestFromFrame[cm.StartChatBRequest](reqFrame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger.Infof("got start chat, %s wants to contact. use startchatc command to "+
|
||||
"decide if you want to accept the chat", startChatBReq.Nickname)
|
||||
|
||||
cliCtx.initiationsLock.Lock()
|
||||
cliCtx.initiations = append(cliCtx.initiations, &cm.Initiation{
|
||||
AbANick: startChatBReq.Nickname,
|
||||
AbBNick: "",
|
||||
Stage: cm.InitiationStageB,
|
||||
})
|
||||
cliCtx.initiationsLock.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (cliCtx *Context) handleStartChatD(reqFrame cm.RFrame) (res cm.Response, err error) {
|
||||
startChatDReq, err := cm.RequestFromFrame[cm.StartChatDRequest](reqFrame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger.Infof("servers wants to be punched, got start chat d request for %s with code %s",
|
||||
startChatDReq.Nickname, startChatDReq.PunchCode)
|
||||
logger.Warn("handleStartChatD not implemented yet")
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (cliCtx *Context) serverWriter(syncCtx context.Context) error {
|
||||
@ -133,7 +196,7 @@ func init() {
|
||||
}
|
||||
|
||||
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: nick, Password: pass})
|
||||
|
||||
if err != nil {
|
||||
@ -141,7 +204,7 @@ func sendAuth(ctx *Context, nick, pass string) {
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug("Request sent, waiting for response...")
|
||||
logger.Debug("request sent, waiting for response...")
|
||||
arf := ctx.getResponseFrame()
|
||||
ar, err := cm.ResponseFromFrame[cm.AuthResponse](arf)
|
||||
|
||||
@ -154,7 +217,7 @@ func sendAuth(ctx *Context, nick, pass string) {
|
||||
}
|
||||
|
||||
func sendEcho(ctx *Context, echoByte byte) {
|
||||
logger.Info("Testing echo...", "echoByte", echoByte)
|
||||
logger.Info("testing echo...", "echoByte", echoByte)
|
||||
err := ctx.sendRequest(cm.EchoRequest{EchoByte: echoByte})
|
||||
|
||||
if err != nil {
|
||||
@ -162,7 +225,7 @@ func sendEcho(ctx *Context, echoByte byte) {
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug("Request sent, waiting for response...")
|
||||
logger.Debug("request sent, waiting for response...")
|
||||
ereqf := ctx.getResponseFrame()
|
||||
ereq, err := cm.ResponseFromFrame[cm.EchoResponse](ereqf)
|
||||
|
||||
@ -171,11 +234,11 @@ func sendEcho(ctx *Context, echoByte byte) {
|
||||
return
|
||||
}
|
||||
|
||||
logger.Info("Got response", "echoByte", ereq.EchoByte)
|
||||
logger.Info("got response", "echoByte", ereq.EchoByte)
|
||||
}
|
||||
|
||||
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{})
|
||||
|
||||
if err != nil {
|
||||
@ -183,7 +246,7 @@ func sendListPeers(ctx *Context) {
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug("Request sent, waiting for response...")
|
||||
logger.Debug("request sent, waiting for response...")
|
||||
lpreqf := ctx.getResponseFrame()
|
||||
lpreq, err := cm.ResponseFromFrame[cm.ListPeersResponse](lpreqf)
|
||||
|
||||
@ -196,7 +259,7 @@ func sendListPeers(ctx *Context) {
|
||||
}
|
||||
|
||||
func sendStartChatA(ctx *Context, nick string) {
|
||||
logger.Info("Doing chat start A...")
|
||||
logger.Info("doing chat start A...")
|
||||
err := ctx.sendRequest(cm.StartChatARequest{Nickname: nick})
|
||||
|
||||
if err != nil {
|
||||
@ -204,7 +267,27 @@ func sendStartChatA(ctx *Context, nick string) {
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug("Request sent, no wait for response")
|
||||
logger.Debug("request sent, no wait for response")
|
||||
}
|
||||
|
||||
func sendStartChatC(ctx *Context, nick string) {
|
||||
idx := slices.IndexFunc(ctx.initiations, func(i *cm.Initiation) bool {
|
||||
return i.AbANick == nick
|
||||
})
|
||||
|
||||
if idx == -1 {
|
||||
logger.Warn("user of that nick did not initiate connection, ignoring")
|
||||
return
|
||||
}
|
||||
|
||||
err := ctx.sendRequest(cm.StartChatCRequest{Nickname: nick})
|
||||
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug("request sent, no wait for response")
|
||||
}
|
||||
|
||||
func RunClient() {
|
||||
@ -297,6 +380,21 @@ func RunClient() {
|
||||
}
|
||||
|
||||
sendStartChatA(cliCtx, cmdArgs[0])
|
||||
} else if cmdName == "initations" {
|
||||
logger.Info("displaying all initations...")
|
||||
|
||||
cliCtx.initiationsLock.RLock()
|
||||
for _, i := range cliCtx.initiations {
|
||||
logger.Debugf("from %s, stage: %d", i.AbANick, i.Stage)
|
||||
}
|
||||
cliCtx.initiationsLock.RUnlock()
|
||||
} else if cmdName == "startchatc" {
|
||||
if len(cmdArgs) != 1 {
|
||||
logger.Errorf("startchatc command requires 1 argument, but %d was provided", len(cmdArgs))
|
||||
continue
|
||||
}
|
||||
|
||||
sendStartChatC(cliCtx, cmdArgs[0])
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
@ -174,9 +174,24 @@ func (StartChatCRequest) ID() int {
|
||||
|
||||
type StartChatDRequest struct {
|
||||
Nickname string `json:"nickname"`
|
||||
Accept bool `json:"accept"`
|
||||
PunchCode string `json:"punchCode"`
|
||||
}
|
||||
|
||||
func (StartChatDRequest) ID() int {
|
||||
return StartChatDReqID
|
||||
}
|
||||
|
||||
type Initiation struct {
|
||||
AbANick string
|
||||
AbBNick string
|
||||
Stage int
|
||||
AbAPunchCode string
|
||||
AbBPunchCode string
|
||||
}
|
||||
|
||||
const (
|
||||
InitiationStageA = 1
|
||||
InitiationStageB = 2
|
||||
InitiationStageC = 3
|
||||
InitiationStageD = 4
|
||||
)
|
||||
|
133
server/server.go
133
server/server.go
@ -2,6 +2,7 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
@ -30,26 +31,21 @@ func NewPeer(conn *websocket.Conn) *Peer {
|
||||
return &Peer{-1, conn, false, nil}
|
||||
}
|
||||
|
||||
func (p *Peer) NicknameOrEmpty() string {
|
||||
if p.hasAccount {
|
||||
return p.account.nickname
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type Account struct {
|
||||
nickname string
|
||||
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}
|
||||
func NewInitiation(abA string, abB string) *common.Initiation {
|
||||
return &common.Initiation{AbANick: abA, AbBNick: abB, Stage: common.InitiationStageA}
|
||||
}
|
||||
|
||||
type Context struct {
|
||||
@ -59,7 +55,7 @@ type Context struct {
|
||||
peersListLock sync.RWMutex
|
||||
accounts map[string]*Account
|
||||
accountsLock sync.RWMutex
|
||||
initiations []*Initiation
|
||||
initiations []*common.Initiation
|
||||
initiationsLock sync.RWMutex
|
||||
handlerContexts []*HandlerContext
|
||||
handlerContextsLock sync.RWMutex
|
||||
@ -69,7 +65,7 @@ func NewContext() *Context {
|
||||
return &Context{
|
||||
peersList: make([]*Peer, 0),
|
||||
accounts: make(map[string]*Account),
|
||||
initiations: make([]*Initiation, 0),
|
||||
initiations: make([]*common.Initiation, 0),
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,6 +130,11 @@ handleNext:
|
||||
res, err = hdlCtx.handleEcho(&reqFrame)
|
||||
} else if reqFrame.ID == common.StartChatAReqID {
|
||||
res, err = hdlCtx.handleChatStartA(&reqFrame)
|
||||
} else if reqFrame.ID == common.StartChatCReqID {
|
||||
res, err = hdlCtx.handleChatStartC(&reqFrame)
|
||||
} else {
|
||||
logger.Warnf("can't handle request of ID=%d", reqFrame.ID)
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -271,10 +272,10 @@ func (ctx *Context) removePeer(peer *Peer) {
|
||||
return p.id == peer.id
|
||||
})
|
||||
|
||||
ctx.initiations = slices.DeleteFunc[[]*Initiation, *Initiation](
|
||||
ctx.initiations = slices.DeleteFunc[[]*common.Initiation, *common.Initiation](
|
||||
ctx.initiations,
|
||||
func(i *Initiation) bool {
|
||||
return peer.hasAccount && (peer.account.nickname == i.abANick || peer.account.nickname == i.abBNick)
|
||||
func(i *common.Initiation) bool {
|
||||
return peer.hasAccount && (peer.account.nickname == i.AbANick || peer.account.nickname == i.AbBNick)
|
||||
})
|
||||
|
||||
// TODO: Inform the other side about peer leaving
|
||||
@ -317,21 +318,13 @@ func (hdlCtx *HandlerContext) handleListPeers(reqFrame *common.RFrame) (res comm
|
||||
listPeersRes := common.ListPeersResponse{PeersInfo: make([]common.PeerInfo, 0)}
|
||||
|
||||
for _, peer := range peersFreeze {
|
||||
var nickname string
|
||||
|
||||
if peer.hasAccount {
|
||||
nickname = peer.account.nickname
|
||||
} else {
|
||||
nickname = ""
|
||||
}
|
||||
|
||||
listPeersRes.PeersInfo = append(
|
||||
listPeersRes.PeersInfo,
|
||||
common.PeerInfo{
|
||||
ID: peer.id,
|
||||
Addr: peer.conn.RemoteAddr().String(),
|
||||
HasNickname: peer.hasAccount,
|
||||
Nickname: nickname,
|
||||
Nickname: peer.NicknameOrEmpty(),
|
||||
},
|
||||
)
|
||||
}
|
||||
@ -418,47 +411,101 @@ func (hdlCtx *HandlerContext) handleChatStartA(reqFrame *common.RFrame) (res com
|
||||
receiverPeerCtx.rToClient <- chatStartBReqF
|
||||
|
||||
hdlCtx.initiationsLock.Lock()
|
||||
//
|
||||
idx := slices.IndexFunc(hdlCtx.initiations, func(i *common.Initiation) bool {
|
||||
return i.AbANick == hdlCtx.peer.account.nickname && i.AbBNick == startChatAReq.Nickname
|
||||
})
|
||||
hdlCtx.initiations[idx].Stage = common.InitiationStageB
|
||||
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)
|
||||
hdlCtx.initiationsLock.Lock()
|
||||
startChatCReq, err := common.RequestFromFrame[common.StartChatCRequest](*reqFrame)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
receiverPeerCtx, err := hdlCtx.getCtxByNick(startChatAReq.Nickname)
|
||||
logger.Debugf("got chat start c for %s", startChatCReq.Nickname)
|
||||
|
||||
receiverPeerCtx, err := hdlCtx.getCtxByNick(startChatCReq.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()
|
||||
idx := slices.IndexFunc(hdlCtx.initiations, func(i *common.Initiation) bool {
|
||||
return i.AbBNick == hdlCtx.peer.account.nickname && i.AbANick == startChatCReq.Nickname
|
||||
})
|
||||
|
||||
chatStartB := common.StartChatBRequest{
|
||||
Nickname: hdlCtx.peer.account.nickname,
|
||||
if idx == -1 {
|
||||
logger.Debug("initation not found, won't handle")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
chatStartBReqF, err := common.RequestFrameFrom(chatStartB)
|
||||
if hdlCtx.initiations[idx].Stage != common.InitiationStageB {
|
||||
logger.Debug("initation found, but is not in stage B, won't handle")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
hdlCtx.initiations[idx].Stage = common.InitiationStageC
|
||||
|
||||
aCode, err := generatePunchCode()
|
||||
if err != nil {
|
||||
logger.Error("failed generating punch code for a")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
bCode, err := generatePunchCode()
|
||||
if err != nil {
|
||||
logger.Error("failed generating punch code for b")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
hdlCtx.initiations[idx].AbAPunchCode = aCode
|
||||
hdlCtx.initiations[idx].AbBPunchCode = bCode
|
||||
|
||||
dReqToA := common.StartChatDRequest{Nickname: hdlCtx.peer.account.nickname, PunchCode: aCode}
|
||||
dReqToB := common.StartChatDRequest{Nickname: startChatCReq.Nickname, PunchCode: bCode}
|
||||
|
||||
err = hdlCtx.sendRequest(dReqToB)
|
||||
|
||||
if err != nil {
|
||||
logger.Debug("chat start B req frame creation failed")
|
||||
return nil, err
|
||||
logger.Errorf("could not send chatstartd to B=%s", hdlCtx.peer.account.nickname)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
receiverPeerCtx.rToClient <- chatStartBReqF
|
||||
err = receiverPeerCtx.sendRequest(dReqToA)
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("could not send chatstartd to A=%s", startChatCReq.Nickname)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
hdlCtx.initiations[idx].Stage = common.InitiationStageD
|
||||
|
||||
hdlCtx.initiationsLock.Unlock()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func generatePunchCode() (string, error) {
|
||||
codeBytes := make([]byte, 8)
|
||||
_, err := rand.Read(codeBytes)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for idx, cb := range codeBytes {
|
||||
codeBytes[idx] = 65 + (cb % 26)
|
||||
}
|
||||
|
||||
return string(codeBytes), nil
|
||||
}
|
||||
|
||||
func (ctx *Context) printDebugInfo() {
|
||||
ctx.peersListLock.RLock()
|
||||
logger.Debug("================================ server state")
|
||||
@ -477,7 +524,7 @@ func (ctx *Context) printDebugInfo() {
|
||||
logger.Debug("displaying all initiations:")
|
||||
|
||||
for _, i := range ctx.initiations {
|
||||
logger.Debugf("from %s to %s, stage: %d", i.abBNick, i.abBNick, i.stage)
|
||||
logger.Debugf("from %s to %s, stage: %d", i.AbANick, i.AbBNick, i.Stage)
|
||||
}
|
||||
|
||||
ctx.peersListLock.RUnlock()
|
||||
|
Loading…
x
Reference in New Issue
Block a user