Browse Source

Support agent= capability in wire protocol

Since git-core ff5effd (v1.7.12.1) the native wire protocol transmits
the server and client implementation and version strings using
capability "agent=git/1.7.12.1" or similar.

Support this in JGit and hang the implementation data off UploadPack
and ReceivePack.  On HTTP transports default to the User-Agent HTTP
header until the client overrides this with the optional capability
string in the first line.

Extract the user agent string into a UserAgent class under transport
where it can be specified to a different value if the application's
build process has broken the Implementation-Version header in the
JGit package.

Change-Id: Icfc6524d84a787386d1786310b421b2f92ae9e65
stable-4.1
Shawn Pearce 10 years ago
parent
commit
4a984e2033
  1. 4
      org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
  2. 4
      org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
  3. 24
      org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseConnection.java
  4. 14
      org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
  5. 1
      org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
  6. 1
      org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
  7. 22
      org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
  8. 9
      org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java
  9. 1
      org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
  10. 7
      org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
  11. 80
      org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalHttpServerGlue.java
  12. 13
      org.eclipse.jgit/src/org/eclipse/jgit/transport/OperationResult.java
  13. 1
      org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
  14. 18
      org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
  15. 40
      org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
  16. 27
      org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
  17. 147
      org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java
  18. 6
      org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java

4
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java

@ -76,6 +76,7 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.errors.UnpackException; import org.eclipse.jgit.errors.UnpackException;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.InternalHttpServerGlue;
import org.eclipse.jgit.transport.ReceivePack; import org.eclipse.jgit.transport.ReceivePack;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser; import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
import org.eclipse.jgit.transport.resolver.ReceivePackFactory; import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
@ -100,6 +101,9 @@ class ReceivePackServlet extends HttpServlet {
throws IOException, ServiceNotEnabledException, throws IOException, ServiceNotEnabledException,
ServiceNotAuthorizedException { ServiceNotAuthorizedException {
ReceivePack rp = receivePackFactory.create(req, db); ReceivePack rp = receivePackFactory.create(req, db);
InternalHttpServerGlue.setPeerUserAgent(
rp,
req.getHeader(HDR_USER_AGENT));
req.setAttribute(ATTRIBUTE_HANDLER, rp); req.setAttribute(ATTRIBUTE_HANDLER, rp);
} }

4
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java

@ -74,6 +74,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.InternalHttpServerGlue;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser; import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
import org.eclipse.jgit.transport.UploadPack; import org.eclipse.jgit.transport.UploadPack;
import org.eclipse.jgit.transport.UploadPackInternalServerErrorException; import org.eclipse.jgit.transport.UploadPackInternalServerErrorException;
@ -100,6 +101,9 @@ class UploadPackServlet extends HttpServlet {
throws IOException, ServiceNotEnabledException, throws IOException, ServiceNotEnabledException,
ServiceNotAuthorizedException { ServiceNotAuthorizedException {
UploadPack up = uploadPackFactory.create(req, db); UploadPack up = uploadPackFactory.create(req, db);
InternalHttpServerGlue.setPeerUserAgent(
up,
req.getHeader(HDR_USER_AGENT));
req.setAttribute(ATTRIBUTE_HANDLER, up); req.setAttribute(ATTRIBUTE_HANDLER, up);
} }

24
org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseConnection.java

@ -65,6 +65,8 @@ import org.eclipse.jgit.lib.Ref;
public abstract class BaseConnection implements Connection { public abstract class BaseConnection implements Connection {
private Map<String, Ref> advertisedRefs = Collections.emptyMap(); private Map<String, Ref> advertisedRefs = Collections.emptyMap();
private String peerUserAgent;
private boolean startedOperation; private boolean startedOperation;
private Writer messageWriter; private Writer messageWriter;
@ -85,6 +87,28 @@ public abstract class BaseConnection implements Connection {
return messageWriter != null ? messageWriter.toString() : ""; //$NON-NLS-1$ return messageWriter != null ? messageWriter.toString() : ""; //$NON-NLS-1$
} }
/**
* User agent advertised by the remote server.
*
* @return agent (version of Git) running on the remote server. Null if the
* server does not advertise this version.
* @since 4.0
*/
public String getPeerUserAgent() {
return peerUserAgent;
}
/**
* Remember the remote peer's agent.
*
* @param agent
* remote peer agent string.
* @since 4.0
*/
protected void setPeerUserAgent(String agent) {
peerUserAgent = agent;
}
public abstract void close(); public abstract void close();
/** /**

14
org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java

@ -46,6 +46,8 @@
package org.eclipse.jgit.transport; package org.eclipse.jgit.transport;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -275,6 +277,18 @@ abstract class BasePackConnection extends BaseConnection {
return true; return true;
} }
protected void addUserAgentCapability(StringBuilder b) {
String a = UserAgent.get();
if (a != null && UserAgent.hasAgent(remoteCapablities)) {
b.append(' ').append(OPTION_AGENT).append('=').append(a);
}
}
@Override
public String getPeerUserAgent() {
return UserAgent.getAgent(remoteCapablities, super.getPeerUserAgent());
}
private PackProtocolException duplicateAdvertisement(final String name) { private PackProtocolException duplicateAdvertisement(final String name) {
return new PackProtocolException(uri, MessageFormat.format(JGitText.get().duplicateAdvertisementsOf, name)); return new PackProtocolException(uri, MessageFormat.format(JGitText.get().duplicateAdvertisementsOf, name));
} }

1
org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java

@ -521,6 +521,7 @@ public abstract class BasePackFetchConnection extends BasePackConnection
OPTION_MULTI_ACK_DETAILED)); OPTION_MULTI_ACK_DETAILED));
} }
addUserAgentCapability(line);
return line.toString(); return line.toString();
} }

1
org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java

@ -268,6 +268,7 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
outputStream); outputStream);
pckIn = new PacketLineIn(in); pckIn = new PacketLineIn(in);
} }
addUserAgentCapability(line);
if (line.length() > 0) if (line.length() > 0)
line.setCharAt(0, '\0'); line.setCharAt(0, '\0');

22
org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java

@ -48,6 +48,7 @@ import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_DELETE_
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_OFS_DELTA; import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_OFS_DELTA;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REPORT_STATUS; import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REPORT_STATUS;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K; import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
import static org.eclipse.jgit.transport.SideBandOutputStream.CH_DATA; import static org.eclipse.jgit.transport.SideBandOutputStream.CH_DATA;
import static org.eclipse.jgit.transport.SideBandOutputStream.CH_PROGRESS; import static org.eclipse.jgit.transport.SideBandOutputStream.CH_PROGRESS;
import static org.eclipse.jgit.transport.SideBandOutputStream.MAX_BUF; import static org.eclipse.jgit.transport.SideBandOutputStream.MAX_BUF;
@ -224,6 +225,7 @@ public abstract class BaseReceivePack {
/** Capabilities requested by the client. */ /** Capabilities requested by the client. */
private Set<String> enabledCapabilities; private Set<String> enabledCapabilities;
String userAgent;
private Set<ObjectId> clientShallowCommits; private Set<ObjectId> clientShallowCommits;
private List<ReceiveCommand> commands; private List<ReceiveCommand> commands;
@ -738,6 +740,25 @@ public abstract class BaseReceivePack {
return enabledCapabilities.contains(CAPABILITY_SIDE_BAND_64K); return enabledCapabilities.contains(CAPABILITY_SIDE_BAND_64K);
} }
/**
* Get the user agent of the client.
* <p>
* If the client is new enough to use {@code agent=} capability that value
* will be returned. Older HTTP clients may also supply their version using
* the HTTP {@code User-Agent} header. The capability overrides the HTTP
* header if both are available.
* <p>
* When an HTTP request has been received this method returns the HTTP
* {@code User-Agent} header value until capabilities have been parsed.
*
* @return user agent supplied by the client. Available only if the client
* is new enough to advertise its user agent.
* @since 4.0
*/
public String getPeerUserAgent() {
return UserAgent.getAgent(enabledCapabilities, userAgent);
}
/** @return all of the command received by the current request. */ /** @return all of the command received by the current request. */
public List<ReceiveCommand> getAllCommands() { public List<ReceiveCommand> getAllCommands() {
return Collections.unmodifiableList(commands); return Collections.unmodifiableList(commands);
@ -955,6 +976,7 @@ public abstract class BaseReceivePack {
adv.advertiseCapability(CAPABILITY_ATOMIC); adv.advertiseCapability(CAPABILITY_ATOMIC);
if (allowOfsDelta) if (allowOfsDelta)
adv.advertiseCapability(CAPABILITY_OFS_DELTA); adv.advertiseCapability(CAPABILITY_OFS_DELTA);
adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
adv.send(getAdvertisedOrDefaultRefs()); adv.send(getAdvertisedOrDefaultRefs());
for (ObjectId obj : advertisedHaves) for (ObjectId obj : advertisedHaves)
adv.advertiseHave(obj); adv.advertiseHave(obj);

9
org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java

@ -127,4 +127,13 @@ public interface Connection {
* remote produced no additional messages. * remote produced no additional messages.
*/ */
public String getMessages(); public String getMessages();
/**
* User agent advertised by the remote server.
*
* @return agent (version of Git) running on the remote server. Null if the
* server does not advertise this version.
* @since 4.0
*/
public String getPeerUserAgent();
} }

1
org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java

@ -136,6 +136,7 @@ class FetchProcess {
conn = transport.openFetch(); conn = transport.openFetch();
try { try {
result.setAdvertisedRefs(transport.getURI(), conn.getRefsMap()); result.setAdvertisedRefs(transport.getURI(), conn.getRefsMap());
result.peerUserAgent = conn.getPeerUserAgent();
final Set<Ref> matched = new HashSet<Ref>(); final Set<Ref> matched = new HashSet<Ref>();
for (final RefSpec spec : toFetch) { for (final RefSpec spec : toFetch) {
if (spec.getSource() == null) if (spec.getSource() == null)

7
org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java

@ -186,6 +186,13 @@ public class GitProtocolConstants {
*/ */
public static final String CAPABILITY_PUSH_CERT = "push-cert"; //$NON-NLS-1$ public static final String CAPABILITY_PUSH_CERT = "push-cert"; //$NON-NLS-1$
/**
* Implementation name and version of the client or server.
*
* @since 4.0
*/
public static final String OPTION_AGENT = "agent"; //$NON-NLS-1$
static enum MultiAck { static enum MultiAck {
OFF, CONTINUE, DETAILED; OFF, CONTINUE, DETAILED;
} }

80
org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalHttpServerGlue.java

@ -0,0 +1,80 @@
/*
* Copyright (C) 2015, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.transport;
/**
* Internal API to to assist {@code org.eclipse.jgit.http.server}.
* <p>
* <b>Do not call.</b>
*
* @since 4.0
*/
public class InternalHttpServerGlue {
/**
* Apply a default user agent for a request.
*
* @param up
* current UploadPack instance.
* @param agent
* user agent string from the HTTP headers.
*/
public static void setPeerUserAgent(UploadPack up, String agent) {
up.userAgent = agent;
}
/**
* Apply a default user agent for a request.
*
* @param rp
* current ReceivePack instance.
* @param agent
* user agent string from the HTTP headers.
*/
public static void setPeerUserAgent(ReceivePack rp, String agent) {
rp.userAgent = agent;
}
private InternalHttpServerGlue() {
}
}

13
org.eclipse.jgit/src/org/eclipse/jgit/transport/OperationResult.java

@ -68,6 +68,8 @@ public abstract class OperationResult {
StringBuilder messageBuffer; StringBuilder messageBuffer;
String peerUserAgent;
/** /**
* Get the URI this result came from. * Get the URI this result came from.
* <p> * <p>
@ -165,4 +167,15 @@ public abstract class OperationResult {
messageBuffer.append('\n'); messageBuffer.append('\n');
} }
} }
/**
* Get the user agent advertised by the peer server, if available.
*
* @return advertised user agent, e.g. {@code "JGit/4.0"}. Null if the peer
* did not advertise version information.
* @since 4.0
*/
public String getPeerUserAgent() {
return peerUserAgent;
}
} }

1
org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java

@ -155,6 +155,7 @@ class PushProcess {
try { try {
res.setAdvertisedRefs(transport.getURI(), connection res.setAdvertisedRefs(transport.getURI(), connection
.getRefsMap()); .getRefsMap());
res.peerUserAgent = connection.getPeerUserAgent();
res.setRemoteUpdates(toPush); res.setRemoteUpdates(toPush);
monitor.endTask(); monitor.endTask();

18
org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java

@ -147,6 +147,21 @@ public abstract class RefAdvertiser {
capablities.add(name); capablities.add(name);
} }
/**
* Add one protocol capability with a value ({@code "name=value"}).
*
* @param name
* name of the capability.
* @param value
* value. If null the capability will not be added.
* @since 4.0
*/
public void advertiseCapability(String name, String value) {
if (value != null) {
capablities.add(name + '=' + value);
}
}
/** /**
* Add a symbolic ref to capabilities. * Add a symbolic ref to capabilities.
* <p> * <p>
@ -164,8 +179,7 @@ public abstract class RefAdvertiser {
* @since 3.6 * @since 3.6
*/ */
public void addSymref(String from, String to) { public void addSymref(String from, String to) {
String symref = String.format("%s=%s:%s", OPTION_SYMREF, from, to); //$NON-NLS-1$ advertiseCapability(OPTION_SYMREF, from + ':' + to);
advertiseCapability(symref);
} }
/** /**

40
org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java

@ -134,8 +134,6 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
private static final String SVC_RECEIVE_PACK = "git-receive-pack"; //$NON-NLS-1$ private static final String SVC_RECEIVE_PACK = "git-receive-pack"; //$NON-NLS-1$
private static final String userAgent = computeUserAgent();
static final TransportProtocol PROTO_HTTP = new TransportProtocol() { static final TransportProtocol PROTO_HTTP = new TransportProtocol() {
private final String[] schemeNames = { "http", "https" }; //$NON-NLS-1$ //$NON-NLS-2$ private final String[] schemeNames = { "http", "https" }; //$NON-NLS-1$ //$NON-NLS-2$
@ -204,17 +202,6 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
} }
}; };
private static String computeUserAgent() {
String version;
final Package pkg = TransportHttp.class.getPackage();
if (pkg != null && pkg.getImplementationVersion() != null) {
version = pkg.getImplementationVersion();
} else {
version = "unknown"; //$NON-NLS-1$
}
return "JGit/" + version; //$NON-NLS-1$
}
private static final Config.SectionParser<HttpConfig> HTTP_KEY = new SectionParser<HttpConfig>() { private static final Config.SectionParser<HttpConfig> HTTP_KEY = new SectionParser<HttpConfig>() {
public HttpConfig parse(final Config cfg) { public HttpConfig parse(final Config cfg) {
return new HttpConfig(cfg); return new HttpConfig(cfg);
@ -309,16 +296,17 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
final HttpConnection c = connect(service); final HttpConnection c = connect(service);
final InputStream in = openInputStream(c); final InputStream in = openInputStream(c);
try { try {
BaseConnection f;
if (isSmartHttp(c, service)) { if (isSmartHttp(c, service)) {
readSmartHeaders(in, service); readSmartHeaders(in, service);
return new SmartHttpFetchConnection(in); f = new SmartHttpFetchConnection(in);
} else { } else {
// Assume this server doesn't support smart HTTP fetch // Assume this server doesn't support smart HTTP fetch
// and fall back on dumb object walking. // and fall back on dumb object walking.
// f = newDumbConnection(in);
return newDumbConnection(in);
} }
f.setPeerUserAgent(c.getHeaderField(HttpSupport.HDR_SERVER));
return (FetchConnection) f;
} finally { } finally {
in.close(); in.close();
} }
@ -331,7 +319,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
} }
} }
private FetchConnection newDumbConnection(InputStream in) private WalkFetchConnection newDumbConnection(InputStream in)
throws IOException, PackProtocolException { throws IOException, PackProtocolException {
HttpObjectDB d = new HttpObjectDB(objectsUrl); HttpObjectDB d = new HttpObjectDB(objectsUrl);
BufferedReader br = toBufferedReader(in); BufferedReader br = toBufferedReader(in);
@ -400,9 +388,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
final InputStream in = openInputStream(c); final InputStream in = openInputStream(c);
try { try {
if (isSmartHttp(c, service)) { if (isSmartHttp(c, service)) {
readSmartHeaders(in, service); return smartPush(service, c, in);
return new SmartHttpPushConnection(in);
} else if (!useSmartHttp) { } else if (!useSmartHttp) {
final String msg = JGitText.get().smartHTTPPushDisabled; final String msg = JGitText.get().smartHTTPPushDisabled;
throw new NotSupportedException(msg); throw new NotSupportedException(msg);
@ -423,6 +409,14 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
} }
} }
private PushConnection smartPush(String service, HttpConnection c,
InputStream in) throws IOException, TransportException {
readSmartHeaders(in, service);
SmartHttpPushConnection p = new SmartHttpPushConnection(in);
p.setPeerUserAgent(c.getHeaderField(HttpSupport.HDR_SERVER));
return p;
}
@Override @Override
public void close() { public void close() {
// No explicit connections are maintained. // No explicit connections are maintained.
@ -551,7 +545,9 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
conn.setUseCaches(false); conn.setUseCaches(false);
conn.setRequestProperty(HDR_ACCEPT_ENCODING, ENCODING_GZIP); conn.setRequestProperty(HDR_ACCEPT_ENCODING, ENCODING_GZIP);
conn.setRequestProperty(HDR_PRAGMA, "no-cache"); //$NON-NLS-1$ conn.setRequestProperty(HDR_PRAGMA, "no-cache"); //$NON-NLS-1$
conn.setRequestProperty(HDR_USER_AGENT, userAgent); if (UserAgent.get() != null) {
conn.setRequestProperty(HDR_USER_AGENT, UserAgent.get());
}
int timeOut = getTimeout(); int timeOut = getTimeout();
if (timeOut != -1) { if (timeOut != -1) {
int effTimeOut = timeOut * 1000; int effTimeOut = timeOut * 1000;

27
org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java

@ -44,6 +44,7 @@
package org.eclipse.jgit.transport; package org.eclipse.jgit.transport;
import static org.eclipse.jgit.lib.RefDatabase.ALL; import static org.eclipse.jgit.lib.RefDatabase.ALL;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_INCLUDE_TAG; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_INCLUDE_TAG;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK;
@ -253,6 +254,7 @@ public class UploadPack {
/** Capabilities requested by the client. */ /** Capabilities requested by the client. */
private Set<String> options; private Set<String> options;
String userAgent;
/** Raw ObjectIds the client has asked for, before validating them. */ /** Raw ObjectIds the client has asked for, before validating them. */
private final Set<ObjectId> wantIds = new HashSet<ObjectId>(); private final Set<ObjectId> wantIds = new HashSet<ObjectId>();
@ -806,6 +808,7 @@ public class UploadPack {
|| policy == RequestPolicy.REACHABLE_COMMIT_TIP || policy == RequestPolicy.REACHABLE_COMMIT_TIP
|| policy == null) || policy == null)
adv.advertiseCapability(OPTION_ALLOW_TIP_SHA1_IN_WANT); adv.advertiseCapability(OPTION_ALLOW_TIP_SHA1_IN_WANT);
adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
adv.setDerefTags(true); adv.setDerefTags(true);
Map<String, Ref> refs = getAdvertisedOrDefaultRefs(); Map<String, Ref> refs = getAdvertisedOrDefaultRefs();
findSymrefs(adv, refs); findSymrefs(adv, refs);
@ -891,12 +894,30 @@ public class UploadPack {
* @since 4.0 * @since 4.0
*/ */
public int getDepth() { public int getDepth() {
if (options == null) { if (options == null)
throw new IllegalStateException("do not call getDepth() before recvWants()"); throw new RequestNotYetReadException();
}
return depth; return depth;
} }
/**
* Get the user agent of the client.
* <p>
* If the client is new enough to use {@code agent=} capability that value
* will be returned. Older HTTP clients may also supply their version using
* the HTTP {@code User-Agent} header. The capability overrides the HTTP
* header if both are available.
* <p>
* When an HTTP request has been received this method returns the HTTP
* {@code User-Agent} header value until capabilities have been parsed.
*
* @return user agent supplied by the client. Available only if the client
* is new enough to advertise its user agent.
* @since 4.0
*/
public String getPeerUserAgent() {
return UserAgent.getAgent(options, userAgent);
}
private boolean negotiate() throws IOException { private boolean negotiate() throws IOException {
okToGiveUp = Boolean.FALSE; okToGiveUp = Boolean.FALSE;

147
org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java

@ -0,0 +1,147 @@
/*
* Copyright (C) 2015, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.transport;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
import java.util.Set;
import org.eclipse.jgit.util.StringUtils;
/**
* User agent to be reported by this JGit client and server on the network.
* <p>
* On HTTP transports this user agent string is always supplied by the JGit
* client in the {@code User-Agent} HTTP header.
* <p>
* On native transports this user agent string is always sent when JGit is a
* server. When JGit is a client the user agent string will be supplied to the
* remote server only if the remote server advertises its own agent identity.
*
* @since 4.0
*/
public class UserAgent {
private static volatile String userAgent = computeUserAgent();
private static String computeUserAgent() {
return clean("JGit/" + computeVersion()); //$NON-NLS-1$
}
private static String computeVersion() {
Package pkg = UserAgent.class.getPackage();
if (pkg != null) {
String ver = pkg.getImplementationVersion();
if (!StringUtils.isEmptyOrNull(ver)) {
return ver;
}
}
return "unknown"; //$NON-NLS-1$
}
private static String clean(String s) {
s = s.trim();
StringBuilder b = new StringBuilder(s.length());
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c <= 32 || c >= 127) {
if (b.length() > 0 && b.charAt(b.length() - 1) == '.')
continue;
c = '.';
}
b.append(c);
}
return b.length() > 0 ? b.toString() : null;
}
/**
* Get the user agent string advertised by JGit.
*
* @return a string similar to {@code "JGit/4.0"}; null if the agent has
* been cleared and should not be shared with a peer.
*/
public static String get() {
return userAgent;
}
/**
* Change the user agent string advertised by JGit.
* <p>
* The new string should start with {@code "JGit/"} (for example
* {@code "JGit/4.0"}) to advertise the implementation as JGit based.
* <p>
* Spaces and other whitespace should be avoided as these will be
* automatically converted to {@code "."}.
* <p>
* User agent strings are restricted to printable ASCII.
*
* @param agent
* new user agent string for this running JGit library. Setting
* to null or empty string will avoid sending any identification
* to the peer.
*/
public static void set(String agent) {
userAgent = StringUtils.isEmptyOrNull(agent) ? null : clean(agent);
}
static String getAgent(Set<String> options, String transportAgent) {
if (options == null || options.isEmpty()) {
return transportAgent;
}
for (String o : options) {
if (o.startsWith(OPTION_AGENT)
&& o.length() > OPTION_AGENT.length()
&& o.charAt(OPTION_AGENT.length()) == '=') {
return o.substring(OPTION_AGENT.length() + 1);
}
}
return transportAgent;
}
static boolean hasAgent(Set<String> options) {
return getAgent(options, null) != null;
}
private UserAgent() {
}
}

6
org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java

@ -74,6 +74,12 @@ public class HttpSupport {
/** The {@code User-Agent} header. */ /** The {@code User-Agent} header. */
public static final String HDR_USER_AGENT = "User-Agent"; //$NON-NLS-1$ public static final String HDR_USER_AGENT = "User-Agent"; //$NON-NLS-1$
/**
* The {@code Server} header.
* @since 4.0
*/
public static final String HDR_SERVER = "Server"; //$NON-NLS-1$
/** The {@code Date} header. */ /** The {@code Date} header. */
public static final String HDR_DATE = "Date"; //$NON-NLS-1$ public static final String HDR_DATE = "Date"; //$NON-NLS-1$

Loading…
Cancel
Save