Wildcard subdomain full support | v3.0.0-beta.2

Update to v3.0.0-beta.2.
Wildcard subdomain e-book section:
https://kataras.gitbooks.io/iris/content/subdomains.html
This commit is contained in:
Makis Maropoulos 2016-06-04 16:20:32 +03:00
parent b4612dcfe0
commit 1a433e34d5
9 changed files with 93 additions and 24 deletions

View File

@ -6,7 +6,7 @@
[Travis]: http://travis-ci.org/kataras/iris [Travis]: http://travis-ci.org/kataras/iris
[License Widget]: https://img.shields.io/badge/license-Apache%20License%202.0-E91E63.svg?style=flat-square [License Widget]: https://img.shields.io/badge/license-Apache%20License%202.0-E91E63.svg?style=flat-square
[License]: https://github.com/kataras/iris/blob/master/LICENSE [License]: https://github.com/kataras/iris/blob/master/LICENSE
[Release Widget]: https://img.shields.io/badge/release-v3.0.0--beta-blue.svg?style=flat-square [Release Widget]: https://img.shields.io/badge/release-v3.0.0--beta.2-blue.svg?style=flat-square
[Release]: https://github.com/kataras/iris/releases [Release]: https://github.com/kataras/iris/releases
[Gitter Widget]: https://img.shields.io/badge/chat-on%20gitter-00BCD4.svg?style=flat-square [Gitter Widget]: https://img.shields.io/badge/chat-on%20gitter-00BCD4.svg?style=flat-square
[Gitter]: https://gitter.im/kataras/iris [Gitter]: https://gitter.im/kataras/iris
@ -116,7 +116,7 @@ Iris suggests you to use [this](https://github.com/gavv/httpexpect) new suite t
Versioning Versioning
------------ ------------
Current: **v3.0.0-beta.1** Current: **v3.0.0-beta.2**
> Iris is an active project > Iris is an active project
@ -131,11 +131,13 @@ Todo
------------ ------------
> for the next release 'v3' > for the next release 'v3'
- [x] [Dynamic/Wildcard subdomains](https://kataras.gitbooks.io/iris/content/subdomains.html).
- [x] Create server & client side (js) library for .on('event', func action(...)) / .emit('event')... (like socket.io but supports only websocket). - [x] Create server & client side (js) library for .on('event', func action(...)) / .emit('event')... (like socket.io but supports only websocket).
- [x] Find and provide support for the most stable template engine and be able to change it via the configuration, keep html/templates support. - [x] Find and provide support for the most stable template engine and be able to change it via the configuration, keep html/templates support.
- [x] Extend, test and publish to the public the [Iris' cmd](https://github.com/kataras/iris/tree/master/iris). - [x] Extend, test and publish to the public the [Iris' cmd](https://github.com/kataras/iris/tree/master/iris).
- [x] Route naming and html url func, requested [here](https://github.com/kataras/iris/issues/165). - [x] Route naming and html url func, requested [here](https://github.com/kataras/iris/issues/165).
If you're willing to donate click [here](DONATIONS.md) If you're willing to donate click [here](DONATIONS.md)
People People

View File

@ -55,6 +55,8 @@ 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

@ -91,6 +91,7 @@ type (
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,6 +95,20 @@ 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
func (ctx *Context) GetSubdomain() string {
if ctx.subdomain == "" {
host := ctx.HostString()
if index := strings.IndexByte(host, '.'); index > 0 {
subdomain := host[0:index]
ctx.subdomain = subdomain
}
}
return ctx.subdomain
}
// URLEncode returns the path encoded as url // URLEncode returns the path encoded as url
// useful when you want to pass something to a database and be valid to retrieve it via context.Param // useful when you want to pass something to a database and be valid to retrieve it via context.Param
// use it only for special cases, when the default behavior doesn't suits you. // use it only for special cases, when the default behavior doesn't suits you.

View File

@ -1,4 +1,4 @@
// Package iris v3.0.0-beta.1 // Package iris v3.0.0-beta.2
// //
// Note: When 'Station', we mean the Iris type. // Note: When 'Station', we mean the Iris type.
package iris package iris
@ -31,7 +31,7 @@ import (
const ( const (
// Version of the iris // Version of the iris
Version = "v3.0.0-beta.1" Version = "v3.0.0-beta.2"
banner = ` _____ _ banner = ` _____ _
|_ _| (_) |_ _| (_)
| | ____ _ ___ | | ____ _ ___

View File

@ -81,6 +81,18 @@ func HandleFunc(method string, path string, handlersFn ...HandlerFunc) IRoute {
return DefaultIris.HandleFunc(method, path, handlersFn...) return DefaultIris.HandleFunc(method, path, handlersFn...)
} }
// Wildcard same as .Party("*.")
// registers a route for Dynamic subdomain
// receives three parameters
// the first is the http method
// the second is the request path, can be a dynamic path also like others
// the third are the handlerfuncs
//
// example: subdomains_2
func Wildcard(method string, registedPath string, handlersFn ...HandlerFunc) {
DefaultIris.Wildcard(method, registedPath, handlersFn...)
}
// API converts & registers a custom struct to the router // API converts & registers a custom struct to the router
// receives two parameters // receives two parameters
// first is the request path (string) // first is the request path (string)

View File

@ -21,6 +21,7 @@ type (
IParty interface { IParty interface {
Handle(string, string, ...Handler) IRoute Handle(string, string, ...Handler) IRoute
HandleFunc(string, string, ...HandlerFunc) IRoute HandleFunc(string, string, ...HandlerFunc) IRoute
Wildcard(string, string, ...HandlerFunc)
API(path string, controller HandlerAPI, middlewares ...HandlerFunc) error API(path string, controller HandlerAPI, middlewares ...HandlerFunc) error
Get(string, ...HandlerFunc) RouteNameFunc Get(string, ...HandlerFunc) RouteNameFunc
Post(string, ...HandlerFunc) RouteNameFunc Post(string, ...HandlerFunc) RouteNameFunc
@ -88,6 +89,20 @@ func (p *GardenParty) HandleFunc(method string, registedPath string, handlersFn
return p.Handle(method, registedPath, ConvertToHandlers(handlersFn)...) return p.Handle(method, registedPath, ConvertToHandlers(handlersFn)...)
} }
// Wildcard same as .Party("*.")
// registers a route for Dynamic subdomain
// receives three parameters
// the first is the http method
// the second is the request path, can be a dynamic path also like others
// the third are the handlerfuncs
//
// Note that this is just a global route, no party's route.
// example: subdomains_2
func (p *GardenParty) Wildcard(method string, registedPath string, handlersFn ...HandlerFunc) {
path := PrefixDynamicSubdomain + registedPath
p.station.router.HandleFunc(method, path, handlersFn...)
}
// API converts & registers a custom struct to the router // API converts & registers a custom struct to the router
// receives two parameters // receives two parameters
// first is the request path (string) // first is the request path (string)
@ -589,7 +604,10 @@ func (p *GardenParty) StaticContent(reqPath string, contentType string, content
func (p *GardenParty) Party(path string, handlersFn ...HandlerFunc) IParty { func (p *GardenParty) Party(path string, handlersFn ...HandlerFunc) IParty {
middleware := ConvertToHandlers(handlersFn) middleware := ConvertToHandlers(handlersFn)
if path[0] != SlashByte && strings.Contains(path, ".") { if path[0] != SlashByte && strings.Contains(path, ".") {
//it's domain so no handlers share (even the global ) or path, nothing. //it's a domain so no handlers share (even the global ) or path, nothing.
if path[0] == MatchEverythingByte { // it's a dynamic subdomain
path = PrefixDynamicSubdomain
}
} else { } else {
// set path to parent+child // set path to parent+child
path = absPath(p.relativePath, path) path = absPath(p.relativePath, path)

View File

@ -240,6 +240,7 @@ func (r *Route) GetURI(args ...interface{}) (uri string) {
} }
host := r.host host := r.host
if parsedPath, ok := r.Parse(args...); ok { if parsedPath, ok := r.Parse(args...); ok {
uri = scheme + host + parsedPath uri = scheme + host + parsedPath
} }

View File

@ -19,6 +19,8 @@ const (
Slash = "/" Slash = "/"
// MatchEverythingByte is just a byte of '*" rune/char // MatchEverythingByte is just a byte of '*" rune/char
MatchEverythingByte = byte('*') MatchEverythingByte = byte('*')
// PrefixDynamicSubdomain is the prefix which dynamic subdomains are registed to, as virtual. Used internaly by Iris but good to know.
PrefixDynamicSubdomain = "www.iris_subd0mAin.iris"
// HTTP Methods(1) // HTTP Methods(1)
@ -43,6 +45,9 @@ 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)
// MethodConnectBytes []byte("CONNECT") // MethodConnectBytes []byte("CONNECT")
@ -217,19 +222,22 @@ func (r *router) optimize() {
// optimizeLookups runs AFTER server's listen // optimizeLookups runs AFTER server's listen
func (r *router) optimizeLookups() { func (r *router) optimizeLookups() {
// set the isTLS on all routes and the listening full host if r.station.server != nil && r.station.server.IsListening() {
listeningHost := r.station.server.Listener().Addr().String() // set the isTLS on all routes and the listening full host
for idx := range r.lookups { listeningHost := r.station.server.Listener().Addr().String()
theR := r.lookups[idx] for idx := range r.lookups {
theR.setTLS(r.station.server.IsSecure()) theR := r.lookups[idx]
if theR.GetDomain() == "" { // means local, no subdomain theR.setTLS(r.station.server.IsSecure())
theR.setHost(listeningHost) if theR.GetDomain() == "" { // means local, no subdomain
} else { theR.setHost(listeningHost)
// it's a subdomain route } else {
theR.setHost(theR.GetDomain() + "." + listeningHost) // 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
@ -271,18 +279,29 @@ func (r *router) serveFunc(reqCtx *fasthttp.RequestCtx) {
// If no route found, it sends an http status 404 // If no route found, it sends an http status 404
func (r *router) serveDomainFunc(reqCtx *fasthttp.RequestCtx) { func (r *router) serveDomainFunc(reqCtx *fasthttp.RequestCtx) {
method := utils.BytesToString(reqCtx.Method()) method := utils.BytesToString(reqCtx.Method())
domain := utils.BytesToString(reqCtx.Host()) host := utils.BytesToString(reqCtx.Host())
path := r.getRequestPath(reqCtx) fulldomain := ""
if strings.Count(host, ".") >= 2 {
if portIdx := strings.Index(host, ":"); portIdx != -1 {
fulldomain = host[0:portIdx]
} else {
fulldomain = host
}
}
path := utils.BytesToString(r.getRequestPath(reqCtx))
tree := r.garden.first tree := r.garden.first
for tree != nil { for tree != nil {
if tree.hosts && tree.domain == domain { if tree.hosts && tree.domain != "" && fulldomain != "" {
// here we have an issue, at fasthttp/uri.go 273-274 line normalize path it adds a '/' slash at the beginning, it doesn't checks for subdomains if tree.domain == fulldomain { // it's a static subdomain
// I could fix it but i leave it as it is, I just create a new function inside tree named 'serveReturn' which accepts a path too. -> path = fulldomain + path
//-> reqCtx.Request.URI().SetPathBytes(append(reqCtx.Host(), reqCtx.Path()...)) <- } else if strings.Index(tree.domain, PrefixDynamicSubdomain) != -1 { // it's a dynamic virtual subdomain
path = append(reqCtx.Host(), path...) path = PrefixDynamicSubdomain + path
}
} }
if r.methodMatch(tree.method, method) { if r.methodMatch(tree.method, method) {
if tree.serve(reqCtx, utils.BytesToString(path)) { if tree.serve(reqCtx, path) {
return return
} }
} }