< prev index next >
   1 /*
   2  * Copyright (c) 2015, 2016, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  */
  24 
  25 package java.net.http;
  26 
  27 import java.io.IOException;
  28 import java.nio.ByteBuffer;
  29 import java.util.LinkedList;
  30 import java.util.List;
  31 import java.util.ListIterator;
  32 import java.util.function.Supplier;
  33 
  34 /**
  35  * Takes a List<ByteBuffer> which is assumed to contain at least one HTTP/2
  36  * frame and allows it to be processed supplying bytes, ints, shorts, byte[] etc.
  37  * from the list. As each ByteBuffer is consumed it is removed from the List<>.
  38  *
  39  * NOTE. shorts and bytes returned are UNSIGNED ints
  40  *
  41  * When finished processing the frame, the List may be empty or may contain
  42  * partially read or unread ByteBuffers. A new ByteBufferConsumer can be
  43  * created with the List<>
  44  */
  45 class ByteBufferConsumer {
  46 
  47     ByteBuffer currentBuffer;
  48 
  49     final List<ByteBuffer> buffers;
  50     final ListIterator<ByteBuffer> iterator;
  51     final Supplier<ByteBuffer> newBufferSupplier;
  52 
  53     ByteBufferConsumer(List<ByteBuffer> buffers,
  54                        Supplier<ByteBuffer> newBufferSupplier) {
  55         this.buffers = buffers;
  56         this.newBufferSupplier = newBufferSupplier;
  57         this.iterator = buffers.listIterator();
  58         if (!iterator.hasNext()) {
  59             throw new IllegalArgumentException("Empty buffer list");
  60         }
  61         currentBuffer = iterator.next();
  62     }
  63 
  64     private void dump() {
  65         int l = 0;
  66         System.err.printf("ByteBufferConsumer:\n");
  67         for (ByteBuffer buf : buffers) {
  68             System.err.printf("\t%s\n", buf.toString());
  69             l+= buf.remaining();
  70         }
  71         System.err.printf("BBC contains %d bytes\n", l);
  72     }
  73 
  74     private synchronized ByteBuffer getBuffer(boolean exception) throws IOException {
  75         while (currentBuffer == null || !currentBuffer.hasRemaining()) {
  76             if (currentBuffer != null) {
  77                 iterator.remove();
  78             }
  79             if (!iterator.hasNext()) {
  80                 currentBuffer = null;
  81                 if (exception) {
  82                     throw new IOException ("Connection closed unexpectedly");
  83                 }
  84                 return null;
  85             }
  86             currentBuffer = iterator.next();
  87         }
  88         return currentBuffer;
  89     }
  90 
  91     // call this to check if the data has all been consumed
  92 
  93     public boolean consumed() {
  94         try {
  95             return getBuffer(false) == null;
  96         } catch (IOException e) {
  97             /* CAN'T HAPPEN */
  98             throw new InternalError();
  99         }
 100     }
 101 
 102     public int getByte() throws IOException {
 103         // TODO: what to do if connection is closed. Throw NPE?
 104         ByteBuffer buf = getBuffer(true);
 105         return buf.get() & 0xff;
 106     }
 107 
 108     public byte[] getBytes(int n) throws IOException {
 109         return getBytes(n, null);
 110     }
 111 
 112     public byte[] getBytes(int n, byte[] buf) throws IOException {
 113         if (buf == null) {
 114             buf = new byte[n];
 115         } else if (buf.length < n) {
 116             throw new IllegalArgumentException("getBytes: buffer too small");
 117         }
 118         int offset = 0;
 119         while (n > 0) {
 120             ByteBuffer b = getBuffer(true);
 121             int length = Math.min(n, b.remaining());
 122             b.get(buf, offset, length);
 123             offset += length;
 124             n -= length;
 125         }
 126         return buf;
 127     }
 128 
 129     public int getShort() throws IOException {
 130         ByteBuffer buf = getBuffer(true);
 131         int rem = buf.remaining();
 132         if (rem >= 2) {
 133             return buf.getShort() & 0xffff;
 134         }
 135         // Slow path. Not common
 136         int val = 0;
 137         val = (val << 8) + getByte();
 138         val = (val << 8) + getByte();
 139         return val;
 140     }
 141 
 142     public int getInt() throws IOException {
 143         ByteBuffer buf = getBuffer(true);
 144         int rem = buf.remaining();
 145         if (rem >= 4) {
 146             return buf.getInt();
 147         }
 148         // Slow path. Not common
 149         int val = 0;
 150         for (int nbytes = 0; nbytes < 4; nbytes++) {
 151             val = (val << 8) + getByte();
 152         }
 153         return val;
 154     }
 155 
 156     private static final ByteBuffer[] EMPTY = new ByteBuffer[0];
 157 
 158     /**
 159      * Extracts whatever number of ByteBuffers from list to get required number
 160      * of bytes. Any remaining buffers are 'tidied up' so reading can continue.
 161      */
 162     public ByteBuffer[] getBuffers(int bytecount) throws IOException {
 163         LinkedList<ByteBuffer> l = new LinkedList<>();
 164         while (bytecount > 0) {
 165             ByteBuffer buffer = getBuffer(true);
 166             int remaining = buffer.remaining();
 167             if (remaining > bytecount) {
 168                 int difference = remaining - bytecount;
 169                 // split
 170                 ByteBuffer newb = newBufferSupplier.get();
 171                 newb.clear();
 172                 int limit = buffer.limit();
 173                 buffer.limit(limit - difference);
 174                 newb.put(buffer);
 175                 newb.flip();
 176                 buffer.limit(limit);
 177                 l.add(newb);
 178                 bytecount = 0;
 179             } else {
 180                 l.add(buffer);
 181                 currentBuffer = null;
 182                 iterator.remove();
 183                 bytecount -= remaining;
 184             }
 185         }
 186         return l.toArray(EMPTY);
 187     }
 188 }
< prev index next >