package com.canoo.webtest.extension.applet.runner.http;
import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HeaderGroup;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.log4j.Logger;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ProtocolException;
import java.net.URL;
import java.net.UnknownServiceException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
/**
* <p>Implementation note.
* <ul>
* <li>the class assume that the typical usage is to just {@link
* #addRequestProperty(String, String) add} or {@link #setRequestProperty(String, String) set} request properties, and
* that asking for the {@link #getRequestProperties()} list of request properties is a rare opportunity.</li>
* <li>There is a single cookie jar for all http connection from a jvm.</li>
* </ul>
*
* @author Denis N. Antonioli
*/
public class HttpURLConnection extends java.net.HttpURLConnection {
private static final Logger LOG = Logger.getLogger(HttpURLConnection.class);
private final HeaderGroup fRequestProperties;
private static HttpClient sHttpClient;
private HttpMethod fHttpRequestMethod;
private static Cookie sCookies[];
private ByteArrayOutputStream fOutputStream;
static {
System.setProperty("apache.commons.httpclient.cookiespec", "COMPATIBILITY");
}
public static void setCookies(Cookie cookies[]) {
sCookies = cookies;
}
static {
sHttpClient = new HttpClient();
}
{
fRequestProperties = new HeaderGroup();
fHttpRequestMethod = new GetMethod(url.toExternalForm());
}
public HttpURLConnection(final URL url) {
this(url, sHttpClient);
}
/**
* Testing constructor.
*
* @param url
* @param httpClient
*/
HttpURLConnection(final URL url, final HttpClient httpClient) {
super(url);
sHttpClient = httpClient;
if (sCookies != null && sCookies.length > 0) {
LOG.debug("Adding cookies to state");
sHttpClient.getState().addCookies(sCookies);
}
LOG.debug("new HttpURLConnection(" + url.toExternalForm() + ").");
}
public boolean isConnected() {
return connected;
}
public void disconnect() {
// LOG.info("disconnect(" + url.toExternalForm() + ")");
throw new NoSuchMethodError("Not yet implemented");
}
public boolean usingProxy() {
return false;
}
public void connect() throws IOException {
if (connected) {
return;
}
LOG.info("connect(" + url.toExternalForm() + ")");
Header headers[] = fRequestProperties.getAllHeaders();
for (int i = 0; i < headers.length; i++) {
fHttpRequestMethod.setRequestHeader(headers[i]);
}
if (fOutputStream != null) {
((EntityEnclosingMethod) fHttpRequestMethod).setRequestEntity(new ByteArrayRequestEntity(fOutputStream.toByteArray()));
}
responseCode = sHttpClient.executeMethod(fHttpRequestMethod);
if (responseCode != HttpStatus.SC_OK) {
LOG.error(fHttpRequestMethod.getName() + " " + url.toExternalForm() +" failed: " + fHttpRequestMethod.getStatusLine());
}
connected = true;
}
public InputStream getInputStream() throws IOException {
LOG.info("getInputStream(" + url.toExternalForm() + ")");
if (!getDoInput()) {
LOG.error("Input not allowed");
throw new ProtocolException("Input not allowed on this connection");
}
connect();
return fHttpRequestMethod.getResponseBodyAsStream();
}
public OutputStream getOutputStream() throws IOException {
LOG.info("getOutputStream(" + url.toExternalForm() + ")");
// TODO: return fOutputStream if fOutputStream != null ??
if (!getDoOutput()) {
LOG.error("Output not allowed");
throw new ProtocolException("Output not allowed on this connection");
}
if (fHttpRequestMethod instanceof GetMethod) {
setRequestMethod("POST");
}
if (!(fHttpRequestMethod instanceof EntityEnclosingMethod)) {
throw new UnknownServiceException(fHttpRequestMethod.getName() + " doesn't support output.");
}
fOutputStream = new ByteArrayOutputStream();
return fOutputStream;
}
public String getHeaderFieldKey(final int n) {
final Header headers[] = fHttpRequestMethod.getResponseHeaders();
if (n >= headers.length) {
LOG.info("getHeaderFieldKey(" + n + ") -> null");
return null;
}
final String name = headers[n].getName();
LOG.info("getHeaderFieldKey(" + n + ") -> " + name);
return name;
}
public String getHeaderField(final int n) {
final Header headers[] = fHttpRequestMethod.getResponseHeaders();
if (n >= headers.length) {
LOG.info("getHeaderField(" + n + ") -> null");
return null;
}
final String value = headers[n].getValue();
LOG.info("getHeaderField(" + n + ") -> " + value);
return value;
}
public String getHeaderField(final String name) {
final Header responseHeader = fHttpRequestMethod.getResponseHeader(name);
if (responseHeader == null) {
LOG.info("getHeaderField(" + name + ") -> null");
return null;
}
String value = responseHeader.getValue();
// is this possible outside of a test case?
if (value == null) {
LOG.info("getHeaderField(" + name + ") -> null");
return null;
}
final String trimmedValue = value.substring(value.lastIndexOf(',') + 1).trim();
LOG.info("getHeaderField(" + name + ") -> " + trimmedValue);
return trimmedValue;
}
public Map getHeaderFields() {
LOG.info("getHeaderFields()");
Map hf = new HashMap();
final Header headers[] = fHttpRequestMethod.getResponseHeaders();
for (int i = 0; i < headers.length; i++) {
Header header = headers[i];
List values = new ArrayList();
for (StringTokenizer st = new StringTokenizer(header.getValue(), ","); st.hasMoreTokens();) {
values.add(st.nextToken().trim());
}
hf.put(header.getName(), Collections.unmodifiableList(values));
}
return Collections.unmodifiableMap(hf);
}
public void setRequestProperty(final String key, final String value) {
LOG.info("setRequestProperty(" + key + ", " + value + ")");
super.setRequestProperty(key, value);
if (fRequestProperties.containsHeader(key)) {
final Header[] headers = fRequestProperties.getHeaders(key);
for (int i = 0; i < headers.length; i++) {
fRequestProperties.removeHeader(headers[i]);
}
}
fRequestProperties.addHeader(new Header(key, value));
}
public void addRequestProperty(final String key, final String value) {
LOG.info("addRequestProperty(" + key + ", " + value + ")");
super.addRequestProperty(key, value);
fRequestProperties.addHeader(new Header(key, value));
}
public String getRequestProperty(final String key) {
if (connected) {
throw new IllegalStateException("Already connected");
}
if (key == null) {
return null;
}
final Header header = fRequestProperties.getCondensedHeader(key);
if (header == null) {
return null;
}
return header.getValue();
}
public Map getRequestProperties() {
if (connected) {
throw new IllegalStateException("Already connected");
}
Header headers[] = fRequestProperties.getAllHeaders();
if (headers.length == 0) {
return Collections.EMPTY_MAP;
}
Map hf = new HashMap();
for (int i = 0; i < headers.length; i++) {
Header header = headers[i];
List list = (List) hf.get(header.getName());
if (list == null) {
list = new ArrayList();
hf.put(header.getName(), list);
}
list.add(header.getValue());
}
return Collections.unmodifiableMap(hf);
}
public void setRequestMethod(final String method) throws ProtocolException {
if (connected) {
throw new ProtocolException("Can't reset method: already connected");
}
if (fOutputStream != null) {
// TODO: or just clear the output stream?
// TODO: or allow change between Post and Put?
throw new ProtocolException("Can't reset method: output stream already allocated");
}
LOG.info("setRequestMethod(" + method + ")");
if ("GET".equals(method)) {
setHttpRequestMethod(new GetMethod(url.toExternalForm()));
} else if ("POST".equals(method)) {
setHttpRequestMethod(new PostMethod(url.toExternalForm()));
} else {
throw new ProtocolException("Not implemented/Invalid HTTP method: " + method);
}
}
public String getRequestMethod() {
return fHttpRequestMethod.getName();
}
HttpMethod getHttpRequestMethod() {
return fHttpRequestMethod;
}
void setHttpRequestMethod(HttpMethod method) {
fHttpRequestMethod = method;
}
public int getResponseCode() throws IOException {
return fHttpRequestMethod.getStatusCode();
}
public String getResponseMessage() throws IOException {
return fHttpRequestMethod.getStatusText();
}
}