/* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* http://www.sun.com/cddl/cddl.html or
* install_dir/legal/LICENSE
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at install_dir/legal/LICENSE.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* $Id$
*
* Copyright 2005-2009 Sun Microsystems Inc. All Rights Reserved
*/
package com.sun.faban.driver.transport.sunhttp;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* Implementation of the HttpClient for the Faban driver framework.
* It relies heavily on the sun.net implementation. The only
* difference is actually the use of SocketFactory instead
* of plainly creating new sockets. The HttpClient supports both
* http and https protocols.<br>
* Derived from code contributed by Scott Oaks.
*
* @author Akara Sucharitakul
*/
public class HttpClient extends sun.net.www.http.HttpClient {
private static Method getMethod;
static {
kac = new KeepAliveCache();
try {
getMethod = kac.getClass().getMethod("get", new Class[]{ URL.class, Object.class } );
} catch (NoSuchMethodException nsme) {
throw new ExceptionInInitializerError(nsme);
}
}
/** Superclass' inCache is private. Needed to define and check usage. */
protected boolean inCache;
private static SocketFactory socketFactory;
/**
* Instantiates a HttpClient.
* @param url The URL to connect
* @param proxyHost The proxy server, null if no proxy
* @param proxyPort The proxy server port
* @param useCache Whether to use a client from cache or not
* @param timeout The connect timeout, -1 if no timeout
* @return An instance of HttpClient
* @throws IOException An I/O error occurred
*/
public static HttpClient New(URL url, String proxyHost, int proxyPort,
boolean useCache, int timeout)
throws IOException {
return New(url, proxyHost == null ? null :
newHttpProxy(proxyHost, proxyPort, "http"), -1, useCache);
}
/**
* Instantiates a HttpClient.
* @param url The URL to connect
* @param p The proxy server, null if no proxy
* @param to The connect timeout, -1 if no timeout
* @param useCache Whether to use a client from cache or not
* @return An instance of HttpClient
* @throws IOException An I/O error occurred
*/
public static HttpClient New(URL url, Proxy p, int to,
boolean useCache)
throws IOException {
if (p == null) {
p = Proxy.NO_PROXY;
}
HttpClient ret = null;
/* see if one's already around */
if (useCache) {
try {
ret = (HttpClient) getMethod.invoke(kac, new Object[]{url, null});
} catch (Exception iae) {
throw new IOException("Can't invoke on KeepAliveCache", iae);
}
if (ret != null) {
if ((ret.proxy != null && ret.proxy.equals(p)) ||
(ret.proxy == null && p == null)) {
synchronized (ret) {
ret.cachedHttpClient = true;
assert ret.inCache;
ret.inCache = false;
}
}
}
}
if (ret == null) {
ret = new HttpClient(url, p, to);
} else {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkConnect(url.getHost(), url.getPort());
}
ret.url = url;
}
return ret;
}
/**
* Subclass constructor for the client.
* @param url The URL
* @param proxy Proxies, if any
* @param to The connect timeout
* @throws IOException If an error occurs
*/
protected HttpClient(URL url, Proxy proxy, int to) throws IOException {
super(url, proxy, to);
}
@Override
protected synchronized void putInKeepAliveCache() {
if (inCache) {
assert false : "Duplicate put to keep alive cache";
return;
}
inCache = true;
kac.put(url, null, this);
}
/**
* Return a socket connected to the server, with any
* appropriate options pre-established. This method
* overrides NetworClient.doConnect() to use the provided
* SocketFactory for socket creation.
* @param server The server to connect to
* @param port The port to connect to
* @return The socket connecting the the server
* @throws IOException Communication error
* @throws UnknownHostException The host cannot be found
*/
@Override
protected Socket doConnect (String server, int port)
throws IOException, UnknownHostException {
Socket s;
if (proxy != null) {
if (proxy.type() == Proxy.Type.SOCKS) {
s = AccessController.doPrivileged(
new PrivilegedAction<Socket>() {
public Socket run() {
return socketFactory.createSocket(proxy);
}});
} else {
s = socketFactory.createSocket(Proxy.NO_PROXY);
}
} else {
s = socketFactory.createSocket();
}
// Instance specific timeouts do have priority, that means
// connectTimeout & readTimeout (-1 means not set)
// Then global default timeouts
// Then no timeout.
if (connectTimeout >= 0) {
s.connect(new InetSocketAddress(server, port), connectTimeout);
} else {
if (defaultConnectTimeout > 0) {
s.connect(new InetSocketAddress(server, port),
defaultConnectTimeout);
} else {
s.connect(new InetSocketAddress(server, port));
}
}
if (readTimeout >= 0) {
s.setSoTimeout(readTimeout);
} else if (defaultSoTimeout > 0) {
s.setSoTimeout(defaultSoTimeout);
}
return s;
}
/**
* Sets the socket factory for creating sockets used by this client.
* @param sf The socket factory
*/
protected static void setSocketFactory(SocketFactory sf) {
socketFactory = sf;
}
}