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