feat: initial commit
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good

This commit is contained in:
2023-04-24 20:52:12 +02:00
commit f9a6a3ab2a
110 changed files with 6773 additions and 0 deletions

View File

@ -0,0 +1,60 @@
package director
import (
"context"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/pkg/errors"
)
type contextKey string
const (
contextKeyProxy contextKey = "proxy"
contextKeyLayers contextKey = "layers"
)
var (
errContextKeyNotFound = errors.New("context key not found")
errUnexpectedContextValue = errors.New("unexpected context value")
)
func withProxy(ctx context.Context, proxy *store.Proxy) context.Context {
return context.WithValue(ctx, contextKeyProxy, proxy)
}
func ctxProxy(ctx context.Context) (*store.Proxy, error) {
proxy, err := ctxValue[*store.Proxy](ctx, contextKeyProxy)
if err != nil {
return nil, errors.WithStack(err)
}
return proxy, nil
}
func withLayers(ctx context.Context, layers []*store.Layer) context.Context {
return context.WithValue(ctx, contextKeyLayers, layers)
}
func ctxLayers(ctx context.Context) ([]*store.Layer, error) {
layers, err := ctxValue[[]*store.Layer](ctx, contextKeyLayers)
if err != nil {
return nil, errors.WithStack(err)
}
return layers, nil
}
func ctxValue[T any](ctx context.Context, key contextKey) (T, error) {
raw := ctx.Value(key)
if raw == nil {
return *new(T), errors.WithStack(errContextKeyNotFound)
}
value, ok := raw.(T)
if !ok {
return *new(T), errors.WithStack(errUnexpectedContextValue)
}
return value, nil
}

View File

@ -0,0 +1,135 @@
package director
import (
"context"
"net/http"
"forge.cadoles.com/Cadoles/go-proxy"
"forge.cadoles.com/Cadoles/go-proxy/wildcard"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/davecgh/go-spew/spew"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/logger"
)
type Director struct {
proxyRepository store.ProxyRepository
layerRegistry *LayerRegistry
}
func (d *Director) rewriteRequest(r *http.Request) (*http.Request, error) {
ctx := r.Context()
proxies, err := d.getProxies(ctx)
if err != nil {
return r, errors.WithStack(err)
}
var match *store.Proxy
MAIN:
for _, p := range proxies {
for _, from := range p.From {
if matches := wildcard.Match(r.Host, from); !matches {
continue
}
match = p
break MAIN
}
}
if match == nil {
return r, nil
}
r.URL.Host = match.To.Host
r.URL.Scheme = match.To.Scheme
ctx = withProxy(ctx, match)
r = r.WithContext(ctx)
return r, nil
}
func (d *Director) getProxies(ctx context.Context) ([]*store.Proxy, error) {
headers, err := d.proxyRepository.QueryProxy(ctx)
if err != nil {
return nil, errors.WithStack(err)
}
proxies := make([]*store.Proxy, len(headers))
for i, h := range headers {
proxy, err := d.proxyRepository.GetProxy(ctx, h.Name)
if err != nil {
return nil, errors.WithStack(err)
}
proxies[i] = proxy
}
return proxies, nil
}
func (d *Director) RequestTransformer() proxy.RequestTransformer {
return func(r *http.Request) {
ctx := r.Context()
_, err := ctxProxy(ctx)
if err != nil {
if errors.Is(err, errContextKeyNotFound) {
return
}
logger.Error(ctx, "could not retrieve proxy from context", logger.E(errors.WithStack(err)))
return
}
// spew.Dump(proxy)
}
}
func (d *Director) ResponseTransformer() proxy.ResponseTransformer {
return func(r *http.Response) error {
ctx := r.Request.Context()
proxy, err := ctxProxy(ctx)
if err != nil {
if errors.Is(err, errContextKeyNotFound) {
return nil
}
logger.Error(ctx, "could not retrieve proxy from context", logger.E(errors.WithStack(err)))
return nil
}
spew.Dump(proxy)
return nil
}
}
func (d *Director) Middleware() proxy.Middleware {
return func(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
r, err := d.rewriteRequest(r)
if err != nil {
logger.Error(r.Context(), "could not rewrite request", logger.E(errors.WithStack(err)))
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
}
func New(repo store.ProxyRepository, layers ...Layer) *Director {
registry := NewLayerRegistry(layers...)
return &Director{repo, registry}
}

View File

@ -0,0 +1,9 @@
package director
import (
"forge.cadoles.com/cadoles/bouncer/internal/store"
)
type Layer interface {
LayerType() store.LayerType
}

View File

@ -0,0 +1,97 @@
package director
import (
"forge.cadoles.com/Cadoles/go-proxy"
"forge.cadoles.com/cadoles/bouncer/internal/store"
)
type MiddlewareLayer interface {
Middleware(layer *store.Layer) proxy.Middleware
}
type RequestTransformerLayer interface {
RequestTransformer(layer *store.Layer) proxy.RequestTransformer
}
type ResponseTransformerLayer interface {
ResponseTransformer(layer *store.Layer) proxy.ResponseTransformer
}
type LayerRegistry struct {
index map[store.LayerType]Layer
}
func (r *LayerRegistry) GetLayer(layerType store.LayerType) (Layer, bool) {
layer, exists := r.index[layerType]
if !exists {
return nil, false
}
return layer, true
}
func (r *LayerRegistry) getLayerAsAny(layerType store.LayerType) (any, bool) {
return r.GetLayer(layerType)
}
func (r *LayerRegistry) GetMiddleware(layerType store.LayerType, name store.LayerName) (MiddlewareLayer, bool) {
layer, exists := r.getLayerAsAny(layerType)
if !exists {
return nil, false
}
middleware, ok := layer.(MiddlewareLayer)
if !ok {
return nil, false
}
return middleware, true
}
func (r *LayerRegistry) GetResponseTransformer(layerType store.LayerType) (ResponseTransformerLayer, bool) {
layer, exists := r.getLayerAsAny(layerType)
if !exists {
return nil, false
}
transformer, ok := layer.(ResponseTransformerLayer)
if !ok {
return nil, false
}
return transformer, true
}
func (r *LayerRegistry) GetRequestTransformer(layerType store.LayerType) (RequestTransformerLayer, bool) {
layer, exists := r.getLayerAsAny(layerType)
if !exists {
return nil, false
}
transformer, ok := layer.(RequestTransformerLayer)
if !ok {
return nil, false
}
return transformer, true
}
func (r *LayerRegistry) Load(layers ...Layer) {
index := make(map[store.LayerType]Layer)
for _, l := range layers {
layerType := l.LayerType()
index[layerType] = l
}
r.index = index
}
func NewLayerRegistry(layers ...Layer) *LayerRegistry {
registry := &LayerRegistry{
index: make(map[store.LayerType]Layer),
}
registry.Load(layers...)
return registry
}