From 2290516ddb5a92ccc296df500aa5e9e30cbc240e Mon Sep 17 00:00:00 2001 From: Christian Halstrick Date: Sun, 4 Aug 2013 23:37:50 +0200 Subject: [PATCH] Add an implementation for HttpConnection using Apache HttpClient This change implements the http connection abstraction with the help of org.apache.http.client.HttpClient. The default implementation used by JGit is still the JDK HttpURLConnection. But now JGit users have the possibility to switch completely to org.apache.httpclient. The reason for this is that in certain (e.g. cloud) environments you are forced to use the org.apache classes. Change-Id: I0b357f23243ed13a014c79ba179fa327dfe318b2 Signed-off-by: Christian Halstrick Signed-off-by: Matthias Sohn --- .../META-INF/MANIFEST.MF | 6 +- .../http/test/DumbClientDumbServerTest.java | 21 + .../http/test/DumbClientSmartServerTest.java | 21 + .../http/test/SmartClientSmartServerTest.java | 21 + .../META-INF/MANIFEST.MF | 1 + .../org.eclipse.jgit.feature/feature.xml | 14 + .../feature.xml | 7 + .../org.eclipse.jgit.target/jgit-4.3.target | 6 + .../org.eclipse.jgit.target/jgit-4.4.target | 6 + org.eclipse.jgit/META-INF/MANIFEST.MF | 18 + org.eclipse.jgit/pom.xml | 5 + .../eclipse/jgit/internal/JGitText.properties | 1 + .../org/eclipse/jgit/internal/JGitText.java | 1 + .../jgit/transport/http/HttpConnection.java | 1 + .../http/apache/HttpClientConnection.java | 366 ++++++++++++++++++ .../apache/HttpClientConnectionFactory.java | 66 ++++ .../http/apache/TemporaryBufferEntity.java | 109 ++++++ pom.xml | 8 + 18 files changed, 677 insertions(+), 1 deletion(-) create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/transport/http/apache/HttpClientConnectionFactory.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/transport/http/apache/TemporaryBufferEntity.java diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF index ce69bfb58..4bc2e7459 100644 --- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF @@ -35,7 +35,11 @@ Import-Package: javax.servlet;version="[2.5.0,3.0.0)", org.eclipse.jgit.revwalk;version="[3.3.0,3.4.0)", org.eclipse.jgit.storage.file;version="[3.3.0,3.4.0)", org.eclipse.jgit.transport;version="[3.3.0,3.4.0)", + org.eclipse.jgit.transport.http;version="[3.3.0,3.4.0)", + org.eclipse.jgit.transport.http.apache;version="[3.3.0,3.4.0)", org.eclipse.jgit.transport.resolver;version="[3.3.0,3.4.0)", org.eclipse.jgit.util;version="[3.3.0,3.4.0)", org.hamcrest.core;version="[1.1.0,2.0.0)", - org.junit;version="[4.0.0,5.0.0)" + org.junit;version="[4.0.0,5.0.0)", + org.junit.runner;version="[4.0.0,5.0.0)", + org.junit.runners;version="[4.0.0,5.0.0)" diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java index 1726dc5eb..dec9b59f2 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java @@ -55,6 +55,8 @@ import java.io.File; import java.io.IOException; import java.net.URI; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -76,9 +78,16 @@ import org.eclipse.jgit.transport.Transport; import org.eclipse.jgit.transport.TransportHttp; import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.transport.http.HttpConnectionFactory; +import org.eclipse.jgit.transport.http.JDKHttpConnectionFactory; +import org.eclipse.jgit.transport.http.apache.HttpClientConnectionFactory; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +@RunWith(Parameterized.class) public class DumbClientDumbServerTest extends HttpTestCase { private Repository remoteRepository; @@ -88,6 +97,18 @@ public class DumbClientDumbServerTest extends HttpTestCase { private RevCommit A, B; + @Parameters + public static Collection data() { + // run all tests with both connection factories we have + return Arrays.asList(new Object[][] { + { new JDKHttpConnectionFactory() }, + { new HttpClientConnectionFactory() } }); + } + + public DumbClientDumbServerTest(HttpConnectionFactory cf) { + HttpTransport.setConnectionFactory(cf); + } + @Before public void setUp() throws Exception { super.setUp(); diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java index 354b0439a..7b4270f1b 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java @@ -55,6 +55,8 @@ import static org.junit.Assert.fail; import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -79,11 +81,18 @@ import org.eclipse.jgit.transport.Transport; import org.eclipse.jgit.transport.TransportHttp; import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.transport.http.HttpConnectionFactory; +import org.eclipse.jgit.transport.http.JDKHttpConnectionFactory; +import org.eclipse.jgit.transport.http.apache.HttpClientConnectionFactory; import org.eclipse.jgit.transport.resolver.RepositoryResolver; import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +@RunWith(Parameterized.class) public class DumbClientSmartServerTest extends HttpTestCase { private Repository remoteRepository; @@ -93,6 +102,18 @@ public class DumbClientSmartServerTest extends HttpTestCase { private RevCommit A, B; + @Parameters + public static Collection data() { + // run all tests with both connection factories we have + return Arrays.asList(new Object[][] { + { new JDKHttpConnectionFactory() }, + { new HttpClientConnectionFactory() } }); + } + + public DumbClientSmartServerTest(HttpConnectionFactory cf) { + HttpTransport.setConnectionFactory(cf); + } + @Before public void setUp() throws Exception { super.setUp(); diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java index 7b3c717af..bb612b1a6 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java @@ -56,6 +56,8 @@ import java.io.IOException; import java.io.PrintWriter; import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -98,11 +100,18 @@ import org.eclipse.jgit.transport.Transport; import org.eclipse.jgit.transport.TransportHttp; import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.transport.http.HttpConnectionFactory; +import org.eclipse.jgit.transport.http.JDKHttpConnectionFactory; +import org.eclipse.jgit.transport.http.apache.HttpClientConnectionFactory; import org.eclipse.jgit.transport.resolver.RepositoryResolver; import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +@RunWith(Parameterized.class) public class SmartClientSmartServerTest extends HttpTestCase { private static final String HDR_TRANSFER_ENCODING = "Transfer-Encoding"; @@ -116,6 +125,18 @@ public class SmartClientSmartServerTest extends HttpTestCase { private RevCommit A, B; + @Parameters + public static Collection data() { + // run all tests with both connection factories we have + return Arrays.asList(new Object[][] { + { new JDKHttpConnectionFactory() }, + { new HttpClientConnectionFactory() } }); + } + + public SmartClientSmartServerTest(HttpConnectionFactory cf) { + HttpTransport.setConnectionFactory(cf); + } + @Before public void setUp() throws Exception { super.setUp(); diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF index 0991fb61d..61058ed14 100644 --- a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF @@ -9,6 +9,7 @@ Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: J2SE-1.5 Import-Package: javax.servlet;version="[2.5.0,3.0.0)", javax.servlet.http;version="[2.5.0,3.0.0)", + org.apache.commons.logging;version="[1.1.1,2.0.0)", org.eclipse.jetty.http;version="[7.6.0,8.0.0)", org.eclipse.jetty.security;version="[7.6.0,8.0.0)", org.eclipse.jetty.security.authentication;version="[7.6.0,8.0.0)", diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml index 4ca262922..443fffe07 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml @@ -57,4 +57,18 @@ version="0.0.0" unpack="false"/> + + + + diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml index d23e4d72d..6877a5f24 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml @@ -47,4 +47,11 @@ version="0.0.0" unpack="false"/> + + diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target index 7e1673b50..9bf94ccc8 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target @@ -35,6 +35,12 @@ + + + + + + diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.target index dabfae0dd..a99cfd64f 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.target @@ -35,6 +35,12 @@ + + + + + + diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index 2b0598051..1249b29c6 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF @@ -109,6 +109,12 @@ Export-Package: org.eclipse.jgit.api;version="3.3.0"; org.eclipse.jgit.storage.pack", org.eclipse.jgit.transport.http;version="3.3.0"; uses:="javax.net.ssl", + org.eclipse.jgit.transport.http.apache;version="3.3.0"; + uses:="org.eclipse.jgit.transport.http, + javax.net.ssl, + org.apache.http.client, + org.apache.http.client.methods, + org.apache.http", org.eclipse.jgit.transport.resolver;version="3.3.0"; uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport", org.eclipse.jgit.treewalk;version="3.3.0"; @@ -128,5 +134,17 @@ Require-Bundle: com.jcraft.jsch;bundle-version="[0.1.37,0.2.0)" Import-Package: com.googlecode.javaewah;version="[0.7.9,0.8.0)", javax.crypto, javax.net.ssl, + org.apache.http;version="[4.1.0,5.0.0)", + org.apache.http.client;version="[4.1.0,5.0.0)", + org.apache.http.client.methods;version="[4.1.0,5.0.0)", + org.apache.http.client.params;version="[4.1.0,5.0.0)", + org.apache.http.conn;version="[4.1.0,5.0.0)", + org.apache.http.conn.params;version="[4.1.0,5.0.0)", + org.apache.http.conn.scheme;version="[4.1.0,5.0.0)", + org.apache.http.conn.ssl;version="[4.1.0,5.0.0)", + org.apache.http.entity;version="[4.1.0,5.0.0)", + org.apache.http.impl.client;version="[4.1.0,5.0.0)", + org.apache.http.impl.client.cache;version="[4.1.0,5.0.0)", + org.apache.http.params;version="[4.1.0,5.0.0)", org.xml.sax, org.xml.sax.helpers diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml index f45c4f8a8..c3f681e9c 100644 --- a/org.eclipse.jgit/pom.xml +++ b/org.eclipse.jgit/pom.xml @@ -78,6 +78,11 @@ com.googlecode.javaewah JavaEWAH + + + org.apache.httpcomponents + httpclient + diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index bb67c127a..40b5c0b2d 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -522,6 +522,7 @@ unexpectedOddResult=odd: {0} + {1} - {2} unexpectedRefReport={0}: unexpected ref report: {1} unexpectedReportLine=unexpected report line: {0} unexpectedReportLine2={0} unexpected report line: {1} +unexpectedSSLContextException=unexpected exception when searching for the TLS protocol unknownOrUnsupportedCommand=Unknown or unsupported command "{0}", only "{1}" is allowed. unknownDIRCVersion=Unknown DIRC version {0} unknownHost=unknown host diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index f9700a1ff..dcee70727 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -584,6 +584,7 @@ public static JGitText get() { /***/ public String unexpectedRefReport; /***/ public String unexpectedReportLine; /***/ public String unexpectedReportLine2; + /***/ public String unexpectedSSLContextException; /***/ public String unknownOrUnsupportedCommand; /***/ public String unknownDIRCVersion; /***/ public String unknownHost; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java index 0cf12aaa2..09613fd7a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java @@ -258,6 +258,7 @@ public void setRequestMethod(String method) * @param random * the source of randomness for this generator or null. See * {@link SSLContext#init(KeyManager[], TrustManager[], SecureRandom)} + * * @throws NoSuchAlgorithmException * @throws KeyManagementException */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java new file mode 100644 index 000000000..16003df1f --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2013 Christian Halstrick + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.transport.http.apache; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.ProtocolException; +import java.net.Proxy; +import java.net.URL; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; + +import org.apache.http.Header; +import org.apache.http.HeaderElement; +import org.apache.http.HttpEntity; +import org.apache.http.HttpEntityEnclosingRequest; +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.params.ClientPNames; +import org.apache.http.conn.params.ConnRoutePNames; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.conn.ssl.X509HostnameVerifier; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.params.CoreConnectionPNames; +import org.apache.http.params.HttpParams; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.transport.http.HttpConnection; +import org.eclipse.jgit.util.TemporaryBuffer; +import org.eclipse.jgit.util.TemporaryBuffer.LocalFile; + +/** + * A {@link HttpConnection} which uses {@link HttpClient} + * + * @since 3.3 + */ +public class HttpClientConnection implements HttpConnection { + HttpClient client; + + String urlStr; + + HttpUriRequest req; + + HttpResponse resp = null; + + String method = "GET"; //$NON-NLS-1$ + + private TemporaryBufferEntity entity; + + private boolean isUsingProxy = false; + + private Proxy proxy; + + private Integer timeout = null; + + private Integer readTimeout; + + private Boolean followRedirects; + + private X509HostnameVerifier hostnameverifier; + + SSLContext ctx; + + private HttpClient getClient() { + if (client == null) + client = new DefaultHttpClient(); + HttpParams params = client.getParams(); + if (proxy != null && !Proxy.NO_PROXY.equals(proxy)) { + isUsingProxy = true; + InetSocketAddress adr = (InetSocketAddress) proxy.address(); + params.setParameter(ConnRoutePNames.DEFAULT_PROXY, + new HttpHost(adr.getHostName(), adr.getPort())); + } + if (timeout != null) + params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, + timeout.intValue()); + if (readTimeout != null) + params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, + readTimeout.intValue()); + if (followRedirects != null) + params.setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, + followRedirects.booleanValue()); + if (hostnameverifier != null) { + SSLSocketFactory sf; + sf = new SSLSocketFactory(getSSLContext(), hostnameverifier); + Scheme https = new Scheme("https", 443, sf); //$NON-NLS-1$ + client.getConnectionManager().getSchemeRegistry().register(https); + } + + return client; + } + + private SSLContext getSSLContext() { + if (ctx == null) { + try { + ctx = SSLContext.getInstance("TLS"); //$NON-NLS-1$ + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException( + JGitText.get().unexpectedSSLContextException, e); + } + } + return ctx; + } + + /** + * Sets the buffer from which to take the request body + * + * @param buffer + */ + public void setBuffer(TemporaryBuffer buffer) { + this.entity = new TemporaryBufferEntity(buffer); + } + + /** + * @param urlStr + */ + public HttpClientConnection(String urlStr) { + this(urlStr, null); + } + + /** + * @param urlStr + * @param proxy + */ + public HttpClientConnection(String urlStr, Proxy proxy) { + this(urlStr, proxy, null); + } + + /** + * @param urlStr + * @param proxy + * @param cl + */ + public HttpClientConnection(String urlStr, Proxy proxy, HttpClient cl) { + this.client = cl; + this.urlStr = urlStr; + this.proxy = proxy; + } + + public int getResponseCode() throws IOException { + execute(); + return resp.getStatusLine().getStatusCode(); + } + + public URL getURL() { + try { + return new URL(urlStr); + } catch (MalformedURLException e) { + return null; + } + } + + public String getResponseMessage() throws IOException { + execute(); + return resp.getStatusLine().getReasonPhrase(); + } + + private void execute() throws IOException, ClientProtocolException { + if (resp == null) + if (entity != null) { + if (req instanceof HttpEntityEnclosingRequest) { + HttpEntityEnclosingRequest eReq = (HttpEntityEnclosingRequest) req; + eReq.setEntity(entity); + } + resp = getClient().execute(req); + entity.getBuffer().close(); + entity = null; + } else + resp = getClient().execute(req); + } + + public Map> getHeaderFields() { + Map> ret = new HashMap>(); + for (Header hdr : resp.getAllHeaders()) { + List list = new LinkedList(); + for (HeaderElement hdrElem : hdr.getElements()) + list.add(hdrElem.toString()); + ret.put(hdr.getName(), list); + } + return ret; + } + + public void setRequestProperty(String name, String value) { + req.addHeader(name, value); + } + + public void setRequestMethod(String method) throws ProtocolException { + this.method = method; + if ("GET".equalsIgnoreCase(method)) //$NON-NLS-1$ + req = new HttpGet(urlStr); + else if ("PUT".equalsIgnoreCase(method)) //$NON-NLS-1$ + req = new HttpPut(urlStr); + else if ("POST".equalsIgnoreCase(method)) //$NON-NLS-1$ + req = new HttpPost(urlStr); + else { + this.method = null; + throw new UnsupportedOperationException(); + } + } + + public void setUseCaches(boolean usecaches) { + // not needed + } + + public void setConnectTimeout(int timeout) { + this.timeout = new Integer(timeout); + } + + public void setReadTimeout(int readTimeout) { + this.readTimeout = new Integer(readTimeout); + } + + public String getContentType() { + HttpEntity responseEntity = resp.getEntity(); + if (responseEntity != null) { + Header contentType = responseEntity.getContentType(); + if (contentType != null) + return contentType.getValue(); + } + return null; + } + + public InputStream getInputStream() throws IOException { + return resp.getEntity().getContent(); + } + + // will return only the first field + public String getHeaderField(String name) { + Header header = resp.getFirstHeader(name); + return (header == null) ? null : header.getValue(); + } + + public int getContentLength() { + return Integer.parseInt(resp.getFirstHeader("content-length") //$NON-NLS-1$ + .getValue()); + } + + public void setInstanceFollowRedirects(boolean followRedirects) { + this.followRedirects = new Boolean(followRedirects); + } + + public void setDoOutput(boolean dooutput) { + // TODO: check whether we can really ignore this. + } + + public void setFixedLengthStreamingMode(int contentLength) { + if (entity != null) + throw new IllegalArgumentException(); + entity = new TemporaryBufferEntity(new LocalFile()); + entity.setContentLength(contentLength); + } + + public OutputStream getOutputStream() throws IOException { + if (entity == null) + entity = new TemporaryBufferEntity(new LocalFile()); + return entity.getBuffer(); + } + + public void setChunkedStreamingMode(int chunklen) { + if (entity == null) + entity = new TemporaryBufferEntity(new LocalFile()); + entity.setChunked(true); + } + + public String getRequestMethod() { + return method; + } + + public boolean usingProxy() { + return isUsingProxy; + } + + public void connect() throws IOException { + execute(); + } + + public void setHostnameVerifier(final HostnameVerifier hostnameverifier) { + this.hostnameverifier = new X509HostnameVerifier() { + public boolean verify(String hostname, SSLSession session) { + return hostnameverifier.verify(hostname, session); + } + + public void verify(String host, String[] cns, String[] subjectAlts) + throws SSLException { + throw new UnsupportedOperationException(); // TODO message + } + + public void verify(String host, X509Certificate cert) + throws SSLException { + throw new UnsupportedOperationException(); // TODO message + } + + public void verify(String host, SSLSocket ssl) throws IOException { + hostnameverifier.verify(host, ssl.getSession()); + } + }; + } + + public void configure(KeyManager[] km, TrustManager[] tm, + SecureRandom random) throws KeyManagementException { + getSSLContext().init(km, tm, random); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/apache/HttpClientConnectionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/apache/HttpClientConnectionFactory.java new file mode 100644 index 000000000..fe1eef484 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/apache/HttpClientConnectionFactory.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2013 Christian Halstrick + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.transport.http.apache; + +import java.io.IOException; +import java.net.Proxy; +import java.net.URL; + +import org.eclipse.jgit.transport.http.HttpConnection; +import org.eclipse.jgit.transport.http.HttpConnectionFactory; + +/** + * A factory returning instances of {@link HttpClientConnection} + * + * @since 3.3 + */ +public class HttpClientConnectionFactory implements HttpConnectionFactory { + public HttpConnection create(URL url) throws IOException { + return new HttpClientConnection(url.toString()); + } + + public HttpConnection create(URL url, Proxy proxy) + throws IOException { + return new HttpClientConnection(url.toString(), proxy); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/apache/TemporaryBufferEntity.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/apache/TemporaryBufferEntity.java new file mode 100644 index 000000000..1ff168e23 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/apache/TemporaryBufferEntity.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2014 Christian Halstrick + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.transport.http.apache; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.http.HttpEntity; +import org.apache.http.entity.AbstractHttpEntity; +import org.eclipse.jgit.util.TemporaryBuffer; + +/** + * A {@link HttpEntity} which takes it's content from a {@link TemporaryBuffer} + * + * @since 3.3 + */ +public class TemporaryBufferEntity extends AbstractHttpEntity { + private TemporaryBuffer buffer; + + private Integer contentLength; + + /** + * Construct a new {@link HttpEntity} which will contain the content stored + * in the specified buffer + * + * @param buffer + */ + public TemporaryBufferEntity(TemporaryBuffer buffer) { + this.buffer = buffer; + } + + /** + * @return buffer containing the content + */ + public TemporaryBuffer getBuffer() { + return buffer; + } + + public boolean isRepeatable() { + return true; + } + + public long getContentLength() { + if (contentLength != null) + return contentLength.intValue(); + return buffer.length(); + } + + public InputStream getContent() throws IOException, IllegalStateException { + return buffer.openInputStream(); + } + + public void writeTo(OutputStream outstream) throws IOException { + // TODO: dont we need a progressmonitor + buffer.writeTo(outstream, null); + } + + public boolean isStreaming() { + return false; + } + + /** + * @param contentLength + */ + public void setContentLength(int contentLength) { + this.contentLength = new Integer(contentLength); + } +} diff --git a/pom.xml b/pom.xml index 84ac44814..5d564d27e 100644 --- a/pom.xml +++ b/pom.xml @@ -186,6 +186,7 @@ 2.5 7.6.14.v20131031 2.4 + 4.1.3 @@ -193,6 +194,7 @@ jgit-repository http://download.eclipse.org/jgit/maven + @@ -474,6 +476,12 @@ org.osgi.core ${osgi-core-version} + + + org.apache.httpcomponents + httpclient + ${httpclient-version} +