mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 15:30:36 +01:00
NEW: Application#SubdomainRedirect
. Example: https://github.com/kataras/iris/blob/master/_examples/subdomains/redirect/main.go
Former-commit-id: d8dd7c426dc9f14c870f103fef703595a2915612
This commit is contained in:
parent
a4ff39df65
commit
e176ff7b0c
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
## Ασφάλεια | `iris.AutoTLS`
|
## Ασφάλεια | `iris.AutoTLS`
|
||||||
|
|
||||||
**Όλοι οι servers πρέπει να αναβαθμιστούν σε αυτήν την έκδοση**, περιέχει διορθώσεις για το _tls-sni challenge_ το οποίο απενεργοποιήθηκε μερικές μέρες πριν από το letsencrypt.org το οποίο προκάλεσε σχεδόν όλα τα golang https-ενεργποιημένα servers να να μην είναι σε θέση να λειτουργήσουν, έτσι υποστήριξη για το _http-01 challenge_ προστέθηκε σαν αναπλήρωση. Πλέον ο διακομιστής δοκιμάζει όλες τις διαθέσιμες προκλήσεις(challeneges) letsencrypt.
|
**Όλοι οι servers πρέπει να αναβαθμιστούν σε αυτήν την έκδοση**, περιέχει διορθώσεις για το _tls-sni challenge_ το οποίο απενεργοποιήθηκε μερικές μέρες πριν από το letsencrypt.org το οποίο προκάλεσε σχεδόν όλα τα golang https-ενεργποιημένα servers να να μην είναι σε θέση να λειτουργήσουν, έτσι υποστήριξη για το _http-01 challenge_ προστέθηκε σαν αναπλήρωση. Πλέον ο διακομιστής δοκιμάζει όλες τις διαθέσιμες προκλήσεις(challenges) letsencrypt.
|
||||||
|
|
||||||
Διαβάστε περισσότερα:
|
Διαβάστε περισσότερα:
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Веб-фреймворк Iris <a href="README.md"> <img width="20px" src="https://iris-go.com/images/flag-unitedkingdom.svg?v=10" /></a> <a href="README_ZH.md"><img width="20px" src="https://iris-go.com/images/flag-china.svg?v=10" /></a> <a href="README_GR.md"><img width="20px" src="https://iris-go.com/images/flag-greece.svg?v=10" /></a>
|
# Iris Web Framework <a href="README.md"> <img width="20px" src="https://iris-go.com/images/flag-unitedkingdom.svg?v=10" /></a> <a href="README_ZH.md"><img width="20px" src="https://iris-go.com/images/flag-china.svg?v=10" /></a> <a href="README_GR.md"><img width="20px" src="https://iris-go.com/images/flag-greece.svg?v=10" /></a>
|
||||||
|
|
||||||
<img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" />
|
<img align="right" width="169px" src="https://iris-go.com/images/icon.svg?v=a" title="logo created by @merry.dii" />
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ It doesn't always contain the "best ways" but it does cover each important featu
|
||||||
|
|
||||||
- [Hello world!](hello-world/main.go)
|
- [Hello world!](hello-world/main.go)
|
||||||
- [Glimpse](overview/main.go)
|
- [Glimpse](overview/main.go)
|
||||||
|
- [WWW](www/main.go)
|
||||||
- [Tutorial: Online Visitors](tutorial/online-visitors/main.go)
|
- [Tutorial: Online Visitors](tutorial/online-visitors/main.go)
|
||||||
- [Tutorial: A Todo MVC Application using Iris and Vue.js](https://hackernoon.com/a-todo-mvc-application-using-iris-and-vue-js-5019ff870064)
|
- [Tutorial: A Todo MVC Application using Iris and Vue.js](https://hackernoon.com/a-todo-mvc-application-using-iris-and-vue-js-5019ff870064)
|
||||||
- [Tutorial: URL Shortener using BoltDB](https://medium.com/@kataras/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7)
|
- [Tutorial: URL Shortener using BoltDB](https://medium.com/@kataras/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7)
|
||||||
|
@ -268,6 +269,7 @@ Follow the examples below,
|
||||||
- [Multi](subdomains/multi/main.go)
|
- [Multi](subdomains/multi/main.go)
|
||||||
- [Wildcard](subdomains/wildcard/main.go)
|
- [Wildcard](subdomains/wildcard/main.go)
|
||||||
- [WWW](subdomains/www/main.go)
|
- [WWW](subdomains/www/main.go)
|
||||||
|
- [Redirect fast](subdomains/redirect/main.go)
|
||||||
|
|
||||||
### Convert `http.Handler/HandlerFunc`
|
### Convert `http.Handler/HandlerFunc`
|
||||||
|
|
||||||
|
|
4
_examples/subdomains/redirect/hosts
Normal file
4
_examples/subdomains/redirect/hosts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
127.0.0.1 mydomain.com
|
||||||
|
127.0.0.1 www.mydomain.com
|
||||||
|
|
||||||
|
# Windows: Drive:/Windows/system32/drivers/etc/hosts, on Linux: /etc/hosts
|
68
_examples/subdomains/redirect/main.go
Normal file
68
_examples/subdomains/redirect/main.go
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
// Package main shows how to register a simple 'www' subdomain,
|
||||||
|
// using the `app.WWW` method, which will register a router wrapper which will
|
||||||
|
// redirect all 'mydomain.com' requests to 'www.mydomain.com'.
|
||||||
|
// Check the 'hosts' file to see how to test the 'mydomain.com' on your local machine.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/kataras/iris"
|
||||||
|
|
||||||
|
const addr = "mydomain.com:80"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := newApp()
|
||||||
|
|
||||||
|
// http(s)://mydomain.com, will be redirect to http(s)://www.mydomain.com.
|
||||||
|
// The `www` variable is the `app.Subdomain("www")`.
|
||||||
|
//
|
||||||
|
// app.WWW() wraps the router so it can redirect all incoming requests
|
||||||
|
// that comes from 'http(s)://mydomain.com/%path%' (www is missing)
|
||||||
|
// to `http(s)://www.mydomain.com/%path%`.
|
||||||
|
//
|
||||||
|
// Try:
|
||||||
|
// http://mydomain.com -> http://www.mydomain.com
|
||||||
|
// http://mydomain.com/users -> http://www.mydomain.com/users
|
||||||
|
// http://mydomain.com/users/login -> http://www.mydomain.com/users/login
|
||||||
|
app.Run(iris.Addr(addr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func newApp() *iris.Application {
|
||||||
|
app := iris.New()
|
||||||
|
app.Get("/", func(ctx iris.Context) {
|
||||||
|
ctx.Writef("This will never be executed.")
|
||||||
|
})
|
||||||
|
|
||||||
|
www := app.Subdomain("www") // <- same as app.Party("www.")
|
||||||
|
www.Get("/", index)
|
||||||
|
|
||||||
|
// www is an `iris.Party`, use it like you already know, like grouping routes.
|
||||||
|
www.PartyFunc("/users", func(p iris.Party) { // <- same as www.Party("/users").Get(...)
|
||||||
|
p.Get("/", usersIndex)
|
||||||
|
p.Get("/login", getLogin)
|
||||||
|
})
|
||||||
|
|
||||||
|
// redirects mydomain.com/%anypath% to www.mydomain.com/%anypath%.
|
||||||
|
// First argument is the 'from' and second is the 'to/target'.
|
||||||
|
app.SubdomainRedirect(app, www)
|
||||||
|
|
||||||
|
// If you need to redirect any subdomain to 'www' then:
|
||||||
|
// app.SubdomainRedirect(app.WildcardSubdomain(), www)
|
||||||
|
// If you need to redirect from a subdomain to the root domain then:
|
||||||
|
// app.SubdomainRedirect(app.Subdomain("mysubdomain"), app)
|
||||||
|
//
|
||||||
|
// Note that app.Party("mysubdomain.") and app.Subdomain("mysubdomain")
|
||||||
|
// is the same exactly thing, the difference is that the second can omit the last dot('.').
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
func index(ctx iris.Context) {
|
||||||
|
ctx.Writef("This is the www.mydomain.com endpoint.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func usersIndex(ctx iris.Context) {
|
||||||
|
ctx.Writef("This is the www.mydomain.com/users endpoint.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLogin(ctx iris.Context) {
|
||||||
|
ctx.Writef("This is the www.mydomain.com/users/login endpoint.")
|
||||||
|
}
|
33
_examples/subdomains/redirect/main_test.go
Normal file
33
_examples/subdomains/redirect/main_test.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/kataras/iris/httptest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSubdomainRedirectWWW(t *testing.T) {
|
||||||
|
app := newApp()
|
||||||
|
root := strings.TrimSuffix(addr, ":80")
|
||||||
|
|
||||||
|
e := httptest.New(t, app)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
path string
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
{"/", fmt.Sprintf("This is the www.%s endpoint.", root)},
|
||||||
|
{"/users", fmt.Sprintf("This is the www.%s/users endpoint.", root)},
|
||||||
|
{"/users/login", fmt.Sprintf("This is the www.%s/users/login endpoint.", root)},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
req := e.GET(test.path)
|
||||||
|
// req.WithURL("http://www." + root)
|
||||||
|
|
||||||
|
req.Expect().Status(httptest.StatusOK).Body().Equal(test.response)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -11,25 +11,41 @@ func newApp() *iris.Application {
|
||||||
app.Get("/about", info)
|
app.Get("/about", info)
|
||||||
app.Get("/contact", info)
|
app.Get("/contact", info)
|
||||||
|
|
||||||
|
app.PartyFunc("/api/users", func(r iris.Party) {
|
||||||
|
r.Get("/", info)
|
||||||
|
r.Get("/{id:int}", info)
|
||||||
|
|
||||||
|
r.Post("/", info)
|
||||||
|
|
||||||
|
r.Put("/{id:int}", info)
|
||||||
|
}) /* <- same as:
|
||||||
usersAPI := app.Party("/api/users")
|
usersAPI := app.Party("/api/users")
|
||||||
{
|
{ // those brackets are just syntactic-sugar things.
|
||||||
usersAPI.Get("/", info)
|
// This method is rarely used but you can make use of it when you want
|
||||||
usersAPI.Get("/{id:int}", info)
|
// scoped variables to that code block only.
|
||||||
|
usersAPI.Get/Post...
|
||||||
usersAPI.Post("/", info)
|
|
||||||
|
|
||||||
usersAPI.Put("/{id:int}", info)
|
|
||||||
}
|
}
|
||||||
|
usersAPI.Get/Post...
|
||||||
|
*/
|
||||||
|
|
||||||
www := app.Party("www.")
|
www := app.Party("www.")
|
||||||
{
|
{
|
||||||
// get all routes that are registered so far, including all "Parties" but subdomains:
|
// http://www.mydomain.com/hi
|
||||||
|
www.Get("/hi", func(ctx iris.Context) {
|
||||||
|
ctx.Writef("hi from www.mydomain.com")
|
||||||
|
})
|
||||||
|
|
||||||
|
// Just to show how you can get all routes and copy them to another
|
||||||
|
// party or subdomain:
|
||||||
|
// Get all routes that are registered so far, including all "Parties" but subdomains:
|
||||||
currentRoutes := app.GetRoutes()
|
currentRoutes := app.GetRoutes()
|
||||||
// register them to the www subdomain/vhost as well:
|
// Register them to the www subdomain/vhost as well:
|
||||||
for _, r := range currentRoutes {
|
for _, r := range currentRoutes {
|
||||||
www.Handle(r.Method, r.Path, r.Handlers...)
|
www.Handle(r.Method, r.Path, r.Handlers...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// See also the "subdomains/redirect" to register redirect router wrappers between subdomains,
|
||||||
|
// i.e mydomain.com to www.mydomain.com (like facebook does for SEO reasons(;)).
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
@ -43,6 +59,7 @@ func main() {
|
||||||
// http://mydomain.com/api/users/42
|
// http://mydomain.com/api/users/42
|
||||||
|
|
||||||
// http://www.mydomain.com
|
// http://www.mydomain.com
|
||||||
|
// http://www.mydomain.com/hi
|
||||||
// http://www.mydomain.com/about
|
// http://www.mydomain.com/about
|
||||||
// http://www.mydomain.com/contact
|
// http://www.mydomain.com/contact
|
||||||
// http://www.mydomain.com/api/users
|
// http://www.mydomain.com/api/users
|
||||||
|
|
|
@ -735,7 +735,7 @@ func WithConfiguration(c Configuration) Configurator {
|
||||||
|
|
||||||
if v := c.RemoteAddrHeaders; len(v) > 0 {
|
if v := c.RemoteAddrHeaders; len(v) > 0 {
|
||||||
if main.RemoteAddrHeaders == nil {
|
if main.RemoteAddrHeaders == nil {
|
||||||
main.RemoteAddrHeaders = make(map[string]bool)
|
main.RemoteAddrHeaders = make(map[string]bool, len(v))
|
||||||
}
|
}
|
||||||
for key, value := range v {
|
for key, value := range v {
|
||||||
main.RemoteAddrHeaders[key] = value
|
main.RemoteAddrHeaders[key] = value
|
||||||
|
@ -744,7 +744,7 @@ func WithConfiguration(c Configuration) Configurator {
|
||||||
|
|
||||||
if v := c.Other; len(v) > 0 {
|
if v := c.Other; len(v) > 0 {
|
||||||
if main.Other == nil {
|
if main.Other == nil {
|
||||||
main.Other = make(map[string]interface{})
|
main.Other = make(map[string]interface{}, len(v))
|
||||||
}
|
}
|
||||||
for key, value := range v {
|
for key, value := range v {
|
||||||
main.Other[key] = value
|
main.Other[key] = value
|
||||||
|
|
|
@ -366,6 +366,8 @@ type Context interface {
|
||||||
// Subdomain returns the subdomain of this request, if any.
|
// Subdomain returns the subdomain of this request, if any.
|
||||||
// Note that this is a fast method which does not cover all cases.
|
// Note that this is a fast method which does not cover all cases.
|
||||||
Subdomain() (subdomain string)
|
Subdomain() (subdomain string)
|
||||||
|
// IsWWW returns true if the current subdomain (if any) is www.
|
||||||
|
IsWWW() bool
|
||||||
// RemoteAddr tries to parse and return the real client's request IP.
|
// RemoteAddr tries to parse and return the real client's request IP.
|
||||||
//
|
//
|
||||||
// Based on allowed headers names that can be modified from Configuration.RemoteAddrHeaders.
|
// Based on allowed headers names that can be modified from Configuration.RemoteAddrHeaders.
|
||||||
|
@ -1311,11 +1313,16 @@ func (ctx *context) RequestPath(escape bool) string {
|
||||||
// return false
|
// return false
|
||||||
// } no, it will not work because map is a random peek data structure.
|
// } no, it will not work because map is a random peek data structure.
|
||||||
|
|
||||||
// Host returns the host part of the current url.
|
// Host returns the host part of the current URI.
|
||||||
func (ctx *context) Host() string {
|
func (ctx *context) Host() string {
|
||||||
h := ctx.request.URL.Host
|
return GetHost(ctx.request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHost returns the host part of the current URI.
|
||||||
|
func GetHost(r *http.Request) string {
|
||||||
|
h := r.URL.Host
|
||||||
if h == "" {
|
if h == "" {
|
||||||
h = ctx.request.Host
|
h = r.Host
|
||||||
}
|
}
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
@ -1338,6 +1345,18 @@ func (ctx *context) Subdomain() (subdomain string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsWWW returns true if the current subdomain (if any) is www.
|
||||||
|
func (ctx *context) IsWWW() bool {
|
||||||
|
host := ctx.Host()
|
||||||
|
if index := strings.IndexByte(host, '.'); index > 0 {
|
||||||
|
// if it has a subdomain and it's www then return true.
|
||||||
|
if subdomain := host[0:index]; !strings.Contains(ctx.Application().ConfigurationReadOnly().GetVHost(), subdomain) {
|
||||||
|
return subdomain == "www"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// RemoteAddr tries to parse and return the real client's request IP.
|
// RemoteAddr tries to parse and return the real client's request IP.
|
||||||
//
|
//
|
||||||
// Based on allowed headers names that can be modified from Configuration.RemoteAddrHeaders.
|
// Based on allowed headers names that can be modified from Configuration.RemoteAddrHeaders.
|
||||||
|
|
|
@ -195,8 +195,8 @@ func ResolvePort(addr string) int {
|
||||||
return 80
|
return 80
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveScheme returns "https://" if "isTLS" receiver is true,
|
// ResolveScheme returns "https" if "isTLS" receiver is true,
|
||||||
// otherwise "http://".
|
// otherwise "http".
|
||||||
func ResolveScheme(isTLS bool) string {
|
func ResolveScheme(isTLS bool) string {
|
||||||
if isTLS {
|
if isTLS {
|
||||||
return SchemeHTTPS
|
return SchemeHTTPS
|
||||||
|
|
|
@ -111,6 +111,14 @@ func NewAPIBuilder() *APIBuilder {
|
||||||
return api
|
return api
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRelPath returns the current party's relative path.
|
||||||
|
// i.e:
|
||||||
|
// if r := app.Party("/users"), then the `r.GetRelPath()` is the "/users".
|
||||||
|
// if r := app.Party("www.") or app.Subdomain("www") then the `r.GetRelPath()` is the "www.".
|
||||||
|
func (api *APIBuilder) GetRelPath() string {
|
||||||
|
return api.relativePath
|
||||||
|
}
|
||||||
|
|
||||||
// GetReport returns an error may caused by party's methods.
|
// GetReport returns an error may caused by party's methods.
|
||||||
func (api *APIBuilder) GetReport() error {
|
func (api *APIBuilder) GetReport() error {
|
||||||
return api.reporter.Return()
|
return api.reporter.Return()
|
||||||
|
@ -292,7 +300,7 @@ func (api *APIBuilder) PartyFunc(relativePath string, partyBuilderFunc func(p Pa
|
||||||
// this specific "subdomain".
|
// this specific "subdomain".
|
||||||
//
|
//
|
||||||
// If called from a child party then the subdomain will be prepended to the path instead of appended.
|
// If called from a child party then the subdomain will be prepended to the path instead of appended.
|
||||||
// So if app.Subdomain("admin.").Subdomain("panel.") then the result is: "panel.admin.".
|
// So if app.Subdomain("admin").Subdomain("panel") then the result is: "panel.admin.".
|
||||||
func (api *APIBuilder) Subdomain(subdomain string, middleware ...context.Handler) Party {
|
func (api *APIBuilder) Subdomain(subdomain string, middleware ...context.Handler) Party {
|
||||||
if api.relativePath == SubdomainWildcardIndicator {
|
if api.relativePath == SubdomainWildcardIndicator {
|
||||||
// cannot concat wildcard subdomain with something else
|
// cannot concat wildcard subdomain with something else
|
||||||
|
@ -300,6 +308,12 @@ func (api *APIBuilder) Subdomain(subdomain string, middleware ...context.Handler
|
||||||
api.relativePath, subdomain)
|
api.relativePath, subdomain)
|
||||||
return api
|
return api
|
||||||
}
|
}
|
||||||
|
if l := len(subdomain); l < 1 {
|
||||||
|
return api
|
||||||
|
} else if subdomain[l-1] != '.' {
|
||||||
|
subdomain += "."
|
||||||
|
}
|
||||||
|
|
||||||
return api.Party(subdomain, middleware...)
|
return api.Party(subdomain, middleware...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,11 @@ import (
|
||||||
//
|
//
|
||||||
// Look the "APIBuilder" for its implementation.
|
// Look the "APIBuilder" for its implementation.
|
||||||
type Party interface {
|
type Party interface {
|
||||||
|
// GetRelPath returns the current party's relative path.
|
||||||
|
// i.e:
|
||||||
|
// if r := app.Party("/users"), then the `r.GetRelPath()` is the "/users".
|
||||||
|
// if r := app.Party("www.") or app.Subdomain("www") then the `r.GetRelPath()` is the "www.".
|
||||||
|
GetRelPath() string
|
||||||
// GetReporter returns the reporter for adding errors
|
// GetReporter returns the reporter for adding errors
|
||||||
GetReporter() *errors.Reporter
|
GetReporter() *errors.Reporter
|
||||||
// Macros returns the macro map which is responsible
|
// Macros returns the macro map which is responsible
|
||||||
|
|
|
@ -114,13 +114,13 @@ type WrapperFunc func(w http.ResponseWriter, r *http.Request, firstNextIsTheRout
|
||||||
//
|
//
|
||||||
// Before build.
|
// Before build.
|
||||||
func (router *Router) WrapRouter(wrapperFunc WrapperFunc) {
|
func (router *Router) WrapRouter(wrapperFunc WrapperFunc) {
|
||||||
router.mu.Lock()
|
|
||||||
defer router.mu.Unlock()
|
|
||||||
|
|
||||||
if wrapperFunc == nil {
|
if wrapperFunc == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
router.mu.Lock()
|
||||||
|
defer router.mu.Unlock()
|
||||||
|
|
||||||
if router.wrapperFunc != nil {
|
if router.wrapperFunc != nil {
|
||||||
// wrap into one function, from bottom to top, end to begin.
|
// wrap into one function, from bottom to top, end to begin.
|
||||||
nextWrapper := wrapperFunc
|
nextWrapper := wrapperFunc
|
||||||
|
|
163
core/router/router_subdomain_redirect_wrapper.go
Normal file
163
core/router/router_subdomain_redirect_wrapper.go
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/kataras/iris/context"
|
||||||
|
"github.com/kataras/iris/core/netutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
type subdomainRedirectWrapper struct {
|
||||||
|
// the func which will give us the root domain,
|
||||||
|
// it's declared as a func because in that state the application is not configurated neither ran yet.
|
||||||
|
root func() string
|
||||||
|
// the from and to locations, if subdomains must end with dot('.').
|
||||||
|
from, to string
|
||||||
|
// true if from wildcard subdomain is given by 'from' ("*." or '*').
|
||||||
|
isFromAny bool
|
||||||
|
// true for the location that is the root domain ('/', '.' or "").
|
||||||
|
isFromRoot, isToRoot bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathIsRootDomain(partyRelPath string) bool {
|
||||||
|
return partyRelPath == "/" || partyRelPath == "" || partyRelPath == "."
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathIsWildcard(partyRelPath string) bool {
|
||||||
|
return partyRelPath == SubdomainWildcardIndicator || partyRelPath == "*"
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSubdomainRedirectWrapper returns a router wrapper which
|
||||||
|
// if it's registered to the router via `router#WrapRouter` it
|
||||||
|
// redirects(StatusMovedPermanently) a subdomain or the root domain to another subdomain or to the root domain.
|
||||||
|
//
|
||||||
|
// It receives three arguments,
|
||||||
|
// the first one is a function which returns the root domain, (in the application it's the app.ConfigurationReadOnly().GetVHost()).
|
||||||
|
// The second and third are the from and to locations, 'from' can be a wildcard subdomain as well (*. or *)
|
||||||
|
// 'to' is not allowed to be a wildcard for obvious reasons,
|
||||||
|
// 'from' can be the root domain when the 'to' is not the root domain and visa-versa.
|
||||||
|
// To declare a root domain as 'from' or 'to' you MUST pass an empty string or a slash('/') or a dot('.').
|
||||||
|
// Important note: the 'from' and 'to' should end with "." like we use the `APIBuilder#Party`, if they are subdomains.
|
||||||
|
//
|
||||||
|
// Usage(package-level):
|
||||||
|
// sd := NewSubdomainRedirectWrapper(func() string { return "mydomain.com" }, ".", "www.")
|
||||||
|
// router.WrapRouter(sd)
|
||||||
|
//
|
||||||
|
// Usage(high-level using `iris#Application.SubdomainRedirect`)
|
||||||
|
// www := app.Subdomain("www")
|
||||||
|
// app.SubdomainRedirect(app, www)
|
||||||
|
// Because app's rel path is "/" it translates it to the root domain
|
||||||
|
// and www's party's rel path is the "www.", so it's the target subdomain.
|
||||||
|
//
|
||||||
|
// All the above code snippets will register a router wrapper which will
|
||||||
|
// redirect all http(s)://mydomain.com/%anypath% to http(s)://www.mydomain.com/%anypath%.
|
||||||
|
//
|
||||||
|
// One or more subdomain redirect wrappers can be used to the same router instance.
|
||||||
|
//
|
||||||
|
// NewSubdomainRedirectWrapper may return nil if not allowed input arguments values were received
|
||||||
|
// but in that case, the `WrapRouter` will, simply, ignore that wrapper.
|
||||||
|
//
|
||||||
|
// Example: https://github.com/kataras/iris/tree/master/_examples/subdomains/redirect
|
||||||
|
func NewSubdomainRedirectWrapper(rootDomainGetter func() string, from, to string) WrapperFunc {
|
||||||
|
// we can return nil,
|
||||||
|
// because if wrapper is nil then it's not be used on the `router#WrapRouter`.
|
||||||
|
if from == to {
|
||||||
|
// cannot redirect to the same location, cycle.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if pathIsWildcard(to) {
|
||||||
|
// cannot redirect to "any location".
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
isFromRoot, isToRoot := pathIsRootDomain(from), pathIsRootDomain(to)
|
||||||
|
if isFromRoot && isToRoot {
|
||||||
|
// cannot redirect to the root domain from the root domain.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sd := &subdomainRedirectWrapper{
|
||||||
|
root: rootDomainGetter,
|
||||||
|
from: from,
|
||||||
|
to: to,
|
||||||
|
isFromAny: pathIsWildcard(from),
|
||||||
|
isFromRoot: isFromRoot,
|
||||||
|
isToRoot: isToRoot,
|
||||||
|
}
|
||||||
|
|
||||||
|
return sd.Wrapper
|
||||||
|
}
|
||||||
|
|
||||||
|
const sufscheme = "://"
|
||||||
|
|
||||||
|
func getFullScheme(r *http.Request) string {
|
||||||
|
if !r.URL.IsAbs() {
|
||||||
|
// url scheme is empty.
|
||||||
|
return netutil.SchemeHTTP + sufscheme
|
||||||
|
}
|
||||||
|
return r.URL.Scheme + sufscheme
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapper is the function that is being used to wrap the router with a redirect
|
||||||
|
// service that is able to redirect between (sub)domains as fast as possible.
|
||||||
|
// Please take a look at the `NewSubdomainRedirectWrapper` function for more.
|
||||||
|
func (s *subdomainRedirectWrapper) Wrapper(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {
|
||||||
|
// Author's note:
|
||||||
|
// I use the StatusMovedPermanently(301) instead of the the StatusPermanentRedirect(308)
|
||||||
|
// because older browsers may not be able to recognise that status code (the RFC 7538, is not so old)
|
||||||
|
// although note that move is not the same thing as redirect: move reminds a specific address or location moved while
|
||||||
|
// redirect is a new location.
|
||||||
|
|
||||||
|
host := context.GetHost(r)
|
||||||
|
root := s.root()
|
||||||
|
hasSubdomain := host != root
|
||||||
|
|
||||||
|
if !hasSubdomain && !s.isFromRoot {
|
||||||
|
// if the current endpoint is not a subdomain
|
||||||
|
// and the redirect is not configured to be used from root domain to a subdomain.
|
||||||
|
// This check comes first because it's the most common scenario.
|
||||||
|
router(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasSubdomain {
|
||||||
|
// the current endpoint is a subdomain and
|
||||||
|
// redirect is used for a subdomain to another subdomain or to its root domain.
|
||||||
|
subdomain := strings.TrimSuffix(host, root) // with dot '.'.
|
||||||
|
if s.to == subdomain {
|
||||||
|
// we are in the subdomain we wanted to be redirected,
|
||||||
|
// remember: a redirect response will fire a new request.
|
||||||
|
// This check is needed to not allow cycles (too many redirects).
|
||||||
|
router(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if subdomain == s.from || s.isFromAny {
|
||||||
|
resturi := r.URL.RequestURI()
|
||||||
|
if s.isToRoot {
|
||||||
|
// from a specific subdomain or any subdomain to the root domain.
|
||||||
|
http.Redirect(w, r, getFullScheme(r)+root+resturi, http.StatusMovedPermanently)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// from a specific subdomain or any subdomain to a specific subdomain.
|
||||||
|
http.Redirect(w, r, getFullScheme(r)+s.to+root+resturi, http.StatusMovedPermanently)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// the from subdomain is not matched and it's not from root.
|
||||||
|
router(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.isFromRoot {
|
||||||
|
resturi := r.URL.RequestURI()
|
||||||
|
// we are not inside a subdomain, so we are in the root domain
|
||||||
|
// and the redirect is configured to be used from root domain to a subdomain.
|
||||||
|
http.Redirect(w, r, getFullScheme(r)+s.to+root+resturi, http.StatusMovedPermanently)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
router(w, r)
|
||||||
|
}
|
35
iris.go
35
iris.go
|
@ -187,6 +187,41 @@ func Default() *Application {
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WWW creates and returns a "www." subdomain.
|
||||||
|
// The difference from `app.Subdomain("www")` or `app.Party("www.")` is that the `app.WWW()` method
|
||||||
|
// wraps the router so all http(s)://mydomain.com will be redirect to http(s)://www.mydomain.com.
|
||||||
|
// Other subdomains can be registered using the app: `sub := app.Subdomain("mysubdomain")`,
|
||||||
|
// child subdomains can be registered using the www := app.WWW(); www.Subdomain("wwwchildSubdomain").
|
||||||
|
func (app *Application) WWW() router.Party {
|
||||||
|
return app.SubdomainRedirect(app, app.Subdomain("www"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubdomainRedirect registers a router wrapper which
|
||||||
|
// redirects(StatusMovedPermanently) a (sub)domain to another subdomain or to the root domain as fast as possible,
|
||||||
|
// before the router's try to execute route's handler(s).
|
||||||
|
//
|
||||||
|
// It receives two arguments, they are the from and to/target locations,
|
||||||
|
// 'from' can be a wildcard subdomain as well (app.WildcardSubdomain())
|
||||||
|
// 'to' is not allowed to be a wildcard for obvious reasons,
|
||||||
|
// 'from' can be the root domain(app) when the 'to' is not the root domain and visa-versa.
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
// www := app.Subdomain("www") <- same as app.Party("www.")
|
||||||
|
// app.SubdomainRedirect(app, www)
|
||||||
|
// This will redirect all http(s)://mydomain.com/%anypath% to http(s)://www.mydomain.com/%anypath%.
|
||||||
|
//
|
||||||
|
// One or more subdomain redirects can be used to the same app instance.
|
||||||
|
//
|
||||||
|
// If you need more information about this implementation then you have to navigate through
|
||||||
|
// the `core/router#NewSubdomainRedirectWrapper` function instead.
|
||||||
|
//
|
||||||
|
// Example: https://github.com/kataras/iris/tree/master/_examples/subdomains/redirect
|
||||||
|
func (app *Application) SubdomainRedirect(from, to router.Party) router.Party {
|
||||||
|
sd := router.NewSubdomainRedirectWrapper(app.ConfigurationReadOnly().GetVHost, from.GetRelPath(), to.GetRelPath())
|
||||||
|
app.WrapRouter(sd)
|
||||||
|
return to
|
||||||
|
}
|
||||||
|
|
||||||
// Configure can called when modifications to the framework instance needed.
|
// Configure can called when modifications to the framework instance needed.
|
||||||
// It accepts the framework instance
|
// It accepts the framework instance
|
||||||
// and returns an error which if it's not nil it's printed to the logger.
|
// and returns an error which if it's not nil it's printed to the logger.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user