/*
GNU GENERAL LICENSE
Copyright (C) 2006 The Lobo Project. Copyright (C) 2014 - 2017 Lobo Evolution
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
verion 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General License for more details.
You should have received a copy of the GNU General Public
along with this program. If not, see <http://www.gnu.org/licenses/>.
Contact info: lobochief@users.sourceforge.net; ivan.difrancesco@yahoo.it
*/
package org.lobobrowser.http;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* <p>
* Represents a Response from an http {@link Request}. Instances of this class
* are created as a result of executing a <code>Request</code> in a
* {@link Session}. They are not created manually.
* </p>
*
* <p>
* <code>Response</code>s can access all of the http {@link Header}s that were
* returned with the response, as well as the status code, status message, and
* response body. In addition, the url that this response originated from is
* available. This is useful when constructing further requests based on
* relative url paths.
* </p>
*
* <p>
* As with <code>Request</code>, the body of the <code>Response</code> is
* treated specially. Since the response may be very large (like, say,
* downloading an operating system) it is critical that the responses not be
* cached. As a result, once the response body has been read, any futher calls
* to getResponseBody <em>may</em> result in an exception.
* </p>
*
* @author rbair
*/
public class Response {
/** The Constant logger. */
private static final Logger logger = LogManager.getLogger(Response.class);
/**
* The encoding for the body to be used when constructing a String from the
* bytes. This defaults to UTF-8, but can be set by specifying the encoding
* on a Request.
*/
private String charset;
/** The headers. */
private Set<Header> headers;
/** The status code. */
private StatusCode statusCode;
/** The status text. */
private String statusText;
// TODO I'm still dealing with this as a String. I originally used an
// InputStream,
// but ran into problems because HttpClient was automatically closing that
// stream
// on me. Not sure how to do this, skipping it for now. Most likely, a
// future
// implementation will cache to disk. This requires security priviledges,
// however,
/** The response body. */
// and thus is somewhat problematic.
private byte[] responseBody;
/** The url. */
private String url;
/**
* Creates a new instance of Response. Response is an immutable object,
* hence this large constructor.
*/
Response(StatusCode statusCode, String statusText, byte[] responseBody,
String charset, Set<Header> headers, String baseUrl) {
if (statusCode == null) {
throw new NullPointerException("statusCode cannot be null");
}
if (responseBody == null) {
responseBody = new byte[0];
}
if (baseUrl != null) {
// TODO assure that there are no parameters in this URL. If there
// are,
// remove them
}
this.statusCode = statusCode;
this.statusText = statusText;
this.responseBody = responseBody;
this.charset = charset == null ? "ISO-8859-1" : charset;
this.url = baseUrl;
this.headers = new HashSet<Header>();
if (headers != null) {
for (Header h : headers) {
if (h == null) {
throw new IllegalArgumentException(
"There was a null header in the results.");
}
this.headers.add(h);
}
}
}
/**
* Returns the Header with the given name, or null if there is no such
* header. Comparisons with header names are done in a case insensitive
* manner.
*
* @param name
* the name to look for. This must not be null.
* @return the Header with the given name.
*/
public Header getHeader(String name) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
for (Header h : headers) {
if (name.equalsIgnoreCase(h.getName())) {
return h;
}
}
return null;
}
/** Gets the headers.
*
* @return the headers
*/
public Header[] getHeaders() {
return headers.toArray(new Header[0]);
}
/** Gets the last modified.
*
* @return the last modified
*/
public Date getLastModified() {
Header h = getHeader("Last-Modified");
if (h == null) {
return null;
}
try {
String value = h.getValue();
Long longValue = Long.parseLong(value);
return new Date(longValue);
} catch (Exception e) {
// silently fail. Maybe should log using FINER?
return null;
}
}
/** Gets the status code.
*
* @return the status code
*/
public StatusCode getStatusCode() {
return statusCode;
}
/** Gets the status text.
*
* @return the status text
*/
public String getStatusText() {
return statusText;
}
/** Gets the body as stream.
*
* @return the body as stream
*/
public InputStream getBodyAsStream() {
return new ByteArrayInputStream(getBodyAsBytes());
}
/** Gets the body as reader.
*
* @return the body as reader
*/
public Reader getBodyAsReader() {
if (responseBody == null) {
return new StringReader("");
} else {
return new StringReader(getBody());
}
}
/** Gets the body as bytes.
*
* @return the body as bytes
*/
public byte[] getBodyAsBytes() {
return responseBody == null ? new byte[0] : responseBody;
}
/** Gets the body.
*
* @return the body
*/
public String getBody() {
try {
return responseBody == null ? ""
: new String(responseBody, charset);
} catch (UnsupportedEncodingException ex) {
logger.log(Level.ERROR, ex);
return responseBody == null ? "" : new String(responseBody);
}
}
/** Gets the base url.
*
* @return the base url
*/
public String getBaseUrl() {
return url;
}
/**
* @inheritDoc
*/
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(" ").append(statusCode).append("\n");
for (Header h : getHeaders()) {
buffer.append(" ").append(h.getName()).append(": ")
.append(h.getValue());
buffer.append("\n");
}
buffer.append(getBody());
return buffer.toString();
}
}