001: // Copyright (C) 2003,2004,2005 by Object Mentor, Inc. All rights reserved.
002: // Released under the terms of the GNU General Public License version 2 or later.
003: package fitnesse.http;
004:
005: import java.io.*;
006: import java.util.*;
007: import java.util.regex.Matcher;
008: import java.util.regex.Pattern;
009: import java.net.URLDecoder;
010: import fitnesse.components.Base64;
011: import fitnesse.util.StreamReader;
012:
013: public class Request {
014: private static final Pattern requestLinePattern = Pattern
015: .compile("(\\p{Upper}+?) ([^\\s]+)");
016: private static final Pattern requestUriPattern = Pattern
017: .compile("([^?]+)\\??(.*)");
018: private static final Pattern queryStringPattern = Pattern
019: .compile("([^=]*)=?([^&]*)&?");
020: private static final Pattern headerPattern = Pattern
021: .compile("([^:]*): (.*)");
022: private static final Pattern boundaryPattern = Pattern
023: .compile("boundary=(.*)");
024: private static final Pattern multipartHeaderPattern = Pattern
025: .compile("([^ =]+)=\\\"([^\"]*)\\\"");
026:
027: private static Collection allowedMethods = buildAllowedMethodList();
028:
029: protected StreamReader input;
030: protected String requestURI;
031: protected String resource;
032: protected String queryString;
033: protected HashMap inputs = new HashMap();
034: protected HashMap headers = new HashMap();
035: protected String entityBody = "";
036: protected String requestLine;
037: protected String authorizationUsername;
038: protected String authorizationPassword;
039: private boolean hasBeenParsed;
040: private long bytesParsed = 0;
041:
042: public static Set buildAllowedMethodList() {
043: Set methods = new HashSet(20);
044: methods.add("GET");
045: methods.add("POST");
046: return methods;
047: }
048:
049: protected Request() {
050: }
051:
052: public Request(InputStream input) throws Exception {
053: this .input = new StreamReader(new BufferedInputStream(input));
054: }
055:
056: public void parse() throws Exception {
057: readAndParseRequestLine();
058: headers = parseHeaders(input);
059: parseEntityBody();
060: hasBeenParsed = true;
061: }
062:
063: private void readAndParseRequestLine() throws Exception {
064: requestLine = input.readLine();
065: Matcher match = requestLinePattern.matcher(requestLine);
066: checkRequestLine(match);
067: requestURI = match.group(2);
068: parseRequestUri(requestURI);
069: }
070:
071: private HashMap parseHeaders(StreamReader reader) throws Exception {
072: HashMap headers = new HashMap();
073: String line = reader.readLine();
074: while (!"".equals(line)) {
075: Matcher match = headerPattern.matcher(line);
076: if (match.find()) {
077: String key = match.group(1);
078: String value = match.group(2);
079: headers.put(key.toLowerCase(), value);
080: }
081: line = reader.readLine();
082: }
083: return headers;
084: }
085:
086: private void parseEntityBody() throws Exception {
087: if (hasHeader("Content-Length")) {
088: String contentType = (String) getHeader("Content-Type");
089: if (contentType != null
090: && contentType.startsWith("multipart/form-data")) {
091: Matcher match = boundaryPattern.matcher(contentType);
092: match.find();
093: parseMultiPartContent(match.group(1));
094: } else {
095: entityBody = input.read(getContentLength());
096: parseQueryString(entityBody);
097: }
098: }
099: }
100:
101: public int getContentLength() {
102: return Integer.parseInt((String) getHeader("Content-Length"));
103: }
104:
105: private void parseMultiPartContent(String boundary)
106: throws Exception {
107: boundary = "--" + boundary;
108:
109: int numberOfBytesToRead = getContentLength();
110: accumulateBytesReadAndReset();
111: input.readUpTo(boundary);
112: while (numberOfBytesToRead - input.numberOfBytesConsumed() > 10) {
113: input.readLine();
114: HashMap headers = parseHeaders(input);
115: String contentDisposition = (String) headers
116: .get("content-disposition");
117: Matcher matcher = multipartHeaderPattern
118: .matcher(contentDisposition);
119: while (matcher.find())
120: headers.put(matcher.group(1), matcher.group(2));
121:
122: String name = (String) headers.get("name");
123: Object value;
124: if (headers.containsKey("filename"))
125: value = createUploadedFile(headers, input, boundary);
126: else
127: value = input.readUpTo("\r\n" + boundary);
128:
129: inputs.put(name, value);
130: }
131: }
132:
133: private void accumulateBytesReadAndReset() {
134: bytesParsed += input.numberOfBytesConsumed();
135: input.resetNumberOfBytesConsumed();
136: }
137:
138: private Object createUploadedFile(HashMap headers,
139: StreamReader reader, String boundary) throws Exception {
140: String filename = (String) headers.get("filename");
141: String contentType = (String) headers.get("content-type");
142: File tempFile = File
143: .createTempFile("FitNesse", ".uploadedFile");
144: OutputStream output = new BufferedOutputStream(
145: new FileOutputStream(tempFile));
146: reader.copyBytesUpTo("\r\n" + boundary, output);
147: output.close();
148: return new UploadedFile(filename, contentType, tempFile);
149: }
150:
151: private void checkRequestLine(Matcher match) throws HttpException {
152: if (!match.find())
153: throw new HttpException(
154: "The request string is malformed and can not be parsed");
155: if (!allowedMethods.contains(match.group(1)))
156: throw new HttpException("The " + match.group(1)
157: + " method is not currently supported");
158: }
159:
160: public void parseRequestUri(String requestUri) {
161: Matcher match = requestUriPattern.matcher(requestUri);
162: match.find();
163: resource = stripLeadingSlash(match.group(1));
164: queryString = match.group(2);
165: parseQueryString(queryString);
166: }
167:
168: protected void parseQueryString(String queryString) {
169: Matcher match = queryStringPattern.matcher(queryString);
170: while (match.find()) {
171: String key = match.group(1);
172: String value = decodeContent(match.group(2));
173: inputs.put(key, value);
174: }
175: }
176:
177: public String getRequestLine() {
178: return requestLine;
179: }
180:
181: public String getRequestUri() {
182: return requestURI;
183: }
184:
185: public String getResource() {
186: return resource;
187: }
188:
189: public String getQueryString() {
190: return queryString;
191: }
192:
193: public boolean hasInput(String key) {
194: return inputs.containsKey(key);
195: }
196:
197: public Object getInput(String key) {
198: return inputs.get(key);
199: }
200:
201: public boolean hasHeader(String key) {
202: return headers.containsKey(key.toLowerCase());
203: }
204:
205: public Object getHeader(String key) {
206: return headers.get(key.toLowerCase());
207: }
208:
209: public String getBody() {
210: return entityBody;
211: }
212:
213: private String stripLeadingSlash(String url) {
214: return url.substring(1);
215: }
216:
217: public String toString() {
218: StringBuffer buffer = new StringBuffer();
219: buffer.append("--- Request Start ---").append("\n");
220: buffer.append("Request URI: ").append(requestURI).append("\n");
221: buffer.append("Resource: ").append(resource).append("\n");
222: buffer.append("Query String: ").append(queryString)
223: .append("\n");
224: buffer.append("Hearders: (" + headers.size() + ")\n");
225: addMap(headers, buffer);
226: buffer.append("Form Inputs: (" + inputs.size() + ")\n");
227: addMap(inputs, buffer);
228: buffer.append("Entity Body: ").append("\n");
229: buffer.append(entityBody).append("\n");
230: buffer.append("--- End Request ---\n");
231:
232: return buffer.toString();
233: }
234:
235: private void addMap(HashMap map, StringBuffer buffer) {
236: if (map.size() == 0) {
237: buffer.append("\tempty");
238: }
239: for (Iterator iterator = map.keySet().iterator(); iterator
240: .hasNext();) {
241: String key = (String) iterator.next();
242: String value = map.get(key) != null ? escape(map.get(key)
243: .toString()) : null;
244: buffer.append("\t" + escape(key) + " \t-->\t " + value
245: + "\n");
246: }
247: }
248:
249: private String escape(String foo) {
250: return foo.replaceAll("[\n\r]+", "|");
251: }
252:
253: public static String decodeContent(String content) {
254: String escapedContent = null;
255: try {
256: escapedContent = URLDecoder.decode(content, "UTF-8");
257: } catch (UnsupportedEncodingException e) {
258: escapedContent = "URLDecoder Error";
259: }
260: return escapedContent;
261: }
262:
263: public boolean hasBeenParsed() {
264: return hasBeenParsed;
265: }
266:
267: public String getUserpass(String headerValue) throws Exception {
268: String encodedUserpass = headerValue.substring(6);
269: return Base64.decode(encodedUserpass);
270: }
271:
272: public void getCredentials() throws Exception {
273: if (hasHeader("Authorization")) {
274: String authHeader = getHeader("Authorization").toString();
275: String userpass = getUserpass(authHeader);
276: String[] values = userpass.split(":");
277: if (values.length == 2) {
278: authorizationUsername = values[0];
279: authorizationPassword = values[1];
280: }
281: }
282: }
283:
284: public String getAuthorizationUsername() {
285: return authorizationUsername;
286: }
287:
288: public String getAuthorizationPassword() {
289: return authorizationPassword;
290: }
291:
292: public long numberOfBytesParsed() {
293: return bytesParsed + input.numberOfBytesConsumed();
294: }
295: }
|