Mercurial > jhg
comparison src/org/tmatesoft/hg/internal/remote/HttpConnector.java @ 699:a483b2b68a2e
Provisional APIs and respective implementation for http, https and ssh remote repositories
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Thu, 08 Aug 2013 19:18:50 +0200 |
| parents | 822f3a83ff57 |
| children |
comparison
equal
deleted
inserted
replaced
| 698:822f3a83ff57 | 699:a483b2b68a2e |
|---|---|
| 14 * the terms of a license other than GNU General Public License | 14 * the terms of a license other than GNU General Public License |
| 15 * contact TMate Software at support@hg4j.com | 15 * contact TMate Software at support@hg4j.com |
| 16 */ | 16 */ |
| 17 package org.tmatesoft.hg.internal.remote; | 17 package org.tmatesoft.hg.internal.remote; |
| 18 | 18 |
| 19 import static org.tmatesoft.hg.util.LogFacility.Severity.Info; | |
| 20 | |
| 21 import java.io.BufferedReader; | 19 import java.io.BufferedReader; |
| 22 import java.io.ByteArrayInputStream; | 20 import java.io.ByteArrayInputStream; |
| 23 import java.io.FilterOutputStream; | 21 import java.io.FilterOutputStream; |
| 24 import java.io.IOException; | 22 import java.io.IOException; |
| 25 import java.io.InputStream; | 23 import java.io.InputStream; |
| 26 import java.io.InputStreamReader; | 24 import java.io.InputStreamReader; |
| 27 import java.io.OutputStream; | 25 import java.io.OutputStream; |
| 28 import java.io.SequenceInputStream; | 26 import java.io.SequenceInputStream; |
| 29 import java.net.HttpURLConnection; | 27 import java.net.HttpURLConnection; |
| 30 import java.net.MalformedURLException; | 28 import java.net.MalformedURLException; |
| 31 import java.net.URI; | |
| 32 import java.net.URL; | 29 import java.net.URL; |
| 33 import java.net.URLConnection; | 30 import java.net.URLConnection; |
| 34 import java.security.cert.CertificateException; | |
| 35 import java.security.cert.X509Certificate; | |
| 36 import java.util.Collection; | 31 import java.util.Collection; |
| 37 import java.util.List; | 32 import java.util.List; |
| 38 import java.util.Map; | 33 import java.util.Map; |
| 39 import java.util.prefs.BackingStoreException; | 34 |
| 40 import java.util.prefs.Preferences; | 35 import org.tmatesoft.hg.auth.HgAuthFailedException; |
| 41 | 36 import org.tmatesoft.hg.auth.HgAuthenticator; |
| 42 import javax.net.ssl.HttpsURLConnection; | |
| 43 import javax.net.ssl.SSLContext; | |
| 44 import javax.net.ssl.TrustManager; | |
| 45 import javax.net.ssl.X509TrustManager; | |
| 46 | |
| 47 import org.tmatesoft.hg.core.HgRemoteConnectionException; | 37 import org.tmatesoft.hg.core.HgRemoteConnectionException; |
| 48 import org.tmatesoft.hg.core.Nodeid; | 38 import org.tmatesoft.hg.core.Nodeid; |
| 49 import org.tmatesoft.hg.core.SessionContext; | 39 import org.tmatesoft.hg.core.SessionContext; |
| 50 import org.tmatesoft.hg.internal.PropertyMarshal; | 40 import org.tmatesoft.hg.internal.PropertyMarshal; |
| 51 import org.tmatesoft.hg.repo.HgRemoteRepository.Range; | 41 import org.tmatesoft.hg.repo.HgRemoteRepository.Range; |
| 42 import org.tmatesoft.hg.repo.HgRemoteRepository.RemoteDescriptor; | |
| 52 import org.tmatesoft.hg.repo.HgRuntimeException; | 43 import org.tmatesoft.hg.repo.HgRuntimeException; |
| 53 | 44 |
| 54 /** | 45 /** |
| 55 * | 46 * |
| 56 * @author Artem Tikhomirov | 47 * @author Artem Tikhomirov |
| 57 * @author TMate Software Ltd. | 48 * @author TMate Software Ltd. |
| 58 */ | 49 */ |
| 59 public class HttpConnector implements Connector { | 50 public class HttpConnector extends ConnectorBase { |
| 60 private URI uri; | 51 private RemoteDescriptor rd; |
| 61 private URL url; | 52 private URL url; |
| 62 private SSLContext sslContext; | |
| 63 private String authInfo; | |
| 64 private boolean debug; | 53 private boolean debug; |
| 65 private SessionContext sessionCtx; | 54 private SessionContext sessionCtx; |
| 66 // | 55 // |
| 67 private HttpURLConnection conn; | 56 private HttpURLConnection conn; |
| 68 | 57 private HttpAuthMethod authMediator; |
| 69 public void init(URI uri, SessionContext sessionContext, Object globalConfig) throws HgRuntimeException { | 58 |
| 70 this.uri = uri; | 59 public void init(RemoteDescriptor remote, SessionContext sessionContext, Object globalConfig) throws HgRuntimeException { |
| 60 rd = remote; | |
| 61 setURI(remote.getURI()); | |
| 71 sessionCtx = sessionContext; | 62 sessionCtx = sessionContext; |
| 72 debug = new PropertyMarshal(sessionCtx).getBoolean("hg4j.remote.debug", false); | 63 debug = new PropertyMarshal(sessionContext).getBoolean("hg4j.remote.debug", false); |
| 73 if (uri.getUserInfo() != null) { | 64 } |
| 74 String ai = null; | 65 |
| 66 public void connect() throws HgAuthFailedException, HgRemoteConnectionException, HgRuntimeException { | |
| 67 try { | |
| 68 url = uri.toURL(); | |
| 69 } catch (MalformedURLException ex) { | |
| 70 throw new HgRemoteConnectionException("Bad URL", ex); | |
| 71 } | |
| 72 authMediator = new HttpAuthMethod(sessionCtx, url); | |
| 73 authenticateClient(); | |
| 74 } | |
| 75 | |
| 76 private void authenticateClient() throws HgAuthFailedException { | |
| 77 String userInfo = url.getUserInfo(); | |
| 78 if (userInfo != null) { | |
| 75 try { | 79 try { |
| 76 // Hack to get Base64-encoded credentials | 80 authMediator.tryWithUserInfo(userInfo); |
| 77 Preferences tempNode = Preferences.userRoot().node("xxx"); | 81 } catch (HgAuthFailedException ex) { |
| 78 tempNode.putByteArray("xxx", uri.getUserInfo().getBytes()); | 82 // FALL THROUGH to try Authenticator |
| 79 ai = tempNode.get("xxx", null); | 83 } |
| 80 tempNode.removeNode(); | 84 } |
| 81 } catch (BackingStoreException ex) { | 85 HgAuthenticator auth = sessionCtx.getAuthenticator(rd); |
| 82 sessionContext.getLog().dump(getClass(), Info, ex, null); | 86 auth.authenticate(rd, authMediator); |
| 83 // IGNORE | |
| 84 } | |
| 85 authInfo = ai; | |
| 86 } else { | |
| 87 authInfo = null; | |
| 88 } | |
| 89 } | |
| 90 | |
| 91 public void connect() throws HgRemoteConnectionException, HgRuntimeException { | |
| 92 try { | |
| 93 url = uri.toURL(); | |
| 94 } catch (MalformedURLException ex) { | |
| 95 throw new HgRemoteConnectionException("Bad URL", ex); | |
| 96 } | |
| 97 if ("https".equals(url.getProtocol())) { | |
| 98 try { | |
| 99 sslContext = SSLContext.getInstance("SSL"); | |
| 100 class TrustEveryone implements X509TrustManager { | |
| 101 public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { | |
| 102 if (debug) { | |
| 103 System.out.println("checkClientTrusted:" + authType); | |
| 104 } | |
| 105 } | |
| 106 public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { | |
| 107 if (debug) { | |
| 108 System.out.println("checkServerTrusted:" + authType); | |
| 109 } | |
| 110 } | |
| 111 public X509Certificate[] getAcceptedIssuers() { | |
| 112 return new X509Certificate[0]; | |
| 113 } | |
| 114 }; | |
| 115 sslContext.init(null, new TrustManager[] { new TrustEveryone() }, null); | |
| 116 } catch (Exception ex) { | |
| 117 throw new HgRemoteConnectionException("Can't initialize secure connection", ex); | |
| 118 } | |
| 119 } else { | |
| 120 sslContext = null; | |
| 121 } | |
| 122 } | 87 } |
| 123 | 88 |
| 124 public void disconnect() throws HgRemoteConnectionException, HgRuntimeException { | 89 public void disconnect() throws HgRemoteConnectionException, HgRuntimeException { |
| 125 // TODO Auto-generated method stub | 90 // TODO Auto-generated method stub |
| 126 | 91 |
| 136 conn.disconnect(); | 101 conn.disconnect(); |
| 137 conn = null; | 102 conn = null; |
| 138 } | 103 } |
| 139 } | 104 } |
| 140 | 105 |
| 141 public String getServerLocation() { | |
| 142 if (uri.getUserInfo() == null) { | |
| 143 return uri.toString(); | |
| 144 } | |
| 145 if (uri.getPort() != -1) { | |
| 146 return String.format("%s://%s:%d%s", uri.getScheme(), uri.getHost(), uri.getPort(), uri.getPath()); | |
| 147 } else { | |
| 148 return String.format("%s://%s%s", uri.getScheme(), uri.getHost(), uri.getPath()); | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 public String getCapabilities() throws HgRemoteConnectionException { | 106 public String getCapabilities() throws HgRemoteConnectionException { |
| 153 // say hello to server, check response | 107 // say hello to server, check response |
| 154 try { | 108 try { |
| 155 URL u = new URL(url, url.getPath() + "?cmd=hello"); | 109 URL u = new URL(url, url.getPath() + "?cmd=hello"); |
| 156 HttpURLConnection c = setupConnection(u.openConnection()); | 110 HttpURLConnection c = setupConnection(u.openConnection()); |
| 365 } | 319 } |
| 366 | 320 |
| 367 private HttpURLConnection setupConnection(URLConnection urlConnection) { | 321 private HttpURLConnection setupConnection(URLConnection urlConnection) { |
| 368 urlConnection.setRequestProperty("User-Agent", "hg4j/1.0.0"); | 322 urlConnection.setRequestProperty("User-Agent", "hg4j/1.0.0"); |
| 369 urlConnection.addRequestProperty("Accept", "application/mercurial-0.1"); | 323 urlConnection.addRequestProperty("Accept", "application/mercurial-0.1"); |
| 370 if (authInfo != null) { | 324 return authMediator.setupConnection((HttpURLConnection) urlConnection); |
| 371 urlConnection.addRequestProperty("Authorization", "Basic " + authInfo); | |
| 372 } | |
| 373 if (sslContext != null) { | |
| 374 ((HttpsURLConnection) urlConnection).setSSLSocketFactory(sslContext.getSocketFactory()); | |
| 375 } | |
| 376 return (HttpURLConnection) urlConnection; | |
| 377 } | 325 } |
| 378 | 326 |
| 379 private StringBuilder appendNodeidListArgument(String key, List<Nodeid> values, StringBuilder sb) { | 327 private StringBuilder appendNodeidListArgument(String key, List<Nodeid> values, StringBuilder sb) { |
| 380 if (sb == null) { | 328 if (sb == null) { |
| 381 sb = new StringBuilder(20 + values.size() * 41); | 329 sb = new StringBuilder(20 + values.size() * 41); |
