Ability to change the whole default router

This commit is contained in:
Gerasimos Maropoulos 2016-09-18 06:55:44 +03:00
parent 000dfbbee2
commit 131eddb701
5 changed files with 99 additions and 60 deletions

View File

@ -2,6 +2,10 @@
**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`. **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`.
## 4.2.6 -> 4.2.7
- **ADDED**: You are now able to use a raw fasthttp handler as the router instead of the default Iris' one. Example [here](https://github.com/iris-contrib/examples/blob/master/custom_fasthttp_router/main.go). But remember that I'm always recommending to use the Iris' default which supports subdomains, group of routes(parties), auto path correction and many other built'n features. This exists for specific users who told me that they need a feature like that inside Iris, we have no performance cost at all so that's ok to exists.
## 4.2.5 -> 4.2.6 ## 4.2.5 -> 4.2.6
- **CHANGE**: Updater (See 4.2.4 and 4.2.3) runs in its own goroutine now, unless the `iris.Config.CheckForUpdatesSync` is true. - **CHANGE**: Updater (See 4.2.4 and 4.2.3) runs in its own goroutine now, unless the `iris.Config.CheckForUpdatesSync` is true.

View File

@ -19,7 +19,7 @@
<br/> <br/>
<a href="https://github.com/kataras/iris/releases"><img src="https://img.shields.io/badge/%20version%20-%204.2.6%20-blue.svg?style=flat-square" alt="Releases"></a> <a href="https://github.com/kataras/iris/releases"><img src="https://img.shields.io/badge/%20version%20-%204.2.7%20-blue.svg?style=flat-square" alt="Releases"></a>
<a href="https://github.com/iris-contrib/examples"><img src="https://img.shields.io/badge/%20examples-repository-3362c2.svg?style=flat-square" alt="Examples"></a> <a href="https://github.com/iris-contrib/examples"><img src="https://img.shields.io/badge/%20examples-repository-3362c2.svg?style=flat-square" alt="Examples"></a>
@ -181,7 +181,7 @@ I recommend writing your API tests using this new library, [httpexpect](https://
Versioning Versioning
------------ ------------
Current: **v4.2.6** Current: **v4.2.7**
> Iris is an active project > Iris is an active project
@ -224,7 +224,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-Apache%202.0%20%20-E91E63.svg?style=flat-square [License Widget]: https://img.shields.io/badge/license-Apache%202.0%20%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.2.6-blue.svg?style=flat-square [Release Widget]: https://img.shields.io/badge/release-v4.2.7-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

@ -688,6 +688,7 @@ type ServerConfiguration struct {
// for an optional second server with a different port you can always use: // for an optional second server with a different port you can always use:
// iris.AddServer(iris.ServerConfiguration{ListeningAddr: ":9090", MaxRequestsPerConn:100}) // iris.AddServer(iris.ServerConfiguration{ListeningAddr: ":9090", MaxRequestsPerConn:100})
MaxRequestsPerConn int MaxRequestsPerConn int
// RedirectTo, defaults to empty, set it in order to override the station's handler and redirect all requests to this address which is of form(HOST:PORT or :PORT) // RedirectTo, defaults to empty, set it in order to override the station's handler and redirect all requests to this address which is of form(HOST:PORT or :PORT)
// //
// NOTE: the http status is 'StatusMovedPermanently', means one-time-redirect(the browser remembers the new addr and goes to the new address without need to request something from this server // NOTE: the http status is 'StatusMovedPermanently', means one-time-redirect(the browser remembers the new addr and goes to the new address without need to request something from this server

96
http.go
View File

@ -48,24 +48,25 @@ var (
AllMethods = [...]string{MethodGet, MethodPost, MethodPut, MethodDelete, MethodConnect, MethodHead, MethodPatch, MethodOptions, MethodTrace} AllMethods = [...]string{MethodGet, MethodPost, MethodPut, MethodDelete, MethodConnect, MethodHead, MethodPatch, MethodOptions, MethodTrace}
/* methods as []byte, these are really used by iris */ /* methods as []byte, these are really used by iris */
// methodGetBytes "GET"
methodGetBytes = []byte(MethodGet) // MethodGetBytes "GET"
// methodPostBytes "POST" MethodGetBytes = []byte(MethodGet)
methodPostBytes = []byte(MethodPost) // MethodPostBytes "POST"
// methodPutBytes "PUT" MethodPostBytes = []byte(MethodPost)
methodPutBytes = []byte(MethodPut) // MethodPutBytes "PUT"
// methodDeleteBytes "DELETE" MethodPutBytes = []byte(MethodPut)
methodDeleteBytes = []byte(MethodDelete) // MethodDeleteBytes "DELETE"
// methodConnectBytes "CONNECT" MethodDeleteBytes = []byte(MethodDelete)
methodConnectBytes = []byte(MethodConnect) // MethodConnectBytes "CONNECT"
// methodHeadBytes "HEAD" MethodConnectBytes = []byte(MethodConnect)
methodHeadBytes = []byte(MethodHead) // MethodHeadBytes "HEAD"
// methodPatchBytes "PATCH" MethodHeadBytes = []byte(MethodHead)
methodPatchBytes = []byte(MethodPatch) // MethodPatchBytes "PATCH"
// methodOptionsBytes "OPTIONS" MethodPatchBytes = []byte(MethodPatch)
methodOptionsBytes = []byte(MethodOptions) // MethodOptionsBytes "OPTIONS"
// methodTraceBytes "TRACE" MethodOptionsBytes = []byte(MethodOptions)
methodTraceBytes = []byte(MethodTrace) // MethodTraceBytes "TRACE"
MethodTraceBytes = []byte(MethodTrace)
/* */ /* */
) )
@ -1444,7 +1445,7 @@ func (mux *serveMux) register(method []byte, subdomain string, path string, midd
// build collects all routes info and adds them to the registry in order to be served from the request handler // build collects all routes info and adds them to the registry in order to be served from the request handler
// this happens once when server is setting the mux's handler. // this happens once when server is setting the mux's handler.
func (mux *serveMux) build() { func (mux *serveMux) build() (func(reqCtx *fasthttp.RequestCtx) string, func([]byte, []byte) bool) {
mux.tree = nil mux.tree = nil
sort.Sort(bySubdomain(mux.lookups)) sort.Sort(bySubdomain(mux.lookups))
for _, r := range mux.lookups { for _, r := range mux.lookups {
@ -1474,6 +1475,31 @@ func (mux *serveMux) build() {
mux.logger.Panic(err.Error()) mux.logger.Panic(err.Error())
} }
} }
// optimize this once once, we could do that: context.RequestPath(mux.escapePath), but we lose some nanoseconds on if :)
getRequestPath := func(reqCtx *fasthttp.RequestCtx) string {
return utils.BytesToString(reqCtx.Path()) //string(ctx.Path()[:]) // a little bit of memory allocation, old method used: BytesToString, If I see the benchmarks get low I will change it back to old, but this way is safer.
}
if !mux.escapePath {
getRequestPath = func(reqCtx *fasthttp.RequestCtx) string { return utils.BytesToString(reqCtx.RequestURI()) }
}
methodEqual := func(treeMethod []byte, reqMethod []byte) bool {
return bytes.Equal(treeMethod, reqMethod)
}
// check for cors conflicts
for _, r := range mux.lookups {
if r.hasCors() {
methodEqual = func(treeMethod []byte, reqMethod []byte) bool {
return bytes.Equal(treeMethod, reqMethod) || bytes.Equal(reqMethod, MethodOptionsBytes)
}
break
}
}
return getRequestPath, methodEqual
} }
func (mux *serveMux) lookup(routeName string) *route { func (mux *serveMux) lookup(routeName string) *route {
@ -1485,34 +1511,14 @@ func (mux *serveMux) lookup(routeName string) *route {
return nil return nil
} }
func (mux *serveMux) Handler() HandlerFunc { // BuildHandler the default Iris router when iris.Handler is nil
func (mux *serveMux) BuildHandler() HandlerFunc {
// initialize the router once // initialize the router once
mux.build() getRequestPath, methodEqual := mux.build()
// optimize this once once, we could do that: context.RequestPath(mux.escapePath), but we lose some nanoseconds on if :)
getRequestPath := func(ctx *Context) string {
return utils.BytesToString(ctx.Path()) //string(ctx.Path()[:]) // a little bit of memory allocation, old method used: BytesToString, If I see the benchmarks get low I will change it back to old, but this way is safer.
}
if !mux.escapePath {
getRequestPath = func(ctx *Context) string { return utils.BytesToString(ctx.RequestCtx.RequestURI()) }
}
methodEqual := func(treeMethod []byte, reqMethod []byte) bool {
return bytes.Equal(treeMethod, reqMethod)
}
// check for cors conflicts
for _, r := range mux.lookups {
if r.hasCors() {
methodEqual = func(treeMethod []byte, reqMethod []byte) bool {
return bytes.Equal(treeMethod, reqMethod) || bytes.Equal(reqMethod, methodOptionsBytes)
}
break
}
}
return func(context *Context) { return func(context *Context) {
routePath := getRequestPath(context) routePath := getRequestPath(context.RequestCtx)
tree := mux.tree tree := mux.tree
for tree != nil { for tree != nil {
if !methodEqual(tree.method, context.Method()) { if !methodEqual(tree.method, context.Method()) {
@ -1553,7 +1559,7 @@ func (mux *serveMux) Handler() HandlerFunc {
//ctx.Request.Header.SetUserAgentBytes(DefaultUserAgent) //ctx.Request.Header.SetUserAgentBytes(DefaultUserAgent)
context.Do() context.Do()
return return
} else if mustRedirect && mux.correctPath && !bytes.Equal(context.Method(), methodConnectBytes) { } else if mustRedirect && mux.correctPath && !bytes.Equal(context.Method(), MethodConnectBytes) {
reqPath := routePath reqPath := routePath
pathLen := len(reqPath) pathLen := len(reqPath)
@ -1574,7 +1580,7 @@ func (mux *serveMux) Handler() HandlerFunc {
// RFC2616 recommends that a short note "SHOULD" be included in the // RFC2616 recommends that a short note "SHOULD" be included in the
// response because older user agents may not understand 301/307. // response because older user agents may not understand 301/307.
// Shouldn't send the response for POST or HEAD; that leaves GET. // Shouldn't send the response for POST or HEAD; that leaves GET.
if bytes.Equal(tree.method, methodGetBytes) { if bytes.Equal(tree.method, MethodGetBytes) {
note := "<a href=\"" + utils.HTMLEscape(urlToRedirect) + "\">Moved Permanently</a>.\n" note := "<a href=\"" + utils.HTMLEscape(urlToRedirect) + "\">Moved Permanently</a>.\n"
context.Write(note) context.Write(note)
} }

38
iris.go
View File

@ -78,7 +78,7 @@ import (
const ( const (
// Version is the current version of the Iris web framework // Version is the current version of the Iris web framework
Version = "4.2.6" Version = "4.2.7"
banner = ` _____ _ banner = ` _____ _
|_ _| (_) |_ _| (_)
@ -167,6 +167,11 @@ type (
// Implements the FrameworkAPI // Implements the FrameworkAPI
Framework struct { Framework struct {
*muxAPI *muxAPI
// Handler field which can change the default iris' mux behavior
// if you want to get benefit with iris' context make use of:
// ctx:= iris.AcquireCtx(*fasthttp.RequestCtx) to get the context at the beginning of your handler
// iris.ReleaseCtx(ctx) to release/put the context to the pool, at the very end of your custom handler.
Handler fasthttp.RequestHandler
contextPool sync.Pool contextPool sync.Pool
Config *Configuration Config *Configuration
sessions sessions.Sessions sessions sessions.Sessions
@ -301,24 +306,47 @@ func Go() error {
return Default.Go() return Default.Go()
} }
// AcquireCtx gets an Iris' Context from pool
// see iris.Handler & ReleaseCtx, Go()
func (s *Framework) AcquireCtx(reqCtx *fasthttp.RequestCtx) {
ctx := s.contextPool.Get().(*Context) // Changed to use the pool's New 09/07/2016, ~ -4k nanoseconds(9 bench tests) per requests (better performance)
ctx.RequestCtx = reqCtx
}
// ReleaseCtx puts the Iris' Context back to the pool in order to be re-used
// see iris.Handler & AcquireCtx, Go()
func (s *Framework) ReleaseCtx(ctx *Context) {
ctx.Params = ctx.Params[0:0]
ctx.middleware = nil
ctx.session = nil
s.contextPool.Put(ctx)
}
// Go starts the iris station, listens to all registered servers, and prepare only if Virtual // Go starts the iris station, listens to all registered servers, and prepare only if Virtual
func (s *Framework) Go() error { func (s *Framework) Go() error {
s.initialize() s.initialize()
s.Plugins.DoPreListen(s) s.Plugins.DoPreListen(s)
if s.Handler == nil { // use the 'h' which is the default mux' handler
// build and get the default mux' handler(*Context)
serve := s.mux.BuildHandler()
// build the fasthttp handler to bind it to the servers // build the fasthttp handler to bind it to the servers
h := s.mux.Handler() defaultHandler := func(reqCtx *fasthttp.RequestCtx) {
reqHandler := func(reqCtx *fasthttp.RequestCtx) {
ctx := s.contextPool.Get().(*Context) // Changed to use the pool's New 09/07/2016, ~ -4k nanoseconds(9 bench tests) per requests (better performance) ctx := s.contextPool.Get().(*Context) // Changed to use the pool's New 09/07/2016, ~ -4k nanoseconds(9 bench tests) per requests (better performance)
ctx.RequestCtx = reqCtx ctx.RequestCtx = reqCtx
h(ctx) serve(ctx)
ctx.Params = ctx.Params[0:0] ctx.Params = ctx.Params[0:0]
ctx.middleware = nil ctx.middleware = nil
ctx.session = nil ctx.session = nil
s.contextPool.Put(ctx) s.contextPool.Put(ctx)
} }
if firstErr := s.Servers.OpenAll(reqHandler); firstErr != nil {
s.Handler = defaultHandler
}
if firstErr := s.Servers.OpenAll(s.Handler); firstErr != nil {
return firstErr return firstErr
} }