MakyuuIchaival/httputils/server.go

244 lines
5.7 KiB
Go

package httputils
import (
"crypto/tls"
"git.gammaspectra.live/S.O.N.G/MakyuuIchaival/tlsutils"
"github.com/dgrr/http2"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/http3"
"github.com/valyala/fasthttp"
"io"
"log"
"mime"
"net"
"net/http"
"runtime"
"sync"
"time"
)
type Server struct {
ListenAddress string
TLSConfig *tlsutils.Configuration
UseFastHTTP bool
EnableHTTP2 bool
FastHTTPRequestServer FastHTTPRequestServer
NetHTTPRequestServer NetHTTPRequestServer
EnableHTTP3 bool
Debug bool
Handler RequestHandler
ReadTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
extraHeadersMutex sync.RWMutex
extraHeaders map[string]string
}
type RequestContext interface {
GetPath() string
GetConnectionTime() time.Time
GetRequestTime() time.Time
GetTLSServerName() string
GetBody() io.Reader
GetHost() string
GetProtocol() string
GetTLSVersion() uint16
GetTLSCipher() uint16
GetRequestHeader(name string) string
GetResponseHeader(name string) string
AddResponseHeader(name string, value string)
SetResponseHeader(name string, value string)
ServeStream(stream Stream)
ServeBytes(content []byte)
SetResponseCode(code int)
DoRedirect(location string, code int)
IsGet() bool
IsPost() bool
IsOptions() bool
IsHead() bool
GetExtraHeaders() map[string]string
AddTiming(name string, desc string, d time.Duration)
AddTimingInformational(name string, desc string)
}
type RequestHandler func(ctx RequestContext)
type FastHTTPRequestServer func(ctx *fasthttp.RequestCtx) RequestContext
type NetHTTPRequestServer func(w http.ResponseWriter, r *http.Request) RequestContext
func (server *Server) Serve() {
var wg sync.WaitGroup
mime.AddExtensionType(".bin", "application/octet-stream")
if server.EnableHTTP2 {
server.TLSConfig.Config.NextProtos = []string{
"h2",
"http/1.1",
}
} else {
server.AddExtraHeader("Connection", "close")
}
if server.UseFastHTTP {
wg.Add(1)
go func() {
defer wg.Done()
handler := func(ctx *fasthttp.RequestCtx) RequestContext {
return NewRequestContextFromFastHttp(server, ctx)
}
if server.FastHTTPRequestServer != nil {
handler = server.FastHTTPRequestServer
}
s := &fasthttp.Server{
ReadTimeout: server.ReadTimeout,
WriteTimeout: server.WriteTimeout,
IdleTimeout: server.IdleTimeout,
Handler: func(ctx *fasthttp.RequestCtx) {
server.Handler(handler(ctx))
},
NoDefaultServerHeader: true,
NoDefaultDate: true,
DisableKeepalive: !server.EnableHTTP2,
TCPKeepalive: server.EnableHTTP2,
TLSConfig: server.TLSConfig.Config,
}
if server.EnableHTTP2 {
http2.ConfigureServer(s, http2.ServerConfig{
Debug: server.Debug,
})
}
log.Printf("[Server] Serving TCP on %s", server.ListenAddress)
ln, err := net.Listen("tcp", server.ListenAddress)
if err != nil {
log.Panic(err)
}
defer ln.Close()
err = s.Serve(tls.NewListener(ln, s.TLSConfig.Clone()))
if err != nil {
log.Panic(err)
}
}()
} else {
wg.Add(1)
go func() {
defer wg.Done()
handler := func(w http.ResponseWriter, r *http.Request) RequestContext {
return NewRequestContextFromHttp(server, w, r)
}
if server.NetHTTPRequestServer != nil {
handler = server.NetHTTPRequestServer
}
s := &http.Server{
ReadTimeout: server.ReadTimeout,
WriteTimeout: server.WriteTimeout,
IdleTimeout: server.IdleTimeout,
Addr: server.ListenAddress,
TLSConfig: server.TLSConfig.Config,
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
server.Handler(handler(w, r))
}),
}
s.SetKeepAlivesEnabled(!server.EnableHTTP2)
log.Printf("[Server] Serving TCP on %s", server.ListenAddress)
ln, err := net.Listen("tcp", server.ListenAddress)
if err != nil {
log.Panic(err)
}
defer ln.Close()
err = s.Serve(tls.NewListener(ln, s.TLSConfig.Clone()))
if err != nil {
log.Panic(err)
}
}()
}
if server.EnableHTTP3 {
wg.Add(1)
go func() {
defer wg.Done()
handler := func(w http.ResponseWriter, r *http.Request) RequestContext {
return NewRequestContextFromHttp(server, w, r)
}
if server.NetHTTPRequestServer != nil {
handler = server.NetHTTPRequestServer
}
s := &http3.Server{
Addr: server.ListenAddress,
QuicConfig: &quic.Config{
HandshakeIdleTimeout: server.ReadTimeout,
MaxIdleTimeout: server.IdleTimeout,
KeepAlivePeriod: server.IdleTimeout,
},
TLSConfig: server.TLSConfig.QUICConfig,
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if server.Debug {
log.Print("Received HTTP/3 request")
}
server.Handler(handler(w, r))
}),
}
//TODO HACK: SetQuicHeaders does not work before server starts
go func() {
h := http.Header{}
for {
if err := s.SetQuicHeaders(h); err == nil {
server.AddExtraHeader("Alt-Svc", h.Get("Alt-Svc"))
break
}
runtime.Gosched()
}
}()
log.Printf("[Server] Serving UDP on %s", server.ListenAddress)
err := s.ListenAndServe()
if err != nil {
log.Panic(err)
}
}()
}
wg.Wait()
}
func (server *Server) GetExtraHeaders() map[string]string {
server.extraHeadersMutex.RLock()
r := make(map[string]string)
if server.extraHeaders != nil {
for key, value := range server.extraHeaders {
r[key] = value
}
}
server.extraHeadersMutex.RUnlock()
return r
}
func (server *Server) AddExtraHeader(key, value string) {
server.extraHeadersMutex.Lock()
if server.extraHeaders == nil {
server.extraHeaders = make(map[string]string)
}
server.extraHeaders[key] = value
server.extraHeadersMutex.Unlock()
}