You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lfs_getobject_test.go 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package integration
  4. import (
  5. "archive/zip"
  6. "bytes"
  7. "io"
  8. "net/http"
  9. "net/http/httptest"
  10. "testing"
  11. "code.gitea.io/gitea/models/auth"
  12. "code.gitea.io/gitea/models/db"
  13. git_model "code.gitea.io/gitea/models/git"
  14. repo_model "code.gitea.io/gitea/models/repo"
  15. "code.gitea.io/gitea/modules/json"
  16. "code.gitea.io/gitea/modules/lfs"
  17. "code.gitea.io/gitea/modules/setting"
  18. "code.gitea.io/gitea/routers/web"
  19. "code.gitea.io/gitea/tests"
  20. gzipp "github.com/klauspost/compress/gzip"
  21. "github.com/stretchr/testify/assert"
  22. )
  23. func storeObjectInRepo(t *testing.T, repositoryID int64, content *[]byte) string {
  24. pointer, err := lfs.GeneratePointer(bytes.NewReader(*content))
  25. assert.NoError(t, err)
  26. _, err = git_model.NewLFSMetaObject(db.DefaultContext, &git_model.LFSMetaObject{Pointer: pointer, RepositoryID: repositoryID})
  27. assert.NoError(t, err)
  28. contentStore := lfs.NewContentStore()
  29. exist, err := contentStore.Exists(pointer)
  30. assert.NoError(t, err)
  31. if !exist {
  32. err := contentStore.Put(pointer, bytes.NewReader(*content))
  33. assert.NoError(t, err)
  34. }
  35. return pointer.Oid
  36. }
  37. func storeAndGetLfsToken(t *testing.T, content *[]byte, extraHeader *http.Header, expectedStatus int, ts ...auth.AccessTokenScope) *httptest.ResponseRecorder {
  38. repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "repo1")
  39. assert.NoError(t, err)
  40. oid := storeObjectInRepo(t, repo.ID, content)
  41. defer git_model.RemoveLFSMetaObjectByOid(db.DefaultContext, repo.ID, oid)
  42. token := getUserToken(t, "user2", ts...)
  43. // Request OID
  44. req := NewRequest(t, "GET", "/user2/repo1.git/info/lfs/objects/"+oid+"/test")
  45. req.Header.Set("Accept-Encoding", "gzip")
  46. req.SetBasicAuth("user2", token)
  47. if extraHeader != nil {
  48. for key, values := range *extraHeader {
  49. for _, value := range values {
  50. req.Header.Add(key, value)
  51. }
  52. }
  53. }
  54. resp := MakeRequest(t, req, expectedStatus)
  55. return resp
  56. }
  57. func storeAndGetLfs(t *testing.T, content *[]byte, extraHeader *http.Header, expectedStatus int) *httptest.ResponseRecorder {
  58. repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "repo1")
  59. assert.NoError(t, err)
  60. oid := storeObjectInRepo(t, repo.ID, content)
  61. defer git_model.RemoveLFSMetaObjectByOid(db.DefaultContext, repo.ID, oid)
  62. session := loginUser(t, "user2")
  63. // Request OID
  64. req := NewRequest(t, "GET", "/user2/repo1.git/info/lfs/objects/"+oid+"/test")
  65. req.Header.Set("Accept-Encoding", "gzip")
  66. if extraHeader != nil {
  67. for key, values := range *extraHeader {
  68. for _, value := range values {
  69. req.Header.Add(key, value)
  70. }
  71. }
  72. }
  73. resp := session.MakeRequest(t, req, expectedStatus)
  74. return resp
  75. }
  76. func checkResponseTestContentEncoding(t *testing.T, content *[]byte, resp *httptest.ResponseRecorder, expectGzip bool) {
  77. contentEncoding := resp.Header().Get("Content-Encoding")
  78. if !expectGzip || !setting.EnableGzip {
  79. assert.NotContains(t, contentEncoding, "gzip")
  80. result := resp.Body.Bytes()
  81. assert.Equal(t, *content, result)
  82. } else {
  83. assert.Contains(t, contentEncoding, "gzip")
  84. gzippReader, err := gzipp.NewReader(resp.Body)
  85. assert.NoError(t, err)
  86. result, err := io.ReadAll(gzippReader)
  87. assert.NoError(t, err)
  88. assert.Equal(t, *content, result)
  89. }
  90. }
  91. func TestGetLFSSmall(t *testing.T) {
  92. defer tests.PrepareTestEnv(t)()
  93. content := []byte("A very small file\n")
  94. resp := storeAndGetLfs(t, &content, nil, http.StatusOK)
  95. checkResponseTestContentEncoding(t, &content, resp, false)
  96. }
  97. func TestGetLFSSmallToken(t *testing.T) {
  98. defer tests.PrepareTestEnv(t)()
  99. content := []byte("A very small file\n")
  100. resp := storeAndGetLfsToken(t, &content, nil, http.StatusOK, auth.AccessTokenScopePublicOnly, auth.AccessTokenScopeReadRepository)
  101. checkResponseTestContentEncoding(t, &content, resp, false)
  102. }
  103. func TestGetLFSSmallTokenFail(t *testing.T) {
  104. defer tests.PrepareTestEnv(t)()
  105. content := []byte("A very small file\n")
  106. storeAndGetLfsToken(t, &content, nil, http.StatusForbidden, auth.AccessTokenScopeReadNotification)
  107. }
  108. func TestGetLFSLarge(t *testing.T) {
  109. defer tests.PrepareTestEnv(t)()
  110. content := make([]byte, web.GzipMinSize*10)
  111. for i := range content {
  112. content[i] = byte(i % 256)
  113. }
  114. resp := storeAndGetLfs(t, &content, nil, http.StatusOK)
  115. checkResponseTestContentEncoding(t, &content, resp, true)
  116. }
  117. func TestGetLFSGzip(t *testing.T) {
  118. defer tests.PrepareTestEnv(t)()
  119. b := make([]byte, web.GzipMinSize*10)
  120. for i := range b {
  121. b[i] = byte(i % 256)
  122. }
  123. outputBuffer := bytes.NewBuffer([]byte{})
  124. gzippWriter := gzipp.NewWriter(outputBuffer)
  125. gzippWriter.Write(b)
  126. gzippWriter.Close()
  127. content := outputBuffer.Bytes()
  128. resp := storeAndGetLfs(t, &content, nil, http.StatusOK)
  129. checkResponseTestContentEncoding(t, &content, resp, false)
  130. }
  131. func TestGetLFSZip(t *testing.T) {
  132. defer tests.PrepareTestEnv(t)()
  133. b := make([]byte, web.GzipMinSize*10)
  134. for i := range b {
  135. b[i] = byte(i % 256)
  136. }
  137. outputBuffer := bytes.NewBuffer([]byte{})
  138. zipWriter := zip.NewWriter(outputBuffer)
  139. fileWriter, err := zipWriter.Create("default")
  140. assert.NoError(t, err)
  141. fileWriter.Write(b)
  142. zipWriter.Close()
  143. content := outputBuffer.Bytes()
  144. resp := storeAndGetLfs(t, &content, nil, http.StatusOK)
  145. checkResponseTestContentEncoding(t, &content, resp, false)
  146. }
  147. func TestGetLFSRangeNo(t *testing.T) {
  148. defer tests.PrepareTestEnv(t)()
  149. content := []byte("123456789\n")
  150. resp := storeAndGetLfs(t, &content, nil, http.StatusOK)
  151. assert.Equal(t, content, resp.Body.Bytes())
  152. }
  153. func TestGetLFSRange(t *testing.T) {
  154. defer tests.PrepareTestEnv(t)()
  155. content := []byte("123456789\n")
  156. tests := []struct {
  157. in string
  158. out string
  159. status int
  160. }{
  161. {"bytes=0-0", "1", http.StatusPartialContent},
  162. {"bytes=0-1", "12", http.StatusPartialContent},
  163. {"bytes=1-1", "2", http.StatusPartialContent},
  164. {"bytes=1-3", "234", http.StatusPartialContent},
  165. {"bytes=1-", "23456789\n", http.StatusPartialContent},
  166. // end-range smaller than start-range is ignored
  167. {"bytes=1-0", "23456789\n", http.StatusPartialContent},
  168. {"bytes=0-10", "123456789\n", http.StatusPartialContent},
  169. // end-range bigger than length-1 is ignored
  170. {"bytes=0-11", "123456789\n", http.StatusPartialContent},
  171. {"bytes=11-", "Requested Range Not Satisfiable", http.StatusRequestedRangeNotSatisfiable},
  172. // incorrect header value cause whole header to be ignored
  173. {"bytes=-", "123456789\n", http.StatusOK},
  174. {"foobar", "123456789\n", http.StatusOK},
  175. }
  176. for _, tt := range tests {
  177. t.Run(tt.in, func(t *testing.T) {
  178. h := http.Header{
  179. "Range": []string{tt.in},
  180. }
  181. resp := storeAndGetLfs(t, &content, &h, tt.status)
  182. if tt.status == http.StatusPartialContent || tt.status == http.StatusOK {
  183. assert.Equal(t, tt.out, resp.Body.String())
  184. } else {
  185. var er lfs.ErrorResponse
  186. err := json.Unmarshal(resp.Body.Bytes(), &er)
  187. assert.NoError(t, err)
  188. assert.Equal(t, tt.out, er.Message)
  189. }
  190. })
  191. }
  192. }