/* 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; } }