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