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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433
  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.RefDatabase.ALL;
  45. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT;
  46. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_INCLUDE_TAG;
  47. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK;
  48. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK_DETAILED;
  49. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_DONE;
  50. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_PROGRESS;
  51. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_OFS_DELTA;
  52. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SHALLOW;
  53. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
  54. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
  55. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
  56. import java.io.EOFException;
  57. import java.io.IOException;
  58. import java.io.InputStream;
  59. import java.io.OutputStream;
  60. import java.text.MessageFormat;
  61. import java.util.ArrayList;
  62. import java.util.Collection;
  63. import java.util.Collections;
  64. import java.util.HashSet;
  65. import java.util.List;
  66. import java.util.Map;
  67. import java.util.Set;
  68. import org.eclipse.jgit.errors.CorruptObjectException;
  69. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  70. import org.eclipse.jgit.errors.MissingObjectException;
  71. import org.eclipse.jgit.errors.PackProtocolException;
  72. import org.eclipse.jgit.internal.JGitText;
  73. import org.eclipse.jgit.internal.storage.pack.PackWriter;
  74. import org.eclipse.jgit.lib.Constants;
  75. import org.eclipse.jgit.lib.NullProgressMonitor;
  76. import org.eclipse.jgit.lib.ObjectId;
  77. import org.eclipse.jgit.lib.ProgressMonitor;
  78. import org.eclipse.jgit.lib.Ref;
  79. import org.eclipse.jgit.lib.Repository;
  80. import org.eclipse.jgit.revwalk.AsyncRevObjectQueue;
  81. import org.eclipse.jgit.revwalk.DepthWalk;
  82. import org.eclipse.jgit.revwalk.ObjectWalk;
  83. import org.eclipse.jgit.revwalk.RevCommit;
  84. import org.eclipse.jgit.revwalk.RevFlag;
  85. import org.eclipse.jgit.revwalk.RevFlagSet;
  86. import org.eclipse.jgit.revwalk.RevObject;
  87. import org.eclipse.jgit.revwalk.RevTag;
  88. import org.eclipse.jgit.revwalk.RevWalk;
  89. import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
  90. import org.eclipse.jgit.storage.pack.PackConfig;
  91. import org.eclipse.jgit.transport.GitProtocolConstants.MultiAck;
  92. import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
  93. import org.eclipse.jgit.util.io.InterruptTimer;
  94. import org.eclipse.jgit.util.io.NullOutputStream;
  95. import org.eclipse.jgit.util.io.TimeoutInputStream;
  96. import org.eclipse.jgit.util.io.TimeoutOutputStream;
  97. /**
  98. * Implements the server side of a fetch connection, transmitting objects.
  99. */
  100. public class UploadPack {
  101. /** Policy the server uses to validate client requests */
  102. public static enum RequestPolicy {
  103. /** Client may only ask for objects the server advertised a reference for. */
  104. ADVERTISED,
  105. /**
  106. * Client may ask for any commit reachable from a reference advertised by
  107. * the server.
  108. */
  109. REACHABLE_COMMIT,
  110. /**
  111. * Client may ask for objects that are the tip of any reference, even if not
  112. * advertised.
  113. * <p>
  114. * This may happen, for example, when a custom {@link RefFilter} is set.
  115. *
  116. * @since 3.1
  117. */
  118. TIP,
  119. /**
  120. * Client may ask for any commit reachable from any reference, even if that
  121. * reference wasn't advertised.
  122. *
  123. * @since 3.1
  124. */
  125. REACHABLE_COMMIT_TIP,
  126. /** Client may ask for any SHA-1 in the repository. */
  127. ANY;
  128. }
  129. /**
  130. * Validator for client requests.
  131. *
  132. * @since 3.1
  133. */
  134. public interface RequestValidator {
  135. /**
  136. * Check a list of client wants against the request policy.
  137. *
  138. * @param up
  139. * {@link UploadPack} instance.
  140. * @param wants
  141. * objects the client requested that were not advertised.
  142. *
  143. * @throws PackProtocolException
  144. * if one or more wants is not valid.
  145. * @throws IOException
  146. * if a low-level exception occurred.
  147. * @since 3.1
  148. */
  149. void checkWants(UploadPack up, List<ObjectId> wants)
  150. throws PackProtocolException, IOException;
  151. }
  152. /** Data in the first line of a request, the line itself plus options. */
  153. public static class FirstLine {
  154. private final String line;
  155. private final Set<String> options;
  156. /**
  157. * Parse the first line of a receive-pack request.
  158. *
  159. * @param line
  160. * line from the client.
  161. */
  162. public FirstLine(String line) {
  163. if (line.length() > 45) {
  164. final HashSet<String> opts = new HashSet<String>();
  165. String opt = line.substring(45);
  166. if (opt.startsWith(" ")) //$NON-NLS-1$
  167. opt = opt.substring(1);
  168. for (String c : opt.split(" ")) //$NON-NLS-1$
  169. opts.add(c);
  170. this.line = line.substring(0, 45);
  171. this.options = Collections.unmodifiableSet(opts);
  172. } else {
  173. this.line = line;
  174. this.options = Collections.emptySet();
  175. }
  176. }
  177. /** @return non-capabilities part of the line. */
  178. public String getLine() {
  179. return line;
  180. }
  181. /** @return options parsed from the line. */
  182. public Set<String> getOptions() {
  183. return options;
  184. }
  185. }
  186. /** Database we read the objects from. */
  187. private final Repository db;
  188. /** Revision traversal support over {@link #db}. */
  189. private final RevWalk walk;
  190. /** Configuration to pass into the PackWriter. */
  191. private PackConfig packConfig;
  192. /** Configuration for various transfer options. */
  193. private TransferConfig transferConfig;
  194. /** Timeout in seconds to wait for client interaction. */
  195. private int timeout;
  196. /**
  197. * Is the client connection a bi-directional socket or pipe?
  198. * <p>
  199. * If true, this class assumes it can perform multiple read and write cycles
  200. * with the client over the input and output streams. This matches the
  201. * functionality available with a standard TCP/IP connection, or a local
  202. * operating system or in-memory pipe.
  203. * <p>
  204. * If false, this class runs in a read everything then output results mode,
  205. * making it suitable for single round-trip systems RPCs such as HTTP.
  206. */
  207. private boolean biDirectionalPipe = true;
  208. /** Timer to manage {@link #timeout}. */
  209. private InterruptTimer timer;
  210. private InputStream rawIn;
  211. private OutputStream rawOut;
  212. private PacketLineIn pckIn;
  213. private PacketLineOut pckOut;
  214. private OutputStream msgOut = NullOutputStream.INSTANCE;
  215. /** The refs we advertised as existing at the start of the connection. */
  216. private Map<String, Ref> refs;
  217. /** Hook used while advertising the refs to the client. */
  218. private AdvertiseRefsHook advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
  219. /** Filter used while advertising the refs to the client. */
  220. private RefFilter refFilter = RefFilter.DEFAULT;
  221. /** Hook handling the various upload phases. */
  222. private PreUploadHook preUploadHook = PreUploadHook.NULL;
  223. /** Capabilities requested by the client. */
  224. private Set<String> options;
  225. /** Raw ObjectIds the client has asked for, before validating them. */
  226. private final Set<ObjectId> wantIds = new HashSet<ObjectId>();
  227. /** Objects the client wants to obtain. */
  228. private final Set<RevObject> wantAll = new HashSet<RevObject>();
  229. /** Objects on both sides, these don't have to be sent. */
  230. private final Set<RevObject> commonBase = new HashSet<RevObject>();
  231. /** Shallow commits the client already has. */
  232. private final Set<ObjectId> clientShallowCommits = new HashSet<ObjectId>();
  233. /** Shallow commits on the client which are now becoming unshallow */
  234. private final List<ObjectId> unshallowCommits = new ArrayList<ObjectId>();
  235. /** Desired depth from the client on a shallow request. */
  236. private int depth;
  237. /** Commit time of the oldest common commit, in seconds. */
  238. private int oldestTime;
  239. /** null if {@link #commonBase} should be examined again. */
  240. private Boolean okToGiveUp;
  241. private boolean sentReady;
  242. /** Objects we sent in our advertisement list, clients can ask for these. */
  243. private Set<ObjectId> advertised;
  244. /** Marked on objects the client has asked us to give them. */
  245. private final RevFlag WANT;
  246. /** Marked on objects both we and the client have. */
  247. private final RevFlag PEER_HAS;
  248. /** Marked on objects in {@link #commonBase}. */
  249. private final RevFlag COMMON;
  250. /** Objects where we found a path from the want list to a common base. */
  251. private final RevFlag SATISFIED;
  252. private final RevFlagSet SAVE;
  253. private RequestValidator requestValidator = new AdvertisedRequestValidator();
  254. private MultiAck multiAck = MultiAck.OFF;
  255. private boolean noDone;
  256. private PackWriter.Statistics statistics;
  257. private UploadPackLogger logger = UploadPackLogger.NULL;
  258. /**
  259. * Create a new pack upload for an open repository.
  260. *
  261. * @param copyFrom
  262. * the source repository.
  263. */
  264. public UploadPack(final Repository copyFrom) {
  265. db = copyFrom;
  266. walk = new RevWalk(db);
  267. walk.setRetainBody(false);
  268. WANT = walk.newFlag("WANT"); //$NON-NLS-1$
  269. PEER_HAS = walk.newFlag("PEER_HAS"); //$NON-NLS-1$
  270. COMMON = walk.newFlag("COMMON"); //$NON-NLS-1$
  271. SATISFIED = walk.newFlag("SATISFIED"); //$NON-NLS-1$
  272. walk.carry(PEER_HAS);
  273. SAVE = new RevFlagSet();
  274. SAVE.add(WANT);
  275. SAVE.add(PEER_HAS);
  276. SAVE.add(COMMON);
  277. SAVE.add(SATISFIED);
  278. setTransferConfig(null);
  279. }
  280. /** @return the repository this upload is reading from. */
  281. public final Repository getRepository() {
  282. return db;
  283. }
  284. /** @return the RevWalk instance used by this connection. */
  285. public final RevWalk getRevWalk() {
  286. return walk;
  287. }
  288. /**
  289. * Get refs which were advertised to the client.
  290. *
  291. * @return all refs which were advertised to the client, or null if
  292. * {@link #setAdvertisedRefs(Map)} has not been called yet.
  293. */
  294. public final Map<String, Ref> getAdvertisedRefs() {
  295. return refs;
  296. }
  297. /**
  298. * Set the refs advertised by this UploadPack.
  299. * <p>
  300. * Intended to be called from a {@link PreUploadHook}.
  301. *
  302. * @param allRefs
  303. * explicit set of references to claim as advertised by this
  304. * UploadPack instance. This overrides any references that
  305. * may exist in the source repository. The map is passed
  306. * to the configured {@link #getRefFilter()}. If null, assumes
  307. * all refs were advertised.
  308. */
  309. public void setAdvertisedRefs(Map<String, Ref> allRefs) {
  310. if (allRefs != null)
  311. refs = allRefs;
  312. else
  313. refs = db.getAllRefs();
  314. if (refFilter == RefFilter.DEFAULT)
  315. refs = transferConfig.getRefFilter().filter(refs);
  316. else
  317. refs = refFilter.filter(refs);
  318. }
  319. /** @return timeout (in seconds) before aborting an IO operation. */
  320. public int getTimeout() {
  321. return timeout;
  322. }
  323. /**
  324. * Set the timeout before willing to abort an IO call.
  325. *
  326. * @param seconds
  327. * number of seconds to wait (with no data transfer occurring)
  328. * before aborting an IO read or write operation with the
  329. * connected client.
  330. */
  331. public void setTimeout(final int seconds) {
  332. timeout = seconds;
  333. }
  334. /**
  335. * @return true if this class expects a bi-directional pipe opened between
  336. * the client and itself. The default is true.
  337. */
  338. public boolean isBiDirectionalPipe() {
  339. return biDirectionalPipe;
  340. }
  341. /**
  342. * @param twoWay
  343. * if true, this class will assume the socket is a fully
  344. * bidirectional pipe between the two peers and takes advantage
  345. * of that by first transmitting the known refs, then waiting to
  346. * read commands. If false, this class assumes it must read the
  347. * commands before writing output and does not perform the
  348. * initial advertising.
  349. */
  350. public void setBiDirectionalPipe(final boolean twoWay) {
  351. biDirectionalPipe = twoWay;
  352. }
  353. /**
  354. * @return policy used by the service to validate client requests, or null for
  355. * a custom request validator.
  356. */
  357. public RequestPolicy getRequestPolicy() {
  358. if (requestValidator instanceof AdvertisedRequestValidator)
  359. return RequestPolicy.ADVERTISED;
  360. if (requestValidator instanceof ReachableCommitRequestValidator)
  361. return RequestPolicy.REACHABLE_COMMIT;
  362. if (requestValidator instanceof TipRequestValidator)
  363. return RequestPolicy.TIP;
  364. if (requestValidator instanceof ReachableCommitTipRequestValidator)
  365. return RequestPolicy.REACHABLE_COMMIT_TIP;
  366. if (requestValidator instanceof AnyRequestValidator)
  367. return RequestPolicy.ANY;
  368. return null;
  369. }
  370. /**
  371. * @param policy
  372. * the policy used to enforce validation of a client's want list.
  373. * By default the policy is {@link RequestPolicy#ADVERTISED},
  374. * which is the Git default requiring clients to only ask for an
  375. * object that a reference directly points to. This may be relaxed
  376. * to {@link RequestPolicy#REACHABLE_COMMIT} or
  377. * {@link RequestPolicy#REACHABLE_COMMIT_TIP} when callers have
  378. * {@link #setBiDirectionalPipe(boolean)} set to false.
  379. * Overrides any policy specified in a {@link TransferConfig}.
  380. */
  381. public void setRequestPolicy(RequestPolicy policy) {
  382. switch (policy) {
  383. case ADVERTISED:
  384. default:
  385. requestValidator = new AdvertisedRequestValidator();
  386. break;
  387. case REACHABLE_COMMIT:
  388. requestValidator = new ReachableCommitRequestValidator();
  389. break;
  390. case TIP:
  391. requestValidator = new TipRequestValidator();
  392. break;
  393. case REACHABLE_COMMIT_TIP:
  394. requestValidator = new ReachableCommitTipRequestValidator();
  395. break;
  396. case ANY:
  397. requestValidator = new AnyRequestValidator();
  398. break;
  399. }
  400. }
  401. /**
  402. * @param validator
  403. * custom validator for client want list.
  404. * @since 3.1
  405. */
  406. public void setRequestValidator(RequestValidator validator) {
  407. requestValidator = validator != null ? validator
  408. : new AdvertisedRequestValidator();
  409. }
  410. /** @return the hook used while advertising the refs to the client */
  411. public AdvertiseRefsHook getAdvertiseRefsHook() {
  412. return advertiseRefsHook;
  413. }
  414. /** @return the filter used while advertising the refs to the client */
  415. public RefFilter getRefFilter() {
  416. return refFilter;
  417. }
  418. /**
  419. * Set the hook used while advertising the refs to the client.
  420. * <p>
  421. * If the {@link AdvertiseRefsHook} chooses to call
  422. * {@link #setAdvertisedRefs(Map)}, only refs set by this hook <em>and</em>
  423. * selected by the {@link RefFilter} will be shown to the client.
  424. *
  425. * @param advertiseRefsHook
  426. * the hook; may be null to show all refs.
  427. */
  428. public void setAdvertiseRefsHook(final AdvertiseRefsHook advertiseRefsHook) {
  429. if (advertiseRefsHook != null)
  430. this.advertiseRefsHook = advertiseRefsHook;
  431. else
  432. this.advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
  433. }
  434. /**
  435. * Set the filter used while advertising the refs to the client.
  436. * <p>
  437. * Only refs allowed by this filter will be sent to the client.
  438. * The filter is run against the refs specified by the
  439. * {@link AdvertiseRefsHook} (if applicable). If null or not set, uses the
  440. * filter implied by the {@link TransferConfig}.
  441. *
  442. * @param refFilter
  443. * the filter; may be null to show all refs.
  444. */
  445. public void setRefFilter(final RefFilter refFilter) {
  446. this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
  447. }
  448. /** @return the configured upload hook. */
  449. public PreUploadHook getPreUploadHook() {
  450. return preUploadHook;
  451. }
  452. /**
  453. * Set the hook that controls how this instance will behave.
  454. *
  455. * @param hook
  456. * the hook; if null no special actions are taken.
  457. */
  458. public void setPreUploadHook(PreUploadHook hook) {
  459. preUploadHook = hook != null ? hook : PreUploadHook.NULL;
  460. }
  461. /**
  462. * Set the configuration used by the pack generator.
  463. *
  464. * @param pc
  465. * configuration controlling packing parameters. If null the
  466. * source repository's settings will be used.
  467. */
  468. public void setPackConfig(PackConfig pc) {
  469. this.packConfig = pc;
  470. }
  471. /**
  472. * @param tc
  473. * configuration controlling transfer options. If null the source
  474. * repository's settings will be used.
  475. * @since 3.1
  476. */
  477. public void setTransferConfig(TransferConfig tc) {
  478. this.transferConfig = tc != null ? tc : new TransferConfig(db);
  479. setRequestPolicy(transferConfig.isAllowTipSha1InWant()
  480. ? RequestPolicy.TIP : RequestPolicy.ADVERTISED);
  481. }
  482. /** @return the configured logger. */
  483. public UploadPackLogger getLogger() {
  484. return logger;
  485. }
  486. /**
  487. * Set the logger.
  488. *
  489. * @param logger
  490. * the logger instance. If null, no logging occurs.
  491. */
  492. public void setLogger(UploadPackLogger logger) {
  493. this.logger = logger;
  494. }
  495. /**
  496. * Check whether the client expects a side-band stream.
  497. *
  498. * @return true if the client has advertised a side-band capability, false
  499. * otherwise.
  500. * @throws RequestNotYetReadException
  501. * if the client's request has not yet been read from the wire, so
  502. * we do not know if they expect side-band. Note that the client
  503. * may have already written the request, it just has not been
  504. * read.
  505. */
  506. public boolean isSideBand() throws RequestNotYetReadException {
  507. if (options == null)
  508. throw new RequestNotYetReadException();
  509. return (options.contains(OPTION_SIDE_BAND)
  510. || options.contains(OPTION_SIDE_BAND_64K));
  511. }
  512. /**
  513. * Execute the upload task on the socket.
  514. *
  515. * @param input
  516. * raw input to read client commands from. Caller must ensure the
  517. * input is buffered, otherwise read performance may suffer.
  518. * @param output
  519. * response back to the Git network client, to write the pack
  520. * data onto. Caller must ensure the output is buffered,
  521. * otherwise write performance may suffer.
  522. * @param messages
  523. * secondary "notice" channel to send additional messages out
  524. * through. When run over SSH this should be tied back to the
  525. * standard error channel of the command execution. For most
  526. * other network connections this should be null.
  527. * @throws IOException
  528. */
  529. public void upload(final InputStream input, final OutputStream output,
  530. final OutputStream messages) throws IOException {
  531. try {
  532. rawIn = input;
  533. rawOut = output;
  534. if (messages != null)
  535. msgOut = messages;
  536. if (timeout > 0) {
  537. final Thread caller = Thread.currentThread();
  538. timer = new InterruptTimer(caller.getName() + "-Timer"); //$NON-NLS-1$
  539. TimeoutInputStream i = new TimeoutInputStream(rawIn, timer);
  540. TimeoutOutputStream o = new TimeoutOutputStream(rawOut, timer);
  541. i.setTimeout(timeout * 1000);
  542. o.setTimeout(timeout * 1000);
  543. rawIn = i;
  544. rawOut = o;
  545. }
  546. pckIn = new PacketLineIn(rawIn);
  547. pckOut = new PacketLineOut(rawOut);
  548. service();
  549. } finally {
  550. msgOut = NullOutputStream.INSTANCE;
  551. walk.release();
  552. if (timer != null) {
  553. try {
  554. timer.terminate();
  555. } finally {
  556. timer = null;
  557. }
  558. }
  559. }
  560. }
  561. /**
  562. * Get the PackWriter's statistics if a pack was sent to the client.
  563. *
  564. * @return statistics about pack output, if a pack was sent. Null if no pack
  565. * was sent, such as during the negotation phase of a smart HTTP
  566. * connection, or if the client was already up-to-date.
  567. * @since 3.0
  568. */
  569. public PackWriter.Statistics getPackStatistics() {
  570. return statistics;
  571. }
  572. private Map<String, Ref> getAdvertisedOrDefaultRefs() {
  573. if (refs == null)
  574. setAdvertisedRefs(null);
  575. return refs;
  576. }
  577. private void service() throws IOException {
  578. if (biDirectionalPipe)
  579. sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
  580. else if (requestValidator instanceof AnyRequestValidator)
  581. advertised = Collections.emptySet();
  582. else
  583. advertised = refIdSet(getAdvertisedOrDefaultRefs().values());
  584. boolean sendPack;
  585. try {
  586. recvWants();
  587. if (wantIds.isEmpty()) {
  588. preUploadHook.onBeginNegotiateRound(this, wantIds, 0);
  589. preUploadHook.onEndNegotiateRound(this, wantIds, 0, 0, false);
  590. return;
  591. }
  592. if (options.contains(OPTION_MULTI_ACK_DETAILED)) {
  593. multiAck = MultiAck.DETAILED;
  594. noDone = options.contains(OPTION_NO_DONE);
  595. } else if (options.contains(OPTION_MULTI_ACK))
  596. multiAck = MultiAck.CONTINUE;
  597. else
  598. multiAck = MultiAck.OFF;
  599. if (depth != 0)
  600. processShallow();
  601. if (!clientShallowCommits.isEmpty())
  602. walk.assumeShallow(clientShallowCommits);
  603. sendPack = negotiate();
  604. } catch (PackProtocolException err) {
  605. reportErrorDuringNegotiate(err.getMessage());
  606. throw err;
  607. } catch (ServiceMayNotContinueException err) {
  608. if (!err.isOutput() && err.getMessage() != null) {
  609. try {
  610. pckOut.writeString("ERR " + err.getMessage() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  611. err.setOutput();
  612. } catch (Throwable err2) {
  613. // Ignore this secondary failure (and not mark output).
  614. }
  615. }
  616. throw err;
  617. } catch (IOException err) {
  618. reportErrorDuringNegotiate(JGitText.get().internalServerError);
  619. throw err;
  620. } catch (RuntimeException err) {
  621. reportErrorDuringNegotiate(JGitText.get().internalServerError);
  622. throw err;
  623. } catch (Error err) {
  624. reportErrorDuringNegotiate(JGitText.get().internalServerError);
  625. throw err;
  626. }
  627. if (sendPack)
  628. sendPack();
  629. }
  630. private static Set<ObjectId> refIdSet(Collection<Ref> refs) {
  631. Set<ObjectId> ids = new HashSet<ObjectId>(refs.size());
  632. for (Ref ref : refs) {
  633. if (ref.getObjectId() != null)
  634. ids.add(ref.getObjectId());
  635. }
  636. return ids;
  637. }
  638. private void reportErrorDuringNegotiate(String msg) {
  639. try {
  640. pckOut.writeString("ERR " + msg + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  641. } catch (Throwable err) {
  642. // Ignore this secondary failure.
  643. }
  644. }
  645. private void processShallow() throws IOException {
  646. DepthWalk.RevWalk depthWalk =
  647. new DepthWalk.RevWalk(walk.getObjectReader(), depth);
  648. // Find all the commits which will be shallow
  649. for (ObjectId o : wantIds) {
  650. try {
  651. depthWalk.markRoot(depthWalk.parseCommit(o));
  652. } catch (IncorrectObjectTypeException notCommit) {
  653. // Ignore non-commits in this loop.
  654. }
  655. }
  656. RevCommit o;
  657. while ((o = depthWalk.next()) != null) {
  658. DepthWalk.Commit c = (DepthWalk.Commit) o;
  659. // Commits at the boundary which aren't already shallow in
  660. // the client need to be marked as such
  661. if (c.getDepth() == depth && !clientShallowCommits.contains(c))
  662. pckOut.writeString("shallow " + o.name()); //$NON-NLS-1$
  663. // Commits not on the boundary which are shallow in the client
  664. // need to become unshallowed
  665. if (c.getDepth() < depth && clientShallowCommits.remove(c)) {
  666. unshallowCommits.add(c.copy());
  667. pckOut.writeString("unshallow " + c.name()); //$NON-NLS-1$
  668. }
  669. }
  670. pckOut.end();
  671. }
  672. /**
  673. * Generate an advertisement of available refs and capabilities.
  674. *
  675. * @param adv
  676. * the advertisement formatter.
  677. * @throws IOException
  678. * the formatter failed to write an advertisement.
  679. * @throws ServiceMayNotContinueException
  680. * the hook denied advertisement.
  681. */
  682. public void sendAdvertisedRefs(final RefAdvertiser adv) throws IOException,
  683. ServiceMayNotContinueException {
  684. try {
  685. advertiseRefsHook.advertiseRefs(this);
  686. } catch (ServiceMayNotContinueException fail) {
  687. if (fail.getMessage() != null) {
  688. adv.writeOne("ERR " + fail.getMessage()); //$NON-NLS-1$
  689. fail.setOutput();
  690. }
  691. throw fail;
  692. }
  693. adv.init(db);
  694. adv.advertiseCapability(OPTION_INCLUDE_TAG);
  695. adv.advertiseCapability(OPTION_MULTI_ACK_DETAILED);
  696. adv.advertiseCapability(OPTION_MULTI_ACK);
  697. adv.advertiseCapability(OPTION_OFS_DELTA);
  698. adv.advertiseCapability(OPTION_SIDE_BAND);
  699. adv.advertiseCapability(OPTION_SIDE_BAND_64K);
  700. adv.advertiseCapability(OPTION_THIN_PACK);
  701. adv.advertiseCapability(OPTION_NO_PROGRESS);
  702. adv.advertiseCapability(OPTION_SHALLOW);
  703. if (!biDirectionalPipe)
  704. adv.advertiseCapability(OPTION_NO_DONE);
  705. RequestPolicy policy = getRequestPolicy();
  706. if (policy == RequestPolicy.TIP
  707. || policy == RequestPolicy.REACHABLE_COMMIT_TIP
  708. || policy == null)
  709. adv.advertiseCapability(OPTION_ALLOW_TIP_SHA1_IN_WANT);
  710. adv.setDerefTags(true);
  711. advertised = adv.send(getAdvertisedOrDefaultRefs());
  712. if (adv.isEmpty())
  713. adv.advertiseId(ObjectId.zeroId(), "capabilities^{}"); //$NON-NLS-1$
  714. adv.end();
  715. }
  716. /**
  717. * Send a message to the client, if it supports receiving them.
  718. * <p>
  719. * If the client doesn't support receiving messages, the message will be
  720. * discarded, with no other indication to the caller or to the client.
  721. *
  722. * @param what
  723. * string describing the problem identified by the hook. The
  724. * string must not end with an LF, and must not contain an LF.
  725. * @since 3.1
  726. */
  727. public void sendMessage(String what) {
  728. try {
  729. msgOut.write(Constants.encode(what + "\n")); //$NON-NLS-1$
  730. } catch (IOException e) {
  731. // Ignore write failures.
  732. }
  733. }
  734. /**
  735. * @return an underlying stream for sending messages to the client, or null.
  736. * @since 3.1
  737. */
  738. public OutputStream getMessageOutputStream() {
  739. return msgOut;
  740. }
  741. private void recvWants() throws IOException {
  742. boolean isFirst = true;
  743. for (;;) {
  744. String line;
  745. try {
  746. line = pckIn.readString();
  747. } catch (EOFException eof) {
  748. if (isFirst)
  749. break;
  750. throw eof;
  751. }
  752. if (line == PacketLineIn.END)
  753. break;
  754. if (line.startsWith("deepen ")) { //$NON-NLS-1$
  755. depth = Integer.parseInt(line.substring(7));
  756. continue;
  757. }
  758. if (line.startsWith("shallow ")) { //$NON-NLS-1$
  759. clientShallowCommits.add(ObjectId.fromString(line.substring(8)));
  760. continue;
  761. }
  762. if (!line.startsWith("want ") || line.length() < 45) //$NON-NLS-1$
  763. throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "want", line)); //$NON-NLS-1$
  764. if (isFirst) {
  765. if (line.length() > 45) {
  766. FirstLine firstLine = new FirstLine(line);
  767. options = firstLine.getOptions();
  768. line = firstLine.getLine();
  769. } else
  770. options = Collections.emptySet();
  771. }
  772. wantIds.add(ObjectId.fromString(line.substring(5)));
  773. isFirst = false;
  774. }
  775. }
  776. private boolean negotiate() throws IOException {
  777. okToGiveUp = Boolean.FALSE;
  778. ObjectId last = ObjectId.zeroId();
  779. List<ObjectId> peerHas = new ArrayList<ObjectId>(64);
  780. for (;;) {
  781. String line;
  782. try {
  783. line = pckIn.readString();
  784. } catch (EOFException eof) {
  785. // EOF on stateless RPC (aka smart HTTP) and non-shallow request
  786. // means the client asked for the updated shallow/unshallow data,
  787. // disconnected, and will try another request with actual want/have.
  788. // Don't report the EOF here, its a bug in the protocol that the client
  789. // just disconnects without sending an END.
  790. if (!biDirectionalPipe && depth > 0)
  791. return false;
  792. throw eof;
  793. }
  794. if (line == PacketLineIn.END) {
  795. last = processHaveLines(peerHas, last);
  796. if (commonBase.isEmpty() || multiAck != MultiAck.OFF)
  797. pckOut.writeString("NAK\n"); //$NON-NLS-1$
  798. if (noDone && sentReady) {
  799. pckOut.writeString("ACK " + last.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  800. return true;
  801. }
  802. if (!biDirectionalPipe)
  803. return false;
  804. pckOut.flush();
  805. } else if (line.startsWith("have ") && line.length() == 45) { //$NON-NLS-1$
  806. peerHas.add(ObjectId.fromString(line.substring(5)));
  807. } else if (line.equals("done")) { //$NON-NLS-1$
  808. last = processHaveLines(peerHas, last);
  809. if (commonBase.isEmpty())
  810. pckOut.writeString("NAK\n"); //$NON-NLS-1$
  811. else if (multiAck != MultiAck.OFF)
  812. pckOut.writeString("ACK " + last.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  813. return true;
  814. } else {
  815. throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "have", line)); //$NON-NLS-1$
  816. }
  817. }
  818. }
  819. private ObjectId processHaveLines(List<ObjectId> peerHas, ObjectId last)
  820. throws IOException {
  821. preUploadHook.onBeginNegotiateRound(this, wantIds, peerHas.size());
  822. if (wantAll.isEmpty() && !wantIds.isEmpty())
  823. parseWants();
  824. if (peerHas.isEmpty())
  825. return last;
  826. sentReady = false;
  827. int haveCnt = 0;
  828. walk.getObjectReader().setAvoidUnreachableObjects(true);
  829. AsyncRevObjectQueue q = walk.parseAny(peerHas, false);
  830. try {
  831. for (;;) {
  832. RevObject obj;
  833. try {
  834. obj = q.next();
  835. } catch (MissingObjectException notFound) {
  836. continue;
  837. }
  838. if (obj == null)
  839. break;
  840. last = obj;
  841. haveCnt++;
  842. if (obj instanceof RevCommit) {
  843. RevCommit c = (RevCommit) obj;
  844. if (oldestTime == 0 || c.getCommitTime() < oldestTime)
  845. oldestTime = c.getCommitTime();
  846. }
  847. if (obj.has(PEER_HAS))
  848. continue;
  849. obj.add(PEER_HAS);
  850. if (obj instanceof RevCommit)
  851. ((RevCommit) obj).carry(PEER_HAS);
  852. addCommonBase(obj);
  853. // If both sides have the same object; let the client know.
  854. //
  855. switch (multiAck) {
  856. case OFF:
  857. if (commonBase.size() == 1)
  858. pckOut.writeString("ACK " + obj.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  859. break;
  860. case CONTINUE:
  861. pckOut.writeString("ACK " + obj.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
  862. break;
  863. case DETAILED:
  864. pckOut.writeString("ACK " + obj.name() + " common\n"); //$NON-NLS-1$ //$NON-NLS-2$
  865. break;
  866. }
  867. }
  868. } finally {
  869. q.release();
  870. walk.getObjectReader().setAvoidUnreachableObjects(false);
  871. }
  872. int missCnt = peerHas.size() - haveCnt;
  873. // If we don't have one of the objects but we're also willing to
  874. // create a pack at this point, let the client know so it stops
  875. // telling us about its history.
  876. //
  877. boolean didOkToGiveUp = false;
  878. if (0 < missCnt) {
  879. for (int i = peerHas.size() - 1; i >= 0; i--) {
  880. ObjectId id = peerHas.get(i);
  881. if (walk.lookupOrNull(id) == null) {
  882. didOkToGiveUp = true;
  883. if (okToGiveUp()) {
  884. switch (multiAck) {
  885. case OFF:
  886. break;
  887. case CONTINUE:
  888. pckOut.writeString("ACK " + id.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
  889. break;
  890. case DETAILED:
  891. pckOut.writeString("ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
  892. sentReady = true;
  893. break;
  894. }
  895. }
  896. break;
  897. }
  898. }
  899. }
  900. if (multiAck == MultiAck.DETAILED && !didOkToGiveUp && okToGiveUp()) {
  901. ObjectId id = peerHas.get(peerHas.size() - 1);
  902. sentReady = true;
  903. pckOut.writeString("ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
  904. sentReady = true;
  905. }
  906. preUploadHook.onEndNegotiateRound(this, wantAll, haveCnt, missCnt, sentReady);
  907. peerHas.clear();
  908. return last;
  909. }
  910. private void parseWants() throws IOException {
  911. List<ObjectId> notAdvertisedWants = null;
  912. for (ObjectId obj : wantIds) {
  913. if (!advertised.contains(obj)) {
  914. if (notAdvertisedWants == null)
  915. notAdvertisedWants = new ArrayList<ObjectId>();
  916. notAdvertisedWants.add(obj);
  917. }
  918. }
  919. if (notAdvertisedWants != null)
  920. requestValidator.checkWants(this, notAdvertisedWants);
  921. AsyncRevObjectQueue q = walk.parseAny(wantIds, true);
  922. try {
  923. RevObject obj;
  924. while ((obj = q.next()) != null) {
  925. want(obj);
  926. if (!(obj instanceof RevCommit))
  927. obj.add(SATISFIED);
  928. if (obj instanceof RevTag) {
  929. obj = walk.peel(obj);
  930. if (obj instanceof RevCommit)
  931. want(obj);
  932. }
  933. }
  934. wantIds.clear();
  935. } catch (MissingObjectException notFound) {
  936. ObjectId id = notFound.getObjectId();
  937. throw new PackProtocolException(MessageFormat.format(
  938. JGitText.get().wantNotValid, id.name()), notFound);
  939. } finally {
  940. q.release();
  941. }
  942. }
  943. private void want(RevObject obj) {
  944. if (!obj.has(WANT)) {
  945. obj.add(WANT);
  946. wantAll.add(obj);
  947. }
  948. }
  949. /**
  950. * Validator corresponding to {@link RequestPolicy#ADVERTISED}.
  951. *
  952. * @since 3.1
  953. */
  954. public static final class AdvertisedRequestValidator
  955. implements RequestValidator {
  956. public void checkWants(UploadPack up, List<ObjectId> wants)
  957. throws PackProtocolException, IOException {
  958. if (!up.isBiDirectionalPipe())
  959. new ReachableCommitRequestValidator().checkWants(up, wants);
  960. else if (!wants.isEmpty())
  961. throw new PackProtocolException(MessageFormat.format(
  962. JGitText.get().wantNotValid, wants.iterator().next().name()));
  963. }
  964. }
  965. /**
  966. * Validator corresponding to {@link RequestPolicy#REACHABLE_COMMIT}.
  967. *
  968. * @since 3.1
  969. */
  970. public static final class ReachableCommitRequestValidator
  971. implements RequestValidator {
  972. public void checkWants(UploadPack up, List<ObjectId> wants)
  973. throws PackProtocolException, IOException {
  974. checkNotAdvertisedWants(up.getRevWalk(), wants,
  975. refIdSet(up.getAdvertisedRefs().values()));
  976. }
  977. }
  978. /**
  979. * Validator corresponding to {@link RequestPolicy#TIP}.
  980. *
  981. * @since 3.1
  982. */
  983. public static final class TipRequestValidator implements RequestValidator {
  984. public void checkWants(UploadPack up, List<ObjectId> wants)
  985. throws PackProtocolException, IOException {
  986. if (!up.isBiDirectionalPipe())
  987. new ReachableCommitTipRequestValidator().checkWants(up, wants);
  988. else if (!wants.isEmpty()) {
  989. Set<ObjectId> refIds =
  990. refIdSet(up.getRepository().getRefDatabase().getRefs(ALL).values());
  991. for (ObjectId obj : wants) {
  992. if (!refIds.contains(obj))
  993. throw new PackProtocolException(MessageFormat.format(
  994. JGitText.get().wantNotValid, obj.name()));
  995. }
  996. }
  997. }
  998. }
  999. /**
  1000. * Validator corresponding to {@link RequestPolicy#REACHABLE_COMMIT_TIP}.
  1001. *
  1002. * @since 3.1
  1003. */
  1004. public static final class ReachableCommitTipRequestValidator
  1005. implements RequestValidator {
  1006. public void checkWants(UploadPack up, List<ObjectId> wants)
  1007. throws PackProtocolException, IOException {
  1008. checkNotAdvertisedWants(up.getRevWalk(), wants,
  1009. refIdSet(up.getRepository().getRefDatabase().getRefs(ALL).values()));
  1010. }
  1011. }
  1012. /**
  1013. * Validator corresponding to {@link RequestPolicy#ANY}.
  1014. *
  1015. * @since 3.1
  1016. */
  1017. public static final class AnyRequestValidator implements RequestValidator {
  1018. public void checkWants(UploadPack up, List<ObjectId> wants)
  1019. throws PackProtocolException, IOException {
  1020. // All requests are valid.
  1021. }
  1022. }
  1023. private static void checkNotAdvertisedWants(RevWalk walk,
  1024. List<ObjectId> notAdvertisedWants, Set<ObjectId> reachableFrom)
  1025. throws MissingObjectException, IncorrectObjectTypeException, IOException {
  1026. // Walk the requested commits back to the provided set of commits. If any
  1027. // commit exists, a branch was deleted or rewound and the repository owner
  1028. // no longer exports that requested item. If the requested commit is merged
  1029. // into an advertised branch it will be marked UNINTERESTING and no commits
  1030. // return.
  1031. AsyncRevObjectQueue q = walk.parseAny(notAdvertisedWants, true);
  1032. try {
  1033. RevObject obj;
  1034. while ((obj = q.next()) != null) {
  1035. if (!(obj instanceof RevCommit))
  1036. throw new PackProtocolException(MessageFormat.format(
  1037. JGitText.get().wantNotValid, obj.name()));
  1038. walk.markStart((RevCommit) obj);
  1039. }
  1040. } catch (MissingObjectException notFound) {
  1041. ObjectId id = notFound.getObjectId();
  1042. throw new PackProtocolException(MessageFormat.format(
  1043. JGitText.get().wantNotValid, id.name()), notFound);
  1044. } finally {
  1045. q.release();
  1046. }
  1047. for (ObjectId id : reachableFrom) {
  1048. try {
  1049. walk.markUninteresting(walk.parseCommit(id));
  1050. } catch (IncorrectObjectTypeException notCommit) {
  1051. continue;
  1052. }
  1053. }
  1054. RevCommit bad = walk.next();
  1055. if (bad != null) {
  1056. throw new PackProtocolException(MessageFormat.format(
  1057. JGitText.get().wantNotValid,
  1058. bad.name()));
  1059. }
  1060. walk.reset();
  1061. }
  1062. private void addCommonBase(final RevObject o) {
  1063. if (!o.has(COMMON)) {
  1064. o.add(COMMON);
  1065. commonBase.add(o);
  1066. okToGiveUp = null;
  1067. }
  1068. }
  1069. private boolean okToGiveUp() throws PackProtocolException {
  1070. if (okToGiveUp == null)
  1071. okToGiveUp = Boolean.valueOf(okToGiveUpImp());
  1072. return okToGiveUp.booleanValue();
  1073. }
  1074. private boolean okToGiveUpImp() throws PackProtocolException {
  1075. if (commonBase.isEmpty())
  1076. return false;
  1077. try {
  1078. for (RevObject obj : wantAll) {
  1079. if (!wantSatisfied(obj))
  1080. return false;
  1081. }
  1082. return true;
  1083. } catch (IOException e) {
  1084. throw new PackProtocolException(JGitText.get().internalRevisionError, e);
  1085. }
  1086. }
  1087. private boolean wantSatisfied(final RevObject want) throws IOException {
  1088. if (want.has(SATISFIED))
  1089. return true;
  1090. walk.resetRetain(SAVE);
  1091. walk.markStart((RevCommit) want);
  1092. if (oldestTime != 0)
  1093. walk.setRevFilter(CommitTimeRevFilter.after(oldestTime * 1000L));
  1094. for (;;) {
  1095. final RevCommit c = walk.next();
  1096. if (c == null)
  1097. break;
  1098. if (c.has(PEER_HAS)) {
  1099. addCommonBase(c);
  1100. want.add(SATISFIED);
  1101. return true;
  1102. }
  1103. }
  1104. return false;
  1105. }
  1106. private void sendPack() throws IOException {
  1107. final boolean sideband = options.contains(OPTION_SIDE_BAND)
  1108. || options.contains(OPTION_SIDE_BAND_64K);
  1109. if (!biDirectionalPipe) {
  1110. // Ensure the request was fully consumed. Any remaining input must
  1111. // be a protocol error. If we aren't at EOF the implementation is broken.
  1112. int eof = rawIn.read();
  1113. if (0 <= eof)
  1114. throw new CorruptObjectException(MessageFormat.format(
  1115. JGitText.get().expectedEOFReceived,
  1116. "\\x" + Integer.toHexString(eof))); //$NON-NLS-1$
  1117. }
  1118. if (sideband) {
  1119. try {
  1120. sendPack(true);
  1121. } catch (ServiceMayNotContinueException noPack) {
  1122. // This was already reported on (below).
  1123. throw noPack;
  1124. } catch (IOException err) {
  1125. if (reportInternalServerErrorOverSideband())
  1126. throw new UploadPackInternalServerErrorException(err);
  1127. else
  1128. throw err;
  1129. } catch (RuntimeException err) {
  1130. if (reportInternalServerErrorOverSideband())
  1131. throw new UploadPackInternalServerErrorException(err);
  1132. else
  1133. throw err;
  1134. } catch (Error err) {
  1135. if (reportInternalServerErrorOverSideband())
  1136. throw new UploadPackInternalServerErrorException(err);
  1137. else
  1138. throw err;
  1139. }
  1140. } else {
  1141. sendPack(false);
  1142. }
  1143. }
  1144. private boolean reportInternalServerErrorOverSideband() {
  1145. try {
  1146. @SuppressWarnings("resource" /* java 7 */)
  1147. SideBandOutputStream err = new SideBandOutputStream(
  1148. SideBandOutputStream.CH_ERROR,
  1149. SideBandOutputStream.SMALL_BUF,
  1150. rawOut);
  1151. err.write(Constants.encode(JGitText.get().internalServerError));
  1152. err.flush();
  1153. return true;
  1154. } catch (Throwable cannotReport) {
  1155. // Ignore the reason. This is a secondary failure.
  1156. return false;
  1157. }
  1158. }
  1159. private void sendPack(final boolean sideband) throws IOException {
  1160. ProgressMonitor pm = NullProgressMonitor.INSTANCE;
  1161. OutputStream packOut = rawOut;
  1162. if (sideband) {
  1163. int bufsz = SideBandOutputStream.SMALL_BUF;
  1164. if (options.contains(OPTION_SIDE_BAND_64K))
  1165. bufsz = SideBandOutputStream.MAX_BUF;
  1166. packOut = new SideBandOutputStream(SideBandOutputStream.CH_DATA,
  1167. bufsz, rawOut);
  1168. if (!options.contains(OPTION_NO_PROGRESS)) {
  1169. msgOut = new SideBandOutputStream(
  1170. SideBandOutputStream.CH_PROGRESS, bufsz, rawOut);
  1171. pm = new SideBandProgressMonitor(msgOut);
  1172. }
  1173. }
  1174. try {
  1175. if (wantAll.isEmpty()) {
  1176. preUploadHook.onSendPack(this, wantIds, commonBase);
  1177. } else {
  1178. preUploadHook.onSendPack(this, wantAll, commonBase);
  1179. }
  1180. msgOut.flush();
  1181. } catch (ServiceMayNotContinueException noPack) {
  1182. if (sideband && noPack.getMessage() != null) {
  1183. noPack.setOutput();
  1184. @SuppressWarnings("resource" /* java 7 */)
  1185. SideBandOutputStream err = new SideBandOutputStream(
  1186. SideBandOutputStream.CH_ERROR,
  1187. SideBandOutputStream.SMALL_BUF, rawOut);
  1188. err.write(Constants.encode(noPack.getMessage()));
  1189. err.flush();
  1190. }
  1191. throw noPack;
  1192. }
  1193. PackConfig cfg = packConfig;
  1194. if (cfg == null)
  1195. cfg = new PackConfig(db);
  1196. final PackWriter pw = new PackWriter(cfg, walk.getObjectReader());
  1197. try {
  1198. pw.setIndexDisabled(true);
  1199. pw.setUseCachedPacks(true);
  1200. pw.setUseBitmaps(depth == 0 && clientShallowCommits.isEmpty());
  1201. pw.setReuseDeltaCommits(true);
  1202. pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA));
  1203. pw.setThin(options.contains(OPTION_THIN_PACK));
  1204. pw.setReuseValidatingObjects(false);
  1205. if (commonBase.isEmpty() && refs != null) {
  1206. Set<ObjectId> tagTargets = new HashSet<ObjectId>();
  1207. for (Ref ref : refs.values()) {
  1208. if (ref.getPeeledObjectId() != null)
  1209. tagTargets.add(ref.getPeeledObjectId());
  1210. else if (ref.getObjectId() == null)
  1211. continue;
  1212. else if (ref.getName().startsWith(Constants.R_HEADS))
  1213. tagTargets.add(ref.getObjectId());
  1214. }
  1215. pw.setTagTargets(tagTargets);
  1216. }
  1217. if (depth > 0)
  1218. pw.setShallowPack(depth, unshallowCommits);
  1219. RevWalk rw = walk;
  1220. if (wantAll.isEmpty()) {
  1221. pw.preparePack(pm, wantIds, commonBase);
  1222. } else {
  1223. walk.reset();
  1224. ObjectWalk ow = walk.toObjectWalkWithSameObjects();
  1225. pw.preparePack(pm, ow, wantAll, commonBase);
  1226. rw = ow;
  1227. }
  1228. if (options.contains(OPTION_INCLUDE_TAG) && refs != null) {
  1229. for (Ref ref : refs.values()) {
  1230. ObjectId objectId = ref.getObjectId();
  1231. // If the object was already requested, skip it.
  1232. if (wantAll.isEmpty()) {
  1233. if (wantIds.contains(objectId))
  1234. continue;
  1235. } else {
  1236. RevObject obj = rw.lookupOrNull(objectId);
  1237. if (obj != null && obj.has(WANT))
  1238. continue;
  1239. }
  1240. if (!ref.isPeeled())
  1241. ref = db.peel(ref);
  1242. ObjectId peeledId = ref.getPeeledObjectId();
  1243. if (peeledId == null)
  1244. continue;
  1245. objectId = ref.getObjectId();
  1246. if (pw.willInclude(peeledId) && !pw.willInclude(objectId))
  1247. pw.addObject(rw.parseAny(objectId));
  1248. }
  1249. }
  1250. pw.writePack(pm, NullProgressMonitor.INSTANCE, packOut);
  1251. if (msgOut != NullOutputStream.INSTANCE) {
  1252. String msg = pw.getStatistics().getMessage() + '\n';
  1253. msgOut.write(Constants.encode(msg));
  1254. msgOut.flush();
  1255. }
  1256. } finally {
  1257. statistics = pw.getStatistics();
  1258. if (statistics != null)
  1259. logger.onPackStatistics(statistics);
  1260. pw.release();
  1261. }
  1262. if (sideband)
  1263. pckOut.end();
  1264. }
  1265. }