Iris 4.0.0-alpha.4. Book is finally updated https://kataras.gitbooks.io/iris/content/ also

This commit is contained in:
Gerasimos Maropoulos 2016-07-20 06:33:24 +03:00
parent 566a194836
commit 7ed5ed4519
9 changed files with 86 additions and 47 deletions

View File

@ -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`. **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 ## 4.0.0-alpha.2 -> 4.0.0-alpha.3
**New** **New**
@ -21,7 +40,6 @@ A **Response Engine** gives you the freedom to create/change the render/response
**Fix** **Fix**
- https://github.com/kataras/iris/issues/294 - https://github.com/kataras/iris/issues/294
- https://github.com/kataras/iris/issues/301
- https://github.com/kataras/iris/issues/303 - 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.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.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** **Websockets changes**

View File

@ -141,7 +141,7 @@ I recommend writing your API tests using this new library, [httpexpect](https://
Versioning Versioning
------------ ------------
Current: **v4.0.0-alpha.3** Current: **v4.0.0-alpha.4**
> Iris is an active project > Iris is an active project
@ -186,7 +186,7 @@ License can be found [here](LICENSE).
[Travis]: http://travis-ci.org/kataras/iris [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 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 [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 [Release]: https://github.com/kataras/iris/releases
[Chat Widget]: https://img.shields.io/badge/community-chat-00BCD4.svg?style=flat-square [Chat Widget]: https://img.shields.io/badge/community-chat-00BCD4.svg?style=flat-square
[Chat]: https://kataras.rocket.chat/channel/iris [Chat]: https://kataras.rocket.chat/channel/iris

View File

@ -13,17 +13,6 @@ const (
type ( type (
// Iris configs for the station // 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 { Iris struct {
// DisablePathCorrection corrects and redirects the requested path to the registed path // DisablePathCorrection corrects and redirects the requested path to the registed path
@ -106,6 +95,7 @@ type (
Websocket *Websocket 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 // 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 Tester Tester
} }
) )

View File

@ -9,8 +9,8 @@ import (
"github.com/valyala/fasthttp" "github.com/valyala/fasthttp"
) )
// Default values for base Server conf, can be changed for global use // Default values for base Server conf
var ( const (
// DefaultServerHostname returns the default hostname which is 127.0.0.1 // DefaultServerHostname returns the default hostname which is 127.0.0.1
DefaultServerHostname = "127.0.0.1" DefaultServerHostname = "127.0.0.1"
// DefaultServerPort returns the default port which is 8080 // DefaultServerPort returns the default port which is 8080
@ -31,6 +31,9 @@ var (
// //
// Default buffer size is 8MB // Default buffer size is 8MB
DefaultWriteBufferSize = 8096 DefaultWriteBufferSize = 8096
// DefaultServerName the response header of the 'Server' value when writes to the client
DefaultServerName = "iris"
) )
var ( var (
@ -38,9 +41,6 @@ var (
DefaultServerAddr = DefaultServerHostname + ":" + strconv.Itoa(DefaultServerPort) 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 // Server used inside server for listening
type Server struct { type Server struct {
// ListenningAddr the addr that server listens to // ListenningAddr the addr that server listens to
@ -79,6 +79,9 @@ type Server struct {
RedirectTo string 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 If this server is not really listens to a real host, it mostly used in order to achieve testing without system modifications
Virtual bool 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 // 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 // DefaultServer returns the default configs for the server
func DefaultServer() Server { func DefaultServer() Server {
return Server{ListeningAddr: DefaultServerAddr, return Server{ListeningAddr: DefaultServerAddr, Name: DefaultServerName,
MaxRequestBodySize: DefaultMaxRequestBodySize, MaxRequestBodySize: DefaultMaxRequestBodySize,
ReadBufferSize: DefaultReadBufferSize, ReadBufferSize: DefaultReadBufferSize,
WriteBufferSize: DefaultWriteBufferSize, WriteBufferSize: DefaultWriteBufferSize,

View File

@ -34,11 +34,12 @@ type (
// DecodeCookie set it to true to decode the cookie key with base64 URLEncoding // DecodeCookie set it to true to decode the cookie key with base64 URLEncoding
// Defaults to false // Defaults to false
DecodeCookie bool DecodeCookie bool
//Expires the date which the cookie must expires. Default infinitive/unlimited life // Expires the duration of which the cookie must expires (created_time.Add(Expires)).
Expires time.Time // Default infinitive/unlimited life duration(0)
Expires time.Duration
// GcDuration every how much duration(GcDuration) the memory should be clear for unused cookies (GcDuration) // 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, // 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 // Default 2 hours
GcDuration time.Duration GcDuration time.Duration
@ -54,7 +55,7 @@ func DefaultSessions() Sessions {
return Sessions{ return Sessions{
Cookie: DefaultCookieName, Cookie: DefaultCookieName,
DecodeCookie: false, DecodeCookie: false,
Expires: CookieExpireNever, Expires: 0,
GcDuration: DefaultSessionGcDuration, GcDuration: DefaultSessionGcDuration,
DisableSubdomainPersistence: false, DisableSubdomainPersistence: false,
} }

View File

@ -6,7 +6,6 @@ import (
"github.com/imdario/mergo" "github.com/imdario/mergo"
) )
// Currently only these 5 values are used for real
const ( const (
// DefaultWriteTimeout 15 * time.Second // DefaultWriteTimeout 15 * time.Second
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 { type Websocket struct {
// WriteTimeout time allowed to write a message to the connection. // WriteTimeout time allowed to write a message to the connection.
// Default value is 15 * time.Second // Default value is 15 * time.Second

View File

@ -254,7 +254,10 @@ type (
// newServer returns a pointer to a Server object, and set it's options if any, nothing more // newServer returns a pointer to a Server object, and set it's options if any, nothing more
func newServer(cfg config.Server) *Server { 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() s.prepare()
return s return s
} }

26
iris.go
View File

@ -84,7 +84,7 @@ import (
const ( const (
// Version of the iris // Version of the iris
Version = "4.0.0-alpha.3" Version = "4.0.0-alpha.4"
banner = ` _____ _ banner = ` _____ _
|_ _| (_) |_ _| (_)
@ -219,8 +219,6 @@ func New(cfg ...config.Iris) *Framework {
}, },
engines: make([]*templateEngineWrapper, 0), engines: make([]*templateEngineWrapper, 0),
} }
//set the session manager
s.sessions = newSessionsManager(&c.Sessions)
// set the websocket server // set the websocket server
s.Websocket = NewWebsocketServer(s.Config.Websocket) s.Websocket = NewWebsocketServer(s.Config.Websocket)
// set the servemux, which will provide us the public API also, with its context pool // 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() { 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 // prepare the response engines, if no response engines setted for the default content-types
// then add them // 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) // 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 // 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) 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 // 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) // 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 // 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) 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 // returns an error, you're responsible to handle that
// or use the iris.Must(iris.ListenTo(config.Server{})) // or use the iris.Must(iris.ListenTo(config.Server{}))
// //
@ -378,7 +381,10 @@ func ListenTo(cfg config.Server) error {
return Default.ListenTo(cfg) 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 // it's a blocking func
func (s *Framework) ListenTo(cfg config.Server) (err error) { func (s *Framework) ListenTo(cfg config.Server) (err error) {
if cfg.ReadBufferSize == 0 { if cfg.ReadBufferSize == 0 {

View File

@ -42,6 +42,7 @@ type session struct {
values map[string]interface{} // here is the real values values map[string]interface{} // here is the real values
mu sync.Mutex mu sync.Mutex
lastAccessedTime time.Time lastAccessedTime time.Time
createdAt time.Time
provider *sessionProvider 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 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 list *list.List // for GC
databases []SessionDatabase databases []SessionDatabase
expires time.Duration
} }
) )
@ -145,12 +147,25 @@ func (p *sessionProvider) registerDatabase(db SessionDatabase) {
} }
func (p *sessionProvider) newSession(sid string) *session { func (p *sessionProvider) newSession(sid string) *session {
return &session{
sess := &session{
sid: sid, sid: sid,
provider: p, provider: p,
lastAccessedTime: time.Now(), lastAccessedTime: time.Now(),
values: p.loadSessionValues(sid), 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{} { func (p *sessionProvider) loadSessionValues(sid string) map[string]interface{} {
@ -203,7 +218,6 @@ func (p *sessionProvider) destroy(sid string) {
p.updateDatabases(sid, nil) p.updateDatabases(sid, nil)
delete(p.sessions, sid) delete(p.sessions, sid)
p.list.Remove(elem) p.list.Remove(elem)
} }
p.mu.Unlock() 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 // 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 // we are not destroy the session completely for the case this is re-used after
sess := elem.Value.(*session)
if time.Now().After(elem.Value.(*session).lastAccessedTime.Add(duration)) { if time.Now().After(sess.lastAccessedTime.Add(duration)) {
p.list.Remove(elem) p.list.Remove(elem)
delete(p.sessions, elem.Value.(*session).sid)
} else { } else {
break break
} }
@ -254,19 +267,19 @@ type (
// sessionsManager implements the ISessionsManager interface // sessionsManager implements the ISessionsManager interface
// contains the cookie's name, the provider and a duration for GC and cookie life expire // contains the cookie's name, the provider and a duration for GC and cookie life expire
sessionsManager struct { sessionsManager struct {
config *config.Sessions config config.Sessions
provider *sessionProvider provider *sessionProvider
} }
) )
// newSessionsManager creates & returns a new SessionsManager and start its GC // 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 { if c.DecodeCookie {
c.Cookie = base64.URLEncoding.EncodeToString([]byte(c.Cookie)) // change the cookie's name/key to a more safe(?) 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: // get the real value for your tests by:
//sessIdKey := url.QueryEscape(base64.URLEncoding.EncodeToString([]byte(iris.Config.Sessions.Cookie))) //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 //run the GC here
go manager.gc() go manager.gc()
return manager return manager
@ -327,7 +340,13 @@ func (m *sessionsManager) start(ctx *Context) *session {
} }
cookie.SetHTTPOnly(true) 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) ctx.SetCookie(cookie)
fasthttp.ReleaseCookie(cookie) fasthttp.ReleaseCookie(cookie)
} else { } else {