aboutsummaryrefslogtreecommitdiff
path: root/okio/src/jvmTest/java/okio/SocketTimeoutTest.java
blob: 6a6aadcd246947ed7c761d61dda3502eebf0642d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
 * 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 okio;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.concurrent.TimeUnit;
import org.junit.Test;

import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

public final class SocketTimeoutTest {

  // The size of the socket buffers to use. Less than half the data transferred during tests to
  // ensure send and receive buffers are flooded and any necessary blocking behavior takes place.
  private static final int SOCKET_BUFFER_SIZE = 256 * 1024;
  private static final int ONE_MB = 1024 * 1024;

  @Test public void readWithoutTimeout() throws Exception {
    Socket socket = socket(ONE_MB, 0);
    BufferedSource source = Okio.buffer(Okio.source(socket));
    source.timeout().timeout(5000, TimeUnit.MILLISECONDS);
    source.require(ONE_MB);
    socket.close();
  }

  @Test public void readWithTimeout() throws Exception {
    Socket socket = socket(0, 0);
    BufferedSource source = Okio.buffer(Okio.source(socket));
    source.timeout().timeout(250, TimeUnit.MILLISECONDS);
    try {
      source.require(ONE_MB);
      fail();
    } catch (SocketTimeoutException expected) {
    }
    socket.close();
  }

  @Test public void writeWithoutTimeout() throws Exception {
    Socket socket = socket(0, ONE_MB);
    Sink sink = Okio.buffer(Okio.sink(socket));
    sink.timeout().timeout(500, TimeUnit.MILLISECONDS);
    byte[] data = new byte[ONE_MB];
    sink.write(new Buffer().write(data), data.length);
    sink.flush();
    socket.close();
  }

  @Test public void writeWithTimeout() throws Exception {
    Socket socket = socket(0, 0);
    Sink sink = Okio.sink(socket);
    sink.timeout().timeout(500, TimeUnit.MILLISECONDS);
    byte[] data = new byte[ONE_MB];
    long start = System.nanoTime();
    try {
      sink.write(new Buffer().write(data), data.length);
      sink.flush();
      fail();
    } catch (SocketTimeoutException expected) {
    }
    long elapsed = System.nanoTime() - start;
    socket.close();

    assertTrue("elapsed: " + elapsed, TimeUnit.NANOSECONDS.toMillis(elapsed) >= 500);
    assertTrue("elapsed: " + elapsed, TimeUnit.NANOSECONDS.toMillis(elapsed) <= 750);
  }

  /**
   * Returns a socket that can read {@code readableByteCount} incoming bytes and
   * will accept {@code writableByteCount} written bytes. The socket will idle
   * for 5 seconds when the required data has been read and written.
   */
  static Socket socket(final int readableByteCount, final int writableByteCount) throws IOException {
    final ServerSocket serverSocket = new ServerSocket(0);
    serverSocket.setReuseAddress(true);
    serverSocket.setReceiveBufferSize(SOCKET_BUFFER_SIZE);

    Thread peer = new Thread("peer") {
      @Override public void run() {
        Socket socket = null;
        try {
          socket = serverSocket.accept();
          socket.setSendBufferSize(SOCKET_BUFFER_SIZE);
          writeFully(socket.getOutputStream(), readableByteCount);
          readFully(socket.getInputStream(), writableByteCount);
          Thread.sleep(5000); // Sleep 5 seconds so the peer can close the connection.
        } catch (Exception ignored) {
        } finally {
          try {
            if (socket != null) socket.close();
          } catch (IOException ignored) {
          }
        }
      }
    };
    peer.start();

    Socket socket = new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort());
    socket.setReceiveBufferSize(SOCKET_BUFFER_SIZE);
    socket.setSendBufferSize(SOCKET_BUFFER_SIZE);
    return socket;
  }

  private static void writeFully(OutputStream out, int byteCount) throws IOException {
    out.write(new byte[byteCount]);
    out.flush();
  }

  private static byte[] readFully(InputStream in, int byteCount) throws IOException {
    int count = 0;
    byte[] result = new byte[byteCount];
    while (count < byteCount) {
      int read = in.read(result, count, result.length - count);
      if (read == -1) throw new EOFException();
      count += read;
    }
    return result;
  }
}