You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SmartClientSmartServerTest.java 48KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361
  1. /*
  2. * Copyright (C) 2010, 2017 Google Inc. 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.http.test;
  11. import static java.nio.charset.StandardCharsets.UTF_8;
  12. import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING;
  13. import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_LENGTH;
  14. import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_TYPE;
  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. import java.io.IOException;
  22. import java.io.PrintWriter;
  23. import java.net.URI;
  24. import java.net.URISyntaxException;
  25. import java.text.MessageFormat;
  26. import java.util.Collections;
  27. import java.util.EnumSet;
  28. import java.util.List;
  29. import java.util.Map;
  30. import java.util.regex.Matcher;
  31. import java.util.regex.Pattern;
  32. import javax.servlet.DispatcherType;
  33. import javax.servlet.Filter;
  34. import javax.servlet.FilterChain;
  35. import javax.servlet.FilterConfig;
  36. import javax.servlet.RequestDispatcher;
  37. import javax.servlet.ServletException;
  38. import javax.servlet.ServletRequest;
  39. import javax.servlet.ServletResponse;
  40. import javax.servlet.http.HttpServletRequest;
  41. import javax.servlet.http.HttpServletResponse;
  42. import org.eclipse.jetty.servlet.FilterHolder;
  43. import org.eclipse.jetty.servlet.ServletContextHandler;
  44. import org.eclipse.jetty.servlet.ServletHolder;
  45. import org.eclipse.jgit.errors.RemoteRepositoryException;
  46. import org.eclipse.jgit.errors.TransportException;
  47. import org.eclipse.jgit.errors.UnsupportedCredentialItem;
  48. import org.eclipse.jgit.http.server.GitServlet;
  49. import org.eclipse.jgit.http.server.resolver.DefaultUploadPackFactory;
  50. import org.eclipse.jgit.internal.JGitText;
  51. import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
  52. import org.eclipse.jgit.junit.TestRepository;
  53. import org.eclipse.jgit.junit.TestRng;
  54. import org.eclipse.jgit.junit.http.AccessEvent;
  55. import org.eclipse.jgit.junit.http.AppServer;
  56. import org.eclipse.jgit.lib.ConfigConstants;
  57. import org.eclipse.jgit.lib.Constants;
  58. import org.eclipse.jgit.lib.NullProgressMonitor;
  59. import org.eclipse.jgit.lib.ObjectId;
  60. import org.eclipse.jgit.lib.ObjectIdRef;
  61. import org.eclipse.jgit.lib.ObjectInserter;
  62. import org.eclipse.jgit.lib.Ref;
  63. import org.eclipse.jgit.lib.ReflogEntry;
  64. import org.eclipse.jgit.lib.ReflogReader;
  65. import org.eclipse.jgit.lib.Repository;
  66. import org.eclipse.jgit.lib.StoredConfig;
  67. import org.eclipse.jgit.revwalk.RevBlob;
  68. import org.eclipse.jgit.revwalk.RevCommit;
  69. import org.eclipse.jgit.revwalk.RevWalk;
  70. import org.eclipse.jgit.transport.AbstractAdvertiseRefsHook;
  71. import org.eclipse.jgit.transport.AdvertiseRefsHook;
  72. import org.eclipse.jgit.transport.CredentialItem;
  73. import org.eclipse.jgit.transport.CredentialsProvider;
  74. import org.eclipse.jgit.transport.FetchConnection;
  75. import org.eclipse.jgit.transport.HttpTransport;
  76. import org.eclipse.jgit.transport.RefSpec;
  77. import org.eclipse.jgit.transport.RemoteRefUpdate;
  78. import org.eclipse.jgit.transport.Transport;
  79. import org.eclipse.jgit.transport.TransportHttp;
  80. import org.eclipse.jgit.transport.URIish;
  81. import org.eclipse.jgit.transport.UploadPack;
  82. import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
  83. import org.eclipse.jgit.transport.http.HttpConnectionFactory;
  84. import org.eclipse.jgit.util.HttpSupport;
  85. import org.eclipse.jgit.util.SystemReader;
  86. import org.hamcrest.Matchers;
  87. import org.junit.Before;
  88. import org.junit.Rule;
  89. import org.junit.Test;
  90. import org.junit.rules.ExpectedException;
  91. public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
  92. private static final String HDR_TRANSFER_ENCODING = "Transfer-Encoding";
  93. @Rule
  94. public ExpectedException thrown = ExpectedException.none();
  95. private AdvertiseRefsHook advertiseRefsHook;
  96. private Repository remoteRepository;
  97. private CredentialsProvider testCredentials = new UsernamePasswordCredentialsProvider(
  98. AppServer.username, AppServer.password);
  99. private URIish remoteURI;
  100. private URIish brokenURI;
  101. private URIish redirectURI;
  102. private URIish authURI;
  103. private URIish authOnPostURI;
  104. private RevBlob A_txt;
  105. private RevCommit A, B, unreachableCommit;
  106. public SmartClientSmartServerTest(HttpConnectionFactory cf) {
  107. super(cf);
  108. }
  109. @Override
  110. @Before
  111. public void setUp() throws Exception {
  112. super.setUp();
  113. final TestRepository<Repository> src = createTestRepository();
  114. final String srcName = src.getRepository().getDirectory().getName();
  115. src.getRepository()
  116. .getConfig()
  117. .setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
  118. ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
  119. GitServlet gs = new GitServlet();
  120. gs.setUploadPackFactory((HttpServletRequest req, Repository db) -> {
  121. DefaultUploadPackFactory f = new DefaultUploadPackFactory();
  122. UploadPack up = f.create(req, db);
  123. if (advertiseRefsHook != null) {
  124. up.setAdvertiseRefsHook(advertiseRefsHook);
  125. }
  126. return up;
  127. });
  128. ServletContextHandler app = addNormalContext(gs, src, srcName);
  129. ServletContextHandler broken = addBrokenContext(gs, srcName);
  130. ServletContextHandler redirect = addRedirectContext(gs);
  131. ServletContextHandler auth = addAuthContext(gs, "auth");
  132. ServletContextHandler authOnPost = addAuthContext(gs, "pauth", "POST");
  133. server.setUp();
  134. remoteRepository = src.getRepository();
  135. remoteURI = toURIish(app, srcName);
  136. brokenURI = toURIish(broken, srcName);
  137. redirectURI = toURIish(redirect, srcName);
  138. authURI = toURIish(auth, srcName);
  139. authOnPostURI = toURIish(authOnPost, srcName);
  140. A_txt = src.blob("A");
  141. A = src.commit().add("A_txt", A_txt).create();
  142. B = src.commit().parent(A).add("A_txt", "C").add("B", "B").create();
  143. src.update(master, B);
  144. unreachableCommit = src.commit().add("A_txt", A_txt).create();
  145. src.update("refs/garbage/a/very/long/ref/name/to/compress", B);
  146. }
  147. private ServletContextHandler addNormalContext(GitServlet gs, TestRepository<Repository> src, String srcName) {
  148. ServletContextHandler app = server.addContext("/git");
  149. app.addFilter(new FilterHolder(new Filter() {
  150. @Override
  151. public void init(FilterConfig filterConfig)
  152. throws ServletException {
  153. // empty
  154. }
  155. // Does an internal forward for GET requests containing "/post/",
  156. // and issues a 301 redirect on POST requests for such URLs. Used
  157. // in the POST redirect tests.
  158. @Override
  159. public void doFilter(ServletRequest request,
  160. ServletResponse response, FilterChain chain)
  161. throws IOException, ServletException {
  162. final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
  163. final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  164. final StringBuffer fullUrl = httpServletRequest.getRequestURL();
  165. if (httpServletRequest.getQueryString() != null) {
  166. fullUrl.append("?")
  167. .append(httpServletRequest.getQueryString());
  168. }
  169. String urlString = fullUrl.toString();
  170. if ("POST".equalsIgnoreCase(httpServletRequest.getMethod())) {
  171. httpServletResponse.setStatus(
  172. HttpServletResponse.SC_MOVED_PERMANENTLY);
  173. httpServletResponse.setHeader(HttpSupport.HDR_LOCATION,
  174. urlString.replace("/post/", "/"));
  175. } else {
  176. String path = httpServletRequest.getPathInfo();
  177. path = path.replace("/post/", "/");
  178. if (httpServletRequest.getQueryString() != null) {
  179. path += '?' + httpServletRequest.getQueryString();
  180. }
  181. RequestDispatcher dispatcher = httpServletRequest
  182. .getRequestDispatcher(path);
  183. dispatcher.forward(httpServletRequest, httpServletResponse);
  184. }
  185. }
  186. @Override
  187. public void destroy() {
  188. // empty
  189. }
  190. }), "/post/*", EnumSet.of(DispatcherType.REQUEST));
  191. gs.setRepositoryResolver(new TestRepositoryResolver(src, srcName));
  192. app.addServlet(new ServletHolder(gs), "/*");
  193. return app;
  194. }
  195. private ServletContextHandler addBrokenContext(GitServlet gs,
  196. String srcName) {
  197. ServletContextHandler broken = server.addContext("/bad");
  198. broken.addFilter(new FilterHolder(new Filter() {
  199. @Override
  200. public void doFilter(ServletRequest request,
  201. ServletResponse response, FilterChain chain)
  202. throws IOException, ServletException {
  203. final HttpServletResponse r = (HttpServletResponse) response;
  204. r.setContentType("text/plain");
  205. r.setCharacterEncoding(UTF_8.name());
  206. try (PrintWriter w = r.getWriter()) {
  207. w.print("OK");
  208. }
  209. }
  210. @Override
  211. public void init(FilterConfig filterConfig)
  212. throws ServletException {
  213. // empty
  214. }
  215. @Override
  216. public void destroy() {
  217. // empty
  218. }
  219. }), "/" + srcName + "/git-upload-pack",
  220. EnumSet.of(DispatcherType.REQUEST));
  221. broken.addServlet(new ServletHolder(gs), "/*");
  222. return broken;
  223. }
  224. private ServletContextHandler addAuthContext(GitServlet gs,
  225. String contextPath, String... methods) {
  226. ServletContextHandler auth = server.addContext('/' + contextPath);
  227. auth.addServlet(new ServletHolder(gs), "/*");
  228. return server.authBasic(auth, methods);
  229. }
  230. private ServletContextHandler addRedirectContext(GitServlet gs) {
  231. ServletContextHandler redirect = server.addContext("/redirect");
  232. redirect.addFilter(new FilterHolder(new Filter() {
  233. // Enables tests for different codes, and for multiple redirects.
  234. // First parameter is the number of redirects, second one is the
  235. // redirect status code that should be used
  236. private Pattern responsePattern = Pattern
  237. .compile("/response/(\\d+)/(30[1237])/");
  238. // Enables tests to specify the context that the request should be
  239. // redirected to in the end. If not present, redirects got to the
  240. // normal /git context.
  241. private Pattern targetPattern = Pattern.compile("/target(/\\w+)/");
  242. @Override
  243. public void init(FilterConfig filterConfig)
  244. throws ServletException {
  245. // empty
  246. }
  247. private String local(String url, boolean toLocal) {
  248. if (!toLocal) {
  249. return url;
  250. }
  251. try {
  252. URI u = new URI(url);
  253. String fragment = u.getRawFragment();
  254. if (fragment != null) {
  255. return u.getRawPath() + '#' + fragment;
  256. }
  257. return u.getRawPath();
  258. } catch (URISyntaxException e) {
  259. return url;
  260. }
  261. }
  262. @Override
  263. public void doFilter(ServletRequest request,
  264. ServletResponse response, FilterChain chain)
  265. throws IOException, ServletException {
  266. final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
  267. final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  268. final StringBuffer fullUrl = httpServletRequest.getRequestURL();
  269. if (httpServletRequest.getQueryString() != null) {
  270. fullUrl.append("?")
  271. .append(httpServletRequest.getQueryString());
  272. }
  273. String urlString = fullUrl.toString();
  274. boolean localRedirect = false;
  275. if (urlString.contains("/local")) {
  276. urlString = urlString.replace("/local", "");
  277. localRedirect = true;
  278. }
  279. if (urlString.contains("/loop/")) {
  280. urlString = urlString.replace("/loop/", "/loop/x/");
  281. if (urlString.contains("/loop/x/x/x/x/x/x/x/x/")) {
  282. // Go back to initial.
  283. urlString = urlString.replace("/loop/x/x/x/x/x/x/x/x/",
  284. "/loop/");
  285. }
  286. httpServletResponse.setStatus(
  287. HttpServletResponse.SC_MOVED_TEMPORARILY);
  288. httpServletResponse.setHeader(HttpSupport.HDR_LOCATION,
  289. local(urlString, localRedirect));
  290. return;
  291. }
  292. int responseCode = HttpServletResponse.SC_MOVED_PERMANENTLY;
  293. int nofRedirects = 0;
  294. Matcher matcher = responsePattern.matcher(urlString);
  295. if (matcher.find()) {
  296. nofRedirects = Integer
  297. .parseUnsignedInt(matcher.group(1));
  298. responseCode = Integer.parseUnsignedInt(matcher.group(2));
  299. if (--nofRedirects <= 0) {
  300. urlString = urlString.substring(0, matcher.start())
  301. + '/' + urlString.substring(matcher.end());
  302. } else {
  303. urlString = urlString.substring(0, matcher.start())
  304. + "/response/" + nofRedirects + "/"
  305. + responseCode + '/'
  306. + urlString.substring(matcher.end());
  307. }
  308. }
  309. httpServletResponse.setStatus(responseCode);
  310. if (nofRedirects <= 0) {
  311. String targetContext = "/git";
  312. matcher = targetPattern.matcher(urlString);
  313. if (matcher.find()) {
  314. urlString = urlString.substring(0, matcher.start())
  315. + '/' + urlString.substring(matcher.end());
  316. targetContext = matcher.group(1);
  317. }
  318. urlString = urlString.replace("/redirect", targetContext);
  319. }
  320. httpServletResponse.setHeader(HttpSupport.HDR_LOCATION,
  321. local(urlString, localRedirect));
  322. }
  323. @Override
  324. public void destroy() {
  325. // empty
  326. }
  327. }), "/*", EnumSet.of(DispatcherType.REQUEST));
  328. redirect.addServlet(new ServletHolder(gs), "/*");
  329. return redirect;
  330. }
  331. @Test
  332. public void testListRemote() throws IOException {
  333. assertEquals("http", remoteURI.getScheme());
  334. Map<String, Ref> map;
  335. try (Repository dst = createBareRepository();
  336. Transport t = Transport.open(dst, remoteURI)) {
  337. // I didn't make up these public interface names, I just
  338. // approved them for inclusion into the code base. Sorry.
  339. // --spearce
  340. //
  341. assertTrue("isa TransportHttp", t instanceof TransportHttp);
  342. assertTrue("isa HttpTransport", t instanceof HttpTransport);
  343. try (FetchConnection c = t.openFetch()) {
  344. map = c.getRefsMap();
  345. }
  346. }
  347. assertNotNull("have map of refs", map);
  348. assertEquals(3, map.size());
  349. assertNotNull("has " + master, map.get(master));
  350. assertEquals(B, map.get(master).getObjectId());
  351. assertNotNull("has " + Constants.HEAD, map.get(Constants.HEAD));
  352. assertEquals(B, map.get(Constants.HEAD).getObjectId());
  353. List<AccessEvent> requests = getRequests();
  354. assertEquals(1, requests.size());
  355. AccessEvent info = requests.get(0);
  356. assertEquals("GET", info.getMethod());
  357. assertEquals(join(remoteURI, "info/refs"), info.getPath());
  358. assertEquals(1, info.getParameters().size());
  359. assertEquals("git-upload-pack", info.getParameter("service"));
  360. assertEquals(200, info.getStatus());
  361. assertEquals("application/x-git-upload-pack-advertisement", info
  362. .getResponseHeader(HDR_CONTENT_TYPE));
  363. assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
  364. }
  365. @Test
  366. public void testListRemote_BadName() throws IOException, URISyntaxException {
  367. URIish uri = new URIish(this.remoteURI.toString() + ".invalid");
  368. try (Repository dst = createBareRepository();
  369. Transport t = Transport.open(dst, uri)) {
  370. try {
  371. t.openFetch();
  372. fail("fetch connection opened");
  373. } catch (RemoteRepositoryException notFound) {
  374. assertEquals(uri + ": Git repository not found",
  375. notFound.getMessage());
  376. }
  377. }
  378. List<AccessEvent> requests = getRequests();
  379. assertEquals(1, requests.size());
  380. AccessEvent info = requests.get(0);
  381. assertEquals("GET", info.getMethod());
  382. assertEquals(join(uri, "info/refs"), info.getPath());
  383. assertEquals(1, info.getParameters().size());
  384. assertEquals("git-upload-pack", info.getParameter("service"));
  385. assertEquals(200, info.getStatus());
  386. assertEquals("application/x-git-upload-pack-advertisement",
  387. info.getResponseHeader(HDR_CONTENT_TYPE));
  388. }
  389. @Test
  390. public void testFetchBySHA1() throws Exception {
  391. try (Repository dst = createBareRepository();
  392. Transport t = Transport.open(dst, remoteURI)) {
  393. assertFalse(dst.getObjectDatabase().has(A_txt));
  394. t.fetch(NullProgressMonitor.INSTANCE,
  395. Collections.singletonList(new RefSpec(B.name())));
  396. assertTrue(dst.getObjectDatabase().has(A_txt));
  397. }
  398. }
  399. @Test
  400. public void testFetchBySHA1Unreachable() throws Exception {
  401. try (Repository dst = createBareRepository();
  402. Transport t = Transport.open(dst, remoteURI)) {
  403. assertFalse(dst.getObjectDatabase().has(A_txt));
  404. thrown.expect(TransportException.class);
  405. thrown.expectMessage(Matchers.containsString(
  406. "want " + unreachableCommit.name() + " not valid"));
  407. t.fetch(NullProgressMonitor.INSTANCE, Collections
  408. .singletonList(new RefSpec(unreachableCommit.name())));
  409. }
  410. }
  411. @Test
  412. public void testFetchBySHA1UnreachableByAdvertiseRefsHook()
  413. throws Exception {
  414. advertiseRefsHook = new AbstractAdvertiseRefsHook() {
  415. @Override
  416. protected Map<String, Ref> getAdvertisedRefs(Repository repository,
  417. RevWalk revWalk) {
  418. return Collections.emptyMap();
  419. }
  420. };
  421. try (Repository dst = createBareRepository();
  422. Transport t = Transport.open(dst, remoteURI)) {
  423. assertFalse(dst.getObjectDatabase().has(A_txt));
  424. thrown.expect(TransportException.class);
  425. thrown.expectMessage(Matchers.containsString(
  426. "want " + A.name() + " not valid"));
  427. t.fetch(NullProgressMonitor.INSTANCE, Collections
  428. .singletonList(new RefSpec(A.name())));
  429. }
  430. }
  431. @Test
  432. public void testInitialClone_Small() throws Exception {
  433. try (Repository dst = createBareRepository();
  434. Transport t = Transport.open(dst, remoteURI)) {
  435. assertFalse(dst.getObjectDatabase().has(A_txt));
  436. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  437. assertTrue(dst.getObjectDatabase().has(A_txt));
  438. assertEquals(B, dst.exactRef(master).getObjectId());
  439. fsck(dst, B);
  440. }
  441. List<AccessEvent> requests = getRequests();
  442. assertEquals(2, requests.size());
  443. AccessEvent info = requests.get(0);
  444. assertEquals("GET", info.getMethod());
  445. assertEquals(join(remoteURI, "info/refs"), info.getPath());
  446. assertEquals(1, info.getParameters().size());
  447. assertEquals("git-upload-pack", info.getParameter("service"));
  448. assertEquals(200, info.getStatus());
  449. assertEquals("application/x-git-upload-pack-advertisement", info
  450. .getResponseHeader(HDR_CONTENT_TYPE));
  451. assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
  452. AccessEvent service = requests.get(1);
  453. assertEquals("POST", service.getMethod());
  454. assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
  455. assertEquals(0, service.getParameters().size());
  456. assertNotNull("has content-length", service
  457. .getRequestHeader(HDR_CONTENT_LENGTH));
  458. assertNull("not chunked", service
  459. .getRequestHeader(HDR_TRANSFER_ENCODING));
  460. assertEquals(200, service.getStatus());
  461. assertEquals("application/x-git-upload-pack-result", service
  462. .getResponseHeader(HDR_CONTENT_TYPE));
  463. }
  464. private void initialClone_Redirect(int nofRedirects, int code)
  465. throws Exception {
  466. initialClone_Redirect(nofRedirects, code, false);
  467. }
  468. private void initialClone_Redirect(int nofRedirects, int code,
  469. boolean localRedirect) throws Exception {
  470. URIish cloneFrom = redirectURI;
  471. if (localRedirect) {
  472. cloneFrom = extendPath(cloneFrom, "/local");
  473. }
  474. if (code != 301 || nofRedirects > 1) {
  475. cloneFrom = extendPath(cloneFrom,
  476. "/response/" + nofRedirects + "/" + code);
  477. }
  478. try (Repository dst = createBareRepository();
  479. Transport t = Transport.open(dst, cloneFrom)) {
  480. assertFalse(dst.getObjectDatabase().has(A_txt));
  481. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  482. assertTrue(dst.getObjectDatabase().has(A_txt));
  483. assertEquals(B, dst.exactRef(master).getObjectId());
  484. fsck(dst, B);
  485. }
  486. List<AccessEvent> requests = getRequests();
  487. assertEquals(2 + nofRedirects, requests.size());
  488. int n = 0;
  489. while (n < nofRedirects) {
  490. AccessEvent redirect = requests.get(n++);
  491. assertEquals(code, redirect.getStatus());
  492. }
  493. AccessEvent info = requests.get(n++);
  494. assertEquals("GET", info.getMethod());
  495. assertEquals(join(remoteURI, "info/refs"), info.getPath());
  496. assertEquals(1, info.getParameters().size());
  497. assertEquals("git-upload-pack", info.getParameter("service"));
  498. assertEquals(200, info.getStatus());
  499. assertEquals("application/x-git-upload-pack-advertisement",
  500. info.getResponseHeader(HDR_CONTENT_TYPE));
  501. assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
  502. AccessEvent service = requests.get(n++);
  503. assertEquals("POST", service.getMethod());
  504. assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
  505. assertEquals(0, service.getParameters().size());
  506. assertNotNull("has content-length",
  507. service.getRequestHeader(HDR_CONTENT_LENGTH));
  508. assertNull("not chunked",
  509. service.getRequestHeader(HDR_TRANSFER_ENCODING));
  510. assertEquals(200, service.getStatus());
  511. assertEquals("application/x-git-upload-pack-result",
  512. service.getResponseHeader(HDR_CONTENT_TYPE));
  513. }
  514. @Test
  515. public void testInitialClone_Redirect301Small() throws Exception {
  516. initialClone_Redirect(1, 301);
  517. }
  518. @Test
  519. public void testInitialClone_Redirect301Local() throws Exception {
  520. initialClone_Redirect(1, 301, true);
  521. }
  522. @Test
  523. public void testInitialClone_Redirect302Small() throws Exception {
  524. initialClone_Redirect(1, 302);
  525. }
  526. @Test
  527. public void testInitialClone_Redirect303Small() throws Exception {
  528. initialClone_Redirect(1, 303);
  529. }
  530. @Test
  531. public void testInitialClone_Redirect307Small() throws Exception {
  532. initialClone_Redirect(1, 307);
  533. }
  534. @Test
  535. public void testInitialClone_RedirectMultiple() throws Exception {
  536. initialClone_Redirect(4, 302);
  537. }
  538. @Test
  539. public void testInitialClone_RedirectMax() throws Exception {
  540. StoredConfig userConfig = SystemReader.getInstance()
  541. .getUserConfig();
  542. userConfig.setInt("http", null, "maxRedirects", 4);
  543. userConfig.save();
  544. initialClone_Redirect(4, 302);
  545. }
  546. @Test
  547. public void testInitialClone_RedirectTooOften() throws Exception {
  548. StoredConfig userConfig = SystemReader.getInstance()
  549. .getUserConfig();
  550. userConfig.setInt("http", null, "maxRedirects", 3);
  551. userConfig.save();
  552. URIish cloneFrom = extendPath(redirectURI, "/response/4/302");
  553. String remoteUri = cloneFrom.toString();
  554. try (Repository dst = createBareRepository();
  555. Transport t = Transport.open(dst, cloneFrom)) {
  556. assertFalse(dst.getObjectDatabase().has(A_txt));
  557. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  558. fail("Should have failed (too many redirects)");
  559. } catch (TransportException e) {
  560. String expectedMessageBegin = remoteUri.toString() + ": "
  561. + MessageFormat.format(JGitText.get().redirectLimitExceeded,
  562. "3", remoteUri.replace("/4/", "/1/") + '/', "");
  563. String message = e.getMessage();
  564. if (message.length() > expectedMessageBegin.length()) {
  565. message = message.substring(0, expectedMessageBegin.length());
  566. }
  567. assertEquals(expectedMessageBegin, message);
  568. }
  569. }
  570. @Test
  571. public void testInitialClone_RedirectLoop() throws Exception {
  572. URIish cloneFrom = extendPath(redirectURI, "/loop");
  573. try (Repository dst = createBareRepository();
  574. Transport t = Transport.open(dst, cloneFrom)) {
  575. assertFalse(dst.getObjectDatabase().has(A_txt));
  576. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  577. fail("Should have failed (redirect loop)");
  578. } catch (TransportException e) {
  579. assertTrue(e.getMessage().contains("Redirected more than"));
  580. }
  581. }
  582. @Test
  583. public void testInitialClone_RedirectOnPostAllowed() throws Exception {
  584. StoredConfig userConfig = SystemReader.getInstance()
  585. .getUserConfig();
  586. userConfig.setString("http", null, "followRedirects", "true");
  587. userConfig.save();
  588. URIish cloneFrom = extendPath(remoteURI, "/post");
  589. try (Repository dst = createBareRepository();
  590. Transport t = Transport.open(dst, cloneFrom)) {
  591. assertFalse(dst.getObjectDatabase().has(A_txt));
  592. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  593. assertTrue(dst.getObjectDatabase().has(A_txt));
  594. assertEquals(B, dst.exactRef(master).getObjectId());
  595. fsck(dst, B);
  596. }
  597. List<AccessEvent> requests = getRequests();
  598. assertEquals(3, requests.size());
  599. AccessEvent info = requests.get(0);
  600. assertEquals("GET", info.getMethod());
  601. assertEquals(join(cloneFrom, "info/refs"), info.getPath());
  602. assertEquals(1, info.getParameters().size());
  603. assertEquals("git-upload-pack", info.getParameter("service"));
  604. assertEquals(200, info.getStatus());
  605. assertEquals("application/x-git-upload-pack-advertisement",
  606. info.getResponseHeader(HDR_CONTENT_TYPE));
  607. assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
  608. AccessEvent redirect = requests.get(1);
  609. assertEquals("POST", redirect.getMethod());
  610. assertEquals(301, redirect.getStatus());
  611. AccessEvent service = requests.get(2);
  612. assertEquals("POST", service.getMethod());
  613. assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
  614. assertEquals(0, service.getParameters().size());
  615. assertNotNull("has content-length",
  616. service.getRequestHeader(HDR_CONTENT_LENGTH));
  617. assertNull("not chunked",
  618. service.getRequestHeader(HDR_TRANSFER_ENCODING));
  619. assertEquals(200, service.getStatus());
  620. assertEquals("application/x-git-upload-pack-result",
  621. service.getResponseHeader(HDR_CONTENT_TYPE));
  622. }
  623. @Test
  624. public void testInitialClone_RedirectOnPostForbidden() throws Exception {
  625. URIish cloneFrom = extendPath(remoteURI, "/post");
  626. try (Repository dst = createBareRepository();
  627. Transport t = Transport.open(dst, cloneFrom)) {
  628. assertFalse(dst.getObjectDatabase().has(A_txt));
  629. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  630. fail("Should have failed (redirect on POST)");
  631. } catch (TransportException e) {
  632. assertTrue(e.getMessage().contains("301"));
  633. }
  634. }
  635. @Test
  636. public void testInitialClone_RedirectForbidden() throws Exception {
  637. StoredConfig userConfig = SystemReader.getInstance()
  638. .getUserConfig();
  639. userConfig.setString("http", null, "followRedirects", "false");
  640. userConfig.save();
  641. try (Repository dst = createBareRepository();
  642. Transport t = Transport.open(dst, redirectURI)) {
  643. assertFalse(dst.getObjectDatabase().has(A_txt));
  644. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  645. fail("Should have failed (redirects forbidden)");
  646. } catch (TransportException e) {
  647. assertTrue(
  648. e.getMessage().contains("http.followRedirects is false"));
  649. }
  650. }
  651. @Test
  652. public void testInitialClone_WithAuthentication() throws Exception {
  653. try (Repository dst = createBareRepository();
  654. Transport t = Transport.open(dst, authURI)) {
  655. assertFalse(dst.getObjectDatabase().has(A_txt));
  656. t.setCredentialsProvider(testCredentials);
  657. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  658. assertTrue(dst.getObjectDatabase().has(A_txt));
  659. assertEquals(B, dst.exactRef(master).getObjectId());
  660. fsck(dst, B);
  661. }
  662. List<AccessEvent> requests = getRequests();
  663. assertEquals(3, requests.size());
  664. AccessEvent info = requests.get(0);
  665. assertEquals("GET", info.getMethod());
  666. assertEquals(401, info.getStatus());
  667. info = requests.get(1);
  668. assertEquals("GET", info.getMethod());
  669. assertEquals(join(authURI, "info/refs"), info.getPath());
  670. assertEquals(1, info.getParameters().size());
  671. assertEquals("git-upload-pack", info.getParameter("service"));
  672. assertEquals(200, info.getStatus());
  673. assertEquals("application/x-git-upload-pack-advertisement",
  674. info.getResponseHeader(HDR_CONTENT_TYPE));
  675. assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
  676. AccessEvent service = requests.get(2);
  677. assertEquals("POST", service.getMethod());
  678. assertEquals(join(authURI, "git-upload-pack"), service.getPath());
  679. assertEquals(0, service.getParameters().size());
  680. assertNotNull("has content-length",
  681. service.getRequestHeader(HDR_CONTENT_LENGTH));
  682. assertNull("not chunked",
  683. service.getRequestHeader(HDR_TRANSFER_ENCODING));
  684. assertEquals(200, service.getStatus());
  685. assertEquals("application/x-git-upload-pack-result",
  686. service.getResponseHeader(HDR_CONTENT_TYPE));
  687. }
  688. @Test
  689. public void testInitialClone_WithAuthenticationNoCredentials()
  690. throws Exception {
  691. try (Repository dst = createBareRepository();
  692. Transport t = Transport.open(dst, authURI)) {
  693. assertFalse(dst.getObjectDatabase().has(A_txt));
  694. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  695. fail("Should not have succeeded -- no authentication");
  696. } catch (TransportException e) {
  697. String msg = e.getMessage();
  698. assertTrue("Unexpected exception message: " + msg,
  699. msg.contains("no CredentialsProvider"));
  700. }
  701. List<AccessEvent> requests = getRequests();
  702. assertEquals(1, requests.size());
  703. AccessEvent info = requests.get(0);
  704. assertEquals("GET", info.getMethod());
  705. assertEquals(401, info.getStatus());
  706. }
  707. @Test
  708. public void testInitialClone_WithAuthenticationWrongCredentials()
  709. throws Exception {
  710. try (Repository dst = createBareRepository();
  711. Transport t = Transport.open(dst, authURI)) {
  712. assertFalse(dst.getObjectDatabase().has(A_txt));
  713. t.setCredentialsProvider(new UsernamePasswordCredentialsProvider(
  714. AppServer.username, "wrongpassword"));
  715. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  716. fail("Should not have succeeded -- wrong password");
  717. } catch (TransportException e) {
  718. String msg = e.getMessage();
  719. assertTrue("Unexpected exception message: " + msg,
  720. msg.contains("auth"));
  721. }
  722. List<AccessEvent> requests = getRequests();
  723. // Once without authentication plus three re-tries with authentication
  724. assertEquals(4, requests.size());
  725. for (AccessEvent event : requests) {
  726. assertEquals("GET", event.getMethod());
  727. assertEquals(401, event.getStatus());
  728. }
  729. }
  730. @Test
  731. public void testInitialClone_WithAuthenticationAfterRedirect()
  732. throws Exception {
  733. URIish cloneFrom = extendPath(redirectURI, "/target/auth");
  734. CredentialsProvider uriSpecificCredentialsProvider = new UsernamePasswordCredentialsProvider(
  735. "unknown", "none") {
  736. @Override
  737. public boolean get(URIish uri, CredentialItem... items)
  738. throws UnsupportedCredentialItem {
  739. // Only return the true credentials if the uri path starts with
  740. // /auth. This ensures that we do provide the correct
  741. // credentials only for the URi after the redirect, making the
  742. // test fail if we should be asked for the credentials for the
  743. // original URI.
  744. if (uri.getPath().startsWith("/auth")) {
  745. return testCredentials.get(uri, items);
  746. }
  747. return super.get(uri, items);
  748. }
  749. };
  750. try (Repository dst = createBareRepository();
  751. Transport t = Transport.open(dst, cloneFrom)) {
  752. assertFalse(dst.getObjectDatabase().has(A_txt));
  753. t.setCredentialsProvider(uriSpecificCredentialsProvider);
  754. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  755. assertTrue(dst.getObjectDatabase().has(A_txt));
  756. assertEquals(B, dst.exactRef(master).getObjectId());
  757. fsck(dst, B);
  758. }
  759. List<AccessEvent> requests = getRequests();
  760. assertEquals(4, requests.size());
  761. AccessEvent redirect = requests.get(0);
  762. assertEquals("GET", redirect.getMethod());
  763. assertEquals(join(cloneFrom, "info/refs"), redirect.getPath());
  764. assertEquals(301, redirect.getStatus());
  765. AccessEvent info = requests.get(1);
  766. assertEquals("GET", info.getMethod());
  767. assertEquals(join(authURI, "info/refs"), info.getPath());
  768. assertEquals(401, info.getStatus());
  769. info = requests.get(2);
  770. assertEquals("GET", info.getMethod());
  771. assertEquals(join(authURI, "info/refs"), info.getPath());
  772. assertEquals(1, info.getParameters().size());
  773. assertEquals("git-upload-pack", info.getParameter("service"));
  774. assertEquals(200, info.getStatus());
  775. assertEquals("application/x-git-upload-pack-advertisement",
  776. info.getResponseHeader(HDR_CONTENT_TYPE));
  777. assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
  778. AccessEvent service = requests.get(3);
  779. assertEquals("POST", service.getMethod());
  780. assertEquals(join(authURI, "git-upload-pack"), service.getPath());
  781. assertEquals(0, service.getParameters().size());
  782. assertNotNull("has content-length",
  783. service.getRequestHeader(HDR_CONTENT_LENGTH));
  784. assertNull("not chunked",
  785. service.getRequestHeader(HDR_TRANSFER_ENCODING));
  786. assertEquals(200, service.getStatus());
  787. assertEquals("application/x-git-upload-pack-result",
  788. service.getResponseHeader(HDR_CONTENT_TYPE));
  789. }
  790. @Test
  791. public void testInitialClone_WithAuthenticationOnPostOnly()
  792. throws Exception {
  793. try (Repository dst = createBareRepository();
  794. Transport t = Transport.open(dst, authOnPostURI)) {
  795. assertFalse(dst.getObjectDatabase().has(A_txt));
  796. t.setCredentialsProvider(testCredentials);
  797. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  798. assertTrue(dst.getObjectDatabase().has(A_txt));
  799. assertEquals(B, dst.exactRef(master).getObjectId());
  800. fsck(dst, B);
  801. }
  802. List<AccessEvent> requests = getRequests();
  803. assertEquals(3, requests.size());
  804. AccessEvent info = requests.get(0);
  805. assertEquals("GET", info.getMethod());
  806. assertEquals(join(authOnPostURI, "info/refs"), info.getPath());
  807. assertEquals(1, info.getParameters().size());
  808. assertEquals("git-upload-pack", info.getParameter("service"));
  809. assertEquals(200, info.getStatus());
  810. assertEquals("application/x-git-upload-pack-advertisement",
  811. info.getResponseHeader(HDR_CONTENT_TYPE));
  812. assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
  813. AccessEvent service = requests.get(1);
  814. assertEquals("POST", service.getMethod());
  815. assertEquals(join(authOnPostURI, "git-upload-pack"), service.getPath());
  816. assertEquals(401, service.getStatus());
  817. service = requests.get(2);
  818. assertEquals("POST", service.getMethod());
  819. assertEquals(join(authOnPostURI, "git-upload-pack"), service.getPath());
  820. assertEquals(0, service.getParameters().size());
  821. assertNotNull("has content-length",
  822. service.getRequestHeader(HDR_CONTENT_LENGTH));
  823. assertNull("not chunked",
  824. service.getRequestHeader(HDR_TRANSFER_ENCODING));
  825. assertEquals(200, service.getStatus());
  826. assertEquals("application/x-git-upload-pack-result",
  827. service.getResponseHeader(HDR_CONTENT_TYPE));
  828. }
  829. @Test
  830. public void testFetch_FewLocalCommits() throws Exception {
  831. // Bootstrap by doing the clone.
  832. //
  833. TestRepository dst = createTestRepository();
  834. try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
  835. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  836. }
  837. assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
  838. List<AccessEvent> cloneRequests = getRequests();
  839. // Only create a few new commits.
  840. TestRepository.BranchBuilder b = dst.branch(master);
  841. for (int i = 0; i < 4; i++)
  842. b.commit().tick(3600 /* 1 hour */).message("c" + i).create();
  843. // Create a new commit on the remote.
  844. //
  845. RevCommit Z;
  846. try (TestRepository<Repository> tr = new TestRepository<>(
  847. remoteRepository)) {
  848. b = tr.branch(master);
  849. Z = b.commit().message("Z").create();
  850. }
  851. // Now incrementally update.
  852. //
  853. try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
  854. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  855. }
  856. assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
  857. List<AccessEvent> requests = getRequests();
  858. requests.removeAll(cloneRequests);
  859. assertEquals(2, requests.size());
  860. AccessEvent info = requests.get(0);
  861. assertEquals("GET", info.getMethod());
  862. assertEquals(join(remoteURI, "info/refs"), info.getPath());
  863. assertEquals(1, info.getParameters().size());
  864. assertEquals("git-upload-pack", info.getParameter("service"));
  865. assertEquals(200, info.getStatus());
  866. assertEquals("application/x-git-upload-pack-advertisement",
  867. info.getResponseHeader(HDR_CONTENT_TYPE));
  868. // We should have needed one request to perform the fetch.
  869. //
  870. AccessEvent service = requests.get(1);
  871. assertEquals("POST", service.getMethod());
  872. assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
  873. assertEquals(0, service.getParameters().size());
  874. assertNotNull("has content-length",
  875. service.getRequestHeader(HDR_CONTENT_LENGTH));
  876. assertNull("not chunked",
  877. service.getRequestHeader(HDR_TRANSFER_ENCODING));
  878. assertEquals(200, service.getStatus());
  879. assertEquals("application/x-git-upload-pack-result",
  880. service.getResponseHeader(HDR_CONTENT_TYPE));
  881. }
  882. @Test
  883. public void testFetch_TooManyLocalCommits() throws Exception {
  884. // Bootstrap by doing the clone.
  885. //
  886. TestRepository dst = createTestRepository();
  887. try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
  888. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  889. }
  890. assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
  891. List<AccessEvent> cloneRequests = getRequests();
  892. // Force enough into the local client that enumeration will
  893. // need multiple packets, but not too many to overflow and
  894. // not pick up the ACK_COMMON message.
  895. //
  896. TestRepository.BranchBuilder b = dst.branch(master);
  897. for (int i = 0; i < 32 - 1; i++)
  898. b.commit().tick(3600 /* 1 hour */).message("c" + i).create();
  899. // Create a new commit on the remote.
  900. //
  901. RevCommit Z;
  902. try (TestRepository<Repository> tr = new TestRepository<>(
  903. remoteRepository)) {
  904. b = tr.branch(master);
  905. Z = b.commit().message("Z").create();
  906. }
  907. // Now incrementally update.
  908. //
  909. try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
  910. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  911. }
  912. assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
  913. List<AccessEvent> requests = getRequests();
  914. requests.removeAll(cloneRequests);
  915. assertEquals(3, requests.size());
  916. AccessEvent info = requests.get(0);
  917. assertEquals("GET", info.getMethod());
  918. assertEquals(join(remoteURI, "info/refs"), info.getPath());
  919. assertEquals(1, info.getParameters().size());
  920. assertEquals("git-upload-pack", info.getParameter("service"));
  921. assertEquals(200, info.getStatus());
  922. assertEquals("application/x-git-upload-pack-advertisement", info
  923. .getResponseHeader(HDR_CONTENT_TYPE));
  924. // We should have needed two requests to perform the fetch
  925. // due to the high number of local unknown commits.
  926. //
  927. AccessEvent service = requests.get(1);
  928. assertEquals("POST", service.getMethod());
  929. assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
  930. assertEquals(0, service.getParameters().size());
  931. assertNotNull("has content-length", service
  932. .getRequestHeader(HDR_CONTENT_LENGTH));
  933. assertNull("not chunked", service
  934. .getRequestHeader(HDR_TRANSFER_ENCODING));
  935. assertEquals(200, service.getStatus());
  936. assertEquals("application/x-git-upload-pack-result", service
  937. .getResponseHeader(HDR_CONTENT_TYPE));
  938. service = requests.get(2);
  939. assertEquals("POST", service.getMethod());
  940. assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
  941. assertEquals(0, service.getParameters().size());
  942. assertNotNull("has content-length", service
  943. .getRequestHeader(HDR_CONTENT_LENGTH));
  944. assertNull("not chunked", service
  945. .getRequestHeader(HDR_TRANSFER_ENCODING));
  946. assertEquals(200, service.getStatus());
  947. assertEquals("application/x-git-upload-pack-result", service
  948. .getResponseHeader(HDR_CONTENT_TYPE));
  949. }
  950. @Test
  951. public void testInitialClone_BrokenServer() throws Exception {
  952. try (Repository dst = createBareRepository();
  953. Transport t = Transport.open(dst, brokenURI)) {
  954. assertFalse(dst.getObjectDatabase().has(A_txt));
  955. try {
  956. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  957. fail("fetch completed despite upload-pack being broken");
  958. } catch (TransportException err) {
  959. String exp = brokenURI + ": expected"
  960. + " Content-Type application/x-git-upload-pack-result;"
  961. + " received Content-Type text/plain;charset=utf-8";
  962. assertEquals(exp, err.getMessage());
  963. }
  964. }
  965. List<AccessEvent> requests = getRequests();
  966. assertEquals(2, requests.size());
  967. AccessEvent info = requests.get(0);
  968. assertEquals("GET", info.getMethod());
  969. assertEquals(join(brokenURI, "info/refs"), info.getPath());
  970. assertEquals(1, info.getParameters().size());
  971. assertEquals("git-upload-pack", info.getParameter("service"));
  972. assertEquals(200, info.getStatus());
  973. assertEquals("application/x-git-upload-pack-advertisement", info
  974. .getResponseHeader(HDR_CONTENT_TYPE));
  975. AccessEvent service = requests.get(1);
  976. assertEquals("POST", service.getMethod());
  977. assertEquals(join(brokenURI, "git-upload-pack"), service.getPath());
  978. assertEquals(0, service.getParameters().size());
  979. assertEquals(200, service.getStatus());
  980. assertEquals("text/plain;charset=utf-8",
  981. service.getResponseHeader(HDR_CONTENT_TYPE));
  982. }
  983. @Test
  984. public void testInvalidWant() throws Exception {
  985. ObjectId id;
  986. try (ObjectInserter.Formatter formatter = new ObjectInserter.Formatter()) {
  987. id = formatter.idFor(Constants.OBJ_BLOB,
  988. "testInvalidWant".getBytes(UTF_8));
  989. }
  990. try (Repository dst = createBareRepository();
  991. Transport t = Transport.open(dst, remoteURI);
  992. FetchConnection c = t.openFetch()) {
  993. Ref want = new ObjectIdRef.Unpeeled(Ref.Storage.NETWORK, id.name(),
  994. id);
  995. c.fetch(NullProgressMonitor.INSTANCE, Collections.singleton(want),
  996. Collections.<ObjectId> emptySet());
  997. fail("Server accepted want " + id.name());
  998. } catch (TransportException err) {
  999. assertEquals("want " + id.name() + " not valid", err.getMessage());
  1000. }
  1001. }
  1002. @Test
  1003. public void testFetch_RefsUnreadableOnUpload() throws Exception {
  1004. AppServer noRefServer = new AppServer();
  1005. try {
  1006. final String repoName = "refs-unreadable";
  1007. RefsUnreadableInMemoryRepository badRefsRepo = new RefsUnreadableInMemoryRepository(
  1008. new DfsRepositoryDescription(repoName));
  1009. final TestRepository<Repository> repo = new TestRepository<>(
  1010. badRefsRepo);
  1011. ServletContextHandler app = noRefServer.addContext("/git");
  1012. GitServlet gs = new GitServlet();
  1013. gs.setRepositoryResolver(new TestRepositoryResolver(repo, repoName));
  1014. app.addServlet(new ServletHolder(gs), "/*");
  1015. noRefServer.setUp();
  1016. RevBlob A2_txt = repo.blob("A2");
  1017. RevCommit A2 = repo.commit().add("A2_txt", A2_txt).create();
  1018. RevCommit B2 = repo.commit().parent(A2).add("A2_txt", "C2")
  1019. .add("B2", "B2").create();
  1020. repo.update(master, B2);
  1021. URIish badRefsURI = new URIish(noRefServer.getURI()
  1022. .resolve(app.getContextPath() + "/" + repoName).toString());
  1023. try (Repository dst = createBareRepository();
  1024. Transport t = Transport.open(dst, badRefsURI);
  1025. FetchConnection c = t.openFetch()) {
  1026. // We start failing here to exercise the post-advertisement
  1027. // upload pack handler.
  1028. badRefsRepo.startFailing();
  1029. // Need to flush caches because ref advertisement populated them.
  1030. badRefsRepo.getRefDatabase().refresh();
  1031. c.fetch(NullProgressMonitor.INSTANCE,
  1032. Collections.singleton(c.getRef(master)),
  1033. Collections.<ObjectId> emptySet());
  1034. fail("Successfully served ref with value " + c.getRef(master));
  1035. } catch (TransportException err) {
  1036. assertEquals("Internal server error", err.getMessage());
  1037. }
  1038. } finally {
  1039. noRefServer.tearDown();
  1040. }
  1041. }
  1042. @Test
  1043. public void testPush_NotAuthorized() throws Exception {
  1044. final TestRepository src = createTestRepository();
  1045. final RevBlob Q_txt = src.blob("new text");
  1046. final RevCommit Q = src.commit().add("Q", Q_txt).create();
  1047. final Repository db = src.getRepository();
  1048. final String dstName = Constants.R_HEADS + "new.branch";
  1049. // push anonymous shouldn't be allowed.
  1050. //
  1051. try (Transport t = Transport.open(db, remoteURI)) {
  1052. final String srcExpr = Q.name();
  1053. final boolean forceUpdate = false;
  1054. final String localName = null;
  1055. final ObjectId oldId = null;
  1056. RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
  1057. srcExpr, dstName, forceUpdate, localName, oldId);
  1058. try {
  1059. t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
  1060. fail("anonymous push incorrectly accepted without error");
  1061. } catch (TransportException e) {
  1062. final String exp = remoteURI + ": "
  1063. + JGitText.get().authenticationNotSupported;
  1064. assertEquals(exp, e.getMessage());
  1065. }
  1066. }
  1067. List<AccessEvent> requests = getRequests();
  1068. assertEquals(1, requests.size());
  1069. AccessEvent info = requests.get(0);
  1070. assertEquals("GET", info.getMethod());
  1071. assertEquals(join(remoteURI, "info/refs"), info.getPath());
  1072. assertEquals(1, info.getParameters().size());
  1073. assertEquals("git-receive-pack", info.getParameter("service"));
  1074. assertEquals(401, info.getStatus());
  1075. }
  1076. @Test
  1077. public void testPush_CreateBranch() throws Exception {
  1078. final TestRepository src = createTestRepository();
  1079. final RevBlob Q_txt = src.blob("new text");
  1080. final RevCommit Q = src.commit().add("Q", Q_txt).create();
  1081. final Repository db = src.getRepository();
  1082. final String dstName = Constants.R_HEADS + "new.branch";
  1083. enableReceivePack();
  1084. try (Transport t = Transport.open(db, remoteURI)) {
  1085. final String srcExpr = Q.name();
  1086. final boolean forceUpdate = false;
  1087. final String localName = null;
  1088. final ObjectId oldId = null;
  1089. RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
  1090. srcExpr, dstName, forceUpdate, localName, oldId);
  1091. t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
  1092. }
  1093. assertTrue(remoteRepository.getObjectDatabase().has(Q_txt));
  1094. assertNotNull("has " + dstName, remoteRepository.exactRef(dstName));
  1095. assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
  1096. fsck(remoteRepository, Q);
  1097. final ReflogReader log = remoteRepository.getReflogReader(dstName);
  1098. assertNotNull("has log for " + dstName, log);
  1099. final ReflogEntry last = log.getLastEntry();
  1100. assertNotNull("has last entry", last);
  1101. assertEquals(ObjectId.zeroId(), last.getOldId());
  1102. assertEquals(Q, last.getNewId());
  1103. assertEquals("anonymous", last.getWho().getName());
  1104. // Assumption: The host name we use to contact the server should
  1105. // be the server's own host name, because it should be the loopback
  1106. // network interface.
  1107. //
  1108. final String clientHost = remoteURI.getHost();
  1109. assertEquals("anonymous@" + clientHost, last.getWho().getEmailAddress());
  1110. assertEquals("push: created", last.getComment());
  1111. List<AccessEvent> requests = getRequests();
  1112. assertEquals(2, requests.size());
  1113. AccessEvent info = requests.get(0);
  1114. assertEquals("GET", info.getMethod());
  1115. assertEquals(join(remoteURI, "info/refs"), info.getPath());
  1116. assertEquals(1, info.getParameters().size());
  1117. assertEquals("git-receive-pack", info.getParameter("service"));
  1118. assertEquals(200, info.getStatus());
  1119. assertEquals("application/x-git-receive-pack-advertisement", info
  1120. .getResponseHeader(HDR_CONTENT_TYPE));
  1121. AccessEvent service = requests.get(1);
  1122. assertEquals("POST", service.getMethod());
  1123. assertEquals(join(remoteURI, "git-receive-pack"), service.getPath());
  1124. assertEquals(0, service.getParameters().size());
  1125. assertNotNull("has content-length", service
  1126. .getRequestHeader(HDR_CONTENT_LENGTH));
  1127. assertNull("not chunked", service
  1128. .getRequestHeader(HDR_TRANSFER_ENCODING));
  1129. assertEquals(200, service.getStatus());
  1130. assertEquals("application/x-git-receive-pack-result", service
  1131. .getResponseHeader(HDR_CONTENT_TYPE));
  1132. }
  1133. @Test
  1134. public void testPush_ChunkedEncoding() throws Exception {
  1135. final TestRepository<Repository> src = createTestRepository();
  1136. final RevBlob Q_bin = src.blob(new TestRng("Q").nextBytes(128 * 1024));
  1137. final RevCommit Q = src.commit().add("Q", Q_bin).create();
  1138. final Repository db = src.getRepository();
  1139. final String dstName = Constants.R_HEADS + "new.branch";
  1140. enableReceivePack();
  1141. final StoredConfig cfg = db.getConfig();
  1142. cfg.setInt("core", null, "compression", 0);
  1143. cfg.setInt("http", null, "postbuffer", 8 * 1024);
  1144. cfg.save();
  1145. try (Transport t = Transport.open(db, remoteURI)) {
  1146. final String srcExpr = Q.name();
  1147. final boolean forceUpdate = false;
  1148. final String localName = null;
  1149. final ObjectId oldId = null;
  1150. RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
  1151. srcExpr, dstName, forceUpdate, localName, oldId);
  1152. t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
  1153. }
  1154. assertTrue(remoteRepository.getObjectDatabase().has(Q_bin));
  1155. assertNotNull("has " + dstName, remoteRepository.exactRef(dstName));
  1156. assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
  1157. fsck(remoteRepository, Q);
  1158. List<AccessEvent> requests = getRequests();
  1159. assertEquals(2, requests.size());
  1160. AccessEvent info = requests.get(0);
  1161. assertEquals("GET", info.getMethod());
  1162. assertEquals(join(remoteURI, "info/refs"), info.getPath());
  1163. assertEquals(1, info.getParameters().size());
  1164. assertEquals("git-receive-pack", info.getParameter("service"));
  1165. assertEquals(200, info.getStatus());
  1166. assertEquals("application/x-git-receive-pack-advertisement", info
  1167. .getResponseHeader(HDR_CONTENT_TYPE));
  1168. AccessEvent service = requests.get(1);
  1169. assertEquals("POST", service.getMethod());
  1170. assertEquals(join(remoteURI, "git-receive-pack"), service.getPath());
  1171. assertEquals(0, service.getParameters().size());
  1172. assertNull("no content-length", service
  1173. .getRequestHeader(HDR_CONTENT_LENGTH));
  1174. assertEquals("chunked", service.getRequestHeader(HDR_TRANSFER_ENCODING));
  1175. assertEquals(200, service.getStatus());
  1176. assertEquals("application/x-git-receive-pack-result", service
  1177. .getResponseHeader(HDR_CONTENT_TYPE));
  1178. }
  1179. private void enableReceivePack() throws IOException {
  1180. final StoredConfig cfg = remoteRepository.getConfig();
  1181. cfg.setBoolean("http", null, "receivepack", true);
  1182. cfg.save();
  1183. }
  1184. }