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`.
## 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
- **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/>
<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>
@ -181,7 +181,7 @@ I recommend writing your API tests using this new library, [httpexpect](https://
Versioning
------------
Current: **v4.2.6**
Current: **v4.2.7**
> Iris is an active project
@ -224,7 +224,7 @@ License can be found [here](LICENSE).
[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]: 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
[Chat Widget]: https://img.shields.io/badge/community-chat-00BCD4.svg?style=flat-square
[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:
// iris.AddServer(iris.ServerConfiguration{ListeningAddr: ":9090", MaxRequestsPerConn:100})
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)
//
// 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}
/* methods as []byte, these are really used by iris */
// methodGetBytes "GET"
methodGetBytes = []byte(MethodGet)
// methodPostBytes "POST"
methodPostBytes = []byte(MethodPost)
// methodPutBytes "PUT"
methodPutBytes = []byte(MethodPut)
// methodDeleteBytes "DELETE"
methodDeleteBytes = []byte(MethodDelete)
// methodConnectBytes "CONNECT"
methodConnectBytes = []byte(MethodConnect)
// methodHeadBytes "HEAD"
methodHeadBytes = []byte(MethodHead)
// methodPatchBytes "PATCH"
methodPatchBytes = []byte(MethodPatch)
// methodOptionsBytes "OPTIONS"
methodOptionsBytes = []byte(MethodOptions)
// methodTraceBytes "TRACE"
methodTraceBytes = []byte(MethodTrace)
// MethodGetBytes "GET"
MethodGetBytes = []byte(MethodGet)
// MethodPostBytes "POST"
MethodPostBytes = []byte(MethodPost)
// MethodPutBytes "PUT"
MethodPutBytes = []byte(MethodPut)
// MethodDeleteBytes "DELETE"
MethodDeleteBytes = []byte(MethodDelete)
// MethodConnectBytes "CONNECT"
MethodConnectBytes = []byte(MethodConnect)
// MethodHeadBytes "HEAD"
MethodHeadBytes = []byte(MethodHead)
// MethodPatchBytes "PATCH"
MethodPatchBytes = []byte(MethodPatch)
// MethodOptionsBytes "OPTIONS"
MethodOptionsBytes = []byte(MethodOptions)
// 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
// 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
sort.Sort(bySubdomain(mux.lookups))
for _, r := range mux.lookups {
@ -1474,6 +1475,31 @@ func (mux *serveMux) build() {
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 {
@ -1485,34 +1511,14 @@ func (mux *serveMux) lookup(routeName string) *route {
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
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
}
}
getRequestPath, methodEqual := mux.build()
return func(context *Context) {
routePath := getRequestPath(context)
routePath := getRequestPath(context.RequestCtx)
tree := mux.tree
for tree != nil {
if !methodEqual(tree.method, context.Method()) {
@ -1553,7 +1559,7 @@ func (mux *serveMux) Handler() HandlerFunc {
//ctx.Request.Header.SetUserAgentBytes(DefaultUserAgent)
context.Do()
return
} else if mustRedirect && mux.correctPath && !bytes.Equal(context.Method(), methodConnectBytes) {
} else if mustRedirect && mux.correctPath && !bytes.Equal(context.Method(), MethodConnectBytes) {
reqPath := routePath
pathLen := len(reqPath)
@ -1574,7 +1580,7 @@ func (mux *serveMux) Handler() HandlerFunc {
// RFC2616 recommends that a short note "SHOULD" be included in the
// response because older user agents may not understand 301/307.
// 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"
context.Write(note)
}

52
iris.go
View File

@ -78,7 +78,7 @@ import (
const (
// Version is the current version of the Iris web framework
Version = "4.2.6"
Version = "4.2.7"
banner = ` _____ _
|_ _| (_)
@ -167,6 +167,11 @@ type (
// Implements the FrameworkAPI
Framework struct {
*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
Config *Configuration
sessions sessions.Sessions
@ -301,24 +306,47 @@ func Go() error {
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
func (s *Framework) Go() error {
s.initialize()
s.Plugins.DoPreListen(s)
// build the fasthttp handler to bind it to the servers
h := s.mux.Handler()
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.RequestCtx = reqCtx
h(ctx)
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
defaultHandler := 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.RequestCtx = reqCtx
ctx.Params = ctx.Params[0:0]
ctx.middleware = nil
ctx.session = nil
s.contextPool.Put(ctx)
serve(ctx)
ctx.Params = ctx.Params[0:0]
ctx.middleware = nil
ctx.session = nil
s.contextPool.Put(ctx)
}
s.Handler = defaultHandler
}
if firstErr := s.Servers.OpenAll(reqHandler); firstErr != nil {
if firstErr := s.Servers.OpenAll(s.Handler); firstErr != nil {
return firstErr
}