View Javadoc

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