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