package com.gitblit.tests; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.impl.client.HttpClientBuilder; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import com.gitblit.Keys; import com.gitblit.manager.FilestoreManager; import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; import com.gitblit.models.FilestoreModel.Status; import com.gitblit.servlet.FilestoreServlet; import com.gitblit.utils.FileUtils; public class FilestoreServletTest extends GitblitUnitTest { private static final AtomicBoolean started = new AtomicBoolean(false); private static final String SHA256_EG = "9a712c5d4037503a2d5ee1d07ad191eb99d051e84cbb020c171a5ae19bbe3cbd"; private static final String repoName = "helloworld.git"; private static final String repoLfs = "/r/" + repoName + "/info/lfs/objects/"; @BeforeClass public static void startGitblit() throws Exception { started.set(GitBlitSuite.startGitblit()); } @AfterClass public static void stopGitblit() throws Exception { if (started.get()) { GitBlitSuite.stopGitblit(); } } @Test public void testRegexGroups() throws Exception { Pattern p = Pattern.compile(FilestoreServlet.REGEX_PATH); String basicUrl = "https://localhost:8080/r/test.git/info/lfs/objects/"; String batchUrl = basicUrl + "batch"; String oidUrl = basicUrl + SHA256_EG; Matcher m = p.matcher(batchUrl); assertTrue(m.find()); assertEquals("https://localhost:8080", m.group(FilestoreServlet.REGEX_GROUP_BASE_URI)); assertEquals("r", m.group(FilestoreServlet.REGEX_GROUP_PREFIX)); assertEquals("test.git", m.group(FilestoreServlet.REGEX_GROUP_REPOSITORY)); assertEquals("batch", m.group(FilestoreServlet.REGEX_GROUP_ENDPOINT)); m = p.matcher(oidUrl); assertTrue(m.find()); assertEquals("https://localhost:8080", m.group(FilestoreServlet.REGEX_GROUP_BASE_URI)); assertEquals("r", m.group(FilestoreServlet.REGEX_GROUP_PREFIX)); assertEquals("test.git", m.group(FilestoreServlet.REGEX_GROUP_REPOSITORY)); assertEquals(SHA256_EG, m.group(FilestoreServlet.REGEX_GROUP_ENDPOINT)); } @Test public void testRegexGroupsNestedRepo() throws Exception { Pattern p = Pattern.compile(FilestoreServlet.REGEX_PATH); String basicUrl = "https://localhost:8080/r/nested/test.git/info/lfs/objects/"; String batchUrl = basicUrl + "batch"; String oidUrl = basicUrl + SHA256_EG; Matcher m = p.matcher(batchUrl); assertTrue(m.find()); assertEquals("https://localhost:8080", m.group(FilestoreServlet.REGEX_GROUP_BASE_URI)); assertEquals("r", m.group(FilestoreServlet.REGEX_GROUP_PREFIX)); assertEquals("nested/test.git", m.group(FilestoreServlet.REGEX_GROUP_REPOSITORY)); assertEquals("batch", m.group(FilestoreServlet.REGEX_GROUP_ENDPOINT)); m = p.matcher(oidUrl); assertTrue(m.find()); assertEquals("https://localhost:8080", m.group(FilestoreServlet.REGEX_GROUP_BASE_URI)); assertEquals("r", m.group(FilestoreServlet.REGEX_GROUP_PREFIX)); assertEquals("nested/test.git", m.group(FilestoreServlet.REGEX_GROUP_REPOSITORY)); assertEquals(SHA256_EG, m.group(FilestoreServlet.REGEX_GROUP_ENDPOINT)); } @Test public void testDownload() throws Exception { FileUtils.delete(filestore().getStorageFolder()); filestore().clearFilestoreCache(); RepositoryModel r = gitblit().getRepositoryModel(repoName); UserModel u = new UserModel("admin"); u.canAdmin = true; //No upload limit settings().overrideSetting(Keys.filestore.maxUploadSize, FilestoreManager.UNDEFINED_SIZE); final BlobInfo blob = new BlobInfo(512*FileUtils.KB); //Emulate a pre-existing Git-LFS repository by using using internal pre-tested methods assertEquals(Status.Available, filestore().uploadBlob(blob.hash, blob.length, u, r, new ByteArrayInputStream(blob.blob))); final String downloadURL = GitBlitSuite.url + repoLfs + blob.hash; HttpClient client = HttpClientBuilder.create().build(); HttpGet request = new HttpGet(downloadURL); // add request header request.addHeader(HttpHeaders.ACCEPT, FilestoreServlet.GIT_LFS_META_MIME); HttpResponse response = client.execute(request); assertEquals(200, response.getStatusLine().getStatusCode()); String content = IOUtils.toString(response.getEntity().getContent(), "UTF-8"); String expectedContent = String.format("{%s:%s,%s:%d,%s:{%s:{%s:%s}}}", "\"oid\"", "\"" + blob.hash + "\"", "\"size\"", blob.length, "\"actions\"", "\"download\"", "\"href\"", "\"" + downloadURL + "\""); assertEquals(expectedContent, content); //Now try the binary download request.removeHeaders(HttpHeaders.ACCEPT); response = client.execute(request); assertEquals(200, response.getStatusLine().getStatusCode()); byte[] dlData = IOUtils.toByteArray(response.getEntity().getContent()); assertArrayEquals(blob.blob, dlData); } @Test public void testDownloadMultiple() throws Exception { FileUtils.delete(filestore().getStorageFolder()); filestore().clearFilestoreCache(); RepositoryModel r = gitblit().getRepositoryModel(repoName); UserModel u = new UserModel("admin"); u.canAdmin = true; //No upload limit settings().overrideSetting(Keys.filestore.maxUploadSize, FilestoreManager.UNDEFINED_SIZE); final BlobInfo blob = new BlobInfo(512*FileUtils.KB); //Emulate a pre-existing Git-LFS repository by using using internal pre-tested methods assertEquals(Status.Available, filestore().uploadBlob(blob.hash, blob.length, u, r, new ByteArrayInputStream(blob.blob))); final String batchURL = GitBlitSuite.url + repoLfs + "batch"; final String downloadURL = GitBlitSuite.url + repoLfs + blob.hash; HttpClient client = HttpClientBuilder.create().build(); HttpPost request = new HttpPost(batchURL); // add request header request.addHeader(HttpHeaders.ACCEPT, FilestoreServlet.GIT_LFS_META_MIME); request.addHeader(HttpHeaders.CONTENT_ENCODING, FilestoreServlet.GIT_LFS_META_MIME); String content = String.format("{%s:%s,%s:[{%s:%s,%s:%d},{%s:%s,%s:%d}]}", "\"operation\"", "\"download\"", "\"objects\"", "\"oid\"", "\"" + blob.hash + "\"", "\"size\"", blob.length, "\"oid\"", "\"" + SHA256_EG + "\"", "\"size\"", 0); HttpEntity entity = new ByteArrayEntity(content.getBytes("UTF-8")); request.setEntity(entity); HttpResponse response = client.execute(request); String responseMessage = IOUtils.toString(response.getEntity().getContent(), "UTF-8"); assertEquals(200, response.getStatusLine().getStatusCode()); String expectedContent = String.format("{%s:[{%s:%s,%s:%d,%s:{%s:{%s:%s}}},{%s:%s,%s:%d,%s:{%s:%s,%s:%d}}]}", "\"objects\"", "\"oid\"", "\"" + blob.hash + "\"", "\"size\"", blob.length, "\"actions\"", "\"download\"", "\"href\"", "\"" + downloadURL + "\"", "\"oid\"", "\"" + SHA256_EG + "\"", "\"size\"", 0, "\"error\"", "\"message\"", "\"Object not available\"", "\"code\"", 404 ); assertEquals(expectedContent, responseMessage); } @Test public void testDownloadUnavailable() throws Exception { FileUtils.delete(filestore().getStorageFolder()); filestore().clearFilestoreCache(); //No upload limit settings().overrideSetting(Keys.filestore.maxUploadSize, FilestoreManager.UNDEFINED_SIZE); final BlobInfo blob = new BlobInfo(512*FileUtils.KB); final String downloadURL = GitBlitSuite.url + repoLfs + blob.hash; HttpClient client = HttpClientBuilder.create().build(); HttpGet request = new HttpGet(downloadURL); // add request header request.addHeader(HttpHeaders.ACCEPT, FilestoreServlet.GIT_LFS_META_MIME); HttpResponse response = client.execute(request); assertEquals(404, response.getStatusLine().getStatusCode()); String content = IOUtils.toString(response.getEntity().getContent(), "UTF-8"); String expectedError = String.format("{%s:%s,%s:%d}", "\"message\"", "\"Object not available\"", "\"code\"", 404); assertEquals(expectedError, content); } @Test public void testUpload() throws Exception { FileUtils.delete(filestore().getStorageFolder()); filestore().clearFilestoreCache(); RepositoryModel r = gitblit().getRepositoryModel(repoName); UserModel u = new UserModel("admin"); u.canAdmin = true; //No upload limit settings().overrideSetting(Keys.filestore.maxUploadSize, FilestoreManager.UNDEFINED_SIZE); final BlobInfo blob = new BlobInfo(512*FileUtils.KB); final String expectedUploadURL = GitBlitSuite.url + repoLfs + blob.hash; final String initialUploadURL = GitBlitSuite.url + repoLfs + "batch"; HttpClient client = HttpClientBuilder.create().build(); HttpPost request = new HttpPost(initialUploadURL); // add request header request.addHeader(HttpHeaders.ACCEPT, FilestoreServlet.GIT_LFS_META_MIME); request.addHeader(HttpHeaders.CONTENT_ENCODING, FilestoreServlet.GIT_LFS_META_MIME); String content = String.format("{%s:%s,%s:[{%s:%s,%s:%d}]}", "\"operation\"", "\"upload\"", "\"objects\"", "\"oid\"", "\"" + blob.hash + "\"", "\"size\"", blob.length); HttpEntity entity = new ByteArrayEntity(content.getBytes("UTF-8")); request.setEntity(entity); HttpResponse response = client.execute(request); String responseMessage = IOUtils.toString(response.getEntity().getContent(), "UTF-8"); assertEquals(200, response.getStatusLine().getStatusCode()); String expectedContent = String.format("{%s:[{%s:%s,%s:%d,%s:{%s:{%s:%s}}}]}", "\"objects\"", "\"oid\"", "\"" + blob.hash + "\"", "\"size\"", blob.length, "\"actions\"", "\"upload\"", "\"href\"", "\"" + expectedUploadURL + "\""); assertEquals(expectedContent, responseMessage); //Now try to upload the binary download HttpPut putRequest = new HttpPut(expectedUploadURL); putRequest.setEntity(new ByteArrayEntity(blob.blob)); response = client.execute(putRequest); responseMessage = IOUtils.toString(response.getEntity().getContent(), "UTF-8"); assertEquals(200, response.getStatusLine().getStatusCode()); //Confirm behind the scenes that it is available ByteArrayOutputStream savedBlob = new ByteArrayOutputStream(); assertEquals(Status.Available, filestore().downloadBlob(blob.hash, u, r, savedBlob)); assertArrayEquals(blob.blob, savedBlob.toByteArray()); } @Test public void testMalformedUpload() throws Exception { FileUtils.delete(filestore().getStorageFolder()); filestore().clearFilestoreCache(); //No upload limit settings().overrideSetting(Keys.filestore.maxUploadSize, FilestoreManager.UNDEFINED_SIZE); final BlobInfo blob = new BlobInfo(512*FileUtils.KB); final String initialUploadURL = GitBlitSuite.url + repoLfs + "batch"; HttpClient client = HttpClientBuilder.create().build(); HttpPost request = new HttpPost(initialUploadURL); // add request header request.addHeader(HttpHeaders.ACCEPT, FilestoreServlet.GIT_LFS_META_MIME); request.addHeader(HttpHeaders.CONTENT_ENCODING, FilestoreServlet.GIT_LFS_META_MIME); //Malformed JSON, comma instead of colon and unquoted strings String content = String.format("{%s:%s,%s:[{%s:%s,%s,%d}]}", "operation", "upload", "objects", "oid", blob.hash, "size", blob.length); HttpEntity entity = new ByteArrayEntity(content.getBytes("UTF-8")); request.setEntity(entity); HttpResponse response = client.execute(request); String responseMessage = IOUtils.toString(response.getEntity().getContent(), "UTF-8"); assertEquals(400, response.getStatusLine().getStatusCode()); String expectedError = String.format("{%s:%s,%s:%d}", "\"message\"", "\"Malformed Git-LFS request\"", "\"code\"", 400); assertEquals(expectedError, responseMessage); } }