001: package com.canoo.webtest.extension.applet.runner.http;
002:
003: import org.apache.commons.httpclient.Cookie;
004: import org.apache.commons.httpclient.Header;
005: import org.apache.commons.httpclient.HeaderGroup;
006: import org.apache.commons.httpclient.HttpClient;
007: import org.apache.commons.httpclient.HttpMethod;
008: import org.apache.commons.httpclient.HttpStatus;
009: import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
010: import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
011: import org.apache.commons.httpclient.methods.GetMethod;
012: import org.apache.commons.httpclient.methods.PostMethod;
013: import org.apache.log4j.Logger;
014:
015: import java.io.ByteArrayOutputStream;
016: import java.io.IOException;
017: import java.io.InputStream;
018: import java.io.OutputStream;
019: import java.net.ProtocolException;
020: import java.net.URL;
021: import java.net.UnknownServiceException;
022: import java.util.ArrayList;
023: import java.util.Collections;
024: import java.util.HashMap;
025: import java.util.List;
026: import java.util.Map;
027: import java.util.StringTokenizer;
028:
029: /**
030: * <p>Implementation note.
031: * <ul>
032: * <li>the class assume that the typical usage is to just {@link
033: * #addRequestProperty(String, String) add} or {@link #setRequestProperty(String, String) set} request properties, and
034: * that asking for the {@link #getRequestProperties()} list of request properties is a rare opportunity.</li>
035: * <li>There is a single cookie jar for all http connection from a jvm.</li>
036: * </ul>
037: *
038: * @author Denis N. Antonioli
039: */
040: public class HttpURLConnection extends java.net.HttpURLConnection {
041: private static final Logger LOG = Logger
042: .getLogger(HttpURLConnection.class);
043:
044: private final HeaderGroup fRequestProperties;
045: private static HttpClient sHttpClient;
046: private HttpMethod fHttpRequestMethod;
047: private static Cookie sCookies[];
048: private ByteArrayOutputStream fOutputStream;
049:
050: static {
051: System.setProperty("apache.commons.httpclient.cookiespec",
052: "COMPATIBILITY");
053: }
054:
055: public static void setCookies(Cookie cookies[]) {
056: sCookies = cookies;
057: }
058:
059: static {
060: sHttpClient = new HttpClient();
061: }
062:
063: {
064: fRequestProperties = new HeaderGroup();
065: fHttpRequestMethod = new GetMethod(url.toExternalForm());
066: }
067:
068: public HttpURLConnection(final URL url) {
069: this (url, sHttpClient);
070: }
071:
072: /**
073: * Testing constructor.
074: *
075: * @param url
076: * @param httpClient
077: */
078: HttpURLConnection(final URL url, final HttpClient httpClient) {
079: super (url);
080: sHttpClient = httpClient;
081: if (sCookies != null && sCookies.length > 0) {
082: LOG.debug("Adding cookies to state");
083: sHttpClient.getState().addCookies(sCookies);
084: }
085: LOG.debug("new HttpURLConnection(" + url.toExternalForm()
086: + ").");
087: }
088:
089: public boolean isConnected() {
090: return connected;
091: }
092:
093: public void disconnect() {
094: // LOG.info("disconnect(" + url.toExternalForm() + ")");
095: throw new NoSuchMethodError("Not yet implemented");
096: }
097:
098: public boolean usingProxy() {
099: return false;
100: }
101:
102: public void connect() throws IOException {
103: if (connected) {
104: return;
105: }
106: LOG.info("connect(" + url.toExternalForm() + ")");
107: Header headers[] = fRequestProperties.getAllHeaders();
108: for (int i = 0; i < headers.length; i++) {
109: fHttpRequestMethod.setRequestHeader(headers[i]);
110: }
111: if (fOutputStream != null) {
112: ((EntityEnclosingMethod) fHttpRequestMethod)
113: .setRequestEntity(new ByteArrayRequestEntity(
114: fOutputStream.toByteArray()));
115: }
116: responseCode = sHttpClient.executeMethod(fHttpRequestMethod);
117: if (responseCode != HttpStatus.SC_OK) {
118: LOG.error(fHttpRequestMethod.getName() + " "
119: + url.toExternalForm() + " failed: "
120: + fHttpRequestMethod.getStatusLine());
121: }
122:
123: connected = true;
124: }
125:
126: public InputStream getInputStream() throws IOException {
127: LOG.info("getInputStream(" + url.toExternalForm() + ")");
128: if (!getDoInput()) {
129: LOG.error("Input not allowed");
130: throw new ProtocolException(
131: "Input not allowed on this connection");
132: }
133: connect();
134: return fHttpRequestMethod.getResponseBodyAsStream();
135: }
136:
137: public OutputStream getOutputStream() throws IOException {
138: LOG.info("getOutputStream(" + url.toExternalForm() + ")");
139: // TODO: return fOutputStream if fOutputStream != null ??
140: if (!getDoOutput()) {
141: LOG.error("Output not allowed");
142: throw new ProtocolException(
143: "Output not allowed on this connection");
144: }
145: if (fHttpRequestMethod instanceof GetMethod) {
146: setRequestMethod("POST");
147: }
148: if (!(fHttpRequestMethod instanceof EntityEnclosingMethod)) {
149: throw new UnknownServiceException(fHttpRequestMethod
150: .getName()
151: + " doesn't support output.");
152: }
153: fOutputStream = new ByteArrayOutputStream();
154: return fOutputStream;
155: }
156:
157: public String getHeaderFieldKey(final int n) {
158: final Header headers[] = fHttpRequestMethod
159: .getResponseHeaders();
160: if (n >= headers.length) {
161: LOG.info("getHeaderFieldKey(" + n + ") -> null");
162: return null;
163: }
164: final String name = headers[n].getName();
165: LOG.info("getHeaderFieldKey(" + n + ") -> " + name);
166: return name;
167: }
168:
169: public String getHeaderField(final int n) {
170: final Header headers[] = fHttpRequestMethod
171: .getResponseHeaders();
172: if (n >= headers.length) {
173: LOG.info("getHeaderField(" + n + ") -> null");
174: return null;
175: }
176: final String value = headers[n].getValue();
177: LOG.info("getHeaderField(" + n + ") -> " + value);
178: return value;
179: }
180:
181: public String getHeaderField(final String name) {
182: final Header responseHeader = fHttpRequestMethod
183: .getResponseHeader(name);
184: if (responseHeader == null) {
185: LOG.info("getHeaderField(" + name + ") -> null");
186: return null;
187: }
188: String value = responseHeader.getValue();
189: // is this possible outside of a test case?
190: if (value == null) {
191: LOG.info("getHeaderField(" + name + ") -> null");
192: return null;
193: }
194: final String trimmedValue = value.substring(
195: value.lastIndexOf(',') + 1).trim();
196: LOG.info("getHeaderField(" + name + ") -> " + trimmedValue);
197: return trimmedValue;
198: }
199:
200: public Map getHeaderFields() {
201: LOG.info("getHeaderFields()");
202: Map hf = new HashMap();
203:
204: final Header headers[] = fHttpRequestMethod
205: .getResponseHeaders();
206: for (int i = 0; i < headers.length; i++) {
207: Header header = headers[i];
208: List values = new ArrayList();
209: for (StringTokenizer st = new StringTokenizer(header
210: .getValue(), ","); st.hasMoreTokens();) {
211: values.add(st.nextToken().trim());
212: }
213: hf.put(header.getName(), Collections
214: .unmodifiableList(values));
215: }
216: return Collections.unmodifiableMap(hf);
217: }
218:
219: public void setRequestProperty(final String key, final String value) {
220: LOG.info("setRequestProperty(" + key + ", " + value + ")");
221: super .setRequestProperty(key, value);
222: if (fRequestProperties.containsHeader(key)) {
223: final Header[] headers = fRequestProperties.getHeaders(key);
224: for (int i = 0; i < headers.length; i++) {
225: fRequestProperties.removeHeader(headers[i]);
226: }
227: }
228: fRequestProperties.addHeader(new Header(key, value));
229: }
230:
231: public void addRequestProperty(final String key, final String value) {
232: LOG.info("addRequestProperty(" + key + ", " + value + ")");
233: super .addRequestProperty(key, value);
234: fRequestProperties.addHeader(new Header(key, value));
235: }
236:
237: public String getRequestProperty(final String key) {
238: if (connected) {
239: throw new IllegalStateException("Already connected");
240: }
241: if (key == null) {
242: return null;
243: }
244: final Header header = fRequestProperties
245: .getCondensedHeader(key);
246: if (header == null) {
247: return null;
248: }
249: return header.getValue();
250: }
251:
252: public Map getRequestProperties() {
253: if (connected) {
254: throw new IllegalStateException("Already connected");
255: }
256: Header headers[] = fRequestProperties.getAllHeaders();
257: if (headers.length == 0) {
258: return Collections.EMPTY_MAP;
259: }
260:
261: Map hf = new HashMap();
262: for (int i = 0; i < headers.length; i++) {
263: Header header = headers[i];
264: List list = (List) hf.get(header.getName());
265: if (list == null) {
266: list = new ArrayList();
267: hf.put(header.getName(), list);
268: }
269: list.add(header.getValue());
270: }
271: return Collections.unmodifiableMap(hf);
272: }
273:
274: public void setRequestMethod(final String method)
275: throws ProtocolException {
276: if (connected) {
277: throw new ProtocolException(
278: "Can't reset method: already connected");
279: }
280: if (fOutputStream != null) {
281: // TODO: or just clear the output stream?
282: // TODO: or allow change between Post and Put?
283: throw new ProtocolException(
284: "Can't reset method: output stream already allocated");
285: }
286: LOG.info("setRequestMethod(" + method + ")");
287: if ("GET".equals(method)) {
288: setHttpRequestMethod(new GetMethod(url.toExternalForm()));
289: } else if ("POST".equals(method)) {
290: setHttpRequestMethod(new PostMethod(url.toExternalForm()));
291: } else {
292: throw new ProtocolException(
293: "Not implemented/Invalid HTTP method: " + method);
294: }
295: }
296:
297: public String getRequestMethod() {
298: return fHttpRequestMethod.getName();
299: }
300:
301: HttpMethod getHttpRequestMethod() {
302: return fHttpRequestMethod;
303: }
304:
305: void setHttpRequestMethod(HttpMethod method) {
306: fHttpRequestMethod = method;
307: }
308:
309: public int getResponseCode() throws IOException {
310: return fHttpRequestMethod.getStatusCode();
311: }
312:
313: public String getResponseMessage() throws IOException {
314: return fHttpRequestMethod.getStatusText();
315: }
316: }
|