FirstWant.java

  1. /*
  2.  * Copyright (C) 2018, Google LLC. 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.internal.transport.parser;

  11. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
  12. import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SESSION_ID;

  13. import java.util.Collections;
  14. import java.util.HashSet;
  15. import java.util.Set;

  16. import org.eclipse.jgit.annotations.Nullable;
  17. import org.eclipse.jgit.errors.PackProtocolException;
  18. import org.eclipse.jgit.internal.JGitText;

  19. /**
  20.  * In the pack negotiation phase (protocol v0/v1), the client sends a list of
  21.  * wants. The first "want" line is special, as it (can) have a list of
  22.  * capabilities appended.
  23.  *
  24.  * E.g. "want oid cap1 cap2 cap3"
  25.  *
  26.  * Do not confuse this line with the first one in the reference advertisement,
  27.  * which is sent by the server, looks like
  28.  * "b8f7c471373b8583ced0025cfad8c9916c484b76 HEAD\0 cap1 cap2 cap3" and is
  29.  * parsed by the BasePackConnection.readAdvertisedRefs method.
  30.  *
  31.  * This class parses the input want line and holds the results: the actual want
  32.  * line and the capabilities.
  33.  *
  34.  */
  35. public class FirstWant {
  36.     private final String line;

  37.     private final Set<String> capabilities;

  38.     @Nullable
  39.     private final String agent;

  40.     @Nullable
  41.     private final String clientSID;

  42.     private static final String AGENT_PREFIX = OPTION_AGENT + '=';

  43.     private static final String SESSION_ID_PREFIX = OPTION_SESSION_ID + '=';

  44.     /**
  45.      * Parse the first want line in the protocol v0/v1 pack negotiation.
  46.      *
  47.      * @param line
  48.      *            line from the client.
  49.      * @return an instance of FirstWant
  50.      * @throws PackProtocolException
  51.      *             if the line doesn't follow the protocol format.
  52.      */
  53.     public static FirstWant fromLine(String line) throws PackProtocolException {
  54.         String wantLine;
  55.         Set<String> capabilities;
  56.         String agent = null;
  57.         String clientSID = null;

  58.         if (line.length() > 45) {
  59.             String opt = line.substring(45);
  60.             if (!opt.startsWith(" ")) { //$NON-NLS-1$
  61.                 throw new PackProtocolException(JGitText.get().wantNoSpaceWithCapabilities);
  62.             }
  63.             opt = opt.substring(1);

  64.             HashSet<String> opts = new HashSet<>();
  65.             for (String clientCapability : opt.split(" ")) { //$NON-NLS-1$
  66.                 if (clientCapability.startsWith(AGENT_PREFIX)) {
  67.                     agent = clientCapability.substring(AGENT_PREFIX.length());
  68.                 } else if (clientCapability.startsWith(SESSION_ID_PREFIX)) {
  69.                     clientSID = clientCapability
  70.                             .substring(SESSION_ID_PREFIX.length());
  71.                 } else {
  72.                     opts.add(clientCapability);
  73.                 }
  74.             }
  75.             wantLine = line.substring(0, 45);
  76.             capabilities = Collections.unmodifiableSet(opts);
  77.         } else {
  78.             wantLine = line;
  79.             capabilities = Collections.emptySet();
  80.         }

  81.         return new FirstWant(wantLine, capabilities, agent, clientSID);
  82.     }

  83.     private FirstWant(String line, Set<String> capabilities,
  84.             @Nullable String agent, @Nullable String clientSID) {
  85.         this.line = line;
  86.         this.capabilities = capabilities;
  87.         this.agent = agent;
  88.         this.clientSID = clientSID;
  89.     }

  90.     /** @return non-capabilities part of the line. */
  91.     public String getLine() {
  92.         return line;
  93.     }

  94.     /**
  95.      * @return capabilities parsed from the line as an immutable set (excluding
  96.      *         agent and session-id).
  97.      */
  98.     public Set<String> getCapabilities() {
  99.         return capabilities;
  100.     }

  101.     /** @return client user agent parsed from the line. */
  102.     @Nullable
  103.     public String getAgent() {
  104.         return agent;
  105.     }

  106.     /** @return client session-id parsed from the line. */
  107.     @Nullable
  108.     public String getClientSID() {
  109.         return clientSID;
  110.     }
  111. }