add rewrite middleware
This commit is contained in:
Generated
+7
-1
@@ -111,6 +111,12 @@
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/pkg4go/rewrite"
|
||||
packages = ["."]
|
||||
revision = "7a613570d084fe7506c069d2cd8216d4795bd7e0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
@@ -195,6 +201,6 @@
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "64f6f97f932d2714aa7eb0b94e94bb555feb5ab9c33673a7731103f4cee9157f"
|
||||
inputs-digest = "03e0673f56f51384e8528f760cd44327828e5ef158b97bbbebc678c52cec9cc3"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
||||
@@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/pkg4go/rewrite"
|
||||
"github.com/umputun/remark/app/rest"
|
||||
)
|
||||
|
||||
@@ -155,5 +157,35 @@ func Logger(flags ...LoggerFlag) func(http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// Rewrite middleware with from->to rule. Supports regex (like nginx) and prevents multiple rewrites
|
||||
func Rewrite(from, to string) func(http.Handler) http.Handler {
|
||||
rule, err := rewrite.NewRule(from, to)
|
||||
if err != nil {
|
||||
log.Printf("[WARN] can't parse rewrite rule %s - > %s, %s", from, to, err)
|
||||
}
|
||||
|
||||
f := func(h http.Handler) http.Handler {
|
||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
ctx := r.Context()
|
||||
// prevent double rewrites
|
||||
if ctx != nil {
|
||||
if _, ok := ctx.Value(rest.ContextKey("rewrite")).(bool); ok {
|
||||
h.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
rule.Rewrite(r)
|
||||
ctx = context.WithValue(ctx, rest.ContextKey("rewrite"), true)
|
||||
r = r.WithContext(ctx)
|
||||
}
|
||||
h.ServeHTTP(w, r)
|
||||
}
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
.DS_Store
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.5
|
||||
- 1.6
|
||||
- tip
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
|
||||
[![Build status][travis-img]][travis-url]
|
||||
[![License][license-img]][license-url]
|
||||
[![GoDoc][doc-img]][doc-url]
|
||||
|
||||
### rewrite
|
||||
|
||||
golang URL rewriting
|
||||
|
||||
### Usage
|
||||
|
||||
```go
|
||||
import "github.com/pkg4go/rewrite"
|
||||
|
||||
// ...
|
||||
|
||||
handler := rewrite.NewHandler(map[string]string{
|
||||
"/a": "/b",
|
||||
"/api/(.*)", "/api/v1/$1",
|
||||
"/api/(.*)/actions/(.*)", "/api/v1/$1/actions/$2",
|
||||
"/from/:one/to/:two", "/from/:two/to/:one",
|
||||
})
|
||||
|
||||
// ...
|
||||
```
|
||||
|
||||
### License
|
||||
MIT
|
||||
|
||||
[travis-img]: https://img.shields.io/travis/pkg4go/rewrite.svg?style=flat-square
|
||||
[travis-url]: https://travis-ci.org/pkg4go/rewrite
|
||||
[license-img]: https://img.shields.io/badge/license-MIT-green.svg?style=flat-square
|
||||
[license-url]: http://opensource.org/licenses/MIT
|
||||
[doc-img]: https://img.shields.io/badge/GoDoc-reference-blue.svg?style=flat-square
|
||||
[doc-url]: http://godoc.org/github.com/pkg4go/rewrite
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package rewrite
|
||||
|
||||
import "net/http"
|
||||
import "net/url"
|
||||
import "strings"
|
||||
import "regexp"
|
||||
import "path"
|
||||
import "fmt"
|
||||
|
||||
const headerField = "X-Rewrite-Original-URI"
|
||||
|
||||
type Rule struct {
|
||||
Pattern string
|
||||
To string
|
||||
*regexp.Regexp
|
||||
}
|
||||
|
||||
var regfmt = regexp.MustCompile(`:[^/#?()\.\\]+`)
|
||||
|
||||
func NewRule(pattern, to string) (*Rule, error) {
|
||||
pattern = regfmt.ReplaceAllStringFunc(pattern, func(m string) string {
|
||||
return fmt.Sprintf(`(?P<%s>[^/#?]+)`, m[1:])
|
||||
})
|
||||
|
||||
reg, err := regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Rule{
|
||||
pattern,
|
||||
to,
|
||||
reg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Rule) Rewrite(req *http.Request) bool {
|
||||
oriPath := req.URL.Path
|
||||
|
||||
if !r.MatchString(oriPath) {
|
||||
return false
|
||||
}
|
||||
|
||||
to := path.Clean(r.Replace(req.URL))
|
||||
|
||||
u, e := url.Parse(to)
|
||||
if e != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
req.Header.Set(headerField, req.URL.RequestURI())
|
||||
|
||||
req.URL.Path = u.Path
|
||||
req.URL.RawPath = u.RawPath
|
||||
if u.RawQuery != "" {
|
||||
req.URL.RawQuery = u.RawQuery
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *Rule) Replace(u *url.URL) string {
|
||||
if !hit("\\$|\\:", r.To) {
|
||||
return r.To
|
||||
}
|
||||
|
||||
uri := u.RequestURI()
|
||||
|
||||
regFrom := regexp.MustCompile(r.Pattern)
|
||||
match := regFrom.FindStringSubmatchIndex(uri)
|
||||
|
||||
result := regFrom.ExpandString([]byte(""), r.To, uri, match)
|
||||
|
||||
str := string(result[:])
|
||||
|
||||
if hit("\\:", str) {
|
||||
return r.replaceNamedParams(uri, str)
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
var urlreg = regexp.MustCompile(`:[^/#?()\.\\]+|\(\?P<[a-zA-Z0-9]+>.*\)`)
|
||||
|
||||
func (r *Rule) replaceNamedParams(from, to string) string {
|
||||
fromMatches := r.FindStringSubmatch(from)
|
||||
|
||||
if len(fromMatches) > 0 {
|
||||
for i, name := range r.SubexpNames() {
|
||||
if len(name) > 0 {
|
||||
to = strings.Replace(to, ":"+name, fromMatches[i], -1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return to
|
||||
}
|
||||
|
||||
func NewHandler(rules map[string]string) RewriteHandler {
|
||||
var h RewriteHandler
|
||||
|
||||
for key, val := range rules {
|
||||
r, e := NewRule(key, val)
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
|
||||
h.rules = append(h.rules, r)
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
type RewriteHandler struct {
|
||||
rules []*Rule
|
||||
}
|
||||
|
||||
func (h *RewriteHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) {
|
||||
for _, r := range h.rules {
|
||||
ok := r.Rewrite(req)
|
||||
if ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func hit(pattern, str string) bool {
|
||||
r, e := regexp.MatchString(pattern, str)
|
||||
if e != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
Reference in New Issue
Block a user