View Javadoc
1   /*
2    * Copyright (C) 2019, Google LLC. 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.transport;
11  
12  import static org.hamcrest.MatcherAssert.assertThat;
13  import static org.hamcrest.Matchers.containsString;
14  import static org.junit.Assert.assertFalse;
15  import static org.junit.Assert.assertThrows;
16  import static org.junit.Assert.assertTrue;
17  
18  import java.util.Collections;
19  
20  import org.eclipse.jgit.errors.TransportException;
21  import org.eclipse.jgit.internal.storage.dfs.DfsGarbageCollector;
22  import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
23  import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
24  import org.eclipse.jgit.junit.TestRepository;
25  import org.eclipse.jgit.lib.NullProgressMonitor;
26  import org.eclipse.jgit.lib.Repository;
27  import org.eclipse.jgit.revwalk.RevBlob;
28  import org.eclipse.jgit.revwalk.RevCommit;
29  import org.eclipse.jgit.transport.UploadPack.RequestPolicy;
30  import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
31  import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
32  import org.eclipse.jgit.transport.resolver.UploadPackFactory;
33  import org.junit.After;
34  import org.junit.Before;
35  import org.junit.Test;
36  
37  /**
38   * Test combinations of:
39   * <ul>
40   * <li>Fetch a blob or a commit</li>
41   * <li>Fetched object is reachable or not</li>
42   * <li>With and without bitmaps</li>
43   * </ul>
44   */
45  public class UploadPackReachabilityTest {
46  
47  	private URIish uri;
48  
49  	private TestProtocol<Object> testProtocol;
50  
51  	private Object ctx = new Object();
52  
53  	private InMemoryRepository server;
54  
55  	private InMemoryRepository client;
56  
57  	private TestRepository<InMemoryRepository> remote;
58  
59  	@Before
60  	public void setUp() throws Exception {
61  		server = newRepo("server");
62  		client = newRepo("client");
63  
64  		remote = new TestRepository<>(server);
65  	}
66  
67  	@After
68  	public void tearDown() {
69  		Transport.unregister(testProtocol);
70  	}
71  
72  	@Test
73  	public void testFetchUnreachableBlobWithBitmap() throws Exception {
74  		RevBlob blob = remote.blob("foo");
75  		remote.commit(remote.tree(remote.file("foo", blob)));
76  		generateBitmaps(server);
77  
78  		testProtocol = generateReachableCommitUploadPackProtocol();
79  		uri = testProtocol.register(ctx, server);
80  
81  		assertFalse(client.getObjectDatabase().has(blob.toObjectId()));
82  
83  		try (Transport tn = testProtocol.open(uri, client, "server")) {
84  			TransportException e = assertThrows(TransportException.class,
85  					() -> tn.fetch(NullProgressMonitor.INSTANCE, Collections
86  							.singletonList(new RefSpec(blob.name()))));
87  			assertThat(e.getMessage(),
88  					containsString("want " + blob.name() + " not valid"));
89  		}
90  	}
91  
92  	@Test
93  	public void testFetchReachableBlobWithoutBitmap() throws Exception {
94  		RevBlob blob = remote.blob("foo");
95  		RevCommit commit = remote.commit(remote.tree(remote.file("foo", blob)));
96  		remote.update("master", commit);
97  
98  		testProtocol = generateReachableCommitUploadPackProtocol();
99  		uri = testProtocol.register(ctx, server);
100 
101 		assertFalse(client.getObjectDatabase().has(blob.toObjectId()));
102 
103 		try (Transport tn = testProtocol.open(uri, client, "server")) {
104 			TransportException e = assertThrows(TransportException.class,
105 					() -> tn.fetch(NullProgressMonitor.INSTANCE, Collections
106 							.singletonList(new RefSpec(blob.name()))));
107 			assertThat(e.getMessage(),
108 					containsString(
109 						"want " + blob.name() + " not valid"));
110 		}
111 	}
112 
113 	@Test
114 	public void testFetchReachableBlobWithoutBitmapButFilterAllowed() throws Exception {
115 		InMemoryRepository server2 = newRepo("server2");
116 		try (TestRepository<InMemoryRepository> remote2 = new TestRepository<>(
117 				server2)) {
118 			RevBlob blob = remote2.blob("foo");
119 			RevCommit commit = remote2.commit(remote2.tree(remote2.file("foo", blob)));
120 			remote2.update("master", commit);
121 
122 			server2.getConfig().setBoolean("uploadpack", null, "allowfilter",
123 					true);
124 
125 			testProtocol = new TestProtocol<>((Object req, Repository db) -> {
126 				UploadPack up = new UploadPack(db);
127 				up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);
128 				return up;
129 			}, null);
130 			uri = testProtocol.register(ctx, server2);
131 
132 			assertFalse(client.getObjectDatabase().has(blob.toObjectId()));
133 
134 			try (Transport tn = testProtocol.open(uri, client, "server2")) {
135 				tn.fetch(NullProgressMonitor.INSTANCE,
136 						Collections.singletonList(new RefSpec(blob.name())));
137 				assertTrue(client.getObjectDatabase().has(blob.toObjectId()));
138 			}
139 		}
140 	}
141 
142 	@Test
143 	public void testFetchUnreachableBlobWithoutBitmap() throws Exception {
144 		RevBlob blob = remote.blob("foo");
145 		remote.commit(remote.tree(remote.file("foo", blob)));
146 
147 		testProtocol = generateReachableCommitUploadPackProtocol();
148 		uri = testProtocol.register(ctx, server);
149 
150 		assertFalse(client.getObjectDatabase().has(blob.toObjectId()));
151 
152 		try (Transport tn = testProtocol.open(uri, client, "server")) {
153 			TransportException e = assertThrows(TransportException.class, () ->
154 			tn.fetch(NullProgressMonitor.INSTANCE,
155 					Collections.singletonList(new RefSpec(blob.name()))));
156 			assertThat(e.getMessage(),
157 					containsString("want " + blob.name() + " not valid"));
158 		}
159 	}
160 
161 	@Test
162 	public void testFetchReachableBlobWithBitmap() throws Exception {
163 		RevBlob blob = remote.blob("foo");
164 		RevCommit commit = remote.commit(remote.tree(remote.file("foo", blob)));
165 		remote.update("master", commit);
166 		generateBitmaps(server);
167 
168 		testProtocol = generateReachableCommitUploadPackProtocol();
169 		uri = testProtocol.register(ctx, server);
170 
171 		assertFalse(client.getObjectDatabase().has(blob.toObjectId()));
172 
173 		try (Transport tn = testProtocol.open(uri, client, "server")) {
174 			tn.fetch(NullProgressMonitor.INSTANCE,
175 					Collections.singletonList(new RefSpec(blob.name())));
176 			assertTrue(client.getObjectDatabase().has(blob.toObjectId()));
177 		}
178 	}
179 
180 	@Test
181 	public void testFetchReachableCommitWithBitmap() throws Exception {
182 		RevCommit commit = remote
183 				.commit(remote.tree(remote.file("foo", remote.blob("foo"))));
184 		remote.update("master", commit);
185 		generateBitmaps(server);
186 
187 		testProtocol = generateReachableCommitUploadPackProtocol();
188 		uri = testProtocol.register(ctx, server);
189 
190 		assertFalse(client.getObjectDatabase().has(commit.toObjectId()));
191 
192 		try (Transport tn = testProtocol.open(uri, client, "server")) {
193 			tn.fetch(NullProgressMonitor.INSTANCE,
194 					Collections.singletonList(new RefSpec(commit.name())));
195 			assertTrue(client.getObjectDatabase().has(commit.toObjectId()));
196 		}
197 	}
198 
199 	@Test
200 	public void testFetchReachableCommitWithoutBitmap() throws Exception {
201 		RevCommit commit = remote
202 				.commit(remote.tree(remote.file("foo", remote.blob("foo"))));
203 		remote.update("master", commit);
204 		generateBitmaps(server);
205 
206 		testProtocol = generateReachableCommitUploadPackProtocol();
207 		uri = testProtocol.register(ctx, server);
208 
209 		assertFalse(client.getObjectDatabase().has(commit.toObjectId()));
210 
211 		try (Transport tn = testProtocol.open(uri, client, "server")) {
212 			tn.fetch(NullProgressMonitor.INSTANCE,
213 					Collections.singletonList(new RefSpec(commit.name())));
214 			assertTrue(client.getObjectDatabase().has(commit.toObjectId()));
215 		}
216 	}
217 
218 	@Test
219 	public void testFetchUnreachableCommitWithBitmap() throws Exception {
220 		RevCommit commit = remote
221 				.commit(remote.tree(remote.file("foo", remote.blob("foo"))));
222 		generateBitmaps(server);
223 
224 		testProtocol = generateReachableCommitUploadPackProtocol();
225 		uri = testProtocol.register(ctx, server);
226 
227 		assertFalse(client.getObjectDatabase().has(commit.toObjectId()));
228 
229 		try (Transport tn = testProtocol.open(uri, client, "server")) {
230 			TransportException e = assertThrows(TransportException.class,
231 					() -> tn.fetch(NullProgressMonitor.INSTANCE,
232 					Collections.singletonList(new RefSpec(commit.name()))));
233 			assertThat(e.getMessage(),
234 					containsString("want " + commit.name() + " not valid"));
235 		}
236 	}
237 
238 	@Test
239 	public void testFetchUnreachableCommitWithoutBitmap() throws Exception {
240 		RevCommit commit = remote
241 				.commit(remote.tree(remote.file("foo", remote.blob("foo"))));
242 
243 		testProtocol = generateReachableCommitUploadPackProtocol();
244 		uri = testProtocol.register(ctx, server);
245 
246 		assertFalse(client.getObjectDatabase().has(commit.toObjectId()));
247 
248 		try (Transport tn = testProtocol.open(uri, client, "server")) {
249 			TransportException e = assertThrows(TransportException.class,
250 					() -> tn.fetch(NullProgressMonitor.INSTANCE, Collections
251 							.singletonList(new RefSpec(commit.name()))));
252 			assertThat(e.getMessage(),
253 					containsString("want " + commit.name() + " not valid"));
254 		}
255 	}
256 
257 	private static InMemoryRepository newRepo(String name) {
258 		return new InMemoryRepository(new DfsRepositoryDescription(name));
259 	}
260 
261 	private void generateBitmaps(InMemoryRepository repo) throws Exception {
262 		new DfsGarbageCollector(repo).pack(null);
263 		repo.scanForRepoChanges();
264 	}
265 
266 	private static TestProtocol<Object> generateReachableCommitUploadPackProtocol() {
267 		return new TestProtocol<>(new UploadPackFactory<Object>() {
268 			@Override
269 			public UploadPack create(Object req, Repository db)
270 					throws ServiceNotEnabledException,
271 					ServiceNotAuthorizedException {
272 				UploadPack up = new UploadPack(db);
273 				up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);
274 				return up;
275 			}
276 		}, null);
277 	}
278 }