1
0

basic.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package lfs
  2. import (
  3. "encoding/json"
  4. "io"
  5. "net/http"
  6. "strconv"
  7. "gopkg.in/macaron.v1"
  8. log "unknwon.dev/clog/v2"
  9. "gogs.io/gogs/internal/database"
  10. "gogs.io/gogs/internal/lfsutil"
  11. "gogs.io/gogs/internal/strutil"
  12. )
  13. const transferBasic = "basic"
  14. const (
  15. basicOperationUpload = "upload"
  16. basicOperationDownload = "download"
  17. )
  18. type basicHandler struct {
  19. store Store
  20. // The default storage backend for uploading new objects.
  21. defaultStorage lfsutil.Storage
  22. // The list of available storage backends to access objects.
  23. storagers map[lfsutil.Storage]lfsutil.Storager
  24. }
  25. // DefaultStorager returns the default storage backend.
  26. func (h *basicHandler) DefaultStorager() lfsutil.Storager {
  27. return h.storagers[h.defaultStorage]
  28. }
  29. // Storager returns the given storage backend.
  30. func (h *basicHandler) Storager(storage lfsutil.Storage) lfsutil.Storager {
  31. return h.storagers[storage]
  32. }
  33. // GET /{owner}/{repo}.git/info/lfs/object/basic/{oid}
  34. func (h *basicHandler) serveDownload(c *macaron.Context, repo *database.Repository, oid lfsutil.OID) {
  35. object, err := h.store.GetLFSObjectByOID(c.Req.Context(), repo.ID, oid)
  36. if err != nil {
  37. if database.IsErrLFSObjectNotExist(err) {
  38. responseJSON(c.Resp, http.StatusNotFound, responseError{
  39. Message: "Object does not exist",
  40. })
  41. } else {
  42. internalServerError(c.Resp)
  43. log.Error("Failed to get object [repo_id: %d, oid: %s]: %v", repo.ID, oid, err)
  44. }
  45. return
  46. }
  47. s := h.Storager(object.Storage)
  48. if s == nil {
  49. internalServerError(c.Resp)
  50. log.Error("Failed to locate the object [repo_id: %d, oid: %s]: storage %q not found", object.RepoID, object.OID, object.Storage)
  51. return
  52. }
  53. c.Header().Set("Content-Type", "application/octet-stream")
  54. c.Header().Set("Content-Length", strconv.FormatInt(object.Size, 10))
  55. c.Status(http.StatusOK)
  56. err = s.Download(object.OID, c.Resp)
  57. if err != nil {
  58. log.Error("Failed to download object [oid: %s]: %v", object.OID, err)
  59. return
  60. }
  61. }
  62. // PUT /{owner}/{repo}.git/info/lfs/object/basic/{oid}
  63. func (h *basicHandler) serveUpload(c *macaron.Context, repo *database.Repository, oid lfsutil.OID) {
  64. // NOTE: LFS client will retry upload the same object if there was a partial failure,
  65. // therefore we would like to skip ones that already exist.
  66. _, err := h.store.GetLFSObjectByOID(c.Req.Context(), repo.ID, oid)
  67. if err == nil {
  68. // Object exists, drain the request body and we're good.
  69. _, _ = io.Copy(io.Discard, c.Req.Request.Body)
  70. c.Req.Request.Body.Close()
  71. c.Status(http.StatusOK)
  72. return
  73. } else if !database.IsErrLFSObjectNotExist(err) {
  74. internalServerError(c.Resp)
  75. log.Error("Failed to get object [repo_id: %d, oid: %s]: %v", repo.ID, oid, err)
  76. return
  77. }
  78. s := h.DefaultStorager()
  79. written, err := s.Upload(oid, c.Req.Request.Body)
  80. if err != nil {
  81. if err == lfsutil.ErrInvalidOID {
  82. responseJSON(c.Resp, http.StatusBadRequest, responseError{
  83. Message: err.Error(),
  84. })
  85. } else {
  86. internalServerError(c.Resp)
  87. log.Error("Failed to upload object [storage: %s, oid: %s]: %v", s.Storage(), oid, err)
  88. }
  89. return
  90. }
  91. err = h.store.CreateLFSObject(c.Req.Context(), repo.ID, oid, written, s.Storage())
  92. if err != nil {
  93. // NOTE: It is OK to leave the file when the whole operation failed
  94. // with a DB error, a retry on client side can safely overwrite the
  95. // same file as OID is seen as unique to every file.
  96. internalServerError(c.Resp)
  97. log.Error("Failed to create object [repo_id: %d, oid: %s]: %v", repo.ID, oid, err)
  98. return
  99. }
  100. c.Status(http.StatusOK)
  101. log.Trace("[LFS] Object created %q", oid)
  102. }
  103. // POST /{owner}/{repo}.git/info/lfs/object/basic/verify
  104. func (h *basicHandler) serveVerify(c *macaron.Context, repo *database.Repository) {
  105. var request basicVerifyRequest
  106. defer func() { _ = c.Req.Request.Body.Close() }()
  107. err := json.NewDecoder(c.Req.Request.Body).Decode(&request)
  108. if err != nil {
  109. responseJSON(c.Resp, http.StatusBadRequest, responseError{
  110. Message: strutil.ToUpperFirst(err.Error()),
  111. })
  112. return
  113. }
  114. if !lfsutil.ValidOID(request.Oid) {
  115. responseJSON(c.Resp, http.StatusBadRequest, responseError{
  116. Message: "Invalid oid",
  117. })
  118. return
  119. }
  120. object, err := h.store.GetLFSObjectByOID(c.Req.Context(), repo.ID, request.Oid)
  121. if err != nil {
  122. if database.IsErrLFSObjectNotExist(err) {
  123. responseJSON(c.Resp, http.StatusNotFound, responseError{
  124. Message: "Object does not exist",
  125. })
  126. } else {
  127. internalServerError(c.Resp)
  128. log.Error("Failed to get object [repo_id: %d, oid: %s]: %v", repo.ID, request.Oid, err)
  129. }
  130. return
  131. }
  132. if object.Size != request.Size {
  133. responseJSON(c.Resp, http.StatusBadRequest, responseError{
  134. Message: "Object size mismatch",
  135. })
  136. return
  137. }
  138. c.Status(http.StatusOK)
  139. }
  140. type basicVerifyRequest struct {
  141. Oid lfsutil.OID `json:"oid"`
  142. Size int64 `json:"size"`
  143. }