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 af4e8e556c
98 changed files with 5817 additions and 0 deletions

42
internal/proxy/init.go Normal file
View File

@ -0,0 +1,42 @@
package proxy
import (
"context"
"forge.cadoles.com/cadoles/bouncer/internal/setup"
"github.com/pkg/errors"
)
func (s *Server) initRepositories(ctx context.Context) error {
if err := s.initQueueRepository(ctx); err != nil {
return errors.WithStack(err)
}
if err := s.initProxyRepository(ctx); err != nil {
return errors.WithStack(err)
}
return nil
}
func (s *Server) initQueueRepository(ctx context.Context) error {
queueRepository, err := setup.NewQueueRepository(ctx, s.redisConfig)
if err != nil {
return errors.WithStack(err)
}
s.queueRepository = queueRepository
return nil
}
func (s *Server) initProxyRepository(ctx context.Context) error {
proxyRepository, err := setup.NewProxyRepository(ctx, s.redisConfig)
if err != nil {
return errors.WithStack(err)
}
s.proxyRepository = proxyRepository
return nil
}

View File

@ -0,0 +1,90 @@
package director
import (
"context"
"net/http"
"net/url"
"forge.cadoles.com/Cadoles/go-proxy"
"forge.cadoles.com/Cadoles/go-proxy/wildcard"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/logger"
)
type Director struct {
repo store.ProxyRepository
}
func (d *Director) rewriteRequest(r *http.Request) error {
ctx := r.Context()
proxies, err := d.getProxies(ctx)
if err != nil {
return errors.WithStack(err)
}
var match *url.URL
MAIN:
for _, p := range proxies {
for _, from := range p.From {
if matches := wildcard.Match(r.Host, from); !matches {
continue
}
match = p.To
break MAIN
}
}
if match == nil {
return nil
}
r.URL.Host = match.Host
r.URL.Scheme = match.Scheme
return nil
}
func (d *Director) getProxies(ctx context.Context) ([]*store.Proxy, error) {
headers, err := d.repo.QueryProxy(ctx)
if err != nil {
return nil, errors.WithStack(err)
}
proxies := make([]*store.Proxy, len(headers))
for i, h := range headers {
proxy, err := d.repo.GetProxy(ctx, h.ID)
if err != nil {
return nil, errors.WithStack(err)
}
proxies[i] = proxy
}
return proxies, nil
}
func (d *Director) Middleware() proxy.Middleware {
return func(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
if err := d.rewriteRequest(r); 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) *Director {
return &Director{repo}
}

31
internal/proxy/option.go Normal file
View File

@ -0,0 +1,31 @@
package proxy
import (
"forge.cadoles.com/cadoles/bouncer/internal/config"
)
type Option struct {
ServerConfig config.ProxyServerConfig
RedisConfig config.RedisConfig
}
type OptionFunc func(*Option)
func defaultOption() *Option {
return &Option{
ServerConfig: config.NewDefaultProxyServerConfig(),
RedisConfig: config.NewDefaultRedisConfig(),
}
}
func WithServerConfig(conf config.ProxyServerConfig) OptionFunc {
return func(opt *Option) {
opt.ServerConfig = conf
}
}
func WithRedisConfig(conf config.RedisConfig) OptionFunc {
return func(opt *Option) {
opt.RedisConfig = conf
}
}

110
internal/proxy/server.go Normal file
View File

@ -0,0 +1,110 @@
package proxy
import (
"context"
"fmt"
"log"
"net"
"net/http"
"forge.cadoles.com/Cadoles/go-proxy"
"forge.cadoles.com/cadoles/bouncer/internal/config"
"forge.cadoles.com/cadoles/bouncer/internal/proxy/middleware/director"
"forge.cadoles.com/cadoles/bouncer/internal/queue"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/logger"
)
type Server struct {
serverConfig config.ProxyServerConfig
redisConfig config.RedisConfig
queueRepository queue.Repository
proxyRepository store.ProxyRepository
}
func (s *Server) Start(ctx context.Context) (<-chan net.Addr, <-chan error) {
errs := make(chan error)
addrs := make(chan net.Addr)
go s.run(ctx, addrs, errs)
return addrs, errs
}
func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan error) {
defer func() {
close(errs)
close(addrs)
}()
ctx, cancel := context.WithCancel(parentCtx)
defer cancel()
if err := s.initRepositories(ctx); err != nil {
errs <- errors.WithStack(err)
return
}
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.serverConfig.HTTP.Host, s.serverConfig.HTTP.Port))
if err != nil {
errs <- errors.WithStack(err)
return
}
addrs <- listener.Addr()
defer func() {
if err := listener.Close(); err != nil && !errors.Is(err, net.ErrClosed) {
errs <- errors.WithStack(err)
}
}()
go func() {
<-ctx.Done()
if err := listener.Close(); err != nil && !errors.Is(err, net.ErrClosed) {
log.Printf("%+v", errors.WithStack(err))
}
}()
router := chi.NewRouter()
router.Use(middleware.Logger)
logger.Info(ctx, "http server listening")
queue := queue.New(s.queueRepository)
director := director.New(s.proxyRepository)
handler := proxy.New(
proxy.WithMiddlewares(
director.Middleware(),
queue.Middleware(),
),
)
router.Handle("/*", handler)
if err := http.Serve(listener, router); err != nil && !errors.Is(err, net.ErrClosed) {
errs <- errors.WithStack(err)
}
logger.Info(ctx, "http server exiting")
}
func NewServer(funcs ...OptionFunc) *Server {
opt := defaultOption()
for _, fn := range funcs {
fn(opt)
}
return &Server{
serverConfig: opt.ServerConfig,
redisConfig: opt.RedisConfig,
}
}