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