View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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         FrameType frameType = FrameType.from(getFrameType());
136         if (LOG.isDebugEnabled())
137             LOG.debug("Parsed {} frame header from {}", frameType, buffer);
138 
139         if (continuation)
140         {
141             if (frameType != FrameType.CONTINUATION)
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 &&
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         BodyParser bodyParser = bodyParsers[type];
176         if (headerParser.getLength() == 0)
177         {
178             bodyParser.emptyBody(buffer);
179         }
180         else
181         {
182             if (!bodyParser.parse(buffer))
183                 return false;
184         }
185         if (LOG.isDebugEnabled())
186             LOG.debug("Parsed {} frame body from {}", FrameType.from(type), buffer);
187         reset();
188         return true;
189     }
190 
191     protected int getFrameType()
192     {
193         return headerParser.getFrameType();
194     }
195 
196     protected boolean hasFlag(int bit)
197     {
198         return headerParser.hasFlag(bit);
199     }
200 
201     protected void notifyConnectionFailure(int error, String reason)
202     {
203         try
204         {
205             listener.onConnectionFailure(error, reason);
206         }
207         catch (Throwable x)
208         {
209             LOG.info("Failure while notifying listener " + listener, x);
210         }
211     }
212 
213     public interface Listener
214     {
215         public void onData(DataFrame frame);
216 
217         public void onHeaders(HeadersFrame frame);
218 
219         public void onPriority(PriorityFrame frame);
220 
221         public void onReset(ResetFrame frame);
222 
223         public void onSettings(SettingsFrame frame);
224 
225         public void onPushPromise(PushPromiseFrame frame);
226 
227         public void onPing(PingFrame frame);
228 
229         public void onGoAway(GoAwayFrame frame);
230 
231         public void onWindowUpdate(WindowUpdateFrame frame);
232 
233         public void onConnectionFailure(int error, String reason);
234 
235         public static class Adapter implements Listener
236         {
237             @Override
238             public void onData(DataFrame frame)
239             {
240             }
241 
242             @Override
243             public void onHeaders(HeadersFrame frame)
244             {
245             }
246 
247             @Override
248             public void onPriority(PriorityFrame frame)
249             {
250             }
251 
252             @Override
253             public void onReset(ResetFrame frame)
254             {
255             }
256 
257             @Override
258             public void onSettings(SettingsFrame frame)
259             {
260             }
261 
262             @Override
263             public void onPushPromise(PushPromiseFrame frame)
264             {
265             }
266 
267             @Override
268             public void onPing(PingFrame frame)
269             {
270             }
271 
272             @Override
273             public void onGoAway(GoAwayFrame frame)
274             {
275             }
276 
277             @Override
278             public void onWindowUpdate(WindowUpdateFrame frame)
279             {
280             }
281 
282             @Override
283             public void onConnectionFailure(int error, String reason)
284             {
285                 LOG.warn("Connection failure: {}/{}", error, reason);
286             }
287         }
288     }
289 
290     private enum State
291     {
292         HEADER, BODY
293     }
294 }