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