Add PathPrefixStrip and PathStrip rules
Signed-off-by: Emile Vauge <emile@vauge.com>
This commit is contained in:
parent
b84b95fe97
commit
122783e36b
4 changed files with 82 additions and 36 deletions
|
@ -37,7 +37,9 @@ Frontends can be defined using the following rules:
|
||||||
- `Host`: Host adds a matcher for the URL host. It accepts a template with zero or more URL variables enclosed by `{}`. Variables can define an optional regexp pattern to be matched: `www.traefik.io`, `{subdomain:[a-z]+}.traefik.io`
|
- `Host`: Host adds a matcher for the URL host. It accepts a template with zero or more URL variables enclosed by `{}`. Variables can define an optional regexp pattern to be matched: `www.traefik.io`, `{subdomain:[a-z]+}.traefik.io`
|
||||||
- `Methods`: Methods adds a matcher for HTTP methods. It accepts a sequence of one or more methods to be matched, e.g.: `GET`, `POST`, `PUT`
|
- `Methods`: Methods adds a matcher for HTTP methods. It accepts a sequence of one or more methods to be matched, e.g.: `GET`, `POST`, `PUT`
|
||||||
- `Path`: Path adds a matcher for the URL path. It accepts a template with zero or more URL variables enclosed by `{}`. The template must start with a `/`. For exemple `/products/` `/articles/{category}/{id:[0-9]+}`
|
- `Path`: Path adds a matcher for the URL path. It accepts a template with zero or more URL variables enclosed by `{}`. The template must start with a `/`. For exemple `/products/` `/articles/{category}/{id:[0-9]+}`
|
||||||
|
- `PathStrip`: Same as `Path` but strip the given prefix from the request URL's Path.
|
||||||
- `PathPrefix`: PathPrefix adds a matcher for the URL path prefix. This matches if the given template is a prefix of the full URL path.
|
- `PathPrefix`: PathPrefix adds a matcher for the URL path prefix. This matches if the given template is a prefix of the full URL path.
|
||||||
|
- `PathPrefixStrip`: Same as `PathPrefix` but strip the given prefix from the request URL's Path.
|
||||||
|
|
||||||
|
|
||||||
A frontend is a set of rules that forwards the incoming http traffic to a backend.
|
A frontend is a set of rules that forwards the incoming http traffic to a backend.
|
||||||
|
|
22
middlewares/StripPrefix.go
Normal file
22
middlewares/StripPrefix.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StripPrefix is a middleware used to strip prefix from an URL request
|
||||||
|
type StripPrefix struct {
|
||||||
|
Handler http.Handler
|
||||||
|
Prefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StripPrefix) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if p := strings.TrimPrefix(r.URL.Path, s.Prefix); len(p) < len(r.URL.Path) {
|
||||||
|
r.URL.Path = p
|
||||||
|
r.RequestURI = r.URL.RequestURI()
|
||||||
|
s.Handler.ServeHTTP(w, r)
|
||||||
|
} else {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
}
|
||||||
|
}
|
71
server.go
71
server.go
|
@ -7,6 +7,16 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/codegangsta/negroni"
|
"github.com/codegangsta/negroni"
|
||||||
|
@ -18,16 +28,6 @@ import (
|
||||||
"github.com/mailgun/oxy/cbreaker"
|
"github.com/mailgun/oxy/cbreaker"
|
||||||
"github.com/mailgun/oxy/forward"
|
"github.com/mailgun/oxy/forward"
|
||||||
"github.com/mailgun/oxy/roundrobin"
|
"github.com/mailgun/oxy/roundrobin"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"sort"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var oxyLogger = &OxyLogger{}
|
var oxyLogger = &OxyLogger{}
|
||||||
|
@ -335,11 +335,11 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
|
||||||
newRoute := serverEntryPoints[entryPointName].httpRouter.NewRoute().Name(frontendName)
|
newRoute := serverEntryPoints[entryPointName].httpRouter.NewRoute().Name(frontendName)
|
||||||
for routeName, route := range frontend.Routes {
|
for routeName, route := range frontend.Routes {
|
||||||
log.Debugf("Creating route %s %s:%s", routeName, route.Rule, route.Value)
|
log.Debugf("Creating route %s %s:%s", routeName, route.Rule, route.Value)
|
||||||
newRouteReflect, err := invoke(newRoute, route.Rule, route.Value)
|
route, err := getRoute(newRoute, route.Rule, route.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
newRoute = newRouteReflect[0].Interface().(*mux.Route)
|
newRoute = route
|
||||||
}
|
}
|
||||||
entryPoint := globalConfiguration.EntryPoints[entryPointName]
|
entryPoint := globalConfiguration.EntryPoints[entryPointName]
|
||||||
if entryPoint.Redirect != nil {
|
if entryPoint.Redirect != nil {
|
||||||
|
@ -399,7 +399,7 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
|
||||||
} else {
|
} else {
|
||||||
log.Debugf("Reusing backend %s", frontend.Backend)
|
log.Debugf("Reusing backend %s", frontend.Backend)
|
||||||
}
|
}
|
||||||
newRoute.Handler(backends[frontend.Backend])
|
server.wireFrontendBackend(frontend.Routes, newRoute, backends[frontend.Backend])
|
||||||
}
|
}
|
||||||
err := newRoute.GetError()
|
err := newRoute.GetError()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -411,6 +411,32 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
|
||||||
return serverEntryPoints, nil
|
return serverEntryPoints, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (server *Server) wireFrontendBackend(routes map[string]types.Route, newRoute *mux.Route, handler http.Handler) {
|
||||||
|
// strip prefix
|
||||||
|
var strip bool
|
||||||
|
for _, route := range routes {
|
||||||
|
switch route.Rule {
|
||||||
|
case "PathStrip":
|
||||||
|
newRoute.Handler(&middlewares.StripPrefix{
|
||||||
|
Prefix: route.Value,
|
||||||
|
Handler: handler,
|
||||||
|
})
|
||||||
|
strip = true
|
||||||
|
break
|
||||||
|
case "PathPrefixStrip":
|
||||||
|
newRoute.Handler(&middlewares.StripPrefix{
|
||||||
|
Prefix: route.Value,
|
||||||
|
Handler: handler,
|
||||||
|
})
|
||||||
|
strip = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !strip {
|
||||||
|
newRoute.Handler(handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (server *Server) loadEntryPointConfig(entryPointName string, entryPoint *EntryPoint) (http.Handler, error) {
|
func (server *Server) loadEntryPointConfig(entryPointName string, entryPoint *EntryPoint) (http.Handler, error) {
|
||||||
regex := entryPoint.Redirect.Regex
|
regex := entryPoint.Redirect.Regex
|
||||||
replacement := entryPoint.Redirect.Replacement
|
replacement := entryPoint.Redirect.Replacement
|
||||||
|
@ -443,9 +469,28 @@ func (server *Server) loadEntryPointConfig(entryPointName string, entryPoint *En
|
||||||
func (server *Server) buildDefaultHTTPRouter() *mux.Router {
|
func (server *Server) buildDefaultHTTPRouter() *mux.Router {
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
router.NotFoundHandler = http.HandlerFunc(notFoundHandler)
|
router.NotFoundHandler = http.HandlerFunc(notFoundHandler)
|
||||||
|
router.StrictSlash(true)
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getRoute(any interface{}, rule string, value ...interface{}) (*mux.Route, error) {
|
||||||
|
switch rule {
|
||||||
|
case "PathStrip":
|
||||||
|
rule = "Path"
|
||||||
|
case "PathPrefixStrip":
|
||||||
|
rule = "PathPrefix"
|
||||||
|
}
|
||||||
|
inputs := make([]reflect.Value, len(value))
|
||||||
|
for i := range value {
|
||||||
|
inputs[i] = reflect.ValueOf(value[i])
|
||||||
|
}
|
||||||
|
method := reflect.ValueOf(any).MethodByName(rule)
|
||||||
|
if method.IsValid() {
|
||||||
|
return method.Call(inputs)[0].Interface().(*mux.Route), nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("Method not found: " + rule)
|
||||||
|
}
|
||||||
|
|
||||||
func sortedFrontendNamesForConfig(configuration *types.Configuration) []string {
|
func sortedFrontendNamesForConfig(configuration *types.Configuration) []string {
|
||||||
keys := []string{}
|
keys := []string{}
|
||||||
|
|
||||||
|
|
23
utils.go
23
utils.go
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright
|
|
||||||
*/
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Invoke calls the specified method with the specified arguments on the specified interface.
|
|
||||||
// It uses the go(lang) reflect package.
|
|
||||||
func invoke(any interface{}, name string, args ...interface{}) ([]reflect.Value, error) {
|
|
||||||
inputs := make([]reflect.Value, len(args))
|
|
||||||
for i := range args {
|
|
||||||
inputs[i] = reflect.ValueOf(args[i])
|
|
||||||
}
|
|
||||||
method := reflect.ValueOf(any).MethodByName(name)
|
|
||||||
if method.IsValid() {
|
|
||||||
return method.Call(inputs), nil
|
|
||||||
}
|
|
||||||
return nil, errors.New("Method not found: " + name)
|
|
||||||
}
|
|
Loading…
Reference in a new issue