2016-05-30 16:08:09 +02:00
package iris
import (
2016-06-02 03:45:03 +02:00
"fmt"
"strconv"
2016-05-30 16:08:09 +02:00
"strings"
)
type (
// IRoute is the interface which the Route should implements
// it useful to have it as an interface because this interface is passed to the plugins
IRoute interface {
GetMethod ( ) string
GetDomain ( ) string
GetPath ( ) string
2016-06-02 03:45:03 +02:00
GetName ( ) string
// Name sets the name of the route
Name ( string ) IRoute
2016-05-30 16:08:09 +02:00
GetMiddleware ( ) Middleware
HasCors ( ) bool
2016-06-04 22:07:19 +02:00
ParsePath ( ... interface { } ) string
ParseURI ( ... interface { } ) string
2016-05-30 16:08:09 +02:00
}
2016-06-02 03:45:03 +02:00
// RouteNameFunc is returned to from route handle
RouteNameFunc func ( string ) IRoute
2016-05-30 16:08:09 +02:00
// Route contains basic and temporary info about the route in order to be stored to the tree
Route struct {
2016-06-02 03:45:03 +02:00
method string
domain string
fullpath string
// the name of the route, the default name is just the registed path.
name string
2016-05-30 16:08:09 +02:00
middleware Middleware
2016-06-04 22:07:19 +02:00
// station
station * Iris
2016-06-02 03:45:03 +02:00
// this is used to convert /mypath/:aparam/:something to -> /mypath/%v/%v and /mypath/* -> mypath/%v
// we use %v to escape from the conversions between strings,booleans and integers.
// used inside custom html template func 'url'
formattedPath string
// formattedParts is just the formattedPath count, used to see if we have one path parameter then the url's function arguments will be passed as one string to the %v
formattedParts int
2016-05-30 16:08:09 +02:00
}
)
var _ IRoute = & Route { }
// NewRoute creates, from a path string, and a slice of HandlerFunc
2016-06-04 22:07:19 +02:00
func NewRoute ( method string , registedPath string , middleware Middleware , station * Iris ) * Route {
2016-05-30 16:08:09 +02:00
domain := ""
//dirdy but I'm not touching this again:P
if registedPath [ 0 ] != SlashByte && strings . Contains ( registedPath , "." ) && ( strings . IndexByte ( registedPath , SlashByte ) == - 1 || strings . IndexByte ( registedPath , SlashByte ) > strings . IndexByte ( registedPath , '.' ) ) {
//means that is a path with domain
//we have to extract the domain
//find the first '/'
firstSlashIndex := strings . IndexByte ( registedPath , SlashByte )
//firt of all remove the first '/' if that exists and we have domain
if firstSlashIndex == 0 {
//e.g /admin.ideopod.com/hey
//then just remove the first slash and re-execute the NewRoute and return it
registedPath = registedPath [ 1 : ]
2016-06-04 22:07:19 +02:00
return NewRoute ( method , registedPath , middleware , station )
2016-05-30 16:08:09 +02:00
}
//if it's just the domain, then set it(registedPath) as the domain
//and after set the registedPath to a slash '/' for the path part
if firstSlashIndex == - 1 {
domain = registedPath
registedPath = Slash
} else {
//we have a domain + path
domain = registedPath [ 0 : firstSlashIndex ]
registedPath = registedPath [ len ( domain ) : ]
}
}
2016-06-04 22:07:19 +02:00
r := & Route { method : method , domain : domain , fullpath : registedPath , middleware : middleware , name : registedPath , formattedPath : registedPath , station : station }
2016-06-02 03:45:03 +02:00
r . formatPath ( )
2016-05-30 16:08:09 +02:00
return r
}
2016-06-04 22:07:19 +02:00
func ( r * Route ) isWildcard ( ) bool {
return r . domain != r . station . server . Hostname ( ) && r . domain == PrefixDynamicSubdomain
}
2016-06-02 03:45:03 +02:00
func ( r * Route ) formatPath ( ) {
// we don't care about performance here, no runtime func.
n1Len := strings . Count ( r . fullpath , ":" )
isMatchEverything := r . fullpath [ len ( r . fullpath ) - 1 ] == MatchEverythingByte
if n1Len == 0 && ! isMatchEverything {
// its a static
return
}
if n1Len == 0 && isMatchEverything {
//if we have something like: /mypath/anything/* -> /mypatch/anything/%v
r . formattedPath = r . fullpath [ 0 : len ( r . fullpath ) - 2 ] + "%v"
r . formattedParts ++
return
}
tempPath := r . fullpath
splittedN1 := strings . Split ( r . fullpath , "/" )
for _ , v := range splittedN1 {
if len ( v ) > 0 {
if v [ 0 ] == ':' || v [ 0 ] == MatchEverythingByte {
r . formattedParts ++
tempPath = strings . Replace ( tempPath , v , "%v" , - 1 ) // n1Len, but let it we don't care about performance here.
}
}
}
r . formattedPath = tempPath
}
2016-05-30 16:08:09 +02:00
// GetMethod returns the http method
func ( r Route ) GetMethod ( ) string {
return r . method
}
// GetDomain returns the registed domain which this route is ( if none, is "" which is means "localhost"/127.0.0.1)
func ( r Route ) GetDomain ( ) string {
return r . domain
}
// GetPath returns the full registed path
func ( r Route ) GetPath ( ) string {
return r . fullpath
}
2016-06-02 03:45:03 +02:00
// GetName returns the name of the route
func ( r Route ) GetName ( ) string {
return r . name
}
// Name sets the route's name
func ( r * Route ) Name ( newName string ) IRoute {
r . name = newName
return r
}
2016-05-30 16:08:09 +02:00
// GetMiddleware returns the chain of the []HandlerFunc registed to this Route
func ( r Route ) GetMiddleware ( ) Middleware {
return r . middleware
}
// HasCors check if middleware passsed to a route has cors
func ( r * Route ) HasCors ( ) bool {
return RouteConflicts ( r , "httpmethod" )
}
2016-06-04 22:07:19 +02:00
// ParsePath used to check arguments with the route's named parameters and return the correct url
// if parse failed returns empty string
func ( r * Route ) ParsePath ( args ... interface { } ) string {
2016-06-02 03:45:03 +02:00
argsLen := len ( args )
2016-06-03 05:17:40 +02:00
2016-06-02 03:45:03 +02:00
// we have named parameters but arguments not given
if argsLen == 0 && r . formattedParts > 0 {
2016-06-04 22:07:19 +02:00
return ""
2016-06-02 03:45:03 +02:00
}
// 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 . fullpath [ len ( r . fullpath ) - 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 )
2016-06-03 18:53:00 +02:00
} else if arr , ok := v . ( [ ] string ) ; ok {
if len ( arr ) > 0 {
argsString [ i ] = arr [ 0 ]
argsString = append ( argsString , arr [ 1 : ] ... )
}
2016-06-02 03:45:03 +02:00
}
}
parameter := strings . Join ( argsString , Slash )
result := fmt . Sprintf ( r . formattedPath , parameter )
2016-06-04 22:07:19 +02:00
return result
2016-06-02 03:45:03 +02:00
}
// 2 if !1 return false
2016-06-04 22:07:19 +02:00
return ""
2016-06-02 03:45:03 +02:00
}
2016-06-03 18:53:00 +02:00
arguments := args [ 0 : ]
// check for arrays
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
}
arguments [ i ] = interfaceArr [ 0 ]
arguments = append ( arguments , interfaceArr [ 1 : ] ... )
}
}
}
2016-06-04 22:07:19 +02:00
return fmt . Sprintf ( r . formattedPath , arguments ... )
2016-06-02 03:45:03 +02:00
}
2016-06-04 22:07:19 +02:00
// ParseURI returns the subdomain+ host + ParsePath(...optional named parameters if route is dynamic)
// returns an empty string if parse is failed
func ( r * Route ) ParseURI ( args ... interface { } ) ( uri string ) {
2016-06-02 18:22:36 +02:00
scheme := "http://"
2016-06-04 22:07:19 +02:00
if r . station . server . IsSecure ( ) {
2016-06-02 18:22:36 +02:00
scheme = "https://"
}
2016-06-04 22:07:19 +02:00
host := r . station . server . Host ( )
arguments := args [ 0 : ]
// join arrays as arguments
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
}
arguments [ i ] = interfaceArr [ 0 ]
arguments = append ( arguments , interfaceArr [ 1 : ] ... )
}
}
}
// if it's dynamic subdomain then the first argument is the subdomain part
if r . isWildcard ( ) {
if len ( arguments ) == 0 { // it's a wildcard subdomain but not arguments
return
}
if subdomain , ok := arguments [ 0 ] . ( string ) ; ok {
host = subdomain + "." + host
} else {
// it is not array because we join them before. if not pass a string then this is not a subdomain part, return empty uri
return
}
arguments = arguments [ 1 : ]
}
2016-06-04 15:20:32 +02:00
2016-06-04 22:07:19 +02:00
if parsedPath := r . ParsePath ( arguments ... ) ; parsedPath != "" {
2016-06-02 18:22:36 +02:00
uri = scheme + host + parsedPath
}
return
}
2016-05-30 16:08:09 +02:00
// RouteConflicts checks for route's middleware conflicts
func RouteConflicts ( r * Route , with string ) bool {
for _ , h := range r . middleware {
if m , ok := h . ( interface {
Conflicts ( ) string
} ) ; ok {
if c := m . Conflicts ( ) ; c == with {
return true
}
}
}
return false
}