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.fcgi.parser;
20  
21  import java.nio.ByteBuffer;
22  
23  import org.eclipse.jetty.fcgi.FCGI;
24  import org.eclipse.jetty.util.log.Log;
25  import org.eclipse.jetty.util.log.Logger;
26  
27  /**
28   * <p>Parser for FastCGI frame headers.</p>
29   * <pre>
30   * struct frame_header {
31   *     ubyte version;
32   *     ubyte type;
33   *     ushort requestId;
34   *     ushort contentLength;
35   *     ubyte paddingLength;
36   *     ubyte reserved;
37   * }
38   * </pre>
39   *
40   * @see Parser
41   */
42  public class HeaderParser
43  {
44      private static final Logger LOG = Log.getLogger(Parser.class);
45  
46      private State state = State.VERSION;
47      private int cursor;
48      private int version;
49      private int type;
50      private int request;
51      private int length;
52      private int padding;
53  
54      /**
55       * Parses the bytes in the given {@code buffer} as FastCGI header bytes
56       *
57       * @param buffer the bytes to parse
58       * @return whether there were enough bytes for a FastCGI header
59       */
60      public boolean parse(ByteBuffer buffer)
61      {
62          while (buffer.hasRemaining())
63          {
64              switch (state)
65              {
66                  case VERSION:
67                  {
68                      version = buffer.get() & 0xFF;
69                      state = State.TYPE;
70                      break;
71                  }
72                  case TYPE:
73                  {
74                      type = buffer.get() & 0xFF;
75                      state = State.REQUEST;
76                      break;
77                  }
78                  case REQUEST:
79                  {
80                      if (buffer.remaining() >= 2)
81                      {
82                          request = buffer.getShort() & 0xFF_FF;
83                          state = State.LENGTH;
84                      }
85                      else
86                      {
87                          state = State.REQUEST_BYTES;
88                          cursor = 0;
89                      }
90                      break;
91                  }
92                  case REQUEST_BYTES:
93                  {
94                      int halfShort = buffer.get() & 0xFF;
95                      request = (request << 8) + halfShort;
96                      if (++cursor == 2)
97                          state = State.LENGTH;
98                      break;
99                  }
100                 case LENGTH:
101                 {
102                     if (buffer.remaining() >= 2)
103                     {
104                         length = buffer.getShort() & 0xFF_FF;
105                         state = State.PADDING;
106                     }
107                     else
108                     {
109                         state = State.LENGTH_BYTES;
110                         cursor = 0;
111                     }
112                     break;
113                 }
114                 case LENGTH_BYTES:
115                 {
116                     int halfShort = buffer.get() & 0xFF;
117                     length = (length << 8) + halfShort;
118                     if (++cursor == 2)
119                         state = State.PADDING;
120                     break;
121                 }
122                 case PADDING:
123                 {
124                     padding = buffer.get() & 0xFF;
125                     state = State.RESERVED;
126                     break;
127                 }
128                 case RESERVED:
129                 {
130                     buffer.get();
131                     if (LOG.isDebugEnabled())
132                         LOG.debug("Parsed request {} header {} length={}", getRequest(), getFrameType(), getContentLength());
133                     return true;
134                 }
135                 default:
136                 {
137                     throw new IllegalStateException();
138                 }
139             }
140         }
141         return false;
142     }
143 
144     public FCGI.FrameType getFrameType()
145     {
146         return FCGI.FrameType.from(type);
147     }
148 
149     public int getRequest()
150     {
151         return request;
152     }
153 
154     public int getContentLength()
155     {
156         return length;
157     }
158 
159     public int getPaddingLength()
160     {
161         return padding;
162     }
163 
164     protected void reset()
165     {
166         state = State.VERSION;
167         cursor = 0;
168         version = 0;
169         type = 0;
170         request = 0;
171         length = 0;
172         padding = 0;
173     }
174 
175     private enum State
176     {
177         VERSION, TYPE, REQUEST, REQUEST_BYTES, LENGTH, LENGTH_BYTES, PADDING, RESERVED
178     }
179 }