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 [2/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...

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/NTLM.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/NTLM.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/NTLM.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/NTLM.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,549 @@
+/*
+ *  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.NoSuchAlgorithmException;
+import java.security.InvalidKeyException;
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.ahc.util.EncodingUtil;
+
+/**
+ * Provides an implementation of the NTLM authentication protocol.
+ * <p>
+ * This class provides methods for generating authentication
+ * challenge responses for the NTLM authentication protocol.  The NTLM
+ * protocol is a proprietary Microsoft protocol and as such no RFC
+ * exists for it.  This class is based upon the reverse engineering
+ * efforts of a wide range of people.</p>
+ *
+ * <p>Please note that an implementation of JCE must be correctly installed and configured when
+ * using NTLM support.</p>
+ *
+ * <p>This class should not be used externally to HttpClient as it's API is specifically
+ * designed to work with HttpClient's use case, in particular it's connection management.</p>
+ *
+ * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ */
+final class NTLM {
+    /** Character encoding */
+    public static final String DEFAULT_CHARSET = "ASCII";
+
+    /** The current response */
+    private byte[] currentResponse;
+
+    /** The current position */
+    private int currentPosition = 0;
+
+    /** The character set to use for encoding the credentials */
+    private String credentialCharset = DEFAULT_CHARSET;
+
+    /**
+     * Returns the response for the given message.
+     *
+     * @param message the message that was received from the server.
+     * @param username the username to authenticate with.
+     * @param password the password to authenticate with.
+     * @param host The host.
+     * @param domain the NT domain to authenticate in.
+     * @return The response.
+     * @throws AuthenticationException If the messages cannot be retrieved.
+     */
+    public final String getResponseFor(String message,
+            String username, String password, String host, String domain)
+            throws AuthenticationException {
+
+        final String response;
+        if (message == null || message.trim().equals("")) {
+            response = getType1Message(host, domain);
+        } else {
+            response = getType3Message(username, password, host, domain,
+                    parseType2Message(message));
+        }
+        return response;
+    }
+
+    /**
+     * Return the cipher for the specified key.
+     * @param key The key.
+     * @return Cipher The cipher.
+     * @throws AuthenticationException If the cipher cannot be retrieved.
+     */
+    private Cipher getCipher(byte[] key) throws AuthenticationException {
+        try {
+            final Cipher ecipher = Cipher.getInstance("DES/ECB/NoPadding");
+            key = setupKey(key);
+            ecipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "DES"));
+            return ecipher;
+        } catch (NoSuchAlgorithmException e) {
+            throw new AuthenticationException("DES encryption is not available.", e);
+        } catch (InvalidKeyException e) {
+            throw new AuthenticationException("Invalid key for DES encryption.", e);
+        } catch (NoSuchPaddingException e) {
+            throw new AuthenticationException(
+                "NoPadding option for DES is not available.", e);
+        }
+    }
+
+    /**
+     * Adds parity bits to the key.
+     * @param key56 The key
+     * @return The modified key.
+     */
+    private byte[] setupKey(byte[] key56) {
+        byte[] key = new byte[8];
+        key[0] = (byte) ((key56[0] >> 1) & 0xff);
+        key[1] = (byte) ((((key56[0] & 0x01) << 6)
+            | (((key56[1] & 0xff) >> 2) & 0xff)) & 0xff);
+        key[2] = (byte) ((((key56[1] & 0x03) << 5)
+            | (((key56[2] & 0xff) >> 3) & 0xff)) & 0xff);
+        key[3] = (byte) ((((key56[2] & 0x07) << 4)
+            | (((key56[3] & 0xff) >> 4) & 0xff)) & 0xff);
+        key[4] = (byte) ((((key56[3] & 0x0f) << 3)
+            | (((key56[4] & 0xff) >> 5) & 0xff)) & 0xff);
+        key[5] = (byte) ((((key56[4] & 0x1f) << 2)
+            | (((key56[5] & 0xff) >> 6) & 0xff)) & 0xff);
+        key[6] = (byte) ((((key56[5] & 0x3f) << 1)
+            | (((key56[6] & 0xff) >> 7) & 0xff)) & 0xff);
+        key[7] = (byte) (key56[6] & 0x7f);
+
+        for (int i = 0; i < key.length; i++) {
+            key[i] = (byte) (key[i] << 1);
+        }
+        return key;
+    }
+
+    /**
+     * Encrypt the data.
+     * @param key The key.
+     * @param bytes The data
+     * @return byte[] The encrypted data
+     * @throws AuthenticationException If {@link javax.crypto.Cipher#doFinal(byte[])} fails
+     */
+    private byte[] encrypt(byte[] key, byte[] bytes)
+        throws AuthenticationException {
+        Cipher ecipher = getCipher(key);
+        try {
+            byte[] enc = ecipher.doFinal(bytes);
+            return enc;
+        } catch (IllegalBlockSizeException e) {
+            throw new AuthenticationException("Invalid block size for DES encryption.", e);
+        } catch (BadPaddingException e) {
+            throw new AuthenticationException("Data not padded correctly for DES encryption.", e);
+        }
+    }
+
+    /**
+     * Prepares the object to create a response of the given length.
+     * @param length the length of the response to prepare.
+     */
+    private void prepareResponse(int length) {
+        currentResponse = new byte[length];
+        currentPosition = 0;
+    }
+
+    /**
+     * Adds the given byte to the response.
+     * @param b the byte to add.
+     */
+    private void addByte(byte b) {
+        currentResponse[currentPosition] = b;
+        currentPosition++;
+    }
+
+    /**
+     * Adds the given bytes to the response.
+     * @param bytes the bytes to add.
+     */
+    private void addBytes(byte[] bytes) {
+        for (int i = 0; i < bytes.length; i++) {
+            currentResponse[currentPosition] = bytes[i];
+            currentPosition++;
+        }
+    }
+
+    /**
+     * Returns the response that has been generated after shrinking the array if
+     * required and base64 encodes the response.
+     * @return The response as above.
+     */
+    private String getResponse() {
+        byte[] resp;
+        if (currentResponse.length > currentPosition) {
+            byte[] tmp = new byte[currentPosition];
+            for (int i = 0; i < currentPosition; i++) {
+                tmp[i] = currentResponse[i];
+            }
+            resp = tmp;
+        } else {
+            resp = currentResponse;
+        }
+        return EncodingUtil.getAsciiString(Base64.encodeBase64(resp));
+    }
+
+    /**
+     * Creates the first message (type 1 message) in the NTLM authentication sequence.
+     * This message includes the user name, domain and host for the authentication session.
+     *
+     * @param host the computer name of the host requesting authentication.
+     * @param domain The domain to authenticate with.
+     * @return String the message to add to the HTTP request header.
+     */
+    public String getType1Message(String host, String domain) {
+        host = host.toUpperCase();
+        domain = domain.toUpperCase();
+        byte[] hostBytes = EncodingUtil.getBytes(host, DEFAULT_CHARSET);
+        byte[] domainBytes = EncodingUtil.getBytes(domain, DEFAULT_CHARSET);
+
+        int finalLength = 32 + hostBytes.length + domainBytes.length;
+        prepareResponse(finalLength);
+
+        // The initial id string.
+        byte[] protocol = EncodingUtil.getBytes("NTLMSSP", DEFAULT_CHARSET);
+        addBytes(protocol);
+        addByte((byte) 0);
+
+        // Type
+        addByte((byte) 1);
+        addByte((byte) 0);
+        addByte((byte) 0);
+        addByte((byte) 0);
+
+        // Flags
+        addByte((byte) 6);
+        addByte((byte) 82);
+        addByte((byte) 0);
+        addByte((byte) 0);
+
+        // Domain length (first time).
+        int iDomLen = domainBytes.length;
+        byte[] domLen = convertShort(iDomLen);
+        addByte(domLen[0]);
+        addByte(domLen[1]);
+
+        // Domain length (second time).
+        addByte(domLen[0]);
+        addByte(domLen[1]);
+
+        // Domain offset.
+        byte[] domOff = convertShort(hostBytes.length + 32);
+        addByte(domOff[0]);
+        addByte(domOff[1]);
+        addByte((byte) 0);
+        addByte((byte) 0);
+
+        // Host length (first time).
+        byte[] hostLen = convertShort(hostBytes.length);
+        addByte(hostLen[0]);
+        addByte(hostLen[1]);
+
+        // Host length (second time).
+        addByte(hostLen[0]);
+        addByte(hostLen[1]);
+
+        // Host offset (always 32).
+        byte[] hostOff = convertShort(32);
+        addByte(hostOff[0]);
+        addByte(hostOff[1]);
+        addByte((byte) 0);
+        addByte((byte) 0);
+
+        // Host String.
+        addBytes(hostBytes);
+
+        // Domain String.
+        addBytes(domainBytes);
+
+        return getResponse();
+    }
+
+    /**
+     * Extracts the server nonce out of the given message type 2.
+     *
+     * @param message the String containing the base64 encoded message.
+     * @return an array of 8 bytes that the server sent to be used when
+     * hashing the password.
+     */
+    public byte[] parseType2Message(String message) {
+        // Decode the message first.
+        byte[] msg = Base64.decodeBase64(EncodingUtil.getBytes(message, DEFAULT_CHARSET));
+        byte[] nonce = new byte[8];
+        // The nonce is the 8 bytes starting from the byte in position 24.
+        for (int i = 0; i < 8; i++) {
+            nonce[i] = msg[i + 24];
+        }
+        return nonce;
+    }
+
+    /**
+     * Creates the type 3 message using the given server nonce.  The type 3 message includes all the
+     * information for authentication, host, domain, username and the result of encrypting the
+     * nonce sent by the server using the user's password as the key.
+     *
+     * @param user The user name.  This should not include the domain name.
+     * @param password The password.
+     * @param host The host that is originating the authentication request.
+     * @param domain The domain to authenticate within.
+     * @param nonce the 8 byte array the server sent.
+     * @return The type 3 message.
+     * @throws AuthenticationException If {@encrypt(byte[],byte[])} fails.
+     */
+    public String getType3Message(String user, String password,
+            String host, String domain, byte[] nonce)
+    throws AuthenticationException {
+
+        int ntRespLen = 0;
+        int lmRespLen = 24;
+        domain = domain.toUpperCase();
+        host = host.toUpperCase();
+        user = user.toUpperCase();
+        byte[] domainBytes = EncodingUtil.getBytes(domain, DEFAULT_CHARSET);
+        byte[] hostBytes = EncodingUtil.getBytes(host, DEFAULT_CHARSET);
+        byte[] userBytes = EncodingUtil.getBytes(user, credentialCharset);
+        int domainLen = domainBytes.length;
+        int hostLen = hostBytes.length;
+        int userLen = userBytes.length;
+        int finalLength = 64 + ntRespLen + lmRespLen + domainLen
+            + userLen + hostLen;
+        prepareResponse(finalLength);
+        byte[] ntlmssp = EncodingUtil.getBytes("NTLMSSP", DEFAULT_CHARSET);
+        addBytes(ntlmssp);
+        addByte((byte) 0);
+        addByte((byte) 3);
+        addByte((byte) 0);
+        addByte((byte) 0);
+        addByte((byte) 0);
+
+        // LM Resp Length (twice)
+        addBytes(convertShort(24));
+        addBytes(convertShort(24));
+
+        // LM Resp Offset
+        addBytes(convertShort(finalLength - 24));
+        addByte((byte) 0);
+        addByte((byte) 0);
+
+        // NT Resp Length (twice)
+        addBytes(convertShort(0));
+        addBytes(convertShort(0));
+
+        // NT Resp Offset
+        addBytes(convertShort(finalLength));
+        addByte((byte) 0);
+        addByte((byte) 0);
+
+        // Domain length (twice)
+        addBytes(convertShort(domainLen));
+        addBytes(convertShort(domainLen));
+
+        // Domain offset.
+        addBytes(convertShort(64));
+        addByte((byte) 0);
+        addByte((byte) 0);
+
+        // User Length (twice)
+        addBytes(convertShort(userLen));
+        addBytes(convertShort(userLen));
+
+        // User offset
+        addBytes(convertShort(64 + domainLen));
+        addByte((byte) 0);
+        addByte((byte) 0);
+
+        // Host length (twice)
+        addBytes(convertShort(hostLen));
+        addBytes(convertShort(hostLen));
+
+        // Host offset
+        addBytes(convertShort(64 + domainLen + userLen));
+
+        for (int i = 0; i < 6; i++) {
+            addByte((byte) 0);
+        }
+
+        // Message length
+        addBytes(convertShort(finalLength));
+        addByte((byte) 0);
+        addByte((byte) 0);
+
+        // Flags
+        addByte((byte) 6);
+        addByte((byte) 82);
+        addByte((byte) 0);
+        addByte((byte) 0);
+
+        addBytes(domainBytes);
+        addBytes(userBytes);
+        addBytes(hostBytes);
+        addBytes(hashPassword(password, nonce));
+        return getResponse();
+    }
+
+    /**
+     * Creates the LANManager and NT response for the given password using the
+     * given nonce.
+     * @param password the password to create a hash for.
+     * @param nonce the nonce sent by the server.
+     * @return The response.
+     * @throws AuthenticationException If {@link #encrypt(byte[],byte[])} fails.
+     */
+    private byte[] hashPassword(String password, byte[] nonce)
+        throws AuthenticationException {
+        byte[] passw = EncodingUtil.getBytes(password.toUpperCase(), credentialCharset);
+        byte[] lmPw1 = new byte[7];
+        byte[] lmPw2 = new byte[7];
+
+        int len = passw.length;
+        if (len > 7) {
+            len = 7;
+        }
+
+        int idx;
+        for (idx = 0; idx < len; idx++) {
+            lmPw1[idx] = passw[idx];
+        }
+        for (; idx < 7; idx++) {
+            lmPw1[idx] = (byte) 0;
+        }
+
+        len = passw.length;
+        if (len > 14) {
+            len = 14;
+        }
+        for (idx = 7; idx < len; idx++) {
+            lmPw2[idx - 7] = passw[idx];
+        }
+        for (; idx < 14; idx++) {
+            lmPw2[idx - 7] = (byte) 0;
+        }
+
+        // Create LanManager hashed Password
+        byte[] magic = {
+            (byte) 0x4B, (byte) 0x47, (byte) 0x53, (byte) 0x21,
+            (byte) 0x40, (byte) 0x23, (byte) 0x24, (byte) 0x25
+        };
+
+        byte[] lmHpw1;
+        lmHpw1 = encrypt(lmPw1, magic);
+
+        byte[] lmHpw2 = encrypt(lmPw2, magic);
+
+        byte[] lmHpw = new byte[21];
+        for (int i = 0; i < lmHpw1.length; i++) {
+            lmHpw[i] = lmHpw1[i];
+        }
+        for (int i = 0; i < lmHpw2.length; i++) {
+            lmHpw[i + 8] = lmHpw2[i];
+        }
+        for (int i = 0; i < 5; i++) {
+            lmHpw[i + 16] = (byte) 0;
+        }
+
+        // Create the responses.
+        byte[] lmResp = new byte[24];
+        calcResp(lmHpw, nonce, lmResp);
+
+        return lmResp;
+    }
+
+    /**
+     * Takes a 21 byte array and treats it as 3 56-bit DES keys.  The 8 byte
+     * plaintext is encrypted with each key and the resulting 24 bytes are
+     * stored in the results array.
+     *
+     * @param keys The keys.
+     * @param plaintext The plain text to encrypt.
+     * @param results Where the results are stored.
+     * @throws AuthenticationException If {@link #encrypt(byte[],byte[])} fails.
+     */
+    private void calcResp(byte[] keys, byte[] plaintext, byte[] results)
+        throws AuthenticationException {
+        byte[] keys1 = new byte[7];
+        byte[] keys2 = new byte[7];
+        byte[] keys3 = new byte[7];
+        for (int i = 0; i < 7; i++) {
+            keys1[i] = keys[i];
+        }
+
+        for (int i = 0; i < 7; i++) {
+            keys2[i] = keys[i + 7];
+        }
+
+        for (int i = 0; i < 7; i++) {
+            keys3[i] = keys[i + 14];
+        }
+        byte[] results1 = encrypt(keys1, plaintext);
+
+        byte[] results2 = encrypt(keys2, plaintext);
+
+        byte[] results3 = encrypt(keys3, plaintext);
+
+        for (int i = 0; i < 8; i++) {
+            results[i] = results1[i];
+        }
+        for (int i = 0; i < 8; i++) {
+            results[i + 8] = results2[i];
+        }
+        for (int i = 0; i < 8; i++) {
+            results[i + 16] = results3[i];
+        }
+    }
+
+    /**
+     * Converts a given number to a two byte array in little endian order.
+     * @param num the number to convert.
+     * @return The byte representation of <i>num</i> in little endian order.
+     */
+    private byte[] convertShort(int num) {
+        byte[] val = new byte[2];
+        String hex = Integer.toString(num, 16);
+        while (hex.length() < 4) {
+            hex = "0" + hex;
+        }
+        String low = hex.substring(2, 4);
+        String high = hex.substring(0, 2);
+
+        val[0] = (byte) Integer.parseInt(low, 16);
+        val[1] = (byte) Integer.parseInt(high, 16);
+        return val;
+    }
+
+    /**
+     * @return Returns the credentialCharset.
+     */
+    public String getCredentialCharset() {
+        return credentialCharset;
+    }
+
+    /**
+     * @param credentialCharset The credentialCharset to set.
+     */
+    public void setCredentialCharset(String credentialCharset) {
+        this.credentialCharset = credentialCharset;
+    }
+
+}

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/NTLMScheme.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/NTLMScheme.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/NTLMScheme.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/NTLMScheme.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,210 @@
+/*
+ *  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;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An implementation of the Microsoft proprietary NTLM authentication scheme.
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author Rodney Waldhoff
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author Ortwin Gl???ck
+ * @author Sean C. Sullivan
+ * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ */
+public class NTLMScheme implements AuthScheme {
+
+    /**
+     * Log object for this class.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(NTLMScheme.class);
+
+    /**
+     * NTLM challenge string.
+     */
+    private String ntlmchallenge = null;
+
+    private static final int UNINITIATED = 0;
+    private static final int INITIATED = 1;
+    private static final int TYPE1_MSG_GENERATED = 2;
+    private static final int TYPE2_MSG_RECEIVED = 3;
+    private static final int TYPE3_MSG_GENERATED = 4;
+    private static final int FAILED = Integer.MAX_VALUE;
+
+    /**
+     * Authentication process state
+     */
+    private int state;
+
+    /**
+     * Default constructor for the NTLM authentication scheme.
+     */
+    public NTLMScheme() {
+        super();
+        this.state = UNINITIATED;
+    }
+
+    /**
+     * Constructor for the NTLM authentication scheme.
+     *
+     * @param challenge The authentication challenge
+     * @throws MalformedChallengeException is thrown if the authentication challenge
+     *                                     is malformed
+     */
+    public NTLMScheme(final String challenge) throws MalformedChallengeException {
+        super();
+        processChallenge(challenge);
+    }
+
+    /**
+     * Processes the NTLM challenge.
+     *
+     * @param challenge the challenge string
+     * @throws MalformedChallengeException is thrown if the authentication challenge
+     *                                     is malformed
+     */
+    public void processChallenge(final String challenge) throws MalformedChallengeException {
+        String s = AuthChallengeParser.extractScheme(challenge);
+        if (!s.equalsIgnoreCase(getSchemeName())) {
+            throw new MalformedChallengeException("Invalid NTLM challenge: " + challenge);
+        }
+        int i = challenge.indexOf(' ');
+        if (i != -1) {
+            s = challenge.substring(i, challenge.length());
+            this.ntlmchallenge = s.trim();
+            this.state = TYPE2_MSG_RECEIVED;
+        } else {
+            this.ntlmchallenge = "";
+            if (this.state == UNINITIATED) {
+                this.state = INITIATED;
+            } else {
+                this.state = FAILED;
+            }
+        }
+    }
+
+    /**
+     * Tests if the NTLM authentication process has been completed.
+     *
+     * @return <tt>true</tt> if Basic authorization has been processed,
+     *         <tt>false</tt> otherwise.
+     */
+    public boolean isComplete() {
+        return this.state == TYPE3_MSG_GENERATED || this.state == FAILED;
+    }
+
+    /**
+     * Returns textual designation of the NTLM authentication scheme.
+     *
+     * @return <code>ntlm</code>
+     */
+    public String getSchemeName() {
+        return "ntlm";
+    }
+
+    /**
+     * The concept of an authentication realm is not supported by the NTLM
+     * authentication scheme. Always returns <code>null</code>.
+     *
+     * @return <code>null</code>
+     */
+    public String getRealm() {
+        return null;
+    }
+
+    /**
+     * Returns the authentication parameter with the given name, if available.
+     * <p/>
+     * <p>There are no valid parameters for NTLM authentication so this method always returns
+     * <tt>null</tt>.</p>
+     *
+     * @param name The name of the parameter to be returned
+     * @return the parameter with the given name
+     */
+    public String getParameter(String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("Parameter name may not be null");
+        }
+        return null;
+    }
+
+    /**
+     * Returns <tt>true</tt>. NTLM authentication scheme is connection based.
+     *
+     * @return <tt>true</tt>.
+     */
+    public boolean isConnectionBased() {
+        return true;
+    }
+
+    /**
+     * Produces NTLM 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
+     * @return an NTLM authorization string
+     * @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
+     */
+    public String authenticate(Credentials credentials, HttpRequestMessage request)
+        throws AuthenticationException {
+        LOG.trace("enter NTLMScheme.authenticate(Credentials, HttpMethod)");
+
+        if (this.state == UNINITIATED) {
+            throw new IllegalStateException("NTLM authentication process has not been initiated");
+        }
+
+        NTCredentials ntcredentials = null;
+        try {
+            ntcredentials = (NTCredentials)credentials;
+        } catch (ClassCastException e) {
+            throw new InvalidCredentialsException(
+                "Credentials cannot be used for NTLM authentication: "
+                    + credentials.getClass().getName());
+        }
+        NTLM ntlm = new NTLM();
+        ntlm.setCredentialCharset(request.getCredentialCharset());
+        String response = null;
+        if (this.state == INITIATED || this.state == FAILED) {
+            response = ntlm.getType1Message(
+                ntcredentials.getHost(),
+                ntcredentials.getDomain());
+            this.state = TYPE1_MSG_GENERATED;
+        } else {
+            response = ntlm.getType3Message(
+                ntcredentials.getUserName(),
+                ntcredentials.getPassword(),
+                ntcredentials.getHost(),
+                ntcredentials.getDomain(),
+                ntlm.parseType2Message(this.ntlmchallenge));
+            this.state = TYPE3_MSG_GENERATED;
+        }
+        return "NTLM " + response;
+    }
+}
+

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/RFC2617Scheme.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/RFC2617Scheme.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/RFC2617Scheme.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/RFC2617Scheme.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,109 @@
+/*
+ *  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;
+
+public abstract class RFC2617Scheme implements AuthScheme{
+        /**
+     * Authentication parameter map.
+     */
+    private Map params = null;
+
+    /**
+     * Default constructor for RFC2617 compliant authetication schemes.
+     *
+     * @since 3.0
+     */
+    public RFC2617Scheme() {
+        super();
+    }
+
+    /**
+     * Default constructor for RFC2617 compliant 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 RFC2617Scheme(final String challenge) throws MalformedChallengeException {
+        super();
+        processChallenge(challenge);
+    }
+
+    /**
+     * 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
+     *
+     * @throws MalformedChallengeException is thrown if the authentication challenge
+     * is malformed
+     *
+     * @since 3.0
+     */
+    public void processChallenge(final String challenge) throws MalformedChallengeException {
+        String s = AuthChallengeParser.extractScheme(challenge);
+        if (!s.equalsIgnoreCase(getSchemeName())) {
+            throw new MalformedChallengeException("Invalid " + getSchemeName() + " challenge: " + challenge);
+        }
+        this.params = AuthChallengeParser.extractParams(challenge);
+    }
+
+    /**
+     * Returns authentication parameters map. Keys in the map are lower-cased.
+     *
+     * @return the map of authentication parameters
+     */
+    protected Map getParameters() {
+        return this.params;
+    }
+
+    /**
+     * 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
+     */
+    public String getParameter(String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("Parameter name may not be null");
+        }
+        if (this.params == null) {
+            return null;
+        }
+        return (String) this.params.get(name.toLowerCase());
+    }
+
+    /**
+     * Returns authentication realm. The realm may not be null.
+     *
+     * @return the authentication realm
+     */
+    public String getRealm() {
+        return getParameter("realm");
+    }
+}

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/UsernamePasswordCredentials.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/UsernamePasswordCredentials.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/UsernamePasswordCredentials.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/auth/UsernamePasswordCredentials.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,189 @@
+/*
+ *  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 UsernamePasswordCredentials implements Credentials{
+        // ----------------------------------------------------------- Constructors
+
+    /**
+     * Default constructor.
+     *
+     * @deprecated Do not use. Null user name no longer allowed
+     */
+    public UsernamePasswordCredentials() {
+        super();
+    }
+
+
+    /**
+     * The constructor with the username and password combined string argument.
+     *
+     * @param usernamePassword the username:password formed string
+     * @see #toString
+     */
+    public UsernamePasswordCredentials(String usernamePassword) {
+        super();
+        if (usernamePassword == null) {
+            throw new IllegalArgumentException("Username:password string may not be null");
+        }
+        int atColon = usernamePassword.indexOf(':');
+        if (atColon >= 0) {
+            this.userName = usernamePassword.substring(0, atColon);
+            this.password = usernamePassword.substring(atColon + 1);
+        } else {
+            this.userName = usernamePassword;
+        }
+    }
+
+
+    /**
+     * The constructor with the username and password arguments.
+     *
+     * @param userName the user name
+     * @param password the password
+     */
+    public UsernamePasswordCredentials(String userName, String password) {
+        super();
+        if (userName == null) {
+            throw new IllegalArgumentException("Username may not be null");
+        }
+        this.userName = userName;
+        this.password = password;
+    }
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * User name.
+     */
+    private String userName;
+
+
+    /**
+     * Password.
+     */
+    private String password;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * User name property setter. User name may not be null.
+     *
+     * @param userName
+     * @see #getUserName()
+     *
+     * @deprecated Do not use. The UsernamePasswordCredentials objects should be immutable
+     */
+    public void setUserName(String userName) {
+        if (userName == null) {
+            throw new IllegalArgumentException("Username may not be null");
+        }
+        this.userName = userName;
+    }
+
+
+    /**
+     * User name property getter.
+     *
+     * @return the userName
+     * @see #setUserName(String)
+     */
+    public String getUserName() {
+        return userName;
+    }
+
+
+    /**
+     * Password property setter.
+     *
+     * @param password
+     * @see #getPassword()
+     *
+     * @deprecated Do not use. The UsernamePasswordCredentials objects should be immutable
+     */
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+
+    /**
+     * Password property getter.
+     *
+     * @return the password
+     * @see #setPassword(String)
+     */
+    public String getPassword() {
+        return password;
+    }
+
+
+    /**
+     * Get this object string.
+     *
+     * @return the username:password formed string
+     */
+    public String toString() {
+        StringBuffer result = new StringBuffer();
+        result.append(this.userName);
+        result.append(":");
+        result.append((this.password == null) ? "null" : this.password);
+        return result.toString();
+    }
+
+    /**
+     * Does a hash of both user name and password.
+     *
+     * @return The hash code including user name and password.
+     */
+    public int hashCode() {
+        int hash = LangUtils.HASH_SEED;
+        hash = LangUtils.hashCode(hash, this.userName);
+        hash = LangUtils.hashCode(hash, this.password);
+        return hash;
+    }
+
+    /**
+     * These credentials are assumed equal if the username and password are the
+     * same.
+     *
+     * @param o The other object to compare with.
+     *
+     * @return  <code>true</code> if the object is equivalent.
+     */
+    public boolean equals(Object o) {
+        if (o == null) return false;
+        if (this == o) return true;
+        // note - to allow for sub-classing, this checks that class is the same
+        // rather than do "instanceof".
+        if (this.getClass().equals(o.getClass())) {
+            UsernamePasswordCredentials that = (UsernamePasswordCredentials) o;
+
+            if (LangUtils.equals(this.userName, that.userName)
+                    && LangUtils.equals(this.password, that.password) ) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

Modified: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpDecoder.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpDecoder.java?rev=579867&r1=579866&r2=579867&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpDecoder.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpDecoder.java Wed Sep 26 19:01:53 2007
@@ -25,6 +25,11 @@
 import java.util.Date;
 
 import org.apache.ahc.util.DateUtil;
+import org.apache.ahc.util.NameValuePair;
+import org.apache.ahc.auth.AuthChallengeParser;
+import org.apache.ahc.auth.AuthScheme;
+import org.apache.ahc.auth.AuthPolicy;
+import org.apache.ahc.auth.AuthState;
 import org.apache.mina.common.ByteBuffer;
 
 /**
@@ -64,7 +69,10 @@
     
     /** The Constant SET_COOKIE. */
     public static final String SET_COOKIE = "Set-Cookie";
-    
+
+    /** The Constant WWW_AUTH. */
+    public static final String WWW_AUTH = "WWW-Authenticate";
+
     /** The Constant TRANSFER_ENCODING. */
     public static final String TRANSFER_ENCODING = "Transfer-Encoding";
 
@@ -157,7 +165,8 @@
         int pos = line.indexOf(": ");
         String name = line.substring(0, pos);
         String value = line.substring(pos + 2);
-        msg.addHeader(name, value);
+        NameValuePair nvp = new NameValuePair(name, value);
+        msg.addHeader(nvp);
 
         if (name.equalsIgnoreCase(SET_COOKIE)) {
             Cookie cookie = decodeCookie(value);
@@ -184,6 +193,10 @@
 
         if (name.equalsIgnoreCase(TRANSFER_ENCODING) && value != null && value.equalsIgnoreCase(CHUNKED)) {
             msg.setChunked(true);
+        }
+
+        if (name.equalsIgnoreCase(WWW_AUTH)) {
+            msg.addChallenge(nvp);
         }
 
     }

Modified: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpIoHandler.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpIoHandler.java?rev=579867&r1=579866&r2=579867&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpIoHandler.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpIoHandler.java Wed Sep 26 19:01:53 2007
@@ -19,14 +19,19 @@
  */
 package org.apache.ahc.codec;
 
+import java.net.URL;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
-import java.net.URL;
 
-import org.apache.ahc.AsyncHttpClientCallback;
 import org.apache.ahc.AsyncHttpClient;
+import org.apache.ahc.AsyncHttpClientCallback;
+import org.apache.ahc.auth.AuthChallengeParser;
+import org.apache.ahc.auth.AuthPolicy;
+import org.apache.ahc.auth.AuthScheme;
+import org.apache.ahc.auth.AuthState;
+import org.apache.ahc.util.NameValuePair;
 import org.apache.mina.common.IoHandlerAdapter;
 import org.apache.mina.common.IoSession;
 
@@ -35,22 +40,27 @@
  * event processor for the HTTP communication.
  */
 public class HttpIoHandler extends IoHandlerAdapter {
-    
-    /** The Constant CURRENT_REQUEST. */
+
+    /**
+     * The Constant CURRENT_REQUEST.
+     */
     public static final String CURRENT_REQUEST = "CURRENT_REQUEST";
-    
-    /** The Constant CURRENT_RESPONSE. */
+
+    /**
+     * The Constant CURRENT_RESPONSE.
+     */
     public static final String CURRENT_RESPONSE = "CURRENT_RESPONSE";
 
-    /** The Constant DEFAULT_THREAD_POOL_SIZE. */
-    public static final int DEFAULT_THREAD_POOL_SIZE = 10;
-    
-    /** The Constant CONNECTION_CLOSE. */
+    /**
+     * The Constant CONNECTION_CLOSE.
+     */
     public static final String CONNECTION_CLOSE = "close";
 
-    /** The scheduler service to handle timeouts. */
+    /**
+     * The scheduler service to handle timeouts.
+     */
     private ScheduledExecutorService scheduler;
-    
+
     /**
      * Instantiates a new HttpIoHandler.
      */
@@ -61,12 +71,13 @@
     /**
      * Destroys the handler and shuts down the scheduler.
      */
-    public void destroy(){
+    public void destroy() {
         scheduler.shutdownNow();
     }
-    
+
     /**
-     * Stub for handling sessionOpened events. 
+     * Stub for handling sessionOpened events.
+     *
      * @see org.apache.mina.common.IoHandlerAdapter#sessionOpened(org.apache.mina.common.IoSession)
      */
     public void sessionOpened(IoSession ioSession) throws Exception {
@@ -74,10 +85,10 @@
 
     /**
      * Handler for receiving a response from a remote server.
-     * 
+     *
      * @param ioSession the {@link org.apache.mina.common.IoSession} representing the connection to the server.
-     * @param object the {@link HttpResponseMessage} object
-     * @see org.apache.mina.common.IoHandlerAdapter#messageReceived(org.apache.mina.common.IoSession, java.lang.Object)
+     * @param object    the {@link HttpResponseMessage} object
+     * @see org.apache.mina.common.IoHandlerAdapter#messageReceived(org.apache.mina.common.IoSession,java.lang.Object)
      */
     public void messageReceived(IoSession ioSession, Object object) throws Exception {
 
@@ -85,12 +96,11 @@
 
         HttpRequestMessage request = (HttpRequestMessage)ioSession.getAttribute(CURRENT_REQUEST);
 
-
         //Check if we are to handle redirects
         if ((response.getStatusCode() == 301
             || response.getStatusCode() == 302
             || response.getStatusCode() == 307)
-            && request.isFollowRedirects()){
+            && request.isFollowRedirects()) {
             AsyncHttpClient client = (AsyncHttpClient)ioSession.getAttachment();
 
             //Change the request url to the redirect
@@ -105,6 +115,30 @@
             return;
         }
 
+        //Check if we have authentication
+        if (response.getChallenges().size() > 0) {
+            for (NameValuePair nvp : response.getChallenges()) {
+                AuthState state = request.getAuthState();
+                if (state == null){
+                    String id = AuthChallengeParser.extractScheme(nvp.getValue());
+                    AuthScheme authScheme = AuthPolicy.getAuthScheme(id);
+                    state = new AuthState();
+                    state.setAuthScheme(authScheme);
+                    authScheme.processChallenge(nvp.getValue());
+                    request.setAuthState(state);
+                }
+            }
+
+            AsyncHttpClient client = (AsyncHttpClient)ioSession.getAttachment();
+
+            //Authenticate
+            client.sendRequest(request);
+            
+            //Close the current session since we are done with it
+            ioSession.close();
+            return;
+        }
+
         cancelTasks(request);
 
         AsyncHttpClientCallback callback = request.getCallback();
@@ -114,10 +148,10 @@
 
     /**
      * Handler for receiving a notification that an Exception occurred in the communication with the server
-     * 
+     *
      * @param ioSession the {@link org.apache.mina.common.IoSession} representing the connection to the server.
      * @param throwable the {@link java.lang.Throwable} object representing the exception that occurred
-     * @see org.apache.mina.common.IoHandlerAdapter#exceptionCaught(org.apache.mina.common.IoSession, java.lang.Throwable)
+     * @see org.apache.mina.common.IoHandlerAdapter#exceptionCaught(org.apache.mina.common.IoSession,java.lang.Throwable)
      */
     public void exceptionCaught(IoSession ioSession, Throwable throwable) throws Exception {
         //Clean up if any in-proccess decoding was occurring
@@ -128,14 +162,14 @@
 
         AsyncHttpClientCallback callback = request.getCallback();
         callback.onException(throwable);
-        
+
         //Exception is bad, so just close it up
         ioSession.close();
     }
 
     /**
      * Handler for notifying that a connection was closed to the remote server.
-     * 
+     *
      * @param ioSession the {@link org.apache.mina.common.IoSession} representing the connection to the server.
      * @see org.apache.mina.common.IoHandlerAdapter#sessionClosed(org.apache.mina.common.IoSession)
      */
@@ -151,10 +185,10 @@
     /**
      * Handler for notifying that a message was sent to a remote server.  It is responsible for setting up the
      * timeout for a response from the remote server.
-     * 
+     *
      * @param ioSession the {@link org.apache.mina.common.IoSession} representing the connection to the server.
-     * @param object the {@link HttpRequestMessage} object
-     * @see org.apache.mina.common.IoHandlerAdapter#messageSent(org.apache.mina.common.IoSession, java.lang.Object)
+     * @param object    the {@link HttpRequestMessage} object
+     * @see org.apache.mina.common.IoHandlerAdapter#messageSent(org.apache.mina.common.IoSession,java.lang.Object)
      */
     public void messageSent(IoSession ioSession, Object object) throws Exception {
         HttpRequestMessage msg = (HttpRequestMessage)object;
@@ -169,12 +203,12 @@
 
     /**
      * Utility function to cancel a request timeout task.
-     * 
+     *
      * @param request the {@link HttpRequestMessage} request
      */
-    private void cancelTasks(HttpRequestMessage request){
+    private void cancelTasks(HttpRequestMessage request) {
         ScheduledFuture handle = request.getTimeoutHandle();
-        if (handle != null){
+        if (handle != null) {
             boolean canceled = handle.cancel(true);
             //See if it canceled
             if (!canceled) {
@@ -184,18 +218,20 @@
             request.setTimeoutHandle(null);
         }
     }
-    
+
     /**
      * The Class TimeoutTask.  Subclass that encapsulates handler for timeouts for the scheduler.
      */
     class TimeoutTask implements Runnable {
 
-        /** The session object. */
+        /**
+         * The session object.
+         */
         private IoSession sess;
 
         /**
          * Instantiates a new timeout task.
-         * 
+         *
          * @param sess the {@link org.apache.mina.common.IoSession} representing the connection to the server.
          */
         public TimeoutTask(IoSession sess) {
@@ -204,7 +240,7 @@
 
         /**
          * The running task which handles timing out the connection.
-         * 
+         *
          * @see java.lang.Runnable#run()
          */
         public void run() {

Modified: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpMessage.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpMessage.java?rev=579867&r1=579866&r2=579867&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpMessage.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpMessage.java Wed Sep 26 19:01:53 2007
@@ -31,6 +31,9 @@
  */
 public class HttpMessage {
 
+    /** The Constant DEFAULT_CHARSET. */
+    public static final String DEFAULT_CHARSET = "ISO-8859-1";
+
     /** The Constant CONTENT_TYPE. */
     public static final String CONTENT_TYPE = "Content-Type";
     
@@ -51,6 +54,9 @@
     
     /** The content container. */
     protected ByteArrayOutputStream content;
+
+    /** The character charset **/
+    protected String charset = DEFAULT_CHARSET;
 
     /**
      * Gets the <code>String</code> content.  This method should only be

Modified: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestEncoder.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestEncoder.java?rev=579867&r1=579866&r2=579867&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestEncoder.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestEncoder.java Wed Sep 26 19:01:53 2007
@@ -30,6 +30,8 @@
 
 import org.apache.ahc.util.EncodingUtil;
 import org.apache.ahc.util.NameValuePair;
+import org.apache.ahc.auth.AuthState;
+import org.apache.ahc.auth.AuthScope;
 import org.apache.mina.common.ByteBuffer;
 import org.apache.mina.common.IoSession;
 import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
@@ -75,7 +77,7 @@
      * Method responsible for encoding a HttpRequestMessage into raw bytes.
      * 
      * @param ioSession the {@link org.apache.mina.common.IoSession} representing the connection to the server.
-     * @param object the {@link HttpRequestMessage} object
+     * @param message the {@link HttpRequestMessage} object
      * @param out {@link org.apache.mina.filter.codec.ProtocolEncoderOutput} used for output
      * @see org.apache.mina.filter.codec.ProtocolEncoder#encode(org.apache.mina.common.IoSession, java.lang.Object, org.apache.mina.filter.codec.ProtocolEncoderOutput)
      */
@@ -198,6 +200,17 @@
             buf.putString(": ", encoder);
             buf.putString(value, encoder);
             buf.put(CRLF);
+        }
+
+        //Process authentication
+        AuthState state = msg.getAuthState();
+        if (state != null){
+            String auth = state.getAuthScheme().authenticate(msg.getCredential(new AuthScope(msg.getHost(), msg.getPort(), state.getAuthScheme().getRealm())),msg);
+            buf.putString("Authorization", encoder);
+            buf.putString(": ", encoder);
+            buf.putString(auth, encoder);
+            buf.put(CRLF);
+            state.setAuthAttempted(true);
         }
     }
 

Modified: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java?rev=579867&r1=579866&r2=579867&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java Wed Sep 26 19:01:53 2007
@@ -19,72 +19,128 @@
  */
 package org.apache.ahc.codec;
 
-import org.apache.ahc.AsyncHttpClientCallback;
-
 import java.net.ProtocolException;
 import java.net.URL;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.concurrent.ScheduledFuture;
 
+import org.apache.ahc.AsyncHttpClientCallback;
+import org.apache.ahc.auth.AuthScope;
+import org.apache.ahc.auth.AuthState;
+import org.apache.ahc.auth.Credentials;
+
 /**
  * The Class HttpRequestMessage. This is an object representation of an HTTP request.
  */
 public class HttpRequestMessage extends HttpMessage {
 
-    /** The Constant DEFAULT_REQUEST_TIMEOUT. */
+    /**
+     * The Constant DEFAULT_REQUEST_TIMEOUT.
+     */
     public static final int DEFAULT_REQUEST_TIMEOUT = 30000;
 
-    /** The Constant REQUEST_GET. */
+    /**
+     * The Constant DEFAULT_CREDENTIAL_CHARSET.
+     */
+    public static final String DEFAULT_CREDENTIAL_CHARSET = "US-ASCII";
+
+    /**
+     * The Constant REQUEST_GET.
+     */
     public static final String REQUEST_GET = "GET";
-    
-    /** The Constant REQUEST_POST. */
+
+    /**
+     * The Constant REQUEST_POST.
+     */
     public static final String REQUEST_POST = "POST";
-    
-    /** The Constant REQUEST_HEAD. */
+
+    /**
+     * The Constant REQUEST_HEAD.
+     */
     public static final String REQUEST_HEAD = "HEAD";
-    
-    /** The Constant REQUEST_OPTIONS. */
+
+    /**
+     * The Constant REQUEST_OPTIONS.
+     */
     public static final String REQUEST_OPTIONS = "OPTIONS";
-    
-    /** The Constant REQUEST_PUT. */
+
+    /**
+     * The Constant REQUEST_PUT.
+     */
     public static final String REQUEST_PUT = "PUT";
-    
-    /** The Constant REQUEST_DELETE. */
+
+    /**
+     * The Constant REQUEST_DELETE.
+     */
     public static final String REQUEST_DELETE = "DELETE";
-    
-    /** The Constant REQUEST_TRACE. */
+
+    /**
+     * The Constant REQUEST_TRACE.
+     */
     public static final String REQUEST_TRACE = "TRACE";
 
-    /** The request method. */
+    /**
+     * The request method.
+     */
     private String requestMethod = REQUEST_GET;
-    
-    /** The request url. */
+
+    /**
+     * The request url.
+     */
     private URL url;
-    
-    /** The parameters. */
+
+    /**
+     * The parameters.
+     */
     private Map<String, String> parameters = new HashMap<String, String>();
-    
-    /** The user agent. */
+
+    /**
+     * The user agent.
+     */
     private String userAgent = "AsyncHttpClient 1.0";
-    
-    /** The follow redirects. */
+
+    /**
+     * The follow redirects.
+     */
     private boolean followRedirects = true;
-    
-    /** The timeout handle. */
+
+    /**
+     * The timeout handle.
+     */
     private ScheduledFuture timeoutHandle;
-    
-    /** The callback. */
+
+    /**
+     * The callback.
+     */
     private AsyncHttpClientCallback callback;
-    
-    /** The time out. */
+
+    /**
+     * The time out.
+     */
     private int timeOut = DEFAULT_REQUEST_TIMEOUT;
 
     /**
+     * The character credential charset *
+     */
+    protected String credentialCharset = DEFAULT_CREDENTIAL_CHARSET;
+
+    /**
+     * The challenge map *
+     */
+    private AuthState authState;
+
+    /**
+     * The credentials map *
+     */
+    private HashMap<AuthScope, Credentials> credentials = new HashMap<AuthScope, Credentials>();
+
+    /**
      * Instantiates a new http request message.
-     * 
-     * @param url the complete url for which the request including scheme, host, port[optional], and query 
-     * 				(i.e. <code>http://www.example.com:8080/example.cgi?test=me</code>).
+     *
+     * @param url      the complete url for which the request including scheme, host, port[optional], and query
+     *                 (i.e. <code>http://www.example.com:8080/example.cgi?test=me</code>).
      * @param callback the {@link org.apache.ahc.AsyncHttpClientCallback} callback class to receive notifications when they occur.
      */
     public HttpRequestMessage(URL url, AsyncHttpClientCallback callback) {
@@ -94,7 +150,7 @@
 
     /**
      * Gets the time out.
-     * 
+     *
      * @return the time out in milliseconds.  Defaults to {@link #DEFAULT_REQUEST_TIMEOUT} if not set.
      */
     public int getTimeOut() {
@@ -103,7 +159,7 @@
 
     /**
      * Sets the time out.
-     * 
+     *
      * @param timeOut the new time out in milliseconds. Defaults to {@link #DEFAULT_REQUEST_TIMEOUT} if not set.
      */
     public void setTimeOut(int timeOut) {
@@ -112,7 +168,7 @@
 
     /**
      * Gets the timeout handle.
-     * 
+     *
      * @return the timeout <code>ScheduledFuture</code> handle
      */
     protected ScheduledFuture getTimeoutHandle() {
@@ -121,7 +177,7 @@
 
     /**
      * Sets the timeout handle.
-     * 
+     *
      * @param timeoutHandle the new <code>ScheduledFuture</code> timeout handle
      */
     protected void setTimeoutHandle(ScheduledFuture timeoutHandle) {
@@ -130,7 +186,7 @@
 
     /**
      * Gets the request method.
-     * 
+     *
      * @return the request method.  Defaults to {@link #REQUEST_GET} if not set.
      */
     public String getRequestMethod() {
@@ -140,7 +196,7 @@
 
     /**
      * Gets the callback.
-     * 
+     *
      * @return the {@link org.apache.ahc.AsyncHttpClientCallback} callback
      */
     public AsyncHttpClientCallback getCallback() {
@@ -149,18 +205,17 @@
 
     /**
      * Sets the request method.
-     * 
+     *
      * @param requestMethod the new request method
-     * 
-     * @throws ProtocolException 	if the request method is not of type {@link #REQUEST_GET},
-     * 								{@link #REQUEST_POST},{@link #REQUEST_HEAD},{@link #REQUEST_OPTIONS},
-     * 								{@link #REQUEST_PUT},{@link #REQUEST_DELETE}, or {@link #REQUEST_TRACE}
+     * @throws ProtocolException if the request method is not of type {@link #REQUEST_GET},
+     *                           {@link #REQUEST_POST},{@link #REQUEST_HEAD},{@link #REQUEST_OPTIONS},
+     *                           {@link #REQUEST_PUT},{@link #REQUEST_DELETE}, or {@link #REQUEST_TRACE}
      */
     public void setRequestMethod(String requestMethod) throws ProtocolException {
         if (requestMethod.equals(REQUEST_GET)
             || requestMethod.equals(REQUEST_POST)
             || requestMethod.equals(REQUEST_HEAD)
-            || requestMethod.equals(REQUEST_OPTIONS) 
+            || requestMethod.equals(REQUEST_OPTIONS)
             || requestMethod.equals(REQUEST_PUT)
             || requestMethod.equals(REQUEST_DELETE)
             || requestMethod.equals(REQUEST_TRACE)) {
@@ -173,7 +228,7 @@
 
     /**
      * Gets the url.
-     * 
+     *
      * @return the url
      */
     public URL getUrl() {
@@ -182,7 +237,7 @@
 
     /**
      * Sets the url.
-     * 
+     *
      * @param url the new url
      */
     public void setUrl(URL url) {
@@ -191,7 +246,7 @@
 
     /**
      * Gets the path part of the url.
-     * 
+     *
      * @return the path part of the url
      */
     public String getPath() {
@@ -200,19 +255,19 @@
 
     /**
      * Gets the host part of the url.
-     * 
+     *
      * @return the host part of the url
      */
-    public String getHost(){
+    public String getHost() {
         return url.getHost();
     }
 
     /**
      * Gets the port part of the url.
-     * 
+     *
      * @return the port part of the url
      */
-    public int getPort(){
+    public int getPort() {
         String scheme = url.getProtocol();
         int port = url.getPort();
         if (scheme.toLowerCase().equals("https")) {
@@ -228,18 +283,26 @@
 
     /**
      * Gets the protocol part of the url.
-     * 
+     *
      * @return the protocol part of the url
      */
-    public String getProtocol(){
-        return url.getProtocol();     
+    public String getProtocol() {
+        return url.getProtocol();
+    }
+
+    /**
+     * Gets the query part of the url.
+     *
+     * @return the query part of the url
+     */
+    public String getQuery() {
+        return url.getQuery();
     }
 
     /**
      * Gets a parameter from the parameter map.
-     * 
+     *
      * @param name the parameter name
-     * 
      * @return the parameter value
      */
     public String getParameter(String name) {
@@ -248,7 +311,7 @@
 
     /**
      * Gets the parameter map.
-     * 
+     *
      * @return the parameter map
      */
     public Map<String, String> getParameters() {
@@ -257,7 +320,7 @@
 
     /**
      * Sets the parameter map.
-     * 
+     *
      * @param parameters the parameter map
      */
     public void setParameters(Map<String, String> parameters) {
@@ -266,8 +329,8 @@
 
     /**
      * Sets a single parameter.
-     * 
-     * @param name the parameter name
+     *
+     * @param name  the parameter name
      * @param value the value of parameter
      */
     public void setParameter(String name, String value) {
@@ -276,7 +339,7 @@
 
     /**
      * Gets the user agent string.
-     * 
+     *
      * @return the user agent <code>String</code>
      */
     public String getUserAgent() {
@@ -285,7 +348,7 @@
 
     /**
      * Sets the user agent string.
-     * 
+     *
      * @param userAgent the new user agent <code>String</code>
      */
     public void setUserAgent(String userAgent) {
@@ -294,9 +357,9 @@
 
     /**
      * Checks if the request will follow redirects (301,302 and 307 HTTP status).
-     * 
-     * @return <code>true</code>, if the request will follow redirects, <code>false</code> if not. 
-     * 			 Defaults to <code>true</code> if not set.
+     *
+     * @return <code>true</code>, if the request will follow redirects, <code>false</code> if not.
+     *         Defaults to <code>true</code> if not set.
      */
     public boolean isFollowRedirects() {
         return followRedirects;
@@ -304,11 +367,100 @@
 
     /**
      * Sets whether the request will follow redirects (301,302 and 307 HTTP status).
-     * 
-     * @param followRedirects 	the new follow redirects.  Set to <code>true</code>, if the request 
-     * 							will follow redirects, <code>false</code> if not.
+     *
+     * @param followRedirects the new follow redirects.  Set to <code>true</code>, if the request
+     *                        will follow redirects, <code>false</code> if not.
      */
     public void setFollowRedirects(boolean followRedirects) {
         this.followRedirects = followRedirects;
+    }
+
+    /**
+     * Gets the credential character set
+     *
+     * @return the credential character set. Defaults to {@link #DEFAULT_CREDENTIAL_CHARSET} if not set.
+     */
+    public String getCredentialCharset() {
+        return credentialCharset;
+    }
+
+    /**
+     * Sets the credential character set
+     *
+     * @param credentialCharset the new credential character set
+     */
+    public void setCredentialCharset(String credentialCharset) {
+        this.credentialCharset = credentialCharset;
+    }
+
+    /**
+     * Gets the authorization state
+     *
+     * @return the authorization state
+     */
+    public AuthState getAuthState() {
+        return authState;
+    }
+
+    /**
+     * Sets the authorization state
+     *
+     * @param authState the authorization state
+     */
+    public void setAuthState(AuthState authState) {
+        this.authState = authState;
+    }
+
+    /**
+     * Gets the credentials map
+     *
+     * @return the credential map
+     */
+    public HashMap<AuthScope, Credentials> getCredentials() {
+        return credentials;
+    }
+
+    /**
+     * Gets a single credential
+     *
+     * @param scope the (@link org.apache.ahc.AuthScope} object
+     * @return (@linkorg.apache.ahc.Credentials} objectiffoundor <code>null</code> ifnotfound
+     */
+    public Credentials getCredential(AuthScope scope) {
+        return matchCredentials(credentials, scope);
+    }
+
+    private static Credentials matchCredentials(final HashMap map, final AuthScope authscope) {
+        // see if we get a direct hit
+        Credentials creds = (Credentials)map.get(authscope);
+        if (creds == null) {
+            // Nope.
+            // Do a full scan
+            int bestMatchFactor = -1;
+            AuthScope bestMatch = null;
+            Iterator items = map.keySet().iterator();
+            while (items.hasNext()) {
+                AuthScope current = (AuthScope)items.next();
+                int factor = authscope.match(current);
+                if (factor > bestMatchFactor) {
+                    bestMatchFactor = factor;
+                    bestMatch = current;
+                }
+            }
+            if (bestMatch != null) {
+                creds = (Credentials)map.get(bestMatch);
+            }
+        }
+        return creds;
+    }
+
+    /**
+     * Adds a credential
+     *
+     * @param scope       the (@link org.apache.ahc.AuthScope} object
+     * @param credentials (@link org.apache.ahc.Credentials} object
+     */
+    public void addCredentials(AuthScope scope, Credentials credentials) {
+        this.credentials.put(scope, credentials);
     }
 }

Modified: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpResponseMessage.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpResponseMessage.java?rev=579867&r1=579866&r2=579867&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpResponseMessage.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpResponseMessage.java Wed Sep 26 19:01:53 2007
@@ -19,6 +19,12 @@
  */
 package org.apache.ahc.codec;
 
+import java.util.HashMap;
+import java.util.ArrayList;
+
+import org.apache.ahc.auth.AuthState;
+import org.apache.ahc.util.NameValuePair;
+
 /**
  * The Class HttpResponseMessage. This is an object representation of an HTTP response.
  */
@@ -72,6 +78,9 @@
     /** The attachment. */
     private Object attachment;
 
+    /** The challenge list **/
+    private ArrayList<NameValuePair> challenges = new ArrayList<NameValuePair>();
+    
     /**
      * Gets the HTTP status code.
      * 
@@ -215,5 +224,22 @@
      */
     public void setAttachment(Object attachment) {
         this.attachment = attachment;
+    }
+
+        /**
+     * Gets the challenges map
+     * @return the challenges map.
+     */
+    public ArrayList<NameValuePair> getChallenges() {
+        return challenges;
+    }
+
+    /**
+     * Adds a challenge to the challenge list
+     *
+     * @param challenge the challenge name value pair
+     */
+    public void addChallenge(NameValuePair challenge){
+        challenges.add(challenge);
     }
 }

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/ParameterFormatter.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/ParameterFormatter.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/ParameterFormatter.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/ParameterFormatter.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,226 @@
+/*
+ *  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.util;
+
+/**
+ * <p>
+ *  This formatter produces a textual representation of attribute/value pairs. It
+ *  comforms to the generic grammar and formatting rules outlined in the
+ *  <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.1">Section 2.1</a>
+ *  and
+ *  <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">Section 3.6</a>
+ *  of <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>
+ * </p>
+ * <h>2.1 Augmented BNF</h>
+ * <p>
+ *  Many HTTP/1.1 header field values consist of words separated by LWS or special
+ *  characters. These special characters MUST be in a quoted string to be used within
+ *  a parameter value (as defined in section 3.6).
+ * <p>
+ * <pre>
+ * token          = 1*<any CHAR except CTLs or separators>
+ * separators     = "(" | ")" | "<" | ">" | "@"
+ *                | "," | ";" | ":" | "\" | <">
+ *                | "/" | "[" | "]" | "?" | "="
+ *                | "{" | "}" | SP | HT
+ * </pre>
+ * <p>
+ *  A string of text is parsed as a single word if it is quoted using double-quote marks.
+ * </p>
+ * <pre>
+ * quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
+ * qdtext         = <any TEXT except <">>
+ * </pre>
+ * <p>
+ *  The backslash character ("\") MAY be used as a single-character quoting mechanism only
+ *  within quoted-string and comment constructs.
+ * </p>
+ * <pre>
+ * quoted-pair    = "\" CHAR
+ * </pre>
+ * <h>3.6 Transfer Codings</h>
+ * <p>
+ *  Parameters are in the form of attribute/value pairs.
+ * </p>
+ * <pre>
+ * parameter               = attribute "=" value
+ * attribute               = token
+ * value                   = token | quoted-string
+ * </pre>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ */
+public class ParameterFormatter {
+        /**
+     * Special characters that can be used as separators in HTTP parameters.
+     * These special characters MUST be in a quoted string to be used within
+     * a parameter value
+     */
+    private static final char[] SEPARATORS   = {
+            '(', ')', '<', '>', '@',
+            ',', ';', ':', '\\', '"',
+            '/', '[', ']', '?', '=',
+            '{', '}', ' ', '\t'
+            };
+
+    /**
+     * Unsafe special characters that must be escaped using the backslash
+     * character
+     */
+    private static final char[] UNSAFE_CHARS = {
+            '"', '\\'
+            };
+
+    /**
+     * This flag determines whether all parameter values must be enclosed in
+     * quotation marks, even if they do not contain any special characters
+     */
+    private boolean alwaysUseQuotes = true;
+
+    /** Default ParameterFormatter constructor */
+    public ParameterFormatter() {
+        super();
+    }
+
+    private static boolean isOneOf(char[] chars, char ch) {
+        for (int i = 0; i < chars.length; i++) {
+            if (ch == chars[i]) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean isUnsafeChar(char ch) {
+        return isOneOf(UNSAFE_CHARS, ch);
+    }
+
+    private static boolean isSeparator(char ch) {
+        return isOneOf(SEPARATORS, ch);
+    }
+
+    /**
+     * Determines whether all parameter values must be enclosed in quotation
+     * marks, even if they do not contain any special characters
+     *
+     * @return <tt>true</tt> if all parameter values must be enclosed in
+     * quotation marks, <tt>false</tt> otherwise
+     */
+    public boolean isAlwaysUseQuotes() {
+        return alwaysUseQuotes;
+    }
+
+    /**
+     * Defines whether all parameter values must be enclosed in quotation
+     * marks, even if they do not contain any special characters
+     *
+     * @param alwaysUseQuotes
+     */
+    public void setAlwaysUseQuotes(boolean alwaysUseQuotes) {
+        this.alwaysUseQuotes = alwaysUseQuotes;
+    }
+
+    /**
+     * Formats the given parameter value using formatting rules defined
+     * in RFC 2616
+     *
+     * @param buffer output buffer
+     * @param value the parameter value to be formatted
+     * @param alwaysUseQuotes <tt>true</tt> if the parameter value must
+     * be enclosed in quotation marks, even if it does not contain any special
+     * characters<tt>, false</tt> only if the parameter value contains
+     * potentially unsafe special characters
+     */
+    public static void formatValue(
+            final StringBuffer buffer, final String value, boolean alwaysUseQuotes) {
+        if (buffer == null) {
+            throw new IllegalArgumentException("String buffer may not be null");
+        }
+        if (value == null) {
+            throw new IllegalArgumentException("Value buffer may not be null");
+        }
+        if (alwaysUseQuotes) {
+            buffer.append('"');
+            for (int i = 0; i < value.length(); i++) {
+                char ch = value.charAt(i);
+                if (isUnsafeChar(ch)) {
+                    buffer.append('\\');
+                }
+                buffer.append(ch);
+            }
+            buffer.append('"');
+        } else {
+            int offset = buffer.length();
+            boolean unsafe = false;
+            for (int i = 0; i < value.length(); i++) {
+                char ch = value.charAt(i);
+                if (isSeparator(ch)) {
+                    unsafe = true;
+                }
+                if (isUnsafeChar(ch)) {
+                    buffer.append('\\');
+                }
+                buffer.append(ch);
+            }
+            if (unsafe) {
+                buffer.insert(offset, '"');
+                buffer.append('"');
+            }
+        }
+    }
+
+    /**
+     * Produces textual representaion of the attribute/value pair using
+     * formatting rules defined in RFC 2616
+     *
+     * @param buffer output buffer
+     * @param param the parameter to be formatted
+     */
+    public void format(final StringBuffer buffer, final NameValuePair param) {
+        if (buffer == null) {
+            throw new IllegalArgumentException("String buffer may not be null");
+        }
+        if (param == null) {
+            throw new IllegalArgumentException("Parameter may not be null");
+        }
+        buffer.append(param.getName());
+        String value = param.getValue();
+        if (value != null) {
+            buffer.append("=");
+            formatValue(buffer, value, this.alwaysUseQuotes);
+        }
+    }
+
+    /**
+     * Produces textual representaion of the attribute/value pair using
+     * formatting rules defined in RFC 2616
+     *
+     * @param param the parameter to be formatted
+     *
+     * @return RFC 2616 conformant textual representaion of the
+     * attribute/value pair
+     */
+    public String format(final NameValuePair param) {
+        StringBuffer buffer = new StringBuffer();
+        format(buffer, param);
+        return buffer.toString();
+    }
+}

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/ParameterParser.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/ParameterParser.java?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/ParameterParser.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/ParameterParser.java Wed Sep 26 19:01:53 2007
@@ -0,0 +1,228 @@
+/*
+ *  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.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ParameterParser {
+
+    /**
+     * String to be parsed
+     */
+    private char[] chars = null;
+
+    /**
+     * Current position in the string
+     */
+    private int pos = 0;
+
+    /**
+     * Maximum position in the string
+     */
+    private int len = 0;
+
+    /**
+     * Start of a token
+     */
+    private int i1 = 0;
+
+    /**
+     * End of a token
+     */
+    private int i2 = 0;
+
+    /**
+     * Default ParameterParser constructor
+     */
+    public ParameterParser() {
+        super();
+    }
+
+
+    /**
+     * Are there any characters left to parse?
+     */
+    private boolean hasChar() {
+        return this.pos < this.len;
+    }
+
+
+    /**
+     * A helper method to process the parsed token.
+     */
+    private String getToken(boolean quoted) {
+        // Trim leading white spaces
+        while ((i1 < i2) && (Character.isWhitespace(chars[i1]))) {
+            i1++;
+        }
+        // Trim trailing white spaces
+        while ((i2 > i1) && (Character.isWhitespace(chars[i2 - 1]))) {
+            i2--;
+        }
+        // Strip away quotes if necessary
+        if (quoted) {
+            if (((i2 - i1) >= 2)
+                && (chars[i1] == '"')
+                && (chars[i2 - 1] == '"')) {
+                i1++;
+                i2--;
+            }
+        }
+        String result = null;
+        if (i2 >= i1) {
+            result = new String(chars, i1, i2 - i1);
+        }
+        return result;
+    }
+
+
+    /**
+     * Is given character present in the array of characters?
+     */
+    private boolean isOneOf(char ch, char[] charray) {
+        boolean result = false;
+        for (int i = 0; i < charray.length; i++) {
+            if (ch == charray[i]) {
+                result = true;
+                break;
+            }
+        }
+        return result;
+    }
+
+
+    /**
+     * Parse out a token until any of the given terminators
+     * is encountered.
+     */
+    private String parseToken(final char[] terminators) {
+        char ch;
+        i1 = pos;
+        i2 = pos;
+        while (hasChar()) {
+            ch = chars[pos];
+            if (isOneOf(ch, terminators)) {
+                break;
+            }
+            i2++;
+            pos++;
+        }
+        return getToken(false);
+    }
+
+
+    /**
+     * Parse out a token until any of the given terminators
+     * is encountered. Special characters in quoted tokens
+     * are escaped.
+     */
+    private String parseQuotedToken(final char[] terminators) {
+        char ch;
+        i1 = pos;
+        i2 = pos;
+        boolean quoted = false;
+        boolean charEscaped = false;
+        while (hasChar()) {
+            ch = chars[pos];
+            if (!quoted && isOneOf(ch, terminators)) {
+                break;
+            }
+            if (!charEscaped && ch == '"') {
+                quoted = !quoted;
+            }
+            charEscaped = (!charEscaped && ch == '\\');
+            i2++;
+            pos++;
+
+        }
+        return getToken(true);
+    }
+
+    /**
+     * Extracts a list of {@link NameValuePair}s from the given string.
+     *
+     * @param str the string that contains a sequence of name/value pairs
+     * @return a list of {@link NameValuePair}s
+     */
+    public List parse(final String str, char separator) {
+
+        if (str == null) {
+            return new ArrayList();
+        }
+        return parse(str.toCharArray(), separator);
+    }
+
+    /**
+     * Extracts a list of {@link NameValuePair}s from the given array of
+     * characters.
+     *
+     * @param chars the array of characters that contains a sequence of
+     *              name/value pairs
+     * @return a list of {@link NameValuePair}s
+     */
+    public List parse(final char[] chars, char separator) {
+
+        if (chars == null) {
+            return new ArrayList();
+        }
+        return parse(chars, 0, chars.length, separator);
+    }
+
+
+    /**
+     * Extracts a list of {@link NameValuePair}s from the given array of
+     * characters.
+     *
+     * @param chars  the array of characters that contains a sequence of
+     *               name/value pairs
+     * @param offset - the initial offset.
+     * @param length - the length.
+     * @return a list of {@link NameValuePair}s
+     */
+    public List parse(final char[] chars, int offset, int length, char separator) {
+
+        if (chars == null) {
+            return new ArrayList();
+        }
+        List params = new ArrayList();
+        this.chars = chars;
+        this.pos = offset;
+        this.len = length;
+
+        String paramName = null;
+        String paramValue = null;
+        while (hasChar()) {
+            paramName = parseToken(new char[] {'=', separator});
+            paramValue = null;
+            if (hasChar() && (chars[pos] == '=')) {
+                pos++; // skip '='
+                paramValue = parseQuotedToken(new char[] {separator});
+            }
+            if (hasChar() && (chars[pos] == separator)) {
+                pos++; // skip separator
+            }
+            if (paramName != null && !(paramName.equals("") && paramValue == null)) {
+                params.add(new NameValuePair(paramName, paramValue));
+            }
+        }
+        return params;
+    }
+}

Added: geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_basic/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_basic/WEB-INF/web.xml?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_basic/WEB-INF/web.xml (added)
+++ geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_basic/WEB-INF/web.xml Wed Sep 26 19:01:53 2007
@@ -0,0 +1,45 @@
+<!--
+  ~  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.
+  -->
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd"
+         version="2.4">
+    <login-config>
+        <auth-method>BASIC</auth-method>
+        <realm-name>test</realm-name>
+    </login-config>
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>everything</web-resource-name>
+            <url-pattern>/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>test</role-name>
+        </auth-constraint>
+        <user-data-constraint>
+            <transport-guarantee>NONE</transport-guarantee>
+        </user-data-constraint>
+    </security-constraint>
+
+    <security-role>
+        <description>Test role</description>
+        <role-name>test</role-name>
+    </security-role>
+
+</web-app>

Added: geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_basic/secure.jsp
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_basic/secure.jsp?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_basic/secure.jsp (added)
+++ geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_basic/secure.jsp Wed Sep 26 19:01:53 2007
@@ -0,0 +1,2 @@
+Hello World!
+

Added: geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_digest/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_digest/WEB-INF/web.xml?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_digest/WEB-INF/web.xml (added)
+++ geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_digest/WEB-INF/web.xml Wed Sep 26 19:01:53 2007
@@ -0,0 +1,45 @@
+<!--
+  ~  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.
+  -->
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd"
+         version="2.4">
+    <login-config>
+        <auth-method>DIGEST</auth-method>
+        <realm-name>test</realm-name>
+    </login-config>
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>everything</web-resource-name>
+            <url-pattern>/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>test</role-name>
+        </auth-constraint>
+        <user-data-constraint>
+            <transport-guarantee>NONE</transport-guarantee>
+        </user-data-constraint>
+    </security-constraint>
+
+    <security-role>
+        <description>Test role</description>
+        <role-name>test</role-name>
+    </security-role>
+
+</web-app>

Added: geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_digest/secure.jsp
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_digest/secure.jsp?rev=579867&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_digest/secure.jsp (added)
+++ geronimo/sandbox/AsyncHttpClient/src/test/catalina/webapps/auth_digest/secure.jsp Wed Sep 26 19:01:53 2007
@@ -0,0 +1 @@
+Hello World!