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