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!