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 package org.eclipse.jgit.http.test;
45
46 import static org.hamcrest.Matchers.is;
47 import static org.junit.Assert.assertEquals;
48 import static org.junit.Assert.assertFalse;
49 import static org.junit.Assert.assertNotNull;
50 import static org.junit.Assert.assertNull;
51 import static org.junit.Assert.assertThat;
52 import static org.junit.Assert.assertTrue;
53 import static org.junit.Assert.fail;
54
55 import java.io.File;
56 import java.io.OutputStream;
57 import java.net.URI;
58 import java.net.URL;
59 import java.util.List;
60
61 import javax.servlet.http.HttpServletRequest;
62
63 import org.eclipse.jetty.servlet.DefaultServlet;
64 import org.eclipse.jetty.servlet.ServletContextHandler;
65 import org.eclipse.jetty.servlet.ServletHolder;
66 import org.eclipse.jgit.errors.NoRemoteRepositoryException;
67 import org.eclipse.jgit.errors.RepositoryNotFoundException;
68 import org.eclipse.jgit.errors.TransportException;
69 import org.eclipse.jgit.http.server.GitServlet;
70 import org.eclipse.jgit.internal.JGitText;
71 import org.eclipse.jgit.junit.TestRepository;
72 import org.eclipse.jgit.junit.http.AccessEvent;
73 import org.eclipse.jgit.junit.http.AppServer;
74 import org.eclipse.jgit.junit.http.HttpTestCase;
75 import org.eclipse.jgit.lib.Constants;
76 import org.eclipse.jgit.lib.Ref;
77 import org.eclipse.jgit.lib.RefUpdate;
78 import org.eclipse.jgit.lib.Repository;
79 import org.eclipse.jgit.lib.StoredConfig;
80 import org.eclipse.jgit.revwalk.RevCommit;
81 import org.eclipse.jgit.transport.FetchConnection;
82 import org.eclipse.jgit.transport.PacketLineIn;
83 import org.eclipse.jgit.transport.PacketLineOut;
84 import org.eclipse.jgit.transport.Transport;
85 import org.eclipse.jgit.transport.URIish;
86 import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
87 import org.eclipse.jgit.transport.http.HttpConnection;
88 import org.eclipse.jgit.transport.http.JDKHttpConnectionFactory;
89 import org.junit.Before;
90 import org.junit.Test;
91
92 public class HttpClientTests extends HttpTestCase {
93 private TestRepository<Repository> remoteRepository;
94
95 private URIish dumbAuthNoneURI;
96
97 private URIish dumbAuthBasicURI;
98
99 private URIish smartAuthNoneURI;
100
101 private URIish smartAuthBasicURI;
102
103 @Override
104 @Before
105 public void setUp() throws Exception {
106 super.setUp();
107
108 remoteRepository = createTestRepository();
109 remoteRepository.update(master, remoteRepository.commit().create());
110
111 ServletContextHandler dNone = dumb("/dnone");
112 ServletContextHandler dBasic = server.authBasic(dumb("/dbasic"));
113
114 ServletContextHandler sNone = smart("/snone");
115 ServletContextHandler sBasic = server.authBasic(smart("/sbasic"));
116
117 server.setUp();
118
119 final String srcName = nameOf(remoteRepository.getRepository());
120 dumbAuthNoneURI = toURIish(dNone, srcName);
121 dumbAuthBasicURI = toURIish(dBasic, srcName);
122
123 smartAuthNoneURI = toURIish(sNone, srcName);
124 smartAuthBasicURI = toURIish(sBasic, srcName);
125 }
126
127 private ServletContextHandler dumb(String path) {
128 final File srcGit = remoteRepository.getRepository().getDirectory();
129 final URI base = srcGit.getParentFile().toURI();
130
131 ServletContextHandler ctx = server.addContext(path);
132 ctx.setResourceBase(base.toString());
133 ServletHolder holder = ctx.addServlet(DefaultServlet.class, "/");
134
135 holder.setInitParameter("aliases", "true");
136 return ctx;
137 }
138
139 private ServletContextHandler smart(String path) {
140 GitServlet gs = new GitServlet();
141 gs.setRepositoryResolver((HttpServletRequest req, String name) -> {
142 final Repository db = remoteRepository.getRepository();
143 if (!name.equals(nameOf(db))) {
144 throw new RepositoryNotFoundException(name);
145 }
146 db.incrementOpen();
147 return db;
148 });
149
150 ServletContextHandler ctx = server.addContext(path);
151 ctx.addServlet(new ServletHolder(gs), "/*");
152 return ctx;
153 }
154
155 private static String nameOf(Repository db) {
156 return db.getDirectory().getName();
157 }
158
159 @Test
160 public void testRepositoryNotFound_Dumb() throws Exception {
161 URIish uri = toURIish("/dumb.none/not-found");
162 Repository dst = createBareRepository();
163 try (Transport t = Transport.open(dst, uri)) {
164 try {
165 t.openFetch();
166 fail("connection opened to not found repository");
167 } catch (NoRemoteRepositoryException err) {
168 String exp = uri + ": " + uri
169 + "/info/refs?service=git-upload-pack not found";
170 assertNotNull(err.getMessage());
171 assertTrue("Unexpected error message",
172 err.getMessage().startsWith(exp));
173 }
174 }
175 }
176
177 @Test
178 public void testRepositoryNotFound_Smart() throws Exception {
179 URIish uri = toURIish("/smart.none/not-found");
180 Repository dst = createBareRepository();
181 try (Transport t = Transport.open(dst, uri)) {
182 try {
183 t.openFetch();
184 fail("connection opened to not found repository");
185 } catch (NoRemoteRepositoryException err) {
186 String exp = uri + ": " + uri
187 + "/info/refs?service=git-upload-pack not found";
188 assertNotNull(err.getMessage());
189 assertTrue("Unexpected error message",
190 err.getMessage().startsWith(exp));
191 }
192 }
193 }
194
195 @Test
196 public void testListRemote_Dumb_DetachedHEAD() throws Exception {
197 Repository src = remoteRepository.getRepository();
198 RefUpdate u = src.updateRef(Constants.HEAD, true);
199 RevCommit Q = remoteRepository.commit().message("Q").create();
200 u.setNewObjectId(Q);
201 assertEquals(RefUpdate.Result.FORCED, u.forceUpdate());
202
203 Repository dst = createBareRepository();
204 Ref head;
205 try (Transport t = Transport.open(dst, dumbAuthNoneURI);
206 FetchConnection c = t.openFetch()) {
207 head = c.getRef(Constants.HEAD);
208 }
209 assertNotNull("has " + Constants.HEAD, head);
210 assertEquals(Q, head.getObjectId());
211 }
212
213 @Test
214 public void testListRemote_Dumb_NoHEAD() throws Exception {
215 Repository src = remoteRepository.getRepository();
216 File headref = new File(src.getDirectory(), Constants.HEAD);
217 assertTrue("HEAD used to be present", headref.delete());
218 assertFalse("HEAD is gone", headref.exists());
219
220 Repository dst = createBareRepository();
221 Ref head;
222 try (Transport t = Transport.open(dst, dumbAuthNoneURI);
223 FetchConnection c = t.openFetch()) {
224 head = c.getRef(Constants.HEAD);
225 }
226 assertNull("has no " + Constants.HEAD, head);
227 }
228
229 @Test
230 public void testListRemote_Smart_DetachedHEAD() throws Exception {
231 Repository src = remoteRepository.getRepository();
232 RefUpdate u = src.updateRef(Constants.HEAD, true);
233 RevCommit Q = remoteRepository.commit().message("Q").create();
234 u.setNewObjectId(Q);
235 assertEquals(RefUpdate.Result.FORCED, u.forceUpdate());
236
237 Repository dst = createBareRepository();
238 Ref head;
239 try (Transport t = Transport.open(dst, smartAuthNoneURI);
240 FetchConnection c = t.openFetch()) {
241 head = c.getRef(Constants.HEAD);
242 }
243 assertNotNull("has " + Constants.HEAD, head);
244 assertEquals(Q, head.getObjectId());
245 }
246
247 @Test
248 public void testListRemote_Smart_WithQueryParameters() throws Exception {
249 URIish myURI = toURIish("/snone/do?r=1&p=test.git");
250 Repository dst = createBareRepository();
251 try (Transport t = Transport.open(dst, myURI)) {
252 try {
253 t.openFetch();
254 fail("test did not fail to find repository as expected");
255 } catch (NoRemoteRepositoryException err) {
256
257 }
258 }
259
260 List<AccessEvent> requests = getRequests();
261 assertEquals(1, requests.size());
262
263 AccessEvent info = requests.get(0);
264 assertEquals("GET", info.getMethod());
265 assertEquals("/snone/do", info.getPath());
266 assertEquals(3, info.getParameters().size());
267 assertEquals("1", info.getParameter("r"));
268 assertEquals("test.git/info/refs", info.getParameter("p"));
269 assertEquals("git-upload-pack", info.getParameter("service"));
270 assertEquals(404, info.getStatus());
271 }
272
273 @Test
274 public void testListRemote_Dumb_NeedsAuth() throws Exception {
275 Repository dst = createBareRepository();
276 try (Transport t = Transport.open(dst, dumbAuthBasicURI)) {
277 try {
278 t.openFetch();
279 fail("connection opened even info/refs needs auth basic");
280 } catch (TransportException err) {
281 String exp = dumbAuthBasicURI + ": "
282 + JGitText.get().noCredentialsProvider;
283 assertEquals(exp, err.getMessage());
284 }
285 }
286 }
287
288 @Test
289 public void testListRemote_Dumb_Auth() throws Exception {
290 Repository dst = createBareRepository();
291 try (Transport t = Transport.open(dst, dumbAuthBasicURI)) {
292 t.setCredentialsProvider(new UsernamePasswordCredentialsProvider(
293 AppServer.username, AppServer.password));
294 t.openFetch().close();
295 }
296 try (Transport t = Transport.open(dst, dumbAuthBasicURI)) {
297 t.setCredentialsProvider(new UsernamePasswordCredentialsProvider(
298 AppServer.username, ""));
299 try {
300 t.openFetch();
301 fail("connection opened even info/refs needs auth basic and we provide wrong password");
302 } catch (TransportException err) {
303 String exp = dumbAuthBasicURI + ": "
304 + JGitText.get().notAuthorized;
305 assertEquals(exp, err.getMessage());
306 }
307 }
308 }
309
310 @Test
311 public void testListRemote_Smart_UploadPackNeedsAuth() throws Exception {
312 Repository dst = createBareRepository();
313 try (Transport t = Transport.open(dst, smartAuthBasicURI)) {
314 try {
315 t.openFetch();
316 fail("connection opened even though service disabled");
317 } catch (TransportException err) {
318 String exp = smartAuthBasicURI + ": "
319 + JGitText.get().noCredentialsProvider;
320 assertEquals(exp, err.getMessage());
321 }
322 }
323 }
324
325 @Test
326 public void testListRemote_Smart_UploadPackDisabled() throws Exception {
327 Repository src = remoteRepository.getRepository();
328 final StoredConfig cfg = src.getConfig();
329 cfg.setBoolean("http", null, "uploadpack", false);
330 cfg.save();
331
332 Repository dst = createBareRepository();
333 try (Transport t = Transport.open(dst, smartAuthNoneURI)) {
334 try {
335 t.openFetch();
336 fail("connection opened even though service disabled");
337 } catch (TransportException err) {
338 String exp = smartAuthNoneURI + ": "
339 + JGitText.get().serviceNotEnabledNoName;
340 assertEquals(exp, err.getMessage());
341 }
342 }
343 }
344
345 @Test
346 public void testListRemoteWithoutLocalRepository() throws Exception {
347 try (Transport t = Transport.open(smartAuthNoneURI);
348 FetchConnection c = t.openFetch()) {
349 Ref head = c.getRef(Constants.HEAD);
350 assertNotNull(head);
351 }
352 }
353
354 @Test
355 public void testHttpClientWantsV2ButServerNotConfigured() throws Exception {
356 JDKHttpConnectionFactory f = new JDKHttpConnectionFactory();
357 String url = smartAuthNoneURI.toString() + "/info/refs?service=git-upload-pack";
358 HttpConnection c = f.create(new URL(url));
359 c.setRequestMethod("GET");
360 c.setRequestProperty("Git-Protocol", "version=2");
361 c.connect();
362 assertEquals(200, c.getResponseCode());
363
364 PacketLineIn pckIn = new PacketLineIn(c.getInputStream());
365
366
367 assertThat(pckIn.readString(), is("# service=git-upload-pack"));
368 assertTrue(PacketLineIn.isEnd(pckIn.readString()));
369 assertTrue(pckIn.readString().matches("[0-9a-f]{40} HEAD.*"));
370 }
371
372 @Test
373 public void testV2HttpFirstResponse() throws Exception {
374 remoteRepository.getRepository().getConfig().setInt(
375 "protocol", null, "version", 2);
376
377 JDKHttpConnectionFactory f = new JDKHttpConnectionFactory();
378 String url = smartAuthNoneURI.toString() + "/info/refs?service=git-upload-pack";
379 HttpConnection c = f.create(new URL(url));
380 c.setRequestMethod("GET");
381 c.setRequestProperty("Git-Protocol", "version=2");
382 c.connect();
383 assertEquals(200, c.getResponseCode());
384
385 PacketLineIn pckIn = new PacketLineIn(c.getInputStream());
386 assertThat(pckIn.readString(), is("version 2"));
387
388
389
390 for (String s : pckIn.readStrings()) {
391 assertTrue(!s.isEmpty());
392 }
393 }
394
395 @Test
396 public void testV2HttpSubsequentResponse() throws Exception {
397 remoteRepository.getRepository().getConfig().setInt(
398 "protocol", null, "version", 2);
399
400 JDKHttpConnectionFactory f = new JDKHttpConnectionFactory();
401 String url = smartAuthNoneURI.toString() + "/git-upload-pack";
402 HttpConnection c = f.create(new URL(url));
403 c.setRequestMethod("POST");
404 c.setRequestProperty("Content-Type", "application/x-git-upload-pack-request");
405 c.setRequestProperty("Git-Protocol", "version=2");
406 c.setDoOutput(true);
407 c.connect();
408
409
410
411
412
413 try (OutputStream os = c.getOutputStream()) {
414 PacketLineOut pckOut = new PacketLineOut(os);
415 pckOut.writeString("command=ls-refs");
416 pckOut.writeDelim();
417 pckOut.end();
418 }
419
420 PacketLineIn pckIn = new PacketLineIn(c.getInputStream());
421
422
423 for (String s : pckIn.readStrings()) {
424 assertTrue(s.matches("[0-9a-f]{40} [A-Za-z/]*"));
425 }
426
427 assertEquals(200, c.getResponseCode());
428 }
429 }