1 //
2 // ========================================================================
3 // Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
8 //
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
11 //
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
14 //
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
17 //
18
19 package org.eclipse.jetty.spdy.api;
20
21 import java.nio.ByteBuffer;
22 import java.nio.charset.Charset;
23 import java.util.concurrent.TimeUnit;
24 import java.util.concurrent.atomic.AtomicInteger;
25
26 /**
27 * <p>A container for DATA frames metadata and content bytes.</p>
28 * <p>Specialized subclasses (like {@link StringDataInfo}) may be used by applications
29 * to send specific types of content.</p>
30 * <p>Applications may send multiple instances of {@link DataInfo}, usually of the same
31 * type, via {@link Stream#data(DataInfo)}. The last instance must have the
32 * {@link #isClose() close flag} set, so that the client knows that no more content is
33 * expected.</p>
34 * <p>Receivers of {@link DataInfo} via {@link StreamFrameListener#onData(Stream, DataInfo)}
35 * have two different APIs to read the data content bytes: a {@link #readInto(ByteBuffer) read}
36 * API that does not interact with flow control, and a {@link #consumeInto(ByteBuffer) drain}
37 * API that interacts with flow control.</p>
38 * <p>Flow control is defined so that when the sender wants to sends a number of bytes larger
39 * than the {@link Settings.ID#INITIAL_WINDOW_SIZE} value, it will stop sending as soon as it
40 * has sent a number of bytes equal to the window size. The receiver has to <em>consume</em>
41 * the data bytes that it received in order to tell the sender to send more bytes.</p>
42 * <p>Consuming the data bytes can be done only via {@link #consumeInto(ByteBuffer)} or by a combination
43 * of {@link #readInto(ByteBuffer)} and {@link #consume(int)} (possibly at different times).</p>
44 */
45 public abstract class DataInfo extends Info
46 {
47 /**
48 * <p>Flag that indicates that this {@link DataInfo} is the last frame in the stream.</p>
49 *
50 * @see #isClose()
51 * @see #getFlags()
52 */
53 public final static byte FLAG_CLOSE = 1;
54
55 private final AtomicInteger consumed = new AtomicInteger();
56 private boolean close;
57
58 /**
59 * <p>Creates a new {@link DataInfo} with the given close flag and no compression flag.</p>
60 *
61 * @param close the value of the close flag
62 */
63 public DataInfo(boolean close)
64 {
65 setClose(close);
66 }
67
68 /**
69 * <p>Creates a new {@link DataInfo} with the given close flag and no compression flag.</p>
70 *
71 * @param timeout
72 * @param unit
73 * @param close the value of the close flag
74 */
75 protected DataInfo(long timeout, TimeUnit unit, boolean close)
76 {
77 super(timeout, unit);
78 this.close = close;
79 }
80
81 /**
82 * @return the value of the close flag
83 * @see #setClose(boolean)
84 */
85 public boolean isClose()
86 {
87 return close;
88 }
89
90 /**
91 * @param close the value of the close flag
92 * @see #isClose()
93 */
94 public void setClose(boolean close)
95 {
96 this.close = close;
97 }
98
99 /**
100 * @return the close and compress flags as integer
101 * @see #FLAG_CLOSE
102 */
103 public byte getFlags()
104 {
105 return isClose() ? FLAG_CLOSE : 0;
106 }
107
108 /**
109 * @return the total number of content bytes
110 * @see #available()
111 */
112 public abstract int length();
113
114 /**
115 * <p>Returns the available content bytes that can be read via {@link #readInto(ByteBuffer)}.</p>
116 * <p>Each invocation to {@link #readInto(ByteBuffer)} modifies the value returned by this method,
117 * until no more content bytes are available.</p>
118 *
119 * @return the available content bytes
120 * @see #readInto(ByteBuffer)
121 */
122 public abstract int available();
123
124 /**
125 * <p>Copies the content bytes of this {@link DataInfo} into the given {@link ByteBuffer}.</p>
126 * <p>If the given {@link ByteBuffer} cannot contain the whole content of this {@link DataInfo}
127 * then after the read {@link #available()} will return a positive value, and further content
128 * may be retrieved by invoking again this method with a new output buffer.</p>
129 *
130 * @param output the {@link ByteBuffer} to copy the bytes into
131 * @return the number of bytes copied
132 * @see #available()
133 * @see #consumeInto(ByteBuffer)
134 */
135 public abstract int readInto(ByteBuffer output);
136
137 /**
138 * <p>Copies the content bytes of this {@link DataInfo} into the given byte array.</p>
139 * <p>If the given byte array cannot contain the whole content of this {@link DataInfo}
140 * then after the read {@link #available()} will return a positive value, and further content
141 * may be retrieved by invoking again this method with a new byte array.</p>
142 *
143 * @param bytes the byte array to copy the bytes into
144 * @param offset the index of the byte array to start copying
145 * @param length the number of bytes to copy
146 * @return the number of bytes copied
147 */
148 public abstract int readInto(byte[] bytes, int offset, int length);
149
150 /**
151 * <p>Reads and consumes the content bytes of this {@link DataInfo} into the given {@link ByteBuffer}.</p>
152 *
153 * @param output the {@link ByteBuffer} to copy the bytes into
154 * @return the number of bytes copied
155 * @see #consume(int)
156 */
157 public int consumeInto(ByteBuffer output)
158 {
159 int read = readInto(output);
160 consume(read);
161 return read;
162 }
163
164 /**
165 * <p>Reads and consumes the content bytes of this {@link DataInfo} into the given byte array,
166 * starting from index {@code offset} for {@code length} bytes.</p>
167 *
168 * @param bytes the byte array to copy the bytes into
169 * @param offset the offset of the byte array to start copying
170 * @param length the number of bytes to copy
171 * @return the number of bytes copied
172 */
173 public int consumeInto(byte[] bytes, int offset, int length)
174 {
175 int read = readInto(bytes, offset, length);
176 consume(read);
177 return read;
178 }
179
180 /**
181 * <p>Consumes the given number of bytes from this {@link DataInfo}.</p>
182 *
183 * @param delta the number of bytes consumed
184 */
185 public void consume(int delta)
186 {
187 if (delta < 0)
188 throw new IllegalArgumentException();
189 int read = length() - available();
190 int newConsumed = consumed() + delta;
191 // if (newConsumed > read)
192 // throw new IllegalStateException("Consuming without reading: consumed " + newConsumed + " but only read " + read);
193 consumed.addAndGet(delta);
194 }
195
196 /**
197 * @return the number of bytes consumed
198 */
199 public int consumed()
200 {
201 return consumed.get();
202 }
203
204 /**
205 *
206 * @param charset the charset used to convert the bytes
207 * @param consume whether to consume the content
208 * @return a String with the content of this {@link DataInfo}
209 */
210 public String asString(String charset, boolean consume)
211 {
212 return asString(Charset.forName(charset), consume);
213 }
214
215 /**
216 *
217 * @param charset the charset used to convert the bytes
218 * @param consume whether to consume the content
219 * @return a String with the content of this {@link DataInfo}
220 */
221 public String asString(Charset charset, boolean consume)
222 {
223 ByteBuffer buffer = asByteBuffer(consume);
224 return charset.decode(buffer).toString();
225 }
226
227 /**
228 * @return a byte array with the content of this {@link DataInfo}
229 * @param consume whether to consume the content
230 */
231 public byte[] asBytes(boolean consume)
232 {
233 ByteBuffer buffer = asByteBuffer(consume);
234 byte[] result = new byte[buffer.remaining()];
235 buffer.get(result);
236 return result;
237 }
238
239 /**
240 * @return a {@link ByteBuffer} with the content of this {@link DataInfo}
241 * @param consume whether to consume the content
242 */
243 public ByteBuffer asByteBuffer(boolean consume)
244 {
245 ByteBuffer buffer = allocate(available());
246 if (consume)
247 consumeInto(buffer);
248 else
249 readInto(buffer);
250 buffer.flip();
251 return buffer;
252 }
253
254 protected ByteBuffer allocate(int size)
255 {
256 return ByteBuffer.allocate(size);
257 }
258
259 @Override
260 public String toString()
261 {
262 return String.format("DATA @%x available=%d consumed=%d close=%b", hashCode(), available(), consumed(), isClose());
263 }
264 }