| Summary: | jgit hangs when try to fetch through ssh | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| Product: | [Technology] JGit | Reporter: | Dmitry Neverov <dmitry.neverov> | ||||||
| Component: | JGit | Assignee: | Project Inbox <jgit.core-inbox> | ||||||
| Status: | RESOLVED FIXED | QA Contact: | |||||||
| Severity: | normal | ||||||||
| Priority: | P3 | CC: | dmitry.neverov, sop | ||||||
| Version: | unspecified | ||||||||
| Target Milestone: | 0.8.0 | ||||||||
| Hardware: | PC | ||||||||
| OS: | Linux | ||||||||
| Whiteboard: | |||||||||
| Attachments: |
|
||||||||
Created attachment 167876 [details]
patch to fix problem
Created attachment 167878 [details]
test to reproduce problem
Fixed by 3cba5377dfdad8c36d1f3b0642f56549821b8676 |
Build Identifier: standalone jgit library jgit hangs and returns only when timeout is expired when try to fetch through ssh. Reproducible: Always Steps to Reproduce: 1. Here is the test to reproduce this problem: public void testSshFetch() throws IOException, URISyntaxException { String url = "ssh://test@localhost:22/home/test/test-project"; File localRepositoryDir = new File("/tmp/test-ssh"); deleteFile(localRepositoryDir); Repository r = new Repository(localRepositoryDir); r.create(true); final RepositoryConfig config = r.getConfig(); config.setString("master", null, "remote", url); config.save(); final Transport t = Transport.open(r, url); SshTransport ssh = (SshTransport)t; ssh.setSshSessionFactory(new SshSessionFactory() { private final JSch sch = new JSch(); @Override public Session getSession(String user, String pass, String host, int port) throws JSchException { final Session session = sch.getSession(user, host, port); session.setConfig("StrictHostKeyChecking", "no"); session.setPassword("test"); return session; } }); t.setTimeout(100); try { final String refName = "refs/heads/master"; RefSpec spec = new RefSpec().setSource(refName).setDestination(refName).setForceUpdate(true); FetchResult fr = t.fetch(NullProgressMonitor.INSTANCE, Collections.singletonList(spec)); assertNotNull(fr); } finally { t.close(); } } Sometimes it runs without error, but more othen it hangs and return with exception: org.eclipse.jgit.errors.TransportException: Read timed out at org.eclipse.jgit.transport.BasePackConnection.readAdvertisedRefs(BasePackConnection.java:180) at org.eclipse.jgit.transport.TransportGitSsh$SshFetchConnection.<init>(TransportGitSsh.java:265) at org.eclipse.jgit.transport.TransportGitSsh.openFetch(TransportGitSsh.java:98) at org.eclipse.jgit.transport.FetchProcess.executeImp(FetchProcess.java:119) at org.eclipse.jgit.transport.FetchProcess.execute(FetchProcess.java:109) at org.eclipse.jgit.transport.Transport.fetch(Transport.java:814) at org.eclipse.jgit.transport.SshCloneTest.testSshFetch(SshCloneTest.java:50) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at com.intellij.junit3.JUnit3IdeaTestRunner.doRun(JUnit3IdeaTestRunner.java:108) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:64) 2. It seems to me that reason for that is that you connect to exec channel of ssh session before set it's input and output streams. When channel connects it writes something to remote host and remote host answers something, but since output stream is not set we do not get this answer and wait for it until timeout is over. 3. To fix this problem you can change method exec() in TransportGitSsh.java: ChannelExec exec(final String exe) throws TransportException { initSession(); final int tms = getTimeout() > 0 ? getTimeout() * 1000 : 0; try { final ChannelExec channel = (ChannelExec) sock.openChannel("exec"); channel.setCommand(commandFor(exe)); //channel.connect(tms); // <- do not connect until input, output and extOutput streams are set return channel; } catch (JSchException je) { throw new TransportException(uri, je.getMessage(), je); } } and constructor of class TransportGitSsh.SshFetchConnection: SshFetchConnection() throws TransportException { super(TransportGitSsh.this); try { final MessageWriter msg = new MessageWriter(); setMessageWriter(msg); channel = exec(getOptionUploadPack()); //if (!channel.isConnected()) // throw new TransportException(uri, "connection failed"); // <- do not connect until all streams are set final InputStream upErr = channel.getErrStream(); errorThread = new StreamCopyThread(upErr, msg.getRawStream()); errorThread.start(); init(channel.getInputStream(), outputStream(channel)); } catch (TransportException err) { close(); throw err; } catch (IOException err) { close(); throw new TransportException(uri, "remote hung up unexpectedly", err); } try { channel.connect(getTimeout()); // <- now we can connect if (!channel.isConnected()) throw new TransportException(uri, "connection failed"); } catch (JSchException e) { throw new TransportException(uri, e.getMessage(), e); } try { readAdvertisedRefs(); } catch (NoRemoteRepositoryException notFound) { final String msgs = getMessages(); checkExecFailure(exitStatus, getOptionUploadPack(), msgs); throw cleanNotFound(notFound, msgs); } } I hope it will help.