001: /*
002: * Copyright (c) 2002-2008 Gargoyle Software Inc. All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * 1. Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: * 2. Redistributions in binary form must reproduce the above copyright notice,
010: * this list of conditions and the following disclaimer in the documentation
011: * and/or other materials provided with the distribution.
012: * 3. The end-user documentation included with the redistribution, if any, must
013: * include the following acknowledgment:
014: *
015: * "This product includes software developed by Gargoyle Software Inc.
016: * (http://www.GargoyleSoftware.com/)."
017: *
018: * Alternately, this acknowledgment may appear in the software itself, if
019: * and wherever such third-party acknowledgments normally appear.
020: * 4. The name "Gargoyle Software" must not be used to endorse or promote
021: * products derived from this software without prior written permission.
022: * For written permission, please contact info@GargoyleSoftware.com.
023: * 5. Products derived from this software may not be called "HtmlUnit", nor may
024: * "HtmlUnit" appear in their name, without prior written permission of
025: * Gargoyle Software Inc.
026: *
027: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
028: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
029: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
030: * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
031: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
032: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
033: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
034: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
035: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
036: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
037: */
038: package com.gargoylesoftware.htmlunit;
039:
040: import java.io.ByteArrayInputStream;
041: import java.io.IOException;
042: import java.io.InputStream;
043: import java.io.Serializable;
044: import java.util.Collections;
045: import java.util.Iterator;
046: import java.util.List;
047: import java.util.zip.GZIPInputStream;
048:
049: import org.apache.commons.httpclient.NameValuePair;
050: import org.apache.commons.io.IOUtils;
051: import org.apache.commons.lang.StringUtils;
052:
053: /**
054: * Simple data object to simplify WebResponse creation.
055: *
056: * @version $Revision: 2132 $
057: * @author Brad Clarke
058: * @author Daniel Gredler
059: * @author Ahmed Ashour
060: */
061: public class WebResponseData implements Serializable {
062:
063: private static final long serialVersionUID = 2979956380280496543L;
064:
065: private byte[] body_;
066: private int statusCode_;
067: private String statusMessage_;
068: private List responseHeaders_;
069:
070: /**
071: * Construct with a raw byte[] (mostly for testing)
072: *
073: * @param body Body of this response
074: * @param statusCode Status code from the server
075: * @param statusMessage Status message from the server
076: * @param responseHeaders Headers in this response
077: */
078: public WebResponseData(final byte[] body, final int statusCode,
079: final String statusMessage, final List responseHeaders) {
080:
081: validateHeaders(responseHeaders);
082: statusCode_ = statusCode;
083: statusMessage_ = statusMessage;
084: responseHeaders_ = Collections
085: .unmodifiableList(responseHeaders);
086:
087: try {
088: body_ = getBody(new ByteArrayInputStream(body),
089: responseHeaders);
090: } catch (final IOException e) {
091: body_ = body;
092: }
093: }
094:
095: /**
096: * Construct with a data stream to minimize copying of the entire body.
097: *
098: * @param bodyStream Stream of this response's body
099: * @param statusCode Status code from the server
100: * @param statusMessage Status message from the server
101: * @param responseHeaders Headers in this response
102: *
103: * @throws IOException on stream errors
104: */
105: public WebResponseData(final InputStream bodyStream,
106: final int statusCode, final String statusMessage,
107: final List responseHeaders) throws IOException {
108:
109: validateHeaders(responseHeaders);
110: statusCode_ = statusCode;
111: statusMessage_ = statusMessage;
112: responseHeaders_ = Collections
113: .unmodifiableList(responseHeaders);
114: body_ = getBody(bodyStream, responseHeaders);
115: }
116:
117: /**
118: * Construct without data stream for subclasses that override getBody()
119: *
120: * @param statusCode Status code from the server
121: * @param statusMessage Status message from the server
122: * @param responseHeaders Headers in this response
123: *
124: * @throws IOException on stream errors
125: */
126: protected WebResponseData(final int statusCode,
127: final String statusMessage, final List responseHeaders)
128: throws IOException {
129:
130: validateHeaders(responseHeaders);
131: statusCode_ = statusCode;
132: statusMessage_ = statusMessage;
133: responseHeaders_ = Collections
134: .unmodifiableList(responseHeaders);
135: }
136:
137: /**
138: * Validate that the response header list only contains KeyValuePairs
139: * (Java5 generics will obsolete this method)
140: *
141: * @param responseHeaders Header list to be validated
142: */
143: private void validateHeaders(final List responseHeaders) {
144: final Iterator iterator = responseHeaders.iterator();
145: while (iterator.hasNext()) {
146: final Object object = iterator.next();
147: if (object instanceof NameValuePair == false) {
148: final String name = object.getClass().getName();
149: final String msg = "Only NameValuePairs may be in the response header list, but found a "
150: + name + ".";
151: throw new IllegalArgumentException(msg);
152: }
153: }
154: }
155:
156: /**
157: * Returns the body byte array contained by the specified input stream.
158: * If the response headers indicate that the data has been compressed,
159: * the data stream is handled appropriately. If the specified stream is
160: * <tt>null</tt>, this method returns <tt>null</tt>.
161: * @param stream The input stream which contains the body.
162: * @param headers The response headers.
163: * @return The specified body stream, as a byte array.
164: * @throws IOException If a stream error occurs.
165: */
166: protected byte[] getBody(InputStream stream, final List headers)
167: throws IOException {
168: if (stream == null) {
169: return null;
170: }
171: String encoding = null;
172: for (final Iterator i = headers.iterator(); i.hasNext();) {
173: final NameValuePair header = (NameValuePair) i.next();
174: final String headerName = header.getName().trim();
175: if (headerName.equalsIgnoreCase("content-encoding")) {
176: encoding = header.getValue();
177: break;
178: }
179: }
180: if (encoding != null && StringUtils.contains(encoding, "gzip")) {
181: stream = new GZIPInputStream(stream);
182: }
183: return IOUtils.toByteArray(stream);
184: }
185:
186: /**
187: * Return the response body.
188: * @return response body.
189: */
190: public byte[] getBody() {
191: return body_;
192: }
193:
194: /**
195: *
196: * @return response headers
197: */
198: public List getResponseHeaders() {
199: return responseHeaders_;
200: }
201:
202: /**
203: * @return response status code
204: */
205: public int getStatusCode() {
206: return statusCode_;
207: }
208:
209: /**
210: * @return response status message
211: */
212: public String getStatusMessage() {
213: return statusMessage_;
214: }
215:
216: }
|