001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.openejb.webadmin.httpd;
017:
018: import java.io.ByteArrayInputStream;
019: import java.io.ByteArrayOutputStream;
020: import java.io.DataInput;
021: import java.io.DataInputStream;
022: import java.io.File;
023: import java.io.FileOutputStream;
024: import java.io.IOException;
025: import java.io.InputStream;
026: import java.net.URL;
027: import java.net.URLDecoder;
028: import java.rmi.RemoteException;
029: import java.util.HashMap;
030: import java.util.Hashtable;
031: import java.util.Iterator;
032: import java.util.Map;
033: import java.util.StringTokenizer;
034:
035: import javax.naming.InitialContext;
036: import javax.naming.NamingException;
037:
038: import org.apache.commons.fileupload.MultipartStream;
039: import org.apache.openejb.webadmin.HttpRequest;
040: import org.apache.openejb.webadmin.HttpSession;
041: import org.apache.openejb.core.stateful.StatefulEjbObjectHandler;
042: import org.apache.openejb.loader.FileUtils;
043:
044: /** A class to take care of HTTP Requests. It parses headers, content, form and url
045: * parameters.
046: * @author <a href="mailto:david.blevins@visi.com">David Blevins</a>
047: * @author <a href="mailto:tim_urberg@yahoo.com">Tim Urberg</a>
048: */
049: public class HttpRequestImpl implements HttpRequest {
050: public static final String FORM_URL_ENCODED = "application/x-www-form-urlencoded";
051: public static final String MULITPART_FORM_DATA = "multipart/form-data";
052: public static final String FILENAME = "filename";
053: public static final String NAME = "name";
054:
055: /** 5.1 Request-Line */
056: private String line;
057: /** 5.1.1 Method */
058: private int method;
059: /** 5.1.2 Request-URI */
060: private URL uri;
061: /** the headers for this page */
062: private HashMap headers;
063: /** the form parameters for this page */
064: private HashMap formParams = new HashMap();
065: /** the URL (or query) parameters for this page */
066: private HashMap queryParams = new HashMap();
067: /** the content of the body of this page */
068: private byte[] body;
069: private String[][] formParamsArray;
070:
071: private String methodString;
072: private String pathString;
073:
074: /**
075: * @return Returns the methodString.
076: */
077: public String getMethodString() {
078: return methodString;
079: }
080:
081: /**
082: * @return Returns the pathString.
083: */
084: public String getPathString() {
085: return pathString;
086: }
087:
088: /** Gets a header based the header name passed in.
089: * @param name The name of the header to get
090: * @return The value of the header
091: */
092: public String getHeader(String name) {
093: return (String) headers.get(name);
094: }
095:
096: /** Gets a form parameter based on the name passed in.
097: * @param name The name of the form parameter to get
098: * @return The value of the parameter
099: */
100: public String getFormParameter(String name) {
101: return (String) formParams.get(name);
102: }
103:
104: /** Gets all the form parameters in the form of a two-dimentional array
105: * The second dimention has two indexes which contain the key and value
106: * for example:
107: * <code>
108: * for(int i=0; i<formParams.length; i++) {
109: * key = formParams[i][0];
110: * value = formParams[i][1];
111: * }
112: * </code>
113: *
114: * All values are strings
115: * @return All the form parameters
116: */
117: public String[][] getFormParameters() {
118: Iterator keys = formParams.keySet().iterator();
119: String[][] returnValue = new String[formParams.size()][2];
120:
121: String temp;
122: int i = 0;
123: while (keys.hasNext()) {
124: temp = (String) keys.next();
125: returnValue[i][0] = temp;
126: returnValue[i++][1] = (String) formParams.get(temp);
127: }
128:
129: return returnValue;
130: }
131:
132: /** Gets a URL (or query) parameter based on the name passed in.
133: * @param name The name of the URL (or query) parameter
134: * @return The value of the URL (or query) parameter
135: */
136: public String getQueryParameter(String name) {
137: return (String) queryParams.get(name);
138: }
139:
140: /** Gets an integer value of the request method. These values are:
141: *
142: * OPTIONS = 0
143: * GET = 1
144: * HEAD = 2
145: * POST = 3
146: * PUT = 4
147: * DELETE = 5
148: * TRACE = 6
149: * CONNECT = 7
150: * UNSUPPORTED = 8
151: * @return The integer value of the method
152: */
153: public int getMethod() {
154: return method;
155: }
156:
157: /** Gets the URI for the current URL page.
158: * @return The URI
159: */
160: public URL getURI() {
161: return uri;
162: }
163:
164: /*------------------------------------------------------------*/
165: /* Methods for reading in and parsing a request */
166: /*------------------------------------------------------------*/
167: /** parses the request into the 3 different parts, request, headers, and body
168: * @param input the data input for this page
169: * @throws IOException if an exception is thrown
170: */
171: protected void readMessage(InputStream input) throws IOException {
172: DataInput in = new DataInputStream(input);
173:
174: readRequestLine(in);
175: readHeaders(in);
176: readBody(in);
177: }
178:
179: private String requestLine;
180:
181: protected String getRequestLine() {
182: return requestLine;
183: }
184:
185: /** reads and parses the request line
186: * @param in the input to be read
187: * @throws IOException if an exception is thrown
188: */
189: private void readRequestLine(DataInput in) throws IOException {
190:
191: try {
192: line = in.readLine();
193: requestLine = line;
194: // System.out.println(line);
195: } catch (Exception e) {
196: throw new IOException(
197: "Could not read the HTTP Request Line :"
198: + e.getClass().getName() + " : "
199: + e.getMessage());
200: }
201:
202: StringTokenizer lineParts = new StringTokenizer(line, " ");
203: /* [1] Parse the method */
204: parseMethod(lineParts);
205: /* [2] Parse the URI */
206: parseURI(lineParts);
207: }
208:
209: /** parses the method for this page
210: * @param lineParts a StringTokenizer of the request line
211: * @throws IOException if an exeption is thrown
212: */
213: private void parseMethod(StringTokenizer lineParts)
214: throws IOException {
215: String token = null;
216: try {
217: token = lineParts.nextToken();
218: } catch (Exception e) {
219: throw new IOException(
220: "Could not parse the HTTP Request Method :"
221: + e.getClass().getName() + " : "
222: + e.getMessage());
223: }
224:
225: if (token.equalsIgnoreCase("GET")) {
226: method = GET;
227: } else if (token.equalsIgnoreCase("POST")) {
228: method = POST;
229: } else {
230: method = UNSUPPORTED;
231: throw new IOException("Unsupported HTTP Request Method :"
232: + token);
233: }
234: }
235:
236: /** parses the URI into the different parts
237: * @param lineParts a StringTokenizer of the URI
238: * @throws IOException if an exeption is thrown
239: */
240: private void parseURI(StringTokenizer lineParts) throws IOException {
241: String token = null;
242: try {
243: token = lineParts.nextToken();
244: } catch (Exception e) {
245: throw new IOException(
246: "Could not parse the HTTP Request Method :"
247: + e.getClass().getName() + " : "
248: + e.getMessage());
249: }
250:
251: try {
252: uri = new URL("http", "localhost", token);
253: } catch (java.net.MalformedURLException e) {
254: throw new IOException("Malformed URL :" + token
255: + " Exception: " + e.getMessage());
256: }
257:
258: parseQueryParams(uri.getQuery());
259: }
260:
261: /** parses the URL (or query) parameters
262: * @param query the URL (or query) parameters to be parsed
263: * @throws IOException if an exception is thrown
264: */
265: private void parseQueryParams(String query) throws IOException {
266: if (query == null)
267: return;
268: StringTokenizer parameters = new StringTokenizer(query, "&");
269:
270: while (parameters.hasMoreTokens()) {
271: StringTokenizer param = new StringTokenizer(parameters
272: .nextToken(), "=");
273:
274: /* [1] Parse the Name */
275: if (!param.hasMoreTokens())
276: continue;
277: String name = URLDecoder.decode(param.nextToken());
278: if (name == null)
279: continue;
280:
281: /* [2] Parse the Value */
282: if (!param.hasMoreTokens())
283: continue;
284: String value = URLDecoder.decode(param.nextToken());
285: if (value == null)
286: continue;
287:
288: //System.out.println("[] "+name+" = "+value);
289: queryParams.put(name, value);
290: }
291: }
292:
293: /** reads the headers from the data input sent from the browser
294: * @param in the data input sent from the browser
295: * @throws IOException if an exeption is thrown
296: */
297: private void readHeaders(DataInput in) throws IOException {
298: // System.out.println("\nREQUEST");
299: headers = new HashMap();
300: while (true) {
301: // Header Field
302: String hf = null;
303:
304: try {
305: hf = in.readLine();
306: //System.out.println(hf);
307: } catch (Exception e) {
308: throw new IOException(
309: "Could not read the HTTP Request Header Field :"
310: + e.getClass().getName() + " : "
311: + e.getMessage());
312: }
313:
314: if (hf == null || hf.equals("")) {
315: break;
316: }
317:
318: /* [1] parse the name */
319: int colonIndex = hf.indexOf((int) ':');
320: String name = hf.substring(0, colonIndex);
321: if (name == null)
322: break;
323:
324: /* [2] Parse the Value */
325: String value = hf.substring(colonIndex + 1, hf.length());
326: if (value == null)
327: break;
328: value = value.trim();
329: headers.put(name, value);
330: }
331:
332: //temp-debug-------------------------------------------
333: //java.util.Iterator myKeys = headers.keySet().iterator();
334: //String temp = null;
335: //while(myKeys.hasNext()) {
336: // temp = (String)myKeys.next();
337: // System.out.println("Test: " + temp + "=" + headers.get(temp));
338: //}
339: //end temp-debug---------------------------------------
340: }
341:
342: /** reads the body from the data input passed in
343: * @param in the data input with the body of the page
344: * @throws IOException if an exception is thrown
345: */
346: private void readBody(DataInput in) throws IOException {
347: readRequestBody(in);
348: //System.out.println("Body Length: " + body.length);
349: // Content-type: application/x-www-form-urlencoded
350: // or multipart/form-data
351: String type = getHeader(HttpRequest.HEADER_CONTENT_TYPE);
352: if (FORM_URL_ENCODED.equals(type)) {
353: parseFormParams();
354: } else if (type != null && type.startsWith(MULITPART_FORM_DATA)) {
355: parseMultiPartFormParams();
356: }
357: }
358:
359: /** reads the request line of the data input
360: * @param in the data input that contains the request line
361: * @throws IOException if an exception is thrown
362: */
363: private void readRequestBody(DataInput in) throws IOException {
364: // Content-length: 384
365: String len = getHeader(HttpRequest.HEADER_CONTENT_LENGTH);
366: //System.out.println("readRequestBody Content-Length: " + len);
367:
368: int length = -1;
369: if (len != null) {
370: try {
371: length = Integer.parseInt(len);
372: } catch (Exception e) {
373: //don't care
374: }
375: }
376:
377: if (length < 1) {
378: this .body = new byte[0];
379: } else if (length > 0) {
380: this .body = new byte[length];
381:
382: try {
383: in.readFully(body);
384: } catch (Exception e) {
385: throw new IOException(
386: "Could not read the HTTP Request Body :"
387: + e.getClass().getName() + " : "
388: + e.getMessage());
389: }
390: }
391: }
392:
393: /** parses form parameters into the formParams variable
394: * @throws IOException if an exeption is thrown
395: */
396: private void parseFormParams() throws IOException {
397: String rawParams = new String(body);
398: //System.out.println("rawParams: " + rawParams);
399: StringTokenizer parameters = new StringTokenizer(rawParams, "&");
400: String name = null;
401: String value = null;
402:
403: while (parameters.hasMoreTokens()) {
404: StringTokenizer param = new StringTokenizer(parameters
405: .nextToken(), "=");
406:
407: /* [1] Parse the Name */
408: name = URLDecoder.decode(param.nextToken());
409: if (name == null)
410: break;
411:
412: /* [2] Parse the Value */
413: if (param.hasMoreTokens()) {
414: value = URLDecoder.decode(param.nextToken());
415: } else {
416: value = ""; //if there is no token set value to blank string
417: }
418:
419: if (value == null)
420: value = "";
421:
422: formParams.put(name, value);
423: //System.out.println(name + ": " + value);
424: }
425: }
426:
427: /**
428: * A method which parses form parameters that are multipart/form-data
429: * according to <a href="http://www.ietf.org/rfc/rfc1867.txt" target="_blank">
430: * RFC 1867</a>. Currently multipart/mixed is not implemented.
431: */
432: private void parseMultiPartFormParams() throws IOException {
433: /* see http://www.ietf.org/rfc/rfc1867.txt */
434: ByteArrayOutputStream output;
435: StringBuffer multiPartBuffer;
436: int j;
437: Map headerMap;
438: boolean isFile;
439: String fileName = null;
440: byte[] outputArray;
441: FileOutputStream fos;
442:
443: String contentType = getHeader(HttpRequest.HEADER_CONTENT_TYPE);
444: int boundaryIndex = contentType.indexOf("boundary=");
445: if (boundaryIndex < 0) {
446: throw new IOException(
447: "the request was rejected because no multipart boundary was found");
448: }
449: byte[] boundary = contentType.substring(boundaryIndex + 9)
450: .getBytes();
451:
452: ByteArrayInputStream input = new ByteArrayInputStream(body);
453: MultipartStream multi = new MultipartStream(input, boundary);
454:
455: boolean nextPart = multi.skipPreamble();
456: while (nextPart) {
457: try {
458: output = new ByteArrayOutputStream();
459: multi.readBodyData(output);
460: outputArray = output.toByteArray();
461: multiPartBuffer = new StringBuffer(50);
462: isFile = false;
463: File jarFileInTempDir;
464: j = 0;
465:
466: for (int i = 0; i < outputArray.length; i++) {
467: //first check for \r\n end of line
468: if (outputArray[i] == 13
469: && outputArray[i + 1] == 10) {
470: //we've come to the end of a line
471: headerMap = parseMultiPartHeader(multiPartBuffer);
472: if (headerMap.get(NAME) != null) {
473: fileName = (String) headerMap.get(NAME);
474: }
475:
476: //add the filename if there is one
477: if (fileName != null
478: && headerMap.get(FILENAME) != null) {
479: this .formParams.put(fileName, headerMap
480: .get(FILENAME));
481: isFile = true;
482: }
483:
484: if (outputArray[i + 2] == 13
485: && outputArray[i + 3] == 10) {
486: //we've reached the blank line
487: i += 4;
488: j = i;
489: break;
490: } else {
491: i++;
492: }
493:
494: multiPartBuffer = new StringBuffer(50);
495: } else {
496: multiPartBuffer.append((char) outputArray[i]);
497: }
498: }
499:
500: //here we know that we have a file and that we need to write it
501: if (isFile) {
502: //create file
503: jarFileInTempDir = new File(
504: (String) this .formParams.get(fileName));
505: if (!jarFileInTempDir.exists()) {
506: jarFileInTempDir.createNewFile();
507: }
508:
509: //write the byte array to the file
510: fos = new FileOutputStream(jarFileInTempDir);
511: fos.write(outputArray, j, outputArray.length - j);
512: fos.close();
513: } else { //form data, not a file
514: multiPartBuffer = new StringBuffer(
515: outputArray.length - j);
516: for (int i = j; i < outputArray.length; i++) {
517: multiPartBuffer.append((char) outputArray[i]);
518: }
519:
520: this .formParams.put(fileName, multiPartBuffer
521: .toString());
522: }
523:
524: nextPart = multi.readBoundary();
525: } catch (MultipartStream.MalformedStreamException mse) {
526: throw new IOException(mse.getMessage());
527: }
528: }
529: }
530:
531: /**
532: * Parses the first one or two lines of a multipart. The usual headers are
533: * Content-Dispostion or Content-Type.
534: *
535: * @param headerBuffer - the header string to be parsed
536: * @return a map of of header info and their values
537: */
538: private Map parseMultiPartHeader(StringBuffer headerBuffer)
539: throws IOException {
540: Map headerMap = new HashMap();
541: int colonIndex = headerBuffer.toString().indexOf(":");
542: String headerName = headerBuffer.substring(0, colonIndex);
543: StringTokenizer headerValueToken = new StringTokenizer(
544: headerBuffer.substring(colonIndex + 1, headerBuffer
545: .length()), ";");
546:
547: String currentToken;
548: //loop through the tokens of semi-colon
549: while (headerValueToken.hasMoreTokens()) {
550: currentToken = headerValueToken.nextToken();
551: if (currentToken.indexOf("=") > -1) {
552: headerMap.put(currentToken.substring(0,
553: currentToken.indexOf("=")).trim(), currentToken
554: .substring(currentToken.indexOf("=") + 2,
555: currentToken.length() - 1).trim());
556: } else {
557: headerMap.put(headerName, currentToken.trim());
558: }
559: }
560:
561: //first get rid of any path that might already be there then
562: //change the path of the file name to a temp directory
563: String fileName = (String) headerMap.get(FILENAME);
564: if (fileName != null) {
565: StringBuffer temp;
566: if (fileName.indexOf("\\") > -1) {
567: temp = new StringBuffer(fileName).reverse();
568: fileName = temp.delete(temp.toString().indexOf("\\"),
569: temp.length()).reverse().toString();
570: }
571:
572: temp = new StringBuffer();
573: temp.append(FileUtils.createTempDirectory()
574: .getAbsolutePath());
575: temp.append(System.getProperty("file.separator"));
576: temp.append(fileName);
577: headerMap.put(FILENAME, temp.toString());
578: }
579:
580: return headerMap;
581: }
582:
583: private HashMap cookies;
584:
585: protected HashMap getCookies() {
586: if (cookies != null)
587: return cookies;
588:
589: cookies = new HashMap();
590:
591: String cookieHeader = getHeader(HEADER_COOKIE);
592: if (cookieHeader == null)
593: return cookies;
594:
595: StringTokenizer tokens = new StringTokenizer(cookieHeader, ";");
596: while (tokens.hasMoreTokens()) {
597: StringTokenizer token = new StringTokenizer(tokens
598: .nextToken(), "=");
599: String name = token.nextToken();
600: String value = token.nextToken();
601: cookies.put(name, value);
602: }
603: return cookies;
604: }
605:
606: protected static final String EJBSESSIONID = "EJBSESSIONID";
607:
608: protected String getCookie(String name) {
609: return (String) getCookies().get(name);
610: }
611:
612: public HttpSession getSession() {
613: return getSession(true);
614: }
615:
616: private WebSession session;
617:
618: public HttpSession getSession(boolean create) {
619: if (session != null)
620: return session;
621:
622: String id = getCookie(EJBSESSIONID);
623:
624: if (id != null) {
625: session = (WebSession) sessions.get(id);
626: }
627:
628: if (session == null && create) {
629: session = createSession();
630: sessions.put(session.getId(), session);
631: }
632: return session;
633: }
634:
635: private static final Hashtable sessions = new Hashtable();
636:
637: private WebSession createSession() {
638: // Lookup/create sessions
639: WebSessionHome home = null;
640:
641: try {
642: home = (WebSessionHome) new InitialContext()
643: .lookup("java:openejb/ejb/httpd/session");
644: } catch (NamingException e) {
645: // TODO Auto-generated catch block
646: throw new IllegalStateException(
647: "The WebSessionBean has not been deployed. "
648: + " This is required for the HTTPd service to provide HttpSession support. "
649: + e.getClass().getName() + ": "
650: + e.getMessage());
651: }
652:
653: WebSession session = null;
654: try {
655: session = home.create();
656: } catch (RemoteException e) {
657: // TODO Auto-generated catch block
658: e.printStackTrace();
659: }
660: // mark them as nocopy
661: Object obj = org.apache.openejb.util.proxy.ProxyManager
662: .getInvocationHandler(session);
663: StatefulEjbObjectHandler handler = (StatefulEjbObjectHandler) obj;
664: handler.setIntraVmCopyMode(false);
665: return session;
666: }
667: }
|