From 729534bf1627a95f5a10404737a9d5408acb5737 Mon Sep 17 00:00:00 2001 From: schwarzlichtbezirk Date: Wed, 1 Mar 2023 02:35:04 +0300 Subject: [PATCH] uaid, part 2. --- config.go | 17 +++++++++------- config/settings.yaml | 6 ++++-- folder.go | 16 +++++++-------- handlers.go | 48 +++++++++++++++++++------------------------- routes.go | 20 +++++++++--------- users.go | 20 +++++++++++------- 6 files changed, 64 insertions(+), 63 deletions(-) diff --git a/config.go b/config.go index b35f73d..df2daf2 100644 --- a/config.go +++ b/config.go @@ -43,6 +43,8 @@ type CfgJWTAuth struct { AccessKey string `json:"access-key" yaml:"access-key"` // Key for refresh HS-256 JWT-tokens. RefreshKey string `json:"refresh-key" yaml:"refresh-key"` + // Key to calculate user agent ID by xxhash algorithm. + UaidHmacKey string `json:"uaid-hmac-key" yaml:"uaid-hmac-key"` } // CfgWebServ is web server settings. @@ -55,6 +57,8 @@ type CfgWebServ struct { WriteTimeout time.Duration `json:"write-timeout" yaml:"write-timeout" long:"wt" description:"Maximum duration before timing out writes of the response."` IdleTimeout time.Duration `json:"idle-timeout" yaml:"idle-timeout" long:"it" description:"Maximum amount of time to wait for the next request when keep-alives are enabled."` MaxHeaderBytes int `json:"max-header-bytes" yaml:"max-header-bytes" long:"mhb" description:"Controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line, in bytes."` + // Maximum duration between two ajax-calls to think client is online. + OnlineTimeout time.Duration `json:"online-timeout" yaml:"online-timeout" long:"ot" description:"Maximum duration between two ajax-calls to think client is online."` // Maximum duration to wait for graceful shutdown. ShutdownTimeout time.Duration `json:"shutdown-timeout" yaml:"shutdown-timeout" long:"st" description:"Maximum duration to wait for graceful shutdown."` } @@ -83,8 +87,6 @@ type CfgAppSets struct { WPKName []string `json:"wpk-name" yaml:"wpk-name,flow" long:"wpk" description:"Name of wpk-file with program resources."` // Memory mapping technology for WPK, or load into one solid byte slice otherwise. WPKmmap bool `json:"wpk-mmap" yaml:"wpk-mmap" long:"mmap" description:"Memory mapping technology for WPK, or load into one solid byte slice otherwise."` - // Maximum duration between two ajax-calls to think client is online. - OnlineTimeout time.Duration `json:"online-timeout" yaml:"online-timeout" long:"ot" description:"Maximum duration between two ajax-calls to think client is online."` // Maximum number of cached embedded thumbnails. ThumbCacheMaxNum int `json:"thumb-cache-maxnum" yaml:"thumb-cache-maxnum" long:"pcmn" description:"Maximum number of cached embedded thumbnails."` // Maximum number of converted media files at memory cache. @@ -110,10 +112,11 @@ type Config struct { // Instance of common service settings. var cfg = Config{ // inits default values: CfgJWTAuth: CfgJWTAuth{ - AccessTTL: 1 * 24 * time.Hour, - RefreshTTL: 3 * 24 * time.Hour, - AccessKey: "skJgM4NsbP3fs4k7vh0gfdkgGl8dJTszdLxZ1sQ9ksFnxbgvw2RsGH8xxddUV479", - RefreshKey: "zxK4dUnuq3Lhd1Gzhpr3usI5lAzgvy2t3fmxld2spzz7a5nfv0hsksm9cheyutie", + AccessTTL: 1 * 24 * time.Hour, + RefreshTTL: 3 * 24 * time.Hour, + AccessKey: "skJgM4NsbP3fs4k7vh0gfdkgGl8dJTszdLxZ1sQ9ksFnxbgvw2RsGH8xxddUV479", + RefreshKey: "zxK4dUnuq3Lhd1Gzhpr3usI5lAzgvy2t3fmxld2spzz7a5nfv0hsksm9cheyutie", + UaidHmacKey: "hms-ua", }, CfgWebServ: CfgWebServ{ AutoCert: false, @@ -124,6 +127,7 @@ var cfg = Config{ // inits default values: WriteTimeout: 15 * time.Second, IdleTimeout: 60 * time.Second, MaxHeaderBytes: 1 << 20, + OnlineTimeout: 3 * 60 * time.Second, ShutdownTimeout: 15 * time.Second, }, CfgImgProp: CfgImgProp{ @@ -137,7 +141,6 @@ var cfg = Config{ // inits default values: CfgAppSets: CfgAppSets{ WPKName: []string{"hms-full.wpk"}, WPKmmap: false, - OnlineTimeout: 3 * 60 * time.Second, ThumbCacheMaxNum: 16 * 1024, MediaCacheMaxNum: 64, HdCacheMaxNum: 256, diff --git a/config/settings.yaml b/config/settings.yaml index baf50dc..a22e7ba 100644 --- a/config/settings.yaml +++ b/config/settings.yaml @@ -12,6 +12,8 @@ authentication: access-key: skJgM4NsbP3fs4k7vh0gfdkgGl8dJTszdLxZ1sQ9ksFnxbgvw2RsGH8xxddUV479 # Key for refresh HS-256 JWT-tokens. refresh-key: zxK4dUnuq3Lhd1Gzhpr3usI5lAzgvy2t3fmxld2spzz7a5nfv0hsksm9cheyutie + # Key to calculate user agent ID by xxhash algorithm. + uaid-hmac-key: hms-ua web-server: # See https://golang.org/pkg/net/http/#Server for details. # Indicates to get TLS-certificate from letsencrypt.org service # if this value is true. Uses local TLS-certificate otherwise. @@ -37,6 +39,8 @@ web-server: # See https://golang.org/pkg/net/http/#Server for details. # Controls the maximum number of bytes the server will read parsing # the request header's keys and values, including the request line, in bytes. max-header-bytes: 1048576 # 1M + # Maximum duration between two ajax-calls to think client is online. + online-timeout: 180s # 3 minutes # Maximum duration to wait for graceful shutdown. shutdown-timeout: 15s images-prop: @@ -61,8 +65,6 @@ specification: # On 'true' use memory mapping technology for access to wpk-package nested files. # On 'false' use wpk-package loaded into one solid byte slice. wpk-mmap: false - # Maximum duration between two ajax-calls to think client is online. - online-timeout: 180s # 3 minutes # Maximum number of cached embedded thumbnails. thumb-cache-maxnum: 16384 # Maximum number of converted media files at memory cache. diff --git a/folder.go b/folder.go index 93d1d53..175e1a2 100644 --- a/folder.go +++ b/folder.go @@ -305,15 +305,13 @@ func folderAPI(w http.ResponseWriter, r *http.Request, aid, uid ID_t) { var latency = time.Since(t) Log.Infof("id%d: navigate to %s, items %d, timeout %s", acc.ID, syspath, len(ret.List), latency) - if uaid, err := GetUAID(r); err == nil { - go xormUserlog.InsertOne(&OpenStore{ - UAID: uaid, - AID: aid, - UID: uid, - Path: syspath, - Latency: int(latency / time.Millisecond), - }) - } + go xormUserlog.InsertOne(&OpenStore{ + UAID: RequestUAID(r), + AID: aid, + UID: uid, + Path: syspath, + Latency: int(latency / time.Millisecond), + }) WriteOK(w, r, &ret) } diff --git a/handlers.go b/handlers.go index eefed29..7da2d7c 100644 --- a/handlers.go +++ b/handlers.go @@ -112,15 +112,13 @@ func fileHandler(w http.ResponseWriter, r *http.Request, aid, uid ID_t) { if HasRangeBegin(r) { // beginning of content Log.Infof("id%d: media-hd %s", acc.ID, path.Base(syspath)) - if uaid, err := GetUAID(r); err == nil { - go xormUserlog.InsertOne(&OpenStore{ - UAID: uaid, - AID: aid, - UID: uid, - Path: syspath, - Latency: -1, - }) - } + go xormUserlog.InsertOne(&OpenStore{ + UAID: RequestUAID(r), + AID: aid, + UID: uid, + Path: syspath, + Latency: -1, + }) } w.Header().Set("Content-Type", MimeStr[md.Mime]) http.ServeContent(w, r, syspath, starttime, bytes.NewReader(md.Data)) @@ -147,15 +145,13 @@ func fileHandler(w http.ResponseWriter, r *http.Request, aid, uid ID_t) { if HasRangeBegin(r) { // beginning of content Log.Infof("id%d: media %s", acc.ID, path.Base(syspath)) - if uaid, err := GetUAID(r); err == nil { - go xormUserlog.InsertOne(&OpenStore{ - UAID: uaid, - AID: aid, - UID: uid, - Path: syspath, - Latency: -1, - }) - } + go xormUserlog.InsertOne(&OpenStore{ + UAID: RequestUAID(r), + AID: aid, + UID: uid, + Path: syspath, + Latency: -1, + }) } w.Header().Set("Content-Type", MimeStr[md.Mime]) http.ServeContent(w, r, syspath, starttime, bytes.NewReader(md.Data)) @@ -165,15 +161,13 @@ func fileHandler(w http.ResponseWriter, r *http.Request, aid, uid ID_t) { if HasRangeBegin(r) { // beginning of content Log.Infof("id%d: serve %s", acc.ID, path.Base(syspath)) - if uaid, err := GetUAID(r); err == nil { - go xormUserlog.InsertOne(&OpenStore{ - UAID: uaid, - AID: aid, - UID: uid, - Path: syspath, - Latency: -1, - }) - } + go xormUserlog.InsertOne(&OpenStore{ + UAID: RequestUAID(r), + AID: aid, + UID: uid, + Path: syspath, + Latency: -1, + }) } var content io.ReadSeekCloser diff --git a/routes.go b/routes.go index c722ee0..1692979 100644 --- a/routes.go +++ b/routes.go @@ -509,11 +509,8 @@ func AjaxMiddleware(next http.Handler) http.Handler { isold, isnew bool ) - var ast = &AgentStore{ - Addr: StripPort(r.RemoteAddr), - UA: r.UserAgent(), - } - uanew = ID_t(ast.Hash()) + var addr, ua = StripPort(r.RemoteAddr), r.UserAgent() + uanew = CalcUAID(addr, ua) // UAID at cookie if uaold, _ = GetUAID(r); uaold == 0 { @@ -532,12 +529,13 @@ func AjaxMiddleware(next http.Handler) http.Handler { } UaMap[uanew] = cid go func() { - ast.CID = cid - ast.UAID = uanew - if lang, ok := r.Header["Accept-Language"]; ok { - ast.Lang = lang[0] - } - if _, err := xormUserlog.InsertOne(ast); err != nil { + if _, err := xormUserlog.InsertOne(&AgentStore{ + UAID: uanew, + CID: cid, + Addr: addr, + UA: ua, + Lang: r.Header.Get("Accept-Language"), + }); err != nil { panic(err.Error()) } }() diff --git a/users.go b/users.go index 750e367..74780ab 100644 --- a/users.go +++ b/users.go @@ -15,6 +15,7 @@ import ( var xormUserlog *xorm.Engine +// AgentStore is storage record with user agent string and user host address. type AgentStore struct { UAID ID_t `xorm:"unique"` // user agent ID CID ID_t // client ID @@ -24,6 +25,7 @@ type AgentStore struct { Time Time `xorm:"created"` } +// OpenStore is storage record with some opened file or opened folder. type OpenStore struct { UAID ID_t // client ID AID ID_t `xorm:"default 0"` // access profile ID @@ -44,14 +46,18 @@ var ( uamux sync.Mutex ) -const ua_salt = "hms" - -func (ast *AgentStore) Hash() uint64 { +// CalcUAID calculate user agent ID by xxhash from given strings. +func CalcUAID(addr, ua string) ID_t { var h = xxhash.New() - h.Write(s2b(ua_salt)) - h.Write(s2b(ast.Addr)) - h.Write(s2b(ast.UA)) - return h.Sum64() & 0x7fff_ffff_ffff_ffff + h.Write(s2b(cfg.UaidHmacKey)) + h.Write(s2b(addr)) + h.Write(s2b(ua)) + return ID_t(h.Sum64() & 0x7fff_ffff_ffff_ffff) // clear highest bit for xorm compatibility +} + +// RequestUAID calculate user agent ID from given request. +func RequestUAID(r *http.Request) ID_t { + return CalcUAID(StripPort(r.RemoteAddr), r.UserAgent()) } // InitUserlog inits database user log engine.