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.

UploadPack.java 64KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108
  1. /*
  2. * Copyright (C) 2008-2010, Google Inc.
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.transport;
  44. import static org.eclipse.jgit.lib.Constants.R_TAGS;
  45. import static org.eclipse.jgit.lib.RefDatabase.ALL;
  46. import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REF_IN_WANT;
  47. import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_FETCH;
  48. import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_LS_REFS;
  49. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
  50. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_REACHABLE_SHA1_IN_WANT;
  51. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT;
  52. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_DEEPEN_RELATIVE;
  53. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_FILTER;
  54. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_INCLUDE_TAG;
  55. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK;
  56. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK_DETAILED;
  57. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_DONE;
  58. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_PROGRESS;
  59. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_OFS_DELTA;
  60. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SHALLOW;
  61. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
  62. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
  63. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
  64. import java.io.ByteArrayOutputStream;
  65. import java.io.EOFException;
  66. import java.io.IOException;
  67. import java.io.InputStream;
  68. import java.io.OutputStream;
  69. import java.text.MessageFormat;
  70. import java.util.ArrayList;
  71. import java.util.Collection;
  72. import java.util.Collections;
  73. import java.util.HashMap;
  74. import java.util.HashSet;
  75. import java.util.List;
  76. import java.util.Map;
  77. import java.util.Set;
  78. import org.eclipse.jgit.annotations.Nullable;
  79. import org.eclipse.jgit.errors.CorruptObjectException;
  80. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  81. import org.eclipse.jgit.errors.MissingObjectException;
  82. import org.eclipse.jgit.errors.PackProtocolException;
  83. import org.eclipse.jgit.internal.JGitText;
  84. import org.eclipse.jgit.internal.storage.pack.PackWriter;
  85. import org.eclipse.jgit.lib.BitmapIndex;
  86. import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
  87. import org.eclipse.jgit.lib.Constants;
  88. import org.eclipse.jgit.lib.NullProgressMonitor;
  89. import org.eclipse.jgit.lib.ObjectId;
  90. import org.eclipse.jgit.lib.ObjectReader;
  91. import org.eclipse.jgit.lib.ProgressMonitor;
  92. import org.eclipse.jgit.lib.Ref;
  93. import org.eclipse.jgit.lib.Repository;
  94. import org.eclipse.jgit.revwalk.AsyncRevObjectQueue;
  95. import org.eclipse.jgit.revwalk.BitmapWalker;
  96. import org.eclipse.jgit.revwalk.DepthWalk;
  97. import org.eclipse.jgit.revwalk.ObjectWalk;
  98. import org.eclipse.jgit.revwalk.RevCommit;
  99. import org.eclipse.jgit.revwalk.RevFlag;
  100. import org.eclipse.jgit.revwalk.RevFlagSet;
  101. import org.eclipse.jgit.revwalk.RevObject;
  102. import org.eclipse.jgit.revwalk.RevTag;
  103. import org.eclipse.jgit.revwalk.RevWalk;
  104. import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
  105. import org.eclipse.jgit.storage.pack.PackConfig;
  106. import org.eclipse.jgit.storage.pack.PackStatistics;
  107. import org.eclipse.jgit.transport.GitProtocolConstants.MultiAck;
  108. import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
  109. import org.eclipse.jgit.transport.TransferConfig.ProtocolVersion;
  110. import org.eclipse.jgit.util.io.InterruptTimer;
  111. import org.eclipse.jgit.util.io.NullOutputStream;
  112. import org.eclipse.jgit.util.io.TimeoutInputStream;
  113. import org.eclipse.jgit.util.io.TimeoutOutputStream;
  114. /**
  115. * Implements the server side of a fetch connection, transmitting objects.
  116. */
  117. public class UploadPack {
  118. /** Policy the server uses to validate client requests */
  119. public static enum RequestPolicy {
  120. /** Client may only ask for objects the server advertised a reference for. */
  121. ADVERTISED,
  122. /**
  123. * Client may ask for any commit reachable from a reference advertised by
  124. * the server.
  125. */
  126. REACHABLE_COMMIT,
  127. /**
  128. * Client may ask for objects that are the tip of any reference, even if not
  129. * advertised.
  130. * <p>
  131. * This may happen, for example, when a custom {@link RefFilter} is set.
  132. *
  133. * @since 3.1
  134. */
  135. TIP,
  136. /**
  137. * Client may ask for any commit reachable from any reference, even if that
  138. * reference wasn't advertised.
  139. *
  140. * @since 3.1
  141. */
  142. REACHABLE_COMMIT_TIP,
  143. /** Client may ask for any SHA-1 in the repository. */
  144. ANY;
  145. }
  146. /**
  147. * Validator for client requests.
  148. *
  149. * @since 3.1
  150. */
  151. public interface RequestValidator {
  152. /**
  153. * Check a list of client wants against the request policy.
  154. *
  155. * @param up
  156. * {@link UploadPack} instance.
  157. * @param wants
  158. * objects the client requested that were not advertised.
  159. *
  160. * @throws PackProtocolException
  161. * if one or more wants is not valid.
  162. * @throws IOException
  163. * if a low-level exception occurred.
  164. * @since 3.1
  165. */
  166. void checkWants(UploadPack up, List<ObjectId> wants)
  167. throws PackProtocolException, IOException;
  168. }
  169. /** Data in the first line of a request, the line itself plus options. */
  170. public static class FirstLine {
  171. private final String line;
  172. private final Set<String> options;
  173. /**
  174. * Parse the first line of a receive-pack request.
  175. *
  176. * @param line
  177. * line from the client.
  178. */
  179. public FirstLine(String line) {
  180. if (line.length() > 45) {
  181. final HashSet<String> opts = new HashSet<>();
  182. String opt = line.substring(45);
  183. if (opt.startsWith(" ")) //$NON-NLS-1$
  184. opt = opt.substring(1);
  185. for (String c : opt.split(" ")) //$NON-NLS-1$
  186. opts.add(c);
  187. this.line = line.substring(0, 45);
  188. this.options = Collections.unmodifiableSet(opts);
  189. } else {
  190. this.line = line;
  191. this.options = Collections.emptySet();
  192. }
  193. }
  194. /** @return non-capabilities part of the line. */
  195. public String getLine() {
  196. return line;
  197. }
  198. /** @return options parsed from the line. */
  199. public Set<String> getOptions() {
  200. return options;
  201. }
  202. }
  203. /*
  204. * {@link java.util.function.Consumer} doesn't allow throwing checked
  205. * exceptions. Define our own to propagate IOExceptions.
  206. */
  207. @FunctionalInterface
  208. private static interface IOConsumer<R> {
  209. void accept(R t) throws IOException;
  210. }
  211. /** Database we read the objects from. */
  212. private final Repository db;
  213. /** Revision traversal support over {@link #db}. */
  214. private final RevWalk walk;
  215. /** Configuration to pass into the PackWriter. */
  216. private PackConfig packConfig;
  217. /** Configuration for various transfer options. */
  218. private TransferConfig transferConfig;
  219. /** Timeout in seconds to wait for client interaction. */
  220. private int timeout;
  221. /**
  222. * Is the client connection a bi-directional socket or pipe?
  223. * <p>
  224. * If true, this class assumes it can perform multiple read and write cycles
  225. * with the client over the input and output streams. This matches the
  226. * functionality available with a standard TCP/IP connection, or a local
  227. * operating system or in-memory pipe.
  228. * <p>
  229. * If false, this class runs in a read everything then output results mode,
  230. * making it suitable for single round-trip systems RPCs such as HTTP.
  231. */
  232. private boolean biDirectionalPipe = true;
  233. /** Timer to manage {@link #timeout}. */
  234. private InterruptTimer timer;
  235. /**
  236. * Whether the client requested to use protocol V2 through a side
  237. * channel (such as the Git-Protocol HTTP header).
  238. */
  239. private boolean clientRequestedV2;
  240. private InputStream rawIn;
  241. private ResponseBufferedOutputStream rawOut;
  242. private PacketLineIn pckIn;
  243. private PacketLineOut pckOut;
  244. private OutputStream msgOut = NullOutputStream.INSTANCE;
  245. /** The refs we advertised as existing at the start of the connection. */
  246. private Map<String, Ref> refs;
  247. /** Hook used while processing Git protocol v2 requests. */
  248. private ProtocolV2Hook protocolV2Hook = ProtocolV2Hook.DEFAULT;
  249. /** Hook used while advertising the refs to the client. */
  250. private AdvertiseRefsHook advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
  251. /** Filter used while advertising the refs to the client. */
  252. private RefFilter refFilter = RefFilter.DEFAULT;
  253. /** Hook handling the various upload phases. */
  254. private PreUploadHook preUploadHook = PreUploadHook.NULL;
  255. /** Hook for taking post upload actions. */
  256. private PostUploadHook postUploadHook = PostUploadHook.NULL;
  257. /** Capabilities requested by the client. */
  258. private Set<String> options;
  259. String userAgent;
  260. /** Raw ObjectIds the client has asked for, before validating them. */
  261. private final Set<ObjectId> wantIds = new HashSet<>();
  262. /** Objects the client wants to obtain. */
  263. private final Set<RevObject> wantAll = new HashSet<>();
  264. /** Objects on both sides, these don't have to be sent. */
  265. private final Set<RevObject> commonBase = new HashSet<>();
  266. /** Shallow commits the client already has. */
  267. private Set<ObjectId> clientShallowCommits = new HashSet<>();
  268. /** Desired depth from the client on a shallow request. */
  269. private int depth;
  270. /**
  271. * Commit time of the newest objects the client has asked us using
  272. * --shallow-since not to send. Cannot be nonzero if depth is nonzero.
  273. */
  274. private int shallowSince;
  275. /**
  276. * (Possibly short) ref names, ancestors of which the client has asked us
  277. * not to send using --shallow-exclude. Cannot be non-empty if depth is
  278. * nonzero.
  279. */
  280. private List<String> deepenNotRefs = new ArrayList<>();
  281. /** Commit time of the oldest common commit, in seconds. */
  282. private int oldestTime;
  283. /** null if {@link #commonBase} should be examined again. */
  284. private Boolean okToGiveUp;
  285. private boolean sentReady;
  286. /** Objects we sent in our advertisement list, clients can ask for these. */
  287. private Set<ObjectId> advertised;
  288. /** Marked on objects the client has asked us to give them. */
  289. private final RevFlag WANT;
  290. /** Marked on objects both we and the client have. */
  291. private final RevFlag PEER_HAS;
  292. /** Marked on objects in {@link #commonBase}. */
  293. private final RevFlag COMMON;
  294. /** Objects where we found a path from the want list to a common base. */
  295. private final RevFlag SATISFIED;
  296. private final RevFlagSet SAVE;
  297. private RequestValidator requestValidator = new AdvertisedRequestValidator();
  298. private MultiAck multiAck = MultiAck.OFF;
  299. private boolean noDone;
  300. private PackStatistics statistics;
  301. private long filterBlobLimit = -1;
  302. /**
  303. * Create a new pack upload for an open repository.
  304. *
  305. * @param copyFrom
  306. * the source repository.
  307. */
  308. public UploadPack(Repository copyFrom) {
  309. db = copyFrom;
  310. walk = new RevWalk(db);
  311. walk.setRetainBody(false);
  312. WANT = walk.newFlag("WANT"); //$NON-NLS-1$
  313. PEER_HAS = walk.newFlag("PEER_HAS"); //$NON-NLS-1$
  314. COMMON = walk.newFlag("COMMON"); //$NON-NLS-1$
  315. SATISFIED = walk.newFlag("SATISFIED"); //$NON-NLS-1$
  316. walk.carry(PEER_HAS);
  317. SAVE = new RevFlagSet();
  318. SAVE.add(WANT);
  319. SAVE.add(PEER_HAS);
  320. SAVE.add(COMMON);
  321. SAVE.add(SATISFIED);
  322. setTransferConfig(null);
  323. }
  324. /**
  325. * Get the repository this upload is reading from.
  326. *
  327. * @return the repository this upload is reading from.
  328. */
  329. public final Repository getRepository() {
  330. return db;
  331. }
  332. /**
  333. * Get the RevWalk instance used by this connection.
  334. *
  335. * @return the RevWalk instance used by this connection.
  336. */
  337. public final RevWalk getRevWalk() {
  338. return walk;
  339. }
  340. /**
  341. * Get refs which were advertised to the client.
  342. *
  343. * @return all refs which were advertised to the client, or null if
  344. * {@link #setAdvertisedRefs(Map)} has not been called yet.
  345. */
  346. public final Map<String, Ref> getAdvertisedRefs() {
  347. return refs;
  348. }
  349. /**
  350. * Set the refs advertised by this UploadPack.
  351. * <p>
  352. * Intended to be called from a
  353. * {@link org.eclipse.jgit.transport.PreUploadHook}.
  354. *
  355. * @param allRefs
  356. * explicit set of references to claim as advertised by this
  357. * UploadPack instance. This overrides any references that may
  358. * exist in the source repository. The map is passed to the
  359. * configured {@link #getRefFilter()}. If null, assumes all refs
  360. * were advertised.
  361. */
  362. public void setAdvertisedRefs(Map<String, Ref> allRefs) {
  363. if (allRefs != null)
  364. refs = allRefs;
  365. else
  366. refs = db.getAllRefs();
  367. if (refFilter == RefFilter.DEFAULT)
  368. refs = transferConfig.getRefFilter().filter(refs);
  369. else
  370. refs = refFilter.filter(refs);
  371. }
  372. /**
  373. * Get timeout (in seconds) before aborting an IO operation.
  374. *
  375. * @return timeout (in seconds) before aborting an IO operation.
  376. */
  377. public int getTimeout() {
  378. return timeout;
  379. }
  380. /**
  381. * Set the timeout before willing to abort an IO call.
  382. *
  383. * @param seconds
  384. * number of seconds to wait (with no data transfer occurring)
  385. * before aborting an IO read or write operation with the
  386. * connected client.
  387. */
  388. public void setTimeout(int seconds) {
  389. timeout = seconds;
  390. }
  391. /**
  392. * Whether this class expects a bi-directional pipe opened between the
  393. * client and itself.
  394. *
  395. * @return true if this class expects a bi-directional pipe opened between
  396. * the client and itself. The default is true.
  397. */
  398. public boolean isBiDirectionalPipe() {
  399. return biDirectionalPipe;
  400. }
  401. /**
  402. * Set whether this class will assume the socket is a fully bidirectional
  403. * pipe between the two peers
  404. *
  405. * @param twoWay
  406. * if true, this class will assume the socket is a fully
  407. * bidirectional pipe between the two peers and takes advantage
  408. * of that by first transmitting the known refs, then waiting to
  409. * read commands. If false, this class assumes it must read the
  410. * commands before writing output and does not perform the
  411. * initial advertising.
  412. */
  413. public void setBiDirectionalPipe(boolean twoWay) {
  414. biDirectionalPipe = twoWay;
  415. }
  416. /**
  417. * Get policy used by the service to validate client requests
  418. *
  419. * @return policy used by the service to validate client requests, or null
  420. * for a custom request validator.
  421. */
  422. public RequestPolicy getRequestPolicy() {
  423. if (requestValidator instanceof AdvertisedRequestValidator)
  424. return RequestPolicy.ADVERTISED;
  425. if (requestValidator instanceof ReachableCommitRequestValidator)
  426. return RequestPolicy.REACHABLE_COMMIT;
  427. if (requestValidator instanceof TipRequestValidator)
  428. return RequestPolicy.TIP;
  429. if (requestValidator instanceof ReachableCommitTipRequestValidator)
  430. return RequestPolicy.REACHABLE_COMMIT_TIP;
  431. if (requestValidator instanceof AnyRequestValidator)
  432. return RequestPolicy.ANY;
  433. return null;
  434. }
  435. /**
  436. * Set the policy used to enforce validation of a client's want list.
  437. *
  438. * @param policy
  439. * the policy used to enforce validation of a client's want list.
  440. * By default the policy is
  441. * {@link org.eclipse.jgit.transport.UploadPack.RequestPolicy#ADVERTISED},
  442. * which is the Git default requiring clients to only ask for an
  443. * object that a reference directly points to. This may be
  444. * relaxed to
  445. * {@link org.eclipse.jgit.transport.UploadPack.RequestPolicy#REACHABLE_COMMIT}
  446. * or
  447. * {@link org.eclipse.jgit.transport.UploadPack.RequestPolicy#REACHABLE_COMMIT_TIP}
  448. * when callers have {@link #setBiDirectionalPipe(boolean)} set
  449. * to false. Overrides any policy specified in a
  450. * {@link org.eclipse.jgit.transport.TransferConfig}.
  451. */
  452. public void setRequestPolicy(RequestPolicy policy) {
  453. switch (policy) {
  454. case ADVERTISED:
  455. default:
  456. requestValidator = new AdvertisedRequestValidator();
  457. break;
  458. case REACHABLE_COMMIT:
  459. requestValidator = new ReachableCommitRequestValidator();
  460. break;
  461. case TIP:
  462. requestValidator = new TipRequestValidator();
  463. break;
  464. case REACHABLE_COMMIT_TIP:
  465. requestValidator = new ReachableCommitTipRequestValidator();
  466. break;
  467. case ANY:
  468. requestValidator = new AnyRequestValidator();
  469. break;
  470. }
  471. }
  472. /**
  473. * Set custom validator for client want list.
  474. *
  475. * @param validator
  476. * custom validator for client want list.
  477. * @since 3.1
  478. */
  479. public void setRequestValidator(RequestValidator validator) {
  480. requestValidator = validator != null ? validator
  481. : new AdvertisedRequestValidator();
  482. }
  483. /**
  484. * Get the hook used while advertising the refs to the client.
  485. *
  486. * @return the hook used while advertising the refs to the client.
  487. */
  488. public AdvertiseRefsHook getAdvertiseRefsHook() {
  489. return advertiseRefsHook;
  490. }
  491. /**
  492. * Get the filter used while advertising the refs to the client.
  493. *
  494. * @return the filter used while advertising the refs to the client.
  495. */
  496. public RefFilter getRefFilter() {
  497. return refFilter;
  498. }
  499. /**
  500. * Set the hook used while advertising the refs to the client.
  501. * <p>
  502. * If the {@link org.eclipse.jgit.transport.AdvertiseRefsHook} chooses to
  503. * call {@link #setAdvertisedRefs(Map)}, only refs set by this hook
  504. * <em>and</em> selected by the {@link org.eclipse.jgit.transport.RefFilter}
  505. * will be shown to the client.
  506. *
  507. * @param advertiseRefsHook
  508. * the hook; may be null to show all refs.
  509. */
  510. public void setAdvertiseRefsHook(AdvertiseRefsHook advertiseRefsHook) {
  511. if (advertiseRefsHook != null)
  512. this.advertiseRefsHook = advertiseRefsHook;
  513. else
  514. this.advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
  515. }
  516. /**
  517. * Set the protocol V2 hook.
  518. *
  519. * @param hook
  520. * @since 5.1
  521. */
  522. public void setProtocolV2Hook(ProtocolV2Hook hook) {
  523. this.protocolV2Hook = hook;
  524. }
  525. /**
  526. * Set the filter used while advertising the refs to the client.
  527. * <p>
  528. * Only refs allowed by this filter will be sent to the client. The filter
  529. * is run against the refs specified by the
  530. * {@link org.eclipse.jgit.transport.AdvertiseRefsHook} (if applicable). If
  531. * null or not set, uses the filter implied by the
  532. * {@link org.eclipse.jgit.transport.TransferConfig}.
  533. *
  534. * @param refFilter
  535. * the filter; may be null to show all refs.
  536. */
  537. public void setRefFilter(RefFilter refFilter) {
  538. this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
  539. }
  540. /**
  541. * Get the configured pre upload hook.
  542. *
  543. * @return the configured pre upload hook.
  544. */
  545. public PreUploadHook getPreUploadHook() {
  546. return preUploadHook;
  547. }
  548. /**
  549. * Set the hook that controls how this instance will behave.
  550. *
  551. * @param hook
  552. * the hook; if null no special actions are taken.
  553. */
  554. public void setPreUploadHook(PreUploadHook hook) {
  555. preUploadHook = hook != null ? hook : PreUploadHook.NULL;
  556. }
  557. /**
  558. * Get the configured post upload hook.
  559. *
  560. * @return the configured post upload hook.
  561. * @since 4.1
  562. */
  563. public PostUploadHook getPostUploadHook() {
  564. return postUploadHook;
  565. }
  566. /**
  567. * Set the hook for post upload actions (logging, repacking).
  568. *
  569. * @param hook
  570. * the hook; if null no special actions are taken.
  571. * @since 4.1
  572. */
  573. public void setPostUploadHook(PostUploadHook hook) {
  574. postUploadHook = hook != null ? hook : PostUploadHook.NULL;
  575. }
  576. /**
  577. * Set the configuration used by the pack generator.
  578. *
  579. * @param pc
  580. * configuration controlling packing parameters. If null the
  581. * source repository's settings will be used.
  582. */
  583. public void setPackConfig(PackConfig pc) {
  584. this.packConfig = pc;
  585. }
  586. /**
  587. * Set configuration controlling transfer options.
  588. *
  589. * @param tc
  590. * configuration controlling transfer options. If null the source
  591. * repository's settings will be used.
  592. * @since 3.1
  593. */
  594. public void setTransferConfig(TransferConfig tc) {
  595. this.transferConfig = tc != null ? tc : new TransferConfig(db);
  596. if (transferConfig.isAllowTipSha1InWant()) {
  597. setRequestPolicy(transferConfig.isAllowReachableSha1InWant()
  598. ? RequestPolicy.REACHABLE_COMMIT_TIP : RequestPolicy.TIP);
  599. } else {
  600. setRequestPolicy(transferConfig.isAllowReachableSha1InWant()
  601. ? RequestPolicy.REACHABLE_COMMIT : RequestPolicy.ADVERTISED);
  602. }
  603. }
  604. /**
  605. * Check whether the client expects a side-band stream.
  606. *
  607. * @return true if the client has advertised a side-band capability, false
  608. * otherwise.
  609. * @throws org.eclipse.jgit.transport.RequestNotYetReadException
  610. * if the client's request has not yet been read from the wire, so
  611. * we do not know if they expect side-band. Note that the client
  612. * may have already written the request, it just has not been
  613. * read.
  614. */
  615. public boolean isSideBand() throws RequestNotYetReadException {
  616. if (options == null)
  617. throw new RequestNotYetReadException();
  618. return (options.contains(OPTION_SIDE_BAND)
  619. || options.contains(OPTION_SIDE_BAND_64K));
  620. }
  621. /**
  622. * Set the Extra Parameters provided by the client.
  623. *
  624. * <p>These are parameters passed by the client through a side channel
  625. * such as the Git-Protocol HTTP header, to allow a client to request
  626. * a newer response format while remaining compatible with older servers
  627. * that do not understand different request formats.
  628. *
  629. * @param params
  630. * parameters supplied by the client, split at colons or NUL
  631. * bytes.
  632. * @since 5.0
  633. */
  634. public void setExtraParameters(Collection<String> params) {
  635. this.clientRequestedV2 = params.contains("version=2"); //$NON-NLS-1$
  636. }
  637. private boolean useProtocolV2() {
  638. return ProtocolVersion.V2.equals(transferConfig.protocolVersion)
  639. && clientRequestedV2;
  640. }
  641. /**
  642. * Execute the upload task on the socket.
  643. *
  644. * <p>If the client passed extra parameters (e.g., "version=2") through a
  645. * side channel, the caller must call setExtraParameters first to supply
  646. * them.
  647. *
  648. * @param input
  649. * raw input to read client commands from. Caller must ensure the
  650. * input is buffered, otherwise read performance may suffer.
  651. * @param output
  652. * response back to the Git network client, to write the pack
  653. * data onto. Caller must ensure the output is buffered,
  654. * otherwise write performance may suffer.
  655. * @param messages
  656. * secondary "notice" channel to send additional messages out
  657. * through. When run over SSH this should be tied back to the
  658. * standard error channel of the command execution. For most
  659. * other network connections this should be null.
  660. * @throws java.io.IOException
  661. */
  662. public void upload(final InputStream input, OutputStream output,
  663. final OutputStream messages) throws IOException {
  664. try {
  665. rawIn = input;
  666. if (messages != null)
  667. msgOut = messages;
  668. if (timeout > 0) {
  669. final Thread caller = Thread.currentThread();
  670. timer = new InterruptTimer(caller.getName() + "-Timer"); //$NON-NLS-1$
  671. TimeoutInputStream i = new TimeoutInputStream(rawIn, timer);
  672. @SuppressWarnings("resource")
  673. TimeoutOutputStream o = new TimeoutOutputStream(output, timer);
  674. i.setTimeout(timeout * 1000);
  675. o.setTimeout(timeout * 1000);
  676. rawIn = i;
  677. output = o;
  678. }
  679. rawOut = new ResponseBufferedOutputStream(output);
  680. if (biDirectionalPipe) {
  681. rawOut.stopBuffering();
  682. }
  683. pckIn = new PacketLineIn(rawIn);
  684. pckOut = new PacketLineOut(rawOut);
  685. if (useProtocolV2()) {
  686. serviceV2();
  687. } else {
  688. service();
  689. }
  690. } finally {
  691. msgOut = NullOutputStream.INSTANCE;
  692. walk.close();
  693. if (timer != null) {
  694. try {
  695. timer.terminate();
  696. } finally {
  697. timer = null;
  698. }
  699. }
  700. }
  701. }
  702. /**
  703. * Get the PackWriter's statistics if a pack was sent to the client.
  704. *
  705. * @return statistics about pack output, if a pack was sent. Null if no pack
  706. * was sent, such as during the negotiation phase of a smart HTTP
  707. * connection, or if the client was already up-to-date.
  708. * @since 4.1
  709. */
  710. public PackStatistics getStatistics() {
  711. return statistics;
  712. }
  713. private Map<String, Ref> getAdvertisedOrDefaultRefs() throws IOException {
  714. if (refs == null)
  715. setAdvertisedRefs(db.getRefDatabase().getRefs(ALL));
  716. return refs;
  717. }
  718. private void service() throws IOException {
  719. boolean sendPack = false;
  720. // If it's a non-bidi request, we need to read the entire request before
  721. // writing a response. Buffer the response until then.
  722. PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator();
  723. List<ObjectId> unshallowCommits = new ArrayList<>();
  724. try {
  725. if (biDirectionalPipe)
  726. sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
  727. else if (requestValidator instanceof AnyRequestValidator)
  728. advertised = Collections.emptySet();
  729. else
  730. advertised = refIdSet(getAdvertisedOrDefaultRefs().values());
  731. long negotiateStart = System.currentTimeMillis();
  732. accumulator.advertised = advertised.size();
  733. recvWants();
  734. if (wantIds.isEmpty()) {
  735. preUploadHook.onBeginNegotiateRound(this, wantIds, 0);
  736. preUploadHook.onEndNegotiateRound(this, wantIds, 0, 0, false);
  737. return;
  738. }
  739. accumulator.wants = wantIds.size();
  740. if (options.contains(OPTION_MULTI_ACK_DETAILED)) {
  741. multiAck = MultiAck.DETAILED;
  742. noDone = options.contains(OPTION_NO_DONE);
  743. } else if (options.contains(OPTION_MULTI_ACK))
  744. multiAck = MultiAck.CONTINUE;
  745. else
  746. multiAck = MultiAck.OFF;
  747. if (!clientShallowCommits.isEmpty())
  748. verifyClientShallow(clientShallowCommits);
  749. if (depth != 0) {
  750. computeShallowsAndUnshallows(wantIds, shallow -> {
  751. pckOut.writeString("shallow " + shallow.name() + '\n'); //$NON-NLS-1$
  752. }, unshallow -> {
  753. pckOut.writeString("unshallow " + unshallow.name() + '\n'); //$NON-NLS-1$
  754. unshallowCommits.add(unshallow);
  755. });
  756. pckOut.end();
  757. }
  758. if (!clientShallowCommits.isEmpty())
  759. walk.assumeShallow(clientShallowCommits);
  760. sendPack = negotiate(accumulator);
  761. accumulator.timeNegotiating += System.currentTimeMillis()
  762. - negotiateStart;
  763. if (sendPack && !biDirectionalPipe) {
  764. // Ensure the request was fully consumed. Any remaining input must
  765. // be a protocol error. If we aren't at EOF the implementation is broken.
  766. int eof = rawIn.read();
  767. if (0 <= eof) {
  768. sendPack = false;
  769. throw new CorruptObjectException(MessageFormat.format(
  770. JGitText.get().expectedEOFReceived,
  771. "\\x" + Integer.toHexString(eof))); //$NON-NLS-1$
  772. }
  773. }
  774. } catch (ServiceMayNotContinueException err) {
  775. if (!err.isOutput() && err.getMessage() != null) {
  776. try {
  777. pckOut.writeString("ERR " + err.getMessage() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  778. err.setOutput();
  779. } catch (Throwable err2) {
  780. // Ignore this secondary failure (and not mark output).
  781. }
  782. }
  783. throw err;
  784. } catch (IOException | RuntimeException | Error err) {
  785. boolean output = false;
  786. try {
  787. String msg = err instanceof PackProtocolException
  788. ? err.getMessage()
  789. : JGitText.get().internalServerError;
  790. pckOut.writeString("ERR " + msg + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  791. output = true;
  792. } catch (Throwable err2) {
  793. // Ignore this secondary failure, leave output false.
  794. }
  795. if (output) {
  796. throw new UploadPackInternalServerErrorException(err);
  797. }
  798. throw err;
  799. } finally {
  800. if (!sendPack && !biDirectionalPipe) {
  801. while (0 < rawIn.skip(2048) || 0 <= rawIn.read()) {
  802. // Discard until EOF.
  803. }
  804. }
  805. rawOut.stopBuffering();
  806. }
  807. if (sendPack) {
  808. sendPack(accumulator, refs == null ? null : refs.values(), unshallowCommits);
  809. }
  810. }
  811. private void lsRefsV2() throws IOException {
  812. ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig);
  813. LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
  814. protocolV2Hook.onLsRefs(req);
  815. rawOut.stopBuffering();
  816. PacketLineOutRefAdvertiser adv = new PacketLineOutRefAdvertiser(pckOut);
  817. adv.setUseProtocolV2(true);
  818. if (req.getPeel()) {
  819. adv.setDerefTags(true);
  820. }
  821. Map<String, Ref> refsToSend;
  822. if (req.getRefPrefixes().isEmpty()) {
  823. refsToSend = getAdvertisedOrDefaultRefs();
  824. } else {
  825. refsToSend = new HashMap<>();
  826. String[] prefixes = req.getRefPrefixes().toArray(new String[0]);
  827. for (Ref ref : db.getRefDatabase().getRefsByPrefix(prefixes)) {
  828. refsToSend.put(ref.getName(), ref);
  829. }
  830. }
  831. if (req.getSymrefs()) {
  832. findSymrefs(adv, refsToSend);
  833. }
  834. adv.send(refsToSend);
  835. adv.end();
  836. }
  837. private void fetchV2() throws IOException {
  838. // Depending on the requestValidator, #processHaveLines may
  839. // require that advertised be set. Set it only in the required
  840. // circumstances (to avoid a full ref lookup in the case that
  841. // we don't need it).
  842. if (requestValidator instanceof TipRequestValidator ||
  843. requestValidator instanceof ReachableCommitTipRequestValidator ||
  844. requestValidator instanceof AnyRequestValidator) {
  845. advertised = Collections.emptySet();
  846. } else {
  847. advertised = refIdSet(getAdvertisedOrDefaultRefs().values());
  848. }
  849. ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig);
  850. FetchV2Request req = parser.parseFetchRequest(pckIn,
  851. db.getRefDatabase());
  852. rawOut.stopBuffering();
  853. protocolV2Hook.onFetch(req);
  854. // TODO(ifrade): Refactor to pass around the Request object, instead of
  855. // copying data back to class fields
  856. options = req.getOptions();
  857. wantIds.addAll(req.getWantsIds());
  858. clientShallowCommits = req.getClientShallowCommits();
  859. depth = req.getDepth();
  860. shallowSince = req.getDeepenSince();
  861. filterBlobLimit = req.getFilterBlobLimit();
  862. deepenNotRefs = req.getDeepenNotRefs();
  863. boolean sectionSent = false;
  864. boolean mayHaveShallow = req.getDepth() != 0
  865. || req.getDeepenSince() != 0
  866. || !req.getDeepenNotRefs().isEmpty();
  867. List<ObjectId> shallowCommits = new ArrayList<>();
  868. List<ObjectId> unshallowCommits = new ArrayList<>();
  869. if (!req.getClientShallowCommits().isEmpty()) {
  870. verifyClientShallow(req.getClientShallowCommits());
  871. }
  872. if (mayHaveShallow) {
  873. computeShallowsAndUnshallows(req.getWantsIds(),
  874. shallowCommit -> shallowCommits.add(shallowCommit),
  875. unshallowCommit -> unshallowCommits.add(unshallowCommit));
  876. }
  877. if (!req.getClientShallowCommits().isEmpty())
  878. walk.assumeShallow(req.getClientShallowCommits());
  879. if (req.wasDoneReceived()) {
  880. processHaveLines(req.getPeerHas(), ObjectId.zeroId(),
  881. new PacketLineOut(NullOutputStream.INSTANCE));
  882. } else {
  883. pckOut.writeString("acknowledgments\n"); //$NON-NLS-1$
  884. for (ObjectId id : req.getPeerHas()) {
  885. if (walk.getObjectReader().has(id)) {
  886. pckOut.writeString("ACK " + id.getName() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  887. }
  888. }
  889. processHaveLines(req.getPeerHas(), ObjectId.zeroId(),
  890. new PacketLineOut(NullOutputStream.INSTANCE));
  891. if (okToGiveUp()) {
  892. pckOut.writeString("ready\n"); //$NON-NLS-1$
  893. } else if (commonBase.isEmpty()) {
  894. pckOut.writeString("NAK\n"); //$NON-NLS-1$
  895. }
  896. sectionSent = true;
  897. }
  898. if (req.wasDoneReceived() || okToGiveUp()) {
  899. if (mayHaveShallow) {
  900. if (sectionSent)
  901. pckOut.writeDelim();
  902. pckOut.writeString("shallow-info\n"); //$NON-NLS-1$
  903. for (ObjectId o : shallowCommits) {
  904. pckOut.writeString("shallow " + o.getName() + '\n'); //$NON-NLS-1$
  905. }
  906. for (ObjectId o : unshallowCommits) {
  907. pckOut.writeString("unshallow " + o.getName() + '\n'); //$NON-NLS-1$
  908. }
  909. sectionSent = true;
  910. }
  911. if (!req.getWantedRefs().isEmpty()) {
  912. if (sectionSent) {
  913. pckOut.writeDelim();
  914. }
  915. pckOut.writeString("wanted-refs\n"); //$NON-NLS-1$
  916. for (Map.Entry<String, ObjectId> entry : req.getWantedRefs()
  917. .entrySet()) {
  918. pckOut.writeString(entry.getValue().getName() + ' ' +
  919. entry.getKey() + '\n');
  920. }
  921. sectionSent = true;
  922. }
  923. if (sectionSent)
  924. pckOut.writeDelim();
  925. pckOut.writeString("packfile\n"); //$NON-NLS-1$
  926. sendPack(new PackStatistics.Accumulator(),
  927. req.getOptions().contains(OPTION_INCLUDE_TAG)
  928. ? db.getRefDatabase().getRefsByPrefix(R_TAGS)
  929. : null,
  930. unshallowCommits);
  931. // sendPack invokes pckOut.end() for us, so we do not
  932. // need to invoke it here.
  933. } else {
  934. // Invoke pckOut.end() by ourselves.
  935. pckOut.end();
  936. }
  937. }
  938. /*
  939. * Returns true if this is the last command and we should tear down the
  940. * connection.
  941. */
  942. private boolean serveOneCommandV2() throws IOException {
  943. String command;
  944. try {
  945. command = pckIn.readString();
  946. } catch (EOFException eof) {
  947. /* EOF when awaiting command is fine */
  948. return true;
  949. }
  950. if (command == PacketLineIn.END) {
  951. // A blank request is valid according
  952. // to the protocol; do nothing in this
  953. // case.
  954. return true;
  955. }
  956. if (command.equals("command=" + COMMAND_LS_REFS)) { //$NON-NLS-1$
  957. lsRefsV2();
  958. return false;
  959. }
  960. if (command.equals("command=" + COMMAND_FETCH)) { //$NON-NLS-1$
  961. fetchV2();
  962. return false;
  963. }
  964. throw new PackProtocolException(MessageFormat
  965. .format(JGitText.get().unknownTransportCommand, command));
  966. }
  967. private List<String> getV2CapabilityAdvertisement() {
  968. ArrayList<String> caps = new ArrayList<>();
  969. caps.add("version 2"); //$NON-NLS-1$
  970. caps.add(COMMAND_LS_REFS);
  971. boolean advertiseRefInWant = transferConfig.isAllowRefInWant() &&
  972. db.getConfig().getBoolean("uploadpack", null, //$NON-NLS-1$
  973. "advertiserefinwant", true); //$NON-NLS-1$
  974. caps.add(
  975. COMMAND_FETCH + '=' +
  976. (transferConfig.isAllowFilter() ? OPTION_FILTER + ' ' : "") + //$NON-NLS-1$
  977. (advertiseRefInWant ? CAPABILITY_REF_IN_WANT + ' ' : "") + //$NON-NLS-1$
  978. OPTION_SHALLOW);
  979. return caps;
  980. }
  981. private void serviceV2() throws IOException {
  982. if (biDirectionalPipe) {
  983. // Just like in service(), the capability advertisement
  984. // is sent only if this is a bidirectional pipe. (If
  985. // not, the client is expected to call
  986. // sendAdvertisedRefs() on its own.)
  987. protocolV2Hook
  988. .onCapabilities(CapabilitiesV2Request.builder().build());
  989. for (String s : getV2CapabilityAdvertisement()) {
  990. pckOut.writeString(s + "\n"); //$NON-NLS-1$
  991. }
  992. pckOut.end();
  993. while (!serveOneCommandV2()) {
  994. // Repeat until an empty command or EOF.
  995. }
  996. return;
  997. }
  998. try {
  999. serveOneCommandV2();
  1000. } finally {
  1001. while (0 < rawIn.skip(2048) || 0 <= rawIn.read()) {
  1002. // Discard until EOF.
  1003. }
  1004. rawOut.stopBuffering();
  1005. }
  1006. }
  1007. private static Set<ObjectId> refIdSet(Collection<Ref> refs) {
  1008. Set<ObjectId> ids = new HashSet<>(refs.size());
  1009. for (Ref ref : refs) {
  1010. ObjectId id = ref.getObjectId();
  1011. if (id != null) {
  1012. ids.add(id);
  1013. }
  1014. id = ref.getPeeledObjectId();
  1015. if (id != null) {
  1016. ids.add(id);
  1017. }
  1018. }
  1019. return ids;
  1020. }
  1021. /*
  1022. * Determines what object ids must be marked as shallow or unshallow for the
  1023. * client.
  1024. */
  1025. private void computeShallowsAndUnshallows(Iterable<ObjectId> wants,
  1026. IOConsumer<ObjectId> shallowFunc,
  1027. IOConsumer<ObjectId> unshallowFunc)
  1028. throws IOException {
  1029. if (options.contains(OPTION_DEEPEN_RELATIVE) || shallowSince != 0
  1030. || !deepenNotRefs.isEmpty()) {
  1031. // TODO(jonathantanmy): Implement deepen-relative, deepen-since,
  1032. // and deepen-not.
  1033. throw new UnsupportedOperationException();
  1034. }
  1035. int walkDepth = depth - 1;
  1036. try (DepthWalk.RevWalk depthWalk = new DepthWalk.RevWalk(
  1037. walk.getObjectReader(), walkDepth)) {
  1038. // Find all the commits which will be shallow
  1039. for (ObjectId o : wants) {
  1040. try {
  1041. depthWalk.markRoot(depthWalk.parseCommit(o));
  1042. } catch (IncorrectObjectTypeException notCommit) {
  1043. // Ignore non-commits in this loop.
  1044. }
  1045. }
  1046. RevCommit o;
  1047. while ((o = depthWalk.next()) != null) {
  1048. DepthWalk.Commit c = (DepthWalk.Commit) o;
  1049. // Commits at the boundary which aren't already shallow in
  1050. // the client need to be marked as such
  1051. if (c.getDepth() == walkDepth
  1052. && !clientShallowCommits.contains(c)) {
  1053. shallowFunc.accept(c.copy());
  1054. }
  1055. // Commits not on the boundary which are shallow in the client
  1056. // need to become unshallowed
  1057. if (c.getDepth() < walkDepth
  1058. && clientShallowCommits.remove(c)) {
  1059. unshallowFunc.accept(c.copy());
  1060. }
  1061. }
  1062. }
  1063. }
  1064. /*
  1065. * Verify all shallow lines refer to commits
  1066. *
  1067. * It can mutate the input set (removing missing object ids from it)
  1068. */
  1069. private void verifyClientShallow(Set<ObjectId> shallowCommits)
  1070. throws IOException, PackProtocolException {
  1071. AsyncRevObjectQueue q = walk.parseAny(shallowCommits, true);
  1072. try {
  1073. for (;;) {
  1074. try {
  1075. // Shallow objects named by the client must be commits.
  1076. RevObject o = q.next();
  1077. if (o == null) {
  1078. break;
  1079. }
  1080. if (!(o instanceof RevCommit)) {
  1081. throw new PackProtocolException(
  1082. MessageFormat.format(
  1083. JGitText.get().invalidShallowObject,
  1084. o.name()));
  1085. }
  1086. } catch (MissingObjectException notCommit) {
  1087. // shallow objects not known at the server are ignored
  1088. // by git-core upload-pack, match that behavior.
  1089. shallowCommits.remove(notCommit.getObjectId());
  1090. continue;
  1091. }
  1092. }
  1093. } finally {
  1094. q.release();
  1095. }
  1096. }
  1097. /**
  1098. * Generate an advertisement of available refs and capabilities.
  1099. *
  1100. * @param adv
  1101. * the advertisement formatter.
  1102. * @throws java.io.IOException
  1103. * the formatter failed to write an advertisement.
  1104. * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
  1105. * the hook denied advertisement.
  1106. */
  1107. public void sendAdvertisedRefs(RefAdvertiser adv) throws IOException,
  1108. ServiceMayNotContinueException {
  1109. sendAdvertisedRefs(adv, null);
  1110. }
  1111. /**
  1112. * Generate an advertisement of available refs and capabilities.
  1113. *
  1114. * @param adv
  1115. * the advertisement formatter.
  1116. * @param serviceName
  1117. * if not null, also output "# service=serviceName" followed by a
  1118. * flush packet before the advertisement. This is required
  1119. * in v0 of the HTTP protocol, described in Git's
  1120. * Documentation/technical/http-protocol.txt.
  1121. * @throws java.io.IOException
  1122. * the formatter failed to write an advertisement.
  1123. * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
  1124. * the hook denied advertisement.
  1125. * @since 5.0
  1126. */
  1127. public void sendAdvertisedRefs(RefAdvertiser adv,
  1128. @Nullable String serviceName) throws IOException,
  1129. ServiceMayNotContinueException {
  1130. if (useProtocolV2()) {
  1131. // The equivalent in v2 is only the capabilities
  1132. // advertisement.
  1133. protocolV2Hook
  1134. .onCapabilities(CapabilitiesV2Request.builder().build());
  1135. for (String s : getV2CapabilityAdvertisement()) {
  1136. adv.writeOne(s);
  1137. }
  1138. adv.end();
  1139. return;
  1140. }
  1141. try {
  1142. advertiseRefsHook.advertiseRefs(this);
  1143. } catch (ServiceMayNotContinueException fail) {
  1144. if (fail.getMessage() != null) {
  1145. adv.writeOne("ERR " + fail.getMessage()); //$NON-NLS-1$
  1146. fail.setOutput();
  1147. }
  1148. throw fail;
  1149. }
  1150. if (serviceName != null) {
  1151. adv.writeOne("# service=" + serviceName + '\n'); //$NON-NLS-1$
  1152. adv.end();
  1153. }
  1154. adv.init(db);
  1155. adv.advertiseCapability(OPTION_INCLUDE_TAG);
  1156. adv.advertiseCapability(OPTION_MULTI_ACK_DETAILED);
  1157. adv.advertiseCapability(OPTION_MULTI_ACK);
  1158. adv.advertiseCapability(OPTION_OFS_DELTA);
  1159. adv.advertiseCapability(OPTION_SIDE_BAND);
  1160. adv.advertiseCapability(OPTION_SIDE_BAND_64K);
  1161. adv.advertiseCapability(OPTION_THIN_PACK);
  1162. adv.advertiseCapability(OPTION_NO_PROGRESS);
  1163. adv.advertiseCapability(OPTION_SHALLOW);
  1164. if (!biDirectionalPipe)
  1165. adv.advertiseCapability(OPTION_NO_DONE);
  1166. RequestPolicy policy = getRequestPolicy();
  1167. if (policy == RequestPolicy.TIP
  1168. || policy == RequestPolicy.REACHABLE_COMMIT_TIP
  1169. || policy == null)
  1170. adv.advertiseCapability(OPTION_ALLOW_TIP_SHA1_IN_WANT);
  1171. if (policy == RequestPolicy.REACHABLE_COMMIT
  1172. || policy == RequestPolicy.REACHABLE_COMMIT_TIP
  1173. || policy == null)
  1174. adv.advertiseCapability(OPTION_ALLOW_REACHABLE_SHA1_IN_WANT);
  1175. adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
  1176. if (transferConfig.isAllowFilter()) {
  1177. adv.advertiseCapability(OPTION_FILTER);
  1178. }
  1179. adv.setDerefTags(true);
  1180. Map<String, Ref> advertisedOrDefaultRefs = getAdvertisedOrDefaultRefs();
  1181. findSymrefs(adv, advertisedOrDefaultRefs);
  1182. advertised = adv.send(advertisedOrDefaultRefs);
  1183. if (adv.isEmpty())
  1184. adv.advertiseId(ObjectId.zeroId(), "capabilities^{}"); //$NON-NLS-1$
  1185. adv.end();
  1186. }
  1187. /**
  1188. * Send a message to the client, if it supports receiving them.
  1189. * <p>
  1190. * If the client doesn't support receiving messages, the message will be
  1191. * discarded, with no other indication to the caller or to the client.
  1192. *
  1193. * @param what
  1194. * string describing the problem identified by the hook. The
  1195. * string must not end with an LF, and must not contain an LF.
  1196. * @since 3.1
  1197. */
  1198. public void sendMessage(String what) {
  1199. try {
  1200. msgOut.write(Constants.encode(what + "\n")); //$NON-NLS-1$
  1201. } catch (IOException e) {
  1202. // Ignore write failures.
  1203. }
  1204. }
  1205. /**
  1206. * Get an underlying stream for sending messages to the client
  1207. *
  1208. * @return an underlying stream for sending messages to the client, or null.
  1209. * @since 3.1
  1210. */
  1211. public OutputStream getMessageOutputStream() {
  1212. return msgOut;
  1213. }
  1214. private void recvWants() throws IOException {
  1215. boolean isFirst = true;
  1216. boolean filterReceived = false;
  1217. for (;;) {
  1218. String line;
  1219. try {
  1220. line = pckIn.readString();
  1221. } catch (EOFException eof) {
  1222. if (isFirst)
  1223. break;
  1224. throw eof;
  1225. }
  1226. if (line == PacketLineIn.END)
  1227. break;
  1228. if (line.startsWith("deepen ")) { //$NON-NLS-1$
  1229. depth = Integer.parseInt(line.substring(7));
  1230. if (depth <= 0) {
  1231. throw new PackProtocolException(
  1232. MessageFormat.format(JGitText.get().invalidDepth,
  1233. Integer.valueOf(depth)));
  1234. }
  1235. continue;
  1236. }
  1237. if (line.startsWith("shallow ")) { //$NON-NLS-1$
  1238. clientShallowCommits.add(ObjectId.fromString(line.substring(8)));
  1239. continue;
  1240. }
  1241. if (transferConfig.isAllowFilter()
  1242. && line.startsWith(OPTION_FILTER + " ")) { //$NON-NLS-1$
  1243. String arg = line.substring(OPTION_FILTER.length() + 1);
  1244. if (filterReceived) {
  1245. throw new PackProtocolException(JGitText.get().tooManyFilters);
  1246. }
  1247. filterReceived = true;
  1248. filterBlobLimit = ProtocolV2Parser.filterLine(arg);
  1249. continue;
  1250. }
  1251. if (!line.startsWith("want ") || line.length() < 45) //$NON-NLS-1$
  1252. throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "want", line)); //$NON-NLS-1$
  1253. if (isFirst) {
  1254. if (line.length() > 45) {
  1255. FirstLine firstLine = new FirstLine(line);
  1256. options = firstLine.getOptions();
  1257. line = firstLine.getLine();
  1258. } else
  1259. options = Collections.emptySet();
  1260. }
  1261. wantIds.add(ObjectId.fromString(line.substring(5)));
  1262. isFirst = false;
  1263. }
  1264. }
  1265. /**
  1266. * Returns the clone/fetch depth. Valid only after calling recvWants(). A
  1267. * depth of 1 means return only the wants.
  1268. *
  1269. * @return the depth requested by the client, or 0 if unbounded.
  1270. * @since 4.0
  1271. */
  1272. public int getDepth() {
  1273. if (options == null)
  1274. throw new RequestNotYetReadException();
  1275. return depth;
  1276. }
  1277. /**
  1278. * Get the user agent of the client.
  1279. * <p>
  1280. * If the client is new enough to use {@code agent=} capability that value
  1281. * will be returned. Older HTTP clients may also supply their version using
  1282. * the HTTP {@code User-Agent} header. The capability overrides the HTTP
  1283. * header if both are available.
  1284. * <p>
  1285. * When an HTTP request has been received this method returns the HTTP
  1286. * {@code User-Agent} header value until capabilities have been parsed.
  1287. *
  1288. * @return user agent supplied by the client. Available only if the client
  1289. * is new enough to advertise its user agent.
  1290. * @since 4.0
  1291. */
  1292. public String getPeerUserAgent() {
  1293. return UserAgent.getAgent(options, userAgent);
  1294. }
  1295. private boolean negotiate(PackStatistics.Accumulator accumulator)
  1296. throws IOException {
  1297. okToGiveUp = Boolean.FALSE;
  1298. ObjectId last = ObjectId.zeroId();
  1299. List<ObjectId> peerHas = new ArrayList<>(64);
  1300. for (;;) {
  1301. String line;
  1302. try {
  1303. line = pckIn.readString();
  1304. } catch (EOFException eof) {
  1305. // EOF on stateless RPC (aka smart HTTP) and non-shallow request
  1306. // means the client asked for the updated shallow/unshallow data,
  1307. // disconnected, and will try another request with actual want/have.
  1308. // Don't report the EOF here, its a bug in the protocol that the client
  1309. // just disconnects without sending an END.
  1310. if (!biDirectionalPipe && depth > 0)
  1311. return false;
  1312. throw eof;
  1313. }
  1314. if (line == PacketLineIn.END) {
  1315. last = processHaveLines(peerHas, last, pckOut);
  1316. if (commonBase.isEmpty() || multiAck != MultiAck.OFF)
  1317. pckOut.writeString("NAK\n"); //$NON-NLS-1$
  1318. if (noDone && sentReady) {
  1319. pckOut.writeString("ACK " + last.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1320. return true;
  1321. }
  1322. if (!biDirectionalPipe)
  1323. return false;
  1324. pckOut.flush();
  1325. } else if (line.startsWith("have ") && line.length() == 45) { //$NON-NLS-1$
  1326. peerHas.add(ObjectId.fromString(line.substring(5)));
  1327. accumulator.haves++;
  1328. } else if (line.equals("done")) { //$NON-NLS-1$
  1329. last = processHaveLines(peerHas, last, pckOut);
  1330. if (commonBase.isEmpty())
  1331. pckOut.writeString("NAK\n"); //$NON-NLS-1$
  1332. else if (multiAck != MultiAck.OFF)
  1333. pckOut.writeString("ACK " + last.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1334. return true;
  1335. } else {
  1336. throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "have", line)); //$NON-NLS-1$
  1337. }
  1338. }
  1339. }
  1340. private ObjectId processHaveLines(List<ObjectId> peerHas, ObjectId last, PacketLineOut out)
  1341. throws IOException {
  1342. preUploadHook.onBeginNegotiateRound(this, wantIds, peerHas.size());
  1343. if (wantAll.isEmpty() && !wantIds.isEmpty())
  1344. parseWants();
  1345. if (peerHas.isEmpty())
  1346. return last;
  1347. sentReady = false;
  1348. int haveCnt = 0;
  1349. walk.getObjectReader().setAvoidUnreachableObjects(true);
  1350. AsyncRevObjectQueue q = walk.parseAny(peerHas, false);
  1351. try {
  1352. for (;;) {
  1353. RevObject obj;
  1354. try {
  1355. obj = q.next();
  1356. } catch (MissingObjectException notFound) {
  1357. continue;
  1358. }
  1359. if (obj == null)
  1360. break;
  1361. last = obj;
  1362. haveCnt++;
  1363. if (obj instanceof RevCommit) {
  1364. RevCommit c = (RevCommit) obj;
  1365. if (oldestTime == 0 || c.getCommitTime() < oldestTime)
  1366. oldestTime = c.getCommitTime();
  1367. }
  1368. if (obj.has(PEER_HAS))
  1369. continue;
  1370. obj.add(PEER_HAS);
  1371. if (obj instanceof RevCommit)
  1372. ((RevCommit) obj).carry(PEER_HAS);
  1373. addCommonBase(obj);
  1374. // If both sides have the same object; let the client know.
  1375. //
  1376. switch (multiAck) {
  1377. case OFF:
  1378. if (commonBase.size() == 1)
  1379. out.writeString("ACK " + obj.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1380. break;
  1381. case CONTINUE:
  1382. out.writeString("ACK " + obj.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1383. break;
  1384. case DETAILED:
  1385. out.writeString("ACK " + obj.name() + " common\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1386. break;
  1387. }
  1388. }
  1389. } finally {
  1390. q.release();
  1391. walk.getObjectReader().setAvoidUnreachableObjects(false);
  1392. }
  1393. int missCnt = peerHas.size() - haveCnt;
  1394. // If we don't have one of the objects but we're also willing to
  1395. // create a pack at this point, let the client know so it stops
  1396. // telling us about its history.
  1397. //
  1398. boolean didOkToGiveUp = false;
  1399. if (0 < missCnt) {
  1400. for (int i = peerHas.size() - 1; i >= 0; i--) {
  1401. ObjectId id = peerHas.get(i);
  1402. if (walk.lookupOrNull(id) == null) {
  1403. didOkToGiveUp = true;
  1404. if (okToGiveUp()) {
  1405. switch (multiAck) {
  1406. case OFF:
  1407. break;
  1408. case CONTINUE:
  1409. out.writeString("ACK " + id.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1410. break;
  1411. case DETAILED:
  1412. out.writeString("ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1413. sentReady = true;
  1414. break;
  1415. }
  1416. }
  1417. break;
  1418. }
  1419. }
  1420. }
  1421. if (multiAck == MultiAck.DETAILED && !didOkToGiveUp && okToGiveUp()) {
  1422. ObjectId id = peerHas.get(peerHas.size() - 1);
  1423. out.writeString("ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1424. sentReady = true;
  1425. }
  1426. preUploadHook.onEndNegotiateRound(this, wantAll, haveCnt, missCnt, sentReady);
  1427. peerHas.clear();
  1428. return last;
  1429. }
  1430. private void parseWants() throws IOException {
  1431. List<ObjectId> notAdvertisedWants = null;
  1432. for (ObjectId obj : wantIds) {
  1433. if (!advertised.contains(obj)) {
  1434. if (notAdvertisedWants == null)
  1435. notAdvertisedWants = new ArrayList<>();
  1436. notAdvertisedWants.add(obj);
  1437. }
  1438. }
  1439. if (notAdvertisedWants != null)
  1440. requestValidator.checkWants(this, notAdvertisedWants);
  1441. AsyncRevObjectQueue q = walk.parseAny(wantIds, true);
  1442. try {
  1443. RevObject obj;
  1444. while ((obj = q.next()) != null) {
  1445. want(obj);
  1446. if (!(obj instanceof RevCommit))
  1447. obj.add(SATISFIED);
  1448. if (obj instanceof RevTag) {
  1449. obj = walk.peel(obj);
  1450. if (obj instanceof RevCommit)
  1451. want(obj);
  1452. }
  1453. }
  1454. wantIds.clear();
  1455. } catch (MissingObjectException notFound) {
  1456. throw new WantNotValidException(notFound.getObjectId(), notFound);
  1457. } finally {
  1458. q.release();
  1459. }
  1460. }
  1461. private void want(RevObject obj) {
  1462. if (!obj.has(WANT)) {
  1463. obj.add(WANT);
  1464. wantAll.add(obj);
  1465. }
  1466. }
  1467. /**
  1468. * Validator corresponding to {@link RequestPolicy#ADVERTISED}.
  1469. *
  1470. * @since 3.1
  1471. */
  1472. public static final class AdvertisedRequestValidator
  1473. implements RequestValidator {
  1474. @Override
  1475. public void checkWants(UploadPack up, List<ObjectId> wants)
  1476. throws PackProtocolException, IOException {
  1477. if (!up.isBiDirectionalPipe())
  1478. new ReachableCommitRequestValidator().checkWants(up, wants);
  1479. else if (!wants.isEmpty())
  1480. throw new WantNotValidException(wants.iterator().next());
  1481. }
  1482. }
  1483. /**
  1484. * Validator corresponding to {@link RequestPolicy#REACHABLE_COMMIT}.
  1485. *
  1486. * @since 3.1
  1487. */
  1488. public static final class ReachableCommitRequestValidator
  1489. implements RequestValidator {
  1490. @Override
  1491. public void checkWants(UploadPack up, List<ObjectId> wants)
  1492. throws PackProtocolException, IOException {
  1493. checkNotAdvertisedWants(up, wants,
  1494. refIdSet(up.getAdvertisedRefs().values()));
  1495. }
  1496. }
  1497. /**
  1498. * Validator corresponding to {@link RequestPolicy#TIP}.
  1499. *
  1500. * @since 3.1
  1501. */
  1502. public static final class TipRequestValidator implements RequestValidator {
  1503. @Override
  1504. public void checkWants(UploadPack up, List<ObjectId> wants)
  1505. throws PackProtocolException, IOException {
  1506. if (!up.isBiDirectionalPipe())
  1507. new ReachableCommitTipRequestValidator().checkWants(up, wants);
  1508. else if (!wants.isEmpty()) {
  1509. Set<ObjectId> refIds =
  1510. refIdSet(up.getRepository().getRefDatabase().getRefs());
  1511. for (ObjectId obj : wants) {
  1512. if (!refIds.contains(obj))
  1513. throw new WantNotValidException(obj);
  1514. }
  1515. }
  1516. }
  1517. }
  1518. /**
  1519. * Validator corresponding to {@link RequestPolicy#REACHABLE_COMMIT_TIP}.
  1520. *
  1521. * @since 3.1
  1522. */
  1523. public static final class ReachableCommitTipRequestValidator
  1524. implements RequestValidator {
  1525. @Override
  1526. public void checkWants(UploadPack up, List<ObjectId> wants)
  1527. throws PackProtocolException, IOException {
  1528. checkNotAdvertisedWants(up, wants,
  1529. refIdSet(up.getRepository().getRefDatabase().getRefs()));
  1530. }
  1531. }
  1532. /**
  1533. * Validator corresponding to {@link RequestPolicy#ANY}.
  1534. *
  1535. * @since 3.1
  1536. */
  1537. public static final class AnyRequestValidator implements RequestValidator {
  1538. @Override
  1539. public void checkWants(UploadPack up, List<ObjectId> wants)
  1540. throws PackProtocolException, IOException {
  1541. // All requests are valid.
  1542. }
  1543. }
  1544. private static void checkNotAdvertisedWantsUsingBitmap(ObjectReader reader,
  1545. BitmapIndex bitmapIndex, List<ObjectId> notAdvertisedWants,
  1546. Set<ObjectId> reachableFrom) throws IOException {
  1547. BitmapWalker bitmapWalker = new BitmapWalker(new ObjectWalk(reader), bitmapIndex, null);
  1548. BitmapBuilder reachables = bitmapWalker.findObjects(reachableFrom, null, false);
  1549. for (ObjectId oid : notAdvertisedWants) {
  1550. if (!reachables.contains(oid)) {
  1551. throw new WantNotValidException(oid);
  1552. }
  1553. }
  1554. }
  1555. private static void checkNotAdvertisedWants(UploadPack up,
  1556. List<ObjectId> notAdvertisedWants, Set<ObjectId> reachableFrom)
  1557. throws MissingObjectException, IncorrectObjectTypeException, IOException {
  1558. // Walk the requested commits back to the provided set of commits. If any
  1559. // commit exists, a branch was deleted or rewound and the repository owner
  1560. // no longer exports that requested item. If the requested commit is merged
  1561. // into an advertised branch it will be marked UNINTERESTING and no commits
  1562. // return.
  1563. ObjectReader reader = up.getRevWalk().getObjectReader();
  1564. try (RevWalk walk = new RevWalk(reader)) {
  1565. AsyncRevObjectQueue q = walk.parseAny(notAdvertisedWants, true);
  1566. try {
  1567. RevObject obj;
  1568. while ((obj = q.next()) != null) {
  1569. if (!(obj instanceof RevCommit)) {
  1570. // If unadvertized non-commits are requested, use
  1571. // bitmaps. If there are no bitmaps, instead of
  1572. // incurring the expense of a manual walk, reject
  1573. // the request.
  1574. BitmapIndex bitmapIndex = reader.getBitmapIndex();
  1575. if (bitmapIndex != null) {
  1576. checkNotAdvertisedWantsUsingBitmap(
  1577. reader,
  1578. bitmapIndex,
  1579. notAdvertisedWants,
  1580. reachableFrom);
  1581. return;
  1582. }
  1583. throw new WantNotValidException(obj);
  1584. }
  1585. walk.markStart((RevCommit) obj);
  1586. }
  1587. } catch (MissingObjectException notFound) {
  1588. throw new WantNotValidException(notFound.getObjectId(),
  1589. notFound);
  1590. } finally {
  1591. q.release();
  1592. }
  1593. for (ObjectId id : reachableFrom) {
  1594. try {
  1595. walk.markUninteresting(walk.parseCommit(id));
  1596. } catch (IncorrectObjectTypeException notCommit) {
  1597. continue;
  1598. }
  1599. }
  1600. RevCommit bad = walk.next();
  1601. if (bad != null) {
  1602. throw new WantNotValidException(bad);
  1603. }
  1604. }
  1605. }
  1606. private void addCommonBase(RevObject o) {
  1607. if (!o.has(COMMON)) {
  1608. o.add(COMMON);
  1609. commonBase.add(o);
  1610. okToGiveUp = null;
  1611. }
  1612. }
  1613. private boolean okToGiveUp() throws PackProtocolException {
  1614. if (okToGiveUp == null)
  1615. okToGiveUp = Boolean.valueOf(okToGiveUpImp());
  1616. return okToGiveUp.booleanValue();
  1617. }
  1618. private boolean okToGiveUpImp() throws PackProtocolException {
  1619. if (commonBase.isEmpty())
  1620. return false;
  1621. try {
  1622. for (RevObject obj : wantAll) {
  1623. if (!wantSatisfied(obj))
  1624. return false;
  1625. }
  1626. return true;
  1627. } catch (IOException e) {
  1628. throw new PackProtocolException(JGitText.get().internalRevisionError, e);
  1629. }
  1630. }
  1631. private boolean wantSatisfied(RevObject want) throws IOException {
  1632. if (want.has(SATISFIED))
  1633. return true;
  1634. walk.resetRetain(SAVE);
  1635. walk.markStart((RevCommit) want);
  1636. if (oldestTime != 0)
  1637. walk.setRevFilter(CommitTimeRevFilter.after(oldestTime * 1000L));
  1638. for (;;) {
  1639. final RevCommit c = walk.next();
  1640. if (c == null)
  1641. break;
  1642. if (c.has(PEER_HAS)) {
  1643. addCommonBase(c);
  1644. want.add(SATISFIED);
  1645. return true;
  1646. }
  1647. }
  1648. return false;
  1649. }
  1650. /**
  1651. * Send the requested objects to the client.
  1652. *
  1653. * @param accumulator
  1654. * where to write statistics about the content of the pack.
  1655. * @param allTags
  1656. * refs to search for annotated tags to include in the pack
  1657. * if the {@link #OPTION_INCLUDE_TAG} capability was
  1658. * requested.
  1659. * @param unshallowCommits
  1660. * shallow commits on the client that are now becoming
  1661. * unshallow
  1662. * @throws IOException
  1663. * if an error occured while generating or writing the pack.
  1664. */
  1665. private void sendPack(PackStatistics.Accumulator accumulator,
  1666. @Nullable Collection<Ref> allTags,
  1667. List<ObjectId> unshallowCommits) throws IOException {
  1668. final boolean sideband = options.contains(OPTION_SIDE_BAND)
  1669. || options.contains(OPTION_SIDE_BAND_64K);
  1670. if (sideband) {
  1671. try {
  1672. sendPack(true, accumulator, allTags, unshallowCommits);
  1673. } catch (ServiceMayNotContinueException noPack) {
  1674. // This was already reported on (below).
  1675. throw noPack;
  1676. } catch (IOException err) {
  1677. if (reportInternalServerErrorOverSideband())
  1678. throw new UploadPackInternalServerErrorException(err);
  1679. else
  1680. throw err;
  1681. } catch (RuntimeException err) {
  1682. if (reportInternalServerErrorOverSideband())
  1683. throw new UploadPackInternalServerErrorException(err);
  1684. else
  1685. throw err;
  1686. } catch (Error err) {
  1687. if (reportInternalServerErrorOverSideband())
  1688. throw new UploadPackInternalServerErrorException(err);
  1689. else
  1690. throw err;
  1691. }
  1692. } else {
  1693. sendPack(false, accumulator, allTags, unshallowCommits);
  1694. }
  1695. }
  1696. private boolean reportInternalServerErrorOverSideband() {
  1697. try {
  1698. @SuppressWarnings("resource" /* java 7 */)
  1699. SideBandOutputStream err = new SideBandOutputStream(
  1700. SideBandOutputStream.CH_ERROR,
  1701. SideBandOutputStream.SMALL_BUF,
  1702. rawOut);
  1703. err.write(Constants.encode(JGitText.get().internalServerError));
  1704. err.flush();
  1705. return true;
  1706. } catch (Throwable cannotReport) {
  1707. // Ignore the reason. This is a secondary failure.
  1708. return false;
  1709. }
  1710. }
  1711. /**
  1712. * Send the requested objects to the client.
  1713. *
  1714. * @param sideband
  1715. * whether to wrap the pack in side-band pkt-lines,
  1716. * interleaved with progress messages and errors.
  1717. * @param accumulator
  1718. * where to write statistics about the content of the pack.
  1719. * @param allTags
  1720. * refs to search for annotated tags to include in the pack
  1721. * if the {@link #OPTION_INCLUDE_TAG} capability was
  1722. * requested.
  1723. * @param unshallowCommits
  1724. * shallow commits on the client that are now becoming
  1725. * unshallow
  1726. * @throws IOException
  1727. * if an error occured while generating or writing the pack.
  1728. */
  1729. private void sendPack(final boolean sideband,
  1730. PackStatistics.Accumulator accumulator,
  1731. @Nullable Collection<Ref> allTags,
  1732. List<ObjectId> unshallowCommits) throws IOException {
  1733. ProgressMonitor pm = NullProgressMonitor.INSTANCE;
  1734. OutputStream packOut = rawOut;
  1735. if (sideband) {
  1736. int bufsz = SideBandOutputStream.SMALL_BUF;
  1737. if (options.contains(OPTION_SIDE_BAND_64K))
  1738. bufsz = SideBandOutputStream.MAX_BUF;
  1739. packOut = new SideBandOutputStream(SideBandOutputStream.CH_DATA,
  1740. bufsz, rawOut);
  1741. if (!options.contains(OPTION_NO_PROGRESS)) {
  1742. msgOut = new SideBandOutputStream(
  1743. SideBandOutputStream.CH_PROGRESS, bufsz, rawOut);
  1744. pm = new SideBandProgressMonitor(msgOut);
  1745. }
  1746. }
  1747. try {
  1748. if (wantAll.isEmpty()) {
  1749. preUploadHook.onSendPack(this, wantIds, commonBase);
  1750. } else {
  1751. preUploadHook.onSendPack(this, wantAll, commonBase);
  1752. }
  1753. msgOut.flush();
  1754. } catch (ServiceMayNotContinueException noPack) {
  1755. if (sideband && noPack.getMessage() != null) {
  1756. noPack.setOutput();
  1757. @SuppressWarnings("resource" /* java 7 */)
  1758. SideBandOutputStream err = new SideBandOutputStream(
  1759. SideBandOutputStream.CH_ERROR,
  1760. SideBandOutputStream.SMALL_BUF, rawOut);
  1761. err.write(Constants.encode(noPack.getMessage()));
  1762. err.flush();
  1763. }
  1764. throw noPack;
  1765. }
  1766. PackConfig cfg = packConfig;
  1767. if (cfg == null)
  1768. cfg = new PackConfig(db);
  1769. @SuppressWarnings("resource") // PackWriter is referenced in the finally
  1770. // block, and is closed there
  1771. final PackWriter pw = new PackWriter(cfg, walk.getObjectReader(),
  1772. accumulator);
  1773. try {
  1774. pw.setIndexDisabled(true);
  1775. if (filterBlobLimit >= 0) {
  1776. pw.setFilterBlobLimit(filterBlobLimit);
  1777. pw.setUseCachedPacks(false);
  1778. } else {
  1779. pw.setUseCachedPacks(true);
  1780. }
  1781. pw.setUseBitmaps(depth == 0 && clientShallowCommits.isEmpty());
  1782. pw.setClientShallowCommits(clientShallowCommits);
  1783. pw.setReuseDeltaCommits(true);
  1784. pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA));
  1785. pw.setThin(options.contains(OPTION_THIN_PACK));
  1786. pw.setReuseValidatingObjects(false);
  1787. // Objects named directly by references go at the beginning
  1788. // of the pack.
  1789. if (commonBase.isEmpty() && refs != null) {
  1790. Set<ObjectId> tagTargets = new HashSet<>();
  1791. for (Ref ref : refs.values()) {
  1792. if (ref.getPeeledObjectId() != null)
  1793. tagTargets.add(ref.getPeeledObjectId());
  1794. else if (ref.getObjectId() == null)
  1795. continue;
  1796. else if (ref.getName().startsWith(Constants.R_HEADS))
  1797. tagTargets.add(ref.getObjectId());
  1798. }
  1799. pw.setTagTargets(tagTargets);
  1800. }
  1801. RevWalk rw = walk;
  1802. if (depth > 0) {
  1803. pw.setShallowPack(depth, unshallowCommits);
  1804. rw = new DepthWalk.RevWalk(walk.getObjectReader(), depth - 1);
  1805. rw.assumeShallow(clientShallowCommits);
  1806. }
  1807. if (wantAll.isEmpty()) {
  1808. pw.preparePack(pm, wantIds, commonBase, clientShallowCommits);
  1809. } else {
  1810. walk.reset();
  1811. ObjectWalk ow = rw.toObjectWalkWithSameObjects();
  1812. pw.preparePack(pm, ow, wantAll, commonBase, PackWriter.NONE);
  1813. rw = ow;
  1814. }
  1815. if (options.contains(OPTION_INCLUDE_TAG) && allTags != null) {
  1816. for (Ref ref : allTags) {
  1817. ObjectId objectId = ref.getObjectId();
  1818. if (objectId == null) {
  1819. // skip unborn branch
  1820. continue;
  1821. }
  1822. // If the object was already requested, skip it.
  1823. if (wantAll.isEmpty()) {
  1824. if (wantIds.contains(objectId))
  1825. continue;
  1826. } else {
  1827. RevObject obj = rw.lookupOrNull(objectId);
  1828. if (obj != null && obj.has(WANT))
  1829. continue;
  1830. }
  1831. if (!ref.isPeeled())
  1832. ref = db.getRefDatabase().peel(ref);
  1833. ObjectId peeledId = ref.getPeeledObjectId();
  1834. objectId = ref.getObjectId();
  1835. if (peeledId == null || objectId == null)
  1836. continue;
  1837. if (pw.willInclude(peeledId) && !pw.willInclude(objectId)) {
  1838. pw.addObject(rw.parseAny(objectId));
  1839. }
  1840. }
  1841. }
  1842. pw.writePack(pm, NullProgressMonitor.INSTANCE, packOut);
  1843. if (msgOut != NullOutputStream.INSTANCE) {
  1844. String msg = pw.getStatistics().getMessage() + '\n';
  1845. msgOut.write(Constants.encode(msg));
  1846. msgOut.flush();
  1847. }
  1848. } finally {
  1849. statistics = pw.getStatistics();
  1850. if (statistics != null) {
  1851. postUploadHook.onPostUpload(statistics);
  1852. }
  1853. pw.close();
  1854. }
  1855. if (sideband)
  1856. pckOut.end();
  1857. }
  1858. private static void findSymrefs(
  1859. final RefAdvertiser adv, final Map<String, Ref> refs) {
  1860. Ref head = refs.get(Constants.HEAD);
  1861. if (head != null && head.isSymbolic()) {
  1862. adv.addSymref(Constants.HEAD, head.getLeaf().getName());
  1863. }
  1864. }
  1865. private static class ResponseBufferedOutputStream extends OutputStream {
  1866. private final OutputStream rawOut;
  1867. private OutputStream out;
  1868. ResponseBufferedOutputStream(OutputStream rawOut) {
  1869. this.rawOut = rawOut;
  1870. this.out = new ByteArrayOutputStream();
  1871. }
  1872. @Override
  1873. public void write(int b) throws IOException {
  1874. out.write(b);
  1875. }
  1876. @Override
  1877. public void write(byte b[]) throws IOException {
  1878. out.write(b);
  1879. }
  1880. @Override
  1881. public void write(byte b[], int off, int len) throws IOException {
  1882. out.write(b, off, len);
  1883. }
  1884. @Override
  1885. public void flush() throws IOException {
  1886. out.flush();
  1887. }
  1888. @Override
  1889. public void close() throws IOException {
  1890. out.close();
  1891. }
  1892. void stopBuffering() throws IOException {
  1893. if (out != rawOut) {
  1894. ((ByteArrayOutputStream) out).writeTo(rawOut);
  1895. out = rawOut;
  1896. }
  1897. }
  1898. }
  1899. }