add reconnect to websocket, add get world json object

This commit is contained in:
lbenedar
2026-04-13 17:40:59 +03:00
parent cd204ebcf5
commit 5bb592ea8a
14 changed files with 321 additions and 103 deletions

View File

@@ -5,22 +5,13 @@ import (
) )
func (app *application) ShowText(w http.ResponseWriter, r *http.Request) { func (app *application) ShowText(w http.ResponseWriter, r *http.Request) {
data, err := app.foundryApp.HandleWSRequest("/join") data, err := app.foundryApp.Test()
if err != nil { if err != nil {
app.slogger.Error("", "error", err) app.slogger.Error("", "error", err)
w.Write([]byte(err.Error())) w.Write([]byte(err.Error()))
return return
} }
// setupData, err := models.ParseSetupModel(data)
// if err != nil {
// app.slogger.Error("", "error", err)
// w.Write([]byte(err.Error()))
// return
// }
// fmt.Printf("JSON msg: %t\n", setupData.IsAdmin)
w.Write(data) w.Write(data)
} }

View File

@@ -13,6 +13,7 @@ func (app *application) healthcheckHandler(w http.ResponseWriter, r *http.Reques
"status": app.foundryApp.Status, "status": app.foundryApp.Status,
"is_available": app.foundryApp.IsAvailable, "is_available": app.foundryApp.IsAvailable,
}, },
"config": app.foundryApp.GetHTTP(),
} }
err := app.writeJSON(w, http.StatusOK, env, nil) err := app.writeJSON(w, http.StatusOK, env, nil)
if err != nil { if err != nil {

View File

@@ -6,6 +6,7 @@ import (
"flag" "flag"
"log" "log"
"log/slog" "log/slog"
"net/http/cookiejar"
"os" "os"
"time" "time"
@@ -84,7 +85,7 @@ func (app *application) parseFlags() *requests.FoundryHttpRequest {
var mode string var mode string
flag.StringVar(&mode, "service_mode", "api", "Type of service mode(api|discord|tg)") flag.StringVar(&mode, "service_mode", "api", "Type of service mode(api|discord|tg)")
foundryHttpData := requests.FoundryHttpRequest{SessionID: ""} foundryHttpData := requests.FoundryHttpRequest{SessionID: nil}
flag.StringVar(&foundryHttpData.Host, "foundry_host", "127.0.0.1", "Address to connect to Foundry") flag.StringVar(&foundryHttpData.Host, "foundry_host", "127.0.0.1", "Address to connect to Foundry")
flag.StringVar(&foundryHttpData.Password, "foundry_pass", "", "Password to connect to Foundry") flag.StringVar(&foundryHttpData.Password, "foundry_pass", "", "Password to connect to Foundry")
@@ -112,6 +113,12 @@ func main() {
app := application{foundryApp: &foundry.FoundryApi{}} app := application{foundryApp: &foundry.FoundryApi{}}
foundryHttpData := app.parseFlags() foundryHttpData := app.parseFlags()
jar, err := cookiejar.New(nil)
if err != nil {
app.slogger.Error("Error", "text", err.Error())
return
}
foundryHttpData.Jar = jar
app.slogger.Info("", "dsn", app.cfg.db.dsn) app.slogger.Info("", "dsn", app.cfg.db.dsn)

View File

@@ -55,7 +55,7 @@ func (app *application) serve() error {
} }
app.slogger.Info("Stopped server", "addr", srv.Addr) app.slogger.Info("Stopped server", "addr", srv.Addr)
err = <-shutdownError // err = <-shutdownError
if err != nil { if err != nil {
return err return err
} }

View File

@@ -7,12 +7,23 @@ import (
type WsSessionMsg struct { type WsSessionMsg struct {
SessionId string `json:"sessionId"` SessionId string `json:"sessionId"`
UserId *string `json:"userId,omitempty"` UserId string `json:"userId,omitempty"`
} }
func (msg WsSessionMsg) Action(tr *transport.FoundryTransport, status *types.FoundryStatus) error { func (msg WsSessionMsg) Action(tr *transport.FoundryTransport, status *types.FoundryStatus) error {
if status.IsActive { if status.IsActive {
tr.Logger.Warn("World is started. Please, return to setup page to fully initialize database") tr.Logger.Info("Session msg", "userid", msg.UserId)
if msg.UserId == "" {
err := tr.LogInToWorld()
if err != nil {
return err
}
tr.Logger.Info("World is started. Succesfully logged into world")
close(tr.ReadChan.Reconnect())
return nil
}
// tr.Logger.Warn("World is started. Please, return to setup page to fully initialize database")
// go msg.OnActiveWorld(tr)
return nil return nil
} }
@@ -24,3 +35,15 @@ func (msg WsSessionMsg) Action(tr *transport.FoundryTransport, status *types.Fou
go tr.FillDBWithFoundryData() go tr.FillDBWithFoundryData()
return nil return nil
} }
func (msg WsSessionMsg) OnActiveWorld(tr *transport.FoundryTransport) error {
wsMsg := types.NewWsMessage("world", tr.CurrWsId)
tr.CurrWsId++
answer, err := tr.HandleWebsocketRequest(wsMsg)
if err != nil {
return err
}
tr.Logger.Info("World data", "answer", string(answer))
return nil
}

View File

@@ -3,17 +3,18 @@ package foundry
import ( import (
"context" "context"
"errors" "errors"
"log/slog"
"sync" "sync"
"time" "time"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/actions" "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/actions"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/requests"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/transport" "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/transport"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types" "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/types"
) )
var ( var (
ListenIsDone = errors.New("Listen for websocket data in foundry is stopped") ListenIsDone = errors.New("Listen for websocket data in foundry is stopped")
ReconnectToWebSocket = errors.New("Reconnect to websocket")
ChannelIsClosed = errors.New("Channel is closed") ChannelIsClosed = errors.New("Channel is closed")
CloseTimeoutExceed = errors.New("Timeout of websocket close is exceed") CloseTimeoutExceed = errors.New("Timeout of websocket close is exceed")
@@ -24,7 +25,7 @@ type FoundryApi struct {
transport *transport.FoundryTransport transport *transport.FoundryTransport
IsAvailable bool IsAvailable bool
Logger *slog.Logger // Logger *slog.Logger
Status types.FoundryStatus Status types.FoundryStatus
wg sync.WaitGroup wg sync.WaitGroup
} }
@@ -61,6 +62,7 @@ func (foundry *FoundryApi) ServeWebSocket() error {
case message, ok := <-wsChannels.Msg(): case message, ok := <-wsChannels.Msg():
if !ok { if !ok {
foundry.transport.Logger.Debug("Read channel is closed", "type", types.WebSocketCode) foundry.transport.Logger.Debug("Read channel is closed", "type", types.WebSocketCode)
foundry.transport.Http.SessionID = nil
return ChannelIsClosed return ChannelIsClosed
} }
foundry.transport.Logger.Debug("Data has been received\n", "msg", message.Code, "type", types.WebSocketCode) //, "msg", message.MsgJson) foundry.transport.Logger.Debug("Data has been received\n", "msg", message.Code, "type", types.WebSocketCode) //, "msg", message.MsgJson)
@@ -80,6 +82,7 @@ func (foundry *FoundryApi) ServeWebSocket() error {
continue continue
} }
foundry.transport.Logger.Debug("RespServerChangeCode", "data", data)
if data == nil { if data == nil {
continue continue
} }
@@ -100,13 +103,18 @@ func (foundry *FoundryApi) ServeWebSocket() error {
if errors.As(err, &foundryErr) { if errors.As(err, &foundryErr) {
if foundryErr.IsFatal { if foundryErr.IsFatal {
foundry.transport.CloseWebSocketConn() foundry.transport.CloseWebSocketConn()
foundry.transport.Http.SessionID = nil
return foundryErr return foundryErr
} }
foundry.transport.Logger.Warn("Got error when listening or served", "err", err.Error(), "type", foundryErr.Type, "direction", foundryErr.Direction) foundry.transport.Logger.Warn("Got error when listening or served", "err", err.Error(), "type", foundryErr.Type, "direction", foundryErr.Direction)
} }
case <-wsChannels.Done(): case <-wsChannels.Done():
foundry.transport.CloseWebSocketConn() foundry.transport.CloseWebSocketConn()
foundry.transport.Http.SessionID = nil
return ListenIsDone return ListenIsDone
case <-wsChannels.Reconnect():
foundry.transport.CloseWebSocketConn()
return ReconnectToWebSocket
} }
} }
} }
@@ -151,12 +159,12 @@ func (foundry *FoundryApi) ConnectToWebSocket() (bool, error) {
if err != nil { if err != nil {
return false, err return false, err
} }
}
err = foundry.transport.ConnectToFoundry() err = foundry.transport.ConnectToFoundry()
if err != nil { if err != nil {
return false, err return false, err
} }
}
err = foundry.transport.InitWebSocketConnection() err = foundry.transport.InitWebSocketConnection()
if err != nil { if err != nil {
@@ -184,6 +192,16 @@ func (foundry *FoundryApi) StartListenFoundry() {
for { for {
ok, err := foundry.ConnectToWebSocket() ok, err := foundry.ConnectToWebSocket()
if err != nil { if err != nil {
if errors.Is(err, ReconnectToWebSocket) {
if foundry.transport.ReconnectNum >= foundry.transport.ReconnectNumMax {
return
}
time.Sleep(foundry.transport.ReconnectTimeout)
foundry.transport.ReconnectNum++
foundry.transport.Logger.Info("Reconnecting to WebSocket", "times", foundry.transport.ReconnectNum)
continue
}
foundry.transport.Logger.Error("Error raised", "err", err.Error()) foundry.transport.Logger.Error("Error raised", "err", err.Error())
if ok { if ok {
@@ -199,3 +217,20 @@ func (foundry *FoundryApi) StartListenFoundry() {
} }
}) })
} }
func (foundry *FoundryApi) Test() ([]byte, error) {
wsMsg := types.NewWsMessage("world", foundry.transport.CurrWsId)
foundry.transport.CurrWsId++
answer, err := foundry.transport.HandleWebsocketRequest(wsMsg)
if err != nil {
return nil, err
}
// foundry.transport.Logger.Info("World data", "answer", string(answer))
return answer, nil
}
// TODO: remove
func (foundry *FoundryApi) GetHTTP() *requests.FoundryHttpRequest {
return foundry.transport.Http
}

View File

@@ -4,7 +4,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strings" "net/http/cookiejar"
"net/url"
"gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db" "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/db"
) )
@@ -21,6 +22,7 @@ const (
PlayersPath = "/players" PlayersPath = "/players"
SetupPath = "/setup" SetupPath = "/setup"
UpdatePath = "/update" UpdatePath = "/update"
GamePath = "/game"
SocketPath = "/socket.io" SocketPath = "/socket.io"
) )
@@ -49,23 +51,20 @@ var PathToWorldState = map[string]db.StateType{
type FoundryHttpRequest struct { type FoundryHttpRequest struct {
Host string Host string
Password string Password string
SessionID string SessionID *string
Jar *cookiejar.Jar
} }
func (conf *FoundryHttpRequest) SetSessionTokenFromHeader(resp http.Header) (bool, error) { func (conf *FoundryHttpRequest) SetSessionTokenFromHeader() (bool, error) {
setCookieHeader := resp.Get("Set-Cookie") u, err := url.Parse(fmt.Sprintf("http://%s", conf.Host))
if setCookieHeader == "" { if err != nil {
return false, nil return false, err
} }
for value := range strings.SplitSeq(setCookieHeader, ";") { cookies := conf.Jar.Cookies(u)
if strings.Contains(value, "session") { for i := range cookies {
sessionCookie := strings.Split(value, "=") if cookies[i].Name == "session" {
if len(sessionCookie) != 2 { conf.SessionID = &cookies[i].Value
return false, ErrorSidWrongFormat
}
conf.SessionID = sessionCookie[1]
return true, nil return true, nil
} }
} }
@@ -73,15 +72,30 @@ func (conf *FoundryHttpRequest) SetSessionTokenFromHeader(resp http.Header) (boo
return false, nil return false, nil
} }
func (conf *FoundryHttpRequest) GetRequestHeader() *http.Header { func (conf *FoundryHttpRequest) GetRequestHeader(path string) *http.Header {
header := http.Header{} header := http.Header{}
header.Set("Cookie", fmt.Sprintf("session=%s", conf.SessionID)) header.Set("Cookie", fmt.Sprintf("session=%s", *conf.SessionID))
header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
header.Set("Connection", "keep-alive") header.Set("Connection", "keep-alive")
header.Set("Content-Type", "application/x-www-form-urlencoded") header.Set("Content-Type", "application/x-www-form-urlencoded")
header.Set("Host", conf.Host) header.Set("Host", conf.Host)
header.Set("Origin", fmt.Sprintf("http://%s", conf.Host)) header.Set("Origin", fmt.Sprintf("http://%s", conf.Host))
header.Set("Referer", fmt.Sprintf("http://%s%s", conf.Host, AuthPath)) header.Set("Referer", fmt.Sprintf("http://%s%s", conf.Host, path))
return &header
}
func (conf *FoundryHttpRequest) GetRequestHeaderJson(path string) *http.Header {
header := http.Header{}
header.Set("Accept", "*/*")
header.Set("Accept-Encoding", "gzip, deflate, br, zstd")
header.Set("Connection", "keep-alive")
header.Set("Content-Type", "application/json")
header.Set("Cookie", fmt.Sprintf("session=%s", *conf.SessionID))
header.Set("Host", conf.Host)
header.Set("Origin", fmt.Sprintf("http://%s", conf.Host))
header.Set("Priority", "u=0")
header.Set("Referer", fmt.Sprintf("http://%s%s", conf.Host, path))
return &header return &header
} }

View File

@@ -11,41 +11,57 @@ import (
) )
func (conf *FoundryHttpRequest) GetSessionId() error { func (conf *FoundryHttpRequest) GetSessionId() error {
getResp, err := http.Get(fmt.Sprintf("http://%s%s", conf.Host, AuthPath)) path := fmt.Sprintf("http://%s%s", conf.Host, AuthPath)
req, err := http.NewRequest("GET", path, nil)
if err != nil { if err != nil {
return err return err
} }
_, err = conf.SetSessionTokenFromHeader(getResp.Header) client := &http.Client{Jar: conf.Jar}
_, err = client.Do(req)
if err != nil {
return err return err
} }
func (conf *FoundryHttpRequest) GetSessionToken() (bool, error) { fmt.Printf("%v\n", conf.Jar)
req, err := http.NewRequest("GET", fmt.Sprintf("http://%s%s", conf.Host, AuthPath), nil)
if err != nil { _, err = conf.SetSessionTokenFromHeader()
return false, err return err
} }
header := http.Header{} // func (conf *FoundryHttpRequest) GetSessionToken() (bool, error) {
header.Set("Cookie", fmt.Sprintf("session=%s", conf.SessionID)) // req, err := http.NewRequest("GET", fmt.Sprintf("http://%s%s", conf.Host, AuthPath), nil)
header.Set("Connection", "keep-alive") // if err != nil {
header.Set("Host", conf.Host) // return false, err
header.Set("Origin", fmt.Sprintf("http://%s", conf.Host)) // }
header.Set("Referer", fmt.Sprintf("http://%s%s", conf.Host, AuthPath))
req.Header = header // header := http.Header{}
client := &http.Client{} // header.Set("Cookie", fmt.Sprintf("session=%s", conf.SessionID))
// header.Set("Connection", "keep-alive")
// header.Set("Host", conf.Host)
// header.Set("Origin", fmt.Sprintf("http://%s", conf.Host))
// header.Set("Referer", fmt.Sprintf("http://%s%s", conf.Host, AuthPath))
resp, err := client.Do(req) // req.Header = header
if err != nil { // client := &http.Client{Jar: conf.Jar}
return false, err
}
return conf.SetSessionTokenFromHeader(resp.Header) // resp, err := client.Do(req)
} // if err != nil {
// return false, err
// }
// return conf.SetSessionTokenFromHeader(resp.Header)
// }
func (conf *FoundryHttpRequest) GetStatus() (*json_model.Status, error) { func (conf *FoundryHttpRequest) GetStatus() (*json_model.Status, error) {
getResp, err := http.Get(fmt.Sprintf("http://%s/api/status", conf.Host)) req, err := http.NewRequest("GET", fmt.Sprintf("http://%s/api/status", conf.Host), nil)
if err != nil {
return nil, err
}
client := &http.Client{Jar: conf.Jar}
getResp, err := client.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -64,3 +80,22 @@ func (conf *FoundryHttpRequest) GetStatus() (*json_model.Status, error) {
} }
return &status, nil return &status, nil
} }
func (conf *FoundryHttpRequest) GetGame() (bool, error) {
req, err := http.NewRequest("GET", fmt.Sprintf("http://%s%s", conf.Host, GamePath), nil)
if err != nil {
return false, err
}
header := conf.GetRequestHeader(GamePath)
req.Header = *header
client := &http.Client{Jar: conf.Jar}
_, err = client.Do(req)
if err != nil {
return false, err
}
return true, nil
}

View File

@@ -2,6 +2,7 @@ package requests
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
@@ -17,12 +18,48 @@ func (conf *FoundryHttpRequest) PostAuthenticationData() (*http.Response, error)
return nil, err return nil, err
} }
header := conf.GetRequestHeader() header := conf.GetRequestHeader(AuthPath)
header.Set("Content-Length", fmt.Sprintf("%d", len(authData.Encode()))) header.Set("Content-Length", fmt.Sprintf("%d", len(authData.Encode())))
req.Header = header.Clone() req.Header = header.Clone()
client := &http.Client{} client := &http.Client{Jar: conf.Jar}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
return resp, nil
}
func (conf *FoundryHttpRequest) PostJoinWorld(userId, password string) (*http.Response, error) {
data := struct {
UserId string `json:"userid"`
Password string `json:"password"`
Action string `json:"action"`
Session string `json:"session"`
}{
UserId: userId,
Password: password,
Action: "join",
Session: *conf.SessionID,
}
byteData, err := json.Marshal(data)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", fmt.Sprintf("http://%s%s", conf.Host, JoinPath), bytes.NewBuffer(byteData))
if err != nil {
return nil, err
}
header := conf.GetRequestHeaderJson(JoinPath)
header.Set("Content-Length", fmt.Sprintf("%d", len(byteData)))
req.Header = header.Clone()
client := &http.Client{Jar: conf.Jar}
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -40,12 +77,12 @@ func (conf *FoundryHttpRequest) PostLaunchWorld(world string) (*http.Response, e
return nil, err return nil, err
} }
header := conf.GetRequestHeader() header := conf.GetRequestHeader(SetupPath)
header.Set("Content-Length", fmt.Sprintf("%d", len(launchData.Encode()))) header.Set("Content-Length", fmt.Sprintf("%d", len(launchData.Encode())))
req.Header = header.Clone() req.Header = header.Clone()
client := &http.Client{} client := &http.Client{Jar: conf.Jar}
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -62,12 +99,12 @@ func (conf *FoundryHttpRequest) PostReturnToSetup() (*http.Response, error) {
return nil, err return nil, err
} }
header := conf.GetRequestHeader() header := conf.GetRequestHeader(JoinPath)
header.Set("Content-Length", fmt.Sprintf("%d", len(launchData.Encode()))) header.Set("Content-Length", fmt.Sprintf("%d", len(launchData.Encode())))
req.Header = header.Clone() req.Header = header.Clone()
client := &http.Client{} client := &http.Client{Jar: conf.Jar}
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -37,14 +37,7 @@ func (tr *FoundryTransport) LaunchWorld(worldName string) error {
} }
func (tr *FoundryTransport) ConnectToFoundry() error { func (tr *FoundryTransport) ConnectToFoundry() error {
var err error if tr.Http.Password != "" {
ok, err := tr.Http.GetSessionToken()
if err != nil {
return err
}
if tr.Http.Password != "" && !ok {
err := tr.Authenticate() err := tr.Authenticate()
if err != nil { if err != nil {
return err return err
@@ -56,7 +49,7 @@ func (tr *FoundryTransport) ConnectToFoundry() error {
} }
func (tr *FoundryTransport) Authenticate() error { func (tr *FoundryTransport) Authenticate() error {
if tr.Http.SessionID == "" { if tr.Http.SessionID == nil {
err := tr.Http.GetSessionId() err := tr.Http.GetSessionId()
if err != nil { if err != nil {
return err return err
@@ -74,13 +67,13 @@ func (tr *FoundryTransport) Authenticate() error {
return err return err
} }
return tr.CheckAuthRespAnswer(resp.Body, contentLen) return tr.CheckAuthResponse(resp.Body, contentLen)
} }
func (tr *FoundryTransport) CheckAuthRespAnswer(r io.Reader, respLength int) error { func (tr *FoundryTransport) CheckAuthResponse(body io.Reader, respLength int) error {
authBody := make([]byte, respLength) authBody := make([]byte, respLength)
_, err := r.Read(authBody) _, err := body.Read(authBody)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
return err return err
} }
@@ -92,8 +85,50 @@ func (tr *FoundryTransport) CheckAuthRespAnswer(r io.Reader, respLength int) err
return nil return nil
} }
func (tr *FoundryTransport) LogInToWorld() error {
if tr.Http.SessionID == nil {
err := tr.Http.GetSessionId()
if err != nil {
return err
}
}
// TODO: make userid and password from tr object
resp, err := tr.Http.PostJoinWorld("YpMHZge0dxBZS5Cm", "")
if err != nil {
return err
}
defer resp.Body.Close()
contentLen, err := strconv.Atoi(resp.Header.Get("Content-Length"))
if err != nil {
return err
}
return tr.CheckLoginResponse(resp.Body, contentLen)
// {"userid":"YpMHZge0dxBZS5Cm","password":"","action":"join"}
}
func (tr *FoundryTransport) CheckLoginResponse(body io.Reader, respLength int) error {
joinBody := make([]byte, respLength)
_, err := body.Read(joinBody)
if err != nil && err != io.EOF {
return err
}
tr.Logger.Debug("Received response", "response", string(joinBody))
if !strings.Contains(string(joinBody), "\"status\":\"success\"") {
return ErrorAuthPassWrong
}
// tr.IsAuth = true
return nil
}
func (tr *FoundryTransport) HasSessionId() bool { func (tr *FoundryTransport) HasSessionId() bool {
return tr.Http.SessionID != "" return tr.Http.SessionID != nil
} }
func (tr *FoundryTransport) InitSessionId() error { func (tr *FoundryTransport) InitSessionId() error {

View File

@@ -19,8 +19,12 @@ type FoundryTransport struct {
ReadChan types.ReadChannels ReadChan types.ReadChannels
ExchangeChan types.ExchangeChannels ExchangeChan types.ExchangeChannels
Http requests.FoundryHttpRequest Http *requests.FoundryHttpRequest
ReconnectTimeout time.Duration
ReconnectNum int
ReconnectNumMax int
IsAuth bool IsAuth bool
IsLogin bool
Models *db.Models Models *db.Models
IsDbInit bool IsDbInit bool
@@ -36,8 +40,12 @@ func NewFoundryTransport(dbConn *sql.DB, logger *slog.Logger, httpConfig *reques
ProgressMsg: map[string]chan struct{}{}, ProgressMsg: map[string]chan struct{}{},
}, },
Http: *httpConfig, Http: httpConfig,
ReconnectTimeout: 500 * time.Millisecond,
IsAuth: false, IsAuth: false,
IsLogin: false,
ReconnectNumMax: 1,
ReconnectNum: 0,
Models: db.NewModels(dbConn), Models: db.NewModels(dbConn),
IsDbInit: false, IsDbInit: false,

View File

@@ -1,7 +1,6 @@
package transport package transport
import ( import (
"context"
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
@@ -18,20 +17,39 @@ func (tr *FoundryTransport) InitWebSocketConnection() error {
} }
query := url.Values{} query := url.Values{}
query.Add("session", tr.Http.SessionID) query.Add("session", *tr.Http.SessionID)
query.Add("EIO", "4") query.Add("EIO", "4")
query.Add("transport", "websocket") query.Add("transport", "websocket")
u := url.URL{Scheme: "ws", Host: tr.Http.Host, Path: fmt.Sprintf("%s/", requests.SocketPath), RawQuery: query.Encode()} u := url.URL{Scheme: "ws", Host: tr.Http.Host, Path: fmt.Sprintf("%s/", requests.SocketPath), RawQuery: query.Encode()}
wsHeader := http.Header{} wsHeader := http.Header{}
wsHeader.Set("Cookie", fmt.Sprintf("session=%s", tr.Http.SessionID)) wsHeader.Set("Cookie", fmt.Sprintf("session=%s", *tr.Http.SessionID))
wsHeader.Set("Pragma", "no-cache")
wsHeader.Set("Cache-Control", "no-cache")
wsConn, _, err := websocket.DefaultDialer.Dial(u.String(), wsHeader) dialer := websocket.DefaultDialer
dialer.ReadBufferSize = 128 * 1024 * 1024
dialer.WriteBufferSize = 128 * 1024 * 1024
// dialer.Jar = tr.Http.Jar
// ur, err := url.Parse(fmt.Sprintf("http://%s", tr.Http.Host))
// if err != nil {
// return err
// }
// fmt.Printf("TEST:%v:%v\n", u.String(), dialer.Jar.Cookies(&u))
wsConn, resp, err := dialer.Dial(u.String(), wsHeader)
if err != nil { if err != nil {
return err return err
} }
cookies := resp.Cookies()
for i := range cookies {
fmt.Printf("TEST%d: %v\n", i, cookies[i])
}
// fmt.Printf("TEST1: %v\n")
// fmt.Printf("TEST2: %v\n", wsHeader)
tr.WsConn = wsConn tr.WsConn = wsConn
tr.CurrWsId = 0 tr.CurrWsId = 0
tr.Logger.Info("Successefully connected to Foundry Websocket") tr.Logger.Info("Successefully connected to Foundry Websocket")
@@ -39,6 +57,7 @@ func (tr *FoundryTransport) InitWebSocketConnection() error {
} }
func (tr *FoundryTransport) HandleWebsocketRequest(msg *types.WsMessage) ([]byte, error) { func (tr *FoundryTransport) HandleWebsocketRequest(msg *types.WsMessage) ([]byte, error) {
tr.Logger.Debug("WS: Data has been send\n", "msg", msg.ToString())
err := tr.WsConn.WriteMessage(websocket.TextMessage, msg.ToByteSlice()) err := tr.WsConn.WriteMessage(websocket.TextMessage, msg.ToByteSlice())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -52,8 +71,8 @@ func (tr *FoundryTransport) HandleWebsocketRequest(msg *types.WsMessage) ([]byte
} }
func (tr *FoundryTransport) ReceiveMessage(id int) ([]byte, error) { func (tr *FoundryTransport) ReceiveMessage(id int) ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) // ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() // defer cancel()
for { for {
select { select {
@@ -64,12 +83,12 @@ func (tr *FoundryTransport) ReceiveMessage(id int) ([]byte, error) {
} }
tr.CloseMsgChannel(id, 0) tr.CloseMsgChannel(id, 0)
return msg, nil return msg, nil
case <-ctx.Done(): // case <-ctx.Done():
err := ctx.Err() // err := ctx.Err()
if err != nil { // if err != nil {
return nil, err // return nil, err
} // }
return nil, ErrorTimeout // return nil, ErrorTimeout
default: default:
time.Sleep(5 * time.Microsecond) time.Sleep(5 * time.Microsecond)
} }
@@ -83,6 +102,8 @@ func (tr *FoundryTransport) ListenWebSocket() {
select { select {
case <-tr.ReadChan.Done(): case <-tr.ReadChan.Done():
return return
case <-tr.ReadChan.Reconnect():
return
default: default:
tr.ReadChan.Err() <- &types.FoundryError{Direction: types.ReaderCode, Err: err, Type: types.WebSocketCode, IsFatal: true} tr.ReadChan.Err() <- &types.FoundryError{Direction: types.ReaderCode, Err: err, Type: types.WebSocketCode, IsFatal: true}
} }

View File

@@ -31,6 +31,7 @@ func (channels *ExchangeChannels) Close() {
type ReadChannels struct { type ReadChannels struct {
done chan struct{} done chan struct{}
reconnect chan struct{}
msg chan *WsMessage msg chan *WsMessage
err chan error err chan error
} }
@@ -47,6 +48,10 @@ func (channels ReadChannels) Done() chan struct{} {
return channels.done return channels.done
} }
func (channels ReadChannels) Reconnect() chan struct{} {
return channels.reconnect
}
func CloseChannel[V any](channel chan V) { func CloseChannel[V any](channel chan V) {
select { select {
case _, ok := <-channel: case _, ok := <-channel:
@@ -62,6 +67,7 @@ func (channels *ReadChannels) Close() {
CloseChannel(channels.done) CloseChannel(channels.done)
CloseChannel(channels.err) CloseChannel(channels.err)
CloseChannel(channels.msg) CloseChannel(channels.msg)
CloseChannel(channels.reconnect)
} }
func InitWsChannels() *ReadChannels { func InitWsChannels() *ReadChannels {
@@ -69,5 +75,6 @@ func InitWsChannels() *ReadChannels {
done: make(chan struct{}), done: make(chan struct{}),
err: make(chan error, 10), err: make(chan error, 10),
msg: make(chan *WsMessage, 10), msg: make(chan *WsMessage, 10),
reconnect: make(chan struct{}),
} }
} }

View File

@@ -81,3 +81,7 @@ func ParseWsRespMessage(msg string, requestCodes []string) (*WsMessage, error) {
func NewWsMessageByPage(page string, currWsId int) *WsMessage { func NewWsMessageByPage(page string, currWsId int) *WsMessage {
return &WsMessage{Code: CodesRespToReq[RespServerChangeCode], Id: currWsId, MsgJson: fmt.Sprintf("[\"%s\"]", requests.WsTypeData[page])} return &WsMessage{Code: CodesRespToReq[RespServerChangeCode], Id: currWsId, MsgJson: fmt.Sprintf("[\"%s\"]", requests.WsTypeData[page])}
} }
func NewWsMessage(msg string, currWsId int) *WsMessage {
return &WsMessage{Code: CodesRespToReq[RespServerChangeCode], Id: currWsId, MsgJson: fmt.Sprintf("[\"%s\"]", msg)}
}