All Downloads are FREE. Search and download functionalities are using the official Maven repository.

HTTPClient.AuthorizationModule Maven / Gradle / Ivy

Go to download

Modified version of HTTPClient used by The Grinder. The original can be found at http://www.innovation.ch/java/HTTPClient/.

There is a newer version: 3.11
Show newest version
/*
 * @(#)AuthorizationModule.java				0.3-3 06/05/2001
 *
 *  This file is part of the HTTPClient package
 *  Copyright (C) 1996-2001 Ronald Tschalär
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA 02111-1307, USA
 *
 *  For questions, suggestions, bug-reports, enhancement-requests etc.
 *  I may be contacted at:
 *
 *  [email protected]
 *
 *  The HTTPClient's home page is located at:
 *
 *  http://www.innovation.ch/java/HTTPClient/ 
 *
 */

package HTTPClient;

import java.io.IOException;
import java.net.ProtocolException;
import java.util.Hashtable;


/**
 * This module handles authentication requests. Authentication info is
 * preemptively sent if any suitable candidate info is available. If a
 * request returns with an appropriate status (401 or 407) then the
 * necessary info is sought from the AuthenticationInfo class.
 *
 * @version	0.3-3  06/05/2001
 * @author	Ronald Tschalär
 */
class AuthorizationModule implements HTTPClientModule
{
    /** This holds the current Proxy-Authorization-Info for each
        HTTPConnection */
    private static Hashtable proxy_cntxt_list = new Hashtable();

    /** a list of deferred authorization retries (used with
	Response.retryRequest()) */
    private static Hashtable deferred_auth_list = new Hashtable();

    /** counters for challenge and auth-info lists */
    private int	auth_lst_idx,
		prxy_lst_idx,
		auth_scm_idx,
		prxy_scm_idx;

    /** the last auth info sent, if any */
    private AuthorizationInfo auth_sent;
    private AuthorizationInfo prxy_sent;

    /** is the info in auth_sent a preemtive guess or the result of a 4xx */
    private boolean auth_from_4xx;
    private boolean prxy_from_4xx;

    /** guard against bugs on both our side and the server side */
    private int num_tries;

    /** used for deferred authoriation retries */
    private Request  saved_req;
    private Response saved_resp;


    // Constructors

    /**
     * Initialize counters for challenge and auth-info lists.
     */
    AuthorizationModule()
    {
	auth_lst_idx = 0;
	prxy_lst_idx = 0;
	auth_scm_idx = 0;
	prxy_scm_idx = 0;

	auth_sent = null;
	prxy_sent = null;

	auth_from_4xx = false;
	prxy_from_4xx = false;

	num_tries  = 0;
	saved_req  = null;
	saved_resp = null;
    }


    // Methods

    /**
     * Invoked by the HTTPClient.
     */
    public int requestHandler(Request req, Response[] resp)
		throws IOException, AuthSchemeNotImplException
    {
	HTTPConnection con = req.getConnection();
	AuthorizationHandler auth_handler = AuthorizationInfo.getAuthHandler();
	AuthorizationInfo guess;
	NVPair[] hdrs = req.getHeaders();
	int rem_idx = -1;


	// check for retries

	HttpOutputStream out = req.getStream();
	if (out != null  &&  deferred_auth_list.get(out) != null)
	{
	    copyFrom((AuthorizationModule) deferred_auth_list.remove(out));
	    req.copyFrom(saved_req);

	    Log.write(Log.AUTH, "AuthM: Handling deferred auth challenge");

	    handle_auth_challenge(req, saved_resp);

	    if (auth_sent != null)
		Log.write(Log.AUTH, "AuthM: Sending request with " +
				    "Authorization '" + auth_sent + "'");
	    else
		Log.write(Log.AUTH, "AuthM: Sending request with " +
				    "Proxy-Authorization '" + prxy_sent +
				    "'");

	    return REQ_RESTART;
	}


	// Preemptively send proxy authorization info

	Proxy: if (con.getProxyHost() != null  &&  !prxy_from_4xx)
	{
	    // first remove any Proxy-Auth header that still may be around

	    for (int idx=0; idx= 0)
	{
	    System.arraycopy(hdrs, rem_idx+1, hdrs, rem_idx, hdrs.length-rem_idx-1);
	    hdrs = Util.resizeArray(hdrs, hdrs.length-1);
	    req.setHeaders(hdrs);
	}


	// Preemptively send authorization info

	rem_idx = -1;
	Auth: if (!auth_from_4xx)
	{
	    // first remove any Auth header that still may be around

	    for (int idx=0; idx= 0)
	{
	    System.arraycopy(hdrs, rem_idx+1, hdrs, rem_idx, hdrs.length-rem_idx-1);
	    hdrs = Util.resizeArray(hdrs, hdrs.length-1);
	    req.setHeaders(hdrs);
	}

	return REQ_CONTINUE;
    }


    /**
     * Invoked by the HTTPClient.
     */
    public void responsePhase1Handler(Response resp, RoRequest req)
		throws IOException
    {
	/* If auth info successful update path list. Note: if we
	 * preemptively sent auth info we don't actually know if
	 * it was necessary. Therefore we don't update the path
	 * list in this case; this prevents it from being
	 * contaminated. If the info was necessary, then the next
	 * time we access this resource we will again guess the
	 * same info and send it.
	 */
	if (resp.getStatusCode() != 401  &&  resp.getStatusCode() != 407)
	{
	    if (auth_sent != null  &&  auth_from_4xx)
	    {
		try
		{
		    AuthorizationInfo.getAuthorization(auth_sent, req, resp,
				    false).addPath(req.getRequestURI());
		}
		catch (AuthSchemeNotImplException asnie)
		    { /* shouldn't happen */ }
	    }

	    // reset guard if not an auth challenge
	    num_tries = 0;
	}

	auth_from_4xx = false;
	prxy_from_4xx = false;

	if (resp.getHeader("WWW-Authenticate") == null)
	{
	    auth_lst_idx = 0;
	    auth_scm_idx = 0;
	}

	if (resp.getHeader("Proxy-Authenticate") == null)
	{
	    prxy_lst_idx = 0;
	    prxy_scm_idx = 0;
	}
    }


    /**
     * Invoked by the HTTPClient.
     */
    public int responsePhase2Handler(Response resp, Request req)
		throws IOException, AuthSchemeNotImplException
    {
	// Let the AuthHandler handle any Authentication headers.

	AuthorizationHandler h = AuthorizationInfo.getAuthHandler();
	if (h != null)
	    h.handleAuthHeaders(resp, req, auth_sent, prxy_sent);


	// handle 401 and 407 response codes

	int sts  = resp.getStatusCode();
	switch(sts)
	{
	    case 401: // Unauthorized
	    case 407: // Proxy Authentication Required

		// guard against infinite retries due to bugs

		num_tries++;
		if (num_tries > 10)
		    throw new ProtocolException("Bug in authorization handling: server refused the given info 10 times");


		// defer handling if a stream was used

		if (req.getStream() != null)
		{
		    if (!HTTPConnection.deferStreamed)
		    {
			Log.write(Log.AUTH, "AuthM: status " + sts +
					    " not handled - request has " +
					    "an output stream");
			return RSP_CONTINUE;
		    }

		    saved_req  = (Request)  req.clone();
		    saved_resp = (Response) resp.clone();
		    deferred_auth_list.put(req.getStream(), this);

		    req.getStream().reset();
		    resp.setRetryRequest(true);

		    Log.write(Log.AUTH, "AuthM: Handling of status " +
					sts + " deferred because an " +
					"output stream was used");

		    return RSP_CONTINUE;
		}


		// handle the challenge

		Log.write(Log.AUTH, "AuthM: Handling status: " + sts + " " +
				    resp.getReasonLine());

		handle_auth_challenge(req, resp);


		// check for valid challenge

		if (auth_sent != null  ||  prxy_sent != null)
		{
		    try { resp.getInputStream().close(); }
		    catch (IOException ioe) { }

		    if (auth_sent != null)
			Log.write(Log.AUTH, "AuthM: Resending request " +
					    "with Authorization '" +
					    auth_sent + "'");
		    else
			Log.write(Log.AUTH, "AuthM: Resending request " +
					    "with Proxy-Authorization '" +
					    prxy_sent + "'");

		    return RSP_REQUEST;
		}


		if (req.getStream() != null)
		    Log.write(Log.AUTH, "AuthM: status " + sts + " not " +
				        "handled - request has an output " +
				        "stream");
		else
		    Log.write(Log.AUTH, "AuthM: No Auth Info found - " +
				        "status " + sts + " not handled");

		return RSP_CONTINUE;

	    default:

		return RSP_CONTINUE;
	}
    }


    /**
     * Invoked by the HTTPClient.
     */
    public void responsePhase3Handler(Response resp, RoRequest req)
    {
    }


    /**
     * Invoked by the HTTPClient.
     */
    public void trailerHandler(Response resp, RoRequest req)  throws IOException
    {
	// Let the AuthHandler handle any Authentication headers.

	AuthorizationHandler h = AuthorizationInfo.getAuthHandler();
	if (h != null)
	    h.handleAuthTrailers(resp, req, auth_sent, prxy_sent);
    }


    /**
     *
     */
    private void handle_auth_challenge(Request req, Response resp)
	    throws AuthSchemeNotImplException, IOException
    {
	// handle WWW-Authenticate

	int[] idx_arr = { auth_lst_idx,	// hack to pass by ref
			  auth_scm_idx};
	auth_sent = setAuthHeaders(resp.getHeader("WWW-Authenticate"),
				   req, resp, "Authorization", idx_arr,
				   auth_sent);
	if (auth_sent != null)
	{
	    auth_from_4xx = true;
	    auth_lst_idx = idx_arr[0];
	    auth_scm_idx = idx_arr[1];
	}
	else
	{
	    auth_lst_idx = 0;
	    auth_scm_idx = 0;
	}


	// handle Proxy-Authenticate

	idx_arr[0] = prxy_lst_idx;	// hack to pass by ref
	idx_arr[1] = prxy_scm_idx;
	prxy_sent = setAuthHeaders(resp.getHeader("Proxy-Authenticate"),
				   req, resp, "Proxy-Authorization",
				   idx_arr, prxy_sent);
	if (prxy_sent != null)
	{
	    prxy_from_4xx = true;
	    prxy_lst_idx = idx_arr[0];
	    prxy_scm_idx = idx_arr[1];
	}
	else
	{
	    prxy_lst_idx = 0;
	    prxy_scm_idx = 0;
	}

	if (prxy_sent != null)
	{
	    HTTPConnection con = req.getConnection();
	    Util.getList(proxy_cntxt_list, con.getContext())
		.put(con.getProxyHost()+":"+con.getProxyPort(),
		     prxy_sent);
	}

	// check for headers

	if (auth_sent == null  &&  prxy_sent == null  &&
	    resp.getHeader("WWW-Authenticate") == null  &&
	    resp.getHeader("Proxy-Authenticate") == null)
	{
	    if (resp.getStatusCode() == 401)
		throw new ProtocolException("Missing WWW-Authenticate header");
	    else
		throw new ProtocolException("Missing Proxy-Authenticate header");
	}
    }


    /**
     * Handles authentication requests and sets the authorization headers.
     * It tries to retrieve the neccessary parameters from AuthorizationInfo,
     * and failing that calls the AuthHandler. Handles multiple authentication
     * headers.
     *
     * @param  auth_str the authentication header field returned by the server.
     * @param  req      the Request used
     * @param  resp     the full Response received
     * @param  header   the header name to use in the new headers array.
     * @param  idx_arr  an array of indicies holding the state of where we
     *                  are when handling multiple authorization headers.
     * @param  prev     the previous auth info sent, or null if none
     * @return the new credentials, or null if none found
     * @exception ProtocolException if auth_str is null.
     * @exception AuthSchemeNotImplException if thrown by the AuthHandler.
     * @exception IOException if thrown by the AuthHandler.
     */
    private AuthorizationInfo setAuthHeaders(String auth_str, Request req,
					     RoResponse resp, String header,
					     int[] idx_arr,
					     AuthorizationInfo prev)
	throws ProtocolException, AuthSchemeNotImplException, IOException
    {
	if (auth_str == null)  return null;

	// get the list of challenges the server sent
	AuthorizationInfo[] challenges =
			AuthorizationInfo.parseAuthString(auth_str, req, resp);

	if (Log.isEnabled(Log.AUTH))
	{
	    Log.write(Log.AUTH, "AuthM: parsed " + challenges.length +
			        " challenges:");
	    for (int idx=0; idx= challenges.length)
		    idx_arr[1] = 0;

		try
		{
		    credentials = AuthorizationInfo.queryAuthHandler(
					    challenges[idx_arr[1]], req, resp);
		    break;
		}
		catch (AuthSchemeNotImplException asnie)
		{
		    if (idx == challenges.length-1)
			throw asnie;
		}
		finally
		    { idx_arr[1]++; }
	    }
	}

	// if we still don't have any credentials then give up
	if (credentials == null)
	    return null;

	// find auth info
	int auth_idx;
	NVPair[] hdrs = req.getHeaders();
	for (auth_idx=0; auth_idx




© 2015 - 2024 Weber Informatics LLC | Privacy Policy