MakyuuIchaival/httputils/server.go

244 lines
5.7 KiB
Go
Raw Permalink Normal View History

2022-01-18 18:10:57 +00:00
package httputils
import (
"crypto/tls"
"git.gammaspectra.live/S.O.N.G/MakyuuIchaival/tlsutils"
"github.com/dgrr/http2"
2023-08-08 00:37:39 +00:00
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/http3"
2022-01-18 18:10:57 +00:00
"github.com/valyala/fasthttp"
"io"
2022-01-18 18:10:57 +00:00
"log"
"mime"
2022-01-18 18:10:57 +00:00
"net"
"net/http"
2022-06-06 21:25:30 +00:00
"runtime"
2022-01-18 18:10:57 +00:00
"sync"
"time"
)
type Server struct {
ListenAddress string
TLSConfig *tlsutils.Configuration
2022-06-06 20:26:50 +00:00
UseFastHTTP bool
EnableHTTP2 bool
FastHTTPRequestServer FastHTTPRequestServer
NetHTTPRequestServer NetHTTPRequestServer
EnableHTTP3 bool
Debug bool
Handler RequestHandler
2022-01-18 18:10:57 +00:00
2022-06-09 15:49:40 +00:00
ReadTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
2022-01-18 18:10:57 +00:00
extraHeadersMutex sync.RWMutex
extraHeaders map[string]string
}
type RequestContext interface {
GetPath() string
GetConnectionTime() time.Time
GetRequestTime() time.Time
GetTLSServerName() string
GetBody() io.Reader
2022-06-06 23:08:57 +00:00
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
2022-01-18 18:10:57 +00:00
func (server *Server) Serve() {
var wg sync.WaitGroup
mime.AddExtensionType(".bin", "application/octet-stream")
2022-01-18 18:10:57 +00:00
if server.EnableHTTP2 {
server.TLSConfig.Config.NextProtos = []string{
2022-06-06 20:50:40 +00:00
"h2",
2022-01-18 18:10:57 +00:00
"http/1.1",
}
} else {
server.AddExtraHeader("Connection", "close")
}
2022-06-06 20:26:50 +00:00
if server.UseFastHTTP {
wg.Add(1)
go func() {
defer wg.Done()
2022-01-18 18:10:57 +00:00
2022-06-06 20:26:50 +00:00
handler := func(ctx *fasthttp.RequestCtx) RequestContext {
return NewRequestContextFromFastHttp(server, ctx)
}
2022-06-06 20:26:50 +00:00
if server.FastHTTPRequestServer != nil {
handler = server.FastHTTPRequestServer
}
2022-06-06 20:26:50 +00:00
s := &fasthttp.Server{
2022-06-09 15:49:40 +00:00
ReadTimeout: server.ReadTimeout,
WriteTimeout: server.WriteTimeout,
IdleTimeout: server.IdleTimeout,
2022-06-06 20:26:50 +00:00
Handler: func(ctx *fasthttp.RequestCtx) {
server.Handler(handler(ctx))
},
NoDefaultServerHeader: true,
NoDefaultDate: true,
DisableKeepalive: !server.EnableHTTP2,
TCPKeepalive: server.EnableHTTP2,
TLSConfig: server.TLSConfig.Config,
}
2022-01-18 18:10:57 +00:00
2022-06-06 20:26:50 +00:00
if server.EnableHTTP2 {
http2.ConfigureServer(s, http2.ServerConfig{
Debug: server.Debug,
})
}
2022-01-18 18:10:57 +00:00
2022-06-06 20:26:50 +00:00
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{
2022-06-09 15:49:40 +00:00
ReadTimeout: server.ReadTimeout,
WriteTimeout: server.WriteTimeout,
IdleTimeout: server.IdleTimeout,
Addr: server.ListenAddress,
TLSConfig: server.TLSConfig.Config,
2022-06-06 20:26:50 +00:00
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)
}
}()
}
2022-01-18 18:10:57 +00:00
if server.EnableHTTP3 {
wg.Add(1)
go func() {
2022-01-18 18:10:57 +00:00
defer wg.Done()
handler := func(w http.ResponseWriter, r *http.Request) RequestContext {
return NewRequestContextFromHttp(server, w, r)
}
if server.NetHTTPRequestServer != nil {
handler = server.NetHTTPRequestServer
}
2022-01-18 18:10:57 +00:00
s := &http3.Server{
Addr: server.ListenAddress,
QuicConfig: &quic.Config{
HandshakeIdleTimeout: server.ReadTimeout,
MaxIdleTimeout: server.IdleTimeout,
KeepAlivePeriod: server.IdleTimeout,
2022-01-18 18:10:57 +00:00
},
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))
}),
2022-01-18 18:10:57 +00:00
}
2022-06-06 21:25:30 +00:00
//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()
}
}()
2022-01-18 18:10:57 +00:00
log.Printf("[Server] Serving UDP on %s", server.ListenAddress)
err := s.ListenAndServe()
if err != nil {
log.Panic(err)
}
}()
2022-01-18 18:10:57 +00:00
}
wg.Wait()
2022-01-18 18:10:57 +00:00
}
func (server *Server) GetExtraHeaders() map[string]string {
server.extraHeadersMutex.RLock()
r := make(map[string]string)
2022-01-18 18:32:19 +00:00
if server.extraHeaders != nil {
for key, value := range server.extraHeaders {
r[key] = value
}
2022-01-18 18:10:57 +00:00
}
server.extraHeadersMutex.RUnlock()
return r
}
func (server *Server) AddExtraHeader(key, value string) {
server.extraHeadersMutex.Lock()
2022-01-18 18:32:19 +00:00
if server.extraHeaders == nil {
server.extraHeaders = make(map[string]string)
}
2022-01-18 18:10:57 +00:00
server.extraHeaders[key] = value
server.extraHeadersMutex.Unlock()
}