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.frames.GoAwayFrame;
25  
26  public class GoAwayBodyParser extends BodyParser
27  {
28      private State state = State.PREPARE;
29      private int cursor;
30      private int length;
31      private int lastStreamId;
32      private int error;
33      private byte[] payload;
34  
35      public GoAwayBodyParser(HeaderParser headerParser, Parser.Listener listener)
36      {
37          super(headerParser, listener);
38      }
39  
40      private void reset()
41      {
42          state = State.PREPARE;
43          cursor = 0;
44          length = 0;
45          lastStreamId = 0;
46          error = 0;
47          payload = null;
48      }
49  
50      @Override
51      public boolean parse(ByteBuffer buffer)
52      {
53          while (buffer.hasRemaining())
54          {
55              switch (state)
56              {
57                  case PREPARE:
58                  {
59                      state = State.LAST_STREAM_ID;
60                      length = getBodyLength();
61                      break;
62                  }
63                  case LAST_STREAM_ID:
64                  {
65                      if (buffer.remaining() >= 4)
66                      {
67                          lastStreamId = buffer.getInt();
68                          lastStreamId &= 0x7F_FF_FF_FF;
69                          state = State.ERROR;
70                          length -= 4;
71                          if (length <= 0)
72                              return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_go_away_frame");
73                      }
74                      else
75                      {
76                          state = State.LAST_STREAM_ID_BYTES;
77                          cursor = 4;
78                      }
79                      break;
80                  }
81                  case LAST_STREAM_ID_BYTES:
82                  {
83                      int currByte = buffer.get() & 0xFF;
84                      --cursor;
85                      lastStreamId += currByte << (8 * cursor);
86                      --length;
87                      if (cursor > 0 && length <= 0)
88                          return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_go_away_frame");
89                      if (cursor == 0)
90                      {
91                          lastStreamId &= 0x7F_FF_FF_FF;
92                          state = State.ERROR;
93                          if (length == 0)
94                              return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_go_away_frame");
95                      }
96                      break;
97                  }
98                  case ERROR:
99                  {
100                     if (buffer.remaining() >= 4)
101                     {
102                         error = buffer.getInt();
103                         state = State.PAYLOAD;
104                         length -= 4;
105                         if (length < 0)
106                             return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_go_away_frame");
107                         if (length == 0)
108                             return onGoAway(lastStreamId, error, null);
109                     }
110                     else
111                     {
112                         state = State.ERROR_BYTES;
113                         cursor = 4;
114                     }
115                     break;
116                 }
117                 case ERROR_BYTES:
118                 {
119                     int currByte = buffer.get() & 0xFF;
120                     --cursor;
121                     error += currByte << (8 * cursor);
122                     --length;
123                     if (cursor > 0 && length <= 0)
124                         return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_go_away_frame");
125                     if (cursor == 0)
126                     {
127                         state = State.PAYLOAD;
128                         if (length == 0)
129                             return onGoAway(lastStreamId, error, null);
130                     }
131                     break;
132                 }
133                 case PAYLOAD:
134                 {
135                     payload = new byte[length];
136                     if (buffer.remaining() >= length)
137                     {
138                         buffer.get(payload);
139                         return onGoAway(lastStreamId, error, payload);
140                     }
141                     else
142                     {
143                         state = State.PAYLOAD_BYTES;
144                         cursor = length;
145                     }
146                     break;
147                 }
148                 case PAYLOAD_BYTES:
149                 {
150                     payload[payload.length - cursor] = buffer.get();
151                     --cursor;
152                     if (cursor == 0)
153                         return onGoAway(lastStreamId, error, payload);
154                     break;
155                 }
156                 default:
157                 {
158                     throw new IllegalStateException();
159                 }
160             }
161         }
162         return false;
163     }
164 
165     private boolean onGoAway(int lastStreamId, int error, byte[] payload)
166     {
167         GoAwayFrame frame = new GoAwayFrame(lastStreamId, error, payload);
168         reset();
169         notifyGoAway(frame);
170         return true;
171     }
172 
173     private enum State
174     {
175         PREPARE, LAST_STREAM_ID, LAST_STREAM_ID_BYTES, ERROR, ERROR_BYTES, PAYLOAD, PAYLOAD_BYTES
176     }
177 }