001: /*
002: * Copyright (c) 2001 by Matt Welsh and The Regents of the University of
003: * California. All rights reserved.
004: *
005: * Permission to use, copy, modify, and distribute this software and its
006: * documentation for any purpose, without fee, and without written agreement is
007: * hereby granted, provided that the above copyright notice and the following
008: * two paragraphs appear in all copies of this software.
009: *
010: * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
011: * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
012: * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
013: * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
014: *
015: * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
016: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
017: * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
018: * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
019: * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
020: *
021: * Author: Matt Welsh <mdw@cs.berkeley.edu>
022: * Code to parse HTTP query strings by Eric Wagner.
023: *
024: */
025:
026: package seda.sandStorm.lib.http;
027:
028: import seda.sandStorm.api.*;
029: import seda.sandStorm.lib.aSocket.*;
030: import seda.sandStorm.core.*;
031:
032: import java.util.*;
033: import java.io.*;
034: import java.net.*;
035:
036: import com.rimfaxe.util.CaseInsensitiveString;
037:
038: /**
039: * This class represents a single HTTP client request.
040: *
041: * @author Matt Welsh
042: */
043: public class httpRequest extends TimeStampedEvent implements httpConst,
044: ClassQueueElementIF {
045:
046: private static final boolean DEBUG = false;
047:
048: /** getRequest() code corresponding to a GET request. */
049: public static final int REQUEST_GET = 0;
050: /** getRequest() code corresponding to a POST request. */
051: public static final int REQUEST_POST = 1;
052:
053: /** getRequest() code corresponding to a HEAD request. */
054: public static final int REQUEST_HEAD = 2;
055:
056: /** getHttpVer() code corresponding to HTTP/0.9. */
057: public static final int HTTPVER_09 = 0;
058: /** getHttpVer() code corresponding to HTTP/1.0. */
059: public static final int HTTPVER_10 = 1;
060: /** getHttpVer() code corresponding to HTTP/1.1. */
061: public static final int HTTPVER_11 = 2;
062:
063: /** Default value for a query key. */
064: public static final String QUERY_KEY_SET = "true";
065:
066: private httpConnection conn;
067: private int request;
068: private String url;
069: private int httpver;
070: private int user_class = -2;
071:
072: private Vector rawHeader;
073: private Hashtable header;
074: private Hashtable query;
075:
076: private String request_string = null;
077: private String query_string = null;
078: private String request_body = null;
079:
080: private Hashtable special = new Hashtable();
081:
082: private int ServerPort = -1;
083:
084: private String ip_addr = "127.0.0.1";
085:
086: /**
087: * Package-internal: Create an httpRequest from the given connection,
088: * request string, URL, HTTP version, and header.
089: */
090: httpRequest(httpConnection conn, String requestStr,
091: String orig_url, int httpver, Vector header, String req_body)
092: throws IOException {
093: this .conn = conn;
094: this .httpver = httpver;
095: this .rawHeader = header;
096: this .header = null;
097: this .request_body = req_body;
098:
099: String url = orig_url;
100:
101: //System.out.println("Client connection = "+conn);
102:
103: ip_addr = conn.getConnection().getAddress().getHostAddress();
104:
105: //System.out.println("Client IP = "+ip_addr);
106:
107: request_string = requestStr;
108:
109: //System.out.println("httpRequest URL ("+url+")");
110: //System.out.println("httpRequest method ("+requestStr+")");
111:
112: if (url.toLowerCase().startsWith("http://")) {
113: //absolute uri, strip off domain
114: String u1 = url.substring(7);
115: url = u1.substring(u1.indexOf('/'));
116: }
117:
118: //System.out.println("httpRequest URL* ("+url+")");
119:
120: // Check to see if there is a query string
121: int question = url.indexOf('?');
122: if (question != -1) {
123: query = new Hashtable();
124: this .query_string = url.substring(question + 1);
125: this .url = url.substring(0, question);
126:
127: StringTokenizer st = new StringTokenizer(url
128: .substring(question + 1), ";&");
129: while (st.hasMoreTokens()) {
130: String name_value_pair = decodeURL(st.nextToken());
131: int equals = name_value_pair.indexOf('=');
132:
133: if (equals == -1) {
134: putVal(name_value_pair, QUERY_KEY_SET);
135: } else {
136: putVal(name_value_pair.substring(0, equals),
137: name_value_pair.substring(equals + 1));
138: }
139: }
140:
141: } else {
142: this .url = url;
143: }
144:
145: if (requestStr.equalsIgnoreCase("head")) {
146: request = REQUEST_HEAD;
147: } else if (requestStr.equalsIgnoreCase("get")) {
148: request = REQUEST_GET;
149: } else if (requestStr.equalsIgnoreCase("post")) {
150: request = REQUEST_POST;
151:
152: if (query == null)
153: query = new Hashtable();
154:
155: if (request_body.length() > 0) {
156: StringTokenizer tkz = new StringTokenizer(request_body,
157: "&", false);
158: while (tkz.hasMoreTokens()) {
159: String name_value_pair = decodeURL(tkz.nextToken());
160:
161: int equals = name_value_pair.indexOf('=');
162:
163: if (equals == -1) {
164: putVal(name_value_pair, QUERY_KEY_SET);
165: } else {
166:
167: putVal(name_value_pair.substring(0, equals),
168: name_value_pair.substring(equals + 1));
169:
170: }
171:
172: }
173: }
174: } else {
175: throw new IOException("Bad HTTP request: " + request);
176: }
177: }
178:
179: public void setSpecial(String key, Object o) {
180: special.put(key, o);
181: }
182:
183: public Object getSpecial(String key) {
184: return special.get(key);
185: }
186:
187: public void setServerPort(int val) {
188: ServerPort = val;
189: }
190:
191: public int getServerPort() {
192: return ServerPort;
193: }
194:
195: public String getRequestString() {
196: return this .request_string;
197: }
198:
199: public String getQueryString() {
200: return this .query_string;
201: }
202:
203: public String getRequestBody() {
204: return this .request_body;
205: }
206:
207: // Decode special characters in URLs
208: private String decodeURL(String encoded) {
209: StringBuffer out = new StringBuffer(encoded.length());
210: int i = 0;
211: int j = 0;
212:
213: while (i < encoded.length()) {
214: char ch = encoded.charAt(i);
215: i++;
216: if (ch == '+')
217: ch = ' ';
218: else if (ch == '%') {
219: try {
220: ch = (char) Integer.parseInt(encoded.substring(i,
221: i + 2), 16);
222: i += 2;
223: } catch (StringIndexOutOfBoundsException se) {
224: // If nothing's there, just ignore it
225: }
226: }
227: out.append(ch);
228: j++;
229: }
230: return out.toString();
231: }
232:
233: // Add a key to the query set
234: private void putVal(String key, String val) {
235: Object oldval = query.get(key);
236: if (oldval == null) {
237: query.put(key, val);
238: } else if (oldval instanceof String) {
239: query.remove(key);
240: Vector vec = new Vector(2);
241: vec.addElement(oldval);
242: vec.addElement(val);
243: query.put(key, vec);
244: } else {
245: Vector vec = (Vector) oldval;
246: vec.addElement(val);
247: }
248: }
249:
250: /**
251: * Return the code corresponding to the request. Each code has
252: * one of the REQUEST_* values from this class.
253: */
254: public int getRequest() {
255: return request;
256: }
257:
258: public String getMethod() {
259: if (request == this .REQUEST_HEAD)
260: return "HEAD";
261: if (request == this .REQUEST_GET)
262: return "GET";
263: if (request == this .REQUEST_POST)
264: return "POST";
265: return "GET";
266: }
267:
268: /**
269: * Return the request URL.
270: */
271: public String getURL() {
272: return url;
273: }
274:
275: public void setURL(String url) {
276: this .url = url;
277: }
278:
279: /**
280: * Return the code corresponding to the HTTP version. Each code has
281: * one of the HTTPVER_* values from this class.
282: */
283: public int getHttpVer() {
284: return httpver;
285: }
286:
287: /**
288: * Return the corresponding HTTP connection.
289: */
290: public httpConnection getConnection() {
291: return conn;
292: }
293:
294: /**
295: * Return the header line corresponding to the given key.
296: * For example, to get the 'User-Agent' field from the header,
297: * use <tt>getHeader("User-Agent")</tt>.
298: */
299: public String getHeader(String key) {
300: if (header == null) {
301: parseHeader();
302: }
303: if (header == null)
304: return null;
305: return (String) header.get(new CaseInsensitiveString(key));
306: }
307:
308: public boolean hasHeader(String key) {
309: if (getHeader(key) != null)
310: return true;
311: else
312: return false;
313: }
314:
315: public void removeHeader(String name) {
316: header.remove(new CaseInsensitiveString(name));
317: }
318:
319: public void setHeader(String name, String val) {
320: if (hasHeader(name)) {
321: removeHeader(name);
322: }
323:
324: header.put(new CaseInsensitiveString(name), val);
325: }
326:
327: /**
328: * Return an enumeration of keys in the query string, if any.
329: */
330: public Enumeration getQueryKeys() {
331: if (query == null)
332: return null;
333: return query.keys();
334: }
335:
336: /**
337: * Return the value associated with the given query key.
338: * If a key as more than one value then only the first value
339: * will be returned.
340: */
341: public String getQuery(String key) {
342: if (query == null)
343: return null;
344: Object val = query.get(key);
345: if (val == null)
346: return null;
347: else if (val instanceof String)
348: return (String) val;
349: else {
350: Vector vec = (Vector) val;
351: return (String) vec.elementAt(0);
352: }
353: }
354:
355: /**
356: * Return the set of values associated with the given query key.
357: */
358: public String[] getQuerySet(String key) {
359: if (query == null)
360: return null;
361: Object val = query.get(key);
362: if (val == null)
363: return null;
364: else if (val instanceof String) {
365: String ret[] = new String[1];
366: ret[0] = (String) val;
367: return ret;
368: } else {
369: Vector vec = (Vector) val;
370: Object ret[] = vec.toArray();
371: String sret[] = new String[ret.length];
372: for (int i = 0; i < ret.length; i++) {
373: sret[i] = (String) ret[i];
374: }
375: return sret;
376: }
377: }
378:
379: /**
380: * Indicates whether this request requires a header to be sent
381: * in the response (that is, whether this is HTTP/1.0 or later).
382: */
383: public boolean headerNeeded() {
384: if (getHttpVer() > httpRequest.HTTPVER_09) {
385: return true;
386: } else {
387: return false;
388: }
389: }
390:
391: // Convert rawHeader to header (key, value) pairs
392: private void parseHeader() {
393: if (rawHeader == null)
394: return;
395: header = new Hashtable(1);
396: for (int i = 0; i < rawHeader.size(); i++) {
397: String h = (String) rawHeader.elementAt(i);
398:
399: int colon = h.indexOf(':');
400:
401: String k = h.substring(0, colon);
402: String v = h.substring(colon + 2);
403: if (DEBUG)
404: System.err.println("httpRequest: key=" + k + ", val="
405: + v);
406: header.put(new CaseInsensitiveString(k), v);
407: }
408: }
409:
410: public String toString() {
411: String s = "httpRequest[";
412: switch (request) {
413: case REQUEST_GET:
414: s += "GET ";
415: break;
416: case REQUEST_POST:
417: s += "POST ";
418: break;
419: default:
420: s += "??? ";
421: break;
422: }
423: s += url + " ";
424: switch (httpver) {
425: case HTTPVER_09:
426: s += "HTTP/0.9";
427: break;
428: case HTTPVER_10:
429: s += "HTTP/1.0";
430: break;
431: case HTTPVER_11:
432: s += "HTTP/1.1";
433: break;
434: }
435:
436: if (header == null)
437: parseHeader();
438: if (header != null) {
439: Enumeration e = header.keys();
440: while (e.hasMoreElements()) {
441: //String key = (String)e.nextElement();
442: String key = "" + e.nextElement();
443: String val = (String) this .getHeader(key);
444: s += "\n\t[" + key + "] [" + val + "]";
445: }
446: }
447: s += "]";
448: return s;
449: }
450:
451: /* For ClassQueueElementIF */
452:
453: public int getRequestClass() {
454: if (user_class == -2) {
455: String s = getHeader("User-Class");
456: if (s != null) {
457: try {
458: user_class = Integer.parseInt(s);
459: } catch (NumberFormatException e) {
460: user_class = -1;
461: }
462: } else {
463: user_class = -1;
464: }
465: }
466: return user_class;
467: }
468:
469: public void setRequestClass(int theclass) {
470: this .user_class = theclass;
471: }
472:
473: public Enumeration enumerateHeaders()
474: {
475: //System.out.println("httpRequest : enumerateHeaders()");
476: Vector vec = new Vector();
477:
478: Enumeration enum = header.keys();
479: while (enum.hasMoreElements())
480: {
481: String k = ""+enum.nextElement();
482: vec.addElement(k);
483: }
484:
485: return vec.elements();
486: }
487:
488: }
|