feat: middleware, expvar

This commit is contained in:
2026-04-27 14:02:26 +02:00
parent 2fd3a1d57b
commit 7affd35ae4
5 changed files with 44 additions and 11 deletions
+21 -1
View File
@@ -3,8 +3,10 @@ package main
import ( import (
"context" "context"
"database/sql" "database/sql"
"expvar"
"flag" "flag"
"os" "os"
"runtime"
"strings" "strings"
"sync" "sync"
"time" "time"
@@ -34,7 +36,8 @@ type config struct {
maxOpenConns int maxOpenConns int
maxIdleConns int maxIdleConns int
maxIdleTime string maxIdleTime string
} // Add a new limiter struct containing fields for the requests-per-second and burst }
// Add a new limiter struct containing fields for the requests-per-second and burst
// values, and a boolean field which we can use to enable/disable rate limiting // values, and a boolean field which we can use to enable/disable rate limiting
// altogether. // altogether.
limiter struct { limiter struct {
@@ -138,6 +141,23 @@ func main() {
// established. // established.
logger.PrintInfo("database connection pool established", nil) logger.PrintInfo("database connection pool established", nil)
// Publish a new "version" variable in the expvar handler containing our application
// version number (currently the constant "1.0.0").
expvar.NewString("version").Set(version)
expvar.Publish("goroutines", expvar.Func(func() any {
return runtime.NumGoroutine()
}))
// Publish the database connection pool statistics.
expvar.Publish("database", expvar.Func(func() any {
return db.Stats()
}))
// Publish the current Unix timestamp.
expvar.Publish("timestamp", expvar.Func(func() any {
return time.Now().Unix()
}))
app := &application{ app := &application{
config: cfg, config: cfg,
logger: logger, logger: logger,
+18 -9
View File
@@ -247,27 +247,36 @@ func (app *application) requirePermission(code string, next http.HandlerFunc) ht
func (app *application) enableCORS(next http.Handler) http.Handler { func (app *application) enableCORS(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Add the "Vary: Origin" header.
w.Header().Add("Vary", "Origin") w.Header().Add("Vary", "Origin")
// Get the value of the request's Origin header. // Add the "Vary: Access-Control-Request-Method" header.
w.Header().Add("Vary", "Access-Control-Request-Method")
origin := r.Header.Get("Origin") origin := r.Header.Get("Origin")
// Only run this if there's an Origin request header present AND at least one
// trusted origin is configured.
if origin != "" && len(app.config.cors.trustedOrigins) != 0 { if origin != "" && len(app.config.cors.trustedOrigins) != 0 {
// Loop through the list of trusted origins, checking to see if the request
// origin exactly matches one of them.
for i := range app.config.cors.trustedOrigins { for i := range app.config.cors.trustedOrigins {
if origin == app.config.cors.trustedOrigins[i] { if origin == app.config.cors.trustedOrigins[i] {
// If there is a match, then set a "Access-Control-Allow-Origin
// response header with the request origin as the value.
w.Header().Set("Access-Control-Allow-Origin", origin) w.Header().Set("Access-Control-Allow-Origin", origin)
// Check if the request has the HTTP method OPTIONS and contains the
// "Access-Control-Request-Method" header. If it does, then we treat
// it as a preflight request.
if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" {
// Set the necessary preflight response headers, as discussed
// previously.
w.Header().Set("Access-Control-Allow-Methods", "OPTIONS, PUT, PATCH, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")
// Write the headers along with a 200 OK status and return from
// the // the middleware with no further action.
w.WriteHeader(http.StatusOK)
return
}
} }
} }
} }
// Call the next handler in the chain.
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
}) })
} }
+3
View File
@@ -1,6 +1,7 @@
package main package main
import ( import (
"expvar"
"net/http" "net/http"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
@@ -27,6 +28,8 @@ func (app *application) routes() http.Handler {
router.HandlerFunc(http.MethodPost, "/v1/tokens/authentication", router.HandlerFunc(http.MethodPost, "/v1/tokens/authentication",
app.createAuthenticationTokenHandler) app.createAuthenticationTokenHandler)
// Register a new GET /debug/vars endpoint pointing to the expvar handler.
router.Handler(http.MethodGet, "/debug/vars", expvar.Handler())
return app.recoverPanic(app.enableCORS(app.rateLimit(app.authenticate(router)))) return app.recoverPanic(app.enableCORS(app.rateLimit(app.authenticate(router))))
} }
@@ -26,7 +26,7 @@ const html = `
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({ body: JSON.stringify({
email: 'alice@example.com', email: 'grace@example.com',
password: 'pa55word' password: 'pa55word'
}) })
}).then( }).then(
@@ -13,6 +13,7 @@ http:
"name": "Grace", "name": "Grace",
"email": "grace@example.com", "email": "grace@example.com",
"password": "pa55word" "password": "pa55word"
} }
auth: inherit auth: inherit