Skip to content

Commit ca31d47

Browse files
authored
Refactor arch route handlers (#32972)
1 parent 65e45fd commit ca31d47

File tree

3 files changed

+221
-150
lines changed

3 files changed

+221
-150
lines changed

modules/web/route.go

+92
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@
44
package web
55

66
import (
7+
"fmt"
78
"net/http"
89
"net/url"
910
"reflect"
11+
"regexp"
1012
"strings"
1113

14+
"code.gitea.io/gitea/modules/container"
1215
"code.gitea.io/gitea/modules/htmlutil"
1316
"code.gitea.io/gitea/modules/reqctx"
1417
"code.gitea.io/gitea/modules/setting"
18+
"code.gitea.io/gitea/modules/util"
1519
"code.gitea.io/gitea/modules/web/middleware"
1620

1721
"gitea.com/go-chi/binding"
@@ -181,6 +185,17 @@ func (r *Router) NotFound(h http.HandlerFunc) {
181185
r.chiRouter.NotFound(h)
182186
}
183187

188+
type pathProcessorParam struct {
189+
name string
190+
captureGroup int
191+
}
192+
193+
type PathProcessor struct {
194+
methods container.Set[string]
195+
re *regexp.Regexp
196+
params []pathProcessorParam
197+
}
198+
184199
func (r *Router) normalizeRequestPath(resp http.ResponseWriter, req *http.Request, next http.Handler) {
185200
normalized := false
186201
normalizedPath := req.URL.EscapedPath()
@@ -238,6 +253,83 @@ func (r *Router) normalizeRequestPath(resp http.ResponseWriter, req *http.Reques
238253
next.ServeHTTP(resp, req)
239254
}
240255

256+
func (p *PathProcessor) ProcessRequestPath(chiCtx *chi.Context, path string) bool {
257+
if !p.methods.Contains(chiCtx.RouteMethod) {
258+
return false
259+
}
260+
if !strings.HasPrefix(path, "/") {
261+
path = "/" + path
262+
}
263+
pathMatches := p.re.FindStringSubmatchIndex(path) // Golang regexp match pairs [start, end, start, end, ...]
264+
if pathMatches == nil {
265+
return false
266+
}
267+
var paramMatches [][]int
268+
for i := 2; i < len(pathMatches); {
269+
paramMatches = append(paramMatches, []int{pathMatches[i], pathMatches[i+1]})
270+
pmIdx := len(paramMatches) - 1
271+
end := pathMatches[i+1]
272+
i += 2
273+
for ; i < len(pathMatches); i += 2 {
274+
if pathMatches[i] >= end {
275+
break
276+
}
277+
paramMatches[pmIdx] = append(paramMatches[pmIdx], pathMatches[i], pathMatches[i+1])
278+
}
279+
}
280+
for i, pm := range paramMatches {
281+
groupIdx := p.params[i].captureGroup * 2
282+
chiCtx.URLParams.Add(p.params[i].name, path[pm[groupIdx]:pm[groupIdx+1]])
283+
}
284+
return true
285+
}
286+
287+
func NewPathProcessor(methods, pattern string) *PathProcessor {
288+
p := &PathProcessor{methods: make(container.Set[string])}
289+
for _, method := range strings.Split(methods, ",") {
290+
p.methods.Add(strings.TrimSpace(method))
291+
}
292+
re := []byte{'^'}
293+
lastEnd := 0
294+
for lastEnd < len(pattern) {
295+
start := strings.IndexByte(pattern[lastEnd:], '<')
296+
if start == -1 {
297+
re = append(re, pattern[lastEnd:]...)
298+
break
299+
}
300+
end := strings.IndexByte(pattern[lastEnd+start:], '>')
301+
if end == -1 {
302+
panic(fmt.Sprintf("invalid pattern: %s", pattern))
303+
}
304+
re = append(re, pattern[lastEnd:lastEnd+start]...)
305+
partName, partExp, _ := strings.Cut(pattern[lastEnd+start+1:lastEnd+start+end], ":")
306+
lastEnd += start + end + 1
307+
308+
// TODO: it could support to specify a "capture group" for the name, for example: "/<name[2]:(\d)-(\d)>"
309+
// it is not used so no need to implement it now
310+
param := pathProcessorParam{}
311+
if partExp == "*" {
312+
re = append(re, "(.*?)/?"...)
313+
if lastEnd < len(pattern) {
314+
if pattern[lastEnd] == '/' {
315+
lastEnd++
316+
}
317+
}
318+
} else {
319+
partExp = util.IfZero(partExp, "[^/]+")
320+
re = append(re, '(')
321+
re = append(re, partExp...)
322+
re = append(re, ')')
323+
}
324+
param.name = partName
325+
p.params = append(p.params, param)
326+
}
327+
re = append(re, '$')
328+
reStr := string(re)
329+
p.re = regexp.MustCompile(reStr)
330+
return p
331+
}
332+
241333
// Combo delegates requests to Combo
242334
func (r *Router) Combo(pattern string, h ...any) *Combo {
243335
return &Combo{r, pattern, h}

0 commit comments

Comments
 (0)