You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by jg...@apache.org on 2007/09/27 04:01:57 UTC

svn commit: r579867 [1/3] - in /geronimo/sandbox/AsyncHttpClient/src: main/java/org/apache/ahc/auth/ main/java/org/apache/ahc/codec/ main/java/org/apache/ahc/util/ test/catalina/webapps/auth_basic/ test/catalina/webapps/auth_basic/WEB-INF/ test/catalin...

Author: jgenender
Date: Wed Sep 26 19:01:53 2007
New Revision: 579867

URL: http://svn.apache.org/viewvc?rev=579867&view=rev
Log:
Add authentcation...digest and basic tested...NTLM is not

Added:
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthChallengeParser.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthPolicy.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthScheme.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthSchemeBase.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthScope.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthState.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthenticationException.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/BasicScheme.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/Credentials.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/DigestScheme.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/InvalidCredentialsException.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/MalformedChallengeException.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/NTCredentials.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/NTLM.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/NTLMScheme.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/RFC2617Scheme.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/UsernamePasswordCredentials.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/ParameterFormatter.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/ParameterParser.java
    geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_basic/
    geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_basic/WEB-INF/
    geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_basic/WEB-INF/web.xml
    geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_basic/secure.jsp
    geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_digest/
    geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_digest/WEB-INF/
    geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_digest/WEB-INF/web.xml
    geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_digest/secure.jsp
    geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/AuthTest.java
    geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/FakeRealm.java
Modified:
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpDecoder.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpIoHandler.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpMessage.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestEncoder.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpResponseMessage.java
    geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/AbstractTest.java

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthChallengeParser.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthChallengeParser.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthChallengeParser.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthChallengeParser.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,113 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.ahc.auth;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+
+import org.apache.ahc.util.NameValuePair;
+import org.apache.ahc.util.ParameterParser;
+
+public class AuthChallengeParser {
+        /**
+     * Extracts authentication scheme from the given authentication
+     * challenge.
+     *
+     * @param challengeStr the authentication challenge string
+     * @return authentication scheme
+     *
+     * @throws MalformedChallengeException when the authentication challenge string
+     *  is malformed
+     *
+     * @since 2.0beta1
+     */
+    public static String extractScheme(final String challengeStr)
+      throws MalformedChallengeException {
+        if (challengeStr == null) {
+            throw new IllegalArgumentException("Challenge may not be null");
+        }
+        int idx = challengeStr.indexOf(' ');
+        String s = null;
+        if (idx == -1) {
+            s = challengeStr;
+        } else {
+            s = challengeStr.substring(0, idx);
+        }
+        if (s.equals("")) {
+            throw new MalformedChallengeException("Invalid challenge: " + challengeStr);
+        }
+        return s.toLowerCase();
+    }
+
+    /**
+     * Extracts a map of challenge parameters from an authentication challenge.
+     * Keys in the map are lower-cased
+     *
+     * @param challengeStr the authentication challenge string
+     * @return a map of authentication challenge parameters
+     * @throws MalformedChallengeException when the authentication challenge string
+     *  is malformed
+     *
+     * @since 2.0beta1
+     */
+    public static Map extractParams(final String challengeStr)
+      throws MalformedChallengeException {
+        if (challengeStr == null) {
+            throw new IllegalArgumentException("Challenge may not be null");
+        }
+        int idx = challengeStr.indexOf(' ');
+        if (idx == -1) {
+            throw new MalformedChallengeException("Invalid challenge: " + challengeStr);
+        }
+        Map map = new HashMap();
+        ParameterParser parser = new ParameterParser();
+        List params = parser.parse( challengeStr.substring(idx + 1, challengeStr.length()), ',');
+        for (int i = 0; i < params.size(); i++) {
+            NameValuePair param = (NameValuePair) params.get(i);
+            map.put(param.getName().toLowerCase(), param.getValue());
+        }
+        return map;
+    }
+
+    /**
+     * Extracts a map of challenges ordered by authentication scheme name
+     *
+     * @param headers the array of authorization challenges
+     * @return a map of authorization challenges
+     * 
+     * @throws MalformedChallengeException if any of challenge strings
+     *  is malformed
+     */
+//    public static Map parseChallenges(final Header[] headers)
+//      throws MalformedChallengeException {
+//        if (headers == null) {
+//            throw new IllegalArgumentException("Array of challenges may not be null");
+//        }
+//        String challenge = null;
+//        Map challengemap = new HashMap(headers.length);
+//        for (int i = 0; i < headers.length; i++) {
+//            challenge = headers[i].getValue();
+//            String s = AuthChallengeParser.extractScheme(challenge);
+//            challengemap.put(s, challenge);
+//        }
+//        return challengemap;
+//   }
+}

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthPolicy.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthPolicy.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthPolicy.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthPolicy.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,157 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.ahc.auth;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AuthPolicy {
+    private static final HashMap SCHEMES = new HashMap();
+    private static final ArrayList SCHEME_LIST = new ArrayList();
+
+    /**
+     * The key used to look up the list of IDs of supported {@link AuthScheme
+     * authentication schemes} in their order of preference. The scheme IDs are
+     * stored in a {@link java.util.Collection} as {@link java.lang.String}s.
+     * <p/>
+     * <p/>
+     * If several schemes are returned in the <tt>WWW-Authenticate</tt>
+     * or <tt>Proxy-Authenticate</tt> header, this parameter defines which
+     * {@link AuthScheme authentication schemes} takes precedence over others.
+     * The first item in the collection represents the most preferred
+     * {@link AuthScheme authentication scheme}, the last item represents the ID
+     * of the least preferred one.
+     * </p>
+     *
+     */
+    public static final String AUTH_SCHEME_PRIORITY = "http.auth.scheme-priority";
+
+    /**
+     * The NTLM scheme is a proprietary Microsoft Windows Authentication
+     * protocol (considered to be the most secure among currently supported
+     * authentication schemes).
+     */
+    public static final String NTLM = "NTLM";
+
+    /**
+     * Digest authentication scheme as defined in RFC2617.
+     */
+    public static final String DIGEST = "Digest";
+
+    /**
+     * Basic authentication scheme as defined in RFC2617 (considered inherently
+     * insecure, but most widely supported)
+     */
+    public static final String BASIC = "Basic";
+
+    static {
+        AuthPolicy.registerAuthScheme(NTLM, NTLMScheme.class);
+        AuthPolicy.registerAuthScheme(DIGEST, DigestScheme.class);
+        AuthPolicy.registerAuthScheme(BASIC, BasicScheme.class);
+    }
+
+    /**
+     * Log object.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(AuthPolicy.class);
+
+    /**
+     * Registers a class implementing an {@link AuthScheme authentication scheme} with
+     * the given identifier. If a class with the given ID already exists it will be overridden.
+     * This ID is the same one used to retrieve the {@link AuthScheme authentication scheme}
+     * from {@link #getAuthScheme(String)}.
+     * <p/>
+     * <p/>
+     * Please note that custom authentication preferences, if used, need to be updated accordingly
+     * for the new {@link AuthScheme authentication scheme} to take effect.
+     * </p>
+     *
+     * @param id    the identifier for this scheme
+     * @param clazz the class to register
+     * @see #getAuthScheme(String)
+     * @see #AUTH_SCHEME_PRIORITY
+     */
+    public static synchronized void registerAuthScheme(final String id, Class clazz) {
+        if (id == null) {
+            throw new IllegalArgumentException("Id may not be null");
+        }
+        if (clazz == null) {
+            throw new IllegalArgumentException("Authentication scheme class may not be null");
+        }
+        SCHEMES.put(id.toLowerCase(), clazz);
+        SCHEME_LIST.add(id.toLowerCase());
+    }
+
+    /**
+     * Unregisters the class implementing an {@link AuthScheme authentication scheme} with
+     * the given ID.
+     *
+     * @param id the ID of the class to unregister
+     */
+    public static synchronized void unregisterAuthScheme(final String id) {
+        if (id == null) {
+            throw new IllegalArgumentException("Id may not be null");
+        }
+        SCHEMES.remove(id.toLowerCase());
+        SCHEME_LIST.remove(id.toLowerCase());
+    }
+
+    /**
+     * Gets the {@link AuthScheme authentication scheme} with the given ID.
+     *
+     * @param id the {@link AuthScheme authentication scheme} ID
+     * @return {@link AuthScheme authentication scheme}
+     * @throws IllegalStateException if a scheme with the ID cannot be found
+     */
+    public static synchronized AuthScheme getAuthScheme(final String id)
+        throws IllegalStateException {
+
+        if (id == null) {
+            throw new IllegalArgumentException("Id may not be null");
+        }
+        Class clazz = (Class)SCHEMES.get(id.toLowerCase());
+        if (clazz != null) {
+            try {
+                return (AuthScheme)clazz.newInstance();
+            } catch (Exception e) {
+                LOG.error("Error initializing authentication scheme: " + id, e);
+                throw new IllegalStateException(id +
+                    " authentication scheme implemented by " +
+                    clazz.getName() + " could not be initialized");
+            }
+        } else {
+            throw new IllegalStateException("Unsupported authentication scheme " + id);
+        }
+    }
+
+    /**
+     * Returns a list containing all registered {@link AuthScheme authentication
+     * schemes} in their default order.
+     *
+     * @return {@link AuthScheme authentication scheme}
+     */
+    public static synchronized List getDefaultAuthPrefs() {
+        return (List)SCHEME_LIST.clone();
+    }
+}

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthScheme.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthScheme.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthScheme.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthScheme.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,91 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.ahc.auth;
+
+import org.apache.ahc.codec.HttpRequestMessage;
+
+public interface AuthScheme {
+    /**
+     * Processes the given challenge token. Some authentication schemes
+     * may involve multiple challenge-response exchanges. Such schemes must be able
+     * to maintain the state information when dealing with sequential challenges
+     *
+     * @param challenge the challenge string
+     */
+    void processChallenge(final String challenge) throws MalformedChallengeException;
+
+    /**
+     * Returns textual designation of the given authentication scheme.
+     *
+     * @return the name of the given authentication scheme
+     */
+    String getSchemeName();
+
+    /**
+     * Returns authentication parameter with the given name, if available.
+     *
+     * @param name The name of the parameter to be returned
+     *
+     * @return the parameter with the given name
+     */
+    String getParameter(final String name);
+
+    /**
+     * Returns authentication realm. If the concept of an authentication
+     * realm is not applicable to the given authentication scheme, returns
+     * <code>null</code>.
+     *
+     * @return the authentication realm
+     */
+    String getRealm();
+
+    /**
+     * Tests if the authentication scheme is provides authorization on a per
+     * connection basis instead of usual per request basis
+     *
+     * @return <tt>true</tt> if the scheme is connection based, <tt>false</tt>
+     * if the scheme is request based.
+     */
+    boolean isConnectionBased();
+
+    /**
+     * Authentication process may involve a series of challenge-response exchanges.
+     * This method tests if the authorization process has been completed, either
+     * successfully or unsuccessfully, that is, all the required authorization
+     * challenges have been processed in their entirety.
+     *
+     * @return <tt>true</tt> if the authentication process has been completed,
+     * <tt>false</tt> otherwise.
+     */
+    boolean isComplete();
+
+    /**
+     * Produces an authorization string for the given set of {@link Credentials}.
+     *
+     * @param credentials The set of credentials to be used for athentication
+     * @param method The method being authenticated
+     * @throws AuthenticationException if authorization string cannot
+     *   be generated due to an authentication failure
+     *
+     * @return the authorization string
+     */
+    String authenticate(Credentials credentials, HttpRequestMessage method) throws AuthenticationException;
+
+}

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthSchemeBase.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthSchemeBase.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthSchemeBase.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthSchemeBase.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,72 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.ahc.auth;
+
+public class AuthSchemeBase {
+        /**
+     * Original challenge string as received from the server.
+     */
+    private String challenge = null;
+
+    /**
+     * Constructor for an abstract authetication schemes.
+     *
+     * @param challenge authentication challenge
+     *
+     * @throws MalformedChallengeException is thrown if the authentication challenge
+     * is malformed
+     *
+     * @deprecated Use parameterless constructor and {@link AuthScheme#processChallenge(String)}
+     *             method
+     */
+    public AuthSchemeBase(final String challenge)
+      throws MalformedChallengeException {
+        super();
+        if (challenge == null) {
+            throw new IllegalArgumentException("Challenge may not be null");
+        }
+        this.challenge = challenge;
+    }
+
+    /**
+     * @see java.lang.Object#equals(Object)
+     */
+    public boolean equals(Object obj) {
+        if (obj instanceof AuthSchemeBase) {
+            return this.challenge.equals(((AuthSchemeBase) obj).challenge);
+        } else {
+            return super.equals(obj);
+        }
+    }
+
+    /**
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode() {
+        return this.challenge.hashCode();
+    }
+
+    /**
+     * @see java.lang.Object#toString()
+     */
+    public String toString() {
+        return this.challenge;
+    }
+}

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthScope.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthScope.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthScope.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthScope.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,302 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.ahc.auth;
+
+import org.apache.ahc.util.LangUtils;
+
+public class AuthScope {
+    /**
+     * The <tt>null</tt> value represents any host. In the future versions of
+     * HttpClient the use of this parameter will be discontinued.
+     */
+    public static final String ANY_HOST = null;
+
+    /**
+     * The <tt>-1</tt> value represents any port.
+     */
+    public static final int ANY_PORT = -1;
+
+    /**
+     * The <tt>null</tt> value represents any realm.
+     */
+    public static final String ANY_REALM = null;
+
+    /**
+     * The <tt>null</tt> value represents any authentication scheme.
+     */
+    public static final String ANY_SCHEME = null;
+
+    /**
+     * Default scope matching any host, port, realm and authentication scheme.
+     * In the future versions of HttpClient the use of this parameter will be
+     * discontinued.
+     */
+    public static final AuthScope ANY = new AuthScope(ANY_HOST, ANY_PORT, ANY_REALM, ANY_SCHEME);
+
+    /**
+     * The authentication scheme the credentials apply to.
+     */
+    private String scheme = null;
+
+    /**
+     * The realm the credentials apply to.
+     */
+    private String realm = null;
+
+    /**
+     * The host the credentials apply to.
+     */
+    private String host = null;
+
+    /**
+     * The port the credentials apply to.
+     */
+    private int port = -1;
+
+    /**
+     * Creates a new credentials scope for the given
+     * <tt>host</tt>, <tt>port</tt>, <tt>realm</tt>, and
+     * <tt>authentication scheme</tt>.
+     *
+     * @param host   the host the credentials apply to. May be set
+     *               to <tt>null</tt> if credenticals are applicable to
+     *               any host.
+     * @param port   the port the credentials apply to. May be set
+     *               to negative value if credenticals are applicable to
+     *               any port.
+     * @param realm  the realm the credentials apply to. May be set
+     *               to <tt>null</tt> if credenticals are applicable to
+     *               any realm.
+     * @param scheme the authentication scheme the credentials apply to.
+     *               May be set to <tt>null</tt> if credenticals are applicable to
+     *               any authentication scheme.
+     */
+    public AuthScope(final String host, int port,
+                     final String realm, final String scheme) {
+        this.host = (host == null) ? ANY_HOST : host.toLowerCase();
+        this.port = (port < 0) ? ANY_PORT : port;
+        this.realm = (realm == null) ? ANY_REALM : realm;
+        this.scheme = (scheme == null) ? ANY_SCHEME : scheme.toUpperCase();
+        ;
+    }
+
+    /**
+     * Creates a new credentials scope for the given
+     * <tt>host</tt>, <tt>port</tt>, <tt>realm</tt>, and any
+     * authentication scheme.
+     *
+     * @param host  the host the credentials apply to. May be set
+     *              to <tt>null</tt> if credenticals are applicable to
+     *              any host.
+     * @param port  the port the credentials apply to. May be set
+     *              to negative value if credenticals are applicable to
+     *              any port.
+     * @param realm the realm the credentials apply to. May be set
+     *              to <tt>null</tt> if credenticals are applicable to
+     *              any realm.
+     */
+    public AuthScope(final String host, int port, final String realm) {
+        this(host, port, realm, ANY_SCHEME);
+    }
+
+    /**
+     * Creates a new credentials scope for the given
+     * <tt>host</tt>, <tt>port</tt>, any realm name, and any
+     * authentication scheme.
+     *
+     * @param host the host the credentials apply to. May be set
+     *             to <tt>null</tt> if credenticals are applicable to
+     *             any host.
+     * @param port the port the credentials apply to. May be set
+     *             to negative value if credenticals are applicable to
+     *             any port.
+     */
+    public AuthScope(final String host, int port) {
+        this(host, port, ANY_REALM, ANY_SCHEME);
+    }
+
+    /**
+     * Creates a copy of the given credentials scope.
+     */
+    public AuthScope(final AuthScope authscope) {
+        super();
+        if (authscope == null) {
+            throw new IllegalArgumentException("Scope may not be null");
+        }
+        this.host = authscope.getHost();
+        this.port = authscope.getPort();
+        this.realm = authscope.getRealm();
+        this.scheme = authscope.getScheme();
+    }
+
+    /**
+     * @return the host
+     */
+    public String getHost() {
+        return this.host;
+    }
+
+    /**
+     * @return the port
+     */
+    public int getPort() {
+        return this.port;
+    }
+
+    /**
+     * @return the realm name
+     */
+    public String getRealm() {
+        return this.realm;
+    }
+
+    /**
+     * @return the scheme type
+     */
+    public String getScheme() {
+        return this.scheme;
+    }
+
+    /**
+     * Determines if the given parameters are equal.
+     *
+     * @param p1 the parameter
+     * @param p2 the other parameter
+     * @return boolean true if the parameters are equal, otherwise false.
+     */
+    private static boolean paramsEqual(final String p1, final String p2) {
+        if (p1 == null) {
+            return p1 == p2;
+        } else {
+            return p1.equals(p2);
+        }
+    }
+
+    /**
+     * Determines if the given parameters are equal.
+     *
+     * @param p1 the parameter
+     * @param p2 the other parameter
+     * @return boolean true if the parameters are equal, otherwise false.
+     */
+    private static boolean paramsEqual(int p1, int p2) {
+        return p1 == p2;
+    }
+
+    /**
+     * Tests if the authentication scopes match.
+     *
+     * @return the match factor. Negative value signifies no match.
+     *         Non-negative signifies a match. The greater the returned value
+     *         the closer the match.
+     */
+    public int match(final AuthScope that) {
+        int factor = 0;
+        if (paramsEqual(this.scheme, that.scheme)) {
+            factor += 1;
+        } else {
+            if (this.scheme != ANY_SCHEME && that.scheme != ANY_SCHEME) {
+                return -1;
+            }
+        }
+        if (paramsEqual(this.realm, that.realm)) {
+            factor += 2;
+        } else {
+            if (this.realm != ANY_REALM && that.realm != ANY_REALM) {
+                return -1;
+            }
+        }
+        if (paramsEqual(this.port, that.port)) {
+            factor += 4;
+        } else {
+            if (this.port != ANY_PORT && that.port != ANY_PORT) {
+                return -1;
+            }
+        }
+        if (paramsEqual(this.host, that.host)) {
+            factor += 8;
+        } else {
+            if (this.host != ANY_HOST && that.host != ANY_HOST) {
+                return -1;
+            }
+        }
+        return factor;
+    }
+
+    /**
+     * @see java.lang.Object#equals(Object)
+     */
+    public boolean equals(Object o) {
+        if (o == null) {
+            return false;
+        }
+        if (o == this) {
+            return true;
+        }
+        if (!(o instanceof AuthScope)) {
+            return super.equals(o);
+        }
+        AuthScope that = (AuthScope)o;
+        return
+            paramsEqual(this.host, that.host)
+                && paramsEqual(this.port, that.port)
+                && paramsEqual(this.realm, that.realm)
+                && paramsEqual(this.scheme, that.scheme);
+    }
+
+    /**
+     * @see java.lang.Object#toString()
+     */
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        if (this.scheme != null) {
+            buffer.append(this.scheme.toUpperCase());
+            buffer.append(' ');
+        }
+        if (this.realm != null) {
+            buffer.append('\'');
+            buffer.append(this.realm);
+            buffer.append('\'');
+        } else {
+            buffer.append("<any realm>");
+        }
+        if (this.host != null) {
+            buffer.append('@');
+            buffer.append(this.host);
+            if (this.port >= 0) {
+                buffer.append(':');
+                buffer.append(this.port);
+            }
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode() {
+        int hash = LangUtils.HASH_SEED;
+        hash = LangUtils.hashCode(hash, this.host);
+        hash = LangUtils.hashCode(hash, this.port);
+        hash = LangUtils.hashCode(hash, this.realm);
+        hash = LangUtils.hashCode(hash, this.scheme);
+        return hash;
+    }
+}

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthState.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthState.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthState.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthState.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,175 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.ahc.auth;
+
+public class AuthState {
+    public static final String PREEMPTIVE_AUTH_SCHEME = "basic";
+
+    /** Actual authentication scheme */
+    private AuthScheme authScheme = null;
+
+    /** Whether an authetication challenged has been received */
+    private boolean authRequested = false;
+
+    /** Whether the authetication challenge has been responsed to */
+    private boolean authAttempted = false;
+
+    /** Whether preemtive authentication is attempted */
+    private boolean preemptive  = false;
+
+    /**
+     * Default constructor.
+     *
+     */
+    public AuthState() {
+        super();
+    }
+
+    /**
+     * Invalidates the authentication state by resetting its parameters.
+     */
+    public void invalidate() {
+        this.authScheme = null;
+        this.authRequested = false;
+        this.authAttempted = false;
+        this.preemptive = false;
+    }
+
+    /**
+     * Tests whether authenication challenge has been received
+     *
+     * @return <tt>true</tt> if authenication challenge has been received,
+     *  <tt>false</tt> otherwise
+     */
+    public boolean isAuthRequested() {
+        return this.authRequested;
+    }
+
+    /**
+     * Sets authentication request status
+     *
+     * @param challengeReceived <tt>true</tt> if authenication has been requested,
+     *  <tt>false</tt> otherwise
+     */
+    public void setAuthRequested(boolean challengeReceived) {
+        this.authRequested = challengeReceived;
+    }
+
+    /**
+     * Tests whether authenication challenge has been responsed to
+     *
+     * @return <tt>true</tt> if authenication challenge has been responsed to,
+     *  <tt>false</tt> otherwise
+     */
+    public boolean isAuthAttempted() {
+        return this.authAttempted;
+    }
+
+    /**
+     * Sets authentication attempt status
+     *
+     * @param challengeResponded <tt>true</tt> if authenication has been attempted,
+     *  <tt>false</tt> otherwise
+     */
+    public void setAuthAttempted(boolean challengeResponded) {
+        this.authAttempted = challengeResponded;
+    }
+
+    /**
+     * Preemptively assigns Basic authentication scheme.
+     */
+    public void setPreemptive() {
+        if (!this.preemptive) {
+            if (this.authScheme != null) {
+                throw new IllegalStateException("Authentication state already initialized");
+            }
+            this.authScheme = AuthPolicy.getAuthScheme(PREEMPTIVE_AUTH_SCHEME);
+            this.preemptive = true;
+        }
+    }
+
+    /**
+     * Tests if preemptive authentication is used.
+     *
+     * @return <tt>true</tt> if using the default Basic {@link AuthScheme
+     * authentication scheme}, <tt>false</tt> otherwise.
+     */
+    public boolean isPreemptive() {
+        return this.preemptive;
+    }
+
+    /**
+     * Assigns the given {@link AuthScheme authentication scheme}.
+     *
+     * @param authScheme the {@link AuthScheme authentication scheme}
+     */
+    public void setAuthScheme(final AuthScheme authScheme) {
+        if (authScheme == null) {
+            invalidate();
+            return;
+        }
+        if (this.preemptive && !(this.authScheme.getClass().isInstance(authScheme))) {
+            this.preemptive = false;
+            this.authAttempted = false;
+        }
+        this.authScheme = authScheme;
+    }
+
+    /**
+     * Returns the {@link AuthScheme authentication scheme}.
+     *
+     * @return {@link AuthScheme authentication scheme}
+     */
+    public AuthScheme getAuthScheme() {
+        return authScheme;
+    }
+
+    /**
+     * Returns the authentication realm.
+     *
+     * @return the name of the authentication realm
+     */
+    public String getRealm() {
+        if (this.authScheme != null) {
+            return this.authScheme.getRealm();
+        } else {
+            return null;
+        }
+    }
+
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("Auth state: auth requested [");
+        buffer.append(this.authRequested);
+        buffer.append("]; auth attempted [");
+        buffer.append(this.authAttempted);
+        if (this.authScheme != null) {
+            buffer.append("]; auth scheme [");
+            buffer.append(this.authScheme.getSchemeName());
+            buffer.append("]; realm [");
+            buffer.append(this.authScheme.getRealm());
+        }
+        buffer.append("] preemptive [");
+        buffer.append(this.preemptive);
+        buffer.append("]");
+        return buffer.toString();
+    }
+
+}

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthenticationException.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthenticationException.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthenticationException.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/AuthenticationException.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,41 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.ahc.auth;
+
+import org.apache.ahc.AsyncHttpClient;
+
+public class AuthenticationException extends Exception{
+
+    public AuthenticationException() {
+        super();
+    }
+
+    public AuthenticationException(String string) {
+        super(string);
+    }
+
+    public AuthenticationException(String string, Throwable throwable) {
+        super(string, throwable);
+    }
+
+    public AuthenticationException(Throwable throwable) {
+        super(throwable);
+    }
+}

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/BasicScheme.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/BasicScheme.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/BasicScheme.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/BasicScheme.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,156 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.ahc.auth;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.ahc.util.EncodingUtil;
+import org.apache.ahc.codec.HttpRequestMessage;
+
+public class BasicScheme extends RFC2617Scheme{
+        /** Log object for this class. */
+    private static final Logger LOG = LoggerFactory.getLogger(BasicScheme.class);
+
+    /** Whether the basic authentication process is complete */
+    private boolean complete;
+
+    /**
+     * Default constructor for the basic authetication scheme.
+     *
+     * @since 3.0
+     */
+    public BasicScheme() {
+        super();
+        this.complete = false;
+    }
+
+    /**
+     * Returns textual designation of the basic authentication scheme.
+     *
+     * @return <code>basic</code>
+     */
+    public String getSchemeName() {
+        return "basic";
+    }
+
+    /**
+     * Processes the Basic challenge.
+     *
+     * @param challenge the challenge string
+     *
+     * @throws MalformedChallengeException is thrown if the authentication challenge
+     * is malformed
+     *
+     * @since 3.0
+     */
+    public void processChallenge(String challenge)
+        throws MalformedChallengeException
+    {
+        super.processChallenge(challenge);
+        this.complete = true;
+    }
+
+    /**
+     * Tests if the Basic authentication process has been completed.
+     *
+     * @return <tt>true</tt> if Basic authorization has been processed,
+     *   <tt>false</tt> otherwise.
+     *
+     * @since 3.0
+     */
+    public boolean isComplete() {
+        return this.complete;
+    }
+
+    /**
+     * Returns <tt>false</tt>. Basic authentication scheme is request based.
+     *
+     * @return <tt>false</tt>.
+     *
+     * @since 3.0
+     */
+    public boolean isConnectionBased() {
+        return false;
+    }
+
+    /**
+     * Produces basic authorization string for the given set of {@link Credentials}.
+     *
+     * @param credentials The set of credentials to be used for athentication
+     * @param request The request being authenticated
+     * @throws InvalidCredentialsException if authentication credentials
+     *         are not valid or not applicable for this authentication scheme
+     * @throws AuthenticationException if authorization string cannot
+     *   be generated due to an authentication failure
+     *
+     * @return a basic authorization string
+     *
+     * @since 3.0
+     */
+    public String authenticate(Credentials credentials, HttpRequestMessage request) throws AuthenticationException {
+
+        LOG.trace("enter BasicScheme.authenticate(Credentials, HttpMethod)");
+
+        if (request == null) {
+            throw new IllegalArgumentException("Request may not be null");
+        }
+        UsernamePasswordCredentials usernamepassword = null;
+        try {
+            usernamepassword = (UsernamePasswordCredentials) credentials;
+        } catch (ClassCastException e) {
+            throw new InvalidCredentialsException(
+                    "Credentials cannot be used for basic authentication: "
+                    + credentials.getClass().getName());
+        }
+        return BasicScheme.authenticate( usernamepassword, request.getCredentialCharset());
+    }
+
+
+    /**
+     * Returns a basic <tt>Authorization</tt> header value for the given
+     * {@link UsernamePasswordCredentials} and charset.
+     *
+     * @param credentials The credentials to encode.
+     * @param charset The charset to use for encoding the credentials
+     *
+     * @return a basic authorization string
+     *
+     * @since 3.0
+     */
+    public static String authenticate(UsernamePasswordCredentials credentials, String charset) {
+
+        LOG.trace("enter BasicScheme.authenticate(UsernamePasswordCredentials, String)");
+
+        if (credentials == null) {
+            throw new IllegalArgumentException("Credentials may not be null");
+        }
+        if (charset == null || charset.length() == 0) {
+            throw new IllegalArgumentException("charset may not be null or empty");
+        }
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(credentials.getUserName());
+        buffer.append(":");
+        buffer.append(credentials.getPassword());
+
+        return "Basic " + EncodingUtil.getAsciiString(
+                Base64.encodeBase64(EncodingUtil.getBytes(buffer.toString(), charset)));
+    }
+}

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/Credentials.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/Credentials.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/Credentials.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/Credentials.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,23 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.ahc.auth;
+
+public interface Credentials {
+}

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/DigestScheme.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/DigestScheme.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/DigestScheme.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/DigestScheme.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,451 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.ahc.auth;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.StringTokenizer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.ahc.util.EncodingUtil;
+import org.apache.ahc.util.AsyncHttpClientException;
+import org.apache.ahc.util.NameValuePair;
+import org.apache.ahc.util.ParameterFormatter;
+import org.apache.ahc.codec.HttpRequestMessage;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
+
+public class DigestScheme extends RFC2617Scheme {
+
+    /** Log object for this class. */
+    private static final Logger LOG = LoggerFactory.getLogger(DigestScheme.class);
+
+    /**
+     * Hexa values used when creating 32 character long digest in HTTP DigestScheme
+     * in case of authentication.
+     *
+     * @see #encode(byte[])
+     */
+    private static final char[] HEXADECIMAL = {
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
+        'e', 'f'
+    };
+
+    /** Whether the digest authentication process is complete */
+    private boolean complete;
+
+    //TODO: supply a real nonce-count, currently a server will interprete a repeated request as a replay
+    private static final String NC = "00000001"; //nonce-count is always 1
+    private static final int QOP_MISSING = 0;
+    private static final int QOP_AUTH_INT = 1;
+    private static final int QOP_AUTH = 2;
+
+    private int qopVariant = QOP_MISSING;
+    private String cnonce;
+
+    private final ParameterFormatter formatter;
+    /**
+     * Default constructor for the digest authetication scheme.
+     *
+     * @since 3.0
+     */
+    public DigestScheme() {
+        super();
+        this.complete = false;
+        this.formatter = new ParameterFormatter();
+    }
+
+    /**
+     * Processes the Digest challenge.
+     *
+     * @param challenge the challenge string
+     *
+     * @throws MalformedChallengeException is thrown if the authentication challenge
+     * is malformed
+     *
+     * @since 3.0
+     */
+    public void processChallenge(final String challenge)
+      throws MalformedChallengeException {
+        super.processChallenge(challenge);
+
+        if (getParameter("realm") == null) {
+            throw new MalformedChallengeException("missing realm in challange");
+        }
+        if (getParameter("nonce") == null) {
+            throw new MalformedChallengeException("missing nonce in challange");
+        }
+
+        boolean unsupportedQop = false;
+        // qop parsing
+        String qop = getParameter("qop");
+        if (qop != null) {
+            StringTokenizer tok = new StringTokenizer(qop,",");
+            while (tok.hasMoreTokens()) {
+                String variant = tok.nextToken().trim();
+                if (variant.equals("auth")) {
+                    qopVariant = QOP_AUTH;
+                    break; //that's our favourite, because auth-int is unsupported
+                } else if (variant.equals("auth-int")) {
+                    qopVariant = QOP_AUTH_INT;
+                } else {
+                    unsupportedQop = true;
+                    LOG.warn("Unsupported qop detected: "+ variant);
+                }
+            }
+        }
+
+        if (unsupportedQop && (qopVariant == QOP_MISSING)) {
+            throw new MalformedChallengeException("None of the qop methods is supported");
+        }
+
+        cnonce = createCnonce();
+        this.complete = true;
+    }
+
+    /**
+     * Tests if the Digest authentication process has been completed.
+     *
+     * @return <tt>true</tt> if Digest authorization has been processed,
+     *   <tt>false</tt> otherwise.
+     *
+     * @since 3.0
+     */
+    public boolean isComplete() {
+        String s = getParameter("stale");
+        if ("true".equalsIgnoreCase(s)) {
+            return false;
+        } else {
+            return this.complete;
+        }
+    }
+
+    /**
+     * Returns textual designation of the digest authentication scheme.
+     *
+     * @return <code>digest</code>
+     */
+    public String getSchemeName() {
+        return "digest";
+    }
+
+    /**
+     * Returns <tt>false</tt>. Digest authentication scheme is request based.
+     *
+     * @return <tt>false</tt>.
+     *
+     * @since 3.0
+     */
+    public boolean isConnectionBased() {
+        return false;
+    }
+
+    /**
+     * Produces a digest authorization string for the given set of
+     * {@link Credentials}, method name and URI.
+     *
+     * @param credentials A set of credentials to be used for athentication
+     * @param request The request being authenticated
+     *
+     * @throws InvalidCredentialsException if authentication credentials
+     *         are not valid or not applicable for this authentication scheme
+     * @throws AuthenticationException if authorization string cannot
+     *   be generated due to an authentication failure
+     *
+     * @return a digest authorization string
+     *
+     */
+    public String authenticate(Credentials credentials, HttpRequestMessage request)
+    throws AuthenticationException {
+
+        LOG.trace("enter DigestScheme.authenticate(Credentials, HttpMethod)");
+
+        UsernamePasswordCredentials usernamepassword = null;
+        try {
+            usernamepassword = (UsernamePasswordCredentials) credentials;
+        } catch (ClassCastException e) {
+            throw new InvalidCredentialsException(
+                    "Credentials cannot be used for digest authentication: "
+                    + credentials.getClass().getName());
+        }
+        getParameters().put("methodname", request.getRequestMethod());
+        StringBuffer buffer = new StringBuffer(request.getPath());
+        String query = request.getUrl().getQuery();
+        if (query != null) {
+            if (query.indexOf("?") != 0) {
+                buffer.append("?");
+            }
+            buffer.append(request.getQuery());
+        }
+        getParameters().put("uri", buffer.toString());
+        String charset = getParameter("charset");
+        if (charset == null) {
+            getParameters().put("charset", request.getCredentialCharset());
+        }
+        String digest = createDigest(
+            usernamepassword.getUserName(),
+            usernamepassword.getPassword());
+        return "Digest " + createDigestHeader(usernamepassword.getUserName(),
+                digest);
+    }
+
+    /**
+     * Creates an MD5 response digest.
+     *
+     * @param uname Username
+     * @param pwd Password
+     *
+     * @return The created digest as string. This will be the response tag's
+     *         value in the Authentication HTTP header.
+     * @throws AuthenticationException when MD5 is an unsupported algorithm
+     */
+    private String createDigest(final String uname, final String pwd) throws AuthenticationException {
+
+        LOG.trace("enter DigestScheme.createDigest(String, String, Map)");
+
+        final String digAlg = "MD5";
+
+        // Collecting required tokens
+        String uri = getParameter("uri");
+        String realm = getParameter("realm");
+        String nonce = getParameter("nonce");
+        String qop = getParameter("qop");
+        String method = getParameter("methodname");
+        String algorithm = getParameter("algorithm");
+        // If an algorithm is not specified, default to MD5.
+        if (algorithm == null) {
+            algorithm = "MD5";
+        }
+        // If an charset is not specified, default to ISO-8859-1.
+        String charset = getParameter("charset");
+        if (charset == null) {
+            charset = "ISO-8859-1";
+        }
+
+        if (qopVariant == QOP_AUTH_INT) {
+            LOG.warn("qop=auth-int is not supported");
+            throw new AuthenticationException(
+                "Unsupported qop in HTTP Digest authentication");
+        }
+
+        MessageDigest md5Helper;
+
+        try {
+            md5Helper = MessageDigest.getInstance(digAlg);
+        } catch (Exception e) {
+            throw new AuthenticationException(
+              "Unsupported algorithm in HTTP Digest authentication: "
+               + digAlg);
+        }
+
+        // 3.2.2.2: Calculating digest
+        StringBuffer tmp = new StringBuffer(uname.length() + realm.length() + pwd.length() + 2);
+        tmp.append(uname);
+        tmp.append(':');
+        tmp.append(realm);
+        tmp.append(':');
+        tmp.append(pwd);
+        // unq(username-value) ":" unq(realm-value) ":" passwd
+        String a1 = tmp.toString();
+        //a1 is suitable for MD5 algorithm
+        if(algorithm.equals("MD5-sess")) {
+            // H( unq(username-value) ":" unq(realm-value) ":" passwd )
+            //      ":" unq(nonce-value)
+            //      ":" unq(cnonce-value)
+
+            String tmp2=encode(md5Helper.digest(EncodingUtil.getBytes(a1, charset)));
+            StringBuffer tmp3 = new StringBuffer(tmp2.length() + nonce.length() + cnonce.length() + 2);
+            tmp3.append(tmp2);
+            tmp3.append(':');
+            tmp3.append(nonce);
+            tmp3.append(':');
+            tmp3.append(cnonce);
+            a1 = tmp3.toString();
+        } else if(!algorithm.equals("MD5")) {
+            LOG.warn("Unhandled algorithm " + algorithm + " requested");
+        }
+        String md5a1 = encode(md5Helper.digest(EncodingUtil.getBytes(a1, charset)));
+
+        String a2 = null;
+        if (qopVariant == QOP_AUTH_INT) {
+            LOG.error("Unhandled qop auth-int");
+            //we do not have access to the entity-body or its hash
+            //TODO: add Method ":" digest-uri-value ":" H(entity-body)
+        } else {
+            a2 = method + ":" + uri;
+        }
+        String md5a2 = encode(md5Helper.digest(EncodingUtil.getAsciiBytes(a2)));
+
+        // 3.2.2.1
+        String serverDigestValue;
+        if (qopVariant == QOP_MISSING) {
+            LOG.debug("Using null qop method");
+            StringBuffer tmp2 = new StringBuffer(md5a1.length() + nonce.length() + md5a2.length());
+            tmp2.append(md5a1);
+            tmp2.append(':');
+            tmp2.append(nonce);
+            tmp2.append(':');
+            tmp2.append(md5a2);
+            serverDigestValue = tmp2.toString();
+        } else {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Using qop method " + qop);
+            }
+            String qopOption = getQopVariantString();
+            StringBuffer tmp2 = new StringBuffer(md5a1.length() + nonce.length()
+                + NC.length() + cnonce.length() + qopOption.length() + md5a2.length() + 5);
+            tmp2.append(md5a1);
+            tmp2.append(':');
+            tmp2.append(nonce);
+            tmp2.append(':');
+            tmp2.append(NC);
+            tmp2.append(':');
+            tmp2.append(cnonce);
+            tmp2.append(':');
+            tmp2.append(qopOption);
+            tmp2.append(':');
+            tmp2.append(md5a2);
+            serverDigestValue = tmp2.toString();
+        }
+
+        String serverDigest =
+            encode(md5Helper.digest(EncodingUtil.getAsciiBytes(serverDigestValue)));
+
+        return serverDigest;
+    }
+
+    /**
+     * Creates digest-response header as defined in RFC2617.
+     *
+     * @param uname Username
+     * @param digest The response tag's value as String.
+     *
+     * @return The digest-response as String.
+     */
+    private String createDigestHeader(final String uname, final String digest)
+        throws AuthenticationException {
+
+        LOG.trace("enter DigestScheme.createDigestHeader(String, Map, "
+            + "String)");
+
+        String uri = getParameter("uri");
+        String realm = getParameter("realm");
+        String nonce = getParameter("nonce");
+        String opaque = getParameter("opaque");
+        String response = digest;
+        String algorithm = getParameter("algorithm");
+
+        List params = new ArrayList(20);
+        params.add(new NameValuePair("username", uname));
+        params.add(new NameValuePair("realm", realm));
+        params.add(new NameValuePair("nonce", nonce));
+        params.add(new NameValuePair("uri", uri));
+        params.add(new NameValuePair("response", response));
+
+        if (qopVariant != QOP_MISSING) {
+            params.add(new NameValuePair("qop", getQopVariantString()));
+            params.add(new NameValuePair("nc", NC));
+            params.add(new NameValuePair("cnonce", this.cnonce));
+        }
+        if (algorithm != null) {
+            params.add(new NameValuePair("algorithm", algorithm));
+        }
+        if (opaque != null) {
+            params.add(new NameValuePair("opaque", opaque));
+        }
+
+        StringBuffer buffer = new StringBuffer();
+        for (int i = 0; i < params.size(); i++) {
+            NameValuePair param = (NameValuePair) params.get(i);
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            boolean noQuotes = "nc".equals(param.getName()) ||
+                               "qop".equals(param.getName());
+            this.formatter.setAlwaysUseQuotes(!noQuotes);
+            this.formatter.format(buffer, param);
+        }
+        return buffer.toString();
+    }
+
+    private String getQopVariantString() {
+        String qopOption;
+        if (qopVariant == QOP_AUTH_INT) {
+            qopOption = "auth-int";
+        } else {
+            qopOption = "auth";
+        }
+        return qopOption;
+    }
+
+    /**
+     * Encodes the 128 bit (16 bytes) MD5 digest into a 32 characters long
+     * <CODE>String</CODE> according to RFC 2617.
+     *
+     * @param binaryData array containing the digest
+     * @return encoded MD5, or <CODE>null</CODE> if encoding failed
+     */
+    private static String encode(byte[] binaryData) {
+        LOG.trace("enter DigestScheme.encode(byte[])");
+
+        if (binaryData.length != 16) {
+            return null;
+        }
+
+        char[] buffer = new char[32];
+        for (int i = 0; i < 16; i++) {
+            int low = (int) (binaryData[i] & 0x0f);
+            int high = (int) ((binaryData[i] & 0xf0) >> 4);
+            buffer[i * 2] = HEXADECIMAL[high];
+            buffer[(i * 2) + 1] = HEXADECIMAL[low];
+        }
+
+        return new String(buffer);
+    }
+
+
+    /**
+     * Creates a random cnonce value based on the current time.
+     *
+     * @return The cnonce value as String.
+     * @throws AsyncHttpClientException if MD5 algorithm is not supported.
+     */
+    public static String createCnonce() {
+        LOG.trace("enter DigestScheme.createCnonce()");
+
+        String cnonce;
+        final String digAlg = "MD5";
+        MessageDigest md5Helper;
+
+        try {
+            md5Helper = MessageDigest.getInstance(digAlg);
+        } catch (NoSuchAlgorithmException e) {
+            throw new AsyncHttpClientException(
+              "Unsupported algorithm in HTTP Digest authentication: "
+               + digAlg);
+        }
+
+        cnonce = Long.toString(System.currentTimeMillis());
+        cnonce = encode(md5Helper.digest(EncodingUtil.getAsciiBytes(cnonce)));
+
+        return cnonce;
+    }
+}

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/InvalidCredentialsException.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/InvalidCredentialsException.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/InvalidCredentialsException.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/InvalidCredentialsException.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,41 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.ahc.auth;
+
+import org.apache.ahc.util.AsyncHttpClientException;
+
+public class InvalidCredentialsException extends AsyncHttpClientException {
+
+    public InvalidCredentialsException() {
+        super();
+    }
+
+    public InvalidCredentialsException(String string) {
+        super(string);
+    }
+
+    public InvalidCredentialsException(String string, Throwable throwable) {
+        super(string, throwable);
+    }
+
+    public InvalidCredentialsException(Throwable throwable) {
+        super(throwable);
+    }
+}

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/MalformedChallengeException.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/MalformedChallengeException.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/MalformedChallengeException.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/MalformedChallengeException.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,41 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.ahc.auth;
+
+import org.apache.ahc.util.AsyncHttpClientException;
+
+public class MalformedChallengeException extends AsyncHttpClientException {
+
+    public MalformedChallengeException() {
+        super();
+    }
+
+    public MalformedChallengeException(String string) {
+        super(string);
+    }
+
+    public MalformedChallengeException(String string, Throwable throwable) {
+        super(string, throwable);
+    }
+
+    public MalformedChallengeException(Throwable throwable) {
+        super(throwable);
+    }
+}

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/NTCredentials.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/NTCredentials.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/NTCredentials.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/NTCredentials.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,180 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.ahc.auth;
+
+import org.apache.ahc.util.LangUtils;
+
+/**
+ * {@link Credentials} for use with the NTLM authentication scheme which requires additional
+ * information.
+ *
+ * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ */
+public class NTCredentials extends UsernamePasswordCredentials {
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The Domain to authenticate with.
+     */
+    private String domain;
+
+    /**
+     * The host the authentication request is originating from.
+     */
+    private String host;
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Default constructor.
+     *
+     * @deprecated Do not use. Null user name, domain & host no longer allowed
+     */
+    public NTCredentials() {
+        super();
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param userName The user name.  This should not include the domain to authenticate with.
+     *                 For example: "user" is correct whereas "DOMAIN\\user" is not.
+     * @param password The password.
+     * @param host     The host the authentication request is originating from.  Essentially, the
+     *                 computer name for this machine.
+     * @param domain   The domain to authenticate within.
+     */
+    public NTCredentials(String userName, String password, String host,
+                         String domain) {
+        super(userName, password);
+        if (domain == null) {
+            throw new IllegalArgumentException("Domain may not be null");
+        }
+        this.domain = domain;
+        if (host == null) {
+            throw new IllegalArgumentException("Host may not be null");
+        }
+        this.host = host;
+    }
+    // ------------------------------------------------------- Instance Methods
+
+
+    /**
+     * Sets the domain to authenticate with. The domain may not be null.
+     *
+     * @param domain the NT domain to authenticate in.
+     * @see #getDomain()
+     * @deprecated Do not use. The NTCredentials objects should be immutable
+     */
+    public void setDomain(String domain) {
+        if (domain == null) {
+            throw new IllegalArgumentException("Domain may not be null");
+        }
+        this.domain = domain;
+    }
+
+    /**
+     * Retrieves the name to authenticate with.
+     *
+     * @return String the domain these credentials are intended to authenticate with.
+     * @see #setDomain(String)
+     */
+    public String getDomain() {
+        return domain;
+    }
+
+    /**
+     * Sets the host name of the computer originating the request. The host name may
+     * not be null.
+     *
+     * @param host the Host the user is logged into.
+     * @deprecated Do not use. The NTCredentials objects should be immutable
+     */
+    public void setHost(String host) {
+        if (host == null) {
+            throw new IllegalArgumentException("Host may not be null");
+        }
+        this.host = host;
+    }
+
+    /**
+     * Retrieves the host name of the computer originating the request.
+     *
+     * @return String the host the user is logged into.
+     */
+    public String getHost() {
+        return this.host;
+    }
+
+    /**
+     * Return a string representation of this object.
+     *
+     * @return A string represenation of this object.
+     */
+    public String toString() {
+        final StringBuffer sbResult = new StringBuffer(super.toString());
+
+        sbResult.append("@");
+        sbResult.append(this.host);
+        sbResult.append(".");
+        sbResult.append(this.domain);
+
+        return sbResult.toString();
+    }
+
+    /**
+     * Computes a hash code based on all the case-sensitive parts of the credentials object.
+     *
+     * @return The hash code for the credentials.
+     */
+    public int hashCode() {
+        int hash = super.hashCode();
+        hash = LangUtils.hashCode(hash, this.host);
+        hash = LangUtils.hashCode(hash, this.domain);
+        return hash;
+    }
+
+    /**
+     * Performs a case-sensitive check to see if the components of the credentials
+     * are the same.
+     *
+     * @param o The object to match.
+     * @return <code>true</code> if all of the credentials match.
+     */
+    public boolean equals(Object o) {
+        if (o == null) {
+            return false;
+        }
+        if (this == o) {
+            return true;
+        }
+        if (super.equals(o)) {
+            if (o instanceof NTCredentials) {
+                NTCredentials that = (NTCredentials)o;
+
+                return LangUtils.equals(this.domain, that.domain)
+                    && LangUtils.equals(this.host, that.host);
+            }
+        }
+
+        return false;
+    }
+}