mirror of
https://github.com/giongto35/cloud-game.git
synced 2026-01-23 10:35:44 +00:00
193 lines
4.5 KiB
Go
193 lines
4.5 KiB
Go
package httpx
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"time"
|
|
|
|
"github.com/giongto35/cloud-game/v3/pkg/logger"
|
|
"golang.org/x/crypto/acme/autocert"
|
|
)
|
|
|
|
type Server struct {
|
|
http.Server
|
|
|
|
autoCert *autocert.Manager
|
|
opts Options
|
|
|
|
listener *Listener
|
|
redirect *Server
|
|
log *logger.Logger
|
|
}
|
|
|
|
type (
|
|
Mux struct {
|
|
*http.ServeMux
|
|
prefix string
|
|
}
|
|
Handler = http.Handler
|
|
HandlerFunc = http.HandlerFunc
|
|
ResponseWriter = http.ResponseWriter
|
|
Request = http.Request
|
|
)
|
|
|
|
// NewServeMux allocates and returns a new ServeMux.
|
|
func NewServeMux(prefix string) *Mux {
|
|
return &Mux{ServeMux: http.NewServeMux(), prefix: prefix}
|
|
}
|
|
|
|
func (m *Mux) Prefix(v string) { m.prefix = v }
|
|
|
|
func (m *Mux) HandleW(pattern string, h func(http.ResponseWriter)) *Mux {
|
|
m.ServeMux.HandleFunc(m.prefix+pattern, func(w http.ResponseWriter, _ *http.Request) { h(w) })
|
|
return m
|
|
}
|
|
|
|
func (m *Mux) Handle(pattern string, handler Handler) *Mux {
|
|
m.ServeMux.Handle(m.prefix+pattern, handler)
|
|
return m
|
|
}
|
|
|
|
func (m *Mux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) *Mux {
|
|
m.ServeMux.HandleFunc(m.prefix+pattern, handler)
|
|
return m
|
|
}
|
|
|
|
func (m *Mux) ServeHTTP(w ResponseWriter, r *Request) { m.ServeMux.ServeHTTP(w, r) }
|
|
|
|
func NewServer(address string, handler func(*Server) Handler, options ...Option) (*Server, error) {
|
|
opts := &Options{
|
|
Https: false,
|
|
HttpsRedirect: true,
|
|
IdleTimeout: 120 * time.Second,
|
|
ReadTimeout: 500 * time.Second,
|
|
WriteTimeout: 500 * time.Second,
|
|
}
|
|
opts.override(options...)
|
|
|
|
if opts.Logger == nil {
|
|
opts.Logger = logger.Default()
|
|
}
|
|
|
|
server := &Server{
|
|
Server: http.Server{
|
|
Addr: address,
|
|
IdleTimeout: opts.IdleTimeout,
|
|
ReadTimeout: opts.ReadTimeout,
|
|
WriteTimeout: opts.WriteTimeout,
|
|
},
|
|
opts: *opts,
|
|
log: opts.Logger,
|
|
}
|
|
// (╯°□°)╯︵ ┻━┻
|
|
server.Handler = handler(server)
|
|
|
|
if opts.Https && opts.IsAutoHttpsCert() {
|
|
server.autoCert = NewTLSConfig(withZonePrefix(opts.HttpsDomain, opts.Zone)).CertManager
|
|
server.TLSConfig = server.autoCert.TLSConfig()
|
|
}
|
|
|
|
addr := server.Addr
|
|
if server.Addr == "" {
|
|
addr = ":http"
|
|
if opts.Https {
|
|
addr = ":https"
|
|
}
|
|
opts.Logger.Warn().Msgf("Empty server address has been changed to %v", addr)
|
|
}
|
|
listener, err := NewListener(addr, server.opts.PortRoll)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
server.listener = listener
|
|
|
|
addr = buildAddress(server.Addr, opts.Zone, *listener)
|
|
opts.Logger.Info().Msgf("httpx %v (%v)", addr, server.Addr)
|
|
server.Addr = addr
|
|
|
|
return server, nil
|
|
}
|
|
|
|
func (s *Server) MuxX(prefix string) *Mux { return NewServeMux(prefix) }
|
|
func (s *Server) Mux() *Mux { return s.MuxX("") }
|
|
|
|
func (s *Server) Run() { go s.run() }
|
|
|
|
func (s *Server) run() {
|
|
protocol := s.GetProtocol()
|
|
s.log.Debug().Msgf("Starting %s server on %s", protocol, s.Addr)
|
|
|
|
if s.opts.Https && s.opts.HttpsRedirect {
|
|
if rdr, err := s.redirection(); err == nil {
|
|
s.redirect = rdr
|
|
go s.redirect.Run()
|
|
} else {
|
|
s.log.Error().Err(err).Msg("couldn't init redirection server")
|
|
}
|
|
}
|
|
|
|
var err error
|
|
if s.opts.Https {
|
|
err = s.ServeTLS(*s.listener, s.opts.HttpsCert, s.opts.HttpsKey)
|
|
} else {
|
|
err = s.Serve(*s.listener)
|
|
}
|
|
|
|
if errors.Is(err, http.ErrServerClosed) {
|
|
s.log.Debug().Msgf("%s server was closed", protocol)
|
|
return
|
|
}
|
|
s.log.Error().Err(err)
|
|
}
|
|
|
|
func (s *Server) Stop() error {
|
|
if s.redirect != nil {
|
|
_ = s.redirect.Stop()
|
|
}
|
|
return s.Server.Close()
|
|
}
|
|
|
|
func (s *Server) GetHost() string { return extractHost(s.Addr) }
|
|
|
|
func (s *Server) GetProtocol() string {
|
|
protocol := "http"
|
|
if s.opts.Https {
|
|
protocol = "https"
|
|
}
|
|
return protocol
|
|
}
|
|
|
|
func (s *Server) redirection() (*Server, error) {
|
|
address := s.Addr
|
|
if s.opts.HttpsDomain != "" {
|
|
address = s.opts.HttpsDomain
|
|
}
|
|
addr := buildAddress(address, s.opts.Zone, *s.listener)
|
|
s.log.Info().Str("addr", addr).Msg("Start HTTPS redirect server")
|
|
|
|
srv, err := NewServer(s.opts.HttpsRedirectAddress, func(serv *Server) Handler {
|
|
h := NewServeMux("")
|
|
h.Handle("/", HandlerFunc(func(w ResponseWriter, r *Request) {
|
|
httpsURL := url.URL{Scheme: "https", Host: addr, Path: r.URL.Path, RawQuery: r.URL.RawQuery}
|
|
rdr := httpsURL.String()
|
|
if s.log.GetLevel() < logger.InfoLevel {
|
|
s.log.Debug().
|
|
Str("from", fmt.Sprintf("http://%s%s", r.Host, r.URL.String())).
|
|
Str("to", rdr).
|
|
Msg("Redirect")
|
|
}
|
|
http.Redirect(w, r, rdr, http.StatusFound)
|
|
}))
|
|
if serv.autoCert != nil {
|
|
return serv.autoCert.HTTPHandler(h)
|
|
}
|
|
return h
|
|
},
|
|
WithLogger(s.log),
|
|
)
|
|
return srv, err
|
|
}
|
|
|
|
func FileServer(dir string) http.Handler { return http.FileServer(http.Dir(dir)) }
|