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 (
"context"
"database/sql"
"expvar"
"flag"
"os"
"runtime"
"strings"
"sync"
"time"
@@ -34,7 +36,8 @@ type config struct {
maxOpenConns int
maxIdleConns int
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
// altogether.
limiter struct {
@@ -138,6 +141,23 @@ func main() {
// established.
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{
config: cfg,
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 {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Add the "Vary: Origin" header.
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")
// 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 {
// 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 {
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)
// 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)
})
}
+3
View File
@@ -1,6 +1,7 @@
package main
import (
"expvar"
"net/http"
"github.com/julienschmidt/httprouter"
@@ -27,6 +28,8 @@ func (app *application) routes() http.Handler {
router.HandlerFunc(http.MethodPost, "/v1/tokens/authentication",
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))))
}
@@ -26,7 +26,7 @@ const html = `
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: 'alice@example.com',
email: 'grace@example.com',
password: 'pa55word'
})
}).then(
@@ -13,6 +13,7 @@ http:
"name": "Grace",
"email": "grace@example.com",
"password": "pa55word"
}
auth: inherit