# HG changeset patch # User Artem Tikhomirov # Date 1375788874 -7200 # Node ID 24f4efedc9d5690415623650e1658bc8ae08164c # Parent 5b5d199e2eb313cc5e25d9ef959e8a75a05c1418 Respect the fact ssh and http protocols use different compression approach to sent changegroup data diff -r 5b5d199e2eb3 -r 24f4efedc9d5 src/org/tmatesoft/hg/internal/FileUtils.java --- a/src/org/tmatesoft/hg/internal/FileUtils.java Mon Aug 05 19:03:22 2013 +0200 +++ b/src/org/tmatesoft/hg/internal/FileUtils.java Tue Aug 06 13:34:34 2013 +0200 @@ -23,6 +23,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.nio.channels.FileChannel; import org.tmatesoft.hg.core.HgIOException; @@ -93,6 +94,17 @@ * Windows uncached run: 1.6 seconds */ } + + public void write(InputStream is, File file) throws IOException { + FileOutputStream fos = new FileOutputStream(file); + int r; + byte[] buf = new byte[8*1024]; + while ((r = is.read(buf)) != -1) { + fos.write(buf, 0, r); + } + fos.flush(); + fos.close(); + } public void closeQuietly(Closeable stream) { closeQuietly(stream, null); diff -r 5b5d199e2eb3 -r 24f4efedc9d5 src/org/tmatesoft/hg/internal/remote/HttpConnector.java --- a/src/org/tmatesoft/hg/internal/remote/HttpConnector.java Mon Aug 05 19:03:22 2013 +0200 +++ b/src/org/tmatesoft/hg/internal/remote/HttpConnector.java Tue Aug 06 13:34:34 2013 +0200 @@ -19,11 +19,13 @@ import static org.tmatesoft.hg.util.LogFacility.Severity.Info; import java.io.BufferedReader; +import java.io.ByteArrayInputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.SequenceInputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; @@ -261,9 +263,13 @@ if (debug) { dumpResponseHeader(u); } - return conn.getInputStream(); - } catch (MalformedURLException ex) { // XXX in fact, this exception might be better to be re-thrown as RuntimeEx, - // as there's little user can do about this issue (URLs are constructed by our code) + InputStream cg = conn.getInputStream(); + InputStream prefix = new ByteArrayInputStream("HG10GZ".getBytes()); // didn't see any other that zip + return new SequenceInputStream(prefix, cg); + } catch (MalformedURLException ex) { + // although there's little user can do about this issue (URLs are constructed by our code) + // it's still better to throw it as checked exception than RT because url is likely malformed due to parameters + // and this may help user to understand the cause (and e.g. change them) throw new HgRemoteConnectionException("Bad URL", ex).setRemoteCommand("changegroup").setServerInfo(getServerLocation()); } catch (IOException ex) { throw new HgRemoteConnectionException("Communication failure", ex).setRemoteCommand("changegroup").setServerInfo(getServerLocation()); diff -r 5b5d199e2eb3 -r 24f4efedc9d5 src/org/tmatesoft/hg/internal/remote/SshConnector.java --- a/src/org/tmatesoft/hg/internal/remote/SshConnector.java Mon Aug 05 19:03:22 2013 +0200 +++ b/src/org/tmatesoft/hg/internal/remote/SshConnector.java Tue Aug 06 13:34:34 2013 +0200 @@ -17,6 +17,7 @@ package org.tmatesoft.hg.internal.remote; import java.io.BufferedReader; +import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.EOFException; import java.io.File; @@ -26,6 +27,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.SequenceInputStream; import java.net.URL; import java.util.ArrayList; import java.util.Collection; @@ -99,9 +101,11 @@ } try { session = conn.openSession(); - session.execCommand(String.format("hg -R %s serve --stdio", url.getPath())); + final String path = url.getPath(); + session.execCommand(String.format("hg -R %s serve --stdio", path.charAt(0) == '/' ? path.substring(1) : path)); remoteErr = new StreamGobbler(session.getStderr()); remoteOut = new StreamGobbler(session.getStdout()); + remoteIn = session.getStdin(); sessionUse = 1; } catch (IOException ex) { throw new HgRemoteConnectionException("Failed to create ssh session", ex); @@ -119,7 +123,7 @@ } public String getServerLocation() { - return ""; // FIXME + return url.toString(); // FIXME } public String getCapabilities() throws HgRemoteConnectionException { @@ -156,7 +160,7 @@ } public InputStream heads() throws HgRemoteConnectionException { - return executeCommand("heads", Collections.emptyList()); + return executeCommand("heads", Collections.emptyList(), true); } public InputStream between(Collection ranges) throws HgRemoteConnectionException { @@ -167,17 +171,19 @@ if (!ranges.isEmpty()) { sb.setLength(sb.length() - 1); } - return executeCommand("between", Collections.singletonList(new Parameter("pairs", sb.toString()))); + return executeCommand("between", Collections.singletonList(new Parameter("pairs", sb.toString())), true); } public InputStream branches(List nodes) throws HgRemoteConnectionException { String l = join(nodes, ' '); - return executeCommand("branches", Collections.singletonList(new Parameter("nodes", l))); + return executeCommand("branches", Collections.singletonList(new Parameter("nodes", l)), true); } public InputStream changegroup(List roots) throws HgRemoteConnectionException, HgRuntimeException { String l = join(roots, ' '); - return executeCommand("changegroup", Collections.singletonList(new Parameter("roots", l))); + InputStream cg = executeCommand("changegroup", Collections.singletonList(new Parameter("roots", l)), false); + InputStream prefix = new ByteArrayInputStream("HG10UN".getBytes()); + return new SequenceInputStream(prefix, cg); } public OutputStream unbundle(long outputLen, List remoteHeads) throws HgRemoteConnectionException, HgRuntimeException { @@ -210,14 +216,14 @@ p.add(new Parameter("key", key)); p.add(new Parameter("old", oldValue)); p.add(new Parameter("new", newValue)); - return executeCommand("pushkey", p); + return executeCommand("pushkey", p, true); } public InputStream listkeys(String namespace, String actionName) throws HgRemoteConnectionException, HgRuntimeException { - return executeCommand("listkeys", Collections.singletonList(new Parameter("namespace", namespace))); + return executeCommand("listkeys", Collections.singletonList(new Parameter("namespace", namespace)), true); } - private InputStream executeCommand(String cmd, List parameters) throws HgRemoteConnectionException { + private InputStream executeCommand(String cmd, List parameters, boolean expectResponseLength) throws HgRemoteConnectionException { try { consume(remoteOut); consume(remoteErr); @@ -225,9 +231,13 @@ remoteIn.write('\n'); writeParameters(parameters); checkError(); - int responseLen = readResponseLength(); - checkError(); - return new FilterStream(remoteOut, responseLen); + if (expectResponseLength) { + int responseLen = readResponseLength(); + checkError(); + return new FilterStream(remoteOut, responseLen); + } else { + return new FilterStream(remoteOut, Integer.MAX_VALUE); + } } catch (IOException ex) { throw new HgRemoteConnectionException("Communication failure", ex).setRemoteCommand(cmd).setServerInfo(getServerLocation()); } @@ -376,6 +386,7 @@ } @Override public void close() throws IOException { + length = 0; // INTENTIONALLY DOES NOT CLOSE THE STREAM } } diff -r 5b5d199e2eb3 -r 24f4efedc9d5 src/org/tmatesoft/hg/repo/HgRemoteRepository.java --- a/src/org/tmatesoft/hg/repo/HgRemoteRepository.java Mon Aug 05 19:03:22 2013 +0200 +++ b/src/org/tmatesoft/hg/repo/HgRemoteRepository.java Tue Aug 06 13:34:34 2013 +0200 @@ -23,7 +23,6 @@ import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -44,7 +43,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.zip.InflaterInputStream; import org.tmatesoft.hg.core.HgBadArgumentException; import org.tmatesoft.hg.core.HgIOException; @@ -61,6 +59,7 @@ import org.tmatesoft.hg.internal.PropertyMarshal; import org.tmatesoft.hg.internal.remote.Connector; import org.tmatesoft.hg.internal.remote.HttpConnector; +import org.tmatesoft.hg.internal.remote.SshConnector; import org.tmatesoft.hg.util.LogFacility.Severity; import org.tmatesoft.hg.util.Outcome; import org.tmatesoft.hg.util.Pair; @@ -115,7 +114,7 @@ } sessionContext = ctx; debug = new PropertyMarshal(ctx).getBoolean("hg4j.remote.debug", false); - remote = new HttpConnector(); + remote = "ssh".equals(url.getProtocol()) ? new SshConnector() : new HttpConnector(); remote.init(url, ctx, null); } @@ -281,7 +280,7 @@ List _roots = roots.isEmpty() ? Collections.singletonList(Nodeid.NULL) : roots; try { remote.sessionBegin(); - File tf = writeBundle(remote.changegroup(_roots), false, "HG10GZ" /*didn't see any other that zip*/); + File tf = writeBundle(remote.changegroup(_roots)); if (debug) { System.out.printf("Wrote bundle %s for roots %s\n", tf, roots); } @@ -459,18 +458,10 @@ } } - private static File writeBundle(InputStream is, boolean decompress, String header) throws IOException { - InputStream zipStream = decompress ? new InflaterInputStream(is) : is; + private File writeBundle(InputStream is) throws IOException { File tf = File.createTempFile("hg4j-bundle-", null); - FileOutputStream fos = new FileOutputStream(tf); - fos.write(header.getBytes()); - int r; - byte[] buf = new byte[8*1024]; - while ((r = zipStream.read(buf)) != -1) { - fos.write(buf, 0, r); - } - fos.close(); - zipStream.close(); + new FileUtils(sessionContext.getLog(), this).write(is, tf); + is.close(); return tf; }