gitea源码

util.go 3.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. // Copyright 2024 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package backend
  4. import (
  5. "context"
  6. "fmt"
  7. "io"
  8. "net/http"
  9. "net/url"
  10. "strings"
  11. "code.gitea.io/gitea/modules/httplib"
  12. "code.gitea.io/gitea/modules/private"
  13. "code.gitea.io/gitea/modules/setting"
  14. "code.gitea.io/gitea/modules/util"
  15. "github.com/charmbracelet/git-lfs-transfer/transfer"
  16. )
  17. // HTTP headers
  18. const (
  19. headerAccept = "Accept"
  20. headerAuthorization = "Authorization"
  21. headerGiteaInternalAuth = "X-Gitea-Internal-Auth"
  22. headerContentType = "Content-Type"
  23. headerContentLength = "Content-Length"
  24. )
  25. // MIME types
  26. const (
  27. mimeGitLFS = "application/vnd.git-lfs+json"
  28. mimeOctetStream = "application/octet-stream"
  29. )
  30. // SSH protocol action keys
  31. const (
  32. actionDownload = "download"
  33. actionUpload = "upload"
  34. actionVerify = "verify"
  35. )
  36. // SSH protocol argument keys
  37. const (
  38. argCursor = "cursor"
  39. argExpiresAt = "expires-at"
  40. argID = "id"
  41. argLimit = "limit"
  42. argPath = "path"
  43. argRefname = "refname"
  44. argToken = "token"
  45. argTransfer = "transfer"
  46. )
  47. // Default username constants
  48. const (
  49. userSelf = "(self)"
  50. userUnknown = "(unknown)"
  51. )
  52. // Operations enum
  53. const (
  54. opDownload = iota + 1
  55. opUpload
  56. )
  57. var opMap = map[string]int{
  58. "download": opDownload,
  59. "upload": opUpload,
  60. }
  61. var ErrMissingID = fmt.Errorf("%w: missing id arg", transfer.ErrMissingData)
  62. func statusCodeToErr(code int) error {
  63. switch code {
  64. case http.StatusBadRequest:
  65. return transfer.ErrParseError
  66. case http.StatusConflict:
  67. return transfer.ErrConflict
  68. case http.StatusForbidden:
  69. return transfer.ErrForbidden
  70. case http.StatusNotFound:
  71. return transfer.ErrNotFound
  72. case http.StatusUnauthorized:
  73. return transfer.ErrUnauthorized
  74. default:
  75. return fmt.Errorf("server returned status %v: %v", code, http.StatusText(code))
  76. }
  77. }
  78. func toInternalLFSURL(s string) string {
  79. pos1 := strings.Index(s, "://")
  80. if pos1 == -1 {
  81. return ""
  82. }
  83. appSubURLWithSlash := setting.AppSubURL + "/"
  84. pos2 := strings.Index(s[pos1+3:], appSubURLWithSlash)
  85. if pos2 == -1 {
  86. return ""
  87. }
  88. routePath := s[pos1+3+pos2+len(appSubURLWithSlash):]
  89. fields := strings.SplitN(routePath, "/", 3)
  90. if len(fields) < 3 || !strings.HasPrefix(fields[2], "info/lfs") {
  91. return ""
  92. }
  93. return setting.LocalURL + "api/internal/repo/" + routePath
  94. }
  95. func isInternalLFSURL(s string) bool {
  96. if !strings.HasPrefix(s, setting.LocalURL) {
  97. return false
  98. }
  99. u, err := url.Parse(s)
  100. if err != nil {
  101. return false
  102. }
  103. routePath := util.PathJoinRelX(u.Path)
  104. subRoutePath, cut := strings.CutPrefix(routePath, "api/internal/repo/")
  105. if !cut {
  106. return false
  107. }
  108. fields := strings.SplitN(subRoutePath, "/", 3)
  109. if len(fields) < 3 || !strings.HasPrefix(fields[2], "info/lfs") {
  110. return false
  111. }
  112. return true
  113. }
  114. func newInternalRequestLFS(ctx context.Context, internalURL, method string, headers map[string]string, body any) *httplib.Request {
  115. if !isInternalLFSURL(internalURL) {
  116. return nil
  117. }
  118. req := private.NewInternalRequest(ctx, internalURL, method)
  119. req.SetReadWriteTimeout(0)
  120. for k, v := range headers {
  121. req.Header(k, v)
  122. }
  123. switch body := body.(type) {
  124. case nil: // do nothing
  125. case []byte:
  126. req.Body(body) // []byte
  127. case io.Reader:
  128. req.Body(body) // io.Reader or io.ReadCloser
  129. default:
  130. panic(fmt.Sprintf("unsupported request body type %T", body))
  131. }
  132. return req
  133. }