1 /*
   2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @requires os.family != "solaris"
  27  * @run testng ConnectionReset
  28  * @run testng/othervm -Djdk.net.usePlainSocketImpl ConnectionReset
  29  * @summary Test behavior of read and available when a connection is reset
  30  */
  31 
  32 import java.io.IOException;
  33 import java.io.InputStream;
  34 import java.net.InetAddress;
  35 import java.net.InetSocketAddress;
  36 import java.net.ServerSocket;
  37 import java.net.Socket;
  38 
  39 import org.testng.annotations.Test;
  40 import static org.testng.Assert.*;
  41 
  42 @Test
  43 public class ConnectionReset {
  44 
  45     static final int REPEAT_COUNT = 5;
  46 
  47     /**
  48      * Tests available before read when there are no bytes to read
  49      */
  50     public void testAvailableBeforeRead1() throws IOException {
  51         System.out.println("testAvailableBeforeRead1");
  52         withResetConnection(null, s -> {
  53             InputStream in = s.getInputStream();
  54             for (int i=0; i<REPEAT_COUNT; i++) {
  55                 int bytesAvailable = in.available();
  56                 System.out.format("available => %d%n", bytesAvailable);
  57                 assertTrue(bytesAvailable == 0);
  58                 try {
  59                     int bytesRead = in.read();
  60                     if (bytesRead == -1) {
  61                         System.out.println("read => EOF");
  62                     } else {
  63                         System.out.println("read => 1 byte");
  64                     }
  65                     assertTrue(false);
  66                 } catch (IOException ioe) {
  67                     System.out.format("read => %s (expected)%n", ioe);
  68                 }
  69             }
  70         });
  71     }
  72 
  73     /**
  74      * Tests available before read when there are bytes to read
  75      */
  76     public void testAvailableBeforeRead2() throws IOException {
  77         System.out.println("testAvailableBeforeRead2");
  78         byte[] data = { 1, 2, 3 };
  79         withResetConnection(data, s -> {
  80             InputStream in = s.getInputStream();
  81             int remaining = data.length;
  82             for (int i=0; i<REPEAT_COUNT; i++) {
  83                 int bytesAvailable = in.available();
  84                 System.out.format("available => %d%n", bytesAvailable);
  85                 assertTrue(bytesAvailable <= remaining);
  86                 try {
  87                     int bytesRead = in.read();
  88                     if (bytesRead == -1) {
  89                         System.out.println("read => EOF");
  90                         assertTrue(false);
  91                     } else {
  92                         System.out.println("read => 1 byte");
  93                         assertTrue(remaining > 0);
  94                         remaining--;
  95                     }
  96                 } catch (IOException ioe) {
  97                     System.out.format("read => %s%n", ioe);
  98                     remaining = 0;
  99                 }
 100             }
 101         });
 102     }
 103 
 104     /**
 105      * Tests read before available when there are no bytes to read
 106      */
 107     public void testReadBeforeAvailable1() throws IOException {
 108         System.out.println("testReadBeforeAvailable1");
 109         withResetConnection(null, s -> {
 110             InputStream in = s.getInputStream();
 111             for (int i=0; i<REPEAT_COUNT; i++) {
 112                 try {
 113                     int bytesRead = in.read();
 114                     if (bytesRead == -1) {
 115                         System.out.println("read => EOF");
 116                     } else {
 117                         System.out.println("read => 1 byte");
 118                     }
 119                     assertTrue(false);
 120                 } catch (IOException ioe) {
 121                     System.out.format("read => %s (expected)%n", ioe);
 122                 }
 123                 int bytesAvailable = in.available();
 124                 System.out.format("available => %d%n", bytesAvailable);
 125                 assertTrue(bytesAvailable == 0);
 126             }
 127         });
 128     }
 129 
 130     /**
 131      * Tests read before available when there are bytes to read
 132      */
 133     public void testReadBeforeAvailable2() throws IOException {
 134         System.out.println("testReadBeforeAvailable2");
 135         byte[] data = { 1, 2, 3 };
 136         withResetConnection(data, s -> {
 137             InputStream in = s.getInputStream();
 138             int remaining = data.length;
 139             for (int i=0; i<REPEAT_COUNT; i++) {
 140                 try {
 141                     int bytesRead = in.read();
 142                     if (bytesRead == -1) {
 143                         System.out.println("read => EOF");
 144                         assertTrue(false);
 145                     } else {
 146                         System.out.println("read => 1 byte");
 147                         assertTrue(remaining > 0);
 148                         remaining--;
 149                     }
 150                 } catch (IOException ioe) {
 151                     System.out.format("read => %s%n", ioe);
 152                     remaining = 0;
 153                 }
 154                 int bytesAvailable = in.available();
 155                 System.out.format("available => %d%n", bytesAvailable);
 156                 assertTrue(bytesAvailable <= remaining);
 157             }
 158         });
 159     }
 160 
 161     /**
 162      * Tests available and read on a socket closed after connection reset
 163      */
 164     public void testAfterClose() throws IOException {
 165         System.out.println("testAfterClose");
 166         withResetConnection(null, s -> {
 167             InputStream in = s.getInputStream();
 168             try {
 169                 in.read();
 170                 assertTrue(false);
 171             } catch (IOException ioe) {
 172                 // expected
 173             }
 174             s.close();
 175             try {
 176                 int bytesAvailable = in.available();
 177                 System.out.format("available => %d%n", bytesAvailable);
 178                 assertTrue(false);
 179             } catch (IOException ioe) {
 180                 System.out.format("available => %s (expected)%n", ioe);
 181             }
 182             try {
 183                 int n = in.read();
 184                 System.out.format("read => %d%n", n);
 185                 assertTrue(false);
 186             } catch (IOException ioe) {
 187                 System.out.format("read => %s (expected)%n", ioe);
 188             }
 189         });
 190     }
 191 
 192     interface ThrowingConsumer<T> {
 193         void accept(T t) throws IOException;
 194     }
 195 
 196     /**
 197      * Invokes a consumer with a Socket connected to a peer that has closed the
 198      * connection with a "connection reset". The peer sends the given data bytes
 199      * before closing (when data is not null).
 200      */
 201     static void withResetConnection(byte[] data, ThrowingConsumer<Socket> consumer)
 202         throws IOException
 203     {
 204         var loopback = InetAddress.getLoopbackAddress();
 205         try (var listener = new ServerSocket()) {
 206             listener.bind(new InetSocketAddress(loopback, 0));
 207             try (var socket = new Socket()) {
 208                 socket.connect(listener.getLocalSocketAddress());
 209                 try (Socket peer = listener.accept()) {
 210                     if (data != null) {
 211                         peer.getOutputStream().write(data);
 212                     }
 213                     peer.setSoLinger(true, 0);
 214                 }
 215                 consumer.accept(socket);
 216             }
 217         }
 218     }
 219 }