mirror of https://github.com/0xERR0R/blocky.git
120 lines
2.6 KiB
Go
120 lines
2.6 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/0xERR0R/blocky/config"
|
|
"github.com/0xERR0R/blocky/service"
|
|
"github.com/0xERR0R/blocky/util"
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/cors"
|
|
)
|
|
|
|
// httpMiscService implements service.HTTPService.
|
|
//
|
|
// This supports the existing single HTTP/HTTPS endpoints
|
|
// that expose everything. The goal is to split it up
|
|
// and remove it.
|
|
type httpMiscService struct {
|
|
service.SimpleHTTP
|
|
}
|
|
|
|
func newHTTPMiscService(cfg *config.Config) *httpMiscService {
|
|
endpoints := util.ConcatSlices(
|
|
service.EndpointsFromAddrs(service.HTTPProtocol, cfg.Ports.HTTP),
|
|
service.EndpointsFromAddrs(service.HTTPSProtocol, cfg.Ports.HTTPS),
|
|
)
|
|
|
|
s := &httpMiscService{
|
|
SimpleHTTP: service.NewSimpleHTTP("API", endpoints),
|
|
}
|
|
|
|
configureHTTPRouter(s.Router(), cfg)
|
|
|
|
return s
|
|
}
|
|
|
|
// httpServer implements subServer for HTTP.
|
|
type httpServer struct {
|
|
service.HTTPService
|
|
|
|
inner http.Server
|
|
}
|
|
|
|
func newHTTPServer(svc service.HTTPService) *httpServer {
|
|
const (
|
|
readHeaderTimeout = 20 * time.Second
|
|
readTimeout = 20 * time.Second
|
|
writeTimeout = 20 * time.Second
|
|
)
|
|
|
|
return &httpServer{
|
|
HTTPService: svc,
|
|
|
|
inner: http.Server{
|
|
Handler: withCommonMiddleware(svc.Router()),
|
|
ReadHeaderTimeout: readHeaderTimeout,
|
|
ReadTimeout: readTimeout,
|
|
WriteTimeout: writeTimeout,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (s *httpServer) Serve(ctx context.Context, l net.Listener) error {
|
|
go func() {
|
|
<-ctx.Done()
|
|
|
|
s.inner.Close()
|
|
}()
|
|
|
|
return s.inner.Serve(l)
|
|
}
|
|
|
|
func withCommonMiddleware(inner http.Handler) *chi.Mux {
|
|
// Middleware must be defined before routes, so
|
|
// create a new router and mount the inner handler
|
|
mux := chi.NewMux()
|
|
|
|
mux.Use(
|
|
secureHeadersMiddleware,
|
|
newCORSMiddleware(),
|
|
)
|
|
|
|
mux.Mount("/", inner)
|
|
|
|
return mux
|
|
}
|
|
|
|
type httpMiddleware = func(http.Handler) http.Handler
|
|
|
|
func secureHeadersMiddleware(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.TLS != nil {
|
|
w.Header().Set("strict-transport-security", "max-age=63072000")
|
|
w.Header().Set("x-frame-options", "DENY")
|
|
w.Header().Set("x-content-type-options", "nosniff")
|
|
w.Header().Set("x-xss-protection", "1; mode=block")
|
|
}
|
|
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
func newCORSMiddleware() httpMiddleware {
|
|
const corsMaxAge = 5 * time.Minute
|
|
|
|
options := cors.Options{
|
|
AllowCredentials: true,
|
|
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
|
|
AllowedMethods: []string{"GET", "POST"},
|
|
AllowedOrigins: []string{"*"},
|
|
ExposedHeaders: []string{"Link"},
|
|
MaxAge: int(corsMaxAge.Seconds()),
|
|
}
|
|
|
|
return cors.New(options).Handler
|
|
}
|