Source code: org/enhydra/httpServerTest/server/application/presentation/BasicTest.java
1 /*
2 * Enhydra Java Application Server Project
3 *
4 * The contents of this file are subject to the Enhydra Public License
5 * Version 1.1 (the "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy of the License on
7 * the Enhydra web site ( http://www.enhydra.org/ ).
8 *
9 * Software distributed under the License is distributed on an "AS IS"
10 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
11 * the License for the specific terms governing rights and limitations
12 * under the License.
13 *
14 * The Initial Developer of the Enhydra Application Server is Lutris
15 * Technologies, Inc. The Enhydra Application Server and portions created
16 * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
17 * All Rights Reserved.
18 *
19 * Contributor(s):
20 *
21 * $Id: BasicTest.java,v 1.3.2.1.2.1 2000/10/19 17:59:08 jasona Exp $
22 */
23
24 package org.enhydra.httpServerTest.server.application.presentation;
25
26 import org.enhydra.httpServerTest.server.application.BasicTestApp;
27 import org.enhydra.httpServerTest.common.HttpResponseObject;
28 import org.enhydra.httpServerTest.common.HttpServerTestUtils;
29 import org.enhydra.httpServerTest.server.common.FileUploadMangler;
30
31 import com.lutris.appserver.server.httpPresentation.HttpPresentationException;
32
33 import com.lutris.appserver.server.httpPresentation.HttpPresentation;
34 import com.lutris.appserver.server.httpPresentation.HttpPresentationRequest;
35 import com.lutris.appserver.server.httpPresentation.HttpPresentationComms;
36 import com.lutris.appserver.server.httpPresentation.HttpPresentationException;
37 import com.lutris.appserver.server.session.SessionException;
38 import com.lutris.appserver.server.session.SessionManager;
39 import com.lutris.appserver.server.session.Session;
40
41 import HTTPClient.NVPair;
42 import HTTPClient.Codecs;
43
44 import java.util.Random;
45 import java.util.Enumeration;
46 import java.util.StringTokenizer;
47 import java.net.URLEncoder;
48 import java.io.DataInputStream;
49 import java.io.File;
50
51 import javax.servlet.http.Cookie;
52
53 /**
54 * This class is the generic server side test presentation object that client
55 * side test threads talk to. This class respondes to every request comming in
56 * by constructing an HttpResponseObject and returning it in the response as
57 * an XML stream. The response object returned contains information about
58 * all data received via the request. This presentation object properly
59 * handles GET and POST method request and multi-part mime encodings.
60 *
61 * @see org.enhydra.httpServerTest.common.HttpResponseObject
62 *
63 * @author Mike Ward
64 **/
65 public class BasicTest
66 implements HttpPresentation {
67
68 /**
69 * This holds the directory, unique to this request, that any uploaded
70 * files are stored.
71 **/
72 private File fileCacheDir = null;
73
74 /**
75 * The run method for this presentation object. This method runs through
76 * the remainder of the functions held in this class to initialize the
77 * response object based on information pulled from the request. Once
78 * the response object is populated, this method calls toString() on it
79 * and returns the generated XML structure to the client.
80 *
81 * @param comms
82 * The comms objects for this request. All request, response,
83 * and session information are available from this object.
84 **/
85 public void run(HttpPresentationComms comms)
86 throws HttpPresentationException {
87
88 HttpPresentationRequest request = comms.request;
89 Session session = comms.session;
90
91 HttpResponseObject httpResponseObject = new HttpResponseObject();
92 httpResponseObject.HTTP_RESPONSE_OBJECT_DTD =
93 BasicTestApp.httpResponseObjectDTD;
94
95 populateRequestInfo(httpResponseObject, request);
96 if (request.getMethod().equals(HttpServerTestUtils.GET_REQUEST_TAG) ||
97 request.getContentType().equals(HttpServerTestUtils.POST_URL_ENCODED_CONTENT_TYPE)) {
98 // Standard request, url-encoded
99 populateCgiArgsFromGet(httpResponseObject, request);
100 } else {
101 // Multi-part mime request, file uploads expected
102 populateCgiArgsFromPost(httpResponseObject, request);
103 populateFileUploadFromPost(httpResponseObject);
104 }
105 populateCgiParams(httpResponseObject, request);
106 populateHeaders(httpResponseObject, request);
107 populateCookies(httpResponseObject, request);
108 populateSessionInfo(httpResponseObject, comms);
109
110 comms.response.writeHTML(httpResponseObject.toString());
111 }
112
113 /**
114 * This function runs through the HttpPresentationRequest API and
115 * populates the response object with retrieved information. The
116 * request API used here is a sub-set of the HttpPresentationRequest
117 * API that is common with a stardard servlet request object. Each
118 * of the requst method calls are individually wrapped in a try/catch
119 * block so that an error thrown from one will not cause the remaining
120 * calls to be skipped.
121 *
122 * @param httpResponseObject
123 * The response object to populate with request data.
124 * @param request
125 * The HttpPresentationRequest object to pull data from.
126 **/
127 protected void populateRequestInfo(HttpResponseObject httpResponseObject,
128 HttpPresentationRequest request) {
129 try {
130 httpResponseObject.authType = request.getAuthType();
131 } catch (HttpPresentationException e) {
132 }
133
134 try {
135 httpResponseObject.method = request.getMethod();
136 } catch (HttpPresentationException e) {
137 }
138
139 try {
140 httpResponseObject.pathInfo = request.getPathInfo();
141 } catch (HttpPresentationException e) {
142 }
143
144 try {
145 httpResponseObject.pathTranslated = request.getPathTranslated();
146 } catch (Exception e) {
147 }
148
149 try {
150 String queryString = request.getQueryString();
151 if (queryString != null) {
152 httpResponseObject.queryString =
153 URLEncoder.encode(queryString);
154 } else {
155 httpResponseObject.queryString = null;
156 }
157 } catch (HttpPresentationException e) {
158 }
159
160 try {
161 httpResponseObject.remoteUser = request.getRemoteUser();
162 } catch (HttpPresentationException e) {
163 }
164
165 try {
166 httpResponseObject.requestURI = request.getRequestURI();
167 } catch (HttpPresentationException e) {
168 }
169
170 try {
171 httpResponseObject.servletPath = request.getApplicationPath();
172 } catch (HttpPresentationException e) {
173 }
174 }
175
176 /**
177 * This function will populate the response object with all CGI parameters
178 * submitted with the request.
179 *
180 * @param httpResponseObject
181 * The response object to populate with request data.
182 * @param request
183 * The HttpPresentationRequest object to pull data from.
184 **/
185 protected void populateCgiParams(HttpResponseObject httpResponseObject,
186 HttpPresentationRequest request) {
187 StringTokenizer paramTokens = null;
188 try {
189 paramTokens =
190 new StringTokenizer(request.getPathInfo(), ";", false);
191 } catch (HttpPresentationException e) {
192 return;
193 }
194
195 while (paramTokens.hasMoreTokens()) {
196 String currentToken = paramTokens.nextToken();
197 StringTokenizer currentNameValuePair =
198 new StringTokenizer(currentToken, "=", false);
199 if (currentNameValuePair.countTokens() == 2) {
200 String paramName = currentNameValuePair.nextToken();
201 String paramValue = currentNameValuePair.nextToken();
202 httpResponseObject.cgiParams.put(paramName, paramValue);
203 }
204 }
205 }
206
207 /**
208 * This function will populate the response object with all CGI arguments
209 * submitted with the request. This method assumes a stardard GET or
210 * url-encoded POST request (ie. no multi-part mime).
211 *
212 * @param httpResponseObject
213 * The response object to populate with request data.
214 * @param request
215 * The HttpPresentationRequest object to pull data from.
216 **/
217 protected void populateCgiArgsFromGet(HttpResponseObject httpResponseObject,
218 HttpPresentationRequest request) {
219 try {
220 Enumeration cgiArgs = request.getParameterNames();
221 while (cgiArgs.hasMoreElements()) {
222 String cgiArgName = (String)cgiArgs.nextElement();
223 String cgiArgValue =
224 request.getParameter(cgiArgName);
225 httpResponseObject.cgiArgs.put(cgiArgName, cgiArgValue);
226 }
227 } catch (HttpPresentationException e) {
228 }
229 }
230
231 /**
232 * This function will populate the response object with all http headers
233 * submitted with the request. This function will not include any
234 * 'default' headers applied by HTTPConnection.
235 *
236 * @see HTTPClient.HTTPConnection
237 * @see org.enhydra.httpServerTest.common.HttpServerTestUtils
238 *
239 * @param httpResponseObject
240 * The response object to populate with request data.
241 * @param request
242 * The HttpPresentationRequest object to pull data from.
243 **/
244 protected void populateHeaders(HttpResponseObject httpResponseObject,
245 HttpPresentationRequest request) {
246 try {
247 Enumeration headers = request.getHeaderNames();
248 while (headers.hasMoreElements()) {
249 String headerName = (String)headers.nextElement();
250 if (!HttpServerTestUtils.isDefaultHeader(headerName)) {
251 String headerValue =
252 request.getHeader(headerName);
253 httpResponseObject.headers.put(headerName, headerValue);
254 }
255 }
256 } catch (HttpPresentationException e) {
257 }
258
259 }
260
261 /**
262 * This function will populate the response object with all http cookies
263 * submitted with the request.
264 *
265 * @param httpResponseObject
266 * The response object to populate with request data.
267 * @param request
268 * The HttpPresentationRequest object to pull data from.
269 **/
270 protected void populateCookies(HttpResponseObject httpResponseObject,
271 HttpPresentationRequest request) {
272 try {
273 Cookie[] cookies = request.getCookies();
274 for (int i = 0; i < cookies.length; i++) {
275 httpResponseObject.cookies.put(cookies[i].getName(),
276 cookies[i].getValue());
277 }
278 } catch (HttpPresentationException e) {
279 }
280 }
281
282 /**
283 * This function will populate the response object with all CGI arguments
284 * submitted with the request. This method assumes a multi-part mime
285 * encoding from a POST request.
286 *
287 * @see HTTPClient.Codecs
288 *
289 * @param httpResponseObject
290 * The response object to populate with request data.
291 * @param request
292 * The HttpPresentationRequest object to pull data from.
293 **/
294 protected void populateCgiArgsFromPost(HttpResponseObject httpResponseObject,
295 HttpPresentationRequest request) {
296 try {
297 byte[] body = new byte[request.getContentLength()];
298 new DataInputStream(request.getInputStream()).readFully(body);
299 String contentTypeAndBoundary = request.getContentType();
300
301 initFileCacheDir();
302
303 NVPair[] cgiArgs =
304 Codecs.mpFormDataDecode(body,
305 contentTypeAndBoundary,
306 fileCacheDir.toString(),
307 new FileUploadMangler());
308 for (int i = 0; i < cgiArgs.length; i++) {
309 if (!cgiArgs[i].getName().startsWith(HttpServerTestUtils.POST_FILE_UPLOAD_PREFIX)) {
310 httpResponseObject.cgiArgs.put(cgiArgs[i].getName(),
311 cgiArgs[i].getValue());
312 }
313 }
314 } catch (Exception e) {
315 }
316 }
317
318 /**
319 * This function will continually loop until a unique temporary sub-
320 * directory is found. This is accomplished by using the static data
321 * members and methods held in the BasicTestApp class. Once a unique
322 * path is found, the directories are created and the global fileCacheDir
323 * is initialized.
324 **/
325 protected void initFileCacheDir() {
326
327 String fileCacheDirString = BasicTestApp.temporaryBaseDir +
328 BasicTestApp.getFileCacheDirCnt() + File.separator;
329 fileCacheDir = new File(fileCacheDirString);
330
331 while (true) {
332 if (fileCacheDir.exists()) {
333 fileCacheDirString = BasicTestApp.temporaryBaseDir +
334 BasicTestApp.getFileCacheDirCnt() + File.separator;
335 fileCacheDir = new File(fileCacheDirString);
336 } else {
337 break;
338 }
339 }
340 fileCacheDir.mkdirs();
341 }
342
343 /**
344 * This function will loop through all files found under the unique
345 * directory created by initFileCacheDir() and compute an MD5 hashkey
346 * for each individual file. The response object is then populated with
347 * each hashkey.
348 *
349 * @param httpResponseObject
350 * The response object to populate with file upload data.
351 **/
352 protected void populateFileUploadFromPost(HttpResponseObject httpResponseObject) {
353 String[] uploadedFiles = fileCacheDir.list();
354 for (int i = 0; i < uploadedFiles.length; i++) {
355 String currentFile = fileCacheDir.toString() +
356 File.separator + uploadedFiles[i];
357 String hashKey = null;
358 try {
359 hashKey =
360 HttpServerTestUtils.calculateFileHash(currentFile);
361 } catch (Exception e) {
362 }
363 if (!(hashKey == null)) {
364 httpResponseObject.md5HashKey.addElement(hashKey);
365 }
366 }
367 cleanupFiles();
368 }
369
370 /**
371 * This function is called after all file MD5 hashkeys have been computed.
372 * This function will recursively delete all uploaded files and temporary
373 * directories created while caching uploaded files.
374 **/
375 public void cleanupFiles() {
376 String[] uploadedFiles = fileCacheDir.list();
377 for (int i = 0; i < uploadedFiles.length; i++) {
378 String currentFile = fileCacheDir.toString() +
379 File.separator + uploadedFiles[i];
380 File tmpFile = new File(currentFile);
381 tmpFile.delete();
382 }
383 fileCacheDir.delete();
384 }
385
386 /**
387 * This function will populate the response object with all session
388 * information submitted with the request.
389 *
390 * @param httpResponseObject
391 * The response object to populate with request data.
392 * @param request
393 * The HttpPresentationRequest object to pull data from.
394 **/
395 protected void populateSessionInfo(HttpResponseObject httpResponseObject,
396 HttpPresentationComms comms) {
397
398 try {
399 httpResponseObject.sessionIdFromCookie =
400 comms.request.isRequestedSessionIdFromCookie();
401 httpResponseObject.sessionIdFromURL =
402 comms.request.isRequestedSessionIdFromUrl();
403 } catch (HttpPresentationException e) {
404 }
405
406 if (httpResponseObject.sessionIdFromCookie ||
407 httpResponseObject.sessionIdFromURL) {
408 httpResponseObject.sessionIdInvalid = false;
409 httpResponseObject.session = comms.session.getSessionKey();
410 } else {
411 httpResponseObject.sessionIdInvalid = true;
412 httpResponseObject.session = HttpResponseObject.DEFAULT_SESSION;
413 }
414 }
415 }