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 streamId;
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          streamId = 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                  headerBlockFragments.setPriorityFrame(new PriorityFrame(streamId, getStreamId(), weight, exclusive));
74          }
75          reset();
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.STREAM_ID;
126                     break;
127                 }
128                 case STREAM_ID:
129                 {
130                     if (buffer.remaining() >= 4)
131                     {
132                         streamId = buffer.getInt();
133                         streamId &= 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.STREAM_ID_BYTES;
142                         cursor = 4;
143                     }
144                     break;
145                 }
146                 case STREAM_ID_BYTES:
147                 {
148                     int currByte = buffer.get() & 0xFF;
149                     --cursor;
150                     streamId += 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                         streamId &= 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;
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                             state = State.PADDING;
179                             loop = paddingLength == 0;
180                             onHeaders(streamId, weight, exclusive, metaData);
181                         }
182                     }
183                     else
184                     {
185                         int remaining = buffer.remaining();
186                         if (remaining < length)
187                         {
188                             headerBlockFragments.storeFragment(buffer, remaining, false);
189                             length -= remaining;
190                         }
191                         else
192                         {
193                             headerBlockFragments.setStreamId(getStreamId());
194                             headerBlockFragments.setEndStream(isEndStream());
195                             if (hasFlag(Flags.PRIORITY))
196                                 headerBlockFragments.setPriorityFrame(new PriorityFrame(streamId, getStreamId(), weight, exclusive));
197                             headerBlockFragments.storeFragment(buffer, length, false);
198                             state = State.PADDING;
199                             loop = paddingLength == 0;
200                         }
201                     }
202                     break;
203                 }
204                 case PADDING:
205                 {
206                     int size = Math.min(buffer.remaining(), paddingLength);
207                     buffer.position(buffer.position() + size);
208                     paddingLength -= size;
209                     if (paddingLength == 0)
210                     {
211                         reset();
212                         return true;
213                     }
214                     break;
215                 }
216                 default:
217                 {
218                     throw new IllegalStateException();
219                 }
220             }
221         }
222         return false;
223     }
224 
225     private void onHeaders(int streamId, int weight, boolean exclusive, MetaData metaData)
226     {
227         PriorityFrame priorityFrame = null;
228         if (hasFlag(Flags.PRIORITY))
229             priorityFrame = new PriorityFrame(streamId, getStreamId(), weight, exclusive);
230         HeadersFrame frame = new HeadersFrame(getStreamId(), metaData, priorityFrame, isEndStream());
231         notifyHeaders(frame);
232     }
233 
234     private enum State
235     {
236         PREPARE, PADDING_LENGTH, EXCLUSIVE, STREAM_ID, STREAM_ID_BYTES, WEIGHT, HEADERS, PADDING
237     }
238 }