View Javadoc

1   /*
2    * Copyright (c) 2012 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.eclipse.jetty.spdy.parser;
18  
19  import java.nio.ByteBuffer;
20  
21  import org.eclipse.jetty.spdy.api.DataInfo;
22  import org.eclipse.jetty.spdy.frames.DataFrame;
23  
24  public abstract class DataFrameParser
25  {
26      private State state = State.STREAM_ID;
27      private int cursor;
28      private int streamId;
29      private byte flags;
30      private int length;
31  
32      /**
33       * <p>Parses the given {@link ByteBuffer} for a data frame.</p>
34       *
35       * @param buffer the {@link ByteBuffer} to parse
36       * @return true if the data frame has been fully parsed, false otherwise
37       */
38      public boolean parse(ByteBuffer buffer)
39      {
40          while (buffer.hasRemaining())
41          {
42              switch (state)
43              {
44                  case STREAM_ID:
45                  {
46                      if (buffer.remaining() >= 4)
47                      {
48                          streamId = buffer.getInt() & 0x7F_FF_FF_FF;
49                          state = State.FLAGS;
50                      }
51                      else
52                      {
53                          state = State.STREAM_ID_BYTES;
54                          cursor = 4;
55                      }
56                      break;
57                  }
58                  case STREAM_ID_BYTES:
59                  {
60                      byte currByte = buffer.get();
61                      --cursor;
62                      streamId += (currByte & 0xFF) << 8 * cursor;
63                      if (cursor == 0)
64                          state = State.FLAGS;
65                      break;
66                  }
67                  case FLAGS:
68                  {
69                      flags = buffer.get();
70                      cursor = 3;
71                      state = State.LENGTH;
72                      break;
73                  }
74                  case LENGTH:
75                  {
76                      byte currByte = buffer.get();
77                      --cursor;
78                      length += (currByte & 0xFF) << 8 * cursor;
79                      if (cursor > 0)
80                          break;
81                      state = State.DATA;
82                      // Fall down if length == 0: we can't loop because the buffer
83                      // may be empty but we need to invoke the application anyway
84                      if (length > 0)
85                          break;
86                  }
87                  case DATA:
88                  {
89                      // Length can only be at most 3 bytes, which is 16_777_215 i.e. 16 MiB.
90                      // However, compliant clients should implement flow control, so it's
91                      // unlikely that we will get that 16 MiB chunk.
92                      // However, TCP may further split the flow control window, so we may
93                      // only have part of the data at this point.
94  
95                      int size = Math.min(length, buffer.remaining());
96                      int limit = buffer.limit();
97                      buffer.limit(buffer.position() + size);
98                      ByteBuffer bytes = buffer.slice();
99                      buffer.limit(limit);
100                     buffer.position(buffer.position() + size);
101                     length -= size;
102                     if (length == 0)
103                     {
104                         onDataFrame(bytes);
105                         return true;
106                     }
107                     else
108                     {
109                         // We got only part of the frame data bytes,
110                         // so we generate a synthetic data frame
111                         onDataFragment(bytes);
112                     }
113                     break;
114                 }
115                 default:
116                 {
117                     throw new IllegalStateException();
118                 }
119             }
120         }
121         return false;
122     }
123 
124     private void onDataFrame(ByteBuffer bytes)
125     {
126         DataFrame frame = new DataFrame(streamId, flags, bytes.remaining());
127         onDataFrame(frame, bytes);
128         reset();
129     }
130 
131     private void onDataFragment(ByteBuffer bytes)
132     {
133         DataFrame frame = new DataFrame(streamId, (byte)(flags & ~DataInfo.FLAG_CLOSE), bytes.remaining());
134         onDataFrame(frame, bytes);
135         // Do not reset, we're expecting more data
136     }
137 
138     protected abstract void onDataFrame(DataFrame frame, ByteBuffer data);
139 
140     private void reset()
141     {
142         state = State.STREAM_ID;
143         cursor = 0;
144         streamId = 0;
145         flags = 0;
146         length = 0;
147     }
148 
149     private enum State
150     {
151         STREAM_ID, STREAM_ID_BYTES, FLAGS, LENGTH, DATA
152     }
153 }