mirror of
https://github.com/kataras/iris.git
synced 2025-03-21 11:16:28 +01:00
add two new examples and share the app's specific logger instance with sessions databases and APIBuilder
This commit is contained in:
parent
ef7d365e81
commit
889b7942d3
|
@ -60,7 +60,9 @@
|
||||||
* [Multi](routing/subdomains/multi/main.go)
|
* [Multi](routing/subdomains/multi/main.go)
|
||||||
* [Wildcard](routing/subdomains/wildcard/main.go)
|
* [Wildcard](routing/subdomains/wildcard/main.go)
|
||||||
* [WWW](routing/subdomains/www/main.go)
|
* [WWW](routing/subdomains/www/main.go)
|
||||||
|
* [WWW Method](routing/subdomains/www/www-method/main.go)
|
||||||
* [Redirection](routing/subdomains/redirect/main.go)
|
* [Redirection](routing/subdomains/redirect/main.go)
|
||||||
|
* [Multi Instances](routing/subdomains/redirect/multi-instances/main.go)
|
||||||
* [HTTP Errors View](routing/subdomains/http-errors-view/main.go)
|
* [HTTP Errors View](routing/subdomains/http-errors-view/main.go)
|
||||||
* [HTTP Method Override](https://github.com/kataras/iris/blob/master/middleware/methodoverride/methodoverride_test.go)
|
* [HTTP Method Override](https://github.com/kataras/iris/blob/master/middleware/methodoverride/methodoverride_test.go)
|
||||||
* [API Versioning](routing/versioning/main.go)
|
* [API Versioning](routing/versioning/main.go)
|
||||||
|
|
|
@ -14,7 +14,7 @@ func newApp() *iris.Application {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
app.I18n.Subdomain
|
// app.I18n.Subdomain = false to disable resolve lang code from subdomain.
|
||||||
// app.I18n.LoadAssets for go-bindata.
|
// app.I18n.LoadAssets for go-bindata.
|
||||||
|
|
||||||
// Default values:
|
// Default values:
|
||||||
|
|
|
@ -17,9 +17,10 @@ func newApp() *iris.Application {
|
||||||
|
|
||||||
// Optionally, to minify the HTML5 error response.
|
// Optionally, to minify the HTML5 error response.
|
||||||
// Note that minification might be slower, caching is advised.
|
// Note that minification might be slower, caching is advised.
|
||||||
test.UseError(iris.Minify)
|
// test.UseError(iris.Minify)
|
||||||
|
// or pass it to OnErrorCode:
|
||||||
// Register error code 404 handler.
|
// Register error code 404 handler.
|
||||||
test.OnErrorCode(iris.StatusNotFound, handleNotFoundTestSubdomain)
|
test.OnErrorCode(iris.StatusNotFound, iris.Minify, handleNotFoundTestSubdomain)
|
||||||
|
|
||||||
test.Get("/", testIndex)
|
test.Get("/", testIndex)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/kataras/iris/v12"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := iris.New()
|
||||||
|
|
||||||
|
hosts := map[string]*iris.Application{
|
||||||
|
"mydomain.com": createRoot("www.mydomain.com"), // redirects to www.
|
||||||
|
"www.mydomain.com": createWWW(),
|
||||||
|
"test.mydomain.com": createTest(),
|
||||||
|
}
|
||||||
|
for _, r := range hosts {
|
||||||
|
r.Build()
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Downgrade(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
host := r.Host
|
||||||
|
if host == "" {
|
||||||
|
host = r.URL.Host
|
||||||
|
}
|
||||||
|
|
||||||
|
if router, ok := hosts[host]; ok {
|
||||||
|
router.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
http.NotFound(w, r)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.Listen(":80")
|
||||||
|
}
|
||||||
|
|
||||||
|
func createRoot(redirectTo string) *iris.Application {
|
||||||
|
app := iris.New()
|
||||||
|
app.Downgrade(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fullScheme := "http://"
|
||||||
|
if r.TLS != nil {
|
||||||
|
fullScheme = "https://"
|
||||||
|
}
|
||||||
|
|
||||||
|
http.Redirect(w, r, fullScheme+redirectTo, iris.StatusMovedPermanently)
|
||||||
|
})
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
func createWWW() *iris.Application {
|
||||||
|
app := iris.New()
|
||||||
|
app.Get("/", index)
|
||||||
|
|
||||||
|
users := app.Party("/users")
|
||||||
|
users.Get("/", usersIndex)
|
||||||
|
users.Get("/login", getLogin)
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTest() *iris.Application {
|
||||||
|
app := iris.New()
|
||||||
|
app.Get("/", func(ctx iris.Context) {
|
||||||
|
ctx.WriteString("Test Index")
|
||||||
|
})
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
func index(ctx iris.Context) {
|
||||||
|
ctx.Writef("This is the www.mydomain.com endpoint.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func usersIndex(ctx iris.Context) {
|
||||||
|
ctx.Writef("This is the www.mydomain.com/users endpoint.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLogin(ctx iris.Context) {
|
||||||
|
ctx.Writef("This is the www.mydomain.com/users/login endpoint.")
|
||||||
|
}
|
|
@ -1,8 +1,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import "github.com/kataras/iris/v12"
|
||||||
"github.com/kataras/iris/v12"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newApp() *iris.Application {
|
func newApp() *iris.Application {
|
||||||
app := iris.New()
|
app := iris.New()
|
||||||
|
@ -44,9 +42,9 @@ func newApp() *iris.Application {
|
||||||
ctx.Writef("hi from www.mydomain.com")
|
ctx.Writef("hi from www.mydomain.com")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// See also the "subdomains/redirect" to register redirect router wrappers between subdomains,
|
// See "subdomains/redirect" to register redirect router wrappers between subdomains,
|
||||||
// i.e mydomain.com to www.mydomain.com (like facebook does for SEO reasons(;)).
|
// i.e mydomain.com to www.mydomain.com (like facebook does for SEO reasons(;)).
|
||||||
|
// And ./www-method example.
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,9 +62,7 @@ func main() {
|
||||||
// http://www.mydomain.com/contact
|
// http://www.mydomain.com/contact
|
||||||
// http://www.mydomain.com/api/users
|
// http://www.mydomain.com/api/users
|
||||||
// http://www.mydomain.com/api/users/42
|
// http://www.mydomain.com/api/users/42
|
||||||
if err := app.Listen("mydomain.com:80"); err != nil {
|
app.Listen("mydomain.com:80")
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func info(ctx iris.Context) {
|
func info(ctx iris.Context) {
|
||||||
|
|
51
_examples/routing/subdomains/www/www-method/main.go
Normal file
51
_examples/routing/subdomains/www/www-method/main.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/kataras/iris/v12"
|
||||||
|
|
||||||
|
func newApp() *iris.Application {
|
||||||
|
app := iris.New()
|
||||||
|
// This will create a new "www" subdomain
|
||||||
|
// and redirect root-level domain requests
|
||||||
|
// to that one:
|
||||||
|
www := app.WWW()
|
||||||
|
www.Get("/", info)
|
||||||
|
www.Get("/about", info)
|
||||||
|
www.Get("/contact", info)
|
||||||
|
|
||||||
|
www.PartyFunc("/api/users", func(r iris.Party) {
|
||||||
|
r.Get("/", info)
|
||||||
|
r.Get("/{id:uint64}", info)
|
||||||
|
|
||||||
|
r.Post("/", info)
|
||||||
|
|
||||||
|
r.Put("/{id:uint64}", info)
|
||||||
|
})
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := newApp()
|
||||||
|
// http://mydomain.com
|
||||||
|
// http://mydomain.com/about
|
||||||
|
// http://imydomain.com/contact
|
||||||
|
// http://mydomain.com/api/users
|
||||||
|
// http://mydomain.com/api/users/42
|
||||||
|
|
||||||
|
// http://www.mydomain.com
|
||||||
|
// http://www.mydomain.com/hi
|
||||||
|
// http://www.mydomain.com/about
|
||||||
|
// http://www.mydomain.com/contact
|
||||||
|
// http://www.mydomain.com/api/users
|
||||||
|
// http://www.mydomain.com/api/users/42
|
||||||
|
app.Listen("mydomain.com:80")
|
||||||
|
}
|
||||||
|
|
||||||
|
func info(ctx iris.Context) {
|
||||||
|
method := ctx.Method()
|
||||||
|
subdomain := ctx.Subdomain()
|
||||||
|
path := ctx.Path()
|
||||||
|
|
||||||
|
ctx.Writef("\nInfo\n\n")
|
||||||
|
ctx.Writef("Method: %s\nSubdomain: %s\nPath: %s", method, subdomain, path)
|
||||||
|
}
|
17
aliases.go
17
aliases.go
|
@ -244,23 +244,6 @@ var (
|
||||||
ctx.Next()
|
ctx.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minify is a middleware which minifies the responses
|
|
||||||
// based on the response content type.
|
|
||||||
// Note that minification might be slower, caching is advised.
|
|
||||||
// Customize the minifier through `Application.Minifier()`.
|
|
||||||
Minify = func(ctx Context) {
|
|
||||||
w := ctx.Application().Minifier().ResponseWriter(ctx.ResponseWriter().Naive(), ctx.Request())
|
|
||||||
// Note(@kataras):
|
|
||||||
// We don't use defer w.Close()
|
|
||||||
// because this response writer holds a sync.WaitGroup under the hoods
|
|
||||||
// and we MUST be sure that its wg.Wait is called on request cancelation
|
|
||||||
// and not in the end of handlers chain execution
|
|
||||||
// (which if running a time-consuming task it will delay its resource release).
|
|
||||||
ctx.OnCloseErr(w.Close)
|
|
||||||
ctx.ResponseWriter().SetWriter(w)
|
|
||||||
ctx.Next()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatchImagesAssets is a simple regex expression
|
// MatchImagesAssets is a simple regex expression
|
||||||
// that can be passed to the DirOptions.Cache.CompressIgnore field
|
// that can be passed to the DirOptions.Cache.CompressIgnore field
|
||||||
// in order to skip compression on already-compressed file types
|
// in order to skip compression on already-compressed file types
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
stdContext "context"
|
stdContext "context"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/kataras/golog"
|
"github.com/kataras/golog"
|
||||||
"github.com/tdewolff/minify/v2"
|
"github.com/tdewolff/minify/v2"
|
||||||
|
@ -84,3 +85,51 @@ type Application interface {
|
||||||
// Order may change.
|
// Order may change.
|
||||||
FindClosestPaths(subdomain, searchPath string, n int) []string
|
FindClosestPaths(subdomain, searchPath string, n int) []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
registeredApps []Application
|
||||||
|
mu sync.RWMutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterApplication registers an application to the global shared storage.
|
||||||
|
func RegisterApplication(app Application) {
|
||||||
|
if app == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
registeredApps = append(registeredApps, app)
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastApplication returns the last registered Application.
|
||||||
|
// Handlers has access to the current Application,
|
||||||
|
// use `Context.Application()` instead.
|
||||||
|
func LastApplication() Application {
|
||||||
|
mu.RLock()
|
||||||
|
if n := len(registeredApps); n > 0 {
|
||||||
|
if app := registeredApps[n-1]; app != nil {
|
||||||
|
mu.RUnlock()
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mu.RUnlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultLogger returns a Logger instance for an Iris module.
|
||||||
|
// If the program contains at least one registered Iris Application
|
||||||
|
// before this call then it will return a child of that Application's Logger
|
||||||
|
// otherwise a fresh child of the `golog.Default` will be returned instead.
|
||||||
|
//
|
||||||
|
// It should be used when a module has no access to the Application or its Logger.
|
||||||
|
func DefaultLogger(prefix string) (logger *golog.Logger) {
|
||||||
|
if app := LastApplication(); app != nil {
|
||||||
|
logger = app.Logger()
|
||||||
|
} else {
|
||||||
|
logger = golog.Default
|
||||||
|
}
|
||||||
|
|
||||||
|
logger = logger.Child(prefix)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ import (
|
||||||
"github.com/kataras/iris/v12/hero"
|
"github.com/kataras/iris/v12/hero"
|
||||||
"github.com/kataras/iris/v12/macro"
|
"github.com/kataras/iris/v12/macro"
|
||||||
macroHandler "github.com/kataras/iris/v12/macro/handler"
|
macroHandler "github.com/kataras/iris/v12/macro/handler"
|
||||||
|
|
||||||
|
"github.com/kataras/golog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MethodNone is a Virtual method
|
// MethodNone is a Virtual method
|
||||||
|
@ -153,6 +155,8 @@ func overlapRoute(r *Route, next *Route) {
|
||||||
// APIBuilder the visible API for constructing the router
|
// APIBuilder the visible API for constructing the router
|
||||||
// and child routers.
|
// and child routers.
|
||||||
type APIBuilder struct {
|
type APIBuilder struct {
|
||||||
|
// the application logger.
|
||||||
|
logger *golog.Logger
|
||||||
// parent is the creator of this Party.
|
// parent is the creator of this Party.
|
||||||
// It is nil on Root.
|
// It is nil on Root.
|
||||||
parent *APIBuilder // currently it's used only on UseRouter feature.
|
parent *APIBuilder // currently it's used only on UseRouter feature.
|
||||||
|
@ -227,8 +231,9 @@ var (
|
||||||
|
|
||||||
// NewAPIBuilder creates & returns a new builder
|
// NewAPIBuilder creates & returns a new builder
|
||||||
// which is responsible to build the API and the router handler.
|
// which is responsible to build the API and the router handler.
|
||||||
func NewAPIBuilder() *APIBuilder {
|
func NewAPIBuilder(logger *golog.Logger) *APIBuilder {
|
||||||
return &APIBuilder{
|
return &APIBuilder{
|
||||||
|
logger: logger,
|
||||||
parent: nil,
|
parent: nil,
|
||||||
macros: macro.Defaults,
|
macros: macro.Defaults,
|
||||||
errors: errgroup.New("API Builder"),
|
errors: errgroup.New("API Builder"),
|
||||||
|
@ -240,6 +245,11 @@ func NewAPIBuilder() *APIBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logger returns the Application Logger.
|
||||||
|
func (api *APIBuilder) Logger() *golog.Logger {
|
||||||
|
return api.logger
|
||||||
|
}
|
||||||
|
|
||||||
// IsRoot reports whether this Party is the root Application's one.
|
// IsRoot reports whether this Party is the root Application's one.
|
||||||
// It will return false on all children Parties, no exception.
|
// It will return false on all children Parties, no exception.
|
||||||
func (api *APIBuilder) IsRoot() bool {
|
func (api *APIBuilder) IsRoot() bool {
|
||||||
|
|
|
@ -8,6 +8,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kataras/iris/v12/context"
|
"github.com/kataras/iris/v12/context"
|
||||||
|
|
||||||
|
"github.com/kataras/golog"
|
||||||
)
|
)
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -83,7 +85,7 @@ func BenchmarkAPIBuilder(b *testing.B) {
|
||||||
// i.e /gzhyweumidvelqewrvoyqmzopvuxli/{name:string}/bibrkratnrrhvsjwsxygfwmqwhcstc/{age:int}/end
|
// i.e /gzhyweumidvelqewrvoyqmzopvuxli/{name:string}/bibrkratnrrhvsjwsxygfwmqwhcstc/{age:int}/end
|
||||||
paths := genPaths(routesLength, 15, 42)
|
paths := genPaths(routesLength, 15, 42)
|
||||||
|
|
||||||
api := NewAPIBuilder()
|
api := NewAPIBuilder(golog.Default)
|
||||||
requestHandler := NewDefaultHandler(nil, nil)
|
requestHandler := NewDefaultHandler(nil, nil)
|
||||||
|
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
|
|
@ -246,7 +246,7 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
|
||||||
// logger.Debugf("API: %d registered %s (", len(registeredRoutes), tr)
|
// logger.Debugf("API: %d registered %s (", len(registeredRoutes), tr)
|
||||||
// with:
|
// with:
|
||||||
pio.WriteRich(logger.Printer, debugLevel.Title, debugLevel.ColorCode, debugLevel.Style...)
|
pio.WriteRich(logger.Printer, debugLevel.Title, debugLevel.ColorCode, debugLevel.Style...)
|
||||||
fmt.Fprintf(logger.Printer, " %s API: %d registered %s (", time.Now().Format(logger.TimeFormat), len(registeredRoutes), tr)
|
fmt.Fprintf(logger.Printer, " %s %sAPI: %d registered %s (", time.Now().Format(logger.TimeFormat), logger.Prefix, len(registeredRoutes), tr)
|
||||||
//
|
//
|
||||||
logger.NewLine = bckpNewLine
|
logger.NewLine = bckpNewLine
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"github.com/kataras/iris/v12/context"
|
"github.com/kataras/iris/v12/context"
|
||||||
"github.com/kataras/iris/v12/core/errgroup"
|
"github.com/kataras/iris/v12/core/errgroup"
|
||||||
"github.com/kataras/iris/v12/macro"
|
"github.com/kataras/iris/v12/macro"
|
||||||
|
|
||||||
|
"github.com/kataras/golog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Party is just a group joiner of routes which have the same prefix and share same middleware(s) also.
|
// Party is just a group joiner of routes which have the same prefix and share same middleware(s) also.
|
||||||
|
@ -13,6 +15,9 @@ import (
|
||||||
//
|
//
|
||||||
// Look the `APIBuilder` structure for its implementation.
|
// Look the `APIBuilder` structure for its implementation.
|
||||||
type Party interface {
|
type Party interface {
|
||||||
|
// Logger returns the Application Logger.
|
||||||
|
Logger() *golog.Logger
|
||||||
|
|
||||||
// IsRoot reports whether this Party is the root Application's one.
|
// IsRoot reports whether this Party is the root Application's one.
|
||||||
// It will return false on all children Parties, no exception.
|
// It will return false on all children Parties, no exception.
|
||||||
IsRoot() bool
|
IsRoot() bool
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -20,7 +20,7 @@ require (
|
||||||
github.com/iris-contrib/schema v0.0.2
|
github.com/iris-contrib/schema v0.0.2
|
||||||
github.com/json-iterator/go v1.1.10
|
github.com/json-iterator/go v1.1.10
|
||||||
github.com/kataras/blocks v0.0.2
|
github.com/kataras/blocks v0.0.2
|
||||||
github.com/kataras/golog v0.1.0
|
github.com/kataras/golog v0.1.2
|
||||||
github.com/kataras/neffos v0.0.16
|
github.com/kataras/neffos v0.0.16
|
||||||
github.com/kataras/pio v0.0.10
|
github.com/kataras/pio v0.0.10
|
||||||
github.com/kataras/sitemap v0.0.5
|
github.com/kataras/sitemap v0.0.5
|
||||||
|
|
54
iris.go
54
iris.go
|
@ -99,20 +99,21 @@ type Application struct {
|
||||||
// New creates and returns a fresh empty iris *Application instance.
|
// New creates and returns a fresh empty iris *Application instance.
|
||||||
func New() *Application {
|
func New() *Application {
|
||||||
config := DefaultConfiguration()
|
config := DefaultConfiguration()
|
||||||
|
|
||||||
app := &Application{
|
app := &Application{
|
||||||
config: &config,
|
config: &config,
|
||||||
logger: golog.Default,
|
Router: router.NewRouter(),
|
||||||
minifier: newMinifier(),
|
I18n: i18n.New(),
|
||||||
I18n: i18n.New(),
|
minifier: newMinifier(),
|
||||||
APIBuilder: router.NewAPIBuilder(),
|
|
||||||
Router: router.NewRouter(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger := newLogger(app)
|
||||||
|
app.logger = logger
|
||||||
|
app.APIBuilder = router.NewAPIBuilder(logger)
|
||||||
app.ContextPool = context.New(func() interface{} {
|
app.ContextPool = context.New(func() interface{} {
|
||||||
return context.NewContext(app)
|
return context.NewContext(app)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
context.RegisterApplication(app)
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,6 +162,8 @@ func (app *Application) WWW() router.Party {
|
||||||
// Example: https://github.com/kataras/iris/tree/master/_examples/routing/subdomains/redirect
|
// Example: https://github.com/kataras/iris/tree/master/_examples/routing/subdomains/redirect
|
||||||
func (app *Application) SubdomainRedirect(from, to router.Party) router.Party {
|
func (app *Application) SubdomainRedirect(from, to router.Party) router.Party {
|
||||||
sd := router.NewSubdomainRedirectWrapper(app.ConfigurationReadOnly().GetVHost, from.GetRelPath(), to.GetRelPath())
|
sd := router.NewSubdomainRedirectWrapper(app.ConfigurationReadOnly().GetVHost, from.GetRelPath(), to.GetRelPath())
|
||||||
|
// TODO: add a debug message here or wait for a response from the issuer
|
||||||
|
// so we can force these to run on build state (last registered, first executed).
|
||||||
app.Router.WrapRouter(sd)
|
app.Router.WrapRouter(sd)
|
||||||
return to
|
return to
|
||||||
}
|
}
|
||||||
|
@ -186,6 +189,22 @@ func (app *Application) ConfigurationReadOnly() context.ConfigurationReadOnly {
|
||||||
return app.config
|
return app.config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Maybe, if it's requested:
|
||||||
|
// func (app *Application) SetName(appName string) *iris.Application {
|
||||||
|
// app.config.name = appName
|
||||||
|
// app.logger.SetChildPrefix(appName)
|
||||||
|
// return app
|
||||||
|
// }
|
||||||
|
|
||||||
|
func newLogger(app *Application) *golog.Logger {
|
||||||
|
logger := golog.Default.Child(app)
|
||||||
|
if prefix := os.Getenv("IRIS_APP_NAME"); prefix != "" {
|
||||||
|
logger.SetChildPrefix(prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
return logger
|
||||||
|
}
|
||||||
|
|
||||||
// Logger returns the golog logger instance(pointer) that is being used inside the "app".
|
// Logger returns the golog logger instance(pointer) that is being used inside the "app".
|
||||||
//
|
//
|
||||||
// Available levels:
|
// Available levels:
|
||||||
|
@ -200,7 +219,7 @@ func (app *Application) ConfigurationReadOnly() context.ConfigurationReadOnly {
|
||||||
// Defaults to "info" level.
|
// Defaults to "info" level.
|
||||||
//
|
//
|
||||||
// Callers can use the application's logger which is
|
// Callers can use the application's logger which is
|
||||||
// the same `golog.Default` logger,
|
// the same `golog.Default.LastChild()` logger,
|
||||||
// to print custom logs too.
|
// to print custom logs too.
|
||||||
// Usage:
|
// Usage:
|
||||||
// app.Logger().Error/Errorf("...")
|
// app.Logger().Error/Errorf("...")
|
||||||
|
@ -273,6 +292,25 @@ func newMinifier() *minify.M {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Minify is a middleware which minifies the responses
|
||||||
|
// based on the response content type.
|
||||||
|
// Note that minification might be slower, caching is advised.
|
||||||
|
// Customize the minifier through `Application.Minifier()`.
|
||||||
|
// Usage:
|
||||||
|
// app.Use(iris.Minify)
|
||||||
|
func Minify(ctx Context) {
|
||||||
|
w := ctx.Application().Minifier().ResponseWriter(ctx.ResponseWriter().Naive(), ctx.Request())
|
||||||
|
// Note(@kataras):
|
||||||
|
// We don't use defer w.Close()
|
||||||
|
// because this response writer holds a sync.WaitGroup under the hoods
|
||||||
|
// and we MUST be sure that its wg.Wait is called on request cancelation
|
||||||
|
// and not in the end of handlers chain execution
|
||||||
|
// (which if running a time-consuming task it will delay its resource release).
|
||||||
|
ctx.OnCloseErr(w.Close)
|
||||||
|
ctx.ResponseWriter().SetWriter(w)
|
||||||
|
ctx.Next()
|
||||||
|
}
|
||||||
|
|
||||||
// Minifier returns the minifier instance.
|
// Minifier returns the minifier instance.
|
||||||
// By default it can minifies:
|
// By default it can minifies:
|
||||||
// - text/html
|
// - text/html
|
||||||
|
|
|
@ -247,8 +247,8 @@ var _ websocket.ConnHandler = (*Application)(nil)
|
||||||
// It returns a collection of namespace and events that
|
// It returns a collection of namespace and events that
|
||||||
// were registered through `HandleWebsocket` controllers.
|
// were registered through `HandleWebsocket` controllers.
|
||||||
func (app *Application) GetNamespaces() websocket.Namespaces {
|
func (app *Application) GetNamespaces() websocket.Namespaces {
|
||||||
if golog.Default.Level == golog.DebugLevel {
|
if logger := app.Router.Logger(); logger.Level == golog.DebugLevel {
|
||||||
websocket.EnableDebug(golog.Default)
|
websocket.EnableDebug(logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
return websocket.JoinConnHandlers(app.websocketControllers...).GetNamespaces()
|
return websocket.JoinConnHandlers(app.websocketControllers...).GetNamespaces()
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/kataras/iris/v12/context"
|
"github.com/kataras/iris/v12/context"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/kataras/golog"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -16,6 +17,9 @@ const (
|
||||||
type (
|
type (
|
||||||
// Config is the configuration for sessions. Please read it before using sessions.
|
// Config is the configuration for sessions. Please read it before using sessions.
|
||||||
Config struct {
|
Config struct {
|
||||||
|
// Logger instance for sessions usage, e.g. { Logger: app.Logger() }.
|
||||||
|
// Defauls to a child of "sessions" of the latest Iris Application's main Logger.
|
||||||
|
Logger *golog.Logger
|
||||||
// Cookie string, the session's client cookie name, for example: "mysessionid"
|
// Cookie string, the session's client cookie name, for example: "mysessionid"
|
||||||
//
|
//
|
||||||
// Defaults to "irissessionid".
|
// Defaults to "irissessionid".
|
||||||
|
@ -65,6 +69,10 @@ type (
|
||||||
|
|
||||||
// Validate corrects missing fields configuration fields and returns the right configuration
|
// Validate corrects missing fields configuration fields and returns the right configuration
|
||||||
func (c Config) Validate() Config {
|
func (c Config) Validate() Config {
|
||||||
|
if c.Logger == nil {
|
||||||
|
c.Logger = context.DefaultLogger("sessions")
|
||||||
|
}
|
||||||
|
|
||||||
if c.Cookie == "" {
|
if c.Cookie == "" {
|
||||||
c.Cookie = DefaultCookieName
|
c.Cookie = DefaultCookieName
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kataras/iris/v12/core/memstore"
|
"github.com/kataras/iris/v12/core/memstore"
|
||||||
|
|
||||||
|
"github.com/kataras/golog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrNotImplemented is returned when a particular feature is not yet implemented yet.
|
// ErrNotImplemented is returned when a particular feature is not yet implemented yet.
|
||||||
|
@ -22,6 +24,8 @@ var ErrNotImplemented = errors.New("not implemented yet")
|
||||||
//
|
//
|
||||||
// Look the `sessiondb` folder for databases implementations.
|
// Look the `sessiondb` folder for databases implementations.
|
||||||
type Database interface {
|
type Database interface {
|
||||||
|
// SetLogger should inject a logger to this Database.
|
||||||
|
SetLogger(*golog.Logger)
|
||||||
// Acquire receives a session's lifetime from the database,
|
// Acquire receives a session's lifetime from the database,
|
||||||
// if the return value is LifeTime{} then the session manager sets the life time based on the expiration duration lives in configuration.
|
// if the return value is LifeTime{} then the session manager sets the life time based on the expiration duration lives in configuration.
|
||||||
Acquire(sid string, expires time.Duration) LifeTime
|
Acquire(sid string, expires time.Duration) LifeTime
|
||||||
|
@ -36,7 +40,7 @@ type Database interface {
|
||||||
OnUpdateExpiration(sid string, newExpires time.Duration) error
|
OnUpdateExpiration(sid string, newExpires time.Duration) error
|
||||||
// Set sets a key value of a specific session.
|
// Set sets a key value of a specific session.
|
||||||
// The "immutable" input argument depends on the store, it may not implement it at all.
|
// The "immutable" input argument depends on the store, it may not implement it at all.
|
||||||
Set(sid string, lifetime LifeTime, key string, value interface{}, immutable bool)
|
Set(sid string, lifetime *LifeTime, key string, value interface{}, immutable bool)
|
||||||
// Get retrieves a session value based on the key.
|
// Get retrieves a session value based on the key.
|
||||||
Get(sid string, key string) interface{}
|
Get(sid string, key string) interface{}
|
||||||
// Visit loops through all session keys and values.
|
// Visit loops through all session keys and values.
|
||||||
|
@ -61,6 +65,8 @@ var _ Database = (*mem)(nil)
|
||||||
|
|
||||||
func newMemDB() Database { return &mem{values: make(map[string]*memstore.Store)} }
|
func newMemDB() Database { return &mem{values: make(map[string]*memstore.Store)} }
|
||||||
|
|
||||||
|
func (s *mem) SetLogger(*golog.Logger) {}
|
||||||
|
|
||||||
func (s *mem) Acquire(sid string, expires time.Duration) LifeTime {
|
func (s *mem) Acquire(sid string, expires time.Duration) LifeTime {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
s.values[sid] = new(memstore.Store)
|
s.values[sid] = new(memstore.Store)
|
||||||
|
@ -72,7 +78,7 @@ func (s *mem) Acquire(sid string, expires time.Duration) LifeTime {
|
||||||
func (s *mem) OnUpdateExpiration(string, time.Duration) error { return nil }
|
func (s *mem) OnUpdateExpiration(string, time.Duration) error { return nil }
|
||||||
|
|
||||||
// immutable depends on the store, it may not implement it at all.
|
// immutable depends on the store, it may not implement it at all.
|
||||||
func (s *mem) Set(sid string, lifetime LifeTime, key string, value interface{}, immutable bool) {
|
func (s *mem) Set(sid string, lifetime *LifeTime, key string, value interface{}, immutable bool) {
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
s.values[sid].Save(key, value, immutable)
|
s.values[sid].Save(key, value, immutable)
|
||||||
s.mu.RUnlock()
|
s.mu.RUnlock()
|
||||||
|
|
|
@ -70,7 +70,7 @@ func (p *provider) newSession(man *Sessions, sid string, expires time.Duration)
|
||||||
lifetime.Begin(expires, onExpire)
|
lifetime.Begin(expires, onExpire)
|
||||||
}
|
}
|
||||||
|
|
||||||
sess.Lifetime = lifetime
|
sess.Lifetime = &lifetime
|
||||||
return sess
|
return sess
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ type (
|
||||||
mu sync.RWMutex // for flashes.
|
mu sync.RWMutex // for flashes.
|
||||||
// Lifetime it contains the expiration data, use it for read-only information.
|
// Lifetime it contains the expiration data, use it for read-only information.
|
||||||
// See `Sessions.UpdateExpiration` too.
|
// See `Sessions.UpdateExpiration` too.
|
||||||
Lifetime LifeTime
|
Lifetime *LifeTime
|
||||||
// Man is the sessions manager that this session created of.
|
// Man is the sessions manager that this session created of.
|
||||||
Man *Sessions
|
Man *Sessions
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/kataras/iris/v12/context"
|
||||||
"github.com/kataras/iris/v12/sessions"
|
"github.com/kataras/iris/v12/sessions"
|
||||||
|
|
||||||
"github.com/dgraph-io/badger/v2"
|
"github.com/dgraph-io/badger/v2"
|
||||||
|
@ -26,6 +27,7 @@ type Database struct {
|
||||||
// it's initialized at `New` or `NewFromDB`.
|
// it's initialized at `New` or `NewFromDB`.
|
||||||
// Can be used to get stats.
|
// Can be used to get stats.
|
||||||
Service *badger.DB
|
Service *badger.DB
|
||||||
|
logger *golog.Logger
|
||||||
|
|
||||||
closed uint32 // if 1 is closed.
|
closed uint32 // if 1 is closed.
|
||||||
}
|
}
|
||||||
|
@ -53,11 +55,12 @@ func New(directoryPath string) (*Database, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := badger.DefaultOptions(directoryPath)
|
opts := badger.DefaultOptions(directoryPath)
|
||||||
opts.Logger = golog.Default.Child("[sessionsdb.badger]").DisableNewLine()
|
badgerLogger := context.DefaultLogger("sessionsdb.badger").DisableNewLine()
|
||||||
|
opts.Logger = badgerLogger
|
||||||
|
|
||||||
service, err := badger.Open(opts)
|
service, err := badger.Open(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Errorf("unable to initialize the badger-based session database: %v", err)
|
badgerLogger.Errorf("unable to initialize the badger-based session database: %v\n", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +75,12 @@ func NewFromDB(service *badger.DB) *Database {
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLogger sets the logger once before server ran.
|
||||||
|
// By default the Iris one is injected.
|
||||||
|
func (db *Database) SetLogger(logger *golog.Logger) {
|
||||||
|
db.logger = logger
|
||||||
|
}
|
||||||
|
|
||||||
// Acquire receives a session's lifetime from the database,
|
// Acquire receives a session's lifetime from the database,
|
||||||
// if the return value is LifeTime{} then the session manager sets the life time based on the expiration duration lives in configuration.
|
// if the return value is LifeTime{} then the session manager sets the life time based on the expiration duration lives in configuration.
|
||||||
func (db *Database) Acquire(sid string, expires time.Duration) sessions.LifeTime {
|
func (db *Database) Acquire(sid string, expires time.Duration) sessions.LifeTime {
|
||||||
|
@ -94,7 +103,7 @@ func (db *Database) Acquire(sid string, expires time.Duration) sessions.LifeTime
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Error(err)
|
db.logger.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sessions.LifeTime{} // session manager will handle the rest.
|
return sessions.LifeTime{} // session manager will handle the rest.
|
||||||
|
@ -118,10 +127,10 @@ func makeKey(sid, key string) []byte {
|
||||||
|
|
||||||
// Set sets a key value of a specific session.
|
// Set sets a key value of a specific session.
|
||||||
// Ignore the "immutable".
|
// Ignore the "immutable".
|
||||||
func (db *Database) Set(sid string, lifetime sessions.LifeTime, key string, value interface{}, immutable bool) {
|
func (db *Database) Set(sid string, lifetime *sessions.LifeTime, key string, value interface{}, immutable bool) {
|
||||||
valueBytes, err := sessions.DefaultTranscoder.Marshal(value)
|
valueBytes, err := sessions.DefaultTranscoder.Marshal(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Error(err)
|
db.logger.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +140,7 @@ func (db *Database) Set(sid string, lifetime sessions.LifeTime, key string, valu
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Error(err)
|
db.logger.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +158,7 @@ func (db *Database) Get(sid string, key string) (value interface{}) {
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil && err != badger.ErrKeyNotFound {
|
if err != nil && err != badger.ErrKeyNotFound {
|
||||||
golog.Error(err)
|
db.logger.Error(err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +198,7 @@ func (db *Database) Visit(sid string, cb func(key string, value interface{})) {
|
||||||
return sessions.DefaultTranscoder.Unmarshal(valueBytes, &value)
|
return sessions.DefaultTranscoder.Unmarshal(valueBytes, &value)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Errorf("[sessionsdb.badger.Visit] %v", err)
|
db.logger.Errorf("[sessionsdb.badger.Visit] %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +240,7 @@ func (db *Database) Delete(sid string, key string) (deleted bool) {
|
||||||
txn := db.Service.NewTransaction(true)
|
txn := db.Service.NewTransaction(true)
|
||||||
err := txn.Delete(makeKey(sid, key))
|
err := txn.Delete(makeKey(sid, key))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Error(err)
|
db.logger.Error(err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return txn.Commit() == nil
|
return txn.Commit() == nil
|
||||||
|
@ -250,7 +259,7 @@ func (db *Database) Clear(sid string) {
|
||||||
for iter.Rewind(); iter.ValidForPrefix(prefix); iter.Next() {
|
for iter.Rewind(); iter.ValidForPrefix(prefix); iter.Next() {
|
||||||
key := iter.Item().Key()
|
key := iter.Item().Key()
|
||||||
if err := txn.Delete(key); err != nil {
|
if err := txn.Delete(key); err != nil {
|
||||||
golog.Warnf("Database.Clear: %s: %v", key, err)
|
db.logger.Warnf("Database.Clear: %s: %v", key, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,10 +273,10 @@ func (db *Database) Release(sid string) {
|
||||||
// and remove the $sid.
|
// and remove the $sid.
|
||||||
txn := db.Service.NewTransaction(true)
|
txn := db.Service.NewTransaction(true)
|
||||||
if err := txn.Delete([]byte(sid)); err != nil {
|
if err := txn.Delete([]byte(sid)); err != nil {
|
||||||
golog.Warnf("Database.Release.Delete: %s: %v", sid, err)
|
db.logger.Warnf("Database.Release.Delete: %s: %v", sid, err)
|
||||||
}
|
}
|
||||||
if err := txn.Commit(); err != nil {
|
if err := txn.Commit(); err != nil {
|
||||||
golog.Debugf("Database.Release.Commit: %s: %v", sid, err)
|
db.logger.Debugf("Database.Release.Commit: %s: %v", sid, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,7 +291,7 @@ func closeDB(db *Database) error {
|
||||||
}
|
}
|
||||||
err := db.Service.Close()
|
err := db.Service.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Warnf("closing the badger connection: %v", err)
|
db.logger.Warnf("closing the badger connection: %v", err)
|
||||||
} else {
|
} else {
|
||||||
atomic.StoreUint32(&db.closed, 1)
|
atomic.StoreUint32(&db.closed, 1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ type Database struct {
|
||||||
// it's initialized at `New` or `NewFromDB`.
|
// it's initialized at `New` or `NewFromDB`.
|
||||||
// Can be used to get stats.
|
// Can be used to get stats.
|
||||||
Service *bolt.DB
|
Service *bolt.DB
|
||||||
|
logger *golog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
var errPathMissing = errors.New("path is required")
|
var errPathMissing = errors.New("path is required")
|
||||||
|
@ -91,7 +92,7 @@ func (db *Database) getBucketForSession(tx *bolt.Tx, sid string) *bolt.Bucket {
|
||||||
if b == nil {
|
if b == nil {
|
||||||
// session does not exist, it shouldn't happen, session bucket creation happens once at `Acquire`,
|
// session does not exist, it shouldn't happen, session bucket creation happens once at `Acquire`,
|
||||||
// no need to accept the `bolt.bucket.CreateBucketIfNotExists`'s performance cost.
|
// no need to accept the `bolt.bucket.CreateBucketIfNotExists`'s performance cost.
|
||||||
golog.Debugf("unreachable session access for '%s'", sid)
|
db.logger.Debugf("unreachable session access for '%s'", sid)
|
||||||
}
|
}
|
||||||
|
|
||||||
return b
|
return b
|
||||||
|
@ -122,20 +123,20 @@ func (db *Database) cleanup() error {
|
||||||
if bExp := b.Bucket(expirationName); bExp != nil { // has expiration.
|
if bExp := b.Bucket(expirationName); bExp != nil { // has expiration.
|
||||||
_, expValue := bExp.Cursor().First() // the expiration bucket contains only one key(we don't care, see `Acquire`) value(time.Time) pair.
|
_, expValue := bExp.Cursor().First() // the expiration bucket contains only one key(we don't care, see `Acquire`) value(time.Time) pair.
|
||||||
if expValue == nil {
|
if expValue == nil {
|
||||||
golog.Debugf("cleanup: expiration is there but its value is empty '%s'", v) // should never happen.
|
db.logger.Debugf("cleanup: expiration is there but its value is empty '%s'", v) // should never happen.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var expirationTime time.Time
|
var expirationTime time.Time
|
||||||
if err := sessions.DefaultTranscoder.Unmarshal(expValue, &expirationTime); err != nil {
|
if err := sessions.DefaultTranscoder.Unmarshal(expValue, &expirationTime); err != nil {
|
||||||
golog.Debugf("cleanup: unable to retrieve expiration value for '%s'", v)
|
db.logger.Debugf("cleanup: unable to retrieve expiration value for '%s'", v)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if expirationTime.Before(time.Now()) {
|
if expirationTime.Before(time.Now()) {
|
||||||
// expired, delete the expiration bucket.
|
// expired, delete the expiration bucket.
|
||||||
if err := b.DeleteBucket(expirationName); err != nil {
|
if err := b.DeleteBucket(expirationName); err != nil {
|
||||||
golog.Debugf("cleanup: unable to destroy a session '%s'", bsid)
|
db.logger.Debugf("cleanup: unable to destroy a session '%s'", bsid)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,6 +150,12 @@ func (db *Database) cleanup() error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLogger sets the logger once before server ran.
|
||||||
|
// By default the Iris one is injected.
|
||||||
|
func (db *Database) SetLogger(logger *golog.Logger) {
|
||||||
|
db.logger = logger
|
||||||
|
}
|
||||||
|
|
||||||
var expirationKey = []byte("exp") // it can be random.
|
var expirationKey = []byte("exp") // it can be random.
|
||||||
|
|
||||||
// Acquire receives a session's lifetime from the database,
|
// Acquire receives a session's lifetime from the database,
|
||||||
|
@ -166,14 +173,14 @@ func (db *Database) Acquire(sid string, expires time.Duration) (lifetime session
|
||||||
// don't return a lifetime, let it empty, session manager will do its job.
|
// don't return a lifetime, let it empty, session manager will do its job.
|
||||||
b, err = root.CreateBucket(name)
|
b, err = root.CreateBucket(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Debugf("unable to create a session bucket for '%s': %v", sid, err)
|
db.logger.Debugf("unable to create a session bucket for '%s': %v", sid, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
expirationTime := time.Now().Add(expires)
|
expirationTime := time.Now().Add(expires)
|
||||||
timeBytes, err := sessions.DefaultTranscoder.Marshal(expirationTime)
|
timeBytes, err := sessions.DefaultTranscoder.Marshal(expirationTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Debugf("unable to set an expiration value on session expiration bucket for '%s': %v", sid, err)
|
db.logger.Debugf("unable to set an expiration value on session expiration bucket for '%s': %v", sid, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +201,7 @@ func (db *Database) Acquire(sid string, expires time.Duration) (lifetime session
|
||||||
|
|
||||||
var expirationTime time.Time
|
var expirationTime time.Time
|
||||||
if err = sessions.DefaultTranscoder.Unmarshal(expValue, &expirationTime); err != nil {
|
if err = sessions.DefaultTranscoder.Unmarshal(expValue, &expirationTime); err != nil {
|
||||||
golog.Debugf("acquire: unable to retrieve expiration value for '%s', value was: '%s': %v", sid, expValue, err)
|
db.logger.Debugf("acquire: unable to retrieve expiration value for '%s', value was: '%s': %v", sid, expValue, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,7 +214,7 @@ func (db *Database) Acquire(sid string, expires time.Duration) (lifetime session
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Debugf("unable to acquire session '%s': %v", sid, err)
|
db.logger.Debugf("unable to acquire session '%s': %v", sid, err)
|
||||||
return sessions.LifeTime{}
|
return sessions.LifeTime{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +234,7 @@ func (db *Database) OnUpdateExpiration(sid string, newExpires time.Duration) err
|
||||||
root := db.getBucket(tx)
|
root := db.getBucket(tx)
|
||||||
b := root.Bucket(expirationName)
|
b := root.Bucket(expirationName)
|
||||||
if b == nil {
|
if b == nil {
|
||||||
// golog.Debugf("tried to reset the expiration value for '%s' while its configured lifetime is unlimited or the session is already expired and not found now", sid)
|
// db.logger.Debugf("tried to reset the expiration value for '%s' while its configured lifetime is unlimited or the session is already expired and not found now", sid)
|
||||||
return sessions.ErrNotFound
|
return sessions.ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +242,7 @@ func (db *Database) OnUpdateExpiration(sid string, newExpires time.Duration) err
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Debugf("unable to reset the expiration value for '%s': %v", sid, err)
|
db.logger.Debugf("unable to reset the expiration value for '%s': %v", sid, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
@ -247,10 +254,10 @@ func makeKey(key string) []byte {
|
||||||
|
|
||||||
// Set sets a key value of a specific session.
|
// Set sets a key value of a specific session.
|
||||||
// Ignore the "immutable".
|
// Ignore the "immutable".
|
||||||
func (db *Database) Set(sid string, lifetime sessions.LifeTime, key string, value interface{}, immutable bool) {
|
func (db *Database) Set(sid string, _ *sessions.LifeTime, key string, value interface{}, immutable bool) {
|
||||||
valueBytes, err := sessions.DefaultTranscoder.Marshal(value)
|
valueBytes, err := sessions.DefaultTranscoder.Marshal(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Debug(err)
|
db.logger.Debug(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,7 +275,7 @@ func (db *Database) Set(sid string, lifetime sessions.LifeTime, key string, valu
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Debug(err)
|
db.logger.Debug(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +295,7 @@ func (db *Database) Get(sid string, key string) (value interface{}) {
|
||||||
return sessions.DefaultTranscoder.Unmarshal(valueBytes, &value)
|
return sessions.DefaultTranscoder.Unmarshal(valueBytes, &value)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Debugf("session '%s' key '%s' cannot be retrieved: %v", sid, key, err)
|
db.logger.Debugf("session '%s' key '%s' cannot be retrieved: %v", sid, key, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -305,7 +312,7 @@ func (db *Database) Visit(sid string, cb func(key string, value interface{})) {
|
||||||
return b.ForEach(func(k []byte, v []byte) error {
|
return b.ForEach(func(k []byte, v []byte) error {
|
||||||
var value interface{}
|
var value interface{}
|
||||||
if err := sessions.DefaultTranscoder.Unmarshal(v, &value); err != nil {
|
if err := sessions.DefaultTranscoder.Unmarshal(v, &value); err != nil {
|
||||||
golog.Debugf("unable to retrieve value of key '%s' of '%s': %v", k, sid, err)
|
db.logger.Debugf("unable to retrieve value of key '%s' of '%s': %v", k, sid, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,7 +322,7 @@ func (db *Database) Visit(sid string, cb func(key string, value interface{})) {
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Debugf("Database.Visit: %s: %v", sid, err)
|
db.logger.Debugf("Database.Visit: %s: %v", sid, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,7 +339,7 @@ func (db *Database) Len(sid string) (n int) {
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Debugf("Database.Len: %s: %v", sid, err)
|
db.logger.Debugf("Database.Len: %s: %v", sid, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -366,7 +373,7 @@ func (db *Database) Clear(sid string) {
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Debugf("Database.Clear: %s: %v", sid, err)
|
db.logger.Debugf("Database.Clear: %s: %v", sid, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,7 +391,7 @@ func (db *Database) Release(sid string) {
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Debugf("Database.Release: %s: %v", sid, err)
|
db.logger.Debugf("Database.Release: %s: %v", sid, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,7 +403,7 @@ func (db *Database) Close() error {
|
||||||
func closeDB(db *Database) error {
|
func closeDB(db *Database) error {
|
||||||
err := db.Service.Close()
|
err := db.Service.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Warnf("closing the BoltDB connection: %v", err)
|
db.logger.Warnf("closing the BoltDB connection: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -77,7 +77,8 @@ func DefaultConfig() Config {
|
||||||
|
|
||||||
// Database the redis back-end session database for the sessions.
|
// Database the redis back-end session database for the sessions.
|
||||||
type Database struct {
|
type Database struct {
|
||||||
c Config
|
c Config
|
||||||
|
logger *golog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ sessions.Database = (*Database)(nil)
|
var _ sessions.Database = (*Database)(nil)
|
||||||
|
@ -131,6 +132,12 @@ func (db *Database) Config() *Config {
|
||||||
return &db.c // 6 Aug 2019 - keep that for no breaking change.
|
return &db.c // 6 Aug 2019 - keep that for no breaking change.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLogger sets the logger once before server ran.
|
||||||
|
// By default the Iris one is injected.
|
||||||
|
func (db *Database) SetLogger(logger *golog.Logger) {
|
||||||
|
db.logger = logger
|
||||||
|
}
|
||||||
|
|
||||||
// Acquire receives a session's lifetime from the database,
|
// Acquire receives a session's lifetime from the database,
|
||||||
// if the return value is LifeTime{} then the session manager sets the life time based on the expiration duration lives in configuration.
|
// if the return value is LifeTime{} then the session manager sets the life time based on the expiration duration lives in configuration.
|
||||||
func (db *Database) Acquire(sid string, expires time.Duration) sessions.LifeTime {
|
func (db *Database) Acquire(sid string, expires time.Duration) sessions.LifeTime {
|
||||||
|
@ -140,7 +147,7 @@ func (db *Database) Acquire(sid string, expires time.Duration) sessions.LifeTime
|
||||||
// fmt.Printf("db.Acquire expires: %s. Seconds: %v\n", expires, expires.Seconds())
|
// fmt.Printf("db.Acquire expires: %s. Seconds: %v\n", expires, expires.Seconds())
|
||||||
// not found, create an entry with ttl and return an empty lifetime, session manager will do its job.
|
// not found, create an entry with ttl and return an empty lifetime, session manager will do its job.
|
||||||
if err := db.c.Driver.Set(key, sid, int64(expires.Seconds())); err != nil {
|
if err := db.c.Driver.Set(key, sid, int64(expires.Seconds())); err != nil {
|
||||||
golog.Debug(err)
|
db.logger.Debug(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sessions.LifeTime{} // session manager will handle the rest.
|
return sessions.LifeTime{} // session manager will handle the rest.
|
||||||
|
@ -168,17 +175,17 @@ func (db *Database) makeKey(sid, key string) string {
|
||||||
|
|
||||||
// Set sets a key value of a specific session.
|
// Set sets a key value of a specific session.
|
||||||
// Ignore the "immutable".
|
// Ignore the "immutable".
|
||||||
func (db *Database) Set(sid string, lifetime sessions.LifeTime, key string, value interface{}, immutable bool) {
|
func (db *Database) Set(sid string, lifetime *sessions.LifeTime, key string, value interface{}, immutable bool) {
|
||||||
valueBytes, err := sessions.DefaultTranscoder.Marshal(value)
|
valueBytes, err := sessions.DefaultTranscoder.Marshal(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Error(err)
|
db.logger.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// fmt.Println("database.Set")
|
// fmt.Println("database.Set")
|
||||||
// fmt.Printf("lifetime.DurationUntilExpiration(): %s. Seconds: %v\n", lifetime.DurationUntilExpiration(), lifetime.DurationUntilExpiration().Seconds())
|
// fmt.Printf("lifetime.DurationUntilExpiration(): %s. Seconds: %v\n", lifetime.DurationUntilExpiration(), lifetime.DurationUntilExpiration().Seconds())
|
||||||
if err = db.c.Driver.Set(db.makeKey(sid, key), valueBytes, int64(lifetime.DurationUntilExpiration().Seconds())); err != nil {
|
if err = db.c.Driver.Set(db.makeKey(sid, key), valueBytes, int64(lifetime.DurationUntilExpiration().Seconds())); err != nil {
|
||||||
golog.Debug(err)
|
db.logger.Debug(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +203,7 @@ func (db *Database) get(key string, outPtr interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = sessions.DefaultTranscoder.Unmarshal(data.([]byte), outPtr); err != nil {
|
if err = sessions.DefaultTranscoder.Unmarshal(data.([]byte), outPtr); err != nil {
|
||||||
golog.Debugf("unable to unmarshal value of key: '%s': %v", key, err)
|
db.logger.Debugf("unable to unmarshal value of key: '%s': %v", key, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +213,7 @@ func (db *Database) get(key string, outPtr interface{}) error {
|
||||||
func (db *Database) keys(sid string) []string {
|
func (db *Database) keys(sid string) []string {
|
||||||
keys, err := db.c.Driver.GetKeys(db.makeKey(sid, ""))
|
keys, err := db.c.Driver.GetKeys(db.makeKey(sid, ""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Debugf("unable to get all redis keys of session '%s': %v", sid, err)
|
db.logger.Debugf("unable to get all redis keys of session '%s': %v", sid, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,7 +240,7 @@ func (db *Database) Len(sid string) (n int) {
|
||||||
func (db *Database) Delete(sid string, key string) (deleted bool) {
|
func (db *Database) Delete(sid string, key string) (deleted bool) {
|
||||||
err := db.c.Driver.Delete(db.makeKey(sid, key))
|
err := db.c.Driver.Delete(db.makeKey(sid, key))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Error(err)
|
db.logger.Error(err)
|
||||||
}
|
}
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
@ -243,7 +250,7 @@ func (db *Database) Clear(sid string) {
|
||||||
keys := db.keys(sid)
|
keys := db.keys(sid)
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
if err := db.c.Driver.Delete(key); err != nil {
|
if err := db.c.Driver.Delete(key); err != nil {
|
||||||
golog.Debugf("unable to delete session '%s' value of key: '%s': %v", sid, key, err)
|
db.logger.Debugf("unable to delete session '%s' value of key: '%s': %v", sid, key, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,7 +263,7 @@ func (db *Database) Release(sid string) {
|
||||||
// and remove the $sid.
|
// and remove the $sid.
|
||||||
err := db.c.Driver.Delete(db.c.Prefix + sid)
|
err := db.c.Driver.Delete(db.c.Prefix + sid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Debugf("Database.Release.Driver.Delete: %s: %v", sid, err)
|
db.logger.Debugf("Database.Release.Driver.Delete: %s: %v", sid, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ func New(cfg Config) *Sessions {
|
||||||
// UseDatabase adds a session database to the manager's provider,
|
// UseDatabase adds a session database to the manager's provider,
|
||||||
// a session db doesn't have write access
|
// a session db doesn't have write access
|
||||||
func (s *Sessions) UseDatabase(db Database) {
|
func (s *Sessions) UseDatabase(db Database) {
|
||||||
|
db.SetLogger(s.config.Logger) // inject the logger.
|
||||||
s.provider.RegisterDatabase(db)
|
s.provider.RegisterDatabase(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user