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