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.

LfsServerTest.java 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /*
  2. * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.lfs.server.fs;
  11. import static java.nio.charset.StandardCharsets.UTF_8;
  12. import static org.junit.Assert.assertEquals;
  13. import java.io.BufferedInputStream;
  14. import java.io.FileNotFoundException;
  15. import java.io.IOException;
  16. import java.io.InputStream;
  17. import java.nio.ByteBuffer;
  18. import java.nio.channels.Channels;
  19. import java.nio.channels.FileChannel;
  20. import java.nio.channels.ReadableByteChannel;
  21. import java.nio.file.Files;
  22. import java.nio.file.Path;
  23. import java.nio.file.Paths;
  24. import java.nio.file.StandardOpenOption;
  25. import java.security.DigestInputStream;
  26. import java.security.SecureRandom;
  27. import org.apache.http.HttpEntity;
  28. import org.apache.http.HttpResponse;
  29. import org.apache.http.StatusLine;
  30. import org.apache.http.client.ClientProtocolException;
  31. import org.apache.http.client.methods.CloseableHttpResponse;
  32. import org.apache.http.client.methods.HttpGet;
  33. import org.apache.http.client.methods.HttpPut;
  34. import org.apache.http.entity.ContentType;
  35. import org.apache.http.entity.InputStreamEntity;
  36. import org.apache.http.entity.StringEntity;
  37. import org.apache.http.impl.client.CloseableHttpClient;
  38. import org.apache.http.impl.client.HttpClientBuilder;
  39. import org.eclipse.jetty.servlet.ServletContextHandler;
  40. import org.eclipse.jetty.servlet.ServletHolder;
  41. import org.eclipse.jgit.junit.MockSystemReader;
  42. import org.eclipse.jgit.junit.http.AppServer;
  43. import org.eclipse.jgit.lfs.errors.LfsException;
  44. import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
  45. import org.eclipse.jgit.lfs.lib.Constants;
  46. import org.eclipse.jgit.lfs.lib.LongObjectId;
  47. import org.eclipse.jgit.lfs.server.LargeFileRepository;
  48. import org.eclipse.jgit.lfs.server.LfsProtocolServlet;
  49. import org.eclipse.jgit.lfs.test.LongObjectIdTestUtils;
  50. import org.eclipse.jgit.util.FS;
  51. import org.eclipse.jgit.util.FileUtils;
  52. import org.eclipse.jgit.util.IO;
  53. import org.eclipse.jgit.util.SystemReader;
  54. import org.junit.After;
  55. import org.junit.Before;
  56. public abstract class LfsServerTest {
  57. private static final long timeout = /* 10 sec */ 10 * 1000;
  58. protected static final int MiB = 1024 * 1024;
  59. /** In-memory application server; subclass must start. */
  60. protected AppServer server;
  61. private Path tmp;
  62. private Path dir;
  63. protected FileLfsRepository repository;
  64. protected FileLfsServlet servlet;
  65. public LfsServerTest() {
  66. super();
  67. }
  68. public Path getTempDirectory() {
  69. return tmp;
  70. }
  71. public Path getDir() {
  72. return dir;
  73. }
  74. @Before
  75. public void setup() throws Exception {
  76. SystemReader.setInstance(new MockSystemReader());
  77. tmp = Files.createTempDirectory("jgit_test_");
  78. // measure timer resolution before the test to avoid time critical tests
  79. // are affected by time needed for measurement
  80. FS.getFileStoreAttributes(tmp.getParent());
  81. server = new AppServer();
  82. ServletContextHandler app = server.addContext("/lfs");
  83. dir = Paths.get(tmp.toString(), "lfs");
  84. this.repository = new FileLfsRepository(null, dir);
  85. servlet = new FileLfsServlet(repository, timeout);
  86. app.addServlet(new ServletHolder(servlet), "/objects/*");
  87. LfsProtocolServlet protocol = new LfsProtocolServlet() {
  88. private static final long serialVersionUID = 1L;
  89. @Override
  90. protected LargeFileRepository getLargeFileRepository(
  91. LfsRequest request, String path, String auth)
  92. throws LfsException {
  93. return repository;
  94. }
  95. };
  96. app.addServlet(new ServletHolder(protocol), "/objects/batch");
  97. server.setUp();
  98. this.repository.setUrl(server.getURI() + "/lfs/objects/");
  99. }
  100. @After
  101. public void tearDown() throws Exception {
  102. server.tearDown();
  103. FileUtils.delete(tmp.toFile(), FileUtils.RECURSIVE | FileUtils.RETRY);
  104. }
  105. protected AnyLongObjectId putContent(String s)
  106. throws IOException, ClientProtocolException {
  107. AnyLongObjectId id = LongObjectIdTestUtils.hash(s);
  108. return putContent(id, s);
  109. }
  110. protected AnyLongObjectId putContent(AnyLongObjectId id, String s)
  111. throws ClientProtocolException, IOException {
  112. try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
  113. HttpEntity entity = new StringEntity(s,
  114. ContentType.APPLICATION_OCTET_STREAM);
  115. String hexId = id.name();
  116. HttpPut request = new HttpPut(
  117. server.getURI() + "/lfs/objects/" + hexId);
  118. request.setEntity(entity);
  119. try (CloseableHttpResponse response = client.execute(request)) {
  120. StatusLine statusLine = response.getStatusLine();
  121. int status = statusLine.getStatusCode();
  122. if (status >= 400) {
  123. throw new RuntimeException("Status: " + status + ". "
  124. + statusLine.getReasonPhrase());
  125. }
  126. }
  127. return id;
  128. }
  129. }
  130. protected LongObjectId putContent(Path f)
  131. throws FileNotFoundException, IOException {
  132. try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
  133. LongObjectId id1, id2;
  134. String hexId1, hexId2;
  135. try (DigestInputStream in = new DigestInputStream(
  136. new BufferedInputStream(Files.newInputStream(f)),
  137. Constants.newMessageDigest())) {
  138. InputStreamEntity entity = new InputStreamEntity(in,
  139. Files.size(f), ContentType.APPLICATION_OCTET_STREAM);
  140. id1 = LongObjectIdTestUtils.hash(f);
  141. hexId1 = id1.name();
  142. HttpPut request = new HttpPut(
  143. server.getURI() + "/lfs/objects/" + hexId1);
  144. request.setEntity(entity);
  145. HttpResponse response = client.execute(request);
  146. checkResponseStatus(response);
  147. id2 = LongObjectId.fromRaw(in.getMessageDigest().digest());
  148. hexId2 = id2.name();
  149. assertEquals(hexId1, hexId2);
  150. }
  151. return id1;
  152. }
  153. }
  154. private void checkResponseStatus(HttpResponse response) {
  155. StatusLine statusLine = response.getStatusLine();
  156. int status = statusLine.getStatusCode();
  157. if (statusLine.getStatusCode() >= 400) {
  158. String error;
  159. try {
  160. ByteBuffer buf = IO.readWholeStream(new BufferedInputStream(
  161. response.getEntity().getContent()), 1024);
  162. if (buf.hasArray()) {
  163. error = new String(buf.array(),
  164. buf.arrayOffset() + buf.position(), buf.remaining(),
  165. UTF_8);
  166. } else {
  167. final byte[] b = new byte[buf.remaining()];
  168. buf.duplicate().get(b);
  169. error = new String(b, UTF_8);
  170. }
  171. } catch (IOException e) {
  172. error = statusLine.getReasonPhrase();
  173. }
  174. throw new RuntimeException("Status: " + status + " " + error);
  175. }
  176. assertEquals(200, status);
  177. }
  178. protected long getContent(AnyLongObjectId id, Path f) throws IOException {
  179. String hexId = id.name();
  180. return getContent(hexId, f);
  181. }
  182. protected long getContent(String hexId, Path f) throws IOException {
  183. try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
  184. HttpGet request = new HttpGet(
  185. server.getURI() + "/lfs/objects/" + hexId);
  186. HttpResponse response = client.execute(request);
  187. checkResponseStatus(response);
  188. HttpEntity entity = response.getEntity();
  189. long pos = 0;
  190. try (InputStream in = entity.getContent();
  191. ReadableByteChannel inChannel = Channels.newChannel(in);
  192. FileChannel outChannel = FileChannel.open(f,
  193. StandardOpenOption.CREATE_NEW,
  194. StandardOpenOption.WRITE)) {
  195. long transferred;
  196. do {
  197. transferred = outChannel.transferFrom(inChannel, pos, MiB);
  198. pos += transferred;
  199. } while (transferred > 0);
  200. }
  201. return pos;
  202. }
  203. }
  204. /**
  205. * Creates a file with random content, repeatedly writing a random string of
  206. * 4k length to the file until the file has at least the specified length.
  207. *
  208. * @param f
  209. * file to fill
  210. * @param size
  211. * size of the file to generate
  212. * @return length of the generated file in bytes
  213. * @throws IOException
  214. */
  215. protected long createPseudoRandomContentFile(Path f, long size)
  216. throws IOException {
  217. SecureRandom rnd = new SecureRandom();
  218. byte[] buf = new byte[4096];
  219. rnd.nextBytes(buf);
  220. ByteBuffer bytebuf = ByteBuffer.wrap(buf);
  221. try (FileChannel outChannel = FileChannel.open(f,
  222. StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) {
  223. long len = 0;
  224. do {
  225. len += outChannel.write(bytebuf);
  226. if (bytebuf.position() == 4096) {
  227. bytebuf.rewind();
  228. }
  229. } while (len < size);
  230. }
  231. return Files.size(f);
  232. }
  233. }