Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix TLS requiresTunnel which was being computed incorrectly. #29

Merged
merged 1 commit into from
Sep 20, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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 -->
Expand Down
4 changes: 0 additions & 4 deletions src/main/java/com/squareup/okhttp/OkHttpsConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,6 @@ public void setHostnameVerifier(HostnameVerifier v) {

/**
* Returns the hostname verifier used by this instance.
*
* @return the hostname verifier used by this instance.
*/
public HostnameVerifier getHostnameVerifier() {
return hostnameVerifier;
Expand All @@ -290,8 +288,6 @@ public void setSSLSocketFactory(SSLSocketFactory sf) {

/**
* Returns the SSL socket factory used by this instance.
*
* @return the SSL socket factory used by this instance.
*/
public SSLSocketFactory getSSLSocketFactory() {
return sslSocketFactory;
Expand Down
35 changes: 16 additions & 19 deletions src/main/java/libcore/net/http/HttpConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,21 +116,21 @@ private HttpConnection(Address config, int connectTimeout) throws IOException {
* 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);
}

Expand All @@ -148,8 +148,7 @@ public static HttpConnection connect(URI uri, SSLSocketFactory sslSocketFactory,
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
Expand Down Expand Up @@ -293,7 +292,6 @@ public boolean isSpdy() {
*/
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;
Expand All @@ -302,7 +300,6 @@ public static final class Address {

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;
Expand All @@ -313,16 +310,9 @@ public Address(URI uri, SSLSocketFactory sslSocketFactory) throws UnknownHostExc
}
}

/**
* @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;
Expand Down Expand Up @@ -350,8 +340,7 @@ public Proxy getProxy() {
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;
}
Expand All @@ -362,12 +351,20 @@ public Proxy getProxy() {
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;
}
}
}
6 changes: 1 addition & 5 deletions src/main/java/libcore/net/http/HttpEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ protected void connect() throws IOException {

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);
Expand Down Expand Up @@ -580,10 +580,6 @@ protected final String getOriginAddress(URL url) {
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.
Expand Down
6 changes: 1 addition & 5 deletions src/main/java/libcore/net/http/HttpsURLConnectionImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ private void makeSslConnection(boolean tlsTolerant) throws IOException {
// 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());
}
}
Expand Down Expand Up @@ -527,9 +527,5 @@ public ProxyConnectEngine(HttpURLConnectionImpl policy, RawHeaders requestHeader
HttpConnection connection) throws IOException {
super(policy, HttpEngine.CONNECT, requestHeaders, connection, null);
}

@Override protected boolean requiresTunnel() {
return true;
}
}
}
85 changes: 43 additions & 42 deletions src/test/java/libcore/net/http/URLConnectionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
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;
Expand Down Expand Up @@ -54,6 +55,7 @@
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;
Expand Down Expand Up @@ -636,48 +638,47 @@ private void testConnectViaHttpProxyToHttps(ProxyConfig proxyConfig) throws Exce
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.
Expand Down