mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 02:31:04 +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)
|
||||
* [Wildcard](routing/subdomains/wildcard/main.go)
|
||||
* [WWW](routing/subdomains/www/main.go)
|
||||
* [WWW Method](routing/subdomains/www/www-method/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 Method Override](https://github.com/kataras/iris/blob/master/middleware/methodoverride/methodoverride_test.go)
|
||||
* [API Versioning](routing/versioning/main.go)
|
||||
|
|
|
@ -14,7 +14,7 @@ func newApp() *iris.Application {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
app.I18n.Subdomain
|
||||
// app.I18n.Subdomain = false to disable resolve lang code from subdomain.
|
||||
// app.I18n.LoadAssets for go-bindata.
|
||||
|
||||
// Default values:
|
||||
|
|
|
@ -17,9 +17,10 @@ func newApp() *iris.Application {
|
|||
|
||||
// Optionally, to minify the HTML5 error response.
|
||||
// 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.
|
||||
test.OnErrorCode(iris.StatusNotFound, handleNotFoundTestSubdomain)
|
||||
test.OnErrorCode(iris.StatusNotFound, iris.Minify, handleNotFoundTestSubdomain)
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/v12"
|
||||
)
|
||||
import "github.com/kataras/iris/v12"
|
||||
|
||||
func newApp() *iris.Application {
|
||||
app := iris.New()
|
||||
|
@ -44,9 +42,9 @@ func newApp() *iris.Application {
|
|||
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(;)).
|
||||
|
||||
// And ./www-method example.
|
||||
return app
|
||||
}
|
||||
|
||||
|
@ -64,9 +62,7 @@ func main() {
|
|||
// http://www.mydomain.com/contact
|
||||
// http://www.mydomain.com/api/users
|
||||
// http://www.mydomain.com/api/users/42
|
||||
if err := app.Listen("mydomain.com:80"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
app.Listen("mydomain.com:80")
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
// 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
|
||||
// that can be passed to the DirOptions.Cache.CompressIgnore field
|
||||
// in order to skip compression on already-compressed file types
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
stdContext "context"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/golog"
|
||||
"github.com/tdewolff/minify/v2"
|
||||
|
@ -84,3 +85,51 @@ type Application interface {
|
|||
// Order may change.
|
||||
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/macro"
|
||||
macroHandler "github.com/kataras/iris/v12/macro/handler"
|
||||
|
||||
"github.com/kataras/golog"
|
||||
)
|
||||
|
||||
// MethodNone is a Virtual method
|
||||
|
@ -153,6 +155,8 @@ func overlapRoute(r *Route, next *Route) {
|
|||
// APIBuilder the visible API for constructing the router
|
||||
// and child routers.
|
||||
type APIBuilder struct {
|
||||
// the application logger.
|
||||
logger *golog.Logger
|
||||
// parent is the creator of this Party.
|
||||
// It is nil on Root.
|
||||
parent *APIBuilder // currently it's used only on UseRouter feature.
|
||||
|
@ -227,8 +231,9 @@ var (
|
|||
|
||||
// NewAPIBuilder creates & returns a new builder
|
||||
// which is responsible to build the API and the router handler.
|
||||
func NewAPIBuilder() *APIBuilder {
|
||||
func NewAPIBuilder(logger *golog.Logger) *APIBuilder {
|
||||
return &APIBuilder{
|
||||
logger: logger,
|
||||
parent: nil,
|
||||
macros: macro.Defaults,
|
||||
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.
|
||||
// It will return false on all children Parties, no exception.
|
||||
func (api *APIBuilder) IsRoot() bool {
|
||||
|
|
|
@ -8,6 +8,8 @@ import (
|
|||
"time"
|
||||
|
||||
"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
|
||||
paths := genPaths(routesLength, 15, 42)
|
||||
|
||||
api := NewAPIBuilder()
|
||||
api := NewAPIBuilder(golog.Default)
|
||||
requestHandler := NewDefaultHandler(nil, nil)
|
||||
|
||||
b.ReportAllocs()
|
||||
|
|
|
@ -246,7 +246,7 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
|
|||
// logger.Debugf("API: %d registered %s (", len(registeredRoutes), tr)
|
||||
// with:
|
||||
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
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/v12/core/errgroup"
|
||||
"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.
|
||||
|
@ -13,6 +15,9 @@ import (
|
|||
//
|
||||
// Look the `APIBuilder` structure for its implementation.
|
||||
type Party interface {
|
||||
// Logger returns the Application Logger.
|
||||
Logger() *golog.Logger
|
||||
|
||||
// IsRoot reports whether this Party is the root Application's one.
|
||||
// It will return false on all children Parties, no exception.
|
||||
IsRoot() bool
|
||||
|
|
2
go.mod
2
go.mod
|
@ -20,7 +20,7 @@ require (
|
|||
github.com/iris-contrib/schema v0.0.2
|
||||
github.com/json-iterator/go v1.1.10
|
||||
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/pio v0.0.10
|
||||
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.
|
||||
func New() *Application {
|
||||
config := DefaultConfiguration()
|
||||
|
||||
app := &Application{
|
||||
config: &config,
|
||||
logger: golog.Default,
|
||||
minifier: newMinifier(),
|
||||
I18n: i18n.New(),
|
||||
APIBuilder: router.NewAPIBuilder(),
|
||||
Router: router.NewRouter(),
|
||||
config: &config,
|
||||
Router: router.NewRouter(),
|
||||
I18n: i18n.New(),
|
||||
minifier: newMinifier(),
|
||||
}
|
||||
|
||||
logger := newLogger(app)
|
||||
app.logger = logger
|
||||
app.APIBuilder = router.NewAPIBuilder(logger)
|
||||
app.ContextPool = context.New(func() interface{} {
|
||||
return context.NewContext(app)
|
||||
})
|
||||
|
||||
context.RegisterApplication(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
|
||||
func (app *Application) SubdomainRedirect(from, to router.Party) router.Party {
|
||||
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)
|
||||
return to
|
||||
}
|
||||
|
@ -186,6 +189,22 @@ func (app *Application) ConfigurationReadOnly() context.ConfigurationReadOnly {
|
|||
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".
|
||||
//
|
||||
// Available levels:
|
||||
|
@ -200,7 +219,7 @@ func (app *Application) ConfigurationReadOnly() context.ConfigurationReadOnly {
|
|||
// Defaults to "info" level.
|
||||
//
|
||||
// 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.
|
||||
// Usage:
|
||||
// app.Logger().Error/Errorf("...")
|
||||
|
@ -273,6 +292,25 @@ func newMinifier() *minify.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.
|
||||
// By default it can minifies:
|
||||
// - text/html
|
||||
|
|
|
@ -247,8 +247,8 @@ var _ websocket.ConnHandler = (*Application)(nil)
|
|||
// It returns a collection of namespace and events that
|
||||
// were registered through `HandleWebsocket` controllers.
|
||||
func (app *Application) GetNamespaces() websocket.Namespaces {
|
||||
if golog.Default.Level == golog.DebugLevel {
|
||||
websocket.EnableDebug(golog.Default)
|
||||
if logger := app.Router.Logger(); logger.Level == golog.DebugLevel {
|
||||
websocket.EnableDebug(logger)
|
||||
}
|
||||
|
||||
return websocket.JoinConnHandlers(app.websocketControllers...).GetNamespaces()
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/kataras/iris/v12/context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/kataras/golog"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -16,6 +17,9 @@ const (
|
|||
type (
|
||||
// Config is the configuration for sessions. Please read it before using sessions.
|
||||
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"
|
||||
//
|
||||
// Defaults to "irissessionid".
|
||||
|
@ -65,6 +69,10 @@ type (
|
|||
|
||||
// Validate corrects missing fields configuration fields and returns the right configuration
|
||||
func (c Config) Validate() Config {
|
||||
if c.Logger == nil {
|
||||
c.Logger = context.DefaultLogger("sessions")
|
||||
}
|
||||
|
||||
if c.Cookie == "" {
|
||||
c.Cookie = DefaultCookieName
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/kataras/iris/v12/core/memstore"
|
||||
|
||||
"github.com/kataras/golog"
|
||||
)
|
||||
|
||||
// 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.
|
||||
type Database interface {
|
||||
// SetLogger should inject a logger to this Database.
|
||||
SetLogger(*golog.Logger)
|
||||
// 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.
|
||||
Acquire(sid string, expires time.Duration) LifeTime
|
||||
|
@ -36,7 +40,7 @@ type Database interface {
|
|||
OnUpdateExpiration(sid string, newExpires time.Duration) error
|
||||
// Set sets a key value of a specific session.
|
||||
// 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(sid string, key string) interface{}
|
||||
// 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 (s *mem) SetLogger(*golog.Logger) {}
|
||||
|
||||
func (s *mem) Acquire(sid string, expires time.Duration) LifeTime {
|
||||
s.mu.Lock()
|
||||
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 }
|
||||
|
||||
// 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.values[sid].Save(key, value, immutable)
|
||||
s.mu.RUnlock()
|
||||
|
|
|
@ -70,7 +70,7 @@ func (p *provider) newSession(man *Sessions, sid string, expires time.Duration)
|
|||
lifetime.Begin(expires, onExpire)
|
||||
}
|
||||
|
||||
sess.Lifetime = lifetime
|
||||
sess.Lifetime = &lifetime
|
||||
return sess
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ type (
|
|||
mu sync.RWMutex // for flashes.
|
||||
// Lifetime it contains the expiration data, use it for read-only information.
|
||||
// See `Sessions.UpdateExpiration` too.
|
||||
Lifetime LifeTime
|
||||
Lifetime *LifeTime
|
||||
// Man is the sessions manager that this session created of.
|
||||
Man *Sessions
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/v12/sessions"
|
||||
|
||||
"github.com/dgraph-io/badger/v2"
|
||||
|
@ -26,6 +27,7 @@ type Database struct {
|
|||
// it's initialized at `New` or `NewFromDB`.
|
||||
// Can be used to get stats.
|
||||
Service *badger.DB
|
||||
logger *golog.Logger
|
||||
|
||||
closed uint32 // if 1 is closed.
|
||||
}
|
||||
|
@ -53,11 +55,12 @@ func New(directoryPath string) (*Database, error) {
|
|||
}
|
||||
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -72,6 +75,12 @@ func NewFromDB(service *badger.DB) *Database {
|
|||
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,
|
||||
// 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 {
|
||||
|
@ -94,7 +103,7 @@ func (db *Database) Acquire(sid string, expires time.Duration) sessions.LifeTime
|
|||
}
|
||||
|
||||
if err != nil {
|
||||
golog.Error(err)
|
||||
db.logger.Error(err)
|
||||
}
|
||||
|
||||
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.
|
||||
// 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)
|
||||
if err != nil {
|
||||
golog.Error(err)
|
||||
db.logger.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -131,7 +140,7 @@ func (db *Database) Set(sid string, lifetime sessions.LifeTime, key string, valu
|
|||
})
|
||||
|
||||
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 {
|
||||
golog.Error(err)
|
||||
db.logger.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -189,7 +198,7 @@ func (db *Database) Visit(sid string, cb func(key string, value interface{})) {
|
|||
return sessions.DefaultTranscoder.Unmarshal(valueBytes, &value)
|
||||
})
|
||||
if err != nil {
|
||||
golog.Errorf("[sessionsdb.badger.Visit] %v", err)
|
||||
db.logger.Errorf("[sessionsdb.badger.Visit] %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -231,7 +240,7 @@ func (db *Database) Delete(sid string, key string) (deleted bool) {
|
|||
txn := db.Service.NewTransaction(true)
|
||||
err := txn.Delete(makeKey(sid, key))
|
||||
if err != nil {
|
||||
golog.Error(err)
|
||||
db.logger.Error(err)
|
||||
return false
|
||||
}
|
||||
return txn.Commit() == nil
|
||||
|
@ -250,7 +259,7 @@ func (db *Database) Clear(sid string) {
|
|||
for iter.Rewind(); iter.ValidForPrefix(prefix); iter.Next() {
|
||||
key := iter.Item().Key()
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -264,10 +273,10 @@ func (db *Database) Release(sid string) {
|
|||
// and remove the $sid.
|
||||
txn := db.Service.NewTransaction(true)
|
||||
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 {
|
||||
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()
|
||||
if err != nil {
|
||||
golog.Warnf("closing the badger connection: %v", err)
|
||||
db.logger.Warnf("closing the badger connection: %v", err)
|
||||
} else {
|
||||
atomic.StoreUint32(&db.closed, 1)
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ type Database struct {
|
|||
// it's initialized at `New` or `NewFromDB`.
|
||||
// Can be used to get stats.
|
||||
Service *bolt.DB
|
||||
logger *golog.Logger
|
||||
}
|
||||
|
||||
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 {
|
||||
// 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.
|
||||
golog.Debugf("unreachable session access for '%s'", sid)
|
||||
db.logger.Debugf("unreachable session access for '%s'", sid)
|
||||
}
|
||||
|
||||
return b
|
||||
|
@ -122,20 +123,20 @@ func (db *Database) cleanup() error {
|
|||
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.
|
||||
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
|
||||
}
|
||||
|
||||
var expirationTime time.Time
|
||||
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
|
||||
}
|
||||
|
||||
if expirationTime.Before(time.Now()) {
|
||||
// expired, delete the expiration bucket.
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
||||
// 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.
|
||||
b, err = root.CreateBucket(name)
|
||||
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
|
||||
}
|
||||
|
||||
expirationTime := time.Now().Add(expires)
|
||||
timeBytes, err := sessions.DefaultTranscoder.Marshal(expirationTime)
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -194,7 +201,7 @@ func (db *Database) Acquire(sid string, expires time.Duration) (lifetime session
|
|||
|
||||
var expirationTime time.Time
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -207,7 +214,7 @@ func (db *Database) Acquire(sid string, expires time.Duration) (lifetime session
|
|||
return
|
||||
})
|
||||
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{}
|
||||
}
|
||||
|
||||
|
@ -227,7 +234,7 @@ func (db *Database) OnUpdateExpiration(sid string, newExpires time.Duration) err
|
|||
root := db.getBucket(tx)
|
||||
b := root.Bucket(expirationName)
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -235,7 +242,7 @@ func (db *Database) OnUpdateExpiration(sid string, newExpires time.Duration) err
|
|||
})
|
||||
|
||||
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
|
||||
|
@ -247,10 +254,10 @@ func makeKey(key string) []byte {
|
|||
|
||||
// Set sets a key value of a specific session.
|
||||
// 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)
|
||||
if err != nil {
|
||||
golog.Debug(err)
|
||||
db.logger.Debug(err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -268,7 +275,7 @@ func (db *Database) Set(sid string, lifetime sessions.LifeTime, key string, valu
|
|||
})
|
||||
|
||||
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)
|
||||
})
|
||||
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
|
||||
|
@ -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 {
|
||||
var value interface{}
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -315,7 +322,7 @@ func (db *Database) Visit(sid string, cb func(key string, value interface{})) {
|
|||
})
|
||||
|
||||
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 {
|
||||
golog.Debugf("Database.Len: %s: %v", sid, err)
|
||||
db.logger.Debugf("Database.Len: %s: %v", sid, err)
|
||||
}
|
||||
|
||||
return
|
||||
|
@ -366,7 +373,7 @@ func (db *Database) Clear(sid string) {
|
|||
})
|
||||
|
||||
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 {
|
||||
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 {
|
||||
err := db.Service.Close()
|
||||
if err != nil {
|
||||
golog.Warnf("closing the BoltDB connection: %v", err)
|
||||
db.logger.Warnf("closing the BoltDB connection: %v", err)
|
||||
}
|
||||
|
||||
return err
|
||||
|
|
|
@ -77,7 +77,8 @@ func DefaultConfig() Config {
|
|||
|
||||
// Database the redis back-end session database for the sessions.
|
||||
type Database struct {
|
||||
c Config
|
||||
c Config
|
||||
logger *golog.Logger
|
||||
}
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
// 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,
|
||||
// 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 {
|
||||
|
@ -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())
|
||||
// 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 {
|
||||
golog.Debug(err)
|
||||
db.logger.Debug(err)
|
||||
}
|
||||
|
||||
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.
|
||||
// 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)
|
||||
if err != nil {
|
||||
golog.Error(err)
|
||||
db.logger.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
// fmt.Println("database.Set")
|
||||
// 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 {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -206,7 +213,7 @@ func (db *Database) get(key string, outPtr interface{}) error {
|
|||
func (db *Database) keys(sid string) []string {
|
||||
keys, err := db.c.Driver.GetKeys(db.makeKey(sid, ""))
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -233,7 +240,7 @@ func (db *Database) Len(sid string) (n int) {
|
|||
func (db *Database) Delete(sid string, key string) (deleted bool) {
|
||||
err := db.c.Driver.Delete(db.makeKey(sid, key))
|
||||
if err != nil {
|
||||
golog.Error(err)
|
||||
db.logger.Error(err)
|
||||
}
|
||||
return err == nil
|
||||
}
|
||||
|
@ -243,7 +250,7 @@ func (db *Database) Clear(sid string) {
|
|||
keys := db.keys(sid)
|
||||
for _, key := range keys {
|
||||
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.
|
||||
err := db.c.Driver.Delete(db.c.Prefix + sid)
|
||||
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,
|
||||
// a session db doesn't have write access
|
||||
func (s *Sessions) UseDatabase(db Database) {
|
||||
db.SetLogger(s.config.Logger) // inject the logger.
|
||||
s.provider.RegisterDatabase(db)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user