Browse Source

Fix issues with LFS on GitHub (SSH)

 * URIish seems to have a tiny feature (bug?). The path of the URI
   starts with a '/' only if the URI has a port set (it seems).
 * GitHub does not return SSH authorization on a single line as Gerrit
   does - need to account for that.
 * Increase the SSH git-lfs-authenticate timeout, as GitHub sometimes
   responds slower than expected.
 * Guard against NPE in case the download action does not contain any
   additional headers.

Change-Id: Icd1ead3d015479fd4b8bbd42ed42129b0abfb95c
Signed-off-by: Markus Duft <markus.duft@ssi-schaefer.com>
tags/v5.0.0.201806131550-r
Markus Duft 6 years ago
parent
commit
01c52a58f6

+ 2
- 1
org.eclipse.jgit.lfs/META-INF/MANIFEST.MF View File

org.eclipse.jgit.treewalk;version="[5.0.0,5.1.0)", org.eclipse.jgit.treewalk;version="[5.0.0,5.1.0)",
org.eclipse.jgit.treewalk.filter;version="[5.0.0,5.1.0)", org.eclipse.jgit.treewalk.filter;version="[5.0.0,5.1.0)",
org.eclipse.jgit.util;version="[5.0.0,5.1.0)", org.eclipse.jgit.util;version="[5.0.0,5.1.0)",
org.eclipse.jgit.util.io;version="[5.0.0,5.1.0)"
org.eclipse.jgit.util.io;version="[5.0.0,5.1.0)",
org.slf4j;version="[1.7.0,2.0.0)"

+ 1
- 0
org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties View File

cannotDiscoverLfs=Cannot discover LFS URI
corruptLongObject=The content hash ''{0}'' of the long object ''{1}'' doesn''t match its id, the corrupt object will be deleted. corruptLongObject=The content hash ''{0}'' of the long object ''{1}'' doesn''t match its id, the corrupt object will be deleted.
incorrectLONG_OBJECT_ID_LENGTH=Incorrect LONG_OBJECT_ID_LENGTH. incorrectLONG_OBJECT_ID_LENGTH=Incorrect LONG_OBJECT_ID_LENGTH.
inconsistentMediafileLength=Mediafile {0} has unexpected length; expected {1} but found {2}. inconsistentMediafileLength=Mediafile {0} has unexpected length; expected {1} but found {2}.

+ 19
- 5
org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java View File

import org.eclipse.jgit.transport.http.HttpConnection; import org.eclipse.jgit.transport.http.HttpConnection;
import org.eclipse.jgit.util.HttpSupport; import org.eclipse.jgit.util.HttpSupport;
import org.eclipse.jgit.util.SshSupport; import org.eclipse.jgit.util.SshSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/** /**
* Provides means to get a valid LFS connection for a given repository. * Provides means to get a valid LFS connection for a given repository.
*/ */
public class LfsConnectionFactory { public class LfsConnectionFactory {


private static final int SSH_AUTH_TIMEOUT_SECONDS = 5;
private static final Logger log = LoggerFactory
.getLogger(LfsConnectionFactory.class);

private static final int SSH_AUTH_TIMEOUT_SECONDS = 30;
private static final String SCHEME_HTTPS = "https"; //$NON-NLS-1$ private static final String SCHEME_HTTPS = "https"; //$NON-NLS-1$
private static final String SCHEME_SSH = "ssh"; //$NON-NLS-1$ private static final String SCHEME_SSH = "ssh"; //$NON-NLS-1$
private static final Map<String, AuthCache> sshAuthCache = new TreeMap<>(); private static final Map<String, AuthCache> sshAuthCache = new TreeMap<>();
Map<String, String> additionalHeaders, String remoteUrl) { Map<String, String> additionalHeaders, String remoteUrl) {
try { try {
URIish u = new URIish(remoteUrl); URIish u = new URIish(remoteUrl);
if (SCHEME_SSH.equals(u.getScheme())) {
if (u.getScheme() == null || SCHEME_SSH.equals(u.getScheme())) {
Protocol.ExpiringAction action = getSshAuthentication( Protocol.ExpiringAction action = getSshAuthentication(
db, purpose, remoteUrl, u); db, purpose, remoteUrl, u);
additionalHeaders.putAll(action.header); additionalHeaders.putAll(action.header);
return remoteUrl + Protocol.INFO_LFS_ENDPOINT; return remoteUrl + Protocol.INFO_LFS_ENDPOINT;
} }
} catch (Exception e) { } catch (Exception e) {
log.error(LfsText.get().cannotDiscoverLfs, e);
return null; // could not discover return null; // could not discover
} }
} }
.create(contentUrl, HttpSupport .create(contentUrl, HttpSupport
.proxyFor(ProxySelector.getDefault(), contentUrl)); .proxyFor(ProxySelector.getDefault(), contentUrl));
contentServerConn.setRequestMethod(method); contentServerConn.setRequestMethod(method);
action.header
.forEach((k, v) -> contentServerConn.setRequestProperty(k, v));
if (action.header != null) {
action.header.forEach(
(k, v) -> contentServerConn.setRequestProperty(k, v));
}
if (contentUrl.getProtocol().equals(SCHEME_HTTPS) if (contentUrl.getProtocol().equals(SCHEME_HTTPS)
&& !repo.getConfig().getBoolean(HttpConfig.HTTP, && !repo.getConfig().getBoolean(HttpConfig.HTTP,
HttpConfig.SSL_VERIFY_KEY, true)) { HttpConfig.SSL_VERIFY_KEY, true)) {
} }


private static String extractProjectName(URIish u) { private static String extractProjectName(URIish u) {
String path = u.getPath().substring(1);
String path = u.getPath();

// begins with a slash if the url contains a port (gerrit vs. github).
if (path.startsWith("/")) { //$NON-NLS-1$
path = path.substring(1);
}

if (path.endsWith(org.eclipse.jgit.lib.Constants.DOT_GIT)) { if (path.endsWith(org.eclipse.jgit.lib.Constants.DOT_GIT)) {
return path.substring(0, path.length() - 4); return path.substring(0, path.length() - 4);
} else { } else {

+ 1
- 0
org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java View File

} }


// @formatter:off // @formatter:off
/***/ public String cannotDiscoverLfs;
/***/ public String corruptLongObject; /***/ public String corruptLongObject;
/***/ public String inconsistentMediafileLength; /***/ public String inconsistentMediafileLength;
/***/ public String inconsistentContentLength; /***/ public String inconsistentContentLength;

+ 28
- 10
org.eclipse.jgit/src/org/eclipse/jgit/util/SshSupport.java View File

*/ */
package org.eclipse.jgit.util; package org.eclipse.jgit.util;


import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader;


import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.RemoteSession; import org.eclipse.jgit.transport.RemoteSession;
import org.eclipse.jgit.transport.SshSessionFactory; import org.eclipse.jgit.transport.SshSessionFactory;
* @param command * @param command
* the remote command to execute. * the remote command to execute.
* @param timeout * @param timeout
* a timeout in seconds.
* @return The first line of output read from stdout. Stderr is discarded.
* a timeout in seconds. The timeout may be exceeded in corner
* cases.
* @return The entire output read from stdout. Stderr is discarded.
* @throws IOException * @throws IOException
*/ */
public static String runSshCommand(URIish sshUri, public static String runSshCommand(URIish sshUri,
RemoteSession session = null; RemoteSession session = null;
Process process = null; Process process = null;
StreamCopyThread errorThread = null; StreamCopyThread errorThread = null;
try (MessageWriter stderr = new MessageWriter()) {
StreamCopyThread outThread = null;
try (MessageWriter stderr = new MessageWriter();
MessageWriter stdout = new MessageWriter()) {
session = SshSessionFactory.getInstance().getSession(sshUri, session = SshSessionFactory.getInstance().getSession(sshUri,
provider, fs, 1000 * timeout); provider, fs, 1000 * timeout);
process = session.exec(command, 0); process = session.exec(command, 0);
errorThread = new StreamCopyThread(process.getErrorStream(), errorThread = new StreamCopyThread(process.getErrorStream(),
stderr.getRawStream()); stderr.getRawStream());
errorThread.start(); errorThread.start();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream(),
Constants.CHARSET))) {
return reader.readLine();
outThread = new StreamCopyThread(process.getInputStream(),
stdout.getRawStream());
outThread.start();
try {
// waitFor with timeout has a bug - JSch' exitValue() throws the
// wrong exception type :(
if (process.waitFor() == 0) {
return stdout.toString();
} else {
return null; // still running after timeout
}
} catch (InterruptedException e) {
return null; // error
} }
} finally { } finally {
if (errorThread != null) { if (errorThread != null) {
errorThread = null; errorThread = null;
} }
} }
if (outThread != null) {
try {
outThread.halt();
} catch (InterruptedException e) {
// Stop waiting and return anyway.
} finally {
outThread = null;
}
}
if (process != null) { if (process != null) {
process.destroy(); process.destroy();
} }

Loading…
Cancel
Save