chore: move proxy package to arcad/emissary
This commit is contained in:
parent
72765de20b
commit
e09de0b0a4
|
@ -1,29 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"forge.cadoles.com/arcad/edge/pkg/proxy/wildcard"
|
|
||||||
)
|
|
||||||
|
|
||||||
func FilterHosts(allowedHostPatterns ...string) Middleware {
|
|
||||||
return func(h http.Handler) http.Handler {
|
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if matches := wildcard.MatchAny(r.Host, allowedHostPatterns...); !matches {
|
|
||||||
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return http.HandlerFunc(fn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithAllowedHosts(allowedHostPatterns ...string) OptionFunc {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Middlewares = append(o.Middlewares, FilterHosts(allowedHostPatterns...))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"forge.cadoles.com/arcad/edge/pkg/proxy/wildcard"
|
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
func RewriteHosts(mappings map[string]*url.URL) Middleware {
|
|
||||||
patterns := make([]string, len(mappings))
|
|
||||||
|
|
||||||
for p := range mappings {
|
|
||||||
patterns = append(patterns, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(patterns)
|
|
||||||
|
|
||||||
return func(h http.Handler) http.Handler {
|
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := r.Context()
|
|
||||||
|
|
||||||
var match *url.URL
|
|
||||||
|
|
||||||
for _, p := range patterns {
|
|
||||||
logger.Debug(ctx, "matching host to pattern", logger.F("host", r.Host), logger.F("pattern", p))
|
|
||||||
|
|
||||||
if matches := wildcard.Match(r.Host, p); !matches {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
match = mappings[p]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if match == nil {
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = logger.With(ctx, logger.F("originalHost", r.Host))
|
|
||||||
r = r.WithContext(ctx)
|
|
||||||
|
|
||||||
originalURL := r.URL.String()
|
|
||||||
|
|
||||||
r.URL.Host = match.Host
|
|
||||||
r.URL.Scheme = match.Scheme
|
|
||||||
|
|
||||||
logger.Debug(ctx, "rewriting url", logger.F("from", originalURL), logger.F("to", r.URL.String()))
|
|
||||||
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return http.HandlerFunc(fn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithRewriteHosts(mappings map[string]*url.URL) OptionFunc {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Middlewares = append(o.Middlewares, RewriteHosts(mappings))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
type Middleware func(h http.Handler) http.Handler
|
|
||||||
|
|
||||||
type ProxyResponseTransformer interface {
|
|
||||||
TransformResponse(*http.Response) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type defaultProxyResponseTransformer struct{}
|
|
||||||
|
|
||||||
// TransformResponse implements ProxyResponseTransformer
|
|
||||||
func (*defaultProxyResponseTransformer) TransformResponse(*http.Response) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ ProxyResponseTransformer = &defaultProxyResponseTransformer{}
|
|
||||||
|
|
||||||
type ProxyResponseMiddleware func(ProxyResponseTransformer) ProxyResponseTransformer
|
|
||||||
|
|
||||||
type ProxyRequestTransformer interface {
|
|
||||||
TransformRequest(*http.Request)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProxyRequestMiddleware func(ProxyRequestTransformer) ProxyRequestTransformer
|
|
||||||
|
|
||||||
type defaultProxyRequestTransformer struct{}
|
|
||||||
|
|
||||||
// TransformRequest implements ProxyRequestTransformer
|
|
||||||
func (*defaultProxyRequestTransformer) TransformRequest(*http.Request) {}
|
|
||||||
|
|
||||||
var _ ProxyRequestTransformer = &defaultProxyRequestTransformer{}
|
|
|
@ -1,29 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
type Options struct {
|
|
||||||
Middlewares []Middleware
|
|
||||||
ProxyRequestMiddlewares []ProxyRequestMiddleware
|
|
||||||
ProxyResponseMiddlewares []ProxyResponseMiddleware
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultOptions() *Options {
|
|
||||||
return &Options{
|
|
||||||
Middlewares: make([]Middleware, 0),
|
|
||||||
ProxyRequestMiddlewares: make([]ProxyRequestMiddleware, 0),
|
|
||||||
ProxyResponseMiddlewares: make([]ProxyResponseMiddleware, 0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type OptionFunc func(*Options)
|
|
||||||
|
|
||||||
func WithProxyRequestMiddlewares(middlewares ...ProxyRequestMiddleware) OptionFunc {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.ProxyRequestMiddlewares = middlewares
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithproxyResponseMiddlewares(middlewares ...ProxyResponseMiddleware) OptionFunc {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.ProxyResponseMiddlewares = middlewares
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,131 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httputil"
|
|
||||||
"net/url"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Proxy struct {
|
|
||||||
reversers sync.Map
|
|
||||||
handler http.Handler
|
|
||||||
proxyResponseTransformer ProxyResponseTransformer
|
|
||||||
proxyRequestTransformer ProxyRequestTransformer
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeHTTP implements http.Handler
|
|
||||||
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
p.handler.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Proxy) proxyRequest(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := r.Context()
|
|
||||||
|
|
||||||
var reverser *httputil.ReverseProxy
|
|
||||||
|
|
||||||
key := fmt.Sprintf("%s://%s", r.URL.Scheme, r.URL.Host)
|
|
||||||
|
|
||||||
createAndStore := func() {
|
|
||||||
target := &url.URL{
|
|
||||||
Scheme: r.URL.Scheme,
|
|
||||||
Host: r.URL.Host,
|
|
||||||
}
|
|
||||||
|
|
||||||
reverser = httputil.NewSingleHostReverseProxy(target)
|
|
||||||
|
|
||||||
originalDirector := reverser.Director
|
|
||||||
|
|
||||||
if p.proxyRequestTransformer != nil {
|
|
||||||
reverser.Director = func(r *http.Request) {
|
|
||||||
originalURL := r.URL.String()
|
|
||||||
originalDirector(r)
|
|
||||||
p.proxyRequestTransformer.TransformRequest(r)
|
|
||||||
logger.Debug(ctx, "proxying request", logger.F("targetURL", r.URL.String()), logger.F("originalURL", originalURL))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.proxyResponseTransformer != nil {
|
|
||||||
reverser.ModifyResponse = func(r *http.Response) error {
|
|
||||||
if err := p.proxyResponseTransformer.TransformResponse(r); err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.reversers.Store(key, reverser)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, exists := p.reversers.Load(key)
|
|
||||||
if !exists {
|
|
||||||
createAndStore()
|
|
||||||
}
|
|
||||||
|
|
||||||
reverser, ok := raw.(*httputil.ReverseProxy)
|
|
||||||
if !ok {
|
|
||||||
createAndStore()
|
|
||||||
}
|
|
||||||
|
|
||||||
reverser.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(funcs ...OptionFunc) *Proxy {
|
|
||||||
opts := defaultOptions()
|
|
||||||
for _, fn := range funcs {
|
|
||||||
fn(opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
proxy := &Proxy{}
|
|
||||||
|
|
||||||
handler := http.HandlerFunc(proxy.proxyRequest)
|
|
||||||
proxy.handler = createMiddlewareChain(handler, opts.Middlewares)
|
|
||||||
|
|
||||||
proxy.proxyRequestTransformer = createProxyRequestChain(&defaultProxyRequestTransformer{}, opts.ProxyRequestMiddlewares)
|
|
||||||
proxy.proxyResponseTransformer = createProxyResponseChain(&defaultProxyResponseTransformer{}, opts.ProxyResponseMiddlewares)
|
|
||||||
|
|
||||||
return proxy
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ http.Handler = &Proxy{}
|
|
||||||
|
|
||||||
func createMiddlewareChain(handler http.Handler, middlewares []Middleware) http.Handler {
|
|
||||||
reverse(middlewares)
|
|
||||||
|
|
||||||
for _, m := range middlewares {
|
|
||||||
handler = m(handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
return handler
|
|
||||||
}
|
|
||||||
|
|
||||||
func createProxyResponseChain(transformer ProxyResponseTransformer, middlewares []ProxyResponseMiddleware) ProxyResponseTransformer {
|
|
||||||
reverse(middlewares)
|
|
||||||
|
|
||||||
for _, m := range middlewares {
|
|
||||||
transformer = m(transformer)
|
|
||||||
}
|
|
||||||
|
|
||||||
return transformer
|
|
||||||
}
|
|
||||||
|
|
||||||
func createProxyRequestChain(transformer ProxyRequestTransformer, middlewares []ProxyRequestMiddleware) ProxyRequestTransformer {
|
|
||||||
reverse(middlewares)
|
|
||||||
|
|
||||||
for _, m := range middlewares {
|
|
||||||
transformer = m(transformer)
|
|
||||||
}
|
|
||||||
|
|
||||||
return transformer
|
|
||||||
}
|
|
||||||
|
|
||||||
func reverse[S ~[]E, E any](s S) {
|
|
||||||
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
s[i], s[j] = s[j], s[i]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package wildcard
|
|
||||||
|
|
||||||
const wildcard = '*'
|
|
||||||
|
|
||||||
func Match(str, pattern string) bool {
|
|
||||||
if pattern == "" {
|
|
||||||
return str == pattern
|
|
||||||
}
|
|
||||||
|
|
||||||
if pattern == string(wildcard) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return deepMatchRune([]rune(str), []rune(pattern))
|
|
||||||
}
|
|
||||||
|
|
||||||
func MatchAny(str string, patterns ...string) bool {
|
|
||||||
for _, p := range patterns {
|
|
||||||
if matches := Match(str, p); matches {
|
|
||||||
return matches
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func deepMatchRune(str, pattern []rune) bool {
|
|
||||||
for len(pattern) > 0 {
|
|
||||||
switch pattern[0] {
|
|
||||||
default:
|
|
||||||
if len(str) == 0 || str[0] != pattern[0] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
case wildcard:
|
|
||||||
return deepMatchRune(str, pattern[1:]) ||
|
|
||||||
(len(str) > 0 && deepMatchRune(str[1:], pattern))
|
|
||||||
}
|
|
||||||
|
|
||||||
str = str[1:]
|
|
||||||
pattern = pattern[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
return len(str) == 0 && len(pattern) == 0
|
|
||||||
}
|
|
Loading…
Reference in New Issue