summaryrefslogtreecommitdiffstats
path: root/src/test/java/com/gitblit/tests/FilestoreServletTest.java
blob: 4e4b056a9af5f9e416549934bd726be25b36dc18 (plain)
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
<!doctype html>
<html lang="en">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>jQuery UI Menu Demos</title>
</head>
<body>

<ul>
	<li><a href="default.html">Default functionality</a></li>
	<li><a href="icons.html">Icons</a></li>
	<li><a href="categories.html">Categories</a></li>
</ul>

</body>
</html>
n315' href='#n315'>315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
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);
	}
	
}