From 7ed5ed4519f1a5255a0205b091b93235a60d4df8 Mon Sep 17 00:00:00 2001 From: Gerasimos Maropoulos Date: Wed, 20 Jul 2016 06:33:24 +0300 Subject: [PATCH] Iris 4.0.0-alpha.4. Book is finally updated https://kataras.gitbooks.io/iris/content/ also --- HISTORY.md | 22 ++++++++++++++++++++-- README.md | 4 ++-- config/iris.go | 12 +----------- config/server.go | 15 +++++++++------ config/sessions.go | 9 +++++---- config/websocket.go | 3 +-- http.go | 5 ++++- iris.go | 26 ++++++++++++++++---------- sessions.go | 37 ++++++++++++++++++++++++++++--------- 9 files changed, 86 insertions(+), 47 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index ae358b61..49ec6297 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,25 @@ **How to upgrade**: remove your `$GOPATH/src/github.com/kataras/iris` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris/iris`. + +## 4.0.0-alpha.4 -> 4.0.0-alpha.4 + + +** The important** , is that the [book](https://kataras.gitbooks.io/iris/content/) is finally updated! + +If you're **willing to donate** click [here](DONATIONS.md)! + + +- `iris.Config.Gzip`, enables gzip compression on your Render actions, this includes any type of render, templates and pure/raw content. If you don't want to enable it globaly, you could just use the third parameter on context.Render("myfileOrResponse", structBinding{}, iris.RenderOptions{"gzip": true}). It defaults to false + + +**Fix** +- https://github.com/kataras/iris/issues/301 + +**Sessions changes ** + +- `iris.Config.Sessions.Expires` it was time.Time, changed to time.Duration, which defaults to 0, means unlimited session life duration, if you change it then the correct date is setted on client's cookie but also server destroys the session automatically when the duration passed, this is better approach, see [here](https://github.com/kataras/iris/issues/301) + ## 4.0.0-alpha.2 -> 4.0.0-alpha.3 **New** @@ -21,7 +40,6 @@ A **Response Engine** gives you the freedom to create/change the render/response **Fix** - https://github.com/kataras/iris/issues/294 -- https://github.com/kataras/iris/issues/301 - https://github.com/kataras/iris/issues/303 @@ -29,7 +47,7 @@ A **Response Engine** gives you the freedom to create/change the render/response - `iris.Config.Charset`, before alpha.3 was `iris.Config.Rest.Charset` & `iris.Config.Render.Template.Charset`, but you can override it at runtime by passinth a map `iris.RenderOptions` on the `context.Render` call . - `iris.Config.IsDevelopment`, before alpha.1 was `iris.Config.Render.Template.IsDevelopment` -- `iris.Config.Gzip`, enables gzip compression on your Render actions, this includes any type of render, templates and pure/raw content. If you don't want to enable it globaly, you could just use the third parameter on context.Render("myfileOrResponse", structBinding{}, iris.RenderOptions{"gzip": true}). It defaults to false + **Websockets changes** diff --git a/README.md b/README.md index a9cf82e6..ce68b383 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ I recommend writing your API tests using this new library, [httpexpect](https:// Versioning ------------ -Current: **v4.0.0-alpha.3** +Current: **v4.0.0-alpha.4** > Iris is an active project @@ -186,7 +186,7 @@ License can be found [here](LICENSE). [Travis]: http://travis-ci.org/kataras/iris [License Widget]: https://img.shields.io/badge/license-MIT%20%20License%20-E91E63.svg?style=flat-square [License]: https://github.com/kataras/iris/blob/master/LICENSE -[Release Widget]: https://img.shields.io/badge/release-v4.0.0--alpha.3-blue.svg?style=flat-square +[Release Widget]: https://img.shields.io/badge/release-v4.0.0--alpha.4-blue.svg?style=flat-square [Release]: https://github.com/kataras/iris/releases [Chat Widget]: https://img.shields.io/badge/community-chat-00BCD4.svg?style=flat-square [Chat]: https://kataras.rocket.chat/channel/iris diff --git a/config/iris.go b/config/iris.go index f31083d7..45977b44 100644 --- a/config/iris.go +++ b/config/iris.go @@ -13,17 +13,6 @@ const ( type ( // Iris configs for the station - // All fields can be changed before server's listen except the DisablePathCorrection field - // - // MaxRequestBodySize is the only options that can be changed after server listen - - // using Config.MaxRequestBodySize = ... - // Render's rest config can be changed after declaration but before server's listen - - // using Config.Render.Rest... - // Render's Template config can be changed after declaration but before server's listen - - // using Config.Render.Template... - // Sessions config can be changed after declaration but before server's listen - - // using Config.Sessions... - // and so on... Iris struct { // DisablePathCorrection corrects and redirects the requested path to the registed path @@ -106,6 +95,7 @@ type ( Websocket *Websocket // Tester contains the configs for the test framework, so far we have only one because all test framework's configs are setted by the iris itself + // You can find example on the https://github.com/kataras/iris/glob/master/context_test.go Tester Tester } ) diff --git a/config/server.go b/config/server.go index 6c4c450f..1652b990 100644 --- a/config/server.go +++ b/config/server.go @@ -9,8 +9,8 @@ import ( "github.com/valyala/fasthttp" ) -// Default values for base Server conf, can be changed for global use -var ( +// Default values for base Server conf +const ( // DefaultServerHostname returns the default hostname which is 127.0.0.1 DefaultServerHostname = "127.0.0.1" // DefaultServerPort returns the default port which is 8080 @@ -31,6 +31,9 @@ var ( // // Default buffer size is 8MB DefaultWriteBufferSize = 8096 + + // DefaultServerName the response header of the 'Server' value when writes to the client + DefaultServerName = "iris" ) var ( @@ -38,9 +41,6 @@ var ( DefaultServerAddr = DefaultServerHostname + ":" + strconv.Itoa(DefaultServerPort) ) -// ServerName the response header of the 'Server' value when writes to the client -const ServerName = "iris" - // Server used inside server for listening type Server struct { // ListenningAddr the addr that server listens to @@ -79,6 +79,9 @@ type Server struct { RedirectTo string // Virtual If this server is not really listens to a real host, it mostly used in order to achieve testing without system modifications Virtual bool + // Name the server's name, defaults to "iris". + // You're free to change it, but I will trust you to don't, this is the only setting whose somebody, like me, can see if iris web framework is used + Name string } // ServerParseAddr parses the listening addr and returns this @@ -114,7 +117,7 @@ func ServerParseAddr(listeningAddr string) string { // DefaultServer returns the default configs for the server func DefaultServer() Server { - return Server{ListeningAddr: DefaultServerAddr, + return Server{ListeningAddr: DefaultServerAddr, Name: DefaultServerName, MaxRequestBodySize: DefaultMaxRequestBodySize, ReadBufferSize: DefaultReadBufferSize, WriteBufferSize: DefaultWriteBufferSize, diff --git a/config/sessions.go b/config/sessions.go index 6821ab22..fbe3db49 100644 --- a/config/sessions.go +++ b/config/sessions.go @@ -34,11 +34,12 @@ type ( // DecodeCookie set it to true to decode the cookie key with base64 URLEncoding // Defaults to false DecodeCookie bool - //Expires the date which the cookie must expires. Default infinitive/unlimited life - Expires time.Time + // Expires the duration of which the cookie must expires (created_time.Add(Expires)). + // Default infinitive/unlimited life duration(0) + Expires time.Duration // GcDuration every how much duration(GcDuration) the memory should be clear for unused cookies (GcDuration) // for example: time.Duration(2)*time.Hour. it will check every 2 hours if cookie hasn't be used for 2 hours, - // deletes it from memory until the user comes back, then the session continue to work as it was + // deletes it from backend memory until the user comes back, then the session continue to work as it was // // Default 2 hours GcDuration time.Duration @@ -54,7 +55,7 @@ func DefaultSessions() Sessions { return Sessions{ Cookie: DefaultCookieName, DecodeCookie: false, - Expires: CookieExpireNever, + Expires: 0, GcDuration: DefaultSessionGcDuration, DisableSubdomainPersistence: false, } diff --git a/config/websocket.go b/config/websocket.go index 7ca3f039..21fe7afd 100644 --- a/config/websocket.go +++ b/config/websocket.go @@ -6,7 +6,6 @@ import ( "github.com/imdario/mergo" ) -// Currently only these 5 values are used for real const ( // DefaultWriteTimeout 15 * time.Second DefaultWriteTimeout = 15 * time.Second @@ -20,7 +19,7 @@ const ( // -// Websocket the config contains options for 'websocket' package +// Websocket the config contains options for the ../websocket.go type Websocket struct { // WriteTimeout time allowed to write a message to the connection. // Default value is 15 * time.Second diff --git a/http.go b/http.go index c742766e..d4336086 100644 --- a/http.go +++ b/http.go @@ -254,7 +254,10 @@ type ( // newServer returns a pointer to a Server object, and set it's options if any, nothing more func newServer(cfg config.Server) *Server { - s := &Server{Server: &fasthttp.Server{Name: config.ServerName}, Config: cfg} + if cfg.Name == "" { + cfg.Name = config.DefaultServerName + } + s := &Server{Server: &fasthttp.Server{Name: cfg.Name}, Config: cfg} s.prepare() return s } diff --git a/iris.go b/iris.go index 01f8c6df..5714b3b7 100644 --- a/iris.go +++ b/iris.go @@ -84,7 +84,7 @@ import ( const ( // Version of the iris - Version = "4.0.0-alpha.3" + Version = "4.0.0-alpha.4" banner = ` _____ _ |_ _| (_) @@ -219,8 +219,6 @@ func New(cfg ...config.Iris) *Framework { }, engines: make([]*templateEngineWrapper, 0), } - //set the session manager - s.sessions = newSessionsManager(&c.Sessions) // set the websocket server s.Websocket = NewWebsocketServer(s.Config.Websocket) // set the servemux, which will provide us the public API also, with its context pool @@ -236,6 +234,11 @@ func New(cfg ...config.Iris) *Framework { } func (s *Framework) initialize() { + if s.Config.Sessions.Cookie != "" { + //set the session manager + s.sessions = newSessionsManager(s.Config.Sessions) + } + // prepare the response engines, if no response engines setted for the default content-types // then add them @@ -335,9 +338,9 @@ func (s *Framework) Must(err error) { } } -// AddServer same as .Servers.Add(config.Server) instead +// AddServer same as .Servers.Add(config.Server) // -// AddServers starts a server which listens to this station +// AddServer starts a server which listens to this station // Note that the view engine's functions {{ url }} and {{ urlpath }} will return the first's registered server's scheme (http/https) // // this is useful mostly when you want to have two or more listening ports ( two or more servers ) for the same station @@ -352,10 +355,10 @@ func AddServer(cfg config.Server) *Server { return Default.AddServer(cfg) } -// AddServer same as .Servers.Add(config.Server) instead +// AddServer same as .Servers.Add(config.Server) // -// AddServers starts a server which listens to this station -// Note that the view engine's functions {{ url }} and {{ urlpath }} will return the first's registered server's scheme (http/https) +// AddServer starts a server which listens to this station +// Note that the view engine's functions {{ url }} and {{ urlpath }} will return the last registered server's scheme (http/https) // // this is useful mostly when you want to have two or more listening ports ( two or more servers ) for the same station // @@ -369,7 +372,7 @@ func (s *Framework) AddServer(cfg config.Server) *Server { return s.Servers.Add(cfg) } -// ListenTo listens to a server but receives the full server's configuration +// ListenTo listens to a server but accepts the full server's configuration // returns an error, you're responsible to handle that // or use the iris.Must(iris.ListenTo(config.Server{})) // @@ -378,7 +381,10 @@ func ListenTo(cfg config.Server) error { return Default.ListenTo(cfg) } -// ListenTo listens to a server but receives the full server's configuration +// ListenTo listens to a server but acceots the full server's configuration +// returns an error, you're responsible to handle that +// or use the iris.Must(iris.ListenTo(config.Server{})) +// // it's a blocking func func (s *Framework) ListenTo(cfg config.Server) (err error) { if cfg.ReadBufferSize == 0 { diff --git a/sessions.go b/sessions.go index 52c2fb3e..8304babd 100644 --- a/sessions.go +++ b/sessions.go @@ -42,6 +42,7 @@ type session struct { values map[string]interface{} // here is the real values mu sync.Mutex lastAccessedTime time.Time + createdAt time.Time provider *sessionProvider } @@ -135,6 +136,7 @@ type ( sessions map[string]*list.Element // underline TEMPORARY memory store used to give advantage on sessions used more times than others list *list.List // for GC databases []SessionDatabase + expires time.Duration } ) @@ -145,12 +147,25 @@ func (p *sessionProvider) registerDatabase(db SessionDatabase) { } func (p *sessionProvider) newSession(sid string) *session { - return &session{ + + sess := &session{ sid: sid, provider: p, lastAccessedTime: time.Now(), values: p.loadSessionValues(sid), } + if p.expires > 0 { // if not unlimited life duration + time.AfterFunc(p.expires, func() { + // the destroy makes the check if this session is exists then or not, + // this is used to destroy the session from the server-side also + // it's good to have here for security reasons, I didn't add it on the gc function to separate its action + p.destroy(sid) + + }) + } + + return sess + } func (p *sessionProvider) loadSessionValues(sid string) map[string]interface{} { @@ -203,7 +218,6 @@ func (p *sessionProvider) destroy(sid string) { p.updateDatabases(sid, nil) delete(p.sessions, sid) p.list.Remove(elem) - } p.mu.Unlock() } @@ -234,10 +248,9 @@ func (p *sessionProvider) gc(duration time.Duration) { // if the time has passed. session was expired, then delete the session and its memory place // we are not destroy the session completely for the case this is re-used after - - if time.Now().After(elem.Value.(*session).lastAccessedTime.Add(duration)) { + sess := elem.Value.(*session) + if time.Now().After(sess.lastAccessedTime.Add(duration)) { p.list.Remove(elem) - delete(p.sessions, elem.Value.(*session).sid) } else { break } @@ -254,19 +267,19 @@ type ( // sessionsManager implements the ISessionsManager interface // contains the cookie's name, the provider and a duration for GC and cookie life expire sessionsManager struct { - config *config.Sessions + config config.Sessions provider *sessionProvider } ) // newSessionsManager creates & returns a new SessionsManager and start its GC -func newSessionsManager(c *config.Sessions) *sessionsManager { +func newSessionsManager(c config.Sessions) *sessionsManager { if c.DecodeCookie { c.Cookie = base64.URLEncoding.EncodeToString([]byte(c.Cookie)) // change the cookie's name/key to a more safe(?) // get the real value for your tests by: //sessIdKey := url.QueryEscape(base64.URLEncoding.EncodeToString([]byte(iris.Config.Sessions.Cookie))) } - manager := &sessionsManager{config: c, provider: &sessionProvider{list: list.New(), sessions: make(map[string]*list.Element, 0), databases: make([]SessionDatabase, 0)}} + manager := &sessionsManager{config: c, provider: &sessionProvider{list: list.New(), sessions: make(map[string]*list.Element, 0), databases: make([]SessionDatabase, 0), expires: c.Expires}} //run the GC here go manager.gc() return manager @@ -327,7 +340,13 @@ func (m *sessionsManager) start(ctx *Context) *session { } cookie.SetHTTPOnly(true) - cookie.SetExpire(m.config.Expires) + if m.config.Expires == 0 { + // unlimited life + cookie.SetExpire(config.CookieExpireNever) + } else { + cookie.SetExpire(time.Now().Add(m.config.Expires)) + } + ctx.SetCookie(cookie) fasthttp.ReleaseCookie(cookie) } else {