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