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