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