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