1 /*
   2  * Copyright (c) 2012, 2013, 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 /* @test
  25  * @bug 7176630 7074436
  26  * @summary Check for short writes on SocketChannels configured in blocking mode

  27  */
  28 
  29 import java.net.*;
  30 import java.nio.ByteBuffer;
  31 import java.nio.channels.*;
  32 import java.util.concurrent.*;
  33 import java.util.Random;
  34 import java.util.zip.CRC32;
  35 
  36 public class ShortWrite {
  37 
  38     static final Random rand = new Random();
  39 
  40     /**
  41      * Returns a checksum on the remaining bytes in the given buffers.
  42      */
  43     static long computeChecksum(ByteBuffer... bufs) {
  44         CRC32 crc32 = new CRC32();
  45         for (int i=0; i<bufs.length; i++)
  46             crc32.update(bufs[i]);
  47         return crc32.getValue();
  48     }
  49 
  50     /**
  51      * A task that reads the expected number of bytes and returns the CRC32
  52      * of those bytes.
  53      */
  54     static class Reader implements Callable<Long> {
  55         final SocketChannel sc;
  56         final ByteBuffer buf;
  57 
  58         Reader(SocketChannel sc, int expectedSize) {
  59             this.sc = sc;
  60             this.buf = ByteBuffer.allocate(expectedSize);
  61         }
  62 
  63         public Long call() throws Exception {
  64             while (buf.hasRemaining()) {
  65                 int n = sc.read(buf);
  66                 if (n == -1)
  67                     throw new RuntimeException("Premature EOF encountered");
  68             }
  69             buf.flip();
  70             return computeChecksum(buf);
  71         }
  72     }
  73 
  74     /**
  75      * Exercise write(ByteBuffer) with given number of bytes.
  76      */
  77     static void test1(ExecutorService pool,
  78                       SocketChannel source,
  79                       SocketChannel sink,
  80                       int size)
  81         throws Exception
  82     {
  83         System.out.println("write(ByteBuffer), size=" + size);
  84 
  85         // random bytes in the buffer
  86         ByteBuffer buf = ByteBuffer.allocate(size);
  87         rand.nextBytes(buf.array());
  88 
  89         // submit task to read the bytes
  90         Future<Long> result = pool.submit(new Reader(sink, size));
  91 
  92         // write the bytes
  93         int n = source.write(buf);
  94         if (n != size)
  95             throw new RuntimeException("Short write detected");
  96 
  97         // check the bytes that were received match
  98         buf.rewind();
  99         long expected = computeChecksum(buf);
 100         long actual = result.get();
 101         if (actual != expected)
 102             throw new RuntimeException("Checksum did not match");
 103     }
 104 
 105     /**
 106      * Exercise write(ByteBuffer[]) with buffers of the given sizes.
 107      */
 108     static void testN(ExecutorService pool,
 109                       SocketChannel source,
 110                       SocketChannel sink,
 111                       int... sizes)
 112         throws Exception
 113     {
 114         System.out.print("write(ByteBuffer[]), sizes=");
 115         for (int size: sizes)
 116             System.out.print(size + " ");
 117         System.out.println();
 118 
 119         int total = 0;
 120         int len = sizes.length;
 121         ByteBuffer[] bufs = new ByteBuffer[len];
 122         for (int i=0; i<len; i++) {
 123             int size = sizes[i];
 124             ByteBuffer buf = ByteBuffer.allocate(size);
 125             rand.nextBytes(buf.array());
 126             bufs[i] = buf;
 127             total += size;
 128         }
 129 
 130         // submit task to read the bytes
 131         Future<Long> result = pool.submit(new Reader(sink, total));
 132 
 133         // write the bytes
 134         long n = source.write(bufs);
 135         if (n != total)
 136             throw new RuntimeException("Short write detected");
 137 
 138         // check the bytes that were received match
 139         for (int i=0; i<len; i++)
 140             bufs[i].rewind();
 141         long expected = computeChecksum(bufs);
 142         long actual = result.get();
 143         if (actual != expected)
 144             throw new RuntimeException("Checksum did not match");
 145     }
 146 
 147     public static void main(String[] args) throws Exception {
 148         ExecutorService pool = Executors.newSingleThreadExecutor();
 149         try {
 150             try (ServerSocketChannel ssc = ServerSocketChannel.open()) {
 151                 ssc.bind(new InetSocketAddress(0));
 152                 InetAddress lh = InetAddress.getLocalHost();
 153                 int port = ssc.socket().getLocalPort();
 154                 SocketAddress sa = new InetSocketAddress(lh, port);
 155 
 156                 try (SocketChannel source = SocketChannel.open(sa);
 157                      SocketChannel sink = ssc.accept())
 158                 {
 159                     // Exercise write(BufferBuffer) on sizes around 128k
 160                     int BOUNDARY = 128 * 1024;
 161                     for (int size=(BOUNDARY-2); size<=(BOUNDARY+2); size++) {
 162                         test1(pool, source, sink, size);
 163                     }
 164 
 165                     // Exercise write(BufferBuffer) on random sizes
 166                     for (int i=0; i<20; i++) {
 167                         int size = rand.nextInt(1024*1024);
 168                         test1(pool, source, sink, size);
 169                     }
 170 
 171                     // Exercise write(BufferBuffer[]) on sizes around 128k
 172                     for (int i=BOUNDARY-2; i<=BOUNDARY+2; i++) {
 173                         testN(pool, source, sink, i);
 174                         testN(pool, source, sink, 0, i);
 175                         testN(pool, source, sink, i, 0);
 176                         for (int j=BOUNDARY-2; j<=BOUNDARY+2; j++) {
 177                             testN(pool, source, sink, i, j);
 178                             testN(pool, source, sink, 0, i, j);
 179                             testN(pool, source, sink, i, 0, j);
 180                             testN(pool, source, sink, i, j, 0);
 181                             for (int k=BOUNDARY-2; k<=BOUNDARY+2; k++) {
 182                                 testN(pool, source, sink, i, j, k);
 183                                 testN(pool, source, sink, 0, i, j, k);
 184                                 testN(pool, source, sink, i, 0, j, k);
 185                                 testN(pool, source, sink, i, j, 0, k);
 186                                 testN(pool, source, sink, i, j, k, 0);
 187                             }
 188                         }
 189                     }
 190 
 191                     // Exercise write(BufferBuffer[]) on random sizes
 192                     // (assumes IOV_MAX >= 8)
 193                     for (int i=0; i<20; i++) {
 194                         int n = rand.nextInt(9);
 195                         int[] sizes = new int[n];
 196                         for (int j=0; j<n; j++) {
 197                             sizes[j] = rand.nextInt(1024*1024);
 198                         }
 199                         testN(pool, source, sink, sizes);
 200                     }
 201                 }
 202             }
 203 
 204         } finally {
 205             pool.shutdown();
 206         }
 207     }
 208 }
--- EOF ---