Compare commits

..

No commits in common. "main" and "v0.2.4" have entirely different histories.
main ... v0.2.4

11 changed files with 71 additions and 423 deletions
README.md
config
database
go.sum
html_template
pkg.gosession.go
session
static/js
ws_conn_manager

View file

@ -1,10 +0,0 @@
# godoc
Installera verktyget godoc:
```
go install golang.org/x/tools/cmd/godoc@latest
```
Kör godoc i katalogen för webservice-repot.
Gå till http://localhost:6060/

View file

@ -5,6 +5,7 @@ import (
"gopkg.in/yaml.v3"
// Standard
"errors"
"os"
)
@ -32,8 +33,6 @@ type Config struct {
Session struct {
DaysValid int
}
Application any
}
func New(filename string) (config Config, err error) {
@ -48,10 +47,9 @@ func New(filename string) (config Config, err error) {
return
}
if config.Session.DaysValid == 0 {
err = errors.New("Configuration: session.daysvalid needs to be higher than 0.")
}
return
}
func (config *Config) ParseApplicationConfig(v any) {
yStr, _ := yaml.Marshal(config.Application)
yaml.Unmarshal(yStr, v)
}

View file

@ -12,7 +12,6 @@ import (
// Standard
"database/sql"
"encoding/json"
"fmt"
"log/slog"
)
@ -94,10 +93,6 @@ func webserviceSQLProvider(dbname string, version int) ([]byte, bool) { // {{{
END;
$$;
`,
2: `
ALTER TABLE "_webservice"."session" ADD mfa jsonb DEFAULT '{}' NOT NULL;
`,
}
statement, found := sql[version]
@ -164,7 +159,7 @@ func (db *T) Authenticate(username, password string) (authenticated bool, userID
SELECT id
FROM _webservice.user
WHERE
LOWER(username) = LOWER($1) AND
username = $1 AND
password = _webservice.password_hash(SUBSTRING(password FROM 1 FOR 32), $2::bytea)
`,
username,
@ -184,7 +179,7 @@ func (db *T) NewSession(uuid string) (err error) { // {{{
_, err = db.Conn.Exec("INSERT INTO _webservice.session(uuid) VALUES($1)", uuid)
return
} // }}}
func (db *T) RetrieveSession(uuid string) (sess *session.T, err error) { // {{{
func (db *T) RetrieveSession(uuid string) (sess *session.T, err error) {// {{{
var rows *sqlx.Rows
rows, err = db.Conn.Queryx(`
WITH session_data AS (
@ -194,14 +189,13 @@ func (db *T) RetrieveSession(uuid string) (sess *session.T, err error) { // {{{
WHERE
uuid=$1
RETURNING
uuid, created, last_used, user_id, mfa
uuid, created, last_used, user_id
)
SELECT
sd.uuid, sd.created, sd.last_used,
COALESCE(u.username, '') AS username,
COALESCE(u.name, '') AS name,
COALESCE(u.id, 0) AS user_id,
mfa
COALESCE(u.id, 0) AS user_id
FROM session_data sd
LEFT JOIN _webservice.user u ON sd.user_id = u.id
`,
@ -218,50 +212,17 @@ func (db *T) RetrieveSession(uuid string) (sess *session.T, err error) { // {{{
sess.Authenticated = sess.UserID > 0
}
return
} // }}}
}// }}}
func (db *T) SetSessionUser(uuid string, userID int) (err error) { // {{{
_, err = db.Conn.Exec(`
UPDATE _webservice.session
SET
user_id = CASE
WHEN $1 <= 0 THEN NULL
ELSE $1
END
WHERE uuid=$2
`,
userID,
uuid,
)
_, err = db.Conn.Exec("UPDATE _webservice.session SET user_id=$1 WHERE uuid=$2", userID, uuid)
if err != nil {
return
}
return
} // }}}
func (db *T) SetSessionMFA(uuid string, mfa any) (err error) { // {{{
mfaByte, _ := json.Marshal(mfa)
_, err = db.Conn.Exec(`
UPDATE _webservice.session
SET
mfa = $2
WHERE
uuid = $1
`,
uuid,
mfaByte,
)
if err != nil {
return
}
return
} // }}}
func (db *T) UpdateUserTime(userID int) (err error) { // {{{
_, err = db.Conn.Exec(`UPDATE _webservice.user SET last_login=NOW() WHERE id=$1`, userID)
return
} // }}}
func (db *T) CreateUser(username, password, name string) (userID int64, err error) { // {{{
var row *sql.Row
row = db.Conn.QueryRow(`
func (db *T) CreateUser(username, password, name string) (err error) {// {{{
_, err = db.Conn.Exec(`
INSERT INTO _webservice.user(username, password, name)
VALUES(
$1,
@ -274,48 +235,12 @@ func (db *T) CreateUser(username, password, name string) (userID int64, err erro
),
$3
)
ON CONFLICT (username) DO UPDATE
SET username = EXCLUDED.username
RETURNING id
`,
username,
password,
name,
)
err = row.Scan(&userID)
return
} // }}}
func (db *T) ChangePassword(userID int, currentPassword, newPassword string) (changed bool, err error) { // {{{
var res sql.Result
res, err = db.Conn.Exec(`
UPDATE _webservice.user
SET
"password" = _webservice.password_hash(
/* salt in hex */
ENCODE(_webservice.gen_random_bytes(16), 'hex'),
/* password */
$3::bytea
)
WHERE
id = $1 AND
"password" = _webservice.password_hash(SUBSTRING(password FROM 1 FOR 32), $2::bytea)
`,
userID,
currentPassword,
newPassword,
)
var rowsAffected int64
rowsAffected, err = res.RowsAffected()
if err != nil {
return
}
changed = (rowsAffected == 1)
return
} // }}}
}// }}}
// vim: foldmethod=marker

1
go.sum
View file

@ -1,6 +1,5 @@
git.gibonuddevalla.se/go/dbschema v1.2.0 h1:VhHFfkn/4UnlGy2Ax35Po8vb8E/x6DggtvNUKlGGQyY=
git.gibonuddevalla.se/go/dbschema v1.2.0/go.mod h1:BNw3q/574nXbGoeWyK+tLhRfggVkw2j2aXZzrBKC3ig=
git.gibonuddevalla.se/go/dbschema v1.3.0/go.mod h1:BNw3q/574nXbGoeWyK+tLhRfggVkw2j2aXZzrBKC3ig=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=

View file

@ -1,31 +0,0 @@
package HTMLTemplate
type Page interface {
GetVersion() string
GetLayout() string
GetPage() string
GetData() any
}
type SimplePage struct {
Version string
Layout string
Page string
Data any
}
func (s SimplePage) GetVersion() string {
return s.Version
}
func (s SimplePage) GetLayout() string {
return s.Layout
}
func (s SimplePage) GetPage() string {
return s.Page
}
func (s SimplePage) GetData() any {
return s.Data
}

View file

@ -1,158 +0,0 @@
package HTMLTemplate
import (
// External
werr "git.gibonuddevalla.se/go/wrappederror"
// Standard
"fmt"
"html/template"
"io/fs"
"net/http"
"os"
"regexp"
)
type Engine struct {
parsedTemplates map[string]*template.Template
viewFS fs.FS
staticEmbeddedFS http.Handler
staticLocalFS http.Handler
componentFilenames []string
DevMode bool
}
func NewEngine(viewFS, staticFS fs.FS, devmode bool) (e Engine, err error) { // {{{
e.parsedTemplates = make(map[string]*template.Template)
e.viewFS = viewFS
e.DevMode = devmode
e.componentFilenames, err = e.getComponentFilenames()
// Set up fileservers for static resources.
// The embedded FS is using the embedded files intented for production use.
// The local FS is for development of Javascript to avoid server rebuild (devmode).
var staticSubFS fs.FS
staticSubFS, err = fs.Sub(staticFS, "static")
if err != nil {
return
}
e.staticEmbeddedFS = http.FileServer(http.FS(staticSubFS))
e.staticLocalFS = http.FileServer(http.Dir("static"))
return
} // }}}
func (e *Engine) getComponentFilenames() (files []string, err error) { // {{{
files = []string{}
if err := fs.WalkDir(e.viewFS, "views/components", func(path string, d fs.DirEntry, err error) error {
if d == nil {
return nil
}
if d.IsDir() {
return nil
}
files = append(files, path)
return nil
}); err != nil {
return nil, err
}
return files, nil
} // }}}
func (e *Engine) ReloadTemplates() { // {{{
e.parsedTemplates = make(map[string]*template.Template)
} // }}}
func (e *Engine) StaticResource(w http.ResponseWriter, r *http.Request) { // {{{
var err error
// URLs with pattern /(css|images)/v1.0.0/foobar are stripped of the version.
// To get rid of problems with cached content in browser on a new version release,
// while also not disabling cache altogether.
if r.URL.Path == "/favicon.ico" {
e.staticEmbeddedFS.ServeHTTP(w, r)
return
}
rxp := regexp.MustCompile("^/(css|images|js|fonts)/v[0-9]+/(.*)$")
if comp := rxp.FindStringSubmatch(r.URL.Path); comp != nil {
w.Header().Add("Pragma", "public")
w.Header().Add("Cache-Control", "max-age=604800")
r.URL.Path = fmt.Sprintf("/%s/%s", comp[1], comp[2])
if e.DevMode {
p := fmt.Sprintf("static/%s/%s", comp[1], comp[2])
_, err = os.Stat(p)
if err == nil {
e.staticLocalFS.ServeHTTP(w, r)
}
return
}
}
e.staticEmbeddedFS.ServeHTTP(w, r)
} // }}}
func (e *Engine) getPage(layout, page string) (tmpl *template.Template, err error) { // {{{
layoutFilename := fmt.Sprintf("views/layouts/%s.gotmpl", layout)
pageFilename := fmt.Sprintf("views/pages/%s.gotmpl", page)
if tmpl, found := e.parsedTemplates[page]; found {
return tmpl, nil
}
funcMap := template.FuncMap{
/*
"format_time": func(t time.Time) template.HTML {
return template.HTML(
t.In(smonConfig.Timezone()).Format(`<span class="date">2006-01-02</span> <span class="time">15:04:05<span class="seconds">:05</span></span>`),
)
},
*/
}
filenames := []string{layoutFilename, pageFilename}
filenames = append(filenames, e.componentFilenames...)
if e.DevMode {
tmpl, err = template.New(layout+".gotmpl").Funcs(funcMap).ParseFS(os.DirFS("."), filenames...)
} else {
tmpl, err = template.New(layout+".gotmpl").Funcs(funcMap).ParseFS(e.viewFS, filenames...)
}
if err != nil {
err = werr.Wrap(err).Log()
return
}
e.parsedTemplates[page] = tmpl
return
} // }}}
func (e *Engine) Render(p Page, w http.ResponseWriter, r *http.Request) (err error) { // {{{
if e.DevMode {
e.ReloadTemplates()
}
var tmpl *template.Template
tmpl, err = e.getPage(p.GetLayout(), p.GetPage())
if err != nil {
err = werr.Wrap(err)
return
}
data := map[string]any{
"VERSION": p.GetVersion(),
"LAYOUT": p.GetLayout(),
"PAGE": p.GetPage(),
"ERROR": r.URL.Query().Get("_err"),
"Data": p.GetData(),
}
err = tmpl.Execute(w, data)
if err != nil {
err = werr.Wrap(err)
}
return
} // }}}
// vim: foldmethod=marker

82
pkg.go
View file

@ -1,10 +1,6 @@
/*
The webservice package is used to provide a webservice with sessions:
const VERSION = "v1"
var logger *slog.Logger
func sqlProvider(dbname string, version int) (sql []byte, found bool) {
var err error
sql, err = embeddedSQL.ReadFile(fmt.Sprintf("sql/%05d.sql", version))
@ -15,29 +11,21 @@ The webservice package is used to provide a webservice with sessions:
return
}
func init() {
opts := slog.HandlerOptions{}
logger = slog.New(slog.NewJSONHandler(os.Stdout, &opts))
service, err := webservice.New("/etc/some/webservice.yaml")
if err != nil {
logger.Error("application", "error", err)
os.Exit(1)
}
func main() {
service, err := webservice.New("/etc/some/webservice.yaml", VERSION, logger)
if err != nil {
logger.Error("application", "error", err)
os.Exit(1)
}
service.SetDatabase(sqlProvider)
service.SetAuthenticationHandler(authenticate)
service.SetAuthorizationHandler(authorize)
service.Register("/foo", true, true, foo)
service.Register("/bar", true, false, bar)
err = service.Start()
if err != nil {
logger.Error("webserver", "error", err)
os.Exit(1)
}
service.SetDatabase(sqlProvider)
service.SetAuthenticationHandler(authenticate)
service.SetAuthorizationHandler(authorize)
service.Register("/foo", true, true, foo)
service.Register("/bar", true, false, bar)
err = service.Start()
if err != nil {
logger.Error("webserver", "error", err)
os.Exit(1)
}
*/
package webservice
@ -64,7 +52,7 @@ import (
"strings"
)
const VERSION = "v0.2.17"
const VERSION = "v0.1.0"
type HttpHandler func(http.ResponseWriter, *http.Request)
@ -78,7 +66,7 @@ type ServiceError struct {
type Service struct {
logger *slog.Logger
sessions map[string]*session.T
Config config.Config
config config.Config
Db *database.T
Version string
WsConnectionManager ws_conn_manager.ConnectionManager
@ -99,11 +87,11 @@ type ServiceHandler func(http.ResponseWriter, *http.Request, *session.T)
func New(configFilename, version string, logger *slog.Logger) (service *Service, err error) { // {{{
service = new(Service)
service.Config, err = config.New(configFilename)
service.config, err = config.New(configFilename)
if err != nil {
return
}
logger.Debug("config", "config", service.Config)
logger.Debug("config", "config", service.config)
service.Version = version
service.logger = logger
@ -111,7 +99,7 @@ func New(configFilename, version string, logger *slog.Logger) (service *Service,
service.errorHandler = service.defaultErrorHandler
service.authenticationHandler = service.defaultAuthenticationHandler
service.authorizationHandler = service.defaultAuthorizationHandler
service.WsConnectionManager = ws_conn_manager.NewConnectionManager(service.logger, service.Config.Websocket.Domains)
service.WsConnectionManager = ws_conn_manager.NewConnectionManager(service.logger, service.config.Websocket.Domains)
service.Register("/_session/new", false, false, service.sessionNew)
service.Register("/_session/authenticate", true, false, service.sessionAuthenticate)
@ -125,7 +113,7 @@ func New(configFilename, version string, logger *slog.Logger) (service *Service,
return
} // }}}
func (service *Service) defaultAuthenticationHandler(req AuthenticationRequest, sess *session.T, alreadyAuthenticated bool) (resp AuthenticationResponse, err error) { // {{{
func (service *Service) defaultAuthenticationHandler(req AuthenticationRequest, alreadyAuthenticated bool) (resp AuthenticationResponse, err error) { // {{{
resp.Authenticated = alreadyAuthenticated
service.logger.Info("webservice", "op", "authentication", "username", req.Username, "authenticated", resp.Authenticated)
return
@ -172,7 +160,7 @@ func (service *Service) SetStaticDirectory(directory string, useDirectory bool)
} // }}}
func (service *Service) SetDatabase(sqlProv database.SqlProvider) { // {{{
service.Db = database.New(service.Config.Database)
service.Db = database.New(service.config.Database)
service.Db.SetLogger(service.logger)
service.Db.SetSQLProvider(sqlProv)
return
@ -195,7 +183,7 @@ func (service *Service) Register(path string, requireSession, requireAuthenticat
return
}
sess, found = service.RetrieveSession(headerSessionUUID)
sess, found = service.retrieveSession(headerSessionUUID)
if !found {
service.errorHandler(fmt.Errorf("Session '%s' not found", headerSessionUUID), "001-0001", w)
return
@ -219,7 +207,6 @@ func (service *Service) Register(path string, requireSession, requireAuthenticat
}
}
service.logger.Info("webserver", "op", "request", "path", r.URL.String())
handler(w, r, sess)
})
} // }}}
@ -235,7 +222,7 @@ func (service *Service) InitDatabaseConnection() (err error) { // {{{
}
return
} // }}}
func (service *Service) CreateUser(username, password, name string) (userID int64, err error) { // {{{
func (service *Service) CreateUser(username, password, name string) (err error) { // {{{
if service.Db != nil {
err = service.InitDatabaseConnection()
if err != nil {
@ -243,7 +230,7 @@ func (service *Service) CreateUser(username, password, name string) (userID int6
}
}
userID, err = service.Db.CreateUser(username, password, name)
err = service.Db.CreateUser(username, password, name)
return
} // }}}
func (service *Service) CreateUserPrompt() { // {{{
@ -263,7 +250,7 @@ func (service *Service) CreateUserPrompt() { // {{{
password, _ = reader.ReadString('\n')
password = strings.TrimSpace(password)
_, err = service.CreateUser(username, password, name)
err = service.CreateUser(username, password, name)
if err != nil {
service.logger.Error("application", "error", err)
os.Exit(1)
@ -279,7 +266,7 @@ func (service *Service) Start() (err error) { // {{{
go service.WsConnectionManager.BroadcastLoop()
listen := fmt.Sprintf("%s:%d", service.Config.Network.Address, service.Config.Network.Port)
listen := fmt.Sprintf("%s:%d", service.config.Network.Address, service.config.Network.Port)
service.logger.Info("webserver", "listen", listen)
err = http.ListenAndServe(listen, nil)
return
@ -300,19 +287,11 @@ func (service *Service) StaticHandler(w http.ResponseWriter, r *http.Request, se
rxp := regexp.MustCompile("^/(css|images|js|fonts)/v[0-9]+/(.*)$")
if comp := rxp.FindStringSubmatch(r.URL.Path); comp != nil {
w.Header().Add("Pragma", "public")
w.Header().Add("Cache-Control", "max-age=604800")
r.URL.Path = fmt.Sprintf("/%s/%s", comp[1], comp[2])
p := fmt.Sprintf(service.staticDirectory+"/%s/%s", comp[1], comp[2])
if service.useStaticDirectory {
_, err = os.Stat(p)
if err == nil {
service.staticLocalFileserver.ServeHTTP(w, r)
} else {
service.staticEmbeddedFileserver.ServeHTTP(w, r)
}
_, err = os.Stat(p)
if err == nil {
service.staticLocalFileserver.ServeHTTP(w, r)
} else {
service.staticEmbeddedFileserver.ServeHTTP(w, r)
}
@ -384,11 +363,6 @@ func sessionUUID(r *http.Request) (string, error) { // {{{
headers := r.Header["X-Session-Id"]
if len(headers) > 0 {
return headers[0], nil
} else {
cookie, err := r.Cookie("X-Session-ID")
if err == nil && cookie.Value != "" {
return cookie.Value, nil
}
}
return "", errors.New("Invalid session")
} // }}}

View file

@ -15,20 +15,17 @@ import (
)
type AuthenticationRequest struct {
UserID int `json:"-"`
Username string
Password string
Additional interface{}
Username string
Password string
}
type AuthenticationResponse struct {
OK bool
OK bool
Authenticated bool
UserID int
MFA any
UserID int
}
type AuthenticationHandler func(AuthenticationRequest, *session.T, bool) (AuthenticationResponse, error)
type AuthenticationHandler func(AuthenticationRequest, bool) (AuthenticationResponse, error)
type AuthorizationHandler func(*session.T, *http.Request) (bool, error)
func (service *Service) sessionNew(w http.ResponseWriter, r *http.Request, foo *session.T) { // {{{
@ -49,7 +46,7 @@ func (service *Service) sessionNew(w http.ResponseWriter, r *http.Request, foo *
break
} else {
if _, found = service.RetrieveSession(sess.UUID); found {
if _, found = service.retrieveSession(sess.UUID); found {
continue
}
@ -67,7 +64,7 @@ func (service *Service) sessionNew(w http.ResponseWriter, r *http.Request, foo *
respJSON, _ := json.Marshal(
struct {
OK bool
OK bool
Session session.T
}{
true,
@ -75,17 +72,10 @@ func (service *Service) sessionNew(w http.ResponseWriter, r *http.Request, foo *
},
)
cookie := http.Cookie{}
cookie.Name = "X-Session-ID"
cookie.Value = sess.UUID
cookie.MaxAge = 86400 * 365
cookie.Path = "/"
http.SetCookie(w, &cookie)
w.Write(respJSON)
} // }}}
func (service *Service) sessionAuthenticate(w http.ResponseWriter, r *http.Request, sess *session.T) { // {{{
var authenticatedByFramework bool
var authenticated bool
var authResponse AuthenticationResponse
var err error
reqBody, _ := io.ReadAll(r.Body)
@ -99,51 +89,42 @@ func (service *Service) sessionAuthenticate(w http.ResponseWriter, r *http.Reque
}
// Authenticate against webservice user table if using a database.
var userID int = sess.UserID
if service.Db != nil && userID == 0 {
authenticatedByFramework, userID, err = service.Db.Authenticate(authRequest.Username, authRequest.Password)
var userID int
if service.Db != nil {
authenticated, userID, err = service.Db.Authenticate(authRequest.Username, authRequest.Password)
if err != nil {
service.errorHandler(err, "001-A002", w)
return
}
authRequest.UserID = userID
if authenticated && userID > 0 {
err = service.Db.SetSessionUser(sess.UUID, userID)
if err != nil {
service.errorHandler(err, "001-A003", w)
return
}
}
}
// The authentication handler is provided with the authenticated response of the possible database authentication,
// and given a chance to override it.
authResponse, err = service.authenticationHandler(authRequest, sess, authenticatedByFramework)
authResponse, err = service.authenticationHandler(authRequest, authenticated)
if err != nil {
service.errorHandler(err, "001-F002", w)
return
}
authResponse.UserID = userID
authResponse.OK = true
sess.Authenticated = authResponse.Authenticated
if authResponse.MFA != nil {
err = service.Db.SetSessionMFA(sess.UUID, authResponse.MFA)
if err != nil {
service.errorHandler(err, "001-A003", w)
return
}
}
if authResponse.Authenticated && userID > 0 {
err = service.Db.SetSessionUser(sess.UUID, userID)
if err != nil {
service.errorHandler(err, "001-A003", w)
return
}
service.Db.UpdateUserTime(userID)
}
authResp, _ := json.Marshal(authResponse)
w.Write(authResp)
} // }}}
func (service *Service) sessionRetrieve(w http.ResponseWriter, r *http.Request, sess *session.T) { // {{{
func (service *Service) sessionRetrieve(w http.ResponseWriter, r *http.Request, sess *session.T) {// {{{
response := struct {
OK bool
OK bool
Session *session.T
}{
true,
@ -151,9 +132,9 @@ func (service *Service) sessionRetrieve(w http.ResponseWriter, r *http.Request,
}
out, _ := json.Marshal(response)
w.Write(out)
} // }}}
}// }}}
func (service *Service) RetrieveSession(uuid string) (session *session.T, found bool) { // {{{
func (service *Service) retrieveSession(uuid string) (session *session.T, found bool) { // {{{
var err error
if service.Db == nil {

View file

@ -10,11 +10,8 @@ type T struct {
Created time.Time
LastUsed time.Time `db:"last_used"`
Authenticated bool
MFA any
UserID int `db:"user_id"`
Username string
Name string
ApplicationData any
}

View file

@ -17,7 +17,7 @@ export class Websocket {
start() {//{{{
this.connect()
this.loop()
//this.loop()
}//}}}
loop() {//{{{
setInterval(() => {
@ -27,9 +27,6 @@ export class Websocket {
}
}, 1000)
}//}}}
send(data) {//{{{
this.websocket.send(data)
}//}}}
connect() {//{{{
const protocol = location.protocol;

View file

@ -8,19 +8,15 @@ import (
// Standard
"log/slog"
"net/http"
"slices"
"strings"
"sync"
"slices"
)
type ReadHandler func(*ConnectionManager, *WsConnection, []byte)
type WsConnection struct {
ConnectionManager *ConnectionManager
UUID string
Conn *websocket.Conn
Pruned bool
SessionUUID string
}
type ConnectionManager struct {
connections map[string]*WsConnection
@ -28,8 +24,6 @@ type ConnectionManager struct {
sendQueue chan SendRequest
logger *slog.Logger
domains []string
readHandlers []ReadHandler
connSync sync.Mutex
}
type SendRequest struct {
WsConn *WsConnection
@ -69,9 +63,7 @@ func (cm *ConnectionManager) NewConnection(w http.ResponseWriter, r *http.Reques
}
// Keep track of all connections.
cm.connSync.Lock()
cm.connections[wsConn.UUID] = &wsConn
cm.connSync.Unlock()
// Successfully upgraded to a websocket connection.
cm.logger.Info("websocket", "uuid", wsConn.UUID, "remote_addr", r.RemoteAddr)
@ -80,9 +72,6 @@ func (cm *ConnectionManager) NewConnection(w http.ResponseWriter, r *http.Reques
return &wsConn, nil
} // }}}
func (cm *ConnectionManager) AddMsgHandler(handler ReadHandler) { // {{{
cm.readHandlers = append(cm.readHandlers, handler)
} // }}}
// validateOrigin matches host from X-Forwarded-Host or request host against a list of configured domains.
func (cm *ConnectionManager) validateOrigin(r *http.Request) bool { // {{{
@ -101,9 +90,7 @@ func (cm *ConnectionManager) Prune(wsConn *WsConnection, err error) { // {{{
cm.logger.Info("websocket", "op", "prune", "uuid", wsConn.UUID)
wsConn.Conn.Close()
wsConn.Pruned = true
cm.connSync.Lock()
delete(cm.connections, wsConn.UUID)
cm.connSync.Unlock()
} // }}}
func (cm *ConnectionManager) ReadLoop(wsConn *WsConnection) { // {{{
var data []byte
@ -115,10 +102,7 @@ func (cm *ConnectionManager) ReadLoop(wsConn *WsConnection) { // {{{
}
cm.logger.Debug("websocket", "op", "read", "data", data)
for _, handler := range cm.readHandlers {
go handler(cm, wsConn, data)
}
//cm.Send(wsConn, response)
}
} // }}}
func (cm *ConnectionManager) Read(wsConn *WsConnection) ([]byte, bool) { // {{{
@ -134,14 +118,6 @@ func (cm *ConnectionManager) Read(wsConn *WsConnection) ([]byte, bool) { // {{{
func (cm *ConnectionManager) Send(wsConn *WsConnection, msg interface{}) { // {{{
wsConn.Conn.WriteJSON(msg)
} // }}}
func (cm *ConnectionManager) SendToSessionUUID(uuid string, msg interface{}) { // {{{
for _, wsConn := range cm.connections {
if wsConn.SessionUUID == uuid {
wsConn.Conn.WriteJSON(msg)
break
}
}
} // }}}
func (cm *ConnectionManager) Broadcast(msg interface{}) { // {{{
cm.broadcastQueue <- msg
} // }}}