< 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 >