routing.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. package githttp
  2. import (
  3. "fmt"
  4. "net/http"
  5. "os"
  6. "regexp"
  7. "strings"
  8. )
  9. type Service struct {
  10. Method string
  11. Handler func(HandlerReq) error
  12. Rpc string
  13. }
  14. type HandlerReq struct {
  15. w http.ResponseWriter
  16. r *http.Request
  17. Rpc string
  18. Dir string
  19. File string
  20. }
  21. // Routing regexes
  22. var (
  23. _serviceRpcUpload = regexp.MustCompile("git/(.*?)/git-upload-pack$")
  24. _serviceRpcReceive = regexp.MustCompile("git/(.*?)/git-receive-pack$")
  25. _getInfoRefs = regexp.MustCompile("git/(.*?)/info/refs$")
  26. _getHead = regexp.MustCompile("git/(.*?)/HEAD$")
  27. _getAlternates = regexp.MustCompile("git/(.*?)/objects/info/alternates$")
  28. _getHttpAlternates = regexp.MustCompile("git/(.*?)/objects/info/http-alternates$")
  29. _getInfoPacks = regexp.MustCompile("git/(.*?)/objects/info/packs$")
  30. _getInfoFile = regexp.MustCompile("git/(.*?)/objects/info/[^/]*$")
  31. _getLooseObject = regexp.MustCompile("git/(.*?)/objects/[0-9a-f]{2}/[0-9a-f]{38}$")
  32. _getPackFile = regexp.MustCompile("git/(.*?)/objects/pack/pack-[0-9a-f]{40}\\.pack$")
  33. _getIdxFile = regexp.MustCompile("git/(.*?)/objects/pack/pack-[0-9a-f]{40}\\.idx$")
  34. )
  35. func (g *GitHttp) services() map[*regexp.Regexp]Service {
  36. return map[*regexp.Regexp]Service{
  37. _serviceRpcUpload: Service{"POST", g.serviceRpc, "upload-pack"},
  38. _serviceRpcReceive: Service{"POST", g.serviceRpc, "receive-pack"},
  39. _getInfoRefs: Service{"GET", g.getInfoRefs, ""},
  40. _getHead: Service{"GET", g.getTextFile, ""},
  41. _getAlternates: Service{"GET", g.getTextFile, ""},
  42. _getHttpAlternates: Service{"GET", g.getTextFile, ""},
  43. _getInfoPacks: Service{"GET", g.getInfoPacks, ""},
  44. _getInfoFile: Service{"GET", g.getTextFile, ""},
  45. _getLooseObject: Service{"GET", g.getLooseObject, ""},
  46. _getPackFile: Service{"GET", g.getPackFile, ""},
  47. _getIdxFile: Service{"GET", g.getIdxFile, ""},
  48. }
  49. }
  50. // getService return's the service corresponding to the
  51. // current http.Request's URL
  52. // as well as the name of the repo
  53. func (g *GitHttp) getService(path string) (string, *Service) {
  54. for re, service := range g.services() {
  55. if m := re.FindStringSubmatch(path); m != nil {
  56. return m[1], &service
  57. }
  58. }
  59. // No match
  60. return "", nil
  61. }
  62. // Request handling function
  63. func (g *GitHttp) requestHandler(w http.ResponseWriter, r *http.Request) {
  64. // Get service for URL
  65. repo, service := g.getService(r.URL.Path)
  66. fmt.Println("git handler", r.URL.Path, repo)
  67. // No url match
  68. if service == nil {
  69. renderNotFound(w)
  70. return
  71. }
  72. // Bad method
  73. if service.Method != r.Method {
  74. renderMethodNotAllowed(w, r)
  75. return
  76. }
  77. // Rpc type
  78. rpc := service.Rpc
  79. // Get specific file
  80. file := strings.Replace(r.URL.Path, repo+"/", "", 1)
  81. // Resolve directory
  82. dir, err := g.getGitDir(repo)
  83. // Repo not found on disk
  84. if err != nil {
  85. renderNotFound(w)
  86. return
  87. }
  88. // Build request info for handler
  89. hr := HandlerReq{w, r, rpc, dir, file}
  90. // Call handler
  91. if err := service.Handler(hr); err != nil {
  92. if os.IsNotExist(err) {
  93. renderNotFound(w)
  94. return
  95. }
  96. switch err.(type) {
  97. case *ErrorNoAccess:
  98. renderNoAccess(w)
  99. return
  100. }
  101. http.Error(w, err.Error(), 500)
  102. }
  103. }