add rewrite middleware

This commit is contained in:
Umputun
2018-02-18 14:53:51 -06:00
parent 06742311d5
commit 04a1cb8909
6 changed files with 214 additions and 1 deletions
Generated
+7 -1
View File
@@ -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
+32
View File
@@ -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
View File
@@ -0,0 +1 @@
.DS_Store
+5
View File
@@ -0,0 +1,5 @@
language: go
go:
- 1.5
- 1.6
- tip
+35
View File
@@ -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
View File
@@ -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
}