SocketTest.java revision da15009528cc8300a6251f1d0931ac8657c9fc31
1/*
2 * Copyright (C) 2009 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
17package libcore.java.net;
18
19import java.io.IOException;
20import java.io.InputStream;
21import java.io.OutputStream;
22import java.net.ConnectException;
23import java.net.InetAddress;
24import java.net.InetSocketAddress;
25import java.net.ServerSocket;
26import java.net.Socket;
27import java.net.SocketAddress;
28import java.net.SocketException;
29import java.net.SocketImpl;
30import java.nio.channels.ServerSocketChannel;
31import java.nio.channels.SocketChannel;
32import java.util.concurrent.Callable;
33import java.util.concurrent.ExecutorService;
34import java.util.concurrent.Executors;
35import java.util.concurrent.Future;
36
37public class SocketTest extends junit.framework.TestCase {
38    // See http://b/2980559.
39    public void test_close() throws Exception {
40        Socket s = new Socket();
41        s.close();
42        // Closing a closed socket does nothing.
43        s.close();
44    }
45
46    /**
47     * Our getLocalAddress and getLocalPort currently use getsockname(3).
48     * This means they give incorrect results on closed sockets (as well
49     * as requiring an unnecessary call into native code).
50     */
51    public void test_getLocalAddress_after_close() throws Exception {
52        Socket s = new Socket();
53        try {
54            // Bind to an ephemeral local port.
55            s.bind(new InetSocketAddress("localhost", 0));
56            assertTrue(s.getLocalAddress().toString(), s.getLocalAddress().isLoopbackAddress());
57            // What local port did we get?
58            int localPort = s.getLocalPort();
59            assertTrue(localPort > 0);
60            // Now close the socket...
61            s.close();
62            // The RI returns the ANY address but the original local port after close.
63            assertTrue(s.getLocalAddress().isAnyLocalAddress());
64            assertEquals(localPort, s.getLocalPort());
65        } finally {
66            s.close();
67        }
68    }
69
70    // http://code.google.com/p/android/issues/detail?id=7935
71    public void test_newSocket_connection_refused() throws Exception {
72        try {
73            new Socket("localhost", 80);
74            fail("connection should have been refused");
75        } catch (ConnectException expected) {
76        }
77    }
78
79    // http://code.google.com/p/android/issues/detail?id=3123
80    // http://code.google.com/p/android/issues/detail?id=1933
81    public void test_socketLocalAndRemoteAddresses() throws Exception {
82        checkSocketLocalAndRemoteAddresses(false);
83        checkSocketLocalAndRemoteAddresses(true);
84    }
85
86    public void checkSocketLocalAndRemoteAddresses(boolean setOptions) throws Exception {
87        InetAddress host = InetAddress.getLocalHost();
88
89        // Open a local server port.
90        ServerSocketChannel ssc = ServerSocketChannel.open();
91        InetSocketAddress listenAddr = new InetSocketAddress(host, 0);
92        ssc.socket().bind(listenAddr, 0);
93        ServerSocket ss = ssc.socket();
94
95        // Open a socket to the local port.
96        SocketChannel out = SocketChannel.open();
97        out.configureBlocking(false);
98        if (setOptions) {
99            out.socket().setTcpNoDelay(false);
100        }
101        InetSocketAddress addr = new InetSocketAddress(host, ssc.socket().getLocalPort());
102        out.connect(addr);
103        while (!out.finishConnect()) {
104            Thread.sleep(1);
105        }
106
107        SocketChannel in = ssc.accept();
108        if (setOptions) {
109            in.socket().setTcpNoDelay(false);
110        }
111
112        InetSocketAddress outRemoteAddress = (InetSocketAddress) out.socket().getRemoteSocketAddress();
113        InetSocketAddress outLocalAddress = (InetSocketAddress) out.socket().getLocalSocketAddress();
114        InetSocketAddress inLocalAddress = (InetSocketAddress) in.socket().getLocalSocketAddress();
115        InetSocketAddress inRemoteAddress = (InetSocketAddress) in.socket().getRemoteSocketAddress();
116        System.err.println("inLocalAddress: " + inLocalAddress);
117        System.err.println("inRemoteAddress: " + inRemoteAddress);
118        System.err.println("outLocalAddress: " + outLocalAddress);
119        System.err.println("outRemoteAddress: " + outRemoteAddress);
120
121        assertEquals(outRemoteAddress.getPort(), ss.getLocalPort());
122        assertEquals(inLocalAddress.getPort(), ss.getLocalPort());
123        assertEquals(inRemoteAddress.getPort(), outLocalAddress.getPort());
124
125        assertEquals(inLocalAddress.getAddress(), ss.getInetAddress());
126        assertEquals(inRemoteAddress.getAddress(), ss.getInetAddress());
127        assertEquals(outLocalAddress.getAddress(), ss.getInetAddress());
128        assertEquals(outRemoteAddress.getAddress(), ss.getInetAddress());
129
130        in.close();
131        out.close();
132        ssc.close();
133
134        assertNull(in.socket().getRemoteSocketAddress());
135        assertNull(out.socket().getRemoteSocketAddress());
136
137        assertEquals(in.socket().getLocalSocketAddress(), ss.getLocalSocketAddress());
138    }
139
140    // SocketOptions.setOption has weird behavior for setSoLinger/SO_LINGER.
141    // This test ensures we do what the RI does.
142    public void test_SocketOptions_setOption() throws Exception {
143        class MySocketImpl extends SocketImpl {
144            public int option;
145            public Object value;
146
147            public boolean createCalled;
148            public boolean createStream;
149
150            public MySocketImpl() { super(); }
151            @Override protected void accept(SocketImpl arg0) throws IOException { }
152            @Override protected int available() throws IOException { return 0; }
153            @Override protected void bind(InetAddress arg0, int arg1) throws IOException { }
154            @Override protected void close() throws IOException { }
155            @Override protected void connect(String arg0, int arg1) throws IOException { }
156            @Override protected void connect(InetAddress arg0, int arg1) throws IOException { }
157            @Override protected void connect(SocketAddress arg0, int arg1) throws IOException { }
158            @Override protected InputStream getInputStream() throws IOException { return null; }
159            @Override protected OutputStream getOutputStream() throws IOException { return null; }
160            @Override protected void listen(int arg0) throws IOException { }
161            @Override protected void sendUrgentData(int arg0) throws IOException { }
162            public Object getOption(int arg0) throws SocketException { return null; }
163
164            @Override protected void create(boolean isStream) throws IOException {
165                this.createCalled = true;
166                this.createStream = isStream;
167            }
168
169            public void setOption(int option, Object value) throws SocketException {
170                this.option = option;
171                this.value = value;
172            }
173        }
174
175        class MySocket extends Socket {
176            public MySocket(SocketImpl impl) throws SocketException {
177                super(impl);
178            }
179        }
180
181        MySocketImpl impl = new MySocketImpl();
182        Socket s = new MySocket(impl);
183
184        // Check that, as per the SocketOptions.setOption documentation, we pass false rather
185        // than -1 to the SocketImpl when setSoLinger is called with the first argument false.
186        s.setSoLinger(false, -1);
187        assertEquals(Boolean.FALSE, (Boolean) impl.value);
188        // We also check that SocketImpl.create was called. SocketChannelImpl.SocketAdapter
189        // subclasses Socket, and whether or not to call SocketImpl.create is the main behavioral
190        // difference.
191        assertEquals(true, impl.createCalled);
192        s.setSoLinger(false, 0);
193        assertEquals(Boolean.FALSE, (Boolean) impl.value);
194        s.setSoLinger(false, 1);
195        assertEquals(Boolean.FALSE, (Boolean) impl.value);
196
197        // Check that otherwise, we pass down an Integer.
198        s.setSoLinger(true, 0);
199        assertEquals(Integer.valueOf(0), (Integer) impl.value);
200        s.setSoLinger(true, 1);
201        assertEquals(Integer.valueOf(1), (Integer) impl.value);
202    }
203
204    public void test_setTrafficClass() throws Exception {
205        Socket s = new Socket();
206        s.setTrafficClass(123);
207        assertEquals(123, s.getTrafficClass());
208    }
209
210    public void testReadAfterClose() throws Exception {
211        MockServer server = new MockServer();
212        server.enqueue(new byte[]{5, 3}, 0);
213        Socket socket = new Socket("localhost", server.port);
214        InputStream in = socket.getInputStream();
215        assertEquals(5, in.read());
216        assertEquals(3, in.read());
217        assertEquals(-1, in.read());
218        assertEquals(-1, in.read());
219        socket.close();
220        in.close();
221
222        /*
223         * Rather astonishingly, read() doesn't throw even though the stream is
224         * closed. This is consistent with the RI's behavior.
225         */
226        assertEquals(-1, in.read());
227        assertEquals(-1, in.read());
228
229        server.shutdown();
230    }
231
232    public void testWriteAfterClose() throws Exception {
233        MockServer server = new MockServer();
234        server.enqueue(new byte[0], 3);
235        Socket socket = new Socket("localhost", server.port);
236        OutputStream out = socket.getOutputStream();
237        out.write(5);
238        out.write(3);
239        socket.close();
240        out.close();
241
242        try {
243            out.write(9);
244            fail();
245        } catch (IOException expected) {
246        }
247
248        server.shutdown();
249    }
250
251    static class MockServer {
252        private ExecutorService executor;
253        private ServerSocket serverSocket;
254        private int port = -1;
255
256        MockServer() throws IOException {
257            executor = Executors.newCachedThreadPool();
258            serverSocket = new ServerSocket(0);
259            serverSocket.setReuseAddress(true);
260            port = serverSocket.getLocalPort();
261        }
262
263        public Future<byte[]> enqueue(final byte[] sendBytes, final int receiveByteCount)
264                throws IOException {
265            return executor.submit(new Callable<byte[]>() {
266                @Override public byte[] call() throws Exception {
267                    Socket socket = serverSocket.accept();
268                    OutputStream out = socket.getOutputStream();
269                    out.write(sendBytes);
270
271                    InputStream in = socket.getInputStream();
272                    byte[] result = new byte[receiveByteCount];
273                    int total = 0;
274                    while (total < receiveByteCount) {
275                        total += in.read(result, total, result.length - total);
276                    }
277                    socket.close();
278                    return result;
279                }
280            });
281        }
282
283        public void shutdown() throws IOException {
284            serverSocket.close();
285            executor.shutdown();
286        }
287    }
288}
289