1 //
2 // ========================================================================
3 // Copyright (c) 1995-2015 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.parser;
20
21 import java.nio.ByteBuffer;
22
23 import org.eclipse.jetty.spdy.api.DataInfo;
24 import org.eclipse.jetty.spdy.frames.DataFrame;
25
26 public abstract class DataFrameParser
27 {
28 private State state = State.STREAM_ID;
29 private int cursor;
30 private int streamId;
31 private byte flags;
32 private int length;
33
34 /**
35 * <p>Parses the given {@link ByteBuffer} for a data frame.</p>
36 *
37 * @param buffer the {@link ByteBuffer} to parse
38 * @return true if the data frame has been fully parsed, false otherwise
39 */
40 public boolean parse(ByteBuffer buffer)
41 {
42 while (buffer.hasRemaining())
43 {
44 switch (state)
45 {
46 case STREAM_ID:
47 {
48 if (buffer.remaining() >= 4)
49 {
50 streamId = buffer.getInt() & 0x7F_FF_FF_FF;
51 state = State.FLAGS;
52 }
53 else
54 {
55 state = State.STREAM_ID_BYTES;
56 cursor = 4;
57 }
58 break;
59 }
60 case STREAM_ID_BYTES:
61 {
62 byte currByte = buffer.get();
63 --cursor;
64 streamId += (currByte & 0xFF) << 8 * cursor;
65 if (cursor == 0)
66 state = State.FLAGS;
67 break;
68 }
69 case FLAGS:
70 {
71 flags = buffer.get();
72 cursor = 3;
73 state = State.LENGTH;
74 break;
75 }
76 case LENGTH:
77 {
78 byte currByte = buffer.get();
79 --cursor;
80 length += (currByte & 0xFF) << 8 * cursor;
81 if (cursor > 0)
82 break;
83 state = State.DATA;
84 // Fall down if length == 0: we can't loop because the buffer
85 // may be empty but we need to invoke the application anyway
86 if (length > 0)
87 break;
88 }
89 case DATA:
90 {
91 // Length can only be at most 3 bytes, which is 16_777_215 i.e. 16 MiB.
92 // However, compliant clients should implement flow control, so it's
93 // unlikely that we will get that 16 MiB chunk.
94 // However, TCP may further split the flow control window, so we may
95 // only have part of the data at this point.
96
97 int size = Math.min(length, buffer.remaining());
98 int limit = buffer.limit();
99 buffer.limit(buffer.position() + size);
100 ByteBuffer bytes = buffer.slice();
101 buffer.limit(limit);
102 buffer.position(buffer.position() + size);
103 length -= size;
104 if (length == 0)
105 {
106 onDataFrame(bytes);
107 return true;
108 }
109 else
110 {
111 // We got only part of the frame data bytes,
112 // so we generate a synthetic data frame
113 onDataFragment(bytes);
114 }
115 break;
116 }
117 default:
118 {
119 throw new IllegalStateException();
120 }
121 }
122 }
123 return false;
124 }
125
126 private void onDataFrame(ByteBuffer bytes)
127 {
128 DataFrame frame = new DataFrame(streamId, flags, bytes.remaining());
129 onDataFrame(frame, bytes);
130 reset();
131 }
132
133 private void onDataFragment(ByteBuffer bytes)
134 {
135 DataFrame frame = new DataFrame(streamId, (byte)(flags & ~DataInfo.FLAG_CLOSE), bytes.remaining());
136 onDataFrame(frame, bytes);
137 // Do not reset, we're expecting more data
138 }
139
140 protected abstract void onDataFrame(DataFrame frame, ByteBuffer data);
141
142 private void reset()
143 {
144 state = State.STREAM_ID;
145 cursor = 0;
146 streamId = 0;
147 flags = 0;
148 length = 0;
149 }
150
151 private enum State
152 {
153 STREAM_ID, STREAM_ID_BYTES, FLAGS, LENGTH, DATA
154 }
155 }