fix(web): bound the panel's open connections and share asset gzip

The engine-wide gzip middleware gave every in-flight request its own
compressor of about 1 MiB, and the public /assets bundles let an
anonymous client keep a handler blocked in Write until WriteTimeout, so
a thousand stalled downloads held a gigabyte. The listener itself took
any number of connections, each holding buffers and a goroutine before
any login.

The bundles are now served from a gzip copy made once per file, with
the assets prefix left out of the per-request middleware, and the panel
listener closes a new connection once 2048 are open or its source (an
IPv4 address or IPv6 /64) holds 256; a loopback reverse proxy counts
only toward the total.
This commit is contained in:
MHSanaei
2026-11-01 01:01:38 +01:00
parent 1c748adadf
commit 3526a65f5d
7 changed files with 436 additions and 11 deletions
+9 -1
View File
@@ -10,6 +10,7 @@ import (
"testing"
"time"
"github.com/gin-gonic/gin"
"github.com/robfig/cron/v3"
"github.com/mhsanaei/3x-ui/v3/internal/database/dbtest"
@@ -42,7 +43,8 @@ func inContractScope(method, path string) bool {
return strings.HasPrefix(path, "/panel/api/") || contractExtraRoutes[method+" "+path]
}
func registeredContractRoutes(t *testing.T) map[string]bool {
// newTestEngine builds the real panel router on a throwaway database.
func newTestEngine(t *testing.T) *gin.Engine {
t.Helper()
dbtest.InitDB(t, filepath.Join(t.TempDir(), "x-ui.db"))
@@ -59,6 +61,12 @@ func registeredContractRoutes(t *testing.T) map[string]bool {
if err != nil {
t.Fatalf("init router: %v", err)
}
return engine
}
func registeredContractRoutes(t *testing.T) map[string]bool {
t.Helper()
engine := newTestEngine(t)
routes := make(map[string]bool)
for _, r := range engine.Routes() {
routes[r.Method+" "+r.Path] = true