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