001: /*
002: * ========================================================================
003: *
004: * Copyright 2001-2004 The Apache Software Foundation.
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: *
018: * ========================================================================
019: */
020: package org.apache.cactus;
021:
022: import java.io.BufferedReader;
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.io.StringReader;
026:
027: import java.net.HttpURLConnection;
028:
029: import java.util.Vector;
030:
031: import org.apache.cactus.internal.util.CookieUtil;
032: import org.apache.cactus.internal.util.IoUtil;
033: import org.apache.cactus.util.ChainedRuntimeException;
034: import org.apache.commons.httpclient.Header;
035: import org.apache.commons.httpclient.HttpException;
036: import org.apache.commons.httpclient.cookie.CookiePolicy;
037: import org.apache.commons.httpclient.cookie.CookieSpec;
038: import org.apache.commons.logging.Log;
039: import org.apache.commons.logging.LogFactory;
040:
041: /**
042: * Default web response implementation that provides a minimal
043: * API for asserting returned output stream from the server side. For more
044: * complex assertions, use an <code>com.meterware.httpunit.WebResponse</code>
045: * instead as parameter of your <code>endXXX()</code> methods.
046: *
047: * @version $Id: WebResponse.java 238991 2004-05-22 11:34:50Z vmassol $
048: */
049: public class WebResponse {
050: /**
051: * The logger
052: */
053: private static final Log LOGGER = LogFactory
054: .getLog(WebResponse.class);
055:
056: /**
057: * The connection object that was used to call the URL
058: */
059: private HttpURLConnection connection;
060:
061: /**
062: * The request data that were used to open the connection to the server.
063: */
064: private WebRequest request;
065:
066: /**
067: * Save the response content for repeatable reads.
068: */
069: private String content;
070:
071: /**
072: * @param theRequest the request data that were used to open the
073: * connection to the server.
074: * @param theConnection the original <code>HttpURLConnection</code> used
075: * to call the URL
076: */
077: public WebResponse(WebRequest theRequest,
078: HttpURLConnection theConnection) {
079: this .request = theRequest;
080: this .connection = theConnection;
081: }
082:
083: /**
084: * @return the original <code>HttpURLConnection</code> used to call the
085: * URL
086: */
087: public HttpURLConnection getConnection() {
088: return this .connection;
089: }
090:
091: /**
092: * @return the request data the were used to open the connection to the
093: * server
094: */
095: public WebRequest getWebRequest() {
096: return this .request;
097: }
098:
099: /**
100: * @return the text of the response (excluding headers) as a string.
101: */
102: public String getText() {
103: // Get the text from the save content if content has already been
104: // read.
105: if (this .content == null) {
106: try {
107: this .content = IoUtil.getText(this .connection
108: .getInputStream());
109: } catch (IOException e) {
110: throw new ChainedRuntimeException(e);
111: }
112: }
113:
114: return this .content;
115: }
116:
117: /**
118: * @return the text of the response (excluding headers) as an array of
119: * strings (each string is a separate line from the output stream).
120: */
121: public String[] getTextAsArray() {
122: Vector lines = new Vector();
123:
124: try {
125: // Read content first
126: if (this .content == null) {
127: getText();
128: }
129:
130: BufferedReader input = new BufferedReader(new StringReader(
131: this .content));
132: String str;
133:
134: while (null != (str = input.readLine())) {
135: lines.addElement(str);
136: }
137:
138: input.close();
139: } catch (IOException e) {
140: throw new ChainedRuntimeException(e);
141: }
142:
143: // Dummy variable to explicitely tell the object type to copy.
144: String[] dummy = new String[lines.size()];
145:
146: return (String[]) (lines.toArray(dummy));
147: }
148:
149: /**
150: * @return a buffered input stream for reading the response data.
151: **/
152: public InputStream getInputStream() {
153: try {
154: return this .connection.getInputStream();
155: } catch (IOException e) {
156: throw new ChainedRuntimeException(e);
157: }
158: }
159:
160: /**
161: * Return the first cookie found that has the specified name or null
162: * if not found.
163: *
164: * @param theName the cookie name to find
165: * @return the cookie or null if not found
166: */
167: public Cookie getCookie(String theName) {
168: Cookie result = null;
169:
170: Cookie[] cookies = getCookies();
171:
172: for (int i = 0; i < cookies.length; i++) {
173: if (cookies[i].getName().equals(theName)) {
174: result = cookies[i];
175:
176: break;
177: }
178: }
179:
180: return result;
181: }
182:
183: /**
184: * Return the first cookie found that has the specified name or null
185: * if not found. The name is case-insensitive.
186: *
187: * @param theName the cookie name to find (case-insensitive)
188: * @return the cookie or null if not found
189: * @since 1.5
190: */
191: public Cookie getCookieIgnoreCase(String theName) {
192: Cookie result = null;
193:
194: Cookie[] cookies = getCookies();
195:
196: for (int i = 0; i < cookies.length; i++) {
197: if (cookies[i].getName().equalsIgnoreCase(theName)) {
198: result = cookies[i];
199:
200: break;
201: }
202: }
203:
204: return result;
205: }
206:
207: /**
208: * @return the cookies returned by the server
209: */
210: public Cookie[] getCookies() {
211: Cookie[] returnCookies = null;
212:
213: // There can be several headers named "Set-Cookie", so loop through
214: // all the headers, looking for cookies
215: String headerName = this .connection.getHeaderFieldKey(0);
216: String headerValue = this .connection.getHeaderField(0);
217:
218: Vector cookieVector = new Vector();
219: CookieSpec cookieSpec = CookiePolicy.getDefaultSpec();
220:
221: for (int i = 1; (headerName != null) || (headerValue != null); i++) {
222: LOGGER.debug("Header name = [" + headerName + "]");
223: LOGGER.debug("Header value = [" + headerValue + "]");
224:
225: if ((headerName != null)
226: && (headerName.toLowerCase().equals("set-cookie") || headerName
227: .toLowerCase().equals("set-cookie2"))) {
228: // Parse the cookie definition
229: org.apache.commons.httpclient.Cookie[] cookies;
230: try {
231: cookies = cookieSpec
232: .parse(CookieUtil.getCookieDomain(
233: getWebRequest(), getConnection()
234: .getURL().getHost()),
235: CookieUtil.getCookiePort(
236: getWebRequest(),
237: getConnection().getURL()
238: .getPort()),
239: CookieUtil.getCookiePath(
240: getWebRequest(),
241: getConnection().getURL()
242: .getFile()), false,
243: new Header(headerName, headerValue));
244: } catch (HttpException e) {
245: throw new ChainedRuntimeException(
246: "Error parsing cookies", e);
247: }
248:
249: // Transform the HttpClient cookies into Cactus cookies and
250: // add them to the cookieVector vector
251: for (int j = 0; j < cookies.length; j++) {
252: Cookie cookie = new Cookie(cookies[j].getDomain(),
253: cookies[j].getName(), cookies[j].getValue());
254:
255: cookie.setComment(cookies[j].getComment());
256: cookie.setExpiryDate(cookies[j].getExpiryDate());
257: cookie.setPath(cookies[j].getPath());
258: cookie.setSecure(cookies[j].getSecure());
259:
260: cookieVector.addElement(cookie);
261: }
262: }
263:
264: headerName = this .connection.getHeaderFieldKey(i);
265: headerValue = this .connection.getHeaderField(i);
266: }
267:
268: returnCookies = new Cookie[cookieVector.size()];
269: cookieVector.copyInto(returnCookies);
270:
271: return returnCookies;
272: }
273:
274: /**
275: * Returns the status code returned by the server.
276: *
277: * @return The status code
278: * @since 1.5
279: */
280: public int getStatusCode() {
281: try {
282: return this .connection.getResponseCode();
283: } catch (IOException e) {
284: throw new ChainedRuntimeException(e);
285: }
286: }
287:
288: }
|