From 1a433e34d5f8978a58e4f5fd152803afa8d1345a Mon Sep 17 00:00:00 2001 From: Makis Maropoulos Date: Sat, 4 Jun 2016 16:20:32 +0300 Subject: [PATCH] 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 --- README.md | 6 +++-- context.go | 2 ++ context/context.go | 1 + context_request.go | 14 ++++++++++++ iris.go | 4 ++-- iris_singleton.go | 12 ++++++++++ party.go | 20 +++++++++++++++- route.go | 1 + router.go | 57 ++++++++++++++++++++++++++++++---------------- 9 files changed, 93 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index a0f167cc..9591e31d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [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]: 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 [Gitter Widget]: https://img.shields.io/badge/chat-on%20gitter-00BCD4.svg?style=flat-square [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 ------------ -Current: **v3.0.0-beta.1** +Current: **v3.0.0-beta.2** > Iris is an active project @@ -131,11 +131,13 @@ Todo ------------ > 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] 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] 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) People diff --git a/context.go b/context.go index 12f53499..6295c3ad 100644 --- a/context.go +++ b/context.go @@ -55,6 +55,8 @@ type ( sessionStore store.IStore // pos is the position number of the Context, look .Next to understand pos uint8 + // subdomain the subdomain (taken from the host), this is empty until GetSubdomain called + subdomain string } ) diff --git a/context/context.go b/context/context.go index a5aa1041..8b695156 100644 --- a/context/context.go +++ b/context/context.go @@ -91,6 +91,7 @@ type ( RemoteAddr() string RequestHeader(k string) string PostFormValue(string) string + GetSubdomain() string } // IContextResponse is part of the IContext diff --git a/context_request.go b/context_request.go index 617aad14..36952ab6 100644 --- a/context_request.go +++ b/context_request.go @@ -95,6 +95,20 @@ func (ctx *Context) PostFormValue(name string) string { 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 // 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. diff --git a/iris.go b/iris.go index d6644e52..83c12a68 100644 --- a/iris.go +++ b/iris.go @@ -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. package iris @@ -31,7 +31,7 @@ import ( const ( // Version of the iris - Version = "v3.0.0-beta.1" + Version = "v3.0.0-beta.2" banner = ` _____ _ |_ _| (_) | | ____ _ ___ diff --git a/iris_singleton.go b/iris_singleton.go index b66ad4f9..4622d6bf 100644 --- a/iris_singleton.go +++ b/iris_singleton.go @@ -81,6 +81,18 @@ func HandleFunc(method string, path string, handlersFn ...HandlerFunc) IRoute { 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 // receives two parameters // first is the request path (string) diff --git a/party.go b/party.go index 4e1f30a0..55339e80 100644 --- a/party.go +++ b/party.go @@ -21,6 +21,7 @@ type ( IParty interface { Handle(string, string, ...Handler) IRoute HandleFunc(string, string, ...HandlerFunc) IRoute + Wildcard(string, string, ...HandlerFunc) API(path string, controller HandlerAPI, middlewares ...HandlerFunc) error Get(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)...) } +// 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 // receives two parameters // 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 { middleware := ConvertToHandlers(handlersFn) 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 { // set path to parent+child path = absPath(p.relativePath, path) diff --git a/route.go b/route.go index a898564b..14b6c962 100644 --- a/route.go +++ b/route.go @@ -240,6 +240,7 @@ func (r *Route) GetURI(args ...interface{}) (uri string) { } host := r.host + if parsedPath, ok := r.Parse(args...); ok { uri = scheme + host + parsedPath } diff --git a/router.go b/router.go index d19d8349..b07e6822 100644 --- a/router.go +++ b/router.go @@ -19,6 +19,8 @@ const ( Slash = "/" // MatchEverythingByte is just a byte of '*" rune/char 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) @@ -43,6 +45,9 @@ const ( ) 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) // MethodConnectBytes []byte("CONNECT") @@ -217,19 +222,22 @@ func (r *router) optimize() { // optimizeLookups runs AFTER server's listen func (r *router) optimizeLookups() { - // 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) - } + 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 @@ -271,18 +279,29 @@ func (r *router) serveFunc(reqCtx *fasthttp.RequestCtx) { // If no route found, it sends an http status 404 func (r *router) serveDomainFunc(reqCtx *fasthttp.RequestCtx) { method := utils.BytesToString(reqCtx.Method()) - domain := utils.BytesToString(reqCtx.Host()) - path := r.getRequestPath(reqCtx) + host := utils.BytesToString(reqCtx.Host()) + 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 for tree != nil { - if tree.hosts && tree.domain == domain { - // 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 - // 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. -> - //-> reqCtx.Request.URI().SetPathBytes(append(reqCtx.Host(), reqCtx.Path()...)) <- - path = append(reqCtx.Host(), path...) + if tree.hosts && tree.domain != "" && fulldomain != "" { + if tree.domain == fulldomain { // it's a static subdomain + path = fulldomain + path + } else if strings.Index(tree.domain, PrefixDynamicSubdomain) != -1 { // it's a dynamic virtual subdomain + path = PrefixDynamicSubdomain + path + } + } + if r.methodMatch(tree.method, method) { - if tree.serve(reqCtx, utils.BytesToString(path)) { + if tree.serve(reqCtx, path) { return } }