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 fd9267e7a..7aa8696bb 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 e46ef427a..4b32bdbe1 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.ConnectionPool; import com.squareup.okhttp.ConnectionSpec; 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 { @@ -2256,6 +2263,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();