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  import java.util.EventListener;
21  import java.util.List;
22  import java.util.concurrent.CopyOnWriteArrayList;
23  
24  import org.eclipse.jetty.spdy.CompressionFactory;
25  import org.eclipse.jetty.spdy.SessionException;
26  import org.eclipse.jetty.spdy.StreamException;
27  import org.eclipse.jetty.spdy.api.SessionStatus;
28  import org.eclipse.jetty.spdy.frames.ControlFrame;
29  import org.eclipse.jetty.spdy.frames.DataFrame;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  
33  public class Parser
34  {
35      private static final Logger logger = LoggerFactory.getLogger(Parser.class);
36      private final List<Listener> listeners = new CopyOnWriteArrayList<>();
37      private final ControlFrameParser controlFrameParser;
38      private final DataFrameParser dataFrameParser;
39      private State state = State.CONTROL_BIT;
40  
41      public Parser(CompressionFactory.Decompressor decompressor)
42      {
43          // It is important to allocate one decompression context per
44          // SPDY session for the control frames (to decompress the headers)
45          controlFrameParser = new ControlFrameParser(decompressor)
46          {
47              @Override
48              protected void onControlFrame(ControlFrame frame)
49              {
50                  logger.debug("Parsed {}", frame);
51                  notifyControlFrame(frame);
52              }
53          };
54          dataFrameParser = new DataFrameParser()
55          {
56              @Override
57              protected void onDataFrame(DataFrame frame, ByteBuffer data)
58              {
59                  logger.debug("Parsed {}, {} data bytes", frame, data.remaining());
60                  notifyDataFrame(frame, data);
61              }
62          };
63      }
64  
65      public void addListener(Listener listener)
66      {
67          listeners.add(listener);
68      }
69  
70      public void removeListener(Listener listener)
71      {
72          listeners.remove(listener);
73      }
74  
75      protected void notifyControlFrame(ControlFrame frame)
76      {
77          for (Listener listener : listeners)
78          {
79              try
80              {
81                  listener.onControlFrame(frame);
82              }
83              catch (Exception x)
84              {
85                  logger.info("Exception while notifying listener " + listener, x);
86              }
87          }
88      }
89  
90      protected void notifyDataFrame(DataFrame frame, ByteBuffer data)
91      {
92          for (Listener listener : listeners)
93          {
94              try
95              {
96                  listener.onDataFrame(frame, data);
97              }
98              catch (Exception x)
99              {
100                 logger.info("Exception while notifying listener " + listener, x);
101             }
102         }
103     }
104 
105     protected void notifyStreamException(StreamException x)
106     {
107         for (Listener listener : listeners)
108         {
109             listener.onStreamException(x);
110         }
111     }
112 
113     protected void notifySessionException(SessionException x)
114     {
115         logger.debug("SPDY session exception", x);
116         for (Listener listener : listeners)
117         {
118             try
119             {
120                 listener.onSessionException(x);
121             }
122             catch (Exception xx)
123             {
124                 logger.debug("Could not notify listener " + listener, xx);
125             }
126         }
127     }
128 
129     public void parse(ByteBuffer buffer)
130     {
131         try
132         {
133             logger.debug("Parsing {} bytes", buffer.remaining());
134             while (buffer.hasRemaining())
135             {
136                 switch (state)
137                 {
138                     case CONTROL_BIT:
139                     {
140                         // We must only peek the first byte and not advance the buffer
141                         // because the 7 least significant bits may be relevant in data frames
142                         int currByte = buffer.get(buffer.position());
143                         boolean isControlFrame = (currByte & 0x80) == 0x80;
144                         state = isControlFrame ? State.CONTROL_FRAME : State.DATA_FRAME;
145                         break;
146                     }
147                     case CONTROL_FRAME:
148                     {
149                         if (controlFrameParser.parse(buffer))
150                             reset();
151                         break;
152                     }
153                     case DATA_FRAME:
154                     {
155                         if (dataFrameParser.parse(buffer))
156                             reset();
157                         break;
158                     }
159                     default:
160                     {
161                         throw new IllegalStateException();
162                     }
163                 }
164             }
165         }
166         catch (SessionException x)
167         {
168             notifySessionException(x);
169         }
170         catch (StreamException x)
171         {
172             notifyStreamException(x);
173         }
174         catch (Throwable x)
175         {
176             notifySessionException(new SessionException(SessionStatus.PROTOCOL_ERROR, x));
177         }
178         finally
179         {
180             // Be sure to consume after exceptions
181             buffer.position(buffer.limit());
182         }
183     }
184 
185     private void reset()
186     {
187         state = State.CONTROL_BIT;
188     }
189 
190     public interface Listener extends EventListener
191     {
192         public void onControlFrame(ControlFrame frame);
193 
194         public void onDataFrame(DataFrame frame, ByteBuffer data);
195 
196         public void onStreamException(StreamException x);
197 
198         public void onSessionException(SessionException x);
199 
200         public static class Adapter implements Listener
201         {
202             @Override
203             public void onControlFrame(ControlFrame frame)
204             {
205             }
206 
207             @Override
208             public void onDataFrame(DataFrame frame, ByteBuffer data)
209             {
210             }
211 
212             @Override
213             public void onStreamException(StreamException x)
214             {
215             }
216 
217             @Override
218             public void onSessionException(SessionException x)
219             {
220             }
221         }
222     }
223 
224     private enum State
225     {
226         CONTROL_BIT, CONTROL_FRAME, DATA_FRAME
227     }
228 
229 }