Add history for v3.0.0.-beta.2. Some fixes to subdomains listening on local 127.0.0.1. Change GetURI to ParseURI

Examples and e-book updated also.
This commit is contained in:
Makis Maropoulos 2016-06-04 23:07:19 +03:00
parent 1a433e34d5
commit 26ef396959
10 changed files with 109 additions and 91 deletions

View File

@ -1,9 +1,16 @@
# History # History
## 3.0.0-beta -> 3.0.0-beta.2
- NEW: Wildcard(dynamic) subdomains, read [here](https://kataras.gitbooks.io/iris/content/subdomains.html)
- NEW: Implement feature request [#165](https://github.com/kataras/iris/issues/165). Routes can now be selected by `a custom name`, and this allows us to use the {{ url "custom-name" "any" "named" "parameters"}}`` inside HTMLEngine's templates. Example [here](https://github.com/iris-contrib/examples/tree/master/templates_9).
## 3.0.0-alpha.beta -> 3.0.0-beta ## 3.0.0-alpha.beta -> 3.0.0-beta
- New [iris.API] for easy API declaration, read more [here](https://kataras.gitbooks.io/iris/content/using-handlerapi.html), example [there](https://github.com/iris-contrib/examples/tree/master/api_handler_2). - New iris.API for easy API declaration, read more [here](https://kataras.gitbooks.io/iris/content/using-handlerapi.html), example [there](https://github.com/iris-contrib/examples/tree/master/api_handler_2).
- Add [example](https://github.com/iris-contrib/examples/tree/master/middleware_basicauth_2) and fix the Basic Authentication middleware - Add [example](https://github.com/iris-contrib/examples/tree/master/middleware_basicauth_2) and fix the Basic Authentication middleware

View File

@ -55,8 +55,6 @@ type (
sessionStore store.IStore sessionStore store.IStore
// pos is the position number of the Context, look .Next to understand // pos is the position number of the Context, look .Next to understand
pos uint8 pos uint8
// subdomain the subdomain (taken from the host), this is empty until GetSubdomain called
subdomain string
} }
) )

View File

@ -86,12 +86,12 @@ type (
URLParams() map[string]string URLParams() map[string]string
MethodString() string MethodString() string
HostString() string HostString() string
Subdomain() string
PathString() string PathString() string
RequestIP() string RequestIP() string
RemoteAddr() string RemoteAddr() string
RequestHeader(k string) string RequestHeader(k string) string
PostFormValue(string) string PostFormValue(string) string
GetSubdomain() string
} }
// IContextResponse is part of the IContext // IContextResponse is part of the IContext

View File

@ -95,18 +95,14 @@ func (ctx *Context) PostFormValue(name string) string {
return string(ctx.RequestCtx.PostArgs().Peek(name)) return string(ctx.RequestCtx.PostArgs().Peek(name))
} }
// GetSubdomain returns the subdomain if any, else empty string // Subdomain returns the subdomain (string) of this request, if any
func (ctx *Context) GetSubdomain() string { func (ctx *Context) Subdomain() (subdomain string) {
if ctx.subdomain == "" {
host := ctx.HostString() host := ctx.HostString()
if index := strings.IndexByte(host, '.'); index > 0 { if index := strings.IndexByte(host, '.'); index > 0 {
subdomain := host[0:index] subdomain = host[0:index]
ctx.subdomain = subdomain
}
} }
return ctx.subdomain return
} }
// URLEncode returns the path encoded as url // URLEncode returns the path encoded as url

View File

@ -25,11 +25,10 @@ func (ctx *Context) Redirect(urlToRedirect string, statusHeader ...int) {
// RedirectTo does the same thing as Redirect but instead of receiving a uri or path it receives a route name // RedirectTo does the same thing as Redirect but instead of receiving a uri or path it receives a route name
func (ctx *Context) RedirectTo(routeName string, args ...interface{}) { func (ctx *Context) RedirectTo(routeName string, args ...interface{}) {
s, ok := ctx.station.RouteByName(routeName).Parse(args...) s := ctx.station.RouteByName(routeName).ParseURI(args...)
if ok { if s != "" {
ctx.Redirect(s, StatusFound) ctx.Redirect(s, StatusFound)
} }
} }
// Error handling // Error handling

11
iris.go
View File

@ -124,14 +124,12 @@ func (s *Iris) initTemplates() {
"url": func(routeName string, args ...interface{}) (string, error) { "url": func(routeName string, args ...interface{}) (string, error) {
r := s.RouteByName(routeName) r := s.RouteByName(routeName)
// check if not found // check if not found
if r.GetPath() == "" { if r.GetMethod() == "" {
return "", ErrRenderRouteNotFound.Format(routeName) return "", ErrRenderRouteNotFound.Format(routeName)
} }
if result, ok := r.Parse(args...); ok { return r.ParseURI(args...), nil
return result, nil
}
return "", nil
}, },
} }
// these should be already a non-nil map but if .New(cfg) it's not, is mergo's bug, temporary: // these should be already a non-nil map but if .New(cfg) it's not, is mergo's bug, temporary:
@ -259,9 +257,6 @@ func (s *Iris) PreListen(opt config.Server) *server.Server {
func (s *Iris) PostListen() { func (s *Iris) PostListen() {
//if not error opening the server, then: //if not error opening the server, then:
// prepare the route actions, these actions needs real server's access because that it's after server's listen
s.router.optimizeLookups()
//set the rest (for Data, Text, JSON, JSONP, XML) //set the rest (for Data, Text, JSON, JSONP, XML)
s.rest = rest.New(s.config.Render.Rest) s.rest = rest.New(s.config.Render.Rest)
// set the templates // set the templates

View File

@ -75,7 +75,7 @@ func (p *GardenParty) Handle(method string, registedPath string, handlers ...Han
path = fixPath(absPath(p.relativePath, registedPath)) // "/xyz" path = fixPath(absPath(p.relativePath, registedPath)) // "/xyz"
} }
middleware := JoinMiddleware(p.middleware, handlers) middleware := JoinMiddleware(p.middleware, handlers)
route := NewRoute(method, path, middleware) route := NewRoute(method, path, middleware, p.station)
p.station.plugins.DoPreHandle(route) p.station.plugins.DoPreHandle(route)
p.station.addRoute(route) p.station.addRoute(route)
p.station.plugins.DoPostHandle(route) p.station.plugins.DoPostHandle(route)
@ -634,7 +634,7 @@ func fixPath(str string) string {
strafter := strings.Replace(str, "//", Slash, -1) strafter := strings.Replace(str, "//", Slash, -1)
if strafter[0] == SlashByte && strings.Count(strafter, ".") >= 2 { if strafter[0] == SlashByte && strings.Contains(strafter, ".") {
//it's domain, remove the first slash //it's domain, remove the first slash
strafter = strafter[1:] strafter = strafter[1:]
} }

View File

@ -18,18 +18,8 @@ type (
Name(string) IRoute Name(string) IRoute
GetMiddleware() Middleware GetMiddleware() Middleware
HasCors() bool HasCors() bool
// internal methods ParsePath(...interface{}) string
setTLS(bool) ParseURI(...interface{}) string
setHost(string)
//
// used to check arguments with the route's named parameters and return the correct url
// second return value is false when the action cannot be done
Parse(...interface{}) (string, bool)
// GetURI returns the GetDomain() + Parse(...optional named parameters if route is dynamic)
// instead of Parse it just returns an empty string if path parse is failed
GetURI(...interface{}) string
} }
// RouteNameFunc is returned to from route handle // RouteNameFunc is returned to from route handle
@ -43,10 +33,8 @@ type (
// the name of the route, the default name is just the registed path. // the name of the route, the default name is just the registed path.
name string name string
middleware Middleware middleware Middleware
// if true then https:// // station
isTLS bool station *Iris
// the real host
host string
// this is used to convert /mypath/:aparam/:something to -> /mypath/%v/%v and /mypath/* -> mypath/%v // 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. // we use %v to escape from the conversions between strings,booleans and integers.
// used inside custom html template func 'url' // used inside custom html template func 'url'
@ -59,7 +47,7 @@ type (
var _ IRoute = &Route{} var _ IRoute = &Route{}
// NewRoute creates, from a path string, and a slice of HandlerFunc // NewRoute creates, from a path string, and a slice of HandlerFunc
func NewRoute(method string, registedPath string, middleware Middleware) *Route { func NewRoute(method string, registedPath string, middleware Middleware, station *Iris) *Route {
domain := "" domain := ""
//dirdy but I'm not touching this again:P //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, '.')) { if registedPath[0] != SlashByte && strings.Contains(registedPath, ".") && (strings.IndexByte(registedPath, SlashByte) == -1 || strings.IndexByte(registedPath, SlashByte) > strings.IndexByte(registedPath, '.')) {
@ -74,7 +62,7 @@ func NewRoute(method string, registedPath string, middleware Middleware) *Route
//e.g /admin.ideopod.com/hey //e.g /admin.ideopod.com/hey
//then just remove the first slash and re-execute the NewRoute and return it //then just remove the first slash and re-execute the NewRoute and return it
registedPath = registedPath[1:] registedPath = registedPath[1:]
return NewRoute(method, registedPath, middleware) return NewRoute(method, registedPath, middleware, station)
} }
//if it's just the domain, then set it(registedPath) as the domain //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 //and after set the registedPath to a slash '/' for the path part
@ -88,11 +76,15 @@ func NewRoute(method string, registedPath string, middleware Middleware) *Route
} }
} }
r := &Route{method: method, domain: domain, fullpath: registedPath, middleware: middleware, name: registedPath, formattedPath: registedPath} r := &Route{method: method, domain: domain, fullpath: registedPath, middleware: middleware, name: registedPath, formattedPath: registedPath, station: station}
r.formatPath() r.formatPath()
return r return r
} }
func (r *Route) isWildcard() bool {
return r.domain != r.station.server.Hostname() && r.domain == PrefixDynamicSubdomain
}
func (r *Route) formatPath() { func (r *Route) formatPath() {
// we don't care about performance here, no runtime func. // we don't care about performance here, no runtime func.
@ -161,22 +153,14 @@ func (r *Route) HasCors() bool {
return RouteConflicts(r, "httpmethod") return RouteConflicts(r, "httpmethod")
} }
func (r *Route) setTLS(isSecure bool) { // ParsePath used to check arguments with the route's named parameters and return the correct url
r.isTLS = isSecure // if parse failed returns empty string
} func (r *Route) ParsePath(args ...interface{}) string {
func (r *Route) setHost(s string) {
r.host = s
}
// Parse used to check arguments with the route's named parameters and return the correct url
// second return value is false when the action cannot be done
func (r *Route) Parse(args ...interface{}) (string, bool) {
argsLen := len(args) argsLen := len(args)
// we have named parameters but arguments not given // we have named parameters but arguments not given
if argsLen == 0 && r.formattedParts > 0 { if argsLen == 0 && r.formattedParts > 0 {
return "", false return ""
} }
// we have arguments but they are much more than the named parameters // we have arguments but they are much more than the named parameters
@ -205,10 +189,10 @@ func (r *Route) Parse(args ...interface{}) (string, bool) {
parameter := strings.Join(argsString, Slash) parameter := strings.Join(argsString, Slash)
result := fmt.Sprintf(r.formattedPath, parameter) result := fmt.Sprintf(r.formattedPath, parameter)
return result, true return result
} }
// 2 if !1 return false // 2 if !1 return false
return "", false return ""
} }
arguments := args[0:] arguments := args[0:]
@ -228,20 +212,53 @@ func (r *Route) Parse(args ...interface{}) (string, bool) {
} }
} }
return fmt.Sprintf(r.formattedPath, arguments...), true return fmt.Sprintf(r.formattedPath, arguments...)
} }
// GetURI returns the GetDomain() + Parse(...optional named parameters if route is dynamic) // ParseURI returns the subdomain+ host + ParsePath(...optional named parameters if route is dynamic)
// instead of Parse it just returns an empty string if path parse is failed // returns an empty string if parse is failed
func (r *Route) GetURI(args ...interface{}) (uri string) { func (r *Route) ParseURI(args ...interface{}) (uri string) {
scheme := "http://" scheme := "http://"
if r.isTLS { if r.station.server.IsSecure() {
scheme = "https://" scheme = "https://"
} }
host := r.host host := r.station.server.Host()
if parsedPath, ok := r.Parse(args...); ok { 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:]
}
if parsedPath := r.ParsePath(arguments...); parsedPath != "" {
uri = scheme + host + parsedPath uri = scheme + host + parsedPath
} }

View File

@ -45,8 +45,6 @@ const (
) )
var ( var (
// PrefixDynamicSubdomainBytes is the prefix (as []byte) which dynamic subdomains are registed to, as virtual. Used internaly by Iris but good to know.
PrefixDynamicSubdomainBytes = []byte(PrefixDynamicSubdomain)
// HTTP Methods(2) // HTTP Methods(2)
@ -220,26 +218,6 @@ func (r *router) optimize() {
r.optimized = true r.optimized = true
} }
// optimizeLookups runs AFTER server's listen
func (r *router) optimizeLookups() {
if r.station.server != nil && r.station.server.IsListening() {
// set the isTLS on all routes and the listening full host
listeningHost := r.station.server.Listener().Addr().String()
for idx := range r.lookups {
theR := r.lookups[idx]
theR.setTLS(r.station.server.IsSecure())
if theR.GetDomain() == "" { // means local, no subdomain
theR.setHost(listeningHost)
} else {
// it's a subdomain route
theR.setHost(theR.GetDomain() + "." + listeningHost)
}
}
}
}
// notFound internal method, it justs takes the context from pool ( in order to have the custom errors available) and procedure a Not Found 404 error // notFound internal method, it justs takes the context from pool ( in order to have the custom errors available) and procedure a Not Found 404 error
// this is being called when no route was found used on the ServeRequest. // this is being called when no route was found used on the ServeRequest.
func (r *router) notFound(reqCtx *fasthttp.RequestCtx) { func (r *router) notFound(reqCtx *fasthttp.RequestCtx) {
@ -281,13 +259,14 @@ func (r *router) serveDomainFunc(reqCtx *fasthttp.RequestCtx) {
method := utils.BytesToString(reqCtx.Method()) method := utils.BytesToString(reqCtx.Method())
host := utils.BytesToString(reqCtx.Host()) host := utils.BytesToString(reqCtx.Host())
fulldomain := "" fulldomain := ""
if strings.Count(host, ".") >= 2 { if strings.Count(host, ".") >= 2 && host != r.station.server.Host() {
if portIdx := strings.Index(host, ":"); portIdx != -1 { if portIdx := strings.Index(host, ":"); portIdx != -1 {
fulldomain = host[0:portIdx] fulldomain = host[0:portIdx]
} else { } else {
fulldomain = host fulldomain = host
} }
} }
path := utils.BytesToString(r.getRequestPath(reqCtx)) path := utils.BytesToString(r.getRequestPath(reqCtx))
tree := r.garden.first tree := r.garden.first
for tree != nil { for tree != nil {
@ -297,7 +276,6 @@ func (r *router) serveDomainFunc(reqCtx *fasthttp.RequestCtx) {
} else if strings.Index(tree.domain, PrefixDynamicSubdomain) != -1 { // it's a dynamic virtual subdomain } else if strings.Index(tree.domain, PrefixDynamicSubdomain) != -1 { // it's a dynamic virtual subdomain
path = PrefixDynamicSubdomain + path path = PrefixDynamicSubdomain + path
} }
} }
if r.methodMatch(tree.method, method) { if r.methodMatch(tree.method, method) {
@ -307,6 +285,7 @@ func (r *router) serveDomainFunc(reqCtx *fasthttp.RequestCtx) {
} }
tree = tree.next tree = tree.next
} }
//not found, get the first's pool and use that to send a custom http error(if setted) //not found, get the first's pool and use that to send a custom http error(if setted)
r.notFound(reqCtx) r.notFound(reqCtx)
} }

View File

@ -58,6 +58,33 @@ func (s *Server) Listener() net.Listener {
return s.listener return s.listener
} }
// Host returns the Listener().Addr().String(), if server is not listening it returns the config.ListeningAddr
func (s *Server) Host() (host string) {
if s.IsListening() {
return s.Listener().Addr().String()
} else {
return s.Config.ListeningAddr
}
}
// Hostname returns the hostname part only, if host == 0.0.0.0:8080 it will return the 0.0.0.0
// if server is not listening it returns the config.ListeningAddr's hostname part
func (s *Server) Hostname() (hostname string) {
if s.IsListening() {
fullhost := s.Listener().Addr().String()
hostname = fullhost[0:strings.IndexByte(fullhost, ':')] // no the port
} else {
fullhost := s.Config.ListeningAddr
if idx := strings.IndexByte(fullhost, ':'); idx > 1 { // at least after second char
hostname = hostname[0 : idx-1]
} else {
hostname = "0.0.0.0"
}
}
return
}
//Serve just serves a listener, it is a blocking action, plugin.PostListen is not fired here. //Serve just serves a listener, it is a blocking action, plugin.PostListen is not fired here.
func (s *Server) Serve(l net.Listener) error { func (s *Server) Serve(l net.Listener) error {
s.listener = l s.listener = l