Skip to content

Commit

Permalink
matcher vs matcher;keep both on the v1 branch and not master, until I…
Browse files Browse the repository at this point in the history
… decide the final design.
  • Loading branch information
kataras committed Oct 19, 2018
1 parent d52ecc7 commit aa57354
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 42 deletions.
25 changes: 16 additions & 9 deletions _examples/9_subdomains/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"fmt"
"net/http"
"strings"

"github.com/kataras/muxie"
)
Expand All @@ -16,26 +17,32 @@ func main() {
mySubdomain.HandleFunc("/", handleMySubdomainIndex)
mySubdomain.HandleFunc("/about", aboutHandler)

mux.Hosts["mysubdomain.localhost:8080"] = mySubdomain
mux.MatchHost("mysubdomain.localhost:8080", mySubdomain)

// mysubsubdomain.mysubdomain
mySubSubdomain := muxie.NewMux()
// mySubSubdomain := muxie.NewMux()
// mySubSubdomain.HandleFunc("/", handleMySubSubdomainIndex)
// mySubSubdomain.HandleFunc("/about", aboutHandler)
// mux.MatchHost("mysubsubdomain.mysubdomain.localhost:8080", mySubSubdomain)

// the advandage of the below is that we are able to continue with mySubSubdomain.If()..., it can be embedded
// but the same for the above, user is able to add matchers.
// Keep both on the v1 branch and not master, until I decide the final design.
mySubSubdomain := mux.If(muxie.Host("mysubsubdomain.mysubdomain.localhost:8080"))
mySubSubdomain.HandleFunc("/", handleMySubSubdomainIndex)
mySubSubdomain.HandleFunc("/about", aboutHandler)

mux.Hosts["mysubsubdomain.mysubdomain.localhost:8080"] = mySubSubdomain

// any other subdomain
myWildcardSubdomain := muxie.NewMux()
myWildcardSubdomain.HandleFunc("/", handleMyWildcardSubdomainIndex)

// Catch any other subdomain.
// Catch any other host that it is not our main localhost:8080.
// Extremely useful for apps that may need dynamic subdomains based on a database,
// usernames for example.
mux.Hosts["*"] = myWildcardSubdomain

// Browser will automatically work but to use an http client or POSTMAN
// you may want to edit your hosts, i.e on windows is going like this:
mux.Match(func(r *http.Request) bool { return strings.HasSuffix(r.Host, ".localhost:8080") }, myWildcardSubdomain)
// Chrome-based browsers will automatically work but to test with
// firefox or a custom http client or POSTMAN you may want to edit your hosts,
// i.e on windows is going like this:
// 127.0.0.1 mysubdomain.localhost
// 127.0.0.1 mysubsubdomain.mysubdomain.localhost
//
Expand Down
128 changes: 95 additions & 33 deletions mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,10 @@ import (
//
// See `NewMux`.
type Mux struct {
// Hosts is the optional map of hosts to routers.
// The key is the exactly host line, i.e mysubdomain.mydomain.com:8080
// including the domain and the port OR an asterix "*" to match any subdomain.
// The value is any http.Handler, let's say a new muxie.NewMux() which will build
// the subdomain's API.
//
// These hosts are not sharing begin handlers registered via `Use`,
// that is a choice that end-developer's have to take.
// You can share middlewares between different Mux instances
// by using the `muxie.Pre` to define the list of handlers that should ran everywhere
// and add them to each Mux instance, i.e:
// ... mySharingMiddlewares := muxie.Pre(myGlobalRootLogMiddleware, ...)
// ... mux := muxie.NewMux()
// ... mux.Use(mySharingMiddlewares...)
// ...
// ... mySubdomain := muxie.NewMux()
// ... mySubdomain.Use(mySharingMiddlewares...)
// ...
// ... mux.Hosts["mysubdomain.localhost:8080"] = mySubdomain
Hosts map[string]http.Handler /* Design notes, the latest one was selected:
/* Hosts map[string]http.Handler
:: Design notes, none accepted ::
[1]
mySubdomain := mux.WithHost("mysubdomain")
mySubdomain.HandleFunc("/", handleMySubdomainIndex)
Expand All @@ -66,6 +50,32 @@ type Mux struct {
With [2] and [3] we win simplicity and subdomains are not even coupled to this library,
end-developer can use any http.Handler to serve those.
Simplest: [3], choose that.
^ Discarded because on advanced scenarios the end-developer may want to fetch the data
at real-time, so we need a way to execute a validator before the host handler execution.
Replaced with the `Matcher`, `Mux#Match` and `Mux#MatchHost`.
:: Usage::
Hosts is the optional map of hosts to routers.
The key is the exactly host line, i.e mysubdomain.mydomain.com:8080
including the domain and the port OR an asterix "*" to match any subdomain.
The value is any http.Handler, let's say a new muxie.NewMux() which will build
the subdomain's API.
These hosts are not sharing begin handlers registered via `Use`,
that is a choice that end-developer's have to take.
You can share middlewares between different Mux instances
by using the `muxie.Pre` to define the list of handlers that should ran everywhere
and add them to each Mux instance, i.e:
... mySharingMiddlewares := muxie.Pre(myGlobalRootLogMiddleware, ...)
... mux := muxie.NewMux()
... mux.Use(mySharingMiddlewares...)
...
... mySubdomain := muxie.NewMux()
... mySubdomain.Use(mySharingMiddlewares...)
...
... mux.Hosts["mysubdomain.localhost:8080"] = mySubdomain
*/

PathCorrection bool
Expand All @@ -75,6 +85,7 @@ type Mux struct {

// per mux
root string
matchers []Matcher
beginHandlers []Wrapper
}

Expand All @@ -88,11 +99,64 @@ func NewMux() *Mux {
return &paramsWriter{}
},
},
root: "",
Hosts: make(map[string]http.Handler),
root: "",
}
}

type Matcher interface {
http.Handler
Match(*http.Request) bool
}

type simpleMatcher struct {
matchFunc func(*http.Request) bool
handler http.Handler
}

func (m *simpleMatcher) Match(r *http.Request) bool {
return m.matchFunc(r)
}

func (m *simpleMatcher) ServeHTTP(w http.ResponseWriter, r *http.Request) {
m.handler.ServeHTTP(w, r)
}

/* TODO THINKING:
mux.If(matcher(request)bool) -> returns a SubMux, or "when":
mux.When(muxie.Host("mysubdomain.localhost:8080")).Handle(mysubdomainHandler)
muxie.Host -> a func(string) which will implement the matcher func(request)bool
*/

func (m *Mux) If(matcher func(*http.Request) bool) SubMux {
subUnlinkedMux := NewMux()
m.matchers = append(m.matchers, &simpleMatcher{matcher, subUnlinkedMux})
return subUnlinkedMux
}

// type Host string

// func (s Host) Match(r *http.Request) bool {
// return r.Host == string(s) || string(s) == WildcardParamStart
// }

func Host(host string) func(r *http.Request) bool {
return func(r *http.Request) bool {
return r.Host == host || host == WildcardParamStart
}
}

func (m *Mux) Match(matchFunc func(*http.Request) bool, handler http.Handler) {
m.matchers = append(m.matchers, &simpleMatcher{matchFunc, handler})
}

func (m *Mux) MatchHost(host string, handler http.Handler) {
m.Match(func(r *http.Request) bool {
return r.Host == host || host == WildcardParamStart
}, handler)
}

// Use adds middleware that should be called before each mux route's main handler.
// Should be called before `Handle/HandleFunc`. Order matters.
//
Expand Down Expand Up @@ -160,14 +224,9 @@ func (m *Mux) HandleFunc(pattern string, handlerFunc func(http.ResponseWriter, *

// ServeHTTP exposes and serves the registered routes.
func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if m.Hosts != nil {
if h, ok := m.Hosts[r.Host]; ok {
h.ServeHTTP(w, r)
return
}

if wildcardSubdomainHandler, ok := m.Hosts[WildcardParamStart]; ok {
wildcardSubdomainHandler.ServeHTTP(w, r)
for _, matcher := range m.matchers {
if matcher.Match(r) {
matcher.ServeHTTP(w, r)
return
}
}
Expand Down Expand Up @@ -231,6 +290,7 @@ func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
type SubMux interface {
Of(prefix string) SubMux
Unlink() SubMux
Match(matchFunc func(*http.Request) bool, handler http.Handler)
Use(middlewares ...Wrapper)
Handle(pattern string, handler http.Handler)
HandleFunc(pattern string, handlerFunc func(http.ResponseWriter, *http.Request))
Expand Down Expand Up @@ -264,11 +324,11 @@ func (m *Mux) Of(prefix string) SubMux {
prefix = pathSep + strings.Trim(m.root+prefix, pathSep)

return &Mux{
Hosts: m.Hosts,
Routes: m.Routes,

root: prefix,
beginHandlers: append([]Wrapper{}, m.beginHandlers...),
matchers: m.matchers[0:],
beginHandlers: m.beginHandlers[0:],
}
}

Expand Down Expand Up @@ -297,9 +357,11 @@ breaking changes and etc. This `Unlink`, which will return the same SubMux, it c
//
// mux := NewMux()
// mux.Use(myLoggerMiddleware)
// v1 := mux.Of("/v1").Unlink() // v1 will no longer have the "myLoggerMiddleware".
// v1 := mux.Of("/v1").Unlink() // v1 will no longer have the "myLoggerMiddleware" or any Matchers.
// v1.HandleFunc("/users", myHandler)
func (m *Mux) Unlink() SubMux {
m.matchers = m.matchers[0:0]
m.beginHandlers = m.beginHandlers[0:0]

return m
}

0 comments on commit aa57354

Please sign in to comment.