Introduce an abstraction for HTTP connections

Previously all HTTP communication was done with the help of
java.net.HttpUrlConnection. In order to make JGit usable in environments
where the direct usage of such connections is not allowed but where the
environment provides other means to get network connections an
abstraction for connections is introduced. The idea is that new
implementations of this interface will be introduced which will not use
java.net.HttpUrlConnection but use e.g.
org.apache.client.http.HttpClient to provide network connections.

One example: certain cloud infrastructures don't allow that components
in the cloud communicate directly with HttpUrlConnection. Instead they
provide services where a component can ask for a connection (given a
symbolic name for the destination) and where the infrastructure returns
a preconfigured org.apache.http.client.HttpClient. In order to allow
JGit to be running in such environments we need the abstraction
introduced in this commit.

Change-Id: I3b06629f90a118bd284e55bb3f6465fe7d10463d
Signed-off-by: Christian Halstrick <christian.halstrick@sap.com>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
This commit is contained in:
Christian Halstrick 2013-08-04 22:49:47 +02:00
parent f08fde3eeb
commit 38c4f35d8b
12 changed files with 741 additions and 55 deletions

View File

@ -38,6 +38,7 @@ Import-Package: com.googlecode.javaewah;version="[0.7.9,0.8.0)",
org.eclipse.jgit.storage.pack;version="[3.3.0,3.4.0)",
org.eclipse.jgit.submodule;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.treewalk;version="[3.3.0,3.4.0)",
org.eclipse.jgit.treewalk.filter;version="[3.3.0,3.4.0)",
org.eclipse.jgit.util;version="[3.3.0,3.4.0)",

View File

@ -45,7 +45,6 @@
import static org.junit.Assert.fail;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
@ -53,6 +52,7 @@
import java.util.List;
import java.util.Map;
import org.eclipse.jgit.transport.http.JDKHttpConnection;
import org.junit.Test;
public class HttpAuthTest {
@ -71,7 +71,7 @@ public class HttpAuthTest {
private static String DIGEST = "Digest";
@Test
public void testHttpAuthScanResponse() throws MalformedURLException {
public void testHttpAuthScanResponse() {
checkResponse(new String[] { basicHeader }, BASIC);
checkResponse(new String[] { digestHeader }, DIGEST);
checkResponse(new String[] { basicHeader, digestHeader }, DIGEST);
@ -83,10 +83,15 @@ public void testHttpAuthScanResponse() throws MalformedURLException {
}
private static void checkResponse(String[] headers,
String expectedAuthMethod) throws MalformedURLException {
String expectedAuthMethod) {
AuthHeadersResponse responce = new AuthHeadersResponse(headers);
HttpAuthMethod authMethod = HttpAuthMethod.scanResponse(responce);
AuthHeadersResponse response = null;
try {
response = new AuthHeadersResponse(headers);
} catch (IOException e) {
fail("Couldn't instantiate AuthHeadersResponse: " + e.toString());
}
HttpAuthMethod authMethod = HttpAuthMethod.scanResponse(response);
if (!expectedAuthMethod.equals(getAuthMethodName(authMethod))) {
fail("Wrong authentication method: expected " + expectedAuthMethod
@ -98,20 +103,15 @@ private static String getAuthMethodName(HttpAuthMethod authMethod) {
return authMethod.getClass().getSimpleName();
}
private static class AuthHeadersResponse extends HttpURLConnection {
private static class AuthHeadersResponse extends JDKHttpConnection {
Map<String, List<String>> headerFields = new HashMap<String, List<String>>();
public AuthHeadersResponse(String[] authHeaders)
throws MalformedURLException {
throws MalformedURLException, IOException {
super(new URL(URL_SAMPLE));
parseHeaders(authHeaders);
}
@Override
public void disconnect() {
fail("The disconnect method shouldn't be invoked");
}
@Override
public boolean usingProxy() {
return false;

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<component id="org.eclipse.jgit" version="2">
<resource path="META-INF/MANIFEST.MF">
<filter id="923795461">
<message_arguments>
<message_argument value="3.3.0"/>
<message_argument value="3.2.0"/>
</message_arguments>
</filter>
</resource>
<resource path="src/org/eclipse/jgit/transport/TransportHttp.java" type="org.eclipse.jgit.transport.TransportHttp">
<filter comment="Method is only used by implementers of TransportHttp's API, minor version are allowed to break implementer API according to OSGi semantic versioning (http://www.osgi.org/wiki/uploads/Links/SemanticVersioning.pdf)" id="338792546">
<message_arguments>
<message_argument value="org.eclipse.jgit.transport.TransportHttp"/>
<message_argument value="httpOpen(String, URL)"/>
</message_arguments>
</filter>
</resource>
</component>

View File

@ -104,8 +104,11 @@ Export-Package: org.eclipse.jgit.api;version="3.3.0";
org.eclipse.jgit.util.io,
org.eclipse.jgit.internal.storage.file,
org.eclipse.jgit.lib,
org.eclipse.jgit.transport.http,
org.eclipse.jgit.errors,
org.eclipse.jgit.storage.pack",
org.eclipse.jgit.transport.http;version="3.3.0";
uses:="javax.net.ssl",
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";
@ -117,7 +120,7 @@ Export-Package: org.eclipse.jgit.api;version="3.3.0";
org.eclipse.jgit.treewalk.filter;version="3.3.0";
uses:="org.eclipse.jgit.treewalk",
org.eclipse.jgit.util;version="3.3.0";
uses:="org.eclipse.jgit.lib,org.eclipse.jgit.storage.file",
uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport.http,org.eclipse.jgit.storage.file",
org.eclipse.jgit.util.io;version="3.3.0"
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: J2SE-1.5

View File

@ -48,7 +48,6 @@
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@ -60,6 +59,7 @@
import java.util.Map.Entry;
import java.util.Random;
import org.eclipse.jgit.transport.http.HttpConnection;
import org.eclipse.jgit.util.Base64;
/**
@ -81,7 +81,7 @@ abstract class HttpAuthMethod {
* the connection that failed.
* @return new authentication method to try.
*/
static HttpAuthMethod scanResponse(final HttpURLConnection conn) {
static HttpAuthMethod scanResponse(final HttpConnection conn) {
final Map<String, List<String>> headers = conn.getHeaderFields();
HttpAuthMethod authentication = NONE;
@ -168,7 +168,7 @@ boolean authorize(URIish uri, CredentialsProvider credentialsProvider) {
* @param conn
* @throws IOException
*/
abstract void configureRequest(HttpURLConnection conn) throws IOException;
abstract void configureRequest(HttpConnection conn) throws IOException;
/** Performs no user authentication. */
private static class None extends HttpAuthMethod {
@ -178,7 +178,7 @@ void authorize(String user, String pass) {
}
@Override
void configureRequest(HttpURLConnection conn) throws IOException {
void configureRequest(HttpConnection conn) throws IOException {
// Do nothing when no authentication is enabled.
}
}
@ -198,7 +198,7 @@ void authorize(final String username, final String password) {
}
@Override
void configureRequest(final HttpURLConnection conn) throws IOException {
void configureRequest(final HttpConnection conn) throws IOException {
String ident = user + ":" + pass; //$NON-NLS-1$
String enc = Base64.encodeBytes(ident.getBytes("UTF-8")); //$NON-NLS-1$
conn.setRequestProperty(HDR_AUTHORIZATION, NAME + " " + enc); //$NON-NLS-1$
@ -238,7 +238,7 @@ void authorize(final String username, final String password) {
@SuppressWarnings("boxing")
@Override
void configureRequest(final HttpURLConnection conn) throws IOException {
void configureRequest(final HttpConnection conn) throws IOException {
final Map<String, String> r = new LinkedHashMap<String, String>();
final String realm = params.get("realm"); //$NON-NLS-1$

View File

@ -46,12 +46,40 @@
package org.eclipse.jgit.transport;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.http.HttpConnectionFactory;
import org.eclipse.jgit.transport.http.JDKHttpConnectionFactory;
/**
* The base class for transports that use HTTP as underlying protocol. This class
* allows customizing HTTP connection settings.
*/
public abstract class HttpTransport extends Transport {
/**
* factory for creating HTTP connections
*
* @since 3.3
*/
protected static HttpConnectionFactory connectionFactory = new JDKHttpConnectionFactory();
/**
* @return the {@link HttpConnectionFactory} used to create new connections
* @since 3.3
*/
public static HttpConnectionFactory getConnectionFactory() {
return connectionFactory;
}
/**
* Set the {@link HttpConnectionFactory} to be used to create new
* connections
*
* @param cf
* @since 3.3
*/
public static void setConnectionFactory(HttpConnectionFactory cf) {
connectionFactory = cf;
}
/**
* Create a new transport instance.
*

View File

@ -62,12 +62,10 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
@ -85,8 +83,6 @@
import java.util.zip.GZIPOutputStream;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
@ -106,6 +102,7 @@
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.SymbolicRef;
import org.eclipse.jgit.transport.http.HttpConnection;
import org.eclipse.jgit.util.HttpSupport;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
@ -131,6 +128,7 @@
*/
public class TransportHttp extends HttpTransport implements WalkTransport,
PackTransport {
private static final String SVC_UPLOAD_PACK = "git-upload-pack"; //$NON-NLS-1$
private static final String SVC_RECEIVE_PACK = "git-receive-pack"; //$NON-NLS-1$
@ -305,7 +303,7 @@ public FetchConnection openFetch() throws TransportException,
NotSupportedException {
final String service = SVC_UPLOAD_PACK;
try {
final HttpURLConnection c = connect(service);
final HttpConnection c = connect(service);
final InputStream in = openInputStream(c);
try {
if (isSmartHttp(c, service)) {
@ -346,10 +344,10 @@ private FetchConnection newDumbConnection(InputStream in)
// is not there) download HEAD by itself as a loose file and do
// the resolution by hand.
//
HttpURLConnection conn = httpOpen(new URL(baseUrl, Constants.HEAD));
HttpConnection conn = httpOpen(new URL(baseUrl, Constants.HEAD));
int status = HttpSupport.response(conn);
switch (status) {
case HttpURLConnection.HTTP_OK: {
case HttpConnection.HTTP_OK: {
br = toBufferedReader(openInputStream(conn));
try {
String line = br.readLine();
@ -371,7 +369,7 @@ private FetchConnection newDumbConnection(InputStream in)
break;
}
case HttpURLConnection.HTTP_NOT_FOUND:
case HttpConnection.HTTP_NOT_FOUND:
break;
default:
@ -395,7 +393,7 @@ public PushConnection openPush() throws NotSupportedException,
TransportException {
final String service = SVC_RECEIVE_PACK;
try {
final HttpURLConnection c = connect(service);
final HttpConnection c = connect(service);
final InputStream in = openInputStream(c);
try {
if (isSmartHttp(c, service)) {
@ -427,7 +425,7 @@ public void close() {
// No explicit connections are maintained.
}
private HttpURLConnection connect(final String service)
private HttpConnection connect(final String service)
throws TransportException, NotSupportedException {
final URL u;
try {
@ -452,7 +450,7 @@ private HttpURLConnection connect(final String service)
try {
int authAttempts = 1;
for (;;) {
final HttpURLConnection conn = httpOpen(u);
final HttpConnection conn = httpOpen(u);
if (useSmartHttp) {
String exp = "application/x-" + service + "-advertisement"; //$NON-NLS-1$ //$NON-NLS-2$
conn.setRequestProperty(HDR_ACCEPT, exp + ", */*"); //$NON-NLS-1$
@ -461,14 +459,14 @@ private HttpURLConnection connect(final String service)
}
final int status = HttpSupport.response(conn);
switch (status) {
case HttpURLConnection.HTTP_OK:
case HttpConnection.HTTP_OK:
return conn;
case HttpURLConnection.HTTP_NOT_FOUND:
case HttpConnection.HTTP_NOT_FOUND:
throw new NoRemoteRepositoryException(uri,
MessageFormat.format(JGitText.get().uriNotFound, u));
case HttpURLConnection.HTTP_UNAUTHORIZED:
case HttpConnection.HTTP_UNAUTHORIZED:
authMethod = HttpAuthMethod.scanResponse(conn);
if (authMethod == HttpAuthMethod.NONE)
throw new TransportException(uri, MessageFormat.format(
@ -482,7 +480,7 @@ private HttpURLConnection connect(final String service)
authAttempts++;
continue;
case HttpURLConnection.HTTP_FORBIDDEN:
case HttpConnection.HTTP_FORBIDDEN:
throw new TransportException(uri, MessageFormat.format(
JGitText.get().serviceNotPermitted, service));
@ -500,23 +498,23 @@ private HttpURLConnection connect(final String service)
}
}
final HttpURLConnection httpOpen(URL u) throws IOException {
final HttpConnection httpOpen(URL u) throws IOException {
return httpOpen(METHOD_GET, u);
}
/**
* Open an HTTP connection.
*
*
* @param method
* @param u
* @return the connection
* @throws IOException
* @since 3.2
* @since 3.3
*/
protected HttpURLConnection httpOpen(String method, URL u)
protected HttpConnection httpOpen(String method, URL u)
throws IOException {
final Proxy proxy = HttpSupport.proxyFor(proxySelector, u);
HttpURLConnection conn = (HttpURLConnection) u.openConnection(proxy);
HttpConnection conn = connectionFactory.create(u, proxy);
if (!http.sslVerify && "https".equals(u.getProtocol())) { //$NON-NLS-1$
disableSslVerify(conn);
@ -537,15 +535,12 @@ protected HttpURLConnection httpOpen(String method, URL u)
return conn;
}
private void disableSslVerify(URLConnection conn)
private void disableSslVerify(HttpConnection conn)
throws IOException {
final TrustManager[] trustAllCerts = new TrustManager[] { new DummyX509TrustManager() };
try {
SSLContext ctx = SSLContext.getInstance("SSL"); //$NON-NLS-1$
ctx.init(null, trustAllCerts, null);
final HttpsURLConnection sslConn = (HttpsURLConnection) conn;
sslConn.setSSLSocketFactory(ctx.getSocketFactory());
sslConn.setHostnameVerifier(new DummyHostnameVerifier());
conn.configure(null, trustAllCerts, null);
conn.setHostnameVerifier(new DummyHostnameVerifier());
} catch (KeyManagementException e) {
throw new IOException(e.getMessage());
} catch (NoSuchAlgorithmException e) {
@ -553,7 +548,7 @@ private void disableSslVerify(URLConnection conn)
}
}
final InputStream openInputStream(HttpURLConnection conn)
final InputStream openInputStream(HttpConnection conn)
throws IOException {
InputStream input = conn.getInputStream();
if (ENCODING_GZIP.equals(conn.getHeaderField(HDR_CONTENT_ENCODING)))
@ -566,7 +561,7 @@ IOException wrongContentType(String expType, String actType) {
return new TransportException(uri, why);
}
private boolean isSmartHttp(final HttpURLConnection c, final String service) {
private boolean isSmartHttp(final HttpConnection c, final String service) {
final String expType = "application/x-" + service + "-advertisement"; //$NON-NLS-1$ //$NON-NLS-2$
final String actType = c.getContentType();
return expType.equals(actType);
@ -662,13 +657,13 @@ Collection<String> getPackNames() throws IOException {
FileStream open(final String path) throws IOException {
final URL base = objectsUrl;
final URL u = new URL(base, path);
final HttpURLConnection c = httpOpen(u);
final HttpConnection c = httpOpen(u);
switch (HttpSupport.response(c)) {
case HttpURLConnection.HTTP_OK:
case HttpConnection.HTTP_OK:
final InputStream in = openInputStream(c);
final int len = c.getContentLength();
return new FileStream(in, len);
case HttpURLConnection.HTTP_NOT_FOUND:
case HttpConnection.HTTP_NOT_FOUND:
throw new FileNotFoundException(u.toString());
default:
throw new IOException(u.toString() + ": " //$NON-NLS-1$
@ -794,7 +789,7 @@ abstract class Service {
protected final String responseType;
protected HttpURLConnection conn;
protected HttpConnection conn;
protected HttpOutputStream out;
@ -849,7 +844,7 @@ void sendRequest() throws IOException {
void openResponse() throws IOException {
final int status = HttpSupport.response(conn);
if (status != HttpURLConnection.HTTP_OK) {
if (status != HttpConnection.HTTP_OK) {
throw new TransportException(uri, status + " " //$NON-NLS-1$
+ conn.getResponseMessage());
}

View File

@ -0,0 +1,277 @@
/*
* Copyright (C) 2013 Christian Halstrick <christian.halstrick@sap.com>
* 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;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
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.TrustManager;
/**
* The interface of connections used during HTTP communication. This interface
* is that subset of the interface exposed by {@link HttpURLConnection} which is
* used by JGit
*
* @since 3.3
*/
public interface HttpConnection {
/**
* @see HttpURLConnection#HTTP_OK
*/
public static final int HTTP_OK = java.net.HttpURLConnection.HTTP_OK;
/**
* @see HttpURLConnection#HTTP_NOT_FOUND
*/
public static final int HTTP_NOT_FOUND = java.net.HttpURLConnection.HTTP_NOT_FOUND;
/**
* @see HttpURLConnection#HTTP_UNAUTHORIZED
*/
public static final int HTTP_UNAUTHORIZED = java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
/**
* @see HttpURLConnection#HTTP_FORBIDDEN
*/
public static final int HTTP_FORBIDDEN = java.net.HttpURLConnection.HTTP_FORBIDDEN;
/**
* @see HttpURLConnection#getResponseCode()
* @return the HTTP Status-Code, or -1
* @throws IOException
*/
public int getResponseCode() throws IOException;
/**
* @see HttpURLConnection#getURL()
* @return the URL.
*/
public URL getURL();
/**
* @see HttpURLConnection#getResponseMessage()
* @return the HTTP response message, or <code>null</code>
* @throws IOException
*/
public String getResponseMessage() throws IOException;
/**
* @see HttpURLConnection#getHeaderFields()
* @return a Map of header fields
*/
public Map<String, List<String>> getHeaderFields();
/**
* @see HttpURLConnection#setRequestProperty(String, String)
* @param key
* the keyword by which the request is known (e.g., "
* <code>Accept</code>").
* @param value
* the value associated with it.
*/
public void setRequestProperty(String key, String value);
/**
* @see HttpURLConnection#setRequestMethod(String)
* @param method
* the HTTP method
* @exception ProtocolException
* if the method cannot be reset or if the requested method
* isn't valid for HTTP.
*/
public void setRequestMethod(String method)
throws ProtocolException;
/**
* @see HttpURLConnection#setUseCaches(boolean)
* @param usecaches
* a <code>boolean</code> indicating whether or not to allow
* caching
*/
public void setUseCaches(boolean usecaches);
/**
* @see HttpURLConnection#setConnectTimeout(int)
* @param timeout
* an <code>int</code> that specifies the connect timeout value
* in milliseconds
*/
public void setConnectTimeout(int timeout);
/**
* @see HttpURLConnection#setReadTimeout(int)
* @param timeout
* an <code>int</code> that specifies the timeout value to be
* used in milliseconds
*/
public void setReadTimeout(int timeout);
/**
* @see HttpURLConnection#getContentType()
* @return the content type of the resource that the URL references, or
* <code>null</code> if not known.
*/
public String getContentType();
/**
* @see HttpURLConnection#getInputStream()
* @return an input stream that reads from this open connection.
* @exception IOException
* if an I/O error occurs while creating the input stream.
*/
public InputStream getInputStream() throws IOException;
/**
* @see HttpURLConnection#getHeaderField(String)
* @param name
* the name of a header field.
* @return the value of the named header field, or <code>null</code> if
* there is no such field in the header.
*/
public String getHeaderField(String name);
/**
* @see HttpURLConnection#getContentLength()
* @return the content length of the resource that this connection's URL
* references, {@code -1} if the content length is not known, or if
* the content length is greater than Integer.MAX_VALUE.
*/
public int getContentLength();
/**
* @see HttpURLConnection#setInstanceFollowRedirects(boolean)
* @param followRedirects
* a <code>boolean</code> indicating whether or not to follow
* HTTP redirects.
*/
public void setInstanceFollowRedirects(boolean followRedirects);
/**
* @see HttpURLConnection#setDoOutput(boolean)
* @param dooutput the new value.
*/
public void setDoOutput(boolean dooutput);
/**
* @see HttpURLConnection#setFixedLengthStreamingMode(int)
* @param contentLength
* The number of bytes which will be written to the OutputStream.
*
*/
public void setFixedLengthStreamingMode(int contentLength);
/**
* @see HttpURLConnection#getOutputStream()
* @return an output stream that writes to this connection.
* @throws IOException
*/
public OutputStream getOutputStream() throws IOException;
/**
* @see HttpURLConnection#setChunkedStreamingMode(int)
* @param chunklen
* The number of bytes to write in each chunk. If chunklen is
* less than or equal to zero, a default value will be used.
*/
public void setChunkedStreamingMode(int chunklen);
/**
* @see HttpURLConnection#getRequestMethod()
* @return the HTTP request method
*/
public String getRequestMethod();
/**
* @see HttpURLConnection#usingProxy()
* @return a boolean indicating if the connection is using a proxy.
*/
public boolean usingProxy();
/**
* @see HttpURLConnection#connect()
* @throws IOException
*/
public void connect() throws IOException;
/**
* Configure the connection so that it can be used for https communication.
*
* @param km
* the keymanager managing the key material used to authenticate
* the local SSLSocket to its peer
* @param tm
* the trustmanager responsible for managing the trust material
* that is used when making trust decisions, and for deciding
* whether credentials presented by a peer should be accepted.
* @param random
* the source of randomness for this generator or null. See
* {@link SSLContext#init(KeyManager[], TrustManager[], SecureRandom)}
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
public void configure(KeyManager[] km, TrustManager[] tm,
SecureRandom random) throws NoSuchAlgorithmException,
KeyManagementException;
/**
* Set the {@link HostnameVerifier} used during https communication
*
* @param hostnameverifier
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
public void setHostnameVerifier(HostnameVerifier hostnameverifier)
throws NoSuchAlgorithmException, KeyManagementException;
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (C) 2013 Christian Halstrick <christian.halstrick@sap.com>
* 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;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
/**
* The interface of a factory returning {@link HttpConnection}
*
* @since 3.3
*/
public interface HttpConnectionFactory {
/**
* Creates a new connection to a destination defined by a {@link URL}
*
* @param url
* @return a {@link HttpConnection}
* @throws IOException
*/
public HttpConnection create(URL url) throws IOException;
/**
* Creates a new connection to a destination defined by a {@link URL} using
* a proxy
*
* @param url
* @param proxy
* the proxy to be used
* @return a {@link HttpConnection}
*
* @throws IOException
*/
public HttpConnection create(URL url, Proxy proxy)
throws IOException;
}

View File

@ -0,0 +1,194 @@
/*
* Copyright (C) 2013 Christian Halstrick <christian.halstrick@sap.com>
* 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;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
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.util.List;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
/**
* A {@link HttpConnection} which simply delegates every call to a
* {@link HttpURLConnection}. This is the default implementation used by JGit
*
* @since 3.3
*/
public class JDKHttpConnection implements HttpConnection {
HttpURLConnection wrappedUrlConnection;
/**
* @param url
* @throws MalformedURLException
* @throws IOException
*/
protected JDKHttpConnection(URL url)
throws MalformedURLException,
IOException {
this.wrappedUrlConnection = (HttpURLConnection) url.openConnection();
}
/**
* @param url
* @param proxy
* @throws MalformedURLException
* @throws IOException
*/
protected JDKHttpConnection(URL url, Proxy proxy)
throws MalformedURLException, IOException {
this.wrappedUrlConnection = (HttpURLConnection) url
.openConnection(proxy);
}
public int getResponseCode() throws IOException {
return wrappedUrlConnection.getResponseCode();
}
public URL getURL() {
return wrappedUrlConnection.getURL();
}
public String getResponseMessage() throws IOException {
return wrappedUrlConnection.getResponseMessage();
}
public Map<String, List<String>> getHeaderFields() {
return wrappedUrlConnection.getHeaderFields();
}
public void setRequestProperty(String key, String value) {
wrappedUrlConnection.setRequestProperty(key, value);
}
public void setRequestMethod(String method) throws ProtocolException {
wrappedUrlConnection.setRequestMethod(method);
}
public void setUseCaches(boolean usecaches) {
wrappedUrlConnection.setUseCaches(usecaches);
}
public void setConnectTimeout(int timeout) {
wrappedUrlConnection.setConnectTimeout(timeout);
}
public void setReadTimeout(int timeout) {
wrappedUrlConnection.setReadTimeout(timeout);
}
public String getContentType() {
return wrappedUrlConnection.getContentType();
}
public InputStream getInputStream() throws IOException {
return wrappedUrlConnection.getInputStream();
}
public String getHeaderField(String name) {
return wrappedUrlConnection.getHeaderField(name);
}
public int getContentLength() {
return wrappedUrlConnection.getContentLength();
}
public void setInstanceFollowRedirects(boolean followRedirects) {
wrappedUrlConnection.setInstanceFollowRedirects(followRedirects);
}
public void setDoOutput(boolean dooutput) {
wrappedUrlConnection.setDoOutput(dooutput);
}
public void setFixedLengthStreamingMode(int contentLength) {
wrappedUrlConnection.setFixedLengthStreamingMode(contentLength);
}
public OutputStream getOutputStream() throws IOException {
return wrappedUrlConnection.getOutputStream();
}
public void setChunkedStreamingMode(int chunklen) {
wrappedUrlConnection.setChunkedStreamingMode(chunklen);
}
public String getRequestMethod() {
return wrappedUrlConnection.getRequestMethod();
}
public boolean usingProxy() {
return wrappedUrlConnection.usingProxy();
}
public void connect() throws IOException {
wrappedUrlConnection.connect();
}
public void setHostnameVerifier(HostnameVerifier hostnameverifier) {
((HttpsURLConnection) wrappedUrlConnection)
.setHostnameVerifier(hostnameverifier);
}
public void configure(KeyManager[] km, TrustManager[] tm,
SecureRandom random) throws NoSuchAlgorithmException,
KeyManagementException {
SSLContext ctx = SSLContext.getInstance("SSL"); //$NON-NLS-1$
ctx.init(km, tm, random);
((HttpsURLConnection) wrappedUrlConnection).setSSLSocketFactory(ctx
.getSocketFactory());
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (C) 2013 Christian Halstrick <christian.halstrick@sap.com>
* 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;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
/**
* A factory returning instances of {@link JDKHttpConnection}
*
* @since 3.3
*/
public class JDKHttpConnectionFactory implements HttpConnectionFactory {
public HttpConnection create(URL url) throws IOException {
return new JDKHttpConnection(url);
}
public HttpConnection create(URL url, Proxy proxy)
throws IOException {
return new JDKHttpConnection(url, proxy);
}
}

View File

@ -47,7 +47,6 @@
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URISyntaxException;
@ -56,6 +55,7 @@
import java.text.MessageFormat;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.transport.http.HttpConnection;
/** Extra utilities to support usage of HTTP. */
public class HttpSupport {
@ -158,11 +158,12 @@ public static void encode(final StringBuilder urlstr, final String key) {
* @param c
* connection the code should be obtained from.
* @return r HTTP status code, usually 200 to indicate success. See
* {@link HttpURLConnection} for other defined constants.
* {@link HttpConnection} for other defined constants.
* @throws IOException
* communications error prevented obtaining the response code.
* @since 3.3
*/
public static int response(final HttpURLConnection c) throws IOException {
public static int response(final HttpConnection c) throws IOException {
try {
return c.getResponseCode();
} catch (ConnectException ce) {
@ -175,6 +176,34 @@ public static int response(final HttpURLConnection c) throws IOException {
}
}
/**
* Get the HTTP response code from the request.
* <p>
* Roughly the same as <code>c.getResponseCode()</code> but the
* ConnectException is translated to be more understandable.
*
* @param c
* connection the code should be obtained from.
* @return r HTTP status code, usually 200 to indicate success. See
* {@link HttpConnection} for other defined constants.
* @throws IOException
* communications error prevented obtaining the response code.
*/
public static int response(final java.net.HttpURLConnection c)
throws IOException {
try {
return c.getResponseCode();
} catch (ConnectException ce) {
final String host = c.getURL().getHost();
// The standard J2SE error message is not very useful.
//
if ("Connection timed out: connect".equals(ce.getMessage()))
throw new ConnectException(MessageFormat.format(
JGitText.get().connectionTimeOut, host));
throw new ConnectException(ce.getMessage() + " " + host); //$NON-NLS-1$
}
}
/**
* Determine the proxy server (if any) needed to obtain a URL.
*