Publish the new version :airplane: | Look description please!
# FAQ
### Looking for free support?
http://support.iris-go.com
https://kataras.rocket.chat/channel/iris
### Looking for previous versions?
https://github.com/kataras/iris#version
### Should I upgrade my Iris?
Developers are not forced to upgrade if they don't really need it. Upgrade whenever you feel ready.
> Iris uses the [vendor directory](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) feature, so you get truly reproducible builds, as this method guards against upstream renames and deletes.
**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris`.
For further installation support, please click [here](http://support.iris-go.com/d/16-how-to-install-iris-web-framework).
### About our new home page
http://iris-go.com
Thanks to [Santosh Anand](https://github.com/santoshanand) the http://iris-go.com has been upgraded and it's really awesome!
[Santosh](https://github.com/santoshanand) is a freelancer, he has a great knowledge of nodejs and express js, Android, iOS, React Native, Vue.js etc, if you need a developer to find or create a solution for your problem or task, please contact with him.
The amount of the next two or three donations you'll send they will be immediately transferred to his own account balance, so be generous please!
Read more at https://github.com/kataras/iris/blob/master/HISTORY.md
Former-commit-id: eec2d71bbe011d6b48d2526eb25919e36e5ad94e
2017-06-03 22:22:52 +02:00
|
|
|
// 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 (
|
|
|
|
"net/http"
|
|
|
|
"path"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/esemplastic/unis"
|
|
|
|
"github.com/kataras/iris/core/nettools"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// ParamStart the character in string representation where the underline router starts its dynamic named parameter.
|
|
|
|
ParamStart = ":"
|
|
|
|
// WildcardParamStart the character in string representation where the underline router starts its dynamic wildcard
|
|
|
|
// path parameter.
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
// WildcardParam receives a parameter name prefixed with the WildcardParamStart symbol.
|
|
|
|
func WildcardParam(name string) string {
|
|
|
|
if len(name) == 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return prefix(name, WildcardParamStart)
|
|
|
|
}
|
|
|
|
|
|
|
|
func prefix(str string, prefix string) string {
|
|
|
|
return unis.NewExclusivePrepender(prefix).Process(str)
|
|
|
|
}
|
|
|
|
|
|
|
|
func joinPath(path1 string, path2 string) string {
|
|
|
|
return path.Join(path1, path2)
|
|
|
|
}
|
|
|
|
|
|
|
|
// cleanPath applies the following rules
|
|
|
|
// iteratively until no further processing can be done:
|
|
|
|
//
|
|
|
|
// 1. Replace multiple slashes with a single slash.
|
|
|
|
// 3. Eliminate each inner .. path name element (the parent directory)
|
|
|
|
// along with the non-.. element that precedes it.
|
|
|
|
// 4. Eliminate .. elements that begin a rooted path:
|
|
|
|
// 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)
|
2017-06-12 17:23:35 +02:00
|
|
|
if len(path) > 1 {
|
|
|
|
if path[0] == '/' && path[1] == '/' {
|
|
|
|
path = path[1:]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Publish the new version :airplane: | Look description please!
# FAQ
### Looking for free support?
http://support.iris-go.com
https://kataras.rocket.chat/channel/iris
### Looking for previous versions?
https://github.com/kataras/iris#version
### Should I upgrade my Iris?
Developers are not forced to upgrade if they don't really need it. Upgrade whenever you feel ready.
> Iris uses the [vendor directory](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) feature, so you get truly reproducible builds, as this method guards against upstream renames and deletes.
**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris`.
For further installation support, please click [here](http://support.iris-go.com/d/16-how-to-install-iris-web-framework).
### About our new home page
http://iris-go.com
Thanks to [Santosh Anand](https://github.com/santoshanand) the http://iris-go.com has been upgraded and it's really awesome!
[Santosh](https://github.com/santoshanand) is a freelancer, he has a great knowledge of nodejs and express js, Android, iOS, React Native, Vue.js etc, if you need a developer to find or create a solution for your problem or task, please contact with him.
The amount of the next two or three donations you'll send they will be immediately transferred to his own account balance, so be generous please!
Read more at https://github.com/kataras/iris/blob/master/HISTORY.md
Former-commit-id: eec2d71bbe011d6b48d2526eb25919e36e5ad94e
2017-06-03 22:22:52 +02:00
|
|
|
return subdomain, path //cleanPath(path)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExctractSubdomain 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.
|
|
|
|
//
|
|
|
|
// First return value is the subdomain, second is the path.
|
|
|
|
var exctractSubdomain = newSubdomainDivider(SubdomainIndicator)
|
|
|
|
|
|
|
|
// RoutePathReverserOption option signature for the RoutePathReverser.
|
|
|
|
type RoutePathReverserOption func(*RoutePathReverser)
|
|
|
|
|
|
|
|
// WithScheme is an option for the RoutepathReverser,
|
|
|
|
// it sets the optional field "vscheme",
|
|
|
|
// v for virtual.
|
|
|
|
// if vscheme is empty then it will try to resolve it from
|
|
|
|
// the RoutePathReverser's vhost field.
|
|
|
|
//
|
|
|
|
// See WithHost or WithServer to enable the URL feature.
|
|
|
|
func WithScheme(scheme string) RoutePathReverserOption {
|
|
|
|
return func(ps *RoutePathReverser) {
|
|
|
|
ps.vscheme = scheme
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithHost enables the RoutePathReverser's URL feature.
|
|
|
|
// Both "WithHost" and "WithScheme" can be different from
|
|
|
|
// the real server's listening address, i.e nginx in front.
|
|
|
|
func WithHost(host string) RoutePathReverserOption {
|
|
|
|
return func(ps *RoutePathReverser) {
|
|
|
|
ps.vhost = host
|
|
|
|
if ps.vscheme == "" {
|
|
|
|
ps.vscheme = nettools.ResolveScheme(host)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithServer enables the RoutePathReverser's URL feature.
|
|
|
|
// It receives an *http.Server and tries to resolve
|
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// RoutePathReverser contains methods that helps to reverse a
|
|
|
|
// (dynamic) path from a specific route,
|
|
|
|
// route name is required because a route may being registered
|
|
|
|
// on more than one http method.
|
|
|
|
type RoutePathReverser struct {
|
|
|
|
provider RoutesProvider
|
|
|
|
// both vhost and vscheme are being used, optionally, for the URL feature.
|
|
|
|
vhost string
|
|
|
|
vscheme string
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewRoutePathReverser returns a new path reverser based on
|
|
|
|
// a routes provider, needed to get a route based on its name.
|
|
|
|
// Options is required for the URL function.
|
|
|
|
// See WithScheme and WithHost or WithServer.
|
|
|
|
func NewRoutePathReverser(apiRoutesProvider RoutesProvider, options ...RoutePathReverserOption) *RoutePathReverser {
|
|
|
|
ps := &RoutePathReverser{
|
|
|
|
provider: apiRoutesProvider,
|
|
|
|
}
|
|
|
|
for _, o := range options {
|
|
|
|
o(ps)
|
|
|
|
}
|
|
|
|
return ps
|
|
|
|
}
|
|
|
|
|
|
|
|
// Path returns a route path based on a route name and any dynamic named parameter's values-only.
|
|
|
|
func (ps *RoutePathReverser) Path(routeName string, paramValues ...interface{}) string {
|
|
|
|
r := ps.provider.GetRoute(routeName)
|
|
|
|
if r == nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(paramValues) == 0 {
|
|
|
|
return r.Path
|
|
|
|
}
|
|
|
|
|
|
|
|
return r.ResolvePath(toStringSlice(paramValues)...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func toStringSlice(args []interface{}) []string {
|
|
|
|
var argsString []string
|
|
|
|
if len(args) > 0 {
|
|
|
|
argsString = make([]string, len(args), len(args))
|
|
|
|
}
|
|
|
|
|
|
|
|
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:]...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return argsString
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
func (ps *RoutePathReverser) URL(routeName string, paramValues ...interface{}) (url string) {
|
|
|
|
if ps.vhost == "" || ps.vscheme == "" {
|
|
|
|
return "not supported"
|
|
|
|
}
|
|
|
|
|
|
|
|
r := ps.provider.GetRoute(routeName)
|
|
|
|
if r == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(paramValues) == 0 {
|
|
|
|
return r.Path
|
|
|
|
}
|
|
|
|
|
|
|
|
args := toStringSlice(paramValues)
|
|
|
|
|
|
|
|
host := ps.vhost
|
|
|
|
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 {
|
|
|
|
subdomain := args[0]
|
|
|
|
host = subdomain + "." + host
|
|
|
|
args = args[1:] // remove the subdomain part for the arguments,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if parsedPath := r.ResolvePath(args...); parsedPath != "" {
|
|
|
|
url = scheme + "://" + host + parsedPath
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|