1 /*
   2  * Copyright (c) 2019, 2020, 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  * @run testng ConnectionReset
  27  * @run testng/othervm -Djdk.net.usePlainSocketImpl ConnectionReset
  28  * @summary Test behavior of read and available when a connection is reset
  29  */
  30 
  31 import java.io.IOException;
  32 import java.io.InputStream;
  33 import java.net.InetAddress;
  34 import java.net.InetSocketAddress;
  35 import java.net.ServerSocket;
  36 import java.net.Socket;
  37 
  38 import org.testng.annotations.Test;
  39 import static org.testng.Assert.*;
  40 
  41 @Test
  42 public class ConnectionReset {
  43 
  44     static final int REPEAT_COUNT = 5;
  45 
  46     /**
  47      * Tests available before read when there are no bytes to read
  48      */
  49     public void testAvailableBeforeRead1() throws IOException {
  50         System.out.println("testAvailableBeforeRead1");
  51         withResetConnection(null, s -> {
  52             InputStream in = s.getInputStream();
  53             for (int i=0; i<REPEAT_COUNT; i++) {
  54                 int bytesAvailable = in.available();
  55                 System.out.format("available => %d%n", bytesAvailable);
  56                 assertTrue(bytesAvailable == 0);
  57                 try {
  58                     int bytesRead = in.read();
  59                     if (bytesRead == -1) {
  60                         System.out.println("read => EOF");
  61                     } else {
  62                         System.out.println("read => 1 byte");
  63                     }
  64                     assertTrue(false);
  65                 } catch (IOException ioe) {
  66                     System.out.format("read => %s (expected)%n", ioe);
  67                 }
  68             }
  69         });
  70     }
  71 
  72     /**
  73      * Tests available before read when there are bytes to read
  74      */
  75     public void testAvailableBeforeRead2() throws IOException {
  76         System.out.println("testAvailableBeforeRead2");
  77         byte[] data = { 1, 2, 3 };
  78         withResetConnection(data, s -> {
  79             InputStream in = s.getInputStream();
  80             int remaining = data.length;
  81             for (int i=0; i<REPEAT_COUNT; i++) {
  82                 int bytesAvailable = in.available();
  83                 System.out.format("available => %d%n", bytesAvailable);
  84                 assertTrue(bytesAvailable <= remaining);
  85                 try {
  86                     int bytesRead = in.read();
  87                     if (bytesRead == -1) {
  88                         System.out.println("read => EOF");
  89                         assertTrue(false);
  90                     } else {
  91                         System.out.println("read => 1 byte");
  92                         assertTrue(remaining > 0);
  93                         remaining--;
  94                     }
  95                 } catch (IOException ioe) {
  96                     System.out.format("read => %s%n", ioe);
  97                     remaining = 0;
  98                 }
  99             }
 100         });
 101     }
 102 
 103     /**
 104      * Tests read before available when there are no bytes to read
 105      */
 106     public void testReadBeforeAvailable1() throws IOException {
 107         System.out.println("testReadBeforeAvailable1");
 108         withResetConnection(null, s -> {
 109             InputStream in = s.getInputStream();
 110             for (int i=0; i<REPEAT_COUNT; i++) {
 111                 try {
 112                     int bytesRead = in.read();
 113                     if (bytesRead == -1) {
 114                         System.out.println("read => EOF");
 115                     } else {
 116                         System.out.println("read => 1 byte");
 117                     }
 118                     assertTrue(false);
 119                 } catch (IOException ioe) {
 120                     System.out.format("read => %s (expected)%n", ioe);
 121                 }
 122                 int bytesAvailable = in.available();
 123                 System.out.format("available => %d%n", bytesAvailable);
 124                 assertTrue(bytesAvailable == 0);
 125             }
 126         });
 127     }
 128 
 129     /**
 130      * Tests read before available when there are bytes to read
 131      */
 132     public void testReadBeforeAvailable2() throws IOException {
 133         System.out.println("testReadBeforeAvailable2");
 134         byte[] data = { 1, 2, 3 };
 135         withResetConnection(data, s -> {
 136             InputStream in = s.getInputStream();
 137             int remaining = data.length;
 138             for (int i=0; i<REPEAT_COUNT; i++) {
 139                 try {
 140                     int bytesRead = in.read();
 141                     if (bytesRead == -1) {
 142                         System.out.println("read => EOF");
 143                         assertTrue(false);
 144                     } else {
 145                         System.out.println("read => 1 byte");
 146                         assertTrue(remaining > 0);
 147                         remaining--;
 148                     }
 149                 } catch (IOException ioe) {
 150                     System.out.format("read => %s%n", ioe);
 151                     remaining = 0;
 152                 }
 153                 int bytesAvailable = in.available();
 154                 System.out.format("available => %d%n", bytesAvailable);
 155                 assertTrue(bytesAvailable <= remaining);
 156             }
 157         });
 158     }
 159 
 160     /**
 161      * Tests available and read on a socket closed after connection reset
 162      */
 163     public void testAfterClose() throws IOException {
 164         System.out.println("testAfterClose");
 165         withResetConnection(null, s -> {
 166             InputStream in = s.getInputStream();
 167             try {
 168                 in.read();
 169                 assertTrue(false);
 170             } catch (IOException ioe) {
 171                 // expected
 172             }
 173             s.close();
 174             try {
 175                 int bytesAvailable = in.available();
 176                 System.out.format("available => %d%n", bytesAvailable);
 177                 assertTrue(false);
 178             } catch (IOException ioe) {
 179                 System.out.format("available => %s (expected)%n", ioe);
 180             }
 181             try {
 182                 int n = in.read();
 183                 System.out.format("read => %d%n", n);
 184                 assertTrue(false);
 185             } catch (IOException ioe) {
 186                 System.out.format("read => %s (expected)%n", ioe);
 187             }
 188         });
 189     }
 190 
 191     interface ThrowingConsumer<T> {
 192         void accept(T t) throws IOException;
 193     }
 194 
 195     /**
 196      * Invokes a consumer with a Socket connected to a peer that has closed the
 197      * connection with a "connection reset". The peer sends the given data bytes
 198      * before closing (when data is not null).
 199      */
 200     static void withResetConnection(byte[] data, ThrowingConsumer<Socket> consumer)
 201         throws IOException
 202     {
 203         var loopback = InetAddress.getLoopbackAddress();
 204         try (var listener = new ServerSocket()) {
 205             listener.bind(new InetSocketAddress(loopback, 0));
 206             try (var socket = new Socket()) {
 207                 socket.connect(listener.getLocalSocketAddress());
 208                 try (Socket peer = listener.accept()) {
 209                     if (data != null) {
 210                         peer.getOutputStream().write(data);
 211                     }
 212                     peer.setSoLinger(true, 0);
 213                 }
 214                 consumer.accept(socket);
 215             }
 216         }
 217     }
 218 }