1/* 2 * Copyright (C) 2014 Square, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package com.squareup.okhttp.internal.http; 17 18import com.squareup.okhttp.DelegatingServerSocketFactory; 19import com.squareup.okhttp.DelegatingSocketFactory; 20import com.squareup.okhttp.OkHttpClient; 21import com.squareup.okhttp.OkUrlFactory; 22import com.squareup.okhttp.mockwebserver.MockResponse; 23import com.squareup.okhttp.mockwebserver.MockWebServer; 24 25import java.io.IOException; 26import java.io.InputStream; 27import java.io.InterruptedIOException; 28import java.io.OutputStream; 29import java.net.HttpURLConnection; 30import java.net.ServerSocket; 31import java.net.Socket; 32import java.util.concurrent.TimeUnit; 33 34import okio.Buffer; 35import org.junit.Before; 36import org.junit.Test; 37 38import javax.net.ServerSocketFactory; 39import javax.net.SocketFactory; 40 41import static org.junit.Assert.fail; 42 43public final class ThreadInterruptTest { 44 45 // The size of the socket buffers in bytes. 46 private static final int SOCKET_BUFFER_SIZE = 256 * 1024; 47 48 private MockWebServer server; 49 private OkHttpClient client; 50 51 @Before public void setUp() throws Exception { 52 server = new MockWebServer(); 53 client = new OkHttpClient(); 54 55 // Sockets on some platforms can have large buffers that mean writes do not block when 56 // required. These socket factories explicitly set the buffer sizes on sockets created. 57 server.setServerSocketFactory( 58 new DelegatingServerSocketFactory(ServerSocketFactory.getDefault()) { 59 @Override 60 protected ServerSocket configureServerSocket(ServerSocket serverSocket) 61 throws IOException { 62 serverSocket.setReceiveBufferSize(SOCKET_BUFFER_SIZE); 63 return serverSocket; 64 } 65 }); 66 client.setSocketFactory(new DelegatingSocketFactory(SocketFactory.getDefault()) { 67 @Override 68 protected Socket configureSocket(Socket socket) throws IOException { 69 socket.setSendBufferSize(SOCKET_BUFFER_SIZE); 70 socket.setReceiveBufferSize(SOCKET_BUFFER_SIZE); 71 return socket; 72 } 73 }); 74 } 75 76 @Test public void interruptWritingRequestBody() throws Exception { 77 int requestBodySize = 2 * 1024 * 1024; // 2 MiB 78 79 server.enqueue(new MockResponse() 80 .throttleBody(64 * 1024, 125, TimeUnit.MILLISECONDS)); // 500 Kbps 81 server.start(); 82 83 interruptLater(500); 84 85 HttpURLConnection connection = new OkUrlFactory(client).open(server.getUrl("/")); 86 connection.setDoOutput(true); 87 connection.setFixedLengthStreamingMode(requestBodySize); 88 OutputStream requestBody = connection.getOutputStream(); 89 byte[] buffer = new byte[1024]; 90 try { 91 for (int i = 0; i < requestBodySize; i += buffer.length) { 92 requestBody.write(buffer); 93 requestBody.flush(); 94 } 95 fail("Expected thread to be interrupted"); 96 } catch (InterruptedIOException expected) { 97 } 98 99 connection.disconnect(); 100 } 101 102 @Test public void interruptReadingResponseBody() throws Exception { 103 int responseBodySize = 2 * 1024 * 1024; // 2 MiB 104 105 server.enqueue(new MockResponse() 106 .setBody(new Buffer().write(new byte[responseBodySize])) 107 .throttleBody(64 * 1024, 125, TimeUnit.MILLISECONDS)); // 500 Kbps 108 server.start(); 109 110 interruptLater(500); 111 112 HttpURLConnection connection = new OkUrlFactory(client).open(server.getUrl("/")); 113 InputStream responseBody = connection.getInputStream(); 114 byte[] buffer = new byte[1024]; 115 try { 116 while (responseBody.read(buffer) != -1) { 117 } 118 fail("Expected thread to be interrupted"); 119 } catch (InterruptedIOException expected) { 120 } 121 122 connection.disconnect(); 123 } 124 125 private void interruptLater(final int delayMillis) { 126 final Thread toInterrupt = Thread.currentThread(); 127 Thread interruptingCow = new Thread() { 128 @Override public void run() { 129 try { 130 sleep(delayMillis); 131 toInterrupt.interrupt(); 132 } catch (InterruptedException e) { 133 throw new RuntimeException(e); 134 } 135 } 136 }; 137 interruptingCow.start(); 138 } 139} 140