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