UploadPack.java

  1. /*
  2.  * Copyright (C) 2008, 2020 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.unmodifiableMap;
  12. import static java.util.Objects.requireNonNull;
  13. import static org.eclipse.jgit.lib.Constants.R_TAGS;
  14. import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REF_IN_WANT;
  15. import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SERVER_OPTION;
  16. import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_FETCH;
  17. import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_LS_REFS;
  18. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
  19. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_REACHABLE_SHA1_IN_WANT;
  20. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT;
  21. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_DEEPEN_RELATIVE;
  22. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_FILTER;
  23. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_INCLUDE_TAG;
  24. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK;
  25. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK_DETAILED;
  26. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_DONE;
  27. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_PROGRESS;
  28. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_OFS_DELTA;
  29. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SHALLOW;
  30. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDEBAND_ALL;
  31. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
  32. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
  33. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
  34. import static org.eclipse.jgit.transport.GitProtocolConstants.VERSION_2_REQUEST;
  35. import static org.eclipse.jgit.util.RefMap.toRefMap;

  36. import java.io.ByteArrayOutputStream;
  37. import java.io.EOFException;
  38. import java.io.IOException;
  39. import java.io.InputStream;
  40. import java.io.OutputStream;
  41. import java.io.UncheckedIOException;
  42. import java.text.MessageFormat;
  43. import java.time.Duration;
  44. import java.time.Instant;
  45. import java.util.ArrayList;
  46. import java.util.Collection;
  47. import java.util.Collections;
  48. import java.util.HashSet;
  49. import java.util.List;
  50. import java.util.Map;
  51. import java.util.Objects;
  52. import java.util.Optional;
  53. import java.util.Set;
  54. import java.util.TreeMap;
  55. import java.util.function.Predicate;
  56. import java.util.stream.Collectors;
  57. import java.util.stream.Stream;

  58. import org.eclipse.jgit.annotations.NonNull;
  59. import org.eclipse.jgit.annotations.Nullable;
  60. import org.eclipse.jgit.errors.CorruptObjectException;
  61. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  62. import org.eclipse.jgit.errors.MissingObjectException;
  63. import org.eclipse.jgit.errors.PackProtocolException;
  64. import org.eclipse.jgit.internal.JGitText;
  65. import org.eclipse.jgit.internal.storage.pack.CachedPackUriProvider;
  66. import org.eclipse.jgit.internal.storage.pack.PackWriter;
  67. import org.eclipse.jgit.internal.transport.parser.FirstWant;
  68. import org.eclipse.jgit.lib.Constants;
  69. import org.eclipse.jgit.lib.NullProgressMonitor;
  70. import org.eclipse.jgit.lib.ObjectId;
  71. import org.eclipse.jgit.lib.ObjectReader;
  72. import org.eclipse.jgit.lib.ProgressMonitor;
  73. import org.eclipse.jgit.lib.Ref;
  74. import org.eclipse.jgit.lib.RefDatabase;
  75. import org.eclipse.jgit.lib.Repository;
  76. import org.eclipse.jgit.revwalk.AsyncRevObjectQueue;
  77. import org.eclipse.jgit.revwalk.DepthWalk;
  78. import org.eclipse.jgit.revwalk.ObjectReachabilityChecker;
  79. import org.eclipse.jgit.revwalk.ObjectWalk;
  80. import org.eclipse.jgit.revwalk.ReachabilityChecker;
  81. import org.eclipse.jgit.revwalk.RevCommit;
  82. import org.eclipse.jgit.revwalk.RevFlag;
  83. import org.eclipse.jgit.revwalk.RevFlagSet;
  84. import org.eclipse.jgit.revwalk.RevObject;
  85. import org.eclipse.jgit.revwalk.RevTag;
  86. import org.eclipse.jgit.revwalk.RevWalk;
  87. import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
  88. import org.eclipse.jgit.storage.pack.PackConfig;
  89. import org.eclipse.jgit.storage.pack.PackStatistics;
  90. import org.eclipse.jgit.transport.GitProtocolConstants.MultiAck;
  91. import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
  92. import org.eclipse.jgit.transport.TransferConfig.ProtocolVersion;
  93. import org.eclipse.jgit.util.io.InterruptTimer;
  94. import org.eclipse.jgit.util.io.NullOutputStream;
  95. import org.eclipse.jgit.util.io.TimeoutInputStream;
  96. import org.eclipse.jgit.util.io.TimeoutOutputStream;

  97. /**
  98.  * Implements the server side of a fetch connection, transmitting objects.
  99.  */
  100. public class UploadPack {
  101.     /** Policy the server uses to validate client requests */
  102.     public enum RequestPolicy {
  103.         /** Client may only ask for objects the server advertised a reference for. */
  104.         ADVERTISED,

  105.         /**
  106.          * Client may ask for any commit reachable from a reference advertised by
  107.          * the server.
  108.          */
  109.         REACHABLE_COMMIT,

  110.         /**
  111.          * Client may ask for objects that are the tip of any reference, even if not
  112.          * advertised.
  113.          * <p>
  114.          * This may happen, for example, when a custom {@link RefFilter} is set.
  115.          *
  116.          * @since 3.1
  117.          */
  118.         TIP,

  119.         /**
  120.          * Client may ask for any commit reachable from any reference, even if that
  121.          * reference wasn't advertised.
  122.          *
  123.          * @since 3.1
  124.          */
  125.         REACHABLE_COMMIT_TIP,

  126.         /** Client may ask for any SHA-1 in the repository. */
  127.         ANY;
  128.     }

  129.     /**
  130.      * Validator for client requests.
  131.      *
  132.      * @since 3.1
  133.      */
  134.     public interface RequestValidator {
  135.         /**
  136.          * Check a list of client wants against the request policy.
  137.          *
  138.          * @param up
  139.          *            {@link UploadPack} instance.
  140.          * @param wants
  141.          *            objects the client requested that were not advertised.
  142.          *
  143.          * @throws PackProtocolException
  144.          *            if one or more wants is not valid.
  145.          * @throws IOException
  146.          *            if a low-level exception occurred.
  147.          * @since 3.1
  148.          */
  149.         void checkWants(UploadPack up, List<ObjectId> wants)
  150.                 throws PackProtocolException, IOException;
  151.     }

  152.     /**
  153.      * Data in the first line of a want-list, the line itself plus options.
  154.      *
  155.      * @deprecated Use {@link FirstWant} instead
  156.      */
  157.     @Deprecated
  158.     public static class FirstLine {

  159.         private final FirstWant firstWant;

  160.         /**
  161.          * @param line
  162.          *            line from the client.
  163.          */
  164.         public FirstLine(String line) {
  165.             try {
  166.                 firstWant = FirstWant.fromLine(line);
  167.             } catch (PackProtocolException e) {
  168.                 throw new UncheckedIOException(e);
  169.             }
  170.         }

  171.         /** @return non-capabilities part of the line. */
  172.         public String getLine() {
  173.             return firstWant.getLine();
  174.         }

  175.         /** @return capabilities parsed from the line. */
  176.         public Set<String> getOptions() {
  177.             if (firstWant.getAgent() != null) {
  178.                 Set<String> caps = new HashSet<>(firstWant.getCapabilities());
  179.                 caps.add(OPTION_AGENT + '=' + firstWant.getAgent());
  180.                 return caps;
  181.             }
  182.             return firstWant.getCapabilities();
  183.         }
  184.     }

  185.     /*
  186.      * {@link java.util.function.Consumer} doesn't allow throwing checked
  187.      * exceptions. Define our own to propagate IOExceptions.
  188.      */
  189.     @FunctionalInterface
  190.     private static interface IOConsumer<R> {
  191.         void accept(R t) throws IOException;
  192.     }

  193.     /** Database we read the objects from. */
  194.     private final Repository db;

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

  197.     /** Configuration to pass into the PackWriter. */
  198.     private PackConfig packConfig;

  199.     /** Configuration for various transfer options. */
  200.     private TransferConfig transferConfig;

  201.     /** Timeout in seconds to wait for client interaction. */
  202.     private int timeout;

  203.     /**
  204.      * Is the client connection a bi-directional socket or pipe?
  205.      * <p>
  206.      * If true, this class assumes it can perform multiple read and write cycles
  207.      * with the client over the input and output streams. This matches the
  208.      * functionality available with a standard TCP/IP connection, or a local
  209.      * operating system or in-memory pipe.
  210.      * <p>
  211.      * If false, this class runs in a read everything then output results mode,
  212.      * making it suitable for single round-trip systems RPCs such as HTTP.
  213.      */
  214.     private boolean biDirectionalPipe = true;

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

  217.     /**
  218.      * Whether the client requested to use protocol V2 through a side
  219.      * channel (such as the Git-Protocol HTTP header).
  220.      */
  221.     private boolean clientRequestedV2;

  222.     private InputStream rawIn;

  223.     private ResponseBufferedOutputStream rawOut;

  224.     private PacketLineIn pckIn;

  225.     private OutputStream msgOut = NullOutputStream.INSTANCE;

  226.     private ErrorWriter errOut = new PackProtocolErrorWriter();

  227.     /**
  228.      * Refs eligible for advertising to the client, set using
  229.      * {@link #setAdvertisedRefs}.
  230.      */
  231.     private Map<String, Ref> refs;

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

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

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

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

  240.     /** Hook handling the various upload phases. */
  241.     private PreUploadHook preUploadHook = PreUploadHook.NULL;

  242.     /** Hook for taking post upload actions. */
  243.     private PostUploadHook postUploadHook = PostUploadHook.NULL;

  244.     /** Caller user agent */
  245.     String userAgent;

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

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

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

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

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

  256.     private boolean sentReady;

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

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

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

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

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

  267.     private final RevFlagSet SAVE;

  268.     private RequestValidator requestValidator = new AdvertisedRequestValidator();

  269.     private MultiAck multiAck = MultiAck.OFF;

  270.     private boolean noDone;

  271.     private PackStatistics statistics;

  272.     /**
  273.      * Request this instance is handling.
  274.      *
  275.      * We need to keep a reference to it for {@link PreUploadHook pre upload
  276.      * hooks}. They receive a reference this instance and invoke methods like
  277.      * getDepth() to get information about the request.
  278.      */
  279.     private FetchRequest currentRequest;

  280.     private CachedPackUriProvider cachedPackUriProvider;

  281.     /**
  282.      * Create a new pack upload for an open repository.
  283.      *
  284.      * @param copyFrom
  285.      *            the source repository.
  286.      */
  287.     public UploadPack(Repository copyFrom) {
  288.         db = copyFrom;
  289.         walk = new RevWalk(db);
  290.         walk.setRetainBody(false);

  291.         WANT = walk.newFlag("WANT"); //$NON-NLS-1$
  292.         PEER_HAS = walk.newFlag("PEER_HAS"); //$NON-NLS-1$
  293.         COMMON = walk.newFlag("COMMON"); //$NON-NLS-1$
  294.         SATISFIED = walk.newFlag("SATISFIED"); //$NON-NLS-1$
  295.         walk.carry(PEER_HAS);

  296.         SAVE = new RevFlagSet();
  297.         SAVE.add(WANT);
  298.         SAVE.add(PEER_HAS);
  299.         SAVE.add(COMMON);
  300.         SAVE.add(SATISFIED);

  301.         setTransferConfig(null);
  302.     }

  303.     /**
  304.      * Get the repository this upload is reading from.
  305.      *
  306.      * @return the repository this upload is reading from.
  307.      */
  308.     public final Repository getRepository() {
  309.         return db;
  310.     }

  311.     /**
  312.      * Get the RevWalk instance used by this connection.
  313.      *
  314.      * @return the RevWalk instance used by this connection.
  315.      */
  316.     public final RevWalk getRevWalk() {
  317.         return walk;
  318.     }

  319.     /**
  320.      * Get refs which were advertised to the client.
  321.      *
  322.      * @return all refs which were advertised to the client. Only valid during
  323.      *         the negotiation phase. Will return {@code null} if
  324.      *         {@link #setAdvertisedRefs(Map)} has not been called yet or if
  325.      *         {@code #sendPack()} has been called.
  326.      */
  327.     public final Map<String, Ref> getAdvertisedRefs() {
  328.         return refs;
  329.     }

  330.     /**
  331.      * Set the refs advertised by this UploadPack.
  332.      * <p>
  333.      * Intended to be called from a
  334.      * {@link org.eclipse.jgit.transport.PreUploadHook}.
  335.      *
  336.      * @param allRefs
  337.      *            explicit set of references to claim as advertised by this
  338.      *            UploadPack instance. This overrides any references that may
  339.      *            exist in the source repository. The map is passed to the
  340.      *            configured {@link #getRefFilter()}. If null, assumes all refs
  341.      *            were advertised.
  342.      */
  343.     public void setAdvertisedRefs(@Nullable Map<String, Ref> allRefs) {
  344.         if (allRefs != null)
  345.             refs = allRefs;
  346.         else
  347.             refs = db.getAllRefs();
  348.         if (refFilter == RefFilter.DEFAULT)
  349.             refs = transferConfig.getRefFilter().filter(refs);
  350.         else
  351.             refs = refFilter.filter(refs);
  352.     }

  353.     /**
  354.      * Get timeout (in seconds) before aborting an IO operation.
  355.      *
  356.      * @return timeout (in seconds) before aborting an IO operation.
  357.      */
  358.     public int getTimeout() {
  359.         return timeout;
  360.     }

  361.     /**
  362.      * Set the timeout before willing to abort an IO call.
  363.      *
  364.      * @param seconds
  365.      *            number of seconds to wait (with no data transfer occurring)
  366.      *            before aborting an IO read or write operation with the
  367.      *            connected client.
  368.      */
  369.     public void setTimeout(int seconds) {
  370.         timeout = seconds;
  371.     }

  372.     /**
  373.      * Whether this class expects a bi-directional pipe opened between the
  374.      * client and itself.
  375.      *
  376.      * @return true if this class expects a bi-directional pipe opened between
  377.      *         the client and itself. The default is true.
  378.      */
  379.     public boolean isBiDirectionalPipe() {
  380.         return biDirectionalPipe;
  381.     }

  382.     /**
  383.      * Set whether this class will assume the socket is a fully bidirectional
  384.      * pipe between the two peers
  385.      *
  386.      * @param twoWay
  387.      *            if true, this class will assume the socket is a fully
  388.      *            bidirectional pipe between the two peers and takes advantage
  389.      *            of that by first transmitting the known refs, then waiting to
  390.      *            read commands. If false, this class assumes it must read the
  391.      *            commands before writing output and does not perform the
  392.      *            initial advertising.
  393.      */
  394.     public void setBiDirectionalPipe(boolean twoWay) {
  395.         biDirectionalPipe = twoWay;
  396.     }

  397.     /**
  398.      * Get policy used by the service to validate client requests
  399.      *
  400.      * @return policy used by the service to validate client requests, or null
  401.      *         for a custom request validator.
  402.      */
  403.     public RequestPolicy getRequestPolicy() {
  404.         if (requestValidator instanceof AdvertisedRequestValidator)
  405.             return RequestPolicy.ADVERTISED;
  406.         if (requestValidator instanceof ReachableCommitRequestValidator)
  407.             return RequestPolicy.REACHABLE_COMMIT;
  408.         if (requestValidator instanceof TipRequestValidator)
  409.             return RequestPolicy.TIP;
  410.         if (requestValidator instanceof ReachableCommitTipRequestValidator)
  411.             return RequestPolicy.REACHABLE_COMMIT_TIP;
  412.         if (requestValidator instanceof AnyRequestValidator)
  413.             return RequestPolicy.ANY;
  414.         return null;
  415.     }

  416.     /**
  417.      * Set the policy used to enforce validation of a client's want list.
  418.      *
  419.      * @param policy
  420.      *            the policy used to enforce validation of a client's want list.
  421.      *            By default the policy is
  422.      *            {@link org.eclipse.jgit.transport.UploadPack.RequestPolicy#ADVERTISED},
  423.      *            which is the Git default requiring clients to only ask for an
  424.      *            object that a reference directly points to. This may be
  425.      *            relaxed to
  426.      *            {@link org.eclipse.jgit.transport.UploadPack.RequestPolicy#REACHABLE_COMMIT}
  427.      *            or
  428.      *            {@link org.eclipse.jgit.transport.UploadPack.RequestPolicy#REACHABLE_COMMIT_TIP}
  429.      *            when callers have {@link #setBiDirectionalPipe(boolean)} set
  430.      *            to false. Overrides any policy specified in a
  431.      *            {@link org.eclipse.jgit.transport.TransferConfig}.
  432.      */
  433.     public void setRequestPolicy(RequestPolicy policy) {
  434.         switch (policy) {
  435.             case ADVERTISED:
  436.             default:
  437.                 requestValidator = new AdvertisedRequestValidator();
  438.                 break;
  439.             case REACHABLE_COMMIT:
  440.                 requestValidator = new ReachableCommitRequestValidator();
  441.                 break;
  442.             case TIP:
  443.                 requestValidator = new TipRequestValidator();
  444.                 break;
  445.             case REACHABLE_COMMIT_TIP:
  446.                 requestValidator = new ReachableCommitTipRequestValidator();
  447.                 break;
  448.             case ANY:
  449.                 requestValidator = new AnyRequestValidator();
  450.                 break;
  451.         }
  452.     }

  453.     /**
  454.      * Set custom validator for client want list.
  455.      *
  456.      * @param validator
  457.      *            custom validator for client want list.
  458.      * @since 3.1
  459.      */
  460.     public void setRequestValidator(@Nullable RequestValidator validator) {
  461.         requestValidator = validator != null ? validator
  462.                 : new AdvertisedRequestValidator();
  463.     }

  464.     /**
  465.      * Get the hook used while advertising the refs to the client.
  466.      *
  467.      * @return the hook used while advertising the refs to the client.
  468.      */
  469.     public AdvertiseRefsHook getAdvertiseRefsHook() {
  470.         return advertiseRefsHook;
  471.     }

  472.     /**
  473.      * Get the filter used while advertising the refs to the client.
  474.      *
  475.      * @return the filter used while advertising the refs to the client.
  476.      */
  477.     public RefFilter getRefFilter() {
  478.         return refFilter;
  479.     }

  480.     /**
  481.      * Set the hook used while advertising the refs to the client.
  482.      * <p>
  483.      * If the {@link org.eclipse.jgit.transport.AdvertiseRefsHook} chooses to
  484.      * call {@link #setAdvertisedRefs(Map)}, only refs set by this hook
  485.      * <em>and</em> selected by the {@link org.eclipse.jgit.transport.RefFilter}
  486.      * will be shown to the client.
  487.      *
  488.      * @param advertiseRefsHook
  489.      *            the hook; may be null to show all refs.
  490.      */
  491.     public void setAdvertiseRefsHook(
  492.             @Nullable AdvertiseRefsHook advertiseRefsHook) {
  493.         this.advertiseRefsHook = advertiseRefsHook != null ? advertiseRefsHook
  494.                 : AdvertiseRefsHook.DEFAULT;
  495.     }

  496.     /**
  497.      * Set the protocol V2 hook.
  498.      *
  499.      * @param hook
  500.      *            the hook; if null no special actions are taken.
  501.      * @since 5.1
  502.      */
  503.     public void setProtocolV2Hook(@Nullable ProtocolV2Hook hook) {
  504.         this.protocolV2Hook = hook != null ? hook : ProtocolV2Hook.DEFAULT;
  505.     }

  506.     /**
  507.      * Get the currently installed protocol v2 hook.
  508.      *
  509.      * @return the hook or a default implementation if none installed.
  510.      *
  511.      * @since 5.5
  512.      */
  513.     public ProtocolV2Hook getProtocolV2Hook() {
  514.         return this.protocolV2Hook != null ? this.protocolV2Hook
  515.                 : ProtocolV2Hook.DEFAULT;
  516.     }

  517.     /**
  518.      * Set the filter used while advertising the refs to the client.
  519.      * <p>
  520.      * Only refs allowed by this filter will be sent to the client. The filter
  521.      * is run against the refs specified by the
  522.      * {@link org.eclipse.jgit.transport.AdvertiseRefsHook} (if applicable). If
  523.      * null or not set, uses the filter implied by the
  524.      * {@link org.eclipse.jgit.transport.TransferConfig}.
  525.      *
  526.      * @param refFilter
  527.      *            the filter; may be null to show all refs.
  528.      */
  529.     public void setRefFilter(@Nullable RefFilter refFilter) {
  530.         this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
  531.     }

  532.     /**
  533.      * Get the configured pre upload hook.
  534.      *
  535.      * @return the configured pre upload hook.
  536.      */
  537.     public PreUploadHook getPreUploadHook() {
  538.         return preUploadHook;
  539.     }

  540.     /**
  541.      * Set the hook that controls how this instance will behave.
  542.      *
  543.      * @param hook
  544.      *            the hook; if null no special actions are taken.
  545.      */
  546.     public void setPreUploadHook(@Nullable PreUploadHook hook) {
  547.         preUploadHook = hook != null ? hook : PreUploadHook.NULL;
  548.     }

  549.     /**
  550.      * Get the configured post upload hook.
  551.      *
  552.      * @return the configured post upload hook.
  553.      * @since 4.1
  554.      */
  555.     public PostUploadHook getPostUploadHook() {
  556.         return postUploadHook;
  557.     }

  558.     /**
  559.      * Set the hook for post upload actions (logging, repacking).
  560.      *
  561.      * @param hook
  562.      *            the hook; if null no special actions are taken.
  563.      * @since 4.1
  564.      */
  565.     public void setPostUploadHook(@Nullable PostUploadHook hook) {
  566.         postUploadHook = hook != null ? hook : PostUploadHook.NULL;
  567.     }

  568.     /**
  569.      * Set the configuration used by the pack generator.
  570.      *
  571.      * @param pc
  572.      *            configuration controlling packing parameters. If null the
  573.      *            source repository's settings will be used.
  574.      */
  575.     public void setPackConfig(@Nullable PackConfig pc) {
  576.         this.packConfig = pc;
  577.     }

  578.     /**
  579.      * Set configuration controlling transfer options.
  580.      *
  581.      * @param tc
  582.      *            configuration controlling transfer options. If null the source
  583.      *            repository's settings will be used.
  584.      * @since 3.1
  585.      */
  586.     public void setTransferConfig(@Nullable TransferConfig tc) {
  587.         this.transferConfig = tc != null ? tc : new TransferConfig(db);
  588.         if (transferConfig.isAllowTipSha1InWant()) {
  589.             setRequestPolicy(transferConfig.isAllowReachableSha1InWant()
  590.                 ? RequestPolicy.REACHABLE_COMMIT_TIP : RequestPolicy.TIP);
  591.         } else {
  592.             setRequestPolicy(transferConfig.isAllowReachableSha1InWant()
  593.                 ? RequestPolicy.REACHABLE_COMMIT : RequestPolicy.ADVERTISED);
  594.         }
  595.     }

  596.     /**
  597.      * Check whether the client expects a side-band stream.
  598.      *
  599.      * @return true if the client has advertised a side-band capability, false
  600.      *     otherwise.
  601.      * @throws org.eclipse.jgit.transport.RequestNotYetReadException
  602.      *             if the client's request has not yet been read from the wire, so
  603.      *             we do not know if they expect side-band. Note that the client
  604.      *             may have already written the request, it just has not been
  605.      *             read.
  606.      */
  607.     public boolean isSideBand() throws RequestNotYetReadException {
  608.         if (currentRequest == null) {
  609.             throw new RequestNotYetReadException();
  610.         }
  611.         Set<String> caps = currentRequest.getClientCapabilities();
  612.         return caps.contains(OPTION_SIDE_BAND)
  613.                 || caps.contains(OPTION_SIDE_BAND_64K);
  614.     }

  615.     /**
  616.      * Set the Extra Parameters provided by the client.
  617.      *
  618.      * <p>These are parameters passed by the client through a side channel
  619.      * such as the Git-Protocol HTTP header, to allow a client to request
  620.      * a newer response format while remaining compatible with older servers
  621.      * that do not understand different request formats.
  622.      *
  623.      * @param params
  624.      *            parameters supplied by the client, split at colons or NUL
  625.      *            bytes.
  626.      * @since 5.0
  627.      */
  628.     public void setExtraParameters(Collection<String> params) {
  629.         this.clientRequestedV2 = params.contains(VERSION_2_REQUEST);
  630.     }

  631.     /**
  632.      * @param p provider of URIs corresponding to cached packs (to support
  633.      *     the packfile URIs feature)
  634.      * @since 5.5
  635.      */
  636.     public void setCachedPackUriProvider(@Nullable CachedPackUriProvider p) {
  637.         cachedPackUriProvider = p;
  638.     }

  639.     private boolean useProtocolV2() {
  640.         return (transferConfig.protocolVersion == null
  641.             || ProtocolVersion.V2.equals(transferConfig.protocolVersion))
  642.                 && clientRequestedV2;
  643.     }

  644.     /**
  645.      * Execute the upload task on the socket.
  646.      *
  647.      * <p>
  648.      * Same as {@link #uploadWithExceptionPropagation} except that the thrown
  649.      * exceptions are handled in the method, and the error messages are sent to
  650.      * the clients.
  651.      *
  652.      * <p>
  653.      * Call this method if the caller does not have an error handling mechanism.
  654.      * Call {@link #uploadWithExceptionPropagation} if the caller wants to have
  655.      * its own error handling mechanism.
  656.      *
  657.      * @param input
  658.      * @param output
  659.      * @param messages
  660.      * @throws java.io.IOException
  661.      */
  662.     public void upload(InputStream input, OutputStream output,
  663.             @Nullable OutputStream messages) throws IOException {
  664.         try {
  665.             uploadWithExceptionPropagation(input, output, messages);
  666.         } catch (ServiceMayNotContinueException err) {
  667.             if (!err.isOutput() && err.getMessage() != null) {
  668.                 try {
  669.                     errOut.writeError(err.getMessage());
  670.                 } catch (IOException e) {
  671.                     err.addSuppressed(e);
  672.                     throw err;
  673.                 }
  674.                 err.setOutput();
  675.             }
  676.             throw err;
  677.         } catch (IOException | RuntimeException | Error err) {
  678.             if (rawOut != null) {
  679.                 String msg = err instanceof PackProtocolException
  680.                         ? err.getMessage()
  681.                         : JGitText.get().internalServerError;
  682.                 try {
  683.                     errOut.writeError(msg);
  684.                 } catch (IOException e) {
  685.                     err.addSuppressed(e);
  686.                     throw err;
  687.                 }
  688.                 throw new UploadPackInternalServerErrorException(err);
  689.             }
  690.             throw err;
  691.         }
  692.     }

  693.     /**
  694.      * Execute the upload task on the socket.
  695.      *
  696.      * <p>
  697.      * If the client passed extra parameters (e.g., "version=2") through a side
  698.      * channel, the caller must call setExtraParameters first to supply them.
  699.      *
  700.      * @param input
  701.      *            raw input to read client commands from. Caller must ensure the
  702.      *            input is buffered, otherwise read performance may suffer.
  703.      * @param output
  704.      *            response back to the Git network client, to write the pack
  705.      *            data onto. Caller must ensure the output is buffered,
  706.      *            otherwise write performance may suffer.
  707.      * @param messages
  708.      *            secondary "notice" channel to send additional messages out
  709.      *            through. When run over SSH this should be tied back to the
  710.      *            standard error channel of the command execution. For most
  711.      *            other network connections this should be null.
  712.      * @throws ServiceMayNotContinueException
  713.      *             thrown if one of the hooks throws this.
  714.      * @throws IOException
  715.      *             thrown if the server or the client I/O fails, or there's an
  716.      *             internal server error.
  717.      * @since 5.6
  718.      */
  719.     public void uploadWithExceptionPropagation(InputStream input,
  720.             OutputStream output, @Nullable OutputStream messages)
  721.             throws ServiceMayNotContinueException, IOException {
  722.         try {
  723.             rawIn = input;
  724.             if (messages != null) {
  725.                 msgOut = messages;
  726.             }

  727.             if (timeout > 0) {
  728.                 final Thread caller = Thread.currentThread();
  729.                 timer = new InterruptTimer(caller.getName() + "-Timer"); //$NON-NLS-1$
  730.                 TimeoutInputStream i = new TimeoutInputStream(rawIn, timer);
  731.                 @SuppressWarnings("resource")
  732.                 TimeoutOutputStream o = new TimeoutOutputStream(output, timer);
  733.                 i.setTimeout(timeout * 1000);
  734.                 o.setTimeout(timeout * 1000);
  735.                 rawIn = i;
  736.                 output = o;
  737.             }

  738.             rawOut = new ResponseBufferedOutputStream(output);
  739.             if (biDirectionalPipe) {
  740.                 rawOut.stopBuffering();
  741.             }

  742.             pckIn = new PacketLineIn(rawIn);
  743.             PacketLineOut pckOut = new PacketLineOut(rawOut);
  744.             if (useProtocolV2()) {
  745.                 serviceV2(pckOut);
  746.             } else {
  747.                 service(pckOut);
  748.             }
  749.         } finally {
  750.             msgOut = NullOutputStream.INSTANCE;
  751.             walk.close();
  752.             if (timer != null) {
  753.                 try {
  754.                     timer.terminate();
  755.                 } finally {
  756.                     timer = null;
  757.                 }
  758.             }
  759.         }
  760.     }

  761.     /**
  762.      * Get the PackWriter's statistics if a pack was sent to the client.
  763.      *
  764.      * @return statistics about pack output, if a pack was sent. Null if no pack
  765.      *         was sent, such as during the negotiation phase of a smart HTTP
  766.      *         connection, or if the client was already up-to-date.
  767.      * @since 4.1
  768.      */
  769.     public PackStatistics getStatistics() {
  770.         return statistics;
  771.     }

  772.     private Map<String, Ref> getAdvertisedOrDefaultRefs() throws IOException {
  773.         if (refs != null) {
  774.             return refs;
  775.         }

  776.         if (!advertiseRefsHookCalled) {
  777.             advertiseRefsHook.advertiseRefs(this);
  778.             advertiseRefsHookCalled = true;
  779.         }
  780.         if (refs == null) {
  781.             // Fall back to all refs.
  782.             setAdvertisedRefs(
  783.                     db.getRefDatabase().getRefs().stream()
  784.                             .collect(toRefMap((a, b) -> b)));
  785.         }
  786.         return refs;
  787.     }

  788.     private Map<String, Ref> getFilteredRefs(Collection<String> refPrefixes)
  789.                     throws IOException {
  790.         if (refPrefixes.isEmpty()) {
  791.             return getAdvertisedOrDefaultRefs();
  792.         }
  793.         if (refs == null && !advertiseRefsHookCalled) {
  794.             advertiseRefsHook.advertiseRefs(this);
  795.             advertiseRefsHookCalled = true;
  796.         }
  797.         if (refs == null) {
  798.             // Fast path: the advertised refs hook did not set advertised refs.
  799.             String[] prefixes = refPrefixes.toArray(new String[0]);
  800.             Map<String, Ref> rs =
  801.                     db.getRefDatabase().getRefsByPrefix(prefixes).stream()
  802.                             .collect(toRefMap((a, b) -> b));
  803.             if (refFilter != RefFilter.DEFAULT) {
  804.                 return refFilter.filter(rs);
  805.             }
  806.             return transferConfig.getRefFilter().filter(rs);
  807.         }

  808.         // Slow path: filter the refs provided by the advertised refs hook.
  809.         // refFilter has already been applied to refs.
  810.         return refs.values().stream()
  811.                 .filter(ref -> refPrefixes.stream()
  812.                         .anyMatch(ref.getName()::startsWith))
  813.                 .collect(toRefMap((a, b) -> b));
  814.     }

  815.     /**
  816.      * Returns the specified references.
  817.      * <p>
  818.      * This produces an immutable map containing whatever subset of the
  819.      * refs named by the caller are present in the supplied {@code refs}
  820.      * map.
  821.      *
  822.      * @param refs
  823.      *            Map to search for refs to return.
  824.      * @param names
  825.      *            which refs to search for in {@code refs}.
  826.      * @return the requested Refs, omitting any that are null or missing.
  827.      */
  828.     @NonNull
  829.     private static Map<String, Ref> mapRefs(
  830.             Map<String, Ref> refs, List<String> names) {
  831.         return unmodifiableMap(
  832.                 names.stream()
  833.                     .map(refs::get)
  834.                     .filter(Objects::nonNull)
  835.                         .collect(toRefMap((a, b) -> b)));
  836.     }

  837.     /**
  838.      * Read refs on behalf of the client.
  839.      * <p>
  840.      * This checks whether the refs are present in the ref advertisement
  841.      * since otherwise the client might not be supposed to be able to
  842.      * read them.
  843.      *
  844.      * @param names
  845.      *            unabbreviated names of references.
  846.      * @return the requested Refs, omitting any that are not visible or
  847.      *         do not exist.
  848.      * @throws java.io.IOException
  849.      *            on failure to read a ref or check it for visibility.
  850.      */
  851.     @NonNull
  852.     private Map<String, Ref> exactRefs(List<String> names) throws IOException {
  853.         if (refs != null) {
  854.             return mapRefs(refs, names);
  855.         }
  856.         if (!advertiseRefsHookCalled) {
  857.             advertiseRefsHook.advertiseRefs(this);
  858.             advertiseRefsHookCalled = true;
  859.         }
  860.         if (refs == null &&
  861.                 refFilter == RefFilter.DEFAULT &&
  862.                 transferConfig.hasDefaultRefFilter()) {
  863.             // Fast path: no ref filtering is needed.
  864.             String[] ns = names.toArray(new String[0]);
  865.             return unmodifiableMap(db.getRefDatabase().exactRef(ns));
  866.         }
  867.         return mapRefs(getAdvertisedOrDefaultRefs(), names);
  868.     }

  869.     /**
  870.      * Find a ref in the usual search path on behalf of the client.
  871.      * <p>
  872.      * This checks that the ref is present in the ref advertisement since
  873.      * otherwise the client might not be supposed to be able to read it.
  874.      *
  875.      * @param name
  876.      *            short name of the ref to find, e.g. "master" to find
  877.      *            "refs/heads/master".
  878.      * @return the requested Ref, or {@code null} if it is not visible or
  879.      *         does not exist.
  880.      * @throws java.io.IOException
  881.      *            on failure to read the ref or check it for visibility.
  882.      */
  883.     @Nullable
  884.     private Ref findRef(String name) throws IOException {
  885.         if (refs != null) {
  886.             return RefDatabase.findRef(refs, name);
  887.         }
  888.         if (!advertiseRefsHookCalled) {
  889.             advertiseRefsHook.advertiseRefs(this);
  890.             advertiseRefsHookCalled = true;
  891.         }
  892.         if (refs == null &&
  893.                 refFilter == RefFilter.DEFAULT &&
  894.                 transferConfig.hasDefaultRefFilter()) {
  895.             // Fast path: no ref filtering is needed.
  896.             return db.getRefDatabase().findRef(name);
  897.         }
  898.         return RefDatabase.findRef(getAdvertisedOrDefaultRefs(), name);
  899.     }

  900.     private void service(PacketLineOut pckOut) throws IOException {
  901.         boolean sendPack = false;
  902.         // If it's a non-bidi request, we need to read the entire request before
  903.         // writing a response. Buffer the response until then.
  904.         PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator();
  905.         List<ObjectId> unshallowCommits = new ArrayList<>();
  906.         FetchRequest req;
  907.         try {
  908.             if (biDirectionalPipe)
  909.                 sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
  910.             else if (requestValidator instanceof AnyRequestValidator)
  911.                 advertised = Collections.emptySet();
  912.             else
  913.                 advertised = refIdSet(getAdvertisedOrDefaultRefs().values());

  914.             Instant negotiateStart = Instant.now();
  915.             accumulator.advertised = advertised.size();

  916.             ProtocolV0Parser parser = new ProtocolV0Parser(transferConfig);
  917.             req = parser.recvWants(pckIn);
  918.             currentRequest = req;

  919.             wantIds = req.getWantIds();

  920.             if (req.getWantIds().isEmpty()) {
  921.                 preUploadHook.onBeginNegotiateRound(this, req.getWantIds(), 0);
  922.                 preUploadHook.onEndNegotiateRound(this, req.getWantIds(), 0, 0,
  923.                         false);
  924.                 return;
  925.             }
  926.             accumulator.wants = req.getWantIds().size();

  927.             if (req.getClientCapabilities().contains(OPTION_MULTI_ACK_DETAILED)) {
  928.                 multiAck = MultiAck.DETAILED;
  929.                 noDone = req.getClientCapabilities().contains(OPTION_NO_DONE);
  930.             } else if (req.getClientCapabilities().contains(OPTION_MULTI_ACK))
  931.                 multiAck = MultiAck.CONTINUE;
  932.             else
  933.                 multiAck = MultiAck.OFF;

  934.             if (!req.getClientShallowCommits().isEmpty()) {
  935.                 verifyClientShallow(req.getClientShallowCommits());
  936.             }

  937.             if (req.getDepth() != 0 || req.getDeepenSince() != 0) {
  938.                 computeShallowsAndUnshallows(req, shallow -> {
  939.                     pckOut.writeString("shallow " + shallow.name() + '\n'); //$NON-NLS-1$
  940.                 }, unshallow -> {
  941.                     pckOut.writeString("unshallow " + unshallow.name() + '\n'); //$NON-NLS-1$
  942.                     unshallowCommits.add(unshallow);
  943.                 }, Collections.emptyList());
  944.                 pckOut.end();
  945.             }

  946.             if (!req.getClientShallowCommits().isEmpty())
  947.                 walk.assumeShallow(req.getClientShallowCommits());
  948.             sendPack = negotiate(req, accumulator, pckOut);
  949.             accumulator.timeNegotiating = Duration
  950.                     .between(negotiateStart, Instant.now()).toMillis();

  951.             if (sendPack && !biDirectionalPipe) {
  952.                 // Ensure the request was fully consumed. Any remaining input must
  953.                 // be a protocol error. If we aren't at EOF the implementation is broken.
  954.                 int eof = rawIn.read();
  955.                 if (0 <= eof) {
  956.                     sendPack = false;
  957.                     throw new CorruptObjectException(MessageFormat.format(
  958.                             JGitText.get().expectedEOFReceived,
  959.                             "\\x" + Integer.toHexString(eof))); //$NON-NLS-1$
  960.                 }
  961.             }
  962.         } finally {
  963.             if (!sendPack && !biDirectionalPipe) {
  964.                 while (0 < rawIn.skip(2048) || 0 <= rawIn.read()) {
  965.                     // Discard until EOF.
  966.                 }
  967.             }
  968.             rawOut.stopBuffering();
  969.         }

  970.         if (sendPack) {
  971.             sendPack(accumulator, req, refs == null ? null : refs.values(),
  972.                     unshallowCommits, Collections.emptyList(), pckOut);
  973.         }
  974.     }

  975.     private void lsRefsV2(PacketLineOut pckOut) throws IOException {
  976.         ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig);
  977.         LsRefsV2Request req = parser.parseLsRefsRequest(pckIn);
  978.         protocolV2Hook.onLsRefs(req);

  979.         rawOut.stopBuffering();
  980.         PacketLineOutRefAdvertiser adv = new PacketLineOutRefAdvertiser(pckOut);
  981.         adv.setUseProtocolV2(true);
  982.         if (req.getPeel()) {
  983.             adv.setDerefTags(true);
  984.         }
  985.         Map<String, Ref> refsToSend = getFilteredRefs(req.getRefPrefixes());
  986.         if (req.getSymrefs()) {
  987.             findSymrefs(adv, refsToSend);
  988.         }

  989.         adv.send(refsToSend.values());
  990.         adv.end();
  991.     }

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

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

  999.         for (String refName : wanted) {
  1000.             Ref ref = resolved.get(refName);
  1001.             if (ref == null) {
  1002.                 throw new PackProtocolException(MessageFormat
  1003.                         .format(JGitText.get().invalidRefName, refName));
  1004.             }
  1005.             ObjectId oid = ref.getObjectId();
  1006.             if (oid == null) {
  1007.                 throw new PackProtocolException(MessageFormat
  1008.                         .format(JGitText.get().invalidRefName, refName));
  1009.             }
  1010.             result.put(refName, oid);
  1011.         }
  1012.         return result;
  1013.     }

  1014.     private void fetchV2(PacketLineOut pckOut) throws IOException {
  1015.         // Depending on the requestValidator, #processHaveLines may
  1016.         // require that advertised be set. Set it only in the required
  1017.         // circumstances (to avoid a full ref lookup in the case that
  1018.         // we don't need it).
  1019.         if (requestValidator instanceof TipRequestValidator ||
  1020.                 requestValidator instanceof ReachableCommitTipRequestValidator ||
  1021.                 requestValidator instanceof AnyRequestValidator) {
  1022.             advertised = Collections.emptySet();
  1023.         } else {
  1024.             advertised = refIdSet(getAdvertisedOrDefaultRefs().values());
  1025.         }

  1026.         PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator();
  1027.         Instant negotiateStart = Instant.now();

  1028.         ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig);
  1029.         FetchV2Request req = parser.parseFetchRequest(pckIn);
  1030.         currentRequest = req;
  1031.         rawOut.stopBuffering();

  1032.         protocolV2Hook.onFetch(req);

  1033.         if (req.getSidebandAll()) {
  1034.             pckOut.setUsingSideband(true);
  1035.         }

  1036.         // TODO(ifrade): Refactor to pass around the Request object, instead of
  1037.         // copying data back to class fields
  1038.         List<ObjectId> deepenNots = new ArrayList<>();
  1039.         for (String s : req.getDeepenNotRefs()) {
  1040.             Ref ref = findRef(s);
  1041.             if (ref == null) {
  1042.                 throw new PackProtocolException(MessageFormat
  1043.                         .format(JGitText.get().invalidRefName, s));
  1044.             }
  1045.             deepenNots.add(ref.getObjectId());
  1046.         }

  1047.         Map<String, ObjectId> wantedRefs = wantedRefs(req);
  1048.         // TODO(ifrade): Avoid mutating the parsed request.
  1049.         req.getWantIds().addAll(wantedRefs.values());
  1050.         wantIds = req.getWantIds();

  1051.         boolean sectionSent = false;
  1052.         boolean mayHaveShallow = req.getDepth() != 0
  1053.                 || req.getDeepenSince() != 0
  1054.                 || !req.getDeepenNotRefs().isEmpty();
  1055.         List<ObjectId> shallowCommits = new ArrayList<>();
  1056.         List<ObjectId> unshallowCommits = new ArrayList<>();

  1057.         if (!req.getClientShallowCommits().isEmpty()) {
  1058.             verifyClientShallow(req.getClientShallowCommits());
  1059.         }
  1060.         if (mayHaveShallow) {
  1061.             computeShallowsAndUnshallows(req,
  1062.                     shallowCommit -> shallowCommits.add(shallowCommit),
  1063.                     unshallowCommit -> unshallowCommits.add(unshallowCommit),
  1064.                     deepenNots);
  1065.         }
  1066.         if (!req.getClientShallowCommits().isEmpty())
  1067.             walk.assumeShallow(req.getClientShallowCommits());

  1068.         if (req.wasDoneReceived()) {
  1069.             processHaveLines(req.getPeerHas(), ObjectId.zeroId(),
  1070.                     new PacketLineOut(NullOutputStream.INSTANCE, false),
  1071.                     accumulator);
  1072.         } else {
  1073.             pckOut.writeString(
  1074.                     GitProtocolConstants.SECTION_ACKNOWLEDGMENTS + '\n');
  1075.             for (ObjectId id : req.getPeerHas()) {
  1076.                 if (walk.getObjectReader().has(id)) {
  1077.                     pckOut.writeString("ACK " + id.getName() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1078.                 }
  1079.             }
  1080.             processHaveLines(req.getPeerHas(), ObjectId.zeroId(),
  1081.                     new PacketLineOut(NullOutputStream.INSTANCE, false),
  1082.                     accumulator);
  1083.             if (okToGiveUp()) {
  1084.                 pckOut.writeString("ready\n"); //$NON-NLS-1$
  1085.             } else if (commonBase.isEmpty()) {
  1086.                 pckOut.writeString("NAK\n"); //$NON-NLS-1$
  1087.             }
  1088.             sectionSent = true;
  1089.         }

  1090.         if (req.wasDoneReceived() || okToGiveUp()) {
  1091.             if (mayHaveShallow) {
  1092.                 if (sectionSent)
  1093.                     pckOut.writeDelim();
  1094.                 pckOut.writeString("shallow-info\n"); //$NON-NLS-1$
  1095.                 for (ObjectId o : shallowCommits) {
  1096.                     pckOut.writeString("shallow " + o.getName() + '\n'); //$NON-NLS-1$
  1097.                 }
  1098.                 for (ObjectId o : unshallowCommits) {
  1099.                     pckOut.writeString("unshallow " + o.getName() + '\n'); //$NON-NLS-1$
  1100.                 }
  1101.                 sectionSent = true;
  1102.             }

  1103.             if (!wantedRefs.isEmpty()) {
  1104.                 if (sectionSent) {
  1105.                     pckOut.writeDelim();
  1106.                 }
  1107.                 pckOut.writeString("wanted-refs\n"); //$NON-NLS-1$
  1108.                 for (Map.Entry<String, ObjectId> entry :
  1109.                         wantedRefs.entrySet()) {
  1110.                     pckOut.writeString(entry.getValue().getName() + ' ' +
  1111.                             entry.getKey() + '\n');
  1112.                 }
  1113.                 sectionSent = true;
  1114.             }

  1115.             if (sectionSent)
  1116.                 pckOut.writeDelim();
  1117.             if (!pckOut.isUsingSideband()) {
  1118.                 // sendPack will write "packfile\n" for us if sideband-all is used.
  1119.                 // But sideband-all is not used, so we have to write it ourselves.
  1120.                 pckOut.writeString(
  1121.                         GitProtocolConstants.SECTION_PACKFILE + '\n');
  1122.             }

  1123.             accumulator.timeNegotiating = Duration
  1124.                     .between(negotiateStart, Instant.now()).toMillis();

  1125.             sendPack(accumulator,
  1126.                     req,
  1127.                     req.getClientCapabilities().contains(OPTION_INCLUDE_TAG)
  1128.                         ? db.getRefDatabase().getRefsByPrefix(R_TAGS)
  1129.                         : null,
  1130.                     unshallowCommits, deepenNots, pckOut);
  1131.             // sendPack invokes pckOut.end() for us, so we do not
  1132.             // need to invoke it here.
  1133.         } else {
  1134.             // Invoke pckOut.end() by ourselves.
  1135.             pckOut.end();
  1136.         }
  1137.     }

  1138.     /*
  1139.      * Returns true if this is the last command and we should tear down the
  1140.      * connection.
  1141.      */
  1142.     private boolean serveOneCommandV2(PacketLineOut pckOut) throws IOException {
  1143.         String command;
  1144.         try {
  1145.             command = pckIn.readString();
  1146.         } catch (EOFException eof) {
  1147.             /* EOF when awaiting command is fine */
  1148.             return true;
  1149.         }
  1150.         if (PacketLineIn.isEnd(command)) {
  1151.             // A blank request is valid according
  1152.             // to the protocol; do nothing in this
  1153.             // case.
  1154.             return true;
  1155.         }
  1156.         if (command.equals("command=" + COMMAND_LS_REFS)) { //$NON-NLS-1$
  1157.             lsRefsV2(pckOut);
  1158.             return false;
  1159.         }
  1160.         if (command.equals("command=" + COMMAND_FETCH)) { //$NON-NLS-1$
  1161.             fetchV2(pckOut);
  1162.             return false;
  1163.         }
  1164.         throw new PackProtocolException(MessageFormat
  1165.                 .format(JGitText.get().unknownTransportCommand, command));
  1166.     }

  1167.     @SuppressWarnings("nls")
  1168.     private List<String> getV2CapabilityAdvertisement() {
  1169.         ArrayList<String> caps = new ArrayList<>();
  1170.         caps.add("version 2");
  1171.         caps.add(COMMAND_LS_REFS);
  1172.         boolean advertiseRefInWant = transferConfig.isAllowRefInWant()
  1173.                 && db.getConfig().getBoolean("uploadpack", null,
  1174.                         "advertiserefinwant", true);
  1175.         caps.add(COMMAND_FETCH + '='
  1176.                 + (transferConfig.isAllowFilter() ? OPTION_FILTER + ' ' : "")
  1177.                 + (advertiseRefInWant ? CAPABILITY_REF_IN_WANT + ' ' : "")
  1178.                 + (transferConfig.isAdvertiseSidebandAll()
  1179.                         ? OPTION_SIDEBAND_ALL + ' '
  1180.                         : "")
  1181.                 + (cachedPackUriProvider != null ? "packfile-uris " : "")
  1182.                 + OPTION_SHALLOW);
  1183.         caps.add(CAPABILITY_SERVER_OPTION);
  1184.         return caps;
  1185.     }

  1186.     private void serviceV2(PacketLineOut pckOut) throws IOException {
  1187.         if (biDirectionalPipe) {
  1188.             // Just like in service(), the capability advertisement
  1189.             // is sent only if this is a bidirectional pipe. (If
  1190.             // not, the client is expected to call
  1191.             // sendAdvertisedRefs() on its own.)
  1192.             protocolV2Hook
  1193.                     .onCapabilities(CapabilitiesV2Request.builder().build());
  1194.             for (String s : getV2CapabilityAdvertisement()) {
  1195.                 pckOut.writeString(s + "\n"); //$NON-NLS-1$
  1196.             }
  1197.             pckOut.end();

  1198.             while (!serveOneCommandV2(pckOut)) {
  1199.                 // Repeat until an empty command or EOF.
  1200.             }
  1201.             return;
  1202.         }

  1203.         try {
  1204.             serveOneCommandV2(pckOut);
  1205.         } finally {
  1206.             while (0 < rawIn.skip(2048) || 0 <= rawIn.read()) {
  1207.                 // Discard until EOF.
  1208.             }
  1209.             rawOut.stopBuffering();
  1210.         }
  1211.     }

  1212.     private static Set<ObjectId> refIdSet(Collection<Ref> refs) {
  1213.         Set<ObjectId> ids = new HashSet<>(refs.size());
  1214.         for (Ref ref : refs) {
  1215.             ObjectId id = ref.getObjectId();
  1216.             if (id != null) {
  1217.                 ids.add(id);
  1218.             }
  1219.             id = ref.getPeeledObjectId();
  1220.             if (id != null) {
  1221.                 ids.add(id);
  1222.             }
  1223.         }
  1224.         return ids;
  1225.     }

  1226.     /*
  1227.      * Determines what object ids must be marked as shallow or unshallow for the
  1228.      * client.
  1229.      */
  1230.     private void computeShallowsAndUnshallows(FetchRequest req,
  1231.             IOConsumer<ObjectId> shallowFunc,
  1232.             IOConsumer<ObjectId> unshallowFunc,
  1233.             List<ObjectId> deepenNots)
  1234.             throws IOException {
  1235.         if (req.getClientCapabilities().contains(OPTION_DEEPEN_RELATIVE)) {
  1236.             // TODO(jonathantanmy): Implement deepen-relative
  1237.             throw new UnsupportedOperationException();
  1238.         }

  1239.         int walkDepth = req.getDepth() == 0 ? Integer.MAX_VALUE
  1240.                 : req.getDepth() - 1;
  1241.         try (DepthWalk.RevWalk depthWalk = new DepthWalk.RevWalk(
  1242.                 walk.getObjectReader(), walkDepth)) {

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

  1244.             // Find all the commits which will be shallow
  1245.             for (ObjectId o : req.getWantIds()) {
  1246.                 try {
  1247.                     depthWalk.markRoot(depthWalk.parseCommit(o));
  1248.                 } catch (IncorrectObjectTypeException notCommit) {
  1249.                     // Ignore non-commits in this loop.
  1250.                 }
  1251.             }

  1252.             depthWalk.setDeepenNots(deepenNots);

  1253.             RevCommit o;
  1254.             boolean atLeastOne = false;
  1255.             while ((o = depthWalk.next()) != null) {
  1256.                 DepthWalk.Commit c = (DepthWalk.Commit) o;
  1257.                 atLeastOne = true;

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

  1259.                 // Commits at the boundary which aren't already shallow in
  1260.                 // the client need to be marked as such
  1261.                 if (isBoundary && !req.getClientShallowCommits().contains(c)) {
  1262.                     shallowFunc.accept(c.copy());
  1263.                 }

  1264.                 // Commits not on the boundary which are shallow in the client
  1265.                 // need to become unshallowed
  1266.                 if (!isBoundary && req.getClientShallowCommits().remove(c)) {
  1267.                     unshallowFunc.accept(c.copy());
  1268.                 }
  1269.             }
  1270.             if (!atLeastOne) {
  1271.                 throw new PackProtocolException(
  1272.                     JGitText.get().noCommitsSelectedForShallow);
  1273.             }
  1274.         }
  1275.     }

  1276.     /*
  1277.      * Verify all shallow lines refer to commits
  1278.      *
  1279.      * It can mutate the input set (removing missing object ids from it)
  1280.      */
  1281.     private void verifyClientShallow(Set<ObjectId> shallowCommits)
  1282.             throws IOException, PackProtocolException {
  1283.         AsyncRevObjectQueue q = walk.parseAny(shallowCommits, true);
  1284.         try {
  1285.             for (;;) {
  1286.                 try {
  1287.                     // Shallow objects named by the client must be commits.
  1288.                     RevObject o = q.next();
  1289.                     if (o == null) {
  1290.                         break;
  1291.                     }
  1292.                     if (!(o instanceof RevCommit)) {
  1293.                         throw new PackProtocolException(
  1294.                             MessageFormat.format(
  1295.                                 JGitText.get().invalidShallowObject,
  1296.                                 o.name()));
  1297.                     }
  1298.                 } catch (MissingObjectException notCommit) {
  1299.                     // shallow objects not known at the server are ignored
  1300.                     // by git-core upload-pack, match that behavior.
  1301.                     shallowCommits.remove(notCommit.getObjectId());
  1302.                     continue;
  1303.                 }
  1304.             }
  1305.         } finally {
  1306.             q.release();
  1307.         }
  1308.     }

  1309.     /**
  1310.      * Generate an advertisement of available refs and capabilities.
  1311.      *
  1312.      * @param adv
  1313.      *            the advertisement formatter.
  1314.      * @throws java.io.IOException
  1315.      *            the formatter failed to write an advertisement.
  1316.      * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
  1317.      *            the hook denied advertisement.
  1318.      */
  1319.     public void sendAdvertisedRefs(RefAdvertiser adv) throws IOException,
  1320.             ServiceMayNotContinueException {
  1321.         sendAdvertisedRefs(adv, null);
  1322.     }

  1323.     /**
  1324.      * Generate an advertisement of available refs and capabilities.
  1325.      *
  1326.      * @param adv
  1327.      *            the advertisement formatter.
  1328.      * @param serviceName
  1329.      *            if not null, also output "# service=serviceName" followed by a
  1330.      *            flush packet before the advertisement. This is required
  1331.      *            in v0 of the HTTP protocol, described in Git's
  1332.      *            Documentation/technical/http-protocol.txt.
  1333.      * @throws java.io.IOException
  1334.      *            the formatter failed to write an advertisement.
  1335.      * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException
  1336.      *            the hook denied advertisement.
  1337.      * @since 5.0
  1338.      */
  1339.     public void sendAdvertisedRefs(RefAdvertiser adv,
  1340.             @Nullable String serviceName) throws IOException,
  1341.             ServiceMayNotContinueException {
  1342.         if (useProtocolV2()) {
  1343.             // The equivalent in v2 is only the capabilities
  1344.             // advertisement.
  1345.             protocolV2Hook
  1346.                     .onCapabilities(CapabilitiesV2Request.builder().build());
  1347.             for (String s : getV2CapabilityAdvertisement()) {
  1348.                 adv.writeOne(s);
  1349.             }
  1350.             adv.end();
  1351.             return;
  1352.         }

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

  1354.         if (serviceName != null) {
  1355.             adv.writeOne("# service=" + serviceName + '\n'); //$NON-NLS-1$
  1356.             adv.end();
  1357.         }
  1358.         adv.init(db);
  1359.         adv.advertiseCapability(OPTION_INCLUDE_TAG);
  1360.         adv.advertiseCapability(OPTION_MULTI_ACK_DETAILED);
  1361.         adv.advertiseCapability(OPTION_MULTI_ACK);
  1362.         adv.advertiseCapability(OPTION_OFS_DELTA);
  1363.         adv.advertiseCapability(OPTION_SIDE_BAND);
  1364.         adv.advertiseCapability(OPTION_SIDE_BAND_64K);
  1365.         adv.advertiseCapability(OPTION_THIN_PACK);
  1366.         adv.advertiseCapability(OPTION_NO_PROGRESS);
  1367.         adv.advertiseCapability(OPTION_SHALLOW);
  1368.         if (!biDirectionalPipe)
  1369.             adv.advertiseCapability(OPTION_NO_DONE);
  1370.         RequestPolicy policy = getRequestPolicy();
  1371.         if (policy == RequestPolicy.TIP
  1372.                 || policy == RequestPolicy.REACHABLE_COMMIT_TIP
  1373.                 || policy == null)
  1374.             adv.advertiseCapability(OPTION_ALLOW_TIP_SHA1_IN_WANT);
  1375.         if (policy == RequestPolicy.REACHABLE_COMMIT
  1376.                 || policy == RequestPolicy.REACHABLE_COMMIT_TIP
  1377.                 || policy == null)
  1378.             adv.advertiseCapability(OPTION_ALLOW_REACHABLE_SHA1_IN_WANT);
  1379.         adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
  1380.         if (transferConfig.isAllowFilter()) {
  1381.             adv.advertiseCapability(OPTION_FILTER);
  1382.         }
  1383.         adv.setDerefTags(true);
  1384.         findSymrefs(adv, advertisedOrDefaultRefs);
  1385.         advertised = adv.send(advertisedOrDefaultRefs.values());

  1386.         if (adv.isEmpty())
  1387.             adv.advertiseId(ObjectId.zeroId(), "capabilities^{}"); //$NON-NLS-1$
  1388.         adv.end();
  1389.     }

  1390.     /**
  1391.      * Send a message to the client, if it supports receiving them.
  1392.      * <p>
  1393.      * If the client doesn't support receiving messages, the message will be
  1394.      * discarded, with no other indication to the caller or to the client.
  1395.      *
  1396.      * @param what
  1397.      *            string describing the problem identified by the hook. The
  1398.      *            string must not end with an LF, and must not contain an LF.
  1399.      * @since 3.1
  1400.      */
  1401.     public void sendMessage(String what) {
  1402.         try {
  1403.             msgOut.write(Constants.encode(what + "\n")); //$NON-NLS-1$
  1404.         } catch (IOException e) {
  1405.             // Ignore write failures.
  1406.         }
  1407.     }

  1408.     /**
  1409.      * Get an underlying stream for sending messages to the client
  1410.      *
  1411.      * @return an underlying stream for sending messages to the client, or null.
  1412.      * @since 3.1
  1413.      */
  1414.     public OutputStream getMessageOutputStream() {
  1415.         return msgOut;
  1416.     }

  1417.     /**
  1418.      * Returns the clone/fetch depth. Valid only after calling recvWants(). A
  1419.      * depth of 1 means return only the wants.
  1420.      *
  1421.      * @return the depth requested by the client, or 0 if unbounded.
  1422.      * @since 4.0
  1423.      */
  1424.     public int getDepth() {
  1425.         if (currentRequest == null)
  1426.             throw new RequestNotYetReadException();
  1427.         return currentRequest.getDepth();
  1428.     }

  1429.     /**
  1430.      * Deprecated synonym for {@code getFilterSpec().getBlobLimit()}.
  1431.      *
  1432.      * @return filter blob limit requested by the client, or -1 if no limit
  1433.      * @since 5.3
  1434.      * @deprecated Use {@link #getFilterSpec()} instead
  1435.      */
  1436.     @Deprecated
  1437.     public final long getFilterBlobLimit() {
  1438.         return getFilterSpec().getBlobLimit();
  1439.     }

  1440.     /**
  1441.      * Returns the filter spec for the current request. Valid only after
  1442.      * calling recvWants(). This may be a no-op filter spec, but it won't be
  1443.      * null.
  1444.      *
  1445.      * @return filter requested by the client
  1446.      * @since 5.4
  1447.      */
  1448.     public final FilterSpec getFilterSpec() {
  1449.         if (currentRequest == null) {
  1450.             throw new RequestNotYetReadException();
  1451.         }
  1452.         return currentRequest.getFilterSpec();
  1453.     }

  1454.     /**
  1455.      * Get the user agent of the client.
  1456.      * <p>
  1457.      * If the client is new enough to use {@code agent=} capability that value
  1458.      * will be returned. Older HTTP clients may also supply their version using
  1459.      * the HTTP {@code User-Agent} header. The capability overrides the HTTP
  1460.      * header if both are available.
  1461.      * <p>
  1462.      * When an HTTP request has been received this method returns the HTTP
  1463.      * {@code User-Agent} header value until capabilities have been parsed.
  1464.      *
  1465.      * @return user agent supplied by the client. Available only if the client
  1466.      *         is new enough to advertise its user agent.
  1467.      * @since 4.0
  1468.      */
  1469.     public String getPeerUserAgent() {
  1470.         if (currentRequest != null && currentRequest.getAgent() != null) {
  1471.             return currentRequest.getAgent();
  1472.         }

  1473.         return userAgent;
  1474.     }

  1475.     private boolean negotiate(FetchRequest req,
  1476.             PackStatistics.Accumulator accumulator,
  1477.             PacketLineOut pckOut)
  1478.             throws IOException {
  1479.         okToGiveUp = Boolean.FALSE;

  1480.         ObjectId last = ObjectId.zeroId();
  1481.         List<ObjectId> peerHas = new ArrayList<>(64);
  1482.         for (;;) {
  1483.             String line;
  1484.             try {
  1485.                 line = pckIn.readString();
  1486.             } catch (EOFException eof) {
  1487.                 // EOF on stateless RPC (aka smart HTTP) and non-shallow request
  1488.                 // means the client asked for the updated shallow/unshallow data,
  1489.                 // disconnected, and will try another request with actual want/have.
  1490.                 // Don't report the EOF here, its a bug in the protocol that the client
  1491.                 // just disconnects without sending an END.
  1492.                 if (!biDirectionalPipe && req.getDepth() > 0)
  1493.                     return false;
  1494.                 throw eof;
  1495.             }

  1496.             if (PacketLineIn.isEnd(line)) {
  1497.                 last = processHaveLines(peerHas, last, pckOut, accumulator);
  1498.                 if (commonBase.isEmpty() || multiAck != MultiAck.OFF)
  1499.                     pckOut.writeString("NAK\n"); //$NON-NLS-1$
  1500.                 if (noDone && sentReady) {
  1501.                     pckOut.writeString("ACK " + last.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1502.                     return true;
  1503.                 }
  1504.                 if (!biDirectionalPipe)
  1505.                     return false;
  1506.                 pckOut.flush();

  1507.             } else if (line.startsWith("have ") && line.length() == 45) { //$NON-NLS-1$
  1508.                 peerHas.add(ObjectId.fromString(line.substring(5)));
  1509.                 accumulator.haves++;
  1510.             } else if (line.equals("done")) { //$NON-NLS-1$
  1511.                 last = processHaveLines(peerHas, last, pckOut, accumulator);

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

  1514.                 else if (multiAck != MultiAck.OFF)
  1515.                     pckOut.writeString("ACK " + last.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$

  1516.                 return true;

  1517.             } else {
  1518.                 throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "have", line)); //$NON-NLS-1$
  1519.             }
  1520.         }
  1521.     }

  1522.     private ObjectId processHaveLines(List<ObjectId> peerHas, ObjectId last,
  1523.             PacketLineOut out, PackStatistics.Accumulator accumulator)
  1524.             throws IOException {
  1525.         preUploadHook.onBeginNegotiateRound(this, wantIds, peerHas.size());
  1526.         if (wantAll.isEmpty() && !wantIds.isEmpty())
  1527.             parseWants(accumulator);
  1528.         if (peerHas.isEmpty())
  1529.             return last;

  1530.         sentReady = false;
  1531.         int haveCnt = 0;
  1532.         walk.getObjectReader().setAvoidUnreachableObjects(true);
  1533.         AsyncRevObjectQueue q = walk.parseAny(peerHas, false);
  1534.         try {
  1535.             for (;;) {
  1536.                 RevObject obj;
  1537.                 try {
  1538.                     obj = q.next();
  1539.                 } catch (MissingObjectException notFound) {
  1540.                     continue;
  1541.                 }
  1542.                 if (obj == null)
  1543.                     break;

  1544.                 last = obj;
  1545.                 haveCnt++;

  1546.                 if (obj instanceof RevCommit) {
  1547.                     RevCommit c = (RevCommit) obj;
  1548.                     if (oldestTime == 0 || c.getCommitTime() < oldestTime)
  1549.                         oldestTime = c.getCommitTime();
  1550.                 }

  1551.                 if (obj.has(PEER_HAS))
  1552.                     continue;

  1553.                 obj.add(PEER_HAS);
  1554.                 if (obj instanceof RevCommit)
  1555.                     ((RevCommit) obj).carry(PEER_HAS);
  1556.                 addCommonBase(obj);

  1557.                 // If both sides have the same object; let the client know.
  1558.                 //
  1559.                 switch (multiAck) {
  1560.                 case OFF:
  1561.                     if (commonBase.size() == 1)
  1562.                         out.writeString("ACK " + obj.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1563.                     break;
  1564.                 case CONTINUE:
  1565.                     out.writeString("ACK " + obj.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1566.                     break;
  1567.                 case DETAILED:
  1568.                     out.writeString("ACK " + obj.name() + " common\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1569.                     break;
  1570.                 }
  1571.             }
  1572.         } finally {
  1573.             q.release();
  1574.             walk.getObjectReader().setAvoidUnreachableObjects(false);
  1575.         }

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

  1577.         // If we don't have one of the objects but we're also willing to
  1578.         // create a pack at this point, let the client know so it stops
  1579.         // telling us about its history.
  1580.         //
  1581.         boolean didOkToGiveUp = false;
  1582.         if (0 < missCnt) {
  1583.             for (int i = peerHas.size() - 1; i >= 0; i--) {
  1584.                 ObjectId id = peerHas.get(i);
  1585.                 if (walk.lookupOrNull(id) == null) {
  1586.                     didOkToGiveUp = true;
  1587.                     if (okToGiveUp()) {
  1588.                         switch (multiAck) {
  1589.                         case OFF:
  1590.                             break;
  1591.                         case CONTINUE:
  1592.                             out.writeString("ACK " + id.name() + " continue\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1593.                             break;
  1594.                         case DETAILED:
  1595.                             out.writeString("ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1596.                             sentReady = true;
  1597.                             break;
  1598.                         }
  1599.                     }
  1600.                     break;
  1601.                 }
  1602.             }
  1603.         }

  1604.         if (multiAck == MultiAck.DETAILED && !didOkToGiveUp && okToGiveUp()) {
  1605.             ObjectId id = peerHas.get(peerHas.size() - 1);
  1606.             out.writeString("ACK " + id.name() + " ready\n"); //$NON-NLS-1$ //$NON-NLS-2$
  1607.             sentReady = true;
  1608.         }

  1609.         preUploadHook.onEndNegotiateRound(this, wantAll, haveCnt, missCnt, sentReady);
  1610.         peerHas.clear();
  1611.         return last;
  1612.     }

  1613.     private void parseWants(PackStatistics.Accumulator accumulator) throws IOException {
  1614.         List<ObjectId> notAdvertisedWants = null;
  1615.         for (ObjectId obj : wantIds) {
  1616.             if (!advertised.contains(obj)) {
  1617.                 if (notAdvertisedWants == null)
  1618.                     notAdvertisedWants = new ArrayList<>();
  1619.                 notAdvertisedWants.add(obj);
  1620.             }
  1621.         }
  1622.         if (notAdvertisedWants != null) {
  1623.             accumulator.notAdvertisedWants = notAdvertisedWants.size();

  1624.             Instant startReachabilityChecking = Instant.now();

  1625.             requestValidator.checkWants(this, notAdvertisedWants);

  1626.             accumulator.reachabilityCheckDuration = Duration
  1627.                     .between(startReachabilityChecking, Instant.now())
  1628.                     .toMillis();
  1629.         }

  1630.         AsyncRevObjectQueue q = walk.parseAny(wantIds, true);
  1631.         try {
  1632.             RevObject obj;
  1633.             while ((obj = q.next()) != null) {
  1634.                 want(obj);

  1635.                 if (!(obj instanceof RevCommit))
  1636.                     obj.add(SATISFIED);
  1637.                 if (obj instanceof RevTag) {
  1638.                     obj = walk.peel(obj);
  1639.                     if (obj instanceof RevCommit)
  1640.                         want(obj);
  1641.                 }
  1642.             }
  1643.             wantIds.clear();
  1644.         } catch (MissingObjectException notFound) {
  1645.             throw new WantNotValidException(notFound.getObjectId(), notFound);
  1646.         } finally {
  1647.             q.release();
  1648.         }
  1649.     }

  1650.     private void want(RevObject obj) {
  1651.         if (!obj.has(WANT)) {
  1652.             obj.add(WANT);
  1653.             wantAll.add(obj);
  1654.         }
  1655.     }

  1656.     /**
  1657.      * Validator corresponding to {@link RequestPolicy#ADVERTISED}.
  1658.      *
  1659.      * @since 3.1
  1660.      */
  1661.     public static final class AdvertisedRequestValidator
  1662.             implements RequestValidator {
  1663.         @Override
  1664.         public void checkWants(UploadPack up, List<ObjectId> wants)
  1665.                 throws PackProtocolException, IOException {
  1666.             if (!up.isBiDirectionalPipe())
  1667.                 new ReachableCommitRequestValidator().checkWants(up, wants);
  1668.             else if (!wants.isEmpty())
  1669.                 throw new WantNotValidException(wants.iterator().next());
  1670.         }
  1671.     }

  1672.     /**
  1673.      * Validator corresponding to {@link RequestPolicy#REACHABLE_COMMIT}.
  1674.      *
  1675.      * @since 3.1
  1676.      */
  1677.     public static final class ReachableCommitRequestValidator
  1678.             implements RequestValidator {
  1679.         @Override
  1680.         public void checkWants(UploadPack up, List<ObjectId> wants)
  1681.                 throws PackProtocolException, IOException {
  1682.             checkNotAdvertisedWants(up, wants, up.getAdvertisedRefs().values());
  1683.         }
  1684.     }

  1685.     /**
  1686.      * Validator corresponding to {@link RequestPolicy#TIP}.
  1687.      *
  1688.      * @since 3.1
  1689.      */
  1690.     public static final class TipRequestValidator implements RequestValidator {
  1691.         @Override
  1692.         public void checkWants(UploadPack up, List<ObjectId> wants)
  1693.                 throws PackProtocolException, IOException {
  1694.             if (!up.isBiDirectionalPipe())
  1695.                 new ReachableCommitTipRequestValidator().checkWants(up, wants);
  1696.             else if (!wants.isEmpty()) {
  1697.                 Set<ObjectId> refIds =
  1698.                         refIdSet(up.getRepository().getRefDatabase().getRefs());
  1699.                 for (ObjectId obj : wants) {
  1700.                     if (!refIds.contains(obj))
  1701.                         throw new WantNotValidException(obj);
  1702.                 }
  1703.             }
  1704.         }
  1705.     }

  1706.     /**
  1707.      * Validator corresponding to {@link RequestPolicy#REACHABLE_COMMIT_TIP}.
  1708.      *
  1709.      * @since 3.1
  1710.      */
  1711.     public static final class ReachableCommitTipRequestValidator
  1712.             implements RequestValidator {
  1713.         @Override
  1714.         public void checkWants(UploadPack up, List<ObjectId> wants)
  1715.                 throws PackProtocolException, IOException {
  1716.             checkNotAdvertisedWants(up, wants,
  1717.                     up.getRepository().getRefDatabase().getRefs());
  1718.         }
  1719.     }

  1720.     /**
  1721.      * Validator corresponding to {@link RequestPolicy#ANY}.
  1722.      *
  1723.      * @since 3.1
  1724.      */
  1725.     public static final class AnyRequestValidator implements RequestValidator {
  1726.         @Override
  1727.         public void checkWants(UploadPack up, List<ObjectId> wants)
  1728.                 throws PackProtocolException, IOException {
  1729.             // All requests are valid.
  1730.         }
  1731.     }

  1732.     private static void checkNotAdvertisedWants(UploadPack up,
  1733.             List<ObjectId> notAdvertisedWants, Collection<Ref> visibleRefs)
  1734.             throws IOException {

  1735.         ObjectReader reader = up.getRevWalk().getObjectReader();

  1736.         try (RevWalk walk = new RevWalk(reader)) {
  1737.             walk.setRetainBody(false);
  1738.             // Missing "wants" throw exception here
  1739.             List<RevObject> wantsAsObjs = objectIdsToRevObjects(walk,
  1740.                     notAdvertisedWants);
  1741.             List<RevCommit> wantsAsCommits = wantsAsObjs.stream()
  1742.                     .filter(obj -> obj instanceof RevCommit)
  1743.                     .map(obj -> (RevCommit) obj)
  1744.                     .collect(Collectors.toList());
  1745.             boolean allWantsAreCommits = wantsAsObjs.size() == wantsAsCommits
  1746.                     .size();
  1747.             boolean repoHasBitmaps = reader.getBitmapIndex() != null;

  1748.             if (!allWantsAreCommits) {
  1749.                 if (!repoHasBitmaps && !up.transferConfig.isAllowFilter()) {
  1750.                     // Checking unadvertised non-commits without bitmaps
  1751.                     // requires an expensive manual walk. Use allowFilter as an
  1752.                     // indication that the server operator is willing to pay
  1753.                     // this cost. Reject the request otherwise.
  1754.                     RevObject nonCommit = wantsAsObjs
  1755.                             .stream()
  1756.                             .filter(obj -> !(obj instanceof RevCommit))
  1757.                             .limit(1)
  1758.                             .collect(Collectors.toList()).get(0);
  1759.                     throw new WantNotValidException(nonCommit);
  1760.                 }

  1761.                 try (ObjectWalk objWalk = walk.toObjectWalkWithSameObjects()) {
  1762.                     Stream<RevObject> startersAsObjs = importantRefsFirst(visibleRefs)
  1763.                             .map(UploadPack::refToObjectId)
  1764.                             .map(objId -> objectIdToRevObject(objWalk, objId))
  1765.                             .filter(Objects::nonNull); // Ignore missing tips

  1766.                     ObjectReachabilityChecker reachabilityChecker = reader
  1767.                             .createObjectReachabilityChecker(objWalk);
  1768.                     Optional<RevObject> unreachable = reachabilityChecker
  1769.                             .areAllReachable(wantsAsObjs, startersAsObjs);
  1770.                     if (unreachable.isPresent()) {
  1771.                         throw new WantNotValidException(unreachable.get());
  1772.                     }
  1773.                 }
  1774.                 return;
  1775.             }

  1776.             // All wants are commits, we can use ReachabilityChecker
  1777.             ReachabilityChecker reachabilityChecker = reader
  1778.                     .createReachabilityChecker(walk);

  1779.             Stream<RevCommit> reachableCommits = importantRefsFirst(visibleRefs)
  1780.                     .map(UploadPack::refToObjectId)
  1781.                     .map(objId -> objectIdToRevCommit(walk, objId))
  1782.                     .filter(Objects::nonNull); // Ignore missing tips

  1783.             Optional<RevCommit> unreachable = reachabilityChecker
  1784.                     .areAllReachable(wantsAsCommits, reachableCommits);
  1785.             if (unreachable.isPresent()) {
  1786.                 throw new WantNotValidException(unreachable.get());
  1787.             }

  1788.         } catch (MissingObjectException notFound) {
  1789.             throw new WantNotValidException(notFound.getObjectId(), notFound);
  1790.         }
  1791.     }

  1792.     static Stream<Ref> importantRefsFirst(
  1793.             Collection<Ref> visibleRefs) {
  1794.         Predicate<Ref> startsWithRefsHeads = ref -> ref.getName()
  1795.                 .startsWith(Constants.R_HEADS);
  1796.         Predicate<Ref> startsWithRefsTags = ref -> ref.getName()
  1797.                 .startsWith(Constants.R_TAGS);
  1798.         Predicate<Ref> allOther = ref -> !startsWithRefsHeads.test(ref)
  1799.                 && !startsWithRefsTags.test(ref);

  1800.         return Stream.concat(
  1801.                 visibleRefs.stream().filter(startsWithRefsHeads),
  1802.                 Stream.concat(
  1803.                         visibleRefs.stream().filter(startsWithRefsTags),
  1804.                         visibleRefs.stream().filter(allOther)));
  1805.     }

  1806.     private static ObjectId refToObjectId(Ref ref) {
  1807.         return ref.getObjectId() != null ? ref.getObjectId()
  1808.                 : ref.getPeeledObjectId();
  1809.     }

  1810.     /**
  1811.      * Translate an object id to a RevCommit.
  1812.      *
  1813.      * @param walk
  1814.      *            walk on the relevant object storae
  1815.      * @param objectId
  1816.      *            Object Id
  1817.      * @return RevCommit instance or null if the object is missing
  1818.      */
  1819.     @Nullable
  1820.     private static RevCommit objectIdToRevCommit(RevWalk walk,
  1821.             ObjectId objectId) {
  1822.         if (objectId == null) {
  1823.             return null;
  1824.         }

  1825.         try {
  1826.             return walk.parseCommit(objectId);
  1827.         } catch (IOException e) {
  1828.             return null;
  1829.         }
  1830.     }

  1831.     /**
  1832.      * Translate an object id to a RevObject.
  1833.      *
  1834.      * @param walk
  1835.      *            walk on the relevant object storage
  1836.      * @param objectId
  1837.      *            Object Id
  1838.      * @return RevObject instance or null if the object is missing
  1839.      */
  1840.     @Nullable
  1841.     private static RevObject objectIdToRevObject(RevWalk walk,
  1842.             ObjectId objectId) {
  1843.         if (objectId == null) {
  1844.             return null;
  1845.         }

  1846.         try {
  1847.             return walk.parseAny(objectId);
  1848.         } catch (IOException e) {
  1849.             return null;
  1850.         }
  1851.     }

  1852.     // Resolve the ObjectIds into RevObjects. Any missing object raises an
  1853.     // exception
  1854.     private static List<RevObject> objectIdsToRevObjects(RevWalk walk,
  1855.             Iterable<ObjectId> objectIds)
  1856.             throws MissingObjectException, IOException {
  1857.         List<RevObject> result = new ArrayList<>();
  1858.         for (ObjectId objectId : objectIds) {
  1859.             result.add(walk.parseAny(objectId));
  1860.         }
  1861.         return result;
  1862.     }

  1863.     private void addCommonBase(RevObject o) {
  1864.         if (!o.has(COMMON)) {
  1865.             o.add(COMMON);
  1866.             commonBase.add(o);
  1867.             okToGiveUp = null;
  1868.         }
  1869.     }

  1870.     private boolean okToGiveUp() throws PackProtocolException {
  1871.         if (okToGiveUp == null)
  1872.             okToGiveUp = Boolean.valueOf(okToGiveUpImp());
  1873.         return okToGiveUp.booleanValue();
  1874.     }

  1875.     private boolean okToGiveUpImp() throws PackProtocolException {
  1876.         if (commonBase.isEmpty())
  1877.             return false;

  1878.         try {
  1879.             for (RevObject obj : wantAll) {
  1880.                 if (!wantSatisfied(obj))
  1881.                     return false;
  1882.             }
  1883.             return true;
  1884.         } catch (IOException e) {
  1885.             throw new PackProtocolException(JGitText.get().internalRevisionError, e);
  1886.         }
  1887.     }

  1888.     private boolean wantSatisfied(RevObject want) throws IOException {
  1889.         if (want.has(SATISFIED))
  1890.             return true;

  1891.         walk.resetRetain(SAVE);
  1892.         walk.markStart((RevCommit) want);
  1893.         if (oldestTime != 0)
  1894.             walk.setRevFilter(CommitTimeRevFilter.after(oldestTime * 1000L));
  1895.         for (;;) {
  1896.             final RevCommit c = walk.next();
  1897.             if (c == null)
  1898.                 break;
  1899.             if (c.has(PEER_HAS)) {
  1900.                 addCommonBase(c);
  1901.                 want.add(SATISFIED);
  1902.                 return true;
  1903.             }
  1904.         }
  1905.         return false;
  1906.     }

  1907.     /**
  1908.      * Send the requested objects to the client.
  1909.      *
  1910.      * @param accumulator
  1911.      *            where to write statistics about the content of the pack.
  1912.      * @param req
  1913.      *            request in process
  1914.      * @param allTags
  1915.      *            refs to search for annotated tags to include in the pack if
  1916.      *            the {@link #OPTION_INCLUDE_TAG} capability was requested.
  1917.      * @param unshallowCommits
  1918.      *            shallow commits on the client that are now becoming unshallow
  1919.      * @param deepenNots
  1920.      *            objects that the client specified using --shallow-exclude
  1921.      * @param pckOut
  1922.      *            output writer
  1923.      * @throws IOException
  1924.      *             if an error occurred while generating or writing the pack.
  1925.      */
  1926.     private void sendPack(PackStatistics.Accumulator accumulator,
  1927.             FetchRequest req,
  1928.             @Nullable Collection<Ref> allTags,
  1929.             List<ObjectId> unshallowCommits,
  1930.             List<ObjectId> deepenNots,
  1931.             PacketLineOut pckOut) throws IOException {
  1932.         Set<String> caps = req.getClientCapabilities();
  1933.         boolean sideband = caps.contains(OPTION_SIDE_BAND)
  1934.                 || caps.contains(OPTION_SIDE_BAND_64K);

  1935.         if (sideband) {
  1936.             errOut = new SideBandErrorWriter();

  1937.             int bufsz = SideBandOutputStream.SMALL_BUF;
  1938.             if (req.getClientCapabilities().contains(OPTION_SIDE_BAND_64K)) {
  1939.                 bufsz = SideBandOutputStream.MAX_BUF;
  1940.             }
  1941.             OutputStream packOut = new SideBandOutputStream(
  1942.                     SideBandOutputStream.CH_DATA, bufsz, rawOut);

  1943.             ProgressMonitor pm = NullProgressMonitor.INSTANCE;
  1944.             if (!req.getClientCapabilities().contains(OPTION_NO_PROGRESS)) {
  1945.                 msgOut = new SideBandOutputStream(
  1946.                         SideBandOutputStream.CH_PROGRESS, bufsz, rawOut);
  1947.                 pm = new SideBandProgressMonitor(msgOut);
  1948.             }

  1949.             sendPack(pm, pckOut, packOut, req, accumulator, allTags,
  1950.                     unshallowCommits, deepenNots);
  1951.             pckOut.end();
  1952.         } else {
  1953.             sendPack(NullProgressMonitor.INSTANCE, pckOut, rawOut, req,
  1954.                     accumulator, allTags, unshallowCommits, deepenNots);
  1955.         }
  1956.     }

  1957.     /**
  1958.      * Send the requested objects to the client.
  1959.      *
  1960.      * @param pm
  1961.      *            progress monitor
  1962.      * @param pckOut
  1963.      *            PacketLineOut that shares the output with packOut
  1964.      * @param packOut
  1965.      *            packfile output
  1966.      * @param req
  1967.      *            request being processed
  1968.      * @param accumulator
  1969.      *            where to write statistics about the content of the pack.
  1970.      * @param allTags
  1971.      *            refs to search for annotated tags to include in the pack if
  1972.      *            the {@link #OPTION_INCLUDE_TAG} capability was requested.
  1973.      * @param unshallowCommits
  1974.      *            shallow commits on the client that are now becoming unshallow
  1975.      * @param deepenNots
  1976.      *            objects that the client specified using --shallow-exclude
  1977.      * @throws IOException
  1978.      *             if an error occurred while generating or writing the pack.
  1979.      */
  1980.     private void sendPack(ProgressMonitor pm, PacketLineOut pckOut,
  1981.             OutputStream packOut, FetchRequest req,
  1982.             PackStatistics.Accumulator accumulator,
  1983.             @Nullable Collection<Ref> allTags, List<ObjectId> unshallowCommits,
  1984.             List<ObjectId> deepenNots) throws IOException {
  1985.         if (wantAll.isEmpty()) {
  1986.             preUploadHook.onSendPack(this, wantIds, commonBase);
  1987.         } else {
  1988.             preUploadHook.onSendPack(this, wantAll, commonBase);
  1989.         }
  1990.         msgOut.flush();

  1991.         // Advertised objects and refs are not used from here on and can be
  1992.         // cleared.
  1993.         advertised = null;
  1994.         refs = null;

  1995.         PackConfig cfg = packConfig;
  1996.         if (cfg == null)
  1997.             cfg = new PackConfig(db);
  1998.         @SuppressWarnings("resource") // PackWriter is referenced in the finally
  1999.                                         // block, and is closed there
  2000.         final PackWriter pw = new PackWriter(cfg, walk.getObjectReader(),
  2001.                 accumulator);
  2002.         try {
  2003.             pw.setIndexDisabled(true);
  2004.             if (req.getFilterSpec().isNoOp()) {
  2005.                 pw.setUseCachedPacks(true);
  2006.             } else {
  2007.                 pw.setFilterSpec(req.getFilterSpec());
  2008.                 pw.setUseCachedPacks(false);
  2009.             }
  2010.             pw.setUseBitmaps(
  2011.                     req.getDepth() == 0
  2012.                             && req.getClientShallowCommits().isEmpty()
  2013.                             && req.getFilterSpec().getTreeDepthLimit() == -1);
  2014.             pw.setClientShallowCommits(req.getClientShallowCommits());
  2015.             pw.setReuseDeltaCommits(true);
  2016.             pw.setDeltaBaseAsOffset(
  2017.                     req.getClientCapabilities().contains(OPTION_OFS_DELTA));
  2018.             pw.setThin(req.getClientCapabilities().contains(OPTION_THIN_PACK));
  2019.             pw.setReuseValidatingObjects(false);

  2020.             // Objects named directly by references go at the beginning
  2021.             // of the pack.
  2022.             if (commonBase.isEmpty() && refs != null) {
  2023.                 Set<ObjectId> tagTargets = new HashSet<>();
  2024.                 for (Ref ref : refs.values()) {
  2025.                     if (ref.getPeeledObjectId() != null)
  2026.                         tagTargets.add(ref.getPeeledObjectId());
  2027.                     else if (ref.getObjectId() == null)
  2028.                         continue;
  2029.                     else if (ref.getName().startsWith(Constants.R_HEADS))
  2030.                         tagTargets.add(ref.getObjectId());
  2031.                 }
  2032.                 pw.setTagTargets(tagTargets);
  2033.             }

  2034.             RevWalk rw = walk;
  2035.             if (req.getDepth() > 0 || req.getDeepenSince() != 0 || !deepenNots.isEmpty()) {
  2036.                 int walkDepth = req.getDepth() == 0 ? Integer.MAX_VALUE
  2037.                         : req.getDepth() - 1;
  2038.                 pw.setShallowPack(req.getDepth(), unshallowCommits);

  2039.                 @SuppressWarnings("resource") // Ownership is transferred below
  2040.                 DepthWalk.RevWalk dw = new DepthWalk.RevWalk(
  2041.                         walk.getObjectReader(), walkDepth);
  2042.                 dw.setDeepenSince(req.getDeepenSince());
  2043.                 dw.setDeepenNots(deepenNots);
  2044.                 dw.assumeShallow(req.getClientShallowCommits());
  2045.                 rw = dw;
  2046.             }

  2047.             if (wantAll.isEmpty()) {
  2048.                 pw.preparePack(pm, wantIds, commonBase,
  2049.                         req.getClientShallowCommits());
  2050.             } else {
  2051.                 walk.reset();

  2052.                 ObjectWalk ow = rw.toObjectWalkWithSameObjects();
  2053.                 pw.preparePack(pm, ow, wantAll, commonBase, PackWriter.NONE);
  2054.                 rw = ow;
  2055.             }

  2056.             if (req.getClientCapabilities().contains(OPTION_INCLUDE_TAG)
  2057.                     && allTags != null) {
  2058.                 for (Ref ref : allTags) {
  2059.                     ObjectId objectId = ref.getObjectId();
  2060.                     if (objectId == null) {
  2061.                         // skip unborn branch
  2062.                         continue;
  2063.                     }

  2064.                     // If the object was already requested, skip it.
  2065.                     if (wantAll.isEmpty()) {
  2066.                         if (wantIds.contains(objectId))
  2067.                             continue;
  2068.                     } else {
  2069.                         RevObject obj = rw.lookupOrNull(objectId);
  2070.                         if (obj != null && obj.has(WANT))
  2071.                             continue;
  2072.                     }

  2073.                     if (!ref.isPeeled())
  2074.                         ref = db.getRefDatabase().peel(ref);

  2075.                     ObjectId peeledId = ref.getPeeledObjectId();
  2076.                     objectId = ref.getObjectId();
  2077.                     if (peeledId == null || objectId == null)
  2078.                         continue;

  2079.                     objectId = ref.getObjectId();
  2080.                     if (pw.willInclude(peeledId) && !pw.willInclude(objectId)) {
  2081.                         RevObject o = rw.parseAny(objectId);
  2082.                         addTagChain(o, pw);
  2083.                         pw.addObject(o);
  2084.                     }
  2085.                 }
  2086.             }

  2087.             if (pckOut.isUsingSideband()) {
  2088.                 if (req instanceof FetchV2Request &&
  2089.                         cachedPackUriProvider != null &&
  2090.                         !((FetchV2Request) req).getPackfileUriProtocols().isEmpty()) {
  2091.                     FetchV2Request reqV2 = (FetchV2Request) req;
  2092.                     pw.setPackfileUriConfig(new PackWriter.PackfileUriConfig(
  2093.                             pckOut,
  2094.                             reqV2.getPackfileUriProtocols(),
  2095.                             cachedPackUriProvider));
  2096.                 } else {
  2097.                     // PackWriter will write "packfile-uris\n" and "packfile\n"
  2098.                     // for us if provided a PackfileUriConfig. In this case, we
  2099.                     // are not providing a PackfileUriConfig, so we have to
  2100.                     // write this line ourselves.
  2101.                     pckOut.writeString(
  2102.                             GitProtocolConstants.SECTION_PACKFILE + '\n');
  2103.                 }
  2104.             }
  2105.             pw.writePack(pm, NullProgressMonitor.INSTANCE, packOut);

  2106.             if (msgOut != NullOutputStream.INSTANCE) {
  2107.                 String msg = pw.getStatistics().getMessage() + '\n';
  2108.                 msgOut.write(Constants.encode(msg));
  2109.                 msgOut.flush();
  2110.             }

  2111.         } finally {
  2112.             statistics = pw.getStatistics();
  2113.             if (statistics != null) {
  2114.                 postUploadHook.onPostUpload(statistics);
  2115.             }
  2116.             pw.close();
  2117.         }
  2118.     }

  2119.     private static void findSymrefs(
  2120.             final RefAdvertiser adv, final Map<String, Ref> refs) {
  2121.         Ref head = refs.get(Constants.HEAD);
  2122.         if (head != null && head.isSymbolic()) {
  2123.             adv.addSymref(Constants.HEAD, head.getLeaf().getName());
  2124.         }
  2125.     }

  2126.     private void addTagChain(
  2127.             RevObject o, PackWriter pw) throws IOException {
  2128.         while (Constants.OBJ_TAG == o.getType()) {
  2129.             RevTag t = (RevTag) o;
  2130.             o = t.getObject();
  2131.             if (o.getType() == Constants.OBJ_TAG && !pw.willInclude(o.getId())) {
  2132.                 walk.parseBody(o);
  2133.                 pw.addObject(o);
  2134.             }
  2135.         }
  2136.     }

  2137.     private static class ResponseBufferedOutputStream extends OutputStream {
  2138.         private final OutputStream rawOut;

  2139.         private OutputStream out;

  2140.         ResponseBufferedOutputStream(OutputStream rawOut) {
  2141.             this.rawOut = rawOut;
  2142.             this.out = new ByteArrayOutputStream();
  2143.         }

  2144.         @Override
  2145.         public void write(int b) throws IOException {
  2146.             out.write(b);
  2147.         }

  2148.         @Override
  2149.         public void write(byte[] b) throws IOException {
  2150.             out.write(b);
  2151.         }

  2152.         @Override
  2153.         public void write(byte[] b, int off, int len) throws IOException {
  2154.             out.write(b, off, len);
  2155.         }

  2156.         @Override
  2157.         public void flush() throws IOException {
  2158.             out.flush();
  2159.         }

  2160.         @Override
  2161.         public void close() throws IOException {
  2162.             out.close();
  2163.         }

  2164.         void stopBuffering() throws IOException {
  2165.             if (out != rawOut) {
  2166.                 ((ByteArrayOutputStream) out).writeTo(rawOut);
  2167.                 out = rawOut;
  2168.             }
  2169.         }
  2170.     }

  2171.     private interface ErrorWriter {
  2172.         void writeError(String message) throws IOException;
  2173.     }

  2174.     private class SideBandErrorWriter implements ErrorWriter {
  2175.         @Override
  2176.         public void writeError(String message) throws IOException {
  2177.             @SuppressWarnings("resource" /* java 7 */)
  2178.             SideBandOutputStream err = new SideBandOutputStream(
  2179.                     SideBandOutputStream.CH_ERROR,
  2180.                     SideBandOutputStream.SMALL_BUF, requireNonNull(rawOut));
  2181.             err.write(Constants.encode(message));
  2182.             err.flush();
  2183.         }
  2184.     }

  2185.     private class PackProtocolErrorWriter implements ErrorWriter {
  2186.         @Override
  2187.         public void writeError(String message) throws IOException {
  2188.             new PacketLineOut(requireNonNull(rawOut))
  2189.                     .writeString("ERR " + message + '\n'); //$NON-NLS-1$
  2190.         }
  2191.     }
  2192. }