]> 127.0.0.1 Git - bili_danmu/.git/commitdiff
Add Web服务连接限制
authorqydysky <qydysky@foxmail.com>
Sun, 28 May 2023 04:04:41 +0000 (12:04 +0800)
committerqydysky <qydysky@foxmail.com>
Sun, 28 May 2023 04:04:41 +0000 (12:04 +0800)
CV/Var.go
F/api.go
Reply/F.go
demo/config/config_K_v.json
go.mod
go.sum

index aa2956b90b0b832c091be6119fdf9485d4d14ee5..ab934f9e8314dd72f5562fc297a93ddc39e2b070 100644 (file)
--- a/CV/Var.go
+++ b/CV/Var.go
@@ -67,6 +67,7 @@ type Common struct {
        Danmu_Main_mq     *mq.Msgq              //消息
        ReqPool           *pool.Buf[reqf.Req]   //请求池
        SerF              *web.WebPath          //web服务处理
+       SerLimit          *web.Limits           //Web服务连接限制
        StartT            time.Time             //启动时间
 }
 
@@ -145,6 +146,7 @@ func (t *Common) Copy() *Common {
                Danmu_Main_mq:     t.Danmu_Main_mq,
                ReqPool:           t.ReqPool,
                SerF:              t.SerF,
+               SerLimit:          t.SerLimit,
                StartT:            t.StartT,
        }
 
@@ -292,6 +294,7 @@ func (t *Common) Init() *Common {
 
        t.SerF = new(web.WebPath)
        t.Stream_url = &url.URL{}
+       t.SerLimit = &web.Limits{}
 
        if serAdress, ok := t.K_v.LoadV("Web服务地址").(string); ok && serAdress != "" {
                serUrl, e := url.Parse("http://" + serAdress)
@@ -303,8 +306,38 @@ func (t *Common) Init() *Common {
                        Addr: serUrl.Host,
                }, t.SerF)
 
+               if limits, ok := t.K_v.LoadV(`Web服务连接限制`).([]any); ok {
+                       for i := 0; i < len(limits); i++ {
+                               if vm, ok := limits[i].(map[string]any); ok {
+                                       if cidr, ok := vm["cidr"].(string); !ok {
+                                               continue
+                                       } else if max, ok := vm["max"].(float64); !ok {
+                                               continue
+                                       } else {
+                                               t.SerLimit.AddLimitItem(web.NewLimitItem(int(max)).Cidr(cidr))
+                                       }
+                               }
+                       }
+               }
+
                if val, ok := t.K_v.LoadV("性能路径").(string); ok && val != "" {
-                       t.SerF.Store(val, func(w http.ResponseWriter, _ *http.Request) {
+                       var cache web.Cache
+                       t.SerF.Store(val, func(w http.ResponseWriter, r *http.Request) {
+                               //limit
+                               if t.SerLimit.AddCount(r) {
+                                       web.WithStatusCode(w, http.StatusTooManyRequests)
+                                       return
+                               }
+
+                               //cache
+                               if bp, ok := cache.IsCache(val); ok {
+                                       w.Header().Set("Content-Type", "application/json")
+                                       w.Header().Set("Cache-Control", "max-age=5")
+                                       _, _ = w.Write(*bp)
+                                       return
+                               }
+                               w = cache.Cache(val, time.Second*5, w)
+
                                var memStats runtime.MemStats
                                runtime.ReadMemStats(&memStats)
 
@@ -465,7 +498,7 @@ type ResStruct struct {
        Data    any    `json:"data"`
 }
 
-func (t ResStruct) Write(w http.ResponseWriter) {
+func (t ResStruct) Write(w http.ResponseWriter) []byte {
        w.Header().Set("Content-Type", "application/json")
        data, e := json.Marshal(t)
        if e != nil {
@@ -475,4 +508,5 @@ func (t ResStruct) Write(w http.ResponseWriter) {
                data, _ = json.Marshal(t)
        }
        _, _ = w.Write(data)
+       return data
 }
index c6b578463b0bdbb5eae1a2b50dbb6cf7338705f9..386ec1831268e88d603bc22783a80480088a4d2a 100644 (file)
--- a/F/api.go
+++ b/F/api.go
@@ -26,6 +26,7 @@ import (
        limit "github.com/qydysky/part/limit"
        reqf "github.com/qydysky/part/reqf"
        psync "github.com/qydysky/part/sync"
+       web "github.com/qydysky/part/web"
 
        "github.com/mdp/qrterminal/v3"
        qr "github.com/skip2/go-qrcode"
@@ -1340,7 +1341,12 @@ func (c *GetFunc) Get_cookie() (missKey []string) {
                defer os.RemoveAll(`qr.png`)
                //启动web
                if scanPath, ok := c.K_v.LoadV("扫码登录路径").(string); ok && scanPath != "" {
-                       c.SerF.Store(scanPath, func(w http.ResponseWriter, _ *http.Request) {
+                       c.SerF.Store(scanPath, func(w http.ResponseWriter, r *http.Request) {
+                               //limit
+                               if c.SerLimit.AddCount(r) {
+                                       web.WithStatusCode(w, http.StatusTooManyRequests)
+                                       return
+                               }
                                _ = file.New("qr.png", 0, true).CopyToIoWriter(w, humanize.MByte, true)
                        })
                        if c.K_v.LoadV(`扫码登录自动打开标签页`).(bool) {
index e2e1607e829f81ed9b256e8e8e5f5824e8cbc232..3d31e101f80cb0038c6b2fc2d3ee3bb9e5f889be 100644 (file)
@@ -1148,15 +1148,50 @@ func init() {
 
                // debug模式
                if de, ok := c.C.K_v.LoadV(`debug模式`).(bool); ok && de {
-                       c.C.SerF.Store("/debug/pprof/", pprof.Index)
-                       c.C.SerF.Store("/debug/pprof/cmdline", pprof.Cmdline)
-                       c.C.SerF.Store("/debug/pprof/profile", pprof.Profile)
-                       c.C.SerF.Store("/debug/pprof/symbol", pprof.Symbol)
-                       c.C.SerF.Store("/debug/pprof/trace", pprof.Trace)
+                       c.C.SerF.Store("/debug/pprof/", func(w http.ResponseWriter, r *http.Request) {
+                               //limit
+                               if c.C.SerLimit.AddCount(r) {
+                                       pweb.WithStatusCode(w, http.StatusTooManyRequests)
+                                       return
+                               }
+                               pprof.Index(w, r)
+                       })
+                       c.C.SerF.Store("/debug/pprof/cmdline", func(w http.ResponseWriter, r *http.Request) {
+                               //limit
+                               if c.C.SerLimit.AddCount(r) {
+                                       pweb.WithStatusCode(w, http.StatusTooManyRequests)
+                                       return
+                               }
+                               pprof.Cmdline(w, r)
+                       })
+                       c.C.SerF.Store("/debug/pprof/profile", func(w http.ResponseWriter, r *http.Request) {
+                               //limit
+                               if c.C.SerLimit.AddCount(r) {
+                                       pweb.WithStatusCode(w, http.StatusTooManyRequests)
+                                       return
+                               }
+                               pprof.Profile(w, r)
+                       })
+                       c.C.SerF.Store("/debug/pprof/symbol", func(w http.ResponseWriter, r *http.Request) {
+                               //limit
+                               if c.C.SerLimit.AddCount(r) {
+                                       pweb.WithStatusCode(w, http.StatusTooManyRequests)
+                                       return
+                               }
+                               pprof.Symbol(w, r)
+                       })
+                       c.C.SerF.Store("/debug/pprof/trace", func(w http.ResponseWriter, r *http.Request) {
+                               //limit
+                               if c.C.SerLimit.AddCount(r) {
+                                       pweb.WithStatusCode(w, http.StatusTooManyRequests)
+                                       return
+                               }
+                               pprof.Trace(w, r)
+                       })
                }
 
                // 直播流回放连接限制
-               var climit pweb.CountLimits
+               var climit pweb.Limits
                if limits, ok := c.C.K_v.LoadV(`直播流回放连接限制`).([]any); ok {
                        for i := 0; i < len(limits); i++ {
                                if vm, ok := limits[i].(map[string]any); ok {
@@ -1165,24 +1200,28 @@ func init() {
                                        } else if max, ok := vm["max"].(float64); !ok {
                                                continue
                                        } else {
-                                               climit.SetMaxCount(cidr, int(max))
+                                               climit.AddLimitItem(pweb.NewLimitItem(int(max)).Cidr(cidr))
                                        }
                                }
                        }
                }
 
+               // cache
+               var cache pweb.Cache
+
                // 直播流主页
                c.C.SerF.Store(path, func(w http.ResponseWriter, r *http.Request) {
+                       //limit
+                       if c.C.SerLimit.AddCount(r) {
+                               pweb.WithStatusCode(w, http.StatusTooManyRequests)
+                               return
+                       }
+
                        p := strings.TrimPrefix(r.URL.Path, path)
+
                        if len(p) == 0 || p[len(p)-1] == '/' {
                                p += "index.html"
                        }
-                       f := file.New("html/streamList/"+p, 0, true)
-                       if !f.IsExist() || f.IsDir() {
-                               w.WriteHeader(http.StatusNotFound)
-                               return
-                       }
-
                        if strings.HasSuffix(p, ".js") {
                                w.Header().Set("content-type", "application/javascript")
                        } else if strings.HasSuffix(p, ".css") {
@@ -1190,11 +1229,42 @@ func init() {
                        } else if strings.HasSuffix(p, ".html") {
                                w.Header().Set("content-type", "text/html")
                        }
-                       _ = f.CopyToIoWriter(w, humanize.MByte, true)
+
+                       //cache
+                       if bp, ok := cache.IsCache("html/streamList/" + p); ok {
+                               w.Header().Set("Cache-Control", "max-age=60")
+                               _, _ = w.Write(*bp)
+                               return
+                       }
+                       w = cache.Cache("html/streamList/"+p, time.Minute, w)
+
+                       f := file.New("html/streamList/"+p, 0, true)
+                       if !f.IsExist() || f.IsDir() {
+                               w.WriteHeader(http.StatusNotFound)
+                               return
+                       }
+
+                       b, _ := f.ReadAll(humanize.KByte, humanize.MByte)
+                       _, _ = w.Write(b)
                })
 
                // 直播流文件列表api
-               c.C.SerF.Store(path+"filePath", func(w http.ResponseWriter, _ *http.Request) {
+               c.C.SerF.Store(path+"filePath", func(w http.ResponseWriter, r *http.Request) {
+                       //limit
+                       if c.C.SerLimit.AddCount(r) {
+                               pweb.WithStatusCode(w, http.StatusTooManyRequests)
+                               return
+                       }
+
+                       //cache
+                       if bp, ok := cache.IsCache(path + "filePath"); ok {
+                               w.Header().Set("Content-Type", "application/json")
+                               w.Header().Set("Cache-Control", "max-age=5")
+                               _, _ = w.Write(*bp)
+                               return
+                       }
+                       w = cache.Cache(path+"filePath", time.Second*5, w)
+
                        if v, ok := c.C.K_v.LoadV(`直播流保存位置`).(string); ok && v != "" {
                                type dirEntryDirs []fs.DirEntry
                                var list dirEntryDirs
@@ -1246,10 +1316,16 @@ func init() {
 
                // 直播流播放器
                c.C.SerF.Store(path+"player/", func(w http.ResponseWriter, r *http.Request) {
+                       //limit
+                       if c.C.SerLimit.AddCount(r) {
+                               pweb.WithStatusCode(w, http.StatusTooManyRequests)
+                               return
+                       }
+
                        // 直播流回放连接限制
                        if climit.ReachMax(r) {
                                w.WriteHeader(http.StatusTooManyRequests)
-                               _, _ = w.Write([]byte("已达到设定最大连接数"))
+                               _, _ = w.Write([]byte(http.StatusText(http.StatusTooManyRequests)))
                                return
                        }
 
@@ -1257,11 +1333,6 @@ func init() {
                        if len(p) == 0 || p[len(p)-1] == '/' {
                                p += "index.html"
                        }
-                       f := file.New("html/artPlayer/"+p, 0, true)
-                       if !f.IsExist() {
-                               w.WriteHeader(http.StatusNotFound)
-                               return
-                       }
 
                        if strings.HasSuffix(p, ".js") {
                                w.Header().Set("content-type", "application/javascript")
@@ -1270,11 +1341,33 @@ func init() {
                        } else if strings.HasSuffix(p, ".html") {
                                w.Header().Set("content-type", "text/html")
                        }
-                       _ = f.CopyToIoWriter(w, humanize.MByte, true)
+
+                       //cache
+                       if bp, ok := cache.IsCache("html/artPlayer/" + p); ok {
+                               w.Header().Set("Cache-Control", "max-age=60")
+                               _, _ = w.Write(*bp)
+                               return
+                       }
+                       w = cache.Cache("html/artPlayer/"+p, time.Minute, w)
+
+                       f := file.New("html/artPlayer/"+p, 0, true)
+                       if !f.IsExist() {
+                               w.WriteHeader(http.StatusNotFound)
+                               return
+                       }
+
+                       b, _ := f.ReadAll(humanize.KByte, humanize.MByte)
+                       _, _ = w.Write(b)
                })
 
                // 流地址
                c.C.SerF.Store(path+"stream", func(w http.ResponseWriter, r *http.Request) {
+                       //limit
+                       if c.C.SerLimit.AddCount(r) {
+                               pweb.WithStatusCode(w, http.StatusTooManyRequests)
+                               return
+                       }
+
                        // 直播流回放连接限制
                        if climit.AddCount(r) {
                                w.WriteHeader(http.StatusTooManyRequests)
@@ -1399,6 +1492,19 @@ func init() {
 
                // 弹幕回放
                c.C.SerF.Store(path+"player/ws", func(w http.ResponseWriter, r *http.Request) {
+                       //limit
+                       if c.C.SerLimit.AddCount(r) {
+                               pweb.WithStatusCode(w, http.StatusTooManyRequests)
+                               return
+                       }
+
+                       // 直播流回放连接限制
+                       if climit.ReachMax(r) {
+                               w.WriteHeader(http.StatusTooManyRequests)
+                               _, _ = w.Write([]byte(http.StatusText(http.StatusTooManyRequests)))
+                               return
+                       }
+
                        var rpath string
 
                        if qref := r.URL.Query().Get("ref"); rpath == "" && qref != "" {
index 9355cdb1be18e6eb9199472b63c6b3b6f8f12d76..f4e86dc59c88778701c09d251aceadceb4028347 100644 (file)
     ],
     "Web服务地址-help":"填写本程序各组件所用的服务地址 例0.0.0.0:10000 为空时不启动Web服务",
     "Web服务地址":"0.0.0.0:10000",
+    "Web服务连接限制-help": "限制回放连接数,<0无限制,=0禁止,>0最大数量",
+    "Web服务连接限制": [
+        {
+            "cidr":"0.0.0.0/0",
+            "max":-1
+        }
+    ],
     "直播Web服务路径":"/web/",
     "直播Web可以发送弹幕":true,
     "弹幕回放-help": "仅保存当前直播间流为true时才有效",
diff --git a/go.mod b/go.mod
index e6bf7a3596ff1b85e7464c356b5fbb3b319da799..ba299da0be78254f9e78696a28f4ca0bc689982a 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.20
 require (
        github.com/gotk3/gotk3 v0.6.2
        github.com/mdp/qrterminal/v3 v3.0.0
-       github.com/qydysky/part v0.27.12
+       github.com/qydysky/part v0.27.16
        github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
        github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
        golang.org/x/text v0.9.0
diff --git a/go.sum b/go.sum
index f83e64afa4162ccbf7b6e16f23ef076ec118c7b7..f3add4fe2ba3c8dfbf8d980c37ff5afba77e5aa8 100644 (file)
--- a/go.sum
+++ b/go.sum
@@ -31,8 +31,8 @@ github.com/mdp/qrterminal/v3 v3.0.0/go.mod h1:NJpfAs7OAm77Dy8EkWrtE4aq+cE6McoLXl
 github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI=
 github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/qydysky/part v0.27.12 h1:IGVfRuiYYhfaIwlTYZL4QqFNJw7uF0l6uQcaI4Ajnso=
-github.com/qydysky/part v0.27.12/go.mod h1:IEMpGB0NBl6MklZmoenSpS5ChhaIL79JYFo6mF1UkAU=
+github.com/qydysky/part v0.27.16 h1:RJ254afpd3qtTDE5/E4M8pt/XHWp6L5Cr/BYUj7Ai4w=
+github.com/qydysky/part v0.27.16/go.mod h1:IEMpGB0NBl6MklZmoenSpS5ChhaIL79JYFo6mF1UkAU=
 github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=