From d95341bb7bc13ee2a4c7a08d6c7f9944a8a51ae3 Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Wed, 26 Nov 2014 16:03:25 +0000 Subject: [PATCH] Fix tests that fail on some Android devices Some tests fails on some Android devices because they have larger buffer sizes. Setting the buffer size of sockets used in tests that rely on blocking behavior ensures consistency. ThreadInterruptTest#interruptWritingRequestBody URLConnectionTest#writeTimeouts --- .../okhttp/mockwebserver/MockWebServer.java | 9 ++- .../okhttp/DelegatingServerSocketFactory.java | 67 ++++++++++++++++ .../okhttp/DelegatingSocketFactory.java | 76 +++++++++++++++++++ .../internal/http/ThreadInterruptTest.java | 43 ++++++++++- .../internal/http/URLConnectionTest.java | 31 +++++++- 5 files changed, 220 insertions(+), 6 deletions(-) create mode 100644 okhttp-tests/src/test/java/com/squareup/okhttp/DelegatingServerSocketFactory.java create mode 100644 okhttp-tests/src/test/java/com/squareup/okhttp/DelegatingSocketFactory.java diff --git a/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java b/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java index 6c5f5f896..0677c25a2 100644 --- a/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java +++ b/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java @@ -58,6 +58,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; +import javax.net.ServerSocketFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; @@ -101,6 +102,7 @@ public final class MockWebServer { private final Map openSpdyConnections = new ConcurrentHashMap<>(); private final AtomicInteger requestCount = new AtomicInteger(); private int bodyLimit = Integer.MAX_VALUE; + private ServerSocketFactory serverSocketFactory = ServerSocketFactory.getDefault(); private ServerSocket serverSocket; private SSLSocketFactory sslSocketFactory; private ExecutorService executor; @@ -113,6 +115,11 @@ public final class MockWebServer { private List protocols = Util.immutableList(Protocol.HTTP_2, Protocol.SPDY_3, Protocol.HTTP_1_1); + public void setServerSocketFactory(ServerSocketFactory serverSocketFactory) { + if (serverSocketFactory == null) throw new IllegalArgumentException("null serverSocketFactory"); + this.serverSocketFactory = serverSocketFactory; + } + public int getPort() { if (port == -1) throw new IllegalStateException("Call play() before getPort()"); return port; @@ -283,7 +290,7 @@ public final class MockWebServer { if (executor != null) throw new IllegalStateException("play() already called"); executor = Executors.newCachedThreadPool(Util.threadFactory("MockWebServer", false)); inetAddress = InetAddress.getLocalHost(); - serverSocket = new ServerSocket(port, 50, inetAddress); + serverSocket = serverSocketFactory.createServerSocket(port, 50, inetAddress); serverSocket.setReuseAddress(true); this.port = serverSocket.getLocalPort(); diff --git a/okhttp-tests/src/test/java/com/squareup/okhttp/DelegatingServerSocketFactory.java b/okhttp-tests/src/test/java/com/squareup/okhttp/DelegatingServerSocketFactory.java new file mode 100644 index 000000000..9813d6444 --- /dev/null +++ b/okhttp-tests/src/test/java/com/squareup/okhttp/DelegatingServerSocketFactory.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.okhttp; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import javax.net.ServerSocketFactory; + +/** + * A {@link ServerSocketFactory} that delegates calls. Sockets can be configured after creation by + * overriding {@link #configureServerSocket(java.net.ServerSocket)}. + */ +public class DelegatingServerSocketFactory extends ServerSocketFactory { + + private final ServerSocketFactory delegate; + + public DelegatingServerSocketFactory(ServerSocketFactory delegate) { + this.delegate = delegate; + } + + @Override + public ServerSocket createServerSocket() throws IOException { + ServerSocket serverSocket = delegate.createServerSocket(); + configureServerSocket(serverSocket); + return serverSocket; + } + + @Override + public ServerSocket createServerSocket(int port) throws IOException { + ServerSocket serverSocket = delegate.createServerSocket(port); + configureServerSocket(serverSocket); + return serverSocket; + } + + @Override + public ServerSocket createServerSocket(int port, int backlog) throws IOException { + ServerSocket serverSocket = delegate.createServerSocket(port, backlog); + configureServerSocket(serverSocket); + return serverSocket; + } + + @Override + public ServerSocket createServerSocket(int port, int backlog, InetAddress ifAddress) + throws IOException { + ServerSocket serverSocket = delegate.createServerSocket(port, backlog, ifAddress); + configureServerSocket(serverSocket); + return serverSocket; + } + + protected void configureServerSocket(ServerSocket serverSocket) throws IOException { + // no-op by default + } +} diff --git a/okhttp-tests/src/test/java/com/squareup/okhttp/DelegatingSocketFactory.java b/okhttp-tests/src/test/java/com/squareup/okhttp/DelegatingSocketFactory.java new file mode 100644 index 000000000..2437842d8 --- /dev/null +++ b/okhttp-tests/src/test/java/com/squareup/okhttp/DelegatingSocketFactory.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2014 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.okhttp; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import javax.net.SocketFactory; + +/** + * A {@link SocketFactory} that delegates calls. Sockets can be configured after creation by + * overriding {@link #configureSocket(java.net.Socket)}. + */ +public class DelegatingSocketFactory extends SocketFactory { + + private final SocketFactory delegate; + + public DelegatingSocketFactory(SocketFactory delegate) { + this.delegate = delegate; + } + + @Override + public Socket createSocket() throws IOException { + Socket socket = delegate.createSocket(); + configureSocket(socket); + return socket; + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + Socket socket = delegate.createSocket(host, port); + configureSocket(socket); + return socket; + } + + @Override + public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) + throws IOException, UnknownHostException { + Socket socket = delegate.createSocket(host, port, localAddress, localPort); + configureSocket(socket); + return socket; + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + Socket socket = delegate.createSocket(host, port); + configureSocket(socket); + return socket; + } + + @Override + public Socket createSocket(InetAddress host, int port, InetAddress localAddress, int localPort) + throws IOException { + Socket socket = delegate.createSocket(host, port, localAddress, localPort); + configureSocket(socket); + return socket; + } + + protected void configureSocket(Socket socket) throws IOException { + // no-op by default + } +} diff --git a/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/ThreadInterruptTest.java b/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/ThreadInterruptTest.java index 6a64034b7..c9e5a0b18 100644 --- a/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/ThreadInterruptTest.java +++ b/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/ThreadInterruptTest.java @@ -15,22 +15,61 @@ */ package com.squareup.okhttp.internal.http; +import com.squareup.okhttp.DelegatingServerSocketFactory; +import com.squareup.okhttp.DelegatingSocketFactory; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.OkUrlFactory; import com.squareup.okhttp.mockwebserver.MockResponse; import com.squareup.okhttp.mockwebserver.MockWebServer; + +import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.UnknownHostException; import java.util.concurrent.TimeUnit; + +import org.junit.Before; import org.junit.Test; +import javax.net.ServerSocketFactory; +import javax.net.SocketFactory; + import static org.junit.Assert.fail; public final class ThreadInterruptTest { - private final MockWebServer server = new MockWebServer(); - private final OkHttpClient client = new OkHttpClient(); + + // The size of the socket buffers in bytes. + private static final int SOCKET_BUFFER_SIZE = 256 * 1024; + + private MockWebServer server; + private OkHttpClient client; + + @Before public void setUp() throws Exception { + server = new MockWebServer(); + client = new OkHttpClient(); + + // Sockets on some platforms can have large buffers that mean writes do not block when + // required. These socket factories explicitly set the buffer sizes on sockets created. + server.setServerSocketFactory( + new DelegatingServerSocketFactory(ServerSocketFactory.getDefault()) { + @Override + protected void configureServerSocket(ServerSocket serverSocket) throws IOException { + serverSocket.setReceiveBufferSize(SOCKET_BUFFER_SIZE); + } + }); + client.setSocketFactory(new DelegatingSocketFactory(SocketFactory.getDefault()) { + @Override + protected void configureSocket(Socket socket) throws IOException { + socket.setSendBufferSize(SOCKET_BUFFER_SIZE); + socket.setReceiveBufferSize(SOCKET_BUFFER_SIZE); + } + }); + } @Test public void interruptWritingRequestBody() throws Exception { int requestBodySize = 2 * 1024 * 1024; // 2 MiB diff --git a/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/URLConnectionTest.java b/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/URLConnectionTest.java index ae98b44d3..c1f7a4416 100644 --- a/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/URLConnectionTest.java +++ b/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/URLConnectionTest.java @@ -21,6 +21,8 @@ import com.squareup.okhttp.Challenge; import com.squareup.okhttp.ConnectionSpec; import com.squareup.okhttp.ConnectionPool; import com.squareup.okhttp.Credentials; +import com.squareup.okhttp.DelegatingServerSocketFactory; +import com.squareup.okhttp.DelegatingSocketFactory; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.OkUrlFactory; import com.squareup.okhttp.Protocol; @@ -48,6 +50,7 @@ import java.net.InetAddress; import java.net.ProtocolException; import java.net.Proxy; import java.net.ProxySelector; +import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.net.URI; @@ -68,6 +71,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.zip.GZIPInputStream; +import javax.net.ServerSocketFactory; import javax.net.SocketFactory; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; @@ -106,15 +110,18 @@ import static org.junit.Assert.fail; public final class URLConnectionTest { private static final SSLContext sslContext = SslContextBuilder.localhost(); - private MockWebServer server = new MockWebServer(); - private MockWebServer server2 = new MockWebServer(); + private MockWebServer server; + private MockWebServer server2; - private final OkUrlFactory client = new OkUrlFactory(new OkHttpClient()); + private OkUrlFactory client; private HttpURLConnection connection; private Cache cache; @Before public void setUp() throws Exception { + server = new MockWebServer(); server.setProtocolNegotiationEnabled(false); + server2 = new MockWebServer(); + client = new OkUrlFactory(new OkHttpClient()); } @After public void tearDown() throws Exception { @@ -2201,6 +2208,24 @@ public final class URLConnectionTest { /** Confirm that an unacknowledged write times out. */ @Test public void writeTimeouts() throws IOException { + // Sockets on some platforms can have large buffers that mean writes do not block when + // required. These socket factories explicitly set the buffer sizes on sockets created. + final int SOCKET_BUFFER_SIZE = 256 * 1024; + server.setServerSocketFactory( + new DelegatingServerSocketFactory(ServerSocketFactory.getDefault()) { + @Override + protected void configureServerSocket(ServerSocket serverSocket) throws IOException { + serverSocket.setReceiveBufferSize(SOCKET_BUFFER_SIZE); + } + }); + client.client().setSocketFactory(new DelegatingSocketFactory(SocketFactory.getDefault()) { + @Override + protected void configureSocket(Socket socket) throws IOException { + socket.setReceiveBufferSize(SOCKET_BUFFER_SIZE); + socket.setSendBufferSize(SOCKET_BUFFER_SIZE); + } + }); + server.enqueue(new MockResponse() .throttleBody(1, 1, TimeUnit.SECONDS)); // Prevent the server from reading! server.play();