package router import ( "net/http" "github.com/gin-gonic/gin" ) // LimitMiddleware caps the number of requests executing the routes it guards // at limit. A request blocks until a slot frees up; if the request context is // cancelled or expires while waiting, it answers 503 Service Unavailable. // // The semaphore is owned by the returned handler, so sharing a single instance // across several routes serialises those routes against each other. The engine // relies on this to serialise every operation that mutates the canonical game // state file, which must never run concurrently against one storage directory. func LimitMiddleware(limit int) gin.HandlerFunc { if limit <= 0 { panic("limit must be greater than 0") } semaphore := make(chan struct{}, limit) return func(c *gin.Context) { select { case semaphore <- struct{}{}: defer func() { <-semaphore }() c.Next() case <-c.Request.Context().Done(): c.AbortWithStatus(http.StatusServiceUnavailable) } } }