View Javadoc

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.http2.parser;
20  
21  import java.nio.ByteBuffer;
22  
23  import org.eclipse.jetty.http2.ErrorCode;
24  import org.eclipse.jetty.http2.Flags;
25  import org.eclipse.jetty.http2.frames.DataFrame;
26  import org.eclipse.jetty.http2.frames.FrameType;
27  import org.eclipse.jetty.http2.frames.GoAwayFrame;
28  import org.eclipse.jetty.http2.frames.HeadersFrame;
29  import org.eclipse.jetty.http2.frames.PingFrame;
30  import org.eclipse.jetty.http2.frames.PriorityFrame;
31  import org.eclipse.jetty.http2.frames.PushPromiseFrame;
32  import org.eclipse.jetty.http2.frames.ResetFrame;
33  import org.eclipse.jetty.http2.frames.SettingsFrame;
34  import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
35  import org.eclipse.jetty.http2.hpack.HpackDecoder;
36  import org.eclipse.jetty.io.ByteBufferPool;
37  import org.eclipse.jetty.util.BufferUtil;
38  import org.eclipse.jetty.util.log.Log;
39  import org.eclipse.jetty.util.log.Logger;
40  
41  /**
42   * <p>The HTTP/2 protocol parser.</p>
43   * <p>This parser makes use of the {@link HeaderParser} and of
44   * {@link BodyParser}s to parse HTTP/2 frames.</p>
45   */
46  public class Parser
47  {
48      private static final Logger LOG = Log.getLogger(Parser.class);
49  
50      private final Listener listener;
51      private final HeaderParser headerParser;
52      private final BodyParser[] bodyParsers;
53      private boolean continuation;
54      private State state = State.HEADER;
55  
56      public Parser(ByteBufferPool byteBufferPool, Listener listener, int maxDynamicTableSize, int maxHeaderSize)
57      {
58          this.listener = listener;
59          this.headerParser = new HeaderParser();
60          this.bodyParsers = new BodyParser[FrameType.values().length];
61  
62          HeaderBlockParser headerBlockParser = new HeaderBlockParser(byteBufferPool, new HpackDecoder(maxDynamicTableSize, maxHeaderSize));
63          HeaderBlockFragments headerBlockFragments = new HeaderBlockFragments();
64  
65          bodyParsers[FrameType.DATA.getType()] = new DataBodyParser(headerParser, listener);
66          bodyParsers[FrameType.HEADERS.getType()] = new HeadersBodyParser(headerParser, listener, headerBlockParser, headerBlockFragments);
67          bodyParsers[FrameType.PRIORITY.getType()] = new PriorityBodyParser(headerParser, listener);
68          bodyParsers[FrameType.RST_STREAM.getType()] = new ResetBodyParser(headerParser, listener);
69          bodyParsers[FrameType.SETTINGS.getType()] = new SettingsBodyParser(headerParser, listener);
70          bodyParsers[FrameType.PUSH_PROMISE.getType()] = new PushPromiseBodyParser(headerParser, listener, headerBlockParser);
71          bodyParsers[FrameType.PING.getType()] = new PingBodyParser(headerParser, listener);
72          bodyParsers[FrameType.GO_AWAY.getType()] = new GoAwayBodyParser(headerParser, listener);
73          bodyParsers[FrameType.WINDOW_UPDATE.getType()] = new WindowUpdateBodyParser(headerParser, listener);
74          bodyParsers[FrameType.CONTINUATION.getType()] = new ContinuationBodyParser(headerParser, listener, headerBlockParser, headerBlockFragments);
75      }
76  
77      private void reset()
78      {
79          headerParser.reset();
80          state = State.HEADER;
81      }
82  
83      /**
84       * <p>Parses the given {@code buffer} bytes and emit events to a {@link Listener}.</p>
85       * <p>When this method returns, the buffer may not be fully consumed, so invocations
86       * to this method should be wrapped in a loop:</p>
87       * <pre>
88       * while (buffer.hasRemaining())
89       *     parser.parse(buffer);
90       * </pre>
91       *
92       * @param buffer the buffer to parse
93       */
94      public void parse(ByteBuffer buffer)
95      {
96          try
97          {
98              while (true)
99              {
100                 switch (state)
101                 {
102                     case HEADER:
103                     {
104                         if (!parseHeader(buffer))
105                             return;
106                         break;
107                     }
108                     case BODY:
109                     {
110                         if (!parseBody(buffer))
111                             return;
112                         break;
113                     }
114                     default:
115                     {
116                         throw new IllegalStateException();
117                     }
118                 }
119             }
120         }
121         catch (Throwable x)
122         {
123             if (LOG.isDebugEnabled())
124                 LOG.debug(x);
125             BufferUtil.clear(buffer);
126             notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "parser_error");
127         }
128     }
129 
130     protected boolean parseHeader(ByteBuffer buffer)
131     {
132         if (!headerParser.parse(buffer))
133             return false;
134 
135         int frameType = getFrameType();
136         if (LOG.isDebugEnabled())
137             LOG.debug("Parsed {} frame header", FrameType.from(frameType));
138 
139         if (continuation)
140         {
141             if (frameType != FrameType.CONTINUATION.getType())
142             {
143                 // SPEC: CONTINUATION frames must be consecutive.
144                 BufferUtil.clear(buffer);
145                 notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "continuation_frame_expected");
146                 return false;
147             }
148             if (headerParser.hasFlag(Flags.END_HEADERS))
149             {
150                 continuation = false;
151             }
152         }
153         else
154         {
155             if (frameType == FrameType.HEADERS.getType() &&
156                     !headerParser.hasFlag(Flags.END_HEADERS))
157             {
158                 continuation = true;
159             }
160         }
161         state = State.BODY;
162         return true;
163     }
164 
165     protected boolean parseBody(ByteBuffer buffer)
166     {
167         int type = getFrameType();
168         if (type < 0 || type >= bodyParsers.length)
169         {
170             BufferUtil.clear(buffer);
171             notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "unknown_frame_type_" + type);
172             return false;
173         }
174 
175         FrameType frameType = FrameType.from(type);
176         if (LOG.isDebugEnabled())
177             LOG.debug("Parsing {} frame", frameType);
178 
179         BodyParser bodyParser = bodyParsers[type];
180         if (headerParser.getLength() == 0)
181         {
182             bodyParser.emptyBody(buffer);
183         }
184         else
185         {
186             if (!bodyParser.parse(buffer))
187                 return false;
188         }
189         if (LOG.isDebugEnabled())
190             LOG.debug("Parsed {} frame", frameType);
191         reset();
192         return true;
193     }
194 
195     protected int getFrameType()
196     {
197         return headerParser.getFrameType();
198     }
199 
200     protected boolean hasFlag(int bit)
201     {
202         return headerParser.hasFlag(bit);
203     }
204 
205     protected void notifyConnectionFailure(int error, String reason)
206     {
207         try
208         {
209             listener.onConnectionFailure(error, reason);
210         }
211         catch (Throwable x)
212         {
213             LOG.info("Failure while notifying listener " + listener, x);
214         }
215     }
216 
217     public interface Listener
218     {
219         public void onData(DataFrame frame);
220 
221         public void onHeaders(HeadersFrame frame);
222 
223         public void onPriority(PriorityFrame frame);
224 
225         public void onReset(ResetFrame frame);
226 
227         public void onSettings(SettingsFrame frame);
228 
229         public void onPushPromise(PushPromiseFrame frame);
230 
231         public void onPing(PingFrame frame);
232 
233         public void onGoAway(GoAwayFrame frame);
234 
235         public void onWindowUpdate(WindowUpdateFrame frame);
236 
237         public void onConnectionFailure(int error, String reason);
238 
239         public static class Adapter implements Listener
240         {
241             @Override
242             public void onData(DataFrame frame)
243             {
244             }
245 
246             @Override
247             public void onHeaders(HeadersFrame frame)
248             {
249             }
250 
251             @Override
252             public void onPriority(PriorityFrame frame)
253             {
254             }
255 
256             @Override
257             public void onReset(ResetFrame frame)
258             {
259             }
260 
261             @Override
262             public void onSettings(SettingsFrame frame)
263             {
264             }
265 
266             @Override
267             public void onPushPromise(PushPromiseFrame frame)
268             {
269             }
270 
271             @Override
272             public void onPing(PingFrame frame)
273             {
274             }
275 
276             @Override
277             public void onGoAway(GoAwayFrame frame)
278             {
279             }
280 
281             @Override
282             public void onWindowUpdate(WindowUpdateFrame frame)
283             {
284             }
285 
286             @Override
287             public void onConnectionFailure(int error, String reason)
288             {
289                 LOG.warn("Connection failure: {}/{}", error, reason);
290             }
291         }
292     }
293 
294     private enum State
295     {
296         HEADER, BODY
297     }
298 }