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