UploadPack.java

  1. /*
  2.  * Copyright (C) 2008, 2022 Google Inc. and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

  10. package org.eclipse.jgit.transport;

  11. import static java.util.Collections.emptyList;
  12. import static java.util.Collections.unmodifiableMap;
  13. import static java.util.Objects.requireNonNull;
  14. import static org.eclipse.jgit.lib.Constants.R_TAGS;
  15. import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REF_IN_WANT;
  16. import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SERVER_OPTION;
  17. import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_FETCH;
  18. import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_LS_REFS;
  19. import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_OBJECT_INFO;
  20. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
  21. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_REACHABLE_SHA1_IN_WANT;
  22. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT;
  23. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_DEEPEN_RELATIVE;
  24. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_FILTER;
  25. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_INCLUDE_TAG;
  26. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK;
  27. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK_DETAILED;
  28. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_DONE;
  29. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_PROGRESS;
  30. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_OFS_DELTA;
  31. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SHALLOW;
  32. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDEBAND_ALL;
  33. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
  34. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
  35. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SESSION_ID;
  36. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
  37. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WAIT_FOR_DONE;
  38. import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_ACK;
  39. import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DONE;
  40. import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_ERR;
  41. import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_HAVE;
  42. import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_SHALLOW;
  43. import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_UNSHALLOW;
  44. import static org.eclipse.jgit.transport.GitProtocolConstants.VERSION_2_REQUEST;
  45. import static org.eclipse.jgit.util.RefMap.toRefMap;

  46. import java.io.ByteArrayOutputStream;
  47. import java.io.Closeable;
  48. import java.io.EOFException;
  49. import java.io.IOException;
  50. import java.io.InputStream;
  51. import java.io.OutputStream;
  52. import java.io.UncheckedIOException;
  53. import java.text.MessageFormat;
  54. import java.time.Duration;
  55. import java.time.Instant;
  56. import java.util.ArrayList;
  57. import java.util.Collection;
  58. import java.util.Collections;
  59. import java.util.HashSet;
  60. import java.util.List;
  61. import java.util.Map;
  62. import java.util.Objects;
  63. import java.util.Optional;
  64. import java.util.Set;
  65. import java.util.TreeMap;
  66. import java.util.function.Function;
  67. import java.util.function.Predicate;
  68. import java.util.stream.Collectors;
  69. import java.util.stream.Stream;

  70. import org.eclipse.jgit.annotations.NonNull;
  71. import org.eclipse.jgit.annotations.Nullable;
  72. import org.eclipse.jgit.errors.CorruptObjectException;
  73. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  74. import org.eclipse.jgit.errors.MissingObjectException;
  75. import org.eclipse.jgit.errors.PackProtocolException;
  76. import org.eclipse.jgit.internal.JGitText;
  77. import org.eclipse.jgit.internal.storage.pack.CachedPackUriProvider;
  78. import org.eclipse.jgit.internal.storage.pack.PackWriter;
  79. import org.eclipse.jgit.internal.transport.parser.FirstWant;
  80. import org.eclipse.jgit.lib.Constants;
  81. import org.eclipse.jgit.lib.NullProgressMonitor;
  82. import org.eclipse.jgit.lib.ObjectId;
  83. import org.eclipse.jgit.lib.ObjectReader;
  84. import org.eclipse.jgit.lib.ProgressMonitor;
  85. import org.eclipse.jgit.lib.Ref;
  86. import org.eclipse.jgit.lib.RefDatabase;
  87. import org.eclipse.jgit.lib.Repository;
  88. import org.eclipse.jgit.revwalk.AsyncRevObjectQueue;
  89. import org.eclipse.jgit.revwalk.DepthWalk;
  90. import org.eclipse.jgit.revwalk.ObjectReachabilityChecker;
  91. import org.eclipse.jgit.revwalk.ObjectWalk;
  92. import org.eclipse.jgit.revwalk.ReachabilityChecker;
  93. import org.eclipse.jgit.revwalk.RevCommit;
  94. import org.eclipse.jgit.revwalk.RevFlag;
  95. import org.eclipse.jgit.revwalk.RevFlagSet;
  96. import org.eclipse.jgit.revwalk.RevObject;
  97. import org.eclipse.jgit.revwalk.RevTag;
  98. import org.eclipse.jgit.revwalk.RevWalk;
  99. import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
  100. import org.eclipse.jgit.storage.pack.PackConfig;
  101. import org.eclipse.jgit.storage.pack.PackStatistics;
  102. import org.eclipse.jgit.transport.GitProtocolConstants.MultiAck;
  103. import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
  104. import org.eclipse.jgit.transport.TransferConfig.ProtocolVersion;
  105. import org.eclipse.jgit.util.io.InterruptTimer;
  106. import org.eclipse.jgit.util.io.NullOutputStream;
  107. import org.eclipse.jgit.util.io.TimeoutInputStream;
  108. import org.eclipse.jgit.util.io.TimeoutOutputStream;

  109. /**
  110.  * Implements the server side of a fetch connection, transmitting objects.
  111.  */
  112. public class UploadPack implements Closeable {
  113.     /** Policy the server uses to validate client requests */
  114.     public enum RequestPolicy {
  115.         /** Client may only ask for objects the server advertised a reference for. */
  116.         ADVERTISED,

  117.         /**
  118.          * Client may ask for any commit reachable from a reference advertised by
  119.          * the server.
  120.          */
  121.         REACHABLE_COMMIT,

  122.         /**
  123.          * Client may ask for objects that are the tip of any reference, even if not
  124.          * advertised.
  125.          * <p>
  126.          * This may happen, for example, when a custom {@link RefFilter} is set.
  127.          *
  128.          * @since 3.1
  129.          */
  130.         TIP,

  131.         /**
  132.          * Client may ask for any commit reachable from any reference, even if that
  133.          * reference wasn't advertised.
  134.          *
  135.          * @since 3.1
  136.          */
  137.         REACHABLE_COMMIT_TIP,

  138.         /** Client may ask for any SHA-1 in the repository. */
  139.         ANY;
  140.     }

  141.     /**
  142.      * Validator for client requests.
  143.      *
  144.      * @since 3.1
  145.      */
  146.     public interface RequestValidator {
  147.         /**
  148.          * Check a list of client wants against the request policy.
  149.          *
  150.          * @param up
  151.          *            {@link UploadPack} instance.
  152.          * @param wants
  153.          *            objects the client requested that were not advertised.
  154.          *
  155.          * @throws PackProtocolException
  156.          *            if one or more wants is not valid.
  157.          * @throws IOException
  158.          *            if a low-level exception occurred.
  159.          * @since 3.1
  160.          */
  161.         void checkWants(UploadPack up, List<ObjectId> wants)
  162.                 throws PackProtocolException, IOException;
  163.     }

  164.     /**
  165.      * Data in the first line of a want-list, the line itself plus options.
  166.      *
  167.      * @deprecated Use {@link FirstWant} instead
  168.      */
  169.     @Deprecated
  170.     public static class FirstLine {

  171.         private final FirstWant firstWant;

  172.         /**
  173.          * @param line
  174.          *            line from the client.
  175.          */
  176.         public FirstLine(String line) {
  177.             try {
  178.                 firstWant = FirstWant.fromLine(line);
  179.             } catch (PackProtocolException e) {
  180.                 throw new UncheckedIOException(e);
  181.             }
  182.         }

  183.         /** @return non-capabilities part of the line. */
  184.         public String getLine() {
  185.             return firstWant.getLine();
  186.         }

  187.         /** @return capabilities parsed from the line. */
  188.         public Set<String> getOptions() {
  189.             if (firstWant.getAgent() != null) {
  190.                 Set<String> caps = new HashSet<>(firstWant.getCapabilities());
  191.                 caps.add(OPTION_AGENT + '=' + firstWant.getAgent());
  192.                 return caps;
  193.             }
  194.             return firstWant.getCapabilities();
  195.         }
  196.     }

  197.     /*
  198.      * {@link java.util.function.Consumer} doesn't allow throwing checked
  199.      * exceptions. Define our own to propagate IOExceptions.
  200.      */
  201.     @FunctionalInterface
  202.     private static interface IOConsumer<R> {
  203.         void accept(R t) throws IOException;
  204.     }

  205.     /** Database we read the objects from. */
  206.     private final Repository db;

  207.     /** Revision traversal support over {@link #db}. */
  208.     private final RevWalk walk;

  209.     /** Configuration to pass into the PackWriter. */
  210.     private PackConfig packConfig;

  211.     /** Configuration for various transfer options. */
  212.     private TransferConfig transferConfig;

  213.     /** Timeout in seconds to wait for client interaction. */
  214.     private int timeout;

  215.     /**
  216.      * Is the client connection a bi-directional socket or pipe?
  217.      * <p>
  218.      * If true, this class assumes it can perform multiple read and write cycles
  219.      * with the client over the input and output streams. This matches the
  220.      * functionality available with a standard TCP/IP connection, or a local
  221.      * operating system or in-memory pipe.
  222.      * <p>
  223.      * If false, this class runs in a read everything then output results mode,
  224.      * making it suitable for single round-trip systems RPCs such as HTTP.
  225.      */
  226.     private boolean biDirectionalPipe = true;

  227.     /** Timer to manage {@link #timeout}. */
  228.     private InterruptTimer timer;

  229.     /**
  230.      * Whether the client requested to use protocol V2 through a side
  231.      * channel (such as the Git-Protocol HTTP header).
  232.      */
  233.     private boolean clientRequestedV2;

  234.     private InputStream rawIn;

  235.     private ResponseBufferedOutputStream rawOut;

  236.     private PacketLineIn pckIn;

  237.     private OutputStream msgOut = NullOutputStream.INSTANCE;

  238.     private ErrorWriter errOut = new PackProtocolErrorWriter();

  239.     /**
  240.      * Refs eligible for advertising to the client, set using
  241.      * {@link #setAdvertisedRefs}.
  242.      */
  243.     private Map<String, Ref> refs;

  244.     /** Hook used while processing Git protocol v2 requests. */
  245.     private ProtocolV2Hook protocolV2Hook = ProtocolV2Hook.DEFAULT;

  246.     /** Hook used while advertising the refs to the client. */
  247.     private AdvertiseRefsHook advertiseRefsHook = AdvertiseRefsHook.DEFAULT;

  248.     /** Whether the {@link #advertiseRefsHook} has been invoked. */
  249.     private boolean advertiseRefsHookCalled;

  250.     /** Filter used while advertising the refs to the client. */
  251.     private RefFilter refFilter = RefFilter.DEFAULT;

  252.     /** Hook handling the various upload phases. */
  253.     private PreUploadHook preUploadHook = PreUploadHook.NULL;

  254.     /** Hook for taking post upload actions. */
  255.     private PostUploadHook postUploadHook = PostUploadHook.NULL;

  256.     /** Caller user agent */
  257.     String userAgent;

  258.     /** Raw ObjectIds the client has asked for, before validating them. */
  259.     private Set<ObjectId> wantIds = new HashSet<>();

  260.     /** Objects the client wants to obtain. */
  261.     private final Set<RevObject> wantAll = new HashSet<>();

  262.     /** Objects on both sides, these don't have to be sent. */
  263.     private final Set<RevObject> commonBase = new HashSet<>();

  264.     /** Commit time of the oldest common commit, in seconds. */
  265.     private int oldestTime;

  266.     /** null if {@link #commonBase} should be examined again. */
  267.     private Boolean okToGiveUp;

  268.     private boolean sentReady;

  269.     /** Objects we sent in our advertisement list. */
  270.     private Set<ObjectId> advertised;

  271.     /** Marked on objects the client has asked us to give them. */
  272.     private final RevFlag WANT;

  273.     /** Marked on objects both we and the client have. */
  274.     private final RevFlag PEER_HAS;

  275.     /** Marked on objects in {@link #commonBase}. */
  276.     private final RevFlag COMMON;

  277.     /** Objects where we found a path from the want list to a common base. */
  278.     private final RevFlag SATISFIED;

  279.     private final RevFlagSet SAVE;

  280.     private RequestValidator requestValidator = new AdvertisedRequestValidator();

  281.     private MultiAck multiAck = MultiAck.OFF;

  282.     private boolean noDone;

  283.     private PackStatistics statistics;

  284.     /**
  285.      * Request this instance is handling.
  286.      *
  287.      * We need to keep a reference to it for {@link PreUploadHook pre upload
  288.      * hooks}. They receive a reference this instance and invoke methods like
  289.      * getDepth() to get information about the request.
  290.      */
  291.     private FetchRequest currentRequest;

  292.     private CachedPackUriProvider cachedPackUriProvider;

  293.     /**
  294.      * Create a new pack upload for an open repository.
  295.      *
  296.      * @param copyFrom
  297.      *            the source repository.
  298.      */
  299.     public UploadPack(Repository copyFrom) {
  300.         db = copyFrom;
  301.         walk = new RevWalk(db);
  302.         walk.setRetainBody(false);

  303.         WANT = walk.newFlag("WANT"); //$NON-NLS-1$
  304.         PEER_HAS = walk.newFlag("PEER_HAS"); //$NON-NLS-1$
  305.         COMMON = walk.newFlag("COMMON"); //$NON-NLS-1$
  306.         SATISFIED = walk.newFlag("SATISFIED"); //$NON-NLS-1$
  307.         walk.carry(PEER_HAS);

  308.         SAVE = new RevFlagSet();
  309.         SAVE.add(WANT);
  310.         SAVE.add(PEER_HAS);
  311.         SAVE.add(COMMON);
  312.         SAVE.add(SATISFIED);

  313.         setTransferConfig(null);
  314.     }

  315.     /**
  316.      * Get the repository this upload is reading from.
  317.      *
  318.      * @return the repository this upload is reading from.
  319.      */
  320.     public final Repository getRepository() {
  321.         return db;
  322.     }

  323.     /**
  324.      * Get the RevWalk instance used by this connection.
  325.      *
  326.      * @return the RevWalk instance used by this connection.
  327.      */
  328.     public final RevWalk getRevWalk() {
  329.         return walk;
  330.     }

  331.     /**
  332.      * Get refs which were advertised to the client.
  333.      *
  334.      * @return all refs which were advertised to the client. Only valid during
  335.      *         the negotiation phase. Will return {@code null} if
  336.      *         {@link #setAdvertisedRefs(Map)} has not been called yet or if
  337.      *         {@code #sendPack()} has been called.
  338.      */
  339.     public final Map<String, Ref> getAdvertisedRefs() {
  340.         return refs;
  341.     }

  342.     /**
  343.      * Set the refs advertised by this UploadPack.
  344.      * <p>
  345.      * Intended to be called from a
  346.      * {@link org.eclipse.jgit.transport.PreUploadHook}.
  347.      *
  348.      * @param allRefs
  349.      *            explicit set of references to claim as advertised by this
  350.      *            UploadPack instance. This overrides any references that may
  351.      *            exist in the source repository. The map is passed to the
  352.      *            configured {@link #getRefFilter()}. If null, assumes all refs
  353.      *            were advertised.
  354.      */
  355.     public void setAdvertisedRefs(@Nullable Map<String, Ref> allRefs) {
  356.         if (allRefs != null) {
  357.             refs = allRefs;
  358.         } else {
  359.             refs = getAllRefs();
  360.         }
  361.         if (refFilter == RefFilter.DEFAULT) {
  362.             refs = transferConfig.getRefFilter().filter(refs);
  363.         } else {
  364.             refs = refFilter.filter(refs);
  365.         }
  366.     }

  367.     /**
  368.      * Get timeout (in seconds) before aborting an IO operation.
  369.      *
  370.      * @return timeout (in seconds) before aborting an IO operation.
  371.      */
  372.     public int getTimeout() {
  373.         return timeout;
  374.     }

  375.     /**
  376.      * Set the timeout before willing to abort an IO call.
  377.      *
  378.      * @param seconds
  379.      *            number of seconds to wait (with no data transfer occurring)
  380.      *            before aborting an IO read or write operation with the
  381.      *            connected client.
  382.      */
  383.     public void setTimeout(int seconds) {
  384.         timeout = seconds;
  385.     }

  386.     /**
  387.      * Whether this class expects a bi-directional pipe opened between the
  388.      * client and itself.
  389.      *
  390.      * @return true if this class expects a bi-directional pipe opened between
  391.      *         the client and itself. The default is true.
  392.      */
  393.     public boolean isBiDirectionalPipe() {
  394.         return biDirectionalPipe;
  395.     }

  396.     /**
  397.      * Set whether this class will assume the socket is a fully bidirectional
  398.      * pipe between the two peers
  399.      *
  400.      * @param twoWay
  401.      *            if true, this class will assume the socket is a fully
  402.      *            bidirectional pipe between the two peers and takes advantage
  403.      *            of that by first transmitting the known refs, then waiting to
  404.      *            read commands. If false, this class assumes it must read the
  405.      *            commands before writing output and does not perform the
  406.      *            initial advertising.
  407.      */
  408.     public void setBiDirectionalPipe(boolean twoWay) {
  409.         biDirectionalPipe = twoWay;
  410.     }

  411.     /**
  412.      * Get policy used by the service to validate client requests
  413.      *
  414.      * @return policy used by the service to validate client requests, or null
  415.      *         for a custom request validator.
  416.      */
  417.     public RequestPolicy getRequestPolicy() {
  418.         if (requestValidator instanceof AdvertisedRequestValidator)
  419.             return RequestPolicy.ADVERTISED;
  420.         if (requestValidator instanceof ReachableCommitRequestValidator)
  421.             return RequestPolicy.REACHABLE_COMMIT;
  422.         if (requestValidator instanceof TipRequestValidator)
  423.             return RequestPolicy.TIP;
  424.         if (requestValidator instanceof ReachableCommitTipRequestValidator)
  425.             return RequestPolicy.REACHABLE_COMMIT_TIP;
  426.         if (requestValidator instanceof AnyRequestValidator)
  427.             return RequestPolicy.ANY;
  428.         return null;
  429.     }

  430.     /**
  431.      * Set the policy used to enforce validation of a client's want list.
  432.      *
  433.      * @param policy
  434.      *            the policy used to enforce validation of a client's want list.
  435.      *            By default the policy is
  436.      *            {@link org.eclipse.jgit.transport.UploadPack.RequestPolicy#ADVERTISED},
  437.      *            which is the Git default requiring clients to only ask for an
  438.      *            object that a reference directly points to. This may be
  439.      *            relaxed to
  440.      *            {@link org.eclipse.jgit.transport.UploadPack.RequestPolicy#REACHABLE_COMMIT}
  441.      *            or
  442.      *            {@link org.eclipse.jgit.transport.UploadPack.RequestPolicy#REACHABLE_COMMIT_TIP}
  443.      *            when callers have {@link #setBiDirectionalPipe(boolean)} set
  444.      *            to false. Overrides any policy specified in a
  445.      *            {@link org.eclipse.jgit.transport.TransferConfig}.
  446.      */
  447.     public void setRequestPolicy(RequestPolicy policy) {
  448.         switch (policy) {
  449.             case ADVERTISED:
  450.             default:
  451.                 requestValidator = new AdvertisedRequestValidator();
  452.                 break;
  453.             case REACHABLE_COMMIT:
  454.                 requestValidator = new ReachableCommitRequestValidator();
  455.                 break;
  456.             case TIP:
  457.                 requestValidator = new TipRequestValidator();
  458.                 break;
  459.             case REACHABLE_COMMIT_TIP:
  460.                 requestValidator = new ReachableCommitTipRequestValidator();
  461.                 break;
  462.             case ANY:
  463.                 requestValidator = new AnyRequestValidator();
  464.                 break;
  465.         }
  466.     }

  467.     /**
  468.      * Set custom validator for client want list.
  469.      *
  470.      * @param validator
  471.      *            custom validator for client want list.
  472.      * @since 3.1
  473.      */
  474.     public void setRequestValidator(@Nullable RequestValidator validator) {
  475.         requestValidator = validator != null ? validator
  476.                 : new AdvertisedRequestValidator();
  477.     }

  478.     /**
  479.      * Get the hook used while advertising the refs to the client.
  480.      *
  481.      * @return the hook used while advertising the refs to the client.
  482.      */
  483.     public AdvertiseRefsHook getAdvertiseRefsHook() {
  484.         return advertiseRefsHook;
  485.     }

  486.     /**
  487.      * Get the filter used while advertising the refs to the client.
  488.      *
  489.      * @return the filter used while advertising the refs to the client.
  490.      */
  491.     public RefFilter getRefFilter() {
  492.         return refFilter;
  493.     }

  494.     /**
  495.      * Set the hook used while advertising the refs to the client.
  496.      * <p>
  497.      * If the {@link org.eclipse.jgit.transport.AdvertiseRefsHook} chooses to
  498.      * call {@link #setAdvertisedRefs(Map)}, only refs set by this hook
  499.      * <em>and</em> selected by the {@link org.eclipse.jgit.transport.RefFilter}
  500.      * will be shown to the client.
  501.      *
  502.      * @param advertiseRefsHook
  503.      *            the hook; may be null to show all refs.
  504.      */
  505.     public void setAdvertiseRefsHook(
  506.             @Nullable AdvertiseRefsHook advertiseRefsHook) {
  507.         this.advertiseRefsHook = advertiseRefsHook != null ? advertiseRefsHook
  508.                 : AdvertiseRefsHook.DEFAULT;
  509.     }

  510.     /**
  511.      * Set the protocol V2 hook.
  512.      *
  513.      * @param hook
  514.      *            the hook; if null no special actions are taken.
  515.      * @since 5.1
  516.      */
  517.     public void setProtocolV2Hook(@Nullable ProtocolV2Hook hook) {
  518.         this.protocolV2Hook = hook != null ? hook : ProtocolV2Hook.DEFAULT;
  519.     }

  520.     /**
  521.      * Get the currently installed protocol v2 hook.
  522.      *
  523.      * @return the hook or a default implementation if none installed.
  524.      *
  525.      * @since 5.5
  526.      */
  527.     public ProtocolV2Hook getProtocolV2Hook() {
  528.         return this.protocolV2Hook != null ? this.protocolV2Hook
  529.                 : ProtocolV2Hook.DEFAULT;
  530.     }

  531.     /**
  532.      * Set the filter used while advertising the refs to the client.
  533.      * <p>
  534.      * Only refs allowed by this filter will be sent to the client. The filter
  535.      * is run against the refs specified by the
  536.      * {@link org.eclipse.jgit.transport.AdvertiseRefsHook} (if applicable). If
  537.      * null or not set, uses the filter implied by the
  538.      * {@link org.eclipse.jgit.transport.TransferConfig}.
  539.      *
  540.      * @param refFilter
  541.      *            the filter; may be null to show all refs.
  542.      */
  543.     public void setRefFilter(@Nullable RefFilter refFilter) {
  544.         this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
  545.     }

  546.     /**
  547.      * Get the configured pre upload hook.
  548.      *
  549.      * @return the configured pre upload hook.
  550.      */
  551.     public PreUploadHook getPreUploadHook() {
  552.         return preUploadHook;
  553.     }

  554.     /**
  555.      * Set the hook that controls how this instance will behave.
  556.      *
  557.      * @param hook
  558.      *            the hook; if null no special actions are taken.
  559.      */
  560.     public void setPreUploadHook(@Nullable PreUploadHook hook) {
  561.         preUploadHook = hook != null ? hook : PreUploadHook.NULL;
  562.     }

  563.     /**
  564.      * Get the configured post upload hook.
  565.      *
  566.      * @return the configured post upload hook.
  567.      * @since 4.1
  568.      */
  569.     public PostUploadHook getPostUploadHook() {
  570.         return postUploadHook;
  571.     }

  572.     /**
  573.      * Set the hook for post upload actions (logging, repacking).
  574.      *
  575.      * @param hook
  576.      *            the hook; if null no special actions are taken.
  577.      * @since 4.1
  578.      */
  579.     public void setPostUploadHook(@Nullable PostUploadHook hook) {
  580.         postUploadHook = hook != null ? hook : PostUploadHook.NULL;
  581.     }

  582.     /**
  583.      * Set the configuration used by the pack generator.
  584.      *
  585.      * @param pc
  586.      *            configuration controlling packing parameters. If null the
  587.      *            source repository's settings will be used.
  588.      */
  589.     public void setPackConfig(@Nullable PackConfig pc) {
  590.         this.packConfig = pc;
  591.     }

  592.     /**
  593.      * Set configuration controlling transfer options.
  594.      *
  595.      * @param tc
  596.      *            configuration controlling transfer options. If null the source
  597.      *            repository's settings will be used.
  598.      * @since 3.1
  599.      */
  600.     public void setTransferConfig(@Nullable TransferConfig tc) {
  601.         this.transferConfig = tc != null ? tc : new TransferConfig(db);
  602.         if (transferConfig.isAllowTipSha1InWant()) {
  603.             setRequestPolicy(transferConfig.isAllowReachableSha1InWant()
  604.                 ? RequestPolicy.REACHABLE_COMMIT_TIP : RequestPolicy.TIP);
  605.         } else {
  606.             setRequestPolicy(transferConfig.isAllowReachableSha1InWant()
  607.                 ? RequestPolicy.REACHABLE_COMMIT : RequestPolicy.ADVERTISED);
  608.         }
  609.     }

  610.     /**
  611.      * Check whether the client expects a side-band stream.
  612.      *
  613.      * @return true if the client has advertised a side-band capability, false
  614.      *     otherwise.
  615.      * @throws org.eclipse.jgit.transport.RequestNotYetReadException
  616.      *             if the client's request has not yet been read from the wire, so
  617.      *             we do not know if they expect side-band. Note that the client
  618.      *             may have already written the request, it just has not been
  619.      *             read.
  620.      */
  621.     public boolean isSideBand() throws RequestNotYetReadException {
  622.         if (currentRequest == null) {
  623.             throw new RequestNotYetReadException();
  624.         }
  625.         Set<String> caps = currentRequest.getClientCapabilities();
  626.         return caps.contains(OPTION_SIDE_BAND)
  627.                 || caps.contains(OPTION_SIDE_BAND_64K);
  628.     }

  629.     /**
  630.      * Set the Extra Parameters provided by the client.
  631.      *
  632.      * <p>These are parameters passed by the client through a side channel
  633.      * such as the Git-Protocol HTTP header, to allow a client to request
  634.      * a newer response format while remaining compatible with older servers
  635.      * that do not understand different request formats.
  636.      *
  637.      * @param params
  638.      *            parameters supplied by the client, split at colons or NUL
  639.      *            bytes.
  640.      * @since 5.0
  641.      */
  642.     public void setExtraParameters(Collection<String> params) {
  643.         this.clientRequestedV2 = params.contains(VERSION_2_REQUEST);
  644.     }

  645.     /**
  646.      * @param p provider of URIs corresponding to cached packs (to support
  647.      *     the packfile URIs feature)
  648.      * @since 5.5
  649.      */
  650.     public void setCachedPackUriProvider(@Nullable CachedPackUriProvider p) {
  651.         cachedPackUriProvider = p;
  652.     }

  653.     private boolean useProtocolV2() {
  654.         return (transferConfig.protocolVersion == null
  655.             || ProtocolVersion.V2.equals(transferConfig.protocolVersion))
  656.                 && clientRequestedV2;
  657.     }

  658.     @Override
  659.     public void close() {
  660.         if (timer != null) {
  661.             try {
  662.                 timer.terminate();
  663.             } finally {
  664.                 timer = null;
  665.             }
  666.         }
  667.     }

  668.     /**
  669.      * Execute the upload task on the socket.
  670.      *
  671.      * <p>
  672.      * Same as {@link #uploadWithExceptionPropagation} except that the thrown
  673.      * exceptions are handled in the method, and the error messages are sent to
  674.      * the clients.
  675.      *
  676.      * <p>
  677.      * Call this method if the caller does not have an error handling mechanism.
  678.      * Call {@link #uploadWithExceptionPropagation} if the caller wants to have
  679.      * its own error handling mechanism.
  680.      *
  681.      * @param input
  682.      * @param output
  683.      * @param messages
  684.      * @throws java.io.IOException
  685.      */
  686.     public void upload(InputStream input, OutputStream output,
  687.             @Nullable OutputStream messages) throws IOException {
  688.         try {
  689.             uploadWithExceptionPropagation(input, output, messages);
  690.         } catch (ServiceMayNotContinueException err) {
  691.             if (!err.isOutput() && err.getMessage() != null) {
  692.                 try {
  693.                     errOut.writeError(err.getMessage());
  694.                 } catch (IOException e) {
  695.                     err.addSuppressed(e);
  696.                     throw err;
  697.                 }
  698.                 err.setOutput();
  699.             }
  700.             throw err;
  701.         } catch (IOException | RuntimeException | Error err) {
  702.             if (rawOut != null) {
  703.                 String msg = err instanceof PackProtocolException
  704.                         ? err.getMessage()
  705.                         : JGitText.get().internalServerError;
  706.                 try {
  707.                     errOut.writeError(msg);
  708.                 } catch (IOException e) {
  709.                     err.addSuppressed(e);
  710.                     throw err;
  711.                 }
  712.                 throw new UploadPackInternalServerErrorException(err);
  713.             }
  714.             throw err;
  715.         } finally {
  716.             close();
  717.         }
  718.     }

  719.     /**
  720.      * Execute the upload task on the socket.
  721.      *
  722.      * <p>
  723.      * If the client passed extra parameters (e.g., "version=2") through a side
  724.      * channel, the caller must call setExtraParameters first to supply them.
  725.      * Callers of this method should call {@link #close()} to terminate the
  726.      * internal interrupt timer thread. If the caller fails to terminate the
  727.      * thread, it will (eventually) terminate itself when the InterruptTimer
  728.      * instance is garbage collected.
  729.      *
  730.      * @param input
  731.      *            raw input to read client commands from. Caller must ensure the
  732.      *            input is buffered, otherwise read performance may suffer.
  733.      * @param output
  734.      *            response back to the Git network client, to write the pack
  735.      *            data onto. Caller must ensure the output is buffered,
  736.      *            otherwise write performance may suffer.
  737.      * @param messages
  738.      *            secondary "notice" channel to send additional messages out
  739.      *            through. When run over SSH this should be tied back to the
  740.      *            standard error channel of the command execution. For most
  741.      *            other network connections this should be null.
  742.      * @throws ServiceMayNotContinueException
  743.      *             thrown if one of the hooks throws this.
  744.      * @throws IOException
  745.      *             thrown if the server or the client I/O fails, or there's an
  746.      *             internal server error.
  747.      * @since 5.6
  748.      */
  749.     public void uploadWithExceptionPropagation(InputStream input,
  750.             OutputStream output, @Nullable OutputStream messages)
  751.             throws ServiceMayNotContinueException, IOException {
  752.         try {
  753.             rawIn = input;
  754.             if (messages != null) {
  755.                 msgOut = messages;
  756.             }

  757.             if (timeout > 0) {
  758.                 final Thread caller = Thread.currentThread();
  759.                 timer = new InterruptTimer(caller.getName() + "-Timer"); //$NON-NLS-1$
  760.                 TimeoutInputStream i = new TimeoutInputStream(rawIn, timer);
  761.                 @SuppressWarnings("resource")
  762.                 TimeoutOutputStream o = new TimeoutOutputStream(output, timer);
  763.                 i.setTimeout(timeout * 1000);
  764.                 o.setTimeout(timeout * 1000);
  765.                 rawIn = i;
  766.                 output = o;
  767.             }

  768.             rawOut = new ResponseBufferedOutputStream(output);
  769.             if (biDirectionalPipe) {
  770.                 rawOut.stopBuffering();
  771.             }

  772.             pckIn = new PacketLineIn(rawIn);
  773.             PacketLineOut pckOut = new PacketLineOut(rawOut);
  774.             if (useProtocolV2()) {
  775.                 serviceV2(pckOut);
  776.             } else {
  777.                 service(pckOut);
  778.             }
  779.         } finally {
  780.             msgOut = NullOutputStream.INSTANCE;
  781.             walk.close();
  782.         }
  783.     }

  784.     /**
  785.      * Get the PackWriter's statistics if a pack was sent to the client.
  786.      *
  787.      * @return statistics about pack output, if a pack was sent. Null if no pack
  788.      *         was sent, such as during the negotiation phase of a smart HTTP
  789.      *         connection, or if the client was already up-to-date.
  790.      * @since 4.1
  791.      */
  792.     public PackStatistics getStatistics() {
  793.         return statistics;
  794.     }

  795.     /**
  796.      * Extract the full list of refs from the ref-db.
  797.      *
  798.      * @return Map of all refname/ref
  799.      */
  800.     private Map<String, Ref> getAllRefs() {
  801.         try {
  802.             return db.getRefDatabase().getRefs().stream().collect(
  803.                     Collectors.toMap(Ref::getName, Function.identity()));
  804.         } catch (IOException e) {
  805.             throw new UncheckedIOException(e);
  806.         }
  807.     }

  808.     private Map<String, Ref> getAdvertisedOrDefaultRefs() throws IOException {
  809.         if (refs != null) {
  810.             return refs;
  811.         }

  812.         if (!advertiseRefsHookCalled) {
  813.             advertiseRefsHook.advertiseRefs(this);
  814.             advertiseRefsHookCalled = true;
  815.         }
  816.         if (refs == null) {
  817.             // Fall back to all refs.
  818.             setAdvertisedRefs(
  819.                     db.getRefDatabase().getRefs().stream()
  820.                             .collect(toRefMap((a, b) -> b)));
  821.         }
  822.         return refs;
  823.     }

  824.     private Map<String, Ref> getFilteredRefs(Collection<String> refPrefixes)
  825.                     throws IOException {
  826.         if (refPrefixes.isEmpty()) {
  827.             return getAdvertisedOrDefaultRefs();
  828.         }
  829.         if (refs == null && !advertiseRefsHookCalled) {
  830.             advertiseRefsHook.advertiseRefs(this);
  831.             advertiseRefsHookCalled = true;
  832.         }
  833.         if (refs == null) {
  834.             // Fast path: the advertised refs hook did not set advertised refs.
  835.             String[] prefixes = refPrefixes.toArray(new String[0]);
  836.             Map<String, Ref> rs =
  837.                     db.getRefDatabase().getRefsByPrefix(prefixes).stream()
  838.                             .collect(toRefMap((a, b) -> b));
  839.             if (refFilter != RefFilter.DEFAULT) {
  840.                 return refFilter.filter(rs);
  841.             }
  842.             return transferConfig.getRefFilter().filter(rs);
  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(toRefMap((a, b) -> b));
  850.     }

  851.     /**
  852.      * Returns the specified references.
  853.      * <p>
  854.      * This produces an immutable map containing whatever subset of the
  855.      * refs named by the caller are present in the supplied {@code refs}
  856.      * map.
  857.      *
  858.      * @param refs
  859.      *            Map to search for refs to return.
  860.      * @param names
  861.      *            which refs to search for in {@code refs}.
  862.      * @return the requested Refs, omitting any that are null or missing.
  863.      */
  864.     @NonNull
  865.     private static Map<String, Ref> mapRefs(
  866.             Map<String, Ref> refs, List<String> names) {
  867.         return unmodifiableMap(
  868.                 names.stream()
  869.                     .map(refs::get)
  870.                     .filter(Objects::nonNull)
  871.                         .collect(toRefMap((a, b) -> b)));
  872.     }

  873.     /**
  874.      * Read refs on behalf of the client.
  875.      * <p>
  876.      * This checks whether the refs are present in the ref advertisement
  877.      * since otherwise the client might not be supposed to be able to
  878.      * read them.
  879.      *
  880.      * @param names
  881.      *            unabbreviated names of references.
  882.      * @return the requested Refs, omitting any that are not visible or
  883.      *         do not exist.
  884.      * @throws java.io.IOException
  885.      *            on failure to read a ref or check it for visibility.
  886.      */
  887.     @NonNull
  888.     private Map<String, Ref> exactRefs(List<String> names) throws IOException {
  889.         if (refs != null) {
  890.             return mapRefs(refs, names);
  891.         }
  892.         if (!advertiseRefsHookCalled) {
  893.             advertiseRefsHook.advertiseRefs(this);
  894.             advertiseRefsHookCalled = true;
  895.         }
  896.         if (refs == null &&
  897.                 refFilter == RefFilter.DEFAULT &&
  898.                 transferConfig.hasDefaultRefFilter()) {
  899.             // Fast path: no ref filtering is needed.
  900.             String[] ns = names.toArray(new String[0]);
  901.             return unmodifiableMap(db.getRefDatabase().exactRef(ns));
  902.         }
  903.         return mapRefs(getAdvertisedOrDefaultRefs(), names);
  904.     }

  905.     /**
  906.      * Find a ref in the usual search path on behalf of the client.
  907.      * <p>
  908.      * This checks that the ref is present in the ref advertisement since
  909.      * otherwise the client might not be supposed to be able to read it.
  910.      *
  911.      * @param name
  912.      *            short name of the ref to find, e.g. "master" to find
  913.      *            "refs/heads/master".
  914.      * @return the requested Ref, or {@code null} if it is not visible or
  915.      *         does not exist.
  916.      * @throws java.io.IOException
  917.      *            on failure to read the ref or check it for visibility.
  918.      */
  919.     @Nullable
  920.     private Ref findRef(String name) throws IOException {
  921.         if (refs != null) {
  922.             return RefDatabase.findRef(refs, name);
  923.         }
  924.         if (!advertiseRefsHookCalled) {
  925.             advertiseRefsHook.advertiseRefs(this);
  926.             advertiseRefsHookCalled = true;
  927.         }
  928.         if (refs == null &&
  929.                 refFilter == RefFilter.DEFAULT &&
  930.                 transferConfig.hasDefaultRefFilter()) {
  931.             // Fast path: no ref filtering is needed.
  932.             return db.getRefDatabase().findRef(name);
  933.         }
  934.         return RefDatabase.findRef(getAdvertisedOrDefaultRefs(), name);
  935.     }

  936.     private void service(PacketLineOut pckOut) throws IOException {
  937.         boolean sendPack = false;
  938.         // If it's a non-bidi request, we need to read the entire request before
  939.         // writing a response. Buffer the response until then.
  940.         PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator();
  941.         List<ObjectId> unshallowCommits = new ArrayList<>();
  942.         List<ObjectId> deepenNots = emptyList();
  943.         FetchRequest req;
  944.         try {
  945.             if (biDirectionalPipe)
  946.                 sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
  947.             else if (requestValidator instanceof AnyRequestValidator)
  948.                 advertised = Collections.emptySet();
  949.             else
  950.                 advertised = refIdSet(getAdvertisedOrDefaultRefs().values());

  951.             Instant negotiateStart = Instant.now();
  952.             accumulator.advertised = advertised.size();

  953.             ProtocolV0Parser parser = new ProtocolV0Parser(transferConfig);
  954.             req = parser.recvWants(pckIn);
  955.             currentRequest = req;

  956.             wantIds = req.getWantIds();

  957.             if (req.getWantIds().isEmpty()) {
  958.                 preUploadHook.onBeginNegotiateRound(this, req.getWantIds(), 0);
  959.                 preUploadHook.onEndNegotiateRound(this, req.getWantIds(), 0, 0,
  960.                         false);
  961.                 return;
  962.             }
  963.             accumulator.wants = req.getWantIds().size();

  964.             if (req.getClientCapabilities().contains(OPTION_MULTI_ACK_DETAILED)) {
  965.                 multiAck = MultiAck.DETAILED;
  966.                 noDone = req.getClientCapabilities().contains(OPTION_NO_DONE);
  967.             } else if (req.getClientCapabilities().contains(OPTION_MULTI_ACK))
  968.                 multiAck = MultiAck.CONTINUE;
  969.             else
  970.                 multiAck = MultiAck.OFF;

  971.             if (!req.getClientShallowCommits().isEmpty()) {
  972.                 verifyClientShallow(req.getClientShallowCommits());
  973.             }

  974.             deepenNots = parseDeepenNots(req.getDeepenNots());
  975.             if (req.getDepth() != 0 || req.getDeepenSince() != 0 || !req.getDeepenNots().isEmpty()) {
  976.                 computeShallowsAndUnshallows(req, shallow -> {
  977.                     pckOut.writeString(PACKET_SHALLOW + shallow.name() + '\n');
  978.                 }, unshallow -> {
  979.                     pckOut.writeString(
  980.                             PACKET_UNSHALLOW + unshallow.name() + '\n');
  981.                     unshallowCommits.add(unshallow);
  982.                 }, deepenNots);
  983.                 pckOut.end();
  984.             }

  985.             if (!req.getClientShallowCommits().isEmpty())
  986.                 walk.assumeShallow(req.getClientShallowCommits());
  987.             sendPack = negotiate(req, accumulator, pckOut);
  988.             accumulator.timeNegotiating = Duration
  989.                     .between(negotiateStart, Instant.now()).toMillis();

  990.             if (sendPack && !biDirectionalPipe) {
  991.                 // Ensure the request was fully consumed. Any remaining input must
  992.                 // be a protocol error. If we aren't at EOF the implementation is broken.
  993.                 int eof = rawIn.read();
  994.                 if (0 <= eof) {
  995.                     sendPack = false;
  996.                     throw new CorruptObjectException(MessageFormat.format(
  997.                             JGitText.get().expectedEOFReceived,
  998.                             "\\x" + Integer.toHexString(eof))); //$NON-NLS-1$
  999.                 }
  1000.             }
  1001.         } finally {
  1002.             if (!sendPack && !biDirectionalPipe) {
  1003.                 while (0 < rawIn.skip(2048) || 0 <= rawIn.read()) {
  1004.                     // Discard until EOF.
  1005.                 }
  1006.             }
  1007.             rawOut.stopBuffering();
  1008.         }

  1009.         if (sendPack) {
  1010.             sendPack(accumulator, req, refs == null ? null : refs.values(),
  1011.                     unshallowCommits, deepenNots, pckOut);
  1012.         }
  1013.     }

  1014.     private void lsRefsV2(PacketLineOut pckOut) throws IOException {
  1015.         ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig);
  1016.         LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
  1017.         protocolV2Hook.onLsRefs(req);

  1018.         rawOut.stopBuffering();
  1019.         PacketLineOutRefAdvertiser adv = new PacketLineOutRefAdvertiser(pckOut);
  1020.         adv.init(db);
  1021.         adv.setUseProtocolV2(true);
  1022.         if (req.getPeel()) {
  1023.             adv.setDerefTags(true);
  1024.         }
  1025.         Map<String, Ref> refsToSend = getFilteredRefs(req.getRefPrefixes());
  1026.         if (req.getSymrefs()) {
  1027.             findSymrefs(adv, refsToSend);
  1028.         }

  1029.         adv.send(refsToSend.values());
  1030.         adv.end();
  1031.     }

  1032.     // Resolves ref names from the request's want-ref lines to
  1033.     // object ids, throwing PackProtocolException if any are missing.
  1034.     private Map<String, ObjectId> wantedRefs(FetchV2Request req)
  1035.             throws IOException {
  1036.         Map<String, ObjectId> result = new TreeMap<>();

  1037.         List<String> wanted = req.getWantedRefs();
  1038.         Map<String, Ref> resolved = exactRefs(wanted);

  1039.         for (String refName : wanted) {
  1040.             Ref ref = resolved.get(refName);
  1041.             if (ref == null) {
  1042.                 throw new PackProtocolException(MessageFormat
  1043.                         .format(JGitText.get().invalidRefName, refName));
  1044.             }
  1045.             ObjectId oid = ref.getObjectId();
  1046.             if (oid == null) {
  1047.                 throw new PackProtocolException(MessageFormat
  1048.                         .format(JGitText.get().invalidRefName, refName));
  1049.             }
  1050.             result.put(refName, oid);
  1051.         }
  1052.         return result;
  1053.     }

  1054.     private void fetchV2(PacketLineOut pckOut) throws IOException {
  1055.         // Depending on the requestValidator, #processHaveLines may
  1056.         // require that advertised be set. Set it only in the required
  1057.         // circumstances (to avoid a full ref lookup in the case that
  1058.         // we don't need it).
  1059.         if (requestValidator instanceof TipRequestValidator ||
  1060.                 requestValidator instanceof ReachableCommitTipRequestValidator ||
  1061.                 requestValidator instanceof AnyRequestValidator) {
  1062.             advertised = Collections.emptySet();
  1063.         } else {
  1064.             advertised = refIdSet(getAdvertisedOrDefaultRefs().values());
  1065.         }

  1066.         PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator();
  1067.         Instant negotiateStart = Instant.now();

  1068.         ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig);
  1069.         FetchV2Request req = parser.parseFetchRequest(pckIn);
  1070.         currentRequest = req;
  1071.         rawOut.stopBuffering();

  1072.         protocolV2Hook.onFetch(req);

  1073.         if (req.getSidebandAll()) {
  1074.             pckOut.setUsingSideband(true);
  1075.         }

  1076.         // TODO(ifrade): Refactor to pass around the Request object, instead of
  1077.         // copying data back to class fields
  1078.         List<ObjectId> deepenNots = parseDeepenNots(req.getDeepenNots());

  1079.         Map<String, ObjectId> wantedRefs = wantedRefs(req);
  1080.         // TODO(ifrade): Avoid mutating the parsed request.
  1081.         req.getWantIds().addAll(wantedRefs.values());
  1082.         wantIds = req.getWantIds();

  1083.         boolean sectionSent = false;
  1084.         boolean mayHaveShallow = req.getDepth() != 0
  1085.                 || req.getDeepenSince() != 0
  1086.                 || !req.getDeepenNots().isEmpty();
  1087.         List<ObjectId> shallowCommits = new ArrayList<>();
  1088.         List<ObjectId> unshallowCommits = new ArrayList<>();

  1089.         if (!req.getClientShallowCommits().isEmpty()) {
  1090.             verifyClientShallow(req.getClientShallowCommits());
  1091.         }
  1092.         if (mayHaveShallow) {
  1093.             computeShallowsAndUnshallows(req,
  1094.                     shallowCommit -> shallowCommits.add(shallowCommit),
  1095.                     unshallowCommit -> unshallowCommits.add(unshallowCommit),
  1096.                     deepenNots);
  1097.         }
  1098.         if (!req.getClientShallowCommits().isEmpty())
  1099.             walk.assumeShallow(req.getClientShallowCommits());

  1100.         if (req.wasDoneReceived()) {
  1101.             processHaveLines(
  1102.                     req.getPeerHas(), ObjectId.zeroId(),
  1103.                     new PacketLineOut(NullOutputStream.INSTANCE, false),
  1104.                     accumulator, req.wasWaitForDoneReceived() ? Option.WAIT_FOR_DONE : Option.NONE);
  1105.         } else {
  1106.             pckOut.writeString(
  1107.                     GitProtocolConstants.SECTION_ACKNOWLEDGMENTS + '\n');
  1108.             for (ObjectId id : req.getPeerHas()) {
  1109.                 if (walk.getObjectReader().has(id)) {
  1110.                     pckOut.writeString(PACKET_ACK + id.getName() + '\n');
  1111.                 }
  1112.             }
  1113.             processHaveLines(req.getPeerHas(), ObjectId.zeroId(),
  1114.                     new PacketLineOut(NullOutputStream.INSTANCE, false),
  1115.                     accumulator, Option.NONE);
  1116.             if (!req.wasWaitForDoneReceived() && okToGiveUp()) {
  1117.                 pckOut.writeString("ready\n"); //$NON-NLS-1$
  1118.             } else if (commonBase.isEmpty()) {
  1119.                 pckOut.writeString("NAK\n"); //$NON-NLS-1$
  1120.             }
  1121.             sectionSent = true;
  1122.         }

  1123.         if (req.wasDoneReceived() || (!req.wasWaitForDoneReceived() && okToGiveUp())) {
  1124.             if (mayHaveShallow) {
  1125.                 if (sectionSent)
  1126.                     pckOut.writeDelim();
  1127.                 pckOut.writeString(
  1128.                         GitProtocolConstants.SECTION_SHALLOW_INFO + '\n');
  1129.                 for (ObjectId o : shallowCommits) {
  1130.                     pckOut.writeString(PACKET_SHALLOW + o.getName() + '\n');
  1131.                 }
  1132.                 for (ObjectId o : unshallowCommits) {
  1133.                     pckOut.writeString(PACKET_UNSHALLOW + o.getName() + '\n');
  1134.                 }
  1135.                 sectionSent = true;
  1136.             }

  1137.             if (!wantedRefs.isEmpty()) {
  1138.                 if (sectionSent) {
  1139.                     pckOut.writeDelim();
  1140.                 }
  1141.                 pckOut.writeString("wanted-refs\n"); //$NON-NLS-1$
  1142.                 for (Map.Entry<String, ObjectId> entry :
  1143.                         wantedRefs.entrySet()) {
  1144.                     pckOut.writeString(entry.getValue().getName() + ' ' +
  1145.                             entry.getKey() + '\n');
  1146.                 }
  1147.                 sectionSent = true;
  1148.             }

  1149.             if (sectionSent)
  1150.                 pckOut.writeDelim();
  1151.             if (!pckOut.isUsingSideband()) {
  1152.                 // sendPack will write "packfile\n" for us if sideband-all is used.
  1153.                 // But sideband-all is not used, so we have to write it ourselves.
  1154.                 pckOut.writeString(
  1155.                         GitProtocolConstants.SECTION_PACKFILE + '\n');
  1156.             }

  1157.             accumulator.timeNegotiating = Duration
  1158.                     .between(negotiateStart, Instant.now()).toMillis();

  1159.             sendPack(accumulator,
  1160.                     req,
  1161.                     req.getClientCapabilities().contains(OPTION_INCLUDE_TAG)
  1162.                         ? db.getRefDatabase().getRefsByPrefix(R_TAGS)
  1163.                         : null,
  1164.                     unshallowCommits, deepenNots, pckOut);
  1165.             // sendPack invokes pckOut.end() for us, so we do not
  1166.             // need to invoke it here.
  1167.         } else {
  1168.             // Invoke pckOut.end() by ourselves.
  1169.             pckOut.end();
  1170.         }
  1171.     }

  1172.     private void objectInfo(PacketLineOut pckOut) throws IOException {
  1173.         ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig);
  1174.         ObjectInfoRequest req = parser.parseObjectInfoRequest(pckIn);

  1175.         protocolV2Hook.onObjectInfo(req);

  1176.         ObjectReader or = getRepository().newObjectReader();

  1177.         // Size is the only attribute currently supported.
  1178.         pckOut.writeString("size"); //$NON-NLS-1$

  1179.         for (ObjectId oid : req.getObjectIDs()) {
  1180.             long size;
  1181.             try {
  1182.                 size = or.getObjectSize(oid, ObjectReader.OBJ_ANY);
  1183.             } catch (MissingObjectException e) {
  1184.                 throw new PackProtocolException(MessageFormat
  1185.                         .format(JGitText.get().missingObject, oid.name()), e);
  1186.             }

  1187.             pckOut.writeString(oid.getName() + ' ' + size);
  1188.         }

  1189.         pckOut.end();
  1190.     }

  1191.     /*
  1192.      * Returns true if this is the last command and we should tear down the
  1193.      * connection.
  1194.      */
  1195.     private boolean serveOneCommandV2(PacketLineOut pckOut) throws IOException {
  1196.         String command;
  1197.         try {
  1198.             command = pckIn.readString();
  1199.         } catch (EOFException eof) {
  1200.             /* EOF when awaiting command is fine */
  1201.             return true;
  1202.         }
  1203.         if (PacketLineIn.isEnd(command)) {
  1204.             // A blank request is valid according
  1205.             // to the protocol; do nothing in this
  1206.             // case.
  1207.             return true;
  1208.         }
  1209.         if (command.equals("command=" + COMMAND_LS_REFS)) { //$NON-NLS-1$
  1210.             lsRefsV2(pckOut);
  1211.             return false;
  1212.         }
  1213.         if (command.equals("command=" + COMMAND_FETCH)) { //$NON-NLS-1$
  1214.             fetchV2(pckOut);
  1215.             return false;
  1216.         }
  1217.         if (command.equals("command=" + COMMAND_OBJECT_INFO)) { //$NON-NLS-1$
  1218.             objectInfo(pckOut);
  1219.             return false;
  1220.         }
  1221.         throw new PackProtocolException(MessageFormat
  1222.                 .format(JGitText.get().unknownTransportCommand, command));
  1223.     }

  1224.     @SuppressWarnings("nls")
  1225.     private List<String> getV2CapabilityAdvertisement() {
  1226.         ArrayList<String> caps = new ArrayList<>();
  1227.         caps.add("version 2");
  1228.         caps.add(COMMAND_LS_REFS);
  1229.         boolean advertiseRefInWant = transferConfig.isAllowRefInWant()
  1230.                 && db.getConfig().getBoolean("uploadpack", null,
  1231.                         "advertiserefinwant", true);
  1232.         caps.add(COMMAND_FETCH + '='
  1233.                 + (transferConfig.isAllowFilter() ? OPTION_FILTER + ' ' : "")
  1234.                 + (advertiseRefInWant ? CAPABILITY_REF_IN_WANT + ' ' : "")
  1235.                 + (transferConfig.isAdvertiseSidebandAll()
  1236.                         ? OPTION_SIDEBAND_ALL + ' '
  1237.                         : "")
  1238.                 + (cachedPackUriProvider != null ? "packfile-uris " : "")
  1239.                 + (transferConfig.isAdvertiseWaitForDone()
  1240.                         ? OPTION_WAIT_FOR_DONE + ' '
  1241.                         : "")
  1242.                 + OPTION_SHALLOW);
  1243.         caps.add(CAPABILITY_SERVER_OPTION);
  1244.         if (transferConfig.isAllowReceiveClientSID()) {
  1245.             caps.add(OPTION_SESSION_ID);
  1246.         }

  1247.         return caps;
  1248.     }

  1249.     private void serviceV2(PacketLineOut pckOut) throws IOException {
  1250.         if (biDirectionalPipe) {
  1251.             // Just like in service(), the capability advertisement
  1252.             // is sent only if this is a bidirectional pipe. (If
  1253.             // not, the client is expected to call
  1254.             // sendAdvertisedRefs() on its own.)
  1255.             protocolV2Hook
  1256.                     .onCapabilities(CapabilitiesV2Request.builder().build());
  1257.             for (String s : getV2CapabilityAdvertisement()) {
  1258.                 pckOut.writeString(s + '\n');
  1259.             }
  1260.             pckOut.end();

  1261.             while (!serveOneCommandV2(pckOut)) {
  1262.                 // Repeat until an empty command or EOF.
  1263.             }
  1264.             return;
  1265.         }

  1266.         try {
  1267.             serveOneCommandV2(pckOut);
  1268.         } finally {
  1269.             while (0 < rawIn.skip(2048) || 0 <= rawIn.read()) {
  1270.                 // Discard until EOF.
  1271.             }
  1272.             rawOut.stopBuffering();
  1273.         }
  1274.     }

  1275.     private static Set<ObjectId> refIdSet(Collection<Ref> refs) {
  1276.         Set<ObjectId> ids = new HashSet<>(refs.size());
  1277.         for (Ref ref : refs) {
  1278.             ObjectId id = ref.getObjectId();
  1279.             if (id != null) {
  1280.                 ids.add(id);
  1281.             }
  1282.             id = ref.getPeeledObjectId();
  1283.             if (id != null) {
  1284.                 ids.add(id);
  1285.             }
  1286.         }
  1287.         return ids;
  1288.     }

  1289.     /*
  1290.      * Determines what object ids must be marked as shallow or unshallow for the
  1291.      * client.
  1292.      */
  1293.     private void computeShallowsAndUnshallows(FetchRequest req,
  1294.             IOConsumer<ObjectId> shallowFunc,
  1295.             IOConsumer<ObjectId> unshallowFunc,
  1296.             List<ObjectId> deepenNots)
  1297.             throws IOException {
  1298.         if (req.getClientCapabilities().contains(OPTION_DEEPEN_RELATIVE)) {
  1299.             // TODO(jonathantanmy): Implement deepen-relative
  1300.             throw new UnsupportedOperationException();
  1301.         }

  1302.         int walkDepth = req.getDepth() == 0 ? Integer.MAX_VALUE
  1303.                 : req.getDepth() - 1;
  1304.         try (DepthWalk.RevWalk depthWalk = new DepthWalk.RevWalk(
  1305.                 walk.getObjectReader(), walkDepth)) {

  1306.             depthWalk.setDeepenSince(req.getDeepenSince());

  1307.             // Find all the commits which will be shallow
  1308.             for (ObjectId o : req.getWantIds()) {
  1309.                 try {
  1310.                     depthWalk.markRoot(depthWalk.parseCommit(o));
  1311.                 } catch (IncorrectObjectTypeException notCommit) {
  1312.                     // Ignore non-commits in this loop.
  1313.                 }
  1314.             }

  1315.             depthWalk.setDeepenNots(deepenNots);

  1316.             RevCommit o;
  1317.             boolean atLeastOne = false;
  1318.             while ((o = depthWalk.next()) != null) {
  1319.                 DepthWalk.Commit c = (DepthWalk.Commit) o;
  1320.                 atLeastOne = true;

  1321.                 boolean isBoundary = (c.getDepth() == walkDepth) || c.isBoundary();

  1322.                 // Commits at the boundary which aren't already shallow in
  1323.                 // the client need to be marked as such
  1324.                 if (isBoundary && !req.getClientShallowCommits().contains(c)) {
  1325.                     shallowFunc.accept(c.copy());
  1326.                 }

  1327.                 // Commits not on the boundary which are shallow in the client
  1328.                 // need to become unshallowed
  1329.                 if (!isBoundary && req.getClientShallowCommits().remove(c)) {
  1330.                     unshallowFunc.accept(c.copy());
  1331.                 }
  1332.             }
  1333.             if (!atLeastOne) {
  1334.                 throw new PackProtocolException(
  1335.                     JGitText.get().noCommitsSelectedForShallow);
  1336.             }
  1337.         }
  1338.     }

  1339.     /*
  1340.      * Verify all shallow lines refer to commits
  1341.      *
  1342.      * It can mutate the input set (removing missing object ids from it)
  1343.      */
  1344.     private void verifyClientShallow(Set<ObjectId> shallowCommits)
  1345.             throws IOException, PackProtocolException {
  1346.         AsyncRevObjectQueue q = walk.parseAny(shallowCommits, true);
  1347.         try {
  1348.             for (;;) {
  1349.                 try {
  1350.                     // Shallow objects named by the client must be commits.
  1351.                     RevObject o = q.next();
  1352.                     if (o == null) {
  1353.                         break;
  1354.                     }
  1355.                     if (!(o instanceof RevCommit)) {
  1356.                         throw new PackProtocolException(
  1357.                             MessageFormat.format(
  1358.                                 JGitText.get().invalidShallowObject,
  1359.                                 o.name()));
  1360.                     }
  1361.                 } catch (MissingObjectException notCommit) {
  1362.                     // shallow objects not known at the server are ignored
  1363.                     // by git-core upload-pack, match that behavior.
  1364.                     shallowCommits.remove(notCommit.getObjectId());
  1365.                     continue;
  1366.                 }
  1367.             }
  1368.         } finally {
  1369.             q.release();
  1370.         }
  1371.     }

  1372.     /**
  1373.      * Generate an advertisement of available refs and capabilities.
  1374.      *
  1375.      * @param adv
  1376.      *            the advertisement formatter.
  1377.      * @throws java.io.IOException
  1378.      *            the formatter failed to write an advertisement.
  1379.      * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
  1380.      *            the hook denied advertisement.
  1381.      */
  1382.     public void sendAdvertisedRefs(RefAdvertiser adv) throws IOException,
  1383.             ServiceMayNotContinueException {
  1384.         sendAdvertisedRefs(adv, null);
  1385.     }

  1386.     /**
  1387.      * Generate an advertisement of available refs and capabilities.
  1388.      *
  1389.      * @param adv
  1390.      *            the advertisement formatter.
  1391.      * @param serviceName
  1392.      *            if not null, also output "# service=serviceName" followed by a
  1393.      *            flush packet before the advertisement. This is required
  1394.      *            in v0 of the HTTP protocol, described in Git's
  1395.      *            Documentation/technical/http-protocol.txt.
  1396.      * @throws java.io.IOException
  1397.      *            the formatter failed to write an advertisement.
  1398.      * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
  1399.      *            the hook denied advertisement.
  1400.      * @since 5.0
  1401.      */
  1402.     public void sendAdvertisedRefs(RefAdvertiser adv,
  1403.             @Nullable String serviceName) throws IOException,
  1404.             ServiceMayNotContinueException {
  1405.         if (useProtocolV2()) {
  1406.             // The equivalent in v2 is only the capabilities
  1407.             // advertisement.
  1408.             protocolV2Hook
  1409.                     .onCapabilities(CapabilitiesV2Request.builder().build());
  1410.             for (String s : getV2CapabilityAdvertisement()) {
  1411.                 adv.writeOne(s);
  1412.             }
  1413.             adv.end();
  1414.             return;
  1415.         }

  1416.         Map<String, Ref> advertisedOrDefaultRefs = getAdvertisedOrDefaultRefs();

  1417.         if (serviceName != null) {
  1418.             adv.writeOne("# service=" + serviceName + '\n'); //$NON-NLS-1$
  1419.             adv.end();
  1420.         }
  1421.         adv.init(db);
  1422.         adv.advertiseCapability(OPTION_INCLUDE_TAG);
  1423.         adv.advertiseCapability(OPTION_MULTI_ACK_DETAILED);
  1424.         adv.advertiseCapability(OPTION_MULTI_ACK);
  1425.         adv.advertiseCapability(OPTION_OFS_DELTA);
  1426.         adv.advertiseCapability(OPTION_SIDE_BAND);
  1427.         adv.advertiseCapability(OPTION_SIDE_BAND_64K);
  1428.         adv.advertiseCapability(OPTION_THIN_PACK);
  1429.         adv.advertiseCapability(OPTION_NO_PROGRESS);
  1430.         adv.advertiseCapability(OPTION_SHALLOW);
  1431.         if (!biDirectionalPipe)
  1432.             adv.advertiseCapability(OPTION_NO_DONE);
  1433.         RequestPolicy policy = getRequestPolicy();
  1434.         if (policy == RequestPolicy.TIP
  1435.                 || policy == RequestPolicy.REACHABLE_COMMIT_TIP
  1436.                 || policy == null)
  1437.             adv.advertiseCapability(OPTION_ALLOW_TIP_SHA1_IN_WANT);
  1438.         if (policy == RequestPolicy.REACHABLE_COMMIT
  1439.                 || policy == RequestPolicy.REACHABLE_COMMIT_TIP
  1440.                 || policy == null)
  1441.             adv.advertiseCapability(OPTION_ALLOW_REACHABLE_SHA1_IN_WANT);
  1442.         adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
  1443.         if (transferConfig.isAllowFilter()) {
  1444.             adv.advertiseCapability(OPTION_FILTER);
  1445.         }
  1446.         adv.setDerefTags(true);
  1447.         findSymrefs(adv, advertisedOrDefaultRefs);
  1448.         advertised = adv.send(advertisedOrDefaultRefs.values());

  1449.         if (adv.isEmpty())
  1450.             adv.advertiseId(ObjectId.zeroId(), "capabilities^{}"); //$NON-NLS-1$
  1451.         adv.end();
  1452.     }

  1453.     /**
  1454.      * Send a message to the client, if it supports receiving them.
  1455.      * <p>
  1456.      * If the client doesn't support receiving messages, the message will be
  1457.      * discarded, with no other indication to the caller or to the client.
  1458.      *
  1459.      * @param what
  1460.      *            string describing the problem identified by the hook. The
  1461.      *            string must not end with an LF, and must not contain an LF.
  1462.      * @since 3.1
  1463.      */
  1464.     public void sendMessage(String what) {
  1465.         try {
  1466.             msgOut.write(Constants.encode(what + '\n'));
  1467.         } catch (IOException e) {
  1468.             // Ignore write failures.
  1469.         }
  1470.     }

  1471.     /**
  1472.      * Get an underlying stream for sending messages to the client
  1473.      *
  1474.      * @return an underlying stream for sending messages to the client, or null.
  1475.      * @since 3.1
  1476.      */
  1477.     public OutputStream getMessageOutputStream() {
  1478.         return msgOut;
  1479.     }

  1480.     /**
  1481.      * Returns the clone/fetch depth. Valid only after calling recvWants(). A
  1482.      * depth of 1 means return only the wants.
  1483.      *
  1484.      * @return the depth requested by the client, or 0 if unbounded.
  1485.      * @since 4.0
  1486.      */
  1487.     public int getDepth() {
  1488.         if (currentRequest == null)
  1489.             throw new RequestNotYetReadException();
  1490.         return currentRequest.getDepth();
  1491.     }

  1492.     /**
  1493.      * Deprecated synonym for {@code getFilterSpec().getBlobLimit()}.
  1494.      *
  1495.      * @return filter blob limit requested by the client, or -1 if no limit
  1496.      * @since 5.3
  1497.      * @deprecated Use {@link #getFilterSpec()} instead
  1498.      */
  1499.     @Deprecated
  1500.     public final long getFilterBlobLimit() {
  1501.         return getFilterSpec().getBlobLimit();
  1502.     }

  1503.     /**
  1504.      * Returns the filter spec for the current request. Valid only after
  1505.      * calling recvWants(). This may be a no-op filter spec, but it won't be
  1506.      * null.
  1507.      *
  1508.      * @return filter requested by the client
  1509.      * @since 5.4
  1510.      */
  1511.     public final FilterSpec getFilterSpec() {
  1512.         if (currentRequest == null) {
  1513.             throw new RequestNotYetReadException();
  1514.         }
  1515.         return currentRequest.getFilterSpec();
  1516.     }

  1517.     /**
  1518.      * Get the user agent of the client.
  1519.      * <p>
  1520.      * If the client is new enough to use {@code agent=} capability that value
  1521.      * will be returned. Older HTTP clients may also supply their version using
  1522.      * the HTTP {@code User-Agent} header. The capability overrides the HTTP
  1523.      * header if both are available.
  1524.      * <p>
  1525.      * When an HTTP request has been received this method returns the HTTP
  1526.      * {@code User-Agent} header value until capabilities have been parsed.
  1527.      *
  1528.      * @return user agent supplied by the client. Available only if the client
  1529.      *         is new enough to advertise its user agent.
  1530.      * @since 4.0
  1531.      */
  1532.     public String getPeerUserAgent() {
  1533.         if (currentRequest != null && currentRequest.getAgent() != null) {
  1534.             return currentRequest.getAgent();
  1535.         }

  1536.         return userAgent;
  1537.     }

  1538.     /**
  1539.      * Get the session ID if received from the client.
  1540.      *
  1541.      * @return The session ID if it has been received from the client.
  1542.      * @since 6.4
  1543.      */
  1544.     @Nullable
  1545.     public String getClientSID() {
  1546.         if (currentRequest == null) {
  1547.             return null;
  1548.         }

  1549.         return currentRequest.getClientSID();
  1550.     }

  1551.     private boolean negotiate(FetchRequest req,
  1552.             PackStatistics.Accumulator accumulator,
  1553.             PacketLineOut pckOut)
  1554.             throws IOException {
  1555.         okToGiveUp = Boolean.FALSE;

  1556.         ObjectId last = ObjectId.zeroId();
  1557.         List<ObjectId> peerHas = new ArrayList<>(64);
  1558.         for (;;) {
  1559.             String line;
  1560.             try {
  1561.                 line = pckIn.readString();
  1562.             } catch (EOFException eof) {
  1563.                 // EOF on stateless RPC (aka smart HTTP) and non-shallow request
  1564.                 // means the client asked for the updated shallow/unshallow data,
  1565.                 // disconnected, and will try another request with actual want/have.
  1566.                 // Don't report the EOF here, its a bug in the protocol that the client
  1567.                 // just disconnects without sending an END.
  1568.                 if (!biDirectionalPipe && req.getDepth() > 0)
  1569.                     return false;
  1570.                 throw eof;
  1571.             }

  1572.             if (PacketLineIn.isEnd(line)) {
  1573.                 last = processHaveLines(peerHas, last, pckOut, accumulator, Option.NONE);
  1574.                 if (commonBase.isEmpty() || multiAck != MultiAck.OFF)
  1575.                     pckOut.writeString("NAK\n"); //$NON-NLS-1$
  1576.                 if (noDone && sentReady) {
  1577.                     pckOut.writeString(PACKET_ACK + last.name() + '\n');
  1578.                     return true;
  1579.                 }
  1580.                 if (!biDirectionalPipe)
  1581.                     return false;
  1582.                 pckOut.flush();

  1583.             } else if (line.startsWith(PACKET_HAVE)
  1584.                     && line.length() == PACKET_HAVE.length() + 40) {
  1585.                 peerHas.add(ObjectId
  1586.                         .fromString(line.substring(PACKET_HAVE.length())));
  1587.                 accumulator.haves++;
  1588.             } else if (line.equals(PACKET_DONE)) {
  1589.                 last = processHaveLines(peerHas, last, pckOut, accumulator, Option.NONE);

  1590.                 if (commonBase.isEmpty())
  1591.                     pckOut.writeString("NAK\n"); //$NON-NLS-1$

  1592.                 else if (multiAck != MultiAck.OFF)
  1593.                     pckOut.writeString(PACKET_ACK + last.name() + '\n');

  1594.                 return true;

  1595.             } else {
  1596.                 throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "have", line)); //$NON-NLS-1$
  1597.             }
  1598.         }
  1599.     }

  1600.     private enum Option {
  1601.         WAIT_FOR_DONE,
  1602.         NONE;
  1603.     }

  1604.     private ObjectId processHaveLines(List<ObjectId> peerHas, ObjectId last,
  1605.             PacketLineOut out, PackStatistics.Accumulator accumulator,
  1606.             Option option)
  1607.             throws IOException {
  1608.         preUploadHook.onBeginNegotiateRound(this, wantIds, peerHas.size());
  1609.         if (wantAll.isEmpty() && !wantIds.isEmpty())
  1610.             parseWants(accumulator);
  1611.         if (peerHas.isEmpty())
  1612.             return last;

  1613.         sentReady = false;
  1614.         int haveCnt = 0;
  1615.         walk.getObjectReader().setAvoidUnreachableObjects(true);
  1616.         AsyncRevObjectQueue q = walk.parseAny(peerHas, false);
  1617.         try {
  1618.             for (;;) {
  1619.                 RevObject obj;
  1620.                 try {
  1621.                     obj = q.next();
  1622.                 } catch (MissingObjectException notFound) {
  1623.                     continue;
  1624.                 }
  1625.                 if (obj == null)
  1626.                     break;

  1627.                 last = obj;
  1628.                 haveCnt++;

  1629.                 if (obj instanceof RevCommit) {
  1630.                     RevCommit c = (RevCommit) obj;
  1631.                     if (oldestTime == 0 || c.getCommitTime() < oldestTime)
  1632.                         oldestTime = c.getCommitTime();
  1633.                 }

  1634.                 if (obj.has(PEER_HAS))
  1635.                     continue;

  1636.                 obj.add(PEER_HAS);
  1637.                 if (obj instanceof RevCommit)
  1638.                     ((RevCommit) obj).carry(PEER_HAS);
  1639.                 addCommonBase(obj);

  1640.                 // If both sides have the same object; let the client know.
  1641.                 //
  1642.                 switch (multiAck) {
  1643.                 case OFF:
  1644.                     if (commonBase.size() == 1) {
  1645.                         out.writeString(PACKET_ACK + obj.name() + '\n');
  1646.                     }
  1647.                     break;
  1648.                 case CONTINUE:
  1649.                     out.writeString(PACKET_ACK + obj.name() + " continue\n"); //$NON-NLS-1$
  1650.                     break;
  1651.                 case DETAILED:
  1652.                     out.writeString(PACKET_ACK + obj.name() + " common\n"); //$NON-NLS-1$
  1653.                     break;
  1654.                 }
  1655.             }
  1656.         } finally {
  1657.             q.release();
  1658.             walk.getObjectReader().setAvoidUnreachableObjects(false);
  1659.         }

  1660.         int missCnt = peerHas.size() - haveCnt;

  1661.         // If we don't have one of the objects but we're also willing to
  1662.         // create a pack at this point, let the client know so it stops
  1663.         // telling us about its history.
  1664.         //
  1665.         if (option != Option.WAIT_FOR_DONE) {
  1666.             sentReady = shouldGiveUp(peerHas, out, missCnt);
  1667.         }

  1668.         preUploadHook.onEndNegotiateRound(this, wantAll, haveCnt, missCnt, sentReady);
  1669.         peerHas.clear();
  1670.         return last;
  1671.     }

  1672.     private boolean shouldGiveUp(List<ObjectId> peerHas, PacketLineOut out, int missCnt)
  1673.             throws IOException {
  1674.         boolean readySent = false;
  1675.         boolean didOkToGiveUp = false;
  1676.         if (0 < missCnt) {
  1677.             for (int i = peerHas.size() - 1; i >= 0; i--) {
  1678.                 ObjectId id = peerHas.get(i);
  1679.                 if (walk.lookupOrNull(id) == null) {
  1680.                     didOkToGiveUp = true;
  1681.                     if (okToGiveUp()) {
  1682.                         switch (multiAck) {
  1683.                         case OFF:
  1684.                             break;
  1685.                         case CONTINUE:
  1686.                             out.writeString(
  1687.                                     PACKET_ACK + id.name() + " continue\n"); //$NON-NLS-1$
  1688.                             break;
  1689.                         case DETAILED:
  1690.                             out.writeString(
  1691.                                     PACKET_ACK + id.name() + " ready\n"); //$NON-NLS-1$
  1692.                             readySent = true;
  1693.                             break;
  1694.                         }
  1695.                     }
  1696.                     break;
  1697.                 }
  1698.             }
  1699.         }

  1700.         if (multiAck == MultiAck.DETAILED && !didOkToGiveUp
  1701.                 && okToGiveUp()) {
  1702.             ObjectId id = peerHas.get(peerHas.size() - 1);
  1703.             out.writeString(PACKET_ACK + id.name() + " ready\n"); //$NON-NLS-1$
  1704.             readySent = true;
  1705.         }

  1706.         return readySent;
  1707.     }

  1708.     private void parseWants(PackStatistics.Accumulator accumulator) throws IOException {
  1709.         List<ObjectId> notAdvertisedWants = null;
  1710.         for (ObjectId obj : wantIds) {
  1711.             if (!advertised.contains(obj)) {
  1712.                 if (notAdvertisedWants == null)
  1713.                     notAdvertisedWants = new ArrayList<>();
  1714.                 notAdvertisedWants.add(obj);
  1715.             }
  1716.         }
  1717.         if (notAdvertisedWants != null) {
  1718.             accumulator.notAdvertisedWants = notAdvertisedWants.size();

  1719.             Instant startReachabilityChecking = Instant.now();

  1720.             requestValidator.checkWants(this, notAdvertisedWants);

  1721.             accumulator.reachabilityCheckDuration = Duration
  1722.                     .between(startReachabilityChecking, Instant.now())
  1723.                     .toMillis();
  1724.         }

  1725.         AsyncRevObjectQueue q = walk.parseAny(wantIds, true);
  1726.         try {
  1727.             RevObject obj;
  1728.             while ((obj = q.next()) != null) {
  1729.                 want(obj);

  1730.                 if (!(obj instanceof RevCommit))
  1731.                     obj.add(SATISFIED);
  1732.                 if (obj instanceof RevTag) {
  1733.                     obj = walk.peel(obj);
  1734.                     if (obj instanceof RevCommit)
  1735.                         want(obj);
  1736.                 }
  1737.             }
  1738.             wantIds.clear();
  1739.         } catch (MissingObjectException notFound) {
  1740.             throw new WantNotValidException(notFound.getObjectId(), notFound);
  1741.         } finally {
  1742.             q.release();
  1743.         }
  1744.     }

  1745.     private void want(RevObject obj) {
  1746.         if (!obj.has(WANT)) {
  1747.             obj.add(WANT);
  1748.             wantAll.add(obj);
  1749.         }
  1750.     }

  1751.     /**
  1752.      * Validator corresponding to {@link RequestPolicy#ADVERTISED}.
  1753.      *
  1754.      * @since 3.1
  1755.      */
  1756.     public static final class AdvertisedRequestValidator
  1757.             implements RequestValidator {
  1758.         @Override
  1759.         public void checkWants(UploadPack up, List<ObjectId> wants)
  1760.                 throws PackProtocolException, IOException {
  1761.             if (!up.isBiDirectionalPipe())
  1762.                 new ReachableCommitRequestValidator().checkWants(up, wants);
  1763.             else if (!wants.isEmpty())
  1764.                 throw new WantNotValidException(wants.iterator().next());
  1765.         }
  1766.     }

  1767.     /**
  1768.      * Validator corresponding to {@link RequestPolicy#REACHABLE_COMMIT}.
  1769.      *
  1770.      * @since 3.1
  1771.      */
  1772.     public static final class ReachableCommitRequestValidator
  1773.             implements RequestValidator {
  1774.         @Override
  1775.         public void checkWants(UploadPack up, List<ObjectId> wants)
  1776.                 throws PackProtocolException, IOException {
  1777.             checkNotAdvertisedWants(up, wants, up.getAdvertisedRefs().values());
  1778.         }
  1779.     }

  1780.     /**
  1781.      * Validator corresponding to {@link RequestPolicy#TIP}.
  1782.      *
  1783.      * @since 3.1
  1784.      */
  1785.     public static final class TipRequestValidator implements RequestValidator {
  1786.         @Override
  1787.         public void checkWants(UploadPack up, List<ObjectId> wants)
  1788.                 throws PackProtocolException, IOException {
  1789.             if (!up.isBiDirectionalPipe())
  1790.                 new ReachableCommitTipRequestValidator().checkWants(up, wants);
  1791.             else if (!wants.isEmpty()) {
  1792.                 Set<ObjectId> refIds =
  1793.                         refIdSet(up.getRepository().getRefDatabase().getRefs());
  1794.                 for (ObjectId obj : wants) {
  1795.                     if (!refIds.contains(obj))
  1796.                         throw new WantNotValidException(obj);
  1797.                 }
  1798.             }
  1799.         }
  1800.     }

  1801.     /**
  1802.      * Validator corresponding to {@link RequestPolicy#REACHABLE_COMMIT_TIP}.
  1803.      *
  1804.      * @since 3.1
  1805.      */
  1806.     public static final class ReachableCommitTipRequestValidator
  1807.             implements RequestValidator {
  1808.         @Override
  1809.         public void checkWants(UploadPack up, List<ObjectId> wants)
  1810.                 throws PackProtocolException, IOException {
  1811.             checkNotAdvertisedWants(up, wants,
  1812.                     up.getRepository().getRefDatabase().getRefs());
  1813.         }
  1814.     }

  1815.     /**
  1816.      * Validator corresponding to {@link RequestPolicy#ANY}.
  1817.      *
  1818.      * @since 3.1
  1819.      */
  1820.     public static final class AnyRequestValidator implements RequestValidator {
  1821.         @Override
  1822.         public void checkWants(UploadPack up, List<ObjectId> wants)
  1823.                 throws PackProtocolException, IOException {
  1824.             // All requests are valid.
  1825.         }
  1826.     }

  1827.     private static void checkNotAdvertisedWants(UploadPack up,
  1828.             List<ObjectId> notAdvertisedWants, Collection<Ref> visibleRefs)
  1829.             throws IOException {

  1830.         ObjectReader reader = up.getRevWalk().getObjectReader();
  1831.         Set<ObjectId> directlyVisibleObjects = refIdSet(visibleRefs);
  1832.         List<ObjectId> nonTipWants = notAdvertisedWants.stream()
  1833.                 .filter(not(directlyVisibleObjects::contains))
  1834.                 .collect(Collectors.toList());

  1835.         try (RevWalk walk = new RevWalk(reader)) {
  1836.             walk.setRetainBody(false);
  1837.             // Missing "wants" throw exception here
  1838.             List<RevObject> wantsAsObjs = objectIdsToRevObjects(walk,
  1839.                     nonTipWants);
  1840.             List<RevCommit> wantsAsCommits = wantsAsObjs.stream()
  1841.                     .filter(obj -> obj instanceof RevCommit)
  1842.                     .map(obj -> (RevCommit) obj)
  1843.                     .collect(Collectors.toList());
  1844.             boolean allWantsAreCommits = wantsAsObjs.size() == wantsAsCommits
  1845.                     .size();
  1846.             boolean repoHasBitmaps = reader.getBitmapIndex() != null;

  1847.             if (!allWantsAreCommits) {
  1848.                 if (!repoHasBitmaps && !up.transferConfig.isAllowFilter()) {
  1849.                     // Checking unadvertised non-commits without bitmaps
  1850.                     // requires an expensive manual walk. Use allowFilter as an
  1851.                     // indication that the server operator is willing to pay
  1852.                     // this cost. Reject the request otherwise.
  1853.                     RevObject nonCommit = wantsAsObjs
  1854.                             .stream()
  1855.                             .filter(obj -> !(obj instanceof RevCommit))
  1856.                             .limit(1)
  1857.                             .collect(Collectors.toList()).get(0);
  1858.                     throw new WantNotValidException(nonCommit,
  1859.                             new Exception("Cannot walk without bitmaps")); //$NON-NLS-1$
  1860.                 }

  1861.                 try (ObjectWalk objWalk = walk.toObjectWalkWithSameObjects()) {
  1862.                     Stream<RevObject> startersAsObjs = importantRefsFirst(visibleRefs)
  1863.                             .map(UploadPack::refToObjectId)
  1864.                             .map(objId -> objectIdToRevObject(objWalk, objId))
  1865.                             .filter(Objects::nonNull); // Ignore missing tips

  1866.                     ObjectReachabilityChecker reachabilityChecker = reader
  1867.                             .createObjectReachabilityChecker(objWalk);
  1868.                     Optional<RevObject> unreachable = reachabilityChecker
  1869.                             .areAllReachable(wantsAsObjs, startersAsObjs);
  1870.                     if (unreachable.isPresent()) {
  1871.                         if (!repoHasBitmaps) {
  1872.                             throw new WantNotValidException(
  1873.                                     unreachable.get(), new Exception(
  1874.                                             "Retry with bitmaps enabled")); //$NON-NLS-1$
  1875.                         }
  1876.                         throw new WantNotValidException(unreachable.get());
  1877.                     }
  1878.                 }
  1879.                 return;
  1880.             }

  1881.             // All wants are commits, we can use ReachabilityChecker
  1882.             ReachabilityChecker reachabilityChecker = reader
  1883.                     .createReachabilityChecker(walk);

  1884.             Stream<RevCommit> reachableCommits = importantRefsFirst(visibleRefs)
  1885.                     .map(UploadPack::refToObjectId)
  1886.                     .map(objId -> objectIdToRevCommit(walk, objId))
  1887.                     .filter(Objects::nonNull); // Ignore missing tips

  1888.             Optional<RevCommit> unreachable = reachabilityChecker
  1889.                     .areAllReachable(wantsAsCommits, reachableCommits);
  1890.             if (unreachable.isPresent()) {
  1891.                 throw new WantNotValidException(unreachable.get());
  1892.             }

  1893.         } catch (MissingObjectException notFound) {
  1894.             throw new WantNotValidException(notFound.getObjectId(), notFound);
  1895.         }
  1896.     }

  1897.     private static <T> Predicate<T> not(Predicate<T> t) {
  1898.         return t.negate();
  1899.     }

  1900.     static Stream<Ref> importantRefsFirst(
  1901.             Collection<Ref> visibleRefs) {
  1902.         Predicate<Ref> startsWithRefsHeads = ref -> ref.getName()
  1903.                 .startsWith(Constants.R_HEADS);
  1904.         Predicate<Ref> startsWithRefsTags = ref -> ref.getName()
  1905.                 .startsWith(Constants.R_TAGS);
  1906.         Predicate<Ref> allOther = ref -> !startsWithRefsHeads.test(ref)
  1907.                 && !startsWithRefsTags.test(ref);

  1908.         return Stream.concat(
  1909.                 visibleRefs.stream().filter(startsWithRefsHeads),
  1910.                 Stream.concat(
  1911.                         visibleRefs.stream().filter(startsWithRefsTags),
  1912.                         visibleRefs.stream().filter(allOther)));
  1913.     }

  1914.     private static ObjectId refToObjectId(Ref ref) {
  1915.         return ref.getObjectId() != null ? ref.getObjectId()
  1916.                 : ref.getPeeledObjectId();
  1917.     }

  1918.     /**
  1919.      * Translate an object id to a RevCommit.
  1920.      *
  1921.      * @param walk
  1922.      *            walk on the relevant object storae
  1923.      * @param objectId
  1924.      *            Object Id
  1925.      * @return RevCommit instance or null if the object is missing
  1926.      */
  1927.     @Nullable
  1928.     private static RevCommit objectIdToRevCommit(RevWalk walk,
  1929.             ObjectId objectId) {
  1930.         if (objectId == null) {
  1931.             return null;
  1932.         }

  1933.         try {
  1934.             return walk.parseCommit(objectId);
  1935.         } catch (IOException e) {
  1936.             return null;
  1937.         }
  1938.     }

  1939.     /**
  1940.      * Translate an object id to a RevObject.
  1941.      *
  1942.      * @param walk
  1943.      *            walk on the relevant object storage
  1944.      * @param objectId
  1945.      *            Object Id
  1946.      * @return RevObject instance or null if the object is missing
  1947.      */
  1948.     @Nullable
  1949.     private static RevObject objectIdToRevObject(RevWalk walk,
  1950.             ObjectId objectId) {
  1951.         if (objectId == null) {
  1952.             return null;
  1953.         }

  1954.         try {
  1955.             return walk.parseAny(objectId);
  1956.         } catch (IOException e) {
  1957.             return null;
  1958.         }
  1959.     }

  1960.     // Resolve the ObjectIds into RevObjects. Any missing object raises an
  1961.     // exception
  1962.     private static List<RevObject> objectIdsToRevObjects(RevWalk walk,
  1963.             Iterable<ObjectId> objectIds)
  1964.             throws MissingObjectException, IOException {
  1965.         List<RevObject> result = new ArrayList<>();
  1966.         for (ObjectId objectId : objectIds) {
  1967.             result.add(walk.parseAny(objectId));
  1968.         }
  1969.         return result;
  1970.     }

  1971.     private void addCommonBase(RevObject o) {
  1972.         if (!o.has(COMMON)) {
  1973.             o.add(COMMON);
  1974.             commonBase.add(o);
  1975.             okToGiveUp = null;
  1976.         }
  1977.     }

  1978.     private boolean okToGiveUp() throws PackProtocolException {
  1979.         if (okToGiveUp == null)
  1980.             okToGiveUp = Boolean.valueOf(okToGiveUpImp());
  1981.         return okToGiveUp.booleanValue();
  1982.     }

  1983.     private boolean okToGiveUpImp() throws PackProtocolException {
  1984.         if (commonBase.isEmpty())
  1985.             return false;

  1986.         try {
  1987.             for (RevObject obj : wantAll) {
  1988.                 if (!wantSatisfied(obj))
  1989.                     return false;
  1990.             }
  1991.             return true;
  1992.         } catch (IOException e) {
  1993.             throw new PackProtocolException(JGitText.get().internalRevisionError, e);
  1994.         }
  1995.     }

  1996.     private boolean wantSatisfied(RevObject want) throws IOException {
  1997.         if (want.has(SATISFIED))
  1998.             return true;

  1999.         if (((RevCommit) want).getParentCount() == 0) {
  2000.             want.add(SATISFIED);
  2001.             return true;
  2002.         }

  2003.         walk.resetRetain(SAVE);
  2004.         walk.markStart((RevCommit) want);
  2005.         if (oldestTime != 0)
  2006.             walk.setRevFilter(CommitTimeRevFilter.after(oldestTime * 1000L));
  2007.         for (;;) {
  2008.             final RevCommit c = walk.next();
  2009.             if (c == null)
  2010.                 break;
  2011.             if (c.has(PEER_HAS)) {
  2012.                 addCommonBase(c);
  2013.                 want.add(SATISFIED);
  2014.                 return true;
  2015.             }
  2016.         }
  2017.         return false;
  2018.     }

  2019.     /**
  2020.      * Send the requested objects to the client.
  2021.      *
  2022.      * @param accumulator
  2023.      *            where to write statistics about the content of the pack.
  2024.      * @param req
  2025.      *            request in process
  2026.      * @param allTags
  2027.      *            refs to search for annotated tags to include in the pack if
  2028.      *            the {@link #OPTION_INCLUDE_TAG} capability was requested.
  2029.      * @param unshallowCommits
  2030.      *            shallow commits on the client that are now becoming unshallow
  2031.      * @param deepenNots
  2032.      *            objects that the client specified using --shallow-exclude
  2033.      * @param pckOut
  2034.      *            output writer
  2035.      * @throws IOException
  2036.      *             if an error occurred while generating or writing the pack.
  2037.      */
  2038.     private void sendPack(PackStatistics.Accumulator accumulator,
  2039.             FetchRequest req,
  2040.             @Nullable Collection<Ref> allTags,
  2041.             List<ObjectId> unshallowCommits,
  2042.             List<ObjectId> deepenNots,
  2043.             PacketLineOut pckOut) throws IOException {
  2044.         Set<String> caps = req.getClientCapabilities();
  2045.         boolean sideband = caps.contains(OPTION_SIDE_BAND)
  2046.                 || caps.contains(OPTION_SIDE_BAND_64K);

  2047.         if (sideband) {
  2048.             errOut = new SideBandErrorWriter();

  2049.             int bufsz = SideBandOutputStream.SMALL_BUF;
  2050.             if (req.getClientCapabilities().contains(OPTION_SIDE_BAND_64K)) {
  2051.                 bufsz = SideBandOutputStream.MAX_BUF;
  2052.             }
  2053.             OutputStream packOut = new SideBandOutputStream(
  2054.                     SideBandOutputStream.CH_DATA, bufsz, rawOut);

  2055.             ProgressMonitor pm = NullProgressMonitor.INSTANCE;
  2056.             if (!req.getClientCapabilities().contains(OPTION_NO_PROGRESS)) {
  2057.                 msgOut = new SideBandOutputStream(
  2058.                         SideBandOutputStream.CH_PROGRESS, bufsz, rawOut);
  2059.                 pm = new SideBandProgressMonitor(msgOut);
  2060.             }

  2061.             sendPack(pm, pckOut, packOut, req, accumulator, allTags,
  2062.                     unshallowCommits, deepenNots);
  2063.             pckOut.end();
  2064.         } else {
  2065.             sendPack(NullProgressMonitor.INSTANCE, pckOut, rawOut, req,
  2066.                     accumulator, allTags, unshallowCommits, deepenNots);
  2067.         }
  2068.     }

  2069.     /**
  2070.      * Send the requested objects to the client.
  2071.      *
  2072.      * @param pm
  2073.      *            progress monitor
  2074.      * @param pckOut
  2075.      *            PacketLineOut that shares the output with packOut
  2076.      * @param packOut
  2077.      *            packfile output
  2078.      * @param req
  2079.      *            request being processed
  2080.      * @param accumulator
  2081.      *            where to write statistics about the content of the pack.
  2082.      * @param allTags
  2083.      *            refs to search for annotated tags to include in the pack if
  2084.      *            the {@link #OPTION_INCLUDE_TAG} capability was requested.
  2085.      * @param unshallowCommits
  2086.      *            shallow commits on the client that are now becoming unshallow
  2087.      * @param deepenNots
  2088.      *            objects that the client specified using --shallow-exclude
  2089.      * @throws IOException
  2090.      *             if an error occurred while generating or writing the pack.
  2091.      */
  2092.     private void sendPack(ProgressMonitor pm, PacketLineOut pckOut,
  2093.             OutputStream packOut, FetchRequest req,
  2094.             PackStatistics.Accumulator accumulator,
  2095.             @Nullable Collection<Ref> allTags, List<ObjectId> unshallowCommits,
  2096.             List<ObjectId> deepenNots) throws IOException {
  2097.         if (wantAll.isEmpty()) {
  2098.             preUploadHook.onSendPack(this, wantIds, commonBase);
  2099.         } else {
  2100.             preUploadHook.onSendPack(this, wantAll, commonBase);
  2101.         }
  2102.         msgOut.flush();

  2103.         // Advertised objects and refs are not used from here on and can be
  2104.         // cleared.
  2105.         advertised = null;
  2106.         refs = null;

  2107.         PackConfig cfg = packConfig;
  2108.         if (cfg == null)
  2109.             cfg = new PackConfig(db);
  2110.         @SuppressWarnings("resource") // PackWriter is referenced in the finally
  2111.                                         // block, and is closed there
  2112.         final PackWriter pw = new PackWriter(cfg, walk.getObjectReader(),
  2113.                 accumulator);
  2114.         try {
  2115.             pw.setIndexDisabled(true);
  2116.             if (req.getFilterSpec().isNoOp()) {
  2117.                 pw.setUseCachedPacks(true);
  2118.             } else {
  2119.                 pw.setFilterSpec(req.getFilterSpec());
  2120.                 pw.setUseCachedPacks(false);
  2121.             }
  2122.             pw.setUseBitmaps(
  2123.                     req.getDepth() == 0
  2124.                             && req.getClientShallowCommits().isEmpty()
  2125.                             && req.getFilterSpec().getTreeDepthLimit() == -1);
  2126.             pw.setClientShallowCommits(req.getClientShallowCommits());
  2127.             pw.setReuseDeltaCommits(true);
  2128.             pw.setDeltaBaseAsOffset(
  2129.                     req.getClientCapabilities().contains(OPTION_OFS_DELTA));
  2130.             pw.setThin(req.getClientCapabilities().contains(OPTION_THIN_PACK));
  2131.             pw.setReuseValidatingObjects(false);

  2132.             // Objects named directly by references go at the beginning
  2133.             // of the pack.
  2134.             if (commonBase.isEmpty() && refs != null) {
  2135.                 Set<ObjectId> tagTargets = new HashSet<>();
  2136.                 for (Ref ref : refs.values()) {
  2137.                     if (ref.getPeeledObjectId() != null)
  2138.                         tagTargets.add(ref.getPeeledObjectId());
  2139.                     else if (ref.getObjectId() == null)
  2140.                         continue;
  2141.                     else if (ref.getName().startsWith(Constants.R_HEADS))
  2142.                         tagTargets.add(ref.getObjectId());
  2143.                 }
  2144.                 pw.setTagTargets(tagTargets);
  2145.             }

  2146.             RevWalk rw = walk;
  2147.             if (req.getDepth() > 0 || req.getDeepenSince() != 0 || !deepenNots.isEmpty()) {
  2148.                 int walkDepth = req.getDepth() == 0 ? Integer.MAX_VALUE
  2149.                         : req.getDepth() - 1;
  2150.                 pw.setShallowPack(req.getDepth(), unshallowCommits);

  2151.                 // Ownership is transferred below
  2152.                 DepthWalk.RevWalk dw = new DepthWalk.RevWalk(
  2153.                         walk.getObjectReader(), walkDepth);
  2154.                 dw.setDeepenSince(req.getDeepenSince());
  2155.                 dw.setDeepenNots(deepenNots);
  2156.                 dw.assumeShallow(req.getClientShallowCommits());
  2157.                 rw = dw;
  2158.             }

  2159.             if (wantAll.isEmpty()) {
  2160.                 pw.preparePack(pm, wantIds, commonBase,
  2161.                         req.getClientShallowCommits());
  2162.             } else {
  2163.                 walk.reset();

  2164.                 ObjectWalk ow = rw.toObjectWalkWithSameObjects();
  2165.                 pw.preparePack(pm, ow, wantAll, commonBase, PackWriter.NONE);
  2166.                 rw = ow;
  2167.             }

  2168.             if (req.getClientCapabilities().contains(OPTION_INCLUDE_TAG)
  2169.                     && allTags != null) {
  2170.                 for (Ref ref : allTags) {
  2171.                     ObjectId objectId = ref.getObjectId();
  2172.                     if (objectId == null) {
  2173.                         // skip unborn branch
  2174.                         continue;
  2175.                     }

  2176.                     // If the object was already requested, skip it.
  2177.                     if (wantAll.isEmpty()) {
  2178.                         if (wantIds.contains(objectId))
  2179.                             continue;
  2180.                     } else {
  2181.                         RevObject obj = rw.lookupOrNull(objectId);
  2182.                         if (obj != null && obj.has(WANT))
  2183.                             continue;
  2184.                     }

  2185.                     if (!ref.isPeeled())
  2186.                         ref = db.getRefDatabase().peel(ref);

  2187.                     ObjectId peeledId = ref.getPeeledObjectId();
  2188.                     objectId = ref.getObjectId();
  2189.                     if (peeledId == null || objectId == null)
  2190.                         continue;

  2191.                     objectId = ref.getObjectId();
  2192.                     if (pw.willInclude(peeledId) && !pw.willInclude(objectId)) {
  2193.                         RevObject o = rw.parseAny(objectId);
  2194.                         addTagChain(o, pw);
  2195.                         pw.addObject(o);
  2196.                     }
  2197.                 }
  2198.             }

  2199.             if (pckOut.isUsingSideband()) {
  2200.                 if (req instanceof FetchV2Request &&
  2201.                         cachedPackUriProvider != null &&
  2202.                         !((FetchV2Request) req).getPackfileUriProtocols().isEmpty()) {
  2203.                     FetchV2Request reqV2 = (FetchV2Request) req;
  2204.                     pw.setPackfileUriConfig(new PackWriter.PackfileUriConfig(
  2205.                             pckOut,
  2206.                             reqV2.getPackfileUriProtocols(),
  2207.                             cachedPackUriProvider));
  2208.                 } else {
  2209.                     // PackWriter will write "packfile-uris\n" and "packfile\n"
  2210.                     // for us if provided a PackfileUriConfig. In this case, we
  2211.                     // are not providing a PackfileUriConfig, so we have to
  2212.                     // write this line ourselves.
  2213.                     pckOut.writeString(
  2214.                             GitProtocolConstants.SECTION_PACKFILE + '\n');
  2215.                 }
  2216.             }
  2217.             pw.enableSearchForReuseTimeout();
  2218.             pw.writePack(pm, NullProgressMonitor.INSTANCE, packOut);

  2219.             if (msgOut != NullOutputStream.INSTANCE) {
  2220.                 String msg = pw.getStatistics().getMessage() + '\n';
  2221.                 msgOut.write(Constants.encode(msg));
  2222.                 msgOut.flush();
  2223.             }

  2224.         } finally {
  2225.             statistics = pw.getStatistics();
  2226.             if (statistics != null) {
  2227.                 postUploadHook.onPostUpload(statistics);
  2228.             }
  2229.             pw.close();
  2230.         }
  2231.     }

  2232.     private static void findSymrefs(
  2233.             final RefAdvertiser adv, final Map<String, Ref> refs) {
  2234.         Ref head = refs.get(Constants.HEAD);
  2235.         if (head != null && head.isSymbolic()) {
  2236.             adv.addSymref(Constants.HEAD, head.getLeaf().getName());
  2237.         }
  2238.     }

  2239.     private void addTagChain(
  2240.             RevObject o, PackWriter pw) throws IOException {
  2241.         while (Constants.OBJ_TAG == o.getType()) {
  2242.             RevTag t = (RevTag) o;
  2243.             o = t.getObject();
  2244.             if (o.getType() == Constants.OBJ_TAG && !pw.willInclude(o.getId())) {
  2245.                 walk.parseBody(o);
  2246.                 pw.addObject(o);
  2247.             }
  2248.         }
  2249.     }

  2250.     private List<ObjectId> parseDeepenNots(List<String> deepenNots)
  2251.             throws IOException {
  2252.         List<ObjectId> result = new ArrayList<>();
  2253.         for (String s : deepenNots) {
  2254.             if (ObjectId.isId(s)) {
  2255.                 result.add(ObjectId.fromString(s));
  2256.             } else {
  2257.                 Ref ref = findRef(s);
  2258.                 if (ref == null) {
  2259.                     throw new PackProtocolException(MessageFormat
  2260.                             .format(JGitText.get().invalidRefName, s));
  2261.                 }
  2262.                 result.add(ref.getObjectId());
  2263.             }
  2264.         }
  2265.         return result;
  2266.     }

  2267.     private static class ResponseBufferedOutputStream extends OutputStream {
  2268.         private final OutputStream rawOut;

  2269.         private OutputStream out;

  2270.         ResponseBufferedOutputStream(OutputStream rawOut) {
  2271.             this.rawOut = rawOut;
  2272.             this.out = new ByteArrayOutputStream();
  2273.         }

  2274.         @Override
  2275.         public void write(int b) throws IOException {
  2276.             out.write(b);
  2277.         }

  2278.         @Override
  2279.         public void write(byte[] b) throws IOException {
  2280.             out.write(b);
  2281.         }

  2282.         @Override
  2283.         public void write(byte[] b, int off, int len) throws IOException {
  2284.             out.write(b, off, len);
  2285.         }

  2286.         @Override
  2287.         public void flush() throws IOException {
  2288.             out.flush();
  2289.         }

  2290.         @Override
  2291.         public void close() throws IOException {
  2292.             out.close();
  2293.         }

  2294.         void stopBuffering() throws IOException {
  2295.             if (out != rawOut) {
  2296.                 ((ByteArrayOutputStream) out).writeTo(rawOut);
  2297.                 out = rawOut;
  2298.             }
  2299.         }
  2300.     }

  2301.     private interface ErrorWriter {
  2302.         void writeError(String message) throws IOException;
  2303.     }

  2304.     private class SideBandErrorWriter implements ErrorWriter {
  2305.         @Override
  2306.         public void writeError(String message) throws IOException {
  2307.             @SuppressWarnings("resource" /* java 7 */)
  2308.             SideBandOutputStream err = new SideBandOutputStream(
  2309.                     SideBandOutputStream.CH_ERROR,
  2310.                     SideBandOutputStream.SMALL_BUF, requireNonNull(rawOut));
  2311.             err.write(Constants.encode(message));
  2312.             err.flush();
  2313.         }
  2314.     }

  2315.     private class PackProtocolErrorWriter implements ErrorWriter {
  2316.         @Override
  2317.         public void writeError(String message) throws IOException {
  2318.             new PacketLineOut(requireNonNull(rawOut))
  2319.                     .writeString(PACKET_ERR + message + '\n');
  2320.         }
  2321.     }
  2322. }