From f085dae5c7b2caaa1ccbed248c97871b597dd14b Mon Sep 17 00:00:00 2001 From: "yuichi.watanabe" Date: Wed, 22 Nov 2017 22:07:24 +0900 Subject: [PATCH] #23 change the path matching to string comparison from regular expression --- README.md | 8 ++- addon/archivehandler/archivehandler.go | 10 +++- example/main.go | 10 +++- githttptransfer/error.go | 1 - githttptransfer/githttptransfer.go | 71 +++++++++++++++++++++----- githttptransfer/router.go | 15 +++--- githttptransfer/router_test.go | 36 +++++++------ 7 files changed, 110 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 241e9b3..aa7ad93 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,13 @@ func main() { // You can add some custom route. ght.Router.Add(githttptransfer.NewRoute( http.MethodGet, - regexp.MustCompile("(.*?)/hello$"), + func (path string) (match string) { + suffix := "/hello" + if strings.HasSuffix(path, suffix) { + match = suffix + } + return + }, func(ctx githttptransfer.Context) { resp, req := ctx.Response(), ctx.Request() rp, fp := ctx.RepoPath(), ctx.FilePath() diff --git a/addon/archivehandler/archivehandler.go b/addon/archivehandler/archivehandler.go index ee59b65..08167ba 100644 --- a/addon/archivehandler/archivehandler.go +++ b/addon/archivehandler/archivehandler.go @@ -11,8 +11,14 @@ import ( ) var ( - Pattern = regexp.MustCompile("(.*?)/archive/.*?\\.(zip|tar)$") - Method = http.MethodGet + r = regexp.MustCompile(".*?(/archive/.*?\\.(zip|tar))$") + Pattern = func(path string) (match string) { + if m := r.FindStringSubmatch(path); m != nil { + match = m[1] + } + return + } + Method = http.MethodGet ) func New(ght *githttptransfer.GitHTTPTransfer) *ArchiveHandler { diff --git a/example/main.go b/example/main.go index 18bd80f..546339f 100644 --- a/example/main.go +++ b/example/main.go @@ -4,13 +4,13 @@ import ( "fmt" "log" "net/http" - "regexp" "time" "flag" "github.com/vvatanabe/go-git-http-transfer/addon/archivehandler" "github.com/vvatanabe/go-git-http-transfer/githttptransfer" + "strings" ) func main() { @@ -40,7 +40,13 @@ func main() { // You can add some custom route. ght.Router.Add(githttptransfer.NewRoute( http.MethodGet, - regexp.MustCompile("(.*?)/hello$"), + func(path string) (match string) { + suffix := "/hello" + if strings.HasSuffix(path, suffix) { + match = suffix + } + return + }, func(ctx githttptransfer.Context) { resp, req := ctx.Response(), ctx.Request() rp, fp := ctx.RepoPath(), ctx.FilePath() diff --git a/githttptransfer/error.go b/githttptransfer/error.go index 06f6a46..08dfadd 100644 --- a/githttptransfer/error.go +++ b/githttptransfer/error.go @@ -19,4 +19,3 @@ type MethodNotAllowedError struct { func (e *MethodNotAllowedError) Error() string { return fmt.Sprintf("Method Not Allowed: Method %s, Path %s", e.Method, e.Path) } - diff --git a/githttptransfer/githttptransfer.go b/githttptransfer/githttptransfer.go index b5876bd..2c87171 100644 --- a/githttptransfer/githttptransfer.go +++ b/githttptransfer/githttptransfer.go @@ -12,19 +12,64 @@ import ( ) var ( - serviceRPCUpload = regexp.MustCompile("(.*?)/git-upload-pack$") - serviceRPCReceive = regexp.MustCompile("(.*?)/git-receive-pack$") - getInfoRefs = regexp.MustCompile("(.*?)/info/refs$") - getHead = regexp.MustCompile("(.*?)/HEAD$") - getAlternates = regexp.MustCompile("(.*?)/objects/info/alternates$") - getHTTPAlternates = regexp.MustCompile("(.*?)/objects/info/http-alternates$") - getInfoPacks = regexp.MustCompile("(.*?)/objects/info/packs$") - getInfoFile = regexp.MustCompile("(.*?)/objects/info/[^/]*$") - getLooseObject = regexp.MustCompile("(.*?)/objects/[0-9a-f]{2}/[0-9a-f]{38}$") - getPackFile = regexp.MustCompile("(.*?)/objects/pack/pack-[0-9a-f]{40}\\.pack$") - getIdxFile = regexp.MustCompile("(.*?)/objects/pack/pack-[0-9a-f]{40}\\.idx$") + serviceRPCUpload = func(path string) (match string) { + return hasSuffix(path, "/git-upload-pack") + } + serviceRPCReceive = func(path string) (match string) { + return hasSuffix(path, "/git-receive-pack") + } + + getInfoRefs = func(path string) (match string) { + return hasSuffix(path, "/info/refs") + } + + getHead = func(path string) (match string) { + return hasSuffix(path, "/HEAD") + } + + getAlternates = func(path string) (match string) { + return hasSuffix(path, "/objects/info/alternates") + } + + getHTTPAlternates = func(path string) (match string) { + return hasSuffix(path, "/objects/info/http-alternates") + } + + getInfoPacks = func(path string) (match string) { + return hasSuffix(path, "/objects/info/packs") + } + + getInfoFile = func(path string) (match string) { + return findStringSubmatch(path, regexp.MustCompile(".*?(/objects/info/[^/]*)$")) + } + + getLooseObject = func(path string) (match string) { + return findStringSubmatch(path, regexp.MustCompile(".*?(/objects/[0-9a-f]{2}/[0-9a-f]{38})$")) + } + + getPackFile = func(path string) (match string) { + return findStringSubmatch(path, regexp.MustCompile(".*?(/objects/pack/pack-[0-9a-f]{40}\\.pack)$")) + } + + getIdxFile = func(path string) (match string) { + return findStringSubmatch(path, regexp.MustCompile(".*?(/objects/pack/pack-[0-9a-f]{40}\\.idx)$")) + } ) +func hasSuffix(path, suffix string) (match string) { + if strings.HasSuffix(path, suffix) { + match = suffix + } + return +} + +func findStringSubmatch(path string, prefix *regexp.Regexp) (match string) { + if m := prefix.FindStringSubmatch(path); m != nil { + match = m[1] + } + return +} + type options struct { uploadPack bool receivePack bool @@ -72,7 +117,9 @@ func New(gitRootPath, gitBinPath string, opts ...Option) (*GitHTTPTransfer, erro event := newEvent() ght := &GitHTTPTransfer{git, router, event} + ght.Router.Add(NewRoute(http.MethodPost, serviceRPCUpload, ght.serviceRPCUpload)) + ght.Router.Add(NewRoute(http.MethodPost, serviceRPCReceive, ght.serviceRPCReceive)) ght.Router.Add(NewRoute(http.MethodGet, getInfoRefs, ght.getInfoRefs)) @@ -122,7 +169,7 @@ func (ght *GitHTTPTransfer) ServeHTTP(rw http.ResponseWriter, r *http.Request) { func (ght *GitHTTPTransfer) matchRouting(method, path string) (repoPath string, filePath string, handler HandlerFunc, err error) { match, route, err := ght.Router.Match(method, path) if err == nil { - repoPath = match[1] + repoPath = strings.Replace(path, match, "", 1) filePath = strings.Replace(path, repoPath+"/", "", 1) handler = route.Handler } diff --git a/githttptransfer/router.go b/githttptransfer/router.go index 1e74c5c..55eeb97 100644 --- a/githttptransfer/router.go +++ b/githttptransfer/router.go @@ -1,9 +1,5 @@ package githttptransfer -import ( - "regexp" -) - type router struct { routes []*Route } @@ -15,9 +11,9 @@ func (r *router) Add(route *Route) { r.routes = append(r.routes, route) } -func (r *router) Match(method string, path string) (match []string, route *Route, err error) { +func (r *router) Match(method string, path string) (match string, route *Route, err error) { for _, v := range r.routes { - if m := v.Pattern.FindStringSubmatch(path); m != nil { + if m := v.Pattern(path); m != "" { if v.Method != method { err = &MethodNotAllowedError{ Method: method, @@ -30,6 +26,7 @@ func (r *router) Match(method string, path string) (match []string, route *Route return } } + err = &URLNotFoundError{ Method: method, Path: path, @@ -41,12 +38,14 @@ func newRouter() *router { return &router{routes: []*Route{}} } +type Pattern = func(path string) (match string) + type Route struct { Method string - Pattern *regexp.Regexp + Pattern Pattern Handler HandlerFunc } -func NewRoute(method string, pattern *regexp.Regexp, handler HandlerFunc) *Route { +func NewRoute(method string, pattern Pattern, handler HandlerFunc) *Route { return &Route{method, pattern, handler} } diff --git a/githttptransfer/router_test.go b/githttptransfer/router_test.go index 2f9ae4a..d9201c4 100644 --- a/githttptransfer/router_test.go +++ b/githttptransfer/router_test.go @@ -2,7 +2,6 @@ package githttptransfer import ( "net/http" - "regexp" "testing" ) @@ -10,12 +9,16 @@ func Test_Router_Append_should_append_route(t *testing.T) { router := &router{} router.Add(&Route{ http.MethodPost, - regexp.MustCompile("(.*?)/foo"), + func(path string) (match string) { + return hasSuffix(path, "/foo") + }, func(ctx Context) {}, }) router.Add(&Route{ http.MethodPost, - regexp.MustCompile("(.*?)/bar"), + func(path string) (match string) { + return hasSuffix(path, "/bar") + }, func(ctx Context) {}, }) length := len(router.routes) @@ -29,7 +32,9 @@ func Test_Router_Match_should_match_route(t *testing.T) { router := &router{} router.Add(&Route{ http.MethodPost, - regexp.MustCompile("(.*?)/foo"), + func(path string) (match string) { + return hasSuffix(path, "/foo") + }, func(ctx Context) {}, }) match, route, err := router.Match(http.MethodPost, "/base/foo") @@ -39,11 +44,8 @@ func Test_Router_Match_should_match_route(t *testing.T) { if http.MethodPost != route.Method { t.Errorf("http method is not %s . result: %s", http.MethodPost, route.Method) } - if "/base/foo" != match[0] { - t.Errorf("match index 0 is not %s . result: %s", "/base/foo", match[0]) - } - if "/base" != match[1] { - t.Errorf("match index 1 is not %s . result: %s", "/base", match[1]) + if "/foo" != match { + t.Errorf("match is not %s . result: %s", "/foo", match) } } @@ -51,15 +53,17 @@ func Test_Router_Match_should_return_UrlNotFound_error(t *testing.T) { router := &router{} router.Add(&Route{ http.MethodPost, - regexp.MustCompile("(.*?)/foo"), + func(path string) (match string) { + return hasSuffix(path, "/foo") + }, func(ctx Context) {}, }) match, route, err := router.Match(http.MethodPost, "/base/hoge") if err == nil { t.Error("error is nil.") } - if match != nil { - t.Error("match is not nil.") + if match != "" { + t.Error("match is not empty.") } if route != nil { t.Error("route is not nil.") @@ -75,15 +79,17 @@ func Test_Router_Match_should_return_MethodNotAllowed_error(t *testing.T) { router := &router{} router.Add(&Route{ http.MethodPost, - regexp.MustCompile("(.*?)/foo"), + func(path string) (match string) { + return hasSuffix(path, "/foo") + }, func(ctx Context) {}, }) match, route, err := router.Match(http.MethodGet, "/base/foo") if err == nil { t.Error("error is nil.") } - if match != nil { - t.Error("match is not nil.") + if match != "" { + t.Error("match is not empty.") } if route != nil { t.Error("route is not nil.")