basic.go 4.8 KB

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