KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > server > dispatch > InvocationDecoder


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  * Free SoftwareFoundation, Inc.
23  * 59 Temple Place, Suite 330
24  * Boston, MA 02111-1307 USA
25  *
26  * @author Scott Ferguson
27  */

28
29 package com.caucho.server.dispatch;
30
31 import com.caucho.config.ConfigException;
32 import com.caucho.i18n.CharacterEncoding;
33 import com.caucho.log.Log;
34 import com.caucho.server.util.CauchoSystem;
35 import com.caucho.util.CharBuffer;
36 import com.caucho.util.L10N;
37 import com.caucho.vfs.ByteToChar;
38
39 import java.io.IOException JavaDoc;
40 import java.io.UnsupportedEncodingException JavaDoc;
41 import java.util.logging.Level JavaDoc;
42 import java.util.logging.Logger JavaDoc;
43
44 /**
45  * Decodes invocation URI.
46  */

47 public class InvocationDecoder {
48   static final Logger JavaDoc log = Log.open(InvocationDecoder.class);
49   static final L10N L = new L10N(InvocationDecoder.class);
50
51   // The character encoding
52
private String JavaDoc _encoding = "UTF-8";
53
54   private String JavaDoc _sessionCookie = "JSESSIONID";
55   private String JavaDoc _sslSessionCookie;
56   
57   // The URL-encoded session suffix
58
private String JavaDoc _sessionSuffix = ";jsessionid=";
59   private char _sessionSuffixChar = ';';
60
61   // The URL-encoded session prefix
62
private String JavaDoc _sessionPrefix;
63
64   /**
65    * Creates the invocation decoder.
66    */

67   public InvocationDecoder()
68   {
69     _encoding = CharacterEncoding.getLocalEncoding();
70     if (_encoding == null)
71       _encoding = "UTF-8";
72   }
73
74   /**
75    * Returns the character encoding.
76    */

77   public String JavaDoc getEncoding()
78   {
79     return _encoding;
80   }
81
82   /**
83    * Sets the character encoding.
84    */

85   public void setEncoding(String JavaDoc encoding)
86   {
87     _encoding = encoding;
88   }
89   
90   /**
91    * Sets the session cookie
92    */

93   public void setSessionCookie(String JavaDoc cookie)
94   {
95     _sessionCookie = cookie;
96   }
97
98   /**
99    * Gets the session cookie
100    */

101   public String JavaDoc getSessionCookie()
102   {
103     return _sessionCookie;
104   }
105   
106   /**
107    * Sets the SSL session cookie
108    */

109   public void setSSLSessionCookie(String JavaDoc cookie)
110   {
111     _sslSessionCookie = cookie;
112   }
113
114   /**
115    * Gets the SSL session cookie
116    */

117   public String JavaDoc getSSLSessionCookie()
118   {
119     if (_sslSessionCookie != null)
120       return _sslSessionCookie;
121     else
122       return _sessionCookie;
123   }
124
125   /**
126    * Sets the session url prefix.
127    */

128   public void setSessionURLPrefix(String JavaDoc prefix)
129   {
130     _sessionSuffix = prefix;
131     if (_sessionSuffix != null)
132       _sessionSuffixChar = _sessionSuffix.charAt(0);
133   }
134
135   /**
136    * Gets the session url prefix.
137    */

138   public String JavaDoc getSessionURLPrefix()
139   {
140     return _sessionSuffix;
141   }
142
143   /**
144    * Sets the alternate session url prefix.
145    */

146   public void setAlternateSessionURLPrefix(String JavaDoc prefix)
147     throws ConfigException
148   {
149     if (! prefix.startsWith("/"))
150       prefix = '/' + prefix;
151
152     if (prefix.lastIndexOf('/') > 0)
153       throw new ConfigException(L.l("`{0}' is an invalidate alternate-session-url-prefix. The url-prefix must not have any embedded '/'.", prefix));
154     
155     _sessionPrefix = prefix;
156     _sessionSuffix = null;
157   }
158
159   /**
160    * Gets the session url prefix.
161    */

162   public String JavaDoc getAlternateSessionURLPrefix()
163   {
164     return _sessionPrefix;
165   }
166
167   /**
168    * Splits out the query string and unescape the value.
169    */

170   public void splitQueryAndUnescape(Invocation invocation,
171                                     byte []rawURI, int uriLength)
172     throws IOException JavaDoc
173   {
174     for (int i = 0; i < uriLength; i++) {
175       if (rawURI[i] == '?') {
176         i++;
177
178         // XXX: should be the host encoding?
179
String JavaDoc queryString = byteToChar(rawURI, i, uriLength - i,
180                                         "ISO-8859-1");
181         invocation.setQueryString(queryString);
182
183         uriLength = i - 1;
184         break;
185       }
186     }
187
188     String JavaDoc rawURIString = byteToChar(rawURI, 0, uriLength, "ISO-8859-1");
189     invocation.setRawURI(rawURIString);
190     
191     String JavaDoc decodedURI = normalizeUriEscape(rawURI, 0, uriLength, _encoding);
192
193     if (_sessionSuffix != null) {
194       int p = decodedURI.indexOf(_sessionSuffix);
195
196       if (p >= 0) {
197         int suffixLength = _sessionSuffix.length();
198         int tail = decodedURI.indexOf(';', p + suffixLength);
199         String JavaDoc sessionId;
200
201         if (tail > 0)
202           sessionId = decodedURI.substring(p + suffixLength, tail);
203         else
204           sessionId = decodedURI.substring(p + suffixLength);
205
206         decodedURI = decodedURI.substring(0, p);
207
208         invocation.setSessionId(sessionId);
209
210         p = rawURIString.indexOf(_sessionSuffix);
211         if (p > 0) {
212           rawURIString = rawURIString.substring(0, p);
213           invocation.setRawURI(rawURIString);
214         }
215       }
216     }
217     else if (_sessionPrefix != null) {
218       if (decodedURI.startsWith(_sessionPrefix)) {
219         int prefixLength = _sessionPrefix.length();
220
221         int tail = decodedURI.indexOf('/', prefixLength);
222         String JavaDoc sessionId;
223
224         if (tail > 0) {
225           sessionId = decodedURI.substring(prefixLength, tail);
226           decodedURI = decodedURI.substring(tail);
227           invocation.setRawURI(rawURIString.substring(tail));
228         }
229         else {
230           sessionId = decodedURI.substring(prefixLength);
231           decodedURI = "/";
232           invocation.setRawURI("/");
233         }
234
235         invocation.setSessionId(sessionId);
236       }
237     }
238
239     String JavaDoc uri = normalizeUri(decodedURI);
240
241     invocation.setURI(uri);
242     invocation.setContextURI(uri);
243   }
244
245   /**
246    * Splits out the query string, and normalizes the URI, assuming nothing
247    * needs unescaping.
248    */

249   public void splitQuery(Invocation invocation, String JavaDoc rawURI)
250     throws IOException JavaDoc
251   {
252     int p = rawURI.indexOf('?');
253     if (p > 0) {
254       invocation.setQueryString(rawURI.substring(p + 1));
255       
256       rawURI = rawURI.substring(0, p);
257     }
258
259     invocation.setRawURI(rawURI);
260     
261     String JavaDoc uri = normalizeUri(rawURI);
262
263     invocation.setURI(uri);
264     invocation.setContextURI(uri);
265   }
266
267   /**
268    * Just normalize the URI.
269    */

270   public void normalizeURI(Invocation invocation, String JavaDoc rawURI)
271     throws IOException JavaDoc
272   {
273     invocation.setRawURI(rawURI);
274     
275     String JavaDoc uri = normalizeUri(rawURI);
276
277     invocation.setURI(uri);
278     invocation.setContextURI(uri);
279   }
280
281   /**
282    * Splits out the session.
283    */

284   public void splitSession(Invocation invocation)
285   {
286     if (_sessionSuffix != null) {
287       String JavaDoc uri = invocation.getURI();
288     }
289     else if (_sessionPrefix != null) {
290       String JavaDoc uri = invocation.getURI();
291     }
292   }
293
294   private String JavaDoc byteToChar(byte []buffer, int offset, int length,
295                             String JavaDoc encoding)
296   {
297     ByteToChar converter = ByteToChar.create();
298     // XXX: make this configurable
299

300     if (encoding == null)
301       encoding = "utf-8";
302
303     try {
304       converter.setEncoding(encoding);
305     } catch (UnsupportedEncodingException JavaDoc e) {
306       log.log(Level.FINE, e.toString(), e);
307     }
308
309     try {
310       for (; length > 0; length--)
311         converter.addByte(buffer[offset++]);
312       
313       return converter.getConvertedString();
314     } catch (IOException JavaDoc e) {
315       return "unknown";
316     }
317   }
318
319   /**
320    * Normalize a uri to remove '///', '/./', 'foo/..', etc.
321    *
322    * @param uri the raw uri to be normalized
323    * @return a normalized URI
324    */

325   public static String JavaDoc normalizeUri(String JavaDoc uri)
326     throws IOException JavaDoc
327   {
328     return normalizeUri(uri, CauchoSystem.isWindows());
329   }
330
331   /**
332    * Normalize a uri to remove '///', '/./', 'foo/..', etc.
333    *
334    * @param uri the raw uri to be normalized
335    * @return a normalized URI
336    */

337   public static String JavaDoc normalizeUri(String JavaDoc uri, boolean isWindows)
338     throws IOException JavaDoc
339   {
340     CharBuffer cb = CharBuffer.allocate();
341
342     int len = uri.length();
343     
344     if (len > 1024)
345       throw new BadRequestException(L.l("The request contains an illegal URL."));
346
347     boolean isBogus;
348     char ch;
349     char ch1;
350     if (len == 0 || (ch = uri.charAt(0)) != '/' && ch != '\\')
351       cb.append('/');
352
353     for (int i = 0; i < len; i++) {
354       ch = uri.charAt(i);
355
356       if (ch == '/' || ch == '\\') {
357       dots:
358     while (i + 1 < len) {
359       ch = uri.charAt(i + 1);
360
361       if (ch == '/' || ch == '\\')
362         i++;
363       else if (ch != '.')
364         break dots;
365       else if (i + 2 >= len ||
366                    (ch = uri.charAt(i + 2)) == '/' || ch == '\\') {
367         i += 2;
368       }
369       else if (ch != '.')
370         break dots;
371       else if (i + 3 >= len ||
372                    (ch = uri.charAt(i + 3)) == '/' || ch == '\\') {
373         int j;
374         
375         for (j = cb.length() - 1; j >= 0; j--) {
376           if ((ch = cb.charAt(j)) == '/' || ch == '\\')
377         break;
378         }
379         if (j > 0)
380           cb.setLength(j);
381         else
382           cb.setLength(0);
383         i += 3;
384       } else {
385             throw new BadRequestException(L.l("The request contains an illegal URL."));
386           }
387     }
388
389     while (isWindows && cb.getLength() > 0 &&
390            ((ch = cb.getLastChar()) == '.' || ch == ' ')) {
391       cb.setLength(cb.getLength() - 1);
392     }
393
394     cb.append('/');
395       }
396       else if (ch == 0)
397         throw new BadRequestException(L.l("The request contains an illegal URL."));
398       else
399     cb.append(ch);
400     }
401
402     return cb.close();
403   }
404
405   /**
406    * Converts the escaped URI to a string.
407    *
408    * @param rawUri the escaped URI
409    * @param i index into the URI
410    * @param len the length of the uri
411    * @param encoding the character encoding to handle %xx
412    *
413    * @return the converted URI
414    */

415   private static String JavaDoc normalizeUriEscape(byte []rawUri, int i, int len,
416                                            String JavaDoc encoding)
417     throws IOException JavaDoc
418   {
419     ByteToChar converter = ByteToChar.create();
420     // XXX: make this configurable
421

422     if (encoding == null)
423       encoding = "utf-8";
424
425     try {
426       converter.setEncoding(encoding);
427     } catch (UnsupportedEncodingException JavaDoc e) {
428       log.log(Level.FINE, e.toString(), e);
429     }
430
431     try {
432       while (i < len) {
433         int ch = rawUri[i++] & 0xff;
434
435         if (ch == '%')
436           i = scanUriEscape(converter, rawUri, i, len);
437         else
438           converter.addByte(ch);
439       }
440
441       return converter.getConvertedString();
442     } catch (Exception JavaDoc e) {
443       throw new BadRequestException(L.l("The URL contains escaped bytes unsupported by the {0} encoding.", encoding));
444     }
445   }
446
447   /**
448    * Scans the next character from URI, adding it to the converter.
449    *
450    * @param converter the byte-to-character converter
451    * @param rawUri the raw URI
452    * @param i index into the URI
453    * @param len the raw URI length
454    *
455    * @return next index into the URI
456    */

457   private static int scanUriEscape(ByteToChar converter,
458                                    byte []rawUri, int i, int len)
459     throws IOException JavaDoc
460   {
461     int ch1 = i < len ? (rawUri[i++] & 0xff) : -1;
462
463     if (ch1 == 'u') {
464       ch1 = i < len ? (rawUri[i++] & 0xff) : -1;
465       int ch2 = i < len ? (rawUri[i++] & 0xff) : -1;
466       int ch3 = i < len ? (rawUri[i++] & 0xff) : -1;
467       int ch4 = i < len ? (rawUri[i++] & 0xff) : -1;
468
469       converter.addChar((char) ((toHex(ch1) << 12) +
470                                 (toHex(ch2) << 8) +
471                                 (toHex(ch3) << 4) +
472                                 (toHex(ch4))));
473     }
474     else {
475       int ch2 = i < len ? (rawUri[i++] & 0xff) : -1;
476
477       int b = (toHex(ch1) << 4) + toHex(ch2);;
478
479       converter.addByte(b);
480     }
481
482     return i;
483   }
484
485   /**
486    * Convert a character to hex
487    */

488   private static int toHex(int ch)
489   {
490     if (ch >= '0' && ch <= '9')
491       return ch - '0';
492     else if (ch >= 'a' && ch <= 'f')
493       return ch - 'a' + 10;
494     else if (ch >= 'A' && ch <= 'F')
495       return ch - 'A' + 10;
496     else
497       return -1;
498   }
499 }
500
Popular Tags