View Javadoc
1   /*
2    * Copyright (C) 2008, 2022 Google Inc. and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.transport;
12  
13  import static java.util.Collections.emptyList;
14  import static java.util.Collections.unmodifiableMap;
15  import static java.util.Objects.requireNonNull;
16  import static org.eclipse.jgit.lib.Constants.R_TAGS;
17  import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REF_IN_WANT;
18  import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SERVER_OPTION;
19  import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_FETCH;
20  import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_LS_REFS;
21  import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_OBJECT_INFO;
22  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
23  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_REACHABLE_SHA1_IN_WANT;
24  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT;
25  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_DEEPEN_RELATIVE;
26  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_FILTER;
27  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_INCLUDE_TAG;
28  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK;
29  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK_DETAILED;
30  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_DONE;
31  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_PROGRESS;
32  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_OFS_DELTA;
33  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SHALLOW;
34  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDEBAND_ALL;
35  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
36  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
37  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SESSION_ID;
38  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
39  import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WAIT_FOR_DONE;
40  import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_ACK;
41  import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DONE;
42  import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_ERR;
43  import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_HAVE;
44  import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_SHALLOW;
45  import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_UNSHALLOW;
46  import static org.eclipse.jgit.transport.GitProtocolConstants.VERSION_2_REQUEST;
47  import static org.eclipse.jgit.util.RefMap.toRefMap;
48  
49  import java.io.ByteArrayOutputStream;
50  import java.io.Closeable;
51  import java.io.EOFException;
52  import java.io.IOException;
53  import java.io.InputStream;
54  import java.io.OutputStream;
55  import java.io.UncheckedIOException;
56  import java.text.MessageFormat;
57  import java.time.Duration;
58  import java.time.Instant;
59  import java.util.ArrayList;
60  import java.util.Collection;
61  import java.util.Collections;
62  import java.util.HashSet;
63  import java.util.List;
64  import java.util.Map;
65  import java.util.Objects;
66  import java.util.Optional;
67  import java.util.Set;
68  import java.util.TreeMap;
69  import java.util.function.Function;
70  import java.util.function.Predicate;
71  import java.util.stream.Collectors;
72  import java.util.stream.Stream;
73  
74  import org.eclipse.jgit.annotations.NonNull;
75  import org.eclipse.jgit.annotations.Nullable;
76  import org.eclipse.jgit.errors.CorruptObjectException;
77  import org.eclipse.jgit.errors.IncorrectObjectTypeException;
78  import org.eclipse.jgit.errors.MissingObjectException;
79  import org.eclipse.jgit.errors.PackProtocolException;
80  import org.eclipse.jgit.internal.JGitText;
81  import org.eclipse.jgit.internal.storage.pack.CachedPackUriProvider;
82  import org.eclipse.jgit.internal.storage.pack.PackWriter;
83  import org.eclipse.jgit.internal.transport.parser.FirstWant;
84  import org.eclipse.jgit.lib.Constants;
85  import org.eclipse.jgit.lib.NullProgressMonitor;
86  import org.eclipse.jgit.lib.ObjectId;
87  import org.eclipse.jgit.lib.ObjectReader;
88  import org.eclipse.jgit.lib.ProgressMonitor;
89  import org.eclipse.jgit.lib.Ref;
90  import org.eclipse.jgit.lib.RefDatabase;
91  import org.eclipse.jgit.lib.Repository;
92  import org.eclipse.jgit.revwalk.AsyncRevObjectQueue;
93  import org.eclipse.jgit.revwalk.DepthWalk;
94  import org.eclipse.jgit.revwalk.ObjectReachabilityChecker;
95  import org.eclipse.jgit.revwalk.ObjectWalk;
96  import org.eclipse.jgit.revwalk.ReachabilityChecker;
97  import org.eclipse.jgit.revwalk.RevCommit;
98  import org.eclipse.jgit.revwalk.RevFlag;
99  import org.eclipse.jgit.revwalk.RevFlagSet;
100 import org.eclipse.jgit.revwalk.RevObject;
101 import org.eclipse.jgit.revwalk.RevTag;
102 import org.eclipse.jgit.revwalk.RevWalk;
103 import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
104 import org.eclipse.jgit.storage.pack.PackConfig;
105 import org.eclipse.jgit.storage.pack.PackStatistics;
106 import org.eclipse.jgit.transport.GitProtocolConstants.MultiAck;
107 import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
108 import org.eclipse.jgit.transport.TransferConfig.ProtocolVersion;
109 import org.eclipse.jgit.util.io.InterruptTimer;
110 import org.eclipse.jgit.util.io.NullOutputStream;
111 import org.eclipse.jgit.util.io.TimeoutInputStream;
112 import org.eclipse.jgit.util.io.TimeoutOutputStream;
113 
114 /**
115  * Implements the server side of a fetch connection, transmitting objects.
116  */
117 public class UploadPack implements Closeable {
118 	/** Policy the server uses to validate client requests */
119 	public enum RequestPolicy {
120 		/** Client may only ask for objects the server advertised a reference for. */
121 		ADVERTISED,
122 
123 		/**
124 		 * Client may ask for any commit reachable from a reference advertised by
125 		 * the server.
126 		 */
127 		REACHABLE_COMMIT,
128 
129 		/**
130 		 * Client may ask for objects that are the tip of any reference, even if not
131 		 * advertised.
132 		 * <p>
133 		 * This may happen, for example, when a custom {@link RefFilter} is set.
134 		 *
135 		 * @since 3.1
136 		 */
137 		TIP,
138 
139 		/**
140 		 * Client may ask for any commit reachable from any reference, even if that
141 		 * reference wasn't advertised.
142 		 *
143 		 * @since 3.1
144 		 */
145 		REACHABLE_COMMIT_TIP,
146 
147 		/** Client may ask for any SHA-1 in the repository. */
148 		ANY;
149 	}
150 
151 	/**
152 	 * Validator for client requests.
153 	 *
154 	 * @since 3.1
155 	 */
156 	public interface RequestValidator {
157 		/**
158 		 * Check a list of client wants against the request policy.
159 		 *
160 		 * @param up
161 		 *            {@link UploadPack} instance.
162 		 * @param wants
163 		 *            objects the client requested that were not advertised.
164 		 *
165 		 * @throws PackProtocolException
166 		 *            if one or more wants is not valid.
167 		 * @throws IOException
168 		 *            if a low-level exception occurred.
169 		 * @since 3.1
170 		 */
171 		void checkWants(UploadPack up, List<ObjectId> wants)
172 				throws PackProtocolException, IOException;
173 	}
174 
175 	/**
176 	 * Data in the first line of a want-list, the line itself plus options.
177 	 *
178 	 * @deprecated Use {@link FirstWant} instead
179 	 */
180 	@Deprecated
181 	public static class FirstLine {
182 
183 		private final FirstWant firstWant;
184 
185 		/**
186 		 * @param line
187 		 *            line from the client.
188 		 */
189 		public FirstLine(String line) {
190 			try {
191 				firstWant = FirstWant.fromLine(line);
192 			} catch (PackProtocolException e) {
193 				throw new UncheckedIOException(e);
194 			}
195 		}
196 
197 		/** @return non-capabilities part of the line. */
198 		public String getLine() {
199 			return firstWant.getLine();
200 		}
201 
202 		/** @return capabilities parsed from the line. */
203 		public Set<String> getOptions() {
204 			if (firstWant.getAgent() != null) {
205 				Set<String> caps = new HashSet<>(firstWant.getCapabilities());
206 				caps.add(OPTION_AGENT + '=' + firstWant.getAgent());
207 				return caps;
208 			}
209 			return firstWant.getCapabilities();
210 		}
211 	}
212 
213 	/*
214 	 * {@link java.util.function.Consumer} doesn't allow throwing checked
215 	 * exceptions. Define our own to propagate IOExceptions.
216 	 */
217 	@FunctionalInterface
218 	private static interface IOConsumer<R> {
219 		void accept(R t) throws IOException;
220 	}
221 
222 	/** Database we read the objects from. */
223 	private final Repository db;
224 
225 	/** Revision traversal support over {@link #db}. */
226 	private final RevWalk walk;
227 
228 	/** Configuration to pass into the PackWriter. */
229 	private PackConfig packConfig;
230 
231 	/** Configuration for various transfer options. */
232 	private TransferConfig transferConfig;
233 
234 	/** Timeout in seconds to wait for client interaction. */
235 	private int timeout;
236 
237 	/**
238 	 * Is the client connection a bi-directional socket or pipe?
239 	 * <p>
240 	 * If true, this class assumes it can perform multiple read and write cycles
241 	 * with the client over the input and output streams. This matches the
242 	 * functionality available with a standard TCP/IP connection, or a local
243 	 * operating system or in-memory pipe.
244 	 * <p>
245 	 * If false, this class runs in a read everything then output results mode,
246 	 * making it suitable for single round-trip systems RPCs such as HTTP.
247 	 */
248 	private boolean biDirectionalPipe = true;
249 
250 	/** Timer to manage {@link #timeout}. */
251 	private InterruptTimer timer;
252 
253 	/**
254 	 * Whether the client requested to use protocol V2 through a side
255 	 * channel (such as the Git-Protocol HTTP header).
256 	 */
257 	private boolean clientRequestedV2;
258 
259 	private InputStream rawIn;
260 
261 	private ResponseBufferedOutputStream rawOut;
262 
263 	private PacketLineIn pckIn;
264 
265 	private OutputStream msgOut = NullOutputStream.INSTANCE;
266 
267 	private ErrorWriter errOut = new PackProtocolErrorWriter();
268 
269 	/**
270 	 * Refs eligible for advertising to the client, set using
271 	 * {@link #setAdvertisedRefs}.
272 	 */
273 	private Map<String, Ref> refs;
274 
275 	/** Hook used while processing Git protocol v2 requests. */
276 	private ProtocolV2Hook protocolV2Hook = ProtocolV2Hook.DEFAULT;
277 
278 	/** Hook used while advertising the refs to the client. */
279 	private AdvertiseRefsHook advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
280 
281 	/** Whether the {@link #advertiseRefsHook} has been invoked. */
282 	private boolean advertiseRefsHookCalled;
283 
284 	/** Filter used while advertising the refs to the client. */
285 	private RefFilter refFilter = RefFilter.DEFAULT;
286 
287 	/** Hook handling the various upload phases. */
288 	private PreUploadHook preUploadHook = PreUploadHook.NULL;
289 
290 	/** Hook for taking post upload actions. */
291 	private PostUploadHook postUploadHook = PostUploadHook.NULL;
292 
293 	/** Caller user agent */
294 	String userAgent;
295 
296 	/** Raw ObjectIds the client has asked for, before validating them. */
297 	private Set<ObjectId> wantIds = new HashSet<>();
298 
299 	/** Objects the client wants to obtain. */
300 	private final Set<RevObject> wantAll = new HashSet<>();
301 
302 	/** Objects on both sides, these don't have to be sent. */
303 	private final Set<RevObject> commonBase = new HashSet<>();
304 
305 	/** Commit time of the oldest common commit, in seconds. */
306 	private int oldestTime;
307 
308 	/** null if {@link #commonBase} should be examined again. */
309 	private Boolean okToGiveUp;
310 
311 	private boolean sentReady;
312 
313 	/** Objects we sent in our advertisement list. */
314 	private Set<ObjectId> advertised;
315 
316 	/** Marked on objects the client has asked us to give them. */
317 	private final RevFlag WANT;
318 
319 	/** Marked on objects both we and the client have. */
320 	private final RevFlag PEER_HAS;
321 
322 	/** Marked on objects in {@link #commonBase}. */
323 	private final RevFlag COMMON;
324 
325 	/** Objects where we found a path from the want list to a common base. */
326 	private final RevFlag SATISFIED;
327 
328 	private final RevFlagSet SAVE;
329 
330 	private RequestValidator requestValidator = new AdvertisedRequestValidator();
331 
332 	private MultiAck multiAck = MultiAck.OFF;
333 
334 	private boolean noDone;
335 
336 	private PackStatistics statistics;
337 
338 	/**
339 	 * Request this instance is handling.
340 	 *
341 	 * We need to keep a reference to it for {@link PreUploadHook pre upload
342 	 * hooks}. They receive a reference this instance and invoke methods like
343 	 * getDepth() to get information about the request.
344 	 */
345 	private FetchRequest currentRequest;
346 
347 	private CachedPackUriProvider cachedPackUriProvider;
348 
349 	/**
350 	 * Create a new pack upload for an open repository.
351 	 *
352 	 * @param copyFrom
353 	 *            the source repository.
354 	 */
355 	public UploadPack(Repository copyFrom) {
356 		db = copyFrom;
357 		walk = new RevWalk(db);
358 		walk.setRetainBody(false);
359 
360 		WANT = walk.newFlag("WANT"); //$NON-NLS-1$
361 		PEER_HAS = walk.newFlag("PEER_HAS"); //$NON-NLS-1$
362 		COMMON = walk.newFlag("COMMON"); //$NON-NLS-1$
363 		SATISFIED = walk.newFlag("SATISFIED"); //$NON-NLS-1$
364 		walk.carry(PEER_HAS);
365 
366 		SAVE = new RevFlagSet();
367 		SAVE.add(WANT);
368 		SAVE.add(PEER_HAS);
369 		SAVE.add(COMMON);
370 		SAVE.add(SATISFIED);
371 
372 		setTransferConfig(null);
373 	}
374 
375 	/**
376 	 * Get the repository this upload is reading from.
377 	 *
378 	 * @return the repository this upload is reading from.
379 	 */
380 	public final Repository getRepository() {
381 		return db;
382 	}
383 
384 	/**
385 	 * Get the RevWalk instance used by this connection.
386 	 *
387 	 * @return the RevWalk instance used by this connection.
388 	 */
389 	public final RevWalk getRevWalk() {
390 		return walk;
391 	}
392 
393 	/**
394 	 * Get refs which were advertised to the client.
395 	 *
396 	 * @return all refs which were advertised to the client. Only valid during
397 	 *         the negotiation phase. Will return {@code null} if
398 	 *         {@link #setAdvertisedRefs(Map)} has not been called yet or if
399 	 *         {@code #sendPack()} has been called.
400 	 */
401 	public final Map<String, Ref> getAdvertisedRefs() {
402 		return refs;
403 	}
404 
405 	/**
406 	 * Set the refs advertised by this UploadPack.
407 	 * <p>
408 	 * Intended to be called from a
409 	 * {@link org.eclipse.jgit.transport.PreUploadHook}.
410 	 *
411 	 * @param allRefs
412 	 *            explicit set of references to claim as advertised by this
413 	 *            UploadPack instance. This overrides any references that may
414 	 *            exist in the source repository. The map is passed to the
415 	 *            configured {@link #getRefFilter()}. If null, assumes all refs
416 	 *            were advertised.
417 	 */
418 	public void setAdvertisedRefs(@Nullable Map<String, Ref> allRefs) {
419 		if (allRefs != null) {
420 			refs = allRefs;
421 		} else {
422 			refs = getAllRefs();
423 		}
424 		if (refFilter == RefFilter.DEFAULT) {
425 			refs = transferConfig.getRefFilter().filter(refs);
426 		} else {
427 			refs = refFilter.filter(refs);
428 		}
429 	}
430 
431 	/**
432 	 * Get timeout (in seconds) before aborting an IO operation.
433 	 *
434 	 * @return timeout (in seconds) before aborting an IO operation.
435 	 */
436 	public int getTimeout() {
437 		return timeout;
438 	}
439 
440 	/**
441 	 * Set the timeout before willing to abort an IO call.
442 	 *
443 	 * @param seconds
444 	 *            number of seconds to wait (with no data transfer occurring)
445 	 *            before aborting an IO read or write operation with the
446 	 *            connected client.
447 	 */
448 	public void setTimeout(int seconds) {
449 		timeout = seconds;
450 	}
451 
452 	/**
453 	 * Whether this class expects a bi-directional pipe opened between the
454 	 * client and itself.
455 	 *
456 	 * @return true if this class expects a bi-directional pipe opened between
457 	 *         the client and itself. The default is true.
458 	 */
459 	public boolean isBiDirectionalPipe() {
460 		return biDirectionalPipe;
461 	}
462 
463 	/**
464 	 * Set whether this class will assume the socket is a fully bidirectional
465 	 * pipe between the two peers
466 	 *
467 	 * @param twoWay
468 	 *            if true, this class will assume the socket is a fully
469 	 *            bidirectional pipe between the two peers and takes advantage
470 	 *            of that by first transmitting the known refs, then waiting to
471 	 *            read commands. If false, this class assumes it must read the
472 	 *            commands before writing output and does not perform the
473 	 *            initial advertising.
474 	 */
475 	public void setBiDirectionalPipe(boolean twoWay) {
476 		biDirectionalPipe = twoWay;
477 	}
478 
479 	/**
480 	 * Get policy used by the service to validate client requests
481 	 *
482 	 * @return policy used by the service to validate client requests, or null
483 	 *         for a custom request validator.
484 	 */
485 	public RequestPolicy getRequestPolicy() {
486 		if (requestValidator instanceof AdvertisedRequestValidator)
487 			return RequestPolicy.ADVERTISED;
488 		if (requestValidator instanceof ReachableCommitRequestValidator)
489 			return RequestPolicy.REACHABLE_COMMIT;
490 		if (requestValidator instanceof TipRequestValidator)
491 			return RequestPolicy.TIP;
492 		if (requestValidator instanceof ReachableCommitTipRequestValidator)
493 			return RequestPolicy.REACHABLE_COMMIT_TIP;
494 		if (requestValidator instanceof AnyRequestValidator)
495 			return RequestPolicy.ANY;
496 		return null;
497 	}
498 
499 	/**
500 	 * Set the policy used to enforce validation of a client's want list.
501 	 *
502 	 * @param policy
503 	 *            the policy used to enforce validation of a client's want list.
504 	 *            By default the policy is
505 	 *            {@link org.eclipse.jgit.transport.UploadPack.RequestPolicy#ADVERTISED},
506 	 *            which is the Git default requiring clients to only ask for an
507 	 *            object that a reference directly points to. This may be
508 	 *            relaxed to
509 	 *            {@link org.eclipse.jgit.transport.UploadPack.RequestPolicy#REACHABLE_COMMIT}
510 	 *            or
511 	 *            {@link org.eclipse.jgit.transport.UploadPack.RequestPolicy#REACHABLE_COMMIT_TIP}
512 	 *            when callers have {@link #setBiDirectionalPipe(boolean)} set
513 	 *            to false. Overrides any policy specified in a
514 	 *            {@link org.eclipse.jgit.transport.TransferConfig}.
515 	 */
516 	public void setRequestPolicy(RequestPolicy policy) {
517 		switch (policy) {
518 			case ADVERTISED:
519 			default:
520 				requestValidator = new AdvertisedRequestValidator();
521 				break;
522 			case REACHABLE_COMMIT:
523 				requestValidator = new ReachableCommitRequestValidator();
524 				break;
525 			case TIP:
526 				requestValidator = new TipRequestValidator();
527 				break;
528 			case REACHABLE_COMMIT_TIP:
529 				requestValidator = new ReachableCommitTipRequestValidator();
530 				break;
531 			case ANY:
532 				requestValidator = new AnyRequestValidator();
533 				break;
534 		}
535 	}
536 
537 	/**
538 	 * Set custom validator for client want list.
539 	 *
540 	 * @param validator
541 	 *            custom validator for client want list.
542 	 * @since 3.1
543 	 */
544 	public void setRequestValidator(@Nullable RequestValidator validator) {
545 		requestValidator = validator != null ? validator
546 				: new AdvertisedRequestValidator();
547 	}
548 
549 	/**
550 	 * Get the hook used while advertising the refs to the client.
551 	 *
552 	 * @return the hook used while advertising the refs to the client.
553 	 */
554 	public AdvertiseRefsHook getAdvertiseRefsHook() {
555 		return advertiseRefsHook;
556 	}
557 
558 	/**
559 	 * Get the filter used while advertising the refs to the client.
560 	 *
561 	 * @return the filter used while advertising the refs to the client.
562 	 */
563 	public RefFilter getRefFilter() {
564 		return refFilter;
565 	}
566 
567 	/**
568 	 * Set the hook used while advertising the refs to the client.
569 	 * <p>
570 	 * If the {@link org.eclipse.jgit.transport.AdvertiseRefsHook} chooses to
571 	 * call {@link #setAdvertisedRefs(Map)}, only refs set by this hook
572 	 * <em>and</em> selected by the {@link org.eclipse.jgit.transport.RefFilter}
573 	 * will be shown to the client.
574 	 *
575 	 * @param advertiseRefsHook
576 	 *            the hook; may be null to show all refs.
577 	 */
578 	public void setAdvertiseRefsHook(
579 			@Nullable AdvertiseRefsHook advertiseRefsHook) {
580 		this.advertiseRefsHook = advertiseRefsHook != null ? advertiseRefsHook
581 				: AdvertiseRefsHook.DEFAULT;
582 	}
583 
584 	/**
585 	 * Set the protocol V2 hook.
586 	 *
587 	 * @param hook
588 	 *            the hook; if null no special actions are taken.
589 	 * @since 5.1
590 	 */
591 	public void setProtocolV2Hook(@Nullable ProtocolV2Hook hook) {
592 		this.protocolV2Hook = hook != null ? hook : ProtocolV2Hook.DEFAULT;
593 	}
594 
595 	/**
596 	 * Get the currently installed protocol v2 hook.
597 	 *
598 	 * @return the hook or a default implementation if none installed.
599 	 *
600 	 * @since 5.5
601 	 */
602 	public ProtocolV2Hook getProtocolV2Hook() {
603 		return this.protocolV2Hook != null ? this.protocolV2Hook
604 				: ProtocolV2Hook.DEFAULT;
605 	}
606 
607 	/**
608 	 * Set the filter used while advertising the refs to the client.
609 	 * <p>
610 	 * Only refs allowed by this filter will be sent to the client. The filter
611 	 * is run against the refs specified by the
612 	 * {@link org.eclipse.jgit.transport.AdvertiseRefsHook} (if applicable). If
613 	 * null or not set, uses the filter implied by the
614 	 * {@link org.eclipse.jgit.transport.TransferConfig}.
615 	 *
616 	 * @param refFilter
617 	 *            the filter; may be null to show all refs.
618 	 */
619 	public void setRefFilter(@Nullable RefFilter refFilter) {
620 		this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
621 	}
622 
623 	/**
624 	 * Get the configured pre upload hook.
625 	 *
626 	 * @return the configured pre upload hook.
627 	 */
628 	public PreUploadHook getPreUploadHook() {
629 		return preUploadHook;
630 	}
631 
632 	/**
633 	 * Set the hook that controls how this instance will behave.
634 	 *
635 	 * @param hook
636 	 *            the hook; if null no special actions are taken.
637 	 */
638 	public void setPreUploadHook(@Nullable PreUploadHook hook) {
639 		preUploadHook = hook != null ? hook : PreUploadHook.NULL;
640 	}
641 
642 	/**
643 	 * Get the configured post upload hook.
644 	 *
645 	 * @return the configured post upload hook.
646 	 * @since 4.1
647 	 */
648 	public PostUploadHook getPostUploadHook() {
649 		return postUploadHook;
650 	}
651 
652 	/**
653 	 * Set the hook for post upload actions (logging, repacking).
654 	 *
655 	 * @param hook
656 	 *            the hook; if null no special actions are taken.
657 	 * @since 4.1
658 	 */
659 	public void setPostUploadHook(@Nullable PostUploadHook hook) {
660 		postUploadHook = hook != null ? hook : PostUploadHook.NULL;
661 	}
662 
663 	/**
664 	 * Set the configuration used by the pack generator.
665 	 *
666 	 * @param pc
667 	 *            configuration controlling packing parameters. If null the
668 	 *            source repository's settings will be used.
669 	 */
670 	public void setPackConfig(@Nullable PackConfig pc) {
671 		this.packConfig = pc;
672 	}
673 
674 	/**
675 	 * Set configuration controlling transfer options.
676 	 *
677 	 * @param tc
678 	 *            configuration controlling transfer options. If null the source
679 	 *            repository's settings will be used.
680 	 * @since 3.1
681 	 */
682 	public void setTransferConfig(@Nullable TransferConfig tc) {
683 		this.transferConfig = tc != null ? tc : new TransferConfig(db);
684 		if (transferConfig.isAllowTipSha1InWant()) {
685 			setRequestPolicy(transferConfig.isAllowReachableSha1InWant()
686 				? RequestPolicy.REACHABLE_COMMIT_TIP : RequestPolicy.TIP);
687 		} else {
688 			setRequestPolicy(transferConfig.isAllowReachableSha1InWant()
689 				? RequestPolicy.REACHABLE_COMMIT : RequestPolicy.ADVERTISED);
690 		}
691 	}
692 
693 	/**
694 	 * Check whether the client expects a side-band stream.
695 	 *
696 	 * @return true if the client has advertised a side-band capability, false
697 	 *     otherwise.
698 	 * @throws org.eclipse.jgit.transport.RequestNotYetReadException
699 	 *             if the client's request has not yet been read from the wire, so
700 	 *             we do not know if they expect side-band. Note that the client
701 	 *             may have already written the request, it just has not been
702 	 *             read.
703 	 */
704 	public boolean isSideBand() throws RequestNotYetReadException {
705 		if (currentRequest == null) {
706 			throw new RequestNotYetReadException();
707 		}
708 		Set<String> caps = currentRequest.getClientCapabilities();
709 		return caps.contains(OPTION_SIDE_BAND)
710 				|| caps.contains(OPTION_SIDE_BAND_64K);
711 	}
712 
713 	/**
714 	 * Set the Extra Parameters provided by the client.
715 	 *
716 	 * <p>These are parameters passed by the client through a side channel
717 	 * such as the Git-Protocol HTTP header, to allow a client to request
718 	 * a newer response format while remaining compatible with older servers
719 	 * that do not understand different request formats.
720 	 *
721 	 * @param params
722 	 *            parameters supplied by the client, split at colons or NUL
723 	 *            bytes.
724 	 * @since 5.0
725 	 */
726 	public void setExtraParameters(Collection<String> params) {
727 		this.clientRequestedV2 = params.contains(VERSION_2_REQUEST);
728 	}
729 
730 	/**
731 	 * @param p provider of URIs corresponding to cached packs (to support
732 	 *     the packfile URIs feature)
733 	 * @since 5.5
734 	 */
735 	public void setCachedPackUriProvider(@Nullable CachedPackUriProvider p) {
736 		cachedPackUriProvider = p;
737 	}
738 
739 	private boolean useProtocolV2() {
740 		return (transferConfig.protocolVersion == null
741 			|| ProtocolVersion.V2.equals(transferConfig.protocolVersion))
742 				&& clientRequestedV2;
743 	}
744 
745 	@Override
746 	public void close() {
747 		if (timer != null) {
748 			try {
749 				timer.terminate();
750 			} finally {
751 				timer = null;
752 			}
753 		}
754 	}
755 
756 	/**
757 	 * Execute the upload task on the socket.
758 	 *
759 	 * <p>
760 	 * Same as {@link #uploadWithExceptionPropagation} except that the thrown
761 	 * exceptions are handled in the method, and the error messages are sent to
762 	 * the clients.
763 	 *
764 	 * <p>
765 	 * Call this method if the caller does not have an error handling mechanism.
766 	 * Call {@link #uploadWithExceptionPropagation} if the caller wants to have
767 	 * its own error handling mechanism.
768 	 *
769 	 * @param input
770 	 * @param output
771 	 * @param messages
772 	 * @throws java.io.IOException
773 	 */
774 	public void upload(InputStream input, OutputStream output,
775 			@Nullable OutputStream messages) throws IOException {
776 		try {
777 			uploadWithExceptionPropagation(input, output, messages);
778 		} catch (ServiceMayNotContinueException err) {
779 			if (!err.isOutput() && err.getMessage() != null) {
780 				try {
781 					errOut.writeError(err.getMessage());
782 				} catch (IOException e) {
783 					err.addSuppressed(e);
784 					throw err;
785 				}
786 				err.setOutput();
787 			}
788 			throw err;
789 		} catch (IOException | RuntimeException | Error err) {
790 			if (rawOut != null) {
791 				String msg = err instanceof PackProtocolException
792 						? err.getMessage()
793 						: JGitText.get().internalServerError;
794 				try {
795 					errOut.writeError(msg);
796 				} catch (IOException e) {
797 					err.addSuppressed(e);
798 					throw err;
799 				}
800 				throw new UploadPackInternalServerErrorException(err);
801 			}
802 			throw err;
803 		} finally {
804 			close();
805 		}
806 	}
807 
808 	/**
809 	 * Execute the upload task on the socket.
810 	 *
811 	 * <p>
812 	 * If the client passed extra parameters (e.g., "version=2") through a side
813 	 * channel, the caller must call setExtraParameters first to supply them.
814 	 * Callers of this method should call {@link #close()} to terminate the
815 	 * internal interrupt timer thread. If the caller fails to terminate the
816 	 * thread, it will (eventually) terminate itself when the InterruptTimer
817 	 * instance is garbage collected.
818 	 *
819 	 * @param input
820 	 *            raw input to read client commands from. Caller must ensure the
821 	 *            input is buffered, otherwise read performance may suffer.
822 	 * @param output
823 	 *            response back to the Git network client, to write the pack
824 	 *            data onto. Caller must ensure the output is buffered,
825 	 *            otherwise write performance may suffer.
826 	 * @param messages
827 	 *            secondary "notice" channel to send additional messages out
828 	 *            through. When run over SSH this should be tied back to the
829 	 *            standard error channel of the command execution. For most
830 	 *            other network connections this should be null.
831 	 * @throws ServiceMayNotContinueException
832 	 *             thrown if one of the hooks throws this.
833 	 * @throws IOException
834 	 *             thrown if the server or the client I/O fails, or there's an
835 	 *             internal server error.
836 	 * @since 5.6
837 	 */
838 	public void uploadWithExceptionPropagation(InputStream input,
839 			OutputStream output, @Nullable OutputStream messages)
840 			throws ServiceMayNotContinueException, IOException {
841 		try {
842 			rawIn = input;
843 			if (messages != null) {
844 				msgOut = messages;
845 			}
846 
847 			if (timeout > 0) {
848 				final Thread caller = Thread.currentThread();
849 				timer = new InterruptTimer(caller.getName() + "-Timer"); //$NON-NLS-1$
850 				TimeoutInputStream i = new TimeoutInputStream(rawIn, timer);
851 				@SuppressWarnings("resource")
852 				TimeoutOutputStream o = new TimeoutOutputStream(output, timer);
853 				i.setTimeout(timeout * 1000);
854 				o.setTimeout(timeout * 1000);
855 				rawIn = i;
856 				output = o;
857 			}
858 
859 			rawOut = new ResponseBufferedOutputStream(output);
860 			if (biDirectionalPipe) {
861 				rawOut.stopBuffering();
862 			}
863 
864 			pckIn = new PacketLineIn(rawIn);
865 			PacketLineOut pckOut = new PacketLineOut(rawOut);
866 			if (useProtocolV2()) {
867 				serviceV2(pckOut);
868 			} else {
869 				service(pckOut);
870 			}
871 		} finally {
872 			msgOut = NullOutputStream.INSTANCE;
873 			walk.close();
874 		}
875 	}
876 
877 	/**
878 	 * Get the PackWriter's statistics if a pack was sent to the client.
879 	 *
880 	 * @return statistics about pack output, if a pack was sent. Null if no pack
881 	 *         was sent, such as during the negotiation phase of a smart HTTP
882 	 *         connection, or if the client was already up-to-date.
883 	 * @since 4.1
884 	 */
885 	public PackStatistics getStatistics() {
886 		return statistics;
887 	}
888 
889 	/**
890 	 * Extract the full list of refs from the ref-db.
891 	 *
892 	 * @return Map of all refname/ref
893 	 */
894 	private Map<String, Ref> getAllRefs() {
895 		try {
896 			return db.getRefDatabase().getRefs().stream().collect(
897 					Collectors.toMap(Ref::getName, Function.identity()));
898 		} catch (IOException e) {
899 			throw new UncheckedIOException(e);
900 		}
901 	}
902 
903 	private Map<String, Ref> getAdvertisedOrDefaultRefs() throws IOException {
904 		if (refs != null) {
905 			return refs;
906 		}
907 
908 		if (!advertiseRefsHookCalled) {
909 			advertiseRefsHook.advertiseRefs(this);
910 			advertiseRefsHookCalled = true;
911 		}
912 		if (refs == null) {
913 			// Fall back to all refs.
914 			setAdvertisedRefs(
915 					db.getRefDatabase().getRefs().stream()
916 							.collect(toRefMap((a, b) -> b)));
917 		}
918 		return refs;
919 	}
920 
921 	private Map<String, Ref> getFilteredRefs(Collection<String> refPrefixes)
922 					throws IOException {
923 		if (refPrefixes.isEmpty()) {
924 			return getAdvertisedOrDefaultRefs();
925 		}
926 		if (refs == null && !advertiseRefsHookCalled) {
927 			advertiseRefsHook.advertiseRefs(this);
928 			advertiseRefsHookCalled = true;
929 		}
930 		if (refs == null) {
931 			// Fast path: the advertised refs hook did not set advertised refs.
932 			String[] prefixes = refPrefixes.toArray(new String[0]);
933 			Map<String, Ref> rs =
934 					db.getRefDatabase().getRefsByPrefix(prefixes).stream()
935 							.collect(toRefMap((a, b) -> b));
936 			if (refFilter != RefFilter.DEFAULT) {
937 				return refFilter.filter(rs);
938 			}
939 			return transferConfig.getRefFilter().filter(rs);
940 		}
941 
942 		// Slow path: filter the refs provided by the advertised refs hook.
943 		// refFilter has already been applied to refs.
944 		return refs.values().stream()
945 				.filter(ref -> refPrefixes.stream()
946 						.anyMatch(ref.getName()::startsWith))
947 				.collect(toRefMap((a, b) -> b));
948 	}
949 
950 	/**
951 	 * Returns the specified references.
952 	 * <p>
953 	 * This produces an immutable map containing whatever subset of the
954 	 * refs named by the caller are present in the supplied {@code refs}
955 	 * map.
956 	 *
957 	 * @param refs
958 	 *            Map to search for refs to return.
959 	 * @param names
960 	 *            which refs to search for in {@code refs}.
961 	 * @return the requested Refs, omitting any that are null or missing.
962 	 */
963 	@NonNull
964 	private static Map<String, Ref> mapRefs(
965 			Map<String, Ref> refs, List<String> names) {
966 		return unmodifiableMap(
967 				names.stream()
968 					.map(refs::get)
969 					.filter(Objects::nonNull)
970 						.collect(toRefMap((a, b) -> b)));
971 	}
972 
973 	/**
974 	 * Read refs on behalf of the client.
975 	 * <p>
976 	 * This checks whether the refs are present in the ref advertisement
977 	 * since otherwise the client might not be supposed to be able to
978 	 * read them.
979 	 *
980 	 * @param names
981 	 *            unabbreviated names of references.
982 	 * @return the requested Refs, omitting any that are not visible or
983 	 *         do not exist.
984 	 * @throws java.io.IOException
985 	 *            on failure to read a ref or check it for visibility.
986 	 */
987 	@NonNull
988 	private Map<String, Ref> exactRefs(List<String> names) throws IOException {
989 		if (refs != null) {
990 			return mapRefs(refs, names);
991 		}
992 		if (!advertiseRefsHookCalled) {
993 			advertiseRefsHook.advertiseRefs(this);
994 			advertiseRefsHookCalled = true;
995 		}
996 		if (refs == null &&
997 				refFilter == RefFilter.DEFAULT &&
998 				transferConfig.hasDefaultRefFilter()) {
999 			// Fast path: no ref filtering is needed.
1000 			String[] ns = names.toArray(new String[0]);
1001 			return unmodifiableMap(db.getRefDatabase().exactRef(ns));
1002 		}
1003 		return mapRefs(getAdvertisedOrDefaultRefs(), names);
1004 	}
1005 
1006 	/**
1007 	 * Find a ref in the usual search path on behalf of the client.
1008 	 * <p>
1009 	 * This checks that the ref is present in the ref advertisement since
1010 	 * otherwise the client might not be supposed to be able to read it.
1011 	 *
1012 	 * @param name
1013 	 *            short name of the ref to find, e.g. "master" to find
1014 	 *            "refs/heads/master".
1015 	 * @return the requested Ref, or {@code null} if it is not visible or
1016 	 *         does not exist.
1017 	 * @throws java.io.IOException
1018 	 *            on failure to read the ref or check it for visibility.
1019 	 */
1020 	@Nullable
1021 	private Ref findRef(String name) throws IOException {
1022 		if (refs != null) {
1023 			return RefDatabase.findRef(refs, name);
1024 		}
1025 		if (!advertiseRefsHookCalled) {
1026 			advertiseRefsHook.advertiseRefs(this);
1027 			advertiseRefsHookCalled = true;
1028 		}
1029 		if (refs == null &&
1030 				refFilter == RefFilter.DEFAULT &&
1031 				transferConfig.hasDefaultRefFilter()) {
1032 			// Fast path: no ref filtering is needed.
1033 			return db.getRefDatabase().findRef(name);
1034 		}
1035 		return RefDatabase.findRef(getAdvertisedOrDefaultRefs(), name);
1036 	}
1037 
1038 	private void service(PacketLineOut pckOut) throws IOException {
1039 		boolean sendPack = false;
1040 		// If it's a non-bidi request, we need to read the entire request before
1041 		// writing a response. Buffer the response until then.
1042 		PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator();
1043 		List<ObjectId> unshallowCommits = new ArrayList<>();
1044 		List<ObjectId> deepenNots = emptyList();
1045 		FetchRequest req;
1046 		try {
1047 			if (biDirectionalPipe)
1048 				sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
1049 			else if (requestValidator instanceof AnyRequestValidator)
1050 				advertised = Collections.emptySet();
1051 			else
1052 				advertised = refIdSet(getAdvertisedOrDefaultRefs().values());
1053 
1054 			Instant negotiateStart = Instant.now();
1055 			accumulator.advertised = advertised.size();
1056 
1057 			ProtocolV0Parser parser = new ProtocolV0Parser(transferConfig);
1058 			req = parser.recvWants(pckIn);
1059 			currentRequest = req;
1060 
1061 			wantIds = req.getWantIds();
1062 
1063 			if (req.getWantIds().isEmpty()) {
1064 				preUploadHook.onBeginNegotiateRound(this, req.getWantIds(), 0);
1065 				preUploadHook.onEndNegotiateRound(this, req.getWantIds(), 0, 0,
1066 						false);
1067 				return;
1068 			}
1069 			accumulator.wants = req.getWantIds().size();
1070 
1071 			if (req.getClientCapabilities().contains(OPTION_MULTI_ACK_DETAILED)) {
1072 				multiAck = MultiAck.DETAILED;
1073 				noDone = req.getClientCapabilities().contains(OPTION_NO_DONE);
1074 			} else if (req.getClientCapabilities().contains(OPTION_MULTI_ACK))
1075 				multiAck = MultiAck.CONTINUE;
1076 			else
1077 				multiAck = MultiAck.OFF;
1078 
1079 			if (!req.getClientShallowCommits().isEmpty()) {
1080 				verifyClientShallow(req.getClientShallowCommits());
1081 			}
1082 
1083 			deepenNots = parseDeepenNots(req.getDeepenNots());
1084 			if (req.getDepth() != 0 || req.getDeepenSince() != 0 || !req.getDeepenNots().isEmpty()) {
1085 				computeShallowsAndUnshallows(req, shallow -> {
1086 					pckOut.writeString(PACKET_SHALLOW + shallow.name() + '\n');
1087 				}, unshallow -> {
1088 					pckOut.writeString(
1089 							PACKET_UNSHALLOW + unshallow.name() + '\n');
1090 					unshallowCommits.add(unshallow);
1091 				}, deepenNots);
1092 				pckOut.end();
1093 			}
1094 
1095 			if (!req.getClientShallowCommits().isEmpty())
1096 				walk.assumeShallow(req.getClientShallowCommits());
1097 			sendPack = negotiate(req, accumulator, pckOut);
1098 			accumulator.timeNegotiating = Duration
1099 					.between(negotiateStart, Instant.now()).toMillis();
1100 
1101 			if (sendPack && !biDirectionalPipe) {
1102 				// Ensure the request was fully consumed. Any remaining input must
1103 				// be a protocol error. If we aren't at EOF the implementation is broken.
1104 				int eof = rawIn.read();
1105 				if (0 <= eof) {
1106 					sendPack = false;
1107 					throw new CorruptObjectException(MessageFormat.format(
1108 							JGitText.get().expectedEOFReceived,
1109 							"\\x" + Integer.toHexString(eof))); //$NON-NLS-1$
1110 				}
1111 			}
1112 		} finally {
1113 			if (!sendPack && !biDirectionalPipe) {
1114 				while (0 < rawIn.skip(2048) || 0 <= rawIn.read()) {
1115 					// Discard until EOF.
1116 				}
1117 			}
1118 			rawOut.stopBuffering();
1119 		}
1120 
1121 		if (sendPack) {
1122 			sendPack(accumulator, req, refs == null ? null : refs.values(),
1123 					unshallowCommits, deepenNots, pckOut);
1124 		}
1125 	}
1126 
1127 	private void lsRefsV2(PacketLineOut pckOut) throws IOException {
1128 		ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig);
1129 		LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
1130 		protocolV2Hook.onLsRefs(req);
1131 
1132 		rawOut.stopBuffering();
1133 		PacketLineOutRefAdvertiser adv = new PacketLineOutRefAdvertiser(pckOut);
1134 		adv.init(db);
1135 		adv.setUseProtocolV2(true);
1136 		if (req.getPeel()) {
1137 			adv.setDerefTags(true);
1138 		}
1139 		Map<String, Ref> refsToSend = getFilteredRefs(req.getRefPrefixes());
1140 		if (req.getSymrefs()) {
1141 			findSymrefs(adv, refsToSend);
1142 		}
1143 
1144 		adv.send(refsToSend.values());
1145 		adv.end();
1146 	}
1147 
1148 	// Resolves ref names from the request's want-ref lines to
1149 	// object ids, throwing PackProtocolException if any are missing.
1150 	private Map<String, ObjectId> wantedRefs(FetchV2Request req)
1151 			throws IOException {
1152 		Map<String, ObjectId> result = new TreeMap<>();
1153 
1154 		List<String> wanted = req.getWantedRefs();
1155 		Map<String, Ref> resolved = exactRefs(wanted);
1156 
1157 		for (String refName : wanted) {
1158 			Ref ref = resolved.get(refName);
1159 			if (ref == null) {
1160 				throw new PackProtocolException(MessageFormat
1161 						.format(JGitText.get().invalidRefName, refName));
1162 			}
1163 			ObjectId oid = ref.getObjectId();
1164 			if (oid == null) {
1165 				throw new PackProtocolException(MessageFormat
1166 						.format(JGitText.get().invalidRefName, refName));
1167 			}
1168 			result.put(refName, oid);
1169 		}
1170 		return result;
1171 	}
1172 
1173 	private void fetchV2(PacketLineOut pckOut) throws IOException {
1174 		// Depending on the requestValidator, #processHaveLines may
1175 		// require that advertised be set. Set it only in the required
1176 		// circumstances (to avoid a full ref lookup in the case that
1177 		// we don't need it).
1178 		if (requestValidator instanceof TipRequestValidator ||
1179 				requestValidator instanceof ReachableCommitTipRequestValidator ||
1180 				requestValidator instanceof AnyRequestValidator) {
1181 			advertised = Collections.emptySet();
1182 		} else {
1183 			advertised = refIdSet(getAdvertisedOrDefaultRefs().values());
1184 		}
1185 
1186 		PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator();
1187 		Instant negotiateStart = Instant.now();
1188 
1189 		ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig);
1190 		FetchV2Request req = parser.parseFetchRequest(pckIn);
1191 		currentRequest = req;
1192 		rawOut.stopBuffering();
1193 
1194 		protocolV2Hook.onFetch(req);
1195 
1196 		if (req.getSidebandAll()) {
1197 			pckOut.setUsingSideband(true);
1198 		}
1199 
1200 		// TODO(ifrade): Refactor to pass around the Request object, instead of
1201 		// copying data back to class fields
1202 		List<ObjectId> deepenNots = parseDeepenNots(req.getDeepenNots());
1203 
1204 		Map<String, ObjectId> wantedRefs = wantedRefs(req);
1205 		// TODO(ifrade): Avoid mutating the parsed request.
1206 		req.getWantIds().addAll(wantedRefs.values());
1207 		wantIds = req.getWantIds();
1208 
1209 		boolean sectionSent = false;
1210 		boolean mayHaveShallow = req.getDepth() != 0
1211 				|| req.getDeepenSince() != 0
1212 				|| !req.getDeepenNots().isEmpty();
1213 		List<ObjectId> shallowCommits = new ArrayList<>();
1214 		List<ObjectId> unshallowCommits = new ArrayList<>();
1215 
1216 		if (!req.getClientShallowCommits().isEmpty()) {
1217 			verifyClientShallow(req.getClientShallowCommits());
1218 		}
1219 		if (mayHaveShallow) {
1220 			computeShallowsAndUnshallows(req,
1221 					shallowCommit -> shallowCommits.add(shallowCommit),
1222 					unshallowCommit -> unshallowCommits.add(unshallowCommit),
1223 					deepenNots);
1224 		}
1225 		if (!req.getClientShallowCommits().isEmpty())
1226 			walk.assumeShallow(req.getClientShallowCommits());
1227 
1228 		if (req.wasDoneReceived()) {
1229 			processHaveLines(
1230 					req.getPeerHas(), ObjectId.zeroId(),
1231 					new PacketLineOut(NullOutputStream.INSTANCE, false),
1232 					accumulator, req.wasWaitForDoneReceived() ? Option.WAIT_FOR_DONE : Option.NONE);
1233 		} else {
1234 			pckOut.writeString(
1235 					GitProtocolConstants.SECTION_ACKNOWLEDGMENTS + '\n');
1236 			for (ObjectId id : req.getPeerHas()) {
1237 				if (walk.getObjectReader().has(id)) {
1238 					pckOut.writeString(PACKET_ACK + id.getName() + '\n');
1239 				}
1240 			}
1241 			processHaveLines(req.getPeerHas(), ObjectId.zeroId(),
1242 					new PacketLineOut(NullOutputStream.INSTANCE, false),
1243 					accumulator, Option.NONE);
1244 			if (!req.wasWaitForDoneReceived() && okToGiveUp()) {
1245 				pckOut.writeString("ready\n"); //$NON-NLS-1$
1246 			} else if (commonBase.isEmpty()) {
1247 				pckOut.writeString("NAK\n"); //$NON-NLS-1$
1248 			}
1249 			sectionSent = true;
1250 		}
1251 
1252 		if (req.wasDoneReceived() || (!req.wasWaitForDoneReceived() && okToGiveUp())) {
1253 			if (mayHaveShallow) {
1254 				if (sectionSent)
1255 					pckOut.writeDelim();
1256 				pckOut.writeString(
1257 						GitProtocolConstants.SECTION_SHALLOW_INFO + '\n');
1258 				for (ObjectId o : shallowCommits) {
1259 					pckOut.writeString(PACKET_SHALLOW + o.getName() + '\n');
1260 				}
1261 				for (ObjectId o : unshallowCommits) {
1262 					pckOut.writeString(PACKET_UNSHALLOW + o.getName() + '\n');
1263 				}
1264 				sectionSent = true;
1265 			}
1266 
1267 			if (!wantedRefs.isEmpty()) {
1268 				if (sectionSent) {
1269 					pckOut.writeDelim();
1270 				}
1271 				pckOut.writeString("wanted-refs\n"); //$NON-NLS-1$
1272 				for (Map.Entry<String, ObjectId> entry :
1273 						wantedRefs.entrySet()) {
1274 					pckOut.writeString(entry.getValue().getName() + ' ' +
1275 							entry.getKey() + '\n');
1276 				}
1277 				sectionSent = true;
1278 			}
1279 
1280 			if (sectionSent)
1281 				pckOut.writeDelim();
1282 			if (!pckOut.isUsingSideband()) {
1283 				// sendPack will write "packfile\n" for us if sideband-all is used.
1284 				// But sideband-all is not used, so we have to write it ourselves.
1285 				pckOut.writeString(
1286 						GitProtocolConstants.SECTION_PACKFILE + '\n');
1287 			}
1288 
1289 			accumulator.timeNegotiating = Duration
1290 					.between(negotiateStart, Instant.now()).toMillis();
1291 
1292 			sendPack(accumulator,
1293 					req,
1294 					req.getClientCapabilities().contains(OPTION_INCLUDE_TAG)
1295 						? db.getRefDatabase().getRefsByPrefix(R_TAGS)
1296 						: null,
1297 					unshallowCommits, deepenNots, pckOut);
1298 			// sendPack invokes pckOut.end() for us, so we do not
1299 			// need to invoke it here.
1300 		} else {
1301 			// Invoke pckOut.end() by ourselves.
1302 			pckOut.end();
1303 		}
1304 	}
1305 
1306 	private void objectInfo(PacketLineOut pckOut) throws IOException {
1307 		ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig);
1308 		ObjectInfoRequest req = parser.parseObjectInfoRequest(pckIn);
1309 
1310 		protocolV2Hook.onObjectInfo(req);
1311 
1312 		ObjectReader or = getRepository().newObjectReader();
1313 
1314 		// Size is the only attribute currently supported.
1315 		pckOut.writeString("size"); //$NON-NLS-1$
1316 
1317 		for (ObjectId oid : req.getObjectIDs()) {
1318 			long size;
1319 			try {
1320 				size = or.getObjectSize(oid, ObjectReader.OBJ_ANY);
1321 			} catch (MissingObjectException e) {
1322 				throw new PackProtocolException(MessageFormat
1323 						.format(JGitText.get().missingObject, oid.name()), e);
1324 			}
1325 
1326 			pckOut.writeString(oid.getName() + ' ' + size);
1327 		}
1328 
1329 		pckOut.end();
1330 	}
1331 
1332 	/*
1333 	 * Returns true if this is the last command and we should tear down the
1334 	 * connection.
1335 	 */
1336 	private boolean serveOneCommandV2(PacketLineOut pckOut) throws IOException {
1337 		String command;
1338 		try {
1339 			command = pckIn.readString();
1340 		} catch (EOFException eof) {
1341 			/* EOF when awaiting command is fine */
1342 			return true;
1343 		}
1344 		if (PacketLineIn.isEnd(command)) {
1345 			// A blank request is valid according
1346 			// to the protocol; do nothing in this
1347 			// case.
1348 			return true;
1349 		}
1350 		if (command.equals("command=" + COMMAND_LS_REFS)) { //$NON-NLS-1$
1351 			lsRefsV2(pckOut);
1352 			return false;
1353 		}
1354 		if (command.equals("command=" + COMMAND_FETCH)) { //$NON-NLS-1$
1355 			fetchV2(pckOut);
1356 			return false;
1357 		}
1358 		if (command.equals("command=" + COMMAND_OBJECT_INFO)) { //$NON-NLS-1$
1359 			objectInfo(pckOut);
1360 			return false;
1361 		}
1362 		throw new PackProtocolException(MessageFormat
1363 				.format(JGitText.get().unknownTransportCommand, command));
1364 	}
1365 
1366 	@SuppressWarnings("nls")
1367 	private List<String> getV2CapabilityAdvertisement() {
1368 		ArrayList<String> caps = new ArrayList<>();
1369 		caps.add("version 2");
1370 		caps.add(COMMAND_LS_REFS);
1371 		boolean advertiseRefInWant = transferConfig.isAllowRefInWant()
1372 				&& db.getConfig().getBoolean("uploadpack", null,
1373 						"advertiserefinwant", true);
1374 		caps.add(COMMAND_FETCH + '='
1375 				+ (transferConfig.isAllowFilter() ? OPTION_FILTER + ' ' : "")
1376 				+ (advertiseRefInWant ? CAPABILITY_REF_IN_WANT + ' ' : "")
1377 				+ (transferConfig.isAdvertiseSidebandAll()
1378 						? OPTION_SIDEBAND_ALL + ' '
1379 						: "")
1380 				+ (cachedPackUriProvider != null ? "packfile-uris " : "")
1381 				+ (transferConfig.isAdvertiseWaitForDone()
1382 						? OPTION_WAIT_FOR_DONE + ' '
1383 						: "")
1384 				+ OPTION_SHALLOW);
1385 		caps.add(CAPABILITY_SERVER_OPTION);
1386 		if (transferConfig.isAllowReceiveClientSID()) {
1387 			caps.add(OPTION_SESSION_ID);
1388 		}
1389 
1390 		return caps;
1391 	}
1392 
1393 	private void serviceV2(PacketLineOut pckOut) throws IOException {
1394 		if (biDirectionalPipe) {
1395 			// Just like in service(), the capability advertisement
1396 			// is sent only if this is a bidirectional pipe. (If
1397 			// not, the client is expected to call
1398 			// sendAdvertisedRefs() on its own.)
1399 			protocolV2Hook
1400 					.onCapabilities(CapabilitiesV2Request.builder().build());
1401 			for (String s : getV2CapabilityAdvertisement()) {
1402 				pckOut.writeString(s + '\n');
1403 			}
1404 			pckOut.end();
1405 
1406 			while (!serveOneCommandV2(pckOut)) {
1407 				// Repeat until an empty command or EOF.
1408 			}
1409 			return;
1410 		}
1411 
1412 		try {
1413 			serveOneCommandV2(pckOut);
1414 		} finally {
1415 			while (0 < rawIn.skip(2048) || 0 <= rawIn.read()) {
1416 				// Discard until EOF.
1417 			}
1418 			rawOut.stopBuffering();
1419 		}
1420 	}
1421 
1422 	private static Set<ObjectId> refIdSet(Collection<Ref> refs) {
1423 		Set<ObjectId> ids = new HashSet<>(refs.size());
1424 		for (Ref ref : refs) {
1425 			ObjectId id = ref.getObjectId();
1426 			if (id != null) {
1427 				ids.add(id);
1428 			}
1429 			id = ref.getPeeledObjectId();
1430 			if (id != null) {
1431 				ids.add(id);
1432 			}
1433 		}
1434 		return ids;
1435 	}
1436 
1437 	/*
1438 	 * Determines what object ids must be marked as shallow or unshallow for the
1439 	 * client.
1440 	 */
1441 	private void computeShallowsAndUnshallows(FetchRequest req,
1442 			IOConsumer<ObjectId> shallowFunc,
1443 			IOConsumer<ObjectId> unshallowFunc,
1444 			List<ObjectId> deepenNots)
1445 			throws IOException {
1446 		if (req.getClientCapabilities().contains(OPTION_DEEPEN_RELATIVE)) {
1447 			// TODO(jonathantanmy): Implement deepen-relative
1448 			throw new UnsupportedOperationException();
1449 		}
1450 
1451 		int walkDepth = req.getDepth() == 0 ? Integer.MAX_VALUE
1452 				: req.getDepth() - 1;
1453 		try (DepthWalk.RevWalk depthWalk = new DepthWalk.RevWalk(
1454 				walk.getObjectReader(), walkDepth)) {
1455 
1456 			depthWalk.setDeepenSince(req.getDeepenSince());
1457 
1458 			// Find all the commits which will be shallow
1459 			for (ObjectId o : req.getWantIds()) {
1460 				try {
1461 					depthWalk.markRoot(depthWalk.parseCommit(o));
1462 				} catch (IncorrectObjectTypeException notCommit) {
1463 					// Ignore non-commits in this loop.
1464 				}
1465 			}
1466 
1467 			depthWalk.setDeepenNots(deepenNots);
1468 
1469 			RevCommit o;
1470 			boolean atLeastOne = false;
1471 			while ((o = depthWalk.next()) != null) {
1472 				DepthWalk.Commit c = (DepthWalk.Commit) o;
1473 				atLeastOne = true;
1474 
1475 				boolean isBoundary = (c.getDepth() == walkDepth) || c.isBoundary();
1476 
1477 				// Commits at the boundary which aren't already shallow in
1478 				// the client need to be marked as such
1479 				if (isBoundary && !req.getClientShallowCommits().contains(c)) {
1480 					shallowFunc.accept(c.copy());
1481 				}
1482 
1483 				// Commits not on the boundary which are shallow in the client
1484 				// need to become unshallowed
1485 				if (!isBoundary && req.getClientShallowCommits().remove(c)) {
1486 					unshallowFunc.accept(c.copy());
1487 				}
1488 			}
1489 			if (!atLeastOne) {
1490 				throw new PackProtocolException(
1491 					JGitText.get().noCommitsSelectedForShallow);
1492 			}
1493 		}
1494 	}
1495 
1496 	/*
1497 	 * Verify all shallow lines refer to commits
1498 	 *
1499 	 * It can mutate the input set (removing missing object ids from it)
1500 	 */
1501 	private void verifyClientShallow(Set<ObjectId> shallowCommits)
1502 			throws IOException, PackProtocolException {
1503 		AsyncRevObjectQueue q = walk.parseAny(shallowCommits, true);
1504 		try {
1505 			for (;;) {
1506 				try {
1507 					// Shallow objects named by the client must be commits.
1508 					RevObject o = q.next();
1509 					if (o == null) {
1510 						break;
1511 					}
1512 					if (!(o instanceof RevCommit)) {
1513 						throw new PackProtocolException(
1514 							MessageFormat.format(
1515 								JGitText.get().invalidShallowObject,
1516 								o.name()));
1517 					}
1518 				} catch (MissingObjectException notCommit) {
1519 					// shallow objects not known at the server are ignored
1520 					// by git-core upload-pack, match that behavior.
1521 					shallowCommits.remove(notCommit.getObjectId());
1522 					continue;
1523 				}
1524 			}
1525 		} finally {
1526 			q.release();
1527 		}
1528 	}
1529 
1530 	/**
1531 	 * Generate an advertisement of available refs and capabilities.
1532 	 *
1533 	 * @param adv
1534 	 *            the advertisement formatter.
1535 	 * @throws java.io.IOException
1536 	 *            the formatter failed to write an advertisement.
1537 	 * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
1538 	 *            the hook denied advertisement.
1539 	 */
1540 	public void sendAdvertisedRefs(RefAdvertiser adv) throws IOException,
1541 			ServiceMayNotContinueException {
1542 		sendAdvertisedRefs(adv, null);
1543 	}
1544 
1545 	/**
1546 	 * Generate an advertisement of available refs and capabilities.
1547 	 *
1548 	 * @param adv
1549 	 *            the advertisement formatter.
1550 	 * @param serviceName
1551 	 *            if not null, also output "# service=serviceName" followed by a
1552 	 *            flush packet before the advertisement. This is required
1553 	 *            in v0 of the HTTP protocol, described in Git's
1554 	 *            Documentation/technical/http-protocol.txt.
1555 	 * @throws java.io.IOException
1556 	 *            the formatter failed to write an advertisement.
1557 	 * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
1558 	 *            the hook denied advertisement.
1559 	 * @since 5.0
1560 	 */
1561 	public void sendAdvertisedRefs(RefAdvertiser adv,
1562 			@Nullable String serviceName) throws IOException,
1563 			ServiceMayNotContinueException {
1564 		if (useProtocolV2()) {
1565 			// The equivalent in v2 is only the capabilities
1566 			// advertisement.
1567 			protocolV2Hook
1568 					.onCapabilities(CapabilitiesV2Request.builder().build());
1569 			for (String s : getV2CapabilityAdvertisement()) {
1570 				adv.writeOne(s);
1571 			}
1572 			adv.end();
1573 			return;
1574 		}
1575 
1576 		Map<String, Ref> advertisedOrDefaultRefs = getAdvertisedOrDefaultRefs();
1577 
1578 		if (serviceName != null) {
1579 			adv.writeOne("# service=" + serviceName + '\n'); //$NON-NLS-1$
1580 			adv.end();
1581 		}
1582 		adv.init(db);
1583 		adv.advertiseCapability(OPTION_INCLUDE_TAG);
1584 		adv.advertiseCapability(OPTION_MULTI_ACK_DETAILED);
1585 		adv.advertiseCapability(OPTION_MULTI_ACK);
1586 		adv.advertiseCapability(OPTION_OFS_DELTA);
1587 		adv.advertiseCapability(OPTION_SIDE_BAND);
1588 		adv.advertiseCapability(OPTION_SIDE_BAND_64K);
1589 		adv.advertiseCapability(OPTION_THIN_PACK);
1590 		adv.advertiseCapability(OPTION_NO_PROGRESS);
1591 		adv.advertiseCapability(OPTION_SHALLOW);
1592 		if (!biDirectionalPipe)
1593 			adv.advertiseCapability(OPTION_NO_DONE);
1594 		RequestPolicy policy = getRequestPolicy();
1595 		if (policy == RequestPolicy.TIP
1596 				|| policy == RequestPolicy.REACHABLE_COMMIT_TIP
1597 				|| policy == null)
1598 			adv.advertiseCapability(OPTION_ALLOW_TIP_SHA1_IN_WANT);
1599 		if (policy == RequestPolicy.REACHABLE_COMMIT
1600 				|| policy == RequestPolicy.REACHABLE_COMMIT_TIP
1601 				|| policy == null)
1602 			adv.advertiseCapability(OPTION_ALLOW_REACHABLE_SHA1_IN_WANT);
1603 		adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
1604 		if (transferConfig.isAllowFilter()) {
1605 			adv.advertiseCapability(OPTION_FILTER);
1606 		}
1607 		adv.setDerefTags(true);
1608 		findSymrefs(adv, advertisedOrDefaultRefs);
1609 		advertised = adv.send(advertisedOrDefaultRefs.values());
1610 
1611 		if (adv.isEmpty())
1612 			adv.advertiseId(ObjectId.zeroId(), "capabilities^{}"); //$NON-NLS-1$
1613 		adv.end();
1614 	}
1615 
1616 	/**
1617 	 * Send a message to the client, if it supports receiving them.
1618 	 * <p>
1619 	 * If the client doesn't support receiving messages, the message will be
1620 	 * discarded, with no other indication to the caller or to the client.
1621 	 *
1622 	 * @param what
1623 	 *            string describing the problem identified by the hook. The
1624 	 *            string must not end with an LF, and must not contain an LF.
1625 	 * @since 3.1
1626 	 */
1627 	public void sendMessage(String what) {
1628 		try {
1629 			msgOut.write(Constants.encode(what + '\n'));
1630 		} catch (IOException e) {
1631 			// Ignore write failures.
1632 		}
1633 	}
1634 
1635 	/**
1636 	 * Get an underlying stream for sending messages to the client
1637 	 *
1638 	 * @return an underlying stream for sending messages to the client, or null.
1639 	 * @since 3.1
1640 	 */
1641 	public OutputStream getMessageOutputStream() {
1642 		return msgOut;
1643 	}
1644 
1645 	/**
1646 	 * Returns the clone/fetch depth. Valid only after calling recvWants(). A
1647 	 * depth of 1 means return only the wants.
1648 	 *
1649 	 * @return the depth requested by the client, or 0 if unbounded.
1650 	 * @since 4.0
1651 	 */
1652 	public int getDepth() {
1653 		if (currentRequest == null)
1654 			throw new RequestNotYetReadException();
1655 		return currentRequest.getDepth();
1656 	}
1657 
1658 	/**
1659 	 * Deprecated synonym for {@code getFilterSpec().getBlobLimit()}.
1660 	 *
1661 	 * @return filter blob limit requested by the client, or -1 if no limit
1662 	 * @since 5.3
1663 	 * @deprecated Use {@link #getFilterSpec()} instead
1664 	 */
1665 	@Deprecated
1666 	public final long getFilterBlobLimit() {
1667 		return getFilterSpec().getBlobLimit();
1668 	}
1669 
1670 	/**
1671 	 * Returns the filter spec for the current request. Valid only after
1672 	 * calling recvWants(). This may be a no-op filter spec, but it won't be
1673 	 * null.
1674 	 *
1675 	 * @return filter requested by the client
1676 	 * @since 5.4
1677 	 */
1678 	public final FilterSpec getFilterSpec() {
1679 		if (currentRequest == null) {
1680 			throw new RequestNotYetReadException();
1681 		}
1682 		return currentRequest.getFilterSpec();
1683 	}
1684 
1685 	/**
1686 	 * Get the user agent of the client.
1687 	 * <p>
1688 	 * If the client is new enough to use {@code agent=} capability that value
1689 	 * will be returned. Older HTTP clients may also supply their version using
1690 	 * the HTTP {@code User-Agent} header. The capability overrides the HTTP
1691 	 * header if both are available.
1692 	 * <p>
1693 	 * When an HTTP request has been received this method returns the HTTP
1694 	 * {@code User-Agent} header value until capabilities have been parsed.
1695 	 *
1696 	 * @return user agent supplied by the client. Available only if the client
1697 	 *         is new enough to advertise its user agent.
1698 	 * @since 4.0
1699 	 */
1700 	public String getPeerUserAgent() {
1701 		if (currentRequest != null && currentRequest.getAgent() != null) {
1702 			return currentRequest.getAgent();
1703 		}
1704 
1705 		return userAgent;
1706 	}
1707 
1708 	/**
1709 	 * Get the session ID if received from the client.
1710 	 *
1711 	 * @return The session ID if it has been received from the client.
1712 	 * @since 6.4
1713 	 */
1714 	@Nullable
1715 	public String getClientSID() {
1716 		if (currentRequest == null) {
1717 			return null;
1718 		}
1719 
1720 		return currentRequest.getClientSID();
1721 	}
1722 
1723 	private boolean negotiate(FetchRequest req,
1724 			PackStatistics.Accumulator accumulator,
1725 			PacketLineOut pckOut)
1726 			throws IOException {
1727 		okToGiveUp = Boolean.FALSE;
1728 
1729 		ObjectId last = ObjectId.zeroId();
1730 		List<ObjectId> peerHas = new ArrayList<>(64);
1731 		for (;;) {
1732 			String line;
1733 			try {
1734 				line = pckIn.readString();
1735 			} catch (EOFException eof) {
1736 				// EOF on stateless RPC (aka smart HTTP) and non-shallow request
1737 				// means the client asked for the updated shallow/unshallow data,
1738 				// disconnected, and will try another request with actual want/have.
1739 				// Don't report the EOF here, its a bug in the protocol that the client
1740 				// just disconnects without sending an END.
1741 				if (!biDirectionalPipe && req.getDepth() > 0)
1742 					return false;
1743 				throw eof;
1744 			}
1745 
1746 			if (PacketLineIn.isEnd(line)) {
1747 				last = processHaveLines(peerHas, last, pckOut, accumulator, Option.NONE);
1748 				if (commonBase.isEmpty() || multiAck != MultiAck.OFF)
1749 					pckOut.writeString("NAK\n"); //$NON-NLS-1$
1750 				if (noDone && sentReady) {
1751 					pckOut.writeString(PACKET_ACK + last.name() + '\n');
1752 					return true;
1753 				}
1754 				if (!biDirectionalPipe)
1755 					return false;
1756 				pckOut.flush();
1757 
1758 			} else if (line.startsWith(PACKET_HAVE)
1759 					&& line.length() == PACKET_HAVE.length() + 40) {
1760 				peerHas.add(ObjectId
1761 						.fromString(line.substring(PACKET_HAVE.length())));
1762 				accumulator.haves++;
1763 			} else if (line.equals(PACKET_DONE)) {
1764 				last = processHaveLines(peerHas, last, pckOut, accumulator, Option.NONE);
1765 
1766 				if (commonBase.isEmpty())
1767 					pckOut.writeString("NAK\n"); //$NON-NLS-1$
1768 
1769 				else if (multiAck != MultiAck.OFF)
1770 					pckOut.writeString(PACKET_ACK + last.name() + '\n');
1771 
1772 				return true;
1773 
1774 			} else {
1775 				throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "have", line)); //$NON-NLS-1$
1776 			}
1777 		}
1778 	}
1779 
1780 	private enum Option {
1781 		WAIT_FOR_DONE,
1782 		NONE;
1783 	}
1784 
1785 	private ObjectId processHaveLines(List<ObjectId> peerHas, ObjectId last,
1786 			PacketLineOut out, PackStatistics.Accumulator accumulator,
1787 			Option option)
1788 			throws IOException {
1789 		preUploadHook.onBeginNegotiateRound(this, wantIds, peerHas.size());
1790 		if (wantAll.isEmpty() && !wantIds.isEmpty())
1791 			parseWants(accumulator);
1792 		if (peerHas.isEmpty())
1793 			return last;
1794 
1795 		sentReady = false;
1796 		int haveCnt = 0;
1797 		walk.getObjectReader().setAvoidUnreachableObjects(true);
1798 		AsyncRevObjectQueue q = walk.parseAny(peerHas, false);
1799 		try {
1800 			for (;;) {
1801 				RevObject obj;
1802 				try {
1803 					obj = q.next();
1804 				} catch (MissingObjectException notFound) {
1805 					continue;
1806 				}
1807 				if (obj == null)
1808 					break;
1809 
1810 				last = obj;
1811 				haveCnt++;
1812 
1813 				if (obj instanceof RevCommit) {
1814 					RevCommit c = (RevCommit) obj;
1815 					if (oldestTime == 0 || c.getCommitTime() < oldestTime)
1816 						oldestTime = c.getCommitTime();
1817 				}
1818 
1819 				if (obj.has(PEER_HAS))
1820 					continue;
1821 
1822 				obj.add(PEER_HAS);
1823 				if (obj instanceof RevCommit)
1824 					((RevCommit) obj).carry(PEER_HAS);
1825 				addCommonBase(obj);
1826 
1827 				// If both sides have the same object; let the client know.
1828 				//
1829 				switch (multiAck) {
1830 				case OFF:
1831 					if (commonBase.size() == 1) {
1832 						out.writeString(PACKET_ACK + obj.name() + '\n');
1833 					}
1834 					break;
1835 				case CONTINUE:
1836 					out.writeString(PACKET_ACK + obj.name() + " continue\n"); //$NON-NLS-1$
1837 					break;
1838 				case DETAILED:
1839 					out.writeString(PACKET_ACK + obj.name() + " common\n"); //$NON-NLS-1$
1840 					break;
1841 				}
1842 			}
1843 		} finally {
1844 			q.release();
1845 			walk.getObjectReader().setAvoidUnreachableObjects(false);
1846 		}
1847 
1848 		int missCnt = peerHas.size() - haveCnt;
1849 
1850 		// If we don't have one of the objects but we're also willing to
1851 		// create a pack at this point, let the client know so it stops
1852 		// telling us about its history.
1853 		//
1854 		if (option != Option.WAIT_FOR_DONE) {
1855 			sentReady = shouldGiveUp(peerHas, out, missCnt);
1856 		}
1857 
1858 		preUploadHook.onEndNegotiateRound(this, wantAll, haveCnt, missCnt, sentReady);
1859 		peerHas.clear();
1860 		return last;
1861 	}
1862 
1863 	private boolean shouldGiveUp(List<ObjectId> peerHas, PacketLineOut out, int missCnt)
1864 			throws IOException {
1865 		boolean readySent = false;
1866 		boolean didOkToGiveUp = false;
1867 		if (0 < missCnt) {
1868 			for (int i = peerHas.size() - 1; i >= 0; i--) {
1869 				ObjectId id = peerHas.get(i);
1870 				if (walk.lookupOrNull(id) == null) {
1871 					didOkToGiveUp = true;
1872 					if (okToGiveUp()) {
1873 						switch (multiAck) {
1874 						case OFF:
1875 							break;
1876 						case CONTINUE:
1877 							out.writeString(
1878 									PACKET_ACK + id.name() + " continue\n"); //$NON-NLS-1$
1879 							break;
1880 						case DETAILED:
1881 							out.writeString(
1882 									PACKET_ACK + id.name() + " ready\n"); //$NON-NLS-1$
1883 							readySent = true;
1884 							break;
1885 						}
1886 					}
1887 					break;
1888 				}
1889 			}
1890 		}
1891 
1892 		if (multiAck == MultiAck.DETAILED && !didOkToGiveUp
1893 				&& okToGiveUp()) {
1894 			ObjectId id = peerHas.get(peerHas.size() - 1);
1895 			out.writeString(PACKET_ACK + id.name() + " ready\n"); //$NON-NLS-1$
1896 			readySent = true;
1897 		}
1898 
1899 		return readySent;
1900 	}
1901 
1902 	private void parseWants(PackStatistics.Accumulator accumulator) throws IOException {
1903 		List<ObjectId> notAdvertisedWants = null;
1904 		for (ObjectId obj : wantIds) {
1905 			if (!advertised.contains(obj)) {
1906 				if (notAdvertisedWants == null)
1907 					notAdvertisedWants = new ArrayList<>();
1908 				notAdvertisedWants.add(obj);
1909 			}
1910 		}
1911 		if (notAdvertisedWants != null) {
1912 			accumulator.notAdvertisedWants = notAdvertisedWants.size();
1913 
1914 			Instant startReachabilityChecking = Instant.now();
1915 
1916 			requestValidator.checkWants(this, notAdvertisedWants);
1917 
1918 			accumulator.reachabilityCheckDuration = Duration
1919 					.between(startReachabilityChecking, Instant.now())
1920 					.toMillis();
1921 		}
1922 
1923 		AsyncRevObjectQueue q = walk.parseAny(wantIds, true);
1924 		try {
1925 			RevObject obj;
1926 			while ((obj = q.next()) != null) {
1927 				want(obj);
1928 
1929 				if (!(obj instanceof RevCommit))
1930 					obj.add(SATISFIED);
1931 				if (obj instanceof RevTag) {
1932 					obj = walk.peel(obj);
1933 					if (obj instanceof RevCommit)
1934 						want(obj);
1935 				}
1936 			}
1937 			wantIds.clear();
1938 		} catch (MissingObjectException notFound) {
1939 			throw new WantNotValidException(notFound.getObjectId(), notFound);
1940 		} finally {
1941 			q.release();
1942 		}
1943 	}
1944 
1945 	private void want(RevObject obj) {
1946 		if (!obj.has(WANT)) {
1947 			obj.add(WANT);
1948 			wantAll.add(obj);
1949 		}
1950 	}
1951 
1952 	/**
1953 	 * Validator corresponding to {@link RequestPolicy#ADVERTISED}.
1954 	 *
1955 	 * @since 3.1
1956 	 */
1957 	public static final class AdvertisedRequestValidator
1958 			implements RequestValidator {
1959 		@Override
1960 		public void checkWants(UploadPack up, List<ObjectId> wants)
1961 				throws PackProtocolException, IOException {
1962 			if (!up.isBiDirectionalPipe())
1963 				new ReachableCommitRequestValidator().checkWants(up, wants);
1964 			else if (!wants.isEmpty())
1965 				throw new WantNotValidException(wants.iterator().next());
1966 		}
1967 	}
1968 
1969 	/**
1970 	 * Validator corresponding to {@link RequestPolicy#REACHABLE_COMMIT}.
1971 	 *
1972 	 * @since 3.1
1973 	 */
1974 	public static final class ReachableCommitRequestValidator
1975 			implements RequestValidator {
1976 		@Override
1977 		public void checkWants(UploadPack up, List<ObjectId> wants)
1978 				throws PackProtocolException, IOException {
1979 			checkNotAdvertisedWants(up, wants, up.getAdvertisedRefs().values());
1980 		}
1981 	}
1982 
1983 	/**
1984 	 * Validator corresponding to {@link RequestPolicy#TIP}.
1985 	 *
1986 	 * @since 3.1
1987 	 */
1988 	public static final class TipRequestValidator implements RequestValidator {
1989 		@Override
1990 		public void checkWants(UploadPack up, List<ObjectId> wants)
1991 				throws PackProtocolException, IOException {
1992 			if (!up.isBiDirectionalPipe())
1993 				new ReachableCommitTipRequestValidator().checkWants(up, wants);
1994 			else if (!wants.isEmpty()) {
1995 				Set<ObjectId> refIds =
1996 						refIdSet(up.getRepository().getRefDatabase().getRefs());
1997 				for (ObjectId obj : wants) {
1998 					if (!refIds.contains(obj))
1999 						throw new WantNotValidException(obj);
2000 				}
2001 			}
2002 		}
2003 	}
2004 
2005 	/**
2006 	 * Validator corresponding to {@link RequestPolicy#REACHABLE_COMMIT_TIP}.
2007 	 *
2008 	 * @since 3.1
2009 	 */
2010 	public static final class ReachableCommitTipRequestValidator
2011 			implements RequestValidator {
2012 		@Override
2013 		public void checkWants(UploadPack up, List<ObjectId> wants)
2014 				throws PackProtocolException, IOException {
2015 			checkNotAdvertisedWants(up, wants,
2016 					up.getRepository().getRefDatabase().getRefs());
2017 		}
2018 	}
2019 
2020 	/**
2021 	 * Validator corresponding to {@link RequestPolicy#ANY}.
2022 	 *
2023 	 * @since 3.1
2024 	 */
2025 	public static final class AnyRequestValidator implements RequestValidator {
2026 		@Override
2027 		public void checkWants(UploadPack up, List<ObjectId> wants)
2028 				throws PackProtocolException, IOException {
2029 			// All requests are valid.
2030 		}
2031 	}
2032 
2033 	private static void checkNotAdvertisedWants(UploadPack up,
2034 			List<ObjectId> notAdvertisedWants, Collection<Ref> visibleRefs)
2035 			throws IOException {
2036 
2037 		ObjectReader reader = up.getRevWalk().getObjectReader();
2038 		Set<ObjectId> directlyVisibleObjects = refIdSet(visibleRefs);
2039 		List<ObjectId> nonTipWants = notAdvertisedWants.stream()
2040 				.filter(not(directlyVisibleObjects::contains))
2041 				.collect(Collectors.toList());
2042 
2043 		try (RevWalk walk = new RevWalk(reader)) {
2044 			walk.setRetainBody(false);
2045 			// Missing "wants" throw exception here
2046 			List<RevObject> wantsAsObjs = objectIdsToRevObjects(walk,
2047 					nonTipWants);
2048 			List<RevCommit> wantsAsCommits = wantsAsObjs.stream()
2049 					.filter(obj -> obj instanceof RevCommit)
2050 					.map(obj -> (RevCommit) obj)
2051 					.collect(Collectors.toList());
2052 			boolean allWantsAreCommits = wantsAsObjs.size() == wantsAsCommits
2053 					.size();
2054 			boolean repoHasBitmaps = reader.getBitmapIndex() != null;
2055 
2056 			if (!allWantsAreCommits) {
2057 				if (!repoHasBitmaps && !up.transferConfig.isAllowFilter()) {
2058 					// Checking unadvertised non-commits without bitmaps
2059 					// requires an expensive manual walk. Use allowFilter as an
2060 					// indication that the server operator is willing to pay
2061 					// this cost. Reject the request otherwise.
2062 					RevObject nonCommit = wantsAsObjs
2063 							.stream()
2064 							.filter(obj -> !(obj instanceof RevCommit))
2065 							.limit(1)
2066 							.collect(Collectors.toList()).get(0);
2067 					throw new WantNotValidException(nonCommit,
2068 							new Exception("Cannot walk without bitmaps")); //$NON-NLS-1$
2069 				}
2070 
2071 				try (ObjectWalk objWalk = walk.toObjectWalkWithSameObjects()) {
2072 					Stream<RevObject> startersAsObjs = importantRefsFirst(visibleRefs)
2073 							.map(UploadPack::refToObjectId)
2074 							.map(objId -> objectIdToRevObject(objWalk, objId))
2075 							.filter(Objects::nonNull); // Ignore missing tips
2076 
2077 					ObjectReachabilityChecker reachabilityChecker = reader
2078 							.createObjectReachabilityChecker(objWalk);
2079 					Optional<RevObject> unreachable = reachabilityChecker
2080 							.areAllReachable(wantsAsObjs, startersAsObjs);
2081 					if (unreachable.isPresent()) {
2082 						if (!repoHasBitmaps) {
2083 							throw new WantNotValidException(
2084 									unreachable.get(), new Exception(
2085 											"Retry with bitmaps enabled")); //$NON-NLS-1$
2086 						}
2087 						throw new WantNotValidException(unreachable.get());
2088 					}
2089 				}
2090 				return;
2091 			}
2092 
2093 			// All wants are commits, we can use ReachabilityChecker
2094 			ReachabilityChecker reachabilityChecker = reader
2095 					.createReachabilityChecker(walk);
2096 
2097 			Stream<RevCommit> reachableCommits = importantRefsFirst(visibleRefs)
2098 					.map(UploadPack::refToObjectId)
2099 					.map(objId -> objectIdToRevCommit(walk, objId))
2100 					.filter(Objects::nonNull); // Ignore missing tips
2101 
2102 			Optional<RevCommit> unreachable = reachabilityChecker
2103 					.areAllReachable(wantsAsCommits, reachableCommits);
2104 			if (unreachable.isPresent()) {
2105 				throw new WantNotValidException(unreachable.get());
2106 			}
2107 
2108 		} catch (MissingObjectException notFound) {
2109 			throw new WantNotValidException(notFound.getObjectId(), notFound);
2110 		}
2111 	}
2112 
2113 	private static <T> Predicate<T> not(Predicate<T> t) {
2114 	    return t.negate();
2115 	}
2116 
2117 	static Stream<Ref> importantRefsFirst(
2118 			Collection<Ref> visibleRefs) {
2119 		Predicate<Ref> startsWithRefsHeads = ref -> ref.getName()
2120 				.startsWith(Constants.R_HEADS);
2121 		Predicate<Ref> startsWithRefsTags = ref -> ref.getName()
2122 				.startsWith(Constants.R_TAGS);
2123 		Predicate<Ref> allOther = ref -> !startsWithRefsHeads.test(ref)
2124 				&& !startsWithRefsTags.test(ref);
2125 
2126 		return Stream.concat(
2127 				visibleRefs.stream().filter(startsWithRefsHeads),
2128 				Stream.concat(
2129 						visibleRefs.stream().filter(startsWithRefsTags),
2130 						visibleRefs.stream().filter(allOther)));
2131 	}
2132 
2133 	private static ObjectId refToObjectId(Ref ref) {
2134 		return ref.getObjectId() != null ? ref.getObjectId()
2135 				: ref.getPeeledObjectId();
2136 	}
2137 
2138 	/**
2139 	 * Translate an object id to a RevCommit.
2140 	 *
2141 	 * @param walk
2142 	 *            walk on the relevant object storae
2143 	 * @param objectId
2144 	 *            Object Id
2145 	 * @return RevCommit instance or null if the object is missing
2146 	 */
2147 	@Nullable
2148 	private static RevCommit objectIdToRevCommit(RevWalk walk,
2149 			ObjectId objectId) {
2150 		if (objectId == null) {
2151 			return null;
2152 		}
2153 
2154 		try {
2155 			return walk.parseCommit(objectId);
2156 		} catch (IOException e) {
2157 			return null;
2158 		}
2159 	}
2160 
2161 	/**
2162 	 * Translate an object id to a RevObject.
2163 	 *
2164 	 * @param walk
2165 	 *            walk on the relevant object storage
2166 	 * @param objectId
2167 	 *            Object Id
2168 	 * @return RevObject instance or null if the object is missing
2169 	 */
2170 	@Nullable
2171 	private static RevObject objectIdToRevObject(RevWalk walk,
2172 			ObjectId objectId) {
2173 		if (objectId == null) {
2174 			return null;
2175 		}
2176 
2177 		try {
2178 			return walk.parseAny(objectId);
2179 		} catch (IOException e) {
2180 			return null;
2181 		}
2182 	}
2183 
2184 	// Resolve the ObjectIds into RevObjects. Any missing object raises an
2185 	// exception
2186 	private static List<RevObject> objectIdsToRevObjects(RevWalk walk,
2187 			Iterable<ObjectId> objectIds)
2188 			throws MissingObjectException, IOException {
2189 		List<RevObject> result = new ArrayList<>();
2190 		for (ObjectId objectId : objectIds) {
2191 			result.add(walk.parseAny(objectId));
2192 		}
2193 		return result;
2194 	}
2195 
2196 	private void addCommonBase(RevObject o) {
2197 		if (!o.has(COMMON)) {
2198 			o.add(COMMON);
2199 			commonBase.add(o);
2200 			okToGiveUp = null;
2201 		}
2202 	}
2203 
2204 	private boolean okToGiveUp() throws PackProtocolException {
2205 		if (okToGiveUp == null)
2206 			okToGiveUp = Boolean.valueOf(okToGiveUpImp());
2207 		return okToGiveUp.booleanValue();
2208 	}
2209 
2210 	private boolean okToGiveUpImp() throws PackProtocolException {
2211 		if (commonBase.isEmpty())
2212 			return false;
2213 
2214 		try {
2215 			for (RevObject obj : wantAll) {
2216 				if (!wantSatisfied(obj))
2217 					return false;
2218 			}
2219 			return true;
2220 		} catch (IOException e) {
2221 			throw new PackProtocolException(JGitText.get().internalRevisionError, e);
2222 		}
2223 	}
2224 
2225 	private boolean wantSatisfied(RevObject want) throws IOException {
2226 		if (want.has(SATISFIED))
2227 			return true;
2228 
2229 		if (((RevCommit) want).getParentCount() == 0) {
2230 			want.add(SATISFIED);
2231 			return true;
2232 		}
2233 
2234 		walk.resetRetain(SAVE);
2235 		walk.markStart((RevCommit) want);
2236 		if (oldestTime != 0)
2237 			walk.setRevFilter(CommitTimeRevFilter.after(oldestTime * 1000L));
2238 		for (;;) {
2239 			final RevCommit c = walk.next();
2240 			if (c == null)
2241 				break;
2242 			if (c.has(PEER_HAS)) {
2243 				addCommonBase(c);
2244 				want.add(SATISFIED);
2245 				return true;
2246 			}
2247 		}
2248 		return false;
2249 	}
2250 
2251 	/**
2252 	 * Send the requested objects to the client.
2253 	 *
2254 	 * @param accumulator
2255 	 *            where to write statistics about the content of the pack.
2256 	 * @param req
2257 	 *            request in process
2258 	 * @param allTags
2259 	 *            refs to search for annotated tags to include in the pack if
2260 	 *            the {@link #OPTION_INCLUDE_TAG} capability was requested.
2261 	 * @param unshallowCommits
2262 	 *            shallow commits on the client that are now becoming unshallow
2263 	 * @param deepenNots
2264 	 *            objects that the client specified using --shallow-exclude
2265 	 * @param pckOut
2266 	 *            output writer
2267 	 * @throws IOException
2268 	 *             if an error occurred while generating or writing the pack.
2269 	 */
2270 	private void sendPack(PackStatistics.Accumulator accumulator,
2271 			FetchRequest req,
2272 			@Nullable Collection<Ref> allTags,
2273 			List<ObjectId> unshallowCommits,
2274 			List<ObjectId> deepenNots,
2275 			PacketLineOut pckOut) throws IOException {
2276 		Set<String> caps = req.getClientCapabilities();
2277 		boolean sideband = caps.contains(OPTION_SIDE_BAND)
2278 				|| caps.contains(OPTION_SIDE_BAND_64K);
2279 
2280 		if (sideband) {
2281 			errOut = new SideBandErrorWriter();
2282 
2283 			int bufsz = SideBandOutputStream.SMALL_BUF;
2284 			if (req.getClientCapabilities().contains(OPTION_SIDE_BAND_64K)) {
2285 				bufsz = SideBandOutputStream.MAX_BUF;
2286 			}
2287 			OutputStream packOut = new SideBandOutputStream(
2288 					SideBandOutputStream.CH_DATA, bufsz, rawOut);
2289 
2290 			ProgressMonitor pm = NullProgressMonitor.INSTANCE;
2291 			if (!req.getClientCapabilities().contains(OPTION_NO_PROGRESS)) {
2292 				msgOut = new SideBandOutputStream(
2293 						SideBandOutputStream.CH_PROGRESS, bufsz, rawOut);
2294 				pm = new SideBandProgressMonitor(msgOut);
2295 			}
2296 
2297 			sendPack(pm, pckOut, packOut, req, accumulator, allTags,
2298 					unshallowCommits, deepenNots);
2299 			pckOut.end();
2300 		} else {
2301 			sendPack(NullProgressMonitor.INSTANCE, pckOut, rawOut, req,
2302 					accumulator, allTags, unshallowCommits, deepenNots);
2303 		}
2304 	}
2305 
2306 	/**
2307 	 * Send the requested objects to the client.
2308 	 *
2309 	 * @param pm
2310 	 *            progress monitor
2311 	 * @param pckOut
2312 	 *            PacketLineOut that shares the output with packOut
2313 	 * @param packOut
2314 	 *            packfile output
2315 	 * @param req
2316 	 *            request being processed
2317 	 * @param accumulator
2318 	 *            where to write statistics about the content of the pack.
2319 	 * @param allTags
2320 	 *            refs to search for annotated tags to include in the pack if
2321 	 *            the {@link #OPTION_INCLUDE_TAG} capability was requested.
2322 	 * @param unshallowCommits
2323 	 *            shallow commits on the client that are now becoming unshallow
2324 	 * @param deepenNots
2325 	 *            objects that the client specified using --shallow-exclude
2326 	 * @throws IOException
2327 	 *             if an error occurred while generating or writing the pack.
2328 	 */
2329 	private void sendPack(ProgressMonitor pm, PacketLineOut pckOut,
2330 			OutputStream packOut, FetchRequest req,
2331 			PackStatistics.Accumulator accumulator,
2332 			@Nullable Collection<Ref> allTags, List<ObjectId> unshallowCommits,
2333 			List<ObjectId> deepenNots) throws IOException {
2334 		if (wantAll.isEmpty()) {
2335 			preUploadHook.onSendPack(this, wantIds, commonBase);
2336 		} else {
2337 			preUploadHook.onSendPack(this, wantAll, commonBase);
2338 		}
2339 		msgOut.flush();
2340 
2341 		// Advertised objects and refs are not used from here on and can be
2342 		// cleared.
2343 		advertised = null;
2344 		refs = null;
2345 
2346 		PackConfig cfg = packConfig;
2347 		if (cfg == null)
2348 			cfg = new PackConfig(db);
2349 		@SuppressWarnings("resource") // PackWriter is referenced in the finally
2350 										// block, and is closed there
2351 		final PackWriter pw = new PackWriter(cfg, walk.getObjectReader(),
2352 				accumulator);
2353 		try {
2354 			pw.setIndexDisabled(true);
2355 			if (req.getFilterSpec().isNoOp()) {
2356 				pw.setUseCachedPacks(true);
2357 			} else {
2358 				pw.setFilterSpec(req.getFilterSpec());
2359 				pw.setUseCachedPacks(false);
2360 			}
2361 			pw.setUseBitmaps(
2362 					req.getDepth() == 0
2363 							&& req.getClientShallowCommits().isEmpty()
2364 							&& req.getFilterSpec().getTreeDepthLimit() == -1);
2365 			pw.setClientShallowCommits(req.getClientShallowCommits());
2366 			pw.setReuseDeltaCommits(true);
2367 			pw.setDeltaBaseAsOffset(
2368 					req.getClientCapabilities().contains(OPTION_OFS_DELTA));
2369 			pw.setThin(req.getClientCapabilities().contains(OPTION_THIN_PACK));
2370 			pw.setReuseValidatingObjects(false);
2371 
2372 			// Objects named directly by references go at the beginning
2373 			// of the pack.
2374 			if (commonBase.isEmpty() && refs != null) {
2375 				Set<ObjectId> tagTargets = new HashSet<>();
2376 				for (Ref ref : refs.values()) {
2377 					if (ref.getPeeledObjectId() != null)
2378 						tagTargets.add(ref.getPeeledObjectId());
2379 					else if (ref.getObjectId() == null)
2380 						continue;
2381 					else if (ref.getName().startsWith(Constants.R_HEADS))
2382 						tagTargets.add(ref.getObjectId());
2383 				}
2384 				pw.setTagTargets(tagTargets);
2385 			}
2386 
2387 			RevWalk rw = walk;
2388 			if (req.getDepth() > 0 || req.getDeepenSince() != 0 || !deepenNots.isEmpty()) {
2389 				int walkDepth = req.getDepth() == 0 ? Integer.MAX_VALUE
2390 						: req.getDepth() - 1;
2391 				pw.setShallowPack(req.getDepth(), unshallowCommits);
2392 
2393 				// Ownership is transferred below
2394 				DepthWalk.RevWalk dw = new DepthWalk.RevWalk(
2395 						walk.getObjectReader(), walkDepth);
2396 				dw.setDeepenSince(req.getDeepenSince());
2397 				dw.setDeepenNots(deepenNots);
2398 				dw.assumeShallow(req.getClientShallowCommits());
2399 				rw = dw;
2400 			}
2401 
2402 			if (wantAll.isEmpty()) {
2403 				pw.preparePack(pm, wantIds, commonBase,
2404 						req.getClientShallowCommits());
2405 			} else {
2406 				walk.reset();
2407 
2408 				ObjectWalk ow = rw.toObjectWalkWithSameObjects();
2409 				pw.preparePack(pm, ow, wantAll, commonBase, PackWriter.NONE);
2410 				rw = ow;
2411 			}
2412 
2413 			if (req.getClientCapabilities().contains(OPTION_INCLUDE_TAG)
2414 					&& allTags != null) {
2415 				for (Ref ref : allTags) {
2416 					ObjectId objectId = ref.getObjectId();
2417 					if (objectId == null) {
2418 						// skip unborn branch
2419 						continue;
2420 					}
2421 
2422 					// If the object was already requested, skip it.
2423 					if (wantAll.isEmpty()) {
2424 						if (wantIds.contains(objectId))
2425 							continue;
2426 					} else {
2427 						RevObject obj = rw.lookupOrNull(objectId);
2428 						if (obj != null && obj.has(WANT))
2429 							continue;
2430 					}
2431 
2432 					if (!ref.isPeeled())
2433 						ref = db.getRefDatabase().peel(ref);
2434 
2435 					ObjectId peeledId = ref.getPeeledObjectId();
2436 					objectId = ref.getObjectId();
2437 					if (peeledId == null || objectId == null)
2438 						continue;
2439 
2440 					objectId = ref.getObjectId();
2441 					if (pw.willInclude(peeledId) && !pw.willInclude(objectId)) {
2442 						RevObject o = rw.parseAny(objectId);
2443 						addTagChain(o, pw);
2444 						pw.addObject(o);
2445 					}
2446 				}
2447 			}
2448 
2449 			if (pckOut.isUsingSideband()) {
2450 				if (req instanceof FetchV2Request &&
2451 						cachedPackUriProvider != null &&
2452 						!((FetchV2Request) req).getPackfileUriProtocols().isEmpty()) {
2453 					FetchV2Request reqV2 = (FetchV2Request) req;
2454 					pw.setPackfileUriConfig(new PackWriter.PackfileUriConfig(
2455 							pckOut,
2456 							reqV2.getPackfileUriProtocols(),
2457 							cachedPackUriProvider));
2458 				} else {
2459 					// PackWriter will write "packfile-uris\n" and "packfile\n"
2460 					// for us if provided a PackfileUriConfig. In this case, we
2461 					// are not providing a PackfileUriConfig, so we have to
2462 					// write this line ourselves.
2463 					pckOut.writeString(
2464 							GitProtocolConstants.SECTION_PACKFILE + '\n');
2465 				}
2466 			}
2467 			pw.enableSearchForReuseTimeout();
2468 			pw.writePack(pm, NullProgressMonitor.INSTANCE, packOut);
2469 
2470 			if (msgOut != NullOutputStream.INSTANCE) {
2471 				String msg = pw.getStatistics().getMessage() + '\n';
2472 				msgOut.write(Constants.encode(msg));
2473 				msgOut.flush();
2474 			}
2475 
2476 		} finally {
2477 			statistics = pw.getStatistics();
2478 			if (statistics != null) {
2479 				postUploadHook.onPostUpload(statistics);
2480 			}
2481 			pw.close();
2482 		}
2483 	}
2484 
2485 	private static void findSymrefs(
2486 			final RefAdvertiser adv, final Map<String, Ref> refs) {
2487 		Ref head = refs.get(Constants.HEAD);
2488 		if (head != null && head.isSymbolic()) {
2489 			adv.addSymref(Constants.HEAD, head.getLeaf().getName());
2490 		}
2491 	}
2492 
2493 	private void addTagChain(
2494 			RevObject o, PackWriter pw) throws IOException {
2495 		while (Constants.OBJ_TAG == o.getType()) {
2496 			RevTag t = (RevTag) o;
2497 			o = t.getObject();
2498 			if (o.getType() == Constants.OBJ_TAG && !pw.willInclude(o.getId())) {
2499 				walk.parseBody(o);
2500 				pw.addObject(o);
2501 			}
2502 		}
2503 	}
2504 
2505 	private List<ObjectId> parseDeepenNots(List<String> deepenNots)
2506 			throws IOException {
2507 		List<ObjectId> result = new ArrayList<>();
2508 		for (String s : deepenNots) {
2509 			if (ObjectId.isId(s)) {
2510 				result.add(ObjectId.fromString(s));
2511 			} else {
2512 				Ref ref = findRef(s);
2513 				if (ref == null) {
2514 					throw new PackProtocolException(MessageFormat
2515 							.format(JGitText.get().invalidRefName, s));
2516 				}
2517 				result.add(ref.getObjectId());
2518 			}
2519 		}
2520 		return result;
2521 	}
2522 
2523 	private static class ResponseBufferedOutputStream extends OutputStream {
2524 		private final OutputStream rawOut;
2525 
2526 		private OutputStream out;
2527 
2528 		ResponseBufferedOutputStream(OutputStream rawOut) {
2529 			this.rawOut = rawOut;
2530 			this.out = new ByteArrayOutputStream();
2531 		}
2532 
2533 		@Override
2534 		public void write(int b) throws IOException {
2535 			out.write(b);
2536 		}
2537 
2538 		@Override
2539 		public void write(byte[] b) throws IOException {
2540 			out.write(b);
2541 		}
2542 
2543 		@Override
2544 		public void write(byte[] b, int off, int len) throws IOException {
2545 			out.write(b, off, len);
2546 		}
2547 
2548 		@Override
2549 		public void flush() throws IOException {
2550 			out.flush();
2551 		}
2552 
2553 		@Override
2554 		public void close() throws IOException {
2555 			out.close();
2556 		}
2557 
2558 		void stopBuffering() throws IOException {
2559 			if (out != rawOut) {
2560 				((ByteArrayOutputStream) out).writeTo(rawOut);
2561 				out = rawOut;
2562 			}
2563 		}
2564 	}
2565 
2566 	private interface ErrorWriter {
2567 		void writeError(String message) throws IOException;
2568 	}
2569 
2570 	private class SideBandErrorWriter implements ErrorWriter {
2571 		@Override
2572 		public void writeError(String message) throws IOException {
2573 			@SuppressWarnings("resource" /* java 7 */)
2574 			SideBandOutputStream err = new SideBandOutputStream(
2575 					SideBandOutputStream.CH_ERROR,
2576 					SideBandOutputStream.SMALL_BUF, requireNonNull(rawOut));
2577 			err.write(Constants.encode(message));
2578 			err.flush();
2579 		}
2580 	}
2581 
2582 	private class PackProtocolErrorWriter implements ErrorWriter {
2583 		@Override
2584 		public void writeError(String message) throws IOException {
2585 			new PacketLineOut(requireNonNull(rawOut))
2586 					.writeString(PACKET_ERR + message + '\n');
2587 		}
2588 	}
2589 }