mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 18:51:03 +01:00
82afcc5aa6
https://github.com/kataras/go-websocket/issues/27 Former-commit-id: 0b7e52e0a61150a8bba973ef653986d8b3ddd26b
726 lines
19 KiB
Go
726 lines
19 KiB
Go
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(`
|
|
httprouter: '%s' in new path '%s'
|
|
conflicts with existing wildcarded route with path: '%s'
|
|
in existing prefix of'%s' `)
|
|
|
|
errMuxEntryMiddlewareAlreadyExists = errors.New(`
|
|
httprouter: Middleware were already registered for the path: '%s'`)
|
|
|
|
errMuxEntryInvalidWildcard = errors.New(`
|
|
httprouter: More than one wildcard found in the path part: '%s' in route's path: '%s'`)
|
|
|
|
errMuxEntryConflictsExistingWildcard = errors.New(`
|
|
httprouter: Wildcard for route path: '%s' conflicts with existing children in route path: '%s'`)
|
|
|
|
errMuxEntryWildcardUnnamed = errors.New(`
|
|
httprouter: Unnamed wildcard found in path: '%s'`)
|
|
|
|
errMuxEntryWildcardInvalidPlace = errors.New(`
|
|
httprouter: Wildcard is only allowed at the end of the path, in the route path: '%s'`)
|
|
|
|
errMuxEntryWildcardConflictsMiddleware = errors.New(`
|
|
httprouter: Wildcard conflicts with existing middleware for the route path: '%s'`)
|
|
|
|
errMuxEntryWildcardMissingSlash = errors.New(`
|
|
httprouter: 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, 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)
|
|
}
|