HTTPClient.AuthorizationModule Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of grinder-httpclient Show documentation
Show all versions of grinder-httpclient Show documentation
Modified version of HTTPClient used by The Grinder. The original
can be found at http://www.innovation.ch/java/HTTPClient/.
/*
* @(#)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