1
0
mirror of https://github.com/square/okhttp.git synced 2026-01-27 04:22:07 +03:00

Merge pull request #29 from square/jwilson/tunnel

Fix TLS requiresTunnel which was being computed incorrectly.
This commit is contained in:
Jake Wharton
2012-09-20 13:04:25 -07:00
6 changed files with 62 additions and 76 deletions

View File

@@ -38,7 +38,7 @@
<!-- Compilation -->
<java.version>1.6</java.version>
<npn.version>8.1.2.v20120308</npn.version>
<mockwebserver.version>20120731</mockwebserver.version>
<mockwebserver.version>20120905</mockwebserver.version>
<bouncycastle.version>1.47</bouncycastle.version>
<!-- Test Dependencies -->

View File

@@ -266,8 +266,6 @@ public abstract class OkHttpsConnection extends OkHttpConnection {
/**
* Returns the hostname verifier used by this instance.
*
* @return the hostname verifier used by this instance.
*/
public HostnameVerifier getHostnameVerifier() {
return hostnameVerifier;
@@ -290,8 +288,6 @@ public abstract class OkHttpsConnection extends OkHttpConnection {
/**
* Returns the SSL socket factory used by this instance.
*
* @return the SSL socket factory used by this instance.
*/
public SSLSocketFactory getSSLSocketFactory() {
return sslSocketFactory;

View File

@@ -116,21 +116,21 @@ final class HttpConnection {
* for the SSL socket.
*/
int bufferSize = 128;
inputStream = address.requiresTunnel
inputStream = address.requiresTunnel()
? socket.getInputStream()
: new BufferedInputStream(socket.getInputStream(), bufferSize);
outputStream = socket.getOutputStream();
}
public static HttpConnection connect(URI uri, SSLSocketFactory sslSocketFactory,
Proxy proxy, boolean requiresTunnel, int connectTimeout) throws IOException {
Proxy proxy, int connectTimeout) throws IOException {
/*
* Try an explicitly-specified proxy.
*/
if (proxy != null) {
Address address = (proxy.type() == Proxy.Type.DIRECT)
? new Address(uri, sslSocketFactory)
: new Address(uri, sslSocketFactory, proxy, requiresTunnel);
: new Address(uri, sslSocketFactory, proxy);
return HttpConnectionPool.INSTANCE.get(address, connectTimeout);
}
@@ -148,8 +148,7 @@ final class HttpConnection {
continue;
}
try {
Address address = new Address(uri, sslSocketFactory,
selectedProxy, requiresTunnel);
Address address = new Address(uri, sslSocketFactory, selectedProxy);
return HttpConnectionPool.INSTANCE.get(address, connectTimeout);
} catch (IOException e) {
// failed to connect, tell it to the selector
@@ -293,7 +292,6 @@ final class HttpConnection {
*/
public static final class Address {
private final Proxy proxy;
private final boolean requiresTunnel;
private final String uriHost;
private final int uriPort;
private final String socketHost;
@@ -302,7 +300,6 @@ final class HttpConnection {
public Address(URI uri, SSLSocketFactory sslSocketFactory) throws UnknownHostException {
this.proxy = null;
this.requiresTunnel = false;
this.uriHost = uri.getHost();
this.uriPort = Libcore.getEffectivePort(uri);
this.sslSocketFactory = sslSocketFactory;
@@ -313,16 +310,9 @@ final class HttpConnection {
}
}
/**
* @param requiresTunnel true if the HTTP connection needs to tunnel one
* protocol over another, such as when using HTTPS through an HTTP
* proxy. When doing so, we must avoid buffering bytes intended for
* the higher-level protocol.
*/
public Address(URI uri, SSLSocketFactory sslSocketFactory,
Proxy proxy, boolean requiresTunnel) throws UnknownHostException {
public Address(URI uri, SSLSocketFactory sslSocketFactory, Proxy proxy)
throws UnknownHostException {
this.proxy = proxy;
this.requiresTunnel = requiresTunnel;
this.uriHost = uri.getHost();
this.uriPort = Libcore.getEffectivePort(uri);
this.sslSocketFactory = sslSocketFactory;
@@ -350,8 +340,7 @@ final class HttpConnection {
return Objects.equal(this.proxy, that.proxy)
&& this.uriHost.equals(that.uriHost)
&& this.uriPort == that.uriPort
&& Objects.equal(this.sslSocketFactory, that.sslSocketFactory)
&& this.requiresTunnel == that.requiresTunnel;
&& Objects.equal(this.sslSocketFactory, that.sslSocketFactory);
}
return false;
}
@@ -362,12 +351,20 @@ final class HttpConnection {
result = 31 * result + uriPort;
result = 31 * result + (sslSocketFactory != null ? sslSocketFactory.hashCode() : 0);
result = 31 * result + (proxy != null ? proxy.hashCode() : 0);
result = 31 * result + (requiresTunnel ? 1 : 0);
return result;
}
public HttpConnection connect(int connectTimeout) throws IOException {
return new HttpConnection(this, connectTimeout);
}
/**
* Returns true if the HTTP connection needs to tunnel one protocol over
* another, such as when using HTTPS through an HTTP proxy. When doing so,
* we must avoid buffering bytes intended for the higher-level protocol.
*/
public boolean requiresTunnel() {
return sslSocketFactory != null && proxy != null && proxy.type() == Proxy.Type.HTTP;
}
}
}

View File

@@ -279,7 +279,7 @@ public class HttpEngine {
protected final HttpConnection openSocketConnection() throws IOException {
HttpConnection result = HttpConnection.connect(uri, getSslSocketFactory(),
policy.getProxy(), requiresTunnel(), policy.getConnectTimeout());
policy.getProxy(), policy.getConnectTimeout());
Proxy proxy = result.getAddress().getProxy();
if (proxy != null) {
policy.setProxy(proxy);
@@ -580,10 +580,6 @@ public class HttpEngine {
return result;
}
protected boolean requiresTunnel() {
return false;
}
/**
* Flushes the remaining request header and body, parses the HTTP response
* headers and starts reading the HTTP response body if it exists.

View File

@@ -452,7 +452,7 @@ public final class HttpsURLConnectionImpl extends OkHttpsConnection {
// make an SSL Tunnel on the first message pair of each SSL + proxy connection
if (connection == null) {
connection = openSocketConnection();
if (connection.getAddress().getProxy() != null) {
if (connection.getAddress().requiresTunnel()) {
makeTunnel(policy, connection, getRequestHeaders());
}
}
@@ -527,9 +527,5 @@ public final class HttpsURLConnectionImpl extends OkHttpsConnection {
HttpConnection connection) throws IOException {
super(policy, HttpEngine.CONNECT, requestHeaders, connection, null);
}
@Override protected boolean requiresTunnel() {
return true;
}
}
}

View File

@@ -23,6 +23,7 @@ import com.google.mockwebserver.SocketPolicy;
import com.squareup.okhttp.OkHttpConnection;
import com.squareup.okhttp.OkHttpsConnection;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -54,6 +55,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.GZIPInputStream;
@@ -636,48 +638,47 @@ public final class URLConnectionTest extends TestCase {
assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls);
}
// /**
// * Tolerate bad https proxy response when using HttpResponseCache. http://b/6754912
// */
// public void testConnectViaHttpProxyToHttpsUsingBadProxyAndHttpResponseCache() throws Exception {
// ProxyConfig proxyConfig = ProxyConfig.PROXY_SYSTEM_PROPERTY;
//
// TestSSLContext testSSLContext = TestSSLContext.create();
//
// initResponseCache();
//
// server.useHttps(testSSLContext.serverContext.getSocketFactory(), true);
// server.enqueue(new MockResponse()
// .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END)
// .clearHeaders()
// .setBody("bogus proxy connect response content")); // Key to reproducing b/6754912
// server.play();
//
// URL url = new URL("https://android.com/foo");
// HttpsURLConnection connection = (HttpsURLConnection) proxyConfig.connect(server, url);
// connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
//
// try {
// connection.connect();
// fail();
// } catch (IOException expected) {
// // Thrown when the connect causes SSLSocket.startHandshake() to throw
// // when it sees the "bogus proxy connect response content"
// // instead of a ServerHello handshake message.
// }
//
// RecordedRequest connect = server.takeRequest();
// assertEquals("Connect line failure on proxy",
// "CONNECT android.com:443 HTTP/1.1", connect.getRequestLine());
// assertContains(connect.getHeaders(), "Host: android.com");
// }
//
// private void initResponseCache() throws IOException {
// String tmp = System.getProperty("java.io.tmpdir");
// File cacheDir = new File(tmp, "HttpCache-" + UUID.randomUUID());
// cache = new HttpResponseCache(cacheDir, Integer.MAX_VALUE);
// ResponseCache.setDefault(cache);
// }
/**
* Tolerate bad https proxy response when using HttpResponseCache. http://b/6754912
*/
public void testConnectViaHttpProxyToHttpsUsingBadProxyAndHttpResponseCache() throws Exception {
ProxyConfig proxyConfig = ProxyConfig.PROXY_SYSTEM_PROPERTY;
initResponseCache();
server.useHttps(sslContext.getSocketFactory(), true);
MockResponse response = new MockResponse() // Key to reproducing b/6754912
.setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END)
.setBody("bogus proxy connect response content");
server.enqueue(response); // For the first TLS tolerant connection
server.enqueue(response); // For the backwards-compatible SSLv3 retry
server.play();
URL url = new URL("https://android.com/foo");
OkHttpsConnection connection = (OkHttpsConnection) proxyConfig.connect(server, url);
connection.setSSLSocketFactory(sslContext.getSocketFactory());
try {
connection.connect();
fail();
} catch (IOException expected) {
// Thrown when the connect causes SSLSocket.startHandshake() to throw
// when it sees the "bogus proxy connect response content"
// instead of a ServerHello handshake message.
}
RecordedRequest connect = server.takeRequest();
assertEquals("Connect line failure on proxy",
"CONNECT android.com:443 HTTP/1.1", connect.getRequestLine());
assertContains(connect.getHeaders(), "Host: android.com");
}
private void initResponseCache() throws IOException {
String tmp = System.getProperty("java.io.tmpdir");
File cacheDir = new File(tmp, "HttpCache-" + UUID.randomUUID());
cache = new HttpResponseCache(cacheDir, Integer.MAX_VALUE);
ResponseCache.setDefault(cache);
}
/**
* Test which headers are sent unencrypted to the HTTP proxy.