Migrate ssl.go TLS routers from chi to routegroup
First step of the go-chi -> go-pkgz/routegroup migration. The HTTP->HTTPS redirect and ACME http-01 challenge routers are small, self-contained http.Handlers separate from the main API router, so they move cleanly: - chi.NewRouter() -> routegroup.New(http.NewServeMux()) - middleware.Throttle -> rest.Throttle (same concurrency-limit semantics) - middleware.Timeout -> local timeout helper (context deadline, mirrors chi) - drop middleware.RealIP: these routers do redirect/challenge only, with no per-IP logic, so the spoofable header trust is simply removed here - return http.Handler instead of chi.Router (callers already take http.Handler) chi stays a dependency (still used by the main API router); this only removes its use from ssl.go. go test -race, vet, golangci-lint and govulncheck clean.
This commit is contained in:
committed by
Umputun
parent
8318f89dde
commit
bb6d1450f1
+34
-15
@@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -8,12 +9,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
log "github.com/go-pkgz/lgr"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
|
||||
R "github.com/go-pkgz/rest"
|
||||
"github.com/go-pkgz/routegroup"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
// sslMode defines ssl mode for rest server
|
||||
@@ -42,13 +41,13 @@ type SSLConfig struct {
|
||||
|
||||
// httpToHTTPSRouter creates new router which does redirect from http to https server
|
||||
// with default middlewares. Used in 'static' ssl mode.
|
||||
func (s *Rest) httpToHTTPSRouter() chi.Router {
|
||||
log.Printf("[DEBUG] create https-to-http redirect routes")
|
||||
router := chi.NewRouter()
|
||||
router.Use(middleware.RealIP, R.Recoverer(log.Default()))
|
||||
router.Use(middleware.Throttle(1000), middleware.Timeout(60*time.Second))
|
||||
func (s *Rest) httpToHTTPSRouter() http.Handler {
|
||||
log.Printf("[DEBUG] create http-to-https redirect routes")
|
||||
router := routegroup.New(http.NewServeMux())
|
||||
router.Use(R.Recoverer(log.Default()))
|
||||
router.Use(R.Throttle(1000), timeout(60*time.Second))
|
||||
|
||||
router.Handle("/*", s.redirectHandler())
|
||||
router.Handle("/", s.redirectHandler())
|
||||
return router
|
||||
}
|
||||
|
||||
@@ -56,16 +55,36 @@ func (s *Rest) httpToHTTPSRouter() chi.Router {
|
||||
// with default middlewares. This part is necessary to obtain certificate from LE.
|
||||
// If it receives not a acme challenge it performs redirect to https server.
|
||||
// Used in 'auto' ssl mode.
|
||||
func (s *Rest) httpChallengeRouter(m *autocert.Manager) chi.Router {
|
||||
func (s *Rest) httpChallengeRouter(m *autocert.Manager) http.Handler {
|
||||
log.Printf("[DEBUG] create http-challenge routes")
|
||||
router := chi.NewRouter()
|
||||
router.Use(middleware.RealIP, R.Recoverer(log.Default()))
|
||||
router.Use(middleware.Throttle(1000), middleware.Timeout(60*time.Second))
|
||||
router := routegroup.New(http.NewServeMux())
|
||||
router.Use(R.Recoverer(log.Default()))
|
||||
router.Use(R.Throttle(1000), timeout(60*time.Second))
|
||||
|
||||
router.Handle("/*", m.HTTPHandler(s.redirectHandler()))
|
||||
router.Handle("/", m.HTTPHandler(s.redirectHandler()))
|
||||
return router
|
||||
}
|
||||
|
||||
// timeout returns a middleware matching chi's middleware.Timeout: it sets a
|
||||
// deadline on the request context and writes 504 Gateway Timeout if the
|
||||
// deadline is exceeded. The 504 is sent once the downstream handler returns
|
||||
// after observing the canceled context; a handler that ignores r.Context()
|
||||
// is not aborted.
|
||||
func timeout(d time.Duration) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), d)
|
||||
defer func() {
|
||||
cancel()
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
w.WriteHeader(http.StatusGatewayTimeout)
|
||||
}
|
||||
}()
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Rest) redirectHandler() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
newURL, err := s.redirectURL(r)
|
||||
|
||||
@@ -8,11 +8,37 @@ import (
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTimeout(t *testing.T) {
|
||||
t.Run("fast handler passes through and gets a deadline", func(t *testing.T) {
|
||||
var gotDeadline bool
|
||||
h := timeout(time.Second)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, gotDeadline = r.Context().Deadline()
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
_, _ = w.Write([]byte("ok"))
|
||||
}))
|
||||
rec := httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/", http.NoBody))
|
||||
assert.True(t, gotDeadline, "request context should carry a deadline")
|
||||
assert.Equal(t, http.StatusCreated, rec.Code)
|
||||
assert.Equal(t, "ok", rec.Body.String())
|
||||
})
|
||||
|
||||
t.Run("deadline exceeded writes 504", func(t *testing.T) {
|
||||
h := timeout(10*time.Millisecond)(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
|
||||
<-r.Context().Done() // honor the context: return only once the deadline fires
|
||||
}))
|
||||
rec := httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/", http.NoBody))
|
||||
assert.Equal(t, http.StatusGatewayTimeout, rec.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSSL_Redirect(t *testing.T) {
|
||||
rest := Rest{RemarkURL: "https://localhost:443"}
|
||||
|
||||
|
||||
+1
-1
@@ -16,6 +16,7 @@ require (
|
||||
github.com/go-pkgz/notify v1.3.0
|
||||
github.com/go-pkgz/repeater/v2 v2.2.0
|
||||
github.com/go-pkgz/rest v1.21.0
|
||||
github.com/go-pkgz/routegroup v1.6.0
|
||||
github.com/go-pkgz/syncs v1.3.2
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||
github.com/google/uuid v1.6.0
|
||||
@@ -48,7 +49,6 @@ require (
|
||||
github.com/go-pkgz/email v0.6.0 // indirect
|
||||
github.com/go-pkgz/expirable-cache/v3 v3.1.0 // indirect
|
||||
github.com/go-pkgz/repeater v1.2.0 // indirect
|
||||
github.com/go-pkgz/routegroup v1.6.0 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
|
||||
Reference in New Issue
Block a user