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  import java.nio.charset.Charset;
23  import java.nio.charset.StandardCharsets;
24  
25  import org.eclipse.jetty.http.HttpField;
26  import org.eclipse.jetty.util.log.Log;
27  import org.eclipse.jetty.util.log.Logger;
28  
29  /**
30   * <p>Parser for the PARAMS frame body.</p>
31   * <pre>
32   * struct small_name_small_value_params_body {
33   *     ubyte nameLength;
34   *     ubyte valueLength;
35   *     ubyte[] nameBytes;
36   *     ubyte[] valueBytes;
37   * }
38   *
39   * struct small_name_large_value_params_body {
40   *     ubyte nameLength;
41   *     uint valueLength;
42   *     ubyte[] nameBytes;
43   *     ubyte[] valueBytes;
44   * }
45   *
46   * struct large_name_small_value_params_body {
47   *     uint nameLength;
48   *     ubyte valueLength;
49   *     ubyte[] nameBytes;
50   *     ubyte[] valueBytes;
51   * }
52   *
53   * struct large_name_large_value_params_body {
54   *     uint nameLength;
55   *     uint valueLength;
56   *     ubyte[] nameBytes;
57   *     ubyte[] valueBytes;
58   * }
59   * </pre>
60   */
61  public class ParamsContentParser extends ContentParser
62  {
63      private static final Logger LOG = Log.getLogger(ParamsContentParser.class);
64  
65      private final ServerParser.Listener listener;
66      private State state = State.LENGTH;
67      private int cursor;
68      private int length;
69      private int nameLength;
70      private int valueLength;
71      private byte[] nameBytes;
72      private byte[] valueBytes;
73  
74      public ParamsContentParser(HeaderParser headerParser, ServerParser.Listener listener)
75      {
76          super(headerParser);
77          this.listener = listener;
78      }
79  
80      @Override
81      public Result parse(ByteBuffer buffer)
82      {
83          while (buffer.hasRemaining() || state == State.PARAM)
84          {
85              switch (state)
86              {
87                  case LENGTH:
88                  {
89                      length = getContentLength();
90                      state = State.NAME_LENGTH;
91                      break;
92                  }
93                  case NAME_LENGTH:
94                  {
95                      if (isLargeLength(buffer))
96                      {
97                          if (buffer.remaining() >= 4)
98                          {
99                              nameLength = buffer.getInt() & 0x7F_FF;
100                             state = State.VALUE_LENGTH;
101                             length -= 4;
102                         }
103                         else
104                         {
105                             state = State.NAME_LENGTH_BYTES;
106                             cursor = 0;
107                         }
108                     }
109                     else
110                     {
111                         nameLength = buffer.get() & 0xFF;
112                         state = State.VALUE_LENGTH;
113                         --length;
114                     }
115                     break;
116                 }
117                 case NAME_LENGTH_BYTES:
118                 {
119                     int quarterInt = buffer.get() & 0xFF;
120                     nameLength = (nameLength << 8) + quarterInt;
121                     --length;
122                     if (++cursor == 4)
123                     {
124                         nameLength &= 0x7F_FF;
125                         state = State.VALUE_LENGTH;
126                     }
127                     break;
128                 }
129                 case VALUE_LENGTH:
130                 {
131                     if (isLargeLength(buffer))
132                     {
133                         if (buffer.remaining() >= 4)
134                         {
135                             valueLength = buffer.getInt() & 0x7F_FF;
136                             state = State.NAME;
137                             length -= 4;
138                         }
139                         else
140                         {
141                             state = State.VALUE_LENGTH_BYTES;
142                             cursor = 0;
143                         }
144                     }
145                     else
146                     {
147                         valueLength = buffer.get() & 0xFF;
148                         state = State.NAME;
149                         --length;
150                     }
151                     break;
152                 }
153                 case VALUE_LENGTH_BYTES:
154                 {
155                     int quarterInt = buffer.get() & 0xFF;
156                     valueLength = (valueLength << 8) + quarterInt;
157                     --length;
158                     if (++cursor == 4)
159                     {
160                         valueLength &= 0x7F_FF;
161                         state = State.NAME;
162                     }
163                     break;
164                 }
165                 case NAME:
166                 {
167                     nameBytes = new byte[nameLength];
168                     if (buffer.remaining() >= nameLength)
169                     {
170                         buffer.get(nameBytes);
171                         state = State.VALUE;
172                         length -= nameLength;
173                     }
174                     else
175                     {
176                         state = State.NAME_BYTES;
177                         cursor = 0;
178                     }
179                     break;
180                 }
181                 case NAME_BYTES:
182                 {
183                     nameBytes[cursor] = buffer.get();
184                     --length;
185                     if (++cursor == nameLength)
186                         state = State.VALUE;
187                     break;
188                 }
189                 case VALUE:
190                 {
191                     valueBytes = new byte[valueLength];
192                     if (buffer.remaining() >= valueLength)
193                     {
194                         buffer.get(valueBytes);
195                         state = State.PARAM;
196                         length -= valueLength;
197                     }
198                     else
199                     {
200                         state = State.VALUE_BYTES;
201                         cursor = 0;
202                     }
203                     break;
204                 }
205                 case VALUE_BYTES:
206                 {
207                     valueBytes[cursor] = buffer.get();
208                     --length;
209                     if (++cursor == valueLength)
210                         state = State.PARAM;
211                     break;
212                 }
213                 case PARAM:
214                 {
215                     Charset utf8 = StandardCharsets.UTF_8;
216                     onParam(new String(nameBytes, utf8), new String(valueBytes, utf8));
217                     partialReset();
218                     if (length == 0)
219                     {
220                         reset();
221                         return Result.COMPLETE;
222                     }
223                     break;
224                 }
225                 default:
226                 {
227                     throw new IllegalStateException();
228                 }
229             }
230         }
231         return Result.PENDING;
232     }
233 
234     @Override
235     public void noContent()
236     {
237         onParams();
238     }
239 
240     protected void onParam(String name, String value)
241     {
242         try
243         {
244             listener.onHeader(getRequest(), new HttpField(name, value));
245         }
246         catch (Throwable x)
247         {
248             if (LOG.isDebugEnabled())
249                 LOG.debug("Exception while invoking listener " + listener, x);
250         }
251     }
252 
253     protected void onParams()
254     {
255         try
256         {
257             listener.onHeaders(getRequest());
258         }
259         catch (Throwable x)
260         {
261             if (LOG.isDebugEnabled())
262                 LOG.debug("Exception while invoking listener " + listener, x);
263         }
264     }
265 
266     private boolean isLargeLength(ByteBuffer buffer)
267     {
268         return (buffer.get(buffer.position()) & 0x80) == 0x80;
269     }
270 
271     private void partialReset()
272     {
273         state = State.NAME_LENGTH;
274         cursor = 0;
275         nameLength = 0;
276         valueLength = 0;
277         nameBytes = null;
278         valueBytes = null;
279     }
280 
281     private void reset()
282     {
283         partialReset();
284         state = State.LENGTH;
285         length = 0;
286     }
287 
288     private enum State
289     {
290         LENGTH, NAME_LENGTH, NAME_LENGTH_BYTES, VALUE_LENGTH, VALUE_LENGTH_BYTES, NAME, NAME_BYTES, VALUE, VALUE_BYTES, PARAM
291     }
292 }