1 /*
2  * Copyright (C) 2010 The Android Open Source Project
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  */
16 
17 package libcore.java.net;
18 
19 import java.io.Closeable;
20 import java.io.IOException;
21 import java.net.DatagramPacket;
22 import java.net.DatagramSocket;
23 import java.net.InetAddress;
24 import java.net.InetSocketAddress;
25 import java.net.ServerSocket;
26 import java.net.Socket;
27 import java.net.SocketAddress;
28 import java.net.SocketException;
29 import java.net.UnknownHostException;
30 import java.nio.channels.AsynchronousCloseException;
31 import java.nio.channels.ClosedChannelException;
32 import java.nio.channels.SocketChannel;
33 import java.util.ArrayList;
34 import java.util.List;
35 import java.util.concurrent.CopyOnWriteArrayList;
36 
37 /**
38  * Test that Socket.close called on another thread interrupts a thread that's blocked doing
39  * network I/O.
40  */
41 public class ConcurrentCloseTest extends junit.framework.TestCase {
42     private static final InetSocketAddress UNREACHABLE_ADDRESS
43             = new InetSocketAddress("192.0.2.0", 80); // RFC 5737
44 
test_accept()45     public void test_accept() throws Exception {
46         ServerSocket ss = new ServerSocket(0);
47         new Killer(ss).start();
48         try {
49             System.err.println("accept...");
50             Socket s = ss.accept();
51             fail("accept returned " + s + "!");
52         } catch (SocketException expected) {
53             assertEquals("Socket closed", expected.getMessage());
54         }
55     }
56 
test_connect()57     public void test_connect() throws Exception {
58         Socket s = new Socket();
59         new Killer(s).start();
60         try {
61             System.err.println("connect...");
62             s.connect(UNREACHABLE_ADDRESS);
63             fail("connect returned: " + s + "!");
64         } catch (SocketException expected) {
65             assertEquals("Socket closed", expected.getMessage());
66         }
67     }
68 
test_connect_timeout()69     public void test_connect_timeout() throws Exception {
70         Socket s = new Socket();
71         new Killer(s).start();
72         try {
73             System.err.println("connect (with timeout)...");
74             s.connect(UNREACHABLE_ADDRESS, 3600 * 1000);
75             fail("connect returned: " + s + "!");
76         } catch (SocketException expected) {
77             assertEquals("Socket closed", expected.getMessage());
78         }
79     }
80 
test_connect_nonBlocking()81     public void test_connect_nonBlocking() throws Exception {
82         SocketChannel s = SocketChannel.open();
83         new Killer(s.socket()).start();
84         try {
85             System.err.println("connect (non-blocking)...");
86             s.configureBlocking(false);
87             s.connect(UNREACHABLE_ADDRESS);
88             while (!s.finishConnect()) {
89                 // Spin like a mad thing!
90             }
91             fail("connect returned: " + s + "!");
92         } catch (SocketException expected) {
93             assertEquals("Socket closed", expected.getMessage());
94         } catch (AsynchronousCloseException alsoOkay) {
95             // See below.
96         } catch (ClosedChannelException alsoOkay) {
97             // For now, I'm assuming that we're happy as long as we get any reasonable exception.
98             // It may be that we're supposed to guarantee only one or the other.
99         }
100     }
101 
test_read()102     public void test_read() throws Exception {
103         SilentServer ss = new SilentServer();
104         Socket s = new Socket();
105         s.connect(ss.getLocalSocketAddress());
106         new Killer(s).start();
107         try {
108             System.err.println("read...");
109             int i = s.getInputStream().read();
110             fail("read returned: " + i);
111         } catch (SocketException expected) {
112             assertEquals("Socket closed", expected.getMessage());
113         }
114         ss.close();
115     }
116 
test_read_multiple()117     public void test_read_multiple() throws Throwable {
118         SilentServer ss = new SilentServer();
119         final Socket s = new Socket();
120         s.connect(ss.getLocalSocketAddress());
121 
122         // We want to test that we unblock *all* the threads blocked on a socket, not just one.
123         // We know the implementation uses the same mechanism for all blocking calls, so we just
124         // test read(2) because it's the easiest to test. (recv(2), for example, is only accessible
125         // from Java via a synchronized method.)
126         final ArrayList<Thread> threads = new ArrayList<Thread>();
127         final List<Throwable> thrownExceptions = new CopyOnWriteArrayList<Throwable>();
128         for (int i = 0; i < 10; ++i) {
129             Thread t = new Thread(new Runnable() {
130                 public void run() {
131                     try {
132                         try {
133                             System.err.println("read...");
134                             int i = s.getInputStream().read();
135                             fail("read returned: " + i);
136                         } catch (SocketException expected) {
137                             assertEquals("Socket closed", expected.getMessage());
138                         }
139                     } catch (Throwable ex) {
140                         thrownExceptions.add(ex);
141                     }
142                 }
143             });
144             threads.add(t);
145         }
146         for (Thread t : threads) {
147             t.start();
148         }
149         new Killer(s).start();
150         for (Thread t : threads) {
151             t.join();
152         }
153         for (Throwable exception : thrownExceptions) {
154             throw exception;
155         }
156 
157         ss.close();
158     }
159 
test_recv()160     public void test_recv() throws Exception {
161         DatagramSocket s = new DatagramSocket();
162         byte[] buf = new byte[200];
163         DatagramPacket p = new DatagramPacket(buf, 200);
164         new Killer(s).start();
165         try {
166             System.err.println("receive...");
167             s.receive(p);
168             fail("receive returned!");
169         } catch (SocketException expected) {
170             assertEquals("Socket closed", expected.getMessage());
171         }
172     }
173 
test_write()174     public void test_write() throws Exception {
175         final SilentServer ss = new SilentServer(128); // Minimal receive buffer size.
176         Socket s = new Socket();
177 
178         // Set the send buffer size really small, to ensure we block.
179         int sendBufferSize = 1024;
180         s.setSendBufferSize(sendBufferSize);
181         sendBufferSize = s.getSendBufferSize(); // How big is the buffer really, Linux?
182 
183         // Linux still seems to accept more than it should.
184         // How much seems to differ from device to device. This used to be (sendBufferSize * 2)
185         // but that still failed on a bullhead (Nexus 5X).
186         sendBufferSize *= 4;
187 
188         s.connect(ss.getLocalSocketAddress());
189         new Killer(s).start();
190         try {
191             System.err.println("write...");
192             // Write too much so the buffer is full and we block,
193             // waiting for the server to read (which it never will).
194             // If the asynchronous close fails, we'll see a test timeout here.
195             byte[] buf = new byte[sendBufferSize];
196             s.getOutputStream().write(buf);
197             fail();
198         } catch (SocketException expected) {
199             // We throw "Connection reset by peer", which I don't _think_ is a problem.
200             // assertEquals("Socket closed", expected.getMessage());
201         }
202         ss.close();
203     }
204 
205     // This server accepts connections, but doesn't read or write anything.
206     // It holds on to the Socket connecting to the client so it won't be GCed.
207     // Call "close" to close both the server socket and its client connection.
208     static class SilentServer {
209         private final ServerSocket ss;
210         private Socket client;
211 
SilentServer()212         public SilentServer() throws IOException {
213             this(0);
214         }
215 
SilentServer(int receiveBufferSize)216         public SilentServer(int receiveBufferSize) throws IOException {
217             ss = new ServerSocket(0);
218             if (receiveBufferSize != 0) {
219                 ss.setReceiveBufferSize(receiveBufferSize);
220             }
221             new Thread(new Runnable() {
222                 public void run() {
223                     try {
224                         client = ss.accept();
225                     } catch (Exception ex) {
226                         ex.printStackTrace();
227                     }
228                 }
229             }).start();
230         }
231 
getLocalSocketAddress()232         public SocketAddress getLocalSocketAddress() {
233             return ss.getLocalSocketAddress();
234         }
235 
close()236         public void close() throws IOException {
237             client.close();
238             ss.close();
239         }
240     }
241 
242     // This thread calls the "close" method on the supplied T after 2s.
243     static class Killer<T extends Closeable> extends Thread {
244         private final T s;
245 
Killer(T s)246         public Killer(T s) {
247             this.s = s;
248         }
249 
run()250         public void run() {
251             try {
252                 System.err.println("sleep...");
253                 Thread.sleep(2000);
254                 System.err.println("close...");
255                 s.close();
256             } catch (Exception ex) {
257                 ex.printStackTrace();
258             }
259         }
260     }
261 }
262