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