mirror of
https://github.com/foomo/gofoomo.git
synced 2025-10-16 12:25:44 +00:00
176 lines
4.6 KiB
Go
176 lines
4.6 KiB
Go
package proxy
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"errors"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httputil"
|
|
|
|
"github.com/foomo/gofoomo/foomo"
|
|
"github.com/foomo/tlsconfig"
|
|
"time"
|
|
)
|
|
|
|
// Handler proxy handler
|
|
type Handler interface {
|
|
HandlesRequest(incomingRequest *http.Request) bool
|
|
ServeHTTP(w http.ResponseWriter, incomingRequest *http.Request)
|
|
}
|
|
|
|
// Listener deprecated
|
|
type Listener interface {
|
|
ListenServeHTTPStart(w http.ResponseWriter, incomingRequest *http.Request) http.ResponseWriter
|
|
ListenServeHTTPDone(w http.ResponseWriter, incomingRequest *http.Request)
|
|
}
|
|
|
|
// Based on values from https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/
|
|
// And https://blog.cloudflare.com/exposing-go-on-the-internet/
|
|
const (
|
|
READ_TIMEOUT = 30 * time.Second
|
|
WRITE_TIMEOUT = 30 * time.Second
|
|
IDLE_TIMEOUT = 120 * time.Second
|
|
)
|
|
|
|
// Proxy foomo proxy
|
|
type Proxy struct {
|
|
foomo *foomo.Foomo
|
|
ReverseProxy *httputil.ReverseProxy
|
|
handlers []Handler
|
|
listeners []Listener
|
|
auth *Auth
|
|
ServeHTTPFunc func(http.ResponseWriter, *http.Request)
|
|
}
|
|
|
|
// Server server for Proxy
|
|
type Server struct {
|
|
Proxy *Proxy
|
|
Config *Config
|
|
TLSConfig *tls.Config
|
|
Foomo *foomo.Foomo
|
|
}
|
|
|
|
// NewProxy constructor
|
|
func NewProxy(f *foomo.Foomo) *Proxy {
|
|
proxy := &Proxy{
|
|
foomo: f,
|
|
}
|
|
proxy.ServeHTTPFunc = proxy.serveHTTP
|
|
proxy.ReverseProxy = httputil.NewSingleHostReverseProxy(proxy.foomo.URL)
|
|
return proxy
|
|
}
|
|
|
|
func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, incomingRequest *http.Request) {
|
|
proxy.ServeHTTPFunc(w, incomingRequest)
|
|
}
|
|
|
|
func (proxy *Proxy) serveHTTP(w http.ResponseWriter, incomingRequest *http.Request) {
|
|
if proxy.auth != nil && len(proxy.auth.Domain) > 0 {
|
|
//&& !proxy.foomo.BasicAuthForRequest(w, incomingRequest, proxy.auth.Domain, proxy.auth.Realm, "access denied") {
|
|
|
|
}
|
|
for _, listener := range proxy.listeners {
|
|
w = listener.ListenServeHTTPStart(w, incomingRequest)
|
|
}
|
|
for _, handler := range proxy.handlers {
|
|
if handler.HandlesRequest(incomingRequest) {
|
|
handler.ServeHTTP(w, incomingRequest)
|
|
return
|
|
}
|
|
}
|
|
incomingRequest.Host = proxy.foomo.URL.Host
|
|
// incomingRequest.URL.Opaque = incomingRequest.RequestURI + incomingRequest.
|
|
|
|
proxy.ReverseProxy.ServeHTTP(w, incomingRequest)
|
|
for _, listener := range proxy.listeners {
|
|
listener.ListenServeHTTPDone(w, incomingRequest)
|
|
}
|
|
|
|
}
|
|
|
|
// AddHandler add a handler
|
|
func (proxy *Proxy) AddHandler(handler Handler) {
|
|
proxy.handlers = append(proxy.handlers, handler)
|
|
}
|
|
|
|
// AddListener deprecated
|
|
func (proxy *Proxy) AddListener(listener Listener) {
|
|
proxy.listeners = append(proxy.listeners, listener)
|
|
}
|
|
|
|
// NewServerWithConfig constructor with a config file
|
|
func NewServerWithConfig(filename string) (p *Server, err error) {
|
|
config, err := ReadConfig(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return NewServer(config)
|
|
}
|
|
|
|
// NewServer constructor with config struct
|
|
func NewServer(config *Config) (p *Server, err error) {
|
|
p = &Server{
|
|
Config: config,
|
|
}
|
|
f, err := foomo.NewFoomo(config.Foomo.Dir, config.Foomo.RunMode, config.Foomo.Address)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.Foomo = f
|
|
p.Proxy = NewProxy(p.Foomo)
|
|
p.Proxy.auth = config.Server.Auth
|
|
p.TLSConfig = tlsconfig.NewServerTLSConfig(p.Config.Server.TLS.Mode)
|
|
|
|
return p, nil
|
|
}
|
|
|
|
func (p *Server) ListenAndServe() error {
|
|
return p.listenAndServeWithHandler(p.Proxy)
|
|
}
|
|
|
|
func (p *Server) ListenAndServeWithHandler(handler http.Handler) error {
|
|
return p.listenAndServeWithHandler(handler)
|
|
}
|
|
|
|
// ListenAndServe until things go bad, depending upon configuration this will\
|
|
// listen to http and https requests
|
|
func (p *Server) listenAndServeWithHandler(handler http.Handler) error {
|
|
c := p.Config.Server
|
|
errorChan := make(chan error)
|
|
startedHTTPS := false
|
|
startedHTTP := false
|
|
if p.Config.hasCertificates() {
|
|
log.Println("listening for https on", c.TLS.Address)
|
|
certificates, certificatesErr := p.Config.getCertificates()
|
|
if certificatesErr != nil {
|
|
return errors.New("could not load certificates: " + certificatesErr.Error())
|
|
}
|
|
p.TLSConfig.Certificates = certificates
|
|
p.TLSConfig.BuildNameToCertificate()
|
|
go func() {
|
|
tlsServer := &http.Server{
|
|
ReadTimeout: READ_TIMEOUT,
|
|
WriteTimeout: WRITE_TIMEOUT,
|
|
IdleTimeout: IDLE_TIMEOUT,
|
|
Addr: c.TLS.Address,
|
|
Handler: handler,
|
|
TLSConfig: p.TLSConfig,
|
|
}
|
|
errorChan <- tlsServer.ListenAndServeTLS("", "")
|
|
}()
|
|
startedHTTPS = true
|
|
}
|
|
if len(c.Address) > 0 {
|
|
log.Println("listening for http on", c.Address)
|
|
go func() {
|
|
errorChan <- http.ListenAndServe(c.Address, handler)
|
|
}()
|
|
startedHTTP = true
|
|
}
|
|
if !startedHTTP && !startedHTTPS {
|
|
return errors.New("nothing to listen to")
|
|
}
|
|
err := <-errorChan
|
|
return err
|
|
}
|