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
This commit is contained in:
parent
7e0ebb8e29
commit
4a984e2033
|
@ -76,6 +76,7 @@
|
||||||
|
|
||||||
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 @@ protected void begin(HttpServletRequest req, Repository db)
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,7 @@
|
||||||
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 @@ protected void begin(HttpServletRequest req, Repository db)
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,8 @@
|
||||||
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 String getMessages() {
|
||||||
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();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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 @@ protected boolean wantCapability(final StringBuilder b, final String option) {
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -521,6 +521,7 @@ else if (wantCapability(line, OPTION_SIDE_BAND))
|
||||||
OPTION_MULTI_ACK_DETAILED));
|
OPTION_MULTI_ACK_DETAILED));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addUserAgentCapability(line);
|
||||||
return line.toString();
|
return line.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -268,6 +268,7 @@ private String enableCapabilities(final ProgressMonitor monitor,
|
||||||
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');
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
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 Set<String> getCapabilities() {
|
||||||
|
|
||||||
/** 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 boolean isSideBand() throws RequestNotYetReadException {
|
||||||
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 void sendAdvertisedRefs(final RefAdvertiser adv)
|
||||||
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);
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,6 +136,7 @@ private void executeImp(final ProgressMonitor monitor,
|
||||||
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)
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 @@ void addMessages(final String msg) {
|
||||||
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,6 +155,7 @@ PushResult execute(final ProgressMonitor monitor)
|
||||||
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();
|
||||||
|
|
||||||
|
|
|
@ -147,6 +147,21 @@ public void advertiseCapability(String name) {
|
||||||
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 void advertiseCapability(String name) {
|
||||||
* @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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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 Transport open(URIish uri, Repository local, String remoteName)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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 FetchConnection openFetch() throws TransportException,
|
||||||
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 FetchConnection openFetch() throws TransportException,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 PushConnection openPush() throws NotSupportedException,
|
||||||
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 PushConnection openPush() throws NotSupportedException,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 @@ protected HttpConnection httpOpen(String method, URL u)
|
||||||
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;
|
||||||
|
|
|
@ -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 Set<String> getOptions() {
|
||||||
|
|
||||||
/** 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 void sendAdvertisedRefs(final RefAdvertiser adv) throws IOException,
|
||||||
|| 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 @@ private void recvWants() throws IOException {
|
||||||
* @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;
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -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…
Reference in New Issue