Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

SmartClientSmartServerTest.java 50KB

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