Source code: com/tripi/asp/Request.java
1 /**
2 * ArrowHead ASP Server
3 * This is a source file for the ArrowHead ASP Server - an 100% Java
4 * VBScript interpreter and ASP server.
5 *
6 * For more information, see http://www.tripi.com/arrowhead
7 *
8 * Copyright (C) 2002 Terence Haddock
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25 package com.tripi.asp;
26
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.Reader;
30 import java.util.Enumeration;
31 import java.util.Hashtable;
32 import java.util.Iterator;
33 import java.util.Map;
34
35 import javax.servlet.http.Cookie;
36 import javax.servlet.http.HttpServletRequest;
37
38 import org.apache.log4j.Category;
39
40 import com.tripi.asp.util.ParseQueryString;
41 import com.tripi.asp.util.Tools;
42
43 /**
44 * Request is a class which holds request-specific data.
45 * It is an adaptor class which allows access to the Java Servlet
46 * javax.servlet.http.HttpServletRequest class.
47 * Implementation state:
48 * <ul>
49 * <li><b>Cookies</b> - Implemented. <b>Note</b> does not support cookies
50 * like "VALUE=First&VALUE=Second", does support cookies like
51 * "VALUE1=First&VALUE2=Second".
52 * <li><b>Form</b> - Fully implemented.
53 * <li><b>QueryString</b> - Fully implemented.
54 * <li><b>ServerVariables</b> - Fully implemented.
55 * <li><b>TotalBytes</b> - Fully implemented.
56 * <li><b>BinaryRead</b> - Fully implemented.
57 * </ul>
58 *
59 * @author Terence Haddock
60 * @version 0.9
61 */
62 public class Request implements SimpleMap
63 {
64 /** Debugging category */
65 static private Category DBG = Category.getInstance(Request.class);
66
67 /** Java servlet request object */
68 HttpServletRequest request;
69
70 /** QueryString sub-object */
71 public QueryStringObj QueryString;
72
73 /** ServerVariables sub-object */
74 public ServerVariablesObj ServerVariables;
75
76 /** Form sub-object */
77 public FormObj Form;
78
79 /** Cookies sub-object */
80 public CookiesObj Cookies;
81
82 /**
83 * Constructor.
84 * @param request Java servlet request object.
85 * @see HttpServletRequest
86 */
87 public Request(HttpServletRequest request) throws AspException
88 {
89 this.request = request;
90 try {
91 request.setCharacterEncoding("UTF-8");
92 } catch (java.io.UnsupportedEncodingException ex)
93 {
94 throw new AspNestedException(ex);
95 }
96 QueryString = new QueryStringObj(request.getQueryString());
97 ServerVariables = new ServerVariablesObj(request);
98 Form = new FormObj(request);
99 Cookies = new CookiesObj(request);
100 }
101
102 /**
103 * This unsupported ASP-accessible function obtains the
104 * HttpServletRequest object that this Request object points to.
105 * @return HttpServletRequest object
106 */
107 public HttpServletRequest getHttpServletRequest()
108 {
109 return request;
110 }
111
112 /**
113 * SimpleMap interface, obtains the value of an object stored in one
114 * of the sub objects, in the following order: QueryString, Form, Cookies,
115 * ServerVariables.
116 * @param obj Key of object to obtain. Will be coerced to string and
117 * is case insensitive.
118 * @return value stored at the given position, or UndefinedValueNode
119 * if no value is found
120 * @throws AspException on error.
121 * @see SimpleMap#get(Object)
122 */
123 public Object get(Object obj) throws AspException
124 {
125 Object queryString = QueryString.get(obj);
126 if (!(queryString instanceof UndefinedValueNode)) {
127 return queryString;
128 }
129 Object formValue = Form.get(obj);
130 if (!(formValue instanceof UndefinedValueNode)) {
131 return formValue;
132 }
133 Object serverVariable = ServerVariables.get(obj);
134 if (!(serverVariable instanceof UndefinedValueNode)) {
135 return serverVariable;
136 }
137 return Constants.undefinedValueNode;
138 }
139
140 /**
141 * Stores a value in this object. This method will always throw
142 * a AspReadOnlyException error.
143 * @param key Key to store this object under.
144 * @param value Value to store with the given key.
145 * @throws AspException always, throws AspReadOnlyException.
146 * @see SimpleMap#put(Object,Object)
147 */
148 public void put(Object key, Object value) throws AspException
149 {
150 throw new AspReadOnlyException("Request");
151 }
152
153 /**
154 * Obtains the list of keys in this object. Not currently implemented.
155 * @return enumeration of keys in this object.
156 * @throws AspException always, throws AspNotImplementedException
157 * @see SimpleMap#getKeys
158 */
159 public Enumeration getKeys() throws AspException
160 {
161 throw new AspNotImplementedException("getKeys");
162 }
163
164 /**
165 * Obtain the content length of the request.
166 * @return content length of the request.
167 */
168 public int TotalBytes()
169 {
170 int totalBytes = request.getContentLength();
171 if (totalBytes == -1) return 0;
172 return totalBytes;
173 }
174
175 /**
176 * Read the data from the form.
177 * @param length number of bytes to read
178 * @return array of data from the form.
179 */
180 public byte[] BinaryRead(int length) throws IOException
181 {
182 StringBuffer buf = new StringBuffer();
183 byte bytes[] = new byte[length];
184 InputStream is = request.getInputStream();
185
186 int nread = 0;
187
188
189 if (DBG.isDebugEnabled()) DBG.debug("Length: " + length);
190 while (nread < length) {
191 int thistime = is.read(bytes, nread, length - nread);
192 if (DBG.isDebugEnabled()) {
193 DBG.debug("nread: " + nread);
194 DBG.debug("thistime: " + thistime);
195 }
196 if (thistime <= 0) break;
197 nread += thistime;
198 }
199
200 if (DBG.isDebugEnabled()) DBG.debug("Total read: " + nread);
201 while (nread < length) {
202 byte nbytes[] = new byte[nread];
203 System.arraycopy(bytes, 0, nbytes, 0, nread);
204 bytes = nbytes;
205 }
206 return bytes;
207 }
208
209 /**
210 * The ServerVariablesObj implemnets the Request.ServerVariables
211 * object, containing a list of server variables.
212 */
213 static public class ServerVariablesObj implements SimpleMap
214 {
215 /** Reference to the collection object */
216 public AspCollection Contents = new ServerVariablesContents();
217
218 /** HTTP Servlet Request object */
219 HttpServletRequest req;
220
221 /**
222 * Constructor.
223 * @param req HTTP Servlet Request object
224 */
225 ServerVariablesObj(HttpServletRequest req)
226 {
227 this.req = req;
228 }
229
230 /**
231 * This class handles the Contents varable, which is not defined
232 * until it is first accessed.
233 */
234 class ServerVariablesContents extends AspCollection
235 {
236 /**
237 * Initialize the values for this collection
238 */
239 protected void initialize() throws AspException
240 {
241 try {
242 super.initialize();
243 /* Fill the hashtable with the predefined server variable
244 names. */
245 String REQUEST_METHOD = req.getMethod();
246 if (REQUEST_METHOD != null)
247 this.put("REQUEST_METHOD", REQUEST_METHOD);
248 String URL = req.getRequestURI();
249 if (URL != null)
250 this.put("URL", URL);
251 String SERVER_PROTOCOL = req.getProtocol();
252 if (SERVER_PROTOCOL != null)
253 this.put("SERVER_PROTOCOL", SERVER_PROTOCOL);
254 String SCRIPT_NAME = req.getServletPath();
255 if (SCRIPT_NAME != null)
256 this.put("SCRIPT_NAME", SCRIPT_NAME);
257 String PATH_INFO = req.getPathInfo();
258 if (PATH_INFO != null)
259 this.put("PATH_INFO", PATH_INFO);
260 String PATH_TRANSLATED = req.getPathTranslated();
261 if (PATH_TRANSLATED != null)
262 this.put("PATH_TRANSLATED", PATH_TRANSLATED);
263 String QUERY_STRING = req.getQueryString();
264 if (QUERY_STRING != null)
265 this.put("QUERY_STRING", QUERY_STRING);
266 int ContentLength = req.getContentLength();
267 if (ContentLength == -1) ContentLength = 0;
268 Integer CONTENT_LENGTH = new Integer(ContentLength);
269 this.put("CONTENT_LENGTH", CONTENT_LENGTH);
270 String CONTENT_TYPE = req.getContentType();
271 if (CONTENT_TYPE != null)
272 this.put("CONTENT_TYPE", CONTENT_TYPE);
273 String SERVER_NAME = req.getServerName();
274 if (SERVER_NAME != null)
275 this.put("SERVER_NAME", SERVER_NAME);
276 this.put("SERVER_PORT",
277 new Integer(req.getServerPort()));
278 String REMOTE_USER = req.getRemoteUser();
279 if (REMOTE_USER != null)
280 this.put("REMOTE_USER", REMOTE_USER);
281 String REMOTE_ADDR = req.getRemoteAddr();
282 if (REMOTE_ADDR != null)
283 this.put("REMOTE_ADDR", REMOTE_ADDR);
284 String REMOTE_HOST = req.getRemoteHost();
285 if (REMOTE_HOST != null)
286 this.put("REMOTE_HOST", REMOTE_HOST);
287 String AUTH_TYPE = req.getAuthType();
288 if (AUTH_TYPE != null)
289 this.put("AUTH_TYPE", AUTH_TYPE);
290 /* Fill the hashtable with all of the included server headers,
291 mangling the name (prepending with HTTP_ and converting
292 -'s to _'s. */
293 Enumeration enNames = req.getHeaderNames();
294 while (enNames.hasMoreElements()) {
295 String name = (String)enNames.nextElement();
296 String value = req.getHeader(name);
297 String newName = "HTTP_" + name.toUpperCase();
298 newName = newName.replace('-', '_');
299 this.put(newName, value);
300 }
301 } finally {
302 setReadOnly();
303 }
304 }
305 }
306
307 /**
308 * Obtain the value of a server variable.
309 * @param obj Key to obtain, will be converted to string and is
310 * case insensitive.
311 * @return value of server variable with the specified key.
312 * @throws AspException on error
313 * @see SimpleMap#get(Object)
314 */
315 public Object get(Object obj) throws AspException
316 {
317 return Contents.get(obj);
318 }
319
320 /**
321 * Stores a value with a specified key. ServerVariables are read-only
322 * so this method always throws an AspReadOnlyException error.
323 * @param key Key to store value under.
324 * @param value Value to store.
325 * @throws AspException always, AspReadOnlyException
326 * @see SimpleMap#put
327 */
328 public void put(Object key, Object value) throws AspException
329 {
330 throw new AspReadOnlyException("Request.ServerVariables");
331 }
332
333 /**
334 * Obtains the keys of objects stored in this object.
335 * @return Enumeration of keys stored in this object.
336 * @see SimpleMap#getKeys
337 */
338 public Enumeration getKeys() throws AspException
339 {
340 return Contents.keys();
341 }
342 }
343
344 /**
345 * QueryStringObj implements the Request.QueryString object.
346 */
347 static public class QueryStringObj implements SimpleMap, SimpleReference
348 {
349 /** Collection reference to query string values */
350 public AspCollection Contents = new QueryStringContents();
351
352 /** The entire query string */
353 String wholeString;
354
355 /**
356 * Constructor.
357 * @param queryString the entire query string.
358 */
359 public QueryStringObj(String queryString) {
360 this.wholeString = queryString;
361 }
362
363 /**
364 * SimpleReference read method which obtains the full value of this string.
365 * @return full query string.
366 * @throws AspException on error.
367 * @see SimpleReference#getValue
368 */
369 public Object getValue() throws AspException
370 {
371 return wholeString;
372 }
373
374 /**
375 * SimpleReference write method which always throws an
376 * AspReadOnlyException because Request.QueryString objects
377 * are read-only.
378 * @param obj New value for Request.QueryString
379 * @throws AspException always throws AspReadOnlyException
380 * @see SimpleReference#setValue
381 */
382 public void setValue(Object obj) throws AspException
383 {
384 throw new AspReadOnlyException("Request.QueryString");
385 }
386
387 /**
388 * SimpleMap read method which obtains the value of a specific
389 * parameter in the query string.
390 * @param obj Name of object to obtain, will be converted to
391 * string and is case insensitive.
392 * @return value of query string parameter given by obj key, this
393 * value will actually be an instance of the QueryStringValue class in
394 * order to implement methods and arrays of query string values.
395 * @throws AspException on error.
396 * @see SimpleReference#get
397 */
398 public Object get(Object obj) throws AspException
399 {
400 return Contents.get(obj);
401 }
402
403 /**
404 * SimpleMap write method which stores a value with the specified
405 * key. This method always throws AspReadOnlyException since
406 * Request.QueryString is read-only.
407 * @param key Key of object to store
408 * @param value object to store.
409 * @throws AspException always throws AspReadOnlyException
410 * @see SimpleMap#put
411 */
412 public void put(Object key, Object value) throws AspException
413 {
414 throw new AspReadOnlyException("Request.QueryString");
415 }
416
417 /**
418 * Obtains the names of parameters stored in this object.
419 * @return Enumeration of keys stored in this object.
420 * @see SimpleMap#getKeys
421 */
422 public Enumeration getKeys() throws AspException
423 {
424 return Contents.getKeys();
425 }
426
427 /**
428 * ASP-Accessible function to obtain the number of items in this
429 * query string.
430 * @return number of items in this query string
431 */
432 public int Count() throws AspException
433 {
434 return Contents.Count();
435 }
436
437 /**
438 * This class handles the Contents varable, which is not defined
439 * until it is first accessed.
440 */
441 class QueryStringContents extends AspCollection
442 {
443 /**
444 * Initialized the values for the query string contents
445 */
446 protected void initialize() throws AspException
447 {
448 try {
449 super.initialize();
450 /* Parse out all of the param/value pairs */
451 Map paramValues = Tools.parseQueryString(wholeString);
452 Tools.convertToMultiValue(this, paramValues, true);
453 } finally {
454 setReadOnly();
455 }
456 }
457 }
458 }
459
460 /**
461 * The FormObj class implements the interface between ASP Request.Form
462 * object and the Servlet API. The form values are 'lazy' evaluated
463 * to keep from accessing the Servlet's input stream until required,
464 * in case the BinaryRead function will be called.
465 */
466 static public class FormObj implements SimpleMap
467 {
468 /** AspCollection to store form data */
469 public AspCollection Contents = new FormContents();
470
471 /** Java Servlet request object */
472 HttpServletRequest request;
473
474 /**
475 * Constructor.
476 * @param request Java Servlet request object.
477 */
478 FormObj(HttpServletRequest request)
479 {
480 this.request = request;
481 }
482
483 /**
484 * Obtains the list of keys in this object.
485 * @return Enumeration of keys in this object.
486 * @throws AspException on error.
487 */
488 public Enumeration getKeys() throws AspException
489 {
490 return Contents.getKeys();
491 }
492
493 /**
494 * Obtains a form value with the specified key.
495 * @param obj Key of form parameter, will be converted to string
496 * and is case insensitive.
497 * @return instance of FormValue class corresponding to the
498 * parameter name given.
499 * @throws AspException on error.
500 * @see SimpleMap#get
501 */
502 public Object get(Object obj) throws AspException
503 {
504 return Contents.get(obj);
505 }
506
507 /**
508 * Implements the SimpleMap.put interface function. Will always
509 * throw a AspReadOnlyException becaues Request.Form is read-only.
510 * @param key Key of object to store.
511 * @param value Object to store
512 * @throws AspException on error
513 * @see SimpleMap#put
514 */
515 public void put(Object key, Object value) throws AspException
516 {
517 throw new AspReadOnlyException("Request.Form");
518 }
519
520 /**
521 * ASP-Accessible function to obtain the number of items in this form.
522 * @return number of items in this form
523 */
524 public int Count() throws AspException
525 {
526 return Contents.Count();
527 }
528
529 /**
530 * This class implements processing specific to a form
531 * data object.
532 */
533 class FormContents extends AspCollection
534 {
535 /**
536 * Empty mult-value
537 */
538 public Tools.MultiValue emptyValue = null;
539
540 /**
541 * This internal function initialized the form contents.
542 */
543 protected void initialize() throws AspException
544 {
545 super.initialize();
546
547 try {
548 /* Check if this is a POST */
549 if (!request.getMethod().equalsIgnoreCase("POST"))
550 {
551 return;
552 }
553 /* Check if this is an understood content type */
554 if (!request.getContentType().
555 equalsIgnoreCase("application/x-www-form-urlencoded")) {
556 return;
557 }
558 int len = request.getContentLength();
559 Reader in;
560 Map htValues;
561 try {
562 in = request.getReader();
563 htValues = ParseQueryString.parse(in, len);
564 } catch (IOException ex) {
565 throw new AspException("Input error: " + ex);
566 }
567 Tools.convertToMultiValue(this, htValues, false);
568
569 emptyValue = new Tools.MultiValue();
570 } finally {
571 setReadOnly();
572 }
573 }
574
575 /**
576 * Overriding the get function in order to handle empty items.
577 * @param key Key/Index to obtain
578 * @return value for the key/index
579 * @throws AspException in an error occurs
580 */
581 public synchronized Object get(Object key) throws AspException
582 {
583 Object value = super.get(key);
584 if (emptyValue != null && value instanceof UndefinedValueNode)
585 {
586 return emptyValue;
587 }
588 return value;
589 }
590 }
591 }
592
593 /**
594 * The CookiesObj class implementes the interface between the ASP
595 * Request.Cookies object and the Servlet API.
596 */
597 static public class CookiesObj implements SimpleMap
598 {
599 /** List of value of cookies */
600 Hashtable cookieValues = null;
601
602 /** An "empty" value used for non-existant cookies */
603 CookieValue emptyCookie = new CookieValue();
604
605 /** List of cookies in this request */
606 Cookie cookies[];
607
608 /**
609 * Constructor.
610 * @param request Java Servlet request object.
611 */
612 CookiesObj(HttpServletRequest request)
613 {
614 this.cookies = request.getCookies();
615 /* Parse through the cookies and get their values */
616 cookieValues = new Hashtable();
617 if (cookies != null)
618 for (int i = 0; i < cookies.length; i++)
619 {
620 Cookie cookie = cookies[i];
621 CookieValue cookieValue = new CookieValue(cookie);
622 if (DBG.isDebugEnabled()) {
623 DBG.debug("Adding cookie: " + cookie);
624 }
625 cookieValues.put(new IdentNode(cookie.getName()), cookieValue);
626 }
627 }
628
629 /**
630 * Obtains the list of keys in this object.
631 * @return Enumeration of keys in this object.
632 * @throws AspException on error.
633 */
634 public Enumeration getKeys() throws AspException
635 {
636 return cookieValues.keys();
637 }
638
639 /**
640 * Obtains a cookie value with the specified key.
641 * @param obj Key of form parameter, will be converted to string
642 * and is case insensitive.
643 * @return instance of CookieValue class corresponding to the
644 * parameter name given.
645 * @throws AspException on error.
646 * @see SimpleMap#get
647 */
648 public Object get(Object obj) throws AspException
649 {
650 IdentNode strValue = new IdentNode(Types.coerceToString(obj));
651 if (!cookieValues.containsKey(strValue)) {
652 return emptyCookie;
653 }
654 return cookieValues.get(strValue);
655 }
656
657 /**
658 * Implements the SimpleMap.put interface function. Will always
659 * throw a AspReadOnlyException becaues Request.Form is read-only.
660 * @param key Key of object to store.
661 * @param value Object to store
662 * @throws AspException on error
663 * @see SimpleMap#put
664 */
665 public void put(Object key, Object value) throws AspException
666 {
667 throw new AspReadOnlyException("Request.Cookies");
668 }
669
670 /**
671 * CookieValue class contains methods and functions related
672 * to the value of a cookie.
673 */
674 static class CookieValue implements SimpleReference, SimpleMap
675 {
676 /** Hashtable of values in this cookie */
677 Hashtable cookieValues = new Hashtable();
678
679 /** String containing the entire list of values */
680 String valuesAsString;
681
682 /**
683 * Constructor. FormValue is initally created with no values.
684 */
685 CookieValue()
686 {
687 valuesAsString = null;
688 }
689
690 /**
691 * Constructor from a cookie object.
692 * @param cookie Cookie object
693 */
694 CookieValue(Cookie cookie)
695 {
696 valuesAsString = cookie.getValue();
697 /* XXX Not sure if this works */
698 if (DBG.isDebugEnabled()) DBG.debug("Cookie values: " +
699 valuesAsString);
700 if (valuesAsString.indexOf('=') != -1)
701 {
702 Map hashValues = ParseQueryString.parse(valuesAsString);
703 /* Lowercase the value keys */
704 for (Iterator i = hashValues.keySet().iterator(); i.hasNext();)
705 {
706 String key = (String)i.next();
707 String value[] = (String[])hashValues.get(key);
708 if (DBG.isDebugEnabled()) {
709 DBG.debug("Key: " + key);
710 DBG.debug("Value: " + value[0]);
711 }
712 cookieValues.put(new IdentNode(key), value[0]);
713 }
714 }
715 }
716
717 /**
718 * Obtains a specific index in this array of values.
719 * @param value index to obtain, will be converted to an string.
720 * @return string value of form at the specified index.
721 * @throws AspException on error
722 * @see SimpleMap#get
723 */
724 public Object get(Object value) throws AspException
725 {
726 IdentNode iValue = new IdentNode(Types.coerceToString(value));
727 if (!cookieValues.containsKey(iValue)) {
728 return Constants.undefinedValueNode;
729 }
730 Object res = cookieValues.get(iValue);
731 if (DBG.isDebugEnabled())
732 DBG.debug("Get key: " + res);
733 return res;
734 }
735
736 /**
737 * Stores a value. This method always returned an
738 * AspReadOnlyException because Request.Form values
739 * are read-only.
740 * @param key Key to store
741 * @param value Value to store.
742 * @throws AspException always throws AspReadOnlyException
743 */
744 public void put(Object key, Object value) throws AspException
745 {
746 throw new AspReadOnlyException("Request.Cookies");
747 }
748
749 /**
750 * Obtains the list of values in this Request.Form(x) array.
751 * @return enumeration of the list of values in this array.
752 * @throws AspException on error
753 */
754 public Enumeration getKeys() throws AspException
755 {
756 return cookieValues.keys();
757 }
758
759 /**
760 * Obtains the entire value array
761 * @return the entire value array
762 * @throws AspException on error
763 */
764 public Object getValue() throws AspException
765 {
766 if (valuesAsString == null)
767 return Constants.undefinedValueNode;
768 return valuesAsString;
769 }
770
771 /**
772 * Sets the value of this value array. Always throws
773 * AspReadOnlyException because this array is read-only.
774 * @param obj Value to set.
775 * @throws AspException on error.
776 */
777 public void setValue(Object obj) throws AspException
778 {
779 throw new AspException("Modification of read-only variable");
780 }
781
782 /**
783 * Converts this value array to a string. For debugging only.
784 * @return string representation of this value array.
785 */
786 public String toString()
787 {
788 return "{CookieValue(" + cookieValues + ")}";
789 }
790 }
791 }
792 }