mirror of
https://github.com/kataras/iris.git
synced 2026-05-12 17:13:50 +00:00
Add notes for the new lead maintainer of the open-source iris project and align with @get-ion/ion by @hiveminded
Former-commit-id: da4f38eb9034daa49446df3ee529423b98f9b331
This commit is contained in:
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
@@ -62,13 +58,6 @@ func (r *repository) getAll() []*Route {
|
||||
return r.routes
|
||||
}
|
||||
|
||||
// RoutesProvider should be implemented by
|
||||
// iteral which contains the registered routes.
|
||||
type RoutesProvider interface { // api builder
|
||||
GetRoutes() []*Route
|
||||
GetRoute(routeName string) *Route
|
||||
}
|
||||
|
||||
// APIBuilder the visible API for constructing the router
|
||||
// and child routers.
|
||||
type APIBuilder struct {
|
||||
@@ -81,6 +70,11 @@ type APIBuilder struct {
|
||||
// the api builder global route path reverser object
|
||||
// used by the view engine but it can be used anywhere.
|
||||
reverser *RoutePathReverser
|
||||
// the api builder global errors, can be filled by the Subdomain, WildcardSubdomain, Handle...
|
||||
// the list of possible errors that can be
|
||||
// collected on the build state to log
|
||||
// to the end-user.
|
||||
reporter *errors.Reporter
|
||||
|
||||
// the per-party middleware
|
||||
middleware context.Handlers
|
||||
@@ -101,6 +95,7 @@ func NewAPIBuilder() *APIBuilder {
|
||||
rb := &APIBuilder{
|
||||
macros: defaultMacros(),
|
||||
errorCodeHandlers: defaultErrorCodeHandlers(),
|
||||
reporter: errors.NewReporter(),
|
||||
relativePath: "/",
|
||||
routes: new(repository),
|
||||
}
|
||||
@@ -108,17 +103,22 @@ func NewAPIBuilder() *APIBuilder {
|
||||
return rb
|
||||
}
|
||||
|
||||
// GetReport returns an error may caused by party's methods.
|
||||
func (rb *APIBuilder) GetReport() error {
|
||||
return rb.reporter.Return()
|
||||
}
|
||||
|
||||
// Handle registers a route to the server's rb.
|
||||
// if empty method is passed then handler(s) are being registered to all methods, same as .Any.
|
||||
//
|
||||
// Returns a *Route and an error which will be filled if route wasn't registered successfully.
|
||||
func (rb *APIBuilder) Handle(method string, registeredPath string, handlers ...context.Handler) (*Route, error) {
|
||||
// Returns a *Route, app will throw any errors later on.
|
||||
func (rb *APIBuilder) Handle(method string, registeredPath string, handlers ...context.Handler) *Route {
|
||||
// if registeredPath[0] != '/' {
|
||||
// return nil, errors.New("path should start with slash and should not be empty")
|
||||
// }
|
||||
|
||||
if method == "" || method == "ALL" || method == "ANY" { // then use like it was .Any
|
||||
return nil, rb.Any(registeredPath, handlers...)
|
||||
return rb.Any(registeredPath, handlers...)[0]
|
||||
}
|
||||
|
||||
// no clean path yet because of subdomain indicator/separator which contains a dot.
|
||||
@@ -136,31 +136,33 @@ func (rb *APIBuilder) Handle(method string, registeredPath string, handlers ...c
|
||||
routeHandlers := joinHandlers(rb.middleware, handlers)
|
||||
|
||||
// here we separate the subdomain and relative path
|
||||
subdomain, path := exctractSubdomain(fullpath)
|
||||
subdomain, path := splitSubdomainAndPath(fullpath)
|
||||
if len(rb.doneHandlers) > 0 {
|
||||
routeHandlers = append(routeHandlers, rb.doneHandlers...) // register the done middleware, if any
|
||||
}
|
||||
|
||||
r, err := NewRoute(method, subdomain, path, routeHandlers, rb.macros)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if err != nil { // template path parser errors:
|
||||
rb.reporter.Add("%v -> %s:%s:%s", err, method, subdomain, path)
|
||||
return nil
|
||||
}
|
||||
|
||||
// global
|
||||
rb.routes.register(r)
|
||||
|
||||
// per -party
|
||||
// per -party, used for done handlers
|
||||
rb.apiRoutes = append(rb.apiRoutes, r)
|
||||
// should we remove the rb.apiRoutes on the .Party (new children party) ?, No, because the user maybe use this party later
|
||||
// should we add to the 'inheritance tree' the rb.apiRoutes, No, these are for this specific party only, because the user propably, will have unexpected behavior when using Use/Use, Done/DoneFunc
|
||||
return r, nil
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// Party is just a group joiner of routes which have the same prefix and share same middleware(s) also.
|
||||
// Party could also be named as 'Join' or 'Node' or 'Group' , Party chosen because it is fun.
|
||||
func (rb *APIBuilder) Party(relativePath string, handlers ...context.Handler) Party {
|
||||
parentPath := rb.relativePath
|
||||
dot := string(SubdomainIndicator[0])
|
||||
if len(parentPath) > 0 && parentPath[0] == '/' && strings.HasSuffix(relativePath, dot) { // if ends with . , example: admin., it's subdomain->
|
||||
dot := string(SubdomainPrefix[0])
|
||||
if len(parentPath) > 0 && parentPath[0] == '/' && strings.HasSuffix(relativePath, dot) {
|
||||
// if ends with . , i.e admin., it's subdomain->
|
||||
parentPath = parentPath[1:] // remove first slash
|
||||
}
|
||||
|
||||
@@ -169,6 +171,16 @@ func (rb *APIBuilder) Party(relativePath string, handlers ...context.Handler) Pa
|
||||
parentPath = parentPath[1:] // remove first slash if parent ended with / and new one started with /.
|
||||
}
|
||||
|
||||
// if it's subdomain then it has priority, i.e:
|
||||
// rb.relativePath == "admin."
|
||||
// relativePath == "panel."
|
||||
// then it should be panel.admin.
|
||||
// instead of admin.panel.
|
||||
if hasSubdomain(parentPath) && hasSubdomain(relativePath) {
|
||||
relativePath = relativePath + parentPath
|
||||
parentPath = ""
|
||||
}
|
||||
|
||||
fullpath := parentPath + relativePath
|
||||
// append the parent's +child's handlers
|
||||
middleware := joinHandlers(rb.middleware, handlers)
|
||||
@@ -179,16 +191,46 @@ func (rb *APIBuilder) Party(relativePath string, handlers ...context.Handler) Pa
|
||||
routes: rb.routes,
|
||||
errorCodeHandlers: rb.errorCodeHandlers,
|
||||
doneHandlers: rb.doneHandlers,
|
||||
reporter: rb.reporter,
|
||||
// per-party/children
|
||||
middleware: middleware,
|
||||
relativePath: fullpath,
|
||||
}
|
||||
}
|
||||
|
||||
// Subdomain returns a new party which is responsible to register routes to
|
||||
// this specific "subdomain".
|
||||
//
|
||||
// If called from a child party then the subdomain will be prepended to the path instead of appended.
|
||||
// So if app.Subdomain("admin.").Subdomain("panel.") then the result is: "panel.admin.".
|
||||
func (rb *APIBuilder) Subdomain(subdomain string, middleware ...context.Handler) Party {
|
||||
if rb.relativePath == SubdomainWildcardIndicator {
|
||||
// cannot concat wildcard subdomain with something else
|
||||
rb.reporter.Add("cannot concat parent wildcard subdomain with anything else -> %s , %s",
|
||||
rb.relativePath, subdomain)
|
||||
return rb
|
||||
}
|
||||
return rb.Party(subdomain, middleware...)
|
||||
}
|
||||
|
||||
// WildcardSubdomain returns a new party which is responsible to register routes to
|
||||
// a dynamic, wildcard(ed) subdomain. A dynamic subdomain is a subdomain which
|
||||
// can reply to any subdomain requests. Server will accept any subdomain
|
||||
// (if not static subdomain found) and it will search and execute the handlers of this party.
|
||||
func (rb *APIBuilder) WildcardSubdomain(middleware ...context.Handler) Party {
|
||||
if hasSubdomain(rb.relativePath) {
|
||||
// cannot concat static subdomain with a dynamic one, wildcard should be at the root level
|
||||
rb.reporter.Add("cannot concat static subdomain with a dynamic one. Dynamic subdomains should be at the root level -> %s",
|
||||
rb.relativePath)
|
||||
return rb
|
||||
}
|
||||
return rb.Subdomain(SubdomainWildcardIndicator, middleware...)
|
||||
}
|
||||
|
||||
// Macros returns the macro map which is responsible
|
||||
// to register custom macro functions for all routes.
|
||||
//
|
||||
// Learn more at: https://github.com/kataras/iris/tree/master/_examples/beginner/routing/dynamic-path
|
||||
// Learn more at: https://github.com/kataras/iris/tree/master/_examples/routing/dynamic-path
|
||||
func (rb *APIBuilder) Macros() *macro.Map {
|
||||
return rb.macros
|
||||
}
|
||||
@@ -209,8 +251,8 @@ func (rb *APIBuilder) GetRoute(routeName string) *Route {
|
||||
|
||||
// Use appends Handler(s) to the current Party's routes and child routes.
|
||||
// If the current Party is the root, then it registers the middleware to all child Parties' routes too.
|
||||
func (rb *APIBuilder) Use(handlers ...context.Handler) {
|
||||
rb.middleware = append(rb.middleware, handlers...)
|
||||
func (rb *APIBuilder) Use(middleware ...context.Handler) {
|
||||
rb.middleware = append(rb.middleware, middleware...)
|
||||
}
|
||||
|
||||
// Done appends to the very end, Handler(s) to the current Party's routes and child routes
|
||||
@@ -245,83 +287,84 @@ func (rb *APIBuilder) UseGlobal(handlers ...context.Handler) {
|
||||
// Offline(handleResultRouteInfo)
|
||||
//
|
||||
// Returns a *Route and an error which will be filled if route wasn't registered successfully.
|
||||
func (rb *APIBuilder) None(path string, handlers ...context.Handler) (*Route, error) {
|
||||
func (rb *APIBuilder) None(path string, handlers ...context.Handler) *Route {
|
||||
return rb.Handle(MethodNone, path, handlers...)
|
||||
}
|
||||
|
||||
// Get registers a route for the Get http method.
|
||||
//
|
||||
// Returns a *Route and an error which will be filled if route wasn't registered successfully.
|
||||
func (rb *APIBuilder) Get(path string, handlers ...context.Handler) (*Route, error) {
|
||||
func (rb *APIBuilder) Get(path string, handlers ...context.Handler) *Route {
|
||||
return rb.Handle(http.MethodGet, path, handlers...)
|
||||
}
|
||||
|
||||
// Post registers a route for the Post http method.
|
||||
//
|
||||
// Returns a *Route and an error which will be filled if route wasn't registered successfully.
|
||||
func (rb *APIBuilder) Post(path string, handlers ...context.Handler) (*Route, error) {
|
||||
func (rb *APIBuilder) Post(path string, handlers ...context.Handler) *Route {
|
||||
return rb.Handle(http.MethodPost, path, handlers...)
|
||||
}
|
||||
|
||||
// Put registers a route for the Put http method.
|
||||
//
|
||||
// Returns a *Route and an error which will be filled if route wasn't registered successfully.
|
||||
func (rb *APIBuilder) Put(path string, handlers ...context.Handler) (*Route, error) {
|
||||
func (rb *APIBuilder) Put(path string, handlers ...context.Handler) *Route {
|
||||
return rb.Handle(http.MethodPut, path, handlers...)
|
||||
}
|
||||
|
||||
// Delete registers a route for the Delete http method.
|
||||
//
|
||||
// Returns a *Route and an error which will be filled if route wasn't registered successfully.
|
||||
func (rb *APIBuilder) Delete(path string, handlers ...context.Handler) (*Route, error) {
|
||||
func (rb *APIBuilder) Delete(path string, handlers ...context.Handler) *Route {
|
||||
return rb.Handle(http.MethodDelete, path, handlers...)
|
||||
}
|
||||
|
||||
// Connect registers a route for the Connect http method.
|
||||
//
|
||||
// Returns a *Route and an error which will be filled if route wasn't registered successfully.
|
||||
func (rb *APIBuilder) Connect(path string, handlers ...context.Handler) (*Route, error) {
|
||||
func (rb *APIBuilder) Connect(path string, handlers ...context.Handler) *Route {
|
||||
return rb.Handle(http.MethodConnect, path, handlers...)
|
||||
}
|
||||
|
||||
// Head registers a route for the Head http method.
|
||||
//
|
||||
// Returns a *Route and an error which will be filled if route wasn't registered successfully.
|
||||
func (rb *APIBuilder) Head(path string, handlers ...context.Handler) (*Route, error) {
|
||||
func (rb *APIBuilder) Head(path string, handlers ...context.Handler) *Route {
|
||||
return rb.Handle(http.MethodHead, path, handlers...)
|
||||
}
|
||||
|
||||
// Options registers a route for the Options http method.
|
||||
//
|
||||
// Returns a *Route and an error which will be filled if route wasn't registered successfully.
|
||||
func (rb *APIBuilder) Options(path string, handlers ...context.Handler) (*Route, error) {
|
||||
func (rb *APIBuilder) Options(path string, handlers ...context.Handler) *Route {
|
||||
return rb.Handle(http.MethodOptions, path, handlers...)
|
||||
}
|
||||
|
||||
// Patch registers a route for the Patch http method.
|
||||
//
|
||||
// Returns a *Route and an error which will be filled if route wasn't registered successfully.
|
||||
func (rb *APIBuilder) Patch(path string, handlers ...context.Handler) (*Route, error) {
|
||||
func (rb *APIBuilder) Patch(path string, handlers ...context.Handler) *Route {
|
||||
return rb.Handle(http.MethodPatch, path, handlers...)
|
||||
}
|
||||
|
||||
// Trace registers a route for the Trace http method.
|
||||
//
|
||||
// Returns a *Route and an error which will be filled if route wasn't registered successfully.
|
||||
func (rb *APIBuilder) Trace(path string, handlers ...context.Handler) (*Route, error) {
|
||||
func (rb *APIBuilder) Trace(path string, handlers ...context.Handler) *Route {
|
||||
return rb.Handle(http.MethodTrace, path, handlers...)
|
||||
}
|
||||
|
||||
// Any registers a route for ALL of the http methods
|
||||
// (Get,Post,Put,Head,Patch,Options,Connect,Delete).
|
||||
func (rb *APIBuilder) Any(registeredPath string, handlers ...context.Handler) error {
|
||||
for _, k := range AllMethods {
|
||||
if _, err := rb.Handle(k, registeredPath, handlers...); err != nil {
|
||||
return err
|
||||
}
|
||||
func (rb *APIBuilder) Any(registeredPath string, handlers ...context.Handler) []*Route {
|
||||
routes := make([]*Route, len(AllMethods), len(AllMethods))
|
||||
|
||||
for i, k := range AllMethods {
|
||||
r := rb.Handle(k, registeredPath, handlers...)
|
||||
routes[i] = r
|
||||
}
|
||||
|
||||
return nil
|
||||
return routes
|
||||
}
|
||||
|
||||
// StaticCacheDuration expiration duration for INACTIVE file handlers, it's the only one global configuration
|
||||
@@ -341,10 +384,8 @@ const (
|
||||
varyHeaderKey = "Vary"
|
||||
)
|
||||
|
||||
func (rb *APIBuilder) registerResourceRoute(reqPath string, h context.Handler) (*Route, error) {
|
||||
if _, err := rb.Head(reqPath, h); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func (rb *APIBuilder) registerResourceRoute(reqPath string, h context.Handler) *Route {
|
||||
rb.Head(reqPath, h)
|
||||
return rb.Get(reqPath, h)
|
||||
}
|
||||
|
||||
@@ -365,7 +406,7 @@ func (rb *APIBuilder) registerResourceRoute(reqPath string, h context.Handler) (
|
||||
// mySubdomainFsServer.Get("/static", h)
|
||||
// ...
|
||||
//
|
||||
func (rb *APIBuilder) StaticHandler(systemPath string, showList bool, enableGzip bool, exceptRoutes ...*Route) context.Handler {
|
||||
func (rb *APIBuilder) StaticHandler(systemPath string, showList bool, enableGzip bool) context.Handler {
|
||||
// Note: this doesn't need to be here but we'll keep it for consistently
|
||||
return StaticHandler(systemPath, showList, enableGzip)
|
||||
}
|
||||
@@ -379,7 +420,7 @@ func (rb *APIBuilder) StaticHandler(systemPath string, showList bool, enableGzip
|
||||
// it uses gzip compression (compression on each request, no file cache).
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
func (rb *APIBuilder) StaticServe(systemPath string, requestPath ...string) (*Route, error) {
|
||||
func (rb *APIBuilder) StaticServe(systemPath string, requestPath ...string) *Route {
|
||||
var reqPath string
|
||||
|
||||
if len(requestPath) == 0 {
|
||||
@@ -411,12 +452,13 @@ func (rb *APIBuilder) StaticServe(systemPath string, requestPath ...string) (*Ro
|
||||
// that are ready to serve raw static bytes, memory cached.
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
func (rb *APIBuilder) StaticContent(reqPath string, cType string, content []byte) (*Route, error) {
|
||||
func (rb *APIBuilder) StaticContent(reqPath string, cType string, content []byte) *Route {
|
||||
modtime := time.Now()
|
||||
h := func(ctx context.Context) {
|
||||
if err := ctx.WriteWithExpiration(content, cType, modtime); err != nil {
|
||||
ctx.NotFound()
|
||||
// ctx.Application().Log("error while serving []byte via StaticContent: %s", err.Error())
|
||||
ctx.ContentType(cType)
|
||||
if _, err := ctx.WriteWithExpiration(content, modtime); err != nil {
|
||||
ctx.StatusCode(http.StatusInternalServerError)
|
||||
// ctx.Application().Logger().Infof("error while serving []byte via StaticContent: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,7 +486,7 @@ func (rb *APIBuilder) StaticEmbeddedHandler(vdir string, assetFn func(name strin
|
||||
// Returns the GET *Route.
|
||||
//
|
||||
// Examples: https://github.com/kataras/iris/tree/master/_examples/file-server
|
||||
func (rb *APIBuilder) StaticEmbedded(requestPath string, vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string) (*Route, error) {
|
||||
func (rb *APIBuilder) StaticEmbedded(requestPath string, vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string) *Route {
|
||||
fullpath := joinPath(rb.relativePath, requestPath)
|
||||
requestPath = joinPath(fullpath, WildcardParam("file"))
|
||||
|
||||
@@ -466,12 +508,13 @@ var errDirectoryFileNotFound = errors.New("Directory or file %s couldn't found.
|
||||
// Note that you have to call it on every favicon you have to serve automatically (desktop, mobile and so on).
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
func (rb *APIBuilder) Favicon(favPath string, requestPath ...string) (*Route, error) {
|
||||
func (rb *APIBuilder) Favicon(favPath string, requestPath ...string) *Route {
|
||||
favPath = Abs(favPath)
|
||||
|
||||
f, err := os.Open(favPath)
|
||||
if err != nil {
|
||||
return nil, errDirectoryFileNotFound.Format(favPath, err.Error())
|
||||
rb.reporter.AddErr(errDirectoryFileNotFound.Format(favPath, err.Error()))
|
||||
return nil
|
||||
}
|
||||
|
||||
// ignore error f.Close()
|
||||
@@ -496,8 +539,9 @@ func (rb *APIBuilder) Favicon(favPath string, requestPath ...string) (*Route, er
|
||||
// So we could panic but we don't,
|
||||
// we just interrupt with a message
|
||||
// to the (user-defined) logger.
|
||||
return nil, errDirectoryFileNotFound.
|
||||
Format(favPath, "favicon: couldn't read the data bytes for file: "+err.Error())
|
||||
rb.reporter.AddErr(errDirectoryFileNotFound.
|
||||
Format(favPath, "favicon: couldn't read the data bytes for file: "+err.Error()))
|
||||
return nil
|
||||
}
|
||||
modtime := ""
|
||||
h := func(ctx context.Context) {
|
||||
@@ -516,7 +560,7 @@ func (rb *APIBuilder) Favicon(favPath string, requestPath ...string) (*Route, er
|
||||
ctx.ResponseWriter().Header().Set(lastModifiedHeaderKey, modtime)
|
||||
ctx.StatusCode(http.StatusOK)
|
||||
if _, err := ctx.Write(cacheFav); err != nil {
|
||||
// ctx.Application().Log("error while trying to serve the favicon: %s", err.Error())
|
||||
// ctx.Application().Logger().Infof("error while trying to serve the favicon: %s", err.Error())
|
||||
ctx.StatusCode(http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
@@ -534,9 +578,8 @@ func (rb *APIBuilder) Favicon(favPath string, requestPath ...string) (*Route, er
|
||||
//
|
||||
// first parameter: the route path
|
||||
// second parameter: the system directory
|
||||
// third OPTIONAL parameter: the exception routes
|
||||
// (= give priority to these routes instead of the static handler)
|
||||
// for more options look rb.StaticHandler.
|
||||
//
|
||||
// for more options look router.StaticHandler.
|
||||
//
|
||||
// rb.StaticWeb("/static", "./static")
|
||||
//
|
||||
@@ -547,13 +590,13 @@ func (rb *APIBuilder) Favicon(favPath string, requestPath ...string) (*Route, er
|
||||
// StaticWeb calls the StaticHandler(systemPath, listingDirectories: false, gzip: false ).
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
func (rb *APIBuilder) StaticWeb(requestPath string, systemPath string, exceptRoutes ...*Route) (*Route, error) {
|
||||
func (rb *APIBuilder) StaticWeb(requestPath string, systemPath string) *Route {
|
||||
|
||||
paramName := "file"
|
||||
|
||||
fullpath := joinPath(rb.relativePath, requestPath)
|
||||
|
||||
h := StripPrefix(fullpath, rb.StaticHandler(systemPath, false, false, exceptRoutes...))
|
||||
h := StripPrefix(fullpath, rb.StaticHandler(systemPath, false, false))
|
||||
|
||||
handler := func(ctx context.Context) {
|
||||
h(ctx)
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ & Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
@@ -21,7 +18,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/core/errors"
|
||||
)
|
||||
|
||||
// StaticEmbeddedHandler returns a Handler which can serve
|
||||
@@ -86,8 +82,8 @@ func StaticEmbeddedHandler(vdir string, assetFn func(name string) ([]byte, error
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := ctx.WriteWithExpiration(buf, cType, modtime); err != nil {
|
||||
ctx.ContentType(cType)
|
||||
if _, err := ctx.WriteWithExpiration(buf, modtime); err != nil {
|
||||
ctx.StatusCode(http.StatusInternalServerError)
|
||||
ctx.StopExecution()
|
||||
}
|
||||
@@ -102,36 +98,6 @@ func StaticEmbeddedHandler(vdir string, assetFn func(name string) ([]byte, error
|
||||
return h
|
||||
}
|
||||
|
||||
// Prioritize is a middleware which executes a route against this path
|
||||
// when the request's Path has a prefix of the route's STATIC PART
|
||||
// is not executing ExecRoute to determinate if it's valid, for performance reasons
|
||||
// if this function is not enough for you and you want to test more than one parameterized path
|
||||
// then use the: if c := ExecRoute(r); c == nil { /* move to the next, the route is not valid */ }
|
||||
//
|
||||
// You can find the Route by iris.Default.Routes().Lookup("theRouteName")
|
||||
// you can set a route name as: myRoute := iris.Default.Get("/mypath", handler)("theRouteName")
|
||||
// that will set a name to the route and returns its iris.Route instance for further usage.
|
||||
//
|
||||
// if the route found then it executes that and don't continue to the next handler
|
||||
// if not found then continue to the next handler
|
||||
func Prioritize(r *Route) context.Handler {
|
||||
if r != nil {
|
||||
return func(ctx context.Context) {
|
||||
reqPath := ctx.Path()
|
||||
staticPath := ResolveStaticPath(reqPath)
|
||||
if strings.HasPrefix(reqPath, staticPath) {
|
||||
ctx.Exec(r.Method, reqPath) // execute the route based on this request path
|
||||
// we are done here.
|
||||
return
|
||||
}
|
||||
// execute the next handler if no prefix
|
||||
// here look, the only error we catch is the 404.
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
return func(ctx context.Context) { ctx.Next() }
|
||||
}
|
||||
|
||||
// StaticHandler returns a new Handler which is ready
|
||||
// to serve all kind of static files.
|
||||
//
|
||||
@@ -148,20 +114,18 @@ func Prioritize(r *Route) context.Handler {
|
||||
// app.Get("/static", h)
|
||||
// ...
|
||||
//
|
||||
func StaticHandler(systemPath string, showList bool, enableGzip bool, exceptRoutes ...*Route) context.Handler {
|
||||
func StaticHandler(systemPath string, showList bool, enableGzip bool) context.Handler {
|
||||
return NewStaticHandlerBuilder(systemPath).
|
||||
Listing(showList).
|
||||
Gzip(enableGzip).
|
||||
Except(exceptRoutes...).
|
||||
Build()
|
||||
}
|
||||
|
||||
// StaticHandlerBuilder is the web file system's Handler builder
|
||||
// use that or the iris.StaticHandler/StaticWeb methods
|
||||
// use that or the iris.StaticHandler/StaticWeb methods.
|
||||
type StaticHandlerBuilder interface {
|
||||
Gzip(enable bool) StaticHandlerBuilder
|
||||
Listing(listDirectoriesOnOff bool) StaticHandlerBuilder
|
||||
Except(r ...*Route) StaticHandlerBuilder
|
||||
Build() context.Handler
|
||||
}
|
||||
|
||||
@@ -179,7 +143,6 @@ type fsHandler struct {
|
||||
// these are init on the Build() call
|
||||
filesystem http.FileSystem
|
||||
once sync.Once
|
||||
exceptions []*Route
|
||||
handler context.Handler
|
||||
}
|
||||
|
||||
@@ -235,13 +198,6 @@ func (w *fsHandler) Listing(listDirectoriesOnOff bool) StaticHandlerBuilder {
|
||||
return w
|
||||
}
|
||||
|
||||
// Except add a route exception,
|
||||
// gives priority to that Route over the static handler.
|
||||
func (w *fsHandler) Except(r ...*Route) StaticHandlerBuilder {
|
||||
w.exceptions = append(w.exceptions, r...)
|
||||
return w
|
||||
}
|
||||
|
||||
type (
|
||||
noListFile struct {
|
||||
http.File
|
||||
@@ -308,7 +264,7 @@ func (w *fsHandler) Build() context.Handler {
|
||||
// headers[contentEncodingHeader] = nil
|
||||
// headers[contentLength] = nil
|
||||
}
|
||||
// ctx.Application().Log(errMsg)
|
||||
// ctx.Application().Logger().Infof(errMsg)
|
||||
ctx.StatusCode(prevStatusCode)
|
||||
return
|
||||
}
|
||||
@@ -317,21 +273,6 @@ func (w *fsHandler) Build() context.Handler {
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
if len(w.exceptions) > 0 {
|
||||
middleware := make(context.Handlers, len(w.exceptions)+1)
|
||||
for i := range w.exceptions {
|
||||
middleware[i] = Prioritize(w.exceptions[i])
|
||||
}
|
||||
middleware[len(w.exceptions)] = fileserver
|
||||
|
||||
w.handler = func(ctx context.Context) {
|
||||
ctxHandlers := ctx.Handlers()
|
||||
ctx.SetHandlers(append(middleware, ctxHandlers...))
|
||||
ctx.Handlers()[0](ctx)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
w.handler = fileserver
|
||||
|
||||
})
|
||||
@@ -357,7 +298,7 @@ func StripPrefix(prefix string, h context.Handler) context.Handler {
|
||||
// here we separate the path from the subdomain (if any), we care only for the path
|
||||
// fixes a bug when serving static files via a subdomain
|
||||
fixedPrefix := prefix
|
||||
if dotWSlashIdx := strings.Index(fixedPrefix, SubdomainIndicator); dotWSlashIdx > 0 {
|
||||
if dotWSlashIdx := strings.Index(fixedPrefix, SubdomainPrefix); dotWSlashIdx > 0 {
|
||||
fixedPrefix = fixedPrefix[dotWSlashIdx+1:]
|
||||
}
|
||||
fixedPrefix = toWebPath(fixedPrefix)
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
@@ -11,7 +7,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/core/nettools"
|
||||
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"github.com/kataras/iris/core/netutil"
|
||||
"github.com/kataras/iris/core/router/node"
|
||||
)
|
||||
|
||||
@@ -70,6 +68,13 @@ func NewDefaultHandler() RequestHandler {
|
||||
return h
|
||||
}
|
||||
|
||||
// RoutesProvider should be implemented by
|
||||
// iteral which contains the registered routes.
|
||||
type RoutesProvider interface { // api builder
|
||||
GetRoutes() []*Route
|
||||
GetRoute(routeName string) *Route
|
||||
}
|
||||
|
||||
func (h *routerHandler) Build(provider RoutesProvider) error {
|
||||
registeredRoutes := provider.GetRoutes()
|
||||
h.trees = h.trees[0:0] // reset, inneed when rebuilding.
|
||||
@@ -79,20 +84,24 @@ func (h *routerHandler) Build(provider RoutesProvider) error {
|
||||
return len(registeredRoutes[i].Subdomain) >= len(registeredRoutes[j].Subdomain)
|
||||
})
|
||||
|
||||
rp := errors.NewReporter()
|
||||
|
||||
for _, r := range registeredRoutes {
|
||||
if r.Subdomain != "" {
|
||||
h.hosts = true
|
||||
}
|
||||
|
||||
// the only "bad" with this is if the user made an error
|
||||
// on route, it will be stacked shown in this build state
|
||||
// and no in the lines of the user's action, they should read
|
||||
// the docs better. Or TODO: add a link here in order to help new users.
|
||||
if err := h.addRoute(r.Method, r.Subdomain, r.Path, r.Handlers); err != nil {
|
||||
return err
|
||||
// node errors:
|
||||
rp.Add("%v -> %s", err, r.String())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return rp.Return()
|
||||
}
|
||||
|
||||
func (h *routerHandler) HandleRequest(ctx context.Context) {
|
||||
@@ -134,14 +143,14 @@ func (h *routerHandler) HandleRequest(ctx context.Context) {
|
||||
|
||||
if h.hosts && t.Subdomain != "" {
|
||||
requestHost := ctx.Host()
|
||||
if nettools.IsLoopbackSubdomain(requestHost) {
|
||||
if netutil.IsLoopbackSubdomain(requestHost) {
|
||||
// this fixes a bug when listening on
|
||||
// 127.0.0.1:8080 for example
|
||||
// and have a wildcard subdomain and a route registered to root domain.
|
||||
continue // it's not a subdomain, it's something like 127.0.0.1 probably
|
||||
}
|
||||
// it's a dynamic wildcard subdomain, we have just to check if ctx.subdomain is not empty
|
||||
if t.Subdomain == DynamicSubdomainIndicator {
|
||||
if t.Subdomain == SubdomainWildcardIndicator {
|
||||
// mydomain.com -> invalid
|
||||
// localhost -> invalid
|
||||
// sub.mydomain.com -> valid
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
Copyright (c) 2017 Gerasimos Maropoulos, ΓΜ. 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.
|
||||
* Neither the name of Gerasimos Maropoulos nor the name of his
|
||||
username, kataras, may 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 THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS 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.
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package lexer
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package lexer
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package parser
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package parser
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package token
|
||||
|
||||
// Type is a specific type of int which describes the symbols.
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package macro
|
||||
|
||||
import (
|
||||
@@ -239,7 +235,7 @@ type Map struct {
|
||||
// NewMap returns a new macro Map with default
|
||||
// type evaluators.
|
||||
//
|
||||
// Learn more at: https://github.com/kataras/iris/tree/master/_examples/beginner/routing/dynamic-path
|
||||
// Learn more at: https://github.com/kataras/iris/tree/master/_examples/routing/dynamic-path
|
||||
func NewMap() *Map {
|
||||
return &Map{
|
||||
// it allows everything, so no need for a regexp here.
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package macro
|
||||
|
||||
import (
|
||||
@@ -179,7 +175,7 @@ func TestPathEvaluatorRaw(t *testing.T) {
|
||||
// }
|
||||
// })
|
||||
|
||||
// p, err := Parse("/user/@kataras")
|
||||
// p, err := Parse("/user/@iris")
|
||||
// if err != nil {
|
||||
// t.Fatalf(err)
|
||||
// }
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package macro
|
||||
|
||||
import (
|
||||
|
||||
@@ -20,8 +20,8 @@ type node struct {
|
||||
root bool
|
||||
}
|
||||
|
||||
// ErrDublicate returned from MakeChild when more than one routes have the same registered path.
|
||||
var ErrDublicate = errors.New("more than one routes have the same registered path")
|
||||
// ErrDublicate returnned from `Add` when two or more routes have the same registered path.
|
||||
var ErrDublicate = errors.New("two or more routes have the same registered path")
|
||||
|
||||
// Add adds a node to the tree, returns an ErrDublicate error on failure.
|
||||
func (nodes *Nodes) Add(path string, handlers context.Handlers) error {
|
||||
@@ -48,12 +48,22 @@ func (nodes *Nodes) Add(path string, handlers context.Handlers) error {
|
||||
paramEnd -= paramEnd - paramStart
|
||||
}
|
||||
|
||||
for _, idx := range paramsPos(path) {
|
||||
var p []int
|
||||
for i := 0; i < len(path); i++ {
|
||||
idx := strings.IndexByte(path[i:], ':')
|
||||
if idx == -1 {
|
||||
break
|
||||
}
|
||||
p = append(p, idx+i)
|
||||
i = idx + i
|
||||
}
|
||||
|
||||
if err := nodes.add(path[:idx], nil, nil, true); err != nil { // take the static path to its own node
|
||||
for _, idx := range p {
|
||||
|
||||
if err := nodes.add(path[:idx], nil, nil, true); err != nil {
|
||||
return err
|
||||
}
|
||||
// create a second, empty, dynamic parameter node without the last slash
|
||||
|
||||
if nidx := idx + 1; len(path) > nidx {
|
||||
if err := nodes.add(path[:nidx], nil, nil, true); err != nil {
|
||||
return err
|
||||
@@ -61,13 +71,12 @@ func (nodes *Nodes) Add(path string, handlers context.Handlers) error {
|
||||
}
|
||||
}
|
||||
|
||||
// last, create the node filled by the full path, parameters and its handlers
|
||||
if err := nodes.add(path, params, handlers, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// sort by static path, remember, they were already sorted by subdomains too.
|
||||
nodes.Sort()
|
||||
// prioritize by static path remember, they were already sorted by subdomains too.
|
||||
nodes.prioritize()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -153,7 +162,7 @@ loop:
|
||||
return nil
|
||||
}
|
||||
if len(n.handlers) > 0 { // n.handlers already setted
|
||||
return ErrDublicate.Append("for: %s", n.s)
|
||||
return ErrDublicate
|
||||
}
|
||||
n.paramNames = paramNames
|
||||
n.handlers = handlers
|
||||
@@ -216,7 +225,7 @@ func (nodes Nodes) findChild(path string, params []string) (*node, []string) {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(path) == len(n.s) { // Node matched until the end of path.
|
||||
if len(path) == len(n.s) {
|
||||
if len(n.handlers) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -226,7 +235,7 @@ func (nodes Nodes) findChild(path string, params []string) (*node, []string) {
|
||||
child, childParamNames := n.children.findChild(path[len(n.s):], params)
|
||||
|
||||
if child == nil || len(child.handlers) == 0 {
|
||||
// is wildcard and it is not root neither has children
|
||||
|
||||
if n.s[len(n.s)-1] == '/' && !(n.root && (n.s == "/" || len(n.children) > 0)) {
|
||||
if len(n.handlers) == 0 {
|
||||
return nil, nil
|
||||
@@ -254,8 +263,8 @@ func (n *node) isDynamic() bool {
|
||||
return n.s == ":"
|
||||
}
|
||||
|
||||
// Sort sets the static paths first.
|
||||
func (nodes Nodes) Sort() {
|
||||
// prioritize sets the static paths first.
|
||||
func (nodes Nodes) prioritize() {
|
||||
|
||||
sort.Slice(nodes, func(i, j int) bool {
|
||||
if nodes[i].isDynamic() {
|
||||
@@ -268,18 +277,6 @@ func (nodes Nodes) Sort() {
|
||||
})
|
||||
|
||||
for _, n := range nodes {
|
||||
n.children.Sort()
|
||||
n.children.prioritize()
|
||||
}
|
||||
}
|
||||
|
||||
func paramsPos(s string) (pos []int) {
|
||||
for i := 0; i < len(s); i++ {
|
||||
p := strings.IndexByte(s[i:], ':')
|
||||
if p == -1 {
|
||||
break
|
||||
}
|
||||
pos = append(pos, p+i)
|
||||
i = p + i
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
@@ -15,11 +11,17 @@ import (
|
||||
// Look the "APIBuilder" for its implementation.
|
||||
type Party interface {
|
||||
// Party creates and returns a new child Party with the following features.
|
||||
Party(relativePath string, handlers ...context.Handler) Party
|
||||
Party(relativePath string, middleware ...context.Handler) Party
|
||||
// Subdomain returns a new party which is responsible to register routes to
|
||||
// this specific "subdomain".
|
||||
//
|
||||
// If called from a child party then the subdomain will be prepended to the path instead of appended.
|
||||
// So if app.Subdomain("admin.").Subdomain("panel.") then the result is: "panel.admin.".
|
||||
Subdomain(subdomain string, middleware ...context.Handler) Party
|
||||
|
||||
// Use appends Handler(s) to the current Party's routes and child routes.
|
||||
// If the current Party is the root, then it registers the middleware to all child Parties' routes too.
|
||||
Use(handlers ...context.Handler)
|
||||
Use(middleware ...context.Handler)
|
||||
|
||||
// Done appends to the very end, Handler(s) to the current Party's routes and child routes
|
||||
// The difference from .Use is that this/or these Handler(s) are being always running last.
|
||||
@@ -29,7 +31,7 @@ type Party interface {
|
||||
// if empty method is passed then handler(s) are being registered to all methods, same as .Any.
|
||||
//
|
||||
// Returns the read-only route information.
|
||||
Handle(method string, registeredPath string, handlers ...context.Handler) (*Route, error)
|
||||
Handle(method string, registeredPath string, handlers ...context.Handler) *Route
|
||||
|
||||
// None registers an "offline" route
|
||||
// see context.ExecRoute(routeName) and
|
||||
@@ -37,47 +39,47 @@ type Party interface {
|
||||
// Offline(handleResultregistry.*Route)
|
||||
//
|
||||
// Returns the read-only route information.
|
||||
None(path string, handlers ...context.Handler) (*Route, error)
|
||||
None(path string, handlers ...context.Handler) *Route
|
||||
|
||||
// Get registers a route for the Get http method.
|
||||
//
|
||||
// Returns the read-only route information.
|
||||
Get(path string, handlers ...context.Handler) (*Route, error)
|
||||
Get(path string, handlers ...context.Handler) *Route
|
||||
// Post registers a route for the Post http method.
|
||||
//
|
||||
// Returns the read-only route information.
|
||||
Post(path string, handlers ...context.Handler) (*Route, error)
|
||||
Post(path string, handlers ...context.Handler) *Route
|
||||
// Put registers a route for the Put http method.
|
||||
//
|
||||
// Returns the read-only route information.
|
||||
Put(path string, handlers ...context.Handler) (*Route, error)
|
||||
Put(path string, handlers ...context.Handler) *Route
|
||||
// Delete registers a route for the Delete http method.
|
||||
//
|
||||
// Returns the read-only route information.
|
||||
Delete(path string, handlers ...context.Handler) (*Route, error)
|
||||
Delete(path string, handlers ...context.Handler) *Route
|
||||
// Connect registers a route for the Connect http method.
|
||||
//
|
||||
// Returns the read-only route information.
|
||||
Connect(path string, handlers ...context.Handler) (*Route, error)
|
||||
Connect(path string, handlers ...context.Handler) *Route
|
||||
// Head registers a route for the Head http method.
|
||||
//
|
||||
// Returns the read-only route information.
|
||||
Head(path string, handlers ...context.Handler) (*Route, error)
|
||||
Head(path string, handlers ...context.Handler) *Route
|
||||
// Options registers a route for the Options http method.
|
||||
//
|
||||
// Returns the read-only route information.
|
||||
Options(path string, handlers ...context.Handler) (*Route, error)
|
||||
Options(path string, handlers ...context.Handler) *Route
|
||||
// Patch registers a route for the Patch http method.
|
||||
//
|
||||
// Returns the read-only route information.
|
||||
Patch(path string, handlers ...context.Handler) (*Route, error)
|
||||
Patch(path string, handlers ...context.Handler) *Route
|
||||
// Trace registers a route for the Trace http method.
|
||||
//
|
||||
// Returns the read-only route information.
|
||||
Trace(path string, handlers ...context.Handler) (*Route, error)
|
||||
Trace(path string, handlers ...context.Handler) *Route
|
||||
// Any registers a route for ALL of the http methods
|
||||
// (Get,Post,Put,Head,Patch,Options,Connect,Delete).
|
||||
Any(registeredPath string, handlers ...context.Handler) error
|
||||
Any(registeredPath string, handlers ...context.Handler) []*Route
|
||||
|
||||
// StaticHandler returns a new Handler which is ready
|
||||
// to serve all kind of static files.
|
||||
@@ -96,7 +98,7 @@ type Party interface {
|
||||
// mySubdomainFsServer.Get("/static", h)
|
||||
// ...
|
||||
//
|
||||
StaticHandler(systemPath string, showList bool, enableGzip bool, exceptRoutes ...*Route) context.Handler
|
||||
StaticHandler(systemPath string, showList bool, enableGzip bool) context.Handler
|
||||
|
||||
// StaticServe serves a directory as web resource
|
||||
// it's the simpliest form of the Static* functions
|
||||
@@ -107,12 +109,12 @@ type Party interface {
|
||||
// it uses gzip compression (compression on each request, no file cache).
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
StaticServe(systemPath string, requestPath ...string) (*Route, error)
|
||||
StaticServe(systemPath string, requestPath ...string) *Route
|
||||
// StaticContent registers a GET and HEAD method routes to the requestPath
|
||||
// that are ready to serve raw static bytes, memory cached.
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
StaticContent(requestPath string, cType string, content []byte) (*Route, error)
|
||||
StaticContent(requestPath string, cType string, content []byte) *Route
|
||||
|
||||
// StaticEmbedded used when files are distributed inside the app executable, using go-bindata mostly
|
||||
// First parameter is the request path, the path which the files in the vdir will be served to, for example "/static"
|
||||
@@ -122,8 +124,8 @@ type Party interface {
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/serve-embedded-files
|
||||
StaticEmbedded(requestPath string, vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string) (*Route, error)
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/embedding-files-into-app
|
||||
StaticEmbedded(requestPath string, vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string) *Route
|
||||
|
||||
// Favicon serves static favicon
|
||||
// accepts 2 parameters, second is optional
|
||||
@@ -136,14 +138,13 @@ type Party interface {
|
||||
// Note that you have to call it on every favicon you have to serve automatically (desktop, mobile and so on).
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
Favicon(favPath string, requestPath ...string) (*Route, error)
|
||||
Favicon(favPath string, requestPath ...string) *Route
|
||||
// StaticWeb returns a handler that serves HTTP requests
|
||||
// with the contents of the file system rooted at directory.
|
||||
//
|
||||
// first parameter: the route path
|
||||
// second parameter: the system directory
|
||||
// third OPTIONAL parameter: the exception routes
|
||||
// (= give priority to these routes instead of the static handler)
|
||||
//
|
||||
// for more options look router.StaticHandler.
|
||||
//
|
||||
// router.StaticWeb("/static", "./static")
|
||||
@@ -155,7 +156,7 @@ type Party interface {
|
||||
// StaticWeb calls the StaticHandler(systemPath, listingDirectories: false, gzip: false ).
|
||||
//
|
||||
// Returns the GET *Route.
|
||||
StaticWeb(requestPath string, systemPath string, exceptRoutes ...*Route) (*Route, error)
|
||||
StaticWeb(requestPath string, systemPath string) *Route
|
||||
|
||||
// Layout oerrides the parent template layout with a more specific layout for this Party
|
||||
// returns this Party, to continue as normal
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
@@ -10,8 +6,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/esemplastic/unis"
|
||||
"github.com/kataras/iris/core/nettools"
|
||||
"github.com/kataras/iris/core/netutil"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -22,18 +17,6 @@ const (
|
||||
WildcardParamStart = "*"
|
||||
)
|
||||
|
||||
// ResolveStaticPath receives a (dynamic) path and tries to return its static path part.
|
||||
func ResolveStaticPath(original string) string {
|
||||
i := strings.Index(original, ParamStart)
|
||||
v := strings.Index(original, WildcardParamStart)
|
||||
|
||||
return unis.NewChain(
|
||||
unis.NewConditional(
|
||||
unis.NewRangeEnd(i),
|
||||
unis.NewRangeEnd(v)),
|
||||
cleanPath).Process(original)
|
||||
}
|
||||
|
||||
// Param receives a parameter name prefixed with the ParamStart symbol.
|
||||
func Param(name string) string {
|
||||
return prefix(name, ParamStart)
|
||||
@@ -47,8 +30,19 @@ func WildcardParam(name string) string {
|
||||
return prefix(name, WildcardParamStart)
|
||||
}
|
||||
|
||||
func prefix(str string, prefix string) string {
|
||||
return unis.NewExclusivePrepender(prefix).Process(str)
|
||||
func prefix(s string, prefix string) string {
|
||||
if !strings.HasPrefix(s, prefix) {
|
||||
return prefix + s
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func suffix(s string, suffix string) string {
|
||||
if !strings.HasSuffix(s, suffix) {
|
||||
return s + suffix
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func joinPath(path1 string, path2 string) string {
|
||||
@@ -65,59 +59,78 @@ func joinPath(path1 string, path2 string) string {
|
||||
// that is, replace "/.." by "/" at the beginning of a path.
|
||||
//
|
||||
// The returned path ends in a slash only if it is the root "/".
|
||||
var cleanPath = unis.NewChain(
|
||||
|
||||
unis.NewSuffixRemover("/"),
|
||||
unis.NewTargetedJoiner(0, '/'),
|
||||
unis.ProcessorFunc(path.Clean),
|
||||
unis.NewReplacer(map[string]string{
|
||||
"//": "/",
|
||||
"\\": "/",
|
||||
}),
|
||||
unis.ProcessorFunc(func(s string) string {
|
||||
if s == "" || s == "." {
|
||||
return "/"
|
||||
}
|
||||
return s
|
||||
}),
|
||||
)
|
||||
|
||||
const (
|
||||
// DynamicSubdomainIndicator where a registered path starts with '*.' then it contains a dynamic subdomain, if subdomain == "*." then its dynamic
|
||||
//
|
||||
// used internally by URLPath and the router serve.
|
||||
DynamicSubdomainIndicator = "*."
|
||||
// SubdomainIndicator where './' exists in a registered path then it contains subdomain
|
||||
//
|
||||
// used on router builder
|
||||
SubdomainIndicator = "./"
|
||||
)
|
||||
|
||||
func newSubdomainDivider(sep string) unis.DividerFunc {
|
||||
// invert if indiciator not found
|
||||
// because we need the first parameter to be the subdomain
|
||||
// even if empty, but the second parameter
|
||||
// should be the path, in order to normalize it
|
||||
// (because of the reason of subdomains shouldn't be normalized as path)
|
||||
subdomainDevider := unis.NewInvertOnFailureDivider(unis.NewDivider(sep))
|
||||
return func(fullpath string) (string, string) {
|
||||
subdomain, path := subdomainDevider.Divide(fullpath)
|
||||
if len(path) > 1 {
|
||||
if path[0] == '/' && path[1] == '/' {
|
||||
path = path[1:]
|
||||
}
|
||||
}
|
||||
|
||||
return subdomain, path //cleanPath(path)
|
||||
func cleanPath(s string) string {
|
||||
if s == "" || s == "." {
|
||||
return "/"
|
||||
}
|
||||
|
||||
// remove suffix "/"
|
||||
if lidx := len(s) - 1; s[lidx] == '/' {
|
||||
s = s[:lidx]
|
||||
}
|
||||
|
||||
// prefix with "/"
|
||||
s = prefix(s, "/")
|
||||
|
||||
// remove the os specific dir sep
|
||||
s = strings.Replace(s, "\\", "/", -1)
|
||||
|
||||
// use std path to clean the path
|
||||
s = path.Clean(s)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// ExctractSubdomain checks if the path has subdomain and if it's
|
||||
const (
|
||||
// SubdomainWildcardIndicator where a registered path starts with '*.'.
|
||||
// if subdomain == "*." then its wildcard.
|
||||
//
|
||||
// used internally by router and api builder.
|
||||
SubdomainWildcardIndicator = "*."
|
||||
|
||||
// SubdomainWildcardPrefix where a registered path starts with "*./",
|
||||
// then this route should accept any subdomain.
|
||||
SubdomainWildcardPrefix = SubdomainWildcardIndicator + "/"
|
||||
// SubdomainPrefix where './' exists in a registered path then it contains subdomain
|
||||
//
|
||||
// used on api builder.
|
||||
SubdomainPrefix = "./" // i.e subdomain./ -> Subdomain: subdomain. Path: /
|
||||
)
|
||||
|
||||
func hasSubdomain(s string) bool {
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
// subdomain./path
|
||||
// .*/path
|
||||
//
|
||||
// remember: path always starts with "/"
|
||||
// if not start with "/" then it should be something else,
|
||||
// we don't assume anything else but subdomain.
|
||||
slashIdx := strings.IndexByte(s, '/')
|
||||
return slashIdx > 0 || s[0] == SubdomainPrefix[0] || (len(s) >= 2 && s[0:2] == SubdomainWildcardIndicator)
|
||||
}
|
||||
|
||||
// splitSubdomainAndPath checks if the path has subdomain and if it's
|
||||
// it splits the subdomain and path and returns them, otherwise it returns
|
||||
// an empty subdomain and the given path.
|
||||
// an empty subdomain and the clean path.
|
||||
//
|
||||
// First return value is the subdomain, second is the path.
|
||||
var exctractSubdomain = newSubdomainDivider(SubdomainIndicator)
|
||||
func splitSubdomainAndPath(fullUnparsedPath string) (subdomain string, path string) {
|
||||
s := fullUnparsedPath
|
||||
if s == "" || s == "/" {
|
||||
return "", "/"
|
||||
}
|
||||
|
||||
slashIdx := strings.IndexByte(s, '/')
|
||||
if slashIdx == 0 {
|
||||
// no subdomain
|
||||
return "", cleanPath(s)
|
||||
}
|
||||
|
||||
return s[0:slashIdx], cleanPath(s[slashIdx:]) // return subdomain without slash, path with slash
|
||||
}
|
||||
|
||||
// RoutePathReverserOption option signature for the RoutePathReverser.
|
||||
type RoutePathReverserOption func(*RoutePathReverser)
|
||||
@@ -142,7 +155,7 @@ func WithHost(host string) RoutePathReverserOption {
|
||||
return func(ps *RoutePathReverser) {
|
||||
ps.vhost = host
|
||||
if ps.vscheme == "" {
|
||||
ps.vscheme = nettools.ResolveScheme(host)
|
||||
ps.vscheme = netutil.ResolveSchemeFromVHost(host)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,8 +165,8 @@ func WithHost(host string) RoutePathReverserOption {
|
||||
// a scheme and a host to be used in the URL function.
|
||||
func WithServer(srv *http.Server) RoutePathReverserOption {
|
||||
return func(ps *RoutePathReverser) {
|
||||
ps.vhost = nettools.ResolveVHost(srv.Addr)
|
||||
ps.vscheme = nettools.ResolveSchemeFromServer(srv)
|
||||
ps.vhost = netutil.ResolveVHost(srv.Addr)
|
||||
ps.vscheme = netutil.ResolveSchemeFromServer(srv)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,7 +235,7 @@ func toStringSlice(args []interface{}) []string {
|
||||
// Remove the URL for now, it complicates things for the whole framework without a specific benefits,
|
||||
// developers can just concat the subdomain, (host can be auto-retrieve by browser using the Path).
|
||||
|
||||
// URL same as Path but returns the full uri, i.e https://mysubdomain.mydomain.com/hello/kataras
|
||||
// URL same as Path but returns the full uri, i.e https://mysubdomain.mydomain.com/hello/iris
|
||||
func (ps *RoutePathReverser) URL(routeName string, paramValues ...interface{}) (url string) {
|
||||
if ps.vhost == "" || ps.vscheme == "" {
|
||||
return "not supported"
|
||||
@@ -243,7 +256,7 @@ func (ps *RoutePathReverser) URL(routeName string, paramValues ...interface{}) (
|
||||
scheme := ps.vscheme
|
||||
// if it's dynamic subdomain then the first argument is the subdomain part
|
||||
// for this part we are responsible not the custom routers
|
||||
if r.Subdomain == DynamicSubdomainIndicator {
|
||||
if r.Subdomain == SubdomainWildcardIndicator {
|
||||
subdomain := args[0]
|
||||
host = subdomain + "." + host
|
||||
args = args[1:] // remove the subdomain part for the arguments,
|
||||
|
||||
29
core/router/path_test.go
Normal file
29
core/router/path_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSplitSubdomainAndPath(t *testing.T) {
|
||||
tests := []struct {
|
||||
original string
|
||||
subdomain string
|
||||
path string
|
||||
}{
|
||||
{"admin./users/42", "admin.", "/users/42"},
|
||||
{"//api/users\\42", "", "/api/users/42"},
|
||||
{"admin./users/\\42", "admin.", "/users/42"},
|
||||
{"*./users/\\42", "*.", "/users/42"},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
subdomain, path := splitSubdomainAndPath(tt.original)
|
||||
|
||||
if expected, got := tt.subdomain, subdomain; expected != got {
|
||||
t.Fatalf("[%d] - expected subdomain '%s' but got '%s'", i, expected, got)
|
||||
}
|
||||
if expected, got := tt.path, path; expected != got {
|
||||
t.Fatalf("[%d] - expected path '%s' but got '%s'", i, expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
@@ -61,6 +57,12 @@ func NewRoute(method, subdomain, unparsedPath string,
|
||||
return route, nil
|
||||
}
|
||||
|
||||
// String returns the form of METHOD, SUBDOMAIN, TMPL PATH
|
||||
func (r Route) String() string {
|
||||
return fmt.Sprintf("%s %s%s",
|
||||
r.Method, r.Subdomain, r.Tmpl().Src)
|
||||
}
|
||||
|
||||
// Tmpl returns the path template, i
|
||||
// it contains the parsed template
|
||||
// for the route's path.
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
|
||||
@@ -57,7 +57,7 @@ func (s *SPABuilder) isAsset(reqPath string) bool {
|
||||
// It should be passed to the router's `WrapRouter`:
|
||||
// https://godoc.org/github.com/kataras/iris/core/router#Router.WrapRouter
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/beginner/file-server/single-page-application-builder
|
||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/single-page-application-builder
|
||||
func (s *SPABuilder) BuildWrapper(cPool *context.Pool) WrapperFunc {
|
||||
|
||||
fileServer := s.AssetHandler
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
@@ -81,7 +77,7 @@ func defaultErrorCodeHandlers() *ErrorCodeHandlers {
|
||||
func statusText(statusCode int) context.Handler {
|
||||
return func(ctx context.Context) {
|
||||
if _, err := ctx.WriteString(http.StatusText(statusCode)); err != nil {
|
||||
// ctx.Application().Log("(status code: %d) %s",
|
||||
// ctx.Application().Logger().Infof("(status code: %d) %s",
|
||||
// err.Error(), statusCode)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user