1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 package org.eclipse.jgit.lfs.server.fs;
44
45 import static org.eclipse.jgit.lib.Constants.CHARSET;
46 import static org.junit.Assert.assertEquals;
47
48 import java.io.BufferedInputStream;
49 import java.io.FileNotFoundException;
50 import java.io.IOException;
51 import java.io.InputStream;
52 import java.nio.ByteBuffer;
53 import java.nio.channels.Channels;
54 import java.nio.channels.FileChannel;
55 import java.nio.channels.ReadableByteChannel;
56 import java.nio.file.Files;
57 import java.nio.file.Path;
58 import java.nio.file.Paths;
59 import java.nio.file.StandardOpenOption;
60 import java.security.DigestInputStream;
61 import java.security.SecureRandom;
62
63 import org.apache.http.HttpEntity;
64 import org.apache.http.HttpResponse;
65 import org.apache.http.StatusLine;
66 import org.apache.http.client.ClientProtocolException;
67 import org.apache.http.client.methods.CloseableHttpResponse;
68 import org.apache.http.client.methods.HttpGet;
69 import org.apache.http.client.methods.HttpPut;
70 import org.apache.http.entity.ContentType;
71 import org.apache.http.entity.InputStreamEntity;
72 import org.apache.http.entity.StringEntity;
73 import org.apache.http.impl.client.CloseableHttpClient;
74 import org.apache.http.impl.client.HttpClientBuilder;
75 import org.eclipse.jetty.servlet.ServletContextHandler;
76 import org.eclipse.jetty.servlet.ServletHolder;
77 import org.eclipse.jgit.junit.http.AppServer;
78 import org.eclipse.jgit.lfs.errors.LfsException;
79 import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
80 import org.eclipse.jgit.lfs.lib.Constants;
81 import org.eclipse.jgit.lfs.lib.LongObjectId;
82 import org.eclipse.jgit.lfs.server.LargeFileRepository;
83 import org.eclipse.jgit.lfs.server.LfsProtocolServlet;
84 import org.eclipse.jgit.lfs.test.LongObjectIdTestUtils;
85 import org.eclipse.jgit.util.FileUtils;
86 import org.eclipse.jgit.util.IO;
87 import org.junit.After;
88 import org.junit.Before;
89
90 public abstract class LfsServerTest {
91
92 private static final long timeout = 10 * 1000;
93
94 protected static final int MiB = 1024 * 1024;
95
96
97 protected AppServer server;
98
99 private Path tmp;
100
101 private Path dir;
102
103 protected FileLfsRepository repository;
104
105 protected FileLfsServlet servlet;
106
107 public LfsServerTest() {
108 super();
109 }
110
111 public Path getTempDirectory() {
112 return tmp;
113 }
114
115 public Path getDir() {
116 return dir;
117 }
118
119 @Before
120 public void setup() throws Exception {
121 tmp = Files.createTempDirectory("jgit_test_");
122 server = new AppServer();
123 ServletContextHandler app = server.addContext("/lfs");
124 dir = Paths.get(tmp.toString(), "lfs");
125 this.repository = new FileLfsRepository(null, dir);
126 servlet = new FileLfsServlet(repository, timeout);
127 app.addServlet(new ServletHolder(servlet), "/objects/*");
128
129 LfsProtocolServlet protocol = new LfsProtocolServlet() {
130 private static final long serialVersionUID = 1L;
131
132 @Override
133 protected LargeFileRepository getLargeFileRepository(
134 LfsRequest request, String path, String auth)
135 throws LfsException {
136 return repository;
137 }
138 };
139 app.addServlet(new ServletHolder(protocol), "/objects/batch");
140
141 server.setUp();
142 this.repository.setUrl(server.getURI() + "/lfs/objects/");
143 }
144
145 @After
146 public void tearDown() throws Exception {
147 server.tearDown();
148 FileUtils.delete(tmp.toFile(), FileUtils.RECURSIVE | FileUtils.RETRY);
149 }
150
151 protected AnyLongObjectId putContent(String s)
152 throws IOException, ClientProtocolException {
153 AnyLongObjectId id = LongObjectIdTestUtils.hash(s);
154 return putContent(id, s);
155 }
156
157 protected AnyLongObjectId putContent(AnyLongObjectId id, String s)
158 throws ClientProtocolException, IOException {
159 try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
160 HttpEntity entity = new StringEntity(s,
161 ContentType.APPLICATION_OCTET_STREAM);
162 String hexId = id.name();
163 HttpPut request = new HttpPut(
164 server.getURI() + "/lfs/objects/" + hexId);
165 request.setEntity(entity);
166 try (CloseableHttpResponse response = client.execute(request)) {
167 StatusLine statusLine = response.getStatusLine();
168 int status = statusLine.getStatusCode();
169 if (status >= 400) {
170 throw new RuntimeException("Status: " + status + ". "
171 + statusLine.getReasonPhrase());
172 }
173 }
174 return id;
175 }
176 }
177
178 protected LongObjectId putContent(Path f)
179 throws FileNotFoundException, IOException {
180 try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
181 LongObjectId id1, id2;
182 String hexId1, hexId2;
183 try (DigestInputStream in = new DigestInputStream(
184 new BufferedInputStream(Files.newInputStream(f)),
185 Constants.newMessageDigest())) {
186 InputStreamEntity entity = new InputStreamEntity(in,
187 Files.size(f), ContentType.APPLICATION_OCTET_STREAM);
188 id1 = LongObjectIdTestUtils.hash(f);
189 hexId1 = id1.name();
190 HttpPut request = new HttpPut(
191 server.getURI() + "/lfs/objects/" + hexId1);
192 request.setEntity(entity);
193 HttpResponse response = client.execute(request);
194 checkResponseStatus(response);
195 id2 = LongObjectId.fromRaw(in.getMessageDigest().digest());
196 hexId2 = id2.name();
197 assertEquals(hexId1, hexId2);
198 }
199 return id1;
200 }
201 }
202
203 private void checkResponseStatus(HttpResponse response) {
204 StatusLine statusLine = response.getStatusLine();
205 int status = statusLine.getStatusCode();
206 if (statusLine.getStatusCode() >= 400) {
207 String error;
208 try {
209 ByteBuffer buf = IO.readWholeStream(new BufferedInputStream(
210 response.getEntity().getContent()), 1024);
211 if (buf.hasArray()) {
212 error = new String(buf.array(),
213 buf.arrayOffset() + buf.position(), buf.remaining(),
214 CHARSET);
215 } else {
216 final byte[] b = new byte[buf.remaining()];
217 buf.duplicate().get(b);
218 error = new String(b, CHARSET);
219 }
220 } catch (IOException e) {
221 error = statusLine.getReasonPhrase();
222 }
223 throw new RuntimeException("Status: " + status + " " + error);
224 }
225 assertEquals(200, status);
226 }
227
228 protected long getContent(AnyLongObjectId id, Path f) throws IOException {
229 String hexId = id.name();
230 return getContent(hexId, f);
231 }
232
233 protected long getContent(String hexId, Path f) throws IOException {
234 try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
235 HttpGet request = new HttpGet(
236 server.getURI() + "/lfs/objects/" + hexId);
237 HttpResponse response = client.execute(request);
238 checkResponseStatus(response);
239 HttpEntity entity = response.getEntity();
240 long pos = 0;
241 try (InputStream in = entity.getContent();
242 ReadableByteChannel inChannel = Channels.newChannel(in);
243 FileChannel outChannel = FileChannel.open(f,
244 StandardOpenOption.CREATE_NEW,
245 StandardOpenOption.WRITE)) {
246 long transferred;
247 do {
248 transferred = outChannel.transferFrom(inChannel, pos, MiB);
249 pos += transferred;
250 } while (transferred > 0);
251 }
252 return pos;
253 }
254 }
255
256
257
258
259
260
261
262
263
264
265
266
267 protected long createPseudoRandomContentFile(Path f, long size)
268 throws IOException {
269 SecureRandom rnd = new SecureRandom();
270 byte[] buf = new byte[4096];
271 rnd.nextBytes(buf);
272 ByteBuffer bytebuf = ByteBuffer.wrap(buf);
273 try (FileChannel outChannel = FileChannel.open(f,
274 StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) {
275 long len = 0;
276 do {
277 len += outChannel.write(bytebuf);
278 if (bytebuf.position() == 4096) {
279 bytebuf.rewind();
280 }
281 } while (len < size);
282 }
283 return Files.size(f);
284 }
285 }