#4894 fix-4893-支持对接口进行流量控制

Merged
chenyifan01 merged 2 commits from fix-4893 into V20231120 5 months ago
  1. +27
    -0
      modules/context/repo.go
  2. +23
    -0
      modules/setting/ratelimit.go
  3. +1
    -0
      modules/setting/setting.go
  4. +2
    -0
      options/locale/locale_en-US.ini
  5. +2
    -0
      options/locale/locale_zh-CN.ini
  6. +6
    -6
      routers/routes/routes.go
  7. +12
    -0
      templates/status/429.tmpl

+ 27
- 0
modules/context/repo.go View File

@@ -8,6 +8,7 @@ package context
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"path"
"strings"
@@ -22,6 +23,7 @@ import (
"gitea.com/macaron/macaron"
"github.com/editorconfig/editorconfig-core-go/v2"
"github.com/unknwon/com"
"golang.org/x/time/rate"
)

// PullRequest contains informations to make a pull request
@@ -371,6 +373,31 @@ func RepoIDAssignment() macaron.Handler {
}
}

func LowLimiter() macaron.Handler {
return limiterHandler(setting.RateLimitConfig.LowRate, setting.RateLimitConfig.LowBurst)
}

func limiterHandler(rateLimit int, burst int) macaron.Handler {
var limiter *rate.Limiter
if setting.RateLimitConfig.Enabled {
limiter = rate.NewLimiter(rate.Limit(rateLimit), burst)
}

return func(ctx *Context) {
if !ctx.IsSigned {
if setting.RateLimitConfig.Enabled && limiter != nil && !limiter.Allow() {
ctx.HTML(http.StatusTooManyRequests, "status/429")
return
}
}
ctx.Next()
}
}

func HighLimiter() macaron.Handler {
return limiterHandler(setting.RateLimitConfig.HighRate, setting.RateLimitConfig.HighBurst)
}

// RepoAssignment returns a macaron to handle repository assignment
func RepoAssignment() macaron.Handler {
return func(ctx *Context) {


+ 23
- 0
modules/setting/ratelimit.go View File

@@ -0,0 +1,23 @@
package setting

var RateLimitConfig *RateLimit

type RateLimit struct {
Enabled bool
LowRate int
LowBurst int
HighRate int
HighBurst int
}

func initRateLimitConfig() {
sec := Cfg.Section("rate_limit")
RateLimitConfig = &RateLimit{
Enabled: sec.Key("Enabled").MustBool(true),
LowRate: sec.Key("LowRate").MustInt(10),
LowBurst: sec.Key("LowBurst").MustInt(20),
HighRate: sec.Key("HighRate").MustInt(20),
HighBurst: sec.Key("HighBurst").MustInt(30),
}

}

+ 1
- 0
modules/setting/setting.go View File

@@ -2122,4 +2122,5 @@ func NewServices() {
newTaskService()
NewQueueService()
newPhoneService()
initRateLimitConfig()
}

+ 2
- 0
options/locale/locale_en-US.ini View File

@@ -99,8 +99,10 @@ loading = Loading…

error404_index = Request forbidden by administrative rules
error500_index = Internal Server Error
error429_index = Request forbidden by administrative rules!
error404 = The page you are trying to reach either <strong>does not exist</strong> or <strong>you are not authorized</strong> to view it.
error500= Sorry, the site has encountered some problems, we are trying to <strong>fix the page</strong>, please try again later.
error429 = Too many requests, please try again later.
[error]
occurred = An error has occurred
report_message = An error has occurred


+ 2
- 0
options/locale/locale_zh-CN.ini View File

@@ -99,8 +99,10 @@ loading=正在加载...

error404_index = 您的访问受限!
error500_index = 抱歉!您指定的网页无法访问。
error429_index = 您的访问受限!
error404=您正尝试访问的页面 <strong>不存在</strong> 或 <strong>您尚未被授权</strong> 查看该页面。
error500=抱歉,站点遇到一些问题,我们正尝试<strong>修复网页</strong>,请您稍后再试。
error429 = 您访问的接口访问量超过限制,请稍后再试。

[error]
occurred=发生错误


+ 6
- 6
routers/routes/routes.go View File

@@ -966,7 +966,7 @@ func RegisterRoutes(m *macaron.Macaron) {
}, reqSignIn)

// ***** Release Attachment Download without Signin
m.Get("/:username/:reponame/releases/download/:vTag/:fileName", ignSignIn, context.RepoAssignment(), repo.MustBeNotEmpty, repo.RedirectDownload)
m.Get("/:username/:reponame/releases/download/:vTag/:fileName", ignSignIn, context.LowLimiter(), context.RepoAssignment(), repo.MustBeNotEmpty, repo.RedirectDownload)

m.Group("/:username/:reponame", func() {
m.Group("/settings", func() {
@@ -1060,7 +1060,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Combo("/compare/*", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists).
Get(repo.SetDiffViewStyle, repo.CompareDiff).
Post(reqSignIn, context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
}, context.RepoAssignment(), context.UnitTypes())
}, context.LowLimiter(), context.RepoAssignment(), context.UnitTypes())

// Grouping for those endpoints that do require authentication
m.Group("/:username/:reponame", func() {
@@ -1187,7 +1187,7 @@ func RegisterRoutes(m *macaron.Macaron) {
}
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
})
}, ignSignIn, context.RepoAssignment(), context.UnitTypes(), reqRepoReleaseReader)
}, ignSignIn, context.LowLimiter(), context.RepoAssignment(), context.UnitTypes(), reqRepoReleaseReader)

m.Group("/:username/:reponame", func() {
m.Post("/topics", repo.TopicsPost)
@@ -1583,18 +1583,18 @@ func RegisterRoutes(m *macaron.Macaron) {
}, context.RepoRef(), reqRepoCodeReader)
m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)",
repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff)
}, ignSignIn, context.RepoAssignment(), context.UnitTypes())
}, ignSignIn, context.LowLimiter(), context.RepoAssignment(), context.UnitTypes())
m.Group("/:username/:reponame", func() {
m.Get("/stars", repo.Stars)
m.Get("/watchers", repo.Watchers)
m.Get("/search", reqRepoCodeReader, repo.Search)
}, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes())
}, ignSignIn, context.LowLimiter(), context.RepoAssignment(), context.RepoRef(), context.UnitTypes())

m.Group("/:username", func() {
m.Group("/:reponame", func() {
m.Get("", repo.SetEditorconfigIfExists, repo.Home)
m.Get("\\.git$", repo.SetEditorconfigIfExists, repo.Home)
}, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes())
}, ignSignIn, context.HighLimiter(), context.RepoAssignment(), context.RepoRef(), context.UnitTypes())

m.Group("/:reponame", func() {
m.Group("\\.git/info/lfs", func() {


+ 12
- 0
templates/status/429.tmpl View File

@@ -0,0 +1,12 @@
{{template "base/head" .}}
{{if .IsRepo}}<div class="repository">{{template "repo/header" .}}</div>{{end}}

<div class="ui container center" style="min-height: 70%;">
<div class="ui basic very padded segment">

<h2>{{.i18n.Tr "error429_index"}}</h2>
<p>{{.i18n.Tr "error429" | Safe}}</p>
</div>
</div>

{{template "base/footer" .}}

Loading…
Cancel
Save