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