Quadcap Embeddable Server

com/quadcap/http/server22/HttpRequest.java

Go to the documentation of this file.
00001 package com.quadcap.http.server22; 00002 00003 /* Copyright 1997 - 2003 Quadcap Software. All rights reserved. 00004 * 00005 * This software is distributed under the Quadcap Free Software License. 00006 * This software may be used or modified for any purpose, personal or 00007 * commercial. Open Source redistributions are permitted. Commercial 00008 * redistribution of larger works derived from, or works which bundle 00009 * this software requires a "Commercial Redistribution License"; see 00010 * http://www.quadcap.com/purchase. 00011 * 00012 * Redistributions qualify as "Open Source" under one of the following terms: 00013 * 00014 * Redistributions are made at no charge beyond the reasonable cost of 00015 * materials and delivery. 00016 * 00017 * Redistributions are accompanied by a copy of the Source Code or by an 00018 * irrevocable offer to provide a copy of the Source Code for up to three 00019 * years at the cost of materials and delivery. Such redistributions 00020 * must allow further use, modification, and redistribution of the Source 00021 * Code under substantially the same terms as this license. 00022 * 00023 * Redistributions of source code must retain the copyright notices as they 00024 * appear in each source code file, these license terms, and the 00025 * disclaimer/limitation of liability set forth as paragraph 6 below. 00026 * 00027 * Redistributions in binary form must reproduce this Copyright Notice, 00028 * these license terms, and the disclaimer/limitation of liability set 00029 * forth as paragraph 6 below, in the documentation and/or other materials 00030 * provided with the distribution. 00031 * 00032 * The Software is provided on an "AS IS" basis. No warranty is 00033 * provided that the Software is free of defects, or fit for a 00034 * particular purpose. 00035 * 00036 * Limitation of Liability. Quadcap Software shall not be liable 00037 * for any damages suffered by the Licensee or any third party resulting 00038 * from use of the Software. 00039 */ 00040 00041 import java.util.Collections; 00042 import java.util.Comparator; 00043 import java.util.Date; 00044 import java.util.Enumeration; 00045 import java.util.Hashtable; 00046 import java.util.Locale; 00047 import java.util.Vector; 00048 00049 import java.io.BufferedReader; 00050 import java.io.ByteArrayInputStream; 00051 import java.io.IOException; 00052 import java.io.InputStream; 00053 import java.io.InputStreamReader; 00054 import java.io.PushbackInputStream; 00055 00056 import java.net.InetAddress; 00057 00058 import java.text.DateFormat; 00059 00060 import java.security.Principal; 00061 00062 import javax.servlet.RequestDispatcher; 00063 import javax.servlet.ServletInputStream; 00064 import javax.servlet.http.Cookie; 00065 import javax.servlet.http.HttpServletRequest; 00066 import javax.servlet.http.HttpSession; 00067 00068 import com.quadcap.http.util.HeaderParser; 00069 00070 import com.quadcap.util.Debug; 00071 import com.quadcap.util.Util; 00072 00073 import com.quadcap.io.URLDecodeInputStream; 00074 00075 import com.quadcap.util.text.OctetMap; 00076 import com.quadcap.util.text.Scanner; 00077 00078 00079 /** 00080 * This class encapsulates the information that makes up a single HTTP 00081 * request, including the method, URI, and the headers. 00082 * 00083 * @author Stan Bailes 00084 */ 00085 public class HttpRequest implements HttpServletRequest { 00086 WebWorker w; 00087 HttpResponse res; 00088 HttpInputStream his; 00089 HttpDispatcher rd; 00090 HSession session = null; 00091 BufferedReader reader = null; 00092 00093 Scanner scanner; 00094 static OctetMap mapM = new OctetMap('&'); 00095 static { 00096 mapM.include('\r'); 00097 mapM.include('\n'); 00098 } 00099 static OctetMap mapE = new OctetMap('='); 00100 static OctetMap mapQuote = new OctetMap('"'); 00101 00102 String method = null; 00103 String uri = null; 00104 String pathInfo = null; 00105 String queryString = null; 00106 int queryStringStart = 0; 00107 int queryStringLen = 0; 00108 String protocol = null; 00109 byte[] headers = new byte[4096]; 00110 int[] hOffsets = new int[32]; 00111 Hashtable parameters = new Hashtable(); 00112 Hashtable attributes = null; 00113 boolean getInputStreamCalled = false; 00114 boolean getReaderCalled = false; 00115 00116 static DateFormat dateFormat = DateFormat.getInstance(); 00117 00118 static final int CR = '\r'; 00119 00120 Cookie[] cookies = null; 00121 boolean badRequest = false; 00122 00123 static final String methodGET = "GET"; 00124 static final String methodHEAD = "HEAD"; 00125 static final String methodPOST = "POST"; 00126 00127 static final String proto_09 = "HTTP/0.9"; 00128 static final String proto_10 = "HTTP/1.0"; 00129 static final String proto_11 = "HTTP/1.1"; 00130 00131 /** 00132 * Using the specified worker's input stream, read an HTTP request, 00133 * and construct a new <code>HttpRequest</code> object to represent it. 00134 * 00135 * @param w the worker 00136 */ 00137 public HttpRequest(WebWorker w) { 00138 this.w = w; 00139 this.scanner = new Scanner(null); 00140 } 00141 00142 /** 00143 * Reset the request object and bind it to the input stream. 00144 */ 00145 public void reset(HttpInputStream is) throws IOException { 00146 //Jni j2 = new Jni("HttpRequest"); 00147 //j2.reset(); 00148 this.his = is; 00149 parameters.clear(); 00150 attributes = null; 00151 method = null; 00152 uri = null; 00153 pathInfo = null; 00154 queryString = null; 00155 protocol = null; 00156 reader = null; 00157 session = null; 00158 rd = null; 00159 cookies = null; 00160 queryStringStart = 0; 00161 getInputStreamCalled = false; 00162 getReaderCalled = false; 00163 00164 //j2.dump("clear"); 00165 int hsize = is.getInputStream().readHeaders(headers, hOffsets); 00166 //Debug.println(Util.strBytes(headers, 0, hsize)); 00167 //j2.dump("readHeaders"); 00168 int lim = hOffsets[1]; 00169 int pos = 0; 00170 while (headers[pos] == '\r' || headers[pos] == '\n') pos++; 00171 int start = pos; 00172 while (headers[pos] != ' ' && pos < lim) pos++; 00173 00174 final byte b1 = headers[start]; 00175 if (b1 == 'G') { 00176 if (headers[start+1] == 'E') { 00177 if (headers[start+2] == 'T') { 00178 method = methodGET; 00179 } 00180 } 00181 } else if (b1 == 'P') { 00182 if (headers[start+1] == 'O') { 00183 if (headers[start+2] == 'S') { 00184 if (headers[start+3] == 'T') { 00185 method = methodPOST; 00186 } 00187 } 00188 } 00189 } else if (b1 == 'H') { 00190 if (headers[start+1] == 'E') { 00191 if (headers[start+2] == 'A') { 00192 if (headers[start+3] == 'D') { 00193 method = methodHEAD; 00194 } 00195 } 00196 } 00197 } 00198 if (method == null) { 00199 method = new String(headers, start, pos - start); 00200 if (method.equalsIgnoreCase("get")) { 00201 method = methodGET; 00202 } else if (method.equalsIgnoreCase("post")) { 00203 method = methodPOST; 00204 } else if (method.equalsIgnoreCase("head")) { 00205 method = methodHEAD; 00206 } 00207 } 00208 00209 //j2.dump("getMethod"); 00210 while (headers[pos] == ' ' && pos < lim) pos++; 00211 start = pos; 00212 while (headers[pos] != ' ' && headers[pos] != '?' && pos < lim) pos++; 00213 uri = new String(headers, start, pos - start); 00214 //j2.dump("getUri"); 00215 if (headers[pos] == '?') { 00216 start = ++pos; 00217 while (headers[pos] != ' ' && headers[pos] != '\r'&& pos < lim) { 00218 pos++; 00219 } 00220 queryStringStart = start; 00221 queryStringLen = pos - start; 00222 parseParameters(parameters, 00223 new ByteArrayInputStream(headers, start, 00224 pos-start)); 00225 00226 } 00227 while (headers[pos] == ' ' && pos < lim) pos++; 00228 start = pos; 00229 if (pos < lim) { 00230 for (byte c = headers[pos]; 00231 c != '\r' && c != '\n' && c != ' '; 00232 c = headers[pos]) { 00233 if (++pos >= lim) break; 00234 } 00235 } 00236 protocol = proto_10; 00237 try { 00238 if (headers[pos-1] == '0') { 00239 protocol = proto_10; 00240 } else if (headers[pos-1] == '1') { 00241 protocol = proto_11; 00242 } 00243 if (protocol == null) { 00244 protocol = new String(headers, start, pos - start); 00245 } 00246 } catch (Throwable t) {} 00247 //j2.dump("getProto"); 00248 00249 int cl = getContentLength(); 00250 if (cl >= 0) { 00251 is.setContentLength(cl); 00252 } 00253 } 00254 00255 final void maybeParsePostData() { 00256 if (!getReaderCalled && !getInputStreamCalled && 00257 method == methodPOST) { 00258 String type = getContentType(); 00259 if (type != null && 00260 type.equals("application/x-www-form-urlencoded")) 00261 { 00262 scanner.reset(his); 00263 parseParameters(parameters, scanner); 00264 } 00265 } 00266 } 00267 00268 boolean badRequest() { return badRequest; } 00269 00270 public void setURI(String s) { 00271 try { 00272 Scanner scanner = 00273 new Scanner(new ByteArrayInputStream(s.getBytes())); 00274 this.uri = scanner.parseWhile(OctetMap.uriChars); 00275 if (scanner.peek() == '?') { 00276 scanner.matchChar('?'); 00277 queryString = scanner.parseWhile(OctetMap.uriChars); 00278 parseParameters(parameters, 00279 new ByteArrayInputStream(queryString.getBytes())); 00280 } else { 00281 queryString = ""; 00282 } 00283 } catch (IOException e) {} 00284 } 00285 00286 /** 00287 * ----- ServletRequest methods 00288 */ 00289 00290 /** 00291 * Returns the size of the request entity data, or -1 if not known. 00292 * Same as the CGI variable CONTENT_LENGTH. 00293 */ 00294 public int getContentLength() { 00295 String s = getHeader("Content-Length"); 00296 if (s == null) return -1; 00297 try { 00298 return Integer.parseInt(s); 00299 } catch (Exception e) { 00300 return -1; 00301 } 00302 } 00303 00304 /** 00305 * Returns the Internet Media Type of the request entity data, or 00306 * null if not known. Same as the CGI variable CONTENT_TYPE. 00307 */ 00308 public String getContentType() { 00309 return getHeader("Content-Type"); 00310 } 00311 00312 /** 00313 * Returns the protocol and version of the request as a string of 00314 * the form <code>&lt;protocol&gt;/&lt;major version&gt;.&lt;minor 00315 * version&gt</code>. Same as the CGI variable SERVER_PROTOCOL. 00316 */ 00317 public String getProtocol() { 00318 return protocol; 00319 } 00320 00321 /** 00322 * Returns the scheme of the URL used in this request, for example 00323 * "http", "https", or "ftp". Different schemes have different 00324 * rules for constructing URLs, as noted in RFC 1738. The URL used 00325 * to create a request may be reconstructed using this scheme, the 00326 * server name and port, and additional information such as URIs. 00327 */ 00328 public String getScheme() { 00329 return "HTTP"; 00330 } 00331 00332 /** 00333 * Returns the host name of the server that received the request. 00334 * Same as the CGI variable SERVER_NAME. 00335 */ 00336 public String getServerName() { 00337 return w.getHostName(); 00338 } 00339 00340 /** 00341 * Returns the port number on which this request was received. 00342 * Same as the CGI variable SERVER_PORT. 00343 */ 00344 public int getServerPort() { 00345 return w.getPort(); 00346 } 00347 00348 /** 00349 * Returns the IP address of the agent that sent the request. 00350 * Same as the CGI variable REMOTE_ADDR. 00351 */ 00352 public String getRemoteAddr() { 00353 return w.getRemoteAddr(); 00354 } 00355 00356 /** 00357 * Returns the fully qualified host name of the agent that sent the 00358 * request. Same as the CGI variable REMOTE_HOST. 00359 */ 00360 public String getRemoteHost() { 00361 return w.getRemoteHost(); 00362 } 00363 00364 /** 00365 * Applies alias rules to the specified virtual path and returns 00366 * the corresponding real path, or null if the translation can not 00367 * be performed for any reason. For example, an HTTP servlet would 00368 * resolve the path using the virtual docroot, if virtual hosting 00369 * is enabled, and with the default docroot otherwise. Calling 00370 * this method with the string "/" as an argument returns the 00371 * document root. 00372 * 00373 * @param path the virtual path to be translated to a real path 00374 */ 00375 public String getRealPath(String path) { 00376 WebApplication app = rd.getContext(); 00377 return app.getRealPath(path); 00378 } 00379 00380 /** 00381 * Returns an input stream for reading binary data in the request body. 00382 * 00383 * @exception IllegalStateException if getReader has been 00384 * called on this same request. 00385 * @exception IOException on other I/O related errors. 00386 */ 00387 public ServletInputStream getInputStream() throws IOException { 00388 if (getReaderCalled) { 00389 throw new IllegalStateException("getReader() already called"); 00390 } 00391 getInputStreamCalled = true; 00392 return w.getHttpInputStream(); 00393 } 00394 00395 /** 00396 * Returns a string containing the lone value of the specified 00397 * parameter, or null if the parameter does not exist. For example, 00398 * in an HTTP servlet this method would return the value of the 00399 * specified query string parameter. Servlet writers should use 00400 * this method only when they are sure that there is only one value 00401 * for the parameter. If the parameter has (or could have) 00402 * multiple values, servlet writers should use 00403 * getParameterValues. If a multiple valued parameter name is 00404 * passed as an argument, the return value is implementation 00405 * dependent. 00406 * 00407 * 00408 * @param name the name of the parameter whose value is required. 00409 */ 00410 public String getParameter(String name) { 00411 maybeParsePostData(); 00412 String[] s = (String[])parameters.get(name); 00413 if (s == null) return null; 00414 return s[0]; 00415 } 00416 00417 /** 00418 * Returns the values of the specified parameter for the request as 00419 * an array of strings, or null if the named parameter does not 00420 * exist. For example, in an HTTP servlet this method would return 00421 * the values of the specified query string or posted form as an 00422 * array of strings. 00423 * 00424 * @param name the name of the parameter whose value is required. 00425 */ 00426 public String[] getParameterValues(String name) { 00427 maybeParsePostData(); 00428 String[] ret =(String[])parameters.get(name); 00429 return ret; 00430 } 00431 00432 /** 00433 * Returns the parameter names for this request as an enumeration 00434 * of strings, or an empty enumeration if there are no parameters 00435 * or the input stream is empty. The input stream would be empty 00436 * if all the data had been read from the stream returned by the 00437 * method getInputStream. 00438 */ 00439 public Enumeration getParameterNames() { 00440 maybeParsePostData(); 00441 return parameters.keys(); 00442 } 00443 00444 /** 00445 * Returns the value of the named attribute of the request, or 00446 * null if the attribute does not exist. 00447 * 00448 * @param name the name of the attribute whose value is required 00449 */ 00450 public Object getAttribute(String name) { 00451 Object obj = null; 00452 if (attributes != null) obj = attributes.get(name); 00453 return obj; 00454 } 00455 00456 /** 00457 * Set the value of the named attribute 00458 * 00459 * @param name the name of the attribute 00460 * @param obj the value 00461 */ 00462 public void setAttribute(String name, Object obj) { 00463 if (attributes == null) attributes = new Hashtable(); 00464 attributes.put(name, obj); 00465 } 00466 00467 /** 00468 * Return an enumeration of all attribute names of this service 00469 */ 00470 public Enumeration getAttributeNames() { 00471 if (attributes == null) return new Vector().elements(); 00472 return attributes.keys(); 00473 } 00474 00475 /** 00476 * Remove an attribute from the request 00477 */ 00478 public void removeAttribute(String name) { 00479 attributes.remove(name); 00480 } 00481 00482 /** 00483 * Returns a buffered reader for reading text in the request body. 00484 * This translates character set encodings as appropriate. 00485 * 00486 * 00487 * @exception UnsupportedEncodingException if the character set encoding 00488 * is unsupported, so the text can't be correctly decoded. 00489 * @exception IllegalStateException if getInputStream has been 00490 * called on this same request. 00491 * @exception IOException on other I/O related errors. 00492 */ 00493 public BufferedReader getReader() throws IOException { 00494 if (getInputStreamCalled) { 00495 throw new IllegalStateException("getInputStream() already called"); 00496 } 00497 if (reader == null) { 00498 reader = 00499 new BufferedReader(new InputStreamReader(getInputStream())); 00500 } 00501 getReaderCalled = true; 00502 return reader; 00503 } 00504 00505 /** 00506 * Returns the character set encoding for the input of this request. 00507 */ 00508 public String getCharacterEncoding () { 00509 String s = getContentType(); 00510 if (s == null) return null; 00511 00512 int idx = s.indexOf(';'); 00513 while (idx > 0) { 00514 s = s.substring(idx+1).trim(); 00515 idx = s.indexOf(';'); 00516 String c = s; 00517 if (idx > 0) { 00518 c = s.substring(0, idx).trim(); 00519 } 00520 if (c.startsWith("charset=")) { 00521 return c.substring(8); 00522 } 00523 } 00524 return null; 00525 } 00526 00527 /** 00528 * ----- HttpServletRequest methods ----- 00529 */ 00530 00531 /** 00532 * Gets the array of cookies found in this request. 00533 * 00534 * @return the array of cookies found in this request 00535 */ 00536 public Cookie[] getCookies() { 00537 if (cookies == null) { 00538 CookieParser cp = new CookieParser(getHeader("Cookie")); 00539 try { 00540 cookies = cp.parseCookies(); 00541 } catch (IOException e) { 00542 Debug.print(e); 00543 cookies = new Cookie[0]; 00544 } 00545 } 00546 return cookies; 00547 } 00548 00549 /** 00550 * Gets the HTTP method (for example, GET, POST, PUT) with which 00551 * this request was made. Same as the CGI variable REQUEST_METHOD. 00552 * 00553 * @return the HTTP method with which this request was made 00554 */ 00555 public String getMethod() { 00556 return method; 00557 } 00558 00559 /** 00560 * Gets, from the first line of the HTTP request, the part of this 00561 * request's URI that is to the left of any query string. 00562 * For example, 00563 * 00564 * <blockquote> 00565 * <table> 00566 * <tr align=left><th>First line of HTTP request<th> 00567 * <th>Return from <code>getRequestURI</code> 00568 * <tr><td>POST /some/path.html HTTP/1.1<td><td>/some/path.html 00569 * <tr><td>GET http://foo.bar/a.html HTTP/1.0 00570 * <td><td>http://foo.bar/a.html 00571 * <tr><td>HEAD /xyz?a=b HTTP/1.1<td><td>/xyz 00572 * </table> 00573 * </blockquote> 00574 * 00575 * <p>To reconstruct a URL with a URL scheme and host, use the 00576 * method javax.servlet.http.HttpUtils.getRequestURL, which returns 00577 * a StringBuffer. 00578 * 00579 * @return this request's URI 00580 */ 00581 public String getRequestURI() { 00582 return uri; 00583 } 00584 00585 /** 00586 * Gets the part of this request's URI that refers to the servlet 00587 * being invoked. Analogous to the CGI variable SCRIPT_NAME. 00588 * 00589 * @return the servlet being invoked, as contained in this 00590 * request's URI 00591 */ 00592 public String getServletPath() { 00593 return rd.getServletPath(); 00594 } 00595 00596 /** 00597 * Gets any optional extra path information following the servlet 00598 * path of this request's URI, but immediately preceding its query 00599 * string. Same as the CGI variable PATH_INFO. 00600 * 00601 * @return the optional path information following the servlet 00602 * path, but before the query string, in this request's URI; null 00603 * if this request's URI contains no extra path information 00604 */ 00605 public String getPathInfo() { 00606 return rd.getPathInfo(); 00607 } 00608 00609 /** 00610 * Gets any optional extra path information following the servlet 00611 * path of this request's URI, but immediately preceding its query 00612 * string, and translates it to a real path. Similar to the CGI 00613 * variable PATH_TRANSLATED 00614 * 00615 * @return extra path information translated to a real path or null 00616 * if no extra path information is in the request's URI 00617 */ 00618 public String getPathTranslated() { 00619 return null; 00620 } 00621 00622 /** 00623 * Gets any query string that is part of the HTTP request URI. 00624 * Same as the CGI variable QUERY_STRING. 00625 * 00626 * @return query string that is part of this request's URI, or null 00627 * if it contains no query string 00628 */ 00629 public String getQueryString() { 00630 if (queryString == null) { 00631 if (queryStringStart > 0) { 00632 queryString = new String(headers, queryStringStart, 00633 (queryStringLen)); 00634 } 00635 } 00636 return queryString; 00637 } 00638 00639 /** 00640 * Gets the name of the user making this request. The user name is 00641 * set with HTTP authentication. Whether the user name will 00642 * continue to be sent with each subsequent communication is 00643 * browser-dependent. Same as the CGI variable REMOTE_USER. 00644 * 00645 * @return the name of the user making this request, or null if not 00646 * known. 00647 */ 00648 public String getRemoteUser() { 00649 return null; 00650 } 00651 00652 /** 00653 * Gets the authentication scheme of this request. Same as the CGI 00654 * variable AUTH_TYPE. 00655 * 00656 * @return this request's authentication scheme, or null if none. 00657 */ 00658 public String getAuthType() { 00659 return null; 00660 } 00661 00662 /** 00663 * Gets the value of the requested header field of this request. 00664 * The case of the header field name is ignored. 00665 * 00666 * @param name the String containing the name of the requested 00667 * header field 00668 * @return the value of the requested header field, or null if not 00669 * known. 00670 */ 00671 public String getHeader(String name) { 00672 String ret = null; 00673 int hcnt = hOffsets[0]; 00674 for (int i = 1; i < hcnt; i++) { 00675 int off = hOffsets[i]; 00676 int lim = hOffsets[i+1]; 00677 int pos = off; 00678 while (headers[pos] != ':' && pos < lim) pos++; 00679 String hdr = new String(headers, off, pos - off); 00680 if (name.equalsIgnoreCase(hdr)) { 00681 ret = new String(headers, pos+1, lim-pos-1).trim(); 00682 break; 00683 } 00684 } 00685 return ret; 00686 } 00687 00688 00689 /** 00690 * Gets the value of the specified integer header field of this 00691 * request. The case of the header field name is ignored. If the 00692 * header can't be converted to an integer, the method throws a 00693 * NumberFormatException. 00694 * 00695 * @param name the String containing the name of the requested 00696 * header field 00697 * @return the value of the requested header field, or -1 if not 00698 * found. 00699 */ 00700 public int getIntHeader(String name) { 00701 int ret = -1; 00702 String val = getHeader(name); 00703 if (val != null) { 00704 ret = Integer.parseInt(val); 00705 } 00706 return ret; 00707 } 00708 00709 /** 00710 * Gets the value of the requested date header field of this 00711 * request. If the header can't be converted to a date, the method 00712 * throws an IllegalArgumentException. The case of the header 00713 * field name is ignored. 00714 * 00715 * @param name the String containing the name of the requested 00716 * header field 00717 * @return the value the requested date header field, or -1 if not 00718 * found. 00719 */ 00720 public long getDateHeader(String name) { 00721 long ret = -1; 00722 String val = getHeader(name); 00723 if (val != null) { 00724 try { 00725 synchronized (dateFormat) { 00726 Date d = dateFormat.parse(val); 00727 ret = d.getTime(); 00728 } 00729 } catch (Exception e) { 00730 } 00731 } 00732 return ret; 00733 } 00734 00735 /** 00736 * Gets the header names for this request. 00737 * 00738 * @return an enumeration of strings representing the header names 00739 * for this request. Some server implementations do not allow 00740 * headers to be accessed in this way, in which case this method 00741 * will return null. 00742 */ 00743 public Enumeration getHeaders() { 00744 return getHeaderNames(); 00745 } 00746 00747 public Enumeration getHeaders(String name) { 00748 Vector v = new Vector(); 00749 int hcnt = hOffsets[0]; 00750 for (int i = 1; i < hcnt; i++) { 00751 int off = hOffsets[i]; 00752 int lim = hOffsets[i+1]; 00753 int pos = off; 00754 while (headers[pos] != ':' && pos < lim) pos++; 00755 if (name.equalsIgnoreCase(new String(headers, off, pos-off))) { 00756 pos++; 00757 int len = lim - pos; 00758 v.addElement(new String(headers, pos, len).trim()); 00759 } 00760 } 00761 return v.elements(); 00762 } 00763 00764 public Enumeration getHeaderNames() { 00765 Vector v = new Vector(); 00766 int hcnt = hOffsets[0]; 00767 for (int i = 1; i < hcnt; i++) { 00768 int off = hOffsets[i]; 00769 int lim = hOffsets[i+1]; 00770 int pos = off; 00771 while (headers[pos] != ':' && pos < lim) pos++; 00772 v.addElement(new String(headers, off, pos-off)); 00773 } 00774 return v.elements(); 00775 } 00776 00777 /** 00778 * Gets the current valid session associated with this request, if 00779 * create is false or, if necessary, creates a new session for the 00780 * request, if create is true. 00781 * 00782 * <p><b>Note</b>: to ensure the session is properly maintained, 00783 * the servlet developer must call this method (at least once) 00784 * before any output is written to the response. 00785 * 00786 * <p>Additionally, application-writers need to be aware that newly 00787 * created sessions (that is, sessions for which 00788 * <code>HttpSession.isNew</code> returns true) do not have any 00789 * application-specific state. 00790 * 00791 * @return the session associated with this request or null if 00792 * create was false and no valid session is associated 00793 * with this request. 00794 */ 00795 public HttpSession getSession(boolean create) { 00796 if (session == null) { 00797 WebApplication app = rd.getContext(); 00798 String sessionId = getRequestedSessionId(); 00799 if (sessionId != null) { 00800 session = app.getSession(sessionId); 00801 } else if (create) { 00802 session = app.createSession(); 00803 sessionId = session.getId(); 00804 } 00805 if (sessionId != null) { 00806 Cookie c = new Cookie("sessionId", sessionId); 00807 c.setPath(getContextPath()); 00808 res.addCookie(c); 00809 } 00810 } 00811 if (session != null) session.updateLastAccess(); 00812 return session; 00813 } 00814 00815 /** 00816 * Gets the current valid session associated with this request, and 00817 * if necessary, creates a new session for the request. 00818 * 00819 * @return the session associated with this request. 00820 */ 00821 public HttpSession getSession() { 00822 return getSession(true); 00823 } 00824 00825 /** 00826 * Gets the session id specified with this request. This may 00827 * differ from the actual session id. For example, if the request 00828 * specified an id for an invalid session, then this will get a new 00829 * session with a new id. 00830 * 00831 * @return the session id specified by this request, or null if the 00832 * request did not specify a session id 00833 * 00834 */ 00835 public String getRequestedSessionId() { 00836 String sessionId = null; 00837 getCookies(); 00838 WebApplication app = rd.getContext(); 00839 boolean foundSession = false; 00840 for (int i = 0; i < cookies.length; i++) { 00841 Cookie c = cookies[i]; 00842 if (c.getName().equals("sessionId")) { 00843 sessionId = c.getValue(); 00844 if (app.getSession(sessionId) != null) { 00845 foundSession = true; 00846 break; 00847 // } else { 00848 // Debug.println("invalid session id: " + sessionId); 00849 } 00850 } 00851 } 00852 if (sessionId != null && !foundSession) { 00853 sessionId = app.createSession().getId(); 00854 } 00855 return sessionId; 00856 } 00857 00858 /** 00859 * Checks whether this request is associated with a session that 00860 * is valid in the current session context. If it is not valid, 00861 * the requested session will never be returned from the 00862 * <code>getSession</code> method. 00863 * 00864 * @return true if this request is assocated with a session that is 00865 * valid in the current session context. 00866 * 00867 */ 00868 public boolean isRequestedSessionIdValid() { 00869 HSession sess = null; 00870 String sessionId = getRequestedSessionId(); 00871 if (sessionId != null) { 00872 sess = rd.getContext().getSession(sessionId); 00873 } 00874 return (sess != null && sess.isValid()); 00875 } 00876 00877 /** 00878 * Checks whether the session id specified by this request came in 00879 * as a cookie. (The requested session may not be one returned by 00880 * the <code>getSession</code> method.) 00881 * 00882 * @return true if the session id specified by this request came in 00883 * as a cookie; false otherwise 00884 * 00885 */ 00886 public boolean isRequestedSessionIdFromCookie() { 00887 return true; 00888 } 00889 00890 /** 00891 * Checks whether the session id specified by this request came in 00892 * as part of the URL. (The requested session may not be the one 00893 * returned by the <code>getSession</code> method.) 00894 * 00895 * @return true if the session id specified by the request for this 00896 * session came in as part of the URL; false otherwise 00897 * 00898 */ 00899 public boolean isRequestedSessionIdFromURL() { 00900 return false; 00901 } 00902 public boolean isRequestedSessionIdFromUrl() { 00903 return false; 00904 } 00905 00906 /** 00907 * ---- private parsing helpers 00908 */ 00909 00910 00911 /** 00912 * Parsing helper: Get the next bytes all of which are in the 00913 * specified map 00914 * 00915 * @param map the octet map specifying the bytes we want 00916 */ 00917 String getToken(OctetMap map) throws IOException { 00918 scanner.skipWhile(OctetMap.wsChars); 00919 return scanner.parseWhile(map); 00920 } 00921 00922 static OctetMap versionMap = new OctetMap("HhTtPp/1.0"); 00923 00924 /** 00925 * Parse the http version field from the request line 00926 * 00927 * @return the http protocol and version 00928 * 00929 * @exception IOException if the version field is incorrect 00930 */ 00931 String parseHttpVersion() throws IOException { 00932 scanner.skipWhile(OctetMap.wsChars); 00933 return scanner.parseWhile(versionMap); 00934 } 00935 00936 static final String urlDecode(String s) { 00937 StringBuffer sb = null; 00938 for (int i = 0; i < s.length(); i++) { 00939 char c = s.charAt(i); 00940 switch (c) { 00941 case '+': 00942 if (sb == null) sb = new StringBuffer(s.substring(0, i)); 00943 sb.append(' '); 00944 break; 00945 case '%': 00946 if (sb == null) sb = new StringBuffer(s.substring(0, i)); 00947 try { 00948 sb.append((char) Integer.parseInt(s.substring(i+1, i+3), 00949 16)); 00950 i += 2; 00951 } catch (NumberFormatException e) { 00952 throw new IllegalArgumentException(); 00953 } catch (StringIndexOutOfBoundsException e) { 00954 String rest = s.substring(i); 00955 sb.append(rest); 00956 if (rest.length()==2) i++; 00957 } 00958 break; 00959 default: 00960 if (sb != null) sb.append(c); 00961 } 00962 } 00963 return sb == null ? s : sb.toString(); 00964 } 00965 00966 /** 00967 * Parse a set of parameters from the specified input stream. 00968 * 00969 * @param is the input stream 00970 * @return a table containing the parameters as String -> String[] 00971 * entries. 00972 */ 00973 public static void parseParameters(Hashtable params, InputStream is) { 00974 parseParameters(params, new Scanner(is)); 00975 } 00976 00977 public static void parseParameters(Hashtable params, Scanner s) { 00978 try { 00979 do { 00980 String name = urlDecode(s.parseUntil(mapE)); 00981 s.matchChar('='); 00982 String val = urlDecode(s.parseUntil(mapM)); 00983 String[] vals = (String[])params.get(name); 00984 if (vals == null) { 00985 vals = new String[1]; 00986 } else { 00987 String[] oldvals = vals; 00988 vals = new String[vals.length+1]; 00989 for (int i = 0; i < oldvals.length; i++) { 00990 vals[i] = oldvals[i]; 00991 } 00992 } 00993 vals[vals.length-1] = val; 00994 params.put(name, vals); 00995 s.matchChar('&'); 00996 } while (s.peek() >= 0); 00997 } catch (IOException e) { 00998 } 00999 } 01000 01001 /** 01002 * Associate this request with its matching response. We need this 01003 * to facilitate session management via cookies 01004 * 01005 * @param res the HttpResponse that goes with this request 01006 */ 01007 void setResponse(HttpResponse res) { this.res = res; } 01008 01009 /** 01010 * Return the portion of the request URI that specifies the context 01011 * for this request 01012 */ 01013 public String getContextPath() { 01014 return rd.getContextPath(); 01015 } 01016 01017 /** 01018 * Returns a boolean indicating whether the authenticated user is 01019 * included in the indicated 'role'. 01020 */ 01021 public boolean isUserInRole(String role) { 01022 return false; // XXX implement me 01023 } 01024 01025 /** 01026 * Return a Principal object indicating the identity of the user 01027 * associated with this request. 01028 */ 01029 public Principal getUserPrincipal() { 01030 return null; // XXX implement me 01031 } 01032 01033 /** 01034 * Return the preferred Locale that the client will accept content 01035 * from based on the <code>Accept-Language</code> header, or the 01036 * server default Locale 01037 */ 01038 public Locale getLocale() { 01039 Locale locale = null; 01040 String accept = getHeader("Accept-Language"); 01041 if (accept != null) { 01042 int idx = accept.indexOf(','); 01043 if (idx > 0) { 01044 accept = accept.substring(0, idx).trim(); 01045 } 01046 locale = makeLocale(accept); 01047 } else { 01048 locale = Locale.getDefault(); 01049 } 01050 return locale; 01051 } 01052 01053 final Locale makeLocale(String s) { 01054 int idx = s.indexOf(';'); 01055 if (idx > 0) { 01056 s = s.substring(0, idx).trim(); 01057 } 01058 idx = s.indexOf('-'); 01059 String lang = null; 01060 String country = ""; 01061 if (idx > 0) { 01062 lang = s.substring(0, idx); 01063 country = s.substring(idx+1); 01064 } else { 01065 lang = s; 01066 } 01067 return new Locale(lang, country); 01068 } 01069 01070 final float getLangQual(String s) { 01071 float q = 1.0f; 01072 int idx = s.indexOf(';'); 01073 if (idx > 0) { 01074 s = s.substring(idx+1).trim(); 01075 idx = s.indexOf("q="); 01076 if (idx >= 0) { 01077 q =Float.parseFloat(s.substring(idx+2)); 01078 } 01079 } 01080 return q; 01081 } 01082 01083 final int compareAccept(String a, String b) { 01084 float aq = getLangQual(a); 01085 float bq = getLangQual(b); 01086 if (aq < bq) return -1; 01087 if (aq > bq) return 1; 01088 return 0; 01089 } 01090 01091 public Enumeration getLocales() { 01092 String accept = getHeader("Accept-Language"); 01093 if (accept == null) { 01094 Vector v = new Vector(); 01095 v.addElement(Locale.getDefault()); 01096 return v.elements(); 01097 } 01098 Vector v = Util.split(accept, ','); 01099 Comparator c = new Comparator() { 01100 public int compare(Object a, Object b) { 01101 return compareAccept(a.toString(), b.toString()); 01102 } 01103 }; 01104 Collections.sort(v, c); 01105 final Enumeration e = v.elements(); 01106 return new Enumeration() { 01107 public boolean hasMoreElements() { 01108 return e.hasMoreElements(); 01109 } 01110 public Object nextElement() { 01111 return makeLocale(e.nextElement().toString()); 01112 } 01113 }; 01114 } 01115 01116 public RequestDispatcher getRequestDispatcher(String path) { 01117 return rd.getContext().getRelativeRequestDispatcher(path, this); 01118 } 01119 01120 public boolean isSecure() { 01121 return false; 01122 } 01123 01124 final void setRequestDispatcher(HttpDispatcher rd) { 01125 this.rd = rd; 01126 } 01127 01128 }