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