Java tutorial
/* * Adito * * Copyright (C) 2003-2006 3SP LTD. All Rights Reserved * * 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 version 2 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 Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package com.maverick.http; import java.io.EOFException; import java.io.IOException; import java.net.UnknownHostException; import java.text.MessageFormat; import java.util.Date; import java.util.Enumeration; import java.util.StringTokenizer; import java.util.Vector; /** * * @author Lee David Painter <a href="mailto:lee@localhost"><lee@localhost></a> */ public class HttpClient { static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; static final String AUTHORIZATION = "Authorization"; static final String WWW_AUTHENTICATE = "WWW-Authenticate"; static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; public static final int PROXY_NONE = 0; public static final int PROXY_HTTP = 1; public static final int PROXY_HTTPS = 2; public static String USER_AGENT = "Maverick-HttpClient/1.0"; //$NON-NLS-1$ /** * The target server */ String hostname; int port; boolean isSecure; PasswordCredentials credentials; AuthenticationPrompt prompt; String preferedAuthentication = HttpAuthenticatorFactory.BASIC; boolean preemptiveAuthentication = false; HttpConnectionManager connections = new HttpConnectionManager(this); boolean includeCookies = true; Vector cookies = new Vector(); int maxAuthenticationAttempts = 5; int proxyMaxAuthenticationAttempts = 5; boolean proxyPreemptiveAuthentication = false; HttpClient proxyClient; boolean credentialsFailed = false; /** * Proxy information */ String proxyHost; int proxyPort = -1; int proxyType = PROXY_NONE; String reverseProxyHost; int reverseProxyPort = -1; int reverseProxyType = PROXY_NONE; PasswordCredentials proxyCredentials; AuthenticationPrompt proxyAuthenticationPrompt; String proxyPreferedAuthentication = HttpAuthenticatorFactory.BASIC; boolean isProxyClient = false; // #ifdef DEBUG static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(HttpClient.class); // #endif public HttpClient(String hostname, int port, boolean isSecure) { this.hostname = hostname; this.port = port; this.isSecure = isSecure; } /** * Set the default user agent. * * @param userAgent default user agent */ public static void setUserAgent(String userAgent) { USER_AGENT = userAgent; } HttpClient(HttpClient client) { this(client.proxyHost, client.proxyPort, client.proxyType == HttpClient.PROXY_HTTPS); setAuthenticationPrompt(client.proxyAuthenticationPrompt); setCredentials(client.proxyCredentials); setMaxAuthenticationAttempts(client.proxyMaxAuthenticationAttempts); setPreemtiveAuthentication(client.proxyPreemptiveAuthentication); setPreferredAuthentication(client.proxyPreferedAuthentication); this.isProxyClient = true; } boolean configureProxy() { /** * Attempt to automatically configure proxy from Maverick SSL proxy * settings */ if (!isProxyClient && !isNonProxiedHost(hostname)) { if (System.getProperty("com.maverick.ssl.https.HTTPProxyHostname") != null) { //$NON-NLS-1$ setProxyHost(System.getProperty("com.maverick.ssl.https.HTTPProxyHostname")); //$NON-NLS-1$ // #ifdef DEBUG log.debug(MessageFormat.format(Messages.getString("HttpClient.setClientProxyHost"), //$NON-NLS-1$ new Object[] { proxyHost })); // #endif if (System.getProperty("com.maverick.ssl.https.HTTPProxyPort") != null) { //$NON-NLS-1$ setProxyPort(Integer.parseInt(System.getProperty("com.maverick.ssl.https.HTTPProxyPort"))); //$NON-NLS-1$ // #ifdef DEBUG log.debug(MessageFormat.format(Messages.getString("HttpClient.setClientProxyPort"), //$NON-NLS-1$ new Object[] { new Integer(proxyPort) })); // #endif } else setProxyPort(80); if (System.getProperty("com.maverick.ssl.https.HTTPProxySecure") != null) { //$NON-NLS-1$ setProxyType( System.getProperty("com.maverick.ssl.https.HTTPProxySecure").equalsIgnoreCase("true") //$NON-NLS-1$//$NON-NLS-2$ ? PROXY_HTTPS : PROXY_HTTP); } else setProxyType(PROXY_HTTP); if (System.getProperty("com.maverick.ssl.https.HTTPProxyUsername") != null) { //$NON-NLS-1$ setProxyCredentials( new PasswordCredentials(System.getProperty("com.maverick.ssl.https.HTTPProxyUsername"), System.getProperty("com.maverick.ssl.https.HTTPProxyPassword") == null ? "" : System.getProperty("com.maverick.ssl.https.HTTPProxyPassword"))); } return true; } } return false; } public boolean isProxyConfigured() { if (proxyType == PROXY_NONE) return configureProxy(); else return true; } public boolean isReverseProxyConfigured() { if (reverseProxyType == PROXY_NONE) return false; else return true; } public static boolean isNonProxiedHost(String host) { String nonProxiedHosts = System.getProperty("com.maverick.ssl.https.HTTPProxyNonProxyHosts"); //$NON-NLS-1$ if (nonProxiedHosts == null || nonProxiedHosts.equals("")) { //$NON-NLS-1$ return false; } StringTokenizer t = new StringTokenizer(nonProxiedHosts, "|"); //$NON-NLS-1$ while (t.hasMoreTokens()) { String token = t.nextToken(); int idx = token.indexOf('*'); if (idx != -1) { if (token.length() == 1) { return true; } String before = token.substring(0, idx); String after = token.substring(idx + 1); if (((before.length() == 0) || host.startsWith(before)) && ((after.length() == 0) || host.endsWith(after))) { return true; } } else { if (host.equalsIgnoreCase(token)) { return true; } } } return false; } public HttpConnectionManager getConnectionManager() { return connections; } public String getHost() { return hostname; } public int getPort() { return port; } public boolean isSecure() { return isSecure; } public void setMaxAuthenticationAttempts(int maxAuthenticationAttempts) { this.maxAuthenticationAttempts = maxAuthenticationAttempts; } public void setProxyHost(String proxyHost) { this.proxyHost = proxyHost; } public void setProxyPort(int proxyPort) { this.proxyPort = proxyPort; } public void setProxyMaxAuthenticationAttempts(int proxyMaxAuthenticationAttempts) { this.proxyMaxAuthenticationAttempts = proxyMaxAuthenticationAttempts; } public void setProxyType(int proxyType) { if (proxyType > PROXY_HTTPS || proxyType < PROXY_NONE) { throw new IllegalArgumentException(MessageFormat.format( Messages.getString("HttpClient.notValidProxyType"), new Object[] { new Integer(proxyType) })); //$NON-NLS-1$ } this.proxyType = proxyType; } public void setReverseProxyHost(String reverseProxyHost) { this.reverseProxyHost = reverseProxyHost; } public void setReverseProxyPort(int reverseProxyPort) { this.reverseProxyPort = reverseProxyPort; } public void setReverseProxyType(int reverseProxyType) { if (reverseProxyType > PROXY_HTTPS || reverseProxyType < PROXY_NONE) { throw new IllegalArgumentException( MessageFormat.format(Messages.getString("HttpClient.notValidProxyType"), //$NON-NLS-1$ new Object[] { new Integer(reverseProxyType) })); } this.reverseProxyType = reverseProxyType; } public void setProxyCredentials(PasswordCredentials proxyCredentials) { this.proxyCredentials = proxyCredentials; } public void setProxyPreemptiveAuthentication(boolean proxyPreemptiveAuthentication) { this.proxyPreemptiveAuthentication = proxyPreemptiveAuthentication; } public void setProxyAuthenticationPrompt(AuthenticationPrompt proxyAuthenticationPrompt) { this.proxyAuthenticationPrompt = proxyAuthenticationPrompt; } public void setProxyPreferedAuthentication(String scheme) { this.proxyPreferedAuthentication = scheme; } public void setAuthenticationPrompt(AuthenticationPrompt prompt) { this.prompt = prompt; } public void setPreferredAuthentication(String scheme) { this.preferedAuthentication = scheme; } public void setCredentials(PasswordCredentials credentials) { this.credentials = credentials; credentialsFailed = false; } public void close() { connections.closeConnections(); } synchronized void prepareRequest(HttpRequest request, HttpMethod method, HttpConnection con) throws IOException, HttpException, UnsupportedAuthenticationException, AuthenticationCancelledException { request.reset(); if (preemptiveAuthentication && credentials != null) { // Discard old authenticator and create a new one BasicAuthentication authenticator = new BasicAuthentication(method.getURI(), con.getHost(), con.getPort(), con.isSecure()); authenticator.setCredentials(credentials); authenticator.setConnection(con); authenticator.setChallenge("Basic"); //$NON-NLS-1$ authenticator.setAuthenicationHeader( method.getName().equals("CONNECT") ? PROXY_AUTHENTICATE : WWW_AUTHENTICATE); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ authenticator.setAuthorizationHeader( method.getName().equals("CONNECT") ? PROXY_AUTHORIZATION : AUTHORIZATION); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ con.setAuthenticator(authenticator); } connections.checkConnection(con); if (includeCookies) { String cookiesHeader = ""; //$NON-NLS-1$ for (Enumeration e = cookies.elements(); e.hasMoreElements();) { Cookie cookie = (Cookie) e.nextElement(); // Evaluate whether the cookie should be included Date now = new Date(); if (cookie.getExpires() == null || cookie.expires.after(now)) { if (method.getURI().startsWith(cookie.getPath()) && getHost().endsWith(cookie.getDomain()) && (cookie.isSecure() == isSecure || !cookie.isSecure())) { cookiesHeader += cookie + "; "; //$NON-NLS-1$ } } } if (!cookiesHeader.equals("")) { //$NON-NLS-1$ request.setHeaderField("Cookie", cookiesHeader); //$NON-NLS-1$ } } if (con.isKeepAlive()) request.setHeaderField("Connection", "Keep-Alive"); //$NON-NLS-1$ //$NON-NLS-2$ if (con.getAuthenticator() != null && con.getAuthenticator().canAuthenticate()) { // #ifdef DEBUG log.debug(MessageFormat.format(Messages.getString("HttpClient.settingAuthCredentials"), //$NON-NLS-1$ new Object[] { con.getAuthenticator().getScheme() })); // #endif try { request.removeFields(con.getAuthenticator().getAuthorizationHeader()); con.getAuthenticator().authenticate(request, method); } catch (Exception ex) { // #ifdef DEBUG log.info(Messages.getString("HttpClient.authenticatorException"), ex); //$NON-NLS-1$ // #endif con.setAuthenticator(null); } } } HttpResponse execute(HttpRequest request, HttpMethod method, HttpConnection con) throws IOException, HttpException, UnsupportedAuthenticationException, AuthenticationCancelledException { prepareRequest(request, method, con); // #ifdef DEBUG log.info(MessageFormat.format(Messages.getString("HttpClient.executingMethod"), //$NON-NLS-1$ new Object[] { method.getName(), con.getHost() })); // #endif return processResponse(request, method, con, method.execute(request, con)); } HttpConnection executeAsync(HttpRequest request, AsyncHttpMethod method, HttpConnection con) throws IOException, HttpException, UnsupportedAuthenticationException, AuthenticationCancelledException { prepareRequest(request, method, con); // #ifdef DEBUG log.info(MessageFormat.format(Messages.getString("HttpClient.executingMethod"), //$NON-NLS-1$ new Object[] { method.getName(), con.getHost() })); // #endif method.executeAsync(request, con); return con; } synchronized HttpResponse processResponse(HttpRequest request, HttpMethod method, HttpConnection con, HttpResponse response) throws IOException, HttpException, UnsupportedAuthenticationException, AuthenticationCancelledException { // Process any cookies if (includeCookies) { String[] cookies = response.getHeaderFields("Set-Cookie"); //$NON-NLS-1$ if (cookies != null) { for (int i = 0; i < cookies.length; i++) { this.cookies.addElement(new Cookie(cookies[i])); } } } try { /** * Now process the response to see if we need to handle * authentication */ switch (response.getStatus()) { case 401: case 407: if (con.getAuthenticator() != null) { int success = con.getAuthenticator().processResponse(response); switch (success) { case HttpAuthenticator.AUTHENTICATION_COMPLETED: // w00t we're in return response; case HttpAuthenticator.AUTHENTICATION_FAILED: // Up the number of cycles and try again request.cycles++; credentialsFailed = true; break; case HttpAuthenticator.AUTHENTICATION_IN_PROGRESS: default: // Do nothing just keep on authenticating } } else if (credentials == null && prompt == null) { return response; } if (con.getAuthenticator() == null || request.cycles < maxAuthenticationAttempts) return doAuthentication(response.getStatus() == 401 ? WWW_AUTHENTICATE : PROXY_AUTHENTICATE, response //$NON-NLS-1$ //$NON-NLS-2$ .getStatus() == 401 ? AUTHORIZATION : PROXY_AUTHORIZATION, request, method, response, //$NON-NLS-1$ //$NON-NLS-2$ con); else return response; default: // Set the authenticator as complete and return if (con.getAuthenticator() != null) { credentials = con.getAuthenticator().credentials; con.getAuthenticator().complete(); } return response; } } catch (UnsupportedAuthenticationException ex) { /** * We dont support this type of authentication so return the * response */ return response; } } private HttpResponse doAuthentication(String authenticateHeader, String authorizationHeader, HttpRequest request, HttpMethod method, HttpResponse response, HttpConnection con) throws IOException, HttpException, UnsupportedAuthenticationException, AuthenticationCancelledException { // Check for failed authentication limit if (credentialsFailed) { con.setAuthenticator(null); return response; } // If we're called we are disgaurding the previous response response.close(false); // Authorization required String[] challenges = response.getHeaderFields(authenticateHeader); if (challenges == null) return response; // #ifdef DEBUG for (int i = 0; i < challenges.length; i++) { log.info(MessageFormat.format(Messages.getString("HttpClient.requiresAuthType"), new Object[] { //$NON-NLS-1$ con.getHost(), HttpAuthenticatorFactory.getAuthenticationMethod(challenges[i]) })); } // #endif /** * If we don't already have an authenticator we should create one */ if (credentials == null && prompt == null && con.getAuthenticator() == null) { // We cannot authenticate as we do not have any credentials // or a prompt return response; } /** * If we got this far then we can authenticate so try to create an * appropriate authenticator */ if (con.getAuthenticator() == null || !con.getAuthenticator().getURI().startsWith(method.getURI())) { con.setAuthenticator(HttpAuthenticatorFactory.createAuthenticator(con, challenges, authenticateHeader, authorizationHeader, preferedAuthentication, method.getURI())); } if (credentials != null) { // #ifdef DEBUG log.info(Messages.getString("HttpClient.settingUserCreds")); //$NON-NLS-1$ // #endif con.getAuthenticator().setCredentials(credentials); } else if (prompt != null && con.getAuthenticator().wantsPrompt()) { // #ifdef DEBUG log.info(Messages.getString("HttpClient.promptingForCreds")); //$NON-NLS-1$ // #endif if (!prompt.promptForCredentials(authenticateHeader.equals(PROXY_AUTHENTICATE), con.getAuthenticator())) { throw new AuthenticationCancelledException(); } } if (!con.canReuse()) con.reconnect(); try { return execute(request, method, con); } catch (EOFException ex) { // This was possibly caused by the connection not being reusable // This will not work with NTLM con.reconnect(); return execute(request, method, con); } } public HttpResponse execute(HttpMethod method) throws UnknownHostException, IOException, HttpException, UnsupportedAuthenticationException, AuthenticationCancelledException { // #ifdef DEBUG log.debug(MessageFormat.format(Messages.getString("HttpClient.executing"), //$NON-NLS-1$ new Object[] { method.getName() })); // #endif for (int i = 0; i < 2; i++) { try { return execute(method, connections.getConnection()); } catch (EOFException eof) { // #ifdef DEBUG if (i != 1) { log.warn(MessageFormat.format(Messages.getString("HttpClient.eof.attemptingAgain"), //$NON-NLS-1$ new Object[] { new Integer(i) })); } else { log.warn(MessageFormat.format(Messages.getString("HttpClient.eof.givingUp"), //$NON-NLS-1$ new Object[] { new Integer(i) })); } // #endif } } throw new EOFException(Messages.getString("HttpClient.couldNotConnect")); //$NON-NLS-1$ } public HttpResponse execute(HttpMethod method, HttpConnection con) throws UnknownHostException, IOException, HttpException, UnsupportedAuthenticationException, AuthenticationCancelledException { return execute(new HttpRequest(), method, con); } public HttpConnection executeAsync(AsyncHttpMethod method) throws UnknownHostException, IOException, HttpException, UnsupportedAuthenticationException, AuthenticationCancelledException { return executeAsync(method, connections.getConnection()); } public HttpConnection executeAsync(AsyncHttpMethod method, HttpConnection con) throws UnknownHostException, IOException, HttpException, UnsupportedAuthenticationException, AuthenticationCancelledException { // #ifdef DEBUG log.debug(MessageFormat.format(Messages.getString("HttpClient.executing"), //$NON-NLS-1$ new Object[] { method.getName() })); // #endif return executeAsync(new HttpRequest(), method, connections.getConnection()); } public void setIncludeCookies(boolean includeCookies) { this.includeCookies = includeCookies; } public void setPreemtiveAuthentication(boolean preemptiveAuthentication) { this.preemptiveAuthentication = preemptiveAuthentication; } public void removeAllCookies() { cookies.removeAllElements(); } }