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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443
  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. Map<String, Ref> refs = getAdvertisedOrDefaultRefs();
  712. findSymrefs(adv, refs);
  713. advertised = adv.send(refs);
  714. if (adv.isEmpty())
  715. adv.advertiseId(ObjectId.zeroId(), "capabilities^{}"); //$NON-NLS-1$
  716. adv.end();
  717. }
  718. /**
  719. * Send a message to the client, if it supports receiving them.
  720. * <p>
  721. * If the client doesn't support receiving messages, the message will be
  722. * discarded, with no other indication to the caller or to the client.
  723. *
  724. * @param what
  725. * string describing the problem identified by the hook. The
  726. * string must not end with an LF, and must not contain an LF.
  727. * @since 3.1
  728. */
  729. public void sendMessage(String what) {
  730. try {
  731. msgOut.write(Constants.encode(what + "\n")); //$NON-NLS-1$
  732. } catch (IOException e) {
  733. // Ignore write failures.
  734. }
  735. }
  736. /**
  737. * @return an underlying stream for sending messages to the client, or null.
  738. * @since 3.1
  739. */
  740. public OutputStream getMessageOutputStream() {
  741. return msgOut;
  742. }
  743. private void recvWants() throws IOException {
  744. boolean isFirst = true;
  745. for (;;) {
  746. String line;
  747. try {
  748. line = pckIn.readString();
  749. } catch (EOFException eof) {
  750. if (isFirst)
  751. break;
  752. throw eof;
  753. }
  754. if (line == PacketLineIn.END)
  755. break;
  756. if (line.startsWith("deepen ")) { //$NON-NLS-1$
  757. depth = Integer.parseInt(line.substring(7));
  758. continue;
  759. }
  760. if (line.startsWith("shallow ")) { //$NON-NLS-1$
  761. clientShallowCommits.add(ObjectId.fromString(line.substring(8)));
  762. continue;
  763. }
  764. if (!line.startsWith("want ") || line.length() < 45) //$NON-NLS-1$
  765. throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "want", line)); //$NON-NLS-1$
  766. if (isFirst) {
  767. if (line.length() > 45) {
  768. FirstLine firstLine = new FirstLine(line);
  769. options = firstLine.getOptions();
  770. line = firstLine.getLine();
  771. } else
  772. options = Collections.emptySet();
  773. }
  774. wantIds.add(ObjectId.fromString(line.substring(5)));
  775. isFirst = false;
  776. }
  777. }
  778. private boolean negotiate() throws IOException {
  779. okToGiveUp = Boolean.FALSE;
  780. ObjectId last = ObjectId.zeroId();
  781. List<ObjectId> peerHas = new ArrayList<ObjectId>(64);
  782. for (;;) {
  783. String line;
  784. try {
  785. line = pckIn.readString();
  786. } catch (EOFException eof) {
  787. // EOF on stateless RPC (aka smart HTTP) and non-shallow request
  788. // means the client asked for the updated shallow/unshallow data,
  789. // disconnected, and will try another request with actual want/have.
  790. // Don't report the EOF here, its a bug in the protocol that the client
  791. // just disconnects without sending an END.
  792. if (!biDirectionalPipe && depth > 0)
  793. return false;
  794. throw eof;
  795. }
  796. if (line == PacketLineIn.END) {
  797. last = processHaveLines(peerHas, last);
  798. if (commonBase.isEmpty() || multiAck != MultiAck.OFF)
  799. pckOut.writeString("NAK\n"); //$NON-NLS-1$
  800. if (noDone && sentReady) {
  801. pckOut.writeString("ACK " + last.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  802. return true;
  803. }
  804. if (!biDirectionalPipe)
  805. return false;
  806. pckOut.flush();
  807. } else if (line.startsWith("have ") && line.length() == 45) { //$NON-NLS-1$
  808. peerHas.add(ObjectId.fromString(line.substring(5)));
  809. } else if (line.equals("done")) { //$NON-NLS-1$
  810. last = processHaveLines(peerHas, last);
  811. if (commonBase.isEmpty())
  812. pckOut.writeString("NAK\n"); //$NON-NLS-1$
  813. else if (multiAck != MultiAck.OFF)
  814. pckOut.writeString("ACK " + last.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  815. return true;
  816. } else {
  817. throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "have", line)); //$NON-NLS-1$
  818. }
  819. }
  820. }
  821. private ObjectId processHaveLines(List<ObjectId> peerHas, ObjectId last)
  822. throws IOException {
  823. preUploadHook.onBeginNegotiateRound(this, wantIds, peerHas.size());
  824. if (wantAll.isEmpty() && !wantIds.isEmpty())
  825. parseWants();
  826. if (peerHas.isEmpty())
  827. return last;
  828. sentReady = false;
  829. int haveCnt = 0;
  830. walk.getObjectReader().setAvoidUnreachableObjects(true);
  831. AsyncRevObjectQueue q = walk.parseAny(peerHas, false);
  832. try {
  833. for (;;) {
  834. RevObject obj;
  835. try {
  836. obj = q.next();
  837. } catch (MissingObjectException notFound) {
  838. continue;
  839. }
  840. if (obj == null)
  841. break;
  842. last = obj;
  843. haveCnt++;
  844. if (obj instanceof RevCommit) {
  845. RevCommit c = (RevCommit) obj;
  846. if (oldestTime == 0 || c.getCommitTime() < oldestTime)
  847. oldestTime = c.getCommitTime();
  848. }
  849. if (obj.has(PEER_HAS))
  850. continue;
  851. obj.add(PEER_HAS);
  852. if (obj instanceof RevCommit)
  853. ((RevCommit) obj).carry(PEER_HAS);
  854. addCommonBase(obj);
  855. // If both sides have the same object; let the client know.
  856. //
  857. switch (multiAck) {
  858. case OFF:
  859. if (commonBase.size() == 1)
  860. pckOut.writeString("ACK " + obj.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  861. break;
  862. case CONTINUE:
  863. pckOut.writeString("ACK " + obj.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
  864. break;
  865. case DETAILED:
  866. pckOut.writeString("ACK " + obj.name() + " common\n"); //$NON-NLS-1$ //$NON-NLS-2$
  867. break;
  868. }
  869. }
  870. } finally {
  871. q.release();
  872. walk.getObjectReader().setAvoidUnreachableObjects(false);
  873. }
  874. int missCnt = peerHas.size() - haveCnt;
  875. // If we don't have one of the objects but we're also willing to
  876. // create a pack at this point, let the client know so it stops
  877. // telling us about its history.
  878. //
  879. boolean didOkToGiveUp = false;
  880. if (0 < missCnt) {
  881. for (int i = peerHas.size() - 1; i >= 0; i--) {
  882. ObjectId id = peerHas.get(i);
  883. if (walk.lookupOrNull(id) == null) {
  884. didOkToGiveUp = true;
  885. if (okToGiveUp()) {
  886. switch (multiAck) {
  887. case OFF:
  888. break;
  889. case CONTINUE:
  890. pckOut.writeString("ACK " + id.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
  891. break;
  892. case DETAILED:
  893. pckOut.writeString("ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
  894. sentReady = true;
  895. break;
  896. }
  897. }
  898. break;
  899. }
  900. }
  901. }
  902. if (multiAck == MultiAck.DETAILED && !didOkToGiveUp && okToGiveUp()) {
  903. ObjectId id = peerHas.get(peerHas.size() - 1);
  904. sentReady = true;
  905. pckOut.writeString("ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
  906. sentReady = true;
  907. }
  908. preUploadHook.onEndNegotiateRound(this, wantAll, haveCnt, missCnt, sentReady);
  909. peerHas.clear();
  910. return last;
  911. }
  912. private void parseWants() throws IOException {
  913. List<ObjectId> notAdvertisedWants = null;
  914. for (ObjectId obj : wantIds) {
  915. if (!advertised.contains(obj)) {
  916. if (notAdvertisedWants == null)
  917. notAdvertisedWants = new ArrayList<ObjectId>();
  918. notAdvertisedWants.add(obj);
  919. }
  920. }
  921. if (notAdvertisedWants != null)
  922. requestValidator.checkWants(this, notAdvertisedWants);
  923. AsyncRevObjectQueue q = walk.parseAny(wantIds, true);
  924. try {
  925. RevObject obj;
  926. while ((obj = q.next()) != null) {
  927. want(obj);
  928. if (!(obj instanceof RevCommit))
  929. obj.add(SATISFIED);
  930. if (obj instanceof RevTag) {
  931. obj = walk.peel(obj);
  932. if (obj instanceof RevCommit)
  933. want(obj);
  934. }
  935. }
  936. wantIds.clear();
  937. } catch (MissingObjectException notFound) {
  938. ObjectId id = notFound.getObjectId();
  939. throw new PackProtocolException(MessageFormat.format(
  940. JGitText.get().wantNotValid, id.name()), notFound);
  941. } finally {
  942. q.release();
  943. }
  944. }
  945. private void want(RevObject obj) {
  946. if (!obj.has(WANT)) {
  947. obj.add(WANT);
  948. wantAll.add(obj);
  949. }
  950. }
  951. /**
  952. * Validator corresponding to {@link RequestPolicy#ADVERTISED}.
  953. *
  954. * @since 3.1
  955. */
  956. public static final class AdvertisedRequestValidator
  957. implements RequestValidator {
  958. public void checkWants(UploadPack up, List<ObjectId> wants)
  959. throws PackProtocolException, IOException {
  960. if (!up.isBiDirectionalPipe())
  961. new ReachableCommitRequestValidator().checkWants(up, wants);
  962. else if (!wants.isEmpty())
  963. throw new PackProtocolException(MessageFormat.format(
  964. JGitText.get().wantNotValid, wants.iterator().next().name()));
  965. }
  966. }
  967. /**
  968. * Validator corresponding to {@link RequestPolicy#REACHABLE_COMMIT}.
  969. *
  970. * @since 3.1
  971. */
  972. public static final class ReachableCommitRequestValidator
  973. implements RequestValidator {
  974. public void checkWants(UploadPack up, List<ObjectId> wants)
  975. throws PackProtocolException, IOException {
  976. checkNotAdvertisedWants(up.getRevWalk(), wants,
  977. refIdSet(up.getAdvertisedRefs().values()));
  978. }
  979. }
  980. /**
  981. * Validator corresponding to {@link RequestPolicy#TIP}.
  982. *
  983. * @since 3.1
  984. */
  985. public static final class TipRequestValidator implements RequestValidator {
  986. public void checkWants(UploadPack up, List<ObjectId> wants)
  987. throws PackProtocolException, IOException {
  988. if (!up.isBiDirectionalPipe())
  989. new ReachableCommitTipRequestValidator().checkWants(up, wants);
  990. else if (!wants.isEmpty()) {
  991. Set<ObjectId> refIds =
  992. refIdSet(up.getRepository().getRefDatabase().getRefs(ALL).values());
  993. for (ObjectId obj : wants) {
  994. if (!refIds.contains(obj))
  995. throw new PackProtocolException(MessageFormat.format(
  996. JGitText.get().wantNotValid, obj.name()));
  997. }
  998. }
  999. }
  1000. }
  1001. /**
  1002. * Validator corresponding to {@link RequestPolicy#REACHABLE_COMMIT_TIP}.
  1003. *
  1004. * @since 3.1
  1005. */
  1006. public static final class ReachableCommitTipRequestValidator
  1007. implements RequestValidator {
  1008. public void checkWants(UploadPack up, List<ObjectId> wants)
  1009. throws PackProtocolException, IOException {
  1010. checkNotAdvertisedWants(up.getRevWalk(), wants,
  1011. refIdSet(up.getRepository().getRefDatabase().getRefs(ALL).values()));
  1012. }
  1013. }
  1014. /**
  1015. * Validator corresponding to {@link RequestPolicy#ANY}.
  1016. *
  1017. * @since 3.1
  1018. */
  1019. public static final class AnyRequestValidator implements RequestValidator {
  1020. public void checkWants(UploadPack up, List<ObjectId> wants)
  1021. throws PackProtocolException, IOException {
  1022. // All requests are valid.
  1023. }
  1024. }
  1025. private static void checkNotAdvertisedWants(RevWalk walk,
  1026. List<ObjectId> notAdvertisedWants, Set<ObjectId> reachableFrom)
  1027. throws MissingObjectException, IncorrectObjectTypeException, IOException {
  1028. // Walk the requested commits back to the provided set of commits. If any
  1029. // commit exists, a branch was deleted or rewound and the repository owner
  1030. // no longer exports that requested item. If the requested commit is merged
  1031. // into an advertised branch it will be marked UNINTERESTING and no commits
  1032. // return.
  1033. AsyncRevObjectQueue q = walk.parseAny(notAdvertisedWants, true);
  1034. try {
  1035. RevObject obj;
  1036. while ((obj = q.next()) != null) {
  1037. if (!(obj instanceof RevCommit))
  1038. throw new PackProtocolException(MessageFormat.format(
  1039. JGitText.get().wantNotValid, obj.name()));
  1040. walk.markStart((RevCommit) obj);
  1041. }
  1042. } catch (MissingObjectException notFound) {
  1043. ObjectId id = notFound.getObjectId();
  1044. throw new PackProtocolException(MessageFormat.format(
  1045. JGitText.get().wantNotValid, id.name()), notFound);
  1046. } finally {
  1047. q.release();
  1048. }
  1049. for (ObjectId id : reachableFrom) {
  1050. try {
  1051. walk.markUninteresting(walk.parseCommit(id));
  1052. } catch (IncorrectObjectTypeException notCommit) {
  1053. continue;
  1054. }
  1055. }
  1056. RevCommit bad = walk.next();
  1057. if (bad != null) {
  1058. throw new PackProtocolException(MessageFormat.format(
  1059. JGitText.get().wantNotValid,
  1060. bad.name()));
  1061. }
  1062. walk.reset();
  1063. }
  1064. private void addCommonBase(final RevObject o) {
  1065. if (!o.has(COMMON)) {
  1066. o.add(COMMON);
  1067. commonBase.add(o);
  1068. okToGiveUp = null;
  1069. }
  1070. }
  1071. private boolean okToGiveUp() throws PackProtocolException {
  1072. if (okToGiveUp == null)
  1073. okToGiveUp = Boolean.valueOf(okToGiveUpImp());
  1074. return okToGiveUp.booleanValue();
  1075. }
  1076. private boolean okToGiveUpImp() throws PackProtocolException {
  1077. if (commonBase.isEmpty())
  1078. return false;
  1079. try {
  1080. for (RevObject obj : wantAll) {
  1081. if (!wantSatisfied(obj))
  1082. return false;
  1083. }
  1084. return true;
  1085. } catch (IOException e) {
  1086. throw new PackProtocolException(JGitText.get().internalRevisionError, e);
  1087. }
  1088. }
  1089. private boolean wantSatisfied(final RevObject want) throws IOException {
  1090. if (want.has(SATISFIED))
  1091. return true;
  1092. walk.resetRetain(SAVE);
  1093. walk.markStart((RevCommit) want);
  1094. if (oldestTime != 0)
  1095. walk.setRevFilter(CommitTimeRevFilter.after(oldestTime * 1000L));
  1096. for (;;) {
  1097. final RevCommit c = walk.next();
  1098. if (c == null)
  1099. break;
  1100. if (c.has(PEER_HAS)) {
  1101. addCommonBase(c);
  1102. want.add(SATISFIED);
  1103. return true;
  1104. }
  1105. }
  1106. return false;
  1107. }
  1108. private void sendPack() throws IOException {
  1109. final boolean sideband = options.contains(OPTION_SIDE_BAND)
  1110. || options.contains(OPTION_SIDE_BAND_64K);
  1111. if (!biDirectionalPipe) {
  1112. // Ensure the request was fully consumed. Any remaining input must
  1113. // be a protocol error. If we aren't at EOF the implementation is broken.
  1114. int eof = rawIn.read();
  1115. if (0 <= eof)
  1116. throw new CorruptObjectException(MessageFormat.format(
  1117. JGitText.get().expectedEOFReceived,
  1118. "\\x" + Integer.toHexString(eof))); //$NON-NLS-1$
  1119. }
  1120. if (sideband) {
  1121. try {
  1122. sendPack(true);
  1123. } catch (ServiceMayNotContinueException noPack) {
  1124. // This was already reported on (below).
  1125. throw noPack;
  1126. } catch (IOException err) {
  1127. if (reportInternalServerErrorOverSideband())
  1128. throw new UploadPackInternalServerErrorException(err);
  1129. else
  1130. throw err;
  1131. } catch (RuntimeException err) {
  1132. if (reportInternalServerErrorOverSideband())
  1133. throw new UploadPackInternalServerErrorException(err);
  1134. else
  1135. throw err;
  1136. } catch (Error err) {
  1137. if (reportInternalServerErrorOverSideband())
  1138. throw new UploadPackInternalServerErrorException(err);
  1139. else
  1140. throw err;
  1141. }
  1142. } else {
  1143. sendPack(false);
  1144. }
  1145. }
  1146. private boolean reportInternalServerErrorOverSideband() {
  1147. try {
  1148. @SuppressWarnings("resource" /* java 7 */)
  1149. SideBandOutputStream err = new SideBandOutputStream(
  1150. SideBandOutputStream.CH_ERROR,
  1151. SideBandOutputStream.SMALL_BUF,
  1152. rawOut);
  1153. err.write(Constants.encode(JGitText.get().internalServerError));
  1154. err.flush();
  1155. return true;
  1156. } catch (Throwable cannotReport) {
  1157. // Ignore the reason. This is a secondary failure.
  1158. return false;
  1159. }
  1160. }
  1161. private void sendPack(final boolean sideband) throws IOException {
  1162. ProgressMonitor pm = NullProgressMonitor.INSTANCE;
  1163. OutputStream packOut = rawOut;
  1164. if (sideband) {
  1165. int bufsz = SideBandOutputStream.SMALL_BUF;
  1166. if (options.contains(OPTION_SIDE_BAND_64K))
  1167. bufsz = SideBandOutputStream.MAX_BUF;
  1168. packOut = new SideBandOutputStream(SideBandOutputStream.CH_DATA,
  1169. bufsz, rawOut);
  1170. if (!options.contains(OPTION_NO_PROGRESS)) {
  1171. msgOut = new SideBandOutputStream(
  1172. SideBandOutputStream.CH_PROGRESS, bufsz, rawOut);
  1173. pm = new SideBandProgressMonitor(msgOut);
  1174. }
  1175. }
  1176. try {
  1177. if (wantAll.isEmpty()) {
  1178. preUploadHook.onSendPack(this, wantIds, commonBase);
  1179. } else {
  1180. preUploadHook.onSendPack(this, wantAll, commonBase);
  1181. }
  1182. msgOut.flush();
  1183. } catch (ServiceMayNotContinueException noPack) {
  1184. if (sideband && noPack.getMessage() != null) {
  1185. noPack.setOutput();
  1186. @SuppressWarnings("resource" /* java 7 */)
  1187. SideBandOutputStream err = new SideBandOutputStream(
  1188. SideBandOutputStream.CH_ERROR,
  1189. SideBandOutputStream.SMALL_BUF, rawOut);
  1190. err.write(Constants.encode(noPack.getMessage()));
  1191. err.flush();
  1192. }
  1193. throw noPack;
  1194. }
  1195. PackConfig cfg = packConfig;
  1196. if (cfg == null)
  1197. cfg = new PackConfig(db);
  1198. final PackWriter pw = new PackWriter(cfg, walk.getObjectReader());
  1199. try {
  1200. pw.setIndexDisabled(true);
  1201. pw.setUseCachedPacks(true);
  1202. pw.setUseBitmaps(depth == 0 && clientShallowCommits.isEmpty());
  1203. pw.setReuseDeltaCommits(true);
  1204. pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA));
  1205. pw.setThin(options.contains(OPTION_THIN_PACK));
  1206. pw.setReuseValidatingObjects(false);
  1207. if (commonBase.isEmpty() && refs != null) {
  1208. Set<ObjectId> tagTargets = new HashSet<ObjectId>();
  1209. for (Ref ref : refs.values()) {
  1210. if (ref.getPeeledObjectId() != null)
  1211. tagTargets.add(ref.getPeeledObjectId());
  1212. else if (ref.getObjectId() == null)
  1213. continue;
  1214. else if (ref.getName().startsWith(Constants.R_HEADS))
  1215. tagTargets.add(ref.getObjectId());
  1216. }
  1217. pw.setTagTargets(tagTargets);
  1218. }
  1219. if (depth > 0)
  1220. pw.setShallowPack(depth, unshallowCommits);
  1221. RevWalk rw = walk;
  1222. if (wantAll.isEmpty()) {
  1223. pw.preparePack(pm, wantIds, commonBase);
  1224. } else {
  1225. walk.reset();
  1226. ObjectWalk ow = walk.toObjectWalkWithSameObjects();
  1227. pw.preparePack(pm, ow, wantAll, commonBase);
  1228. rw = ow;
  1229. }
  1230. if (options.contains(OPTION_INCLUDE_TAG) && refs != null) {
  1231. for (Ref ref : refs.values()) {
  1232. ObjectId objectId = ref.getObjectId();
  1233. // If the object was already requested, skip it.
  1234. if (wantAll.isEmpty()) {
  1235. if (wantIds.contains(objectId))
  1236. continue;
  1237. } else {
  1238. RevObject obj = rw.lookupOrNull(objectId);
  1239. if (obj != null && obj.has(WANT))
  1240. continue;
  1241. }
  1242. if (!ref.isPeeled())
  1243. ref = db.peel(ref);
  1244. ObjectId peeledId = ref.getPeeledObjectId();
  1245. if (peeledId == null)
  1246. continue;
  1247. objectId = ref.getObjectId();
  1248. if (pw.willInclude(peeledId) && !pw.willInclude(objectId))
  1249. pw.addObject(rw.parseAny(objectId));
  1250. }
  1251. }
  1252. pw.writePack(pm, NullProgressMonitor.INSTANCE, packOut);
  1253. if (msgOut != NullOutputStream.INSTANCE) {
  1254. String msg = pw.getStatistics().getMessage() + '\n';
  1255. msgOut.write(Constants.encode(msg));
  1256. msgOut.flush();
  1257. }
  1258. } finally {
  1259. statistics = pw.getStatistics();
  1260. if (statistics != null)
  1261. logger.onPackStatistics(statistics);
  1262. pw.release();
  1263. }
  1264. if (sideband)
  1265. pckOut.end();
  1266. }
  1267. private void findSymrefs(
  1268. final RefAdvertiser adv, final Map<String, Ref> refs) {
  1269. Ref head = refs.get(Constants.HEAD);
  1270. if (head != null && head.isSymbolic()) {
  1271. adv.addSymref(Constants.HEAD, head.getLeaf().getName());
  1272. }
  1273. }
  1274. }