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.http.MetaData;
24  import org.eclipse.jetty.http2.ErrorCode;
25  import org.eclipse.jetty.http2.Flags;
26  import org.eclipse.jetty.http2.frames.FrameType;
27  import org.eclipse.jetty.http2.frames.HeadersFrame;
28  import org.eclipse.jetty.http2.frames.PriorityFrame;
29  import org.eclipse.jetty.util.BufferUtil;
30  
31  public class HeadersBodyParser extends BodyParser
32  {
33      private final HeaderBlockParser headerBlockParser;
34      private final HeaderBlockFragments headerBlockFragments;
35      private State state = State.PREPARE;
36      private int cursor;
37      private int length;
38      private int paddingLength;
39      private boolean exclusive;
40      private int parentStreamId;
41      private int weight;
42  
43      public HeadersBodyParser(HeaderParser headerParser, Parser.Listener listener, HeaderBlockParser headerBlockParser, HeaderBlockFragments headerBlockFragments)
44      {
45          super(headerParser, listener);
46          this.headerBlockParser = headerBlockParser;
47          this.headerBlockFragments = headerBlockFragments;
48      }
49  
50      private void reset()
51      {
52          state = State.PREPARE;
53          cursor = 0;
54          length = 0;
55          paddingLength = 0;
56          exclusive = false;
57          parentStreamId = 0;
58          weight = 0;
59      }
60  
61      @Override
62      protected void emptyBody(ByteBuffer buffer)
63      {
64          if (hasFlag(Flags.END_HEADERS))
65          {
66              MetaData metaData = headerBlockParser.parse(BufferUtil.EMPTY_BUFFER, 0);
67              onHeaders(0, 0, false, metaData);
68          }
69          else
70          {
71              headerBlockFragments.setStreamId(getStreamId());
72              headerBlockFragments.setEndStream(isEndStream());
73              if (hasFlag(Flags.PRIORITY))
74                  connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_headers_priority_frame");
75          }
76      }
77  
78      @Override
79      public boolean parse(ByteBuffer buffer)
80      {
81          boolean loop = false;
82          while (buffer.hasRemaining() || loop)
83          {
84              switch (state)
85              {
86                  case PREPARE:
87                  {
88                      // SPEC: wrong streamId is treated as connection error.
89                      if (getStreamId() == 0)
90                          return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_headers_frame");
91  
92                      length = getBodyLength();
93  
94                      if (isPadding())
95                      {
96                          state = State.PADDING_LENGTH;
97                      }
98                      else if (hasFlag(Flags.PRIORITY))
99                      {
100                         state = State.EXCLUSIVE;
101                     }
102                     else
103                     {
104                         state = State.HEADERS;
105                     }
106                     break;
107                 }
108                 case PADDING_LENGTH:
109                 {
110                     paddingLength = buffer.get() & 0xFF;
111                     --length;
112                     length -= paddingLength;
113                     state = hasFlag(Flags.PRIORITY) ? State.EXCLUSIVE : State.HEADERS;
114                     loop = length == 0;
115                     if (length < 0)
116                         return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_headers_frame_padding");
117                     break;
118                 }
119                 case EXCLUSIVE:
120                 {
121                     // We must only peek the first byte and not advance the buffer
122                     // because the 31 least significant bits represent the stream id.
123                     int currByte = buffer.get(buffer.position());
124                     exclusive = (currByte & 0x80) == 0x80;
125                     state = State.PARENT_STREAM_ID;
126                     break;
127                 }
128                 case PARENT_STREAM_ID:
129                 {
130                     if (buffer.remaining() >= 4)
131                     {
132                         parentStreamId = buffer.getInt();
133                         parentStreamId &= 0x7F_FF_FF_FF;
134                         length -= 4;
135                         state = State.WEIGHT;
136                         if (length < 1)
137                             return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_headers_frame");
138                     }
139                     else
140                     {
141                         state = State.PARENT_STREAM_ID_BYTES;
142                         cursor = 4;
143                     }
144                     break;
145                 }
146                 case PARENT_STREAM_ID_BYTES:
147                 {
148                     int currByte = buffer.get() & 0xFF;
149                     --cursor;
150                     parentStreamId += currByte << (8 * cursor);
151                     --length;
152                     if (cursor > 0 && length <= 0)
153                         return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_headers_frame");
154                     if (cursor == 0)
155                     {
156                         parentStreamId &= 0x7F_FF_FF_FF;
157                         state = State.WEIGHT;
158                         if (length < 1)
159                             return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_headers_frame");
160                     }
161                     break;
162                 }
163                 case WEIGHT:
164                 {
165                     weight = (buffer.get() & 0xFF) + 1;
166                     --length;
167                     state = State.HEADERS;
168                     loop = length == 0;
169                     break;
170                 }
171                 case HEADERS:
172                 {
173                     if (hasFlag(Flags.END_HEADERS))
174                     {
175                         MetaData metaData = headerBlockParser.parse(buffer, length);
176                         if (metaData != null)
177                         {
178                             if (LOG.isDebugEnabled())
179                                 LOG.debug("Parsed {} frame hpack from {}", FrameType.HEADERS, buffer);
180                             state = State.PADDING;
181                             loop = paddingLength == 0;
182                             onHeaders(parentStreamId, weight, exclusive, metaData);
183                         }
184                     }
185                     else
186                     {
187                         int remaining = buffer.remaining();
188                         if (remaining < length)
189                         {
190                             headerBlockFragments.storeFragment(buffer, remaining, false);
191                             length -= remaining;
192                         }
193                         else
194                         {
195                             headerBlockFragments.setStreamId(getStreamId());
196                             headerBlockFragments.setEndStream(isEndStream());
197                             if (hasFlag(Flags.PRIORITY))
198                                 headerBlockFragments.setPriorityFrame(new PriorityFrame(getStreamId(), parentStreamId, weight, exclusive));
199                             headerBlockFragments.storeFragment(buffer, length, false);
200                             state = State.PADDING;
201                             loop = paddingLength == 0;
202                         }
203                     }
204                     break;
205                 }
206                 case PADDING:
207                 {
208                     int size = Math.min(buffer.remaining(), paddingLength);
209                     buffer.position(buffer.position() + size);
210                     paddingLength -= size;
211                     if (paddingLength == 0)
212                     {
213                         reset();
214                         return true;
215                     }
216                     break;
217                 }
218                 default:
219                 {
220                     throw new IllegalStateException();
221                 }
222             }
223         }
224         return false;
225     }
226 
227     private void onHeaders(int parentStreamId, int weight, boolean exclusive, MetaData metaData)
228     {
229         PriorityFrame priorityFrame = null;
230         if (hasFlag(Flags.PRIORITY))
231             priorityFrame = new PriorityFrame(getStreamId(), parentStreamId, weight, exclusive);
232         HeadersFrame frame = new HeadersFrame(getStreamId(), metaData, priorityFrame, isEndStream());
233         notifyHeaders(frame);
234     }
235 
236     private enum State
237     {
238         PREPARE, PADDING_LENGTH, EXCLUSIVE, PARENT_STREAM_ID, PARENT_STREAM_ID_BYTES, WEIGHT, HEADERS, PADDING
239     }
240 }