Mercurial > jhg
comparison src/org/tmatesoft/hg/internal/remote/SshConnector.java @ 687:9859fcea475d
Towards ssh remote repositories: refactor HgRemoteRepository - move http related code to HttpConnector
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Sat, 27 Jul 2013 18:34:14 +0200 |
parents | 9897cbfd2790 |
children | 5b5d199e2eb3 |
comparison
equal
deleted
inserted
replaced
686:f1f095e42555 | 687:9859fcea475d |
---|---|
19 import java.io.BufferedReader; | 19 import java.io.BufferedReader; |
20 import java.io.Closeable; | 20 import java.io.Closeable; |
21 import java.io.EOFException; | 21 import java.io.EOFException; |
22 import java.io.File; | 22 import java.io.File; |
23 import java.io.FilterInputStream; | 23 import java.io.FilterInputStream; |
24 import java.io.FilterOutputStream; | |
24 import java.io.IOException; | 25 import java.io.IOException; |
25 import java.io.InputStream; | 26 import java.io.InputStream; |
26 import java.io.InputStreamReader; | 27 import java.io.InputStreamReader; |
27 import java.io.OutputStream; | 28 import java.io.OutputStream; |
28 import java.net.URL; | 29 import java.net.URL; |
29 import java.util.ArrayList; | 30 import java.util.ArrayList; |
30 import java.util.Arrays; | |
31 import java.util.Collection; | 31 import java.util.Collection; |
32 import java.util.Collections; | 32 import java.util.Collections; |
33 import java.util.HashSet; | |
34 import java.util.List; | 33 import java.util.List; |
35 import java.util.Set; | |
36 | 34 |
37 import org.tmatesoft.hg.core.HgRemoteConnectionException; | 35 import org.tmatesoft.hg.core.HgRemoteConnectionException; |
38 import org.tmatesoft.hg.core.Nodeid; | 36 import org.tmatesoft.hg.core.Nodeid; |
39 import org.tmatesoft.hg.core.SessionContext; | 37 import org.tmatesoft.hg.core.SessionContext; |
40 import org.tmatesoft.hg.internal.Internals; | |
41 import org.tmatesoft.hg.repo.HgBundle; | |
42 import org.tmatesoft.hg.repo.HgRemoteRepository.Range; | 38 import org.tmatesoft.hg.repo.HgRemoteRepository.Range; |
43 import org.tmatesoft.hg.repo.HgRuntimeException; | 39 import org.tmatesoft.hg.repo.HgRuntimeException; |
44 import org.tmatesoft.hg.util.LogFacility.Severity; | 40 import org.tmatesoft.hg.util.LogFacility.Severity; |
45 | 41 |
46 import ch.ethz.ssh2.Connection; | 42 import ch.ethz.ssh2.Connection; |
52 * Remote repository via SSH | 48 * Remote repository via SSH |
53 * | 49 * |
54 * @author Artem Tikhomirov | 50 * @author Artem Tikhomirov |
55 * @author TMate Software Ltd. | 51 * @author TMate Software Ltd. |
56 */ | 52 */ |
57 public class SshConnector { | 53 public class SshConnector implements Connector { |
58 private SessionContext sessionCtx; | 54 private SessionContext sessionCtx; |
59 private URL url; | 55 private URL url; |
60 private Connection conn; | 56 private Connection conn; |
61 private Session session; | 57 private Session session; |
62 private int sessionUse; | 58 private int sessionUse; |
63 | 59 |
64 private StreamGobbler remoteErr, remoteOut; | 60 private StreamGobbler remoteErr, remoteOut; |
65 private OutputStream remoteIn; | 61 private OutputStream remoteIn; |
66 | 62 |
67 public void connect(URL url, SessionContext sessionContext, Object globalConfig) throws HgRemoteConnectionException { | 63 public void init(URL url, SessionContext sessionContext, Object globalConfig) throws HgRuntimeException { |
68 sessionCtx = sessionContext; | 64 sessionCtx = sessionContext; |
69 this.url = url; | 65 this.url = url; |
66 } | |
67 | |
68 public void connect() throws HgRemoteConnectionException, HgRuntimeException { | |
70 try { | 69 try { |
71 conn = new Connection(url.getHost(), url.getPort() == -1 ? 22 : url.getPort()); | 70 conn = new Connection(url.getHost(), url.getPort() == -1 ? 22 : url.getPort()); |
72 conn.connect(); | 71 conn.connect(); |
73 } catch (IOException ex) { | 72 } catch (IOException ex) { |
74 throw new HgRemoteConnectionException("Failed to establish connection"); | 73 throw new HgRemoteConnectionException("Failed to establish connection"); |
76 try { | 75 try { |
77 conn.authenticateWithPublicKey(System.getProperty("user.name"), new File(System.getProperty("user.home"), ".ssh/id_rsa"), null); | 76 conn.authenticateWithPublicKey(System.getProperty("user.name"), new File(System.getProperty("user.home"), ".ssh/id_rsa"), null); |
78 ConnectionInfo ci = conn.getConnectionInfo(); | 77 ConnectionInfo ci = conn.getConnectionInfo(); |
79 System.out.printf("%s %s %s %d %s %s %s\n", ci.clientToServerCryptoAlgorithm, ci.clientToServerMACAlgorithm, ci.keyExchangeAlgorithm, ci.keyExchangeCounter, ci.serverHostKeyAlgorithm, ci.serverToClientCryptoAlgorithm, ci.serverToClientMACAlgorithm); | 78 System.out.printf("%s %s %s %d %s %s %s\n", ci.clientToServerCryptoAlgorithm, ci.clientToServerMACAlgorithm, ci.keyExchangeAlgorithm, ci.keyExchangeCounter, ci.serverHostKeyAlgorithm, ci.serverToClientCryptoAlgorithm, ci.serverToClientMACAlgorithm); |
80 } catch (IOException ex) { | 79 } catch (IOException ex) { |
81 throw new HgRemoteConnectionException("Failed to authenticate", ex).setServerInfo(getLocation()); | 80 throw new HgRemoteConnectionException("Failed to authenticate", ex).setServerInfo(getServerLocation()); |
82 } | 81 } |
83 } | 82 } |
84 | 83 |
85 public void disconnect() throws HgRemoteConnectionException { | 84 public void disconnect() throws HgRemoteConnectionException { |
86 if (session != null) { | 85 if (session != null) { |
117 return; | 116 return; |
118 } | 117 } |
119 forceSessionClose(); | 118 forceSessionClose(); |
120 } | 119 } |
121 | 120 |
122 public String getLocation() { | 121 public String getServerLocation() { |
123 return ""; | 122 return ""; // FIXME |
123 } | |
124 | |
125 public String getCapabilities() throws HgRemoteConnectionException { | |
126 try { | |
127 consume(remoteOut); | |
128 consume(remoteErr); | |
129 remoteIn.write(CMD_HELLO.getBytes()); | |
130 remoteIn.write('\n'); | |
131 remoteIn.write(CMD_CAPABILITIES.getBytes()); // see http connector for details | |
132 remoteIn.write('\n'); | |
133 remoteIn.write(CMD_HEADS.getBytes()); | |
134 remoteIn.write('\n'); | |
135 checkError(); | |
136 int responseLen = readResponseLength(); | |
137 checkError(); | |
138 FilterStream s = new FilterStream(remoteOut, responseLen); | |
139 BufferedReader r = new BufferedReader(new InputStreamReader(s)); | |
140 String line; | |
141 while ((line = r.readLine()) != null) { | |
142 if (line.startsWith(CMD_CAPABILITIES) && line.length() > (CMD_CAPABILITIES.length()+1)) { | |
143 line = line.substring(CMD_CAPABILITIES.length()); | |
144 if (line.charAt(0) == ':') { | |
145 return line.substring(CMD_CAPABILITIES.length() + 1); | |
146 } | |
147 } | |
148 } | |
149 r.close(); | |
150 consume(remoteOut); | |
151 checkError(); | |
152 return new String(); | |
153 } catch (IOException ex) { | |
154 throw new HgRemoteConnectionException("Failed to initiate dialog with server", ex).setRemoteCommand(CMD_HELLO).setServerInfo(getServerLocation()); | |
155 } | |
124 } | 156 } |
125 | 157 |
126 public InputStream heads() throws HgRemoteConnectionException { | 158 public InputStream heads() throws HgRemoteConnectionException { |
127 return executeCommand("heads", Collections.<Parameter>emptyList()); | 159 return executeCommand("heads", Collections.<Parameter>emptyList()); |
128 } | 160 } |
146 public InputStream changegroup(List<Nodeid> roots) throws HgRemoteConnectionException, HgRuntimeException { | 178 public InputStream changegroup(List<Nodeid> roots) throws HgRemoteConnectionException, HgRuntimeException { |
147 String l = join(roots, ' '); | 179 String l = join(roots, ' '); |
148 return executeCommand("changegroup", Collections.singletonList(new Parameter("roots", l))); | 180 return executeCommand("changegroup", Collections.singletonList(new Parameter("roots", l))); |
149 } | 181 } |
150 | 182 |
151 public void unbundle(HgBundle bundle, List<Nodeid> remoteHeads) throws HgRemoteConnectionException, HgRuntimeException { | 183 public OutputStream unbundle(long outputLen, List<Nodeid> remoteHeads) throws HgRemoteConnectionException, HgRuntimeException { |
152 String l = join(remoteHeads, ' '); | 184 String l = join(remoteHeads, ' '); |
153 Collections.singletonList(new Parameter("heads", l)); | 185 try { |
154 throw Internals.notImplemented(); | 186 consume(remoteOut); |
187 consume(remoteErr); | |
188 remoteIn.write(CMD_UNBUNDLE.getBytes()); | |
189 remoteIn.write('\n'); | |
190 writeParameters(Collections.singletonList(new Parameter("heads", l))); | |
191 checkError(); | |
192 return new FilterOutputStream(remoteIn) { | |
193 @Override | |
194 public void close() throws IOException { | |
195 out.flush(); | |
196 @SuppressWarnings("unused") | |
197 int responseLen = readResponseLength(); | |
198 checkError(); | |
199 // XXX perhaps, need to return responseLen to caller? | |
200 } | |
201 }; | |
202 } catch (IOException ex) { | |
203 throw new HgRemoteConnectionException("Communication failure", ex).setRemoteCommand(CMD_UNBUNDLE).setServerInfo(getServerLocation()); | |
204 } | |
155 } | 205 } |
156 | 206 |
157 public InputStream pushkey(String opName, String namespace, String key, String oldValue, String newValue) throws HgRemoteConnectionException, HgRuntimeException { | 207 public InputStream pushkey(String opName, String namespace, String key, String oldValue, String newValue) throws HgRemoteConnectionException, HgRuntimeException { |
158 ArrayList<Parameter> p = new ArrayList<Parameter>(); | 208 ArrayList<Parameter> p = new ArrayList<Parameter>(); |
159 p.add(new Parameter("namespace", namespace)); | 209 p.add(new Parameter("namespace", namespace)); |
165 | 215 |
166 public InputStream listkeys(String namespace, String actionName) throws HgRemoteConnectionException, HgRuntimeException { | 216 public InputStream listkeys(String namespace, String actionName) throws HgRemoteConnectionException, HgRuntimeException { |
167 return executeCommand("listkeys", Collections.singletonList(new Parameter("namespace", namespace))); | 217 return executeCommand("listkeys", Collections.singletonList(new Parameter("namespace", namespace))); |
168 } | 218 } |
169 | 219 |
170 | |
171 public Set<String> initCapabilities() throws HgRemoteConnectionException { | |
172 try { | |
173 final String CMD_CAPABILITIES = "capabilities"; | |
174 final String CMD_HEADS = "heads"; | |
175 final String CMD_HELLO = "hello"; | |
176 consume(remoteOut); | |
177 consume(remoteErr); | |
178 remoteIn.write(CMD_HELLO.getBytes()); | |
179 remoteIn.write('\n'); | |
180 remoteIn.write(CMD_CAPABILITIES.getBytes()); // see http connector for | |
181 remoteIn.write('\n'); | |
182 remoteIn.write(CMD_HEADS.getBytes()); | |
183 remoteIn.write('\n'); | |
184 checkError(); | |
185 int responseLen = readResponseLength(); | |
186 checkError(); | |
187 FilterStream s = new FilterStream(remoteOut, responseLen); | |
188 BufferedReader r = new BufferedReader(new InputStreamReader(s)); | |
189 String line; | |
190 while ((line = r.readLine()) != null) { | |
191 if (line.startsWith(CMD_CAPABILITIES) && line.length() > (CMD_CAPABILITIES.length()+1)) { | |
192 line = line.substring(CMD_CAPABILITIES.length()); | |
193 if (line.charAt(0) == ':') { | |
194 String[] caps = line.substring(CMD_CAPABILITIES.length() + 1).split("\\s"); | |
195 return new HashSet<String>(Arrays.asList(caps)); | |
196 } | |
197 } | |
198 } | |
199 r.close(); | |
200 consume(remoteOut); | |
201 checkError(); | |
202 return Collections.emptySet(); | |
203 } catch (IOException ex) { | |
204 throw new HgRemoteConnectionException("Failed to initiate dialog with server", ex).setRemoteCommand("hello").setServerInfo(getLocation()); | |
205 } catch (HgRemoteConnectionException ex) { | |
206 ex.setRemoteCommand("hello").setServerInfo(getLocation()); | |
207 throw ex; | |
208 } | |
209 } | |
210 | |
211 private InputStream executeCommand(String cmd, List<Parameter> parameters) throws HgRemoteConnectionException { | 220 private InputStream executeCommand(String cmd, List<Parameter> parameters) throws HgRemoteConnectionException { |
212 try { | 221 try { |
213 consume(remoteOut); | 222 consume(remoteOut); |
214 consume(remoteErr); | 223 consume(remoteErr); |
215 remoteIn.write(cmd.getBytes()); | 224 remoteIn.write(cmd.getBytes()); |
216 remoteIn.write('\n'); | 225 remoteIn.write('\n'); |
217 for (Parameter p : parameters) { | 226 writeParameters(parameters); |
218 remoteIn.write(p.name().getBytes()); | |
219 remoteIn.write(' '); | |
220 remoteIn.write(String.valueOf(p.size()).getBytes()); | |
221 remoteIn.write('\n'); | |
222 remoteIn.write(p.data()); | |
223 remoteIn.write('\n'); | |
224 } | |
225 checkError(); | 227 checkError(); |
226 int responseLen = readResponseLength(); | 228 int responseLen = readResponseLength(); |
227 checkError(); | 229 checkError(); |
228 return new FilterStream(remoteOut, responseLen); | 230 return new FilterStream(remoteOut, responseLen); |
229 } catch (IOException ex) { | 231 } catch (IOException ex) { |
230 throw new HgRemoteConnectionException("Communication failure", ex).setRemoteCommand(cmd).setServerInfo(getLocation()); | 232 throw new HgRemoteConnectionException("Communication failure", ex).setRemoteCommand(cmd).setServerInfo(getServerLocation()); |
231 } catch (HgRemoteConnectionException ex) { | 233 } |
232 ex.setRemoteCommand(cmd).setServerInfo(getLocation()); | 234 } |
233 throw ex; | 235 |
236 private void writeParameters(List<Parameter> parameters) throws IOException { | |
237 for (Parameter p : parameters) { | |
238 remoteIn.write(p.name().getBytes()); | |
239 remoteIn.write(' '); | |
240 remoteIn.write(String.valueOf(p.size()).getBytes()); | |
241 remoteIn.write('\n'); | |
242 remoteIn.write(p.data()); | |
243 remoteIn.write('\n'); | |
234 } | 244 } |
235 } | 245 } |
236 | 246 |
237 private void consume(InputStream is) throws IOException { | 247 private void consume(InputStream is) throws IOException { |
238 while (is.available() > 0) { | 248 while (is.available() > 0) { |
239 is.read(); | 249 is.read(); |
240 } | 250 } |
241 } | 251 } |
242 | 252 |
243 private void checkError() throws IOException, HgRemoteConnectionException { | 253 private void checkError() throws IOException { |
244 if (remoteErr.available() > 0) { | 254 if (remoteErr.available() > 0) { |
245 StringBuilder sb = new StringBuilder(); | 255 StringBuilder sb = new StringBuilder(); |
246 int c; | 256 int c; |
247 while ((c = remoteErr.read()) != -1) { | 257 while ((c = remoteErr.read()) != -1) { |
248 sb.append((char)c); | 258 sb.append((char)c); |
249 } | 259 } |
250 throw new HgRemoteConnectionException(sb.toString()); | 260 throw new IOException(sb.toString()); |
251 } | 261 } |
252 } | 262 } |
253 | 263 |
254 private int readResponseLength() throws IOException { | 264 private int readResponseLength() throws IOException { |
255 int c; | 265 int c; |