Compare commits
No commits in common. "main" and "v0.2.7" have entirely different histories.
8 changed files with 30 additions and 302 deletions
10
README.md
10
README.md
|
@ -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/
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
// Standard
|
// Standard
|
||||||
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,8 +33,6 @@ type Config struct {
|
||||||
Session struct {
|
Session struct {
|
||||||
DaysValid int
|
DaysValid int
|
||||||
}
|
}
|
||||||
|
|
||||||
Application any
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(filename string) (config Config, err error) {
|
func New(filename string) (config Config, err error) {
|
||||||
|
@ -48,10 +47,9 @@ func New(filename string) (config Config, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.Session.DaysValid == 0 {
|
||||||
|
err = errors.New("Configuration: session.daysvalid needs to be higher than 0.")
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (config *Config) ParseApplicationConfig(v any) {
|
|
||||||
yStr, _ := yaml.Marshal(config.Application)
|
|
||||||
yaml.Unmarshal(yStr, v)
|
|
||||||
}
|
|
||||||
|
|
|
@ -164,7 +164,7 @@ func (db *T) Authenticate(username, password string) (authenticated bool, userID
|
||||||
SELECT id
|
SELECT id
|
||||||
FROM _webservice.user
|
FROM _webservice.user
|
||||||
WHERE
|
WHERE
|
||||||
LOWER(username) = LOWER($1) AND
|
username = $1 AND
|
||||||
password = _webservice.password_hash(SUBSTRING(password FROM 1 FOR 32), $2::bytea)
|
password = _webservice.password_hash(SUBSTRING(password FROM 1 FOR 32), $2::bytea)
|
||||||
`,
|
`,
|
||||||
username,
|
username,
|
||||||
|
@ -274,8 +274,6 @@ func (db *T) CreateUser(username, password, name string) (userID int64, err erro
|
||||||
),
|
),
|
||||||
$3
|
$3
|
||||||
)
|
)
|
||||||
ON CONFLICT (username) DO UPDATE
|
|
||||||
SET username = EXCLUDED.username
|
|
||||||
RETURNING id
|
RETURNING id
|
||||||
`,
|
`,
|
||||||
username,
|
username,
|
||||||
|
@ -286,36 +284,5 @@ func (db *T) CreateUser(username, password, name string) (userID int64, err erro
|
||||||
err = row.Scan(&userID)
|
err = row.Scan(&userID)
|
||||||
return
|
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
|
// vim: foldmethod=marker
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
71
pkg.go
71
pkg.go
|
@ -1,10 +1,6 @@
|
||||||
/*
|
/*
|
||||||
The webservice package is used to provide a webservice with sessions:
|
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) {
|
func sqlProvider(dbname string, version int) (sql []byte, found bool) {
|
||||||
var err error
|
var err error
|
||||||
sql, err = embeddedSQL.ReadFile(fmt.Sprintf("sql/%05d.sql", version))
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
service, err := webservice.New("/etc/some/webservice.yaml")
|
||||||
func init() {
|
if err != nil {
|
||||||
opts := slog.HandlerOptions{}
|
logger.Error("application", "error", err)
|
||||||
logger = slog.New(slog.NewJSONHandler(os.Stdout, &opts))
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
service.SetDatabase(sqlProvider)
|
||||||
service, err := webservice.New("/etc/some/webservice.yaml", VERSION, logger)
|
service.SetAuthenticationHandler(authenticate)
|
||||||
if err != nil {
|
service.SetAuthorizationHandler(authorize)
|
||||||
logger.Error("application", "error", err)
|
service.Register("/foo", true, true, foo)
|
||||||
os.Exit(1)
|
service.Register("/bar", true, false, bar)
|
||||||
}
|
err = service.Start()
|
||||||
|
if err != nil {
|
||||||
service.SetDatabase(sqlProvider)
|
logger.Error("webserver", "error", err)
|
||||||
service.SetAuthenticationHandler(authenticate)
|
os.Exit(1)
|
||||||
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
|
package webservice
|
||||||
|
@ -64,7 +52,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const VERSION = "v0.2.17"
|
const VERSION = "v0.1.0"
|
||||||
|
|
||||||
type HttpHandler func(http.ResponseWriter, *http.Request)
|
type HttpHandler func(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
@ -78,7 +66,7 @@ type ServiceError struct {
|
||||||
type Service struct {
|
type Service struct {
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
sessions map[string]*session.T
|
sessions map[string]*session.T
|
||||||
Config config.Config
|
config config.Config
|
||||||
Db *database.T
|
Db *database.T
|
||||||
Version string
|
Version string
|
||||||
WsConnectionManager ws_conn_manager.ConnectionManager
|
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) { // {{{
|
func New(configFilename, version string, logger *slog.Logger) (service *Service, err error) { // {{{
|
||||||
service = new(Service)
|
service = new(Service)
|
||||||
|
|
||||||
service.Config, err = config.New(configFilename)
|
service.config, err = config.New(configFilename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.Debug("config", "config", service.Config)
|
logger.Debug("config", "config", service.config)
|
||||||
|
|
||||||
service.Version = version
|
service.Version = version
|
||||||
service.logger = logger
|
service.logger = logger
|
||||||
|
@ -111,7 +99,7 @@ func New(configFilename, version string, logger *slog.Logger) (service *Service,
|
||||||
service.errorHandler = service.defaultErrorHandler
|
service.errorHandler = service.defaultErrorHandler
|
||||||
service.authenticationHandler = service.defaultAuthenticationHandler
|
service.authenticationHandler = service.defaultAuthenticationHandler
|
||||||
service.authorizationHandler = service.defaultAuthorizationHandler
|
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/new", false, false, service.sessionNew)
|
||||||
service.Register("/_session/authenticate", true, false, service.sessionAuthenticate)
|
service.Register("/_session/authenticate", true, false, service.sessionAuthenticate)
|
||||||
|
@ -172,7 +160,7 @@ func (service *Service) SetStaticDirectory(directory string, useDirectory bool)
|
||||||
} // }}}
|
} // }}}
|
||||||
|
|
||||||
func (service *Service) SetDatabase(sqlProv database.SqlProvider) { // {{{
|
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.SetLogger(service.logger)
|
||||||
service.Db.SetSQLProvider(sqlProv)
|
service.Db.SetSQLProvider(sqlProv)
|
||||||
return
|
return
|
||||||
|
@ -279,7 +267,7 @@ func (service *Service) Start() (err error) { // {{{
|
||||||
|
|
||||||
go service.WsConnectionManager.BroadcastLoop()
|
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)
|
service.logger.Info("webserver", "listen", listen)
|
||||||
err = http.ListenAndServe(listen, nil)
|
err = http.ListenAndServe(listen, nil)
|
||||||
return
|
return
|
||||||
|
@ -300,19 +288,11 @@ func (service *Service) StaticHandler(w http.ResponseWriter, r *http.Request, se
|
||||||
|
|
||||||
rxp := regexp.MustCompile("^/(css|images|js|fonts)/v[0-9]+/(.*)$")
|
rxp := regexp.MustCompile("^/(css|images|js|fonts)/v[0-9]+/(.*)$")
|
||||||
if comp := rxp.FindStringSubmatch(r.URL.Path); comp != nil {
|
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])
|
r.URL.Path = fmt.Sprintf("/%s/%s", comp[1], comp[2])
|
||||||
p := fmt.Sprintf(service.staticDirectory+"/%s/%s", comp[1], comp[2])
|
p := fmt.Sprintf(service.staticDirectory+"/%s/%s", comp[1], comp[2])
|
||||||
|
_, err = os.Stat(p)
|
||||||
if service.useStaticDirectory {
|
if err == nil {
|
||||||
_, err = os.Stat(p)
|
service.staticLocalFileserver.ServeHTTP(w, r)
|
||||||
if err == nil {
|
|
||||||
service.staticLocalFileserver.ServeHTTP(w, r)
|
|
||||||
} else {
|
|
||||||
service.staticEmbeddedFileserver.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
service.staticEmbeddedFileserver.ServeHTTP(w, r)
|
service.staticEmbeddedFileserver.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
@ -384,11 +364,6 @@ func sessionUUID(r *http.Request) (string, error) { // {{{
|
||||||
headers := r.Header["X-Session-Id"]
|
headers := r.Header["X-Session-Id"]
|
||||||
if len(headers) > 0 {
|
if len(headers) > 0 {
|
||||||
return headers[0], nil
|
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")
|
return "", errors.New("Invalid session")
|
||||||
} // }}}
|
} // }}}
|
||||||
|
|
|
@ -75,13 +75,6 @@ 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)
|
w.Write(respJSON)
|
||||||
} // }}}
|
} // }}}
|
||||||
func (service *Service) sessionAuthenticate(w http.ResponseWriter, r *http.Request, sess *session.T) { // {{{
|
func (service *Service) sessionAuthenticate(w http.ResponseWriter, r *http.Request, sess *session.T) { // {{{
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ReadHandler func(*ConnectionManager, *WsConnection, []byte)
|
type ReadHandler func(*ConnectionManager, *WsConnection, []byte)
|
||||||
|
@ -29,7 +28,6 @@ type ConnectionManager struct {
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
domains []string
|
domains []string
|
||||||
readHandlers []ReadHandler
|
readHandlers []ReadHandler
|
||||||
connSync sync.Mutex
|
|
||||||
}
|
}
|
||||||
type SendRequest struct {
|
type SendRequest struct {
|
||||||
WsConn *WsConnection
|
WsConn *WsConnection
|
||||||
|
@ -69,9 +67,7 @@ func (cm *ConnectionManager) NewConnection(w http.ResponseWriter, r *http.Reques
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep track of all connections.
|
// Keep track of all connections.
|
||||||
cm.connSync.Lock()
|
|
||||||
cm.connections[wsConn.UUID] = &wsConn
|
cm.connections[wsConn.UUID] = &wsConn
|
||||||
cm.connSync.Unlock()
|
|
||||||
|
|
||||||
// Successfully upgraded to a websocket connection.
|
// Successfully upgraded to a websocket connection.
|
||||||
cm.logger.Info("websocket", "uuid", wsConn.UUID, "remote_addr", r.RemoteAddr)
|
cm.logger.Info("websocket", "uuid", wsConn.UUID, "remote_addr", r.RemoteAddr)
|
||||||
|
@ -101,9 +97,7 @@ func (cm *ConnectionManager) Prune(wsConn *WsConnection, err error) { // {{{
|
||||||
cm.logger.Info("websocket", "op", "prune", "uuid", wsConn.UUID)
|
cm.logger.Info("websocket", "op", "prune", "uuid", wsConn.UUID)
|
||||||
wsConn.Conn.Close()
|
wsConn.Conn.Close()
|
||||||
wsConn.Pruned = true
|
wsConn.Pruned = true
|
||||||
cm.connSync.Lock()
|
|
||||||
delete(cm.connections, wsConn.UUID)
|
delete(cm.connections, wsConn.UUID)
|
||||||
cm.connSync.Unlock()
|
|
||||||
} // }}}
|
} // }}}
|
||||||
func (cm *ConnectionManager) ReadLoop(wsConn *WsConnection) { // {{{
|
func (cm *ConnectionManager) ReadLoop(wsConn *WsConnection) { // {{{
|
||||||
var data []byte
|
var data []byte
|
||||||
|
@ -117,7 +111,7 @@ func (cm *ConnectionManager) ReadLoop(wsConn *WsConnection) { // {{{
|
||||||
cm.logger.Debug("websocket", "op", "read", "data", data)
|
cm.logger.Debug("websocket", "op", "read", "data", data)
|
||||||
|
|
||||||
for _, handler := range cm.readHandlers {
|
for _, handler := range cm.readHandlers {
|
||||||
go handler(cm, wsConn, data)
|
handler(cm, wsConn, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // }}}
|
} // }}}
|
||||||
|
|
Loading…
Add table
Reference in a new issue