mirror of
https://github.com/kataras/iris.git
synced 2026-01-11 14:05:59 +00:00
20 days of unstoppable work. Waiting fo go 1.8, I didn't finish yet, some touches remains.
Former-commit-id: ed84f99c89f43fe5e980a8e6d0ee22c186f0e1b9
This commit is contained in:
47
adaptors/httprouter/LICENSE
Normal file
47
adaptors/httprouter/LICENSE
Normal file
@@ -0,0 +1,47 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2017 Gerasimos Maropoulos
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
Copyright (c) 2013 Julien Schmidt. All rights reserved.
|
||||
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The names of the contributors may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL JULIEN SCHMIDT BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
39
adaptors/httprouter/_example/main.go
Normal file
39
adaptors/httprouter/_example/main.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"gopkg.in/kataras/iris.v6"
|
||||
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
|
||||
)
|
||||
|
||||
func hello(ctx *iris.Context) {
|
||||
ctx.Writef("Hello from %s", ctx.Path())
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app.Adapt(iris.DevLogger())
|
||||
app.Adapt(httprouter.New())
|
||||
|
||||
app.OnError(iris.StatusNotFound, func(ctx *iris.Context) {
|
||||
ctx.HTML(iris.StatusNotFound, "<h1>Custom not found handler </h1>")
|
||||
})
|
||||
|
||||
app.Get("/", hello)
|
||||
app.Get("/users/:userid", func(ctx *iris.Context) {
|
||||
ctx.Writef("Hello user with id: %s", ctx.Param("userid"))
|
||||
})
|
||||
|
||||
app.Get("/myfiles/*file", func(ctx *iris.Context) {
|
||||
ctx.HTML(iris.StatusOK, "Hello, the dynamic path after /myfiles is:<br/> <b>"+ctx.Param("file")+"</b>")
|
||||
})
|
||||
|
||||
app.Get("/users/:userid/messages/:messageid", func(ctx *iris.Context) {
|
||||
ctx.HTML(iris.StatusOK, `Message from user with id:<br/> <b>`+ctx.Param("userid")+`</b>,
|
||||
message id: <b>`+ctx.Param("messageid")+`</b>`)
|
||||
})
|
||||
|
||||
// http://127.0.0.1:8080/users/42
|
||||
// http://127.0.0.1:8080/myfiles/mydirectory/myfile.zip
|
||||
// http://127.0.0.1:8080/users/42/messages/1
|
||||
app.Listen(":8080")
|
||||
}
|
||||
725
adaptors/httprouter/httprouter.go
Normal file
725
adaptors/httprouter/httprouter.go
Normal file
@@ -0,0 +1,725 @@
|
||||
package httprouter
|
||||
|
||||
// +------------------------------------------------------------+
|
||||
// | Usage |
|
||||
// +------------------------------------------------------------+
|
||||
//
|
||||
//
|
||||
// package main
|
||||
//
|
||||
// import (
|
||||
// "gopkg.in/kataras/iris.v6/adaptors/httprouter"
|
||||
// "gopkg.in/kataras/iris.v6"
|
||||
// )
|
||||
//
|
||||
// func main() {
|
||||
// app := iris.New()
|
||||
//
|
||||
// app.Adapt(httprouter.New()) // Add this line and you're ready.
|
||||
//
|
||||
// app.Get("/api/users/:userid", func(ctx *iris.Context) {
|
||||
// ctx.Writef("User with id: %s", ctx.Param("userid"))
|
||||
// })
|
||||
//
|
||||
// app.Listen(":8080")
|
||||
// }
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/go-errors"
|
||||
"gopkg.in/kataras/iris.v6"
|
||||
)
|
||||
|
||||
const (
|
||||
// parameterStartByte is very used on the node, it's just contains the byte for the ':' rune/char
|
||||
parameterStartByte = byte(':')
|
||||
// slashByte is just a byte of '/' rune/char
|
||||
slashByte = byte('/')
|
||||
// slash is just a string of "/"
|
||||
slash = "/"
|
||||
// matchEverythingByte is just a byte of '*" rune/char
|
||||
matchEverythingByte = byte('*')
|
||||
|
||||
isRoot entryCase = iota
|
||||
hasParams
|
||||
matchEverything
|
||||
)
|
||||
|
||||
type (
|
||||
// entryCase is the type which the type of muxEntryusing in order to determinate what type (parameterized, anything, static...) is the perticular node
|
||||
entryCase uint8
|
||||
|
||||
// muxEntry is the node of a tree of the routes,
|
||||
// in order to learn how this is working, google 'trie' or watch this lecture: https://www.youtube.com/watch?v=uhAUk63tLRM
|
||||
// this method is used by the BSD's kernel also
|
||||
muxEntry struct {
|
||||
part string
|
||||
entryCase entryCase
|
||||
hasWildNode bool
|
||||
tokens string
|
||||
nodes []*muxEntry
|
||||
middleware iris.Middleware
|
||||
precedence uint64
|
||||
paramsLen uint8
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
errMuxEntryConflictsWildcard = errors.New(`
|
||||
Router: '%s' in new path '%s'
|
||||
conflicts with existing wildcarded route with path: '%s'
|
||||
in existing prefix of'%s' `)
|
||||
|
||||
errMuxEntryMiddlewareAlreadyExists = errors.New(`
|
||||
Router: Middleware were already registered for the path: '%s'`)
|
||||
|
||||
errMuxEntryInvalidWildcard = errors.New(`
|
||||
Router: More than one wildcard found in the path part: '%s' in route's path: '%s'`)
|
||||
|
||||
errMuxEntryConflictsExistingWildcard = errors.New(`
|
||||
Router: Wildcard for route path: '%s' conflicts with existing children in route path: '%s'`)
|
||||
|
||||
errMuxEntryWildcardUnnamed = errors.New(`
|
||||
Router: Unnamed wildcard found in path: '%s'`)
|
||||
|
||||
errMuxEntryWildcardInvalidPlace = errors.New(`
|
||||
Router: Wildcard is only allowed at the end of the path, in the route path: '%s'`)
|
||||
|
||||
errMuxEntryWildcardConflictsMiddleware = errors.New(`
|
||||
Router: Wildcard conflicts with existing middleware for the route path: '%s'`)
|
||||
|
||||
errMuxEntryWildcardMissingSlash = errors.New(`
|
||||
Router: No slash(/) were found before wildcard in the route path: '%s'`)
|
||||
)
|
||||
|
||||
// getParamsLen returns the parameters length from a given path
|
||||
func getParamsLen(path string) uint8 {
|
||||
var n uint
|
||||
for i := 0; i < len(path); i++ {
|
||||
if path[i] != ':' && path[i] != '*' { // ParameterStartByte & MatchEverythingByte
|
||||
continue
|
||||
}
|
||||
n++
|
||||
}
|
||||
if n >= 255 {
|
||||
return 255
|
||||
}
|
||||
return uint8(n)
|
||||
}
|
||||
|
||||
// findLower returns the smaller number between a and b
|
||||
func findLower(a, b int) int {
|
||||
if a <= b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// add adds a muxEntry to the existing muxEntry or to the tree if no muxEntry has the prefix of
|
||||
func (e *muxEntry) add(path string, middleware iris.Middleware) error {
|
||||
fullPath := path
|
||||
e.precedence++
|
||||
numParams := getParamsLen(path)
|
||||
|
||||
if len(e.part) > 0 || len(e.nodes) > 0 {
|
||||
loop:
|
||||
for {
|
||||
if numParams > e.paramsLen {
|
||||
e.paramsLen = numParams
|
||||
}
|
||||
|
||||
i := 0
|
||||
max := findLower(len(path), len(e.part))
|
||||
for i < max && path[i] == e.part[i] {
|
||||
i++
|
||||
}
|
||||
|
||||
if i < len(e.part) {
|
||||
node := muxEntry{
|
||||
part: e.part[i:],
|
||||
hasWildNode: e.hasWildNode,
|
||||
tokens: e.tokens,
|
||||
nodes: e.nodes,
|
||||
middleware: e.middleware,
|
||||
precedence: e.precedence - 1,
|
||||
}
|
||||
|
||||
for i := range node.nodes {
|
||||
if node.nodes[i].paramsLen > node.paramsLen {
|
||||
node.paramsLen = node.nodes[i].paramsLen
|
||||
}
|
||||
}
|
||||
|
||||
e.nodes = []*muxEntry{&node}
|
||||
e.tokens = string([]byte{e.part[i]})
|
||||
e.part = path[:i]
|
||||
e.middleware = nil
|
||||
e.hasWildNode = false
|
||||
}
|
||||
|
||||
if i < len(path) {
|
||||
path = path[i:]
|
||||
|
||||
if e.hasWildNode {
|
||||
e = e.nodes[0]
|
||||
e.precedence++
|
||||
|
||||
if numParams > e.paramsLen {
|
||||
e.paramsLen = numParams
|
||||
}
|
||||
numParams--
|
||||
|
||||
if len(path) >= len(e.part) && e.part == path[:len(e.part)] &&
|
||||
// Check for longer wildcard, e.g. :name and :names
|
||||
(len(e.part) >= len(path) || path[len(e.part)] == '/') {
|
||||
continue loop
|
||||
} else {
|
||||
// Wildcard conflict
|
||||
part := strings.SplitN(path, "/", 2)[0]
|
||||
prefix := fullPath[:strings.Index(fullPath, part)] + e.part
|
||||
return errMuxEntryConflictsWildcard.Format(fullPath, e.part, prefix)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
c := path[0]
|
||||
|
||||
if e.entryCase == hasParams && c == slashByte && len(e.nodes) == 1 {
|
||||
e = e.nodes[0]
|
||||
e.precedence++
|
||||
continue loop
|
||||
}
|
||||
for i := range e.tokens {
|
||||
if c == e.tokens[i] {
|
||||
i = e.precedenceTo(i)
|
||||
e = e.nodes[i]
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
|
||||
if c != parameterStartByte && c != matchEverythingByte {
|
||||
|
||||
e.tokens += string([]byte{c})
|
||||
node := &muxEntry{
|
||||
paramsLen: numParams,
|
||||
}
|
||||
e.nodes = append(e.nodes, node)
|
||||
e.precedenceTo(len(e.tokens) - 1)
|
||||
e = node
|
||||
}
|
||||
return e.addNode(numParams, path, fullPath, middleware)
|
||||
|
||||
} else if i == len(path) {
|
||||
if e.middleware != nil {
|
||||
return errMuxEntryMiddlewareAlreadyExists.Format(fullPath)
|
||||
}
|
||||
e.middleware = middleware
|
||||
}
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
if err := e.addNode(numParams, path, fullPath, middleware); err != nil {
|
||||
return err
|
||||
}
|
||||
e.entryCase = isRoot
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// addNode adds a muxEntry as children to other muxEntry
|
||||
func (e *muxEntry) addNode(numParams uint8, path string, fullPath string, middleware iris.Middleware) error {
|
||||
var offset int
|
||||
|
||||
for i, max := 0, len(path); numParams > 0; i++ {
|
||||
c := path[i]
|
||||
if c != parameterStartByte && c != matchEverythingByte {
|
||||
continue
|
||||
}
|
||||
|
||||
end := i + 1
|
||||
for end < max && path[end] != slashByte {
|
||||
switch path[end] {
|
||||
case parameterStartByte, matchEverythingByte:
|
||||
return errMuxEntryInvalidWildcard.Format(path[i:], fullPath)
|
||||
default:
|
||||
end++
|
||||
}
|
||||
}
|
||||
|
||||
if len(e.nodes) > 0 {
|
||||
return errMuxEntryConflictsExistingWildcard.Format(path[i:end], fullPath)
|
||||
}
|
||||
|
||||
if end-i < 2 {
|
||||
return errMuxEntryWildcardUnnamed.Format(fullPath)
|
||||
}
|
||||
|
||||
if c == parameterStartByte {
|
||||
|
||||
if i > 0 {
|
||||
e.part = path[offset:i]
|
||||
offset = i
|
||||
}
|
||||
|
||||
child := &muxEntry{
|
||||
entryCase: hasParams,
|
||||
paramsLen: numParams,
|
||||
}
|
||||
e.nodes = []*muxEntry{child}
|
||||
e.hasWildNode = true
|
||||
e = child
|
||||
e.precedence++
|
||||
numParams--
|
||||
|
||||
if end < max {
|
||||
e.part = path[offset:end]
|
||||
offset = end
|
||||
|
||||
child := &muxEntry{
|
||||
paramsLen: numParams,
|
||||
precedence: 1,
|
||||
}
|
||||
e.nodes = []*muxEntry{child}
|
||||
e = child
|
||||
}
|
||||
|
||||
} else {
|
||||
if end != max || numParams > 1 {
|
||||
return errMuxEntryWildcardInvalidPlace.Format(fullPath)
|
||||
}
|
||||
|
||||
if len(e.part) > 0 && e.part[len(e.part)-1] == '/' {
|
||||
return errMuxEntryWildcardConflictsMiddleware.Format(fullPath)
|
||||
}
|
||||
|
||||
i--
|
||||
if path[i] != slashByte {
|
||||
return errMuxEntryWildcardMissingSlash.Format(fullPath)
|
||||
}
|
||||
|
||||
e.part = path[offset:i]
|
||||
|
||||
child := &muxEntry{
|
||||
hasWildNode: true,
|
||||
entryCase: matchEverything,
|
||||
paramsLen: 1,
|
||||
}
|
||||
e.nodes = []*muxEntry{child}
|
||||
e.tokens = string(path[i])
|
||||
e = child
|
||||
e.precedence++
|
||||
|
||||
child = &muxEntry{
|
||||
part: path[i:],
|
||||
entryCase: matchEverything,
|
||||
paramsLen: 1,
|
||||
middleware: middleware,
|
||||
precedence: 1,
|
||||
}
|
||||
e.nodes = []*muxEntry{child}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
e.part = path[offset:]
|
||||
e.middleware = middleware
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// get is used by the Router, it finds and returns the correct muxEntry for a path
|
||||
func (e *muxEntry) get(path string, ctx *iris.Context) (mustRedirect bool) {
|
||||
loop:
|
||||
for {
|
||||
if len(path) > len(e.part) {
|
||||
if path[:len(e.part)] == e.part {
|
||||
path = path[len(e.part):]
|
||||
|
||||
if !e.hasWildNode {
|
||||
c := path[0]
|
||||
for i := range e.tokens {
|
||||
if c == e.tokens[i] {
|
||||
e = e.nodes[i]
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
|
||||
mustRedirect = (path == slash && e.middleware != nil)
|
||||
return
|
||||
}
|
||||
|
||||
e = e.nodes[0]
|
||||
switch e.entryCase {
|
||||
case hasParams:
|
||||
|
||||
end := 0
|
||||
for end < len(path) && path[end] != '/' {
|
||||
end++
|
||||
}
|
||||
|
||||
ctx.Set(e.part[1:], path[:end])
|
||||
|
||||
if end < len(path) {
|
||||
if len(e.nodes) > 0 {
|
||||
path = path[end:]
|
||||
e = e.nodes[0]
|
||||
continue loop
|
||||
}
|
||||
|
||||
mustRedirect = (len(path) == end+1)
|
||||
return
|
||||
}
|
||||
if ctx.Middleware = e.middleware; ctx.Middleware != nil {
|
||||
return
|
||||
} else if len(e.nodes) == 1 {
|
||||
e = e.nodes[0]
|
||||
mustRedirect = (e.part == slash && e.middleware != nil)
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
case matchEverything:
|
||||
|
||||
ctx.Set(e.part[2:], path)
|
||||
ctx.Middleware = e.middleware
|
||||
return
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if path == e.part {
|
||||
if ctx.Middleware = e.middleware; ctx.Middleware != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if path == slash && e.hasWildNode && e.entryCase != isRoot {
|
||||
mustRedirect = true
|
||||
return
|
||||
}
|
||||
|
||||
for i := range e.tokens {
|
||||
if e.tokens[i] == slashByte {
|
||||
e = e.nodes[i]
|
||||
mustRedirect = (len(e.part) == 1 && e.middleware != nil) ||
|
||||
(e.entryCase == matchEverything && e.nodes[0].middleware != nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
mustRedirect = (path == slash) ||
|
||||
(len(e.part) == len(path)+1 && e.part[len(path)] == slashByte &&
|
||||
path == e.part[:len(e.part)-1] && e.middleware != nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// precedenceTo just adds the priority of this muxEntry by an index
|
||||
func (e *muxEntry) precedenceTo(index int) int {
|
||||
e.nodes[index].precedence++
|
||||
_precedence := e.nodes[index].precedence
|
||||
|
||||
newindex := index
|
||||
for newindex > 0 && e.nodes[newindex-1].precedence < _precedence {
|
||||
tmpN := e.nodes[newindex-1]
|
||||
e.nodes[newindex-1] = e.nodes[newindex]
|
||||
e.nodes[newindex] = tmpN
|
||||
|
||||
newindex--
|
||||
}
|
||||
|
||||
if newindex != index {
|
||||
e.tokens = e.tokens[:newindex] +
|
||||
e.tokens[index:index+1] +
|
||||
e.tokens[newindex:index] + e.tokens[index+1:]
|
||||
}
|
||||
|
||||
return newindex
|
||||
}
|
||||
|
||||
type (
|
||||
muxTree struct {
|
||||
method string
|
||||
// subdomain is empty for default-hostname routes,
|
||||
// ex: mysubdomain.
|
||||
subdomain string
|
||||
entry *muxEntry
|
||||
}
|
||||
|
||||
serveMux struct {
|
||||
garden []*muxTree
|
||||
maxParameters uint8
|
||||
methodEqual func(string, string) bool
|
||||
hosts bool
|
||||
}
|
||||
)
|
||||
|
||||
// New returns a new iris' policy to create and attach the router.
|
||||
// It's based on the julienschmidt/httprouter with more features and some iris-relative performance tips:
|
||||
// subdomains(wildcard/dynamic and static) and faster parameters set (use of the already-created context's values)
|
||||
// and support for reverse routing.
|
||||
func New() iris.Policies {
|
||||
var logger func(iris.LogMode, string)
|
||||
mux := &serveMux{
|
||||
methodEqual: func(reqMethod string, treeMethod string) bool {
|
||||
return reqMethod == treeMethod
|
||||
},
|
||||
}
|
||||
matchEverythingString := string(matchEverythingByte)
|
||||
return iris.Policies{
|
||||
EventPolicy: iris.EventPolicy{
|
||||
Boot: func(s *iris.Framework) {
|
||||
logger = s.Log
|
||||
},
|
||||
},
|
||||
RouterReversionPolicy: iris.RouterReversionPolicy{
|
||||
// path normalization done on iris' side
|
||||
StaticPath: func(path string) string {
|
||||
|
||||
i := strings.IndexByte(path, parameterStartByte)
|
||||
x := strings.IndexByte(path, matchEverythingByte)
|
||||
if i > -1 {
|
||||
return path[0:i]
|
||||
}
|
||||
if x > -1 {
|
||||
return path[0:x]
|
||||
}
|
||||
|
||||
return path
|
||||
},
|
||||
WildcardPath: func(path string, paramName string) string {
|
||||
return path + slash + matchEverythingString + paramName
|
||||
},
|
||||
|
||||
// URLPath: func(r iris.RouteInfo, args ...string) string {
|
||||
// argsLen := len(args)
|
||||
//
|
||||
// // we have named parameters but arguments not given
|
||||
// if argsLen == 0 && r.formattedParts > 0 {
|
||||
// return ""
|
||||
// } else if argsLen == 0 && r.formattedParts == 0 {
|
||||
// // it's static then just return the path
|
||||
// return r.path
|
||||
// }
|
||||
//
|
||||
// // we have arguments but they are much more than the named parameters
|
||||
//
|
||||
// // 1 check if we have /*, if yes then join all arguments to one as path and pass that as parameter
|
||||
// if argsLen > r.formattedParts {
|
||||
// if r.path[len(r.path)-1] == matchEverythingByte {
|
||||
// // we have to convert each argument to a string in this case
|
||||
//
|
||||
// argsString := make([]string, argsLen, argsLen)
|
||||
//
|
||||
// for i, v := range args {
|
||||
// if s, ok := v.(string); ok {
|
||||
// argsString[i] = s
|
||||
// } else if num, ok := v.(int); ok {
|
||||
// argsString[i] = strconv.Itoa(num)
|
||||
// } else if b, ok := v.(bool); ok {
|
||||
// argsString[i] = strconv.FormatBool(b)
|
||||
// } else if arr, ok := v.([]string); ok {
|
||||
// if len(arr) > 0 {
|
||||
// argsString[i] = arr[0]
|
||||
// argsString = append(argsString, arr[1:]...)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// parameter := strings.Join(argsString, slash)
|
||||
// result := fmt.Sprintf(r.formattedPath, parameter)
|
||||
// return result
|
||||
// }
|
||||
// // 2 if !1 return false
|
||||
// return ""
|
||||
// }
|
||||
//
|
||||
// arguments := joinPathArguments(args...)
|
||||
//
|
||||
// return fmt.Sprintf(r.formattedPath, arguments...)
|
||||
// },
|
||||
RouteContextLinker: func(r iris.RouteInfo, ctx *iris.Context) {
|
||||
tree := mux.getTree(r.Method(), r.Subdomain())
|
||||
if tree != nil {
|
||||
tree.entry.get(ctx.Request.URL.Path, ctx)
|
||||
}
|
||||
},
|
||||
},
|
||||
RouterBuilderPolicy: func(repo iris.RouteRepository, context iris.ContextPool) http.Handler {
|
||||
fatalErr := false
|
||||
repo.Visit(func(r iris.RouteInfo) {
|
||||
if fatalErr {
|
||||
return
|
||||
}
|
||||
// add to the registry tree
|
||||
method := r.Method()
|
||||
subdomain := r.Subdomain()
|
||||
path := r.Path()
|
||||
middleware := r.Middleware()
|
||||
tree := mux.getTree(method, subdomain)
|
||||
if tree == nil {
|
||||
//first time we register a route to this method with this domain
|
||||
tree = &muxTree{method: method, subdomain: subdomain, entry: &muxEntry{}}
|
||||
mux.garden = append(mux.garden, tree)
|
||||
}
|
||||
// I decide that it's better to explicit give subdomain and a path to it than registeredPath(mysubdomain./something) now its: subdomain: mysubdomain., path: /something
|
||||
// we have different tree for each of subdomains, now you can use everything you can use with the normal paths ( before you couldn't set /any/*path)
|
||||
if err := tree.entry.add(path, middleware); err != nil {
|
||||
// while ProdMode means that the iris should not continue running
|
||||
// by-default it panics on these errors, but to make sure let's introduce the fatalErr to stop visiting
|
||||
fatalErr = true
|
||||
logger(iris.ProdMode, "fatal error on httprouter build adaptor: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if mp := tree.entry.paramsLen; mp > mux.maxParameters {
|
||||
mux.maxParameters = mp
|
||||
}
|
||||
|
||||
// check for method equality if at least one route has cors
|
||||
if r.HasCors() {
|
||||
mux.methodEqual = func(reqMethod string, treeMethod string) bool {
|
||||
// preflights
|
||||
return reqMethod == iris.MethodOptions || reqMethod == treeMethod
|
||||
}
|
||||
}
|
||||
|
||||
if subdomain != "" {
|
||||
mux.hosts = true
|
||||
}
|
||||
})
|
||||
if !fatalErr {
|
||||
return mux.buildHandler(context)
|
||||
}
|
||||
return nil
|
||||
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (mux *serveMux) getTree(method string, subdomain string) *muxTree {
|
||||
for i := range mux.garden {
|
||||
t := mux.garden[i]
|
||||
if t.method == method && t.subdomain == subdomain {
|
||||
return t
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mux *serveMux) buildHandler(pool iris.ContextPool) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
pool.Run(w, r, func(context *iris.Context) {
|
||||
routePath := context.Path()
|
||||
for i := range mux.garden {
|
||||
tree := mux.garden[i]
|
||||
if !mux.methodEqual(context.Request.Method, tree.method) {
|
||||
continue
|
||||
}
|
||||
|
||||
if mux.hosts && tree.subdomain != "" {
|
||||
// context.VirtualHost() is a slow method because it makes
|
||||
// string.Replaces but user can understand that if subdomain then server will have some nano/or/milleseconds performance cost
|
||||
requestHost := context.VirtualHostname()
|
||||
hostname := context.Framework().Config.VHost
|
||||
if requestHost != hostname {
|
||||
//println(requestHost + " != " + mux.hostname)
|
||||
// we have a subdomain
|
||||
if strings.Contains(tree.subdomain, iris.DynamicSubdomainIndicator) {
|
||||
} else {
|
||||
//println(requestHost + " = " + mux.hostname)
|
||||
// mux.host = iris-go.com:8080, the subdomain for example is api.,
|
||||
// so the host must be api.iris-go.com:8080
|
||||
if tree.subdomain+hostname != requestHost {
|
||||
// go to the next tree, we have a subdomain but it is not the correct
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
//("it's subdomain but the request is the same as the listening addr mux.host == requestHost =>" + mux.host + "=" + requestHost + " ____ and tree's subdomain was: " + tree.subdomain)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
mustRedirect := tree.entry.get(routePath, context) // pass the parameters here for 0 allocation
|
||||
if context.Middleware != nil {
|
||||
// ok we found the correct route, serve it and exit entirely from here
|
||||
//ctx.Request.Header.SetUserAgentBytes(DefaultUserAgent)
|
||||
context.Do()
|
||||
return
|
||||
} else if mustRedirect && !context.Framework().Config.DisablePathCorrection { // && context.Method() == MethodConnect {
|
||||
reqPath := routePath
|
||||
pathLen := len(reqPath)
|
||||
|
||||
if pathLen > 1 {
|
||||
if reqPath[pathLen-1] == '/' {
|
||||
reqPath = reqPath[:pathLen-1] //remove the last /
|
||||
} else {
|
||||
//it has path prefix, it doesn't ends with / and it hasn't be found, then just add the slash
|
||||
reqPath = reqPath + "/"
|
||||
}
|
||||
|
||||
urlToRedirect := reqPath
|
||||
|
||||
statusForRedirect := iris.StatusMovedPermanently // StatusMovedPermanently, this document is obselte, clients caches this.
|
||||
if tree.method == iris.MethodPost ||
|
||||
tree.method == iris.MethodPut ||
|
||||
tree.method == iris.MethodDelete {
|
||||
statusForRedirect = iris.StatusTemporaryRedirect // To maintain POST data
|
||||
}
|
||||
|
||||
context.Redirect(urlToRedirect, statusForRedirect)
|
||||
// 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 tree.method == iris.MethodGet {
|
||||
note := "<a href=\"" + HTMLEscape(urlToRedirect) + "\">Moved Permanently</a>.\n"
|
||||
// ignore error
|
||||
context.WriteString(note)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
// not found
|
||||
break
|
||||
}
|
||||
// https://github.com/kataras/iris/issues/469
|
||||
if context.Framework().Config.FireMethodNotAllowed {
|
||||
for i := range mux.garden {
|
||||
tree := mux.garden[i]
|
||||
if !mux.methodEqual(context.Method(), tree.method) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
context.EmitError(iris.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
context.EmitError(iris.StatusNotFound)
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
//THESE ARE FROM Go Authors "html" package
|
||||
var htmlReplacer = strings.NewReplacer(
|
||||
"&", "&",
|
||||
"<", "<",
|
||||
">", ">",
|
||||
// """ is shorter than """.
|
||||
`"`, """,
|
||||
// "'" is shorter than "'" and apos was not in HTML until HTML5.
|
||||
"'", "'",
|
||||
)
|
||||
|
||||
// HTMLEscape returns a string which has no valid html code
|
||||
func HTMLEscape(s string) string {
|
||||
return htmlReplacer.Replace(s)
|
||||
}
|
||||
23
adaptors/httprouter/urlpath.go
Normal file
23
adaptors/httprouter/urlpath.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package httprouter
|
||||
|
||||
func joinPathArguments(args ...interface{}) []interface{} {
|
||||
arguments := args[0:]
|
||||
for i, v := range arguments {
|
||||
if arr, ok := v.([]string); ok {
|
||||
if len(arr) > 0 {
|
||||
interfaceArr := make([]interface{}, len(arr))
|
||||
for j, sv := range arr {
|
||||
interfaceArr[j] = sv
|
||||
}
|
||||
// replace the current slice
|
||||
// with the first string element (always as interface{})
|
||||
arguments[i] = interfaceArr[0]
|
||||
// append the rest of them to the slice itself
|
||||
// the range is not affected by these things in go,
|
||||
// so we are safe to do it.
|
||||
arguments = append(args, interfaceArr[1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
return arguments
|
||||
}
|
||||
Reference in New Issue
Block a user