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