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 65KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829
  1. /*
  2. * Copyright (C) 2010, 2020 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.ByteArrayOutputStream;
  23. import java.io.File;
  24. import java.io.IOException;
  25. import java.io.OutputStreamWriter;
  26. import java.io.PrintWriter;
  27. import java.io.Writer;
  28. import java.net.Proxy;
  29. import java.net.URI;
  30. import java.net.URISyntaxException;
  31. import java.net.URL;
  32. import java.nio.charset.StandardCharsets;
  33. import java.text.MessageFormat;
  34. import java.util.Collections;
  35. import java.util.EnumSet;
  36. import java.util.List;
  37. import java.util.Map;
  38. import java.util.regex.Matcher;
  39. import java.util.regex.Pattern;
  40. import javax.servlet.DispatcherType;
  41. import javax.servlet.Filter;
  42. import javax.servlet.FilterChain;
  43. import javax.servlet.FilterConfig;
  44. import javax.servlet.RequestDispatcher;
  45. import javax.servlet.ServletException;
  46. import javax.servlet.ServletRequest;
  47. import javax.servlet.ServletResponse;
  48. import javax.servlet.http.HttpServletRequest;
  49. import javax.servlet.http.HttpServletResponse;
  50. import org.eclipse.jetty.servlet.FilterHolder;
  51. import org.eclipse.jetty.servlet.ServletContextHandler;
  52. import org.eclipse.jetty.servlet.ServletHolder;
  53. import org.eclipse.jgit.api.Git;
  54. import org.eclipse.jgit.api.TransportConfigCallback;
  55. import org.eclipse.jgit.errors.RemoteRepositoryException;
  56. import org.eclipse.jgit.errors.TransportException;
  57. import org.eclipse.jgit.errors.UnsupportedCredentialItem;
  58. import org.eclipse.jgit.http.server.GitServlet;
  59. import org.eclipse.jgit.http.server.resolver.DefaultUploadPackFactory;
  60. import org.eclipse.jgit.internal.JGitText;
  61. import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
  62. import org.eclipse.jgit.junit.TestRepository;
  63. import org.eclipse.jgit.junit.TestRng;
  64. import org.eclipse.jgit.junit.http.AccessEvent;
  65. import org.eclipse.jgit.junit.http.AppServer;
  66. import org.eclipse.jgit.lib.ConfigConstants;
  67. import org.eclipse.jgit.lib.Constants;
  68. import org.eclipse.jgit.lib.NullProgressMonitor;
  69. import org.eclipse.jgit.lib.ObjectId;
  70. import org.eclipse.jgit.lib.ObjectIdRef;
  71. import org.eclipse.jgit.lib.ObjectInserter;
  72. import org.eclipse.jgit.lib.Ref;
  73. import org.eclipse.jgit.lib.ReflogEntry;
  74. import org.eclipse.jgit.lib.ReflogReader;
  75. import org.eclipse.jgit.lib.Repository;
  76. import org.eclipse.jgit.lib.StoredConfig;
  77. import org.eclipse.jgit.lib.TextProgressMonitor;
  78. import org.eclipse.jgit.revwalk.RevBlob;
  79. import org.eclipse.jgit.revwalk.RevCommit;
  80. import org.eclipse.jgit.revwalk.RevWalk;
  81. import org.eclipse.jgit.transport.AbstractAdvertiseRefsHook;
  82. import org.eclipse.jgit.transport.AdvertiseRefsHook;
  83. import org.eclipse.jgit.transport.CredentialItem;
  84. import org.eclipse.jgit.transport.CredentialsProvider;
  85. import org.eclipse.jgit.transport.FetchConnection;
  86. import org.eclipse.jgit.transport.HttpTransport;
  87. import org.eclipse.jgit.transport.RefSpec;
  88. import org.eclipse.jgit.transport.RemoteRefUpdate;
  89. import org.eclipse.jgit.transport.Transport;
  90. import org.eclipse.jgit.transport.TransportHttp;
  91. import org.eclipse.jgit.transport.URIish;
  92. import org.eclipse.jgit.transport.UploadPack;
  93. import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
  94. import org.eclipse.jgit.transport.http.HttpConnection;
  95. import org.eclipse.jgit.transport.http.HttpConnectionFactory;
  96. import org.eclipse.jgit.util.HttpSupport;
  97. import org.eclipse.jgit.util.SystemReader;
  98. import org.junit.Before;
  99. import org.junit.Test;
  100. public class SmartClientSmartServerTest extends AllProtocolsHttpTestCase {
  101. private static final String HDR_TRANSFER_ENCODING = "Transfer-Encoding";
  102. private AdvertiseRefsHook advertiseRefsHook;
  103. private Repository remoteRepository;
  104. private CredentialsProvider testCredentials = new UsernamePasswordCredentialsProvider(
  105. AppServer.username, AppServer.password);
  106. private URIish remoteURI;
  107. private URIish brokenURI;
  108. private URIish redirectURI;
  109. private URIish authURI;
  110. private URIish authOnPostURI;
  111. private URIish slowURI;
  112. private URIish slowAuthURI;
  113. private RevBlob A_txt;
  114. private RevCommit A, B, unreachableCommit;
  115. public SmartClientSmartServerTest(TestParameters params) {
  116. super(params);
  117. }
  118. @Override
  119. @Before
  120. public void setUp() throws Exception {
  121. super.setUp();
  122. final TestRepository<Repository> src = createTestRepository();
  123. final String srcName = src.getRepository().getDirectory().getName();
  124. StoredConfig cfg = src.getRepository().getConfig();
  125. cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
  126. ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
  127. cfg.setInt("protocol", null, "version", enableProtocolV2 ? 2 : 0);
  128. cfg.save();
  129. GitServlet gs = new GitServlet();
  130. gs.setUploadPackFactory((HttpServletRequest req, Repository db) -> {
  131. DefaultUploadPackFactory f = new DefaultUploadPackFactory();
  132. UploadPack up = f.create(req, db);
  133. if (advertiseRefsHook != null) {
  134. up.setAdvertiseRefsHook(advertiseRefsHook);
  135. }
  136. return up;
  137. });
  138. ServletContextHandler app = addNormalContext(gs, src, srcName);
  139. ServletContextHandler broken = addBrokenContext(gs, srcName);
  140. ServletContextHandler redirect = addRedirectContext(gs);
  141. ServletContextHandler auth = addAuthContext(gs, "auth");
  142. ServletContextHandler authOnPost = addAuthContext(gs, "pauth", "POST");
  143. ServletContextHandler slow = addSlowContext(gs, "slow", false);
  144. ServletContextHandler slowAuth = addSlowContext(gs, "slowAuth", true);
  145. server.setUp();
  146. remoteRepository = src.getRepository();
  147. remoteURI = toURIish(app, srcName);
  148. brokenURI = toURIish(broken, srcName);
  149. redirectURI = toURIish(redirect, srcName);
  150. authURI = toURIish(auth, srcName);
  151. authOnPostURI = toURIish(authOnPost, srcName);
  152. slowURI = toURIish(slow, srcName);
  153. slowAuthURI = toURIish(slowAuth, srcName);
  154. A_txt = src.blob("A");
  155. A = src.commit().add("A_txt", A_txt).create();
  156. B = src.commit().parent(A).add("A_txt", "C").add("B", "B").create();
  157. src.update(master, B);
  158. unreachableCommit = src.commit().add("A_txt", A_txt).create();
  159. src.update("refs/garbage/a/very/long/ref/name/to/compress", B);
  160. }
  161. private ServletContextHandler addNormalContext(GitServlet gs, TestRepository<Repository> src, String srcName) {
  162. ServletContextHandler app = server.addContext("/git");
  163. app.addFilter(new FilterHolder(new Filter() {
  164. @Override
  165. public void init(FilterConfig filterConfig)
  166. throws ServletException {
  167. // empty
  168. }
  169. // Does an internal forward for GET requests containing "/post/",
  170. // and issues a 301 redirect on POST requests for such URLs. Used
  171. // in the POST redirect tests.
  172. @Override
  173. public void doFilter(ServletRequest request,
  174. ServletResponse response, FilterChain chain)
  175. throws IOException, ServletException {
  176. final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
  177. final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  178. final StringBuffer fullUrl = httpServletRequest.getRequestURL();
  179. if (httpServletRequest.getQueryString() != null) {
  180. fullUrl.append("?")
  181. .append(httpServletRequest.getQueryString());
  182. }
  183. String urlString = fullUrl.toString();
  184. if ("POST".equalsIgnoreCase(httpServletRequest.getMethod())) {
  185. httpServletResponse.setStatus(
  186. HttpServletResponse.SC_MOVED_PERMANENTLY);
  187. httpServletResponse.setHeader(HttpSupport.HDR_LOCATION,
  188. urlString.replace("/post/", "/"));
  189. } else {
  190. String path = httpServletRequest.getPathInfo();
  191. path = path.replace("/post/", "/");
  192. if (httpServletRequest.getQueryString() != null) {
  193. path += '?' + httpServletRequest.getQueryString();
  194. }
  195. RequestDispatcher dispatcher = httpServletRequest
  196. .getRequestDispatcher(path);
  197. dispatcher.forward(httpServletRequest, httpServletResponse);
  198. }
  199. }
  200. @Override
  201. public void destroy() {
  202. // empty
  203. }
  204. }), "/post/*", EnumSet.of(DispatcherType.REQUEST));
  205. gs.setRepositoryResolver(new TestRepositoryResolver(src, srcName));
  206. app.addServlet(new ServletHolder(gs), "/*");
  207. return app;
  208. }
  209. private ServletContextHandler addBrokenContext(GitServlet gs,
  210. String srcName) {
  211. ServletContextHandler broken = server.addContext("/bad");
  212. broken.addFilter(new FilterHolder(new Filter() {
  213. @Override
  214. public void doFilter(ServletRequest request,
  215. ServletResponse response, FilterChain chain)
  216. throws IOException, ServletException {
  217. final HttpServletResponse r = (HttpServletResponse) response;
  218. r.setContentType("text/plain");
  219. r.setCharacterEncoding(UTF_8.name());
  220. try (PrintWriter w = r.getWriter()) {
  221. w.print("OK");
  222. }
  223. }
  224. @Override
  225. public void init(FilterConfig filterConfig)
  226. throws ServletException {
  227. // empty
  228. }
  229. @Override
  230. public void destroy() {
  231. // empty
  232. }
  233. }), "/" + srcName + "/git-upload-pack",
  234. EnumSet.of(DispatcherType.REQUEST));
  235. broken.addServlet(new ServletHolder(gs), "/*");
  236. return broken;
  237. }
  238. private ServletContextHandler addAuthContext(GitServlet gs,
  239. String contextPath, String... methods) {
  240. ServletContextHandler auth = server.addContext('/' + contextPath);
  241. auth.addServlet(new ServletHolder(gs), "/*");
  242. return server.authBasic(auth, methods);
  243. }
  244. private ServletContextHandler addRedirectContext(GitServlet gs) {
  245. ServletContextHandler redirect = server.addContext("/redirect");
  246. redirect.addFilter(new FilterHolder(new Filter() {
  247. // Enables tests for different codes, and for multiple redirects.
  248. // First parameter is the number of redirects, second one is the
  249. // redirect status code that should be used
  250. private Pattern responsePattern = Pattern
  251. .compile("/response/(\\d+)/(30[1237])/");
  252. // Enables tests to specify the context that the request should be
  253. // redirected to in the end. If not present, redirects got to the
  254. // normal /git context.
  255. private Pattern targetPattern = Pattern.compile("/target(/\\w+)/");
  256. @Override
  257. public void init(FilterConfig filterConfig)
  258. throws ServletException {
  259. // empty
  260. }
  261. private String local(String url, boolean toLocal) {
  262. if (!toLocal) {
  263. return url;
  264. }
  265. try {
  266. URI u = new URI(url);
  267. String fragment = u.getRawFragment();
  268. if (fragment != null) {
  269. return u.getRawPath() + '#' + fragment;
  270. }
  271. return u.getRawPath();
  272. } catch (URISyntaxException e) {
  273. return url;
  274. }
  275. }
  276. @Override
  277. public void doFilter(ServletRequest request,
  278. ServletResponse response, FilterChain chain)
  279. throws IOException, ServletException {
  280. final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
  281. final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  282. final StringBuffer fullUrl = httpServletRequest.getRequestURL();
  283. if (httpServletRequest.getQueryString() != null) {
  284. fullUrl.append("?")
  285. .append(httpServletRequest.getQueryString());
  286. }
  287. String urlString = fullUrl.toString();
  288. boolean localRedirect = false;
  289. if (urlString.contains("/local")) {
  290. urlString = urlString.replace("/local", "");
  291. localRedirect = true;
  292. }
  293. if (urlString.contains("/loop/")) {
  294. urlString = urlString.replace("/loop/", "/loop/x/");
  295. if (urlString.contains("/loop/x/x/x/x/x/x/x/x/")) {
  296. // Go back to initial.
  297. urlString = urlString.replace("/loop/x/x/x/x/x/x/x/x/",
  298. "/loop/");
  299. }
  300. httpServletResponse.setStatus(
  301. HttpServletResponse.SC_MOVED_TEMPORARILY);
  302. httpServletResponse.setHeader(HttpSupport.HDR_LOCATION,
  303. local(urlString, localRedirect));
  304. return;
  305. }
  306. int responseCode = HttpServletResponse.SC_MOVED_PERMANENTLY;
  307. int nofRedirects = 0;
  308. Matcher matcher = responsePattern.matcher(urlString);
  309. if (matcher.find()) {
  310. nofRedirects = Integer
  311. .parseUnsignedInt(matcher.group(1));
  312. responseCode = Integer.parseUnsignedInt(matcher.group(2));
  313. if (--nofRedirects <= 0) {
  314. urlString = urlString.substring(0, matcher.start())
  315. + '/' + urlString.substring(matcher.end());
  316. } else {
  317. urlString = urlString.substring(0, matcher.start())
  318. + "/response/" + nofRedirects + "/"
  319. + responseCode + '/'
  320. + urlString.substring(matcher.end());
  321. }
  322. }
  323. httpServletResponse.setStatus(responseCode);
  324. if (nofRedirects <= 0) {
  325. String targetContext = "/git";
  326. matcher = targetPattern.matcher(urlString);
  327. if (matcher.find()) {
  328. urlString = urlString.substring(0, matcher.start())
  329. + '/' + urlString.substring(matcher.end());
  330. targetContext = matcher.group(1);
  331. }
  332. urlString = urlString.replace("/redirect", targetContext);
  333. }
  334. httpServletResponse.setHeader(HttpSupport.HDR_LOCATION,
  335. local(urlString, localRedirect));
  336. }
  337. @Override
  338. public void destroy() {
  339. // empty
  340. }
  341. }), "/*", EnumSet.of(DispatcherType.REQUEST));
  342. redirect.addServlet(new ServletHolder(gs), "/*");
  343. return redirect;
  344. }
  345. private ServletContextHandler addSlowContext(GitServlet gs, String path,
  346. boolean auth) {
  347. ServletContextHandler slow = server.addContext('/' + path);
  348. slow.addFilter(new FilterHolder(new Filter() {
  349. @Override
  350. public void init(FilterConfig filterConfig)
  351. throws ServletException {
  352. // empty
  353. }
  354. // Simply delays the servlet for two seconds. Used for timeout
  355. // tests, which use a one-second timeout.
  356. @Override
  357. public void doFilter(ServletRequest request,
  358. ServletResponse response, FilterChain chain)
  359. throws IOException, ServletException {
  360. try {
  361. Thread.sleep(2000);
  362. } catch (InterruptedException e) {
  363. throw new IOException(e);
  364. }
  365. chain.doFilter(request, response);
  366. }
  367. @Override
  368. public void destroy() {
  369. // empty
  370. }
  371. }), "/*", EnumSet.of(DispatcherType.REQUEST));
  372. slow.addServlet(new ServletHolder(gs), "/*");
  373. if (auth) {
  374. return server.authBasic(slow);
  375. }
  376. return slow;
  377. }
  378. @Test
  379. public void testListRemote() throws IOException {
  380. assertEquals("http", remoteURI.getScheme());
  381. Map<String, Ref> map;
  382. try (Repository dst = createBareRepository();
  383. Transport t = Transport.open(dst, remoteURI)) {
  384. // I didn't make up these public interface names, I just
  385. // approved them for inclusion into the code base. Sorry.
  386. // --spearce
  387. //
  388. assertTrue("isa TransportHttp", t instanceof TransportHttp);
  389. assertTrue("isa HttpTransport", t instanceof HttpTransport);
  390. try (FetchConnection c = t.openFetch()) {
  391. map = c.getRefsMap();
  392. }
  393. }
  394. assertNotNull("have map of refs", map);
  395. assertEquals(3, map.size());
  396. assertNotNull("has " + master, map.get(master));
  397. assertEquals(B, map.get(master).getObjectId());
  398. assertNotNull("has " + Constants.HEAD, map.get(Constants.HEAD));
  399. assertEquals(B, map.get(Constants.HEAD).getObjectId());
  400. List<AccessEvent> requests = getRequests();
  401. assertEquals(enableProtocolV2 ? 2 : 1, requests.size());
  402. AccessEvent info = requests.get(0);
  403. assertEquals("GET", info.getMethod());
  404. assertEquals(join(remoteURI, "info/refs"), info.getPath());
  405. assertEquals(1, info.getParameters().size());
  406. assertEquals("git-upload-pack", info.getParameter("service"));
  407. assertEquals(200, info.getStatus());
  408. assertEquals("application/x-git-upload-pack-advertisement", info
  409. .getResponseHeader(HDR_CONTENT_TYPE));
  410. if (!enableProtocolV2) {
  411. assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
  412. } else {
  413. AccessEvent lsRefs = requests.get(1);
  414. assertEquals("POST", lsRefs.getMethod());
  415. assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
  416. assertEquals(0, lsRefs.getParameters().size());
  417. assertNotNull("has content-length",
  418. lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
  419. assertNull("not chunked",
  420. lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
  421. assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
  422. assertEquals(200, lsRefs.getStatus());
  423. assertEquals("application/x-git-upload-pack-result",
  424. lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
  425. }
  426. }
  427. @Test
  428. public void testListRemote_BadName() throws IOException, URISyntaxException {
  429. URIish uri = new URIish(this.remoteURI.toString() + ".invalid");
  430. try (Repository dst = createBareRepository();
  431. Transport t = Transport.open(dst, uri)) {
  432. try {
  433. t.openFetch();
  434. fail("fetch connection opened");
  435. } catch (RemoteRepositoryException notFound) {
  436. assertEquals(uri + ": Git repository not found",
  437. notFound.getMessage());
  438. }
  439. }
  440. List<AccessEvent> requests = getRequests();
  441. assertEquals(1, requests.size());
  442. AccessEvent info = requests.get(0);
  443. assertEquals("GET", info.getMethod());
  444. assertEquals(join(uri, "info/refs"), info.getPath());
  445. assertEquals(1, info.getParameters().size());
  446. assertEquals("git-upload-pack", info.getParameter("service"));
  447. assertEquals(200, info.getStatus());
  448. assertEquals("application/x-git-upload-pack-advertisement",
  449. info.getResponseHeader(HDR_CONTENT_TYPE));
  450. }
  451. @Test
  452. public void testFetchBySHA1() throws Exception {
  453. try (Repository dst = createBareRepository();
  454. Transport t = Transport.open(dst, remoteURI)) {
  455. assertFalse(dst.getObjectDatabase().has(A_txt));
  456. t.fetch(NullProgressMonitor.INSTANCE,
  457. Collections.singletonList(new RefSpec(B.name())));
  458. assertTrue(dst.getObjectDatabase().has(A_txt));
  459. }
  460. }
  461. @Test
  462. public void testFetchBySHA1Unreachable() throws Exception {
  463. try (Repository dst = createBareRepository();
  464. Transport t = Transport.open(dst, remoteURI)) {
  465. assertFalse(dst.getObjectDatabase().has(A_txt));
  466. Exception e = assertThrows(TransportException.class,
  467. () -> t.fetch(NullProgressMonitor.INSTANCE,
  468. Collections.singletonList(
  469. new RefSpec(unreachableCommit.name()))));
  470. assertTrue(e.getMessage().contains(
  471. "want " + unreachableCommit.name() + " not valid"));
  472. }
  473. }
  474. @Test
  475. public void testFetchBySHA1UnreachableByAdvertiseRefsHook()
  476. throws Exception {
  477. advertiseRefsHook = new AbstractAdvertiseRefsHook() {
  478. @Override
  479. protected Map<String, Ref> getAdvertisedRefs(Repository repository,
  480. RevWalk revWalk) {
  481. return Collections.emptyMap();
  482. }
  483. };
  484. try (Repository dst = createBareRepository();
  485. Transport t = Transport.open(dst, remoteURI)) {
  486. assertFalse(dst.getObjectDatabase().has(A_txt));
  487. Exception e = assertThrows(TransportException.class,
  488. () -> t.fetch(NullProgressMonitor.INSTANCE,
  489. Collections.singletonList(new RefSpec(A.name()))));
  490. assertTrue(
  491. e.getMessage().contains("want " + A.name() + " not valid"));
  492. }
  493. }
  494. @Test
  495. public void testTimeoutExpired() throws Exception {
  496. try (Repository dst = createBareRepository();
  497. Transport t = Transport.open(dst, slowURI)) {
  498. t.setTimeout(1);
  499. TransportException expected = assertThrows(TransportException.class,
  500. () -> t.fetch(NullProgressMonitor.INSTANCE,
  501. mirror(master)));
  502. assertTrue("Unexpected exception message: " + expected.toString(),
  503. expected.getMessage().contains("time"));
  504. }
  505. }
  506. @Test
  507. public void testTimeoutExpiredWithAuth() throws Exception {
  508. try (Repository dst = createBareRepository();
  509. Transport t = Transport.open(dst, slowAuthURI)) {
  510. t.setTimeout(1);
  511. t.setCredentialsProvider(testCredentials);
  512. TransportException expected = assertThrows(TransportException.class,
  513. () -> t.fetch(NullProgressMonitor.INSTANCE,
  514. mirror(master)));
  515. assertTrue("Unexpected exception message: " + expected.toString(),
  516. expected.getMessage().contains("time"));
  517. assertFalse("Unexpected exception message: " + expected.toString(),
  518. expected.getMessage().contains("auth"));
  519. }
  520. }
  521. @Test
  522. public void testInitialClone_Small() throws Exception {
  523. try (Repository dst = createBareRepository();
  524. Transport t = Transport.open(dst, remoteURI)) {
  525. assertFalse(dst.getObjectDatabase().has(A_txt));
  526. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  527. assertTrue(dst.getObjectDatabase().has(A_txt));
  528. assertEquals(B, dst.exactRef(master).getObjectId());
  529. fsck(dst, B);
  530. }
  531. List<AccessEvent> requests = getRequests();
  532. assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
  533. int requestNumber = 0;
  534. AccessEvent info = requests.get(requestNumber++);
  535. assertEquals("GET", info.getMethod());
  536. assertEquals(join(remoteURI, "info/refs"), info.getPath());
  537. assertEquals(1, info.getParameters().size());
  538. assertEquals("git-upload-pack", info.getParameter("service"));
  539. assertEquals(200, info.getStatus());
  540. assertEquals("application/x-git-upload-pack-advertisement", info
  541. .getResponseHeader(HDR_CONTENT_TYPE));
  542. if (!enableProtocolV2) {
  543. assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
  544. } else {
  545. AccessEvent lsRefs = requests.get(requestNumber++);
  546. assertEquals("POST", lsRefs.getMethod());
  547. assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
  548. assertEquals(0, lsRefs.getParameters().size());
  549. assertNotNull("has content-length",
  550. lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
  551. assertNull("not chunked",
  552. lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
  553. assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
  554. assertEquals(200, lsRefs.getStatus());
  555. assertEquals("application/x-git-upload-pack-result",
  556. lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
  557. }
  558. AccessEvent service = requests.get(requestNumber);
  559. assertEquals("POST", service.getMethod());
  560. assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
  561. assertEquals(0, service.getParameters().size());
  562. assertNotNull("has content-length", service
  563. .getRequestHeader(HDR_CONTENT_LENGTH));
  564. assertNull("not chunked", service
  565. .getRequestHeader(HDR_TRANSFER_ENCODING));
  566. assertEquals(200, service.getStatus());
  567. assertEquals("application/x-git-upload-pack-result", service
  568. .getResponseHeader(HDR_CONTENT_TYPE));
  569. }
  570. @Test
  571. public void test_CloneWithCustomFactory() throws Exception {
  572. HttpConnectionFactory globalFactory = HttpTransport
  573. .getConnectionFactory();
  574. HttpConnectionFactory failingConnectionFactory = new HttpConnectionFactory() {
  575. @Override
  576. public HttpConnection create(URL url) throws IOException {
  577. throw new IOException("Should not be reached");
  578. }
  579. @Override
  580. public HttpConnection create(URL url, Proxy proxy)
  581. throws IOException {
  582. throw new IOException("Should not be reached");
  583. }
  584. };
  585. HttpTransport.setConnectionFactory(failingConnectionFactory);
  586. try {
  587. File tmp = createTempDirectory("cloneViaApi");
  588. boolean[] localFactoryUsed = { false };
  589. TransportConfigCallback callback = new TransportConfigCallback() {
  590. @Override
  591. public void configure(Transport transport) {
  592. if (transport instanceof TransportHttp) {
  593. ((TransportHttp) transport).setHttpConnectionFactory(
  594. new HttpConnectionFactory() {
  595. @Override
  596. public HttpConnection create(URL url)
  597. throws IOException {
  598. localFactoryUsed[0] = true;
  599. return globalFactory.create(url);
  600. }
  601. @Override
  602. public HttpConnection create(URL url,
  603. Proxy proxy) throws IOException {
  604. localFactoryUsed[0] = true;
  605. return globalFactory.create(url, proxy);
  606. }
  607. });
  608. }
  609. }
  610. };
  611. try (Git git = Git.cloneRepository().setDirectory(tmp)
  612. .setTransportConfigCallback(callback)
  613. .setURI(remoteURI.toPrivateString()).call()) {
  614. assertTrue("Should have used the local HttpConnectionFactory",
  615. localFactoryUsed[0]);
  616. }
  617. } finally {
  618. HttpTransport.setConnectionFactory(globalFactory);
  619. }
  620. }
  621. private void initialClone_Redirect(int nofRedirects, int code)
  622. throws Exception {
  623. initialClone_Redirect(nofRedirects, code, false);
  624. }
  625. private void initialClone_Redirect(int nofRedirects, int code,
  626. boolean localRedirect) throws Exception {
  627. URIish cloneFrom = redirectURI;
  628. if (localRedirect) {
  629. cloneFrom = extendPath(cloneFrom, "/local");
  630. }
  631. if (code != 301 || nofRedirects > 1) {
  632. cloneFrom = extendPath(cloneFrom,
  633. "/response/" + nofRedirects + "/" + code);
  634. }
  635. try (Repository dst = createBareRepository();
  636. Transport t = Transport.open(dst, cloneFrom)) {
  637. assertFalse(dst.getObjectDatabase().has(A_txt));
  638. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  639. assertTrue(dst.getObjectDatabase().has(A_txt));
  640. assertEquals(B, dst.exactRef(master).getObjectId());
  641. fsck(dst, B);
  642. }
  643. List<AccessEvent> requests = getRequests();
  644. assertEquals((enableProtocolV2 ? 3 : 2) + nofRedirects,
  645. requests.size());
  646. int n = 0;
  647. while (n < nofRedirects) {
  648. AccessEvent redirect = requests.get(n++);
  649. assertEquals(code, redirect.getStatus());
  650. }
  651. AccessEvent info = requests.get(n++);
  652. assertEquals("GET", info.getMethod());
  653. assertEquals(join(remoteURI, "info/refs"), info.getPath());
  654. assertEquals(1, info.getParameters().size());
  655. assertEquals("git-upload-pack", info.getParameter("service"));
  656. assertEquals(200, info.getStatus());
  657. assertEquals("application/x-git-upload-pack-advertisement",
  658. info.getResponseHeader(HDR_CONTENT_TYPE));
  659. if (!enableProtocolV2) {
  660. assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
  661. } else {
  662. AccessEvent lsRefs = requests.get(n++);
  663. assertEquals("POST", lsRefs.getMethod());
  664. assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
  665. assertEquals(0, lsRefs.getParameters().size());
  666. assertNotNull("has content-length",
  667. lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
  668. assertNull("not chunked",
  669. lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
  670. assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
  671. assertEquals(200, lsRefs.getStatus());
  672. assertEquals("application/x-git-upload-pack-result",
  673. lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
  674. }
  675. AccessEvent service = requests.get(n++);
  676. assertEquals("POST", service.getMethod());
  677. assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
  678. assertEquals(0, service.getParameters().size());
  679. assertNotNull("has content-length",
  680. service.getRequestHeader(HDR_CONTENT_LENGTH));
  681. assertNull("not chunked",
  682. service.getRequestHeader(HDR_TRANSFER_ENCODING));
  683. assertEquals(200, service.getStatus());
  684. assertEquals("application/x-git-upload-pack-result",
  685. service.getResponseHeader(HDR_CONTENT_TYPE));
  686. }
  687. @Test
  688. public void testInitialClone_Redirect301Small() throws Exception {
  689. initialClone_Redirect(1, 301);
  690. }
  691. @Test
  692. public void testInitialClone_Redirect301Local() throws Exception {
  693. initialClone_Redirect(1, 301, true);
  694. }
  695. @Test
  696. public void testInitialClone_Redirect302Small() throws Exception {
  697. initialClone_Redirect(1, 302);
  698. }
  699. @Test
  700. public void testInitialClone_Redirect303Small() throws Exception {
  701. initialClone_Redirect(1, 303);
  702. }
  703. @Test
  704. public void testInitialClone_Redirect307Small() throws Exception {
  705. initialClone_Redirect(1, 307);
  706. }
  707. @Test
  708. public void testInitialClone_RedirectMultiple() throws Exception {
  709. initialClone_Redirect(4, 302);
  710. }
  711. @Test
  712. public void testInitialClone_RedirectMax() throws Exception {
  713. StoredConfig userConfig = SystemReader.getInstance()
  714. .getUserConfig();
  715. userConfig.setInt("http", null, "maxRedirects", 4);
  716. userConfig.save();
  717. initialClone_Redirect(4, 302);
  718. }
  719. @Test
  720. public void testInitialClone_RedirectTooOften() throws Exception {
  721. StoredConfig userConfig = SystemReader.getInstance()
  722. .getUserConfig();
  723. userConfig.setInt("http", null, "maxRedirects", 3);
  724. userConfig.save();
  725. URIish cloneFrom = extendPath(redirectURI, "/response/4/302");
  726. String remoteUri = cloneFrom.toString();
  727. try (Repository dst = createBareRepository();
  728. Transport t = Transport.open(dst, cloneFrom)) {
  729. assertFalse(dst.getObjectDatabase().has(A_txt));
  730. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  731. fail("Should have failed (too many redirects)");
  732. } catch (TransportException e) {
  733. String expectedMessageBegin = remoteUri.toString() + ": "
  734. + MessageFormat.format(JGitText.get().redirectLimitExceeded,
  735. "3", remoteUri.replace("/4/", "/1/") + '/', "");
  736. String message = e.getMessage();
  737. if (message.length() > expectedMessageBegin.length()) {
  738. message = message.substring(0, expectedMessageBegin.length());
  739. }
  740. assertEquals(expectedMessageBegin, message);
  741. }
  742. }
  743. @Test
  744. public void testInitialClone_RedirectLoop() throws Exception {
  745. URIish cloneFrom = extendPath(redirectURI, "/loop");
  746. try (Repository dst = createBareRepository();
  747. Transport t = Transport.open(dst, cloneFrom)) {
  748. assertFalse(dst.getObjectDatabase().has(A_txt));
  749. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  750. fail("Should have failed (redirect loop)");
  751. } catch (TransportException e) {
  752. assertTrue(e.getMessage().contains("Redirected more than"));
  753. }
  754. }
  755. @Test
  756. public void testInitialClone_RedirectOnPostAllowed() throws Exception {
  757. StoredConfig userConfig = SystemReader.getInstance()
  758. .getUserConfig();
  759. userConfig.setString("http", null, "followRedirects", "true");
  760. userConfig.save();
  761. URIish cloneFrom = extendPath(remoteURI, "/post");
  762. try (Repository dst = createBareRepository();
  763. Transport t = Transport.open(dst, cloneFrom)) {
  764. assertFalse(dst.getObjectDatabase().has(A_txt));
  765. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  766. assertTrue(dst.getObjectDatabase().has(A_txt));
  767. assertEquals(B, dst.exactRef(master).getObjectId());
  768. fsck(dst, B);
  769. }
  770. List<AccessEvent> requests = getRequests();
  771. assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
  772. AccessEvent info = requests.get(0);
  773. assertEquals("GET", info.getMethod());
  774. assertEquals(join(cloneFrom, "info/refs"), info.getPath());
  775. assertEquals(1, info.getParameters().size());
  776. assertEquals("git-upload-pack", info.getParameter("service"));
  777. assertEquals(200, info.getStatus());
  778. assertEquals("application/x-git-upload-pack-advertisement",
  779. info.getResponseHeader(HDR_CONTENT_TYPE));
  780. if (!enableProtocolV2) {
  781. assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
  782. }
  783. AccessEvent redirect = requests.get(1);
  784. assertEquals("POST", redirect.getMethod());
  785. assertEquals(301, redirect.getStatus());
  786. for (int i = 2; i < requests.size(); i++) {
  787. AccessEvent service = requests.get(i);
  788. assertEquals("POST", service.getMethod());
  789. assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
  790. assertEquals(0, service.getParameters().size());
  791. assertNotNull("has content-length",
  792. service.getRequestHeader(HDR_CONTENT_LENGTH));
  793. assertNull("not chunked",
  794. service.getRequestHeader(HDR_TRANSFER_ENCODING));
  795. assertEquals(200, service.getStatus());
  796. assertEquals("application/x-git-upload-pack-result",
  797. service.getResponseHeader(HDR_CONTENT_TYPE));
  798. }
  799. }
  800. @Test
  801. public void testInitialClone_RedirectOnPostForbidden() throws Exception {
  802. URIish cloneFrom = extendPath(remoteURI, "/post");
  803. try (Repository dst = createBareRepository();
  804. Transport t = Transport.open(dst, cloneFrom)) {
  805. assertFalse(dst.getObjectDatabase().has(A_txt));
  806. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  807. fail("Should have failed (redirect on POST)");
  808. } catch (TransportException e) {
  809. assertTrue(e.getMessage().contains("301"));
  810. }
  811. }
  812. @Test
  813. public void testInitialClone_RedirectForbidden() throws Exception {
  814. StoredConfig userConfig = SystemReader.getInstance()
  815. .getUserConfig();
  816. userConfig.setString("http", null, "followRedirects", "false");
  817. userConfig.save();
  818. try (Repository dst = createBareRepository();
  819. Transport t = Transport.open(dst, redirectURI)) {
  820. assertFalse(dst.getObjectDatabase().has(A_txt));
  821. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  822. fail("Should have failed (redirects forbidden)");
  823. } catch (TransportException e) {
  824. assertTrue(
  825. e.getMessage().contains("http.followRedirects is false"));
  826. }
  827. }
  828. private void assertFetchRequests(List<AccessEvent> requests, int index) {
  829. AccessEvent info = requests.get(index++);
  830. assertEquals("GET", info.getMethod());
  831. assertEquals(join(authURI, "info/refs"), info.getPath());
  832. assertEquals(1, info.getParameters().size());
  833. assertEquals("git-upload-pack", info.getParameter("service"));
  834. assertEquals(200, info.getStatus());
  835. assertEquals("application/x-git-upload-pack-advertisement",
  836. info.getResponseHeader(HDR_CONTENT_TYPE));
  837. if (!enableProtocolV2) {
  838. assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
  839. }
  840. for (int i = index; i < requests.size(); i++) {
  841. AccessEvent service = requests.get(i);
  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. }
  854. @Test
  855. public void testInitialClone_WithAuthentication() throws Exception {
  856. try (Repository dst = createBareRepository();
  857. Transport t = Transport.open(dst, authURI)) {
  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(enableProtocolV2 ? 4 : 3, requests.size());
  867. AccessEvent info = requests.get(0);
  868. assertEquals("GET", info.getMethod());
  869. assertEquals(401, info.getStatus());
  870. assertFetchRequests(requests, 1);
  871. }
  872. @Test
  873. public void testInitialClone_WithPreAuthentication() throws Exception {
  874. try (Repository dst = createBareRepository();
  875. Transport t = Transport.open(dst, authURI)) {
  876. assertFalse(dst.getObjectDatabase().has(A_txt));
  877. ((TransportHttp) t).setPreemptiveBasicAuthentication(
  878. AppServer.username, AppServer.password);
  879. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  880. assertTrue(dst.getObjectDatabase().has(A_txt));
  881. assertEquals(B, dst.exactRef(master).getObjectId());
  882. fsck(dst, B);
  883. }
  884. List<AccessEvent> requests = getRequests();
  885. assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
  886. assertFetchRequests(requests, 0);
  887. }
  888. @Test
  889. public void testInitialClone_WithPreAuthenticationCleared()
  890. throws Exception {
  891. try (Repository dst = createBareRepository();
  892. Transport t = Transport.open(dst, authURI)) {
  893. assertFalse(dst.getObjectDatabase().has(A_txt));
  894. ((TransportHttp) t).setPreemptiveBasicAuthentication(
  895. AppServer.username, AppServer.password);
  896. ((TransportHttp) t).setPreemptiveBasicAuthentication(null, null);
  897. t.setCredentialsProvider(testCredentials);
  898. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  899. assertTrue(dst.getObjectDatabase().has(A_txt));
  900. assertEquals(B, dst.exactRef(master).getObjectId());
  901. fsck(dst, B);
  902. }
  903. List<AccessEvent> requests = getRequests();
  904. assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
  905. AccessEvent info = requests.get(0);
  906. assertEquals("GET", info.getMethod());
  907. assertEquals(401, info.getStatus());
  908. assertFetchRequests(requests, 1);
  909. }
  910. @Test
  911. public void testInitialClone_PreAuthenticationTooLate() throws Exception {
  912. try (Repository dst = createBareRepository();
  913. Transport t = Transport.open(dst, authURI)) {
  914. assertFalse(dst.getObjectDatabase().has(A_txt));
  915. ((TransportHttp) t).setPreemptiveBasicAuthentication(
  916. AppServer.username, AppServer.password);
  917. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  918. assertTrue(dst.getObjectDatabase().has(A_txt));
  919. assertEquals(B, dst.exactRef(master).getObjectId());
  920. fsck(dst, B);
  921. List<AccessEvent> requests = getRequests();
  922. assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
  923. assertFetchRequests(requests, 0);
  924. assertThrows(IllegalStateException.class,
  925. () -> ((TransportHttp) t).setPreemptiveBasicAuthentication(
  926. AppServer.username, AppServer.password));
  927. assertThrows(IllegalStateException.class, () -> ((TransportHttp) t)
  928. .setPreemptiveBasicAuthentication(null, null));
  929. }
  930. }
  931. @Test
  932. public void testInitialClone_WithWrongPreAuthenticationAndCredentialProvider()
  933. throws Exception {
  934. try (Repository dst = createBareRepository();
  935. Transport t = Transport.open(dst, authURI)) {
  936. assertFalse(dst.getObjectDatabase().has(A_txt));
  937. ((TransportHttp) t).setPreemptiveBasicAuthentication(
  938. AppServer.username, AppServer.password + 'x');
  939. t.setCredentialsProvider(testCredentials);
  940. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  941. assertTrue(dst.getObjectDatabase().has(A_txt));
  942. assertEquals(B, dst.exactRef(master).getObjectId());
  943. fsck(dst, B);
  944. }
  945. List<AccessEvent> requests = getRequests();
  946. assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
  947. AccessEvent info = requests.get(0);
  948. assertEquals("GET", info.getMethod());
  949. assertEquals(401, info.getStatus());
  950. assertFetchRequests(requests, 1);
  951. }
  952. @Test
  953. public void testInitialClone_WithWrongPreAuthentication() throws Exception {
  954. try (Repository dst = createBareRepository();
  955. Transport t = Transport.open(dst, authURI)) {
  956. assertFalse(dst.getObjectDatabase().has(A_txt));
  957. ((TransportHttp) t).setPreemptiveBasicAuthentication(
  958. AppServer.username, AppServer.password + 'x');
  959. TransportException e = assertThrows(TransportException.class,
  960. () -> t.fetch(NullProgressMonitor.INSTANCE,
  961. mirror(master)));
  962. String msg = e.getMessage();
  963. assertTrue("Unexpected exception message: " + msg,
  964. msg.contains("no CredentialsProvider"));
  965. }
  966. List<AccessEvent> requests = getRequests();
  967. assertEquals(1, requests.size());
  968. AccessEvent info = requests.get(0);
  969. assertEquals("GET", info.getMethod());
  970. assertEquals(401, info.getStatus());
  971. }
  972. @Test
  973. public void testInitialClone_WithUserInfo() throws Exception {
  974. URIish withUserInfo = authURI.setUser(AppServer.username)
  975. .setPass(AppServer.password);
  976. try (Repository dst = createBareRepository();
  977. Transport t = Transport.open(dst, withUserInfo)) {
  978. assertFalse(dst.getObjectDatabase().has(A_txt));
  979. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  980. assertTrue(dst.getObjectDatabase().has(A_txt));
  981. assertEquals(B, dst.exactRef(master).getObjectId());
  982. fsck(dst, B);
  983. }
  984. List<AccessEvent> requests = getRequests();
  985. assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
  986. assertFetchRequests(requests, 0);
  987. }
  988. @Test
  989. public void testInitialClone_PreAuthOverridesUserInfo() throws Exception {
  990. URIish withUserInfo = authURI.setUser(AppServer.username)
  991. .setPass(AppServer.password + 'x');
  992. try (Repository dst = createBareRepository();
  993. Transport t = Transport.open(dst, withUserInfo)) {
  994. assertFalse(dst.getObjectDatabase().has(A_txt));
  995. ((TransportHttp) t).setPreemptiveBasicAuthentication(
  996. AppServer.username, AppServer.password);
  997. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  998. assertTrue(dst.getObjectDatabase().has(A_txt));
  999. assertEquals(B, dst.exactRef(master).getObjectId());
  1000. fsck(dst, B);
  1001. }
  1002. List<AccessEvent> requests = getRequests();
  1003. assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
  1004. assertFetchRequests(requests, 0);
  1005. }
  1006. @Test
  1007. public void testInitialClone_WithAuthenticationNoCredentials()
  1008. throws Exception {
  1009. try (Repository dst = createBareRepository();
  1010. Transport t = Transport.open(dst, authURI)) {
  1011. assertFalse(dst.getObjectDatabase().has(A_txt));
  1012. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  1013. fail("Should not have succeeded -- no authentication");
  1014. } catch (TransportException e) {
  1015. String msg = e.getMessage();
  1016. assertTrue("Unexpected exception message: " + msg,
  1017. msg.contains("no CredentialsProvider"));
  1018. }
  1019. List<AccessEvent> requests = getRequests();
  1020. assertEquals(1, requests.size());
  1021. AccessEvent info = requests.get(0);
  1022. assertEquals("GET", info.getMethod());
  1023. assertEquals(401, info.getStatus());
  1024. }
  1025. @Test
  1026. public void testInitialClone_WithAuthenticationWrongCredentials()
  1027. throws Exception {
  1028. try (Repository dst = createBareRepository();
  1029. Transport t = Transport.open(dst, authURI)) {
  1030. assertFalse(dst.getObjectDatabase().has(A_txt));
  1031. t.setCredentialsProvider(new UsernamePasswordCredentialsProvider(
  1032. AppServer.username, "wrongpassword"));
  1033. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  1034. fail("Should not have succeeded -- wrong password");
  1035. } catch (TransportException e) {
  1036. String msg = e.getMessage();
  1037. assertTrue("Unexpected exception message: " + msg,
  1038. msg.contains("auth"));
  1039. }
  1040. List<AccessEvent> requests = getRequests();
  1041. // Once without authentication plus three re-tries with authentication
  1042. assertEquals(4, requests.size());
  1043. for (AccessEvent event : requests) {
  1044. assertEquals("GET", event.getMethod());
  1045. assertEquals(401, event.getStatus());
  1046. }
  1047. }
  1048. @Test
  1049. public void testInitialClone_WithAuthenticationAfterRedirect()
  1050. throws Exception {
  1051. URIish cloneFrom = extendPath(redirectURI, "/target/auth");
  1052. CredentialsProvider uriSpecificCredentialsProvider = new UsernamePasswordCredentialsProvider(
  1053. "unknown", "none") {
  1054. @Override
  1055. public boolean get(URIish uri, CredentialItem... items)
  1056. throws UnsupportedCredentialItem {
  1057. // Only return the true credentials if the uri path starts with
  1058. // /auth. This ensures that we do provide the correct
  1059. // credentials only for the URi after the redirect, making the
  1060. // test fail if we should be asked for the credentials for the
  1061. // original URI.
  1062. if (uri.getPath().startsWith("/auth")) {
  1063. return testCredentials.get(uri, items);
  1064. }
  1065. return super.get(uri, items);
  1066. }
  1067. };
  1068. try (Repository dst = createBareRepository();
  1069. Transport t = Transport.open(dst, cloneFrom)) {
  1070. assertFalse(dst.getObjectDatabase().has(A_txt));
  1071. t.setCredentialsProvider(uriSpecificCredentialsProvider);
  1072. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  1073. assertTrue(dst.getObjectDatabase().has(A_txt));
  1074. assertEquals(B, dst.exactRef(master).getObjectId());
  1075. fsck(dst, B);
  1076. }
  1077. List<AccessEvent> requests = getRequests();
  1078. assertEquals(enableProtocolV2 ? 5 : 4, requests.size());
  1079. int requestNumber = 0;
  1080. AccessEvent redirect = requests.get(requestNumber++);
  1081. assertEquals("GET", redirect.getMethod());
  1082. assertEquals(join(cloneFrom, "info/refs"), redirect.getPath());
  1083. assertEquals(301, redirect.getStatus());
  1084. AccessEvent info = requests.get(requestNumber++);
  1085. assertEquals("GET", info.getMethod());
  1086. assertEquals(join(authURI, "info/refs"), info.getPath());
  1087. assertEquals(401, info.getStatus());
  1088. info = requests.get(requestNumber++);
  1089. assertEquals("GET", info.getMethod());
  1090. assertEquals(join(authURI, "info/refs"), info.getPath());
  1091. assertEquals(1, info.getParameters().size());
  1092. assertEquals("git-upload-pack", info.getParameter("service"));
  1093. assertEquals(200, info.getStatus());
  1094. assertEquals("application/x-git-upload-pack-advertisement",
  1095. info.getResponseHeader(HDR_CONTENT_TYPE));
  1096. if (!enableProtocolV2) {
  1097. assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
  1098. } else {
  1099. AccessEvent lsRefs = requests.get(requestNumber++);
  1100. assertEquals("POST", lsRefs.getMethod());
  1101. assertEquals(join(authURI, "git-upload-pack"), lsRefs.getPath());
  1102. assertEquals(0, lsRefs.getParameters().size());
  1103. assertNotNull("has content-length",
  1104. lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
  1105. assertNull("not chunked",
  1106. lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
  1107. assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
  1108. assertEquals(200, lsRefs.getStatus());
  1109. assertEquals("application/x-git-upload-pack-result",
  1110. lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
  1111. }
  1112. AccessEvent service = requests.get(requestNumber);
  1113. assertEquals("POST", service.getMethod());
  1114. assertEquals(join(authURI, "git-upload-pack"), service.getPath());
  1115. assertEquals(0, service.getParameters().size());
  1116. assertNotNull("has content-length",
  1117. service.getRequestHeader(HDR_CONTENT_LENGTH));
  1118. assertNull("not chunked",
  1119. service.getRequestHeader(HDR_TRANSFER_ENCODING));
  1120. assertEquals(200, service.getStatus());
  1121. assertEquals("application/x-git-upload-pack-result",
  1122. service.getResponseHeader(HDR_CONTENT_TYPE));
  1123. }
  1124. @Test
  1125. public void testInitialClone_WithAuthenticationOnPostOnly()
  1126. throws Exception {
  1127. try (Repository dst = createBareRepository();
  1128. Transport t = Transport.open(dst, authOnPostURI)) {
  1129. assertFalse(dst.getObjectDatabase().has(A_txt));
  1130. t.setCredentialsProvider(testCredentials);
  1131. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  1132. assertTrue(dst.getObjectDatabase().has(A_txt));
  1133. assertEquals(B, dst.exactRef(master).getObjectId());
  1134. fsck(dst, B);
  1135. }
  1136. List<AccessEvent> requests = getRequests();
  1137. assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
  1138. AccessEvent info = requests.get(0);
  1139. assertEquals("GET", info.getMethod());
  1140. assertEquals(join(authOnPostURI, "info/refs"), info.getPath());
  1141. assertEquals(1, info.getParameters().size());
  1142. assertEquals("git-upload-pack", info.getParameter("service"));
  1143. assertEquals(200, info.getStatus());
  1144. assertEquals("application/x-git-upload-pack-advertisement",
  1145. info.getResponseHeader(HDR_CONTENT_TYPE));
  1146. if (!enableProtocolV2) {
  1147. assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
  1148. }
  1149. AccessEvent service = requests.get(1);
  1150. assertEquals("POST", service.getMethod());
  1151. assertEquals(join(authOnPostURI, "git-upload-pack"), service.getPath());
  1152. assertEquals(401, service.getStatus());
  1153. for (int i = 2; i < requests.size(); i++) {
  1154. service = requests.get(i);
  1155. assertEquals("POST", service.getMethod());
  1156. assertEquals(join(authOnPostURI, "git-upload-pack"),
  1157. service.getPath());
  1158. assertEquals(0, service.getParameters().size());
  1159. assertNotNull("has content-length",
  1160. service.getRequestHeader(HDR_CONTENT_LENGTH));
  1161. assertNull("not chunked",
  1162. service.getRequestHeader(HDR_TRANSFER_ENCODING));
  1163. assertEquals(200, service.getStatus());
  1164. assertEquals("application/x-git-upload-pack-result",
  1165. service.getResponseHeader(HDR_CONTENT_TYPE));
  1166. }
  1167. }
  1168. @Test
  1169. public void testFetch_FewLocalCommits() throws Exception {
  1170. // Bootstrap by doing the clone.
  1171. //
  1172. TestRepository dst = createTestRepository();
  1173. try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
  1174. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  1175. }
  1176. assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
  1177. List<AccessEvent> cloneRequests = getRequests();
  1178. // Only create a few new commits.
  1179. TestRepository.BranchBuilder b = dst.branch(master);
  1180. for (int i = 0; i < 4; i++)
  1181. b.commit().tick(3600 /* 1 hour */).message("c" + i).create();
  1182. // Create a new commit on the remote.
  1183. //
  1184. RevCommit Z;
  1185. try (TestRepository<Repository> tr = new TestRepository<>(
  1186. remoteRepository)) {
  1187. b = tr.branch(master);
  1188. Z = b.commit().message("Z").create();
  1189. }
  1190. // Now incrementally update.
  1191. //
  1192. try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
  1193. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  1194. }
  1195. assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
  1196. List<AccessEvent> requests = getRequests();
  1197. requests.removeAll(cloneRequests);
  1198. assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
  1199. int requestNumber = 0;
  1200. AccessEvent info = requests.get(requestNumber++);
  1201. assertEquals("GET", info.getMethod());
  1202. assertEquals(join(remoteURI, "info/refs"), info.getPath());
  1203. assertEquals(1, info.getParameters().size());
  1204. assertEquals("git-upload-pack", info.getParameter("service"));
  1205. assertEquals(200, info.getStatus());
  1206. assertEquals("application/x-git-upload-pack-advertisement",
  1207. info.getResponseHeader(HDR_CONTENT_TYPE));
  1208. if (enableProtocolV2) {
  1209. AccessEvent lsRefs = requests.get(requestNumber++);
  1210. assertEquals("POST", lsRefs.getMethod());
  1211. assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
  1212. assertEquals(0, lsRefs.getParameters().size());
  1213. assertNotNull("has content-length",
  1214. lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
  1215. assertNull("not chunked",
  1216. lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
  1217. assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
  1218. assertEquals(200, lsRefs.getStatus());
  1219. assertEquals("application/x-git-upload-pack-result",
  1220. lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
  1221. }
  1222. // We should have needed one request to perform the fetch.
  1223. //
  1224. AccessEvent service = requests.get(requestNumber);
  1225. assertEquals("POST", service.getMethod());
  1226. assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
  1227. assertEquals(0, service.getParameters().size());
  1228. assertNotNull("has content-length",
  1229. service.getRequestHeader(HDR_CONTENT_LENGTH));
  1230. assertNull("not chunked",
  1231. service.getRequestHeader(HDR_TRANSFER_ENCODING));
  1232. assertEquals(200, service.getStatus());
  1233. assertEquals("application/x-git-upload-pack-result",
  1234. service.getResponseHeader(HDR_CONTENT_TYPE));
  1235. }
  1236. @Test
  1237. public void testFetch_TooManyLocalCommits() throws Exception {
  1238. // Bootstrap by doing the clone.
  1239. //
  1240. TestRepository dst = createTestRepository();
  1241. try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
  1242. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  1243. }
  1244. assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
  1245. List<AccessEvent> cloneRequests = getRequests();
  1246. // Force enough into the local client that enumeration will
  1247. // need multiple packets, but not too many to overflow and
  1248. // not pick up the ACK_COMMON message.
  1249. //
  1250. TestRepository.BranchBuilder b = dst.branch(master);
  1251. for (int i = 0; i < 32 - 1; i++)
  1252. b.commit().tick(3600 /* 1 hour */).message("c" + i).create();
  1253. // Create a new commit on the remote.
  1254. //
  1255. RevCommit Z;
  1256. try (TestRepository<Repository> tr = new TestRepository<>(
  1257. remoteRepository)) {
  1258. b = tr.branch(master);
  1259. Z = b.commit().message("Z").create();
  1260. }
  1261. // Now incrementally update.
  1262. //
  1263. try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
  1264. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  1265. }
  1266. assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
  1267. List<AccessEvent> requests = getRequests();
  1268. requests.removeAll(cloneRequests);
  1269. assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
  1270. int requestNumber = 0;
  1271. AccessEvent info = requests.get(requestNumber++);
  1272. assertEquals("GET", info.getMethod());
  1273. assertEquals(join(remoteURI, "info/refs"), info.getPath());
  1274. assertEquals(1, info.getParameters().size());
  1275. assertEquals("git-upload-pack", info.getParameter("service"));
  1276. assertEquals(200, info.getStatus());
  1277. assertEquals("application/x-git-upload-pack-advertisement", info
  1278. .getResponseHeader(HDR_CONTENT_TYPE));
  1279. if (enableProtocolV2) {
  1280. AccessEvent lsRefs = requests.get(requestNumber++);
  1281. assertEquals("POST", lsRefs.getMethod());
  1282. assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
  1283. assertEquals(0, lsRefs.getParameters().size());
  1284. assertNotNull("has content-length",
  1285. lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
  1286. assertNull("not chunked",
  1287. lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
  1288. assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
  1289. assertEquals(200, lsRefs.getStatus());
  1290. assertEquals("application/x-git-upload-pack-result",
  1291. lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
  1292. }
  1293. // We should have needed two requests to perform the fetch
  1294. // due to the high number of local unknown commits.
  1295. //
  1296. AccessEvent service = requests.get(requestNumber++);
  1297. assertEquals("POST", service.getMethod());
  1298. assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
  1299. assertEquals(0, service.getParameters().size());
  1300. assertNotNull("has content-length", service
  1301. .getRequestHeader(HDR_CONTENT_LENGTH));
  1302. assertNull("not chunked", service
  1303. .getRequestHeader(HDR_TRANSFER_ENCODING));
  1304. assertEquals(200, service.getStatus());
  1305. assertEquals("application/x-git-upload-pack-result", service
  1306. .getResponseHeader(HDR_CONTENT_TYPE));
  1307. service = requests.get(requestNumber);
  1308. assertEquals("POST", service.getMethod());
  1309. assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
  1310. assertEquals(0, service.getParameters().size());
  1311. assertNotNull("has content-length", service
  1312. .getRequestHeader(HDR_CONTENT_LENGTH));
  1313. assertNull("not chunked", service
  1314. .getRequestHeader(HDR_TRANSFER_ENCODING));
  1315. assertEquals(200, service.getStatus());
  1316. assertEquals("application/x-git-upload-pack-result", service
  1317. .getResponseHeader(HDR_CONTENT_TYPE));
  1318. }
  1319. @Test
  1320. public void testFetch_MaxHavesCutoffAfterAckOnly() throws Exception {
  1321. // Bootstrap by doing the clone.
  1322. //
  1323. TestRepository dst = createTestRepository();
  1324. try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
  1325. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  1326. }
  1327. assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
  1328. // Force enough into the local client that enumeration will
  1329. // need more than MAX_HAVES (256) haves to be sent. The server
  1330. // doesn't know any of these, so it will never ACK. The client
  1331. // should keep going.
  1332. //
  1333. // If it does, client and server will find a common commit, and
  1334. // the pack file will contain exactly the one commit object Z
  1335. // we create on the remote, which we can test for via the progress
  1336. // monitor, which should have something like
  1337. // "Receiving objects: 100% (1/1)". If the client sends a "done"
  1338. // too early, the server will send more objects, and we'll have
  1339. // a line like "Receiving objects: 100% (8/8)".
  1340. TestRepository.BranchBuilder b = dst.branch(master);
  1341. // The client will send 32 + 64 + 128 + 256 + 512 haves. Only the
  1342. // last one will be a common commit. If the cutoff kicks in too
  1343. // early (after 480), we'll get too many objects in the fetch.
  1344. for (int i = 0; i < 992; i++)
  1345. b.commit().tick(3600 /* 1 hour */).message("c" + i).create();
  1346. // Create a new commit on the remote.
  1347. //
  1348. RevCommit Z;
  1349. try (TestRepository<Repository> tr = new TestRepository<>(
  1350. remoteRepository)) {
  1351. b = tr.branch(master);
  1352. Z = b.commit().message("Z").create();
  1353. }
  1354. // Now incrementally update.
  1355. //
  1356. ByteArrayOutputStream buffer = new ByteArrayOutputStream();
  1357. Writer writer = new OutputStreamWriter(buffer, StandardCharsets.UTF_8);
  1358. TextProgressMonitor monitor = new TextProgressMonitor(writer);
  1359. try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
  1360. t.fetch(monitor, mirror(master));
  1361. }
  1362. assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
  1363. String progressMessages = new String(buffer.toByteArray(),
  1364. StandardCharsets.UTF_8);
  1365. Pattern expected = Pattern
  1366. .compile("Receiving objects:\\s+100% \\(1/1\\)\n");
  1367. if (!expected.matcher(progressMessages).find()) {
  1368. System.out.println(progressMessages);
  1369. fail("Expected only one object to be sent");
  1370. }
  1371. }
  1372. @Test
  1373. public void testInitialClone_BrokenServer() throws Exception {
  1374. try (Repository dst = createBareRepository();
  1375. Transport t = Transport.open(dst, brokenURI)) {
  1376. assertFalse(dst.getObjectDatabase().has(A_txt));
  1377. try {
  1378. t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
  1379. fail("fetch completed despite upload-pack being broken");
  1380. } catch (TransportException err) {
  1381. String exp = brokenURI + ": expected"
  1382. + " Content-Type application/x-git-upload-pack-result;"
  1383. + " received Content-Type text/plain;charset=utf-8";
  1384. assertEquals(exp, err.getMessage());
  1385. }
  1386. }
  1387. List<AccessEvent> requests = getRequests();
  1388. assertEquals(2, requests.size());
  1389. AccessEvent info = requests.get(0);
  1390. assertEquals("GET", info.getMethod());
  1391. assertEquals(join(brokenURI, "info/refs"), info.getPath());
  1392. assertEquals(1, info.getParameters().size());
  1393. assertEquals("git-upload-pack", info.getParameter("service"));
  1394. assertEquals(200, info.getStatus());
  1395. assertEquals("application/x-git-upload-pack-advertisement", info
  1396. .getResponseHeader(HDR_CONTENT_TYPE));
  1397. AccessEvent service = requests.get(1);
  1398. assertEquals("POST", service.getMethod());
  1399. assertEquals(join(brokenURI, "git-upload-pack"), service.getPath());
  1400. assertEquals(0, service.getParameters().size());
  1401. assertEquals(200, service.getStatus());
  1402. assertEquals("text/plain;charset=utf-8",
  1403. service.getResponseHeader(HDR_CONTENT_TYPE));
  1404. }
  1405. @Test
  1406. public void testInvalidWant() throws Exception {
  1407. ObjectId id;
  1408. try (ObjectInserter.Formatter formatter = new ObjectInserter.Formatter()) {
  1409. id = formatter.idFor(Constants.OBJ_BLOB,
  1410. "testInvalidWant".getBytes(UTF_8));
  1411. }
  1412. try (Repository dst = createBareRepository();
  1413. Transport t = Transport.open(dst, remoteURI);
  1414. FetchConnection c = t.openFetch()) {
  1415. Ref want = new ObjectIdRef.Unpeeled(Ref.Storage.NETWORK, id.name(),
  1416. id);
  1417. c.fetch(NullProgressMonitor.INSTANCE, Collections.singleton(want),
  1418. Collections.<ObjectId> emptySet());
  1419. fail("Server accepted want " + id.name());
  1420. } catch (TransportException err) {
  1421. assertTrue(err.getMessage()
  1422. .contains("want " + id.name() + " not valid"));
  1423. }
  1424. }
  1425. @Test
  1426. public void testFetch_RefsUnreadableOnUpload() throws Exception {
  1427. AppServer noRefServer = new AppServer();
  1428. try {
  1429. final String repoName = "refs-unreadable";
  1430. RefsUnreadableInMemoryRepository badRefsRepo = new RefsUnreadableInMemoryRepository(
  1431. new DfsRepositoryDescription(repoName));
  1432. final TestRepository<Repository> repo = new TestRepository<>(
  1433. badRefsRepo);
  1434. badRefsRepo.getConfig().setInt("protocol", null, "version",
  1435. enableProtocolV2 ? 2 : 0);
  1436. ServletContextHandler app = noRefServer.addContext("/git");
  1437. GitServlet gs = new GitServlet();
  1438. gs.setRepositoryResolver(new TestRepositoryResolver(repo, repoName));
  1439. app.addServlet(new ServletHolder(gs), "/*");
  1440. noRefServer.setUp();
  1441. RevBlob A2_txt = repo.blob("A2");
  1442. RevCommit A2 = repo.commit().add("A2_txt", A2_txt).create();
  1443. RevCommit B2 = repo.commit().parent(A2).add("A2_txt", "C2")
  1444. .add("B2", "B2").create();
  1445. repo.update(master, B2);
  1446. URIish badRefsURI = new URIish(noRefServer.getURI()
  1447. .resolve(app.getContextPath() + "/" + repoName).toString());
  1448. try (Repository dst = createBareRepository();
  1449. Transport t = Transport.open(dst, badRefsURI);
  1450. FetchConnection c = t.openFetch()) {
  1451. // We start failing here to exercise the post-advertisement
  1452. // upload pack handler.
  1453. badRefsRepo.startFailing();
  1454. // Need to flush caches because ref advertisement populated them.
  1455. badRefsRepo.getRefDatabase().refresh();
  1456. c.fetch(NullProgressMonitor.INSTANCE,
  1457. Collections.singleton(c.getRef(master)),
  1458. Collections.<ObjectId> emptySet());
  1459. fail("Successfully served ref with value " + c.getRef(master));
  1460. } catch (TransportException err) {
  1461. assertTrue("Unexpected exception message " + err.getMessage(),
  1462. err.getMessage().contains("Internal server error"));
  1463. }
  1464. } finally {
  1465. noRefServer.tearDown();
  1466. }
  1467. }
  1468. @Test
  1469. public void testPush_NotAuthorized() throws Exception {
  1470. final TestRepository src = createTestRepository();
  1471. final RevBlob Q_txt = src.blob("new text");
  1472. final RevCommit Q = src.commit().add("Q", Q_txt).create();
  1473. final Repository db = src.getRepository();
  1474. final String dstName = Constants.R_HEADS + "new.branch";
  1475. // push anonymous shouldn't be allowed.
  1476. //
  1477. try (Transport t = Transport.open(db, remoteURI)) {
  1478. final String srcExpr = Q.name();
  1479. final boolean forceUpdate = false;
  1480. final String localName = null;
  1481. final ObjectId oldId = null;
  1482. RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
  1483. srcExpr, dstName, forceUpdate, localName, oldId);
  1484. try {
  1485. t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
  1486. fail("anonymous push incorrectly accepted without error");
  1487. } catch (TransportException e) {
  1488. final String exp = remoteURI + ": "
  1489. + JGitText.get().authenticationNotSupported;
  1490. assertEquals(exp, e.getMessage());
  1491. }
  1492. }
  1493. List<AccessEvent> requests = getRequests();
  1494. assertEquals(1, requests.size());
  1495. AccessEvent info = requests.get(0);
  1496. assertEquals("GET", info.getMethod());
  1497. assertEquals(join(remoteURI, "info/refs"), info.getPath());
  1498. assertEquals(1, info.getParameters().size());
  1499. assertEquals("git-receive-pack", info.getParameter("service"));
  1500. assertEquals(401, info.getStatus());
  1501. }
  1502. @Test
  1503. public void testPush_CreateBranch() throws Exception {
  1504. final TestRepository src = createTestRepository();
  1505. final RevBlob Q_txt = src.blob("new text");
  1506. final RevCommit Q = src.commit().add("Q", Q_txt).create();
  1507. final Repository db = src.getRepository();
  1508. final String dstName = Constants.R_HEADS + "new.branch";
  1509. enableReceivePack();
  1510. try (Transport t = Transport.open(db, remoteURI)) {
  1511. final String srcExpr = Q.name();
  1512. final boolean forceUpdate = false;
  1513. final String localName = null;
  1514. final ObjectId oldId = null;
  1515. RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
  1516. srcExpr, dstName, forceUpdate, localName, oldId);
  1517. t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
  1518. }
  1519. assertTrue(remoteRepository.getObjectDatabase().has(Q_txt));
  1520. assertNotNull("has " + dstName, remoteRepository.exactRef(dstName));
  1521. assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
  1522. fsck(remoteRepository, Q);
  1523. final ReflogReader log = remoteRepository.getReflogReader(dstName);
  1524. assertNotNull("has log for " + dstName, log);
  1525. final ReflogEntry last = log.getLastEntry();
  1526. assertNotNull("has last entry", last);
  1527. assertEquals(ObjectId.zeroId(), last.getOldId());
  1528. assertEquals(Q, last.getNewId());
  1529. assertEquals("anonymous", last.getWho().getName());
  1530. // Assumption: The host name we use to contact the server should
  1531. // be the server's own host name, because it should be the loopback
  1532. // network interface.
  1533. //
  1534. final String clientHost = remoteURI.getHost();
  1535. assertEquals("anonymous@" + clientHost, last.getWho().getEmailAddress());
  1536. assertEquals("push: created", last.getComment());
  1537. List<AccessEvent> requests = getRequests();
  1538. assertEquals(2, requests.size());
  1539. AccessEvent info = requests.get(0);
  1540. assertEquals("GET", info.getMethod());
  1541. assertEquals(join(remoteURI, "info/refs"), info.getPath());
  1542. assertEquals(1, info.getParameters().size());
  1543. assertEquals("git-receive-pack", info.getParameter("service"));
  1544. assertEquals(200, info.getStatus());
  1545. assertEquals("application/x-git-receive-pack-advertisement", info
  1546. .getResponseHeader(HDR_CONTENT_TYPE));
  1547. AccessEvent service = requests.get(1);
  1548. assertEquals("POST", service.getMethod());
  1549. assertEquals(join(remoteURI, "git-receive-pack"), service.getPath());
  1550. assertEquals(0, service.getParameters().size());
  1551. assertNotNull("has content-length", service
  1552. .getRequestHeader(HDR_CONTENT_LENGTH));
  1553. assertNull("not chunked", service
  1554. .getRequestHeader(HDR_TRANSFER_ENCODING));
  1555. assertEquals(200, service.getStatus());
  1556. assertEquals("application/x-git-receive-pack-result", service
  1557. .getResponseHeader(HDR_CONTENT_TYPE));
  1558. }
  1559. @Test
  1560. public void testPush_ChunkedEncoding() throws Exception {
  1561. final TestRepository<Repository> src = createTestRepository();
  1562. final RevBlob Q_bin = src.blob(new TestRng("Q").nextBytes(128 * 1024));
  1563. final RevCommit Q = src.commit().add("Q", Q_bin).create();
  1564. final Repository db = src.getRepository();
  1565. final String dstName = Constants.R_HEADS + "new.branch";
  1566. enableReceivePack();
  1567. final StoredConfig cfg = db.getConfig();
  1568. cfg.setInt("core", null, "compression", 0);
  1569. cfg.setInt("http", null, "postbuffer", 8 * 1024);
  1570. cfg.save();
  1571. try (Transport t = Transport.open(db, remoteURI)) {
  1572. final String srcExpr = Q.name();
  1573. final boolean forceUpdate = false;
  1574. final String localName = null;
  1575. final ObjectId oldId = null;
  1576. RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
  1577. srcExpr, dstName, forceUpdate, localName, oldId);
  1578. t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
  1579. }
  1580. assertTrue(remoteRepository.getObjectDatabase().has(Q_bin));
  1581. assertNotNull("has " + dstName, remoteRepository.exactRef(dstName));
  1582. assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
  1583. fsck(remoteRepository, Q);
  1584. List<AccessEvent> requests = getRequests();
  1585. assertEquals(2, requests.size());
  1586. AccessEvent info = requests.get(0);
  1587. assertEquals("GET", info.getMethod());
  1588. assertEquals(join(remoteURI, "info/refs"), info.getPath());
  1589. assertEquals(1, info.getParameters().size());
  1590. assertEquals("git-receive-pack", info.getParameter("service"));
  1591. assertEquals(200, info.getStatus());
  1592. assertEquals("application/x-git-receive-pack-advertisement", info
  1593. .getResponseHeader(HDR_CONTENT_TYPE));
  1594. AccessEvent service = requests.get(1);
  1595. assertEquals("POST", service.getMethod());
  1596. assertEquals(join(remoteURI, "git-receive-pack"), service.getPath());
  1597. assertEquals(0, service.getParameters().size());
  1598. assertNull("no content-length", service
  1599. .getRequestHeader(HDR_CONTENT_LENGTH));
  1600. assertEquals("chunked", service.getRequestHeader(HDR_TRANSFER_ENCODING));
  1601. assertEquals(200, service.getStatus());
  1602. assertEquals("application/x-git-receive-pack-result", service
  1603. .getResponseHeader(HDR_CONTENT_TYPE));
  1604. }
  1605. private void enableReceivePack() throws IOException {
  1606. final StoredConfig cfg = remoteRepository.getConfig();
  1607. cfg.setBoolean("http", null, "receivepack", true);
  1608. cfg.save();
  1609. }
  1610. }