auto import from //depot/cupcake/@135843
diff --git a/src/org/apache/commons/codec/BinaryDecoder.java b/src/org/apache/commons/codec/BinaryDecoder.java
new file mode 100644
index 0000000..7aebabf
--- /dev/null
+++ b/src/org/apache/commons/codec/BinaryDecoder.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec;
+
+/**
+ * Defines common decoding methods for byte array decoders.
+ *
+ * @author Apache Software Foundation
+ * @version $Id: BinaryDecoder.java,v 1.10 2004/06/15 18:14:15 ggregory Exp $
+ */
+public interface BinaryDecoder extends Decoder {
+
+    /**
+     * Decodes a byte array and returns the results as a byte array. 
+     *
+     * @param pArray A byte array which has been encoded with the
+     *      appropriate encoder
+     * 
+     * @return a byte array that contains decoded content
+     * 
+     * @throws DecoderException A decoder exception is thrown
+     *          if a Decoder encounters a failure condition during
+     *          the decode process.
+     */
+    byte[] decode(byte[] pArray) throws DecoderException;
+}  
+
diff --git a/src/org/apache/commons/codec/BinaryEncoder.java b/src/org/apache/commons/codec/BinaryEncoder.java
new file mode 100644
index 0000000..52859ed
--- /dev/null
+++ b/src/org/apache/commons/codec/BinaryEncoder.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec;
+
+/**
+ * Defines common encoding methods for byte array encoders.
+ * 
+ * @author Apache Software Foundation
+ * @version $Id: BinaryEncoder.java,v 1.10 2004/02/29 04:08:31 tobrien Exp $
+ */
+public interface BinaryEncoder extends Encoder {
+    
+    /**
+     * Encodes a byte array and return the encoded data
+     * as a byte array.
+     * 
+     * @param pArray Data to be encoded
+     *
+     * @return A byte array containing the encoded data
+     * 
+     * @throws EncoderException thrown if the Encoder
+     *      encounters a failure condition during the
+     *      encoding process.
+     */
+    byte[] encode(byte[] pArray) throws EncoderException;
+}  
+
diff --git a/src/org/apache/commons/codec/Decoder.java b/src/org/apache/commons/codec/Decoder.java
new file mode 100644
index 0000000..184920c
--- /dev/null
+++ b/src/org/apache/commons/codec/Decoder.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec;
+
+/**
+ * <p>Provides the highest level of abstraction for Decoders.
+ * This is the sister interface of {@link Encoder}.  All
+ * Decoders implement this common generic interface.</p>
+ * 
+ * <p>Allows a user to pass a generic Object to any Decoder 
+ * implementation in the codec package.</p>
+ * 
+ * <p>One of the two interfaces at the center of the codec package.</p>
+ * 
+ * @author Apache Software Foundation
+ * @version $Id: Decoder.java,v 1.9 2004/02/29 04:08:31 tobrien Exp $
+ */
+public interface Decoder {
+
+    /**
+     * Decodes an "encoded" Object and returns a "decoded"
+     * Object.  Note that the implementation of this
+     * interface will try to cast the Object parameter
+     * to the specific type expected by a particular Decoder
+     * implementation.  If a {@link java.lang.ClassCastException} occurs
+     * this decode method will throw a DecoderException.
+     * 
+     * @param pObject an object to "decode"
+     * 
+     * @return a 'decoded" object
+     * 
+     * @throws DecoderException a decoder exception can
+     * be thrown for any number of reasons.  Some good
+     * candidates are that the parameter passed to this
+     * method is null, a param cannot be cast to the
+     * appropriate type for a specific encoder.
+     */
+    Object decode(Object pObject) throws DecoderException;
+}  
+
diff --git a/src/org/apache/commons/codec/DecoderException.java b/src/org/apache/commons/codec/DecoderException.java
new file mode 100644
index 0000000..f35c016
--- /dev/null
+++ b/src/org/apache/commons/codec/DecoderException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec;
+
+/**
+ * Thrown when a Decoder has encountered a failure condition during a decode. 
+ * 
+ * @author Apache Software Foundation
+ * @version $Id: DecoderException.java,v 1.9 2004/02/29 04:08:31 tobrien Exp $
+ */
+public class DecoderException extends Exception {
+
+    /**
+     * Creates a DecoderException
+     * 
+     * @param pMessage A message with meaning to a human
+     */
+    public DecoderException(String pMessage) {
+        super(pMessage);
+    }
+
+}  
+
diff --git a/src/org/apache/commons/codec/Encoder.java b/src/org/apache/commons/codec/Encoder.java
new file mode 100644
index 0000000..fa339ee
--- /dev/null
+++ b/src/org/apache/commons/codec/Encoder.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec;
+
+/**
+ * <p>Provides the highest level of abstraction for Encoders.
+ * This is the sister interface of {@link Decoder}.  Every implementation of
+ * Encoder provides this common generic interface whic allows a user to pass a 
+ * generic Object to any Encoder implementation in the codec package.</p>
+ *
+ * @author Apache Software Foundation
+ * @version $Id: Encoder.java,v 1.10 2004/02/29 04:08:31 tobrien Exp $
+ */
+public interface Encoder {
+    
+    /**
+     * Encodes an "Object" and returns the encoded content 
+     * as an Object.  The Objects here may just be <code>byte[]</code>
+     * or <code>String</code>s depending on the implementation used.
+     *   
+     * @param pObject An object ot encode
+     * 
+     * @return An "encoded" Object
+     * 
+     * @throws EncoderException an encoder exception is
+     *  thrown if the encoder experiences a failure
+     *  condition during the encoding process.
+     */
+    Object encode(Object pObject) throws EncoderException;
+}  
+
diff --git a/src/org/apache/commons/codec/EncoderException.java b/src/org/apache/commons/codec/EncoderException.java
new file mode 100644
index 0000000..0e202c1
--- /dev/null
+++ b/src/org/apache/commons/codec/EncoderException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec;
+
+/**
+ * Thrown when there is a failure condition during the encoding process.  This
+ * exception is thrown when an Encoder encounters a encoding specific exception
+ * such as invalid data, inability to calculate a checksum, characters outside of the 
+ * expected range.
+ * 
+ * @author Apache Software Foundation
+ * @version $Id: EncoderException.java,v 1.10 2004/02/29 04:08:31 tobrien Exp $
+ */
+public class EncoderException extends Exception {
+
+    /**
+     * Creates a new instance of this exception with an useful message.
+     * 
+     * @param pMessage a useful message relating to the encoder specific error.
+     */
+    public EncoderException(String pMessage) {
+        super(pMessage);
+    }
+}  
+
diff --git a/src/org/apache/commons/codec/StringDecoder.java b/src/org/apache/commons/codec/StringDecoder.java
new file mode 100644
index 0000000..9b1a0cd
--- /dev/null
+++ b/src/org/apache/commons/codec/StringDecoder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec;
+
+/**
+ * Decodes a String into a String. 
+ *
+ * @author Apache Software Foundation
+ * @version $Id: StringDecoder.java,v 1.9 2004/02/29 04:08:31 tobrien Exp $
+ */
+public interface StringDecoder extends Decoder {
+    
+    /**
+     * Decodes a String and returns a String.
+     * 
+     * @param pString a String to encode
+     * 
+     * @return the encoded String
+     * 
+     * @throws DecoderException thrown if there is
+     *  an error conidition during the Encoding process.
+     */
+    String decode(String pString) throws DecoderException;
+}  
+
diff --git a/src/org/apache/commons/codec/StringEncoder.java b/src/org/apache/commons/codec/StringEncoder.java
new file mode 100644
index 0000000..46f5404
--- /dev/null
+++ b/src/org/apache/commons/codec/StringEncoder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec;
+
+/**
+ * Encodes a String into a String. 
+ *
+ * @author Apache Software Foundation
+ * @version $Id: StringEncoder.java,v 1.9 2004/02/29 04:08:31 tobrien Exp $
+ */
+public interface StringEncoder extends Encoder {
+    
+    /**
+     * Encodes a String and returns a String.
+     * 
+     * @param pString a String to encode
+     * 
+     * @return the encoded String
+     * 
+     * @throws EncoderException thrown if there is
+     *  an error conidition during the Encoding process.
+     */
+    String encode(String pString) throws EncoderException;
+}  
+
diff --git a/src/org/apache/commons/codec/StringEncoderComparator.java b/src/org/apache/commons/codec/StringEncoderComparator.java
new file mode 100644
index 0000000..6d29af2
--- /dev/null
+++ b/src/org/apache/commons/codec/StringEncoderComparator.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec;
+
+import java.util.Comparator;
+
+/**
+ * Strings are comparable, and this comparator allows 
+ * you to configure it with an instance of a class
+ * which implements StringEncoder.  This comparator
+ * is used to sort Strings by an encoding scheme such
+ * as Soundex, Metaphone, etc.  This class can come in
+ * handy if one need to sort Strings by an encoded
+ * form of a name such as Soundex.
+ *
+ * @author Apache Software Foundation
+ * @version $Id: StringEncoderComparator.java,v 1.14 2004/06/21 23:24:17 ggregory Exp $
+ */
+public class StringEncoderComparator implements Comparator {
+
+    /**
+     * Internal encoder instance.
+     */
+    private StringEncoder stringEncoder;
+
+    /**
+     * Constructs a new instance.
+     */
+    public StringEncoderComparator() {
+        // no init.
+    }
+
+    /**
+     * Constructs a new instance with the given algorithm.
+     * @param stringEncoder the StringEncoder used for comparisons.
+     */
+    public StringEncoderComparator(StringEncoder stringEncoder) {
+        this.stringEncoder = stringEncoder;
+    }
+
+    /**
+     * Compares two strings based not on the strings 
+     * themselves, but on an encoding of the two 
+     * strings using the StringEncoder this Comparator
+     * was created with.
+     * 
+     * If an {@link EncoderException} is encountered, return <code>0</code>.
+     * 
+     * @param o1 the object to compare
+     * @param o2 the object to compare to
+     * @return the Comparable.compareTo() return code or 0 if an encoding error was caught.
+     * @see Comparable
+     */
+    public int compare(Object o1, Object o2) {
+
+        int compareCode = 0;
+
+        try {
+            Comparable s1 = (Comparable) ((Encoder) this.stringEncoder).encode(o1);
+            Comparable s2 = (Comparable) ((Encoder) this.stringEncoder).encode(o2);
+            compareCode = s1.compareTo(s2);
+        } 
+        catch (EncoderException ee) {
+            compareCode = 0;
+        }
+        return compareCode;
+    }
+
+}
diff --git a/src/org/apache/commons/codec/binary/Base64.java b/src/org/apache/commons/codec/binary/Base64.java
new file mode 100644
index 0000000..ea479e9
--- /dev/null
+++ b/src/org/apache/commons/codec/binary/Base64.java
@@ -0,0 +1,524 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec.binary;
+
+import org.apache.commons.codec.BinaryDecoder;
+import org.apache.commons.codec.BinaryEncoder;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+
+/**
+ * Provides Base64 encoding and decoding as defined by RFC 2045.
+ * 
+ * <p>This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite> 
+ * from RFC 2045 <cite>Multipurpose Internet Mail Extensions (MIME) Part One: 
+ * Format of Internet Message Bodies</cite> by Freed and Borenstein.</p> 
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
+ * @author Apache Software Foundation
+ * @since 1.0-dev
+ * @version $Id: Base64.java,v 1.20 2004/05/24 00:21:24 ggregory Exp $
+ */
+public class Base64 implements BinaryEncoder, BinaryDecoder {
+
+    /**
+     * Chunk size per RFC 2045 section 6.8.
+     * 
+     * <p>The {@value} character limit does not count the trailing CRLF, but counts 
+     * all other characters, including any equal signs.</p>
+     * 
+     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
+     */
+    static final int CHUNK_SIZE = 76;
+
+    /**
+     * Chunk separator per RFC 2045 section 2.1.
+     * 
+     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
+     */
+    static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes();
+
+    /**
+     * The base length.
+     */
+    static final int BASELENGTH = 255;
+
+    /**
+     * Lookup length.
+     */
+    static final int LOOKUPLENGTH = 64;
+
+    /**
+     * Used to calculate the number of bits in a byte.
+     */
+    static final int EIGHTBIT = 8;
+
+    /**
+     * Used when encoding something which has fewer than 24 bits.
+     */
+    static final int SIXTEENBIT = 16;
+
+    /**
+     * Used to determine how many bits data contains.
+     */
+    static final int TWENTYFOURBITGROUP = 24;
+
+    /**
+     * Used to get the number of Quadruples.
+     */
+    static final int FOURBYTE = 4;
+
+    /**
+     * Used to test the sign of a byte.
+     */
+    static final int SIGN = -128;
+    
+    /**
+     * Byte used to pad output.
+     */
+    static final byte PAD = (byte) '=';
+
+    // Create arrays to hold the base64 characters and a 
+    // lookup for base64 chars
+    private static byte[] base64Alphabet = new byte[BASELENGTH];
+    private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
+
+    // Populating the lookup and character arrays
+    static {
+        for (int i = 0; i < BASELENGTH; i++) {
+            base64Alphabet[i] = (byte) -1;
+        }
+        for (int i = 'Z'; i >= 'A'; i--) {
+            base64Alphabet[i] = (byte) (i - 'A');
+        }
+        for (int i = 'z'; i >= 'a'; i--) {
+            base64Alphabet[i] = (byte) (i - 'a' + 26);
+        }
+        for (int i = '9'; i >= '0'; i--) {
+            base64Alphabet[i] = (byte) (i - '0' + 52);
+        }
+
+        base64Alphabet['+'] = 62;
+        base64Alphabet['/'] = 63;
+
+        for (int i = 0; i <= 25; i++) {
+            lookUpBase64Alphabet[i] = (byte) ('A' + i);
+        }
+
+        for (int i = 26, j = 0; i <= 51; i++, j++) {
+            lookUpBase64Alphabet[i] = (byte) ('a' + j);
+        }
+
+        for (int i = 52, j = 0; i <= 61; i++, j++) {
+            lookUpBase64Alphabet[i] = (byte) ('0' + j);
+        }
+
+        lookUpBase64Alphabet[62] = (byte) '+';
+        lookUpBase64Alphabet[63] = (byte) '/';
+    }
+
+    private static boolean isBase64(byte octect) {
+        if (octect == PAD) {
+            return true;
+        } else if (base64Alphabet[octect] == -1) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * Tests a given byte array to see if it contains
+     * only valid characters within the Base64 alphabet.
+     *
+     * @param arrayOctect byte array to test
+     * @return true if all bytes are valid characters in the Base64
+     *         alphabet or if the byte array is empty; false, otherwise
+     */
+    public static boolean isArrayByteBase64(byte[] arrayOctect) {
+
+        arrayOctect = discardWhitespace(arrayOctect);
+
+        int length = arrayOctect.length;
+        if (length == 0) {
+            // shouldn't a 0 length array be valid base64 data?
+            // return false;
+            return true;
+        }
+        for (int i = 0; i < length; i++) {
+            if (!isBase64(arrayOctect[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Encodes binary data using the base64 algorithm but
+     * does not chunk the output.
+     *
+     * @param binaryData binary data to encode
+     * @return Base64 characters
+     */
+    public static byte[] encodeBase64(byte[] binaryData) {
+        return encodeBase64(binaryData, false);
+    }
+
+    /**
+     * Encodes binary data using the base64 algorithm and chunks
+     * the encoded output into 76 character blocks
+     *
+     * @param binaryData binary data to encode
+     * @return Base64 characters chunked in 76 character blocks
+     */
+    public static byte[] encodeBase64Chunked(byte[] binaryData) {
+        return encodeBase64(binaryData, true);
+    }
+
+
+    /**
+     * Decodes an Object using the base64 algorithm.  This method
+     * is provided in order to satisfy the requirements of the
+     * Decoder interface, and will throw a DecoderException if the
+     * supplied object is not of type byte[].
+     *
+     * @param pObject Object to decode
+     * @return An object (of type byte[]) containing the 
+     *         binary data which corresponds to the byte[] supplied.
+     * @throws DecoderException if the parameter supplied is not
+     *                          of type byte[]
+     */
+    public Object decode(Object pObject) throws DecoderException {
+        if (!(pObject instanceof byte[])) {
+            throw new DecoderException("Parameter supplied to Base64 decode is not a byte[]");
+        }
+        return decode((byte[]) pObject);
+    }
+
+    /**
+     * Decodes a byte[] containing containing
+     * characters in the Base64 alphabet.
+     *
+     * @param pArray A byte array containing Base64 character data
+     * @return a byte array containing binary data
+     */
+    public byte[] decode(byte[] pArray) {
+        return decodeBase64(pArray);
+    }
+
+    /**
+     * Encodes binary data using the base64 algorithm, optionally
+     * chunking the output into 76 character blocks.
+     *
+     * @param binaryData Array containing binary data to encode.
+     * @param isChunked if isChunked is true this encoder will chunk
+     *                  the base64 output into 76 character blocks
+     * @return Base64-encoded data.
+     */
+    public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) {
+        int lengthDataBits = binaryData.length * EIGHTBIT;
+        int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
+        int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
+        byte encodedData[] = null;
+        int encodedDataLength = 0;
+        int nbrChunks = 0;
+
+        if (fewerThan24bits != 0) {
+            //data not divisible by 24 bit
+            encodedDataLength = (numberTriplets + 1) * 4;
+        } else {
+            // 16 or 8 bit
+            encodedDataLength = numberTriplets * 4;
+        }
+
+        // If the output is to be "chunked" into 76 character sections, 
+        // for compliance with RFC 2045 MIME, then it is important to 
+        // allow for extra length to account for the separator(s)
+        if (isChunked) {
+
+            nbrChunks =
+                (CHUNK_SEPARATOR.length == 0 ? 0 : (int) Math.ceil((float) encodedDataLength / CHUNK_SIZE));
+            encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length;
+        }
+
+        encodedData = new byte[encodedDataLength];
+
+        byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
+
+        int encodedIndex = 0;
+        int dataIndex = 0;
+        int i = 0;
+        int nextSeparatorIndex = CHUNK_SIZE;
+        int chunksSoFar = 0;
+
+        //log.debug("number of triplets = " + numberTriplets);
+        for (i = 0; i < numberTriplets; i++) {
+            dataIndex = i * 3;
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex + 1];
+            b3 = binaryData[dataIndex + 2];
+
+            //log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
+
+            l = (byte) (b2 & 0x0f);
+            k = (byte) (b1 & 0x03);
+
+            byte val1 =
+                ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            byte val2 =
+                ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
+            byte val3 =
+                ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
+
+            encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
+            //log.debug( "val2 = " + val2 );
+            //log.debug( "k4   = " + (k<<4) );
+            //log.debug(  "vak  = " + (val2 | (k<<4)) );
+            encodedData[encodedIndex + 1] =
+                lookUpBase64Alphabet[val2 | (k << 4)];
+            encodedData[encodedIndex + 2] =
+                lookUpBase64Alphabet[(l << 2) | val3];
+            encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
+
+            encodedIndex += 4;
+
+            // If we are chunking, let's put a chunk separator down.
+            if (isChunked) {
+                // this assumes that CHUNK_SIZE % 4 == 0
+                if (encodedIndex == nextSeparatorIndex) {
+                    System.arraycopy(
+                        CHUNK_SEPARATOR,
+                        0,
+                        encodedData,
+                        encodedIndex,
+                        CHUNK_SEPARATOR.length);
+                    chunksSoFar++;
+                    nextSeparatorIndex =
+                        (CHUNK_SIZE * (chunksSoFar + 1)) + 
+                        (chunksSoFar * CHUNK_SEPARATOR.length);
+                    encodedIndex += CHUNK_SEPARATOR.length;
+                }
+            }
+        }
+
+        // form integral number of 6-bit groups
+        dataIndex = i * 3;
+
+        if (fewerThan24bits == EIGHTBIT) {
+            b1 = binaryData[dataIndex];
+            k = (byte) (b1 & 0x03);
+            //log.debug("b1=" + b1);
+            //log.debug("b1<<2 = " + (b1>>2) );
+            byte val1 =
+                ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
+            encodedData[encodedIndex + 2] = PAD;
+            encodedData[encodedIndex + 3] = PAD;
+        } else if (fewerThan24bits == SIXTEENBIT) {
+
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex + 1];
+            l = (byte) (b2 & 0x0f);
+            k = (byte) (b1 & 0x03);
+
+            byte val1 =
+                ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            byte val2 =
+                ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
+
+            encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex + 1] =
+                lookUpBase64Alphabet[val2 | (k << 4)];
+            encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
+            encodedData[encodedIndex + 3] = PAD;
+        }
+
+        if (isChunked) {
+            // we also add a separator to the end of the final chunk.
+            if (chunksSoFar < nbrChunks) {
+                System.arraycopy(
+                    CHUNK_SEPARATOR,
+                    0,
+                    encodedData,
+                    encodedDataLength - CHUNK_SEPARATOR.length,
+                    CHUNK_SEPARATOR.length);
+            }
+        }
+
+        return encodedData;
+    }
+
+    /**
+     * Decodes Base64 data into octects
+     *
+     * @param base64Data Byte array containing Base64 data
+     * @return Array containing decoded data.
+     */
+    public static byte[] decodeBase64(byte[] base64Data) {
+        // RFC 2045 requires that we discard ALL non-Base64 characters
+        base64Data = discardNonBase64(base64Data);
+
+        // handle the edge case, so we don't have to worry about it later
+        if (base64Data.length == 0) {
+            return new byte[0];
+        }
+
+        int numberQuadruple = base64Data.length / FOURBYTE;
+        byte decodedData[] = null;
+        byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
+
+        // Throw away anything not in base64Data
+
+        int encodedIndex = 0;
+        int dataIndex = 0;
+        {
+            // this sizes the output array properly - rlw
+            int lastData = base64Data.length;
+            // ignore the '=' padding
+            while (base64Data[lastData - 1] == PAD) {
+                if (--lastData == 0) {
+                    return new byte[0];
+                }
+            }
+            decodedData = new byte[lastData - numberQuadruple];
+        }
+        
+        for (int i = 0; i < numberQuadruple; i++) {
+            dataIndex = i * 4;
+            marker0 = base64Data[dataIndex + 2];
+            marker1 = base64Data[dataIndex + 3];
+            
+            b1 = base64Alphabet[base64Data[dataIndex]];
+            b2 = base64Alphabet[base64Data[dataIndex + 1]];
+            
+            if (marker0 != PAD && marker1 != PAD) {
+                //No PAD e.g 3cQl
+                b3 = base64Alphabet[marker0];
+                b4 = base64Alphabet[marker1];
+                
+                decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+                decodedData[encodedIndex + 1] =
+                    (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+                decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
+            } else if (marker0 == PAD) {
+                //Two PAD e.g. 3c[Pad][Pad]
+                decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+            } else if (marker1 == PAD) {
+                //One PAD e.g. 3cQ[Pad]
+                b3 = base64Alphabet[marker0];
+                
+                decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+                decodedData[encodedIndex + 1] =
+                    (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+            }
+            encodedIndex += 3;
+        }
+        return decodedData;
+    }
+    
+    /**
+     * Discards any whitespace from a base-64 encoded block.
+     *
+     * @param data The base-64 encoded data to discard the whitespace
+     * from.
+     * @return The data, less whitespace (see RFC 2045).
+     */
+    static byte[] discardWhitespace(byte[] data) {
+        byte groomedData[] = new byte[data.length];
+        int bytesCopied = 0;
+        
+        for (int i = 0; i < data.length; i++) {
+            switch (data[i]) {
+            case (byte) ' ' :
+            case (byte) '\n' :
+            case (byte) '\r' :
+            case (byte) '\t' :
+                    break;
+            default:
+                    groomedData[bytesCopied++] = data[i];
+            }
+        }
+
+        byte packedData[] = new byte[bytesCopied];
+
+        System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
+
+        return packedData;
+    }
+
+    /**
+     * Discards any characters outside of the base64 alphabet, per
+     * the requirements on page 25 of RFC 2045 - "Any characters
+     * outside of the base64 alphabet are to be ignored in base64
+     * encoded data."
+     *
+     * @param data The base-64 encoded data to groom
+     * @return The data, less non-base64 characters (see RFC 2045).
+     */
+    static byte[] discardNonBase64(byte[] data) {
+        byte groomedData[] = new byte[data.length];
+        int bytesCopied = 0;
+
+        for (int i = 0; i < data.length; i++) {
+            if (isBase64(data[i])) {
+                groomedData[bytesCopied++] = data[i];
+            }
+        }
+
+        byte packedData[] = new byte[bytesCopied];
+
+        System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
+
+        return packedData;
+    }
+
+
+    // Implementation of the Encoder Interface
+
+    /**
+     * Encodes an Object using the base64 algorithm.  This method
+     * is provided in order to satisfy the requirements of the
+     * Encoder interface, and will throw an EncoderException if the
+     * supplied object is not of type byte[].
+     *
+     * @param pObject Object to encode
+     * @return An object (of type byte[]) containing the 
+     *         base64 encoded data which corresponds to the byte[] supplied.
+     * @throws EncoderException if the parameter supplied is not
+     *                          of type byte[]
+     */
+    public Object encode(Object pObject) throws EncoderException {
+        if (!(pObject instanceof byte[])) {
+            throw new EncoderException(
+                "Parameter supplied to Base64 encode is not a byte[]");
+        }
+        return encode((byte[]) pObject);
+    }
+
+    /**
+     * Encodes a byte[] containing binary data, into a byte[] containing
+     * characters in the Base64 alphabet.
+     *
+     * @param pArray a byte array containing binary data
+     * @return A byte array containing only Base64 character data
+     */
+    public byte[] encode(byte[] pArray) {
+        return encodeBase64(pArray, false);
+    }
+
+}
diff --git a/src/org/apache/commons/codec/binary/BinaryCodec.java b/src/org/apache/commons/codec/binary/BinaryCodec.java
new file mode 100644
index 0000000..98c6409
--- /dev/null
+++ b/src/org/apache/commons/codec/binary/BinaryCodec.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec.binary;
+
+import org.apache.commons.codec.BinaryDecoder;
+import org.apache.commons.codec.BinaryEncoder;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+
+/**
+ * Translates between byte arrays and strings of "0"s and "1"s.
+ * 
+ * <b>TODO:</b> may want to add more bit vector functions like and/or/xor/nand.
+ * <B>TODO:</b> also might be good to generate boolean[]
+ * from byte[] et. cetera.
+ * 
+ * @author Apache Software Foundation
+ * @since 1.3
+ * @version $Id $
+ */
+public class BinaryCodec implements BinaryDecoder, BinaryEncoder {
+    /*
+     * tried to avoid using ArrayUtils to minimize dependencies while using these empty arrays - dep is just not worth
+     * it.
+     */
+    /** Empty char array. */
+    private static final char[] EMPTY_CHAR_ARRAY = new char[0];
+
+    /** Empty byte array. */
+    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+
+    /** Mask for bit 0 of a byte. */
+    private static final int BIT_0 = 1;
+
+    /** Mask for bit 1 of a byte. */
+    private static final int BIT_1 = 0x02;
+
+    /** Mask for bit 2 of a byte. */
+    private static final int BIT_2 = 0x04;
+
+    /** Mask for bit 3 of a byte. */
+    private static final int BIT_3 = 0x08;
+
+    /** Mask for bit 4 of a byte. */
+    private static final int BIT_4 = 0x10;
+
+    /** Mask for bit 5 of a byte. */
+    private static final int BIT_5 = 0x20;
+
+    /** Mask for bit 6 of a byte. */
+    private static final int BIT_6 = 0x40;
+
+    /** Mask for bit 7 of a byte. */
+    private static final int BIT_7 = 0x80;
+
+    private static final int[] BITS = {BIT_0, BIT_1, BIT_2, BIT_3, BIT_4, BIT_5, BIT_6, BIT_7};
+
+    /**
+     * Converts an array of raw binary data into an array of ascii 0 and 1 characters.
+     * 
+     * @param raw
+     *                  the raw binary data to convert
+     * @return 0 and 1 ascii character bytes one for each bit of the argument
+     * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
+     */
+    public byte[] encode(byte[] raw) {
+        return toAsciiBytes(raw);
+    }
+
+    /**
+     * Converts an array of raw binary data into an array of ascii 0 and 1 chars.
+     * 
+     * @param raw
+     *                  the raw binary data to convert
+     * @return 0 and 1 ascii character chars one for each bit of the argument
+     * @throws EncoderException
+     *                  if the argument is not a byte[]
+     * @see org.apache.commons.codec.Encoder#encode(java.lang.Object)
+     */
+    public Object encode(Object raw) throws EncoderException {
+        if (!(raw instanceof byte[])) {
+            throw new EncoderException("argument not a byte array");
+        }
+        return toAsciiChars((byte[]) raw);
+    }
+
+    /**
+     * Decodes a byte array where each byte represents an ascii '0' or '1'.
+     * 
+     * @param ascii
+     *                  each byte represents an ascii '0' or '1'
+     * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
+     * @throws DecoderException
+     *                  if argument is not a byte[], char[] or String
+     * @see org.apache.commons.codec.Decoder#decode(java.lang.Object)
+     */
+    public Object decode(Object ascii) throws DecoderException {
+        if (ascii == null) {
+            return EMPTY_BYTE_ARRAY;
+        }
+        if (ascii instanceof byte[]) {
+            return fromAscii((byte[]) ascii);
+        }
+        if (ascii instanceof char[]) {
+            return fromAscii((char[]) ascii);
+        }
+        if (ascii instanceof String) {
+            return fromAscii(((String) ascii).toCharArray());
+        }
+        throw new DecoderException("argument not a byte array");
+    }
+
+    /**
+     * Decodes a byte array where each byte represents an ascii '0' or '1'.
+     * 
+     * @param ascii
+     *                  each byte represents an ascii '0' or '1'
+     * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
+     * @see org.apache.commons.codec.Decoder#decode(Object)
+     */
+    public byte[] decode(byte[] ascii) {
+        return fromAscii(ascii);
+    }
+
+    /**
+     * Decodes a String where each char of the String represents an ascii '0' or '1'.
+     * 
+     * @param ascii
+     *                  String of '0' and '1' characters
+     * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
+     * @see org.apache.commons.codec.Decoder#decode(Object)
+     */
+    public byte[] toByteArray(String ascii) {
+        if (ascii == null) {
+            return EMPTY_BYTE_ARRAY;
+        }
+        return fromAscii(ascii.toCharArray());
+    }
+
+    // ------------------------------------------------------------------------
+    //
+    // static codec operations
+    //
+    // ------------------------------------------------------------------------
+    /**
+     * Decodes a byte array where each char represents an ascii '0' or '1'.
+     * 
+     * @param ascii
+     *                  each char represents an ascii '0' or '1'
+     * @return the raw encoded binary where each bit corresponds to a char in the char array argument
+     */
+    public static byte[] fromAscii(char[] ascii) {
+        if (ascii == null || ascii.length == 0) {
+            return EMPTY_BYTE_ARRAY;
+        }
+        // get length/8 times bytes with 3 bit shifts to the right of the length
+        byte[] l_raw = new byte[ascii.length >> 3];
+        /*
+         * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
+         * loop.
+         */
+        for (int ii = 0, jj = ascii.length - 1; ii < l_raw.length; ii++, jj -= 8) {
+            for (int bits = 0; bits < BITS.length; ++bits) {
+                if (ascii[jj - bits] == '1') {
+                    l_raw[ii] |= BITS[bits];
+                }
+            }
+        }
+        return l_raw;
+    }
+
+    /**
+     * Decodes a byte array where each byte represents an ascii '0' or '1'.
+     * 
+     * @param ascii
+     *                  each byte represents an ascii '0' or '1'
+     * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
+     */
+    public static byte[] fromAscii(byte[] ascii) {
+        if (ascii == null || ascii.length == 0) {
+            return EMPTY_BYTE_ARRAY;
+        }
+        // get length/8 times bytes with 3 bit shifts to the right of the length
+        byte[] l_raw = new byte[ascii.length >> 3];
+        /*
+         * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
+         * loop.
+         */
+        for (int ii = 0, jj = ascii.length - 1; ii < l_raw.length; ii++, jj -= 8) {
+            for (int bits = 0; bits < BITS.length; ++bits) {
+                if (ascii[jj - bits] == '1') {
+                    l_raw[ii] |= BITS[bits];
+                }
+            }
+        }
+        return l_raw;
+    }
+
+    /**
+     * Converts an array of raw binary data into an array of ascii 0 and 1 character bytes - each byte is a truncated
+     * char.
+     * 
+     * @param raw
+     *                  the raw binary data to convert
+     * @return an array of 0 and 1 character bytes for each bit of the argument
+     * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
+     */
+    public static byte[] toAsciiBytes(byte[] raw) {
+        if (raw == null || raw.length == 0) {
+            return EMPTY_BYTE_ARRAY;
+        }
+        // get 8 times the bytes with 3 bit shifts to the left of the length
+        byte[] l_ascii = new byte[raw.length << 3];
+        /*
+         * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
+         * loop.
+         */
+        for (int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8) {
+            for (int bits = 0; bits < BITS.length; ++bits) {
+                if ((raw[ii] & BITS[bits]) == 0) {
+                    l_ascii[jj - bits] = '0';
+                } else {
+                    l_ascii[jj - bits] = '1';
+                }
+            }
+        }
+        return l_ascii;
+    }
+
+    /**
+     * Converts an array of raw binary data into an array of ascii 0 and 1 characters.
+     * 
+     * @param raw
+     *                  the raw binary data to convert
+     * @return an array of 0 and 1 characters for each bit of the argument
+     * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
+     */
+    public static char[] toAsciiChars(byte[] raw) {
+        if (raw == null || raw.length == 0) {
+            return EMPTY_CHAR_ARRAY;
+        }
+        // get 8 times the bytes with 3 bit shifts to the left of the length
+        char[] l_ascii = new char[raw.length << 3];
+        /*
+         * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
+         * loop.
+         */
+        for (int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8) {
+            for (int bits = 0; bits < BITS.length; ++bits) {
+                if ((raw[ii] & BITS[bits]) == 0) {
+                    l_ascii[jj - bits] = '0';
+                } else {
+                    l_ascii[jj - bits] = '1';
+                }
+            }
+        }
+        return l_ascii;
+    }
+
+    /**
+     * Converts an array of raw binary data into a String of ascii 0 and 1 characters.
+     * 
+     * @param raw
+     *                  the raw binary data to convert
+     * @return a String of 0 and 1 characters representing the binary data
+     * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
+     */
+    public static String toAsciiString(byte[] raw) {
+        return new String(toAsciiChars(raw));
+    }
+}
diff --git a/src/org/apache/commons/codec/binary/Hex.java b/src/org/apache/commons/codec/binary/Hex.java
new file mode 100644
index 0000000..78f5510
--- /dev/null
+++ b/src/org/apache/commons/codec/binary/Hex.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec.binary;
+
+import org.apache.commons.codec.BinaryDecoder;
+import org.apache.commons.codec.BinaryEncoder;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+
+/**
+ * Hex encoder and decoder.
+ * 
+ * @since 1.1
+ * @author Apache Software Foundation
+ * @version $Id: Hex.java,v 1.13 2004/04/18 18:22:33 ggregory Exp $
+ */
+public class Hex implements BinaryEncoder, BinaryDecoder {
+
+    /** 
+     * Used building output as Hex 
+     */
+    private static final char[] DIGITS = {
+        '0', '1', '2', '3', '4', '5', '6', '7',
+           '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+    };
+
+    /**
+     * Converts an array of characters representing hexidecimal values into an
+     * array of bytes of those same values. The returned array will be half the
+     * length of the passed array, as it takes two characters to represent any
+     * given byte. An exception is thrown if the passed char array has an odd
+     * number of elements.
+     * 
+     * @param data An array of characters containing hexidecimal digits
+     * @return A byte array containing binary data decoded from
+     *         the supplied char array.
+     * @throws DecoderException Thrown if an odd number or illegal of characters 
+     *         is supplied
+     */
+    public static byte[] decodeHex(char[] data) throws DecoderException {
+
+        int len = data.length;
+
+        if ((len & 0x01) != 0) {
+            throw new DecoderException("Odd number of characters.");
+        }
+
+        byte[] out = new byte[len >> 1];
+
+        // two characters form the hex value.
+        for (int i = 0, j = 0; j < len; i++) {
+            int f = toDigit(data[j], j) << 4;
+            j++;
+            f = f | toDigit(data[j], j);
+            j++;
+            out[i] = (byte) (f & 0xFF);
+        }
+
+        return out;
+    }
+
+    /**
+     * Converts a hexadecimal character to an integer.
+     *  
+     * @param ch A character to convert to an integer digit
+     * @param index The index of the character in the source
+     * @return An integer
+     * @throws DecoderException Thrown if ch is an illegal hex character
+     */
+    protected static int toDigit(char ch, int index) throws DecoderException {
+        int digit = Character.digit(ch, 16);
+        if (digit == -1) {
+            throw new DecoderException("Illegal hexadecimal charcter " + ch + " at index " + index);
+        }
+        return digit;
+    }
+
+    /**
+     * Converts an array of bytes into an array of characters representing the hexidecimal values of each byte in order.
+     * The returned array will be double the length of the passed array, as it takes two characters to represent any
+     * given byte.
+     * 
+     * @param data
+     *                  a byte[] to convert to Hex characters
+     * @return A char[] containing hexidecimal characters
+     */
+    public static char[] encodeHex(byte[] data) {
+
+        int l = data.length;
+
+           char[] out = new char[l << 1];
+
+           // two characters form the hex value.
+           for (int i = 0, j = 0; i < l; i++) {
+               out[j++] = DIGITS[(0xF0 & data[i]) >>> 4 ];
+               out[j++] = DIGITS[ 0x0F & data[i] ];
+           }
+
+           return out;
+    }
+    
+    /**
+     * Converts an array of character bytes representing hexidecimal values into an
+     * array of bytes of those same values. The returned array will be half the
+     * length of the passed array, as it takes two characters to represent any
+     * given byte. An exception is thrown if the passed char array has an odd
+     * number of elements.
+     * 
+     * @param array An array of character bytes containing hexidecimal digits
+     * @return A byte array containing binary data decoded from
+     *         the supplied byte array (representing characters).
+     * @throws DecoderException Thrown if an odd number of characters is supplied
+     *                   to this function
+     * @see #decodeHex(char[])
+     */
+    public byte[] decode(byte[] array) throws DecoderException {
+        return decodeHex(new String(array).toCharArray());
+    }
+    
+    /**
+     * Converts a String or an array of character bytes representing hexidecimal values into an
+     * array of bytes of those same values. The returned array will be half the
+     * length of the passed String or array, as it takes two characters to represent any
+     * given byte. An exception is thrown if the passed char array has an odd
+     * number of elements.
+     * 
+     * @param object A String or, an array of character bytes containing hexidecimal digits
+     * @return A byte array containing binary data decoded from
+     *         the supplied byte array (representing characters).
+     * @throws DecoderException Thrown if an odd number of characters is supplied
+     *                   to this function or the object is not a String or char[]
+     * @see #decodeHex(char[])
+     */
+    public Object decode(Object object) throws DecoderException {
+        try {
+            char[] charArray = object instanceof String ? ((String) object).toCharArray() : (char[]) object;
+            return decodeHex(charArray);
+        } catch (ClassCastException e) {
+            throw new DecoderException(e.getMessage());
+        }
+    }
+    
+    /**
+     * Converts an array of bytes into an array of bytes for the characters representing the
+     * hexidecimal values of each byte in order. The returned array will be
+     * double the length of the passed array, as it takes two characters to
+     * represent any given byte.
+     *
+     * @param array a byte[] to convert to Hex characters
+     * @return A byte[] containing the bytes of the hexidecimal characters
+     * @see #encodeHex(byte[])
+     */
+    public byte[] encode(byte[] array) {
+        return new String(encodeHex(array)).getBytes();
+    }
+
+    /**
+     * Converts a String or an array of bytes into an array of characters representing the
+     * hexidecimal values of each byte in order. The returned array will be
+     * double the length of the passed String or array, as it takes two characters to
+     * represent any given byte.
+     *
+     * @param object a String, or byte[] to convert to Hex characters
+     * @return A char[] containing hexidecimal characters
+     * @throws EncoderException Thrown if the given object is not a String or byte[]
+     * @see #encodeHex(byte[])
+     */
+    public Object encode(Object object) throws EncoderException {    
+        try {
+            byte[] byteArray = object instanceof String ? ((String) object).getBytes() : (byte[]) object;
+            return encodeHex(byteArray);
+        } catch (ClassCastException e) {
+            throw new EncoderException(e.getMessage());
+        }
+    }
+
+}
+
diff --git a/src/org/apache/commons/codec/binary/package.html b/src/org/apache/commons/codec/binary/package.html
new file mode 100644
index 0000000..844d918
--- /dev/null
+++ b/src/org/apache/commons/codec/binary/package.html
@@ -0,0 +1,20 @@
+<!--
+Copyright 2003-2004 The Apache Software Foundation.
+ 
+Licensed 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.
+-->
+<html>
+ <body>
+  Base64, Binary, and Hexadecimal String encoding and decoding.
+ </body>
+</html>
diff --git a/src/org/apache/commons/codec/language/DoubleMetaphone.java b/src/org/apache/commons/codec/language/DoubleMetaphone.java
new file mode 100644
index 0000000..1cad991
--- /dev/null
+++ b/src/org/apache/commons/codec/language/DoubleMetaphone.java
@@ -0,0 +1,1103 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec.language;
+
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.StringEncoder;
+
+/**
+ * Encodes a string into a double metaphone value.
+ * This Implementation is based on the algorithm by <CITE>Lawrence Philips</CITE>.
+ * <ul>
+ * <li>Original Article: <a 
+ * href="http://www.cuj.com/documents/s=8038/cuj0006philips/">
+ * http://www.cuj.com/documents/s=8038/cuj0006philips/</a></li>
+ * <li>Original Source Code: <a href="ftp://ftp.cuj.com/pub/2000/1806/philips.zip">
+ * ftp://ftp.cuj.com/pub/2000/1806/philips.zip</a></li>
+ * </ul>
+ * 
+ * @author Apache Software Foundation
+ * @version $Id: DoubleMetaphone.java,v 1.24 2004/06/05 18:32:04 ggregory Exp $
+ */
+public class DoubleMetaphone implements StringEncoder {
+
+    /**
+     * "Vowels" to test for
+     */
+    private static final String VOWELS = "AEIOUY";
+
+    /**
+     * Prefixes when present which are not pronounced
+     */
+    private static final String[] SILENT_START = 
+    { "GN", "KN", "PN", "WR", "PS" };
+    private static final String[] L_R_N_M_B_H_F_V_W_SPACE = 
+    { "L", "R", "N", "M", "B", "H", "F", "V", "W", " " };
+    private static final String[] ES_EP_EB_EL_EY_IB_IL_IN_IE_EI_ER = 
+    { "ES", "EP", "EB", "EL", "EY", "IB", "IL", "IN", "IE", "EI", "ER" };
+    private static final String[] L_T_K_S_N_M_B_Z = 
+    { "L", "T", "K", "S", "N", "M", "B", "Z" };
+
+    /**
+     * Maximum length of an encoding, default is 4
+     */
+    protected int maxCodeLen = 4;
+
+    /**
+     * Creates an instance of this DoubleMetaphone encoder
+     */
+    public DoubleMetaphone() {
+        super();
+    }
+    
+    /**
+     * Encode a value with Double Metaphone
+     *
+     * @param value String to encode
+     * @return an encoded string
+     */
+    public String doubleMetaphone(String value) {
+        return doubleMetaphone(value, false);
+    }
+    
+    /**
+     * Encode a value with Double Metaphone, optionally using the alternate
+     * encoding.
+     *
+     * @param value String to encode
+     * @param alternate use alternate encode
+     * @return an encoded string
+     */
+    public String doubleMetaphone(String value, boolean alternate) {
+        value = cleanInput(value);
+        if (value == null) {
+            return null;
+        }
+        
+        boolean slavoGermanic = isSlavoGermanic(value);
+        int index = isSilentStart(value) ? 1 : 0;
+        
+        DoubleMetaphoneResult result = new DoubleMetaphoneResult(this.getMaxCodeLen());
+        
+        while (!result.isComplete() && index <= value.length() - 1) {
+            switch (value.charAt(index)) {
+            case 'A':
+            case 'E':
+            case 'I':
+            case 'O':
+            case 'U':
+            case 'Y':
+                index = handleAEIOUY(value, result, index);
+                break;
+            case 'B':
+                result.append('P');
+                index = charAt(value, index + 1) == 'B' ? index + 2 : index + 1;
+                break;
+            case '\u00C7':
+                // A C with a Cedilla
+                result.append('S');
+                index++;
+                break; 
+            case 'C':
+                index = handleC(value, result, index);
+                break;
+            case 'D':
+                index = handleD(value, result, index);
+                break;
+            case 'F':
+                result.append('F');
+                index = charAt(value, index + 1) == 'F' ? index + 2 : index + 1;
+                break;
+            case 'G':
+                index = handleG(value, result, index, slavoGermanic);
+                break;
+            case 'H':
+                index = handleH(value, result, index);
+                break;
+            case 'J':
+                index = handleJ(value, result, index, slavoGermanic);
+                break;
+            case 'K':
+                result.append('K');
+                index = charAt(value, index + 1) == 'K' ? index + 2 : index + 1;
+                break;
+            case 'L':
+                index = handleL(value, result, index);
+                break;
+            case 'M':
+                result.append('M');
+                index = conditionM0(value, index) ? index + 2 : index + 1;
+                break;
+            case 'N':
+                result.append('N');
+                index = charAt(value, index + 1) == 'N' ? index + 2 : index + 1;
+                break;
+            case '\u00D1':
+                // N with a tilde (spanish ene)
+                result.append('N');
+                index++;
+                break;
+            case 'P':
+                index = handleP(value, result, index);
+                break;
+            case 'Q':
+                result.append('K');
+                index = charAt(value, index + 1) == 'Q' ? index + 2 : index + 1;
+                break;
+            case 'R':
+                index = handleR(value, result, index, slavoGermanic);
+                break;
+            case 'S':
+                index = handleS(value, result, index, slavoGermanic);
+                break;
+            case 'T':
+                index = handleT(value, result, index);
+                break;
+            case 'V':
+                result.append('F');
+                index = charAt(value, index + 1) == 'V' ? index + 2 : index + 1;
+                break;
+            case 'W':
+                index = handleW(value, result, index);
+                break;
+            case 'X':
+                index = handleX(value, result, index);
+                break;
+            case 'Z':
+                index = handleZ(value, result, index, slavoGermanic);
+                break;
+            default:
+                index++;
+                break;
+            }
+        }
+
+        return alternate ? result.getAlternate() : result.getPrimary();
+    }
+    
+    /**
+     * Encode the value using DoubleMetaphone.  It will only work if 
+     * <code>obj</code> is a <code>String</code> (like <code>Metaphone</code>).
+     *
+     * @param obj Object to encode (should be of type String)
+     * @return An encoded Object (will be of type String)
+     * @throws EncoderException encode parameter is not of type String
+     */
+    public Object encode(Object obj) throws EncoderException {
+        if (!(obj instanceof String)) {
+            throw new EncoderException("DoubleMetaphone encode parameter is not of type String"); 
+        } 
+        return doubleMetaphone((String) obj);
+    }
+
+    /**
+     * Encode the value using DoubleMetaphone.
+     *
+     * @param value String to encode
+     * @return An encoded String
+     */
+    public String encode(String value) {
+        return doubleMetaphone(value);   
+    }
+
+    /**
+     * Check if the Double Metaphone values of two <code>String</code> values
+     * are equal.
+     * 
+     * @param value1 The left-hand side of the encoded {@link String#equals(Object)}.
+     * @param value2 The right-hand side of the encoded {@link String#equals(Object)}.
+     * @return <code>true</code> if the encoded <code>String</code>s are equal;
+     *          <code>false</code> otherwise.
+     * @see #isDoubleMetaphoneEqual(String,String,boolean)
+     */
+    public boolean isDoubleMetaphoneEqual(String value1, String value2) {
+        return isDoubleMetaphoneEqual(value1, value2, false);
+    }
+    
+    /**
+     * Check if the Double Metaphone values of two <code>String</code> values
+     * are equal, optionally using the alternate value.
+     * 
+     * @param value1 The left-hand side of the encoded {@link String#equals(Object)}.
+     * @param value2 The right-hand side of the encoded {@link String#equals(Object)}.
+     * @param alternate use the alternate value if <code>true</code>.
+     * @return <code>true</code> if the encoded <code>String</code>s are equal;
+     *          <code>false</code> otherwise.
+     */
+    public boolean isDoubleMetaphoneEqual(String value1, 
+                                          String value2, 
+                                          boolean alternate) {
+        return doubleMetaphone(value1, alternate).equals(doubleMetaphone
+                                                         (value2, alternate));
+    }
+    
+    /**
+     * Returns the maxCodeLen.
+     * @return int
+     */
+    public int getMaxCodeLen() {
+        return this.maxCodeLen;
+    }
+
+    /**
+     * Sets the maxCodeLen.
+     * @param maxCodeLen The maxCodeLen to set
+     */
+    public void setMaxCodeLen(int maxCodeLen) {
+        this.maxCodeLen = maxCodeLen;
+    }
+
+    //-- BEGIN HANDLERS --//
+
+    /**
+     * Handles 'A', 'E', 'I', 'O', 'U', and 'Y' cases
+     */
+    private int handleAEIOUY(String value, DoubleMetaphoneResult result, int 
+                             index) {
+        if (index == 0) {
+            result.append('A');
+        }
+        return index + 1;
+    }
+    
+    /**
+     * Handles 'C' cases
+     */
+    private int handleC(String value, 
+                        DoubleMetaphoneResult result, 
+                        int index) {
+        if (conditionC0(value, index)) {  // very confusing, moved out
+            result.append('K');
+            index += 2;
+        } else if (index == 0 && contains(value, index, 6, "CAESAR")) {
+            result.append('S');
+            index += 2;
+        } else if (contains(value, index, 2, "CH")) {
+            index = handleCH(value, result, index);
+        } else if (contains(value, index, 2, "CZ") && 
+                   !contains(value, index - 2, 4, "WICZ")) {
+            //-- "Czerny" --//
+            result.append('S', 'X');
+            index += 2;
+        } else if (contains(value, index + 1, 3, "CIA")) {
+            //-- "focaccia" --//
+            result.append('X');
+            index += 3;
+        } else if (contains(value, index, 2, "CC") && 
+                   !(index == 1 && charAt(value, 0) == 'M')) {
+            //-- double "cc" but not "McClelland" --//
+            return handleCC(value, result, index);
+        } else if (contains(value, index, 2, "CK", "CG", "CQ")) {
+            result.append('K');
+            index += 2;
+        } else if (contains(value, index, 2, "CI", "CE", "CY")) {
+            //-- Italian vs. English --//
+            if (contains(value, index, 3, "CIO", "CIE", "CIA")) {
+                result.append('S', 'X');
+            } else {
+                result.append('S');
+            }
+            index += 2;
+        } else {
+            result.append('K');
+            if (contains(value, index + 1, 2, " C", " Q", " G")) { 
+                //-- Mac Caffrey, Mac Gregor --//
+                index += 3;
+            } else if (contains(value, index + 1, 1, "C", "K", "Q") && 
+                       !contains(value, index + 1, 2, "CE", "CI")) {
+                index += 2;
+            } else {
+                index++;
+            }
+        }
+        
+        return index;
+    }
+
+    /**
+     * Handles 'CC' cases
+     */
+    private int handleCC(String value, 
+                         DoubleMetaphoneResult result, 
+                         int index) {
+        if (contains(value, index + 2, 1, "I", "E", "H") && 
+            !contains(value, index + 2, 2, "HU")) {
+            //-- "bellocchio" but not "bacchus" --//
+            if ((index == 1 && charAt(value, index - 1) == 'A') || 
+                contains(value, index - 1, 5, "UCCEE", "UCCES")) {
+                //-- "accident", "accede", "succeed" --//
+                result.append("KS");
+            } else {
+                //-- "bacci", "bertucci", other Italian --//
+                result.append('X');
+            }
+            index += 3;
+        } else {    // Pierce's rule
+            result.append('K');
+            index += 2;
+        }
+        
+        return index;
+    }
+    
+    /**
+     * Handles 'CH' cases
+     */
+    private int handleCH(String value, 
+                         DoubleMetaphoneResult result, 
+                         int index) {
+        if (index > 0 && contains(value, index, 4, "CHAE")) {   // Michael
+            result.append('K', 'X');
+            return index + 2;
+        } else if (conditionCH0(value, index)) {
+            //-- Greek roots ("chemistry", "chorus", etc.) --//
+            result.append('K');
+            return index + 2;
+        } else if (conditionCH1(value, index)) {
+            //-- Germanic, Greek, or otherwise 'ch' for 'kh' sound --//
+            result.append('K');
+            return index + 2;
+        } else {
+            if (index > 0) {
+                if (contains(value, 0, 2, "MC")) {
+                    result.append('K');
+                } else {
+                    result.append('X', 'K');
+                }
+            } else {
+                result.append('X');
+            }
+            return index + 2;
+        }
+    }
+
+    /**
+     * Handles 'D' cases
+     */
+    private int handleD(String value, 
+                        DoubleMetaphoneResult result, 
+                        int index) {
+        if (contains(value, index, 2, "DG")) {
+            //-- "Edge" --//
+            if (contains(value, index + 2, 1, "I", "E", "Y")) {
+                result.append('J');
+                index += 3;
+                //-- "Edgar" --//
+            } else {
+                result.append("TK");
+                index += 2;
+            }
+        } else if (contains(value, index, 2, "DT", "DD")) {
+            result.append('T');
+            index += 2;
+        } else {
+            result.append('T');
+            index++;
+        }
+        return index;
+    }
+
+    /**
+     * Handles 'G' cases
+     */
+    private int handleG(String value, 
+                        DoubleMetaphoneResult result, 
+                        int index, 
+                        boolean slavoGermanic) {
+        if (charAt(value, index + 1) == 'H') {
+            index = handleGH(value, result, index);
+        } else if (charAt(value, index + 1) == 'N') {
+            if (index == 1 && isVowel(charAt(value, 0)) && !slavoGermanic) {
+                result.append("KN", "N");
+            } else if (!contains(value, index + 2, 2, "EY") && 
+                       charAt(value, index + 1) != 'Y' && !slavoGermanic) {
+                result.append("N", "KN");
+            } else {
+                result.append("KN");
+            }
+            index = index + 2;
+        } else if (contains(value, index + 1, 2, "LI") && !slavoGermanic) {
+            result.append("KL", "L");
+            index += 2;
+        } else if (index == 0 && (charAt(value, index + 1) == 'Y' || contains(value, index + 1, 2, ES_EP_EB_EL_EY_IB_IL_IN_IE_EI_ER))) {
+            //-- -ges-, -gep-, -gel-, -gie- at beginning --//
+            result.append('K', 'J');
+            index += 2;
+        } else if ((contains(value, index + 1, 2, "ER") || 
+                    charAt(value, index + 1) == 'Y') &&
+                   !contains(value, 0, 6, "DANGER", "RANGER", "MANGER") &&
+                   !contains(value, index - 1, 1, "E", "I") && 
+                   !contains(value, index - 1, 3, "RGY", "OGY")) {
+            //-- -ger-, -gy- --//
+            result.append('K', 'J');
+            index += 2;
+        } else if (contains(value, index + 1, 1, "E", "I", "Y") || 
+                   contains(value, index - 1, 4, "AGGI", "OGGI")) {
+            //-- Italian "biaggi" --//
+            if ((contains(value, 0 ,4, "VAN ", "VON ") || contains(value, 0, 3, "SCH")) || contains(value, index + 1, 2, "ET")) {
+                //-- obvious germanic --//
+                result.append('K');
+            } else if (contains(value, index + 1, 4, "IER")) {
+                result.append('J');
+            } else {
+                result.append('J', 'K');
+            }
+            index += 2;
+        } else if (charAt(value, index + 1) == 'G') {
+            index += 2;
+            result.append('K');
+        } else {
+            index++;
+            result.append('K');
+        }
+        return index;
+    }
+    
+    /**
+     * Handles 'GH' cases
+     */
+    private int handleGH(String value, 
+                         DoubleMetaphoneResult result, 
+                         int index) {
+        if (index > 0 && !isVowel(charAt(value, index - 1))) {
+            result.append('K');
+            index += 2;
+        } else if (index == 0) {
+            if (charAt(value, index + 2) == 'I') {
+                result.append('J');
+            } else {
+                result.append('K');
+            }
+            index += 2;
+        } else if ((index > 1 && contains(value, index - 2, 1, "B", "H", "D")) ||
+                   (index > 2 && contains(value, index - 3, 1, "B", "H", "D")) ||
+                   (index > 3 && contains(value, index - 4, 1, "B", "H"))) {
+            //-- Parker's rule (with some further refinements) - "hugh"
+            index += 2;
+        } else {
+            if (index > 2 && charAt(value, index - 1) == 'U' && 
+                contains(value, index - 3, 1, "C", "G", "L", "R", "T")) {
+                //-- "laugh", "McLaughlin", "cough", "gough", "rough", "tough"
+                result.append('F');
+            } else if (index > 0 && charAt(value, index - 1) != 'I') {
+                result.append('K');
+            }
+            index += 2;
+        }
+        return index;
+    }
+
+    /**
+     * Handles 'H' cases
+     */
+    private int handleH(String value, 
+                        DoubleMetaphoneResult result, 
+                        int index) {
+        //-- only keep if first & before vowel or between 2 vowels --//
+        if ((index == 0 || isVowel(charAt(value, index - 1))) && 
+            isVowel(charAt(value, index + 1))) {
+            result.append('H');
+            index += 2;
+            //-- also takes car of "HH" --//
+        } else {
+            index++;
+        }
+        return index;
+    }
+    
+    /**
+     * Handles 'J' cases
+     */
+    private int handleJ(String value, DoubleMetaphoneResult result, int index, 
+                        boolean slavoGermanic) {
+        if (contains(value, index, 4, "JOSE") || contains(value, 0, 4, "SAN ")) {
+                //-- obvious Spanish, "Jose", "San Jacinto" --//
+                if ((index == 0 && (charAt(value, index + 4) == ' ') || 
+                     value.length() == 4) || contains(value, 0, 4, "SAN ")) {
+                    result.append('H');
+                } else {
+                    result.append('J', 'H');
+                }
+                index++;
+            } else {
+                if (index == 0 && !contains(value, index, 4, "JOSE")) {
+                    result.append('J', 'A');
+                } else if (isVowel(charAt(value, index - 1)) && !slavoGermanic && 
+                              (charAt(value, index + 1) == 'A' || charAt(value, index + 1) == 'O')) {
+                    result.append('J', 'H');
+                } else if (index == value.length() - 1) {
+                    result.append('J', ' ');
+                } else if (!contains(value, index + 1, 1, L_T_K_S_N_M_B_Z) && !contains(value, index - 1, 1, "S", "K", "L")) {
+                    result.append('J');
+                }
+
+                if (charAt(value, index + 1) == 'J') {
+                    index += 2;
+                } else {
+                    index++;
+                }
+            }
+        return index;
+    }
+    
+    /**
+     * Handles 'L' cases
+     */
+    private int handleL(String value, 
+                        DoubleMetaphoneResult result, 
+                        int index) {
+        result.append('L');
+        if (charAt(value, index + 1) == 'L') {
+            if (conditionL0(value, index)) {
+                result.appendAlternate(' ');
+            }
+            index += 2;
+        } else {
+            index++;
+        }
+        return index;
+    }
+
+    /**
+     * Handles 'P' cases
+     */
+    private int handleP(String value, 
+                        DoubleMetaphoneResult result, 
+                        int index) {
+        if (charAt(value, index + 1) == 'H') {
+            result.append('F');
+            index += 2;
+        } else {
+            result.append('P');
+            index = contains(value, index + 1, 1, "P", "B") ? index + 2 : index + 1;
+        }
+        return index;
+    }
+
+    /**
+     * Handles 'R' cases
+     */
+    private int handleR(String value, 
+                        DoubleMetaphoneResult result, 
+                        int index, 
+                        boolean slavoGermanic) {
+        if (index == value.length() - 1 && !slavoGermanic && 
+            contains(value, index - 2, 2, "IE") && 
+            !contains(value, index - 4, 2, "ME", "MA")) {
+            result.appendAlternate('R');
+        } else {
+            result.append('R');
+        }
+        return charAt(value, index + 1) == 'R' ? index + 2 : index + 1;
+    }
+
+    /**
+     * Handles 'S' cases
+     */
+    private int handleS(String value, 
+                        DoubleMetaphoneResult result, 
+                        int index, 
+                        boolean slavoGermanic) {
+        if (contains(value, index - 1, 3, "ISL", "YSL")) {
+            //-- special cases "island", "isle", "carlisle", "carlysle" --//
+            index++;
+        } else if (index == 0 && contains(value, index, 5, "SUGAR")) {
+            //-- special case "sugar-" --//
+            result.append('X', 'S');
+            index++;
+        } else if (contains(value, index, 2, "SH")) {
+            if (contains(value, index + 1, 4, 
+                         "HEIM", "HOEK", "HOLM", "HOLZ")) {
+                //-- germanic --//
+                result.append('S');
+            } else {
+                result.append('X');
+            }
+            index += 2;
+        } else if (contains(value, index, 3, "SIO", "SIA") || contains(value, index, 4, "SIAN")) {
+            //-- Italian and Armenian --//
+            if (slavoGermanic) {
+                result.append('S');
+            } else {
+                result.append('S', 'X');
+            }
+            index += 3;
+        } else if ((index == 0 && contains(value, index + 1, 1, "M", "N", "L", "W")) || contains(value, index + 1, 1, "Z")) {
+            //-- german & anglicisations, e.g. "smith" match "schmidt" //
+            // "snider" match "schneider" --//
+            //-- also, -sz- in slavic language altho in hungarian it //
+            //   is pronounced "s" --//
+            result.append('S', 'X');
+            index = contains(value, index + 1, 1, "Z") ? index + 2 : index + 1;
+        } else if (contains(value, index, 2, "SC")) {
+            index = handleSC(value, result, index);
+        } else {
+            if (index == value.length() - 1 && contains(value, index - 2, 
+                                                        2, "AI", "OI")){
+                //-- french e.g. "resnais", "artois" --//
+                result.appendAlternate('S');
+            } else {
+                result.append('S');
+            }
+            index = contains(value, index + 1, 1, "S", "Z") ? index + 2 : index + 1;
+        }
+        return index;
+    }
+
+    /**
+     * Handles 'SC' cases
+     */
+    private int handleSC(String value, 
+                         DoubleMetaphoneResult result, 
+                         int index) {
+        if (charAt(value, index + 2) == 'H') {
+            //-- Schlesinger's rule --//
+            if (contains(value, index + 3, 
+                         2, "OO", "ER", "EN", "UY", "ED", "EM")) {
+                //-- Dutch origin, e.g. "school", "schooner" --//
+                if (contains(value, index + 3, 2, "ER", "EN")) {
+                    //-- "schermerhorn", "schenker" --//
+                    result.append("X", "SK");
+                } else {
+                    result.append("SK");
+                }
+            } else {
+                if (index == 0 && !isVowel(charAt(value, 3)) && charAt(value, 3) != 'W') {
+                    result.append('X', 'S');
+                } else {
+                    result.append('X');
+                }
+            }
+        } else if (contains(value, index + 2, 1, "I", "E", "Y")) {
+            result.append('S');
+        } else {
+            result.append("SK");
+        }
+        return index + 3;
+    }
+
+    /**
+     * Handles 'T' cases
+     */
+    private int handleT(String value, 
+                        DoubleMetaphoneResult result, 
+                        int index) {
+        if (contains(value, index, 4, "TION")) {
+            result.append('X');
+            index += 3;
+        } else if (contains(value, index, 3, "TIA", "TCH")) {
+            result.append('X');
+            index += 3;
+        } else if (contains(value, index, 2, "TH") || contains(value, index, 
+                                                               3, "TTH")) {
+            if (contains(value, index + 2, 2, "OM", "AM") || 
+                //-- special case "thomas", "thames" or germanic --//
+                contains(value, 0, 4, "VAN ", "VON ") || 
+                contains(value, 0, 3, "SCH")) {
+                result.append('T');
+            } else {
+                result.append('0', 'T');
+            }
+            index += 2;
+        } else {
+            result.append('T');
+            index = contains(value, index + 1, 1, "T", "D") ? index + 2 : index + 1;
+        }
+        return index;
+    }
+
+    /**
+     * Handles 'W' cases
+     */
+    private int handleW(String value, 
+                        DoubleMetaphoneResult result, 
+                        int index) {
+        if (contains(value, index, 2, "WR")) {
+            //-- can also be in middle of word --//
+            result.append('R');
+            index += 2;
+        } else {
+            if (index == 0 && (isVowel(charAt(value, index + 1)) || 
+                               contains(value, index, 2, "WH"))) {
+                if (isVowel(charAt(value, index + 1))) {
+                    //-- Wasserman should match Vasserman --//
+                    result.append('A', 'F');
+                } else {
+                    //-- need Uomo to match Womo --//
+                    result.append('A');
+                }
+                index++;
+            } else if ((index == value.length() - 1 && isVowel(charAt(value, index - 1))) ||
+                       contains(value, index - 1, 
+                                5, "EWSKI", "EWSKY", "OWSKI", "OWSKY") ||
+                       contains(value, 0, 3, "SCH")) {
+                //-- Arnow should match Arnoff --//
+                result.appendAlternate('F');
+                index++;
+            } else if (contains(value, index, 4, "WICZ", "WITZ")) {
+                //-- Polish e.g. "filipowicz" --//
+                result.append("TS", "FX");
+                index += 4;
+            } else {
+                index++;
+            }
+        }
+        return index;
+    }
+    
+    /**
+     * Handles 'X' cases
+     */
+    private int handleX(String value, 
+                        DoubleMetaphoneResult result, 
+                        int index) {
+        if (index == 0) {
+            result.append('S');
+            index++;
+        } else {
+            if (!((index == value.length() - 1) && 
+                  (contains(value, index - 3, 3, "IAU", "EAU") || 
+                   contains(value, index - 2, 2, "AU", "OU")))) {
+                //-- French e.g. breaux --//
+                result.append("KS");
+            }
+            index = contains(value, index + 1, 1, "C", "X") ? index + 2 : index + 1;
+        }
+        return index;
+    }
+
+    /**
+     * Handles 'Z' cases
+     */
+    private int handleZ(String value, DoubleMetaphoneResult result, int index, 
+                        boolean slavoGermanic) {
+        if (charAt(value, index + 1) == 'H') {
+            //-- Chinese pinyin e.g. "zhao" or Angelina "Zhang" --//
+            result.append('J');
+            index += 2;
+        } else {
+            if (contains(value, index + 1, 2, "ZO", "ZI", "ZA") || (slavoGermanic && (index > 0 && charAt(value, index - 1) != 'T'))) {
+                result.append("S", "TS");
+            } else {
+                result.append('S');
+            }
+            index = charAt(value, index + 1) == 'Z' ? index + 2 : index + 1;
+        }
+        return index;
+    }
+
+    //-- BEGIN CONDITIONS --//
+
+    /**
+     * Complex condition 0 for 'C'
+     */
+    private boolean conditionC0(String value, int index) {
+        if (contains(value, index, 4, "CHIA")) {
+            return true;
+        } else if (index <= 1) {
+            return false;
+        } else if (isVowel(charAt(value, index - 2))) {
+            return false;
+        } else if (!contains(value, index - 1, 3, "ACH")) {
+            return false;
+        } else {
+            char c = charAt(value, index + 2);
+            return (c != 'I' && c != 'E')
+                    || contains(value, index - 2, 6, "BACHER", "MACHER");
+        }
+    }
+    
+    /**
+     * Complex condition 0 for 'CH'
+     */
+    private boolean conditionCH0(String value, int index) {
+        if (index != 0) {
+            return false;
+        } else if (!contains(value, index + 1, 5, "HARAC", "HARIS") && 
+                   !contains(value, index + 1, 3, "HOR", "HYM", "HIA", "HEM")) {
+            return false;
+        } else if (contains(value, 0, 5, "CHORE")) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+    
+    /**
+     * Complex condition 1 for 'CH'
+     */
+    private boolean conditionCH1(String value, int index) {
+        return ((contains(value, 0, 4, "VAN ", "VON ") || contains(value, 0, 
+                                                                   3, "SCH")) ||
+                contains(value, index - 2, 6, "ORCHES", "ARCHIT", "ORCHID") ||
+                contains(value, index + 2, 1, "T", "S") ||
+                ((contains(value, index - 1, 1, "A", "O", "U", "E") || index == 0) &&
+                 (contains(value, index + 2, 1, L_R_N_M_B_H_F_V_W_SPACE) || index + 1 == value.length() - 1)));
+    }
+    
+    /**
+     * Complex condition 0 for 'L'
+     */
+    private boolean conditionL0(String value, int index) {
+        if (index == value.length() - 3 && 
+            contains(value, index - 1, 4, "ILLO", "ILLA", "ALLE")) {
+            return true;
+        } else if ((contains(value, index - 1, 2, "AS", "OS") || 
+                    contains(value, value.length() - 1, 1, "A", "O")) &&
+                   contains(value, index - 1, 4, "ALLE")) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+    
+    /**
+     * Complex condition 0 for 'M'
+     */
+    private boolean conditionM0(String value, int index) {
+        if (charAt(value, index + 1) == 'M') {
+            return true;
+        }
+        return contains(value, index - 1, 3, "UMB")
+                && ((index + 1) == value.length() - 1 || contains(value,
+                        index + 2, 2, "ER"));
+    }
+    
+    //-- BEGIN HELPER FUNCTIONS --//
+
+    /**
+     * Determines whether or not a value is of slavo-germanic orgin. A value is
+     * of slavo-germanic origin if it contians any of 'W', 'K', 'CZ', or 'WITZ'.
+     */
+    private boolean isSlavoGermanic(String value) {
+        return value.indexOf('W') > -1 || value.indexOf('K') > -1 || 
+            value.indexOf("CZ") > -1 || value.indexOf("WITZ") > -1;
+    }
+
+    /**
+     * Determines whether or not a character is a vowel or not
+     */
+    private boolean isVowel(char ch) {
+        return VOWELS.indexOf(ch) != -1;
+    }
+
+    /**
+     * Determines whether or not the value starts with a silent letter.  It will
+     * return <code>true</code> if the value starts with any of 'GN', 'KN',
+     * 'PN', 'WR' or 'PS'.
+     */    
+    private boolean isSilentStart(String value) {
+        boolean result = false;
+        for (int i = 0; i < SILENT_START.length; i++) {
+            if (value.startsWith(SILENT_START[i])) {
+                result = true;
+                break;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Cleans the input
+     */    
+    private String cleanInput(String input) {
+        if (input == null) {
+            return null;
+        }
+        input = input.trim();
+        if (input.length() == 0) {
+            return null;
+        }
+        return input.toUpperCase();
+    }
+
+    /**
+     * Gets the character at index <code>index</code> if available, otherwise
+     * it returns <code>Character.MIN_VALUE</code> so that there is some sort
+     * of a default
+     */    
+    protected char charAt(String value, int index) {
+        if (index < 0 || index >= value.length()) {
+            return Character.MIN_VALUE;
+        } 
+        return value.charAt(index);
+    }
+
+    /**
+     * Shortcut method with 1 criteria
+     */    
+    private static boolean contains(String value, int start, int length, 
+                                    String criteria) {
+        return contains(value, start, length, 
+                        new String[] { criteria });
+    }
+
+    /**
+     * Shortcut method with 2 criteria
+     */    
+    private static boolean contains(String value, int start, int length, 
+                                    String criteria1, String criteria2) {
+        return contains(value, start, length, 
+                        new String[] { criteria1, criteria2 });
+    }
+
+    /**
+     * Shortcut method with 3 criteria
+     */    
+    private static boolean contains(String value, int start, int length, 
+                                    String criteria1, String criteria2, 
+                                    String criteria3) {
+        return contains(value, start, length, 
+                        new String[] { criteria1, criteria2, criteria3 });
+    }
+
+    /**
+     * Shortcut method with 4 criteria
+     */    
+    private static boolean contains(String value, int start, int length, 
+                                    String criteria1, String criteria2, 
+                                    String criteria3, String criteria4) {
+        return contains(value, start, length, 
+                        new String[] { criteria1, criteria2, criteria3, 
+                                       criteria4 });
+    }
+
+    /**
+     * Shortcut method with 5 criteria
+     */    
+    private static boolean contains(String value, int start, int length, 
+                                    String criteria1, String criteria2, 
+                                    String criteria3, String criteria4, 
+                                    String criteria5) {
+        return contains(value, start, length, 
+                        new String[] { criteria1, criteria2, criteria3, 
+                                       criteria4, criteria5 });
+    }
+
+    /**
+     * Shortcut method with 6 criteria
+     */    
+    private static boolean contains(String value, int start, int length, 
+                                    String criteria1, String criteria2, 
+                                    String criteria3, String criteria4, 
+                                    String criteria5, String criteria6) {
+        return contains(value, start, length, 
+                        new String[] { criteria1, criteria2, criteria3, 
+                                       criteria4, criteria5, criteria6 });
+    }
+    
+    /**
+     * Determines whether <code>value</code> contains any of the criteria 
+     starting
+     * at index <code>start</code> and matching up to length <code>length</code>
+     */    
+    protected static boolean contains(String value, int start, int length, 
+                                      String[] criteria) {
+        boolean result = false;
+        if (start >= 0 && start + length <= value.length()) {
+            String target = value.substring(start, start + length);
+
+            for (int i = 0; i < criteria.length; i++) {
+                if (target.equals(criteria[i])) {
+                    result = true;
+                    break;
+                }
+            }
+        }
+        return result;
+    }
+    
+    //-- BEGIN INNER CLASSES --//
+    
+    /**
+     * Inner class for storing results, since there is the optional alternate
+     * encoding.
+     */
+    public class DoubleMetaphoneResult {
+
+        private StringBuffer primary = new StringBuffer(getMaxCodeLen());
+        private StringBuffer alternate = new StringBuffer(getMaxCodeLen());
+        private int maxLength;
+
+        public DoubleMetaphoneResult(int maxLength) {
+            this.maxLength = maxLength;
+        }
+
+        public void append(char value) {
+            appendPrimary(value);
+            appendAlternate(value);
+        }
+
+        public void append(char primary, char alternate) {
+            appendPrimary(primary);
+            appendAlternate(alternate);
+        }
+
+        public void appendPrimary(char value) {
+            if (this.primary.length() < this.maxLength) {
+                this.primary.append(value);
+            }
+        }
+
+        public void appendAlternate(char value) {
+            if (this.alternate.length() < this.maxLength) {
+                this.alternate.append(value);
+            }
+        }
+
+        public void append(String value) {
+            appendPrimary(value);
+            appendAlternate(value);
+        }
+
+        public void append(String primary, String alternate) {
+            appendPrimary(primary);
+            appendAlternate(alternate);
+        }
+
+        public void appendPrimary(String value) {
+            int addChars = this.maxLength - this.primary.length();
+            if (value.length() <= addChars) {
+                this.primary.append(value);
+            } else {
+                this.primary.append(value.substring(0, addChars));
+            }
+        }
+
+        public void appendAlternate(String value) {
+            int addChars = this.maxLength - this.alternate.length();
+            if (value.length() <= addChars) {
+                this.alternate.append(value);
+            } else {
+                this.alternate.append(value.substring(0, addChars));
+            }
+        }
+
+        public String getPrimary() {
+            return this.primary.toString();
+        }
+
+        public String getAlternate() {
+            return this.alternate.toString();
+        }
+
+        public boolean isComplete() {
+            return this.primary.length() >= this.maxLength && 
+                this.alternate.length() >= this.maxLength;
+        }
+    }
+}
diff --git a/src/org/apache/commons/codec/language/Metaphone.java b/src/org/apache/commons/codec/language/Metaphone.java
new file mode 100644
index 0000000..dce2c72
--- /dev/null
+++ b/src/org/apache/commons/codec/language/Metaphone.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec.language;
+
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.StringEncoder;
+
+/**
+ * Encodes a string into a metaphone value. 
+ * <p>
+ * Initial Java implementation by <CITE>William B. Brogden. December, 1997</CITE>. 
+ * Permission given by <CITE>wbrogden</CITE> for code to be used anywhere.
+ * </p>
+ * <p>
+ * <CITE>Hanging on the Metaphone</CITE> by <CITE>Lawrence Philips</CITE> in <CITE>Computer Language of Dec. 1990, p
+ * 39.</CITE>
+ * </p>
+ * 
+ * @author Apache Software Foundation
+ * @version $Id: Metaphone.java,v 1.20 2004/06/05 18:32:04 ggregory Exp $
+ */
+public class Metaphone implements StringEncoder {
+
+    /**
+     * Five values in the English language 
+     */
+    private String vowels = "AEIOU" ;
+
+    /**
+     * Variable used in Metaphone algorithm
+     */
+    private String frontv = "EIY"   ;
+
+    /**
+     * Variable used in Metaphone algorithm
+     */
+    private String varson = "CSPTG" ;
+
+    /**
+     * The max code length for metaphone is 4
+     */
+    private int maxCodeLen = 4 ;
+
+    /**
+     * Creates an instance of the Metaphone encoder
+     */
+    public Metaphone() {
+        super();
+    }
+
+    /**
+     * Find the metaphone value of a String. This is similar to the
+     * soundex algorithm, but better at finding similar sounding words.
+     * All input is converted to upper case.
+     * Limitations: Input format is expected to be a single ASCII word
+     * with only characters in the A - Z range, no punctuation or numbers.
+     *
+     * @param txt String to find the metaphone code for
+     * @return A metaphone code corresponding to the String supplied
+     */
+    public String metaphone(String txt) {
+        boolean hard = false ;
+        if ((txt == null) || (txt.length() == 0)) {
+            return "" ;
+        }
+        // single character is itself
+        if (txt.length() == 1) {
+            return txt.toUpperCase() ;
+        }
+      
+        char[] inwd = txt.toUpperCase().toCharArray() ;
+      
+        StringBuffer local = new StringBuffer(40); // manipulate
+        StringBuffer code = new StringBuffer(10) ; //   output
+        // handle initial 2 characters exceptions
+        switch(inwd[0]) {
+        case 'K' : 
+        case 'G' : 
+        case 'P' : /* looking for KN, etc*/
+            if (inwd[1] == 'N') {
+                local.append(inwd, 1, inwd.length - 1);
+            } else {
+                local.append(inwd);
+            }
+            break;
+        case 'A': /* looking for AE */
+            if (inwd[1] == 'E') {
+                local.append(inwd, 1, inwd.length - 1);
+            } else {
+                local.append(inwd);
+            }
+            break;
+        case 'W' : /* looking for WR or WH */
+            if (inwd[1] == 'R') {   // WR -> R
+                local.append(inwd, 1, inwd.length - 1); 
+                break ;
+            }
+            if (inwd[1] == 'H') {
+                local.append(inwd, 1, inwd.length - 1);
+                local.setCharAt(0, 'W'); // WH -> W
+            } else {
+                local.append(inwd);
+            }
+            break;
+        case 'X' : /* initial X becomes S */
+            inwd[0] = 'S';
+            local.append(inwd);
+            break ;
+        default :
+            local.append(inwd);
+        } // now local has working string with initials fixed
+
+        int wdsz = local.length();
+        int n = 0 ;
+
+        while ((code.length() < this.getMaxCodeLen()) && 
+               (n < wdsz) ) { // max code size of 4 works well
+            char symb = local.charAt(n) ;
+            // remove duplicate letters except C
+            if ((symb != 'C') && (isPreviousChar( local, n, symb )) ) {
+                n++ ;
+            } else { // not dup
+                switch(symb) {
+                case 'A' : case 'E' : case 'I' : case 'O' : case 'U' :
+                    if (n == 0) { 
+                        code.append(symb);
+                    }
+                    break ; // only use vowel if leading char
+                case 'B' :
+                    if ( isPreviousChar(local, n, 'M') && 
+                         isLastChar(wdsz, n) ) { // B is silent if word ends in MB
+                        break;
+                    }
+                    code.append(symb);
+                    break;
+                case 'C' : // lots of C special cases
+                    /* discard if SCI, SCE or SCY */
+                    if ( isPreviousChar(local, n, 'S') && 
+                         !isLastChar(wdsz, n) && 
+                         (this.frontv.indexOf(local.charAt(n + 1)) >= 0) ) { 
+                        break;
+                    }
+                    if (regionMatch(local, n, "CIA")) { // "CIA" -> X
+                        code.append('X'); 
+                        break;
+                    }
+                    if (!isLastChar(wdsz, n) && 
+                        (this.frontv.indexOf(local.charAt(n + 1)) >= 0)) {
+                        code.append('S');
+                        break; // CI,CE,CY -> S
+                    }
+                    if (isPreviousChar(local, n, 'S') &&
+                        isNextChar(local, n, 'H') ) { // SCH->sk
+                        code.append('K') ; 
+                        break ;
+                    }
+                    if (isNextChar(local, n, 'H')) { // detect CH
+                        if ((n == 0) && 
+                            (wdsz >= 3) && 
+                            isVowel(local,2) ) { // CH consonant -> K consonant
+                            code.append('K');
+                        } else { 
+                            code.append('X'); // CHvowel -> X
+                        }
+                    } else { 
+                        code.append('K');
+                    }
+                    break ;
+                case 'D' :
+                    if (!isLastChar(wdsz, n + 1) && 
+                        isNextChar(local, n, 'G') && 
+                        (this.frontv.indexOf(local.charAt(n + 2)) >= 0)) { // DGE DGI DGY -> J 
+                        code.append('J'); n += 2 ;
+                    } else { 
+                        code.append('T');
+                    }
+                    break ;
+                case 'G' : // GH silent at end or before consonant
+                    if (isLastChar(wdsz, n + 1) && 
+                        isNextChar(local, n, 'H')) {
+                        break;
+                    }
+                    if (!isLastChar(wdsz, n + 1) &&  
+                        isNextChar(local,n,'H') && 
+                        !isVowel(local,n+2)) {
+                        break;
+                    }
+                    if ((n > 0) && 
+                        ( regionMatch(local, n, "GN") ||
+                          regionMatch(local, n, "GNED") ) ) {
+                        break; // silent G
+                    }
+                    if (isPreviousChar(local, n, 'G')) {
+                        hard = true ;
+                    } else {
+                        hard = false ;
+                    }
+                    if (!isLastChar(wdsz, n) && 
+                        (this.frontv.indexOf(local.charAt(n + 1)) >= 0) && 
+                        (!hard)) {
+                        code.append('J');
+                    } else {
+                        code.append('K');
+                    }
+                    break ;
+                case 'H':
+                    if (isLastChar(wdsz, n)) {
+                        break ; // terminal H
+                    }
+                    if ((n > 0) && 
+                        (this.varson.indexOf(local.charAt(n - 1)) >= 0)) {
+                        break;
+                    }
+                    if (isVowel(local,n+1)) {
+                        code.append('H'); // Hvowel
+                    }
+                    break;
+                case 'F': 
+                case 'J' : 
+                case 'L' :
+                case 'M': 
+                case 'N' : 
+                case 'R' :
+                    code.append(symb); 
+                    break;
+                case 'K' :
+                    if (n > 0) { // not initial
+                        if (!isPreviousChar(local, n, 'C')) {
+                            code.append(symb);
+                        }
+                    } else {
+                        code.append(symb); // initial K
+                    }
+                    break ;
+                case 'P' :
+                    if (isNextChar(local,n,'H')) {
+                        // PH -> F
+                        code.append('F');
+                    } else {
+                        code.append(symb);
+                    }
+                    break ;
+                case 'Q' :
+                    code.append('K');
+                    break;
+                case 'S' :
+                    if (regionMatch(local,n,"SH") || 
+                        regionMatch(local,n,"SIO") || 
+                        regionMatch(local,n,"SIA")) {
+                        code.append('X');
+                    } else {
+                        code.append('S');
+                    }
+                    break;
+                case 'T' :
+                    if (regionMatch(local,n,"TIA") || 
+                        regionMatch(local,n,"TIO")) {
+                        code.append('X'); 
+                        break;
+                    }
+                    if (regionMatch(local,n,"TCH")) {
+                        // Silent if in "TCH"
+                        break;
+                    }
+                    // substitute numeral 0 for TH (resembles theta after all)
+                    if (regionMatch(local,n,"TH")) {
+                        code.append('0');
+                    } else {
+                        code.append('T');
+                    }
+                    break ;
+                case 'V' :
+                    code.append('F'); break ;
+                case 'W' : case 'Y' : // silent if not followed by vowel
+                    if (!isLastChar(wdsz,n) && 
+                        isVowel(local,n+1)) {
+                        code.append(symb);
+                    }
+                    break ;
+                case 'X' :
+                    code.append('K'); code.append('S');
+                    break ;
+                case 'Z' :
+                    code.append('S'); break ;
+                } // end switch
+                n++ ;
+            } // end else from symb != 'C'
+            if (code.length() > this.getMaxCodeLen()) { 
+                code.setLength(this.getMaxCodeLen()); 
+            }
+        }
+        return code.toString();
+    }
+
+    private boolean isVowel(StringBuffer string, int index) {
+        return (this.vowels.indexOf(string.charAt(index)) >= 0);
+    }
+
+    private boolean isPreviousChar(StringBuffer string, int index, char c) {
+        boolean matches = false;
+        if( index > 0 &&
+            index < string.length() ) {
+            matches = string.charAt(index - 1) == c;
+        }
+        return matches;
+    }
+
+    private boolean isNextChar(StringBuffer string, int index, char c) {
+        boolean matches = false;
+        if( index >= 0 &&
+            index < string.length() - 1 ) {
+            matches = string.charAt(index + 1) == c;
+        }
+        return matches;
+    }
+
+    private boolean regionMatch(StringBuffer string, int index, String test) {
+        boolean matches = false;
+        if( index >= 0 &&
+            (index + test.length() - 1) < string.length() ) {
+            String substring = string.substring( index, index + test.length());
+            matches = substring.equals( test );
+        }
+        return matches;
+    }
+
+    private boolean isLastChar(int wdsz, int n) {
+        return n + 1 == wdsz;
+    } 
+    
+    
+    /**
+     * Encodes an Object using the metaphone algorithm.  This method
+     * is provided in order to satisfy the requirements of the
+     * Encoder interface, and will throw an EncoderException if the
+     * supplied object is not of type java.lang.String.
+     *
+     * @param pObject Object to encode
+     * @return An object (or type java.lang.String) containing the 
+     *         metaphone code which corresponds to the String supplied.
+     * @throws EncoderException if the parameter supplied is not
+     *                          of type java.lang.String
+     */
+    public Object encode(Object pObject) throws EncoderException {
+        if (!(pObject instanceof java.lang.String)) {
+            throw new EncoderException("Parameter supplied to Metaphone encode is not of type java.lang.String"); 
+        }
+        return metaphone((String) pObject);
+    }
+
+    /**
+     * Encodes a String using the Metaphone algorithm. 
+     *
+     * @param pString String object to encode
+     * @return The metaphone code corresponding to the String supplied
+     */
+    public String encode(String pString) {
+        return metaphone(pString);   
+    }
+
+    /**
+     * Tests is the metaphones of two strings are identical.
+     *
+     * @param str1 First of two strings to compare
+     * @param str2 Second of two strings to compare
+     * @return true if the metaphones of these strings are identical, 
+     *         false otherwise.
+     */
+    public boolean isMetaphoneEqual(String str1, String str2) {
+        return metaphone(str1).equals(metaphone(str2));
+    }
+
+    /**
+     * Returns the maxCodeLen.
+     * @return int
+     */
+    public int getMaxCodeLen() { return this.maxCodeLen; }
+
+    /**
+     * Sets the maxCodeLen.
+     * @param maxCodeLen The maxCodeLen to set
+     */
+    public void setMaxCodeLen(int maxCodeLen) { this.maxCodeLen = maxCodeLen; }
+
+}
diff --git a/src/org/apache/commons/codec/language/RefinedSoundex.java b/src/org/apache/commons/codec/language/RefinedSoundex.java
new file mode 100644
index 0000000..dbf60fe
--- /dev/null
+++ b/src/org/apache/commons/codec/language/RefinedSoundex.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec.language;
+
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.StringEncoder;
+
+/**
+ * Encodes a string into a Refined Soundex value. A refined soundex code is
+ * optimized for spell checking words. Soundex method originally developed by
+ * <CITE>Margaret Odell</CITE> and <CITE>Robert Russell</CITE>.
+ * 
+ * @author Apache Software Foundation
+ * @version $Id: RefinedSoundex.java,v 1.21 2004/06/05 18:32:04 ggregory Exp $
+ */
+public class RefinedSoundex implements StringEncoder {
+
+    /**
+     * This static variable contains an instance of the RefinedSoundex using
+     * the US_ENGLISH mapping.
+     */
+    public static final RefinedSoundex US_ENGLISH = new RefinedSoundex();
+
+    /**
+     * RefinedSoundex is *refined* for a number of reasons one being that the
+     * mappings have been altered. This implementation contains default
+     * mappings for US English.
+     */
+    public static final char[] US_ENGLISH_MAPPING = "01360240043788015936020505".toCharArray();
+
+    /**
+     * Every letter of the alphabet is "mapped" to a numerical value. This char
+     * array holds the values to which each letter is mapped. This
+     * implementation contains a default map for US_ENGLISH
+     */
+    private char[] soundexMapping;
+
+    /**
+     * Creates an instance of the RefinedSoundex object using the default US
+     * English mapping.
+     */
+    public RefinedSoundex() {
+        this(US_ENGLISH_MAPPING);
+    }
+
+    /**
+     * Creates a refined soundex instance using a custom mapping. This
+     * constructor can be used to customize the mapping, and/or possibly
+     * provide an internationalized mapping for a non-Western character set.
+     * 
+     * @param mapping
+     *                  Mapping array to use when finding the corresponding code for
+     *                  a given character
+     */
+    public RefinedSoundex(char[] mapping) {
+        this.soundexMapping = mapping;
+    }
+
+    // BEGIN android-note
+    // Removed @see reference to SoundexUtils below, since the class isn't
+    // public.
+    // END android-note
+    /**
+     * Returns the number of characters in the two encoded Strings that are the
+     * same. This return value ranges from 0 to the length of the shortest
+     * encoded String: 0 indicates little or no similarity, and 4 out of 4 (for
+     * example) indicates strong similarity or identical values. For refined
+     * Soundex, the return value can be greater than 4.
+     * 
+     * @param s1
+     *                  A String that will be encoded and compared.
+     * @param s2
+     *                  A String that will be encoded and compared.
+     * @return The number of characters in the two encoded Strings that are the
+     *             same from 0 to to the length of the shortest encoded String.
+     * 
+     * @see <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tsqlref/ts_de-dz_8co5.asp">
+     *          MS T-SQL DIFFERENCE</a>
+     * 
+     * @throws EncoderException
+     *                  if an error occurs encoding one of the strings
+     * @since 1.3
+     */
+    public int difference(String s1, String s2) throws EncoderException {
+        return SoundexUtils.difference(this, s1, s2);
+    }
+
+    /**
+     * Encodes an Object using the refined soundex algorithm. This method is
+     * provided in order to satisfy the requirements of the Encoder interface,
+     * and will throw an EncoderException if the supplied object is not of type
+     * java.lang.String.
+     * 
+     * @param pObject
+     *                  Object to encode
+     * @return An object (or type java.lang.String) containing the refined
+     *             soundex code which corresponds to the String supplied.
+     * @throws EncoderException
+     *                  if the parameter supplied is not of type java.lang.String
+     */
+    public Object encode(Object pObject) throws EncoderException {
+        if (!(pObject instanceof java.lang.String)) {
+            throw new EncoderException("Parameter supplied to RefinedSoundex encode is not of type java.lang.String");
+        }
+        return soundex((String) pObject);
+    }
+
+    /**
+     * Encodes a String using the refined soundex algorithm.
+     * 
+     * @param pString
+     *                  A String object to encode
+     * @return A Soundex code corresponding to the String supplied
+     */
+    public String encode(String pString) {
+        return soundex(pString);
+    }
+
+    /**
+     * Returns the mapping code for a given character. The mapping codes are
+     * maintained in an internal char array named soundexMapping, and the
+     * default values of these mappings are US English.
+     * 
+     * @param c
+     *                  char to get mapping for
+     * @return A character (really a numeral) to return for the given char
+     */
+    char getMappingCode(char c) {
+        if (!Character.isLetter(c)) {
+            return 0;
+        }
+        return this.soundexMapping[Character.toUpperCase(c) - 'A'];
+    }
+
+    /**
+     * Retreives the Refined Soundex code for a given String object.
+     * 
+     * @param str
+     *                  String to encode using the Refined Soundex algorithm
+     * @return A soundex code for the String supplied
+     */
+    public String soundex(String str) {
+        if (str == null) {
+            return null;
+        }
+        str = SoundexUtils.clean(str);
+        if (str.length() == 0) {
+            return str;
+        }
+
+        StringBuffer sBuf = new StringBuffer();
+        sBuf.append(str.charAt(0));
+
+        char last, current;
+        last = '*';
+
+        for (int i = 0; i < str.length(); i++) {
+
+            current = getMappingCode(str.charAt(i));
+            if (current == last) {
+                continue;
+            } else if (current != 0) {
+                sBuf.append(current);
+            }
+
+            last = current;
+
+        }
+
+        return sBuf.toString();
+    }
+}
diff --git a/src/org/apache/commons/codec/language/Soundex.java b/src/org/apache/commons/codec/language/Soundex.java
new file mode 100644
index 0000000..61ce440
--- /dev/null
+++ b/src/org/apache/commons/codec/language/Soundex.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec.language;
+
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.StringEncoder;
+
+/**
+ * Encodes a string into a Soundex value. Soundex is an encoding used to relate similar names, but can also be used as a
+ * general purpose scheme to find word with similar phonemes.
+ * 
+ * @author Apache Software Foundation
+ * @version $Id: Soundex.java,v 1.26 2004/07/07 23:15:24 ggregory Exp $
+ */
+public class Soundex implements StringEncoder {
+
+    /**
+     * An instance of Soundex using the US_ENGLISH_MAPPING mapping.
+     * 
+     * @see #US_ENGLISH_MAPPING
+     */
+    public static final Soundex US_ENGLISH = new Soundex();
+
+    /**
+     * This is a default mapping of the 26 letters used in US English. A value of <code>0</code> for a letter position
+     * means do not encode.
+     * <p>
+     * (This constant is provided as both an implementation convenience and to allow Javadoc to pick
+     * up the value for the constant values page.)
+     * </p>
+     * 
+     * @see #US_ENGLISH_MAPPING
+     */
+    public static final String US_ENGLISH_MAPPING_STRING = "01230120022455012623010202";
+
+    /**
+     * This is a default mapping of the 26 letters used in US English. A value of <code>0</code> for a letter position
+     * means do not encode.
+     * 
+     * @see Soundex#Soundex(char[])
+     */
+    public static final char[] US_ENGLISH_MAPPING = US_ENGLISH_MAPPING_STRING.toCharArray();
+
+    // BEGIN android-note
+    // Removed @see reference to SoundexUtils below, since the class isn't
+    // public.
+    // END android-note
+    /**
+     * Encodes the Strings and returns the number of characters in the two encoded Strings that are the same. This
+     * return value ranges from 0 through 4: 0 indicates little or no similarity, and 4 indicates strong similarity or
+     * identical values.
+     * 
+     * @param s1
+     *                  A String that will be encoded and compared.
+     * @param s2
+     *                  A String that will be encoded and compared.
+     * @return The number of characters in the two encoded Strings that are the same from 0 to 4.
+     * 
+     * @see <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tsqlref/ts_de-dz_8co5.asp"> MS
+     *          T-SQL DIFFERENCE </a>
+     * 
+     * @throws EncoderException
+     *                  if an error occurs encoding one of the strings
+     * @since 1.3
+     */
+    public int difference(String s1, String s2) throws EncoderException {
+        return SoundexUtils.difference(this, s1, s2);
+    }
+
+    /**
+     * The maximum length of a Soundex code - Soundex codes are only four characters by definition.
+     * 
+     * @deprecated This feature is not needed since the encoding size must be constant. Will be removed in 2.0.
+     */
+    private int maxLength = 4;
+
+    /**
+     * Every letter of the alphabet is "mapped" to a numerical value. This char array holds the values to which each
+     * letter is mapped. This implementation contains a default map for US_ENGLISH
+     */
+    private char[] soundexMapping;
+
+    /**
+     * Creates an instance using US_ENGLISH_MAPPING
+     * 
+     * @see Soundex#Soundex(char[])
+     * @see Soundex#US_ENGLISH_MAPPING
+     */
+    public Soundex() {
+        this(US_ENGLISH_MAPPING);
+    }
+
+    /**
+     * Creates a soundex instance using the given mapping. This constructor can be used to provide an internationalized
+     * mapping for a non-Western character set.
+     * 
+     * Every letter of the alphabet is "mapped" to a numerical value. This char array holds the values to which each
+     * letter is mapped. This implementation contains a default map for US_ENGLISH
+     * 
+     * @param mapping
+     *                  Mapping array to use when finding the corresponding code for a given character
+     */
+    public Soundex(char[] mapping) {
+        this.setSoundexMapping(mapping);
+    }
+
+    /**
+     * Encodes an Object using the soundex algorithm. This method is provided in order to satisfy the requirements of
+     * the Encoder interface, and will throw an EncoderException if the supplied object is not of type java.lang.String.
+     * 
+     * @param pObject
+     *                  Object to encode
+     * @return An object (or type java.lang.String) containing the soundex code which corresponds to the String
+     *             supplied.
+     * @throws EncoderException
+     *                  if the parameter supplied is not of type java.lang.String
+     * @throws IllegalArgumentException
+     *                  if a character is not mapped
+     */
+    public Object encode(Object pObject) throws EncoderException {
+        if (!(pObject instanceof String)) {
+            throw new EncoderException("Parameter supplied to Soundex encode is not of type java.lang.String");
+        }
+        return soundex((String) pObject);
+    }
+
+    /**
+     * Encodes a String using the soundex algorithm.
+     * 
+     * @param pString
+     *                  A String object to encode
+     * @return A Soundex code corresponding to the String supplied
+     * @throws IllegalArgumentException
+     *                  if a character is not mapped
+     */
+    public String encode(String pString) {
+        return soundex(pString);
+    }
+
+    /**
+     * Used internally by the SoundEx algorithm.
+     * 
+     * Consonants from the same code group separated by W or H are treated as one.
+     * 
+     * @param str
+     *                  the cleaned working string to encode (in upper case).
+     * @param index
+     *                  the character position to encode
+     * @return Mapping code for a particular character
+     * @throws IllegalArgumentException
+     *                  if the character is not mapped
+     */
+    private char getMappingCode(String str, int index) {
+        char mappedChar = this.map(str.charAt(index));
+        // HW rule check
+        if (index > 1 && mappedChar != '0') {
+            char hwChar = str.charAt(index - 1);
+            if ('H' == hwChar || 'W' == hwChar) {
+                char preHWChar = str.charAt(index - 2);
+                char firstCode = this.map(preHWChar);
+                if (firstCode == mappedChar || 'H' == preHWChar || 'W' == preHWChar) {
+                    return 0;
+                }
+            }
+        }
+        return mappedChar;
+    }
+
+    /**
+     * Returns the maxLength. Standard Soundex
+     * 
+     * @deprecated This feature is not needed since the encoding size must be constant. Will be removed in 2.0.
+     * @return int
+     */
+    public int getMaxLength() {
+        return this.maxLength;
+    }
+
+    /**
+     * Returns the soundex mapping.
+     * 
+     * @return soundexMapping.
+     */
+    private char[] getSoundexMapping() {
+        return this.soundexMapping;
+    }
+
+    /**
+     * Maps the given upper-case character to it's Soudex code.
+     * 
+     * @param ch
+     *                  An upper-case character.
+     * @return A Soundex code.
+     * @throws IllegalArgumentException
+     *                  Thrown if <code>ch</code> is not mapped.
+     */
+    private char map(char ch) {
+        int index = ch - 'A';
+        if (index < 0 || index >= this.getSoundexMapping().length) {
+            throw new IllegalArgumentException("The character is not mapped: " + ch);
+        }
+        return this.getSoundexMapping()[index];
+    }
+
+    /**
+     * Sets the maxLength.
+     * 
+     * @deprecated This feature is not needed since the encoding size must be constant. Will be removed in 2.0.
+     * @param maxLength
+     *                  The maxLength to set
+     */
+    public void setMaxLength(int maxLength) {
+        this.maxLength = maxLength;
+    }
+
+    /**
+     * Sets the soundexMapping.
+     * 
+     * @param soundexMapping
+     *                  The soundexMapping to set.
+     */
+    private void setSoundexMapping(char[] soundexMapping) {
+        this.soundexMapping = soundexMapping;
+    }
+
+    /**
+     * Retreives the Soundex code for a given String object.
+     * 
+     * @param str
+     *                  String to encode using the Soundex algorithm
+     * @return A soundex code for the String supplied
+     * @throws IllegalArgumentException
+     *                  if a character is not mapped
+     */
+    public String soundex(String str) {
+        if (str == null) {
+            return null;
+        }
+        str = SoundexUtils.clean(str);
+        if (str.length() == 0) {
+            return str;
+        }
+        char out[] = {'0', '0', '0', '0'};
+        char last, mapped;
+        int incount = 1, count = 1;
+        out[0] = str.charAt(0);
+        last = getMappingCode(str, 0);
+        while ((incount < str.length()) && (count < out.length)) {
+            mapped = getMappingCode(str, incount++);
+            if (mapped != 0) {
+                if ((mapped != '0') && (mapped != last)) {
+                    out[count++] = mapped;
+                }
+                last = mapped;
+            }
+        }
+        return new String(out);
+    }
+
+}
diff --git a/src/org/apache/commons/codec/language/SoundexUtils.java b/src/org/apache/commons/codec/language/SoundexUtils.java
new file mode 100644
index 0000000..48f2d87
--- /dev/null
+++ b/src/org/apache/commons/codec/language/SoundexUtils.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec.language;
+
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.StringEncoder;
+
+/**
+ * Utility methods for {@link Soundex} and {@link RefinedSoundex} classes.
+ * 
+ * @author Apache Software Foundation
+ * @version $Id: SoundexUtils.java,v 1.5 2004/03/17 18:31:35 ggregory Exp $
+ * @since 1.3
+ */
+final class SoundexUtils {
+
+    /**
+     * Cleans up the input string before Soundex processing by only returning
+     * upper case letters.
+     * 
+     * @param str
+     *                  The String to clean.
+     * @return A clean String.
+     */
+    static String clean(String str) {
+        if (str == null || str.length() == 0) {
+            return str;
+        }
+        int len = str.length();
+        char[] chars = new char[len];
+        int count = 0;
+        for (int i = 0; i < len; i++) {
+            if (Character.isLetter(str.charAt(i))) {
+                chars[count++] = str.charAt(i);
+            }
+        }
+        if (count == len) {
+            return str.toUpperCase();
+        }
+        return new String(chars, 0, count).toUpperCase();
+    }
+
+    /**
+     * Encodes the Strings and returns the number of characters in the two
+     * encoded Strings that are the same.
+     * <ul>
+     * <li>For Soundex, this return value ranges from 0 through 4: 0 indicates
+     * little or no similarity, and 4 indicates strong similarity or identical
+     * values.</li>
+     * <li>For refined Soundex, the return value can be greater than 4.</li>
+     * </ul>
+     * 
+     * @param encoder
+     *                  The encoder to use to encode the Strings.
+     * @param s1
+     *                  A String that will be encoded and compared.
+     * @param s2
+     *                  A String that will be encoded and compared.
+     * @return The number of characters in the two Soundex encoded Strings that
+     *             are the same.
+     * 
+     * @see #differenceEncoded(String,String)
+     * @see <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tsqlref/ts_de-dz_8co5.asp">
+     *          MS T-SQL DIFFERENCE</a>
+     * 
+     * @throws EncoderException
+     *                  if an error occurs encoding one of the strings
+     */
+    static int difference(StringEncoder encoder, String s1, String s2) throws EncoderException {
+        return differenceEncoded(encoder.encode(s1), encoder.encode(s2));
+    }
+
+    /**
+     * Returns the number of characters in the two Soundex encoded Strings that
+     * are the same.
+     * <ul>
+     * <li>For Soundex, this return value ranges from 0 through 4: 0 indicates
+     * little or no similarity, and 4 indicates strong similarity or identical
+     * values.</li>
+     * <li>For refined Soundex, the return value can be greater than 4.</li>
+     * </ul>
+     * 
+     * @param es1
+     *                  An encoded String.
+     * @param es2
+     *                  An encoded String.
+     * @return The number of characters in the two Soundex encoded Strings that
+     *             are the same.
+     * 
+     * @see <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tsqlref/ts_de-dz_8co5.asp">
+     *          MS T-SQL DIFFERENCE</a>
+     */
+    static int differenceEncoded(String es1, String es2) {
+
+        if (es1 == null || es2 == null) {
+            return 0;
+        }
+        int lengthToMatch = Math.min(es1.length(), es2.length());
+        int diff = 0;
+        for (int i = 0; i < lengthToMatch; i++) {
+            if (es1.charAt(i) == es2.charAt(i)) {
+                diff++;
+            }
+        }
+        return diff;
+    }
+
+}
diff --git a/src/org/apache/commons/codec/language/package.html b/src/org/apache/commons/codec/language/package.html
new file mode 100644
index 0000000..fab8e4c
--- /dev/null
+++ b/src/org/apache/commons/codec/language/package.html
@@ -0,0 +1,20 @@
+<!--
+Copyright 2003-2004 The Apache Software Foundation.
+ 
+Licensed 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.
+-->
+<html>
+ <body>
+  Language and phonetic encoders.
+ </body>
+</html>
diff --git a/src/org/apache/commons/codec/net/BCodec.java b/src/org/apache/commons/codec/net/BCodec.java
new file mode 100644
index 0000000..b164100
--- /dev/null
+++ b/src/org/apache/commons/codec/net/BCodec.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec.net;
+
+import java.io.UnsupportedEncodingException;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.StringDecoder;
+import org.apache.commons.codec.StringEncoder;
+import org.apache.commons.codec.binary.Base64;
+
+/**
+ * <p>
+ * Identical to the Base64 encoding defined by <a href="http://www.ietf.org/rfc/rfc1521.txt">RFC
+ * 1521</a> and allows a character set to be specified.
+ * </p>
+ * 
+ * <p>
+ * <a href="http://www.ietf.org/rfc/rfc1522.txt">RFC 1522</a> describes techniques to allow the encoding of non-ASCII
+ * text in various portions of a RFC 822 [2] message header, in a manner which is unlikely to confuse existing message
+ * handling software.
+ * </p>
+ * 
+ * @see <a href="http://www.ietf.org/rfc/rfc1522.txt">MIME (Multipurpose Internet Mail Extensions) Part Two: Message
+ *          Header Extensions for Non-ASCII Text</a>
+ * 
+ * @author Apache Software Foundation
+ * @since 1.3
+ * @version $Id: BCodec.java,v 1.5 2004/04/13 22:46:37 ggregory Exp $
+ */
+public class BCodec extends RFC1522Codec implements StringEncoder, StringDecoder {
+    /**
+     * The default charset used for string decoding and encoding.
+     */
+    private String charset = StringEncodings.UTF8;
+
+    /**
+     * Default constructor.
+     */
+    public BCodec() {
+        super();
+    }
+
+    /**
+     * Constructor which allows for the selection of a default charset
+     * 
+     * @param charset
+     *                  the default string charset to use.
+     * 
+     * @see <a href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE character
+     *          encoding names</a>
+     */
+    public BCodec(final String charset) {
+        super();
+        this.charset = charset;
+    }
+
+    protected String getEncoding() {
+        return "B";
+    }
+
+    protected byte[] doEncoding(byte[] bytes) throws EncoderException {
+        if (bytes == null) {
+            return null;
+        }
+        return Base64.encodeBase64(bytes);
+    }
+
+    protected byte[] doDecoding(byte[] bytes) throws DecoderException {
+        if (bytes == null) {
+            return null;
+        }
+        return Base64.decodeBase64(bytes);
+    }
+
+    /**
+     * Encodes a string into its Base64 form using the specified charset. Unsafe characters are escaped.
+     * 
+     * @param value
+     *                  string to convert to Base64 form
+     * @param charset
+     *                  the charset for pString
+     * @return Base64 string
+     * 
+     * @throws EncoderException
+     *                  thrown if a failure condition is encountered during the encoding process.
+     */
+    public String encode(final String value, final String charset) throws EncoderException {
+        if (value == null) {
+            return null;
+        }
+        try {
+            return encodeText(value, charset);
+        } catch (UnsupportedEncodingException e) {
+            throw new EncoderException(e.getMessage());
+        }
+    }
+
+    /**
+     * Encodes a string into its Base64 form using the default charset. Unsafe characters are escaped.
+     * 
+     * @param value
+     *                  string to convert to Base64 form
+     * @return Base64 string
+     * 
+     * @throws EncoderException
+     *                  thrown if a failure condition is encountered during the encoding process.
+     */
+    public String encode(String value) throws EncoderException {
+        if (value == null) {
+            return null;
+        }
+        return encode(value, getDefaultCharset());
+    }
+
+    /**
+     * Decodes a Base64 string into its original form. Escaped characters are converted back to their original
+     * representation.
+     * 
+     * @param value
+     *                  Base64 string to convert into its original form
+     * 
+     * @return original string
+     * 
+     * @throws DecoderException
+     *                  A decoder exception is thrown if a failure condition is encountered during the decode process.
+     */
+    public String decode(String value) throws DecoderException {
+        if (value == null) {
+            return null;
+        }
+        try {
+            return decodeText(value);
+        } catch (UnsupportedEncodingException e) {
+            throw new DecoderException(e.getMessage());
+        }
+    }
+
+    /**
+     * Encodes an object into its Base64 form using the default charset. Unsafe characters are escaped.
+     * 
+     * @param value
+     *                  object to convert to Base64 form
+     * @return Base64 object
+     * 
+     * @throws EncoderException
+     *                  thrown if a failure condition is encountered during the encoding process.
+     */
+    public Object encode(Object value) throws EncoderException {
+        if (value == null) {
+            return null;
+        } else if (value instanceof String) {
+            return encode((String) value);
+        } else {
+            throw new EncoderException("Objects of type "
+                + value.getClass().getName()
+                + " cannot be encoded using BCodec");
+        }
+    }
+
+    /**
+     * Decodes a Base64 object into its original form. Escaped characters are converted back to their original
+     * representation.
+     * 
+     * @param value
+     *                  Base64 object to convert into its original form
+     * 
+     * @return original object
+     * 
+     * @throws DecoderException
+     *                  A decoder exception is thrown if a failure condition is encountered during the decode process.
+     */
+    public Object decode(Object value) throws DecoderException {
+        if (value == null) {
+            return null;
+        } else if (value instanceof String) {
+            return decode((String) value);
+        } else {
+            throw new DecoderException("Objects of type "
+                + value.getClass().getName()
+                + " cannot be decoded using BCodec");
+        }
+    }
+
+    /**
+     * The default charset used for string decoding and encoding.
+     * 
+     * @return the default string charset.
+     */
+    public String getDefaultCharset() {
+        return this.charset;
+    }
+}
diff --git a/src/org/apache/commons/codec/net/QCodec.java b/src/org/apache/commons/codec/net/QCodec.java
new file mode 100644
index 0000000..5736080
--- /dev/null
+++ b/src/org/apache/commons/codec/net/QCodec.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec.net;
+
+import java.io.UnsupportedEncodingException;
+import java.util.BitSet;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.StringDecoder;
+import org.apache.commons.codec.StringEncoder;
+
+/**
+ * <p>
+ * Similar to the Quoted-Printable content-transfer-encoding defined in <a
+ * href="http://www.ietf.org/rfc/rfc1521.txt">RFC 1521</a> and designed to allow text containing mostly ASCII
+ * characters to be decipherable on an ASCII terminal without decoding.
+ * </p>
+ * 
+ * <p>
+ * <a href="http://www.ietf.org/rfc/rfc1522.txt">RFC 1522</a> describes techniques to allow the encoding of non-ASCII
+ * text in various portions of a RFC 822 [2] message header, in a manner which is unlikely to confuse existing message
+ * handling software.
+ * </p>
+ * 
+ * @see <a href="http://www.ietf.org/rfc/rfc1522.txt">MIME (Multipurpose Internet Mail Extensions) Part Two: Message
+ *          Header Extensions for Non-ASCII Text</a>
+ * 
+ * @author Apache Software Foundation
+ * @since 1.3
+ * @version $Id: QCodec.java,v 1.6 2004/05/24 00:24:32 ggregory Exp $
+ */
+public class QCodec extends RFC1522Codec implements StringEncoder, StringDecoder {
+    /**
+     * The default charset used for string decoding and encoding.
+     */
+    private String charset = StringEncodings.UTF8;
+
+    /**
+     * BitSet of printable characters as defined in RFC 1522.
+     */
+    private static final BitSet PRINTABLE_CHARS = new BitSet(256);
+    // Static initializer for printable chars collection
+    static {
+        // alpha characters
+        PRINTABLE_CHARS.set(' ');
+        PRINTABLE_CHARS.set('!');
+        PRINTABLE_CHARS.set('"');
+        PRINTABLE_CHARS.set('#');
+        PRINTABLE_CHARS.set('$');
+        PRINTABLE_CHARS.set('%');
+        PRINTABLE_CHARS.set('&');
+        PRINTABLE_CHARS.set('\'');
+        PRINTABLE_CHARS.set('(');
+        PRINTABLE_CHARS.set(')');
+        PRINTABLE_CHARS.set('*');
+        PRINTABLE_CHARS.set('+');
+        PRINTABLE_CHARS.set(',');
+        PRINTABLE_CHARS.set('-');
+        PRINTABLE_CHARS.set('.');
+        PRINTABLE_CHARS.set('/');
+        for (int i = '0'; i <= '9'; i++) {
+            PRINTABLE_CHARS.set(i);
+        }
+        PRINTABLE_CHARS.set(':');
+        PRINTABLE_CHARS.set(';');
+        PRINTABLE_CHARS.set('<');
+        PRINTABLE_CHARS.set('>');
+        PRINTABLE_CHARS.set('@');
+        for (int i = 'A'; i <= 'Z'; i++) {
+            PRINTABLE_CHARS.set(i);
+        }
+        PRINTABLE_CHARS.set('[');
+        PRINTABLE_CHARS.set('\\');
+        PRINTABLE_CHARS.set(']');
+        PRINTABLE_CHARS.set('^');
+        PRINTABLE_CHARS.set('`');
+        for (int i = 'a'; i <= 'z'; i++) {
+            PRINTABLE_CHARS.set(i);
+        }
+        PRINTABLE_CHARS.set('{');
+        PRINTABLE_CHARS.set('|');
+        PRINTABLE_CHARS.set('}');
+        PRINTABLE_CHARS.set('~');
+    }
+
+    private static byte BLANK = 32;
+
+    private static byte UNDERSCORE = 95;
+
+    private boolean encodeBlanks = false;
+
+    /**
+     * Default constructor.
+     */
+    public QCodec() {
+        super();
+    }
+
+    /**
+     * Constructor which allows for the selection of a default charset
+     * 
+     * @param charset
+     *                  the default string charset to use.
+     * 
+     * @see <a href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE character
+     *          encoding names</a>
+     */
+    public QCodec(final String charset) {
+        super();
+        this.charset = charset;
+    }
+
+    protected String getEncoding() {
+        return "Q";
+    }
+
+    protected byte[] doEncoding(byte[] bytes) throws EncoderException {
+        if (bytes == null) {
+            return null;
+        }
+        byte[] data = QuotedPrintableCodec.encodeQuotedPrintable(PRINTABLE_CHARS, bytes);
+        if (this.encodeBlanks) {
+            for (int i = 0; i < data.length; i++) {
+                if (data[i] == BLANK) {
+                    data[i] = UNDERSCORE;
+                }
+            }
+        }
+        return data;
+    }
+
+    protected byte[] doDecoding(byte[] bytes) throws DecoderException {
+        if (bytes == null) {
+            return null;
+        }
+        boolean hasUnderscores = false;
+        for (int i = 0; i < bytes.length; i++) {
+            if (bytes[i] == UNDERSCORE) {
+                hasUnderscores = true;
+                break;
+            }
+        }
+        if (hasUnderscores) {
+            byte[] tmp = new byte[bytes.length];
+            for (int i = 0; i < bytes.length; i++) {
+                byte b = bytes[i];
+                if (b != UNDERSCORE) {
+                    tmp[i] = b;
+                } else {
+                    tmp[i] = BLANK;
+                }
+            }
+            return QuotedPrintableCodec.decodeQuotedPrintable(tmp);
+        } 
+        return QuotedPrintableCodec.decodeQuotedPrintable(bytes);       
+    }
+
+    /**
+     * Encodes a string into its quoted-printable form using the specified charset. Unsafe characters are escaped.
+     * 
+     * @param pString
+     *                  string to convert to quoted-printable form
+     * @param charset
+     *                  the charset for pString
+     * @return quoted-printable string
+     * 
+     * @throws EncoderException
+     *                  thrown if a failure condition is encountered during the encoding process.
+     */
+    public String encode(final String pString, final String charset) throws EncoderException {
+        if (pString == null) {
+            return null;
+        }
+        try {
+            return encodeText(pString, charset);
+        } catch (UnsupportedEncodingException e) {
+            throw new EncoderException(e.getMessage());
+        }
+    }
+
+    /**
+     * Encodes a string into its quoted-printable form using the default charset. Unsafe characters are escaped.
+     * 
+     * @param pString
+     *                  string to convert to quoted-printable form
+     * @return quoted-printable string
+     * 
+     * @throws EncoderException
+     *                  thrown if a failure condition is encountered during the encoding process.
+     */
+    public String encode(String pString) throws EncoderException {
+        if (pString == null) {
+            return null;
+        }
+        return encode(pString, getDefaultCharset());
+    }
+
+    /**
+     * Decodes a quoted-printable string into its original form. Escaped characters are converted back to their original
+     * representation.
+     * 
+     * @param pString
+     *                  quoted-printable string to convert into its original form
+     * 
+     * @return original string
+     * 
+     * @throws DecoderException
+     *                  A decoder exception is thrown if a failure condition is encountered during the decode process.
+     */
+    public String decode(String pString) throws DecoderException {
+        if (pString == null) {
+            return null;
+        }
+        try {
+            return decodeText(pString);
+        } catch (UnsupportedEncodingException e) {
+            throw new DecoderException(e.getMessage());
+        }
+    }
+
+    /**
+     * Encodes an object into its quoted-printable form using the default charset. Unsafe characters are escaped.
+     * 
+     * @param pObject
+     *                  object to convert to quoted-printable form
+     * @return quoted-printable object
+     * 
+     * @throws EncoderException
+     *                  thrown if a failure condition is encountered during the encoding process.
+     */
+    public Object encode(Object pObject) throws EncoderException {
+        if (pObject == null) {
+            return null;
+        } else if (pObject instanceof String) {
+            return encode((String) pObject);
+        } else {
+            throw new EncoderException("Objects of type "
+                + pObject.getClass().getName()
+                + " cannot be encoded using Q codec");
+        }
+    }
+
+    /**
+     * Decodes a quoted-printable object into its original form. Escaped characters are converted back to their original
+     * representation.
+     * 
+     * @param pObject
+     *                  quoted-printable object to convert into its original form
+     * 
+     * @return original object
+     * 
+     * @throws DecoderException
+     *                  A decoder exception is thrown if a failure condition is encountered during the decode process.
+     */
+    public Object decode(Object pObject) throws DecoderException {
+        if (pObject == null) {
+            return null;
+        } else if (pObject instanceof String) {
+            return decode((String) pObject);
+        } else {
+            throw new DecoderException("Objects of type "
+                + pObject.getClass().getName()
+                + " cannot be decoded using Q codec");
+        }
+    }
+
+    /**
+     * The default charset used for string decoding and encoding.
+     * 
+     * @return the default string charset.
+     */
+    public String getDefaultCharset() {
+        return this.charset;
+    }
+
+    /**
+     * Tests if optional tranformation of SPACE characters is to be used
+     * 
+     * @return <code>true</code> if SPACE characters are to be transformed, <code>false</code> otherwise
+     */
+    public boolean isEncodeBlanks() {
+        return this.encodeBlanks;
+    }
+
+    /**
+     * Defines whether optional tranformation of SPACE characters is to be used
+     * 
+     * @param b
+     *                  <code>true</code> if SPACE characters are to be transformed, <code>false</code> otherwise
+     */
+    public void setEncodeBlanks(boolean b) {
+        this.encodeBlanks = b;
+    }
+}
diff --git a/src/org/apache/commons/codec/net/QuotedPrintableCodec.java b/src/org/apache/commons/codec/net/QuotedPrintableCodec.java
new file mode 100644
index 0000000..c2fcd27
--- /dev/null
+++ b/src/org/apache/commons/codec/net/QuotedPrintableCodec.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec.net;
+
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.BitSet;
+import org.apache.commons.codec.BinaryDecoder;
+import org.apache.commons.codec.BinaryEncoder;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.StringDecoder;
+import org.apache.commons.codec.StringEncoder;
+
+/**
+ * <p>
+ * Codec for the Quoted-Printable section of <a href="http://www.ietf.org/rfc/rfc1521.txt">RFC 1521 </a>.
+ * </p>
+ * <p>
+ * The Quoted-Printable encoding is intended to represent data that largely consists of octets that correspond to
+ * printable characters in the ASCII character set. It encodes the data in such a way that the resulting octets are
+ * unlikely to be modified by mail transport. If the data being encoded are mostly ASCII text, the encoded form of the
+ * data remains largely recognizable by humans. A body which is entirely ASCII may also be encoded in Quoted-Printable
+ * to ensure the integrity of the data should the message pass through a character- translating, and/or line-wrapping
+ * gateway.
+ * </p>
+ * 
+ * <p>
+ * Note:
+ * </p>
+ * <p>
+ * Rules #3, #4, and #5 of the quoted-printable spec are not implemented yet because the complete quoted-printable spec
+ * does not lend itself well into the byte[] oriented codec framework. Complete the codec once the steamable codec
+ * framework is ready. The motivation behind providing the codec in a partial form is that it can already come in handy
+ * for those applications that do not require quoted-printable line formatting (rules #3, #4, #5), for instance Q codec.
+ * </p>
+ * 
+ * @see <a href="http://www.ietf.org/rfc/rfc1521.txt"> RFC 1521 MIME (Multipurpose Internet Mail Extensions) Part One:
+ *          Mechanisms for Specifying and Describing the Format of Internet Message Bodies </a>
+ * 
+ * @author Apache Software Foundation
+ * @since 1.3
+ * @version $Id: QuotedPrintableCodec.java,v 1.7 2004/04/09 22:21:07 ggregory Exp $
+ */
+public class QuotedPrintableCodec implements BinaryEncoder, BinaryDecoder, StringEncoder, StringDecoder {
+    /**
+     * The default charset used for string decoding and encoding.
+     */
+    private String charset = StringEncodings.UTF8;
+
+    /**
+     * BitSet of printable characters as defined in RFC 1521.
+     */
+    private static final BitSet PRINTABLE_CHARS = new BitSet(256);
+
+    private static byte ESCAPE_CHAR = '=';
+
+    private static byte TAB = 9;
+
+    private static byte SPACE = 32;
+    // Static initializer for printable chars collection
+    static {
+        // alpha characters
+        for (int i = 33; i <= 60; i++) {
+            PRINTABLE_CHARS.set(i);
+        }
+        for (int i = 62; i <= 126; i++) {
+            PRINTABLE_CHARS.set(i);
+        }
+        PRINTABLE_CHARS.set(TAB);
+        PRINTABLE_CHARS.set(SPACE);
+    }
+
+    /**
+     * Default constructor.
+     */
+    public QuotedPrintableCodec() {
+        super();
+    }
+
+    /**
+     * Constructor which allows for the selection of a default charset
+     * 
+     * @param charset
+     *                  the default string charset to use.
+     */
+    public QuotedPrintableCodec(String charset) {
+        super();
+        this.charset = charset;
+    }
+
+    /**
+     * Encodes byte into its quoted-printable representation.
+     * 
+     * @param b
+     *                  byte to encode
+     * @param buffer
+     *                  the buffer to write to
+     */
+    private static final void encodeQuotedPrintable(int b, ByteArrayOutputStream buffer) {
+        buffer.write(ESCAPE_CHAR);
+        char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16));
+        char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16));
+        buffer.write(hex1);
+        buffer.write(hex2);
+    }
+
+    /**
+     * Encodes an array of bytes into an array of quoted-printable 7-bit characters. Unsafe characters are escaped.
+     * 
+     * <p>
+     * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in
+     * RFC 1521 and is suitable for encoding binary data and unformatted text.
+     * </p>
+     * 
+     * @param printable
+     *                  bitset of characters deemed quoted-printable
+     * @param bytes
+     *                  array of bytes to be encoded
+     * @return array of bytes containing quoted-printable data
+     */
+    public static final byte[] encodeQuotedPrintable(BitSet printable, byte[] bytes) {
+        if (bytes == null) {
+            return null;
+        }
+        if (printable == null) {
+            printable = PRINTABLE_CHARS;
+        }
+        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+        for (int i = 0; i < bytes.length; i++) {
+            int b = bytes[i];
+            if (b < 0) {
+                b = 256 + b;
+            }
+            if (printable.get(b)) {
+                buffer.write(b);
+            } else {
+                encodeQuotedPrintable(b, buffer);
+            }
+        }
+        return buffer.toByteArray();
+    }
+
+    /**
+     * Decodes an array quoted-printable characters into an array of original bytes. Escaped characters are converted
+     * back to their original representation.
+     * 
+     * <p>
+     * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in
+     * RFC 1521.
+     * </p>
+     * 
+     * @param bytes
+     *                  array of quoted-printable characters
+     * @return array of original bytes
+     * @throws DecoderException
+     *                  Thrown if quoted-printable decoding is unsuccessful
+     */
+    public static final byte[] decodeQuotedPrintable(byte[] bytes) throws DecoderException {
+        if (bytes == null) {
+            return null;
+        }
+        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+        for (int i = 0; i < bytes.length; i++) {
+            int b = bytes[i];
+            if (b == ESCAPE_CHAR) {
+                try {
+                    int u = Character.digit((char) bytes[++i], 16);
+                    int l = Character.digit((char) bytes[++i], 16);
+                    if (u == -1 || l == -1) {
+                        throw new DecoderException("Invalid quoted-printable encoding");
+                    }
+                    buffer.write((char) ((u << 4) + l));
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    throw new DecoderException("Invalid quoted-printable encoding");
+                }
+            } else {
+                buffer.write(b);
+            }
+        }
+        return buffer.toByteArray();
+    }
+
+    /**
+     * Encodes an array of bytes into an array of quoted-printable 7-bit characters. Unsafe characters are escaped.
+     * 
+     * <p>
+     * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in
+     * RFC 1521 and is suitable for encoding binary data and unformatted text.
+     * </p>
+     * 
+     * @param bytes
+     *                  array of bytes to be encoded
+     * @return array of bytes containing quoted-printable data
+     */
+    public byte[] encode(byte[] bytes) {
+        return encodeQuotedPrintable(PRINTABLE_CHARS, bytes);
+    }
+
+    /**
+     * Decodes an array of quoted-printable characters into an array of original bytes. Escaped characters are converted
+     * back to their original representation.
+     * 
+     * <p>
+     * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in
+     * RFC 1521.
+     * </p>
+     * 
+     * @param bytes
+     *                  array of quoted-printable characters
+     * @return array of original bytes
+     * @throws DecoderException
+     *                  Thrown if quoted-printable decoding is unsuccessful
+     */
+    public byte[] decode(byte[] bytes) throws DecoderException {
+        return decodeQuotedPrintable(bytes);
+    }
+
+    /**
+     * Encodes a string into its quoted-printable form using the default string charset. Unsafe characters are escaped.
+     * 
+     * <p>
+     * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in
+     * RFC 1521 and is suitable for encoding binary data.
+     * </p>
+     * 
+     * @param pString
+     *                  string to convert to quoted-printable form
+     * @return quoted-printable string
+     * 
+     * @throws EncoderException
+     *                  Thrown if quoted-printable encoding is unsuccessful
+     * 
+     * @see #getDefaultCharset()
+     */
+    public String encode(String pString) throws EncoderException {
+        if (pString == null) {
+            return null;
+        }
+        try {
+            return encode(pString, getDefaultCharset());
+        } catch (UnsupportedEncodingException e) {
+            throw new EncoderException(e.getMessage());
+        }
+    }
+
+    /**
+     * Decodes a quoted-printable string into its original form using the specified string charset. Escaped characters
+     * are converted back to their original representation.
+     * 
+     * @param pString
+     *                  quoted-printable string to convert into its original form
+     * @param charset
+     *                  the original string charset
+     * @return original string
+     * @throws DecoderException
+     *                  Thrown if quoted-printable decoding is unsuccessful
+     * @throws UnsupportedEncodingException
+     *                  Thrown if charset is not supported
+     */
+    public String decode(String pString, String charset) throws DecoderException, UnsupportedEncodingException {
+        if (pString == null) {
+            return null;
+        }
+        return new String(decode(pString.getBytes(StringEncodings.US_ASCII)), charset);
+    }
+
+    /**
+     * Decodes a quoted-printable string into its original form using the default string charset. Escaped characters are
+     * converted back to their original representation.
+     * 
+     * @param pString
+     *                  quoted-printable string to convert into its original form
+     * @return original string
+     * @throws DecoderException
+     *                  Thrown if quoted-printable decoding is unsuccessful
+     * @throws UnsupportedEncodingException
+     *                  Thrown if charset is not supported
+     * @see #getDefaultCharset()
+     */
+    public String decode(String pString) throws DecoderException {
+        if (pString == null) {
+            return null;
+        }
+        try {
+            return decode(pString, getDefaultCharset());
+        } catch (UnsupportedEncodingException e) {
+            throw new DecoderException(e.getMessage());
+        }
+    }
+
+    /**
+     * Encodes an object into its quoted-printable safe form. Unsafe characters are escaped.
+     * 
+     * @param pObject
+     *                  string to convert to a quoted-printable form
+     * @return quoted-printable object
+     * @throws EncoderException
+     *                  Thrown if quoted-printable encoding is not applicable to objects of this type or if encoding is
+     *                  unsuccessful
+     */
+    public Object encode(Object pObject) throws EncoderException {
+        if (pObject == null) {
+            return null;
+        } else if (pObject instanceof byte[]) {
+            return encode((byte[]) pObject);
+        } else if (pObject instanceof String) {
+            return encode((String) pObject);
+        } else {
+            throw new EncoderException("Objects of type "
+                + pObject.getClass().getName()
+                + " cannot be quoted-printable encoded");
+        }
+    }
+
+    /**
+     * Decodes a quoted-printable object into its original form. Escaped characters are converted back to their original
+     * representation.
+     * 
+     * @param pObject
+     *                  quoted-printable object to convert into its original form
+     * @return original object
+     * @throws DecoderException
+     *                  Thrown if quoted-printable decoding is not applicable to objects of this type if decoding is
+     *                  unsuccessful
+     */
+    public Object decode(Object pObject) throws DecoderException {
+        if (pObject == null) {
+            return null;
+        } else if (pObject instanceof byte[]) {
+            return decode((byte[]) pObject);
+        } else if (pObject instanceof String) {
+            return decode((String) pObject);
+        } else {
+            throw new DecoderException("Objects of type "
+                + pObject.getClass().getName()
+                + " cannot be quoted-printable decoded");
+        }
+    }
+
+    /**
+     * Returns the default charset used for string decoding and encoding.
+     * 
+     * @return the default string charset.
+     */
+    public String getDefaultCharset() {
+        return this.charset;
+    }
+
+    /**
+     * Encodes a string into its quoted-printable form using the specified charset. Unsafe characters are escaped.
+     * 
+     * <p>
+     * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in
+     * RFC 1521 and is suitable for encoding binary data and unformatted text.
+     * </p>
+     * 
+     * @param pString
+     *                  string to convert to quoted-printable form
+     * @param charset
+     *                  the charset for pString
+     * @return quoted-printable string
+     * 
+     * @throws UnsupportedEncodingException
+     *                  Thrown if the charset is not supported
+     */
+    public String encode(String pString, String charset) throws UnsupportedEncodingException {
+        if (pString == null) {
+            return null;
+        }
+        return new String(encode(pString.getBytes(charset)), StringEncodings.US_ASCII);
+    }
+}
diff --git a/src/org/apache/commons/codec/net/RFC1522Codec.java b/src/org/apache/commons/codec/net/RFC1522Codec.java
new file mode 100644
index 0000000..0acf921
--- /dev/null
+++ b/src/org/apache/commons/codec/net/RFC1522Codec.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec.net;
+
+import java.io.UnsupportedEncodingException;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+
+/**
+ * <p>
+ * Implements methods common to all codecs defined in RFC 1522.
+ * </p>
+ * 
+ * <p>
+ * <a href="http://www.ietf.org/rfc/rfc1522.txt">RFC 1522</a> 
+ * describes techniques to allow the encoding of non-ASCII text in 
+ * various portions of a RFC 822 [2] message header, in a manner which
+ * is unlikely to confuse existing message handling software.
+ * </p>
+
+ * @see <a href="http://www.ietf.org/rfc/rfc1522.txt">
+ * MIME (Multipurpose Internet Mail Extensions) Part Two:
+ * Message Header Extensions for Non-ASCII Text</a>
+ * </p>
+ * 
+ * @author Apache Software Foundation
+ * @since 1.3
+ * @version $Id: RFC1522Codec.java,v 1.2 2004/04/09 22:21:43 ggregory Exp $
+ */
+abstract class RFC1522Codec {
+    
+    /**
+     * Applies an RFC 1522 compliant encoding scheme to the given string of text with the 
+     * given charset. This method constructs the "encoded-word" header common to all the 
+     * RFC 1522 codecs and then invokes {@link #doEncoding(byte [])} method of a concrete 
+     * class to perform the specific enconding.
+     * 
+     * @param text a string to encode
+     * @param charset a charset to be used
+     * 
+     * @return RFC 1522 compliant "encoded-word"
+     * 
+     * @throws EncoderException thrown if there is an error conidition during the Encoding 
+     *  process.
+     * @throws UnsupportedEncodingException thrown if charset is not supported 
+     * 
+     * @see <a href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE character
+     *          encoding names</a>
+     */
+    protected String encodeText(final String text, final String charset)
+     throws EncoderException, UnsupportedEncodingException  
+    {
+        if (text == null) {
+            return null;
+        }
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("=?"); 
+        buffer.append(charset); 
+        buffer.append('?'); 
+        buffer.append(getEncoding()); 
+        buffer.append('?');
+        byte [] rawdata = doEncoding(text.getBytes(charset)); 
+        buffer.append(new String(rawdata, StringEncodings.US_ASCII));
+        buffer.append("?="); 
+        return buffer.toString();
+    }
+    
+    /**
+     * Applies an RFC 1522 compliant decoding scheme to the given string of text. This method 
+     * processes the "encoded-word" header common to all the RFC 1522 codecs and then invokes 
+     * {@link #doEncoding(byte [])} method of a concrete class to perform the specific deconding.
+     * 
+     * @param text a string to decode
+     * 
+     * @throws DecoderException thrown if there is an error conidition during the Decoding 
+     *  process.
+     * @throws UnsupportedEncodingException thrown if charset specified in the "encoded-word" 
+     *  header is not supported 
+     */
+    protected String decodeText(final String text)
+     throws DecoderException, UnsupportedEncodingException  
+    {
+        if (text == null) {
+            return null;
+        }
+        if ((!text.startsWith("=?")) || (!text.endsWith("?="))) {
+            throw new DecoderException("RFC 1522 violation: malformed encoded content");
+        }
+        int termnator = text.length() - 2;
+        int from = 2;
+        int to = text.indexOf("?", from);
+        if ((to == -1) || (to == termnator)) {
+            throw new DecoderException("RFC 1522 violation: charset token not found");
+        }
+        String charset = text.substring(from, to);
+        if (charset.equals("")) {
+            throw new DecoderException("RFC 1522 violation: charset not specified");
+        }
+        from = to + 1;
+        to = text.indexOf("?", from);
+        if ((to == -1) || (to == termnator)) {
+            throw new DecoderException("RFC 1522 violation: encoding token not found");
+        }
+        String encoding = text.substring(from, to);
+        if (!getEncoding().equalsIgnoreCase(encoding)) {
+            throw new DecoderException("This codec cannot decode " + 
+                encoding + " encoded content");
+        }
+        from = to + 1;
+        to = text.indexOf("?", from);
+        byte[] data = text.substring(from, to).getBytes(StringEncodings.US_ASCII);
+        data = doDecoding(data); 
+        return new String(data, charset);
+    }
+
+    /**
+     * Returns the codec name (referred to as encoding in the RFC 1522)
+     * 
+     * @return name of the codec
+     */    
+    protected abstract String getEncoding();
+
+    /**
+     * Encodes an array of bytes using the defined encoding scheme
+     * 
+     * @param bytes Data to be encoded
+     *
+     * @return A byte array containing the encoded data
+     * 
+     * @throws EncoderException thrown if the Encoder encounters a failure condition 
+     *  during the encoding process.
+     */    
+    protected abstract byte[] doEncoding(byte[] bytes) throws EncoderException;
+
+    /**
+     * Decodes an array of bytes using the defined encoding scheme
+     * 
+     * @param bytes Data to be decoded
+     *
+     * @return a byte array that contains decoded data
+     * 
+     * @throws DecoderException A decoder exception is thrown if a Decoder encounters a 
+     *  failure condition during the decode process.
+     */    
+    protected abstract byte[] doDecoding(byte[] bytes) throws DecoderException;
+}
diff --git a/src/org/apache/commons/codec/net/StringEncodings.java b/src/org/apache/commons/codec/net/StringEncodings.java
new file mode 100644
index 0000000..e7f6bb8
--- /dev/null
+++ b/src/org/apache/commons/codec/net/StringEncodings.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec.net;
+
+/**
+ * String encodings used in this package.
+ * 
+ * @author Apache Software Foundation
+ * @since 1.3
+ * @version $Id: StringEncodings.java,v 1.2 2004/04/09 22:21:07 ggregory Exp $
+ */
+interface StringEncodings {
+    /**
+     * <p>
+     * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set.
+     * </p>
+     * <p>
+     * Every implementation of the Java platform is required to support this character encoding.
+     * </p>
+     * 
+     * @see <a href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE character
+     *          encoding names</a>
+     */
+    String US_ASCII = "US-ASCII";
+
+    /**
+     * <p>
+     * Eight-bit Unicode Transformation Format.
+     * </p>
+     * <p>
+     * Every implementation of the Java platform is required to support this character encoding.
+     * </p>
+     * 
+     * @see <a href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE character
+     *          encoding names</a>
+     */
+    String UTF8 = "UTF-8";
+}
diff --git a/src/org/apache/commons/codec/net/URLCodec.java b/src/org/apache/commons/codec/net/URLCodec.java
new file mode 100644
index 0000000..1bc3507
--- /dev/null
+++ b/src/org/apache/commons/codec/net/URLCodec.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.codec.net;
+
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.BitSet;
+
+import org.apache.commons.codec.BinaryDecoder;
+import org.apache.commons.codec.BinaryEncoder;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.StringDecoder;
+import org.apache.commons.codec.StringEncoder;
+
+/**
+ * <p>Implements the 'www-form-urlencoded' encoding scheme, 
+ * also misleadingly known as URL encoding.</p>
+ *  
+ * <p>For more detailed information please refer to 
+ * <a href="http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1">
+ * Chapter 17.13.4 'Form content types'</a> of the 
+ * <a href="http://www.w3.org/TR/html4/">HTML 4.01 Specification<a></p>
+ * 
+ * <p> 
+ * This codec is meant to be a replacement for standard Java classes
+ * {@link java.net.URLEncoder} and {@link java.net.URLDecoder} 
+ * on older Java platforms, as these classes in Java versions below 
+ * 1.4 rely on the platform's default charset encoding.
+ * </p>
+ * 
+ * @author Apache Software Foundation
+ * @since 1.2
+ * @version $Id: URLCodec.java,v 1.19 2004/03/29 07:59:00 ggregory Exp $
+ */
+public class URLCodec implements BinaryEncoder, BinaryDecoder, StringEncoder, StringDecoder {
+    
+    /**
+     * The default charset used for string decoding and encoding.
+     */
+    protected String charset = StringEncodings.UTF8;
+    
+    protected static byte ESCAPE_CHAR = '%';
+    /**
+     * BitSet of www-form-url safe characters.
+     */
+    protected static final BitSet WWW_FORM_URL = new BitSet(256);
+    
+    // Static initializer for www_form_url
+    static {
+        // alpha characters
+        for (int i = 'a'; i <= 'z'; i++) {
+            WWW_FORM_URL.set(i);
+        }
+        for (int i = 'A'; i <= 'Z'; i++) {
+            WWW_FORM_URL.set(i);
+        }
+        // numeric characters
+        for (int i = '0'; i <= '9'; i++) {
+            WWW_FORM_URL.set(i);
+        }
+        // special chars
+        WWW_FORM_URL.set('-');
+        WWW_FORM_URL.set('_');
+        WWW_FORM_URL.set('.');
+        WWW_FORM_URL.set('*');
+        // blank to be replaced with +
+        WWW_FORM_URL.set(' ');
+    }
+
+
+    /**
+     * Default constructor.
+     */
+    public URLCodec() {
+        super();
+    }
+
+    /**
+     * Constructor which allows for the selection of a default charset
+     * 
+     * @param charset the default string charset to use.
+     */
+    public URLCodec(String charset) {
+        super();
+        this.charset = charset;
+    }
+
+    /**
+     * Encodes an array of bytes into an array of URL safe 7-bit 
+     * characters. Unsafe characters are escaped.
+     *
+     * @param urlsafe bitset of characters deemed URL safe
+     * @param bytes array of bytes to convert to URL safe characters
+     * @return array of bytes containing URL safe characters
+     */
+    public static final byte[] encodeUrl(BitSet urlsafe, byte[] bytes) 
+    {
+        if (bytes == null) {
+            return null;
+        }
+        if (urlsafe == null) {
+            urlsafe = WWW_FORM_URL;
+        }
+        
+        ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 
+        for (int i = 0; i < bytes.length; i++) {
+            int b = bytes[i];
+            if (b < 0) {
+                b = 256 + b;
+            }
+            if (urlsafe.get(b)) {
+                if (b == ' ') {
+                    b = '+';
+                }
+                buffer.write(b);
+            } else {
+                buffer.write('%');
+                char hex1 = Character.toUpperCase(
+                  Character.forDigit((b >> 4) & 0xF, 16));
+                char hex2 = Character.toUpperCase(
+                  Character.forDigit(b & 0xF, 16));
+                buffer.write(hex1);
+                buffer.write(hex2);
+            }
+        }
+        return buffer.toByteArray(); 
+    }
+
+
+    /**
+     * Decodes an array of URL safe 7-bit characters into an array of 
+     * original bytes. Escaped characters are converted back to their 
+     * original representation.
+     *
+     * @param bytes array of URL safe characters
+     * @return array of original bytes 
+     * @throws DecoderException Thrown if URL decoding is unsuccessful
+     */
+    public static final byte[] decodeUrl(byte[] bytes) 
+         throws DecoderException
+    {
+        if (bytes == null) {
+            return null;
+        }
+        ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 
+        for (int i = 0; i < bytes.length; i++) {
+            int b = bytes[i];
+            if (b == '+') {
+                buffer.write(' ');
+            } else if (b == '%') {
+                try {
+                    int u = Character.digit((char)bytes[++i], 16);
+                    int l = Character.digit((char)bytes[++i], 16);
+                    if (u == -1 || l == -1) {
+                        throw new DecoderException("Invalid URL encoding");
+                    }
+                    buffer.write((char)((u << 4) + l));
+                } catch(ArrayIndexOutOfBoundsException e) {
+                    throw new DecoderException("Invalid URL encoding");
+                }
+            } else {
+                buffer.write(b);
+            }
+        }
+        return buffer.toByteArray(); 
+    }
+
+
+    /**
+     * Encodes an array of bytes into an array of URL safe 7-bit 
+     * characters. Unsafe characters are escaped.
+     *
+     * @param bytes array of bytes to convert to URL safe characters
+     * @return array of bytes containing URL safe characters
+     */
+    public byte[] encode(byte[] bytes) {
+        return encodeUrl(WWW_FORM_URL, bytes);
+    }
+
+
+    /**
+     * Decodes an array of URL safe 7-bit characters into an array of 
+     * original bytes. Escaped characters are converted back to their 
+     * original representation.
+     *
+     * @param bytes array of URL safe characters
+     * @return array of original bytes 
+     * @throws DecoderException Thrown if URL decoding is unsuccessful
+     */
+    public byte[] decode(byte[] bytes) throws DecoderException {
+        return decodeUrl(bytes);
+    }
+
+
+    /**
+     * Encodes a string into its URL safe form using the specified
+     * string charset. Unsafe characters are escaped.
+     *
+     * @param pString string to convert to a URL safe form
+     * @param charset the charset for pString
+     * @return URL safe string
+     * @throws UnsupportedEncodingException Thrown if charset is not
+     *                                      supported 
+     */
+    public String encode(String pString, String charset) 
+        throws UnsupportedEncodingException  
+    {
+        if (pString == null) {
+            return null;
+        }
+        return new String(encode(pString.getBytes(charset)), StringEncodings.US_ASCII);
+    }
+
+
+    /**
+     * Encodes a string into its URL safe form using the default string 
+     * charset. Unsafe characters are escaped.
+     *
+     * @param pString string to convert to a URL safe form
+     * @return URL safe string
+     * @throws EncoderException Thrown if URL encoding is unsuccessful
+     * 
+     * @see #getDefaultCharset()
+     */
+    public String encode(String pString) throws EncoderException {
+        if (pString == null) {
+            return null;
+        }
+        try {
+            return encode(pString, getDefaultCharset());
+        } catch(UnsupportedEncodingException e) {
+            throw new EncoderException(e.getMessage());
+        }
+    }
+
+
+    /**
+     * Decodes a URL safe string into its original form using the 
+     * specified encoding. Escaped characters are converted back 
+     * to their original representation.
+     *
+     * @param pString URL safe string to convert into its original form
+     * @param charset the original string charset
+     * @return original string 
+     * @throws DecoderException Thrown if URL decoding is unsuccessful
+     * @throws UnsupportedEncodingException Thrown if charset is not
+     *                                      supported 
+     */
+    public String decode(String pString, String charset) 
+        throws DecoderException, UnsupportedEncodingException 
+    {
+        if (pString == null) {
+            return null;
+        }
+        return new String(decode(pString.getBytes(StringEncodings.US_ASCII)), charset);
+    }
+
+
+    /**
+     * Decodes a URL safe string into its original form using the default
+     * string charset. Escaped characters are converted back to their 
+     * original representation.
+     *
+     * @param pString URL safe string to convert into its original form
+     * @return original string 
+     * @throws DecoderException Thrown if URL decoding is unsuccessful
+     * 
+     * @see #getDefaultCharset()
+     */
+    public String decode(String pString) throws DecoderException {
+        if (pString == null) {
+            return null;
+        }
+        try {
+            return decode(pString, getDefaultCharset());
+        } catch(UnsupportedEncodingException e) {
+            throw new DecoderException(e.getMessage());
+        }
+    }
+
+    /**
+     * Encodes an object into its URL safe form. Unsafe characters are 
+     * escaped.
+     *
+     * @param pObject string to convert to a URL safe form
+     * @return URL safe object
+     * @throws EncoderException Thrown if URL encoding is not 
+     *                          applicable to objects of this type or
+     *                          if encoding is unsuccessful
+     */
+    public Object encode(Object pObject) throws EncoderException {
+        if (pObject == null) {
+            return null;
+        } else if (pObject instanceof byte[]) {
+            return encode((byte[])pObject);
+        } else if (pObject instanceof String) {
+            return encode((String)pObject);
+        } else {
+            throw new EncoderException("Objects of type " +
+                pObject.getClass().getName() + " cannot be URL encoded"); 
+              
+        }
+    }
+
+    /**
+     * Decodes a URL safe object into its original form. Escaped 
+     * characters are converted back to their original representation.
+     *
+     * @param pObject URL safe object to convert into its original form
+     * @return original object 
+     * @throws DecoderException Thrown if URL decoding is not 
+     *                          applicable to objects of this type
+     *                          if decoding is unsuccessful
+     */
+    public Object decode(Object pObject) throws DecoderException {
+        if (pObject == null) {
+            return null;
+        } else if (pObject instanceof byte[]) {
+            return decode((byte[])pObject);
+        } else if (pObject instanceof String) {
+            return decode((String)pObject);
+        } else {
+            throw new DecoderException("Objects of type " +
+                pObject.getClass().getName() + " cannot be URL decoded"); 
+              
+        }
+    }
+
+    /**
+     * The <code>String</code> encoding used for decoding and encoding.
+     *
+     * @return Returns the encoding.
+     * 
+     * @deprecated use #getDefaultCharset()
+     */
+    public String getEncoding() {
+        return this.charset;
+    }
+
+    /**
+     * The default charset used for string decoding and encoding.
+     *
+     * @return the default string charset.
+     */
+    public String getDefaultCharset() {
+        return this.charset;
+    }
+
+}
diff --git a/src/org/apache/commons/codec/net/package.html b/src/org/apache/commons/codec/net/package.html
new file mode 100644
index 0000000..4607c57
--- /dev/null
+++ b/src/org/apache/commons/codec/net/package.html
@@ -0,0 +1,22 @@
+<!--
+Copyright 2003-2004 The Apache Software Foundation.
+ 
+Licensed 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.
+-->
+<html>
+ <body>
+  <p>
+  Network related encoding and decoding.
+  </p>
+ </body>
+</html>
diff --git a/src/org/apache/commons/codec/overview.html b/src/org/apache/commons/codec/overview.html
new file mode 100644
index 0000000..6b6f6c9
--- /dev/null
+++ b/src/org/apache/commons/codec/overview.html
@@ -0,0 +1,28 @@
+<!--
+Copyright 2003-2004 The Apache Software Foundation.
+ 
+Licensed 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.
+-->
+<!-- $Id: overview.html,v 1.6 2004/05/17 17:06:10 ggregory Exp $ -->
+<html>
+<body>
+<p>
+This document is the API specification for the Apache Jakarta Commons Codec Library, version 1.3.
+</p>
+<p>
+This library requires a JRE version of 1.2.2 or greater.
+The hypertext links originating from this document point to Sun's version 1.3 API as the 1.2.2 API documentation 
+is no longer on-line.
+</p>
+</body>
+</html>
diff --git a/src/org/apache/commons/codec/package.html b/src/org/apache/commons/codec/package.html
new file mode 100644
index 0000000..b7ccf03
--- /dev/null
+++ b/src/org/apache/commons/codec/package.html
@@ -0,0 +1,99 @@
+<!--
+Copyright 2003-2004 The Apache Software Foundation.
+ 
+Licensed 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.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+  </head>
+  <body>
+    <p>A small set of interfaces used by
+    the various implementations in the sub-packages.</p>
+
+    <p>Definitive implementations of commonly used encoders and decoders.</p>
+
+    <p>Codec is currently comprised of a modest set of utilities and a
+    simple framework for String encoding and decoding in three categories:
+    Binary Encoders, Language Encoders, and Network Encoders. </p>
+
+    <h4><a name="Common Encoders">Binary Encoders</a></h4>
+
+    <table border="1" width="100%" cellspacing="2" cellpadding="3">
+  	  <tbody>
+        <tr>
+          <td> 
+            <a href="binary/Base64.html">
+              org.apache.commons.codec.binary.Base64</a>
+          </td>
+          <td> 
+            Provides Base64 content-transfer-encoding as defined in 
+            <a href="http://www.ietf.org/rfc/rfc2045.txt"> RFC 2045</a> 
+          </td>
+          <td>Production</td>
+        </tr>
+        <tr>
+          <td>
+           <a href="binary/Hex.html">
+             org.apache.commons.codec.binary.Hex</a>
+          </td>
+          <td> 
+            Converts an array of bytes into an array of characters
+            representing the hexidecimal values of each byte in order 
+          </td>
+          <td>Production</td>
+        </tr>
+      </tbody>
+    </table>
+    <h4> 
+      <a name="Language Encoders">Language Encoders</a> 
+    </h4>
+    <p> 
+      Codec contains a number of commonly used language and phonetic
+      encoders
+    </p>
+    <table border="1" width="100%" cellspacing="2" cellpadding="3">
+      <tbody>
+        <tr>
+          <td> 
+            <a href="#">org.apache.commons.codec.language.Soundex</a>
+          </td>
+          <td>Implementation of the Soundex algorithm.</td>
+          <td>Production</td>
+        </tr>
+        <tr>
+          <td> 
+            <a href="#">org.apache.commons.codec.language.Metaphone</a>
+          </td>
+          <td>Implementation of the Metaphone algorithm.</td>
+          <td>Production</td>
+        </tr>
+      </tbody>
+    </table>
+    <h4><a name="Network_Encoders">Network Encoders</a></h4>
+    <h4> </h4>
+    <p> Codec contains network related encoders </p>
+    <table border="1" width="100%" cellspacing="2" cellpadding="3">
+      <tbody>
+        <tr>
+          <td> 
+            <a href="#">org.apache.commons.codec.net.URLCodec</a>
+          </td>
+          <td>Implements the 'www-form-urlencoded' encoding scheme.</td>
+          <td>Production</td>
+        </tr>
+      </tbody>
+    </table>
+    <br>
+  </body>
+</html>
diff --git a/src/org/apache/commons/logging/Log.java b/src/org/apache/commons/logging/Log.java
new file mode 100644
index 0000000..9203f3f
--- /dev/null
+++ b/src/org/apache/commons/logging/Log.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.logging;
+
+/**
+ * <p>A simple logging interface abstracting logging APIs.  In order to be
+ * instantiated successfully by {@link LogFactory}, classes that implement
+ * this interface must have a constructor that takes a single String
+ * parameter representing the "name" of this Log.</p>
+ *
+ * <p> The six logging levels used by <code>Log</code> are (in order):
+ * <ol>
+ * <li>trace (the least serious)</li>
+ * <li>debug</li>
+ * <li>info</li>
+ * <li>warn</li>
+ * <li>error</li>
+ * <li>fatal (the most serious)</li>
+ * </ol>
+ * The mapping of these log levels to the concepts used by the underlying
+ * logging system is implementation dependent.
+ * The implemention should ensure, though, that this ordering behaves
+ * as expected.</p>
+ *
+ * <p>Performance is often a logging concern.
+ * By examining the appropriate property,
+ * a component can avoid expensive operations (producing information
+ * to be logged).</p>
+ *
+ * <p> For example,
+ * <code><pre>
+ *    if (log.isDebugEnabled()) {
+ *        ... do something expensive ...
+ *        log.debug(theResult);
+ *    }
+ * </pre></code>
+ * </p>
+ *
+ * <p>Configuration of the underlying logging system will generally be done
+ * external to the Logging APIs, through whatever mechanism is supported by
+ * that system.</p>
+ *
+ * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
+ * @author Rod Waldhoff
+ * @version $Id: Log.java 381838 2006-02-28 23:57:11Z skitching $
+ */
+public interface Log {
+
+
+    // ----------------------------------------------------- Logging Properties
+
+
+    /**
+     * <p> Is debug logging currently enabled? </p>
+     *
+     * <p> Call this method to prevent having to perform expensive operations
+     * (for example, <code>String</code> concatenation)
+     * when the log level is more than debug. </p>
+     *
+     * @return true if debug is enabled in the underlying logger.
+     */
+    public boolean isDebugEnabled();
+
+
+    /**
+     * <p> Is error logging currently enabled? </p>
+     *
+     * <p> Call this method to prevent having to perform expensive operations
+     * (for example, <code>String</code> concatenation)
+     * when the log level is more than error. </p>
+     *
+     * @return true if error is enabled in the underlying logger.
+     */
+    public boolean isErrorEnabled();
+
+
+    /**
+     * <p> Is fatal logging currently enabled? </p>
+     *
+     * <p> Call this method to prevent having to perform expensive operations
+     * (for example, <code>String</code> concatenation)
+     * when the log level is more than fatal. </p>
+     *
+     * @return true if fatal is enabled in the underlying logger.
+     */
+    public boolean isFatalEnabled();
+
+
+    /**
+     * <p> Is info logging currently enabled? </p>
+     *
+     * <p> Call this method to prevent having to perform expensive operations
+     * (for example, <code>String</code> concatenation)
+     * when the log level is more than info. </p>
+     *
+     * @return true if info is enabled in the underlying logger.
+     */
+    public boolean isInfoEnabled();
+
+
+    /**
+     * <p> Is trace logging currently enabled? </p>
+     *
+     * <p> Call this method to prevent having to perform expensive operations
+     * (for example, <code>String</code> concatenation)
+     * when the log level is more than trace. </p>
+     *
+     * @return true if trace is enabled in the underlying logger.
+     */
+    public boolean isTraceEnabled();
+
+
+    /**
+     * <p> Is warn logging currently enabled? </p>
+     *
+     * <p> Call this method to prevent having to perform expensive operations
+     * (for example, <code>String</code> concatenation)
+     * when the log level is more than warn. </p>
+     *
+     * @return true if warn is enabled in the underlying logger.
+     */
+    public boolean isWarnEnabled();
+
+
+    // -------------------------------------------------------- Logging Methods
+
+
+    /**
+     * <p> Log a message with trace log level. </p>
+     *
+     * @param message log this message
+     */
+    public void trace(Object message);
+
+
+    /**
+     * <p> Log an error with trace log level. </p>
+     *
+     * @param message log this message
+     * @param t log this cause
+     */
+    public void trace(Object message, Throwable t);
+
+
+    /**
+     * <p> Log a message with debug log level. </p>
+     *
+     * @param message log this message
+     */
+    public void debug(Object message);
+
+
+    /**
+     * <p> Log an error with debug log level. </p>
+     *
+     * @param message log this message
+     * @param t log this cause
+     */
+    public void debug(Object message, Throwable t);
+
+
+    /**
+     * <p> Log a message with info log level. </p>
+     *
+     * @param message log this message
+     */
+    public void info(Object message);
+
+
+    /**
+     * <p> Log an error with info log level. </p>
+     *
+     * @param message log this message
+     * @param t log this cause
+     */
+    public void info(Object message, Throwable t);
+
+
+    /**
+     * <p> Log a message with warn log level. </p>
+     *
+     * @param message log this message
+     */
+    public void warn(Object message);
+
+
+    /**
+     * <p> Log an error with warn log level. </p>
+     *
+     * @param message log this message
+     * @param t log this cause
+     */
+    public void warn(Object message, Throwable t);
+
+
+    /**
+     * <p> Log a message with error log level. </p>
+     *
+     * @param message log this message
+     */
+    public void error(Object message);
+
+
+    /**
+     * <p> Log an error with error log level. </p>
+     *
+     * @param message log this message
+     * @param t log this cause
+     */
+    public void error(Object message, Throwable t);
+
+
+    /**
+     * <p> Log a message with fatal log level. </p>
+     *
+     * @param message log this message
+     */
+    public void fatal(Object message);
+
+
+    /**
+     * <p> Log an error with fatal log level. </p>
+     *
+     * @param message log this message
+     * @param t log this cause
+     */
+    public void fatal(Object message, Throwable t);
+
+
+}
diff --git a/src/org/apache/commons/logging/LogConfigurationException.java b/src/org/apache/commons/logging/LogConfigurationException.java
new file mode 100644
index 0000000..b34387b
--- /dev/null
+++ b/src/org/apache/commons/logging/LogConfigurationException.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.logging;
+
+
+/**
+ * <p>An exception that is thrown only if a suitable <code>LogFactory</code>
+ * or <code>Log</code> instance cannot be created by the corresponding
+ * factory methods.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 155426 $ $Date: 2005-02-26 13:10:49 +0000 (Sat, 26 Feb 2005) $
+ */
+
+public class LogConfigurationException extends RuntimeException {
+
+
+    /**
+     * Construct a new exception with <code>null</code> as its detail message.
+     */
+    public LogConfigurationException() {
+
+        super();
+
+    }
+
+
+    /**
+     * Construct a new exception with the specified detail message.
+     *
+     * @param message The detail message
+     */
+    public LogConfigurationException(String message) {
+
+        super(message);
+
+    }
+
+
+    /**
+     * Construct a new exception with the specified cause and a derived
+     * detail message.
+     *
+     * @param cause The underlying cause
+     */
+    public LogConfigurationException(Throwable cause) {
+
+        this((cause == null) ? null : cause.toString(), cause);
+
+    }
+
+
+    /**
+     * Construct a new exception with the specified detail message and cause.
+     *
+     * @param message The detail message
+     * @param cause The underlying cause
+     */
+    public LogConfigurationException(String message, Throwable cause) {
+
+        super(message + " (Caused by " + cause + ")");
+        this.cause = cause; // Two-argument version requires JDK 1.4 or later
+
+    }
+
+
+    /**
+     * The underlying cause of this exception.
+     */
+    protected Throwable cause = null;
+
+
+    /**
+     * Return the underlying cause of this exception (if any).
+     */
+    public Throwable getCause() {
+
+        return (this.cause);
+
+    }
+
+
+}
diff --git a/src/org/apache/commons/logging/LogFactory.java b/src/org/apache/commons/logging/LogFactory.java
new file mode 100644
index 0000000..107d0f7
--- /dev/null
+++ b/src/org/apache/commons/logging/LogFactory.java
@@ -0,0 +1,1740 @@
+/*
+ * Copyright 2001-2006 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.logging;
+
+
+import java.io.BufferedReader;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+
+
+/**
+ * <p>Factory for creating {@link Log} instances, with discovery and
+ * configuration features similar to that employed by standard Java APIs
+ * such as JAXP.</p>
+ * 
+ * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is heavily
+ * based on the SAXParserFactory and DocumentBuilderFactory implementations
+ * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.</p>
+ *
+ * @author Craig R. McClanahan
+ * @author Costin Manolache
+ * @author Richard A. Sitze
+ * @version $Revision: 399431 $ $Date: 2006-05-03 21:58:34 +0100 (Wed, 03 May 2006) $
+ */
+
+public abstract class LogFactory {
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+    /**
+     * The name (<code>priority</code>) of the key in the config file used to 
+     * specify the priority of that particular config file. The associated value 
+     * is a floating-point number; higher values take priority over lower values.
+     */
+    public static final String PRIORITY_KEY = "priority";
+
+    /**
+     * The name (<code>use_tccl</code>) of the key in the config file used 
+     * to specify whether logging classes should be loaded via the thread 
+     * context class loader (TCCL), or not. By default, the TCCL is used.
+     */
+    public static final String TCCL_KEY = "use_tccl";
+
+    /**
+     * The name (<code>org.apache.commons.logging.LogFactory</code>) of the property 
+     * used to identify the LogFactory implementation
+     * class name. This can be used as a system property, or as an entry in a
+     * configuration properties file.
+     */
+    public static final String FACTORY_PROPERTY =
+        "org.apache.commons.logging.LogFactory";
+
+    /**
+     * The fully qualified class name of the fallback <code>LogFactory</code>
+     * implementation class to use, if no other can be found.
+     */
+    public static final String FACTORY_DEFAULT =
+        "org.apache.commons.logging.impl.LogFactoryImpl";
+
+    /**
+     * The name (<code>commons-logging.properties</code>) of the properties file to search for.
+     */
+    public static final String FACTORY_PROPERTIES =
+        "commons-logging.properties";
+
+    /**
+     * JDK1.3+ <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">
+     * 'Service Provider' specification</a>.
+     * 
+     */
+    protected static final String SERVICE_ID =
+        "META-INF/services/org.apache.commons.logging.LogFactory";
+
+    /**
+     * The name (<code>org.apache.commons.logging.diagnostics.dest</code>) 
+     * of the property used to enable internal commons-logging
+     * diagnostic output, in order to get information on what logging
+     * implementations are being discovered, what classloaders they 
+     * are loaded through, etc.
+     * <p>
+     * If a system property of this name is set then the value is
+     * assumed to be the name of a file. The special strings
+     * STDOUT or STDERR (case-sensitive) indicate output to
+     * System.out and System.err respectively.
+     * <p>
+     * Diagnostic logging should be used only to debug problematic
+     * configurations and should not be set in normal production use.
+     */
+    public static final String DIAGNOSTICS_DEST_PROPERTY =
+        "org.apache.commons.logging.diagnostics.dest";
+
+    /**
+     * When null (the usual case), no diagnostic output will be
+     * generated by LogFactory or LogFactoryImpl. When non-null,
+     * interesting events will be written to the specified object.
+     */
+    private static PrintStream diagnosticsStream = null;
+    
+    /**
+     * A string that gets prefixed to every message output by the
+     * logDiagnostic method, so that users can clearly see which
+     * LogFactory class is generating the output.
+     */
+    private static String diagnosticPrefix;
+    
+    /**
+     * <p>Setting this system property 
+     * (<code>org.apache.commons.logging.LogFactory.HashtableImpl</code>) 
+     * value allows the <code>Hashtable</code> used to store
+     * classloaders to be substituted by an alternative implementation.
+     * </p>
+     * <p>
+     * <strong>Note:</strong> <code>LogFactory</code> will print:
+     * <code><pre>
+     * [ERROR] LogFactory: Load of custom hashtable failed</em>
+     * </pre></code>
+     * to system error and then continue using a standard Hashtable.
+     * </p>
+     * <p>
+     * <strong>Usage:</strong> Set this property when Java is invoked
+     * and <code>LogFactory</code> will attempt to load a new instance 
+     * of the given implementation class.
+     * For example, running the following ant scriplet:
+     * <code><pre>
+     *  &lt;java classname="${test.runner}" fork="yes" failonerror="${test.failonerror}"&gt;
+     *     ...
+     *     &lt;sysproperty 
+     *        key="org.apache.commons.logging.LogFactory.HashtableImpl"
+     *        value="org.apache.commons.logging.AltHashtable"/&gt;
+     *  &lt;/java&gt;
+     * </pre></code>
+     * will mean that <code>LogFactory</code> will load an instance of
+     * <code>org.apache.commons.logging.AltHashtable</code>.
+     * </p>
+     * <p>
+     * A typical use case is to allow a custom
+     * Hashtable implementation using weak references to be substituted.
+     * This will allow classloaders to be garbage collected without
+     * the need to release them (on 1.3+ JVMs only, of course ;)
+     * </p>
+     */
+    public static final String HASHTABLE_IMPLEMENTATION_PROPERTY =
+        "org.apache.commons.logging.LogFactory.HashtableImpl";
+    /** Name used to load the weak hashtable implementation by names */
+    private static final String WEAK_HASHTABLE_CLASSNAME = 
+        "org.apache.commons.logging.impl.WeakHashtable";
+
+    /**
+     * A reference to the classloader that loaded this class. This is the
+     * same as LogFactory.class.getClassLoader(). However computing this
+     * value isn't quite as simple as that, as we potentially need to use
+     * AccessControllers etc. It's more efficient to compute it once and
+     * cache it here.
+     */
+    private static ClassLoader thisClassLoader;
+    
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Protected constructor that is not available for public use.
+     */
+    protected LogFactory() {
+    }
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the configuration attribute with the specified name (if any),
+     * or <code>null</code> if there is no such attribute.
+     *
+     * @param name Name of the attribute to return
+     */
+    public abstract Object getAttribute(String name);
+
+
+    /**
+     * Return an array containing the names of all currently defined
+     * configuration attributes.  If there are no such attributes, a zero
+     * length array is returned.
+     */
+    public abstract String[] getAttributeNames();
+
+
+    /**
+     * Convenience method to derive a name from the specified class and
+     * call <code>getInstance(String)</code> with it.
+     *
+     * @param clazz Class for which a suitable Log name will be derived
+     *
+     * @exception LogConfigurationException if a suitable <code>Log</code>
+     *  instance cannot be returned
+     */
+    public abstract Log getInstance(Class clazz)
+        throws LogConfigurationException;
+
+
+    /**
+     * <p>Construct (if necessary) and return a <code>Log</code> instance,
+     * using the factory's current set of configuration attributes.</p>
+     *
+     * <p><strong>NOTE</strong> - Depending upon the implementation of
+     * the <code>LogFactory</code> you are using, the <code>Log</code>
+     * instance you are returned may or may not be local to the current
+     * application, and may or may not be returned again on a subsequent
+     * call with the same name argument.</p>
+     *
+     * @param name Logical name of the <code>Log</code> instance to be
+     *  returned (the meaning of this name is only known to the underlying
+     *  logging implementation that is being wrapped)
+     *
+     * @exception LogConfigurationException if a suitable <code>Log</code>
+     *  instance cannot be returned
+     */
+    public abstract Log getInstance(String name)
+        throws LogConfigurationException;
+
+
+    /**
+     * Release any internal references to previously created {@link Log}
+     * instances returned by this factory.  This is useful in environments
+     * like servlet containers, which implement application reloading by
+     * throwing away a ClassLoader.  Dangling references to objects in that
+     * class loader would prevent garbage collection.
+     */
+    public abstract void release();
+
+
+    /**
+     * Remove any configuration attribute associated with the specified name.
+     * If there is no such attribute, no action is taken.
+     *
+     * @param name Name of the attribute to remove
+     */
+    public abstract void removeAttribute(String name);
+
+
+    /**
+     * Set the configuration attribute with the specified name.  Calling
+     * this with a <code>null</code> value is equivalent to calling
+     * <code>removeAttribute(name)</code>.
+     *
+     * @param name Name of the attribute to set
+     * @param value Value of the attribute to set, or <code>null</code>
+     *  to remove any setting for this attribute
+     */
+    public abstract void setAttribute(String name, Object value);
+
+
+    // ------------------------------------------------------- Static Variables
+
+
+    /**
+     * The previously constructed <code>LogFactory</code> instances, keyed by
+     * the <code>ClassLoader</code> with which it was created.
+     */
+    protected static Hashtable factories = null;
+    
+    /**
+     * Prevously constructed <code>LogFactory</code> instance as in the
+     * <code>factories</code> map, but for the case where
+     * <code>getClassLoader</code> returns <code>null</code>.
+     * This can happen when:
+     * <ul>
+     * <li>using JDK1.1 and the calling code is loaded via the system
+     *  classloader (very common)</li>
+     * <li>using JDK1.2+ and the calling code is loaded via the boot
+     *  classloader (only likely for embedded systems work).</li>
+     * </ul>
+     * Note that <code>factories</code> is a <i>Hashtable</i> (not a HashMap),
+     * and hashtables don't allow null as a key.
+     */
+    protected static LogFactory nullClassLoaderFactory = null;
+
+    /**
+     * Create the hashtable which will be used to store a map of
+     * (context-classloader -> logfactory-object). Version 1.2+ of Java
+     * supports "weak references", allowing a custom Hashtable class
+     * to be used which uses only weak references to its keys. Using weak
+     * references can fix memory leaks on webapp unload in some cases (though
+     * not all). Version 1.1 of Java does not support weak references, so we
+     * must dynamically determine which we are using. And just for fun, this
+     * code also supports the ability for a system property to specify an
+     * arbitrary Hashtable implementation name.
+     * <p>
+     * Note that the correct way to ensure no memory leaks occur is to ensure
+     * that LogFactory.release(contextClassLoader) is called whenever a 
+     * webapp is undeployed.
+     */
+    private static final Hashtable createFactoryStore() {
+        Hashtable result = null;
+        String storeImplementationClass 
+            = System.getProperty(HASHTABLE_IMPLEMENTATION_PROPERTY);
+        if (storeImplementationClass == null) {
+            storeImplementationClass = WEAK_HASHTABLE_CLASSNAME;
+        }
+        try {
+            Class implementationClass = Class.forName(storeImplementationClass);
+            result = (Hashtable) implementationClass.newInstance();
+            
+        } catch (Throwable t) {
+            // ignore
+            if (!WEAK_HASHTABLE_CLASSNAME.equals(storeImplementationClass)) {
+                // if the user's trying to set up a custom implementation, give a clue
+                if (isDiagnosticsEnabled()) {
+                    // use internal logging to issue the warning
+                    logDiagnostic("[ERROR] LogFactory: Load of custom hashtable failed");
+                } else {
+                    // we *really* want this output, even if diagnostics weren't
+                    // explicitly enabled by the user.
+                    System.err.println("[ERROR] LogFactory: Load of custom hashtable failed");
+                }
+            }
+        }
+        if (result == null) {
+            result = new Hashtable();
+        }
+        return result;
+    }
+
+
+    // --------------------------------------------------------- Static Methods
+
+    /**
+     * <p>Construct (if necessary) and return a <code>LogFactory</code>
+     * instance, using the following ordered lookup procedure to determine
+     * the name of the implementation class to be loaded.</p>
+     * <ul>
+     * <li>The <code>org.apache.commons.logging.LogFactory</code> system
+     *     property.</li>
+     * <li>The JDK 1.3 Service Discovery mechanism</li>
+     * <li>Use the properties file <code>commons-logging.properties</code>
+     *     file, if found in the class path of this class.  The configuration
+     *     file is in standard <code>java.util.Properties</code> format and
+     *     contains the fully qualified name of the implementation class
+     *     with the key being the system property defined above.</li>
+     * <li>Fall back to a default implementation class
+     *     (<code>org.apache.commons.logging.impl.LogFactoryImpl</code>).</li>
+     * </ul>
+     *
+     * <p><em>NOTE</em> - If the properties file method of identifying the
+     * <code>LogFactory</code> implementation class is utilized, all of the
+     * properties defined in this file will be set as configuration attributes
+     * on the corresponding <code>LogFactory</code> instance.</p>
+     * 
+     * <p><em>NOTE</em> - In a multithreaded environment it is possible 
+     * that two different instances will be returned for the same 
+     * classloader environment. 
+     * </p>
+     *
+     * @exception LogConfigurationException if the implementation class is not
+     *  available or cannot be instantiated.
+     */
+    public static LogFactory getFactory() throws LogConfigurationException {
+        // Identify the class loader we will be using
+        ClassLoader contextClassLoader = getContextClassLoader();
+
+        if (contextClassLoader == null) {
+            // This is an odd enough situation to report about. This
+            // output will be a nuisance on JDK1.1, as the system
+            // classloader is null in that environment.
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("Context classloader is null.");
+            }
+        }
+
+        // Return any previously registered factory for this class loader
+        LogFactory factory = getCachedFactory(contextClassLoader);
+        if (factory != null) {
+            return factory;
+        }
+
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic(
+                    "[LOOKUP] LogFactory implementation requested for the first time for context classloader "
+                    + objectId(contextClassLoader));
+            logHierarchy("[LOOKUP] ", contextClassLoader);
+        }
+
+        // Load properties file.
+        //
+        // If the properties file exists, then its contents are used as
+        // "attributes" on the LogFactory implementation class. One particular
+        // property may also control which LogFactory concrete subclass is
+        // used, but only if other discovery mechanisms fail..
+        //
+        // As the properties file (if it exists) will be used one way or 
+        // another in the end we may as well look for it first.
+
+        Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);
+
+        // Determine whether we will be using the thread context class loader to
+        // load logging classes or not by checking the loaded properties file (if any).
+        ClassLoader baseClassLoader = contextClassLoader;
+        if (props != null) {
+            String useTCCLStr = props.getProperty(TCCL_KEY);
+            if (useTCCLStr != null) {
+                // The Boolean.valueOf(useTCCLStr).booleanValue() formulation
+                // is required for Java 1.2 compatability.
+                if (Boolean.valueOf(useTCCLStr).booleanValue() == false) {
+                    // Don't use current context classloader when locating any
+                    // LogFactory or Log classes, just use the class that loaded
+                    // this abstract class. When this class is deployed in a shared
+                    // classpath of a container, it means webapps cannot deploy their
+                    // own logging implementations. It also means that it is up to the
+                    // implementation whether to load library-specific config files
+                    // from the TCCL or not.
+                    baseClassLoader = thisClassLoader; 
+                }
+            }
+        }
+
+        // Determine which concrete LogFactory subclass to use.
+        // First, try a global system property
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic(
+                    "[LOOKUP] Looking for system property [" + FACTORY_PROPERTY 
+                    + "] to define the LogFactory subclass to use...");
+        }
+        
+        try {
+            String factoryClass = System.getProperty(FACTORY_PROPERTY);
+            if (factoryClass != null) {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic(
+                            "[LOOKUP] Creating an instance of LogFactory class '" + factoryClass
+                            + "' as specified by system property " + FACTORY_PROPERTY);
+                }
+                
+                factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
+            } else {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic(
+                            "[LOOKUP] No system property [" + FACTORY_PROPERTY 
+                            + "] defined.");
+                }
+            }
+        } catch (SecurityException e) {
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic(
+                        "[LOOKUP] A security exception occurred while trying to create an"
+                        + " instance of the custom factory class"
+                        + ": [" + e.getMessage().trim()
+                        + "]. Trying alternative implementations...");
+            }
+            ;  // ignore
+        } catch(RuntimeException e) {
+            // This is not consistent with the behaviour when a bad LogFactory class is
+            // specified in a services file.
+            //
+            // One possible exception that can occur here is a ClassCastException when
+            // the specified class wasn't castable to this LogFactory type.
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic(
+                        "[LOOKUP] An exception occurred while trying to create an"
+                        + " instance of the custom factory class"
+                        + ": [" + e.getMessage().trim()
+                        + "] as specified by a system property.");
+            }
+            throw e;
+        }
+
+
+        // Second, try to find a service by using the JDK1.3 class
+        // discovery mechanism, which involves putting a file with the name
+        // of an interface class in the META-INF/services directory, where the
+        // contents of the file is a single line specifying a concrete class 
+        // that implements the desired interface.
+
+        if (factory == null) {
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic(
+                        "[LOOKUP] Looking for a resource file of name [" + SERVICE_ID
+                        + "] to define the LogFactory subclass to use...");
+            }
+            try {
+                InputStream is = getResourceAsStream(contextClassLoader,
+                                                     SERVICE_ID);
+
+                if( is != null ) {
+                    // This code is needed by EBCDIC and other strange systems.
+                    // It's a fix for bugs reported in xerces
+                    BufferedReader rd;
+                    try {
+                        rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+                    } catch (java.io.UnsupportedEncodingException e) {
+                        rd = new BufferedReader(new InputStreamReader(is));
+                    }
+
+                    String factoryClassName = rd.readLine();
+                    rd.close();
+
+                    if (factoryClassName != null &&
+                        ! "".equals(factoryClassName)) {
+                        if (isDiagnosticsEnabled()) {
+                            logDiagnostic(
+                                    "[LOOKUP]  Creating an instance of LogFactory class " + factoryClassName
+                                    + " as specified by file '" + SERVICE_ID 
+                                    + "' which was present in the path of the context"
+                                    + " classloader.");
+                        }
+                        factory = newFactory(factoryClassName, baseClassLoader, contextClassLoader );
+                    }
+                } else {
+                    // is == null
+                    if (isDiagnosticsEnabled()) {
+                        logDiagnostic(
+                            "[LOOKUP] No resource file with name '" + SERVICE_ID
+                            + "' found.");
+                    }
+                }
+            } catch( Exception ex ) {
+                // note: if the specified LogFactory class wasn't compatible with LogFactory
+                // for some reason, a ClassCastException will be caught here, and attempts will
+                // continue to find a compatible class.
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic(
+                        "[LOOKUP] A security exception occurred while trying to create an"
+                        + " instance of the custom factory class"
+                        + ": [" + ex.getMessage().trim()
+                        + "]. Trying alternative implementations...");
+                }
+                ; // ignore
+            }
+        }
+
+
+        // Third try looking into the properties file read earlier (if found)
+
+        if (factory == null) {
+            if (props != null) {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic(
+                        "[LOOKUP] Looking in properties file for entry with key '" 
+                        + FACTORY_PROPERTY
+                        + "' to define the LogFactory subclass to use...");
+                }
+                String factoryClass = props.getProperty(FACTORY_PROPERTY);
+                if (factoryClass != null) {
+                    if (isDiagnosticsEnabled()) {
+                        logDiagnostic(
+                            "[LOOKUP] Properties file specifies LogFactory subclass '" 
+                            + factoryClass + "'");
+                    }
+                    factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
+                    
+                    // TODO: think about whether we need to handle exceptions from newFactory
+                } else {
+                    if (isDiagnosticsEnabled()) {
+                        logDiagnostic(
+                            "[LOOKUP] Properties file has no entry specifying LogFactory subclass.");
+                    }
+                }
+            } else {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic(
+                        "[LOOKUP] No properties file available to determine"
+                        + " LogFactory subclass from..");
+                }
+            }
+        }
+
+
+        // Fourth, try the fallback implementation class
+
+        if (factory == null) {
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic(
+                "[LOOKUP] Loading the default LogFactory implementation '" + FACTORY_DEFAULT
+                + "' via the same classloader that loaded this LogFactory"
+                + " class (ie not looking in the context classloader).");
+            }
+            
+            // Note: unlike the above code which can try to load custom LogFactory
+            // implementations via the TCCL, we don't try to load the default LogFactory
+            // implementation via the context classloader because:
+            // * that can cause problems (see comments in newFactory method)
+            // * no-one should be customising the code of the default class
+            // Yes, we do give up the ability for the child to ship a newer
+            // version of the LogFactoryImpl class and have it used dynamically
+            // by an old LogFactory class in the parent, but that isn't 
+            // necessarily a good idea anyway.
+            factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);
+        }
+
+        if (factory != null) {
+            /**
+             * Always cache using context class loader.
+             */
+            cacheFactory(contextClassLoader, factory);
+
+            if( props!=null ) {
+                Enumeration names = props.propertyNames();
+                while (names.hasMoreElements()) {
+                    String name = (String) names.nextElement();
+                    String value = props.getProperty(name);
+                    factory.setAttribute(name, value);
+                }
+            }
+        }
+
+        return factory;
+    }
+
+
+    /**
+     * Convenience method to return a named logger, without the application
+     * having to care about factories.
+     *
+     * @param clazz Class from which a log name will be derived
+     *
+     * @exception LogConfigurationException if a suitable <code>Log</code>
+     *  instance cannot be returned
+     */
+    public static Log getLog(Class clazz)
+        throws LogConfigurationException {
+
+        // BEGIN android-added
+        return getLog(clazz.getName());
+        // END android-added
+        // BEGIN android-deleted
+        //return (getFactory().getInstance(clazz));
+        // END android-deleted
+
+    }
+
+
+    /**
+     * Convenience method to return a named logger, without the application
+     * having to care about factories.
+     *
+     * @param name Logical name of the <code>Log</code> instance to be
+     *  returned (the meaning of this name is only known to the underlying
+     *  logging implementation that is being wrapped)
+     *
+     * @exception LogConfigurationException if a suitable <code>Log</code>
+     *  instance cannot be returned
+     */
+    public static Log getLog(String name)
+        throws LogConfigurationException {
+
+        // BEGIN android-added
+        return new org.apache.commons.logging.impl.Jdk14Logger(name);
+        // END android-added
+        // BEGIN android-deleted
+        //return (getFactory().getInstance(name));
+        // END android-deleted
+
+    }
+
+
+    /**
+     * Release any internal references to previously created {@link LogFactory}
+     * instances that have been associated with the specified class loader
+     * (if any), after calling the instance method <code>release()</code> on
+     * each of them.
+     *
+     * @param classLoader ClassLoader for which to release the LogFactory
+     */
+    public static void release(ClassLoader classLoader) {
+
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("Releasing factory for classloader " + objectId(classLoader));
+        }
+        synchronized (factories) {
+            if (classLoader == null) {
+                if (nullClassLoaderFactory != null) {
+                    nullClassLoaderFactory.release();
+                    nullClassLoaderFactory = null;
+                }
+            } else {
+                LogFactory factory = (LogFactory) factories.get(classLoader);
+                if (factory != null) {
+                    factory.release();
+                    factories.remove(classLoader);
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Release any internal references to previously created {@link LogFactory}
+     * instances, after calling the instance method <code>release()</code> on
+     * each of them.  This is useful in environments like servlet containers,
+     * which implement application reloading by throwing away a ClassLoader.
+     * Dangling references to objects in that class loader would prevent
+     * garbage collection.
+     */
+    public static void releaseAll() {
+
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("Releasing factory for all classloaders.");
+        }
+        synchronized (factories) {
+            Enumeration elements = factories.elements();
+            while (elements.hasMoreElements()) {
+                LogFactory element = (LogFactory) elements.nextElement();
+                element.release();
+            }
+            factories.clear();
+
+            if (nullClassLoaderFactory != null) {
+                nullClassLoaderFactory.release();
+                nullClassLoaderFactory = null;
+            }
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+    /**
+     * Safely get access to the classloader for the specified class.
+     * <p>
+     * Theoretically, calling getClassLoader can throw a security exception,
+     * and so should be done under an AccessController in order to provide
+     * maximum flexibility. However in practice people don't appear to use
+     * security policies that forbid getClassLoader calls. So for the moment
+     * all code is written to call this method rather than Class.getClassLoader,
+     * so that we could put AccessController stuff in this method without any
+     * disruption later if we need to.
+     * <p>
+     * Even when using an AccessController, however, this method can still
+     * throw SecurityException. Commons-logging basically relies on the
+     * ability to access classloaders, ie a policy that forbids all
+     * classloader access will also prevent commons-logging from working: 
+     * currently this method will throw an exception preventing the entire app
+     * from starting up. Maybe it would be good to detect this situation and
+     * just disable all commons-logging? Not high priority though - as stated
+     * above, security policies that prevent classloader access aren't common.
+     * 
+     * @since 1.1
+     */
+    protected static ClassLoader getClassLoader(Class clazz) {
+        try {
+            return clazz.getClassLoader();
+        } catch(SecurityException ex) {
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic(
+                        "Unable to get classloader for class '" + clazz
+                        + "' due to security restrictions - " + ex.getMessage());
+            }
+            throw ex;
+        }
+    }
+
+    /**
+     * Calls LogFactory.directGetContextClassLoader under the control of an
+     * AccessController class. This means that java code running under a
+     * security manager that forbids access to ClassLoaders will still work
+     * if this class is given appropriate privileges, even when the caller
+     * doesn't have such privileges. Without using an AccessController, the
+     * the entire call stack must have the privilege before the call is
+     * allowed.
+     *  
+     * @return the context classloader associated with the current thread,
+     * or null if security doesn't allow it.
+     * 
+     * @throws LogConfigurationException if there was some weird error while
+     * attempting to get the context classloader.
+     * 
+     * @throws SecurityException if the current java security policy doesn't
+     * allow this class to access the context classloader.
+     */
+    protected static ClassLoader getContextClassLoader()
+        throws LogConfigurationException {
+
+        return (ClassLoader)AccessController.doPrivileged(
+            new PrivilegedAction() {
+                public Object run() {
+                    return directGetContextClassLoader();
+                }
+            });
+    }
+
+    /**
+     * Return the thread context class loader if available; otherwise return 
+     * null. 
+     * <p>
+     * Most/all code should call getContextClassLoader rather than calling
+     * this method directly.
+     * <p>
+     * The thread context class loader is available for JDK 1.2
+     * or later, if certain security conditions are met.
+     * <p>
+     * Note that no internal logging is done within this method because
+     * this method is called every time LogFactory.getLogger() is called,
+     * and we don't want too much output generated here.
+     * 
+     * @exception LogConfigurationException if a suitable class loader
+     * cannot be identified.
+     * 
+     * @exception SecurityException if the java security policy forbids
+     * access to the context classloader from one of the classes in the
+     * current call stack. 
+     * @since 1.1
+     */
+    protected static ClassLoader directGetContextClassLoader()
+        throws LogConfigurationException
+    {
+        ClassLoader classLoader = null;
+
+        try {
+            // Are we running on a JDK 1.2 or later system?
+            Method method = Thread.class.getMethod("getContextClassLoader", 
+                    (Class[]) null);
+
+            // Get the thread context class loader (if there is one)
+            try {
+                classLoader = (ClassLoader)method.invoke(Thread.currentThread(), 
+                        (Object[]) null);
+            } catch (IllegalAccessException e) {
+                throw new LogConfigurationException
+                    ("Unexpected IllegalAccessException", e);
+            } catch (InvocationTargetException e) {
+                /**
+                 * InvocationTargetException is thrown by 'invoke' when
+                 * the method being invoked (getContextClassLoader) throws
+                 * an exception.
+                 *
+                 * getContextClassLoader() throws SecurityException when
+                 * the context class loader isn't an ancestor of the
+                 * calling class's class loader, or if security
+                 * permissions are restricted.
+                 *
+                 * In the first case (not related), we want to ignore and
+                 * keep going.  We cannot help but also ignore the second
+                 * with the logic below, but other calls elsewhere (to
+                 * obtain a class loader) will trigger this exception where
+                 * we can make a distinction.
+                 */
+                if (e.getTargetException() instanceof SecurityException) {
+                    ;  // ignore
+                } else {
+                    // Capture 'e.getTargetException()' exception for details
+                    // alternate: log 'e.getTargetException()', and pass back 'e'.
+                    throw new LogConfigurationException
+                        ("Unexpected InvocationTargetException", e.getTargetException());
+                }
+            }
+        } catch (NoSuchMethodException e) {
+            // Assume we are running on JDK 1.1
+            classLoader = getClassLoader(LogFactory.class);
+
+            // We deliberately don't log a message here to outputStream;
+            // this message would be output for every call to LogFactory.getLog()
+            // when running on JDK1.1
+            //
+            // if (outputStream != null) {
+            //    outputStream.println(
+            //        "Method Thread.getContextClassLoader does not exist;"
+            //         + " assuming this is JDK 1.1, and that the context"
+            //         + " classloader is the same as the class that loaded"
+            //         + " the concrete LogFactory class.");
+            // }
+            
+        }
+
+        // Return the selected class loader
+        return classLoader;
+    }
+
+    /**
+     * Check cached factories (keyed by contextClassLoader)
+     *
+     * @param contextClassLoader is the context classloader associated
+     * with the current thread. This allows separate LogFactory objects
+     * per component within a container, provided each component has
+     * a distinct context classloader set. This parameter may be null
+     * in JDK1.1, and in embedded systems where jcl-using code is
+     * placed in the bootclasspath.
+     * 
+     * @return the factory associated with the specified classloader if
+     * one has previously been created, or null if this is the first time
+     * we have seen this particular classloader.
+     */
+    private static LogFactory getCachedFactory(ClassLoader contextClassLoader)
+    {
+        LogFactory factory = null;
+
+        if (contextClassLoader == null) {
+            // We have to handle this specially, as factories is a Hashtable
+            // and those don't accept null as a key value.
+            //
+            // nb: nullClassLoaderFactory might be null. That's ok.
+            factory = nullClassLoaderFactory;
+        } else {
+            factory = (LogFactory) factories.get(contextClassLoader);
+        }
+
+        return factory;
+    }
+
+    /**
+     * Remember this factory, so later calls to LogFactory.getCachedFactory
+     * can return the previously created object (together with all its
+     * cached Log objects).
+     *
+     * @param classLoader should be the current context classloader. Note that
+     * this can be null under some circumstances; this is ok.
+     *
+     * @param factory should be the factory to cache. This should never be null.
+     */
+    private static void cacheFactory(ClassLoader classLoader, LogFactory factory)
+    {
+        // Ideally we would assert(factory != null) here. However reporting
+        // errors from within a logging implementation is a little tricky!
+
+        if (factory != null) {
+            if (classLoader == null) {
+                nullClassLoaderFactory = factory;
+            } else {
+                factories.put(classLoader, factory);
+            }
+        }
+    }
+
+    /**
+     * Return a new instance of the specified <code>LogFactory</code>
+     * implementation class, loaded by the specified class loader.
+     * If that fails, try the class loader used to load this
+     * (abstract) LogFactory.
+     * <p>
+     * <h2>ClassLoader conflicts</h2>
+     * Note that there can be problems if the specified ClassLoader is not the 
+     * same as the classloader that loaded this class, ie when loading a
+     * concrete LogFactory subclass via a context classloader.
+     * <p>
+     * The problem is the same one that can occur when loading a concrete Log
+     * subclass via a context classloader.
+     * <p>
+     * The problem occurs when code running in the context classloader calls
+     * class X which was loaded via a parent classloader, and class X then calls
+     * LogFactory.getFactory (either directly or via LogFactory.getLog). Because
+     * class X was loaded via the parent, it binds to LogFactory loaded via
+     * the parent. When the code in this method finds some LogFactoryYYYY
+     * class in the child (context) classloader, and there also happens to be a
+     * LogFactory class defined in the child classloader, then LogFactoryYYYY
+     * will be bound to LogFactory@childloader. It cannot be cast to
+     * LogFactory@parentloader, ie this method cannot return the object as
+     * the desired type. Note that it doesn't matter if the LogFactory class
+     * in the child classloader is identical to the LogFactory class in the
+     * parent classloader, they are not compatible.
+     * <p>
+     * The solution taken here is to simply print out an error message when
+     * this occurs then throw an exception. The deployer of the application
+     * must ensure they remove all occurrences of the LogFactory class from
+     * the child classloader in order to resolve the issue. Note that they
+     * do not have to move the custom LogFactory subclass; that is ok as
+     * long as the only LogFactory class it can find to bind to is in the
+     * parent classloader.
+     * <p>
+     * @param factoryClass Fully qualified name of the <code>LogFactory</code>
+     *  implementation class
+     * @param classLoader ClassLoader from which to load this class
+     * @param contextClassLoader is the context that this new factory will
+     * manage logging for.
+     *
+     * @exception LogConfigurationException if a suitable instance
+     *  cannot be created
+     * @since 1.1
+     */
+    protected static LogFactory newFactory(final String factoryClass,
+                                           final ClassLoader classLoader,
+                                           final ClassLoader contextClassLoader)
+        throws LogConfigurationException
+    {
+        // Note that any unchecked exceptions thrown by the createFactory
+        // method will propagate out of this method; in particular a
+        // ClassCastException can be thrown.
+        Object result = AccessController.doPrivileged(
+            new PrivilegedAction() {
+                public Object run() {
+                    return createFactory(factoryClass, classLoader);
+                }
+            });
+
+        if (result instanceof LogConfigurationException) {
+            LogConfigurationException ex = (LogConfigurationException) result;
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic(
+                        "An error occurred while loading the factory class:"
+                        + ex.getMessage());
+            }
+            throw ex;
+        }
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic(
+                    "Created object " + objectId(result)
+                    + " to manage classloader " + objectId(contextClassLoader));
+        }
+        return (LogFactory)result;
+    }
+
+    /**
+     * Method provided for backwards compatibility; see newFactory version that
+     * takes 3 parameters.
+     * <p>
+     * This method would only ever be called in some rather odd situation.
+     * Note that this method is static, so overriding in a subclass doesn't
+     * have any effect unless this method is called from a method in that
+     * subclass. However this method only makes sense to use from the
+     * getFactory method, and as that is almost always invoked via
+     * LogFactory.getFactory, any custom definition in a subclass would be
+     * pointless. Only a class with a custom getFactory method, then invoked
+     * directly via CustomFactoryImpl.getFactory or similar would ever call
+     * this. Anyway, it's here just in case, though the "managed class loader"
+     * value output to the diagnostics will not report the correct value.
+     */
+    protected static LogFactory newFactory(final String factoryClass,
+                                           final ClassLoader classLoader) {
+	    return newFactory(factoryClass, classLoader, null);
+    }
+
+    /**
+     * Implements the operations described in the javadoc for newFactory.
+     * 
+     * @param factoryClass
+     * 
+     * @param classLoader used to load the specified factory class. This is
+     * expected to be either the TCCL or the classloader which loaded this
+     * class. Note that the classloader which loaded this class might be
+     * "null" (ie the bootloader) for embedded systems.
+     * 
+     * @return either a LogFactory object or a LogConfigurationException object.
+     * @since 1.1
+     */
+    protected static Object createFactory(String factoryClass, ClassLoader classLoader) {
+
+        // This will be used to diagnose bad configurations
+        // and allow a useful message to be sent to the user
+        Class logFactoryClass = null;
+        try {
+            if (classLoader != null) {
+                try {
+                    // First the given class loader param (thread class loader)
+
+                    // Warning: must typecast here & allow exception
+                    // to be generated/caught & recast properly.
+                    logFactoryClass = classLoader.loadClass(factoryClass);
+                    if (LogFactory.class.isAssignableFrom(logFactoryClass)) {
+                        if (isDiagnosticsEnabled()) {
+                            logDiagnostic(
+                                    "Loaded class " + logFactoryClass.getName()
+                                    + " from classloader " + objectId(classLoader));
+                        }
+                    } else {
+                        //
+                        // This indicates a problem with the ClassLoader tree.
+                        // An incompatible ClassLoader was used to load the 
+                        // implementation. 
+                        // As the same classes
+                        // must be available in multiple class loaders,
+                        // it is very likely that multiple JCL jars are present.
+                        // The most likely fix for this
+                        // problem is to remove the extra JCL jars from the 
+                        // ClassLoader hierarchy. 
+                        //
+                        if (isDiagnosticsEnabled()) {
+                            logDiagnostic(
+                                    "Factory class " + logFactoryClass.getName()
+                                + " loaded from classloader " + objectId(logFactoryClass.getClassLoader())
+                                + " does not extend '" + LogFactory.class.getName()
+                                + "' as loaded by this classloader.");
+                            logHierarchy("[BAD CL TREE] ", classLoader);
+                        }
+                    }
+                    
+                    return (LogFactory) logFactoryClass.newInstance();
+
+                } catch (ClassNotFoundException ex) {
+                    if (classLoader == thisClassLoader) {
+                        // Nothing more to try, onwards.
+                        if (isDiagnosticsEnabled()) {
+                            logDiagnostic(
+                                    "Unable to locate any class called '" + factoryClass
+                                    + "' via classloader " + objectId(classLoader));
+                        }
+                        throw ex;
+                    }
+                    // ignore exception, continue
+                } catch (NoClassDefFoundError e) {
+                    if (classLoader == thisClassLoader) {
+                        // Nothing more to try, onwards.
+                        if (isDiagnosticsEnabled()) {
+                            logDiagnostic(
+                                    "Class '" + factoryClass + "' cannot be loaded"
+                                    + " via classloader " + objectId(classLoader)
+                                    + " - it depends on some other class that cannot"
+                                    + " be found.");
+                        }
+                        throw e;
+                    }
+                    // ignore exception, continue
+                } catch(ClassCastException e) {
+                    if (classLoader == thisClassLoader) {
+                        // There's no point in falling through to the code below that
+                        // tries again with thisClassLoader, because we've just tried
+                        // loading with that loader (not the TCCL). Just throw an
+                        // appropriate exception here.
+
+                    	final boolean implementsLogFactory = implementsLogFactory(logFactoryClass);
+                        
+                        //
+                        // Construct a good message: users may not actual expect that a custom implementation 
+                        // has been specified. Several well known containers use this mechanism to adapt JCL 
+                        // to their native logging system. 
+                        // 
+                        String msg = 
+                            "The application has specified that a custom LogFactory implementation should be used but " +
+                            "Class '" + factoryClass + "' cannot be converted to '"
+                            + LogFactory.class.getName() + "'. ";
+                        if (implementsLogFactory) {
+                            msg = msg + "The conflict is caused by the presence of multiple LogFactory classes in incompatible classloaders. " +
+                    		"Background can be found in http://jakarta.apache.org/commons/logging/tech.html. " +
+                    		"If you have not explicitly specified a custom LogFactory then it is likely that " +
+                    		"the container has set one without your knowledge. " +
+                    		"In this case, consider using the commons-logging-adapters.jar file or " +
+                    		"specifying the standard LogFactory from the command line. ";
+                        } else {
+                        	msg = msg + "Please check the custom implementation. ";
+                        }
+                        msg = msg + "Help can be found @http://jakarta.apache.org/commons/logging/troubleshooting.html.";
+                        
+                        if (isDiagnosticsEnabled()) {
+                            logDiagnostic(msg);
+                        }
+                        
+                        ClassCastException ex = new ClassCastException(msg);
+                        throw ex;
+                    }
+                    
+                    // Ignore exception, continue. Presumably the classloader was the
+                    // TCCL; the code below will try to load the class via thisClassLoader.
+                    // This will handle the case where the original calling class is in
+                    // a shared classpath but the TCCL has a copy of LogFactory and the
+                    // specified LogFactory implementation; we will fall back to using the
+                    // LogFactory implementation from the same classloader as this class.
+                    //
+                    // Issue: this doesn't handle the reverse case, where this LogFactory
+                    // is in the webapp, and the specified LogFactory implementation is
+                    // in a shared classpath. In that case:
+                    // (a) the class really does implement LogFactory (bad log msg above)
+                    // (b) the fallback code will result in exactly the same problem.
+                }
+            }
+
+            /* At this point, either classLoader == null, OR
+             * classLoader was unable to load factoryClass.
+             *
+             * In either case, we call Class.forName, which is equivalent
+             * to LogFactory.class.getClassLoader().load(name), ie we ignore
+             * the classloader parameter the caller passed, and fall back
+             * to trying the classloader associated with this class. See the
+             * javadoc for the newFactory method for more info on the 
+             * consequences of this.
+             *
+             * Notes:
+             * * LogFactory.class.getClassLoader() may return 'null'
+             *   if LogFactory is loaded by the bootstrap classloader.
+             */
+            // Warning: must typecast here & allow exception
+            // to be generated/caught & recast properly.
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic(
+                    "Unable to load factory class via classloader " 
+                    + objectId(classLoader)
+                    + " - trying the classloader associated with this LogFactory.");
+            }
+            logFactoryClass = Class.forName(factoryClass);
+            return (LogFactory) logFactoryClass.newInstance();
+        } catch (Exception e) {
+            // Check to see if we've got a bad configuration
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("Unable to create LogFactory instance.");
+            }
+            if (logFactoryClass != null
+                && !LogFactory.class.isAssignableFrom(logFactoryClass)) {
+                
+                return new LogConfigurationException(
+                    "The chosen LogFactory implementation does not extend LogFactory."
+                    + " Please check your configuration.",
+                    e);
+            }
+            return new LogConfigurationException(e);
+        }
+    }
+
+    /**
+     * Determines whether the given class actually implements <code>LogFactory</code>.
+     * Diagnostic information is also logged.
+     * <p>
+     * <strong>Usage:</strong> to diagnose whether a classloader conflict is the cause
+     * of incompatibility. The test used is whether the class is assignable from
+     * the <code>LogFactory</code> class loaded by the class's classloader.
+     * @param logFactoryClass <code>Class</code> which may implement <code>LogFactory</code>
+     * @return true if the <code>logFactoryClass</code> does extend
+     * <code>LogFactory</code> when that class is loaded via the same
+     * classloader that loaded the <code>logFactoryClass</code>.
+     */
+    private static boolean implementsLogFactory(Class logFactoryClass) {
+        boolean implementsLogFactory = false;
+        if (logFactoryClass != null) {
+            try {
+                ClassLoader logFactoryClassLoader = logFactoryClass.getClassLoader();
+                if (logFactoryClassLoader == null) {
+                    logDiagnostic("[CUSTOM LOG FACTORY] was loaded by the boot classloader");
+                } else {
+                    logHierarchy("[CUSTOM LOG FACTORY] ", logFactoryClassLoader);
+                    Class factoryFromCustomLoader
+                        = Class.forName("org.apache.commons.logging.LogFactory", false, logFactoryClassLoader);
+                    implementsLogFactory = factoryFromCustomLoader.isAssignableFrom(logFactoryClass);
+                    if (implementsLogFactory) {
+                        logDiagnostic("[CUSTOM LOG FACTORY] " + logFactoryClass.getName()
+                                + " implements LogFactory but was loaded by an incompatible classloader.");
+                    } else {
+                        logDiagnostic("[CUSTOM LOG FACTORY] " + logFactoryClass.getName()
+                                + " does not implement LogFactory.");
+                    }
+                }
+            } catch (SecurityException e) {
+                //
+                // The application is running within a hostile security environment.
+                // This will make it very hard to diagnose issues with JCL.
+                // Consider running less securely whilst debugging this issue.
+                //
+                logDiagnostic("[CUSTOM LOG FACTORY] SecurityException thrown whilst trying to determine whether " +
+                        "the compatibility was caused by a classloader conflict: "
+                        + e.getMessage());
+            } catch (LinkageError e) {
+                //
+                // This should be an unusual circumstance.
+                // LinkageError's usually indicate that a dependent class has incompatibly changed.
+                // Another possibility may be an exception thrown by an initializer.
+                // Time for a clean rebuild?
+                //
+                logDiagnostic("[CUSTOM LOG FACTORY] LinkageError thrown whilst trying to determine whether " +
+                        "the compatibility was caused by a classloader conflict: "
+                        + e.getMessage());
+            } catch (ClassNotFoundException e) {
+                //
+                // LogFactory cannot be loaded by the classloader which loaded the custom factory implementation.
+                // The custom implementation is not viable until this is corrected.
+                // Ensure that the JCL jar and the custom class are available from the same classloader.
+                // Running with diagnostics on should give information about the classloaders used
+                // to load the custom factory.
+                //
+                logDiagnostic("[CUSTOM LOG FACTORY] LogFactory class cannot be loaded by classloader which loaded the " +
+                        "custom LogFactory implementation. Is the custom factory in the right classloader?");
+            }
+        }
+        return implementsLogFactory;
+    }
+
+    /**
+     * Applets may run in an environment where accessing resources of a loader is
+     * a secure operation, but where the commons-logging library has explicitly
+     * been granted permission for that operation. In this case, we need to 
+     * run the operation using an AccessController.
+     */
+    private static InputStream getResourceAsStream(final ClassLoader loader,
+                                                   final String name)
+    {
+        return (InputStream)AccessController.doPrivileged(
+            new PrivilegedAction() {
+                public Object run() {
+                    if (loader != null) {
+                        return loader.getResourceAsStream(name);
+                    } else {
+                        return ClassLoader.getSystemResourceAsStream(name);
+                    }
+                }
+            });
+    }
+
+    /**
+     * Given a filename, return an enumeration of URLs pointing to
+     * all the occurrences of that filename in the classpath.
+     * <p>
+     * This is just like ClassLoader.getResources except that the
+     * operation is done under an AccessController so that this method will
+     * succeed when this jarfile is privileged but the caller is not.
+     * This method must therefore remain private to avoid security issues.
+     * <p>
+     * If no instances are found, an Enumeration is returned whose
+     * hasMoreElements method returns false (ie an "empty" enumeration).
+     * If resources could not be listed for some reason, null is returned.
+     */
+    private static Enumeration getResources(final ClassLoader loader,
+            final String name)
+    {
+        PrivilegedAction action = 
+            new PrivilegedAction() {
+                public Object run() {
+                    try {
+                        if (loader != null) {
+                            return loader.getResources(name);
+                        } else {
+                            return ClassLoader.getSystemResources(name);
+                        }
+                    } catch(IOException e) {
+                        if (isDiagnosticsEnabled()) {
+                            logDiagnostic(
+                                "Exception while trying to find configuration file "
+                                + name + ":" + e.getMessage());
+                        }
+                        return null;
+                    } catch(NoSuchMethodError e) {
+                        // we must be running on a 1.1 JVM which doesn't support
+                        // ClassLoader.getSystemResources; just return null in
+                        // this case.
+                        return null;
+                    }
+                }
+            };
+        Object result = AccessController.doPrivileged(action);
+        return (Enumeration) result;
+    }
+
+    /**
+     * Given a URL that refers to a .properties file, load that file.
+     * This is done under an AccessController so that this method will
+     * succeed when this jarfile is privileged but the caller is not.
+     * This method must therefore remain private to avoid security issues.
+     * <p>
+     * Null is returned if the URL cannot be opened.
+     */
+    private static Properties getProperties(final URL url) {
+        PrivilegedAction action = 
+            new PrivilegedAction() {
+                public Object run() {
+                    try {
+                        InputStream stream = url.openStream();
+                        if (stream != null) {
+                            Properties props = new Properties();
+                            props.load(stream);
+                            stream.close();
+                            return props;
+                        }
+                    } catch(IOException e) {
+                        if (isDiagnosticsEnabled()) {
+                            logDiagnostic("Unable to read URL " + url);
+                        }
+                    }
+
+                    return null;
+                }
+            };
+        return (Properties) AccessController.doPrivileged(action);
+    }
+
+    /**
+     * Locate a user-provided configuration file.
+     * <p>
+     * The classpath of the specified classLoader (usually the context classloader)
+     * is searched for properties files of the specified name. If none is found,
+     * null is returned. If more than one is found, then the file with the greatest
+     * value for its PRIORITY property is returned. If multiple files have the
+     * same PRIORITY value then the first in the classpath is returned.
+     * <p> 
+     * This differs from the 1.0.x releases; those always use the first one found.
+     * However as the priority is a new field, this change is backwards compatible.
+     * <p>
+     * The purpose of the priority field is to allow a webserver administrator to
+     * override logging settings in all webapps by placing a commons-logging.properties
+     * file in a shared classpath location with a priority > 0; this overrides any
+     * commons-logging.properties files without priorities which are in the
+     * webapps. Webapps can also use explicit priorities to override a configuration
+     * file in the shared classpath if needed. 
+     */
+    private static final Properties getConfigurationFile(
+            ClassLoader classLoader, String fileName) {
+
+        Properties props = null;
+        double priority = 0.0;
+        URL propsUrl = null;
+        try {
+            Enumeration urls = getResources(classLoader, fileName);
+
+            if (urls == null) {
+                return null;
+            }
+            
+            while (urls.hasMoreElements()) {
+                URL url = (URL) urls.nextElement();
+                
+                Properties newProps = getProperties(url);
+                if (newProps != null) {
+                    if (props == null) {
+                        propsUrl = url; 
+                        props = newProps;
+                        String priorityStr = props.getProperty(PRIORITY_KEY);
+                        priority = 0.0;
+                        if (priorityStr != null) {
+                            priority = Double.parseDouble(priorityStr);
+                        }
+
+                        if (isDiagnosticsEnabled()) {
+                            logDiagnostic(
+                                "[LOOKUP] Properties file found at '" + url + "'"
+                                + " with priority " + priority); 
+                        }
+                    } else {
+                        String newPriorityStr = newProps.getProperty(PRIORITY_KEY);
+                        double newPriority = 0.0;
+                        if (newPriorityStr != null) {
+                            newPriority = Double.parseDouble(newPriorityStr);
+                        }
+
+                        if (newPriority > priority) {
+                            if (isDiagnosticsEnabled()) {
+                                logDiagnostic(
+                                    "[LOOKUP] Properties file at '" + url + "'"
+                                    + " with priority " + newPriority 
+                                    + " overrides file at '" + propsUrl + "'"
+                                    + " with priority " + priority);
+                            }
+
+                            propsUrl = url; 
+                            props = newProps;
+                            priority = newPriority;
+                        } else {
+                            if (isDiagnosticsEnabled()) {
+                                logDiagnostic(
+                                    "[LOOKUP] Properties file at '" + url + "'"
+                                    + " with priority " + newPriority 
+                                    + " does not override file at '" + propsUrl + "'"
+                                    + " with priority " + priority);
+                            }
+                        }
+                    }
+
+                }
+            }
+        } catch (SecurityException e) {
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("SecurityException thrown while trying to find/read config files.");
+            }
+        }
+
+        if (isDiagnosticsEnabled()) {
+            if (props == null) {
+                logDiagnostic(
+                    "[LOOKUP] No properties file of name '" + fileName
+                    + "' found.");
+            } else {
+                logDiagnostic(
+                    "[LOOKUP] Properties file of name '" + fileName
+                    + "' found at '" + propsUrl + '"');
+            }
+        }
+
+        return props;
+    }
+
+    /**
+     * Determines whether the user wants internal diagnostic output. If so,
+     * returns an appropriate writer object. Users can enable diagnostic
+     * output by setting the system property named {@link #DIAGNOSTICS_DEST_PROPERTY} to
+     * a filename, or the special values STDOUT or STDERR. 
+     */
+    private static void initDiagnostics() {
+        String dest;
+    	try {
+    	    dest = System.getProperty(DIAGNOSTICS_DEST_PROPERTY);
+    	    if (dest == null) {
+    	        return;
+    	    }
+    	} catch(SecurityException ex) {
+    	    // We must be running in some very secure environment.
+    	    // We just have to assume output is not wanted..
+    	    return;
+    	}
+    	
+    	if (dest.equals("STDOUT")) {
+    	    diagnosticsStream = System.out;
+    	} else if (dest.equals("STDERR")) {
+    	    diagnosticsStream = System.err;
+    	} else {
+    	    try {
+                // open the file in append mode
+    	        FileOutputStream fos = new FileOutputStream(dest, true);
+    	        diagnosticsStream = new PrintStream(fos);
+    	    } catch(IOException ex) {
+    	        // We should report this to the user - but how?
+    	        return;
+    	    }
+    	}
+
+        // In order to avoid confusion where multiple instances of JCL are
+        // being used via different classloaders within the same app, we
+        // ensure each logged message has a prefix of form
+        // [LogFactory from classloader OID]
+        //
+        // Note that this prefix should be kept consistent with that 
+        // in LogFactoryImpl. However here we don't need to output info
+        // about the actual *instance* of LogFactory, as all methods that
+        // output diagnostics from this class are static.
+        String classLoaderName;
+        try {
+            ClassLoader classLoader = thisClassLoader;
+            if (thisClassLoader == null) {
+                classLoaderName = "BOOTLOADER";
+            } else {
+                classLoaderName = objectId(classLoader);
+            }
+        } catch(SecurityException e) {
+            classLoaderName = "UNKNOWN";
+        }
+        diagnosticPrefix = "[LogFactory from " + classLoaderName + "] ";
+    }
+
+    /**
+     * Indicates true if the user has enabled internal logging.
+     * <p>
+     * By the way, sorry for the incorrect grammar, but calling this method
+     * areDiagnosticsEnabled just isn't java beans style.
+     * 
+     * @return true if calls to logDiagnostic will have any effect.
+     * @since 1.1
+     */
+    protected static boolean isDiagnosticsEnabled() {
+        return diagnosticsStream != null;
+    }
+
+    /**
+     * Write the specified message to the internal logging destination.
+     * <p>
+     * Note that this method is private; concrete subclasses of this class
+     * should not call it because the diagnosticPrefix string this
+     * method puts in front of all its messages is LogFactory@....,
+     * while subclasses should put SomeSubClass@...
+     * <p>
+     * Subclasses should instead compute their own prefix, then call
+     * logRawDiagnostic. Note that calling isDiagnosticsEnabled is
+     * fine for subclasses.
+     * <p>
+     * Note that it is safe to call this method before initDiagnostics
+     * is called; any output will just be ignored (as isDiagnosticsEnabled
+     * will return false).
+     * 
+     * @param msg is the diagnostic message to be output.
+     */
+    private static final void logDiagnostic(String msg) {
+        if (diagnosticsStream != null) {
+            diagnosticsStream.print(diagnosticPrefix);
+            diagnosticsStream.println(msg);
+            diagnosticsStream.flush();
+        }
+    }
+
+    /**
+     * Write the specified message to the internal logging destination.
+     * 
+     * @param msg is the diagnostic message to be output.
+     * @since 1.1
+     */
+    protected static final void logRawDiagnostic(String msg) {
+        if (diagnosticsStream != null) {
+            diagnosticsStream.println(msg);
+            diagnosticsStream.flush();
+        }
+    }
+
+    /**
+     * Generate useful diagnostics regarding the classloader tree for
+     * the specified class.
+     * <p>
+     * As an example, if the specified class was loaded via a webapp's
+     * classloader, then you may get the following output:
+     * <pre>
+     * Class com.acme.Foo was loaded via classloader 11111
+     * ClassLoader tree: 11111 -> 22222 (SYSTEM) -> 33333 -> BOOT 
+     * </pre>
+     * <p>
+     * This method returns immediately if isDiagnosticsEnabled()
+     * returns false.
+     * 
+     * @param clazz is the class whose classloader + tree are to be
+     * output.
+     */
+    private static void logClassLoaderEnvironment(Class clazz) {
+        if (!isDiagnosticsEnabled()) {
+            return;
+        }
+        
+        try {
+            logDiagnostic("[ENV] Extension directories (java.ext.dir): " + System.getProperty("java.ext.dir"));
+            logDiagnostic("[ENV] Application classpath (java.class.path): " + System.getProperty("java.class.path"));
+        } catch(SecurityException ex) {
+            logDiagnostic("[ENV] Security setting prevent interrogation of system classpaths.");
+        }
+        
+        String className = clazz.getName();
+        ClassLoader classLoader;
+        
+        try {
+            classLoader = getClassLoader(clazz);
+        } catch(SecurityException ex) {
+            // not much useful diagnostics we can print here!
+            logDiagnostic(
+                "[ENV] Security forbids determining the classloader for " + className);
+            return;
+        }
+
+        logDiagnostic(
+            "[ENV] Class " + className + " was loaded via classloader "
+            + objectId(classLoader));
+        logHierarchy("[ENV] Ancestry of classloader which loaded " + className + " is ", classLoader);
+    }
+
+    /**
+     * Logs diagnostic messages about the given classloader
+     * and it's hierarchy. The prefix is prepended to the message
+     * and is intended to make it easier to understand the logs.
+     * @param prefix 
+     * @param classLoader
+     */
+    private static void logHierarchy(String prefix, ClassLoader classLoader) {
+        if (!isDiagnosticsEnabled()) {
+            return;
+        }
+        ClassLoader systemClassLoader;
+        if (classLoader != null) {
+            final String classLoaderString = classLoader.toString();
+            logDiagnostic(prefix + objectId(classLoader) + " == '" + classLoaderString + "'");
+        }
+        
+        try {
+            systemClassLoader = ClassLoader.getSystemClassLoader();
+        } catch(SecurityException ex) {
+            logDiagnostic(
+                    prefix + "Security forbids determining the system classloader.");
+            return;
+        }        
+        if (classLoader != null) {
+            StringBuffer buf = new StringBuffer(prefix + "ClassLoader tree:");
+            for(;;) {
+                buf.append(objectId(classLoader));
+                if (classLoader == systemClassLoader) {
+                    buf.append(" (SYSTEM) ");
+                }
+
+                try {
+                    classLoader = classLoader.getParent();
+                } catch(SecurityException ex) {
+                    buf.append(" --> SECRET");
+                    break;
+                }
+
+                buf.append(" --> ");
+                if (classLoader == null) {
+                    buf.append("BOOT");
+                    break;
+                }
+            }
+            logDiagnostic(buf.toString());
+        }
+    }
+
+    /**
+     * Returns a string that uniquely identifies the specified object, including
+     * its class.
+     * <p>
+     * The returned string is of form "classname@hashcode", ie is the same as
+     * the return value of the Object.toString() method, but works even when
+     * the specified object's class has overidden the toString method.
+     * 
+     * @param o may be null.
+     * @return a string of form classname@hashcode, or "null" if param o is null.
+     * @since 1.1
+     */
+    public static String objectId(Object o) {
+        if (o == null) {
+            return "null";
+        } else {
+            return o.getClass().getName() + "@" + System.identityHashCode(o);
+        }
+    }
+
+    // ----------------------------------------------------------------------
+    // Static initialiser block to perform initialisation at class load time.
+    //
+    // We can't do this in the class constructor, as there are many 
+    // static methods on this class that can be called before any
+    // LogFactory instances are created, and they depend upon this
+    // stuff having been set up.
+    //
+    // Note that this block must come after any variable declarations used
+    // by any methods called from this block, as we want any static initialiser
+    // associated with the variable to run first. If static initialisers for
+    // variables run after this code, then (a) their value might be needed
+    // by methods called from here, and (b) they might *override* any value
+    // computed here!
+    //
+    // So the wisest thing to do is just to place this code at the very end
+    // of the class file.
+    // ----------------------------------------------------------------------
+
+    static {
+        // note: it's safe to call methods before initDiagnostics.
+        thisClassLoader = getClassLoader(LogFactory.class);
+        initDiagnostics();
+        logClassLoaderEnvironment(LogFactory.class);
+        factories = createFactoryStore();
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("BOOTSTRAP COMPLETED");
+        }
+    }
+}
diff --git a/src/org/apache/commons/logging/LogSource.java b/src/org/apache/commons/logging/LogSource.java
new file mode 100644
index 0000000..e3c0603
--- /dev/null
+++ b/src/org/apache/commons/logging/LogSource.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.logging;
+
+
+import java.lang.reflect.Constructor;
+import java.util.Hashtable;
+
+import org.apache.commons.logging.impl.NoOpLog;
+
+
+/**
+ * <p>Factory for creating {@link Log} instances.  Applications should call
+ * the <code>makeNewLogInstance()</code> method to instantiate new instances
+ * of the configured {@link Log} implementation class.</p>
+ *
+ * <p>By default, calling <code>getInstance()</code> will use the following
+ * algorithm:</p>
+ * <ul>
+ * <li>If Log4J is available, return an instance of
+ *     <code>org.apache.commons.logging.impl.Log4JLogger</code>.</li>
+ * <li>If JDK 1.4 or later is available, return an instance of
+ *     <code>org.apache.commons.logging.impl.Jdk14Logger</code>.</li>
+ * <li>Otherwise, return an instance of
+ *     <code>org.apache.commons.logging.impl.NoOpLog</code>.</li>
+ * </ul>
+ *
+ * <p>You can change the default behavior in one of two ways:</p>
+ * <ul>
+ * <li>On the startup command line, set the system property
+ *     <code>org.apache.commons.logging.log</code> to the name of the
+ *     <code>org.apache.commons.logging.Log</code> implementation class
+ *     you want to use.</li>
+ * <li>At runtime, call <code>LogSource.setLogImplementation()</code>.</li>
+ * </ul>
+ *
+ * @deprecated Use {@link LogFactory} instead - The default factory
+ *  implementation performs exactly the same algorithm as this class did
+ *
+ * @author Rod Waldhoff
+ * @version $Id: LogSource.java 155426 2005-02-26 13:10:49Z dirkv $
+ */
+public class LogSource {
+
+    // ------------------------------------------------------- Class Attributes
+
+    static protected Hashtable logs = new Hashtable();
+
+    /** Is log4j available (in the current classpath) */
+    static protected boolean log4jIsAvailable = false;
+
+    /** Is JDK 1.4 logging available */
+    static protected boolean jdk14IsAvailable = false;
+
+    /** Constructor for current log class */
+    static protected Constructor logImplctor = null;
+
+
+    // ----------------------------------------------------- Class Initializers
+
+    static {
+
+        // Is Log4J Available?
+        try {
+            if (null != Class.forName("org.apache.log4j.Logger")) {
+                log4jIsAvailable = true;
+            } else {
+                log4jIsAvailable = false;
+            }
+        } catch (Throwable t) {
+            log4jIsAvailable = false;
+        }
+
+        // Is JDK 1.4 Logging Available?
+        try {
+            if ((null != Class.forName("java.util.logging.Logger")) &&
+                (null != Class.forName("org.apache.commons.logging.impl.Jdk14Logger"))) {
+                jdk14IsAvailable = true;
+            } else {
+                jdk14IsAvailable = false;
+            }
+        } catch (Throwable t) {
+            jdk14IsAvailable = false;
+        }
+
+        // Set the default Log implementation
+        String name = null;
+        try {
+            name = System.getProperty("org.apache.commons.logging.log");
+            if (name == null) {
+                name = System.getProperty("org.apache.commons.logging.Log");
+            }
+        } catch (Throwable t) {
+        }
+        if (name != null) {
+            try {
+                setLogImplementation(name);
+            } catch (Throwable t) {
+                try {
+                    setLogImplementation
+                            ("org.apache.commons.logging.impl.NoOpLog");
+                } catch (Throwable u) {
+                    ;
+                }
+            }
+        } else {
+            try {
+                if (log4jIsAvailable) {
+                    setLogImplementation
+                            ("org.apache.commons.logging.impl.Log4JLogger");
+                } else if (jdk14IsAvailable) {
+                    setLogImplementation
+                            ("org.apache.commons.logging.impl.Jdk14Logger");
+                } else {
+                    setLogImplementation
+                            ("org.apache.commons.logging.impl.NoOpLog");
+                }
+            } catch (Throwable t) {
+                try {
+                    setLogImplementation
+                            ("org.apache.commons.logging.impl.NoOpLog");
+                } catch (Throwable u) {
+                    ;
+                }
+            }
+        }
+
+    }
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /** Don't allow others to create instances */
+    private LogSource() {
+    }
+
+
+    // ---------------------------------------------------------- Class Methods
+
+
+    /**
+     * Set the log implementation/log implementation factory
+     * by the name of the class.  The given class
+     * must implement {@link Log}, and provide a constructor that
+     * takes a single {@link String} argument (containing the name
+     * of the log).
+     */
+    static public void setLogImplementation(String classname) throws
+            LinkageError, ExceptionInInitializerError,
+            NoSuchMethodException, SecurityException,
+            ClassNotFoundException {
+        try {
+            Class logclass = Class.forName(classname);
+            Class[] argtypes = new Class[1];
+            argtypes[0] = "".getClass();
+            logImplctor = logclass.getConstructor(argtypes);
+        } catch (Throwable t) {
+            logImplctor = null;
+        }
+    }
+
+
+    /**
+     * Set the log implementation/log implementation factory
+     * by class.  The given class must implement {@link Log},
+     * and provide a constructor that takes a single {@link String}
+     * argument (containing the name of the log).
+     */
+    static public void setLogImplementation(Class logclass) throws
+            LinkageError, ExceptionInInitializerError,
+            NoSuchMethodException, SecurityException {
+        Class[] argtypes = new Class[1];
+        argtypes[0] = "".getClass();
+        logImplctor = logclass.getConstructor(argtypes);
+    }
+
+
+    /** Get a <code>Log</code> instance by class name */
+    static public Log getInstance(String name) {
+        Log log = (Log) (logs.get(name));
+        if (null == log) {
+            log = makeNewLogInstance(name);
+            logs.put(name, log);
+        }
+        return log;
+    }
+
+
+    /** Get a <code>Log</code> instance by class */
+    static public Log getInstance(Class clazz) {
+        return getInstance(clazz.getName());
+    }
+
+
+    /**
+     * Create a new {@link Log} implementation, based
+     * on the given <i>name</i>.
+     * <p>
+     * The specific {@link Log} implementation returned
+     * is determined by the value of the
+     * <tt>org.apache.commons.logging.log</tt> property.
+     * The value of <tt>org.apache.commons.logging.log</tt> may be set to
+     * the fully specified name of a class that implements
+     * the {@link Log} interface.  This class must also
+     * have a public constructor that takes a single
+     * {@link String} argument (containing the <i>name</i>
+     * of the {@link Log} to be constructed.
+     * <p>
+     * When <tt>org.apache.commons.logging.log</tt> is not set,
+     * or when no corresponding class can be found,
+     * this method will return a Log4JLogger
+     * if the log4j Logger class is
+     * available in the {@link LogSource}'s classpath, or a
+     * Jdk14Logger if we are on a JDK 1.4 or later system, or
+     * NoOpLog if neither of the above conditions is true.
+     *
+     * @param name the log name (or category)
+     */
+    static public Log makeNewLogInstance(String name) {
+
+        Log log = null;
+        try {
+            Object[] args = new Object[1];
+            args[0] = name;
+            log = (Log) (logImplctor.newInstance(args));
+        } catch (Throwable t) {
+            log = null;
+        }
+        if (null == log) {
+            log = new NoOpLog(name);
+        }
+        return log;
+
+    }
+
+
+    /**
+     * Returns a {@link String} array containing the names of
+     * all logs known to me.
+     */
+    static public String[] getLogNames() {
+        return (String[]) (logs.keySet().toArray(new String[logs.size()]));
+    }
+
+
+}
diff --git a/src/org/apache/commons/logging/impl/Jdk14Logger.java b/src/org/apache/commons/logging/impl/Jdk14Logger.java
new file mode 100644
index 0000000..d4f840c
--- /dev/null
+++ b/src/org/apache/commons/logging/impl/Jdk14Logger.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.logging.impl;
+
+
+import java.io.Serializable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.commons.logging.Log;
+
+
+/**
+ * <p>Implementation of the <code>org.apache.commons.logging.Log</code>
+ * interface that wraps the standard JDK logging mechanisms that were
+ * introduced in the Merlin release (JDK 1.4).</p>
+ *
+ * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
+ * @author <a href="mailto:bloritsch@apache.org">Berin Loritsch</a>
+ * @author <a href="mailto:donaldp@apache.org">Peter Donald</a>
+ * @version $Revision: 370652 $ $Date: 2006-01-19 22:23:48 +0000 (Thu, 19 Jan 2006) $
+ */
+
+public class Jdk14Logger implements Log, Serializable {
+
+    /**
+     * This member variable simply ensures that any attempt to initialise
+     * this class in a pre-1.4 JVM will result in an ExceptionInInitializerError.
+     * It must not be private, as an optimising compiler could detect that it
+     * is not used and optimise it away.
+     */
+    protected static final Level dummyLevel = Level.FINE;
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a named instance of this Logger.
+     *
+     * @param name Name of the logger to be constructed
+     */
+    public Jdk14Logger(String name) {
+
+        this.name = name;
+        logger = getLogger();
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The underlying Logger implementation we are using.
+     */
+    protected transient Logger logger = null;
+
+
+    /**
+     * The name of the logger we are wrapping.
+     */
+    protected String name = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+    private void log( Level level, String msg, Throwable ex ) {
+
+        Logger logger = getLogger();
+        if (logger.isLoggable(level)) {
+            // Hack (?) to get the stack trace.
+            Throwable dummyException=new Throwable();
+            StackTraceElement locations[]=dummyException.getStackTrace();
+            // Caller will be the third element
+            String cname="unknown";
+            String method="unknown";
+            if( locations!=null && locations.length >2 ) {
+                StackTraceElement caller=locations[2];
+                cname=caller.getClassName();
+                method=caller.getMethodName();
+            }
+            if( ex==null ) {
+                logger.logp( level, cname, method, msg );
+            } else {
+                logger.logp( level, cname, method, msg, ex );
+            }
+        }
+
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.FINE</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#debug(Object)
+     */
+    public void debug(Object message) {
+        log(Level.FINE, String.valueOf(message), null);
+    }
+
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.FINE</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#debug(Object, Throwable)
+     */
+    public void debug(Object message, Throwable exception) {
+        log(Level.FINE, String.valueOf(message), exception);
+    }
+
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.SEVERE</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#error(Object)
+     */
+    public void error(Object message) {
+        log(Level.SEVERE, String.valueOf(message), null);
+    }
+
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.SEVERE</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#error(Object, Throwable)
+     */
+    public void error(Object message, Throwable exception) {
+        log(Level.SEVERE, String.valueOf(message), exception);
+    }
+
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.SEVERE</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#fatal(Object)
+     */
+    public void fatal(Object message) {
+        log(Level.SEVERE, String.valueOf(message), null);
+    }
+
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.SEVERE</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#fatal(Object, Throwable)
+     */
+    public void fatal(Object message, Throwable exception) {
+        log(Level.SEVERE, String.valueOf(message), exception);
+    }
+
+
+    /**
+     * Return the native Logger instance we are using.
+     */
+    public Logger getLogger() {
+        if (logger == null) {
+            logger = Logger.getLogger(name);
+        }
+        return (logger);
+    }
+
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.INFO</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#info(Object)
+     */
+    public void info(Object message) {
+        log(Level.INFO, String.valueOf(message), null);
+    }
+
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.INFO</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#info(Object, Throwable)
+     */
+    public void info(Object message, Throwable exception) {
+        log(Level.INFO, String.valueOf(message), exception);
+    }
+
+
+    /**
+     * Is debug logging currently enabled?
+     */
+    public boolean isDebugEnabled() {
+        return (getLogger().isLoggable(Level.FINE));
+    }
+
+
+    /**
+     * Is error logging currently enabled?
+     */
+    public boolean isErrorEnabled() {
+        return (getLogger().isLoggable(Level.SEVERE));
+    }
+
+
+    /**
+     * Is fatal logging currently enabled?
+     */
+    public boolean isFatalEnabled() {
+        return (getLogger().isLoggable(Level.SEVERE));
+    }
+
+
+    /**
+     * Is info logging currently enabled?
+     */
+    public boolean isInfoEnabled() {
+        return (getLogger().isLoggable(Level.INFO));
+    }
+
+
+    /**
+     * Is trace logging currently enabled?
+     */
+    public boolean isTraceEnabled() {
+        return (getLogger().isLoggable(Level.FINEST));
+    }
+
+
+    /**
+     * Is warn logging currently enabled?
+     */
+    public boolean isWarnEnabled() {
+        return (getLogger().isLoggable(Level.WARNING));
+    }
+
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.FINEST</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#trace(Object)
+     */
+    public void trace(Object message) {
+        log(Level.FINEST, String.valueOf(message), null);
+    }
+
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.FINEST</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#trace(Object, Throwable)
+     */
+    public void trace(Object message, Throwable exception) {
+        log(Level.FINEST, String.valueOf(message), exception);
+    }
+
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.WARNING</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#warn(Object)
+     */
+    public void warn(Object message) {
+        log(Level.WARNING, String.valueOf(message), null);
+    }
+
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.WARNING</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#warn(Object, Throwable)
+     */
+    public void warn(Object message, Throwable exception) {
+        log(Level.WARNING, String.valueOf(message), exception);
+    }
+
+
+}
diff --git a/src/org/apache/commons/logging/impl/LogFactoryImpl.java b/src/org/apache/commons/logging/impl/LogFactoryImpl.java
new file mode 100644
index 0000000..8937b2f
--- /dev/null
+++ b/src/org/apache/commons/logging/impl/LogFactoryImpl.java
@@ -0,0 +1,1400 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.logging.impl;
+
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogConfigurationException;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * <p>Concrete subclass of {@link LogFactory} that implements the
+ * following algorithm to dynamically select a logging implementation
+ * class to instantiate a wrapper for.</p>
+ * <ul>
+ * <li>Use a factory configuration attribute named
+ *     <code>org.apache.commons.logging.Log</code> to identify the
+ *     requested implementation class.</li>
+ * <li>Use the <code>org.apache.commons.logging.Log</code> system property
+ *     to identify the requested implementation class.</li>
+ * <li>If <em>Log4J</em> is available, return an instance of
+ *     <code>org.apache.commons.logging.impl.Log4JLogger</code>.</li>
+ * <li>If <em>JDK 1.4 or later</em> is available, return an instance of
+ *     <code>org.apache.commons.logging.impl.Jdk14Logger</code>.</li>
+ * <li>Otherwise, return an instance of
+ *     <code>org.apache.commons.logging.impl.SimpleLog</code>.</li>
+ * </ul>
+ *
+ * <p>If the selected {@link Log} implementation class has a
+ * <code>setLogFactory()</code> method that accepts a {@link LogFactory}
+ * parameter, this method will be called on each newly created instance
+ * to identify the associated factory.  This makes factory configuration
+ * attributes available to the Log instance, if it so desires.</p>
+ *
+ * <p>This factory will remember previously created <code>Log</code> instances
+ * for the same name, and will return them on repeated requests to the
+ * <code>getInstance()</code> method.</p>
+ *
+ * @author Rod Waldhoff
+ * @author Craig R. McClanahan
+ * @author Richard A. Sitze
+ * @author Brian Stansberry
+ * @version $Revision: 399224 $ $Date: 2006-05-03 10:25:54 +0100 (Wed, 03 May 2006) $
+ */
+
+public class LogFactoryImpl extends LogFactory {
+
+
+    /** Log4JLogger class name */
+    private static final String LOGGING_IMPL_LOG4J_LOGGER = "org.apache.commons.logging.impl.Log4JLogger";
+    /** Jdk14Logger class name */
+    private static final String LOGGING_IMPL_JDK14_LOGGER = "org.apache.commons.logging.impl.Jdk14Logger";
+    /** Jdk13LumberjackLogger class name */
+    private static final String LOGGING_IMPL_LUMBERJACK_LOGGER = "org.apache.commons.logging.impl.Jdk13LumberjackLogger";
+    /** SimpleLog class name */
+    private static final String LOGGING_IMPL_SIMPLE_LOGGER = "org.apache.commons.logging.impl.SimpleLog";
+
+    private static final String PKG_IMPL="org.apache.commons.logging.impl.";
+    private static final int PKG_LEN = PKG_IMPL.length();
+    
+    // ----------------------------------------------------------- Constructors
+
+   
+
+    /**
+     * Public no-arguments constructor required by the lookup mechanism.
+     */
+    public LogFactoryImpl() {
+        super();
+        initDiagnostics();  // method on this object
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("Instance created.");
+        }
+    }
+
+
+    // ----------------------------------------------------- Manifest Constants
+
+
+    /**
+     * The name (<code>org.apache.commons.logging.Log</code>) of the system 
+     * property identifying our {@link Log} implementation class.
+     */
+    public static final String LOG_PROPERTY =
+        "org.apache.commons.logging.Log";
+
+
+    /**
+     * The deprecated system property used for backwards compatibility with
+     * old versions of JCL.
+     */
+    protected static final String LOG_PROPERTY_OLD =
+        "org.apache.commons.logging.log";
+
+    /**
+     * The name (<code>org.apache.commons.logging.Log.allowFlawedContext</code>) 
+     * of the system property which can be set true/false to
+     * determine system behaviour when a bad context-classloader is encountered.
+     * When set to false, a LogConfigurationException is thrown if
+     * LogFactoryImpl is loaded via a child classloader of the TCCL (this
+     * should never happen in sane systems).
+     * 
+     * Default behaviour: true (tolerates bad context classloaders)
+     * 
+     * See also method setAttribute.
+     */
+    public static final String ALLOW_FLAWED_CONTEXT_PROPERTY = 
+        "org.apache.commons.logging.Log.allowFlawedContext";
+
+    /**
+     * The name (<code>org.apache.commons.logging.Log.allowFlawedDiscovery</code>) 
+     * of the system property which can be set true/false to
+     * determine system behaviour when a bad logging adapter class is
+     * encountered during logging discovery. When set to false, an
+     * exception will be thrown and the app will fail to start. When set
+     * to true, discovery will continue (though the user might end up
+     * with a different logging implementation than they expected).
+     * 
+     * Default behaviour: true (tolerates bad logging adapters)
+     * 
+     * See also method setAttribute.
+     */
+    public static final String ALLOW_FLAWED_DISCOVERY_PROPERTY = 
+        "org.apache.commons.logging.Log.allowFlawedDiscovery";
+
+    /**
+     * The name (<code>org.apache.commons.logging.Log.allowFlawedHierarchy</code>) 
+     * of the system property which can be set true/false to
+     * determine system behaviour when a logging adapter class is
+     * encountered which has bound to the wrong Log class implementation.
+     * When set to false, an exception will be thrown and the app will fail
+     * to start. When set to true, discovery will continue (though the user
+     * might end up with a different logging implementation than they expected).
+     * 
+     * Default behaviour: true (tolerates bad Log class hierarchy)
+     * 
+     * See also method setAttribute.
+     */
+    public static final String ALLOW_FLAWED_HIERARCHY_PROPERTY = 
+        "org.apache.commons.logging.Log.allowFlawedHierarchy";
+
+
+    /**
+     * The names of classes that will be tried (in order) as logging
+     * adapters. Each class is expected to implement the Log interface,
+     * and to throw NoClassDefFound or ExceptionInInitializerError when
+     * loaded if the underlying logging library is not available. Any 
+     * other error indicates that the underlying logging library is available
+     * but broken/unusable for some reason.
+     */
+    private static final String[] classesToDiscover = {
+            LOGGING_IMPL_LOG4J_LOGGER,
+            "org.apache.commons.logging.impl.Jdk14Logger",
+            "org.apache.commons.logging.impl.Jdk13LumberjackLogger",
+            "org.apache.commons.logging.impl.SimpleLog"
+    };
+    
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Determines whether logging classes should be loaded using the thread-context
+     * classloader, or via the classloader that loaded this LogFactoryImpl class.
+     */
+    private boolean useTCCL = true;
+
+    /**
+     * The string prefixed to every message output by the logDiagnostic method.
+     */
+    private String diagnosticPrefix;
+
+
+    /**
+     * Configuration attributes.
+     */
+    protected Hashtable attributes = new Hashtable();
+
+
+    /**
+     * The {@link org.apache.commons.logging.Log} instances that have
+     * already been created, keyed by logger name.
+     */
+    protected Hashtable instances = new Hashtable();
+
+
+    /**
+     * Name of the class implementing the Log interface.
+     */
+    private String logClassName;
+
+
+    /**
+     * The one-argument constructor of the
+     * {@link org.apache.commons.logging.Log}
+     * implementation class that will be used to create new instances.
+     * This value is initialized by <code>getLogConstructor()</code>,
+     * and then returned repeatedly.
+     */
+    protected Constructor logConstructor = null;
+
+
+    /**
+     * The signature of the Constructor to be used.
+     */
+    protected Class logConstructorSignature[] =
+    { java.lang.String.class };
+
+
+    /**
+     * The one-argument <code>setLogFactory</code> method of the selected
+     * {@link org.apache.commons.logging.Log} method, if it exists.
+     */
+    protected Method logMethod = null;
+
+
+    /**
+     * The signature of the <code>setLogFactory</code> method to be used.
+     */
+    protected Class logMethodSignature[] =
+    { LogFactory.class };
+
+    /**
+     * See getBaseClassLoader and initConfiguration.
+     */
+    private boolean allowFlawedContext;
+    
+    /**
+     * See handleFlawedDiscovery and initConfiguration.
+     */
+    private boolean allowFlawedDiscovery;
+    
+    /**
+     * See handleFlawedHierarchy and initConfiguration.
+     */
+    private boolean allowFlawedHierarchy;
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the configuration attribute with the specified name (if any),
+     * or <code>null</code> if there is no such attribute.
+     *
+     * @param name Name of the attribute to return
+     */
+    public Object getAttribute(String name) {
+
+        return (attributes.get(name));
+
+    }
+
+
+    /**
+     * Return an array containing the names of all currently defined
+     * configuration attributes.  If there are no such attributes, a zero
+     * length array is returned.
+     */
+    public String[] getAttributeNames() {
+
+        Vector names = new Vector();
+        Enumeration keys = attributes.keys();
+        while (keys.hasMoreElements()) {
+            names.addElement((String) keys.nextElement());
+        }
+        String results[] = new String[names.size()];
+        for (int i = 0; i < results.length; i++) {
+            results[i] = (String) names.elementAt(i);
+        }
+        return (results);
+
+    }
+
+
+    /**
+     * Convenience method to derive a name from the specified class and
+     * call <code>getInstance(String)</code> with it.
+     *
+     * @param clazz Class for which a suitable Log name will be derived
+     *
+     * @exception LogConfigurationException if a suitable <code>Log</code>
+     *  instance cannot be returned
+     */
+    public Log getInstance(Class clazz) throws LogConfigurationException {
+
+        return (getInstance(clazz.getName()));
+
+    }
+
+
+    /**
+     * <p>Construct (if necessary) and return a <code>Log</code> instance,
+     * using the factory's current set of configuration attributes.</p>
+     *
+     * <p><strong>NOTE</strong> - Depending upon the implementation of
+     * the <code>LogFactory</code> you are using, the <code>Log</code>
+     * instance you are returned may or may not be local to the current
+     * application, and may or may not be returned again on a subsequent
+     * call with the same name argument.</p>
+     *
+     * @param name Logical name of the <code>Log</code> instance to be
+     *  returned (the meaning of this name is only known to the underlying
+     *  logging implementation that is being wrapped)
+     *
+     * @exception LogConfigurationException if a suitable <code>Log</code>
+     *  instance cannot be returned
+     */
+    public Log getInstance(String name) throws LogConfigurationException {
+
+        Log instance = (Log) instances.get(name);
+        if (instance == null) {
+            instance = newInstance(name);
+            instances.put(name, instance);
+        }
+        return (instance);
+
+    }
+
+
+    /**
+     * Release any internal references to previously created
+     * {@link org.apache.commons.logging.Log}
+     * instances returned by this factory.  This is useful in environments
+     * like servlet containers, which implement application reloading by
+     * throwing away a ClassLoader.  Dangling references to objects in that
+     * class loader would prevent garbage collection.
+     */
+    public void release() {
+
+        logDiagnostic("Releasing all known loggers");
+        instances.clear();
+    }
+
+
+    /**
+     * Remove any configuration attribute associated with the specified name.
+     * If there is no such attribute, no action is taken.
+     *
+     * @param name Name of the attribute to remove
+     */
+    public void removeAttribute(String name) {
+
+        attributes.remove(name);
+
+    }
+
+
+    /**
+     * Set the configuration attribute with the specified name.  Calling
+     * this with a <code>null</code> value is equivalent to calling
+     * <code>removeAttribute(name)</code>.
+     * <p>
+     * This method can be used to set logging configuration programmatically
+     * rather than via system properties. It can also be used in code running
+     * within a container (such as a webapp) to configure behaviour on a
+     * per-component level instead of globally as system properties would do.
+     * To use this method instead of a system property, call
+     * <pre>
+     * LogFactory.getFactory().setAttribute(...)
+     * </pre>
+     * This must be done before the first Log object is created; configuration
+     * changes after that point will be ignored.
+     * <p>
+     * This method is also called automatically if LogFactory detects a
+     * commons-logging.properties file; every entry in that file is set
+     * automatically as an attribute here.
+     *
+     * @param name Name of the attribute to set
+     * @param value Value of the attribute to set, or <code>null</code>
+     *  to remove any setting for this attribute
+     */
+    public void setAttribute(String name, Object value) {
+
+        if (logConstructor != null) {
+            logDiagnostic("setAttribute: call too late; configuration already performed.");
+        }
+
+        if (value == null) {
+            attributes.remove(name);
+        } else {
+            attributes.put(name, value);
+        }
+        
+        if (name.equals(TCCL_KEY)) {
+            useTCCL = Boolean.valueOf(value.toString()).booleanValue();
+        }
+
+    }
+
+
+    // ------------------------------------------------------ 
+    // Static Methods
+    //
+    // These methods only defined as workarounds for a java 1.2 bug;
+    // theoretically none of these are needed.
+    // ------------------------------------------------------ 
+    
+    /**
+     * Gets the context classloader.
+     * This method is a workaround for a java 1.2 compiler bug.
+     * @since 1.1
+     */
+    protected static ClassLoader getContextClassLoader() throws LogConfigurationException {
+        return LogFactory.getContextClassLoader();
+    }
+
+    
+    /**
+     * Workaround for bug in Java1.2; in theory this method is not needed.
+     * See LogFactory.isDiagnosticsEnabled.
+     */
+    protected static boolean isDiagnosticsEnabled() {
+        return LogFactory.isDiagnosticsEnabled();
+    }
+
+    
+    /**
+     * Workaround for bug in Java1.2; in theory this method is not needed.
+     * See LogFactory.getClassLoader.
+     * @since 1.1
+     */
+    protected static ClassLoader getClassLoader(Class clazz) {
+        return LogFactory.getClassLoader(clazz);
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+    /**
+     * Calculate and cache a string that uniquely identifies this instance,
+     * including which classloader the object was loaded from.
+     * <p>
+     * This string will later be prefixed to each "internal logging" message
+     * emitted, so that users can clearly see any unexpected behaviour.
+     * <p>
+     * Note that this method does not detect whether internal logging is 
+     * enabled or not, nor where to output stuff if it is; that is all
+     * handled by the parent LogFactory class. This method just computes
+     * its own unique prefix for log messages.
+     */
+    private void initDiagnostics() {
+        // It would be nice to include an identifier of the context classloader
+        // that this LogFactoryImpl object is responsible for. However that
+        // isn't possible as that information isn't available. It is possible
+        // to figure this out by looking at the logging from LogFactory to
+        // see the context & impl ids from when this object was instantiated,
+        // in order to link the impl id output as this object's prefix back to
+        // the context it is intended to manage.
+        // Note that this prefix should be kept consistent with that 
+        // in LogFactory.
+        Class clazz = this.getClass();
+        ClassLoader classLoader = getClassLoader(clazz);
+        String classLoaderName;
+        try {
+            if (classLoader == null) {
+                classLoaderName = "BOOTLOADER";
+            } else {
+                classLoaderName = objectId(classLoader);
+            }
+        } catch(SecurityException e) {
+            classLoaderName = "UNKNOWN";
+        }
+        diagnosticPrefix = "[LogFactoryImpl@" + System.identityHashCode(this) + " from " + classLoaderName + "] ";
+    }
+
+    
+    /**
+     * Output a diagnostic message to a user-specified destination (if the
+     * user has enabled diagnostic logging).
+     * 
+     * @param msg diagnostic message
+     * @since 1.1
+     */
+    protected void logDiagnostic(String msg) {
+        if (isDiagnosticsEnabled()) {
+            logRawDiagnostic(diagnosticPrefix + msg);
+        }
+    }
+
+    /**
+     * Return the fully qualified Java classname of the {@link Log}
+     * implementation we will be using.  
+     * 
+     * @deprecated  Never invoked by this class; subclasses should not assume
+     *              it will be.
+     */
+    protected String getLogClassName() {
+
+        if (logClassName == null) {
+            discoverLogImplementation(getClass().getName());
+        }
+        
+        return logClassName;
+    }
+
+
+    /**
+     * <p>Return the <code>Constructor</code> that can be called to instantiate
+     * new {@link org.apache.commons.logging.Log} instances.</p>
+     *
+     * <p><strong>IMPLEMENTATION NOTE</strong> - Race conditions caused by
+     * calling this method from more than one thread are ignored, because
+     * the same <code>Constructor</code> instance will ultimately be derived
+     * in all circumstances.</p>
+     *
+     * @exception LogConfigurationException if a suitable constructor
+     *  cannot be returned   
+     * 
+     * @deprecated  Never invoked by this class; subclasses should not assume
+     *              it will be.
+     */
+    protected Constructor getLogConstructor()
+        throws LogConfigurationException {
+
+        // Return the previously identified Constructor (if any)
+        if (logConstructor == null) {
+            discoverLogImplementation(getClass().getName());
+        }
+
+        return logConstructor;
+    }
+    
+
+    /**
+     * Is <em>JDK 1.3 with Lumberjack</em> logging available?   
+     * 
+     * @deprecated  Never invoked by this class; subclasses should not assume
+     *              it will be.
+     */
+    protected boolean isJdk13LumberjackAvailable() {
+        return isLogLibraryAvailable(
+                "Jdk13Lumberjack",
+                "org.apache.commons.logging.impl.Jdk13LumberjackLogger");
+    }
+
+
+    /**
+     * <p>Return <code>true</code> if <em>JDK 1.4 or later</em> logging
+     * is available.  Also checks that the <code>Throwable</code> class
+     * supports <code>getStackTrace()</code>, which is required by
+     * Jdk14Logger.</p>  
+     * 
+     * @deprecated  Never invoked by this class; subclasses should not assume
+     *              it will be.
+     */
+    protected boolean isJdk14Available() {
+        return isLogLibraryAvailable(
+                "Jdk14",
+                "org.apache.commons.logging.impl.Jdk14Logger");
+    }
+
+
+    /**
+     * Is a <em>Log4J</em> implementation available? 
+     * 
+     * @deprecated  Never invoked by this class; subclasses should not assume
+     *              it will be.
+     */
+    protected boolean isLog4JAvailable() {
+        return isLogLibraryAvailable(
+                "Log4J",
+                LOGGING_IMPL_LOG4J_LOGGER);
+    }
+
+
+    /**
+     * Create and return a new {@link org.apache.commons.logging.Log}
+     * instance for the specified name.
+     *
+     * @param name Name of the new logger
+     *
+     * @exception LogConfigurationException if a new instance cannot
+     *  be created
+     */
+    protected Log newInstance(String name) throws LogConfigurationException {
+
+        Log instance = null;
+        try {
+            if (logConstructor == null) {
+                instance = discoverLogImplementation(name);
+            }
+            else {
+                Object params[] = { name };
+                instance = (Log) logConstructor.newInstance(params);
+            }
+            
+            if (logMethod != null) {
+                Object params[] = { this };
+                logMethod.invoke(instance, params);
+            }
+            
+            return (instance);
+            
+        } catch (LogConfigurationException lce) {
+            
+            // this type of exception means there was a problem in discovery
+            // and we've already output diagnostics about the issue, etc.; 
+            // just pass it on
+            throw (LogConfigurationException) lce;
+            
+        } catch (InvocationTargetException e) {
+            // A problem occurred invoking the Constructor or Method 
+            // previously discovered
+            Throwable c = e.getTargetException();
+            if (c != null) {
+                throw new LogConfigurationException(c);
+            } else {
+                throw new LogConfigurationException(e);
+            }
+        } catch (Throwable t) {
+            // A problem occurred invoking the Constructor or Method 
+            // previously discovered
+            throw new LogConfigurationException(t);
+        }
+    }
+    
+
+    //  ------------------------------------------------------ Private Methods
+    
+    /**
+     * Utility method to check whether a particular logging library is
+     * present and available for use. Note that this does <i>not</i>
+     * affect the future behaviour of this class.
+     */
+    private boolean isLogLibraryAvailable(String name, String classname) {
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("Checking for '" + name + "'.");
+        }
+        try {
+            Log log = createLogFromClass(
+                        classname, 
+                        this.getClass().getName(), // dummy category
+                        false);
+
+            if (log == null) {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic("Did not find '" + name + "'.");
+                }
+                return false;
+            } else {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic("Found '" + name + "'.");
+                }
+                return true;
+            }
+        } catch(LogConfigurationException e) {
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("Logging system '" + name + "' is available but not useable.");
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Attempt to find an attribute (see method setAttribute) or a 
+     * system property with the provided name and return its value.
+     * <p>
+     * The attributes associated with this object are checked before
+     * system properties in case someone has explicitly called setAttribute,
+     * or a configuration property has been set in a commons-logging.properties
+     * file.
+     * 
+     * @return the value associated with the property, or null.
+     */
+    private String getConfigurationValue(String property) {
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("[ENV] Trying to get configuration for item " + property);
+        }
+
+        Object valueObj =  getAttribute(property);
+        if (valueObj != null) {
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("[ENV] Found LogFactory attribute [" + valueObj + "] for " + property);
+            }
+            return valueObj.toString();
+        }
+        
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("[ENV] No LogFactory attribute found for " + property);
+        }
+
+        try {
+            String value = System.getProperty(property);
+            if (value != null) {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic("[ENV] Found system property [" + value + "] for " + property);
+                }
+                return value;
+            }
+
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("[ENV] No system property found for property " + property);
+            }
+        } catch (SecurityException e) {
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("[ENV] Security prevented reading system property " + property);
+            }
+        }
+
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("[ENV] No configuration defined for item " + property);
+        }
+
+        return null;
+    }
+    
+    /**
+     * Get the setting for the user-configurable behaviour specified by key.
+     * If nothing has explicitly been set, then return dflt.  
+     */
+    private boolean getBooleanConfiguration(String key, boolean dflt) {
+        String val = getConfigurationValue(key);
+        if (val == null)
+            return dflt;
+        return Boolean.valueOf(val).booleanValue();
+    }
+
+    /**
+     * Initialize a number of variables that control the behaviour of this
+     * class and that can be tweaked by the user. This is done when the first
+     * logger is created, not in the constructor of this class, because we
+     * need to give the user a chance to call method setAttribute in order to
+     * configure this object.
+     */
+    private void initConfiguration() {
+        allowFlawedContext = getBooleanConfiguration(ALLOW_FLAWED_CONTEXT_PROPERTY, true);
+        allowFlawedDiscovery = getBooleanConfiguration(ALLOW_FLAWED_DISCOVERY_PROPERTY, true);
+        allowFlawedHierarchy = getBooleanConfiguration(ALLOW_FLAWED_HIERARCHY_PROPERTY, true);
+    }
+  
+
+    /**
+     * Attempts to create a Log instance for the given category name.
+     * Follows the discovery process described in the class javadoc.
+     * 
+     * @param logCategory the name of the log category
+     * 
+     * @throws LogConfigurationException if an error in discovery occurs, 
+     * or if no adapter at all can be instantiated
+     */
+    private Log discoverLogImplementation(String logCategory)
+    throws LogConfigurationException
+    {
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("Discovering a Log implementation...");
+        }
+        
+        initConfiguration();
+        
+        Log result = null;
+        
+        // See if the user specified the Log implementation to use
+        String specifiedLogClassName = findUserSpecifiedLogClassName();
+
+        if (specifiedLogClassName != null) {
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("Attempting to load user-specified log class '" + 
+                    specifiedLogClassName + "'...");
+            }
+            
+            result = createLogFromClass(specifiedLogClassName,
+                                        logCategory,
+                                        true);
+            if (result == null) {
+                StringBuffer messageBuffer =  new StringBuffer("User-specified log class '");
+                messageBuffer.append(specifiedLogClassName);
+                messageBuffer.append("' cannot be found or is not useable.");
+                
+                // Mistyping or misspelling names is a common fault.
+                // Construct a good error message, if we can
+                if (specifiedLogClassName != null) {
+                    informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LOG4J_LOGGER);
+                    informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_JDK14_LOGGER);
+                    informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LUMBERJACK_LOGGER);
+                    informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_SIMPLE_LOGGER);
+                }
+                throw new LogConfigurationException(messageBuffer.toString());
+            }
+            
+            return result;
+        }
+        
+        // No user specified log; try to discover what's on the classpath
+        //
+        // Note that we deliberately loop here over classesToDiscover and
+        // expect method createLogFromClass to loop over the possible source
+        // classloaders. The effect is:
+        //   for each discoverable log adapter
+        //      for each possible classloader
+        //          see if it works
+        //
+        // It appears reasonable at first glance to do the opposite: 
+        //   for each possible classloader
+        //     for each discoverable log adapter
+        //        see if it works
+        //
+        // The latter certainly has advantages for user-installable logging
+        // libraries such as log4j; in a webapp for example this code should
+        // first check whether the user has provided any of the possible
+        // logging libraries before looking in the parent classloader. 
+        // Unfortunately, however, Jdk14Logger will always work in jvm>=1.4,
+        // and SimpleLog will always work in any JVM. So the loop would never
+        // ever look for logging libraries in the parent classpath. Yet many
+        // users would expect that putting log4j there would cause it to be
+        // detected (and this is the historical JCL behaviour). So we go with
+        // the first approach. A user that has bundled a specific logging lib
+        // in a webapp should use a commons-logging.properties file or a
+        // service file in META-INF to force use of that logging lib anyway,
+        // rather than relying on discovery.
+        
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic(
+                "No user-specified Log implementation; performing discovery" +
+            	" using the standard supported logging implementations...");
+        }
+        for(int i=0; (i<classesToDiscover.length) && (result == null); ++i) {
+            result = createLogFromClass(classesToDiscover[i], logCategory, true);
+        }
+        
+        if (result == null) {
+            throw new LogConfigurationException
+                        ("No suitable Log implementation");
+        }
+        
+        return result;        
+    }
+
+
+    /**
+     * Appends message if the given name is similar to the candidate.
+     * @param messageBuffer <code>StringBuffer</code> the message should be appended to, 
+     * not null
+     * @param name the (trimmed) name to be test against the candidate, not null
+     * @param candidate the candidate name (not null)
+     */
+    private void informUponSimilarName(final StringBuffer messageBuffer, final String name, 
+            final String candidate) {
+        if (name.equals(candidate)) {
+            // Don't suggest a name that is exactly the same as the one the
+            // user tried...
+            return;
+        }
+
+        // If the user provides a name that is in the right package, and gets
+        // the first 5 characters of the adapter class right (ignoring case),
+        // then suggest the candidate adapter class name.
+        if (name.regionMatches(true, 0, candidate, 0, PKG_LEN + 5)) {
+            messageBuffer.append(" Did you mean '");
+            messageBuffer.append(candidate);
+            messageBuffer.append("'?");
+        }
+    }
+    
+    
+    /**
+     * Checks system properties and the attribute map for 
+     * a Log implementation specified by the user under the 
+     * property names {@link #LOG_PROPERTY} or {@link #LOG_PROPERTY_OLD}.
+     * 
+     * @return classname specified by the user, or <code>null</code>
+     */
+    private String findUserSpecifiedLogClassName()
+    {
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("Trying to get log class from attribute '" + LOG_PROPERTY + "'");
+        }
+        String specifiedClass = (String) getAttribute(LOG_PROPERTY);
+
+        if (specifiedClass == null) { // @deprecated
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("Trying to get log class from attribute '" + 
+                              LOG_PROPERTY_OLD + "'");
+            }
+            specifiedClass = (String) getAttribute(LOG_PROPERTY_OLD);
+        }
+
+        if (specifiedClass == null) {
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("Trying to get log class from system property '" + 
+                          LOG_PROPERTY + "'");
+            }
+            try {
+                specifiedClass = System.getProperty(LOG_PROPERTY);
+            } catch (SecurityException e) {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic("No access allowed to system property '" + 
+                        LOG_PROPERTY + "' - " + e.getMessage());
+                }
+            }
+        }
+
+        if (specifiedClass == null) { // @deprecated
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("Trying to get log class from system property '" + 
+                          LOG_PROPERTY_OLD + "'");
+            }
+            try {
+                specifiedClass = System.getProperty(LOG_PROPERTY_OLD);
+            } catch (SecurityException e) {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic("No access allowed to system property '" + 
+                        LOG_PROPERTY_OLD + "' - " + e.getMessage());
+                }
+            }
+        }
+        
+        // Remove any whitespace; it's never valid in a classname so its
+        // presence just means a user mistake. As we know what they meant,
+        // we may as well strip the spaces.
+        if (specifiedClass != null) {
+            specifiedClass = specifiedClass.trim();
+        }
+
+        return specifiedClass;
+    }
+
+    
+    /**
+     * Attempts to load the given class, find a suitable constructor,
+     * and instantiate an instance of Log.
+     * 
+     * @param logAdapterClassName classname of the Log implementation
+     * 
+     * @param logCategory  argument to pass to the Log implementation's
+     * constructor
+     * 
+     * @param affectState  <code>true</code> if this object's state should
+     * be affected by this method call, <code>false</code> otherwise.
+     * 
+     * @return  an instance of the given class, or null if the logging
+     * library associated with the specified adapter is not available.
+     *                          
+     * @throws LogConfigurationException if there was a serious error with
+     * configuration and the handleFlawedDiscovery method decided this
+     * problem was fatal.
+     */                                  
+    private Log createLogFromClass(String logAdapterClassName,
+                                   String logCategory,
+                                   boolean affectState) 
+            throws LogConfigurationException {       
+
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("Attempting to instantiate '" + logAdapterClassName + "'");
+        }
+        
+        Object[] params = { logCategory };
+        Log logAdapter = null;
+        Constructor constructor = null;
+        
+        Class logAdapterClass = null;
+        ClassLoader currentCL = getBaseClassLoader();
+        
+        for(;;) {
+            // Loop through the classloader hierarchy trying to find
+            // a viable classloader.
+            logDiagnostic(
+                    "Trying to load '"
+                    + logAdapterClassName
+                    + "' from classloader "
+                    + objectId(currentCL));
+            try {
+                if (isDiagnosticsEnabled()) {
+                    // Show the location of the first occurrence of the .class file
+                    // in the classpath. This is the location that ClassLoader.loadClass
+                    // will load the class from -- unless the classloader is doing
+                    // something weird. 
+                    URL url;
+                    String resourceName = logAdapterClassName.replace('.', '/') + ".class";
+                    if (currentCL != null) {
+                        url = currentCL.getResource(resourceName );
+                    } else {
+                        url = ClassLoader.getSystemResource(resourceName + ".class");
+                    }
+
+                    if (url == null) {
+                        logDiagnostic("Class '" + logAdapterClassName + "' [" + resourceName + "] cannot be found.");
+                    } else {
+                        logDiagnostic("Class '" + logAdapterClassName + "' was found at '" + url + "'");
+                    }
+                }
+
+                Class c = null;
+                try {
+                    c = Class.forName(logAdapterClassName, true, currentCL);
+                } catch (ClassNotFoundException originalClassNotFoundException) {
+                    // The current classloader was unable to find the log adapter 
+                    // in this or any ancestor classloader. There's no point in
+                    // trying higher up in the hierarchy in this case..
+                    String msg = "" + originalClassNotFoundException.getMessage();
+                    logDiagnostic(
+                        "The log adapter '"
+                        + logAdapterClassName
+                        + "' is not available via classloader " 
+                        + objectId(currentCL)
+                        + ": "
+                        + msg.trim());
+                    try {
+                        // Try the class classloader.
+                        // This may work in cases where the TCCL
+                        // does not contain the code executed or JCL.
+                        // This behaviour indicates that the application 
+                        // classloading strategy is not consistent with the
+                        // Java 1.2 classloading guidelines but JCL can
+                        // and so should handle this case.
+                        c = Class.forName(logAdapterClassName);
+                    } catch (ClassNotFoundException secondaryClassNotFoundException) {
+                        // no point continuing: this adapter isn't available
+                        msg = "" + secondaryClassNotFoundException.getMessage();
+                        logDiagnostic(
+                            "The log adapter '"
+                            + logAdapterClassName
+                            + "' is not available via the LogFactoryImpl class classloader: "
+                            + msg.trim());
+                        break;
+                    }
+                }
+                
+                constructor = c.getConstructor(logConstructorSignature);
+                Object o = constructor.newInstance(params);
+
+                // Note that we do this test after trying to create an instance
+                // [rather than testing Log.class.isAssignableFrom(c)] so that
+                // we don't complain about Log hierarchy problems when the
+                // adapter couldn't be instantiated anyway.
+                if (o instanceof Log) {
+                    logAdapterClass = c;
+                    logAdapter = (Log) o;
+                    break;
+                }
+                
+                // Oops, we have a potential problem here. An adapter class
+                // has been found and its underlying lib is present too, but
+                // there are multiple Log interface classes available making it
+                // impossible to cast to the type the caller wanted. We 
+                // certainly can't use this logger, but we need to know whether
+                // to keep on discovering or terminate now.
+                //
+                // The handleFlawedHierarchy method will throw 
+                // LogConfigurationException if it regards this problem as
+                // fatal, and just return if not.
+                handleFlawedHierarchy(currentCL, c);
+            } catch (NoClassDefFoundError e) {
+                // We were able to load the adapter but it had references to
+                // other classes that could not be found. This simply means that
+                // the underlying logger library is not present in this or any
+                // ancestor classloader. There's no point in trying higher up
+                // in the hierarchy in this case..
+                String msg = "" + e.getMessage();
+                logDiagnostic(
+                    "The log adapter '"
+                    + logAdapterClassName
+                    + "' is missing dependencies when loaded via classloader "
+                    + objectId(currentCL)
+                    + ": "
+                    + msg.trim());
+                break;
+            } catch (ExceptionInInitializerError e) {
+                // A static initializer block or the initializer code associated 
+                // with a static variable on the log adapter class has thrown
+                // an exception.
+                //
+                // We treat this as meaning the adapter's underlying logging
+                // library could not be found.
+                String msg = "" + e.getMessage();
+                logDiagnostic(
+                    "The log adapter '"
+                    + logAdapterClassName
+                    + "' is unable to initialize itself when loaded via classloader "
+                    + objectId(currentCL)
+                    + ": "
+                    + msg.trim());
+                break;
+            } catch(LogConfigurationException e) {
+                // call to handleFlawedHierarchy above must have thrown
+                // a LogConfigurationException, so just throw it on                
+                throw e;
+            } catch(Throwable t) {
+                // handleFlawedDiscovery will determine whether this is a fatal
+                // problem or not. If it is fatal, then a LogConfigurationException
+                // will be thrown.
+                handleFlawedDiscovery(logAdapterClassName, currentCL, t);
+            }
+                        
+            if (currentCL == null) {
+                break;
+            }
+            
+            // try the parent classloader
+            currentCL = currentCL.getParent();
+        }
+
+        if ((logAdapter != null) && affectState) {
+            // We've succeeded, so set instance fields
+            this.logClassName   = logAdapterClassName;
+            this.logConstructor = constructor;
+            
+            // Identify the <code>setLogFactory</code> method (if there is one)
+            try {
+                this.logMethod = logAdapterClass.getMethod("setLogFactory",
+                                               logMethodSignature);
+                logDiagnostic("Found method setLogFactory(LogFactory) in '" 
+                              + logAdapterClassName + "'");
+            } catch (Throwable t) {
+                this.logMethod = null;
+                logDiagnostic(
+                    "[INFO] '" + logAdapterClassName 
+                    + "' from classloader " + objectId(currentCL)
+                    + " does not declare optional method "
+                    + "setLogFactory(LogFactory)");
+            }
+            
+            logDiagnostic(
+                "Log adapter '" + logAdapterClassName 
+                + "' from classloader " + objectId(logAdapterClass.getClassLoader())
+                + " has been selected for use.");
+        }
+        
+        return logAdapter;
+    }
+    
+    
+    /**
+     * Return the classloader from which we should try to load the logging
+     * adapter classes.
+     * <p>
+     * This method usually returns the context classloader. However if it
+     * is discovered that the classloader which loaded this class is a child
+     * of the context classloader <i>and</i> the allowFlawedContext option
+     * has been set then the classloader which loaded this class is returned
+     * instead.
+     * <p>
+     * The only time when the classloader which loaded this class is a
+     * descendant (rather than the same as or an ancestor of the context
+     * classloader) is when an app has created custom classloaders but
+     * failed to correctly set the context classloader. This is a bug in
+     * the calling application; however we provide the option for JCL to
+     * simply generate a warning rather than fail outright.
+     * 
+     */
+    private ClassLoader getBaseClassLoader() throws LogConfigurationException {
+        ClassLoader thisClassLoader = getClassLoader(LogFactoryImpl.class);
+        
+        if (useTCCL == false) {
+            return thisClassLoader;
+        }
+
+        ClassLoader contextClassLoader = getContextClassLoader();
+
+        ClassLoader baseClassLoader = getLowestClassLoader(
+                contextClassLoader, thisClassLoader);
+        
+        if (baseClassLoader == null) {
+           // The two classloaders are not part of a parent child relationship.
+           // In some classloading setups (e.g. JBoss with its 
+           // UnifiedLoaderRepository) this can still work, so if user hasn't
+           // forbidden it, just return the contextClassLoader.
+           if (allowFlawedContext) {   
+              if (isDiagnosticsEnabled()) {
+                   logDiagnostic(
+                           "[WARNING] the context classloader is not part of a"
+                           + " parent-child relationship with the classloader that"
+                           + " loaded LogFactoryImpl.");
+              }
+              // If contextClassLoader were null, getLowestClassLoader() would
+              // have returned thisClassLoader.  The fact we are here means
+              // contextClassLoader is not null, so we can just return it.
+              return contextClassLoader;
+           }
+           else {
+            throw new LogConfigurationException(
+                "Bad classloader hierarchy; LogFactoryImpl was loaded via"
+                + " a classloader that is not related to the current context"
+                + " classloader.");
+           }           
+        }
+
+        if (baseClassLoader != contextClassLoader) {
+            // We really should just use the contextClassLoader as the starting
+            // point for scanning for log adapter classes. However it is expected
+            // that there are a number of broken systems out there which create
+            // custom classloaders but fail to set the context classloader so
+            // we handle those flawed systems anyway.
+            if (allowFlawedContext) {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic(
+                            "Warning: the context classloader is an ancestor of the"
+                            + " classloader that loaded LogFactoryImpl; it should be"
+                            + " the same or a descendant. The application using"
+                            + " commons-logging should ensure the context classloader"
+                            + " is used correctly.");
+                }
+            } else {
+                throw new LogConfigurationException(
+                        "Bad classloader hierarchy; LogFactoryImpl was loaded via"
+                        + " a classloader that is not related to the current context"
+                        + " classloader."); 
+            }
+        }
+        
+        return baseClassLoader;
+    }
+
+    /**
+     * Given two related classloaders, return the one which is a child of
+     * the other.
+     * <p>
+     * @param c1 is a classloader (including the null classloader)
+     * @param c2 is a classloader (including the null classloader)
+     * 
+     * @return c1 if it has c2 as an ancestor, c2 if it has c1 as an ancestor,
+     * and null if neither is an ancestor of the other.
+     */
+    private ClassLoader getLowestClassLoader(ClassLoader c1, ClassLoader c2) {
+        // TODO: use AccessController when dealing with classloaders here
+        
+        if (c1 == null)
+            return c2;
+        
+        if (c2 == null)
+            return c1;
+        
+        ClassLoader current;
+
+        // scan c1's ancestors to find c2
+        current = c1;
+        while (current != null) {
+            if (current == c2)
+                return c1;
+            current = current.getParent();
+        }
+       
+        // scan c2's ancestors to find c1
+        current = c2;
+        while (current != null) {
+            if (current == c1)
+                return c2;
+            current = current.getParent();
+        }
+
+        return null;
+    }
+
+    /**
+     * Generates an internal diagnostic logging of the discovery failure and 
+     * then throws a <code>LogConfigurationException</code> that wraps 
+     * the passed <code>Throwable</code>.
+     * 
+     * @param logAdapterClassName is the class name of the Log implementation
+     * that could not be instantiated. Cannot be <code>null</code>.
+     * 
+     * @param classLoader is the classloader that we were trying to load the
+     * logAdapterClassName from when the exception occurred.
+     * 
+     * @param discoveryFlaw is the Throwable created by the classloader
+     * 
+     * @throws LogConfigurationException    ALWAYS
+     */
+    private void handleFlawedDiscovery(String logAdapterClassName,
+                                       ClassLoader classLoader,
+                                       Throwable discoveryFlaw) {
+        
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("Could not instantiate Log '"
+                      + logAdapterClassName + "' -- "
+                      + discoveryFlaw.getClass().getName() + ": "
+                      + discoveryFlaw.getLocalizedMessage());       
+        }
+        
+        if (!allowFlawedDiscovery) {
+            throw new LogConfigurationException(discoveryFlaw);
+        }
+    }
+
+    
+    /**
+     * Report a problem loading the log adapter, then either return 
+     * (if the situation is considered recoverable) or throw a
+     * LogConfigurationException.
+     *  <p>
+     * There are two possible reasons why we successfully loaded the 
+     * specified log adapter class then failed to cast it to a Log object:
+     * <ol>
+     * <li>the specific class just doesn't implement the Log interface 
+     *     (user screwed up), or
+     * <li> the specified class has bound to a Log class loaded by some other
+     *      classloader; Log@classloaderX cannot be cast to Log@classloaderY.
+     * </ol>
+     * <p>
+     * Here we try to figure out which case has occurred so we can give the
+     * user some reasonable feedback.
+     * 
+     * @param badClassLoader is the classloader we loaded the problem class from,
+     * ie it is equivalent to badClass.getClassLoader().
+     * 
+     * @param badClass is a Class object with the desired name, but which 
+     * does not implement Log correctly.
+     * 
+     * @throws LogConfigurationException when the situation
+     * should not be recovered from.
+     */
+    private void handleFlawedHierarchy(ClassLoader badClassLoader, Class badClass)
+    throws LogConfigurationException {
+
+        boolean implementsLog = false;
+        String logInterfaceName = Log.class.getName();
+        Class interfaces[] = badClass.getInterfaces();
+        for (int i = 0; i < interfaces.length; i++) {
+            if (logInterfaceName.equals(interfaces[i].getName())) {
+                implementsLog = true;
+                break;
+            }
+        }
+        
+        if (implementsLog) {
+            // the class does implement an interface called Log, but
+            // it is in the wrong classloader
+            if (isDiagnosticsEnabled()) {
+                try {
+                    ClassLoader logInterfaceClassLoader = getClassLoader(Log.class);
+                    logDiagnostic(
+                        "Class '" + badClass.getName()
+                        + "' was found in classloader " 
+                        + objectId(badClassLoader)
+                        + ". It is bound to a Log interface which is not"
+                        + " the one loaded from classloader "
+                        + objectId(logInterfaceClassLoader));
+                } catch (Throwable t) {
+                    logDiagnostic(
+                        "Error while trying to output diagnostics about"
+                        + " bad class '" + badClass + "'");
+                }
+            }
+            
+            if (!allowFlawedHierarchy) {
+                StringBuffer msg = new StringBuffer();
+                msg.append("Terminating logging for this context ");
+                msg.append("due to bad log hierarchy. ");
+                msg.append("You have more than one version of '");
+                msg.append(Log.class.getName());
+                msg.append("' visible.");
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic(msg.toString());
+                } 
+                throw new LogConfigurationException(msg.toString());
+            }
+        
+            if (isDiagnosticsEnabled()) {
+                StringBuffer msg = new StringBuffer();
+                msg.append("Warning: bad log hierarchy. ");
+                msg.append("You have more than one version of '");
+                msg.append(Log.class.getName());
+                msg.append("' visible.");
+                logDiagnostic(msg.toString());
+            }
+        } else {
+            // this is just a bad adapter class
+            if (!allowFlawedDiscovery) {
+                StringBuffer msg = new StringBuffer();
+                msg.append("Terminating logging for this context. ");
+                msg.append("Log class '");
+                msg.append(badClass.getName());
+                msg.append("' does not implement the Log interface.");
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic(msg.toString());
+                }
+                
+                throw new LogConfigurationException(msg.toString());
+            }
+
+            if (isDiagnosticsEnabled()) {
+                StringBuffer msg = new StringBuffer();
+                msg.append("[WARNING] Log class '");
+                msg.append(badClass.getName());
+                msg.append("' does not implement the Log interface.");
+                logDiagnostic(msg.toString());
+            }
+        }
+    }
+}
diff --git a/src/org/apache/commons/logging/impl/NoOpLog.java b/src/org/apache/commons/logging/impl/NoOpLog.java
new file mode 100644
index 0000000..b698813
--- /dev/null
+++ b/src/org/apache/commons/logging/impl/NoOpLog.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.logging.impl;
+
+
+import java.io.Serializable;
+import org.apache.commons.logging.Log;
+
+
+/**
+ * <p>Trivial implementation of Log that throws away all messages.  No
+ * configurable system properties are supported.</p>
+ *
+ * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
+ * @author Rod Waldhoff
+ * @version $Id: NoOpLog.java 155426 2005-02-26 13:10:49Z dirkv $
+ */
+public class NoOpLog implements Log, Serializable {
+
+    /** Convenience constructor */
+    public NoOpLog() { }
+    /** Base constructor */
+    public NoOpLog(String name) { }
+    /** Do nothing */
+    public void trace(Object message) { }
+    /** Do nothing */
+    public void trace(Object message, Throwable t) { }
+    /** Do nothing */
+    public void debug(Object message) { }
+    /** Do nothing */
+    public void debug(Object message, Throwable t) { }
+    /** Do nothing */
+    public void info(Object message) { }
+    /** Do nothing */
+    public void info(Object message, Throwable t) { }
+    /** Do nothing */
+    public void warn(Object message) { }
+    /** Do nothing */
+    public void warn(Object message, Throwable t) { }
+    /** Do nothing */
+    public void error(Object message) { }
+    /** Do nothing */
+    public void error(Object message, Throwable t) { }
+    /** Do nothing */
+    public void fatal(Object message) { }
+    /** Do nothing */
+    public void fatal(Object message, Throwable t) { }
+
+    /**
+     * Debug is never enabled.
+     *
+     * @return false
+     */
+    public final boolean isDebugEnabled() { return false; }
+
+    /**
+     * Error is never enabled.
+     *
+     * @return false
+     */
+    public final boolean isErrorEnabled() { return false; }
+
+    /**
+     * Fatal is never enabled.
+     *
+     * @return false
+     */
+    public final boolean isFatalEnabled() { return false; }
+
+    /**
+     * Info is never enabled.
+     *
+     * @return false
+     */
+    public final boolean isInfoEnabled() { return false; }
+
+    /**
+     * Trace is never enabled.
+     *
+     * @return false
+     */
+    public final boolean isTraceEnabled() { return false; }
+
+    /**
+     * Warn is never enabled.
+     *
+     * @return false
+     */
+    public final boolean isWarnEnabled() { return false; }
+
+}
diff --git a/src/org/apache/commons/logging/impl/SimpleLog.java b/src/org/apache/commons/logging/impl/SimpleLog.java
new file mode 100644
index 0000000..6b643d3
--- /dev/null
+++ b/src/org/apache/commons/logging/impl/SimpleLog.java
@@ -0,0 +1,709 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.logging.impl;
+
+import java.io.InputStream;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogConfigurationException;
+
+/**
+ * <p>Simple implementation of Log that sends all enabled log messages,
+ * for all defined loggers, to System.err.  The following system properties
+ * are supported to configure the behavior of this logger:</p>
+ * <ul>
+ * <li><code>org.apache.commons.logging.simplelog.defaultlog</code> -
+ *     Default logging detail level for all instances of SimpleLog.
+ *     Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
+ *     If not specified, defaults to "info". </li>
+ * <li><code>org.apache.commons.logging.simplelog.log.xxxxx</code> -
+ *     Logging detail level for a SimpleLog instance named "xxxxx".
+ *     Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
+ *     If not specified, the default logging detail level is used.</li>
+ * <li><code>org.apache.commons.logging.simplelog.showlogname</code> -
+ *     Set to <code>true</code> if you want the Log instance name to be
+ *     included in output messages. Defaults to <code>false</code>.</li>
+ * <li><code>org.apache.commons.logging.simplelog.showShortLogname</code> -
+ *     Set to <code>true</code> if you want the last component of the name to be
+ *     included in output messages. Defaults to <code>true</code>.</li>
+ * <li><code>org.apache.commons.logging.simplelog.showdatetime</code> -
+ *     Set to <code>true</code> if you want the current date and time
+ *     to be included in output messages. Default is <code>false</code>.</li>
+ * <li><code>org.apache.commons.logging.simplelog.dateTimeFormat</code> -
+ *     The date and time format to be used in the output messages.
+ *     The pattern describing the date and time format is the same that is
+ *     used in <code>java.text.SimpleDateFormat</code>. If the format is not
+ *     specified or is invalid, the default format is used.
+ *     The default format is <code>yyyy/MM/dd HH:mm:ss:SSS zzz</code>.</li>
+ * </ul>
+ *
+ * <p>In addition to looking for system properties with the names specified
+ * above, this implementation also checks for a class loader resource named
+ * <code>"simplelog.properties"</code>, and includes any matching definitions
+ * from this resource (if it exists).</p>
+ *
+ * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
+ * @author Rod Waldhoff
+ * @author Robert Burrell Donkin
+ *
+ * @version $Id: SimpleLog.java 399221 2006-05-03 09:20:24Z dennisl $
+ */
+public class SimpleLog implements Log, Serializable {
+
+
+    // ------------------------------------------------------- Class Attributes
+
+    /** All system properties used by <code>SimpleLog</code> start with this */
+    static protected final String systemPrefix =
+        "org.apache.commons.logging.simplelog.";
+
+    /** Properties loaded from simplelog.properties */
+    static protected final Properties simpleLogProps = new Properties();
+
+    /** The default format to use when formating dates */
+    static protected final String DEFAULT_DATE_TIME_FORMAT =
+        "yyyy/MM/dd HH:mm:ss:SSS zzz";
+
+    /** Include the instance name in the log message? */
+    static protected boolean showLogName = false;
+    /** Include the short name ( last component ) of the logger in the log
+     *  message. Defaults to true - otherwise we'll be lost in a flood of
+     *  messages without knowing who sends them.
+     */
+    static protected boolean showShortName = true;
+    /** Include the current time in the log message */
+    static protected boolean showDateTime = false;
+    /** The date and time format to use in the log message */
+    static protected String dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;
+    /** Used to format times */
+    static protected DateFormat dateFormatter = null;
+
+    // ---------------------------------------------------- Log Level Constants
+
+
+    /** "Trace" level logging. */
+    public static final int LOG_LEVEL_TRACE  = 1;
+    /** "Debug" level logging. */
+    public static final int LOG_LEVEL_DEBUG  = 2;
+    /** "Info" level logging. */
+    public static final int LOG_LEVEL_INFO   = 3;
+    /** "Warn" level logging. */
+    public static final int LOG_LEVEL_WARN   = 4;
+    /** "Error" level logging. */
+    public static final int LOG_LEVEL_ERROR  = 5;
+    /** "Fatal" level logging. */
+    public static final int LOG_LEVEL_FATAL  = 6;
+
+    /** Enable all logging levels */
+    public static final int LOG_LEVEL_ALL    = (LOG_LEVEL_TRACE - 1);
+
+    /** Enable no logging levels */
+    public static final int LOG_LEVEL_OFF    = (LOG_LEVEL_FATAL + 1);
+
+    // ------------------------------------------------------------ Initializer
+
+    private static String getStringProperty(String name) {
+        String prop = null;
+	try {
+	    prop = System.getProperty(name);
+	} catch (SecurityException e) {
+	    ; // Ignore
+	}
+        return (prop == null) ? simpleLogProps.getProperty(name) : prop;
+    }
+
+    private static String getStringProperty(String name, String dephault) {
+        String prop = getStringProperty(name);
+        return (prop == null) ? dephault : prop;
+    }
+
+    private static boolean getBooleanProperty(String name, boolean dephault) {
+        String prop = getStringProperty(name);
+        return (prop == null) ? dephault : "true".equalsIgnoreCase(prop);
+    }
+
+    // Initialize class attributes.
+    // Load properties file, if found.
+    // Override with system properties.
+    static {
+        // Add props from the resource simplelog.properties
+        InputStream in = getResourceAsStream("simplelog.properties");
+        if(null != in) {
+            try {
+                simpleLogProps.load(in);
+                in.close();
+            } catch(java.io.IOException e) {
+                // ignored
+            }
+        }
+
+        showLogName = getBooleanProperty( systemPrefix + "showlogname", showLogName);
+        showShortName = getBooleanProperty( systemPrefix + "showShortLogname", showShortName);
+        showDateTime = getBooleanProperty( systemPrefix + "showdatetime", showDateTime);
+
+        if(showDateTime) {
+            dateTimeFormat = getStringProperty(systemPrefix + "dateTimeFormat",
+                                               dateTimeFormat);
+            try {
+                dateFormatter = new SimpleDateFormat(dateTimeFormat);
+            } catch(IllegalArgumentException e) {
+                // If the format pattern is invalid - use the default format
+                dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;
+                dateFormatter = new SimpleDateFormat(dateTimeFormat);
+            }
+        }
+    }
+
+
+    // ------------------------------------------------------------- Attributes
+
+    /** The name of this simple log instance */
+    protected String logName = null;
+    /** The current log level */
+    protected int currentLogLevel;
+    /** The short name of this simple log instance */
+    private String shortLogName = null;
+
+
+    // ------------------------------------------------------------ Constructor
+
+    /**
+     * Construct a simple log with given name.
+     *
+     * @param name log name
+     */
+    public SimpleLog(String name) {
+
+        logName = name;
+
+        // Set initial log level
+        // Used to be: set default log level to ERROR
+        // IMHO it should be lower, but at least info ( costin ).
+        setLevel(SimpleLog.LOG_LEVEL_INFO);
+
+        // Set log level from properties
+        String lvl = getStringProperty(systemPrefix + "log." + logName);
+        int i = String.valueOf(name).lastIndexOf(".");
+        while(null == lvl && i > -1) {
+            name = name.substring(0,i);
+            lvl = getStringProperty(systemPrefix + "log." + name);
+            i = String.valueOf(name).lastIndexOf(".");
+        }
+
+        if(null == lvl) {
+            lvl =  getStringProperty(systemPrefix + "defaultlog");
+        }
+
+        if("all".equalsIgnoreCase(lvl)) {
+            setLevel(SimpleLog.LOG_LEVEL_ALL);
+        } else if("trace".equalsIgnoreCase(lvl)) {
+            setLevel(SimpleLog.LOG_LEVEL_TRACE);
+        } else if("debug".equalsIgnoreCase(lvl)) {
+            setLevel(SimpleLog.LOG_LEVEL_DEBUG);
+        } else if("info".equalsIgnoreCase(lvl)) {
+            setLevel(SimpleLog.LOG_LEVEL_INFO);
+        } else if("warn".equalsIgnoreCase(lvl)) {
+            setLevel(SimpleLog.LOG_LEVEL_WARN);
+        } else if("error".equalsIgnoreCase(lvl)) {
+            setLevel(SimpleLog.LOG_LEVEL_ERROR);
+        } else if("fatal".equalsIgnoreCase(lvl)) {
+            setLevel(SimpleLog.LOG_LEVEL_FATAL);
+        } else if("off".equalsIgnoreCase(lvl)) {
+            setLevel(SimpleLog.LOG_LEVEL_OFF);
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Properties
+
+    /**
+     * <p> Set logging level. </p>
+     *
+     * @param currentLogLevel new logging level
+     */
+    public void setLevel(int currentLogLevel) {
+
+        this.currentLogLevel = currentLogLevel;
+
+    }
+
+
+    /**
+     * <p> Get logging level. </p>
+     */
+    public int getLevel() {
+
+        return currentLogLevel;
+    }
+
+
+    // -------------------------------------------------------- Logging Methods
+
+
+    /**
+     * <p> Do the actual logging.
+     * This method assembles the message
+     * and then calls <code>write()</code> to cause it to be written.</p>
+     *
+     * @param type One of the LOG_LEVEL_XXX constants defining the log level
+     * @param message The message itself (typically a String)
+     * @param t The exception whose stack trace should be logged
+     */
+    protected void log(int type, Object message, Throwable t) {
+        // Use a string buffer for better performance
+        StringBuffer buf = new StringBuffer();
+
+        // Append date-time if so configured
+        if(showDateTime) {
+            buf.append(dateFormatter.format(new Date()));
+            buf.append(" ");
+        }
+
+        // Append a readable representation of the log level
+        switch(type) {
+            case SimpleLog.LOG_LEVEL_TRACE: buf.append("[TRACE] "); break;
+            case SimpleLog.LOG_LEVEL_DEBUG: buf.append("[DEBUG] "); break;
+            case SimpleLog.LOG_LEVEL_INFO:  buf.append("[INFO] ");  break;
+            case SimpleLog.LOG_LEVEL_WARN:  buf.append("[WARN] ");  break;
+            case SimpleLog.LOG_LEVEL_ERROR: buf.append("[ERROR] "); break;
+            case SimpleLog.LOG_LEVEL_FATAL: buf.append("[FATAL] "); break;
+        }
+
+        // Append the name of the log instance if so configured
+ 	if( showShortName) {
+            if( shortLogName==null ) {
+                // Cut all but the last component of the name for both styles
+                shortLogName = logName.substring(logName.lastIndexOf(".") + 1);
+                shortLogName =
+                    shortLogName.substring(shortLogName.lastIndexOf("/") + 1);
+            }
+            buf.append(String.valueOf(shortLogName)).append(" - ");
+        } else if(showLogName) {
+            buf.append(String.valueOf(logName)).append(" - ");
+        }
+
+        // Append the message
+        buf.append(String.valueOf(message));
+
+        // Append stack trace if not null
+        if(t != null) {
+            buf.append(" <");
+            buf.append(t.toString());
+            buf.append(">");
+
+            java.io.StringWriter sw= new java.io.StringWriter(1024);
+            java.io.PrintWriter pw= new java.io.PrintWriter(sw);
+            t.printStackTrace(pw);
+            pw.close();
+            buf.append(sw.toString());
+        }
+
+        // Print to the appropriate destination
+        write(buf);
+
+    }
+
+
+    /**
+     * <p>Write the content of the message accumulated in the specified
+     * <code>StringBuffer</code> to the appropriate output destination.  The
+     * default implementation writes to <code>System.err</code>.</p>
+     *
+     * @param buffer A <code>StringBuffer</code> containing the accumulated
+     *  text to be logged
+     */
+    protected void write(StringBuffer buffer) {
+
+        System.err.println(buffer.toString());
+
+    }
+
+
+    /**
+     * Is the given log level currently enabled?
+     *
+     * @param logLevel is this level enabled?
+     */
+    protected boolean isLevelEnabled(int logLevel) {
+        // log level are numerically ordered so can use simple numeric
+        // comparison
+        return (logLevel >= currentLogLevel);
+    }
+
+
+    // -------------------------------------------------------- Log Implementation
+
+
+    /**
+     * Logs a message with 
+     * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_DEBUG</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#debug(Object)
+     */
+    public final void debug(Object message) {
+
+        if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) {
+            log(SimpleLog.LOG_LEVEL_DEBUG, message, null);
+        }
+    }
+
+
+    /**
+     * Logs a message with 
+     * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_DEBUG</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#debug(Object, Throwable)
+     */
+    public final void debug(Object message, Throwable t) {
+
+        if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) {
+            log(SimpleLog.LOG_LEVEL_DEBUG, message, t);
+        }
+    }
+
+
+    /**
+     * Logs a message with 
+     * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_TRACE</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#trace(Object)
+     */
+    public final void trace(Object message) {
+
+        if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) {
+            log(SimpleLog.LOG_LEVEL_TRACE, message, null);
+        }
+    }
+
+
+    /**
+     * Logs a message with 
+     * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_TRACE</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#trace(Object, Throwable)
+     */
+    public final void trace(Object message, Throwable t) {
+
+        if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) {
+            log(SimpleLog.LOG_LEVEL_TRACE, message, t);
+        }
+    }
+
+
+    /**
+     * Logs a message with 
+     * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_INFO</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#info(Object)
+     */
+    public final void info(Object message) {
+
+        if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) {
+            log(SimpleLog.LOG_LEVEL_INFO,message,null);
+        }
+    }
+
+
+    /**
+     * Logs a message with 
+     * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_INFO</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#info(Object, Throwable)
+     */
+    public final void info(Object message, Throwable t) {
+
+        if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) {
+            log(SimpleLog.LOG_LEVEL_INFO, message, t);
+        }
+    }
+
+
+    /**
+     * Logs a message with 
+     * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_WARN</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#warn(Object)
+     */
+    public final void warn(Object message) {
+
+        if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) {
+            log(SimpleLog.LOG_LEVEL_WARN, message, null);
+        }
+    }
+
+
+    /**
+     * Logs a message with 
+     * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_WARN</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#warn(Object, Throwable)
+     */
+    public final void warn(Object message, Throwable t) {
+
+        if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) {
+            log(SimpleLog.LOG_LEVEL_WARN, message, t);
+        }
+    }
+
+
+    /**
+     * Logs a message with 
+     * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_ERROR</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#error(Object)
+     */
+    public final void error(Object message) {
+
+        if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) {
+            log(SimpleLog.LOG_LEVEL_ERROR, message, null);
+        }
+    }
+
+
+    /**
+     * Logs a message with 
+     * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_ERROR</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#error(Object, Throwable)
+     */
+    public final void error(Object message, Throwable t) {
+
+        if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) {
+            log(SimpleLog.LOG_LEVEL_ERROR, message, t);
+        }
+    }
+
+
+    /**
+     * Log a message with 
+     * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_FATAL</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#fatal(Object)
+     */
+    public final void fatal(Object message) {
+
+        if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) {
+            log(SimpleLog.LOG_LEVEL_FATAL, message, null);
+        }
+    }
+
+
+    /**
+     * Logs a message with 
+     * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_FATAL</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#fatal(Object, Throwable)
+     */
+    public final void fatal(Object message, Throwable t) {
+
+        if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) {
+            log(SimpleLog.LOG_LEVEL_FATAL, message, t);
+        }
+    }
+
+
+    /**
+     * <p> Are debug messages currently enabled? </p>
+     *
+     * <p> This allows expensive operations such as <code>String</code>
+     * concatenation to be avoided when the message will be ignored by the
+     * logger. </p>
+     */
+    public final boolean isDebugEnabled() {
+
+        return isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG);
+    }
+
+
+    /**
+     * <p> Are error messages currently enabled? </p>
+     *
+     * <p> This allows expensive operations such as <code>String</code>
+     * concatenation to be avoided when the message will be ignored by the
+     * logger. </p>
+     */
+    public final boolean isErrorEnabled() {
+
+        return isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR);
+    }
+
+
+    /**
+     * <p> Are fatal messages currently enabled? </p>
+     *
+     * <p> This allows expensive operations such as <code>String</code>
+     * concatenation to be avoided when the message will be ignored by the
+     * logger. </p>
+     */
+    public final boolean isFatalEnabled() {
+
+        return isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL);
+    }
+
+
+    /**
+     * <p> Are info messages currently enabled? </p>
+     *
+     * <p> This allows expensive operations such as <code>String</code>
+     * concatenation to be avoided when the message will be ignored by the
+     * logger. </p>
+     */
+    public final boolean isInfoEnabled() {
+
+        return isLevelEnabled(SimpleLog.LOG_LEVEL_INFO);
+    }
+
+
+    /**
+     * <p> Are trace messages currently enabled? </p>
+     *
+     * <p> This allows expensive operations such as <code>String</code>
+     * concatenation to be avoided when the message will be ignored by the
+     * logger. </p>
+     */
+    public final boolean isTraceEnabled() {
+
+        return isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE);
+    }
+
+
+    /**
+     * <p> Are warn messages currently enabled? </p>
+     *
+     * <p> This allows expensive operations such as <code>String</code>
+     * concatenation to be avoided when the message will be ignored by the
+     * logger. </p>
+     */
+    public final boolean isWarnEnabled() {
+
+        return isLevelEnabled(SimpleLog.LOG_LEVEL_WARN);
+    }
+
+
+    /**
+     * Return the thread context class loader if available.
+     * Otherwise return null.
+     *
+     * The thread context class loader is available for JDK 1.2
+     * or later, if certain security conditions are met.
+     *
+     * @exception LogConfigurationException if a suitable class loader
+     * cannot be identified.
+     */
+    private static ClassLoader getContextClassLoader()
+    {
+        ClassLoader classLoader = null;
+
+        if (classLoader == null) {
+            try {
+                // Are we running on a JDK 1.2 or later system?
+                Method method = Thread.class.getMethod("getContextClassLoader",
+                        (Class[]) null);
+
+                // Get the thread context class loader (if there is one)
+                try {
+                    classLoader = (ClassLoader)method.invoke(Thread.currentThread(), 
+                            (Object[]) null);
+
+                } catch (IllegalAccessException e) {
+                    ;  // ignore
+                } catch (InvocationTargetException e) {
+                    /**
+                     * InvocationTargetException is thrown by 'invoke' when
+                     * the method being invoked (getContextClassLoader) throws
+                     * an exception.
+                     *
+                     * getContextClassLoader() throws SecurityException when
+                     * the context class loader isn't an ancestor of the
+                     * calling class's class loader, or if security
+                     * permissions are restricted.
+                     *
+                     * In the first case (not related), we want to ignore and
+                     * keep going.  We cannot help but also ignore the second
+                     * with the logic below, but other calls elsewhere (to
+                     * obtain a class loader) will trigger this exception where
+                     * we can make a distinction.
+                     */
+                    if (e.getTargetException() instanceof SecurityException) {
+                        ;  // ignore
+                    } else {
+                        // Capture 'e.getTargetException()' exception for details
+                        // alternate: log 'e.getTargetException()', and pass back 'e'.
+                        throw new LogConfigurationException
+                            ("Unexpected InvocationTargetException", e.getTargetException());
+                    }
+                }
+            } catch (NoSuchMethodException e) {
+                // Assume we are running on JDK 1.1
+                ;  // ignore
+            }
+        }
+
+        if (classLoader == null) {
+            classLoader = SimpleLog.class.getClassLoader();
+        }
+
+        // Return the selected class loader
+        return classLoader;
+    }
+
+    private static InputStream getResourceAsStream(final String name)
+    {
+        return (InputStream)AccessController.doPrivileged(
+            new PrivilegedAction() {
+                public Object run() {
+                    ClassLoader threadCL = getContextClassLoader();
+
+                    if (threadCL != null) {
+                        return threadCL.getResourceAsStream(name);
+                    } else {
+                        return ClassLoader.getSystemResourceAsStream(name);
+                    }
+                }
+            });
+    }
+}
+
diff --git a/src/org/apache/commons/logging/impl/WeakHashtable.java b/src/org/apache/commons/logging/impl/WeakHashtable.java
new file mode 100644
index 0000000..e4749b6
--- /dev/null
+++ b/src/org/apache/commons/logging/impl/WeakHashtable.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.logging.impl;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.*;
+
+/**
+ * <p>Implementation of <code>Hashtable</code> that uses <code>WeakReference</code>'s
+ * to hold its keys thus allowing them to be reclaimed by the garbage collector.
+ * The associated values are retained using strong references.</p>
+ *
+ * <p>This class follows the symantics of <code>Hashtable</code> as closely as
+ * possible. It therefore does not accept null values or keys.</p>
+ *
+ * <p><strong>Note:</strong>
+ * This is <em>not</em> intended to be a general purpose hash table replacement.
+ * This implementation is also tuned towards a particular purpose: for use as a replacement
+ * for <code>Hashtable</code> in <code>LogFactory</code>. This application requires
+ * good liveliness for <code>get</code> and <code>put</code>. Various tradeoffs
+ * have been made with this in mind.
+ * </p>
+ * <p>
+ * <strong>Usage:</strong> typical use case is as a drop-in replacement 
+ * for the <code>Hashtable</code> used in <code>LogFactory</code> for J2EE enviroments
+ * running 1.3+ JVMs. Use of this class <i>in most cases</i> (see below) will
+ * allow classloaders to be collected by the garbage collector without the need 
+ * to call {@link org.apache.commons.logging.LogFactory#release(ClassLoader) LogFactory.release(ClassLoader)}.
+ * </p>
+ *
+ * <p><code>org.apache.commons.logging.LogFactory</code> checks whether this class
+ * can be supported by the current JVM, and if so then uses it to store
+ * references to the <code>LogFactory</code> implementationd it loads 
+ * (rather than using a standard Hashtable instance). 
+ * Having this class used instead of <code>Hashtable</code> solves
+ * certain issues related to dynamic reloading of applications in J2EE-style
+ * environments. However this class requires java 1.3 or later (due to its use
+ * of <code>java.lang.ref.WeakReference</code> and associates).
+ * And by the way, this extends <code>Hashtable</code> rather than <code>HashMap</code>
+ * for backwards compatibility reasons. See the documentation
+ * for method <code>LogFactory.createFactoryStore</code> for more details.</p>
+ *
+ * <p>The reason all this is necessary is due to a issue which
+ * arises during hot deploy in a J2EE-like containers. 
+ * Each component running in the container owns one or more classloaders; when
+ * the component loads a LogFactory instance via the component classloader
+ * a reference to it gets stored in the static LogFactory.factories member,
+ * keyed by the component's classloader so different components don't
+ * stomp on each other. When the component is later unloaded, the container
+ * sets the component's classloader to null with the intent that all the 
+ * component's classes get garbage-collected. However there's still a
+ * reference to the component's classloader from a key in the "global"
+ * <code>LogFactory</code>'s factories member! If <code>LogFactory.release()</code>
+ * is called whenever component is unloaded, the classloaders will be correctly
+ * garbage collected; this <i>should</i> be done by any container that 
+ * bundles commons-logging by default. However, holding the classloader
+ * references weakly ensures that the classloader will be garbage collected
+ * without the container performing this step. </p>
+ *
+ * <p>
+ * <strong>Limitations:</strong>
+ * There is still one (unusual) scenario in which a component will not 
+ * be correctly unloaded without an explicit release. Though weak references
+ * are used for its keys, it is necessary to use strong references for its values.
+ * </p>
+ * 
+ * <p> If the abstract class <code>LogFactory</code> is 
+ * loaded by the container classloader but a subclass of 
+ * <code>LogFactory</code> [LogFactory1] is loaded by the component's 
+ * classloader and an instance stored in the static map associated with the
+ * base LogFactory class, then there is a strong reference from the LogFactory
+ * class to the LogFactory1 instance (as normal) and a strong reference from
+ * the LogFactory1 instance to the component classloader via
+ * <code>getClass().getClassLoader()</code>. This chain of references will prevent 
+ * collection of the child classloader.</p>
+ *
+ * <p>
+ * Such a situation occurs when the commons-logging.jar is
+ * loaded by a parent classloader (e.g. a server level classloader in a
+ * servlet container) and a custom <code>LogFactory</code> implementation is
+ * loaded by a child classloader (e.g. a web app classloader).</p>
+ * 
+ * <p>To avoid this scenario, ensure
+ * that any custom LogFactory subclass is loaded by the same classloader as 
+ * the base <code>LogFactory</code>. Creating custom LogFactory subclasses is,
+ * however, rare. The standard LogFactoryImpl class should be sufficient
+ * for most or all users.</p>
+ *
+ *
+ * @author Brian Stansberry
+ * 
+ * @since 1.1
+ */
+public final class WeakHashtable extends Hashtable {
+
+    /** 
+     * The maximum number of times put() or remove() can be called before
+     * the map will be purged of all cleared entries.
+     */
+    private static final int MAX_CHANGES_BEFORE_PURGE = 100;
+    
+    /** 
+     * The maximum number of times put() or remove() can be called before
+     * the map will be purged of one cleared entry.
+     */
+    private static final int PARTIAL_PURGE_COUNT     = 10;
+    
+    /* ReferenceQueue we check for gc'd keys */
+    private ReferenceQueue queue = new ReferenceQueue();
+    /* Counter used to control how often we purge gc'd entries */
+    private int changeCount = 0;
+    
+    /**
+     * Constructs a WeakHashtable with the Hashtable default
+     * capacity and load factor.
+     */
+    public WeakHashtable() {}
+    
+    
+    /**
+     *@see Hashtable
+     */
+    public boolean containsKey(Object key) {
+        // purge should not be required
+        Referenced referenced = new Referenced(key);
+        return super.containsKey(referenced);
+    }
+    
+    /**
+     *@see Hashtable
+     */
+    public Enumeration elements() {
+        purge();
+        return super.elements();
+    }
+    
+    /**
+     *@see Hashtable
+     */
+    public Set entrySet() {
+        purge();
+        Set referencedEntries = super.entrySet();
+        Set unreferencedEntries = new HashSet();
+        for (Iterator it=referencedEntries.iterator(); it.hasNext();) {
+            Map.Entry entry = (Map.Entry) it.next();
+            Referenced referencedKey = (Referenced) entry.getKey();
+            Object key = referencedKey.getValue();
+            Object value = entry.getValue();
+            if (key != null) {
+                Entry dereferencedEntry = new Entry(key, value);
+                unreferencedEntries.add(dereferencedEntry);
+            }
+        }
+        return unreferencedEntries;
+    }
+    
+    /**
+     *@see Hashtable
+     */
+    public Object get(Object key) {
+        // for performance reasons, no purge
+        Referenced referenceKey = new Referenced(key);
+        return super.get(referenceKey);
+    }
+    
+    /**
+     *@see Hashtable
+     */
+    public Enumeration keys() {
+        purge();
+        final Enumeration enumer = super.keys();
+        return new Enumeration() {
+            public boolean hasMoreElements() {
+                return enumer.hasMoreElements();
+            }
+            public Object nextElement() {
+                 Referenced nextReference = (Referenced) enumer.nextElement();
+                 return nextReference.getValue();
+            }
+        };
+    }
+    
+        
+    /**
+     *@see Hashtable
+     */
+    public Set keySet() {
+        purge();
+        Set referencedKeys = super.keySet();
+        Set unreferencedKeys = new HashSet();
+        for (Iterator it=referencedKeys.iterator(); it.hasNext();) {
+            Referenced referenceKey = (Referenced) it.next();
+            Object keyValue = referenceKey.getValue();
+            if (keyValue != null) {
+                unreferencedKeys.add(keyValue);
+            }
+        }
+        return unreferencedKeys;
+    }
+    
+    /**
+     *@see Hashtable
+     */    
+    public Object put(Object key, Object value) {
+        // check for nulls, ensuring symantics match superclass
+        if (key == null) {
+            throw new NullPointerException("Null keys are not allowed");
+        }
+        if (value == null) {
+            throw new NullPointerException("Null values are not allowed");
+        }
+
+        // for performance reasons, only purge every 
+        // MAX_CHANGES_BEFORE_PURGE times
+        if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) {
+            purge();
+            changeCount = 0;
+        }
+        // do a partial purge more often
+        else if ((changeCount % PARTIAL_PURGE_COUNT) == 0) {
+            purgeOne();
+        }
+        
+        Object result = null;
+        Referenced keyRef = new Referenced(key, queue);
+        return super.put(keyRef, value);
+    }
+    
+    /**
+     *@see Hashtable
+     */    
+    public void putAll(Map t) {
+        if (t != null) {
+            Set entrySet = t.entrySet();
+            for (Iterator it=entrySet.iterator(); it.hasNext();) {
+                Map.Entry entry = (Map.Entry) it.next();
+                put(entry.getKey(), entry.getValue());
+            }
+        }
+    }
+    
+    /**
+     *@see Hashtable
+     */      
+    public Collection values() {
+        purge();
+        return super.values();
+    }
+    
+    /**
+     *@see Hashtable
+     */     
+    public Object remove(Object key) {
+        // for performance reasons, only purge every 
+        // MAX_CHANGES_BEFORE_PURGE times
+        if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) {
+            purge();
+            changeCount = 0;
+        }
+        // do a partial purge more often
+        else if ((changeCount % PARTIAL_PURGE_COUNT) == 0) {
+            purgeOne();
+        }
+        return super.remove(new Referenced(key));
+    }
+    
+    /**
+     *@see Hashtable
+     */    
+    public boolean isEmpty() {
+        purge();
+        return super.isEmpty();
+    }
+    
+    /**
+     *@see Hashtable
+     */    
+    public int size() {
+        purge();
+        return super.size();
+    }
+    
+    /**
+     *@see Hashtable
+     */        
+    public String toString() {
+        purge();
+        return super.toString();
+    }
+    
+    /**
+     * @see Hashtable
+     */
+    protected void rehash() {
+        // purge here to save the effort of rehashing dead entries
+        purge();
+        super.rehash();
+    }
+    
+    /**
+     * Purges all entries whose wrapped keys
+     * have been garbage collected.
+     */
+    private void purge() {
+        synchronized (queue) {
+            WeakKey key;
+            while ((key = (WeakKey) queue.poll()) != null) {
+                super.remove(key.getReferenced());
+            }
+        }
+    }
+    
+    /**
+     * Purges one entry whose wrapped key 
+     * has been garbage collected.
+     */
+    private void purgeOne() {
+        
+        synchronized (queue) {
+            WeakKey key = (WeakKey) queue.poll();
+            if (key != null) {
+                super.remove(key.getReferenced());
+            }
+        }
+    }
+    
+    /** Entry implementation */
+    private final static class Entry implements Map.Entry {
+    
+        private final Object key;
+        private final Object value;
+        
+        private Entry(Object key, Object value) {
+            this.key = key;
+            this.value = value;
+        }
+    
+        public boolean equals(Object o) {
+            boolean result = false;
+            if (o != null && o instanceof Map.Entry) {
+                Map.Entry entry = (Map.Entry) o;
+                result =    (getKey()==null ?
+                                            entry.getKey() == null : 
+                                            getKey().equals(entry.getKey()))
+                            &&
+                            (getValue()==null ?
+                                            entry.getValue() == null : 
+                                            getValue().equals(entry.getValue()));
+            }
+            return result;
+        } 
+        
+        public int hashCode() {
+
+            return (getKey()==null ? 0 : getKey().hashCode()) ^
+                (getValue()==null ? 0 : getValue().hashCode());
+        }
+
+        public Object setValue(Object value) {
+            throw new UnsupportedOperationException("Entry.setValue is not supported.");
+        }
+        
+        public Object getValue() {
+            return value;
+        }
+        
+        public Object getKey() {
+            return key;
+        }
+    }
+    
+    
+    /** Wrapper giving correct symantics for equals and hashcode */
+    private final static class Referenced {
+        
+        private final WeakReference reference;
+        private final int           hashCode;
+
+        /**
+         * 
+         * @throws NullPointerException if referant is <code>null</code>
+         */        
+        private Referenced(Object referant) {
+            reference = new WeakReference(referant);
+            // Calc a permanent hashCode so calls to Hashtable.remove()
+            // work if the WeakReference has been cleared
+            hashCode  = referant.hashCode();
+        }
+        
+        /**
+         * 
+         * @throws NullPointerException if key is <code>null</code>
+         */
+        private Referenced(Object key, ReferenceQueue queue) {
+            reference = new WeakKey(key, queue, this);
+            // Calc a permanent hashCode so calls to Hashtable.remove()
+            // work if the WeakReference has been cleared
+            hashCode  = key.hashCode();
+
+        }
+        
+        public int hashCode() {
+            return hashCode;
+        }
+        
+        private Object getValue() {
+            return reference.get();
+        }
+        
+        public boolean equals(Object o) {
+            boolean result = false;
+            if (o instanceof Referenced) {
+                Referenced otherKey = (Referenced) o;
+                Object thisKeyValue = getValue();
+                Object otherKeyValue = otherKey.getValue();
+                if (thisKeyValue == null) {                     
+                    result = (otherKeyValue == null);
+                    
+                    // Since our hashcode was calculated from the original
+                    // non-null referant, the above check breaks the 
+                    // hashcode/equals contract, as two cleared Referenced
+                    // objects could test equal but have different hashcodes.
+                    // We can reduce (not eliminate) the chance of this
+                    // happening by comparing hashcodes.
+                    if (result == true) {
+                        result = (this.hashCode() == otherKey.hashCode());
+                    }
+                    // In any case, as our c'tor does not allow null referants
+                    // and Hashtable does not do equality checks between 
+                    // existing keys, normal hashtable operations should never 
+                    // result in an equals comparison between null referants
+                }
+                else
+                {
+                    result = thisKeyValue.equals(otherKeyValue);
+                }
+            }
+            return result;
+        }
+    }
+    
+    /**
+     * WeakReference subclass that holds a hard reference to an
+     * associated <code>value</code> and also makes accessible
+     * the Referenced object holding it.
+     */
+    private final static class WeakKey extends WeakReference {
+
+        private final Referenced referenced;
+        
+        private WeakKey(Object key, 
+                        ReferenceQueue queue,
+                        Referenced referenced) {
+            super(key, queue);
+            this.referenced = referenced;
+        }
+        
+        private Referenced getReferenced() {
+            return referenced;
+        }
+     }
+}
diff --git a/src/org/apache/commons/logging/impl/package.html b/src/org/apache/commons/logging/impl/package.html
new file mode 100644
index 0000000..eb26b76
--- /dev/null
+++ b/src/org/apache/commons/logging/impl/package.html
@@ -0,0 +1,21 @@
+<!--
+
+ Copyright 2001-2004 The Apache Software Foundation.
+ 
+ Licensed 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.
+
+-->
+
+<body>
+<p>Concrete implementations of commons-logging wrapper APIs.</p>
+</body>
diff --git a/src/org/apache/commons/logging/package.html b/src/org/apache/commons/logging/package.html
new file mode 100644
index 0000000..cfde4f0
--- /dev/null
+++ b/src/org/apache/commons/logging/package.html
@@ -0,0 +1,254 @@
+<!--
+
+ Copyright 2001-2004 The Apache Software Foundation.
+ 
+ Licensed 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.
+
+-->
+
+<body>
+<p>Simple wrapper API around multiple logging APIs.</p>
+
+
+<h3>Overview</h3>
+
+<p>This package provides an API for logging in server-based applications that
+can be used around a variety of different logging implementations, including
+prebuilt support for the following:</p>
+<ul>
+<li><a href="http://logging.apache.org/log4j/">Log4J</a> (version 1.2 or later)
+    from Apache's Jakarta project.  Each named <a href="Log.html">Log</a>
+    instance is connected to a corresponding Log4J Logger.</li>
+<li><a href="http://java.sun.com/j2se/1.4/docs/guide/util/logging/index.html">
+    JDK Logging API</a>, included in JDK 1.4 or later systems.  Each named
+    <a href="Log.html">Log</a> instance is connected to a corresponding
+    <code>java.util.logging.Logger</code> instance.</li>
+<li><a href="http://avalon.apache.org/logkit/">LogKit</a> from Apache's
+    Avalon project.  Each named <a href="Log.html">Log</a> instance is
+    connected to a corresponding LogKit <code>Logger</code>.</li>
+<li><a href="impl/NoOpLog.html">NoOpLog</a> implementation that simply swallows
+    all log output, for all named <a href="Log.html">Log</a> instances.</li>
+<li><a href="impl/SimpleLog.html">SimpleLog</a> implementation that writes all
+    log output, for all named <a href="Log.html">Log</a> instances, to
+    System.err.</li>
+</ul>
+
+
+<h3>Quick Start Guide</h3>
+
+<p>For those impatient to just get on with it, the following example
+illustrates the typical declaration and use of a logger that is named (by
+convention) after the calling class:
+
+<pre>
+    import org.apache.commons.logging.Log;
+    import org.apache.commons.logging.LogFactory;
+
+    public class Foo {
+
+        private Log log = LogFactory.getLog(Foo.class);
+
+        public void foo() {
+            ...
+            try {
+                if (log.isDebugEnabled()) {
+                    log.debug("About to do something to object " + name);
+                }
+                name.bar();
+            } catch (IllegalStateException e) {
+                log.error("Something bad happened to " + name, e);
+            }
+            ...
+        }
+</pre>
+
+<p>Unless you configure things differently, all log output will be written
+to System.err.  Therefore, you really will want to review the remainder of
+this page in order to understand how to configure logging for your
+application.</p>
+
+
+<h3>Configuring the Commons Logging Package</h3>
+
+
+<h4>Choosing a <code>LogFactory</code> Implementation</h4>
+
+<p>From an application perspective, the first requirement is to retrieve an
+object reference to the <code>LogFactory</code> instance that will be used
+to create <code><a href="Log.html">Log</a></code> instances for this
+application.  This is normally accomplished by calling the static
+<code>getFactory()</code> method.  This method implements the following
+discovery algorithm to select the name of the <code>LogFactory</code>
+implementation class this application wants to use:</p>
+<ul>
+<li>Check for a system property named
+   <code>org.apache.commons.logging.LogFactory</code>.</li>
+<li>Use the JDK 1.3 JAR Services Discovery mechanism (see
+    <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html">
+    http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html</a> for
+    more information) to look for a resource named
+    <code>META-INF/services/org.apache.commons.logging.LogFactory</code>
+    whose first line is assumed to contain the desired class name.</li>
+<li>Look for a properties file named <code>commons-logging.properties</code>
+    visible in the application class path, with a property named
+    <code>org.apache.commons.logging.LogFactory</code> defining the
+    desired implementation class name.</li>
+<li>Fall back to a default implementation, which is described
+    further below.</li>
+</ul>
+
+<p>If a <code>commons-logging.properties</code> file is found, all of the
+properties defined there are also used to set configuration attributes on
+the instantiated <code>LogFactory</code> instance.</p>
+
+<p>Once an implementation class name is selected, the corresponding class is
+loaded from the current Thread context class loader (if there is one), or
+from the class loader that loaded the <code>LogFactory</code> class itself
+otherwise.  This allows a copy of <code>commons-logging.jar</code> to be
+shared in a multiple class loader environment (such as a servlet container),
+but still allow each web application to provide its own <code>LogFactory</code>
+implementation, if it so desires.  An instance of this class will then be
+created, and cached per class loader.
+
+
+<h4>The Default <code>LogFactory</code> Implementation</h4>
+
+<p>The Logging Package APIs include a default <code>LogFactory</code>
+implementation class (<a href="impl/LogFactoryImpl.html">
+org.apache.commons.logging.impl.LogFactoryImpl</a>) that is selected if no
+other implementation class name can be discovered.  Its primary purpose is
+to create (as necessary) and return <a href="Log.html">Log</a> instances
+in response to calls to the <code>getInstance()</code> method.  The default
+implementation uses the following rules:</p>
+<ul>
+<li>At most one <code>Log</code> instance of the same name will be created.
+    Subsequent <code>getInstance()</code> calls to the same
+    <code>LogFactory</code> instance, with the same name or <code>Class</code>
+    parameter, will return the same <code>Log</code> instance.</li>
+<li>When a new <code>Log</code> instance must be created, the default
+    <code>LogFactory</code> implementation uses the following discovery
+    process:
+    <ul>
+    <li>Look for a configuration attribute of this factory named
+        <code>org.apache.commons.logging.Log</code> (for backwards
+        compatibility to pre-1.0 versions of this API, an attribute
+        <code>org.apache.commons.logging.log</code> is also consulted).</li>
+    <li>Look for a system property named
+        <code>org.apache.commons.logging.Log</code> (for backwards
+        compatibility to pre-1.0 versions of this API, a system property
+        <code>org.apache.commons.logging.log</code> is also consulted).</li>
+    <li>If the Log4J logging system is available in the application
+        class path, use the corresponding wrapper class
+        (<a href="impl/Log4JLogger.html">Log4JLogger</a>).</li>
+    <li>If the application is executing on a JDK 1.4 system, use
+        the corresponding wrapper class
+        (<a href="impl/Jdk14Logger.html">Jdk14Logger</a>).</li>
+    <li>Fall back to the default simple logging implementation
+        (<a href="impl/SimpleLog.html">SimpleLog</a>).</li>
+    </ul></li>
+<li>Load the class of the specified name from the thread context class
+    loader (if any), or from the class loader that loaded the
+    <code>LogFactory</code> class otherwise.</li>
+<li>Instantiate an instance of the selected <code>Log</code>
+    implementation class, passing the specified name as the single
+    argument to its constructor.</li>
+</ul>
+
+<p>See the <a href="impl/SimpleLog.html">SimpleLog</a> JavaDocs for detailed
+configuration information for this default implementation.</p>
+
+
+<h4>Configuring the Underlying Logging System</h4>
+
+<p>The basic principle is that the user is totally responsible for the
+configuration of the underlying logging system.
+Commons-logging should not change the existing configuration.</p>
+
+<p>Each individual <a href="Log.html">Log</a> implementation may
+support its own configuration properties.  These will be documented in the
+class descriptions for the corresponding implementation class.</p>
+
+<p>Finally, some <code>Log</code> implementations (such as the one for Log4J)
+require an external configuration file for the entire logging environment.
+This file should be prepared in a manner that is specific to the actual logging
+technology being used.</p>
+
+
+<h3>Using the Logging Package APIs</h3>
+
+<p>Use of the Logging Package APIs, from the perspective of an application
+component, consists of the following steps:</p>
+<ol>
+<li>Acquire a reference to an instance of
+    <a href="Log.html">org.apache.commons.logging.Log</a>, by calling the
+    factory method
+    <a href="LogFactory.html#getInstance(java.lang.String)">
+    LogFactory.getInstance(String name)</a>.  Your application can contain
+    references to multiple loggers that are used for different
+    purposes.  A typical scenario for a server application is to have each
+    major component of the server use its own Log instance.</li>
+<li>Cause messages to be logged (if the corresponding detail level is enabled)
+    by calling appropriate methods (<code>trace()</code>, <code>debug()</code>,
+    <code>info()</code>, <code>warn()</code>, <code>error</code>, and
+    <code>fatal()</code>).</li>
+</ol>
+
+<p>For convenience, <code>LogFactory</code> also offers a static method
+<code>getLog()</code> that combines the typical two-step pattern:</p>
+<pre>
+  Log log = LogFactory.getFactory().getInstance(Foo.class);
+</pre>
+<p>into a single method call:</p>
+<pre>
+  Log log = LogFactory.getLog(Foo.class);
+</pre>
+
+<p>For example, you might use the following technique to initialize and
+use a <a href="Log.html">Log</a> instance in an application component:</p>
+<pre>
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class MyComponent {
+
+  protected Log log =
+    LogFactory.getLog(MyComponent.class);
+
+  // Called once at startup time
+  public void start() {
+    ...
+    log.info("MyComponent started");
+    ...
+  }
+
+  // Called once at shutdown time
+  public void stop() {
+    ...
+    log.info("MyComponent stopped");
+    ...
+  }
+
+  // Called repeatedly to process a particular argument value
+  // which you want logged if debugging is enabled
+  public void process(String value) {
+    ...
+    // Do the string concatenation only if logging is enabled
+    if (log.isDebugEnabled())
+      log.debug("MyComponent processing " + value);
+    ...
+  }
+
+}
+</pre>
+
+</body>
diff --git a/src/org/apache/http/ConnectionClosedException.java b/src/org/apache/http/ConnectionClosedException.java
new file mode 100644
index 0000000..fa0e2db
--- /dev/null
+++ b/src/org/apache/http/ConnectionClosedException.java
@@ -0,0 +1,58 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/ConnectionClosedException.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.IOException;
+
+/**
+ * Indicates that a connection has been closed.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618017 $
+ * 
+ * @since 4.0
+ */
+public class ConnectionClosedException extends IOException {
+
+    private static final long serialVersionUID = 617550366255636674L;
+    
+    /**
+     * Creates a new ConnectionClosedException with the specified detail message.
+     * 
+     * @param message The exception detail message
+     */
+    public ConnectionClosedException(final String message) {
+        super(message);
+    }
+
+}
diff --git a/src/org/apache/http/ConnectionReuseStrategy.java b/src/org/apache/http/ConnectionReuseStrategy.java
new file mode 100644
index 0000000..635cc5c
--- /dev/null
+++ b/src/org/apache/http/ConnectionReuseStrategy.java
@@ -0,0 +1,75 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/ConnectionReuseStrategy.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * Interface for deciding whether a connection should be kept alive.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 496070 $
+ * 
+ * @since 4.0
+ */
+public interface ConnectionReuseStrategy {
+
+    /**
+     * Decides whether a connection can be kept open after a request.
+     * If this method returns <code>false</code>, the caller MUST
+     * close the connection to correctly implement the HTTP protocol.
+     * If it returns <code>true</code>, the caller SHOULD attempt to
+     * keep the connection open for reuse with another request.
+     * <br/>
+     * One can use the HTTP context to retrieve additional objects that 
+     * may be relevant for the keep-alive strategy: the actual HTTP 
+     * connection, the original HTTP request, target host if known, 
+     * number of times the connection has been reused already and so on.
+     * <br/>
+     * If the connection is already closed, <code>false</code> is returned.
+     * The stale connection check MUST NOT be triggered by a
+     * connection reuse strategy.
+     *
+     * @param response
+     *          The last response received over that connection.
+     * @param context   the context in which the connection is being 
+     *          used.
+     *
+     * @return <code>true</code> if the connection is allowed to be reused, or
+     *         <code>false</code> if it MUST NOT be reused
+     */
+    boolean keepAlive(HttpResponse response, HttpContext context);
+            
+}
diff --git a/src/org/apache/http/FormattedHeader.java b/src/org/apache/http/FormattedHeader.java
new file mode 100644
index 0000000..04ea279
--- /dev/null
+++ b/src/org/apache/http/FormattedHeader.java
@@ -0,0 +1,68 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/FormattedHeader.java $
+ * $Revision: 569781 $
+ * $Date: 2007-08-26 02:05:06 -0700 (Sun, 26 Aug 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * An HTTP header which is already formatted.
+ * For example when headers are received, the original formatting
+ * can be preserved. This allows for the header to be sent without
+ * another formatting step.
+ *
+ *
+ * @version $Revision: 569781 $
+ */
+public interface FormattedHeader extends Header {
+
+
+    /**
+     * Obtains the buffer with the formatted header.
+     * The returned buffer MUST NOT be modified.
+     *
+     * @return  the formatted header, in a buffer that must not be modified
+     */
+    CharArrayBuffer getBuffer()
+        ;
+
+    /**
+     * Obtains the start of the header value in the {@link #getBuffer buffer}.
+     * By accessing the value in the buffer, creation of a temporary string
+     * can be avoided.
+     *
+     * @return  index of the first character of the header value
+     *          in the buffer returned by {@link #getBuffer getBuffer}.
+     */
+    int getValuePos()
+        ;
+
+}
diff --git a/src/org/apache/http/Header.java b/src/org/apache/http/Header.java
new file mode 100644
index 0000000..4e04bec
--- /dev/null
+++ b/src/org/apache/http/Header.java
@@ -0,0 +1,64 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/Header.java $
+ * $Revision: 569636 $
+ * $Date: 2007-08-25 00:34:47 -0700 (Sat, 25 Aug 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * Represents an HTTP header field.
+ * 
+ * <p>The HTTP header fields follow the same generic format as
+ * that given in Section 3.1 of RFC 822. Each header field consists
+ * of a name followed by a colon (":") and the field value. Field names
+ * are case-insensitive. The field value MAY be preceded by any amount
+ * of LWS, though a single SP is preferred. 
+ *
+ *<pre>
+ *     message-header = field-name ":" [ field-value ]
+ *     field-name     = token
+ *     field-value    = *( field-content | LWS )
+ *     field-content  = &lt;the OCTETs making up the field-value
+ *                      and consisting of either *TEXT or combinations
+ *                      of token, separators, and quoted-string&gt;
+ *</pre>
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @version $Revision: 569636 $
+ */
+public interface Header {
+
+    String getName();
+    
+    String getValue();
+
+    HeaderElement[] getElements() throws ParseException;
+    
+}
diff --git a/src/org/apache/http/HeaderElement.java b/src/org/apache/http/HeaderElement.java
new file mode 100644
index 0000000..ddc4a9e
--- /dev/null
+++ b/src/org/apache/http/HeaderElement.java
@@ -0,0 +1,59 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HeaderElement.java $
+ * $Revision: 569828 $
+ * $Date: 2007-08-26 08:49:38 -0700 (Sun, 26 Aug 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * One element of an HTTP {@link Header header} value.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 569828 $ $Date: 2007-08-26 08:49:38 -0700 (Sun, 26 Aug 2007) $
+ * 
+ * @since 4.0
+ */
+public interface HeaderElement {
+
+    String getName();
+
+    String getValue();
+
+    NameValuePair[] getParameters();
+    
+    NameValuePair getParameterByName(String name);
+
+    int getParameterCount();
+
+    NameValuePair getParameter(int index);
+}
+
diff --git a/src/org/apache/http/HeaderElementIterator.java b/src/org/apache/http/HeaderElementIterator.java
new file mode 100644
index 0000000..14137f0
--- /dev/null
+++ b/src/org/apache/http/HeaderElementIterator.java
@@ -0,0 +1,61 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HeaderElementIterator.java $
+ * $Revision: 584542 $
+ * $Date: 2007-10-14 06:29:34 -0700 (Sun, 14 Oct 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.util.Iterator;
+
+/**
+ * A type-safe iterator for {@link HeaderElement HeaderElement} objects.
+ * 
+ * @version $Revision: 584542 $
+ */
+public interface HeaderElementIterator extends Iterator {
+    
+    /**
+     * Indicates whether there is another header element in this 
+     * iteration.
+     *
+     * @return  <code>true</code> if there is another header element,
+     *          <code>false</code> otherwise
+     */
+    boolean hasNext();
+    
+    /**
+     * Obtains the next header element from this iteration.
+     * This method should only be called while {@link #hasNext hasNext}
+     * is true.
+     *
+     * @return  the next header element in this iteration
+     */
+    HeaderElement nextElement();
+    
+}
diff --git a/src/org/apache/http/HeaderIterator.java b/src/org/apache/http/HeaderIterator.java
new file mode 100644
index 0000000..688b611
--- /dev/null
+++ b/src/org/apache/http/HeaderIterator.java
@@ -0,0 +1,64 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HeaderIterator.java $
+ * $Revision: 581981 $
+ * $Date: 2007-10-04 11:26:26 -0700 (Thu, 04 Oct 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+
+import java.util.Iterator;
+
+
+/**
+ * A type-safe iterator for {@link Header Header} objects.
+ * 
+ * @version $Revision: 581981 $
+ */
+public interface HeaderIterator extends Iterator {
+
+    /**
+     * Indicates whether there is another header in this iteration.
+     *
+     * @return  <code>true</code> if there is another header,
+     *          <code>false</code> otherwise
+     */
+    boolean hasNext()
+        ;
+
+
+    /**
+     * Obtains the next header from this iteration.
+     * This method should only be called while {@link #hasNext hasNext}
+     * is true.
+     *
+     * @return  the next header in this iteration
+     */
+    Header nextHeader()
+        ;
+}
diff --git a/src/org/apache/http/HttpClientConnection.java b/src/org/apache/http/HttpClientConnection.java
new file mode 100644
index 0000000..a38c8f3
--- /dev/null
+++ b/src/org/apache/http/HttpClientConnection.java
@@ -0,0 +1,112 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpClientConnection.java $
+ * $Revision: 542199 $
+ * $Date: 2007-05-28 04:23:46 -0700 (Mon, 28 May 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.IOException;
+
+/**
+ * An HTTP connection for use on the client side.
+ * It is used for sending requests and receiving responses.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 542199 $
+ * 
+ * @since 4.0
+ */
+public interface HttpClientConnection extends HttpConnection {
+
+    /**
+     * Checks if response data is available from the connection. May wait for
+     * the specified time until some data becomes available. Note that some
+     * implementations may completely ignore the timeout parameter.
+     * 
+     * @param timeout the maximum time in milliseconds to wait for data
+     * @return true if data is available; false if there was no data available
+     *         even after waiting for <code>timeout</code> milliseconds.
+     * @throws IOException if an error happens on the connection
+     */
+    boolean isResponseAvailable(int timeout) 
+        throws IOException; 
+    
+    /**
+     * Sends the request line and all headers over the connection.
+     * @param request the request whose headers to send.
+     * @throws HttpException 
+     * @throws IOException
+     */
+    void sendRequestHeader(HttpRequest request) 
+        throws HttpException, IOException;
+
+    /**
+     * Sends the request entity over the connection.
+     * @param request the request whose entity to send.
+     * @throws HttpException
+     * @throws IOException
+     */
+    void sendRequestEntity(HttpEntityEnclosingRequest request) 
+        throws HttpException, IOException;
+
+    /**
+     * Receives the request line and headers of the next response available from
+     * this connection. The caller should examine the HttpResponse object to
+     * find out if it should try to receive a response entity as well.
+     * 
+     * @return a new HttpResponse object with status line and headers
+     *         initialized.
+     * @throws HttpException
+     * @throws IOException
+     */
+    HttpResponse receiveResponseHeader() 
+        throws HttpException, IOException;
+
+    /**
+     * Receives the next response entity available from this connection and
+     * attaches it to an existing HttpResponse object.
+     * 
+     * @param response the response to attach the entity to
+     * @throws HttpException
+     * @throws IOException
+     */
+    void receiveResponseEntity(HttpResponse response) 
+        throws HttpException, IOException;
+    
+    /**
+     * Writes out all pending buffered data over the open connection.
+     * 
+     * @throws IOException
+     */
+    void flush() throws IOException;
+    
+}
diff --git a/src/org/apache/http/HttpConnection.java b/src/org/apache/http/HttpConnection.java
new file mode 100644
index 0000000..a3311f8
--- /dev/null
+++ b/src/org/apache/http/HttpConnection.java
@@ -0,0 +1,110 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpConnection.java $
+ * $Revision: 548031 $
+ * $Date: 2007-06-17 04:28:38 -0700 (Sun, 17 Jun 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.IOException;
+
+/**
+ * A generic HTTP connection, useful on client and server side.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 548031 $
+ * 
+ * @since 4.0
+ */
+public interface HttpConnection {
+
+    /**
+     * Closes this connection gracefully.
+     * This method will attempt to  flush the transmitter's
+     * internal buffer prior to closing the underlying socket.
+     * This method MUST NOT be called from a different thread to force 
+     * shutdown of the connection. Use {@link #shutdown shutdown} instead.
+     */
+    public void close() throws IOException;
+    
+    /**
+     * Checks if this connection is open.
+     * @return true if it is open, false if it is closed.
+     */
+    public boolean isOpen();
+ 
+    /**
+     * Checks whether this connection has gone down.
+     * Network connections may get closed during some time of inactivity
+     * for several reasons. The next time a read is attempted on such a
+     * connection it will throw an IOException.
+     * This method tries to alleviate this inconvenience by trying to
+     * find out if a connection is still usable. Implementations may do
+     * that by attempting a read with a very small timeout. Thus this
+     * method may block for a small amount of time before returning a result. 
+     * It is therefore an <i>expensive</i> operation.
+     * 
+     * @return  <code>true</code> if attempts to use this connection are
+     *          likely to succeed, or <code>false</code> if they are likely
+     *          to fail and this connection should be closed
+     */
+    public boolean isStale();
+    
+    /**
+     * Sets the socket timeout value.
+     * 
+     * @param timeout timeout value in milliseconds
+     */
+    void setSocketTimeout(int timeout);
+    
+    /**
+     * Returns the socket timeout value.
+     * 
+     * @return positive value in milliseconds if a timeout is set, 
+     * <code>0</code> if timeout is disabled or <code>-1</code> if 
+     * timeout is undefined.
+     */
+    int getSocketTimeout();
+
+    /**
+     * Force-closes this connection.
+     * This is the only method of a connection which may be called
+     * from a different thread to terminate the connection. 
+     * This method will not attempt to flush the transmitter's
+     * internal buffer prior to closing the underlying socket.
+     */
+    public void shutdown() throws IOException;
+    
+    /**
+     * Returns a collection of connection metrcis
+     * @return HttpConnectionMetrics
+     */
+    HttpConnectionMetrics getMetrics();
+    
+}
diff --git a/src/org/apache/http/HttpConnectionMetrics.java b/src/org/apache/http/HttpConnectionMetrics.java
new file mode 100644
index 0000000..289dd46
--- /dev/null
+++ b/src/org/apache/http/HttpConnectionMetrics.java
@@ -0,0 +1,79 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpConnectionMetrics.java $
+ * $Revision: 548035 $
+ * $Date: 2007-06-17 05:17:03 -0700 (Sun, 17 Jun 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * The point of access to the statistics of an {@link HttpConnection}.
+ */
+public interface HttpConnectionMetrics {
+
+    /**
+     * Returns the number of requests transferred over the connection, 
+     * 0 if not available.
+     */ 
+    long getRequestCount();
+    
+    /**
+     * Returns the number of responses transferred over the connection, 
+     * 0 if not available.
+     */ 
+    long getResponseCount();
+    
+    /**
+     * Returns the number of bytes transferred over the connection, 
+     * 0 if not available.
+     */ 
+    long getSentBytesCount();
+    
+    /**
+     * Returns the number of bytes transferred over the connection, 
+     * 0 if not available.
+     */ 
+    long getReceivedBytesCount(); 
+    
+    /**
+     * Return the value for the specified metric.
+     *
+     *@param metricName the name of the metric to query.
+     *
+     *@return the object representing the metric requested,
+     *        <code>null</code> if the metric cannot not found.
+     */
+    Object getMetric(String metricName);
+    
+    /**
+     * Resets the counts
+     *
+     */
+    void reset();
+    
+}
diff --git a/src/org/apache/http/HttpEntity.java b/src/org/apache/http/HttpEntity.java
new file mode 100644
index 0000000..51ddafc
--- /dev/null
+++ b/src/org/apache/http/HttpEntity.java
@@ -0,0 +1,196 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpEntity.java $
+ * $Revision: 645824 $
+ * $Date: 2008-04-08 03:12:41 -0700 (Tue, 08 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * An entity that can be sent or received with an HTTP message.
+ * Entities can be found in some
+ * {@link HttpEntityEnclosingRequest requests} and in
+ * {@link HttpResponse responses}, where they are optional.
+ * <p>
+ * In some places, the JavaDoc distinguishes three kinds of entities,
+ * depending on where their {@link #getContent content} originates:
+ * <ul>
+ * <li><b>streamed</b>: The content is received from a stream, or
+ *     generated on the fly. In particular, this category includes
+ *     entities being received from a {@link HttpConnection connection}.
+ *     {@link #isStreaming Streamed} entities are generally not
+ *      {@link #isRepeatable repeatable}.
+ *     </li>
+ * <li><b>self-contained</b>: The content is in memory or obtained by
+ *     means that are independent from a connection or other entity.
+ *     Self-contained entities are generally {@link #isRepeatable repeatable}.
+ *     </li>
+ * <li><b>wrapping</b>: The content is obtained from another entity.
+ *     </li>
+ * </ul>
+ * This distinction is important for connection management with incoming
+ * entities. For entities that are created by an application and only sent
+ * using the HTTP components framework, the difference between streamed
+ * and self-contained is of little importance. In that case, it is suggested
+ * to consider non-repeatable entities as streamed, and those that are
+ * repeatable (without a huge effort) as self-contained.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 645824 $
+ * 
+ * @since 4.0
+ */
+public interface HttpEntity {
+
+    /**
+     * Tells if the entity is capable to produce its data more than once.
+     * A repeatable entity's getContent() and writeTo(OutputStream) methods
+     * can be called more than once whereas a non-repeatable entity's can not.
+     * @return true if the entity is repeatable, false otherwise.
+     */
+    boolean isRepeatable();
+
+    /**
+     * Tells about chunked encoding for this entity.
+     * The primary purpose of this method is to indicate whether
+     * chunked encoding should be used when the entity is sent.
+     * For entities that are received, it can also indicate whether
+     * the entity was received with chunked encoding.
+     * <br/>
+     * The behavior of wrapping entities is implementation dependent,
+     * but should respect the primary purpose.
+     *
+     * @return  <code>true</code> if chunked encoding is preferred for this
+     *          entity, or <code>false</code> if it is not
+     */
+    boolean isChunked();
+
+    /**
+     * Tells the length of the content, if known.
+     *
+     * @return  the number of bytes of the content, or
+     *          a negative number if unknown. If the content length is known
+     *          but exceeds {@link java.lang.Long#MAX_VALUE Long.MAX_VALUE},
+     *          a negative number is returned.
+     */
+    long getContentLength();
+
+    /**
+     * Obtains the Content-Type header, if known.
+     * This is the header that should be used when sending the entity,
+     * or the one that was received with the entity. It can include a
+     * charset attribute.
+     *
+     * @return  the Content-Type header for this entity, or
+     *          <code>null</code> if the content type is unknown
+     */
+    Header getContentType();
+
+    /**
+     * Obtains the Content-Encoding header, if known.
+     * This is the header that should be used when sending the entity,
+     * or the one that was received with the entity.
+     * Wrapping entities that modify the content encoding should
+     * adjust this header accordingly.
+     *
+     * @return  the Content-Encoding header for this entity, or
+     *          <code>null</code> if the content encoding is unknown
+     */
+    Header getContentEncoding();
+
+    /**
+     * Creates a new InputStream object of the entity.
+     * It is a programming error
+     * to return the same InputStream object more than once.
+     * Entities that are not {@link #isRepeatable repeatable}
+     * will throw an exception if this method is called multiple times.
+     *
+     * @return a new input stream that returns the entity data.
+     *
+     * @throws IOException if the stream could not be created
+     * @throws IllegalStateException
+     *  if this entity is not repeatable and the stream
+     *  has already been obtained previously
+     */
+    InputStream getContent() throws IOException, IllegalStateException;
+
+    /**
+     * Writes the entity content to the output stream.  
+     * 
+     * @param outstream the output stream to write entity content to
+     * 
+     * @throws IOException if an I/O error occurs
+     */
+    void writeTo(OutputStream outstream) throws IOException;
+
+    /**
+     * Tells whether this entity depends on an underlying stream.
+     * Streamed entities should return <code>true</code> until the
+     * content has been consumed, <code>false</code> afterwards.
+     * Self-contained entities should return <code>false</code>.
+     * Wrapping entities should delegate this call to the wrapped entity.
+     * <br/>
+     * The content of a streamed entity is consumed when the stream
+     * returned by {@link #getContent getContent} has been read to EOF,
+     * or after {@link #consumeContent consumeContent} has been called.
+     * If a streamed entity can not detect whether the stream has been
+     * read to EOF, it should return <code>true</code> until
+     * {@link #consumeContent consumeContent} is called.
+     *
+     * @return  <code>true</code> if the entity content is streamed and
+     *          not yet consumed, <code>false</code> otherwise
+     */
+    boolean isStreaming(); // don't expect an exception here
+
+    /**
+     * TODO: The name of this method is misnomer. It will be renamed to
+     * #finish() in the next major release.
+     * <br/>
+     * This method is called to indicate that the content of this entity
+     * is no longer required. All entity implementations are expected to
+     * release all allocated resources as a result of this method 
+     * invocation. Content streaming entities are also expected to 
+     * dispose of the remaining content, if any. Wrapping entities should 
+     * delegate this call to the wrapped entity.
+     * <br/>
+     * This method is of particular importance for entities being
+     * received from a {@link HttpConnection connection}. The entity
+     * needs to be consumed completely in order to re-use the connection
+     * with keep-alive.
+     *
+     * @throws IOException if an I/O error occurs.
+     *          This indicates that connection keep-alive is not possible.
+     */
+    void consumeContent() throws IOException;
+
+} // interface HttpEntity
diff --git a/src/org/apache/http/HttpEntityEnclosingRequest.java b/src/org/apache/http/HttpEntityEnclosingRequest.java
new file mode 100644
index 0000000..c47c32b
--- /dev/null
+++ b/src/org/apache/http/HttpEntityEnclosingRequest.java
@@ -0,0 +1,63 @@
+/*
+ * $Header: $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * A request with an entity.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618017 $
+ * 
+ * @since 4.0
+ */
+public interface HttpEntityEnclosingRequest extends HttpRequest {
+
+    /**
+     * Tells if this request should use the expect-continue handshake.
+     * The expect continue handshake gives the server a chance to decide
+     * whether to accept the entity enclosing request before the possibly
+     * lengthy entity is sent across the wire.
+     * @return true if the expect continue handshake should be used, false if
+     * not.
+     */
+    boolean expectContinue();
+    
+    /**
+     * Hands the entity to the request.
+     * @param entity the entity to send.
+     */
+    void setEntity(HttpEntity entity);
+    
+    HttpEntity getEntity();
+    
+}
diff --git a/src/org/apache/http/HttpException.java b/src/org/apache/http/HttpException.java
new file mode 100644
index 0000000..77aacb1
--- /dev/null
+++ b/src/org/apache/http/HttpException.java
@@ -0,0 +1,75 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpException.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import org.apache.http.util.ExceptionUtils;
+
+/**
+ * Signals that an HTTP exception has occurred.
+ * 
+ * @author Laura Werner
+ * 
+ * @version $Revision: 618017 $ $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ */
+public class HttpException extends Exception {
+
+    private static final long serialVersionUID = -5437299376222011036L;
+    
+    /**
+     * Creates a new HttpException with a <tt>null</tt> detail message.
+     */
+    public HttpException() {
+        super();
+    }
+
+    /**
+     * Creates a new HttpException with the specified detail message.
+     *
+     * @param message the exception detail message
+     */
+    public HttpException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new HttpException with the specified detail message and cause.
+     * 
+     * @param message the exception detail message
+     * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+     * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+     */
+    public HttpException(final String message, final Throwable cause) {
+        super(message);
+        ExceptionUtils.initCause(this, cause);
+    }
+
+}
diff --git a/src/org/apache/http/HttpHost.java b/src/org/apache/http/HttpHost.java
new file mode 100644
index 0000000..869f5af
--- /dev/null
+++ b/src/org/apache/http/HttpHost.java
@@ -0,0 +1,218 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpHost.java $
+ * $Revision: 653058 $
+ * $Date: 2008-05-03 05:01:10 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.util.Locale;
+
+import org.apache.http.util.CharArrayBuffer;
+import org.apache.http.util.LangUtils;
+
+/**
+ * Holds all of the variables needed to describe an HTTP connection to a host.
+ * This includes remote host name, port and scheme.
+ * 
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author Laura Werner
+ * 
+ * @since 4.0
+ */
+public final class HttpHost implements Cloneable {
+
+    /** The default scheme is "http". */
+    public static final String DEFAULT_SCHEME_NAME = "http";
+    
+    /** The host to use. */
+    protected final String hostname;
+
+    /** The lowercase host, for {@link #equals} and {@link #hashCode}. */
+    protected final String lcHostname;
+
+
+    /** The port to use. */
+    protected final int port;
+
+    /** The scheme */
+    protected final String schemeName;
+
+
+    /**
+     * Creates a new {@link HttpHost HttpHost}, specifying all values.
+     * Constructor for HttpHost.
+     *   
+     * @param hostname  the hostname (IP or DNS name)
+     * @param port      the port number.
+     *                  <code>-1</code> indicates the scheme default port.
+     * @param scheme    the name of the scheme.
+     *                  <code>null</code> indicates the
+     *                  {@link #DEFAULT_SCHEME_NAME default scheme}
+     */
+    public HttpHost(final String hostname, int port, final String scheme) {
+        super();
+        if (hostname == null) {
+            throw new IllegalArgumentException("Host name may not be null");
+        }
+        this.hostname   = hostname;
+        this.lcHostname = hostname.toLowerCase(Locale.ENGLISH);
+        if (scheme != null) {
+            this.schemeName = scheme.toLowerCase(Locale.ENGLISH);
+        } else {
+            this.schemeName = DEFAULT_SCHEME_NAME;
+        }
+        this.port = port;
+    }
+
+    /**
+     * Creates a new {@link HttpHost HttpHost}, with default scheme.
+     *   
+     * @param hostname  the hostname (IP or DNS name)
+     * @param port      the port number.
+     *                  <code>-1</code> indicates the scheme default port.
+     */
+    public HttpHost(final String hostname, int port) {
+        this(hostname, port, null);
+    }
+    
+    /**
+     * Creates a new {@link HttpHost HttpHost}, with default scheme and port.
+     *   
+     * @param hostname  the hostname (IP or DNS name)
+     */
+    public HttpHost(final String hostname) {
+        this(hostname, -1, null);
+    }
+    
+    /**
+     * Copy constructor for {@link HttpHost HttpHost}.
+     * 
+     * @param httphost the HTTP host to copy details from
+     */
+    public HttpHost (final HttpHost httphost) {
+        this(httphost.hostname, httphost.port, httphost.schemeName);
+    }
+
+    /**
+     * Returns the host name.
+     * 
+     * @return the host name (IP or DNS name)
+     */
+    public String getHostName() {
+        return this.hostname;
+    }
+
+    /**
+     * Returns the port.
+     * 
+     * @return the host port, or <code>-1</code> if not set
+     */
+    public int getPort() {
+        return this.port;
+    }
+
+    /**
+     * Returns the scheme name.
+     *
+     * @return the scheme name
+     */
+    public String getSchemeName() {
+        return this.schemeName;
+    }
+
+    /**
+     * Return the host URI, as a string.
+     * 
+     * @return the host URI
+     */
+    public String toURI() {
+        CharArrayBuffer buffer = new CharArrayBuffer(32);        
+        buffer.append(this.schemeName);
+        buffer.append("://");
+        buffer.append(this.hostname);
+        if (this.port != -1) {
+            buffer.append(':');
+            buffer.append(Integer.toString(this.port));
+        }
+        return buffer.toString();
+    }
+
+
+    /**
+     * Obtains the host string, without scheme prefix.
+     *
+     * @return  the host string, for example <code>localhost:8080</code>
+     */
+    public String toHostString() {
+        CharArrayBuffer buffer = new CharArrayBuffer(32);        
+        buffer.append(this.hostname);
+        if (this.port != -1) {
+            buffer.append(':');
+            buffer.append(Integer.toString(this.port));
+        }
+        return buffer.toString();
+    }
+
+
+    public String toString() {
+        return toURI();
+    }    
+    
+
+    public boolean equals(final Object obj) {
+        if (obj == null) return false;
+        if (this == obj) return true;
+        if (obj instanceof HttpHost) {
+            HttpHost that = (HttpHost) obj;
+            return this.lcHostname.equals(that.lcHostname) 
+                && this.port == that.port
+                && this.schemeName.equals(that.schemeName);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode() {
+        int hash = LangUtils.HASH_SEED;
+        hash = LangUtils.hashCode(hash, this.lcHostname);
+        hash = LangUtils.hashCode(hash, this.port);
+        hash = LangUtils.hashCode(hash, this.schemeName);
+        return hash;
+    }
+
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+    
+}
diff --git a/src/org/apache/http/HttpInetConnection.java b/src/org/apache/http/HttpInetConnection.java
new file mode 100644
index 0000000..32ac04a
--- /dev/null
+++ b/src/org/apache/http/HttpInetConnection.java
@@ -0,0 +1,55 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpInetConnection.java $
+ * $Revision: 613298 $
+ * $Date: 2008-01-18 14:09:22 -0800 (Fri, 18 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.net.InetAddress;
+
+/**
+ * An HTTP connection over the Internet Protocol (IP).
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 613298 $
+ * 
+ * @since 4.0
+ */
+public interface HttpInetConnection extends HttpConnection {
+
+    InetAddress getLocalAddress();
+
+    int getLocalPort();
+    
+    InetAddress getRemoteAddress();
+
+    int getRemotePort();
+    
+}
diff --git a/src/org/apache/http/HttpMessage.java b/src/org/apache/http/HttpMessage.java
new file mode 100644
index 0000000..d24f0b4
--- /dev/null
+++ b/src/org/apache/http/HttpMessage.java
@@ -0,0 +1,191 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpMessage.java $
+ * $Revision: 610823 $
+ * $Date: 2008-01-10 07:53:53 -0800 (Thu, 10 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ * A generic HTTP message.
+ * Holds what is common between requests and responses.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 610823 $
+ * 
+ * @since 4.0
+ */
+public interface HttpMessage {
+    
+    /**
+     * Returns the protocol version this message is compatible with.
+     */
+    ProtocolVersion getProtocolVersion();
+
+    /**
+     * Checks if a certain header is present in this message. Header values are
+     * ignored.
+     * 
+     * @param name the header name to check for.
+     * @return true if at least one header with this name is present.
+     */
+    boolean containsHeader(String name);
+    
+    /**
+     * Returns all the headers with a specified name of this message. Header values
+     * are ignored. Headers are orderd in the sequence they will be sent over a
+     * connection.
+     * 
+     * @param name the name of the headers to return.
+     * @return the headers whose name property equals <code>name</code>.
+     */
+    Header[] getHeaders(String name);
+
+    /**
+     * Returns the first header with a specified name of this message. Header
+     * values are ignored. If there is more than one matching header in the
+     * message the first element of {@link #getHeaders(String)} is returned.
+     * If there is no matching header in the message <code>null</code> is 
+     * returned.
+     * 
+     * @param name the name of the header to return.
+     * @return the first header whose name property equals <code>name</code>
+     *   or <code>null</code> if no such header could be found.
+     */
+    Header getFirstHeader(String name);
+
+    /**
+     * Returns the last header with a specified name of this message. Header values
+     * are ignored. If there is more than one matching header in the message the
+     * last element of {@link #getHeaders(String)} is returned. If there is no 
+     * matching header in the message <code>null</code> is returned.
+     * 
+     * @param name the name of the header to return.
+     * @return the last header whose name property equals <code>name</code>.
+     *   or <code>null</code> if no such header could be found.
+     */
+    Header getLastHeader(String name);
+
+    /**
+     * Returns all the headers of this message. Headers are orderd in the sequence
+     * they will be sent over a connection.
+     * 
+     * @return all the headers of this message
+     */
+    Header[] getAllHeaders();
+
+    /**
+     * Adds a header to this message. The header will be appended to the end of
+     * the list.
+     * 
+     * @param header the header to append.
+     */
+    void addHeader(Header header);
+
+    /**
+     * Adds a header to this message. The header will be appended to the end of
+     * the list.
+     * 
+     * @param name the name of the header.
+     * @param value the value of the header.
+     */
+    void addHeader(String name, String value);
+
+    /**
+     * Overwrites the first header with the same name. The new header will be appended to 
+     * the end of the list, if no header with the given name can be found.
+     * 
+     * @param header the header to set.
+     */
+    void setHeader(Header header);
+
+    /**
+     * Overwrites the first header with the same name. The new header will be appended to 
+     * the end of the list, if no header with the given name can be found.
+     * 
+     * @param name the name of the header.
+     * @param value the value of the header.
+     */
+    void setHeader(String name, String value);
+
+    /**
+     * Overwrites all the headers in the message.
+     * 
+     * @param headers the array of headers to set.
+     */
+    void setHeaders(Header[] headers);
+
+    /**
+     * Removes a header from this message.
+     * 
+     * @param header the header to remove.
+     */
+    void removeHeader(Header header);
+    
+    /**
+     * Removes all headers with a certain name from this message.
+     * 
+     * @param name The name of the headers to remove.
+     */
+    void removeHeaders(String name);
+    
+    /**
+     * Returns an iterator of all the headers.
+     * 
+     * @return Iterator that returns Header objects in the sequence they are
+     *         sent over a connection.
+     */
+    HeaderIterator headerIterator();
+
+    /**
+     * Returns an iterator of the headers with a given name.
+     *
+     * @param name      the name of the headers over which to iterate, or
+     *                  <code>null</code> for all headers
+     *
+     * @return Iterator that returns Header objects with the argument name
+     *         in the sequence they are sent over a connection.
+     */
+    HeaderIterator headerIterator(String name);
+
+    /**
+     * Returns the parameters effective for this message as set by
+     * {@link #setParams(HttpParams)}.
+     */
+    HttpParams getParams();
+
+    /**
+     * Provides parameters to be used for the processing of this message.
+     * @param params the parameters
+     */
+    void setParams(HttpParams params);
+        
+}
diff --git a/src/org/apache/http/HttpRequest.java b/src/org/apache/http/HttpRequest.java
new file mode 100644
index 0000000..8558a97
--- /dev/null
+++ b/src/org/apache/http/HttpRequest.java
@@ -0,0 +1,51 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpRequest.java $
+ * $Revision: 528428 $
+ * $Date: 2007-04-13 03:26:04 -0700 (Fri, 13 Apr 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * An HTTP request.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 528428 $
+ * 
+ * @since 4.0
+ */
+public interface HttpRequest extends HttpMessage {
+
+    /**
+     * Returns the request line of this request.
+     * @return the request line.
+     */
+    RequestLine getRequestLine();
+    
+}
diff --git a/src/org/apache/http/HttpRequestFactory.java b/src/org/apache/http/HttpRequestFactory.java
new file mode 100644
index 0000000..d385127
--- /dev/null
+++ b/src/org/apache/http/HttpRequestFactory.java
@@ -0,0 +1,51 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpRequestFactory.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * A factory for {@link HttpRequest HttpRequest} objects.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618017 $
+ * 
+ * @since 4.0
+ */
+public interface HttpRequestFactory {
+    
+    HttpRequest newHttpRequest(RequestLine requestline) 
+        throws MethodNotSupportedException;
+    
+    HttpRequest newHttpRequest(String method, String uri) 
+            throws MethodNotSupportedException;
+    
+}
diff --git a/src/org/apache/http/HttpRequestInterceptor.java b/src/org/apache/http/HttpRequestInterceptor.java
new file mode 100644
index 0000000..db2194f
--- /dev/null
+++ b/src/org/apache/http/HttpRequestInterceptor.java
@@ -0,0 +1,69 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpRequestInterceptor.java $
+ * $Revision: 618367 $
+ * $Date: 2008-02-04 10:26:06 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.IOException;
+
+import org.apache.http.protocol.HttpContext;
+
+
+/**
+ * Processes a request.
+ * Provides the ability to process a request before it is sent
+ * to the server or after it has received on the server side.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 618367 $
+ * 
+ * @since 4.0
+ */
+public interface HttpRequestInterceptor {
+
+    /**
+     * Processes a request.
+     * On the client side, this step is performed before the request is
+     * sent to the server. On the server side, this step is performed
+     * on incoming messages before the message body is evaluated.
+     *
+     * @param request   the request to preprocess
+     * @param context   the context for the request
+     *
+     * @throws IOException      in case of an IO problem
+     * @throws HttpException    in case of a protocol or other problem
+     */
+    void process(HttpRequest request, HttpContext context) 
+        throws HttpException, IOException;
+    
+}
diff --git a/src/org/apache/http/HttpResponse.java b/src/org/apache/http/HttpResponse.java
new file mode 100644
index 0000000..f232f86
--- /dev/null
+++ b/src/org/apache/http/HttpResponse.java
@@ -0,0 +1,161 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpResponse.java $
+ * $Revision: 652956 $
+ * $Date: 2008-05-02 17:13:05 -0700 (Fri, 02 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+
+import java.util.Locale;
+
+
+/**
+ * An HTTP response.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 652956 $
+ * 
+ * @since 4.0
+ */
+public interface HttpResponse extends HttpMessage {
+
+    /**
+     * Obtains the status line of this response.
+     * The status line can be set using one of the
+     * {@link #setStatusLine setStatusLine} methods,
+     * or it can be initialized in a constructor.
+     *
+     * @return  the status line, or <code>null</code> if not yet set
+     */
+    StatusLine getStatusLine();
+
+    /**
+     * Sets the status line of this response.
+     *
+     * @param statusline the status line of this response
+     */
+    void setStatusLine(StatusLine statusline);
+
+    /**
+     * Sets the status line of this response.
+     * The reason phrase will be determined based on the current
+     * {@link #getLocale locale}.
+     *
+     * @param ver       the HTTP version
+     * @param code      the status code
+     */
+    void setStatusLine(ProtocolVersion ver, int code);
+
+    /**
+     * Sets the status line of this response with a reason phrase.
+     *
+     * @param ver       the HTTP version
+     * @param code      the status code
+     * @param reason    the reason phrase, or <code>null</code> to omit
+     */
+    void setStatusLine(ProtocolVersion ver, int code, String reason);
+    
+    /**
+     * Updates the status line of this response with a new status code.
+     * The status line can only be updated if it is available. It must
+     * have been set either explicitly or in a constructor.
+     * <br/>
+     * The reason phrase will be updated according to the new status code,
+     * based on the current {@link #getLocale locale}. It can be set
+     * explicitly using {@link #setReasonPhrase setReasonPhrase}.
+     * 
+     * @param code the HTTP status code.
+     *
+     * @throws IllegalStateException
+     *          if the status line has not be set
+     *
+     * @see HttpStatus
+     * @see #setStatusLine(StatusLine)
+     * @see #setStatusLine(ProtocolVersion,int)
+     */
+    void setStatusCode(int code)
+        throws IllegalStateException;
+
+    /**
+     * Updates the status line of this response with a new reason phrase.
+     * The status line can only be updated if it is available. It must
+     * have been set either explicitly or in a constructor.
+     *
+     * @param reason    the new reason phrase as a single-line string, or
+     *                  <code>null</code> to unset the reason phrase
+     *
+     * @throws IllegalStateException
+     *          if the status line has not be set
+     *
+     * @see #setStatusLine(StatusLine)
+     * @see #setStatusLine(ProtocolVersion,int)
+     */
+    void setReasonPhrase(String reason)
+        throws IllegalStateException;
+
+    /**
+     * Obtains the message entity of this response, if any.
+     * The entity is provided by calling {@link #setEntity setEntity}.
+     *
+     * @return  the response entity, or
+     *          <code>null</code> if there is none
+     */
+    HttpEntity getEntity();
+    
+    /**
+     * Associates a response entity with this response.
+     *
+     * @param entity    the entity to associate with this response, or
+     *                  <code>null</code> to unset
+     */
+    void setEntity(HttpEntity entity);
+
+    /**
+     * Obtains the locale of this response.
+     * The locale is used to determine the reason phrase
+     * for the {@link #setStatusCode status code}.
+     * It can be changed using {@link #setLocale setLocale}.
+     *
+     * @return  the locale of this response, never <code>null</code>
+     */
+    Locale getLocale();
+
+    /**
+     * Changes the locale of this response.
+     * If there is a status line, it's reason phrase will be updated
+     * according to the status code and new locale.
+     *
+     * @param loc       the new locale
+     *
+     * @see #getLocale getLocale
+     * @see #setStatusCode setStatusCode
+     */
+    void setLocale(Locale loc);
+}
diff --git a/src/org/apache/http/HttpResponseFactory.java b/src/org/apache/http/HttpResponseFactory.java
new file mode 100644
index 0000000..3ed06ff
--- /dev/null
+++ b/src/org/apache/http/HttpResponseFactory.java
@@ -0,0 +1,76 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpResponseFactory.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import org.apache.http.protocol.HttpContext;
+
+
+/**
+ * A factory for {@link HttpResponse HttpResponse} objects.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573864 $
+ * 
+ * @since 4.0
+ */
+public interface HttpResponseFactory {
+
+    /**
+     * Creates a new response from status line elements.
+     *
+     * @param ver       the protocol version
+     * @param status    the status code
+     * @param context   the context from which to determine the locale
+     *                  for looking up a reason phrase to the status code, or
+     *                  <code>null</code> to use the default locale
+     *
+     * @return  the new response with an initialized status line
+     */    
+    HttpResponse newHttpResponse(ProtocolVersion ver, int status,
+                                 HttpContext context);
+    
+    /**
+     * Creates a new response from a status line.
+     *
+     * @param statusline the status line
+     * @param context    the context from which to determine the locale
+     *                   for looking up a reason phrase if the status code
+     *                   is updated, or
+     *                   <code>null</code> to use the default locale
+     *
+     * @return  the new response with the argument status line
+     */    
+    HttpResponse newHttpResponse(StatusLine statusline,
+                                 HttpContext context);
+    
+}
diff --git a/src/org/apache/http/HttpResponseInterceptor.java b/src/org/apache/http/HttpResponseInterceptor.java
new file mode 100644
index 0000000..cae1526
--- /dev/null
+++ b/src/org/apache/http/HttpResponseInterceptor.java
@@ -0,0 +1,68 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpResponseInterceptor.java $
+ * $Revision: 618367 $
+ * $Date: 2008-02-04 10:26:06 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.IOException;
+
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * Processes a response.
+ * Provides the ability to process a response before it is sent
+ * to the client or after it has been received on the client side.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 618367 $
+ * 
+ * @since 4.0
+ */
+public interface HttpResponseInterceptor {
+
+    /**
+     * Processes a response.
+     * On the server side, this step is performed before the response is
+     * sent to the client. On the client side, this step is performed
+     * on incoming messages before the message body is evaluated.
+     *
+     * @param response  the response to postprocess
+     * @param context   the context for the request
+     *
+     * @throws IOException      in case of an IO problem
+     * @throws HttpException    in case of a protocol or other problem
+     */
+    void process(HttpResponse response, HttpContext context) 
+        throws HttpException, IOException;
+
+}
diff --git a/src/org/apache/http/HttpServerConnection.java b/src/org/apache/http/HttpServerConnection.java
new file mode 100644
index 0000000..8cea544
--- /dev/null
+++ b/src/org/apache/http/HttpServerConnection.java
@@ -0,0 +1,96 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpServerConnection.java $
+ * $Revision: 542199 $
+ * $Date: 2007-05-28 04:23:46 -0700 (Mon, 28 May 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.IOException;
+
+/**
+ * An HTTP connection for use on the server side.
+ * Requests are received, responses are sent.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 542199 $
+ * 
+ * @since 4.0
+ */
+public interface HttpServerConnection extends HttpConnection {
+
+    /**
+     * Receives the request line and all headers available from this connection.
+     * The caller should examine the returned request and decide if to receive a
+     * request entity as well.
+     * 
+     * @return a new HttpRequest object whose request line and headers are
+     *         initialized.
+     * @throws HttpException
+     * @throws IOException
+     */
+    HttpRequest receiveRequestHeader() 
+        throws HttpException, IOException;
+
+    /**
+     * Receives the next request entity available from this connection and attaches it to 
+     * an existing request. 
+     * @param request the request to attach the entity to.
+     * @throws HttpException
+     * @throws IOException
+     */
+    void receiveRequestEntity(HttpEntityEnclosingRequest request) 
+        throws HttpException, IOException;
+
+    /**
+     * Sends the response line and headers of a response over this connection.
+     * @param response the response whose headers to send.
+     * @throws HttpException
+     * @throws IOException
+     */
+    void sendResponseHeader(HttpResponse response) 
+        throws HttpException, IOException;
+    
+    /**
+     * Sends the response entity of a response over this connection.
+     * @param response the response whose entity to send.
+     * @throws HttpException
+     * @throws IOException
+     */
+    void sendResponseEntity(HttpResponse response) 
+        throws HttpException, IOException;
+    
+    /**
+     * Sends all pending buffered data over this connection.
+     * @throws IOException
+     */
+    void flush()
+        throws IOException;
+    
+}
diff --git a/src/org/apache/http/HttpStatus.java b/src/org/apache/http/HttpStatus.java
new file mode 100644
index 0000000..f8f7f5d
--- /dev/null
+++ b/src/org/apache/http/HttpStatus.java
@@ -0,0 +1,182 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpStatus.java $
+ * $Revision: 503381 $
+ * $Date: 2007-02-04 02:59:10 -0800 (Sun, 04 Feb 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * Constants enumerating the HTTP status codes.
+ * All status codes defined in RFC1945 (HTTP/1.0), RFC2616 (HTTP/1.1), and
+ * RFC2518 (WebDAV) are listed.
+ * 
+ * @see StatusLine
+ * @author Unascribed
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * 
+ * @version $Revision: 503381 $
+ */
+public interface HttpStatus {
+
+    // --- 1xx Informational ---
+
+    /** <tt>100 Continue</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_CONTINUE = 100;
+    /** <tt>101 Switching Protocols</tt> (HTTP/1.1 - RFC 2616)*/
+    public static final int SC_SWITCHING_PROTOCOLS = 101;
+    /** <tt>102 Processing</tt> (WebDAV - RFC 2518) */
+    public static final int SC_PROCESSING = 102;
+
+    // --- 2xx Success ---
+
+    /** <tt>200 OK</tt> (HTTP/1.0 - RFC 1945) */
+    public static final int SC_OK = 200;
+    /** <tt>201 Created</tt> (HTTP/1.0 - RFC 1945) */
+    public static final int SC_CREATED = 201;
+    /** <tt>202 Accepted</tt> (HTTP/1.0 - RFC 1945) */
+    public static final int SC_ACCEPTED = 202;
+    /** <tt>203 Non Authoritative Information</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203;
+    /** <tt>204 No Content</tt> (HTTP/1.0 - RFC 1945) */
+    public static final int SC_NO_CONTENT = 204;
+    /** <tt>205 Reset Content</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_RESET_CONTENT = 205;
+    /** <tt>206 Partial Content</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_PARTIAL_CONTENT = 206;
+    /** 
+     * <tt>207 Multi-Status</tt> (WebDAV - RFC 2518) or <tt>207 Partial Update
+     * OK</tt> (HTTP/1.1 - draft-ietf-http-v11-spec-rev-01?)
+     */
+    public static final int SC_MULTI_STATUS = 207;
+
+    // --- 3xx Redirection ---
+
+    /** <tt>300 Mutliple Choices</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_MULTIPLE_CHOICES = 300;
+    /** <tt>301 Moved Permanently</tt> (HTTP/1.0 - RFC 1945) */
+    public static final int SC_MOVED_PERMANENTLY = 301;
+    /** <tt>302 Moved Temporarily</tt> (Sometimes <tt>Found</tt>) (HTTP/1.0 - RFC 1945) */
+    public static final int SC_MOVED_TEMPORARILY = 302;
+    /** <tt>303 See Other</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_SEE_OTHER = 303;
+    /** <tt>304 Not Modified</tt> (HTTP/1.0 - RFC 1945) */
+    public static final int SC_NOT_MODIFIED = 304;
+    /** <tt>305 Use Proxy</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_USE_PROXY = 305;
+    /** <tt>307 Temporary Redirect</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_TEMPORARY_REDIRECT = 307;
+
+    // --- 4xx Client Error ---
+
+    /** <tt>400 Bad Request</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_BAD_REQUEST = 400;
+    /** <tt>401 Unauthorized</tt> (HTTP/1.0 - RFC 1945) */
+    public static final int SC_UNAUTHORIZED = 401;
+    /** <tt>402 Payment Required</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_PAYMENT_REQUIRED = 402;
+    /** <tt>403 Forbidden</tt> (HTTP/1.0 - RFC 1945) */
+    public static final int SC_FORBIDDEN = 403;
+    /** <tt>404 Not Found</tt> (HTTP/1.0 - RFC 1945) */
+    public static final int SC_NOT_FOUND = 404;
+    /** <tt>405 Method Not Allowed</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_METHOD_NOT_ALLOWED = 405;
+    /** <tt>406 Not Acceptable</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_NOT_ACCEPTABLE = 406;
+    /** <tt>407 Proxy Authentication Required</tt> (HTTP/1.1 - RFC 2616)*/
+    public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
+    /** <tt>408 Request Timeout</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_REQUEST_TIMEOUT = 408;
+    /** <tt>409 Conflict</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_CONFLICT = 409;
+    /** <tt>410 Gone</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_GONE = 410;
+    /** <tt>411 Length Required</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_LENGTH_REQUIRED = 411;
+    /** <tt>412 Precondition Failed</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_PRECONDITION_FAILED = 412;
+    /** <tt>413 Request Entity Too Large</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_REQUEST_TOO_LONG = 413;
+    /** <tt>414 Request-URI Too Long</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_REQUEST_URI_TOO_LONG = 414;
+    /** <tt>415 Unsupported Media Type</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
+    /** <tt>416 Requested Range Not Satisfiable</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+    /** <tt>417 Expectation Failed</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_EXPECTATION_FAILED = 417;
+
+    /**
+     * Static constant for a 418 error.
+     * <tt>418 Unprocessable Entity</tt> (WebDAV drafts?)
+     * or <tt>418 Reauthentication Required</tt> (HTTP/1.1 drafts?)
+     */
+    // not used
+    // public static final int SC_UNPROCESSABLE_ENTITY = 418;
+
+    /**
+     * Static constant for a 419 error.
+     * <tt>419 Insufficient Space on Resource</tt>
+     * (WebDAV - draft-ietf-webdav-protocol-05?)
+     * or <tt>419 Proxy Reauthentication Required</tt>
+     * (HTTP/1.1 drafts?)
+     */
+    public static final int SC_INSUFFICIENT_SPACE_ON_RESOURCE = 419;
+    /**
+     * Static constant for a 420 error.
+     * <tt>420 Method Failure</tt>
+     * (WebDAV - draft-ietf-webdav-protocol-05?)
+     */
+    public static final int SC_METHOD_FAILURE = 420;
+    /** <tt>422 Unprocessable Entity</tt> (WebDAV - RFC 2518) */
+    public static final int SC_UNPROCESSABLE_ENTITY = 422;
+    /** <tt>423 Locked</tt> (WebDAV - RFC 2518) */
+    public static final int SC_LOCKED = 423;
+    /** <tt>424 Failed Dependency</tt> (WebDAV - RFC 2518) */
+    public static final int SC_FAILED_DEPENDENCY = 424;
+
+    // --- 5xx Server Error ---
+
+    /** <tt>500 Server Error</tt> (HTTP/1.0 - RFC 1945) */
+    public static final int SC_INTERNAL_SERVER_ERROR = 500;
+    /** <tt>501 Not Implemented</tt> (HTTP/1.0 - RFC 1945) */
+    public static final int SC_NOT_IMPLEMENTED = 501;
+    /** <tt>502 Bad Gateway</tt> (HTTP/1.0 - RFC 1945) */
+    public static final int SC_BAD_GATEWAY = 502;
+    /** <tt>503 Service Unavailable</tt> (HTTP/1.0 - RFC 1945) */
+    public static final int SC_SERVICE_UNAVAILABLE = 503;
+    /** <tt>504 Gateway Timeout</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_GATEWAY_TIMEOUT = 504;
+    /** <tt>505 HTTP Version Not Supported</tt> (HTTP/1.1 - RFC 2616) */
+    public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
+
+    /** <tt>507 Insufficient Storage</tt> (WebDAV - RFC 2518) */
+    public static final int SC_INSUFFICIENT_STORAGE = 507;
+
+}
diff --git a/src/org/apache/http/HttpVersion.java b/src/org/apache/http/HttpVersion.java
new file mode 100644
index 0000000..243f199
--- /dev/null
+++ b/src/org/apache/http/HttpVersion.java
@@ -0,0 +1,104 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpVersion.java $
+ * $Revision: 609106 $
+ * $Date: 2008-01-05 01:15:42 -0800 (Sat, 05 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.Serializable;
+
+/**
+ * Represents an HTTP version, as specified in RFC 2616.
+ * 
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Revision: 609106 $ $Date: 2008-01-05 01:15:42 -0800 (Sat, 05 Jan 2008) $
+ */
+public final class HttpVersion extends ProtocolVersion
+    implements Serializable {
+
+    private static final long serialVersionUID = -5856653513894415344L;
+
+    /** The protocol name. */
+    public static final String HTTP = "HTTP";
+    
+    /** HTTP protocol version 0.9 */
+    public static final HttpVersion HTTP_0_9 = new HttpVersion(0, 9);  
+
+    /** HTTP protocol version 1.0 */
+    public static final HttpVersion HTTP_1_0 = new HttpVersion(1, 0);  
+
+    /** HTTP protocol version 1.1 */
+    public static final HttpVersion HTTP_1_1 = new HttpVersion(1, 1);  
+
+    
+    /**
+     * Create an HTTP protocol version designator.
+     *
+     * @param major   the major version number of the HTTP protocol
+     * @param minor   the minor version number of the HTTP protocol
+     * 
+     * @throws IllegalArgumentException if either major or minor version number is negative
+     */
+    public HttpVersion(int major, int minor) {
+        super(HTTP, major, minor);
+    }
+
+
+    /**
+     * Obtains a specific HTTP version.
+     *
+     * @param major     the major version
+     * @param minor     the minor version
+     *
+     * @return  an instance of {@link HttpVersion} with the argument version
+     */
+    public ProtocolVersion forVersion(int major, int minor) {
+
+        if ((major == this.major) && (minor == this.minor)) {
+            return this;
+        }
+
+        if (major == 1) {
+            if (minor == 0) {
+                return HTTP_1_0;
+            }
+            if (minor == 1) {
+                return HTTP_1_1;
+            }
+        }
+        if ((major == 0) && (minor == 9)) {
+            return HTTP_0_9;
+        }
+
+        // argument checking is done in the constructor
+        return new HttpVersion(major, minor);
+    }
+
+}
diff --git a/src/org/apache/http/MalformedChunkCodingException.java b/src/org/apache/http/MalformedChunkCodingException.java
new file mode 100644
index 0000000..2267a2e
--- /dev/null
+++ b/src/org/apache/http/MalformedChunkCodingException.java
@@ -0,0 +1,59 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/MalformedChunkCodingException.java $
+ * $Revision: 609106 $
+ * $Date: 2008-01-05 01:15:42 -0800 (Sat, 05 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.IOException;
+
+/**
+ * Signals a malformed chunked stream.
+ */
+public class MalformedChunkCodingException extends IOException {
+
+    private static final long serialVersionUID = 2158560246948994524L;
+
+    /**
+     * Creates a MalformedChunkCodingException without a detail message.
+     */
+    public MalformedChunkCodingException() {
+        super();
+    }
+
+    /**
+     * Creates a MalformedChunkCodingException with the specified detail message.
+     * 
+     * @param message The exception detail message 
+     */
+    public MalformedChunkCodingException(final String message) {
+        super(message);
+    }
+
+}
diff --git a/src/org/apache/http/MethodNotSupportedException.java b/src/org/apache/http/MethodNotSupportedException.java
new file mode 100644
index 0000000..3ccf72d
--- /dev/null
+++ b/src/org/apache/http/MethodNotSupportedException.java
@@ -0,0 +1,67 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/MethodNotSupportedException.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+
+/**
+ * Indicates that an HTTP method is not supported.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618017 $
+ * 
+ * @since 4.0
+ */
+public class MethodNotSupportedException extends HttpException {
+
+    private static final long serialVersionUID = 3365359036840171201L;
+    
+    /**
+     * Creates a new MethodNotSupportedException with the specified detail message.
+     * 
+     * @param message The exception detail message
+     */
+    public MethodNotSupportedException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new MethodNotSupportedException with the specified detail message and cause.
+     * 
+     * @param message the exception detail message
+     * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+     * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+     */
+    public MethodNotSupportedException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/org/apache/http/NameValuePair.java b/src/org/apache/http/NameValuePair.java
new file mode 100644
index 0000000..1ab861a
--- /dev/null
+++ b/src/org/apache/http/NameValuePair.java
@@ -0,0 +1,108 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/NameValuePair.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * A simple class encapsulating an attribute/value pair.
+ * <p>
+ *  This class comforms to the generic grammar and formatting rules outlined in the 
+ *  <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2">Section 2.2</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.2 Basic Rules</h>
+ * <p>
+ *  The following rules are used throughout this specification to describe basic parsing constructs. 
+ *  The US-ASCII coded character set is defined by ANSI X3.4-1986.
+ * </p>
+ * <pre>
+ *     OCTET          = <any 8-bit sequence of data>
+ *     CHAR           = <any US-ASCII character (octets 0 - 127)>
+ *     UPALPHA        = <any US-ASCII uppercase letter "A".."Z">
+ *     LOALPHA        = <any US-ASCII lowercase letter "a".."z">
+ *     ALPHA          = UPALPHA | LOALPHA
+ *     DIGIT          = <any US-ASCII digit "0".."9">
+ *     CTL            = <any US-ASCII control character
+ *                      (octets 0 - 31) and DEL (127)>
+ *     CR             = <US-ASCII CR, carriage return (13)>
+ *     LF             = <US-ASCII LF, linefeed (10)>
+ *     SP             = <US-ASCII SP, space (32)>
+ *     HT             = <US-ASCII HT, horizontal-tab (9)>
+ *     <">            = <US-ASCII double-quote mark (34)>
+ * </pre>
+ * <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.com">Oleg Kalnichevski</a>
+ * 
+ */
+public interface NameValuePair {
+
+    String getName();
+
+    String getValue();
+      
+}
diff --git a/src/org/apache/http/NoHttpResponseException.java b/src/org/apache/http/NoHttpResponseException.java
new file mode 100644
index 0000000..a02ef5a
--- /dev/null
+++ b/src/org/apache/http/NoHttpResponseException.java
@@ -0,0 +1,58 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/NoHttpResponseException.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.IOException;
+
+/**
+ * <p>
+ * Signals that the target server failed to respond with a valid HTTP response.
+ * </p>
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Revision: 618017 $
+ */
+public class NoHttpResponseException extends IOException {
+
+    private static final long serialVersionUID = -7658940387386078766L;
+    
+    /**
+     * Creates a new NoHttpResponseException with the specified detail message.
+     *
+     * @param message exception message
+     */
+    public NoHttpResponseException(String message) {
+        super(message);
+    }
+
+}
diff --git a/src/org/apache/http/ParseException.java b/src/org/apache/http/ParseException.java
new file mode 100644
index 0000000..97083b2
--- /dev/null
+++ b/src/org/apache/http/ParseException.java
@@ -0,0 +1,65 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/ParseException.java $
+ * $Revision: 609106 $
+ * $Date: 2008-01-05 01:15:42 -0800 (Sat, 05 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * Indicates a parse error.
+ * Parse errors when receiving a message will typically trigger
+ * {@link ProtocolException}. Parse errors that do not occur during
+ * protocol execution may be handled differently.
+ * This is an unchecked exceptions, since there are cases where
+ * the data to be parsed has been generated and is therefore
+ * known to be parseable.
+ * 
+ * @since 4.0
+ */
+public class ParseException extends RuntimeException {
+
+    private static final long serialVersionUID = -7288819855864183578L;
+
+    /**
+     * Creates a {@link ParseException} without details.
+     */
+    public ParseException() {
+        super();
+    }
+
+    /**
+     * Creates a {@link ParseException} with a detail message.
+     * 
+     * @param message the exception detail message, or <code>null</code>
+     */
+    public ParseException(String message) {
+        super(message);
+    }
+
+}
diff --git a/src/org/apache/http/ProtocolException.java b/src/org/apache/http/ProtocolException.java
new file mode 100644
index 0000000..b4c34ee
--- /dev/null
+++ b/src/org/apache/http/ProtocolException.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/ProtocolException.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * Signals that an HTTP protocol violation has occurred.
+ * For example a malformed status line or headers, a missing message body, etc.
+ * 
+ * @author <a href="mailto:laura@lwerner.org">Laura Werner</a>
+ * 
+ * @since 4.0
+ */
+public class ProtocolException extends HttpException {
+
+    private static final long serialVersionUID = -2143571074341228994L;
+    
+    /**
+     * Creates a new ProtocolException with a <tt>null</tt> detail message. 
+     */
+    public ProtocolException() {
+        super();
+    }
+
+    /**
+     * Creates a new ProtocolException with the specified detail message.
+     * 
+     * @param message The exception detail message
+     */
+    public ProtocolException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new ProtocolException with the specified detail message and cause.
+     * 
+     * @param message the exception detail message
+     * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+     * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+     */
+    public ProtocolException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/org/apache/http/ProtocolVersion.java b/src/org/apache/http/ProtocolVersion.java
new file mode 100644
index 0000000..ced76a5
--- /dev/null
+++ b/src/org/apache/http/ProtocolVersion.java
@@ -0,0 +1,287 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/ProtocolVersion.java $
+ * $Revision: 609106 $
+ * $Date: 2008-01-05 01:15:42 -0800 (Sat, 05 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.Serializable;
+import org.apache.http.util.CharArrayBuffer;
+
+
+/**
+ * Represents a protocol version, as specified in RFC 2616.
+ * RFC 2616 specifies only HTTP versions, like "HTTP/1.1" and "HTTP/1.0".
+ * RFC 3261 specifies a message format that is identical to HTTP except
+ * for the protocol name. It defines a protocol version "SIP/2.0".
+ * There are some nitty-gritty differences between the interpretation
+ * of versions in HTTP and SIP. In those cases, HTTP takes precedence.
+ * <p>
+ * This class defines a protocol version as a combination of
+ * protocol name, major version number, and minor version number.
+ * Note that {@link #equals} and {@link #hashCode} are defined as
+ * final here, they cannot be overridden in derived classes.
+ * 
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * 
+ * @version $Revision: 609106 $
+ */
+public class ProtocolVersion implements Serializable, Cloneable {
+
+    private static final long serialVersionUID = 8950662842175091068L;
+
+
+    /** Name of the protocol. */
+    protected final String protocol;
+
+    /** Major version number of the protocol */
+    protected final int major;
+
+    /** Minor version number of the protocol */
+    protected final int minor;
+
+    
+    /**
+     * Create a protocol version designator.
+     *
+     * @param protocol   the name of the protocol, for example "HTTP"
+     * @param major      the major version number of the protocol
+     * @param minor      the minor version number of the protocol
+     */
+    public ProtocolVersion(String protocol, int major, int minor) {
+        if (protocol == null) {
+            throw new IllegalArgumentException
+                ("Protocol name must not be null.");
+        }
+        if (major < 0) {
+            throw new IllegalArgumentException
+                ("Protocol major version number must not be negative.");
+        }
+        if (minor < 0) {
+            throw new IllegalArgumentException
+                ("Protocol minor version number may not be negative");
+        }
+        this.protocol = protocol;
+        this.major = major;
+        this.minor = minor;
+    }
+
+    /**
+     * Returns the name of the protocol.
+     * 
+     * @return the protocol name
+     */
+    public final String getProtocol() {
+        return protocol;
+    }
+
+    /**
+     * Returns the major version number of the protocol.
+     * 
+     * @return the major version number.
+     */
+    public final int getMajor() {
+        return major;
+    }
+
+    /**
+     * Returns the minor version number of the HTTP protocol.
+     * 
+     * @return the minor version number.
+     */
+    public final int getMinor() {
+        return minor;
+    }
+
+
+    /**
+     * Obtains a specific version of this protocol.
+     * This can be used by derived classes to instantiate themselves instead
+     * of the base class, and to define constants for commonly used versions.
+     * <br/>
+     * The default implementation in this class returns <code>this</code>
+     * if the version matches, and creates a new {@link ProtocolVersion}
+     * otherwise.
+     *
+     * @param major     the major version
+     * @param minor     the minor version
+     *
+     * @return  a protocol version with the same protocol name
+     *          and the argument version
+     */
+    public ProtocolVersion forVersion(int major, int minor) {
+
+        if ((major == this.major) && (minor == this.minor)) {
+            return this;
+        }
+
+        // argument checking is done in the constructor
+        return new ProtocolVersion(this.protocol, major, minor);
+    }
+
+
+    /**
+     * Obtains a hash code consistent with {@link #equals}.
+     *
+     * @return  the hashcode of this protocol version
+     */
+    public final int hashCode() {
+        return this.protocol.hashCode() ^ (this.major * 100000) ^ this.minor;
+    }
+
+        
+    /**
+     * Checks equality of this protocol version with an object.
+     * The object is equal if it is a protocl version with the same
+     * protocol name, major version number, and minor version number.
+     * The specific class of the object is <i>not</i> relevant,
+     * instances of derived classes with identical attributes are
+     * equal to instances of the base class and vice versa.
+     *
+     * @param obj       the object to compare with
+     *
+     * @return  <code>true</code> if the argument is the same protocol version,
+     *          <code>false</code> otherwise
+     */
+    public final boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof ProtocolVersion)) {
+            return false;
+        }
+        ProtocolVersion that = (ProtocolVersion) obj;
+
+        return ((this.protocol.equals(that.protocol)) &&
+                (this.major == that.major) &&
+                (this.minor == that.minor));
+    }
+
+
+    /**
+     * Checks whether this protocol can be compared to another one.
+     * Only protocol versions with the same protocol name can be
+     * {@link #compareToVersion compared}.
+     *
+     * @param that      the protocol version to consider
+     *
+     * @return  <code>true</code> if {@link #compareToVersion compareToVersion}
+     *          can be called with the argument, <code>false</code> otherwise
+     */
+    public boolean isComparable(ProtocolVersion that) {
+        return (that != null) && this.protocol.equals(that.protocol);
+    }
+
+
+    /**
+     * Compares this protocol version with another one.
+     * Only protocol versions with the same protocol name can be compared.
+     * This method does <i>not</i> define a total ordering, as it would be
+     * required for {@link java.lang.Comparable}.
+     *
+     * @param that      the protocl version to compare with
+     *  
+     * @return   a negative integer, zero, or a positive integer
+     *           as this version is less than, equal to, or greater than
+     *           the argument version.
+     *
+     * @throws IllegalArgumentException
+     *         if the argument has a different protocol name than this object,
+     *         or if the argument is <code>null</code>
+     */
+    public int compareToVersion(ProtocolVersion that) {
+        if (that == null) {
+            throw new IllegalArgumentException
+                ("Protocol version must not be null."); 
+        }
+        if (!this.protocol.equals(that.protocol)) {
+            throw new IllegalArgumentException
+                ("Versions for different protocols cannot be compared. " +
+                 this + " " + that);
+        }
+
+        int delta = getMajor() - that.getMajor();
+        if (delta == 0) {
+            delta = getMinor() - that.getMinor();
+        }
+        return delta;
+    }
+
+
+    /**
+     * Tests if this protocol version is greater or equal to the given one.
+     *
+     * @param version   the version against which to check this version
+     *
+     * @return  <code>true</code> if this protocol version is
+     *          {@link #isComparable comparable} to the argument
+     *          and {@link #compareToVersion compares} as greater or equal,
+     *          <code>false</code> otherwise
+     */
+    public final boolean greaterEquals(ProtocolVersion version) {
+        return isComparable(version) && (compareToVersion(version) >= 0);
+    }
+
+
+    /**
+     * Tests if this protocol version is less or equal to the given one.
+     *
+     * @param version   the version against which to check this version
+     *
+     * @return  <code>true</code> if this protocol version is
+     *          {@link #isComparable comparable} to the argument
+     *          and {@link #compareToVersion compares} as less or equal,
+     *          <code>false</code> otherwise
+     */
+    public final boolean lessEquals(ProtocolVersion version) {
+        return isComparable(version) && (compareToVersion(version) <= 0);
+    }
+
+
+    /**
+     * Converts this protocol version to a string.
+     *
+     * @return  a protocol version string, like "HTTP/1.1"
+     */
+    public String toString() {
+        CharArrayBuffer buffer = new CharArrayBuffer(16);
+        buffer.append(this.protocol); 
+        buffer.append('/'); 
+        buffer.append(Integer.toString(this.major)); 
+        buffer.append('.'); 
+        buffer.append(Integer.toString(this.minor)); 
+        return buffer.toString();
+    }
+
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+    
+}
diff --git a/src/org/apache/http/ReasonPhraseCatalog.java b/src/org/apache/http/ReasonPhraseCatalog.java
new file mode 100644
index 0000000..12ad6d9
--- /dev/null
+++ b/src/org/apache/http/ReasonPhraseCatalog.java
@@ -0,0 +1,63 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/ReasonPhraseCatalog.java $
+ * $Revision: 505744 $
+ * $Date: 2007-02-10 10:58:45 -0800 (Sat, 10 Feb 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.util.Locale;
+
+
+/**
+ * Interface for obtaining reason phrases for HTTP status codes.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 505744 $
+ * 
+ * @since 4.0
+ */
+public interface ReasonPhraseCatalog {
+
+    /**
+     * Obtains the reason phrase for a status code.
+     * The optional context allows for catalogs that detect
+     * the language for the reason phrase.
+     *
+     * @param status    the status code, in the range 100-599
+     * @param loc       the preferred locale for the reason phrase
+     *
+     * @return  the reason phrase, or <code>null</code> if unknown
+     */
+    public String getReason(int status, Locale loc)
+        ;
+
+}
diff --git a/src/org/apache/http/RequestLine.java b/src/org/apache/http/RequestLine.java
new file mode 100644
index 0000000..e865929
--- /dev/null
+++ b/src/org/apache/http/RequestLine.java
@@ -0,0 +1,53 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/RequestLine.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * The first line of an {@link HttpRequest HttpRequest}.
+ * It contains the method, URI, and HTTP version of the request.
+ * For details, see RFC 2616.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573864 $
+ * 
+ * @since 4.0
+ */
+public interface RequestLine {
+
+    String getMethod();
+
+    ProtocolVersion getProtocolVersion();
+
+    String getUri();
+    
+}
diff --git a/src/org/apache/http/StatusLine.java b/src/org/apache/http/StatusLine.java
new file mode 100644
index 0000000..aa8a3bc
--- /dev/null
+++ b/src/org/apache/http/StatusLine.java
@@ -0,0 +1,55 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/StatusLine.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * Represents a status line as returned from a HTTP server.
+ * See <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>,
+ * section 6.1.
+ * Implementations are expected to be thread safe.
+ *
+ * @see HttpStatus
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @version $Id: StatusLine.java 573864 2007-09-08 15:53:25Z rolandw $
+ * 
+ * @since 4.0
+ */
+public interface StatusLine {
+
+    ProtocolVersion getProtocolVersion();
+
+    int getStatusCode();
+
+    String getReasonPhrase();
+    
+}
diff --git a/src/org/apache/http/TokenIterator.java b/src/org/apache/http/TokenIterator.java
new file mode 100644
index 0000000..bfe3473
--- /dev/null
+++ b/src/org/apache/http/TokenIterator.java
@@ -0,0 +1,67 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/TokenIterator.java $
+ * $Revision: 601000 $
+ * $Date: 2007-12-04 09:03:49 -0800 (Tue, 04 Dec 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+
+import java.util.Iterator;
+
+
+/**
+ * An iterator for {@link String} tokens.
+ * This interface is designed as a complement to
+ * {@link HeaderElementIterator}, in cases where the items
+ * are plain strings rather than full header elements.
+ * 
+ * @version $Revision: 601000 $
+ */
+public interface TokenIterator extends Iterator {
+
+    /**
+     * Indicates whether there is another token in this iteration.
+     *
+     * @return  <code>true</code> if there is another token,
+     *          <code>false</code> otherwise
+     */
+    boolean hasNext()
+        ;
+
+
+    /**
+     * Obtains the next token from this iteration.
+     * This method should only be called while {@link #hasNext hasNext}
+     * is true.
+     *
+     * @return  the next token in this iteration
+     */
+    String nextToken()
+        ;
+}
diff --git a/src/org/apache/http/UnsupportedHttpVersionException.java b/src/org/apache/http/UnsupportedHttpVersionException.java
new file mode 100644
index 0000000..716d7a5
--- /dev/null
+++ b/src/org/apache/http/UnsupportedHttpVersionException.java
@@ -0,0 +1,64 @@
+/*
+ * $HeadURL:https://svn.apache.org/repos/asf/jakarta/httpcomponents/trunk/coyote-httpconnector/src/java/org/apache/http/tcconnector/UnsupportedHttpVersionException.java $
+ * $Revision:379772 $
+ * $Date:2006-02-22 14:52:29 +0100 (Wed, 22 Feb 2006) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import org.apache.http.ProtocolException;
+
+/**
+ * Indicates an unsupported version of the HTTP protocol.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision:379772 $
+ */
+public class UnsupportedHttpVersionException extends ProtocolException {
+
+    private static final long serialVersionUID = -1348448090193107031L;
+
+    
+    /**
+     * Creates an exception without a detail message.
+     */
+    public UnsupportedHttpVersionException() {
+        super();
+    }
+
+    /**
+     * Creates an exception with the specified detail message.
+     * 
+     * @param message The exception detail message 
+     */
+    public UnsupportedHttpVersionException(final String message) {
+        super(message);
+    }
+
+}
diff --git a/src/org/apache/http/auth/AUTH.java b/src/org/apache/http/auth/AUTH.java
new file mode 100644
index 0000000..8ab6dc2
--- /dev/null
+++ b/src/org/apache/http/auth/AUTH.java
@@ -0,0 +1,66 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/AUTH.java $
+ * $Revision: 618365 $
+ * $Date: 2008-02-04 10:20:08 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+/**
+ * Constants and static helpers related to the HTTP authentication.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public final class AUTH {
+
+    /**
+     * The www authenticate challange header.
+     */
+    public static final String WWW_AUTH = "WWW-Authenticate";
+
+    /**
+     * The www authenticate response header.
+     */
+    public static final String WWW_AUTH_RESP = "Authorization";
+
+    /**
+     * The proxy authenticate challange header.
+     */
+    public static final String PROXY_AUTH = "Proxy-Authenticate";
+
+    /**
+     * The proxy authenticate response header.
+     */
+    public static final String PROXY_AUTH_RESP = "Proxy-Authorization";
+    
+    private AUTH() {
+    }
+       
+}
diff --git a/src/org/apache/http/auth/AuthScheme.java b/src/org/apache/http/auth/AuthScheme.java
new file mode 100644
index 0000000..bdaebdb
--- /dev/null
+++ b/src/org/apache/http/auth/AuthScheme.java
@@ -0,0 +1,140 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/AuthScheme.java $
+ * $Revision: 537144 $
+ * $Date: 2007-05-11 02:30:13 -0700 (Fri, 11 May 2007) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import org.apache.http.Header;
+import org.apache.http.HttpRequest;
+
+/**
+ * <p>
+ * This interface represents an abstract challenge-response oriented 
+ * authentication scheme.
+ * </p>
+ * <p>
+ * An authentication scheme should be able to support the following
+ * functions:
+ * <ul>
+ *   <li>Parse and process the challenge sent by the targer server
+ *       in response to request for a protected resource
+ *   <li>Provide its textual designation
+ *   <li>Provide its parameters, if available
+ *   <li>Provide the realm this authentication scheme is applicable to,
+ *       if available
+ *   <li>Generate authorization string for the given set of credentials,
+ *       request method and URI as specificed in the HTTP request line
+ *       in response to the actual authorization challenge
+ * </ul>
+ * </p>
+ * <p>
+ * Authentication schemes may ignore method name and URI parameters
+ * if they are not relevant for the given authentication mechanism
+ * </p>
+ * <p>
+ * Authentication schemes may be stateful involving a series of 
+ * challenge-response exchanges
+ * </p>
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
+ *
+ * @since 4.0
+ */
+
+public interface AuthScheme {
+
+    /**
+     * Processes the given challenge token. Some authentication schemes
+     * may involve multiple challenge-response exchanges. Such schemes must be able 
+     * to maintain the state information when dealing with sequential challenges 
+     * 
+     * @param header the challenge header
+     */
+    void processChallenge(final Header header) throws MalformedChallengeException;
+    
+    /**
+     * Returns textual designation of the given authentication scheme.
+     * 
+     * @return the name of the given authentication scheme
+     */
+    String getSchemeName();
+
+    /**
+     * Returns authentication parameter with the given name, if available.
+     * 
+     * @param name The name of the parameter to be returned
+     * 
+     * @return the parameter with the given name
+     */
+    String getParameter(final String name);
+
+    /**
+     * Returns authentication realm. If the concept of an authentication
+     * realm is not applicable to the given authentication scheme, returns
+     * <code>null</code>.
+     * 
+     * @return the authentication realm
+     */
+    String getRealm();
+    
+    /**
+     * Tests if the authentication scheme is provides authorization on a per
+     * connection basis instead of usual per request basis
+     * 
+     * @return <tt>true</tt> if the scheme is connection based, <tt>false</tt>
+     * if the scheme is request based.
+     */
+    boolean isConnectionBased();    
+    
+    /**
+     * Authentication process may involve a series of challenge-response exchanges.
+     * This method tests if the authorization process has been completed, either
+     * successfully or unsuccessfully, that is, all the required authorization 
+     * challenges have been processed in their entirety.
+     * 
+     * @return <tt>true</tt> if the authentication process has been completed, 
+     * <tt>false</tt> otherwise.
+     */
+    boolean isComplete();    
+
+    /**
+     * Produces an authorization string for the given set of {@link Credentials}.
+     * 
+     * @param credentials The set of credentials to be used for athentication
+     * @param request The request being authenticated
+     * @throws AuthenticationException if authorization string cannot 
+     *   be generated due to an authentication failure
+     * 
+     * @return the authorization string
+     */
+    Header authenticate(Credentials credentials, HttpRequest request) 
+            throws AuthenticationException;
+    
+}
diff --git a/src/org/apache/http/auth/AuthSchemeFactory.java b/src/org/apache/http/auth/AuthSchemeFactory.java
new file mode 100644
index 0000000..8f985b0
--- /dev/null
+++ b/src/org/apache/http/auth/AuthSchemeFactory.java
@@ -0,0 +1,46 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/AuthSchemeFactory.java $
+ * $Revision: 527900 $
+ * $Date: 2007-04-12 05:35:25 -0700 (Thu, 12 Apr 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public interface AuthSchemeFactory {    
+
+    AuthScheme newInstance(HttpParams params);
+
+}
diff --git a/src/org/apache/http/auth/AuthSchemeRegistry.java b/src/org/apache/http/auth/AuthSchemeRegistry.java
new file mode 100644
index 0000000..62a5d4d
--- /dev/null
+++ b/src/org/apache/http/auth/AuthSchemeRegistry.java
@@ -0,0 +1,149 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/AuthSchemeRegistry.java $
+ * $Revision: 652950 $
+ * $Date: 2008-05-02 16:49:48 -0700 (Fri, 02 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ * Authentication scheme registry that can be used to obtain the corresponding
+ * authentication scheme implementation for a given type of authorization challenge. 
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * @version $Revision: 652950 $
+ * @since 4.0
+ */
+public final class AuthSchemeRegistry {
+
+    private final Map<String,AuthSchemeFactory> registeredSchemes;
+    
+    public AuthSchemeRegistry() {
+        super();
+        this.registeredSchemes = new LinkedHashMap<String,AuthSchemeFactory>();
+    }
+    
+    /**
+     * Registers a {@link AuthSchemeFactory} with  the given identifier. If a factory with the 
+     * given name already exists it will be overridden. This name is the same one used to 
+     * retrieve the {@link AuthScheme authentication scheme} from {@link #getAuthScheme}.
+     * 
+     * <p>
+     * Please note that custom authentication preferences, if used, need to be updated accordingly 
+     * for the new {@link AuthScheme authentication scheme} to take effect.
+     * </p>    
+     * 
+     * @param name the identifier for this scheme
+     * @param factory the {@link AuthSchemeFactory} class to register
+     * 
+     * @see #getAuthScheme
+     */
+    public synchronized void register(
+            final String name, 
+            final AuthSchemeFactory factory) {
+         if (name == null) {
+             throw new IllegalArgumentException("Name may not be null");
+         }
+        if (factory == null) {
+            throw new IllegalArgumentException("Authentication scheme factory may not be null");
+        }
+        registeredSchemes.put(name.toLowerCase(Locale.ENGLISH), factory);
+    }
+
+    /**
+     * Unregisters the class implementing an {@link AuthScheme authentication scheme} with 
+     * the given name.
+     * 
+     * @param name the identifier of the class to unregister
+     */
+    public synchronized void unregister(final String name) {
+         if (name == null) {
+             throw new IllegalArgumentException("Name may not be null");
+         }
+        registeredSchemes.remove(name.toLowerCase(Locale.ENGLISH));
+    }
+
+    /**
+     * Gets the {@link AuthScheme authentication scheme} with the given name.
+     * 
+     * @param name the {@link AuthScheme authentication scheme} identifier
+     * @param params the {@link HttpParams HTTP parameters} for the authentication
+     *  scheme. 
+     * 
+     * @return {@link AuthScheme authentication scheme}
+     * 
+     * @throws IllegalStateException if a scheme with the given name cannot be found
+     */
+    public synchronized AuthScheme getAuthScheme(final String name, final HttpParams params) 
+        throws IllegalStateException {
+
+        if (name == null) {
+            throw new IllegalArgumentException("Name may not be null");
+        }
+        AuthSchemeFactory factory = registeredSchemes.get(name.toLowerCase(Locale.ENGLISH));
+        if (factory != null) {
+            return factory.newInstance(params);
+        } else {
+            throw new IllegalStateException("Unsupported authentication scheme: " + name);
+        }
+    } 
+
+    /**
+     * Obtains a list containing names of all registered {@link AuthScheme authentication 
+     * schemes} in their default order.
+     * 
+     * @return list of registered scheme names
+     */
+    public synchronized List<String> getSchemeNames() {
+        return new ArrayList<String>(registeredSchemes.keySet()); 
+    } 
+ 
+    /**
+     * Populates the internal collection of registered {@link AuthScheme authentication schemes} 
+     * with the content of the map passed as a parameter.
+     * 
+     * @param map authentication schemes
+     */
+    public synchronized void setItems(final Map<String, AuthSchemeFactory> map) {
+        if (map == null) {
+            return;
+        }
+        registeredSchemes.clear();
+        registeredSchemes.putAll(map);
+    }
+    
+}
diff --git a/src/org/apache/http/auth/AuthScope.java b/src/org/apache/http/auth/AuthScope.java
new file mode 100644
index 0000000..c9d7f56
--- /dev/null
+++ b/src/org/apache/http/auth/AuthScope.java
@@ -0,0 +1,292 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/AuthScope.java $
+ * $Revision: 652950 $
+ * $Date: 2008-05-02 16:49:48 -0700 (Fri, 02 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import java.util.Locale;
+
+import org.apache.http.util.LangUtils;
+
+/** 
+ * The class represents an authentication scope consisting of a host name,
+ * a port number, a realm name and an authentication scheme name which 
+ * {@link Credentials Credentials} apply to.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:adrian@intencha.com">Adrian Sutton</a>
+ * 
+ * @since 4.0
+ */
+public class AuthScope {
+    
+    /** 
+     * The <tt>null</tt> value represents any host. In the future versions of 
+     * HttpClient the use of this parameter will be discontinued.  
+     */
+    public static final String ANY_HOST = null;
+
+    /** 
+     * The <tt>-1</tt> value represents any port.  
+     */
+    public static final int ANY_PORT = -1;
+
+    /** 
+     * The <tt>null</tt> value represents any realm.  
+     */
+    public static final String ANY_REALM = null;
+
+    /** 
+     * The <tt>null</tt> value represents any authentication scheme.  
+     */
+    public static final String ANY_SCHEME = null;
+    
+    /** 
+     * Default scope matching any host, port, realm and authentication scheme. 
+     * In the future versions of HttpClient the use of this parameter will be 
+     * discontinued.  
+     */
+    public static final AuthScope ANY = new AuthScope(ANY_HOST, ANY_PORT, ANY_REALM, ANY_SCHEME);
+
+    /** The authentication scheme the credentials apply to. */
+    private final String scheme;
+    
+    /** The realm the credentials apply to. */
+    private final String realm;
+    
+    /** The host the credentials apply to. */
+    private final String host;
+        
+    /** The port the credentials apply to. */
+    private final int port;
+        
+    /** Creates a new credentials scope for the given 
+     * <tt>host</tt>, <tt>port</tt>, <tt>realm</tt>, and 
+     * <tt>authentication scheme</tt>.
+     * 
+     * @param host the host the credentials apply to. May be set
+     *   to <tt>null</tt> if credenticals are applicable to
+     *   any host. 
+     * @param port the port the credentials apply to. May be set
+     *   to negative value if credenticals are applicable to
+     *   any port. 
+     * @param realm the realm the credentials apply to. May be set 
+     *   to <tt>null</tt> if credenticals are applicable to
+     *   any realm. 
+     * @param scheme the authentication scheme the credentials apply to. 
+     *   May be set to <tt>null</tt> if credenticals are applicable to
+     *   any authentication scheme. 
+     */
+    public AuthScope(final String host, int port, 
+        final String realm, final String scheme)
+    {
+        this.host =   (host == null)   ? ANY_HOST: host.toLowerCase(Locale.ENGLISH);
+        this.port =   (port < 0)       ? ANY_PORT: port;
+        this.realm =  (realm == null)  ? ANY_REALM: realm;
+        this.scheme = (scheme == null) ? ANY_SCHEME: scheme.toUpperCase(Locale.ENGLISH);
+    }
+    
+    /** Creates a new credentials scope for the given 
+     * <tt>host</tt>, <tt>port</tt>, <tt>realm</tt>, and any
+     * authentication scheme.
+     * 
+     * @param host the host the credentials apply to. May be set
+     *   to <tt>null</tt> if credenticals are applicable to
+     *   any host. 
+     * @param port the port the credentials apply to. May be set
+     *   to negative value if credenticals are applicable to
+     *   any port. 
+     * @param realm the realm the credentials apply to. May be set 
+     *   to <tt>null</tt> if credenticals are applicable to
+     *   any realm. 
+     */
+    public AuthScope(final String host, int port, final String realm) {
+        this(host, port, realm, ANY_SCHEME);
+    }
+    
+    /** Creates a new credentials scope for the given 
+     * <tt>host</tt>, <tt>port</tt>, any realm name, and any
+     * authentication scheme.
+     * 
+     * @param host the host the credentials apply to. May be set
+     *   to <tt>null</tt> if credenticals are applicable to
+     *   any host. 
+     * @param port the port the credentials apply to. May be set
+     *   to negative value if credenticals are applicable to
+     *   any port. 
+     */
+    public AuthScope(final String host, int port) {
+        this(host, port, ANY_REALM, ANY_SCHEME);
+    }
+    
+    /** 
+     * Creates a copy of the given credentials scope.
+     */
+    public AuthScope(final AuthScope authscope) {
+        super();
+        if (authscope == null) {
+            throw new IllegalArgumentException("Scope may not be null");
+        }
+        this.host = authscope.getHost();
+        this.port = authscope.getPort();
+        this.realm = authscope.getRealm();
+        this.scheme = authscope.getScheme();
+    }
+    
+    /**
+     * @return the host
+     */
+    public String getHost() {
+        return this.host;
+    }
+
+    /**
+     * @return the port
+     */
+    public int getPort() {
+        return this.port;
+    }
+
+    /**
+     * @return the realm name
+     */
+    public String getRealm() {
+        return this.realm;
+    }
+
+    /**
+     * @return the scheme type
+     */
+    public String getScheme() {
+        return this.scheme;
+    }
+
+    /**
+     * Tests if the authentication scopes match. 
+     * 
+     * @return the match factor. Negative value signifies no match. 
+     *    Non-negative signifies a match. The greater the returned value 
+     *    the closer the match.
+     */
+    public int match(final AuthScope that) {
+        int factor = 0;
+        if (LangUtils.equals(this.scheme, that.scheme)) {
+            factor += 1;
+        } else {
+            if (this.scheme != ANY_SCHEME && that.scheme != ANY_SCHEME) {
+                return -1;
+            }
+        }
+        if (LangUtils.equals(this.realm, that.realm)) {
+            factor += 2;
+        } else {
+            if (this.realm != ANY_REALM && that.realm != ANY_REALM) {
+                return -1;
+            }
+        }
+        if (this.port == that.port) {
+            factor += 4;
+        } else {
+            if (this.port != ANY_PORT && that.port != ANY_PORT) {
+                return -1;
+            }
+        }
+        if (LangUtils.equals(this.host, that.host)) {
+            factor += 8;
+        } else {
+            if (this.host != ANY_HOST && that.host != ANY_HOST) {
+                return -1;
+            }
+        }
+        return factor;
+    }
+
+    /**
+     * @see java.lang.Object#equals(Object)
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o == null) {
+            return false;
+        }
+        if (o == this) {
+            return true;
+        }
+        if (!(o instanceof AuthScope)) {
+            return super.equals(o);
+        }
+        AuthScope that = (AuthScope) o;
+        return 
+        LangUtils.equals(this.host, that.host) 
+          && this.port == that.port
+          && LangUtils.equals(this.realm, that.realm)
+          && LangUtils.equals(this.scheme, that.scheme);
+    }
+
+    /**
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        if (this.scheme != null) {
+            buffer.append(this.scheme.toUpperCase(Locale.ENGLISH));
+            buffer.append(' ');
+        }
+        if (this.realm != null) {
+            buffer.append('\'');
+            buffer.append(this.realm);
+            buffer.append('\'');
+        } else {
+            buffer.append("<any realm>");
+        }
+        if (this.host != null) {
+            buffer.append('@');
+            buffer.append(this.host);
+            if (this.port >= 0) {
+                buffer.append(':');
+                buffer.append(this.port);
+            }
+        }
+        return buffer.toString();
+    }
+    
+    /**
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int hash = LangUtils.HASH_SEED;
+        hash = LangUtils.hashCode(hash, this.host);
+        hash = LangUtils.hashCode(hash, this.port);
+        hash = LangUtils.hashCode(hash, this.realm);
+        hash = LangUtils.hashCode(hash, this.scheme);
+        return hash;
+    }
+}
diff --git a/src/org/apache/http/auth/AuthState.java b/src/org/apache/http/auth/AuthState.java
new file mode 100644
index 0000000..f55bf86
--- /dev/null
+++ b/src/org/apache/http/auth/AuthState.java
@@ -0,0 +1,147 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/AuthState.java $
+ * $Revision: 659971 $
+ * $Date: 2008-05-25 05:01:22 -0700 (Sun, 25 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+
+/**
+ * This class provides detailed information about the state of the
+ * authentication process.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class AuthState {
+
+    /** Actual authentication scheme */
+    private AuthScheme authScheme;
+
+    /** Actual authentication scope */
+    private AuthScope authScope;
+    
+    /** Credentials selected for authentication */
+    private Credentials credentials;
+    
+    /**
+     * Default constructor.
+     * 
+     */
+    public AuthState() {
+        super();
+    }
+
+    /**
+     * Invalidates the authentication state by resetting its parameters.
+     */
+    public void invalidate() {
+        this.authScheme = null;
+        this.authScope = null;
+        this.credentials = null;
+    }
+
+    public boolean isValid() {
+        return this.authScheme != null;
+    }
+    
+    /**
+     * Assigns the given {@link AuthScheme authentication scheme}.
+     * 
+     * @param authScheme the {@link AuthScheme authentication scheme}
+     */
+    public void setAuthScheme(final AuthScheme authScheme) {
+        if (authScheme == null) {
+            invalidate();
+            return;
+        }
+        this.authScheme = authScheme;
+    }
+
+    /**
+     * Returns the {@link AuthScheme authentication scheme}.
+     * 
+     * @return {@link AuthScheme authentication scheme}
+     */
+    public AuthScheme getAuthScheme() {
+        return this.authScheme;
+    }
+    
+    
+    /** 
+     * Returns user {@link Credentials} selected for authentication if available
+     * 
+     * @return user credentials if available, <code>null</code otherwise
+     */
+    public Credentials getCredentials() {
+        return this.credentials;
+    }
+
+    
+    /** 
+     * Sets user {@link Credentials} to be used for authentication
+     * 
+     * @param credentials User credentials
+     */
+    public void setCredentials(final Credentials credentials) {
+        this.credentials = credentials;
+    }
+
+
+    /** 
+     * Returns actual {@link AuthScope} if available
+     * 
+     * @return actual authentication scope if available, <code>null</code otherwise
+     */
+     public AuthScope getAuthScope() {
+        return this.authScope;
+     }
+
+     /** 
+      * Sets actual {@link AuthScope}.
+      * 
+      * @param authScope Authentication scope
+      */
+     public void setAuthScope(final AuthScope authScope) {
+        this.authScope = authScope;
+     }
+
+     
+    @Override
+    public String toString() {
+        StringBuilder buffer = new StringBuilder();
+        buffer.append("auth scope [");
+        buffer.append(this.authScope);
+        buffer.append("]; credentials set [");
+        buffer.append(this.credentials != null ? "true" : "false");
+        buffer.append("]");
+        return buffer.toString();
+    }
+    
+}
diff --git a/src/org/apache/http/auth/AuthenticationException.java b/src/org/apache/http/auth/AuthenticationException.java
new file mode 100644
index 0000000..8b307be
--- /dev/null
+++ b/src/org/apache/http/auth/AuthenticationException.java
@@ -0,0 +1,73 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/AuthenticationException.java $
+ * $Revision: 505684 $
+ * $Date: 2007-02-10 04:40:02 -0800 (Sat, 10 Feb 2007) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import org.apache.http.ProtocolException;
+
+/**
+ * Signals a failure in authentication process
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public class AuthenticationException extends ProtocolException {
+
+    private static final long serialVersionUID = -6794031905674764776L;
+
+    /**
+     * Creates a new AuthenticationException with a <tt>null</tt> detail message. 
+     */
+    public AuthenticationException() {
+        super();
+    }
+
+    /**
+     * Creates a new AuthenticationException with the specified message.
+     * 
+     * @param message the exception detail message
+     */
+    public AuthenticationException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new AuthenticationException with the specified detail message and cause.
+     * 
+     * @param message the exception detail message
+     * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+     * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+     */
+    public AuthenticationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git a/src/org/apache/http/auth/BasicUserPrincipal.java b/src/org/apache/http/auth/BasicUserPrincipal.java
new file mode 100644
index 0000000..2485011
--- /dev/null
+++ b/src/org/apache/http/auth/BasicUserPrincipal.java
@@ -0,0 +1,90 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/BasicUserPrincipal.java $
+ * $Revision: 658430 $
+ * $Date: 2008-05-20 14:04:27 -0700 (Tue, 20 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import java.security.Principal;
+
+import org.apache.http.util.LangUtils;
+
+/**
+ * Basic user principal used for HTTP authentication
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public final class BasicUserPrincipal implements Principal {
+
+    private final String username;
+    
+    public BasicUserPrincipal(final String username) {
+        super();
+        if (username == null) {
+            throw new IllegalArgumentException("User name may not be null");
+        }
+        this.username = username;
+    }
+    
+    public String getName() {
+        return this.username;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = LangUtils.HASH_SEED;
+        hash = LangUtils.hashCode(hash, this.username);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null) return false;
+        if (this == o) return true;
+        if (o instanceof BasicUserPrincipal) {
+            BasicUserPrincipal that = (BasicUserPrincipal) o;
+            if (LangUtils.equals(this.username, that.username)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder buffer = new StringBuilder();
+        buffer.append("[principal: ");
+        buffer.append(this.username);
+        buffer.append("]");
+        return buffer.toString();
+    }
+
+}
+
diff --git a/src/org/apache/http/auth/Credentials.java b/src/org/apache/http/auth/Credentials.java
new file mode 100644
index 0000000..846a23b
--- /dev/null
+++ b/src/org/apache/http/auth/Credentials.java
@@ -0,0 +1,49 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/Credentials.java $
+ * $Revision: 658430 $
+ * $Date: 2008-05-20 14:04:27 -0700 (Tue, 20 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import java.security.Principal;
+
+/**
+ * User name and password based authentication credentials.
+ * 
+ * @author Unascribed
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * 
+ * @version $Revision: 658430 $ $Date: 2008-05-20 14:04:27 -0700 (Tue, 20 May 2008) $
+ */
+public interface Credentials {
+
+    Principal getUserPrincipal();
+
+    String getPassword();
+    
+}
diff --git a/src/org/apache/http/auth/InvalidCredentialsException.java b/src/org/apache/http/auth/InvalidCredentialsException.java
new file mode 100644
index 0000000..50155ec
--- /dev/null
+++ b/src/org/apache/http/auth/InvalidCredentialsException.java
@@ -0,0 +1,71 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/InvalidCredentialsException.java $
+ * $Revision: 505684 $
+ * $Date: 2007-02-10 04:40:02 -0800 (Sat, 10 Feb 2007) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+/**
+ * Authentication credentials required to respond to a authentication 
+ * challenge are invalid
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public class InvalidCredentialsException extends AuthenticationException {
+
+    private static final long serialVersionUID = -4834003835215460648L;
+
+    /**
+     * Creates a new InvalidCredentialsException with a <tt>null</tt> detail message. 
+     */
+    public InvalidCredentialsException() {
+        super();
+    }
+
+    /**
+     * Creates a new InvalidCredentialsException with the specified message.
+     * 
+     * @param message the exception detail message
+     */
+    public InvalidCredentialsException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new InvalidCredentialsException with the specified detail message and cause.
+     * 
+     * @param message the exception detail message
+     * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+     * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+     */
+    public InvalidCredentialsException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/org/apache/http/auth/MalformedChallengeException.java b/src/org/apache/http/auth/MalformedChallengeException.java
new file mode 100644
index 0000000..8c7e373
--- /dev/null
+++ b/src/org/apache/http/auth/MalformedChallengeException.java
@@ -0,0 +1,73 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/MalformedChallengeException.java $
+ * $Revision: 505684 $
+ * $Date: 2007-02-10 04:40:02 -0800 (Sat, 10 Feb 2007) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import org.apache.http.ProtocolException;
+
+/**
+ * Signals that authentication challenge is in some way invalid or 
+ * illegal in the given context
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public class MalformedChallengeException extends ProtocolException {
+
+    private static final long serialVersionUID = 814586927989932284L;
+
+    /**
+     * Creates a new MalformedChallengeException with a <tt>null</tt> detail message. 
+     */
+    public MalformedChallengeException() {
+        super();
+    }
+
+    /**
+     * Creates a new MalformedChallengeException with the specified message.
+     * 
+     * @param message the exception detail message
+     */
+    public MalformedChallengeException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new MalformedChallengeException with the specified detail message and cause.
+     * 
+     * @param message the exception detail message
+     * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+     * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+     */
+    public MalformedChallengeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/org/apache/http/auth/NTCredentials.java b/src/org/apache/http/auth/NTCredentials.java
new file mode 100644
index 0000000..6800c42
--- /dev/null
+++ b/src/org/apache/http/auth/NTCredentials.java
@@ -0,0 +1,180 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/NTCredentials.java $
+ * $Revision: 658430 $
+ * $Date: 2008-05-20 14:04:27 -0700 (Tue, 20 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import java.security.Principal;
+import java.util.Locale;
+
+import org.apache.http.util.LangUtils;
+
+/** {@link Credentials} specific to the Windows platform.
+ *
+ * @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 at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 2.0
+ */
+public class NTCredentials implements Credentials {
+
+    /** The user principal  */
+    private final NTUserPrincipal principal;
+
+    /** Password */
+    private final String password;
+    
+    /** The host the authentication request is originating from.  */
+    private final String workstation;
+
+    /**
+     * The constructor with the fully qualified username and password combined 
+     * string argument.
+     *
+     * @param usernamePassword the domain/username:password formed string
+     */
+    public NTCredentials(String usernamePassword) {
+        super();
+        if (usernamePassword == null) {
+            throw new IllegalArgumentException("Username:password string may not be null");            
+        }
+        String username;
+        int atColon = usernamePassword.indexOf(':');
+        if (atColon >= 0) {
+            username = usernamePassword.substring(0, atColon);
+            this.password = usernamePassword.substring(atColon + 1);
+        } else {
+            username = usernamePassword;
+            this.password = null;
+        }
+        int atSlash = username.indexOf('/');
+        if (atSlash >= 0) {
+            this.principal = new NTUserPrincipal(
+                    username.substring(0, atSlash).toUpperCase(Locale.ENGLISH),
+                    username.substring(atSlash + 1));
+        } else {
+            this.principal = new NTUserPrincipal(
+                    null,
+                    username.substring(atSlash + 1));
+        }
+        this.workstation = null;
+    }
+
+    /**
+     * Constructor.
+     * @param userName The user name.  This should not include the domain to authenticate with.
+     * For example: "user" is correct whereas "DOMAIN\\user" is not.
+     * @param password The password.
+     * @param workstation The workstation the authentication request is originating from. 
+     * Essentially, the computer name for this machine.
+     * @param domain The domain to authenticate within.
+     */
+    public NTCredentials(
+            final String userName, 
+            final String password, 
+            final String workstation,
+            final String domain) {
+        super();
+        if (userName == null) {
+            throw new IllegalArgumentException("User name may not be null");
+        }
+        this.principal = new NTUserPrincipal(domain, userName);
+        this.password = password;
+        if (workstation != null) {
+            this.workstation = workstation.toUpperCase(Locale.ENGLISH);
+        } else {
+            this.workstation = null;
+        }
+    }
+
+    public Principal getUserPrincipal() {
+        return this.principal;
+    }
+    
+    public String getUserName() {
+        return this.principal.getUsername();
+    }
+    
+    public String getPassword() {
+        return this.password;
+    }
+
+    /**
+     * Retrieves the name to authenticate with.
+     *
+     * @return String the domain these credentials are intended to authenticate with.
+     */
+    public String getDomain() {
+        return this.principal.getDomain();
+    }
+
+    /**
+     * Retrieves the workstation name of the computer originating the request.
+     *
+     * @return String the workstation the user is logged into.
+     */
+    public String getWorkstation() {
+        return this.workstation;
+    }
+    
+    @Override
+    public int hashCode() {
+        int hash = LangUtils.HASH_SEED;
+        hash = LangUtils.hashCode(hash, this.principal);
+        hash = LangUtils.hashCode(hash, this.workstation);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null) return false;
+        if (this == o) return true;
+        if (o instanceof NTCredentials) {
+            NTCredentials that = (NTCredentials) o;
+            if (LangUtils.equals(this.principal, that.principal)
+                    && LangUtils.equals(this.workstation, that.workstation)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder buffer = new StringBuilder();
+        buffer.append("[principal: ");
+        buffer.append(this.principal);
+        buffer.append("][workstation: ");
+        buffer.append(this.workstation);
+        buffer.append("]");
+        return buffer.toString();
+    }
+
+}
diff --git a/src/org/apache/http/auth/NTUserPrincipal.java b/src/org/apache/http/auth/NTUserPrincipal.java
new file mode 100644
index 0000000..ac91bb8
--- /dev/null
+++ b/src/org/apache/http/auth/NTUserPrincipal.java
@@ -0,0 +1,113 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/NTUserPrincipal.java $
+ * $Revision: 658430 $
+ * $Date: 2008-05-20 14:04:27 -0700 (Tue, 20 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import java.security.Principal;
+import java.util.Locale;
+
+import org.apache.http.util.LangUtils;
+
+/**  NT (MS Windows specific) user principal used for HTTP authentication
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public class NTUserPrincipal implements Principal {
+
+    private final String username;
+    private final String domain;
+    private final String ntname;
+
+    public NTUserPrincipal(
+            final String domain,
+            final String username) {
+        super();
+        if (username == null) {
+            throw new IllegalArgumentException("User name may not be null");
+        }
+        this.username = username;
+        if (domain != null) {
+            this.domain = domain.toUpperCase(Locale.ENGLISH);
+        } else {
+            this.domain = null;
+        }
+        if (this.domain != null && this.domain.length() > 0) {
+            StringBuilder buffer = new StringBuilder();
+            buffer.append(this.domain);
+            buffer.append('/');
+            buffer.append(this.username);
+            this.ntname = buffer.toString();
+        } else {
+            this.ntname = this.username;
+        }
+    }
+
+    public String getName() {
+        return this.ntname;
+    }
+    
+    public String getDomain() {
+        return this.domain;
+    }
+
+    public String getUsername() {
+        return this.username;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = LangUtils.HASH_SEED;
+        hash = LangUtils.hashCode(hash, this.username);
+        hash = LangUtils.hashCode(hash, this.domain);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null) return false;
+        if (this == o) return true;
+        if (o instanceof NTUserPrincipal) {
+            NTUserPrincipal that = (NTUserPrincipal) o;
+            if (LangUtils.equals(this.username, that.username)
+                    && LangUtils.equals(this.domain, that.domain)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return this.ntname;
+    }
+
+}
diff --git a/src/org/apache/http/auth/UsernamePasswordCredentials.java b/src/org/apache/http/auth/UsernamePasswordCredentials.java
new file mode 100644
index 0000000..f82608c
--- /dev/null
+++ b/src/org/apache/http/auth/UsernamePasswordCredentials.java
@@ -0,0 +1,126 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/UsernamePasswordCredentials.java $
+ * $Revision: 658430 $
+ * $Date: 2008-05-20 14:04:27 -0700 (Tue, 20 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import java.security.Principal;
+
+import org.apache.http.util.LangUtils;
+
+/**
+ * Username and password {@link Credentials}
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author Sean C. Sullivan
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Revision: 658430 $ $Date: 2008-05-20 14:04:27 -0700 (Tue, 20 May 2008) $
+ * 
+ */
+public class UsernamePasswordCredentials implements Credentials {
+
+    private final BasicUserPrincipal principal;
+    private final String password;
+     
+    /**
+     * 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.principal = new BasicUserPrincipal(usernamePassword.substring(0, atColon));
+            this.password = usernamePassword.substring(atColon + 1);
+        } else {
+            this.principal = new BasicUserPrincipal(usernamePassword);
+            this.password = null;
+        }
+    }
+
+
+    /**
+     * 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.principal = new BasicUserPrincipal(userName);
+        this.password = password;
+    }
+
+    public Principal getUserPrincipal() {
+        return this.principal;
+    }
+
+    public String getUserName() {
+        return this.principal.getName();
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    @Override
+    public int hashCode() {
+        return this.principal.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null) return false;
+        if (this == o) return true;
+        if (o instanceof UsernamePasswordCredentials) {
+            UsernamePasswordCredentials that = (UsernamePasswordCredentials) o;
+            if (LangUtils.equals(this.principal, that.principal)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return this.principal.toString();
+    }
+
+}
+
diff --git a/src/org/apache/http/auth/package.html b/src/org/apache/http/auth/package.html
new file mode 100644
index 0000000..1493dfb
--- /dev/null
+++ b/src/org/apache/http/auth/package.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+The API for client-side HTTP authentication against a server,
+commonly referred to as <i>HttpAuth</i>.
+
+</body>
+</html>
diff --git a/src/org/apache/http/auth/params/AuthPNames.java b/src/org/apache/http/auth/params/AuthPNames.java
new file mode 100644
index 0000000..a053435
--- /dev/null
+++ b/src/org/apache/http/auth/params/AuthPNames.java
@@ -0,0 +1,56 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/params/AuthPNames.java $
+ * $Revision: 578403 $
+ * $Date: 2007-09-22 03:56:04 -0700 (Sat, 22 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth.params;
+
+/**
+ * Parameter names for HttpAuth.
+ * 
+ * @version $Revision: 578403 $
+ * 
+ * @since 4.0
+ */
+public interface AuthPNames {
+
+    /**
+     * Defines the charset to be used when encoding 
+     * {@link org.apache.http.auth.Credentials}.
+     * <p>
+     * This parameter expects a value of type {@link String}.
+     * If not defined, then
+     * {@link org.apache.http.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET
+     *        HttpProtocolParams.HTTP_ELEMENT_CHARSET}
+     * should be used.
+     * </p>
+     */
+    public static final String CREDENTIAL_CHARSET = "http.auth.credential-charset"; 
+
+}
diff --git a/src/org/apache/http/auth/params/AuthParamBean.java b/src/org/apache/http/auth/params/AuthParamBean.java
new file mode 100644
index 0000000..5b27328
--- /dev/null
+++ b/src/org/apache/http/auth/params/AuthParamBean.java
@@ -0,0 +1,47 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/params/AuthParamBean.java $
+ * $Revision: 632313 $
+ * $Date: 2008-02-29 05:19:50 -0800 (Fri, 29 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth.params;
+
+import org.apache.http.params.HttpAbstractParamBean;
+import org.apache.http.params.HttpParams;
+
+public class AuthParamBean extends HttpAbstractParamBean {
+
+    public AuthParamBean (final HttpParams params) {
+        super(params);
+    }
+
+    public void setCredentialCharset (final String charset) {
+        AuthParams.setCredentialCharset(params, charset);
+    }
+    
+}
diff --git a/src/org/apache/http/auth/params/AuthParams.java b/src/org/apache/http/auth/params/AuthParams.java
new file mode 100644
index 0000000..a724a8c
--- /dev/null
+++ b/src/org/apache/http/auth/params/AuthParams.java
@@ -0,0 +1,94 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/params/AuthParams.java $
+ * $Revision: 618365 $
+ * $Date: 2008-02-04 10:20:08 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth.params;
+
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * This class implements an adaptor around the {@link HttpParams} interface
+ * to simplify manipulation of the HTTP authentication specific parameters.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Revision: 618365 $
+ * 
+ * @since 4.0
+ *
+ * @see AuthPNames
+ */
+public final class AuthParams {
+
+    private AuthParams() {
+        super();
+    }
+
+    /**
+     * Obtains the charset for encoding
+     * {@link org.apache.http.auth.Credentials}.
+     * If not configured,
+     * {@link HTTP#DEFAULT_PROTOCOL_CHARSET HTTP.DEFAULT_PROTOCOL_CHARSET}
+     * is used instead.
+     * 
+     * @return The charset
+     *
+     * @see AuthPNames#CREDENTIAL_CHARSET
+     */
+    public static String getCredentialCharset(final HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        String charset = (String) params.getParameter
+            (AuthPNames.CREDENTIAL_CHARSET);
+        //@@@ TODO: inconsistent with JavaDoc in AuthPNames,
+        //@@@ TODO: check HTTP_ELEMENT_CHARSET first, or fix JavaDocs
+        if (charset == null) {
+            charset = HTTP.DEFAULT_PROTOCOL_CHARSET;
+        }
+        return charset;
+    }
+
+
+    /**
+     * Sets the charset to be used when encoding
+     * {@link org.apache.http.auth.Credentials}.
+     * 
+     * @param charset The charset
+     */
+    public static void setCredentialCharset(final HttpParams params, final String charset) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        params.setParameter(AuthPNames.CREDENTIAL_CHARSET, charset);
+    }
+
+}
diff --git a/src/org/apache/http/auth/params/package.html b/src/org/apache/http/auth/params/package.html
new file mode 100644
index 0000000..f9258fd
--- /dev/null
+++ b/src/org/apache/http/auth/params/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/params/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Parameters for configuring <i>HttpAuth</i>.
+
+</body>
+</html>
diff --git a/src/org/apache/http/client/AuthenticationHandler.java b/src/org/apache/http/client/AuthenticationHandler.java
new file mode 100644
index 0000000..dacc1b8
--- /dev/null
+++ b/src/org/apache/http/client/AuthenticationHandler.java
@@ -0,0 +1,61 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/AuthenticationHandler.java $
+ * $Revision: 603318 $
+ * $Date: 2007-12-11 10:06:50 -0800 (Tue, 11 Dec 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import java.util.Map;
+
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public interface AuthenticationHandler {
+
+    boolean isAuthenticationRequested(
+            HttpResponse response, 
+            HttpContext context);
+    
+    Map<String, Header> getChallenges(
+            HttpResponse response, 
+            HttpContext context) throws MalformedChallengeException;
+    
+    AuthScheme selectScheme(
+            Map<String, Header> challenges, 
+            HttpResponse response, 
+            HttpContext context) throws AuthenticationException;
+    
+}
diff --git a/src/org/apache/http/client/CircularRedirectException.java b/src/org/apache/http/client/CircularRedirectException.java
new file mode 100644
index 0000000..08dca63
--- /dev/null
+++ b/src/org/apache/http/client/CircularRedirectException.java
@@ -0,0 +1,70 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/CircularRedirectException.java $
+ * $Revision: 558123 $
+ * $Date: 2007-07-20 13:29:58 -0700 (Fri, 20 Jul 2007) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+/**
+ * Signals a circular redirect
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public class CircularRedirectException extends RedirectException {
+
+    private static final long serialVersionUID = 6830063487001091803L;
+
+    /**
+     * Creates a new CircularRedirectException with a <tt>null</tt> detail message. 
+     */
+    public CircularRedirectException() {
+        super();
+    }
+
+    /**
+     * Creates a new CircularRedirectException with the specified detail message.
+     * 
+     * @param message The exception detail message
+     */
+    public CircularRedirectException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new CircularRedirectException with the specified detail message and cause.
+     * 
+     * @param message the exception detail message
+     * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+     * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+     */
+    public CircularRedirectException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/org/apache/http/client/ClientProtocolException.java b/src/org/apache/http/client/ClientProtocolException.java
new file mode 100644
index 0000000..b5a991a
--- /dev/null
+++ b/src/org/apache/http/client/ClientProtocolException.java
@@ -0,0 +1,60 @@
+/*
+ * $HeadURL: $
+ * $Revision: $
+ * $Date: $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import java.io.IOException;
+
+/**
+ * Signals an error in the HTTP protocol.
+ */
+public class ClientProtocolException extends IOException {
+    
+    private static final long serialVersionUID = -5596590843227115865L;
+
+    public ClientProtocolException() {
+        super();
+    }
+
+    public ClientProtocolException(String s) {
+        super(s);
+    }
+    
+    public ClientProtocolException(Throwable cause) {
+        initCause(cause);
+    }
+    
+    public ClientProtocolException(String message, Throwable cause) {
+        super(message);
+        initCause(cause);
+    }
+    
+
+}
diff --git a/src/org/apache/http/client/CookieStore.java b/src/org/apache/http/client/CookieStore.java
new file mode 100644
index 0000000..bc239ac
--- /dev/null
+++ b/src/org/apache/http/client/CookieStore.java
@@ -0,0 +1,76 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/CookieStore.java $
+ * $Revision: 604434 $
+ * $Date: 2007-12-15 06:45:48 -0800 (Sat, 15 Dec 2007) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import java.util.Date;
+import java.util.List;
+
+import org.apache.http.cookie.Cookie;
+
+/**
+ * Abstract cookie store.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public interface CookieStore {
+
+    /**
+     * Adds an {@link Cookie HTTP cookie}, replacing any existing equivalent cookies.
+     * If the given cookie has already expired it will not be added, but existing 
+     * values will still be removed.
+     * 
+     * @param cookie the {@link Cookie cookie} to be added
+     */
+    void addCookie(Cookie cookie);
+
+    /**
+     * Returns all cookies contained in this store.
+     * 
+     * @return all cookies
+     */
+    List<Cookie> getCookies();
+
+    /**
+     * Removes all of {@link Cookie cookies} in this store that have expired by 
+     * the specified {@link java.util.Date date}. 
+     * 
+     * @return true if any cookies were purged.
+     */
+    boolean clearExpired(Date date);
+
+    /**
+     * Clears all cookies.
+     */
+    void clear();
+    
+}
diff --git a/src/org/apache/http/client/CredentialsProvider.java b/src/org/apache/http/client/CredentialsProvider.java
new file mode 100644
index 0000000..8396d84
--- /dev/null
+++ b/src/org/apache/http/client/CredentialsProvider.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/CredentialsProvider.java $
+ * $Revision: 558124 $
+ * $Date: 2007-07-20 13:36:47 -0700 (Fri, 20 Jul 2007) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+
+/**
+ * Abstract credentials provider.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public interface CredentialsProvider {
+
+    /** 
+     * Sets the {@link Credentials credentials} for the given authentication 
+     * scope. Any previous credentials for the given scope will be overwritten.
+     * 
+     * @param authscope the {@link AuthScope authentication scope}
+     * @param credentials the authentication {@link Credentials credentials} 
+     * for the given scope.
+     * 
+     * @see #getCredentials(AuthScope)
+     */
+    void setCredentials(AuthScope authscope, Credentials credentials);
+
+    /**
+     * Get the {@link Credentials credentials} for the given authentication scope.
+     *
+     * @param authscope the {@link AuthScope authentication scope}
+     * @return the credentials 
+     * 
+     * @see #setCredentials(AuthScope, Credentials)
+     */
+    Credentials getCredentials(AuthScope authscope);
+
+    /**
+     * Clears all credentials.
+     */
+    void clear();
+    
+}
diff --git a/src/org/apache/http/client/HttpClient.java b/src/org/apache/http/client/HttpClient.java
new file mode 100644
index 0000000..aaa09e0
--- /dev/null
+++ b/src/org/apache/http/client/HttpClient.java
@@ -0,0 +1,249 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/HttpClient.java $
+ * $Revision: 676020 $
+ * $Date: 2008-07-11 09:38:49 -0700 (Fri, 11 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import java.io.IOException;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.ClientConnectionManager;
+
+/**
+ * Interface for an HTTP client.
+ * HTTP clients encapsulate a smorgasbord of objects required to
+ * execute HTTP requests while handling cookies, authentication,
+ * connection management, and other features.
+ * Thread safety of HTTP clients depends on the implementation
+ * and configuration of the specific client.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version   $Revision: 676020 $
+ *
+ * @since 4.0
+ */
+public interface HttpClient {
+
+
+    /**
+     * Obtains the parameters for this client.
+     * These parameters will become defaults for all requests being
+     * executed with this client, and for the parameters of
+     * dependent objects in this client.
+     *
+     * @return  the default parameters
+     */
+    HttpParams getParams()
+        ;
+
+
+    /**
+     * Obtains the connection manager used by this client.
+     *
+     * @return  the connection manager
+     */
+    ClientConnectionManager getConnectionManager()
+        ;
+
+    /**
+     * Executes a request using the default context.
+     *
+     * @param request   the request to execute
+     *
+     * @return  the response to the request. This is always a final response,
+     *          never an intermediate response with an 1xx status code.
+     *          Whether redirects or authentication challenges will be returned
+     *          or handled automatically depends on the implementation and
+     *          configuration of this client.
+     * @throws IOException in case of a problem or the connection was aborted
+     * @throws ClientProtocolException in case of an http protocol error
+     */
+    HttpResponse execute(HttpUriRequest request)
+        throws IOException, ClientProtocolException
+        ;
+
+
+    /**
+     * Executes a request using the given context.
+     * The route to the target will be determined by the HTTP client.
+     *
+     * @param request   the request to execute
+     * @param context   the context to use for the execution, or
+     *                  <code>null</code> to use the default context
+     *
+     * @return  the response to the request. This is always a final response,
+     *          never an intermediate response with an 1xx status code.
+     *          Whether redirects or authentication challenges will be returned
+     *          or handled automatically depends on the implementation and
+     *          configuration of this client.
+     * @throws IOException in case of a problem or the connection was aborted
+     * @throws ClientProtocolException in case of an http protocol error
+     */
+    HttpResponse execute(HttpUriRequest request, HttpContext context)
+        throws IOException, ClientProtocolException
+        ;
+
+
+    /**
+     * Executes a request to the target using the default context.
+     *
+     * @param target    the target host for the request.
+     *                  Implementations may accept <code>null</code>
+     *                  if they can still determine a route, for example
+     *                  to a default target or by inspecting the request.
+     * @param request   the request to execute
+     *
+     * @return  the response to the request. This is always a final response,
+     *          never an intermediate response with an 1xx status code.
+     *          Whether redirects or authentication challenges will be returned
+     *          or handled automatically depends on the implementation and
+     *          configuration of this client.
+     * @throws IOException in case of a problem or the connection was aborted
+     * @throws ClientProtocolException in case of an http protocol error
+     */
+    HttpResponse execute(HttpHost target, HttpRequest request)
+        throws IOException, ClientProtocolException
+        ;
+
+    /**
+     * Executes a request to the target using the given context.
+     *
+     * @param target    the target host for the request.
+     *                  Implementations may accept <code>null</code>
+     *                  if they can still determine a route, for example
+     *                  to a default target or by inspecting the request.
+     * @param request   the request to execute
+     * @param context   the context to use for the execution, or
+     *                  <code>null</code> to use the default context
+     *
+     * @return  the response to the request. This is always a final response,
+     *          never an intermediate response with an 1xx status code.
+     *          Whether redirects or authentication challenges will be returned
+     *          or handled automatically depends on the implementation and
+     *          configuration of this client.
+     * @throws IOException in case of a problem or the connection was aborted
+     * @throws ClientProtocolException in case of an http protocol error
+     */
+    HttpResponse execute(HttpHost target, HttpRequest request,
+                         HttpContext context)
+        throws IOException, ClientProtocolException
+        ;
+
+    /**
+     * Executes a request using the default context and processes the
+     * response using the given response handler.
+     *
+     * @param request   the request to execute
+     * @param responseHandler the response handler
+     *
+     * @return  the response object as generated by the response handler.
+     * @throws IOException in case of a problem or the connection was aborted
+     * @throws ClientProtocolException in case of an http protocol error
+     */
+    <T> T execute(
+            HttpUriRequest request, 
+            ResponseHandler<? extends T> responseHandler)
+        throws IOException, ClientProtocolException
+        ;
+
+    /**
+     * Executes a request using the given context and processes the
+     * response using the given response handler.
+     *
+     * @param request   the request to execute
+     * @param responseHandler the response handler
+     *
+     * @return  the response object as generated by the response handler.
+     * @throws IOException in case of a problem or the connection was aborted
+     * @throws ClientProtocolException in case of an http protocol error
+     */
+    <T> T execute(
+            HttpUriRequest request, 
+            ResponseHandler<? extends T> responseHandler,
+            HttpContext context)
+        throws IOException, ClientProtocolException
+        ;
+
+    /**
+     * Executes a request to the target using the default context and 
+     * processes the response using the given response handler.
+     *
+     * @param target    the target host for the request.
+     *                  Implementations may accept <code>null</code>
+     *                  if they can still determine a route, for example
+     *                  to a default target or by inspecting the request.
+     * @param request   the request to execute
+     * @param responseHandler the response handler
+     *
+     * @return  the response object as generated by the response handler.
+     * @throws IOException in case of a problem or the connection was aborted
+     * @throws ClientProtocolException in case of an http protocol error
+     */
+    <T> T execute(
+            HttpHost target, 
+            HttpRequest request,
+            ResponseHandler<? extends T> responseHandler)
+        throws IOException, ClientProtocolException
+        ;
+    
+    /**
+     * Executes a request to the target using the given context and 
+     * processes the response using the given response handler.
+     *
+     * @param target    the target host for the request.
+     *                  Implementations may accept <code>null</code>
+     *                  if they can still determine a route, for example
+     *                  to a default target or by inspecting the request.
+     * @param request   the request to execute
+     * @param responseHandler the response handler
+     * @param context   the context to use for the execution, or
+     *                  <code>null</code> to use the default context
+     *
+     * @return  the response object as generated by the response handler.
+     * @throws IOException in case of a problem or the connection was aborted
+     * @throws ClientProtocolException in case of an http protocol error
+     */
+    <T> T execute(
+            HttpHost target, 
+            HttpRequest request,
+            ResponseHandler<? extends T> responseHandler, 
+            HttpContext context)
+        throws IOException, ClientProtocolException
+        ;
+    
+} // interface HttpClient
diff --git a/src/org/apache/http/client/HttpRequestRetryHandler.java b/src/org/apache/http/client/HttpRequestRetryHandler.java
new file mode 100644
index 0000000..9ef8ef9
--- /dev/null
+++ b/src/org/apache/http/client/HttpRequestRetryHandler.java
@@ -0,0 +1,66 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/HttpRequestRetryHandler.java $
+ * $Revision: 535610 $
+ * $Date: 2007-05-06 06:28:13 -0700 (Sun, 06 May 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import java.io.IOException;
+
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * A handler for determining if an HttpRequest should be retried after a 
+ * recoverable exception during execution.
+ * 
+ * <p>
+ * Classes implementing this interface must synchronize access to shared
+ * data as methods of this interfrace may be executed from multiple threads 
+ * </p>
+ * 
+ * @author Michael Becke
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public interface HttpRequestRetryHandler {
+
+    /**
+     * Determines if a method should be retried after an IOException
+     * occurs during execution.
+     * 
+     * @param exception the exception that occurred
+     * @param executionCount the number of times this method has been 
+     * unsuccessfully executed
+     * @param context the context for the request execution
+     * 
+     * @return <code>true</code> if the method should be retried, <code>false</code>
+     * otherwise
+     */
+    boolean retryRequest(IOException exception, int executionCount, HttpContext context);
+
+}
diff --git a/src/org/apache/http/client/HttpResponseException.java b/src/org/apache/http/client/HttpResponseException.java
new file mode 100644
index 0000000..4d8de91
--- /dev/null
+++ b/src/org/apache/http/client/HttpResponseException.java
@@ -0,0 +1,51 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/HttpResponseException.java $
+ * $Revision: 672425 $
+ * $Date: 2008-06-27 16:33:05 -0700 (Fri, 27 Jun 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+/**
+ * Signals a non 2xx HTTP response.
+ */
+public class HttpResponseException extends ClientProtocolException {
+    
+    private static final long serialVersionUID = -7186627969477257933L;
+
+    private final int statusCode;
+    
+    public HttpResponseException(int statusCode, final String s) {
+        super(s);
+        this.statusCode = statusCode;
+    }
+
+    public int getStatusCode() {
+        return this.statusCode;
+    }
+    
+}
diff --git a/src/org/apache/http/client/NonRepeatableRequestException.java b/src/org/apache/http/client/NonRepeatableRequestException.java
new file mode 100644
index 0000000..13ff4d1
--- /dev/null
+++ b/src/org/apache/http/client/NonRepeatableRequestException.java
@@ -0,0 +1,63 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/NonRepeatableRequestException.java $
+ * $Revision: 664326 $
+ * $Date: 2008-06-07 04:48:27 -0700 (Sat, 07 Jun 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import org.apache.http.ProtocolException;
+
+/**
+ * Signals failure to retry the request due to non-repeatable request 
+ * entity.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public class NonRepeatableRequestException extends ProtocolException {
+
+    private static final long serialVersionUID = 82685265288806048L;
+
+    /**
+     * Creates a new NonRepeatableEntityException with a <tt>null</tt> detail message. 
+     */
+    public NonRepeatableRequestException() {
+        super();
+    }
+
+    /**
+     * Creates a new NonRepeatableEntityException with the specified detail message.
+     * 
+     * @param message The exception detail message
+     */
+    public NonRepeatableRequestException(String message) {
+        super(message);
+    }
+
+}
diff --git a/src/org/apache/http/client/RedirectException.java b/src/org/apache/http/client/RedirectException.java
new file mode 100644
index 0000000..82ea9ea
--- /dev/null
+++ b/src/org/apache/http/client/RedirectException.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/RedirectException.java $
+ * $Revision: 664066 $
+ * $Date: 2008-06-06 11:13:18 -0700 (Fri, 06 Jun 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import org.apache.http.ProtocolException;
+
+/**
+ * Signals violation of HTTP specification caused by an invalid redirect
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public class RedirectException extends ProtocolException {
+
+    private static final long serialVersionUID = 4418824536372559326L;
+
+    /**
+     * Creates a new RedirectException with a <tt>null</tt> detail message. 
+     */
+    public RedirectException() {
+        super();
+    }
+
+    /**
+     * Creates a new RedirectException with the specified detail message.
+     * 
+     * @param message The exception detail message
+     */
+    public RedirectException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new RedirectException with the specified detail message and cause.
+     * 
+     * @param message the exception detail message
+     * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+     * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+     */
+    public RedirectException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/org/apache/http/client/RedirectHandler.java b/src/org/apache/http/client/RedirectHandler.java
new file mode 100644
index 0000000..a98b4ae
--- /dev/null
+++ b/src/org/apache/http/client/RedirectHandler.java
@@ -0,0 +1,79 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/RedirectHandler.java $
+ * $Revision: 538647 $
+ * $Date: 2007-05-16 09:41:42 -0700 (Wed, 16 May 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import java.net.URI;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.ProtocolException;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * A handler for determining if an HTTP request should be redirected to 
+ * a new location in response to an HTTP response received from the target
+ * server.
+ * 
+ * <p>
+ * Classes implementing this interface must synchronize access to shared
+ * data as methods of this interfrace may be executed from multiple threads 
+ * </p>
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public interface RedirectHandler {
+
+    /**
+     * Determines if a request should be redirected to a new location
+     * given the response from the target server.
+     * 
+     * @param response the response received from the target server
+     * @param context the context for the request execution
+     * 
+     * @return <code>true</code> if the request should be redirected, <code>false</code>
+     * otherwise
+     */
+    boolean isRedirectRequested(HttpResponse response, HttpContext context);
+    
+    /**
+     * Determines the location request is expected to be redirected to 
+     * given the response from the target server and the current request
+     * execution context.
+     * 
+     * @param response the response received from the target server
+     * @param context the context for the request execution
+     * 
+     * @return redirect URI 
+     */
+    URI getLocationURI(HttpResponse response, HttpContext context)
+            throws ProtocolException;
+
+}
diff --git a/src/org/apache/http/client/RequestDirector.java b/src/org/apache/http/client/RequestDirector.java
new file mode 100644
index 0000000..924c312
--- /dev/null
+++ b/src/org/apache/http/client/RequestDirector.java
@@ -0,0 +1,92 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/RequestDirector.java $
+ * $Revision: 676020 $
+ * $Date: 2008-07-11 09:38:49 -0700 (Fri, 11 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import java.io.IOException;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpException;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * A client-side request director.
+ * The director decides which steps are necessary to execute a request.
+ * It establishes connections and optionally processes redirects and
+ * authentication challenges. The director may therefore generate and
+ * send a sequence of requests in order to execute one initial request.
+ *
+ * <br/><b>Note:</b>
+ * It is most likely that implementations of this interface will
+ * allocate connections, and return responses that depend on those
+ * connections for reading the response entity. Such connections
+ * MUST be released, but that is out of the scope of a request director.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 676020 $
+ *
+ * @since 4.0
+ */
+public interface RequestDirector {
+
+
+    /**
+     * Executes a request.
+     * <br/><b>Note:</b>
+     * For the time being, a new director is instantiated for each request.
+     * This is the same behavior as for <code>HttpMethodDirector</code>
+     * in HttpClient 3.
+     *
+     * @param target    the target host for the request.
+     *                  Implementations may accept <code>null</code>
+     *                  if they can still determine a route, for example
+     *                  to a default target or by inspecting the request.
+     * @param request   the request to execute
+     * @param context   the context for executing the request
+     *
+     * @return  the final response to the request.
+     *          This is never an intermediate response with status code 1xx.
+     *
+     * @throws HttpException            in case of a problem
+     * @throws IOException              in case of an IO problem
+     *                                     or if the connection was aborted
+     */
+    HttpResponse execute(HttpHost target, HttpRequest request,
+                         HttpContext context)
+        throws HttpException, IOException
+        ;
+
+} // class ClientRequestDirector
diff --git a/src/org/apache/http/client/ResponseHandler.java b/src/org/apache/http/client/ResponseHandler.java
new file mode 100644
index 0000000..33a3391
--- /dev/null
+++ b/src/org/apache/http/client/ResponseHandler.java
@@ -0,0 +1,59 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/ResponseHandler.java $
+ * $Revision: 677240 $
+ * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import java.io.IOException;
+
+import org.apache.http.HttpResponse;
+
+/**
+ * Handler that encapsulates the process of generating a response object 
+ * from a {@link HttpResponse}.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public interface ResponseHandler<T> {
+
+    /**
+     * Processes an {@link HttpResponse} and returns some value
+     * corresponding to that response.
+     * 
+     * @param response The response to process
+     * @return A value determined by the response
+     * 
+     * @throws ClientProtocolException in case of an http protocol error
+     * @throws IOException in case of a problem or the connection was aborted
+     */
+    T handleResponse(HttpResponse response) throws ClientProtocolException, IOException;
+    
+}
diff --git a/src/org/apache/http/client/UserTokenHandler.java b/src/org/apache/http/client/UserTokenHandler.java
new file mode 100644
index 0000000..f8e55d8
--- /dev/null
+++ b/src/org/apache/http/client/UserTokenHandler.java
@@ -0,0 +1,64 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/UserTokenHandler.java $
+ * $Revision: 658759 $
+ * $Date: 2008-05-21 10:06:17 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * A handler for determining if the given execution context is user specific 
+ * or not. The token object returned by this handler is expected to uniquely 
+ * identify the current user if the context is user specific or to be 
+ * <code>null</code> if the context does not contain any resources or details 
+ * specific to the current user.
+ * <p/>
+ * The user token will be used to ensure that user specific resouces will not
+ * shared with or reused by other users.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public interface UserTokenHandler {
+
+    /**
+     * The token object returned by this method is expected to uniquely 
+     * identify the current user if the context is user specific or to be 
+     * <code>null</code> if it is not.
+     * 
+     * @param context the execution context
+     * 
+     * @return user token that uniquely identifies the user or 
+     * <code>null</null> if the context is not user specific.
+     */
+    Object getUserToken(HttpContext context);
+
+}
diff --git a/src/org/apache/http/client/entity/UrlEncodedFormEntity.java b/src/org/apache/http/client/entity/UrlEncodedFormEntity.java
new file mode 100644
index 0000000..89b9c45
--- /dev/null
+++ b/src/org/apache/http/client/entity/UrlEncodedFormEntity.java
@@ -0,0 +1,76 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/entity/UrlEncodedFormEntity.java $
+ * $Revision: 655107 $
+ * $Date: 2008-05-10 08:20:42 -0700 (Sat, 10 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.entity;
+
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.utils.URLEncodedUtils;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * An entity composed of a list of url-encoded pairs.
+ * This is typically useful while sending an HTTP POST request.
+ */
+public class UrlEncodedFormEntity extends StringEntity {
+  
+    /**
+     * Constructs a new {@link UrlEncodedFormEntity} with the list
+     * of parameters in the specified encoding.
+     * 
+     * @param parameters list of name/value pairs
+     * @param encoding encoding the name/value pairs be encoded with
+     * @throws UnsupportedEncodingException if the encoding isn't supported
+     */
+    public UrlEncodedFormEntity (
+        final List <? extends NameValuePair> parameters, 
+        final String encoding) throws UnsupportedEncodingException {
+        super(URLEncodedUtils.format(parameters, encoding), 
+            encoding);
+        setContentType(URLEncodedUtils.CONTENT_TYPE);
+    }
+
+    /**
+     * Constructs a new {@link UrlEncodedFormEntity} with the list
+     * of parameters with the default encoding of {@link HTTP#DEFAULT_CONTENT_CHARSET}
+     * 
+     * @param parameters list of name/value pairs
+     * @throws UnsupportedEncodingException if the default encoding isn't supported
+     */
+    public UrlEncodedFormEntity (
+        final List <? extends NameValuePair> parameters) throws UnsupportedEncodingException {
+        super(URLEncodedUtils.format(parameters, HTTP.DEFAULT_CONTENT_CHARSET), 
+            HTTP.DEFAULT_CONTENT_CHARSET);
+        setContentType(URLEncodedUtils.CONTENT_TYPE);
+    }
+    
+}
diff --git a/src/org/apache/http/client/methods/AbortableHttpRequest.java b/src/org/apache/http/client/methods/AbortableHttpRequest.java
new file mode 100644
index 0000000..c402609
--- /dev/null
+++ b/src/org/apache/http/client/methods/AbortableHttpRequest.java
@@ -0,0 +1,91 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/AbortableHttpRequest.java $
+ * $Revision: 639600 $
+ * $Date: 2008-03-21 04:28:15 -0700 (Fri, 21 Mar 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.io.IOException;
+
+import org.apache.http.client.HttpClient;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionRequest;
+import org.apache.http.conn.ConnectionReleaseTrigger;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+
+/**
+ * Interface representing an HTTP request that can be aborted by shutting 
+ * down the underlying HTTP connection.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version   $Revision: 639600 $
+ *
+ * @since 4.0
+ */
+public interface AbortableHttpRequest {
+
+    /**
+     * Sets the {@link ClientConnectionRequest} callback that can be
+     * used to abort a long-lived request for a connection.
+     * If the request is already aborted, throws an {@link IOException}.
+     * 
+     * @see ClientConnectionManager
+     * @see ThreadSafeClientConnManager
+     */
+    void setConnectionRequest(ClientConnectionRequest connRequest) throws IOException;
+    
+    /**
+     * Sets the {@link ConnectionReleaseTrigger} callback that can
+     * be used to abort an active connection.
+     * Typically, this will be the {@link ManagedClientConnection} itself.
+     * If the request is already aborted, throws an {@link IOException}.
+     */
+    void setReleaseTrigger(ConnectionReleaseTrigger releaseTrigger) throws IOException;
+
+    /**
+     * Aborts this http request. Any active execution of this method should
+     * return immediately. If the request has not started, it will abort after
+     * the next execution. Aborting this request will cause all subsequent
+     * executions with this request to fail.
+     * 
+     * @see HttpClient#execute(HttpUriRequest)
+     * @see HttpClient#execute(org.apache.http.HttpHost,
+     *      org.apache.http.HttpRequest)
+     * @see HttpClient#execute(HttpUriRequest,
+     *      org.apache.http.protocol.HttpContext)
+     * @see HttpClient#execute(org.apache.http.HttpHost,
+     *      org.apache.http.HttpRequest, org.apache.http.protocol.HttpContext)
+     */
+    void abort();
+
+}
+
diff --git a/src/org/apache/http/client/methods/HttpDelete.java b/src/org/apache/http/client/methods/HttpDelete.java
new file mode 100644
index 0000000..4a0fb77
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpDelete.java
@@ -0,0 +1,76 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpDelete.java $
+ * $Revision: 664505 $
+ * $Date: 2008-06-08 06:21:20 -0700 (Sun, 08 Jun 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.net.URI;
+
+/**
+ * HTTP DELETE method
+ * <p>
+ * The HTTP DELETE method is defined in section 9.7 of
+ * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
+ * <blockquote>
+ * The DELETE method requests that the origin server delete the resource
+ * identified by the Request-URI. [...] The client cannot
+ * be guaranteed that the operation has been carried out, even if the
+ * status code returned from the origin server indicates that the action
+ * has been completed successfully.
+ * </blockquote>
+ */
+public class HttpDelete extends HttpRequestBase {
+
+    public final static String METHOD_NAME = "DELETE";
+
+    
+    public HttpDelete() {
+        super();
+    }
+
+    public HttpDelete(final URI uri) {
+        super();
+        setURI(uri);
+    }
+
+    /**
+     * @throws IllegalArgumentException if the uri is invalid. 
+     */
+    public HttpDelete(final String uri) {
+        super();
+        setURI(URI.create(uri));
+    }
+
+    @Override
+    public String getMethod() {
+        return METHOD_NAME;
+    }
+
+}
diff --git a/src/org/apache/http/client/methods/HttpEntityEnclosingRequestBase.java b/src/org/apache/http/client/methods/HttpEntityEnclosingRequestBase.java
new file mode 100644
index 0000000..8ac6f01
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpEntityEnclosingRequestBase.java
@@ -0,0 +1,81 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpEntityEnclosingRequestBase.java $
+ * $Revision: 674186 $
+ * $Date: 2008-07-05 05:18:54 -0700 (Sat, 05 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.client.utils.CloneUtils;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * Basic implementation of an HTTP request that can be modified.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 674186 $
+ * 
+ * @since 4.0
+ */
+public abstract class HttpEntityEnclosingRequestBase 
+    extends HttpRequestBase implements HttpEntityEnclosingRequest {
+    
+    private HttpEntity entity;
+    
+    public HttpEntityEnclosingRequestBase() {
+        super();
+    }
+
+    public HttpEntity getEntity() {
+        return this.entity;
+    }
+
+    public void setEntity(final HttpEntity entity) {
+        this.entity = entity;
+    }
+    
+    public boolean expectContinue() {
+        Header expect = getFirstHeader(HTTP.EXPECT_DIRECTIVE);
+        return expect != null && HTTP.EXPECT_CONTINUE.equalsIgnoreCase(expect.getValue());
+    }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        HttpEntityEnclosingRequestBase clone = 
+            (HttpEntityEnclosingRequestBase) super.clone();
+        if (this.entity != null) {
+            clone.entity = (HttpEntity) CloneUtils.clone(this.entity);
+        }
+        return clone;
+    }
+
+}
diff --git a/src/org/apache/http/client/methods/HttpGet.java b/src/org/apache/http/client/methods/HttpGet.java
new file mode 100644
index 0000000..2908f1d
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpGet.java
@@ -0,0 +1,83 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpGet.java $
+ * $Revision: 664505 $
+ * $Date: 2008-06-08 06:21:20 -0700 (Sun, 08 Jun 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.net.URI;
+
+/**
+ * HTTP GET method.
+ * <p>
+ * The HTTP GET method is defined in section 9.3 of
+ * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
+ * <blockquote>
+ * The GET method means retrieve whatever information (in the form of an
+ * entity) is identified by the Request-URI. If the Request-URI refers
+ * to a data-producing process, it is the produced data which shall be
+ * returned as the entity in the response and not the source text of the
+ * process, unless that text happens to be the output of the process.
+ * </blockquote>
+ * </p>
+ * <p>
+ * GetMethods will follow redirect requests from the http server by default.
+ * This behavour can be disabled by calling setFollowRedirects(false).</p>
+ * 
+ * @version $Revision: 664505 $
+ * 
+ * @since 4.0
+ */
+public class HttpGet extends HttpRequestBase {
+
+    public final static String METHOD_NAME = "GET";
+    
+    public HttpGet() {
+        super();
+    }
+
+    public HttpGet(final URI uri) {
+        super();
+        setURI(uri);
+    }
+
+    /**
+     * @throws IllegalArgumentException if the uri is invalid. 
+     */
+    public HttpGet(final String uri) {
+        super();
+        setURI(URI.create(uri));
+    }
+
+    @Override
+    public String getMethod() {
+        return METHOD_NAME;
+    }
+    
+}
diff --git a/src/org/apache/http/client/methods/HttpHead.java b/src/org/apache/http/client/methods/HttpHead.java
new file mode 100644
index 0000000..29e58a3
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpHead.java
@@ -0,0 +1,83 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpHead.java $
+ * $Revision: 664505 $
+ * $Date: 2008-06-08 06:21:20 -0700 (Sun, 08 Jun 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.net.URI;
+
+/**
+ * HTTP HEAD method.
+ * <p>
+ * The HTTP HEAD method is defined in section 9.4 of 
+ * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
+ * <blockquote>
+ * The HEAD method is identical to GET except that the server MUST NOT
+ * return a message-body in the response. The metainformation contained
+ * in the HTTP headers in response to a HEAD request SHOULD be identical
+ * to the information sent in response to a GET request. This method can
+ * be used for obtaining metainformation about the entity implied by the
+ * request without transferring the entity-body itself. This method is
+ * often used for testing hypertext links for validity, accessibility,
+ * and recent modification.
+ * </blockquote>
+ * </p>
+ *
+ * @version $Revision: 664505 $
+ * 
+ * @since 4.0
+ */
+public class HttpHead extends HttpRequestBase {
+
+    public final static String METHOD_NAME = "HEAD";
+    
+    public HttpHead() {
+        super();
+    }
+
+    public HttpHead(final URI uri) {
+        super();
+        setURI(uri);
+    }
+
+    /**
+     * @throws IllegalArgumentException if the uri is invalid. 
+     */
+    public HttpHead(final String uri) {
+        super();
+        setURI(URI.create(uri));
+    }
+
+    @Override
+    public String getMethod() {
+        return METHOD_NAME;
+    }
+    
+}
diff --git a/src/org/apache/http/client/methods/HttpOptions.java b/src/org/apache/http/client/methods/HttpOptions.java
new file mode 100644
index 0000000..3758360
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpOptions.java
@@ -0,0 +1,105 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpOptions.java $
+ * $Revision: 664505 $
+ * $Date: 2008-06-08 06:21:20 -0700 (Sun, 08 Jun 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.HeaderIterator;
+import org.apache.http.HttpResponse;
+
+/**
+ * HTTP OPTIONS method.
+ * <p>
+ * The HTTP OPTIONS method is defined in section 9.2 of 
+ * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
+ * <blockquote>
+ *  The OPTIONS method represents a request for information about the
+ *  communication options available on the request/response chain
+ *  identified by the Request-URI. This method allows the client to
+ *  determine the options and/or requirements associated with a resource,
+ *  or the capabilities of a server, without implying a resource action
+ *  or initiating a resource retrieval.
+ * </blockquote>
+ * </p>
+ * 
+ * @version $Revision: 664505 $
+ * 
+ * @since 4.0
+ */
+public class HttpOptions extends HttpRequestBase {
+
+    public final static String METHOD_NAME = "OPTIONS";
+    
+    public HttpOptions() {
+        super();
+    }
+
+    public HttpOptions(final URI uri) {
+        super();
+        setURI(uri);
+    }
+
+    /**
+     * @throws IllegalArgumentException if the uri is invalid. 
+     */
+    public HttpOptions(final String uri) {
+        super();
+        setURI(URI.create(uri));
+    }
+
+    @Override
+    public String getMethod() {
+        return METHOD_NAME;
+    }
+    
+    public Set<String> getAllowedMethods(final HttpResponse response) {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        
+        HeaderIterator it = response.headerIterator("Allow");
+        Set<String> methods = new HashSet<String>();
+        while (it.hasNext()) {
+            Header header = it.nextHeader();
+            HeaderElement[] elements = header.getElements();
+            for (HeaderElement element : elements) {
+                methods.add(element.getName());
+            }
+        }
+        return methods;
+    }
+    
+}
diff --git a/src/org/apache/http/client/methods/HttpPost.java b/src/org/apache/http/client/methods/HttpPost.java
new file mode 100644
index 0000000..bc58803
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpPost.java
@@ -0,0 +1,87 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpPost.java $
+ * $Revision: 664505 $
+ * $Date: 2008-06-08 06:21:20 -0700 (Sun, 08 Jun 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.net.URI;
+
+/**
+ * HTTP POST method.
+ * <p>
+ * The HTTP POST method is defined in section 9.5 of 
+ * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
+ * <blockquote>
+ * The POST method is used to request that the origin server accept the entity
+ * enclosed in the request as a new subordinate of the resource identified by
+ * the Request-URI in the Request-Line. POST is designed to allow a uniform
+ * method to cover the following functions:
+ * <ul>
+ *   <li>Annotation of existing resources</li>
+ *   <li>Posting a message to a bulletin board, newsgroup, mailing list, or 
+ *     similar group of articles</li>
+ *   <li>Providing a block of data, such as the result of submitting a form,
+ *     to a data-handling process</li>
+ *   <li>Extending a database through an append operation</li>
+ * </ul>
+ * </blockquote>
+ * </p>
+ *
+ * @version $Revision: 664505 $
+ * 
+ * @since 4.0
+ */
+public class HttpPost extends HttpEntityEnclosingRequestBase {
+
+    public final static String METHOD_NAME = "POST";
+    
+    public HttpPost() {
+        super();
+    }
+    
+    public HttpPost(final URI uri) {
+        super();
+        setURI(uri);
+    }
+    
+    /**
+     * @throws IllegalArgumentException if the uri is invalid. 
+     */
+    public HttpPost(final String uri) {
+        super();
+        setURI(URI.create(uri));
+    }
+
+    @Override
+    public String getMethod() {
+        return METHOD_NAME;
+    }
+    
+}
diff --git a/src/org/apache/http/client/methods/HttpPut.java b/src/org/apache/http/client/methods/HttpPut.java
new file mode 100644
index 0000000..5b50135
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpPut.java
@@ -0,0 +1,79 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpPut.java $
+ * $Revision: 664505 $
+ * $Date: 2008-06-08 06:21:20 -0700 (Sun, 08 Jun 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.net.URI;
+
+/**
+ * HTTP PUT method.
+ * <p>
+ * The HTTP PUT method is defined in section 9.6 of 
+ * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
+ * <blockquote>
+ * The PUT method requests that the enclosed entity be stored under the
+ * supplied Request-URI. If the Request-URI refers to an already
+ * existing resource, the enclosed entity SHOULD be considered as a
+ * modified version of the one residing on the origin server. 
+ * </blockquote>
+ * </p>
+ *
+ * @version $Revision: 664505 $
+ * 
+ * @since 4.0
+ */
+public class HttpPut extends HttpEntityEnclosingRequestBase {
+
+    public final static String METHOD_NAME = "PUT";
+    
+    public HttpPut() {
+        super();
+    }
+    
+    public HttpPut(final URI uri) {
+        super();
+        setURI(uri);
+    }
+
+    /**
+     * @throws IllegalArgumentException if the uri is invalid. 
+     */
+    public HttpPut(final String uri) {
+        super();
+        setURI(URI.create(uri));
+    }
+
+    @Override
+    public String getMethod() {
+        return METHOD_NAME;
+    }
+    
+}
diff --git a/src/org/apache/http/client/methods/HttpRequestBase.java b/src/org/apache/http/client/methods/HttpRequestBase.java
new file mode 100644
index 0000000..8938ea0
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpRequestBase.java
@@ -0,0 +1,182 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpRequestBase.java $
+ * $Revision: 674186 $
+ * $Date: 2008-07-05 05:18:54 -0700 (Sat, 05 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.http.ProtocolVersion;
+import org.apache.http.RequestLine;
+import org.apache.http.client.utils.CloneUtils;
+import org.apache.http.conn.ClientConnectionRequest;
+import org.apache.http.conn.ConnectionReleaseTrigger;
+import org.apache.http.message.AbstractHttpMessage;
+import org.apache.http.message.BasicRequestLine;
+import org.apache.http.message.HeaderGroup;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+
+/**
+ * Basic implementation of an HTTP request that can be modified.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 674186 $
+ * 
+ * @since 4.0
+ */
+public abstract class HttpRequestBase extends AbstractHttpMessage 
+    implements HttpUriRequest, AbortableHttpRequest, Cloneable {
+
+    private Lock abortLock;
+
+    private boolean aborted;
+    
+    private URI uri;
+    private ClientConnectionRequest connRequest;
+    private ConnectionReleaseTrigger releaseTrigger;
+    
+    public HttpRequestBase() {
+        super();
+        this.abortLock = new ReentrantLock();
+    }
+
+    public abstract String getMethod();
+
+    public ProtocolVersion getProtocolVersion() {
+        return HttpProtocolParams.getVersion(getParams());
+    }
+
+    public URI getURI() {
+        return this.uri;
+    }
+    
+    public RequestLine getRequestLine() {
+        String method = getMethod();
+        ProtocolVersion ver = getProtocolVersion();
+        URI uri = getURI();
+        String uritext = null;
+        if (uri != null) {
+            uritext = uri.toASCIIString();
+        }
+        if (uritext == null || uritext.length() == 0) {
+            uritext = "/";
+        }
+        return new BasicRequestLine(method, uritext, ver);
+    }
+
+    public void setURI(final URI uri) {
+        this.uri = uri;
+    }
+
+    public void setConnectionRequest(final ClientConnectionRequest connRequest)
+            throws IOException {
+        this.abortLock.lock();
+        try {
+            if (this.aborted) {
+                throw new IOException("Request already aborted");
+            }
+            
+            this.releaseTrigger = null;
+            this.connRequest = connRequest;
+        } finally {
+            this.abortLock.unlock();
+        }
+    }
+
+    public void setReleaseTrigger(final ConnectionReleaseTrigger releaseTrigger)
+            throws IOException {
+        this.abortLock.lock();
+        try {
+            if (this.aborted) {
+                throw new IOException("Request already aborted");
+            }
+            
+            this.connRequest = null;
+            this.releaseTrigger = releaseTrigger;
+        } finally {
+            this.abortLock.unlock();
+        }
+    }
+    
+    public void abort() {
+        ClientConnectionRequest localRequest;
+        ConnectionReleaseTrigger localTrigger;
+        
+        this.abortLock.lock();
+        try {
+            if (this.aborted) {
+                return;
+            }            
+            this.aborted = true;
+            
+            localRequest = connRequest;
+            localTrigger = releaseTrigger;
+        } finally {
+            this.abortLock.unlock();
+        }        
+
+        // Trigger the callbacks outside of the lock, to prevent
+        // deadlocks in the scenario where the callbacks have
+        // their own locks that may be used while calling
+        // setReleaseTrigger or setConnectionRequest.
+        if (localRequest != null) {
+            localRequest.abortRequest();
+        }
+        if (localTrigger != null) {
+            try {
+                localTrigger.abortConnection();
+            } catch (IOException ex) {
+                // ignore
+            }
+        }
+    }
+    
+    public boolean isAborted() {
+        return this.aborted;
+    }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        HttpRequestBase clone = (HttpRequestBase) super.clone();
+        clone.abortLock = new ReentrantLock();
+        clone.aborted = false;
+        clone.releaseTrigger = null;
+        clone.connRequest = null;
+        clone.headergroup = (HeaderGroup) CloneUtils.clone(this.headergroup);
+        clone.params = (HttpParams) CloneUtils.clone(this.params);
+        return clone;
+    }
+    
+}
diff --git a/src/org/apache/http/client/methods/HttpTrace.java b/src/org/apache/http/client/methods/HttpTrace.java
new file mode 100644
index 0000000..94f18ff
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpTrace.java
@@ -0,0 +1,82 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpTrace.java $
+ * $Revision: 664505 $
+ * $Date: 2008-06-08 06:21:20 -0700 (Sun, 08 Jun 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.net.URI;
+
+/**
+ * HTTP TRACE method.
+ * <p>
+ * The HTTP TRACE method is defined in section 9.6 of 
+ * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
+ * <blockquote>
+ *  The TRACE method is used to invoke a remote, application-layer loop-
+ *  back of the request message. The final recipient of the request
+ *  SHOULD reflect the message received back to the client as the
+ *  entity-body of a 200 (OK) response. The final recipient is either the
+ *  origin server or the first proxy or gateway to receive a Max-Forwards
+ *  value of zero (0) in the request (see section 14.31). A TRACE request
+ *  MUST NOT include an entity.
+ * </blockquote>
+ * </p>
+ * 
+ * @version $Revision: 664505 $
+ * 
+ * @since 4.0
+ */
+public class HttpTrace extends HttpRequestBase {
+
+    public final static String METHOD_NAME = "TRACE";
+    
+    public HttpTrace() {
+        super();
+    }
+
+    public HttpTrace(final URI uri) {
+        super();
+        setURI(uri);
+    }
+    
+    /**
+     * @throws IllegalArgumentException if the uri is invalid. 
+     */
+    public HttpTrace(final String uri) {
+        super();
+        setURI(URI.create(uri));
+    }
+
+    @Override
+    public String getMethod() {
+        return METHOD_NAME;
+    }
+    
+}
diff --git a/src/org/apache/http/client/methods/HttpUriRequest.java b/src/org/apache/http/client/methods/HttpUriRequest.java
new file mode 100644
index 0000000..56d064a
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpUriRequest.java
@@ -0,0 +1,80 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpUriRequest.java $
+ * $Revision: 659191 $
+ * $Date: 2008-05-22 11:26:53 -0700 (Thu, 22 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.net.URI;
+
+import org.apache.http.HttpRequest;
+
+/**
+ * Extended version of the {@link HttpRequest} interface that provides 
+ * convenience methods to access request properties such as request URI
+ * and method type.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version   $Revision: 659191 $
+ *
+ * @since 4.0
+ */
+public interface HttpUriRequest extends HttpRequest {
+    
+    /**
+     * Returns the HTTP method this request uses, such as <code>GET</code>,
+     * <code>PUT</code>, <code>POST</code>, or other.
+     */
+    String getMethod();
+
+    /**
+     * Returns the URI this request uses, such as
+     * <code>http://example.org/path/to/file</code>.
+     */
+    URI getURI();
+    
+    /**
+     * Aborts execution of the request. 
+     * 
+     * @throws UnsupportedOperationException if the abort operation 
+     *   is not supported / cannot be implemented.
+     */
+    void abort() throws UnsupportedOperationException;
+    
+    /**
+     * Tests if the request execution has been aborted.
+     * 
+     * @return <code>true</code> if the request execution has been aborted,
+     *   <code>false</code> otherwise.
+     */
+    boolean isAborted();
+    
+}
diff --git a/src/org/apache/http/client/methods/package.html b/src/org/apache/http/client/methods/package.html
new file mode 100644
index 0000000..c2a602a
--- /dev/null
+++ b/src/org/apache/http/client/methods/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Request implementations for the various HTTP methods like GET and POST.
+
+</body>
+</html>
diff --git a/src/org/apache/http/client/package.html b/src/org/apache/http/client/package.html
new file mode 100644
index 0000000..64c7287
--- /dev/null
+++ b/src/org/apache/http/client/package.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+The API for client-side HTTP communication and
+entry point to the <i>HttpClient</i> module.
+
+</body>
+</html>
diff --git a/src/org/apache/http/client/params/AllClientPNames.java b/src/org/apache/http/client/params/AllClientPNames.java
new file mode 100644
index 0000000..e55bca7
--- /dev/null
+++ b/src/org/apache/http/client/params/AllClientPNames.java
@@ -0,0 +1,65 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/params/AllClientPNames.java $
+ * $Revision: 576078 $
+ * $Date: 2007-09-16 04:50:41 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.params;
+
+
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.auth.params.AuthPNames;
+import org.apache.http.cookie.params.CookieSpecPNames;
+import org.apache.http.conn.params.ConnManagerPNames;
+import org.apache.http.conn.params.ConnConnectionPNames;
+import org.apache.http.conn.params.ConnRoutePNames;
+
+
+/**
+ * Collected parameter names for the HttpClient module.
+ * This interface combines the parameter definitions of the HttpClient
+ * module and all dependency modules or informational units.
+ * It does not define additional parameter names, but references
+ * other interfaces defining parameter names.
+ * <br/>
+ * This interface is meant as a navigation aid for developers.
+ * When referring to parameter names, you should use the interfaces
+ * in which the respective constants are actually defined.
+ *
+ * @version $Revision: 576078 $
+ * 
+ * @since 4.0
+ */
+public interface AllClientPNames extends
+    CoreConnectionPNames, CoreProtocolPNames,
+    ClientPNames, AuthPNames, CookieSpecPNames,
+    ConnConnectionPNames, ConnManagerPNames, ConnRoutePNames {
+
+    // no additional definitions
+}
+
diff --git a/src/org/apache/http/client/params/AuthPolicy.java b/src/org/apache/http/client/params/AuthPolicy.java
new file mode 100644
index 0000000..5bcdd38
--- /dev/null
+++ b/src/org/apache/http/client/params/AuthPolicy.java
@@ -0,0 +1,58 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/params/AuthPolicy.java $
+ * $Revision: 534839 $
+ * $Date: 2007-05-03 06:03:41 -0700 (Thu, 03 May 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.params;
+
+public final class AuthPolicy {
+
+    private AuthPolicy() {
+        super();
+    }
+    
+    /**
+     * The NTLM scheme is a proprietary Microsoft Windows Authentication 
+     * protocol (considered to be the most secure among currently supported 
+     * authentication schemes).
+     */
+    public static final String NTLM = "NTLM";
+    
+    /** 
+     * Digest authentication scheme as defined in RFC2617.
+     */
+    public static final String DIGEST = "Digest";
+
+    /** 
+     * Basic authentication scheme as defined in RFC2617 (considered inherently
+     * insecure, but most widely supported)
+     */
+    public static final String BASIC = "Basic";
+    
+}
diff --git a/src/org/apache/http/client/params/ClientPNames.java b/src/org/apache/http/client/params/ClientPNames.java
new file mode 100644
index 0000000..f98eeb7
--- /dev/null
+++ b/src/org/apache/http/client/params/ClientPNames.java
@@ -0,0 +1,139 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/params/ClientPNames.java $
+ * $Revision: 659595 $
+ * $Date: 2008-05-23 09:47:14 -0700 (Fri, 23 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.params;
+
+
+/**
+ * Parameter names for the HttpClient module.
+ * This does not include parameters for informational units
+ * HttpAuth, HttpCookie, or HttpConn.
+ *
+ * @version $Revision: 659595 $
+ * 
+ * @since 4.0
+ */
+public interface ClientPNames {
+
+    /**
+     * Defines the class name of the default {@link org.apache.http.conn.ClientConnectionManager}
+     * <p>
+     * This parameter expects a value of type {@link String}.
+     * </p>
+     */ 
+    public static final String CONNECTION_MANAGER_FACTORY_CLASS_NAME = "http.connection-manager.factory-class-name";
+    
+    /**
+     * Defines the factory to create a default {@link org.apache.http.conn.ClientConnectionManager}.
+     * <p>
+     * This parameters expects a value of type {@link org.apache.http.conn.ClientConnectionManagerFactory}.
+     * </p>
+     */
+    public static final String CONNECTION_MANAGER_FACTORY = "http.connection-manager.factory-object";
+    
+    /** 
+     * Defines whether redirects should be handled automatically
+     * <p>
+     * This parameter expects a value of type {@link Boolean}.
+     * </p>
+     */
+    public static final String HANDLE_REDIRECTS = "http.protocol.handle-redirects";
+
+    /**
+     * Defines whether relative redirects should be rejected.
+     * <p>
+     * This parameter expects a value of type {@link Boolean}.
+     * </p>
+     */
+    public static final String REJECT_RELATIVE_REDIRECT = "http.protocol.reject-relative-redirect"; 
+
+    /** 
+     * Defines the maximum number of redirects to be followed. 
+     * The limit on number of redirects is intended to prevent infinite loops. 
+     * <p>
+     * This parameter expects a value of type {@link Integer}.
+     * </p>
+     */
+    public static final String MAX_REDIRECTS = "http.protocol.max-redirects";
+
+    /** 
+     * Defines whether circular redirects (redirects to the same location) should be allowed. 
+     * The HTTP spec is not sufficiently clear whether circular redirects are permitted, 
+     * therefore optionally they can be enabled
+     * <p>
+     * This parameter expects a value of type {@link Boolean}.
+     * </p>
+     */
+    public static final String ALLOW_CIRCULAR_REDIRECTS = "http.protocol.allow-circular-redirects";
+
+    /**
+     * Defines whether authentication should be handled automatically.
+     * <p>
+     * This parameter expects a value of type {@link Boolean}.
+     * </p>
+     */
+    public static final String HANDLE_AUTHENTICATION = "http.protocol.handle-authentication";
+
+    /**
+     * Defines the name of the cookie specification to be used for HTTP state management.
+     * <p>
+     * This parameter expects a value of type {@link String}.
+     * </p>
+     */
+    public static final String COOKIE_POLICY = "http.protocol.cookie-policy";
+    
+    /**
+     * Defines the virtual host name.
+     * <p>
+     * This parameter expects a value of type {@link org.apache.http.HttpHost}. 
+     * </p>
+     */
+    public static final String VIRTUAL_HOST = "http.virtual-host"; 
+
+    /**
+     * Defines the request headers to be sent per default with each request.
+     * <p>
+     * This parameter expects a value of type {@link java.util.Collection}. The 
+     * collection is expected to contain {@link org.apache.http.Header}s. 
+     * </p>
+     */
+    public static final String DEFAULT_HEADERS = "http.default-headers"; 
+    
+    /**
+     * Defines the default host. The default value will be used if the target host is
+     * not explicitly specified in the request URI.
+     * <p>
+     * This parameter expects a value of type {@link org.apache.http.HttpHost}.
+     * </p>
+     */
+    public static final String DEFAULT_HOST = "http.default-host";
+
+}
+
diff --git a/src/org/apache/http/client/params/ClientParamBean.java b/src/org/apache/http/client/params/ClientParamBean.java
new file mode 100644
index 0000000..76431a7
--- /dev/null
+++ b/src/org/apache/http/client/params/ClientParamBean.java
@@ -0,0 +1,92 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/params/ClientParamBean.java $
+ * $Revision: 659595 $
+ * $Date: 2008-05-23 09:47:14 -0700 (Fri, 23 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.params;
+
+import java.util.Collection;
+
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.conn.ClientConnectionManagerFactory;
+import org.apache.http.params.HttpAbstractParamBean;
+import org.apache.http.params.HttpParams;
+
+public class ClientParamBean extends HttpAbstractParamBean {
+
+    public ClientParamBean (final HttpParams params) {
+        super(params);
+    }
+
+    public void setConnectionManagerFactoryClassName (final String factory) {
+        params.setParameter(ClientPNames.CONNECTION_MANAGER_FACTORY_CLASS_NAME, factory);
+    }
+    
+    public void setConnectionManagerFactory(ClientConnectionManagerFactory factory) {
+        params.setParameter(ClientPNames.CONNECTION_MANAGER_FACTORY, factory);
+    }
+
+    public void setHandleRedirects (final boolean handle) {
+        params.setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, handle);
+    }
+
+    public void setRejectRelativeRedirect (final boolean reject) {
+        params.setBooleanParameter(ClientPNames.REJECT_RELATIVE_REDIRECT, reject);
+    }
+
+    public void setMaxRedirects (final int maxRedirects) {
+        params.setIntParameter(ClientPNames.MAX_REDIRECTS, maxRedirects);
+    }
+
+    public void setAllowCircularRedirects (final boolean allow) {
+        params.setBooleanParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, allow);
+    }
+
+    public void setHandleAuthentication (final boolean handle) {
+        params.setBooleanParameter(ClientPNames.HANDLE_AUTHENTICATION, handle);
+    }
+
+    public void setCookiePolicy (final String policy) {
+        params.setParameter(ClientPNames.COOKIE_POLICY, policy);
+    }
+
+    public void setVirtualHost (final HttpHost host) {
+        params.setParameter(ClientPNames.VIRTUAL_HOST, host);
+    }
+
+    public void setDefaultHeaders (final Collection <Header> headers) {
+        params.setParameter(ClientPNames.DEFAULT_HEADERS, headers);
+    }
+
+    public void setDefaultHost (final HttpHost host) {
+        params.setParameter(ClientPNames.DEFAULT_HOST, host);
+    }
+    
+}
diff --git a/src/org/apache/http/client/params/CookiePolicy.java b/src/org/apache/http/client/params/CookiePolicy.java
new file mode 100644
index 0000000..04a131d
--- /dev/null
+++ b/src/org/apache/http/client/params/CookiePolicy.java
@@ -0,0 +1,66 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/params/CookiePolicy.java $
+ * $Revision: 613865 $
+ * $Date: 2008-01-21 04:04:30 -0800 (Mon, 21 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.params;
+
+public final class CookiePolicy {
+
+    /**
+     * The policy that provides high degree of compatibilty 
+     * with common cookie management of popular HTTP agents.
+     */
+    public static final String BROWSER_COMPATIBILITY = "compatibility";
+    
+    /** 
+     * The Netscape cookie draft compliant policy. 
+     */
+    public static final String NETSCAPE = "netscape";
+
+    /** 
+     * The RFC 2109 compliant policy. 
+     */
+    public static final String RFC_2109 = "rfc2109";
+
+    /**
+     * The RFC 2965 compliant policy.
+     */
+    public static final String RFC_2965 = "rfc2965";
+
+    /**
+     * The default 'best match' policy.
+     */
+    public static final String BEST_MATCH = "best-match";
+
+    private CookiePolicy() {
+        super();
+    }
+    
+}
diff --git a/src/org/apache/http/client/params/HttpClientParams.java b/src/org/apache/http/client/params/HttpClientParams.java
new file mode 100644
index 0000000..c21e2b0
--- /dev/null
+++ b/src/org/apache/http/client/params/HttpClientParams.java
@@ -0,0 +1,101 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/params/HttpClientParams.java $
+ * $Revision: 659595 $
+ * $Date: 2008-05-23 09:47:14 -0700 (Fri, 23 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.params;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ * An adaptor for accessing HTTP client parameters in {@link HttpParams}.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Revision: 659595 $
+ * 
+ * @since 4.0
+ */
+public class HttpClientParams {
+
+    private HttpClientParams() {
+        super();
+    }
+
+    public static boolean isRedirecting(final HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        return params.getBooleanParameter
+            (ClientPNames.HANDLE_REDIRECTS, true); 
+    }
+
+    public static void setRedirecting(final HttpParams params, boolean value) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        params.setBooleanParameter
+            (ClientPNames.HANDLE_REDIRECTS, value); 
+    }
+    
+    public static boolean isAuthenticating(final HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        return params.getBooleanParameter
+            (ClientPNames.HANDLE_AUTHENTICATION, true); 
+    }
+
+    public static void setAuthenticating(final HttpParams params, boolean value) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        params.setBooleanParameter
+            (ClientPNames.HANDLE_AUTHENTICATION, value); 
+    }
+    
+    public static String getCookiePolicy(final HttpParams params) { 
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        String cookiePolicy = (String)
+            params.getParameter(ClientPNames.COOKIE_POLICY);
+        if (cookiePolicy == null) {
+            return CookiePolicy.BEST_MATCH;
+        }
+        return cookiePolicy;
+    }
+    
+    public static void setCookiePolicy(final HttpParams params, final String cookiePolicy) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        params.setParameter(ClientPNames.COOKIE_POLICY, cookiePolicy);
+    }
+
+}
diff --git a/src/org/apache/http/client/params/package.html b/src/org/apache/http/client/params/package.html
new file mode 100644
index 0000000..b66cdfb
--- /dev/null
+++ b/src/org/apache/http/client/params/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/params/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Parameters for configuring <i>HttpClient</i>.
+
+</body>
+</html>
diff --git a/src/org/apache/http/client/protocol/ClientContext.java b/src/org/apache/http/client/protocol/ClientContext.java
new file mode 100644
index 0000000..1859f9e
--- /dev/null
+++ b/src/org/apache/http/client/protocol/ClientContext.java
@@ -0,0 +1,52 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/protocol/ClientContext.java $
+ * $Revision: 658759 $
+ * $Date: 2008-05-21 10:06:17 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.protocol;
+
+
+/**
+ * {@link org.apache.http.protocol.HttpContext Context}
+ * attribute names for client.
+ */
+public interface ClientContext {
+    
+    public static final String COOKIESPEC_REGISTRY   = "http.cookiespec-registry"; 
+    public static final String AUTHSCHEME_REGISTRY   = "http.authscheme-registry"; 
+    public static final String COOKIE_STORE          = "http.cookie-store"; 
+    public static final String COOKIE_SPEC           = "http.cookie-spec"; 
+    public static final String COOKIE_ORIGIN         = "http.cookie-origin"; 
+    public static final String CREDS_PROVIDER        = "http.auth.credentials-provider"; 
+    public static final String TARGET_AUTH_STATE     = "http.auth.target-scope"; 
+    public static final String PROXY_AUTH_STATE      = "http.auth.proxy-scope";
+    public static final String AUTH_SCHEME_PREF      = "http.auth.scheme-pref";
+    public static final String USER_TOKEN            = "http.user-token";
+    
+}
diff --git a/src/org/apache/http/client/protocol/ClientContextConfigurer.java b/src/org/apache/http/client/protocol/ClientContextConfigurer.java
new file mode 100644
index 0000000..f2ced63
--- /dev/null
+++ b/src/org/apache/http/client/protocol/ClientContextConfigurer.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/protocol/ClientContextConfigurer.java $
+ * $Revision: 654886 $
+ * $Date: 2008-05-09 10:06:12 -0700 (Fri, 09 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.protocol;
+
+import java.util.List;
+
+import org.apache.http.auth.AuthSchemeRegistry;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.cookie.CookieSpecRegistry;
+import org.apache.http.protocol.HttpContext;
+
+public class ClientContextConfigurer implements ClientContext {
+    
+    private final HttpContext context;
+
+    public ClientContextConfigurer (final HttpContext context) {
+        if (context == null)
+            throw new IllegalArgumentException("HTTP context may not be null");
+        this.context = context;
+    }
+
+    public void setCookieSpecRegistry(final CookieSpecRegistry registry) {
+        this.context.setAttribute(COOKIESPEC_REGISTRY, registry);
+    }
+    
+    public void setAuthSchemeRegistry(final AuthSchemeRegistry registry) {
+        this.context.setAttribute(AUTHSCHEME_REGISTRY, registry);
+    }
+    
+    public void setCookieStore(final CookieStore store) {
+        this.context.setAttribute(COOKIE_STORE, store);
+    }
+
+    public void setCredentialsProvider(final CredentialsProvider provider) {
+        this.context.setAttribute(CREDS_PROVIDER, provider);
+    }
+
+    public void setAuthSchemePref(final List<String> list) {
+        this.context.setAttribute(AUTH_SCHEME_PREF, list);
+    }
+
+}
diff --git a/src/org/apache/http/client/protocol/RequestAddCookies.java b/src/org/apache/http/client/protocol/RequestAddCookies.java
new file mode 100644
index 0000000..0de8c40
--- /dev/null
+++ b/src/org/apache/http/client/protocol/RequestAddCookies.java
@@ -0,0 +1,192 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/protocol/RequestAddCookies.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.protocol;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.ProtocolException;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.params.HttpClientParams;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.CookieSpecRegistry;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.ExecutionContext;
+
+/**
+ * Request interceptor that matches cookies available in the current
+ * {@link CookieStore} to the request being executed and generates 
+ * corresponding cookierequest headers.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 673450 $
+ * 
+ * @since 4.0
+ */
+public class RequestAddCookies implements HttpRequestInterceptor {
+
+    private final Log log = LogFactory.getLog(getClass());
+    
+    public RequestAddCookies() {
+        super();
+    }
+    
+    public void process(final HttpRequest request, final HttpContext context) 
+            throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+        
+        // Obtain cookie store
+        CookieStore cookieStore = (CookieStore) context.getAttribute(
+                ClientContext.COOKIE_STORE);
+        if (cookieStore == null) {
+            this.log.info("Cookie store not available in HTTP context");
+            return;
+        }
+        
+        // Obtain the registry of cookie specs
+        CookieSpecRegistry registry= (CookieSpecRegistry) context.getAttribute(
+                ClientContext.COOKIESPEC_REGISTRY);
+        if (registry == null) {
+            this.log.info("CookieSpec registry not available in HTTP context");
+            return;
+        }
+        
+        // Obtain the target host (required)
+        HttpHost targetHost = (HttpHost) context.getAttribute(
+                ExecutionContext.HTTP_TARGET_HOST);
+        if (targetHost == null) {
+            throw new IllegalStateException("Target host not specified in HTTP context");
+        }
+        
+        // Obtain the client connection (required)
+        ManagedClientConnection conn = (ManagedClientConnection) context.getAttribute(
+                ExecutionContext.HTTP_CONNECTION);
+        if (conn == null) {
+            throw new IllegalStateException("Client connection not specified in HTTP context");
+        }
+
+        String policy = HttpClientParams.getCookiePolicy(request.getParams());
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("CookieSpec selected: " + policy);
+        }
+        
+        URI requestURI;
+        if (request instanceof HttpUriRequest) {
+            requestURI = ((HttpUriRequest) request).getURI();
+        } else {
+            try {
+                requestURI = new URI(request.getRequestLine().getUri());
+            } catch (URISyntaxException ex) {
+                throw new ProtocolException("Invalid request URI: " + 
+                        request.getRequestLine().getUri(), ex);
+            }
+        }
+        
+        String hostName = targetHost.getHostName();
+        int port = targetHost.getPort();
+        if (port < 0) {
+            port = conn.getRemotePort();
+        }
+        
+        CookieOrigin cookieOrigin = new CookieOrigin(
+                hostName, 
+                port, 
+                requestURI.getPath(),
+                conn.isSecure());
+        
+        // Get an instance of the selected cookie policy
+        CookieSpec cookieSpec = registry.getCookieSpec(policy, request.getParams());
+        // Get all cookies available in the HTTP state
+        List<Cookie> cookies = new ArrayList<Cookie>(cookieStore.getCookies());
+        // Find cookies matching the given origin
+        List<Cookie> matchedCookies = new ArrayList<Cookie>();
+        for (Cookie cookie : cookies) {
+            if (cookieSpec.match(cookie, cookieOrigin)) {
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("Cookie " + cookie + " match " + cookieOrigin);
+                }
+                matchedCookies.add(cookie);
+            }
+        }
+        // Generate Cookie request headers
+        if (!matchedCookies.isEmpty()) {
+            List<Header> headers = cookieSpec.formatCookies(matchedCookies);
+            for (Header header : headers) {
+                request.addHeader(header);
+            }
+        }
+        
+        int ver = cookieSpec.getVersion();
+        if (ver > 0) {
+            boolean needVersionHeader = false;
+            for (Cookie cookie : matchedCookies) {
+                if (ver != cookie.getVersion()) {
+                    needVersionHeader = true;
+                }
+            }
+
+            if (needVersionHeader) {
+                Header header = cookieSpec.getVersionHeader();
+                if (header != null) {
+                    // Advertise cookie version support
+                    request.addHeader(header);
+                }
+            }
+        }
+        
+        // Stick the CookieSpec and CookieOrigin instances to the HTTP context
+        // so they could be obtained by the response interceptor
+        context.setAttribute(ClientContext.COOKIE_SPEC, cookieSpec);
+        context.setAttribute(ClientContext.COOKIE_ORIGIN, cookieOrigin);
+    }
+    
+}
diff --git a/src/org/apache/http/client/protocol/RequestDefaultHeaders.java b/src/org/apache/http/client/protocol/RequestDefaultHeaders.java
new file mode 100644
index 0000000..27d5cc7
--- /dev/null
+++ b/src/org/apache/http/client/protocol/RequestDefaultHeaders.java
@@ -0,0 +1,74 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/protocol/RequestDefaultHeaders.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.protocol;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * Request interceptor that adds default request headers.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 653041 $
+ * 
+ * @since 4.0
+ */
+public class RequestDefaultHeaders implements HttpRequestInterceptor {
+
+    public RequestDefaultHeaders() {
+        super();
+    }
+    
+    public void process(final HttpRequest request, final HttpContext context) 
+            throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        // Add default headers
+        Collection<?> defHeaders = (Collection<?>) request.getParams().getParameter(
+                ClientPNames.DEFAULT_HEADERS);
+        if (defHeaders != null) {
+            for (Object defHeader : defHeaders) {
+                request.addHeader((Header) defHeader);
+            }
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/client/protocol/RequestProxyAuthentication.java b/src/org/apache/http/client/protocol/RequestProxyAuthentication.java
new file mode 100644
index 0000000..b4dfe76
--- /dev/null
+++ b/src/org/apache/http/client/protocol/RequestProxyAuthentication.java
@@ -0,0 +1,104 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/protocol/RequestProxyAuthentication.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.protocol;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthState;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.Credentials;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 673450 $
+ * 
+ * @since 4.0
+ */
+public class RequestProxyAuthentication implements HttpRequestInterceptor {
+
+    private final Log log = LogFactory.getLog(getClass());
+    
+    public RequestProxyAuthentication() {
+        super();
+    }
+    
+    public void process(final HttpRequest request, final HttpContext context) 
+            throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+
+        if (request.containsHeader(AUTH.PROXY_AUTH_RESP)) {
+            return;
+        }
+        
+        // Obtain authentication state
+        AuthState authState = (AuthState) context.getAttribute(
+                ClientContext.PROXY_AUTH_STATE);
+        if (authState == null) {
+            return;
+        }
+
+        AuthScheme authScheme = authState.getAuthScheme();
+        if (authScheme == null) {
+            return;
+        }
+        
+        Credentials creds = authState.getCredentials();
+        if (creds == null) {
+            this.log.debug("User credentials not available");
+            return;
+        }
+        if (authState.getAuthScope() != null || !authScheme.isConnectionBased()) {
+            try {
+                request.addHeader(authScheme.authenticate(creds, request));
+            } catch (AuthenticationException ex) {
+                if (this.log.isErrorEnabled()) {
+                    this.log.error("Proxy authentication error: " + ex.getMessage());
+                }
+            }
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/client/protocol/RequestTargetAuthentication.java b/src/org/apache/http/client/protocol/RequestTargetAuthentication.java
new file mode 100644
index 0000000..c140183
--- /dev/null
+++ b/src/org/apache/http/client/protocol/RequestTargetAuthentication.java
@@ -0,0 +1,105 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/protocol/RequestTargetAuthentication.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.protocol;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthState;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.Credentials;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 673450 $
+ * 
+ * @since 4.0
+ */
+public class RequestTargetAuthentication implements HttpRequestInterceptor {
+
+    private final Log log = LogFactory.getLog(getClass());
+    
+    public RequestTargetAuthentication() {
+        super();
+    }
+    
+    public void process(final HttpRequest request, final HttpContext context) 
+            throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+
+        if (request.containsHeader(AUTH.WWW_AUTH_RESP)) {
+            return;
+        }
+        
+        // Obtain authentication state
+        AuthState authState = (AuthState) context.getAttribute(
+                ClientContext.TARGET_AUTH_STATE);
+        if (authState == null) {
+            return;
+        }
+
+        AuthScheme authScheme = authState.getAuthScheme();
+        if (authScheme == null) {
+            return;
+        }
+        
+        Credentials creds = authState.getCredentials();
+        if (creds == null) {
+            this.log.debug("User credentials not available");
+            return;
+        }
+
+        if (authState.getAuthScope() != null || !authScheme.isConnectionBased()) {
+            try {
+                request.addHeader(authScheme.authenticate(creds, request));
+            } catch (AuthenticationException ex) {
+                if (this.log.isErrorEnabled()) {
+                    this.log.error("Authentication error: " + ex.getMessage());
+                }
+            }
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/client/protocol/ResponseProcessCookies.java b/src/org/apache/http/client/protocol/ResponseProcessCookies.java
new file mode 100644
index 0000000..0689e93
--- /dev/null
+++ b/src/org/apache/http/client/protocol/ResponseProcessCookies.java
@@ -0,0 +1,146 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/protocol/ResponseProcessCookies.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.protocol;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.client.CookieStore;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SM;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * Response interceptor that populates the current {@link CookieStore} with data 
+ * contained in response cookies received in the given the HTTP response.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 673450 $
+ * 
+ * @since 4.0
+ */
+public class ResponseProcessCookies implements HttpResponseInterceptor {
+
+    private final Log log = LogFactory.getLog(getClass());
+    
+    public ResponseProcessCookies() {
+        super();
+    }
+    
+    public void process(final HttpResponse response, final HttpContext context) 
+            throws HttpException, IOException {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+        
+        // Obtain cookie store
+        CookieStore cookieStore = (CookieStore) context.getAttribute(
+                ClientContext.COOKIE_STORE);
+        if (cookieStore == null) {
+            this.log.info("Cookie store not available in HTTP context");
+            return;
+        }
+        // Obtain actual CookieSpec instance
+        CookieSpec cookieSpec = (CookieSpec) context.getAttribute(
+                ClientContext.COOKIE_SPEC);
+        if (cookieSpec == null) {
+            this.log.info("CookieSpec not available in HTTP context");
+            return;
+        }
+        // Obtain actual CookieOrigin instance
+        CookieOrigin cookieOrigin = (CookieOrigin) context.getAttribute(
+                ClientContext.COOKIE_ORIGIN);
+        if (cookieOrigin == null) {
+            this.log.info("CookieOrigin not available in HTTP context");
+            return;
+        }
+        HeaderIterator it = response.headerIterator(SM.SET_COOKIE);
+        processCookies(it, cookieSpec, cookieOrigin, cookieStore);
+        
+        // see if the cookie spec supports cookie versioning.
+        if (cookieSpec.getVersion() > 0) {
+            // process set-cookie2 headers.
+            // Cookie2 will replace equivalent Cookie instances
+            it = response.headerIterator(SM.SET_COOKIE2);
+            processCookies(it, cookieSpec, cookieOrigin, cookieStore);
+        }
+    }
+     
+    private void processCookies(
+            final HeaderIterator iterator, 
+            final CookieSpec cookieSpec,
+            final CookieOrigin cookieOrigin,
+            final CookieStore cookieStore) {
+        while (iterator.hasNext()) {
+            Header header = iterator.nextHeader();
+            try {
+                List<Cookie> cookies = cookieSpec.parse(header, cookieOrigin);
+                for (Cookie cookie : cookies) {
+                    try {
+                        cookieSpec.validate(cookie, cookieOrigin);
+                        cookieStore.addCookie(cookie);
+
+                        if (this.log.isDebugEnabled()) {
+                            this.log.debug("Cookie accepted: \""
+                                    + cookie + "\". ");
+                        }
+                    } catch (MalformedCookieException ex) {
+                        if (this.log.isWarnEnabled()) {
+                            this.log.warn("Cookie rejected: \""
+                                    + cookie + "\". " + ex.getMessage());
+                        }
+                    }
+                }
+            } catch (MalformedCookieException ex) {
+                if (this.log.isWarnEnabled()) {
+                    this.log.warn("Invalid cookie header: \""
+                            + header + "\". " + ex.getMessage());
+                }
+            }
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/client/protocol/package.html b/src/org/apache/http/client/protocol/package.html
new file mode 100644
index 0000000..43dd0d6
--- /dev/null
+++ b/src/org/apache/http/client/protocol/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/protocol/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Additional request and response interceptors.
+
+</body>
+</html>
diff --git a/src/org/apache/http/client/utils/CloneUtils.java b/src/org/apache/http/client/utils/CloneUtils.java
new file mode 100644
index 0000000..fec534b
--- /dev/null
+++ b/src/org/apache/http/client/utils/CloneUtils.java
@@ -0,0 +1,75 @@
+/*
+ * $HeadURL:$
+ * $Revision:$
+ * $Date:$
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.client.utils;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * A collection of utilities to workaround limitations of Java clone framework.
+ */
+public class CloneUtils {
+
+    public static Object clone(final Object obj) throws CloneNotSupportedException {
+        if (obj == null) {
+            return null;
+        }
+        if (obj instanceof Cloneable) {
+            Class<?> clazz = obj.getClass ();
+            Method m;
+            try {
+                m = clazz.getMethod("clone", (Class[]) null);
+            } catch (NoSuchMethodException ex) {
+                throw new NoSuchMethodError(ex.getMessage());
+            }
+            try {
+                return m.invoke(obj, (Object []) null);
+            } catch (InvocationTargetException ex) {
+                Throwable cause = ex.getCause();
+                if (cause instanceof CloneNotSupportedException) {
+                    throw ((CloneNotSupportedException) cause); 
+                } else {
+                    throw new Error("Unexpected exception", cause);
+                }
+            } catch (IllegalAccessException ex) {
+                throw new IllegalAccessError(ex.getMessage());
+            }
+        } else {
+            throw new CloneNotSupportedException();
+        }
+    }
+    
+    /**
+     * This class should not be instantiated.
+     */
+    private CloneUtils() {
+    }
+
+}
diff --git a/src/org/apache/http/client/utils/URIUtils.java b/src/org/apache/http/client/utils/URIUtils.java
new file mode 100644
index 0000000..1cbb9af
--- /dev/null
+++ b/src/org/apache/http/client/utils/URIUtils.java
@@ -0,0 +1,209 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/utils/URIUtils.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.client.utils;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.http.HttpHost;
+
+/**
+ * A collection of utilities for {@link URI URIs}, to workaround
+ * bugs within the class or for ease-of-use features.
+ */
+public class URIUtils {
+
+     /**
+         * Constructs a {@link URI} using all the parameters. This should be
+         * used instead of
+         * {@link URI#URI(String, String, String, int, String, String, String)}
+         * or any of the other URI multi-argument URI constructors.
+         * 
+         * See <a
+         * href="https://issues.apache.org/jira/browse/HTTPCLIENT-730">HTTPCLIENT-730</a>
+         * for more information.
+         * 
+         * @param scheme
+         *            Scheme name
+         * @param host
+         *            Host name
+         * @param port
+         *            Port number
+         * @param path
+         *            Path
+         * @param query
+         *            Query
+         * @param fragment
+         *            Fragment
+         * 
+         * @throws URISyntaxException
+         *             If both a scheme and a path are given but the path is
+         *             relative, if the URI string constructed from the given
+         *             components violates RFC&nbsp;2396, or if the authority
+         *             component of the string is present but cannot be parsed
+         *             as a server-based authority
+         */
+    public static URI createURI(
+            final String scheme,
+            final String host,
+            int port,
+            final String path,
+            final String query,
+            final String fragment) throws URISyntaxException {
+        
+        StringBuilder buffer = new StringBuilder();
+        if (host != null) {
+            if (scheme != null) {
+                buffer.append(scheme);
+                buffer.append("://");
+            }
+            buffer.append(host);
+            if (port > 0) {
+                buffer.append(':');
+                buffer.append(port);
+            }
+        }
+        if (path == null || !path.startsWith("/")) {
+            buffer.append('/');
+        }
+        if (path != null) {
+            buffer.append(path);
+        }
+        if (query != null) {
+            buffer.append('?');
+            buffer.append(query);
+        }
+        if (fragment != null) {
+            buffer.append('#');
+            buffer.append(fragment);
+        }
+        return new URI(buffer.toString());
+    }
+
+    /**
+     * A convenience method for creating a new {@link URI} whose scheme, host
+     * and port are taken from the target host, but whose path, query and
+     * fragment are taken from the existing URI. The fragment is only used if
+     * dropFragment is false.
+     * 
+     * @param uri
+     *            Contains the path, query and fragment to use.
+     * @param target
+     *            Contains the scheme, host and port to use.
+     * @param dropFragment
+     *            True if the fragment should not be copied.
+     * 
+     * @throws URISyntaxException
+     *             If the resulting URI is invalid.
+     */
+    public static URI rewriteURI(
+            final URI uri, 
+            final HttpHost target,
+            boolean dropFragment) throws URISyntaxException {
+        if (uri == null) {
+            throw new IllegalArgumentException("URI may nor be null");
+        }
+        if (target != null) {
+            return URIUtils.createURI(
+                    target.getSchemeName(), 
+                    target.getHostName(), 
+                    target.getPort(), 
+                    uri.getRawPath(), 
+                    uri.getRawQuery(), 
+                    dropFragment ? null : uri.getRawFragment());
+        } else {
+            return URIUtils.createURI(
+                    null, 
+                    null, 
+                    -1, 
+                    uri.getRawPath(), 
+                    uri.getRawQuery(), 
+                    dropFragment ? null : uri.getRawFragment());
+        }
+    }
+    
+    /**
+     * A convenience method for
+     * {@link URIUtils#rewriteURI(URI, HttpHost, boolean)} that always keeps the
+     * fragment.
+     */
+    public static URI rewriteURI(
+            final URI uri, 
+            final HttpHost target) throws URISyntaxException {
+        return rewriteURI(uri, target, false);
+    }
+    
+    /**
+     * Resolves a URI reference against a base URI. Work-around for bug in
+     * java.net.URI (<http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4708535>)
+     *
+     * @param baseURI the base URI
+     * @param reference the URI reference
+     * @return the resulting URI
+     */
+    public static URI resolve(final URI baseURI, final String reference) {
+        return URIUtils.resolve(baseURI, URI.create(reference));
+    }
+
+    /**
+     * Resolves a URI reference against a base URI. Work-around for bug in
+     * java.net.URI (<http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4708535>)
+     *
+     * @param baseURI the base URI
+     * @param reference the URI reference
+     * @return the resulting URI
+     */
+    public static URI resolve(final URI baseURI, URI reference){
+        if (baseURI == null) {
+            throw new IllegalArgumentException("Base URI may nor be null");
+        }
+        if (reference == null) {
+            throw new IllegalArgumentException("Reference URI may nor be null");
+        }
+        boolean emptyReference = reference.toString().length() == 0;
+        if (emptyReference) {
+            reference = URI.create("#");
+        }
+        URI resolved = baseURI.resolve(reference);
+        if (emptyReference) {
+            String resolvedString = resolved.toString();
+            resolved = URI.create(resolvedString.substring(0,
+                resolvedString.indexOf('#')));
+        }
+        return resolved;
+    }
+
+    /**
+     * This class should not be instantiated.
+     */
+    private URIUtils() {
+    }
+
+}
diff --git a/src/org/apache/http/client/utils/URLEncodedUtils.java b/src/org/apache/http/client/utils/URLEncodedUtils.java
new file mode 100644
index 0000000..8b08f90
--- /dev/null
+++ b/src/org/apache/http/client/utils/URLEncodedUtils.java
@@ -0,0 +1,191 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/utils/URLEncodedUtils.java $
+ * $Revision: 655107 $
+ * $Date: 2008-05-10 08:20:42 -0700 (Sat, 10 May 2008) $
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.utils;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Scanner;
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.EntityUtils;
+
+/**
+ * A collection of utilities for encoding URLs.
+ */
+public class URLEncodedUtils {
+
+    public static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
+    private static final String PARAMETER_SEPARATOR = "&";
+    private static final String NAME_VALUE_SEPARATOR = "=";
+
+    /**
+     * Returns a list of {@link NameValuePair NameValuePairs} as built from the
+     * URI's query portion. For example, a URI of
+     * http://example.org/path/to/file?a=1&b=2&c=3 would return a list of three
+     * NameValuePairs, one for a=1, one for b=2, and one for c=3.
+     * <p>
+     * This is typically useful while parsing an HTTP PUT.
+     * 
+     * @param uri
+     *            uri to parse
+     * @param encoding
+     *            encoding to use while parsing the query
+     */
+    public static List <NameValuePair> parse (final URI uri, final String encoding) {
+        List <NameValuePair> result = Collections.emptyList();
+        final String query = uri.getRawQuery();
+        if (query != null && query.length() > 0) {
+            result = new ArrayList <NameValuePair>();
+            parse(result, new Scanner(query), encoding);
+        }
+        return result;
+    }
+
+    /**
+     * Returns a list of {@link NameValuePair NameValuePairs} as parsed from an
+     * {@link HttpEntity}. The encoding is taken from the entity's
+     * Content-Encoding header.
+     * <p>
+     * This is typically used while parsing an HTTP POST.
+     * 
+     * @param entity
+     *            The entity to parse
+     * @throws IOException
+     *             If there was an exception getting the entity's data.
+     */
+    public static List <NameValuePair> parse (
+            final HttpEntity entity) throws IOException {
+        List <NameValuePair> result = Collections.emptyList();
+        if (isEncoded(entity)) {
+            final String content = EntityUtils.toString(entity);
+            final Header encoding = entity.getContentEncoding();
+            if (content != null && content.length() > 0) {
+                result = new ArrayList <NameValuePair>();
+                parse(result, new Scanner(content), 
+                        encoding != null ? encoding.getValue() : null);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns true if the entity's Content-Type header is
+     * <code>application/x-www-form-urlencoded</code>.
+     */
+    public static boolean isEncoded (final HttpEntity entity) {
+        final Header contentType = entity.getContentType();
+        return (contentType != null && contentType.getValue().equalsIgnoreCase(CONTENT_TYPE));
+    }
+
+    /**
+     * Adds all parameters within the Scanner to the list of
+     * <code>parameters</code>, as encoded by <code>encoding</code>. For
+     * example, a scanner containing the string <code>a=1&b=2&c=3</code> would
+     * add the {@link NameValuePair NameValuePairs} a=1, b=2, and c=3 to the
+     * list of parameters.
+     * 
+     * @param parameters
+     *            List to add parameters to.
+     * @param scanner
+     *            Input that contains the parameters to parse.
+     * @param encoding
+     *            Encoding to use when decoding the parameters.
+     */
+    public static void parse (
+            final List <NameValuePair> parameters, 
+            final Scanner scanner, 
+            final String encoding) {
+        scanner.useDelimiter(PARAMETER_SEPARATOR);
+        while (scanner.hasNext()) {
+            final String[] nameValue = scanner.next().split(NAME_VALUE_SEPARATOR);
+            if (nameValue.length == 0 || nameValue.length > 2)
+                throw new IllegalArgumentException("bad parameter");
+
+            final String name = decode(nameValue[0], encoding);
+            String value = null;
+            if (nameValue.length == 2)
+                value = decode(nameValue[1], encoding);
+            parameters.add(new BasicNameValuePair(name, value));
+        }
+    }
+
+    /**
+     * Returns a String that is suitable for use as an <code>application/x-www-form-urlencoded</code>
+     * list of parameters in an HTTP PUT or HTTP POST.
+     * 
+     * @param parameters  The parameters to include.
+     * @param encoding The encoding to use.
+     */
+    public static String format (
+            final List <? extends NameValuePair> parameters, 
+            final String encoding) {
+        final StringBuilder result = new StringBuilder();
+        for (final NameValuePair parameter : parameters) {
+            final String encodedName = encode(parameter.getName(), encoding);
+            final String value = parameter.getValue();
+            final String encodedValue = value != null ? encode(value, encoding) : "";
+            if (result.length() > 0)
+                result.append(PARAMETER_SEPARATOR);
+            result.append(encodedName);
+            result.append(NAME_VALUE_SEPARATOR);
+            result.append(encodedValue);
+        }
+        return result.toString();
+    }
+
+    private static String decode (final String content, final String encoding) {
+        try {
+            return URLDecoder.decode(content, 
+                    encoding != null ? encoding : HTTP.DEFAULT_CONTENT_CHARSET);
+        } catch (UnsupportedEncodingException problem) {
+            throw new IllegalArgumentException(problem);
+        }
+    }
+
+    private static String encode (final String content, final String encoding) {
+        try {
+            return URLEncoder.encode(content, 
+                    encoding != null ? encoding : HTTP.DEFAULT_CONTENT_CHARSET);
+        } catch (UnsupportedEncodingException problem) {
+            throw new IllegalArgumentException(problem);
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/client/utils/package.html b/src/org/apache/http/client/utils/package.html
new file mode 100644
index 0000000..7abeb93
--- /dev/null
+++ b/src/org/apache/http/client/utils/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/utils/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Helpers and utility classes for <i>HttpClient</i>.
+
+</body>
+</html>
diff --git a/src/org/apache/http/conn/BasicEofSensorWatcher.java b/src/org/apache/http/conn/BasicEofSensorWatcher.java
new file mode 100644
index 0000000..9a9f3c5
--- /dev/null
+++ b/src/org/apache/http/conn/BasicEofSensorWatcher.java
@@ -0,0 +1,121 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/BasicEofSensorWatcher.java $
+ * $Revision $
+ * $Date: 2008-06-27 12:49:20 -0700 (Fri, 27 Jun 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+
+/**
+ * Basic implementation of {@link EofSensorWatcher EofSensorWatcher}.
+ * The underlying connection is released on close or EOF.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 672367 $
+ *
+ * @since 4.0
+ */
+public class BasicEofSensorWatcher implements EofSensorWatcher {
+
+
+    /** The connection to auto-release. */
+    protected ManagedClientConnection managedConn;
+
+    /** Whether to keep the connection alive. */
+    protected boolean attemptReuse;
+
+
+
+    /**
+     * Creates a new watcher for auto-releasing a connection.
+     *
+     * @param conn      the connection to auto-release
+     * @param reuse     whether the connection should be re-used
+     */
+    public BasicEofSensorWatcher(ManagedClientConnection conn,
+                                 boolean reuse) {
+        if (conn == null)
+            throw new IllegalArgumentException
+                ("Connection may not be null.");
+
+        managedConn = conn;
+        attemptReuse = reuse;
+    }
+
+
+    // non-javadoc, see interface EofSensorWatcher
+    public boolean eofDetected(InputStream wrapped)
+        throws IOException {
+
+        try {
+            if (attemptReuse) {
+                // there may be some cleanup required, such as
+                // reading trailers after the response body:
+                wrapped.close();
+                managedConn.markReusable();
+            }
+        } finally {
+            managedConn.releaseConnection();
+        }
+        return false;
+    }
+
+
+    // non-javadoc, see interface EofSensorWatcher
+    public boolean streamClosed(InputStream wrapped)
+        throws IOException {
+
+        try {
+            if (attemptReuse) {
+                // this assumes that closing the stream will
+                // consume the remainder of the response body:
+                wrapped.close();
+                managedConn.markReusable();
+            }
+        } finally {
+            managedConn.releaseConnection();
+        }
+        return false;
+    }
+
+
+    // non-javadoc, see interface EofSensorWatcher
+    public boolean streamAbort(InputStream wrapped)
+        throws IOException {
+
+        managedConn.abortConnection();
+        return false;
+    }
+
+} // class BasicEofSensorWatcher
diff --git a/src/org/apache/http/conn/BasicManagedEntity.java b/src/org/apache/http/conn/BasicManagedEntity.java
new file mode 100644
index 0000000..9719e1a
--- /dev/null
+++ b/src/org/apache/http/conn/BasicManagedEntity.java
@@ -0,0 +1,220 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/BasicManagedEntity.java $
+ * $Revision $
+ * $Date: 2008-06-27 12:49:20 -0700 (Fri, 27 Jun 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.entity.HttpEntityWrapper;
+
+
+/**
+ * An entity that releases a {@link ManagedClientConnection connection}.
+ * A {@link ManagedClientConnection} will
+ * typically <i>not</i> return a managed entity, but you can replace
+ * the unmanaged entity in the response with a managed one.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 672367 $
+ *
+ * @since 4.0
+ */
+public class BasicManagedEntity extends HttpEntityWrapper
+    implements ConnectionReleaseTrigger, EofSensorWatcher {
+
+    /** The connection to release. */
+    protected ManagedClientConnection managedConn;
+
+    /** Whether to keep the connection alive. */
+    protected final boolean attemptReuse;
+
+
+    /**
+     * Creates a new managed entity that can release a connection.
+     *
+     * @param entity    the entity of which to wrap the content.
+     *                  Note that the argument entity can no longer be used
+     *                  afterwards, since the content will be taken by this
+     *                  managed entity.
+     * @param conn      the connection to release
+     * @param reuse     whether the connection should be re-used
+     */
+    public BasicManagedEntity(HttpEntity entity,
+                              ManagedClientConnection conn,
+                              boolean reuse) {
+        super(entity);
+
+        if (conn == null)
+            throw new IllegalArgumentException
+                ("Connection may not be null.");
+
+        this.managedConn = conn;
+        this.attemptReuse = reuse;
+    }
+
+
+    // non-javadoc, see interface HttpEntity
+    @Override
+    public boolean isRepeatable() {
+        return false;
+    }
+
+
+    // non-javadoc, see interface HttpEntity
+    @Override
+    public InputStream getContent() throws IOException {
+
+        return new EofSensorInputStream(wrappedEntity.getContent(), this);
+    }
+
+
+    // non-javadoc, see interface HttpEntity
+    @Override
+    public void consumeContent() throws IOException {
+
+        if (managedConn == null)
+            return;
+
+        try {
+            if (attemptReuse) {
+                // this will not trigger a callback from EofSensorInputStream
+                wrappedEntity.consumeContent();
+                managedConn.markReusable();
+            }
+        } finally {
+            releaseManagedConnection();
+        }
+    }
+
+    
+    // non-javadoc, see interface HttpEntity
+    @Override
+    public void writeTo(final OutputStream outstream) throws IOException {
+        super.writeTo(outstream);
+        consumeContent();
+    }
+
+
+    // non-javadoc, see interface ConnectionReleaseTrigger
+    public void releaseConnection()
+        throws IOException {
+
+        this.consumeContent();
+    }
+
+
+    // non-javadoc, see interface ConnectionReleaseTrigger
+    public void abortConnection()
+        throws IOException {
+
+        if (managedConn != null) {
+            try {
+                managedConn.abortConnection();
+            } finally {
+                managedConn = null;
+            }
+        }
+    }
+
+
+    // non-javadoc, see interface EofSensorWatcher
+    public boolean eofDetected(InputStream wrapped)
+        throws IOException {
+
+        try {
+            if (attemptReuse && (managedConn != null)) {
+                // there may be some cleanup required, such as
+                // reading trailers after the response body:
+                wrapped.close();
+                managedConn.markReusable();
+            }
+        } finally {
+            releaseManagedConnection();
+        }
+        return false;
+    }
+
+
+    // non-javadoc, see interface EofSensorWatcher
+    public boolean streamClosed(InputStream wrapped)
+        throws IOException {
+
+        try {
+            if (attemptReuse && (managedConn != null)) {
+                // this assumes that closing the stream will
+                // consume the remainder of the response body:
+                wrapped.close();
+                managedConn.markReusable();
+            }
+        } finally {
+            releaseManagedConnection();
+        }
+        return false;
+    }
+
+
+    // non-javadoc, see interface EofSensorWatcher
+    public boolean streamAbort(InputStream wrapped)
+        throws IOException {
+
+        if (managedConn != null) {
+            managedConn.abortConnection();
+        }
+        return false;
+    }
+
+
+    /**
+     * Releases the connection gracefully.
+     * The connection attribute will be nullified.
+     * Subsequent invocations are no-ops.
+     *
+     * @throws IOException      in case of an IO problem.
+     *         The connection attribute will be nullified anyway.
+     */
+    protected void releaseManagedConnection()
+        throws IOException {
+
+        if (managedConn != null) {
+            try {
+                managedConn.releaseConnection();
+            } finally {
+                managedConn = null;
+            }
+        }
+    }
+
+} // class BasicManagedEntity
diff --git a/src/org/apache/http/conn/ClientConnectionManager.java b/src/org/apache/http/conn/ClientConnectionManager.java
new file mode 100644
index 0000000..e3375e1
--- /dev/null
+++ b/src/org/apache/http/conn/ClientConnectionManager.java
@@ -0,0 +1,129 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionManager.java $
+ * $Revision: 671717 $
+ * $Date: 2008-06-25 21:03:24 -0700 (Wed, 25 Jun 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.scheme.SchemeRegistry;
+
+/**
+ * Management interface for {@link ManagedClientConnection client connections}.
+ * 
+ * @author Michael Becke
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version   $Revision: 671717 $
+ *
+ * @since 4.0
+ */
+public interface ClientConnectionManager {
+
+    /**
+     * Obtains the scheme registry used by this manager.
+     *
+     * @return  the scheme registry, never <code>null</code>
+     */
+    SchemeRegistry getSchemeRegistry()
+        ;
+
+    
+    /**
+     * Returns a new {@link ClientConnectionRequest}, from which a
+     * {@link ManagedClientConnection} can be obtained or the request can be
+     * aborted.
+     */
+    ClientConnectionRequest requestConnection(HttpRoute route, Object state)
+        ;
+
+
+    /**
+     * Releases a connection for use by others.
+     * You may optionally specify how long the connection is valid
+     * to be reused.  Values <= 0 are considered to be valid forever.
+     * If the connection is not marked as reusable, the connection will
+     * not be reused regardless of the valid duration.
+     * 
+     * If the connection has been released before,
+     * the call will be ignored.
+     *
+     * @param conn      the connection to release
+     * @param validDuration the duration of time this connection is valid for reuse
+     * @param timeUnit the unit of time validDuration is measured in
+     * 
+     * @see #closeExpiredConnections()
+     */
+    void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit)
+        ;
+
+
+    /**
+     * Closes idle connections in the pool.
+     * Open connections in the pool that have not been used for the
+     * timespan given by the argument will be closed.
+     * Currently allocated connections are not subject to this method.
+     * Times will be checked with milliseconds precision
+     * 
+     * All expired connections will also be closed.
+     * 
+     * @param idletime  the idle time of connections to be closed
+     * @param tunit     the unit for the <code>idletime</code>
+     * 
+     * @see #closeExpiredConnections()
+     */
+    void closeIdleConnections(long idletime, TimeUnit tunit)
+        ;
+
+    /**
+     * Closes all expired connections in the pool.
+     * Open connections in the pool that have not been used for
+     * the timespan defined when the connection was released will be closed.
+     * Currently allocated connections are not subject to this method.
+     * Times will be checked with milliseconds precision.
+     */
+    void closeExpiredConnections();
+
+    /**
+     * Shuts down this connection manager and releases allocated resources.
+     * This includes closing all connections, whether they are currently
+     * used or not.
+     */
+    void shutdown()
+        ;
+
+
+} // interface ClientConnectionManager
diff --git a/src/org/apache/http/conn/ClientConnectionManagerFactory.java b/src/org/apache/http/conn/ClientConnectionManagerFactory.java
new file mode 100644
index 0000000..4bedc4e
--- /dev/null
+++ b/src/org/apache/http/conn/ClientConnectionManagerFactory.java
@@ -0,0 +1,50 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionManagerFactory.java $
+ * $Revision: 652020 $
+ * $Date: 2008-04-27 14:23:31 -0700 (Sun, 27 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.params.HttpParams;
+
+/**
+ * A factory for creating new {@link ClientConnectionManager} instances.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public interface ClientConnectionManagerFactory {    
+
+    ClientConnectionManager newInstance(
+            HttpParams params, 
+            SchemeRegistry schemeRegistry);
+
+}
diff --git a/src/org/apache/http/conn/ClientConnectionOperator.java b/src/org/apache/http/conn/ClientConnectionOperator.java
new file mode 100644
index 0000000..980b867
--- /dev/null
+++ b/src/org/apache/http/conn/ClientConnectionOperator.java
@@ -0,0 +1,120 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionOperator.java $
+ * $Revision: 645850 $
+ * $Date: 2008-04-08 04:08:52 -0700 (Tue, 08 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+import org.apache.http.HttpHost;
+import org.apache.http.conn.scheme.SocketFactory;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+
+
+
+/**
+ * Interface for opening {@link OperatedClientConnection connections}.
+ * This interface encapsulates the logic to create sockets and to
+ * open or update the connection with the new socket.
+ * Implementations will most likely make use of
+ * {@link SocketFactory socket factories}.
+ * <br/>
+ * The methods in this interface allow the creation of plain and layered
+ * sockets. Creating a tunnelled connection through a proxy, however,
+ * is not within the scope of the operator.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version   $Revision: 645850 $ $Date: 2008-04-08 04:08:52 -0700 (Tue, 08 Apr 2008) $
+ *
+ * @since 4.0
+ */
+public interface ClientConnectionOperator {
+
+
+    /**
+     * Creates a new connection that can be operated.
+     *
+     * @return  a new, unopened connection for use with this operator
+     */
+    OperatedClientConnection createConnection()
+        ;
+
+
+    /**
+     * Opens a connection to the given target host.
+     *
+     * @param conn      the connection to open
+     * @param target    the target host to connect to
+     * @param local     the local address to route from, or
+     *                  <code>null</code> for the default
+     * @param context   the context for the connection
+     * @param params    the parameters for the connection
+     *
+     * @throws IOException      in case of a problem
+     */
+    void openConnection(OperatedClientConnection conn,
+                        HttpHost target,
+                        InetAddress local,
+                        HttpContext context,
+                        HttpParams params)
+        throws IOException
+        ;
+
+
+    /**
+     * Updates a connection with a layered secure connection.
+     * The typical use of this method is to update a tunnelled plain
+     * connection (HTTP) to a secure TLS/SSL connection (HTTPS).
+     *
+     * @param conn      the open connection to update
+     * @param target    the target host for the updated connection.
+     *                  The connection must already be open or tunnelled
+     *                  to the host and port, but the scheme of the target
+     *                  will be used to create a layered connection.
+     * @param context   the context for the connection
+     * @param params    the parameters for the updated connection
+     *
+     * @throws IOException      in case of a problem
+     */
+    void updateSecureConnection(OperatedClientConnection conn,
+                                HttpHost target,
+                                HttpContext context,
+                                HttpParams params)
+        throws IOException
+        ;
+
+
+} // interface ClientConnectionOperator
+
diff --git a/src/org/apache/http/conn/ClientConnectionRequest.java b/src/org/apache/http/conn/ClientConnectionRequest.java
new file mode 100644
index 0000000..6ba02d0
--- /dev/null
+++ b/src/org/apache/http/conn/ClientConnectionRequest.java
@@ -0,0 +1,73 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionRequest.java $
+ * $Revision: 651815 $
+ * $Date: 2008-04-26 04:14:43 -0700 (Sat, 26 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Encapsulates a request for a {@link ManagedClientConnection}.
+ */
+public interface ClientConnectionRequest {
+    
+    /**
+     * Obtains a connection within a given time.
+     * This method will block until a connection becomes available,
+     * the timeout expires, or the connection manager is
+     * {@link ClientConnectionManager#shutdown() shut down}.
+     * Timeouts are handled with millisecond precision.
+     * 
+     * If {@link #abortRequest()} is called while this is blocking or
+     * before this began, an {@link InterruptedException} will
+     * be thrown.
+     * 
+     * @param timeout   the timeout, 0 or negative for no timeout
+     * @param tunit     the unit for the <code>timeout</code>,
+     *                  may be <code>null</code> only if there is no timeout
+     *
+     * @return  a connection that can be used to communicate
+     *          along the given route
+     *
+     * @throws ConnectionPoolTimeoutException
+     *         in case of a timeout
+     * @throws InterruptedException
+     *         if the calling thread is interrupted while waiting
+     */
+    ManagedClientConnection getConnection(long timeout, TimeUnit tunit) 
+        throws InterruptedException, ConnectionPoolTimeoutException;
+    
+    /**
+     * Aborts the call to {@link #getConnection(long, TimeUnit)},
+     * causing it to throw an {@link InterruptedException}.
+     */
+    void abortRequest();
+
+}
diff --git a/src/org/apache/http/conn/ConnectTimeoutException.java b/src/org/apache/http/conn/ConnectTimeoutException.java
new file mode 100644
index 0000000..83a731a
--- /dev/null
+++ b/src/org/apache/http/conn/ConnectTimeoutException.java
@@ -0,0 +1,64 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ConnectTimeoutException.java $
+ * $Revision: 617645 $
+ * $Date: 2008-02-01 13:05:31 -0800 (Fri, 01 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.InterruptedIOException;
+
+/**
+ * A timeout while connecting to an HTTP server or waiting for an
+ * available connection from an HttpConnectionManager.
+ * 
+ * @author <a href="mailto:laura@lwerner.org">Laura Werner</a>
+ * 
+ * @since 4.0
+ */
+public class ConnectTimeoutException extends InterruptedIOException {
+
+    private static final long serialVersionUID = -4816682903149535989L;
+
+    /**
+     * Creates a ConnectTimeoutException with a <tt>null</tt> detail message.
+     */
+    public ConnectTimeoutException() {
+        super();
+    }
+
+    /**
+     * Creates a ConnectTimeoutException with the specified detail message.
+     * 
+     * @param message The exception detail message 
+     */
+    public ConnectTimeoutException(final String message) {
+        super(message);
+    }
+
+}
diff --git a/src/org/apache/http/conn/ConnectionKeepAliveStrategy.java b/src/org/apache/http/conn/ConnectionKeepAliveStrategy.java
new file mode 100644
index 0000000..27d56cb
--- /dev/null
+++ b/src/org/apache/http/conn/ConnectionKeepAliveStrategy.java
@@ -0,0 +1,71 @@
+/*
+ * $HeadURL: $
+ * $Revision: $
+ * $Date: $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.conn;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpResponse;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * Interface for deciding how long a connection can remain
+ * idle before being reused.
+ *
+ * @author <a href="mailto:sberlin at gmail.com">Sam Berlin</a>
+ *
+ *
+ * @version $Revision: $
+ * 
+ * @since 4.0
+ */
+public interface ConnectionKeepAliveStrategy {
+    
+    /**
+     * Returns the duration of time which this connection can be safely kept
+     * idle. If the connection is left idle for longer than this period of time,
+     * it MUST not reused. A value of 0 or less may be returned to indicate that
+     * there is no suitable suggestion.
+     * 
+     * When coupled with a {@link ConnectionReuseStrategy}, if
+     * {@link ConnectionReuseStrategy#keepAlive(HttpResponse, HttpContext)
+     * returns true, this allows you to control how long the reuse will last. If
+     * keepAlive returns false, this should have no meaningful impact
+     * 
+     * @param response
+     *            The last response received over the connection.
+     * @param context
+     *            the context in which the connection is being used.
+     * 
+     * @return the duration in ms for which it is safe to keep the connection
+     *         idle, or <=0 if no suggested duration.
+     */
+    long getKeepAliveDuration(HttpResponse response, HttpContext context);
+    
+}
diff --git a/src/org/apache/http/conn/ConnectionPoolTimeoutException.java b/src/org/apache/http/conn/ConnectionPoolTimeoutException.java
new file mode 100644
index 0000000..7d4985e
--- /dev/null
+++ b/src/org/apache/http/conn/ConnectionPoolTimeoutException.java
@@ -0,0 +1,62 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ConnectionPoolTimeoutException.java $
+ * $Revision: 505684 $
+ * $Date: 2007-02-10 04:40:02 -0800 (Sat, 10 Feb 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+/**
+ * A timeout while waiting for an available connection 
+ * from a connection manager.
+ * 
+ * @author <a href="mailto:laura@lwerner.org">Laura Werner</a>
+ * 
+ * @since 4.0
+ */
+public class ConnectionPoolTimeoutException extends ConnectTimeoutException {
+
+    private static final long serialVersionUID = -7898874842020245128L;
+
+    /**
+     * Creates a ConnectTimeoutException with a <tt>null</tt> detail message.
+     */
+    public ConnectionPoolTimeoutException() {
+        super();
+    }
+
+    /**
+     * Creates a ConnectTimeoutException with the specified detail message.
+     * 
+     * @param message The exception detail message 
+     */
+    public ConnectionPoolTimeoutException(String message) {
+        super(message);
+    }
+
+}
diff --git a/src/org/apache/http/conn/ConnectionReleaseTrigger.java b/src/org/apache/http/conn/ConnectionReleaseTrigger.java
new file mode 100644
index 0000000..a9ac12e
--- /dev/null
+++ b/src/org/apache/http/conn/ConnectionReleaseTrigger.java
@@ -0,0 +1,86 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ConnectionReleaseTrigger.java $
+ * $Revision: 672367 $
+ * $Date: 2008-06-27 12:49:20 -0700 (Fri, 27 Jun 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.IOException;
+
+
+/**
+ * Interface for releasing a connection.
+ * This can be implemented by various "trigger" objects which are
+ * associated with a connection, for example
+ * a {@link EofSensorInputStream stream}
+ * or an {@link BasicManagedEntity entity}
+ * or the {@link ManagedClientConnection connection} itself.
+ * <br/>
+ * The methods in this interface can safely be called multiple times.
+ * The first invocation releases the connection, subsequent calls
+ * are ignored.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 672367 $
+ *
+ * @since 4.0
+ */
+public interface ConnectionReleaseTrigger {
+
+    /**
+     * Releases the connection with the option of keep-alive. This is a
+     * "graceful" release and may cause IO operations for consuming the
+     * remainder of a response entity. Use
+     * {@link #abortConnection abortConnection} for a hard release. The
+     * connection may be reused as specified by the duration.
+     * 
+     * @throws IOException
+     *             in case of an IO problem. The connection will be released
+     *             anyway.
+     */
+    void releaseConnection()
+        throws IOException
+        ;
+
+    /**
+     * Releases the connection without the option of keep-alive.
+     * This is a "hard" release that implies a shutdown of the connection.
+     * Use {@link #releaseConnection releaseConnection} for a graceful release.
+     *
+     * @throws IOException      in case of an IO problem.
+     *         The connection will be released anyway.
+     */
+    void abortConnection()
+        throws IOException
+        ;
+
+
+} // interface ConnectionReleaseTrigger
diff --git a/src/org/apache/http/conn/EofSensorInputStream.java b/src/org/apache/http/conn/EofSensorInputStream.java
new file mode 100644
index 0000000..0e1b90e
--- /dev/null
+++ b/src/org/apache/http/conn/EofSensorInputStream.java
@@ -0,0 +1,326 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/EofSensorInputStream.java $
+ * $Revision: 672367 $
+ * $Date: 2008-06-27 12:49:20 -0700 (Fri, 27 Jun 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+
+/**
+ * A stream wrapper that triggers actions on {@link #close close()} and EOF.
+ * Primarily used to auto-release an underlying
+ * {@link ManagedClientConnection connection}
+ * when the response body is consumed or no longer needed.
+ *
+ * <p>
+ * This class is based on <code>AutoCloseInputStream</code> in HttpClient 3.1,
+ * but has notable differences. It does not allow mark/reset, distinguishes
+ * different kinds of event, and does not always close the underlying stream
+ * on EOF. That decision is left to the {@link EofSensorWatcher watcher}.
+ * </p>
+ *
+ * @see EofSensorWatcher EofSensorWatcher
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author Ortwin Glueck
+ * @author Eric Johnson
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 672367 $
+ *
+ * @since 4.0
+ */
+// don't use FilterInputStream as the base class, we'd have to
+// override markSupported(), mark(), and reset() to disable them
+public class EofSensorInputStream extends InputStream
+    implements ConnectionReleaseTrigger {
+
+    /**
+     * The wrapped input stream, while accessible.
+     * The value changes to <code>null</code> when the wrapped stream
+     * becomes inaccessible.
+     */
+    protected InputStream wrappedStream;
+
+
+    /**
+     * Indicates whether this stream itself is closed.
+     * If it isn't, but {@link #wrappedStream wrappedStream}
+     * is <code>null</code>, we're running in EOF mode.
+     * All read operations will indicate EOF without accessing 
+     * the underlying stream. After closing this stream, read
+     * operations will trigger an {@link IOException IOException}.
+     *
+     * @see #isReadAllowed isReadAllowed
+     */
+    private boolean selfClosed;
+
+    /** The watcher to be notified, if any. */
+    private EofSensorWatcher eofWatcher;
+
+
+    /**
+     * Creates a new EOF sensor.
+     * If no watcher is passed, the underlying stream will simply be
+     * closed when EOF is detected or {@link #close close} is called.
+     * Otherwise, the watcher decides whether the underlying stream
+     * should be closed before detaching from it.
+     *
+     * @param in        the wrapped stream
+     * @param watcher   the watcher for events, or <code>null</code> for
+     *                  auto-close behavior without notification
+     */
+    public EofSensorInputStream(final InputStream in,
+                                final EofSensorWatcher watcher) {
+        if (in == null) {
+            throw new IllegalArgumentException
+                ("Wrapped stream may not be null.");
+        }
+
+        wrappedStream = in;
+        selfClosed = false;
+        eofWatcher = watcher;
+    }
+
+
+    /**
+     * Checks whether the underlying stream can be read from.
+     *
+     * @return  <code>true</code> if the underlying stream is accessible,
+     *          <code>false</code> if this stream is in EOF mode and
+     *          detached from the underlying stream
+     *
+     * @throws IOException      if this stream is already closed
+     */
+    protected boolean isReadAllowed() throws IOException {
+        if (selfClosed) {
+            throw new IOException("Attempted read on closed stream.");
+        }
+        return (wrappedStream != null);
+    }
+
+
+    // non-javadoc, see base class InputStream
+    @Override
+    public int read() throws IOException {
+        int l = -1;
+
+        if (isReadAllowed()) {
+            try {
+                l = wrappedStream.read();
+                checkEOF(l);
+            } catch (IOException ex) {
+                checkAbort();
+                throw ex;
+            }
+        }
+
+        return l;
+    }
+
+
+    // non-javadoc, see base class InputStream
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        int l = -1;
+
+        if (isReadAllowed()) {
+            try {
+                l = wrappedStream.read(b,  off,  len);
+                checkEOF(l);
+            } catch (IOException ex) {
+                checkAbort();
+                throw ex;
+            }
+        }
+
+        return l;
+    }
+
+
+    // non-javadoc, see base class InputStream
+    @Override
+    public int read(byte[] b) throws IOException {
+        int l = -1;
+
+        if (isReadAllowed()) {
+            try {
+                l = wrappedStream.read(b);
+                checkEOF(l);
+            } catch (IOException ex) {
+                checkAbort();
+                throw ex;
+            }
+        }
+        return l;
+    }
+
+
+    // non-javadoc, see base class InputStream
+    @Override
+    public int available() throws IOException {
+        int a = 0; // not -1
+
+        if (isReadAllowed()) {
+            try {
+                a = wrappedStream.available();
+                // no checkEOF() here, available() can't trigger EOF
+            } catch (IOException ex) {
+                checkAbort();
+                throw ex;
+            }
+        }
+
+        return a;
+    }
+
+
+    // non-javadoc, see base class InputStream
+    @Override
+    public void close() throws IOException {
+        // tolerate multiple calls to close()
+        selfClosed = true;
+        checkClose();
+    }
+
+
+    /**
+     * Detects EOF and notifies the watcher.
+     * This method should only be called while the underlying stream is
+     * still accessible. Use {@link #isReadAllowed isReadAllowed} to
+     * check that condition.
+     * <br/>
+     * If EOF is detected, the watcher will be notified and this stream
+     * is detached from the underlying stream. This prevents multiple
+     * notifications from this stream.
+     *
+     * @param eof       the result of the calling read operation.
+     *                  A negative value indicates that EOF is reached.
+     *
+     * @throws IOException
+     *          in case of an IO problem on closing the underlying stream
+     */
+    protected void checkEOF(int eof) throws IOException {
+
+        if ((wrappedStream != null) && (eof < 0)) {
+            try {
+                boolean scws = true; // should close wrapped stream?
+                if (eofWatcher != null)
+                    scws = eofWatcher.eofDetected(wrappedStream);
+                if (scws)
+                    wrappedStream.close();
+            } finally {
+                wrappedStream = null;
+            }
+        }
+    }
+
+
+    /**
+     * Detects stream close and notifies the watcher.
+     * There's not much to detect since this is called by {@link #close close}.
+     * The watcher will only be notified if this stream is closed
+     * for the first time and before EOF has been detected.
+     * This stream will be detached from the underlying stream to prevent
+     * multiple notifications to the watcher.
+     *
+     * @throws IOException
+     *          in case of an IO problem on closing the underlying stream
+     */
+    protected void checkClose() throws IOException {
+
+        if (wrappedStream != null) {
+            try {
+                boolean scws = true; // should close wrapped stream?
+                if (eofWatcher != null)
+                    scws = eofWatcher.streamClosed(wrappedStream);
+                if (scws)
+                    wrappedStream.close();
+            } finally {
+                wrappedStream = null;
+            }
+        }
+    }
+
+
+    /**
+     * Detects stream abort and notifies the watcher.
+     * There's not much to detect since this is called by
+     * {@link #abortConnection abortConnection}.
+     * The watcher will only be notified if this stream is aborted
+     * for the first time and before EOF has been detected or the
+     * stream has been {@link #close closed} gracefully.
+     * This stream will be detached from the underlying stream to prevent
+     * multiple notifications to the watcher.
+     *
+     * @throws IOException
+     *          in case of an IO problem on closing the underlying stream
+     */
+    protected void checkAbort() throws IOException {
+
+        if (wrappedStream != null) {
+            try {
+                boolean scws = true; // should close wrapped stream?
+                if (eofWatcher != null)
+                    scws = eofWatcher.streamAbort(wrappedStream);
+                if (scws)
+                    wrappedStream.close();
+            } finally {
+                wrappedStream = null;
+            }
+        }
+    }
+
+
+    /**
+     * Same as {@link #close close()}.
+     */
+    public void releaseConnection() throws IOException {
+        this.close();
+    }
+
+    /**
+     * Aborts this stream.
+     * This is a special version of {@link #close close()} which prevents
+     * re-use of the underlying connection, if any. Calling this method
+     * indicates that there should be no attempt to read until the end of
+     * the stream.
+     */
+    public void abortConnection() throws IOException {
+        // tolerate multiple calls
+        selfClosed = true;
+        checkAbort();
+    }
+
+} // class EOFSensorInputStream
+
diff --git a/src/org/apache/http/conn/EofSensorWatcher.java b/src/org/apache/http/conn/EofSensorWatcher.java
new file mode 100644
index 0000000..837f8d9
--- /dev/null
+++ b/src/org/apache/http/conn/EofSensorWatcher.java
@@ -0,0 +1,112 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/EofSensorWatcher.java $
+ * $Revision: 552264 $
+ * $Date: 2007-07-01 02:37:47 -0700 (Sun, 01 Jul 2007) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+
+/**
+ * A watcher for {@link EofSensorInputStream EofSensorInputStream}.
+ * Each stream will notify it's watcher at most once.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 552264 $
+ *
+ * @since 4.0
+ */
+public interface EofSensorWatcher {
+
+    /**
+     * Indicates that EOF is detected.
+     *
+     * @param wrapped   the underlying stream which has reached EOF
+     *
+     * @return  <code>true</code> if <code>wrapped</code> should be closed,
+     *          <code>false</code> if it should be left alone
+     *
+     * @throws IOException
+     *         in case of an IO problem, for example if the watcher itself
+     *         closes the underlying stream. The caller will leave the
+     *         wrapped stream alone, as if <code>false</code> was returned.
+     */
+    boolean eofDetected(InputStream wrapped)
+        throws IOException
+        ;
+
+
+    /**
+     * Indicates that the {@link EofSensorInputStream stream} is closed.
+     * This method will be called only if EOF was <i>not</i> detected
+     * before closing. Otherwise, {@link #eofDetected eofDetected} is called.
+     *
+     * @param wrapped   the underlying stream which has not reached EOF
+     *
+     * @return  <code>true</code> if <code>wrapped</code> should be closed,
+     *          <code>false</code> if it should be left alone
+     *
+     * @throws IOException
+     *         in case of an IO problem, for example if the watcher itself
+     *         closes the underlying stream. The caller will leave the
+     *         wrapped stream alone, as if <code>false</code> was returned.
+     */
+    boolean streamClosed(InputStream wrapped)
+        throws IOException
+        ;
+
+
+    /**
+     * Indicates that the {@link EofSensorInputStream stream} is aborted.
+     * This method will be called only if EOF was <i>not</i> detected
+     * before aborting. Otherwise, {@link #eofDetected eofDetected} is called.
+     * <p/>
+     * This method will also be invoked when an input operation causes an
+     * IOException to be thrown to make sure the input stream gets shut down. 
+     *
+     * @param wrapped   the underlying stream which has not reached EOF
+     *
+     * @return  <code>true</code> if <code>wrapped</code> should be closed,
+     *          <code>false</code> if it should be left alone
+     *
+     * @throws IOException
+     *         in case of an IO problem, for example if the watcher itself
+     *         closes the underlying stream. The caller will leave the
+     *         wrapped stream alone, as if <code>false</code> was returned.
+     */
+    boolean streamAbort(InputStream wrapped)
+        throws IOException
+        ;
+
+
+} // interface EofSensorWatcher
diff --git a/src/org/apache/http/conn/HttpHostConnectException.java b/src/org/apache/http/conn/HttpHostConnectException.java
new file mode 100644
index 0000000..743ca77
--- /dev/null
+++ b/src/org/apache/http/conn/HttpHostConnectException.java
@@ -0,0 +1,57 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/HttpHostConnectException.java $
+ * $Revision: 652020 $
+ * $Date: 2008-04-27 14:23:31 -0700 (Sun, 27 Apr 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.net.ConnectException;
+
+import org.apache.http.HttpHost;
+
+/**
+ * A {@link ConnectException} that specifies the {@link HttpHost} that was
+ * being connected to.
+ */
+public class HttpHostConnectException extends ConnectException {
+
+    private static final long serialVersionUID = -3194482710275220224L;
+
+    private final HttpHost host;
+    
+    public HttpHostConnectException(final HttpHost host, final ConnectException cause) {
+        super("Connection to " + host + " refused");
+        this.host = host;
+        initCause(cause);
+    }
+
+    public HttpHost getHost() {
+        return this.host;
+    }
+    
+}
diff --git a/src/org/apache/http/conn/ManagedClientConnection.java b/src/org/apache/http/conn/ManagedClientConnection.java
new file mode 100644
index 0000000..f642cb9
--- /dev/null
+++ b/src/org/apache/http/conn/ManagedClientConnection.java
@@ -0,0 +1,261 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ManagedClientConnection.java $
+ * $Revision: 672969 $
+ * $Date: 2008-06-30 18:09:50 -0700 (Mon, 30 Jun 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLSession;
+
+import org.apache.http.HttpClientConnection;
+import org.apache.http.HttpInetConnection;
+import org.apache.http.HttpHost;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+
+import org.apache.http.conn.routing.HttpRoute;
+
+
+
+/**
+ * A client-side connection with advanced connection logic.
+ * Instances are typically obtained from a connection manager.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version   $Revision: 672969 $
+ *
+ * @since 4.0
+ */
+public interface ManagedClientConnection extends
+    HttpClientConnection, HttpInetConnection, ConnectionReleaseTrigger {
+
+
+    /**
+     * Indicates whether this connection is secure.
+     * The return value is well-defined only while the connection is open.
+     * It may change even while the connection is open.
+     *
+     * @return  <code>true</code> if this connection is secure,
+     *          <code>false</code> otherwise
+     */
+    boolean isSecure()
+        ;
+
+
+    /**
+     * Obtains the current route of this connection.
+     *
+     * @return  the route established so far, or
+     *          <code>null</code> if not connected
+     */
+    HttpRoute getRoute()
+        ;
+
+
+    /**
+     * Obtains the SSL session of the underlying connection, if any.
+     * If this connection is open, and the underlying socket is an
+     * {@link javax.net.ssl.SSLSocket SSLSocket}, the SSL session of
+     * that socket is obtained. This is a potentially blocking operation.
+     * <br/>
+     * <b>Note:</b> Whether the underlying socket is an SSL socket
+     * can not necessarily be determined via {@link #isSecure}.
+     * Plain sockets may be considered secure, for example if they are
+     * connected to a known host in the same network segment.
+     * On the other hand, SSL sockets may be considered insecure,
+     * for example depending on the chosen cipher suite.
+     *
+     * @return  the underlying SSL session if available,
+     *          <code>null</code> otherwise
+     */
+    SSLSession getSSLSession()
+        ;
+
+
+    /**
+     * Opens this connection according to the given route.
+     *
+     * @param route     the route along which to open. It will be opened to
+     *                  the first proxy if present, or directly to the target.
+     * @param context   the context for opening this connection
+     * @param params    the parameters for opening this connection
+     *
+     * @throws IOException      in case of a problem
+     */
+    void open(HttpRoute route, HttpContext context, HttpParams params)
+        throws IOException
+        ;
+
+
+    /**
+     * Indicates that a tunnel to the target has been established.
+     * The route is the one previously passed to {@link #open open}.
+     * Subsequently, {@link #layerProtocol layerProtocol} can be called
+     * to layer the TLS/SSL protocol on top of the tunnelled connection.
+     * <br/>
+     * <b>Note:</b> In HttpClient 3, a call to the corresponding method
+     * would automatically trigger the layering of the TLS/SSL protocol.
+     * This is not the case anymore, you can establish a tunnel without
+     * layering a new protocol over the connection.
+     *
+     * @param secure    <code>true</code> if the tunnel should be considered
+     *                  secure, <code>false</code> otherwise
+     * @param params    the parameters for tunnelling this connection
+     *
+     * @throws IOException  in case of a problem
+     */
+    void tunnelTarget(boolean secure, HttpParams params)
+        throws IOException
+        ;
+
+
+    /**
+     * Indicates that a tunnel to an intermediate proxy has been established.
+     * This is used exclusively for so-called <i>proxy chains</i>, where
+     * a request has to pass through multiple proxies before reaching the
+     * target. In that case, all proxies but the last need to be tunnelled
+     * when establishing the connection. Tunnelling of the last proxy to the
+     * target is optional and would be indicated via {@link #tunnelTarget}.
+     *
+     * @param next      the proxy to which the tunnel was established.
+     *                  This is <i>not</i> the proxy <i>through</i> which
+     *                  the tunnel was established, but the new end point
+     *                  of the tunnel. The tunnel does <i>not</i> yet
+     *                  reach to the target, use {@link #tunnelTarget}
+     *                  to indicate an end-to-end tunnel.
+     * @param secure    <code>true</code> if the connection should be
+     *                  considered secure, <code>false</code> otherwise
+     * @param params    the parameters for tunnelling this connection
+     *
+     * @throws IOException  in case of a problem
+     */
+    void tunnelProxy(HttpHost next, boolean secure, HttpParams params)
+        throws IOException
+        ;
+
+
+    /**
+     * Layers a new protocol on top of a {@link #tunnelTarget tunnelled}
+     * connection. This is typically used to create a TLS/SSL connection
+     * through a proxy.
+     * The route is the one previously passed to {@link #open open}.
+     * It is not guaranteed that the layered connection is
+     * {@link #isSecure secure}.
+     *
+     * @param context   the context for layering on top of this connection
+     * @param params    the parameters for layering on top of this connection
+     *
+     * @throws IOException      in case of a problem
+     */
+    void layerProtocol(HttpContext context, HttpParams params)
+        throws IOException
+        ;
+
+
+    /**
+     * Marks this connection as being in a reusable communication state.
+     * The checkpoints for reuseable communication states (in the absence
+     * of pipelining) are before sending a request and after receiving
+     * the response in it's entirety.
+     * The connection will automatically clear the checkpoint when
+     * used for communication. A call to this method indicates that
+     * the next checkpoint has been reached.
+     * <br/>
+     * A reusable communication state is necessary but not sufficient
+     * for the connection to be reused.
+     * A {@link #getRoute route} mismatch, the connection being closed,
+     * or other circumstances might prevent reuse.
+     */
+    void markReusable()
+        ;
+
+
+    /**
+     * Marks this connection as not being in a reusable state.
+     * This can be used immediately before releasing this connection
+     * to prevent it's reuse. Reasons for preventing reuse include
+     * error conditions and the evaluation of a
+     * {@link org.apache.http.ConnectionReuseStrategy reuse strategy}.
+     * <br/>
+     * <b>Note:</b>
+     * It is <i>not</i> necessary to call here before writing to
+     * or reading from this connection. Communication attempts will
+     * automatically unmark the state as non-reusable. It can then
+     * be switched back using {@link #markReusable markReusable}.
+     */
+    void unmarkReusable()
+        ;
+
+
+    /**
+     * Indicates whether this connection is in a reusable communication state.
+     * See {@link #markReusable markReusable} and
+     * {@link #unmarkReusable unmarkReusable} for details.
+     *
+     * @return  <code>true</code> if this connection is marked as being in
+     *          a reusable communication state,
+     *          <code>false</code> otherwise
+     */
+    boolean isMarkedReusable()
+        ;
+
+    /** 
+     * Assigns a state object to this connection. Connection managers may make 
+     * use of the connection state when allocating persistent connections.
+     *  
+     * @param state The state object
+     */
+    void setState(Object state)
+        ;
+    
+    /**
+     * Returns the state object associated with this connection.
+     * 
+     * @return The state object
+     */
+    Object getState()
+        ;
+    
+    /**
+     * Sets the duration that this connection can remain idle before it is
+     * reused. The connection should not be used again if this time elapses. The
+     * idle duration must be reset after each request sent over this connection.
+     * The elapsed time starts counting when the connection is released, which
+     * is typically after the headers (and any response body, if present) is
+     * fully consumed.
+     */
+    void setIdleDuration(long duration, TimeUnit unit);
+
+} // interface ManagedClientConnection
diff --git a/src/org/apache/http/conn/MultihomePlainSocketFactory.java b/src/org/apache/http/conn/MultihomePlainSocketFactory.java
new file mode 100644
index 0000000..e9549ab
--- /dev/null
+++ b/src/org/apache/http/conn/MultihomePlainSocketFactory.java
@@ -0,0 +1,212 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/MultihomePlainSocketFactory.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Arrays;
+
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.SocketFactory;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Socket factory that implements a simple multi-home fail-over on connect failure, 
+ * provided the same hostname resolves to multiple {@link InetAddress}es. Please note
+ * the {@link #connectSocket(Socket, String, int, InetAddress, int, HttpParams)}
+ * method cannot be reliably interrupted by closing the socket returned by the
+ * {@link #createSocket()} method.
+ */
+public final class MultihomePlainSocketFactory implements SocketFactory {
+
+    /**
+     * The factory singleton.
+     */
+    private static final
+    MultihomePlainSocketFactory DEFAULT_FACTORY = new MultihomePlainSocketFactory();
+
+    /**
+     * Gets the singleton instance of this class.
+     * @return the one and only plain socket factory
+     */
+    public static MultihomePlainSocketFactory getSocketFactory() {
+        return DEFAULT_FACTORY;
+    }
+
+    /**
+     * Restricted default constructor.
+     */
+    private MultihomePlainSocketFactory() {
+        super();
+    }
+
+
+    // non-javadoc, see interface org.apache.http.conn.SocketFactory
+    public Socket createSocket() {
+        return new Socket();
+    }
+
+    /**
+     * Attempts to connects the socket to any of the {@link InetAddress}es the 
+     * given host name resolves to. If connection to all addresses fail, the  
+     * last I/O exception is propagated to the caller.
+     * 
+     * @param sock socket to connect to any of the given addresses
+     * @param host Host name to connect to
+     * @param port the port to connect to
+     * @param localAddress local address
+     * @param localPort local port
+     * @param params HTTP parameters 
+     * 
+     * @throws  IOException if an error occurs during the connection
+     * @throws  SocketTimeoutException if timeout expires before connecting
+     */
+    public Socket connectSocket(Socket sock, String host, int port, 
+                                InetAddress localAddress, int localPort,
+                                HttpParams params)
+        throws IOException {
+
+        if (host == null) {
+            throw new IllegalArgumentException("Target host may not be null.");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException("Parameters may not be null.");
+        }
+
+        if (sock == null)
+            sock = createSocket();
+
+        if ((localAddress != null) || (localPort > 0)) {
+
+            // we need to bind explicitly
+            if (localPort < 0)
+                localPort = 0; // indicates "any"
+
+            InetSocketAddress isa =
+                new InetSocketAddress(localAddress, localPort);
+            sock.bind(isa);
+        }
+
+        int timeout = HttpConnectionParams.getConnectionTimeout(params);
+
+        InetAddress[] inetadrs = InetAddress.getAllByName(host);
+        List<InetAddress> addresses = new ArrayList<InetAddress>(inetadrs.length);
+        addresses.addAll(Arrays.asList(inetadrs));
+        Collections.shuffle(addresses);
+
+        IOException lastEx = null;
+        for (InetAddress address: addresses) {
+            try {
+                sock.connect(new InetSocketAddress(address, port), timeout);
+                break;
+            } catch (SocketTimeoutException ex) {
+                throw ex;
+            } catch (IOException ex) {
+                // create new socket
+                sock = new Socket();
+                // keep the last exception and retry
+                lastEx = ex;
+            }
+        }
+        if (lastEx != null) {
+            throw lastEx;
+        }
+        return sock;
+    } // connectSocket
+
+
+    /**
+     * Checks whether a socket connection is secure.
+     * This factory creates plain socket connections
+     * which are not considered secure.
+     *
+     * @param sock      the connected socket
+     *
+     * @return  <code>false</code>
+     *
+     * @throws IllegalArgumentException if the argument is invalid
+     */
+    public final boolean isSecure(Socket sock)
+        throws IllegalArgumentException {
+
+        if (sock == null) {
+            throw new IllegalArgumentException("Socket may not be null.");
+        }
+        // This class check assumes that createSocket() calls the constructor
+        // directly. If it was using javax.net.SocketFactory, we couldn't make
+        // an assumption about the socket class here.
+        if (sock.getClass() != Socket.class) {
+            throw new IllegalArgumentException
+                ("Socket not created by this factory.");
+        }
+        // This check is performed last since it calls a method implemented
+        // by the argument object. getClass() is final in java.lang.Object.
+        if (sock.isClosed()) {
+            throw new IllegalArgumentException("Socket is closed.");
+        }
+
+        return false;
+
+    } // isSecure
+
+
+    /**
+     * Compares this factory with an object.
+     * There is only one instance of this class.
+     *
+     * @param obj       the object to compare with
+     *
+     * @return  iff the argument is this object
+     */
+    @Override
+    public boolean equals(Object obj) {
+        return (obj == this);
+    }
+
+    /**
+     * Obtains a hash code for this object.
+     * All instances of this class have the same hash code.
+     * There is only one instance of this class.
+     */
+    @Override
+    public int hashCode() {
+        return PlainSocketFactory.class.hashCode();
+    }
+
+}
diff --git a/src/org/apache/http/conn/OperatedClientConnection.java b/src/org/apache/http/conn/OperatedClientConnection.java
new file mode 100644
index 0000000..2eda8e9
--- /dev/null
+++ b/src/org/apache/http/conn/OperatedClientConnection.java
@@ -0,0 +1,174 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/OperatedClientConnection.java $
+ * $Revision: 646087 $
+ * $Date: 2008-04-08 14:36:46 -0700 (Tue, 08 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import org.apache.http.HttpClientConnection;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpInetConnection;
+import org.apache.http.params.HttpParams;
+
+
+/**
+ * A client-side connection that relies on outside logic to connect sockets to the 
+ * appropriate hosts. It can be operated directly by an application, or through an
+ * {@link ClientConnectionOperator operator}.
+ *
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version   $Revision: 646087 $ $Date: 2008-04-08 14:36:46 -0700 (Tue, 08 Apr 2008) $
+ *
+ * @since 4.0
+ */
+public interface OperatedClientConnection
+    extends HttpClientConnection, HttpInetConnection {
+
+    /**
+     * Obtains the target host for this connection.
+     * If the connection is to a proxy but not tunnelled, this is
+     * the proxy. If the connection is tunnelled through a proxy,
+     * this is the target of the tunnel.
+     * <br/>
+     * The return value is well-defined only while the connection is open.
+     * It may change even while the connection is open,
+     * because of an {@link #update update}.
+     *
+     * @return  the host to which this connection is opened
+     */
+    HttpHost getTargetHost()
+        ;
+
+    /**
+     * Indicates whether this connection is secure.
+     * The return value is well-defined only while the connection is open.
+     * It may change even while the connection is open,
+     * because of an {@link #update update}.
+     *
+     * @return  <code>true</code> if this connection is secure,
+     *          <code>false</code> otherwise
+     */
+    boolean isSecure()
+        ;
+
+    /**
+     * Obtains the socket for this connection.
+     * The return value is well-defined only while the connection is open.
+     * It may change even while the connection is open,
+     * because of an {@link #update update}.
+     *
+     * @return  the socket for communicating with the
+     *          {@link #getTargetHost target host}
+     */
+    Socket getSocket()
+        ;
+
+
+    // There is no getParams(). For the moment, we
+    // do not require connections to store parameters.
+
+
+    /**
+     * Signals that this connection is in the process of being open.
+     * <br/>
+     * By calling this method, you can provide the connection with
+     * the unconnected socket that will be connected before
+     * {@link #openCompleted} is called. This allows 
+     * the connection to close that socket if
+     * {@link org.apache.http.HttpConnection#shutdown shutdown}
+     * is called before it is open. Closing the unconnected socket
+     * will interrupt a thread that is blocked on the connect.
+     * Otherwise, that thread will either time out on the connect,
+     * or it returns successfully and then opens this connection
+     * which was just shut down.
+     * <br/>
+     * You also must call {@link #openCompleted} in order to complete
+     * the process
+     *
+     * @param sock      the unconnected socket which is about to
+     *                  be connected.
+     * @param target    the target host of this connection
+     */
+    void opening(Socket sock, HttpHost target)
+        throws IOException
+        ;
+
+
+    /**
+     * Signals that the connection has been successfully open.
+     * An attempt to call this method on an open connection will cause
+     * an exception.
+     *
+     * @param secure    <code>true</code> if this connection is secure, for
+     *                  example if an <code>SSLSocket</code> is used, or
+     *                  <code>false</code> if it is not secure
+     * @param params    parameters for this connection. The parameters will
+     *                  be used when creating dependent objects, for example
+     *                  to determine buffer sizes.
+     */
+    void openCompleted(boolean secure, HttpParams params)
+        throws IOException
+        ;
+
+
+    /**
+     * Updates this connection.
+     * A connection can be updated only while it is open.
+     * Updates are used for example when a tunnel has been established,
+     * or when a TLS/SSL connection has been layered on top of a plain
+     * socket connection.
+     * <br/>
+     * <b>Note:</b> Updating the connection will <i>not</i> close the
+     * previously used socket. It is the caller's responsibility to close
+     * that socket if it is no longer required.
+     *
+     * @param sock      the new socket for communicating with the target host,
+     *                  or <code>null</code> to continue using the old socket.
+     *                  If <code>null</code> is passed, helper objects that
+     *                  depend on the socket should be re-used. In that case,
+     *                  some changes in the parameters will not take effect.
+     * @param target    the new target host of this connection
+     * @param secure    <code>true</code> if this connection is now secure,
+     *                  <code>false</code> if it is not secure
+     * @param params    new parameters for this connection
+     */
+    void update(Socket sock, HttpHost target,
+                boolean secure, HttpParams params)
+        throws IOException
+        ;
+
+
+} // interface OperatedClientConnection
diff --git a/src/org/apache/http/conn/package.html b/src/org/apache/http/conn/package.html
new file mode 100644
index 0000000..e0b29cd
--- /dev/null
+++ b/src/org/apache/http/conn/package.html
@@ -0,0 +1,91 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/package.html $
+ * $Revision: 651813 $
+ * $Date: 2008-04-26 03:43:34 -0700 (Sat, 26 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+The client-side connection management and handling API
+at the heart of what is referred to as <i>HttpConn</i>.
+This component provides interfaces and implementations for
+opening and managing connections.
+
+<p>
+The lowest layer of connection handling is comprised of
+{@link org.apache.http.conn.OperatedClientConnection OperatedClientConnection}
+and
+{@link org.apache.http.conn.ClientConnectionOperator ClientConnectionOperator}.
+The connection interface extends the core
+{@link org.apache.http.HttpClientConnection HttpClientConnection}
+by operations to set and update a socket.
+An operator encapsulates the logic to open and layer sockets,
+typically using a {@link org.apache.http.conn.scheme.SocketFactory SocketFactory}.
+The socket factory for a protocol
+{@link org.apache.http.conn.scheme.Scheme Scheme}
+such as "http" or "https" can be looked up in a
+{@link org.apache.http.conn.scheme.SchemeRegistry SchemeRegistry}.
+Applications without a need for sophisticated connection management
+can use this layer directly.
+</p>
+
+<p>
+On top of that lies the connection management layer. A
+{@link org.apache.http.conn.ClientConnectionManager ClientConnectionManager}
+internally manages operated connections, but hands out instances of
+{@link org.apache.http.conn.ManagedClientConnection ManagedClientConnection}.
+This interface abstracts from the underlying socket operations and
+provides convenient methods for opening and updating sockets in order
+to establish a {@link org.apache.http.conn.routing.HttpRoute route}.
+The operator is encapsulated by the connection manager and called
+automatically.
+
+<br/>
+
+Connections obtained from a manager have to be returned after use.
+This can be {@link org.apache.http.conn.ConnectionReleaseTrigger triggered}
+on various levels, either by releasing the
+{@link org.apache.http.conn.ManagedClientConnection
+       connection}
+directly, or by calling a method on an
+{@link org.apache.http.conn.BasicManagedEntity entity}
+received from the connection, or by closing the
+{@link org.apache.http.conn.EofSensorInputStream stream}
+from which that entity is being read.
+
+Connection managers will try to keep returned connections alive in
+order to re-use them for subsequent requests along the same route.
+The managed connection interface and all triggers for connection release
+provide methods to enable or disable this behavior.
+</p>
+
+</body>
+</html>
diff --git a/src/org/apache/http/conn/params/ConnConnectionPNames.java b/src/org/apache/http/conn/params/ConnConnectionPNames.java
new file mode 100644
index 0000000..ff1a090
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnConnectionPNames.java
@@ -0,0 +1,65 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnConnectionPNames.java $
+ * $Revision: 576068 $
+ * $Date: 2007-09-16 03:25:01 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+
+/**
+ * Parameter names for connections in HttpConn.
+ *
+ * @version $Revision: 576068 $
+ * 
+ * @since 4.0
+ */
+public interface ConnConnectionPNames {
+
+
+    /**
+     * Defines the maximum number of ignorable lines before we expect
+     * a HTTP response's status line.
+     * <p>
+     * With HTTP/1.1 persistent connections, the problem arises that
+     * broken scripts could return a wrong Content-Length
+     * (there are more bytes sent than specified).
+     * Unfortunately, in some cases, this cannot be detected after the
+     * bad response, but only before the next one.
+     * So HttpClient must be able to skip those surplus lines this way.
+     * </p>
+     * <p>
+     * This parameter expects a value of type {@link Integer}.
+     * 0 disallows all garbage/empty lines before the status line.
+     * Use {@link java.lang.Integer#MAX_VALUE} for unlimited
+     * (default in lenient mode).
+     * </p>
+     */
+    public static final String MAX_STATUS_LINE_GARBAGE = "http.connection.max-status-line-garbage";
+
+
+}
diff --git a/src/org/apache/http/conn/params/ConnConnectionParamBean.java b/src/org/apache/http/conn/params/ConnConnectionParamBean.java
new file mode 100644
index 0000000..094b152
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnConnectionParamBean.java
@@ -0,0 +1,55 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnConnectionParamBean.java $
+ * $Revision: 652020 $
+ * $Date: 2008-04-27 14:23:31 -0700 (Sun, 27 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+import org.apache.http.params.HttpAbstractParamBean;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Allows for setting parameters relating to connections on
+ * {@link HttpParams}.  This class ensures that the values set on the params
+ * are type-safe. 
+ */
+public class ConnConnectionParamBean extends HttpAbstractParamBean {
+    
+    public ConnConnectionParamBean (final HttpParams params) {
+        super(params);
+    }
+
+    /**
+     * @see ConnConnectionPNames#MAX_STATUS_LINE_GARBAGE
+     */
+    public void setMaxStatusLineGarbage (final int maxStatusLineGarbage) {
+        params.setIntParameter(ConnConnectionPNames.MAX_STATUS_LINE_GARBAGE, maxStatusLineGarbage);
+    }
+    
+}
diff --git a/src/org/apache/http/conn/params/ConnManagerPNames.java b/src/org/apache/http/conn/params/ConnManagerPNames.java
new file mode 100644
index 0000000..1184b12
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnManagerPNames.java
@@ -0,0 +1,73 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnManagerPNames.java $
+ * $Revision: 658781 $
+ * $Date: 2008-05-21 10:42:13 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+
+/**
+ * Parameter names for connection managers in HttpConn.
+ *
+ * @version $Revision: 658781 $
+ * 
+ * @since 4.0
+ */
+public interface ConnManagerPNames {
+
+    /**
+     * Defines the timeout in milliseconds used when retrieving an instance of 
+     * {@link org.apache.http.conn.ManagedClientConnection} from the
+     * {@link org.apache.http.conn.ClientConnectionManager}.
+     * <p>
+     * This parameter expects a value of type {@link Long}.
+     * </p>
+     */ 
+    public static final String TIMEOUT = "http.conn-manager.timeout"; 
+
+    /** 
+     * Defines the maximum number of connections per route.
+     * This limit is interpreted by client connection managers
+     * and applies to individual manager instances.
+     * <p>
+     * This parameter expects a value of type {@link ConnPerRoute}.
+     * </p>
+     */
+    public static final String MAX_CONNECTIONS_PER_ROUTE = "http.conn-manager.max-per-route";
+
+    /** 
+     * Defines the maximum number of connections in total.
+     * This limit is interpreted by client connection managers
+     * and applies to individual manager instances.
+     * <p>
+     * This parameter expects a value of type {@link Integer}.
+     * </p>
+     */
+    public static final String MAX_TOTAL_CONNECTIONS = "http.conn-manager.max-total";
+
+}
diff --git a/src/org/apache/http/conn/params/ConnManagerParamBean.java b/src/org/apache/http/conn/params/ConnManagerParamBean.java
new file mode 100644
index 0000000..830b7bf
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnManagerParamBean.java
@@ -0,0 +1,62 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnManagerParamBean.java $
+ * $Revision: 658781 $
+ * $Date: 2008-05-21 10:42:13 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+import org.apache.http.params.HttpAbstractParamBean;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Allows for setting parameters relating to connection managers on
+ * {@link HttpParams}.  This class ensures that the values set on the params
+ * are type-safe. 
+ */
+public class ConnManagerParamBean extends HttpAbstractParamBean {
+
+    public ConnManagerParamBean (final HttpParams params) {
+        super(params);
+    }
+    
+    public void setTimeout (final long timeout) {
+        params.setLongParameter(ConnManagerPNames.TIMEOUT, timeout);
+    }
+
+    /** @see ConnManagerPNames#MAX_TOTAL_CONNECTIONS */
+    public void setMaxTotalConnections (final int maxConnections) {
+        params.setIntParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, maxConnections);
+    }
+    
+    /** @see ConnManagerPNames#MAX_CONNECTIONS_PER_ROUTE */
+    public void setConnectionsPerRoute(final ConnPerRouteBean connPerRoute) {
+        params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, connPerRoute);
+    }
+
+}
diff --git a/src/org/apache/http/conn/params/ConnManagerParams.java b/src/org/apache/http/conn/params/ConnManagerParams.java
new file mode 100644
index 0000000..c6e042e
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnManagerParams.java
@@ -0,0 +1,169 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnManagerParams.java $
+ * $Revision: 658785 $
+ * $Date: 2008-05-21 10:47:40 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.params.HttpParams;
+
+/**
+ * This class represents a collection of HTTP protocol parameters applicable
+ * to client-side
+ * {@link org.apache.http.conn.ClientConnectionManager connection managers}. 
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author Michael Becke
+ * 
+ * @version $Revision: 658785 $
+ * 
+ * @since 4.0
+ *
+ * @see ConnManagerPNames
+ */
+public final class ConnManagerParams implements ConnManagerPNames {
+
+    /** The default maximum number of connections allowed overall */
+    public static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 20;
+
+    /**
+     * Returns the timeout in milliseconds used when retrieving a
+     * {@link org.apache.http.conn.ManagedClientConnection} from the
+     * {@link org.apache.http.conn.ClientConnectionManager}.
+     * 
+     * @return timeout in milliseconds.
+     */ 
+    public static long getTimeout(final HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        return params.getLongParameter(TIMEOUT, 0);
+    }
+
+    /**
+     * Sets the timeout in milliseconds used when retrieving a
+     * {@link org.apache.http.conn.ManagedClientConnection} from the
+     * {@link org.apache.http.conn.ClientConnectionManager}.
+     * 
+     * @param timeout the timeout in milliseconds
+     */ 
+    public static void setTimeout(final HttpParams params, long timeout) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        params.setLongParameter(TIMEOUT, timeout);
+    }
+
+    /** The default maximum number of connections allowed per host */
+    private static final ConnPerRoute DEFAULT_CONN_PER_ROUTE = new ConnPerRoute() {
+        
+        public int getMaxForRoute(HttpRoute route) {
+            return ConnPerRouteBean.DEFAULT_MAX_CONNECTIONS_PER_ROUTE;
+        }
+        
+    };
+    
+    /**
+     * Sets lookup interface for maximum number of connections allowed per route.
+     *
+     * @param params HTTP parameters
+     * @param connPerRoute lookup interface for maximum number of connections allowed 
+     *        per route
+     * 
+     * @see ConnManagerPNames#MAX_CONNECTIONS_PER_ROUTE
+     */
+    public static void setMaxConnectionsPerRoute(final HttpParams params,
+                                                final ConnPerRoute connPerRoute) {
+        if (params == null) {
+            throw new IllegalArgumentException
+                ("HTTP parameters must not be null.");
+        }
+        params.setParameter(MAX_CONNECTIONS_PER_ROUTE, connPerRoute);
+    }
+
+    /**
+     * Returns lookup interface for maximum number of connections allowed per route.
+     *
+     * @param params HTTP parameters
+     * 
+     * @return lookup interface for maximum number of connections allowed per route.
+     * 
+     * @see ConnManagerPNames#MAX_CONNECTIONS_PER_ROUTE
+     */
+    public static ConnPerRoute getMaxConnectionsPerRoute(final HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException
+                ("HTTP parameters must not be null.");
+        }
+        ConnPerRoute connPerRoute = (ConnPerRoute) params.getParameter(MAX_CONNECTIONS_PER_ROUTE);
+        if (connPerRoute == null) {
+            connPerRoute = DEFAULT_CONN_PER_ROUTE;
+        }
+        return connPerRoute;
+    }
+
+
+    /**
+     * Sets the maximum number of connections allowed.
+     *
+     * @param params HTTP parameters
+     * @param maxTotalConnections The maximum number of connections allowed.
+     * 
+     * @see ConnManagerPNames#MAX_TOTAL_CONNECTIONS
+     */
+    public static void setMaxTotalConnections(
+            final HttpParams params,
+            int maxTotalConnections) {
+        if (params == null) {
+            throw new IllegalArgumentException
+                ("HTTP parameters must not be null.");
+        }
+        params.setIntParameter(MAX_TOTAL_CONNECTIONS, maxTotalConnections);
+    }
+
+    /**
+     * Gets the maximum number of connections allowed.
+     *
+     * @param params HTTP parameters
+     *
+     * @return The maximum number of connections allowed.
+     * 
+     * @see ConnManagerPNames#MAX_TOTAL_CONNECTIONS
+     */
+    public static int getMaxTotalConnections(
+            final HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException
+                ("HTTP parameters must not be null.");
+        }
+        return params.getIntParameter(MAX_TOTAL_CONNECTIONS, DEFAULT_MAX_TOTAL_CONNECTIONS);
+    }
+
+    
+}
diff --git a/src/org/apache/http/conn/params/ConnPerRoute.java b/src/org/apache/http/conn/params/ConnPerRoute.java
new file mode 100644
index 0000000..abff04e
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnPerRoute.java
@@ -0,0 +1,51 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnPerRoute.java $
+ * $Revision: 651813 $
+ * $Date: 2008-04-26 03:43:34 -0700 (Sat, 26 Apr 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+import org.apache.http.conn.routing.HttpRoute;
+
+/**
+ * This interface is intended for looking up maximum number of connections 
+ * allowed for for a given route. This class can be used by pooling 
+ * {@link org.apache.http.conn.ClientConnectionManager connection managers} for 
+ * a fine-grained control of connections on a per route basis. 
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Revision: 651813 $
+ * 
+ * @since 4.0
+ */
+public interface ConnPerRoute {
+
+    int getMaxForRoute(HttpRoute route);
+    
+}
diff --git a/src/org/apache/http/conn/params/ConnPerRouteBean.java b/src/org/apache/http/conn/params/ConnPerRouteBean.java
new file mode 100644
index 0000000..c6a36e3
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnPerRouteBean.java
@@ -0,0 +1,114 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnPerRouteBean.java $
+ * $Revision: 652947 $
+ * $Date: 2008-05-02 16:15:40 -0700 (Fri, 02 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.http.conn.routing.HttpRoute;
+
+/**
+ * This class maintains a map of HTTP routes to maximum number of connections allowed 
+ * for those routes. This class can be used by pooling 
+ * {@link org.apache.http.conn.ClientConnectionManager connection managers} for 
+ * a fine-grained control of connections on a per route basis. 
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Revision: 652947 $
+ * 
+ * @since 4.0
+ */
+public final class ConnPerRouteBean implements ConnPerRoute {
+
+    /** The default maximum number of connections allowed per host */
+    public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 2;   // Per RFC 2616 sec 8.1.4
+    
+    private final Map<HttpRoute, Integer> maxPerHostMap;
+    
+    private int defaultMax;
+    
+    public ConnPerRouteBean(int defaultMax) {
+        super();
+        this.maxPerHostMap = new HashMap<HttpRoute, Integer>();
+        setDefaultMaxPerRoute(defaultMax);
+    }
+    
+    public ConnPerRouteBean() {
+        this(DEFAULT_MAX_CONNECTIONS_PER_ROUTE);
+    }
+    
+    public int getDefaultMax() {
+        return this.defaultMax;
+    }
+
+    public void setDefaultMaxPerRoute(int max) {
+        if (max < 1) {
+            throw new IllegalArgumentException
+                ("The maximum must be greater than 0.");
+        }
+        this.defaultMax = max;
+    }
+
+    public void setMaxForRoute(final HttpRoute route, int max) {
+        if (route == null) {
+            throw new IllegalArgumentException
+                ("HTTP route may not be null.");
+        }
+        if (max < 1) {
+            throw new IllegalArgumentException
+                ("The maximum must be greater than 0.");
+        }
+        this.maxPerHostMap.put(route, Integer.valueOf(max));
+    }
+
+    public int getMaxForRoute(final HttpRoute route) {
+        if (route == null) {
+            throw new IllegalArgumentException
+                ("HTTP route may not be null.");
+        }
+        Integer max = this.maxPerHostMap.get(route);
+        if (max != null) {
+            return max.intValue();
+        } else {
+            return this.defaultMax;
+        }
+    }
+    
+    public void setMaxForRoutes(final Map<HttpRoute, Integer> map) {
+        if (map == null) {
+            return;
+        }
+        this.maxPerHostMap.clear();
+        this.maxPerHostMap.putAll(map);
+    }
+    
+}
diff --git a/src/org/apache/http/conn/params/ConnRoutePNames.java b/src/org/apache/http/conn/params/ConnRoutePNames.java
new file mode 100644
index 0000000..f9d42db
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnRoutePNames.java
@@ -0,0 +1,84 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnRoutePNames.java $
+ * $Revision: 613656 $
+ * $Date: 2008-01-20 11:06:56 -0800 (Sun, 20 Jan 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+/**
+ * Parameter names for routing in HttpConn.
+ * 
+ * @version $Revision: 613656 $
+ * 
+ * @since 4.0
+ */
+public interface ConnRoutePNames {
+
+    /**
+     * Parameter for the default proxy.
+     * The default value will be used by some
+     * {@link org.apache.http.conn.routing.HttpRoutePlanner HttpRoutePlanner}
+     * implementations, in particular the default implementation.
+     * <p>
+     * This parameter expects a value of type {@link org.apache.http.HttpHost}.
+     * </p>
+     */
+    public static final String DEFAULT_PROXY = "http.route.default-proxy";
+
+
+    /**
+     * Parameter for the local address.
+     * On machines with multiple network interfaces, this parameter
+     * can be used to select the network interface from which the
+     * connection originates.
+     * It will be interpreted by the standard
+     * {@link org.apache.http.conn.routing.HttpRoutePlanner HttpRoutePlanner}
+     * implementations, in particular the default implementation.
+     * <p>
+     * This parameter expects a value of type {@link java.net.InetAddress}.
+     * </p>
+     */
+    public static final String LOCAL_ADDRESS = "http.route.local-address";
+
+
+    /**
+     * Parameter for an forced route.
+     * The forced route will be interpreted by the standard
+     * {@link org.apache.http.conn.routing.HttpRoutePlanner HttpRoutePlanner}
+     * implementations.
+     * Instead of computing a route, the given forced route will be
+     * returned, even if it points to the wrong target host.
+     * <p>
+     * This parameter expects a value of type
+     * {@link org.apache.http.conn.routing.HttpRoute HttpRoute}.
+     * </p>
+     */
+    public static final String FORCED_ROUTE = "http.route.forced-route";
+
+}
+
diff --git a/src/org/apache/http/conn/params/ConnRouteParamBean.java b/src/org/apache/http/conn/params/ConnRouteParamBean.java
new file mode 100644
index 0000000..9464c02
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnRouteParamBean.java
@@ -0,0 +1,67 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnRouteParamBean.java $
+ * $Revision: 652020 $
+ * $Date: 2008-04-27 14:23:31 -0700 (Sun, 27 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+import java.net.InetAddress;
+
+import org.apache.http.HttpHost;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.params.HttpAbstractParamBean;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Allows for setting parameters relating to connection routes on
+ * {@link HttpParams}.  This class ensures that the values set on the params
+ * are type-safe. 
+ */
+public class ConnRouteParamBean extends HttpAbstractParamBean {
+    
+    public ConnRouteParamBean (final HttpParams params) {
+        super(params);
+    }
+
+    /** @see ConnRoutePNames#DEFAULT_PROXY */
+    public void setDefaultProxy (final HttpHost defaultProxy) {
+        params.setParameter(ConnRoutePNames.DEFAULT_PROXY, defaultProxy);
+    }
+
+    /** @see ConnRoutePNames#LOCAL_ADDRESS */
+    public void setLocalAddress (final InetAddress address) {
+        params.setParameter(ConnRoutePNames.LOCAL_ADDRESS, address);
+    }
+
+    /** @see ConnRoutePNames#FORCED_ROUTE */
+    public void setForcedRoute (final HttpRoute route) {
+        params.setParameter(ConnRoutePNames.FORCED_ROUTE, route);
+    }
+    
+}
diff --git a/src/org/apache/http/conn/params/ConnRouteParams.java b/src/org/apache/http/conn/params/ConnRouteParams.java
new file mode 100644
index 0000000..2fa1654
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnRouteParams.java
@@ -0,0 +1,203 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnRouteParams.java $
+ * $Revision: 658785 $
+ * $Date: 2008-05-21 10:47:40 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+
+import java.net.InetAddress;
+
+import org.apache.http.HttpHost;
+import org.apache.http.params.HttpParams;
+import org.apache.http.conn.routing.HttpRoute;
+
+
+
+/**
+ * An adaptor for accessing route related parameters in {@link HttpParams}.
+ * See {@link ConnRoutePNames} for parameter name definitions.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * 
+ * @version $Revision: 658785 $
+ * 
+ * @since 4.0
+ */
+public class ConnRouteParams implements ConnRoutePNames {
+
+    /**
+     * A special value indicating "no host".
+     * This relies on a nonsense scheme name to avoid conflicts
+     * with actual hosts. Note that this is a <i>valid</i> host.
+     */
+    public static final HttpHost NO_HOST =
+        new HttpHost("127.0.0.255", 0, "no-host");
+
+    /**
+     * A special value indicating "no route".
+     * This is a route with {@link #NO_HOST} as the target.
+     */
+    public static final HttpRoute NO_ROUTE = new HttpRoute(NO_HOST);
+
+
+    /** Disabled default constructor. */
+    private ConnRouteParams() {
+        // no body
+    }
+
+
+    /**
+     * Obtains the {@link ConnRoutePNames#DEFAULT_PROXY DEFAULT_PROXY}
+     * parameter value.
+     * {@link #NO_HOST} will be mapped to <code>null</code>,
+     * to allow unsetting in a hierarchy.
+     *
+     * @param params    the parameters in which to look up
+     *
+     * @return  the default proxy set in the argument parameters, or
+     *          <code>null</code> if not set
+     */
+    public static HttpHost getDefaultProxy(HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("Parameters must not be null.");
+        }
+        HttpHost proxy = (HttpHost)
+            params.getParameter(DEFAULT_PROXY);
+        if ((proxy != null) && NO_HOST.equals(proxy)) {
+            // value is explicitly unset
+            proxy = null;
+        }
+        return proxy;
+    }
+
+
+    /**
+     * Sets the {@link ConnRoutePNames#DEFAULT_PROXY DEFAULT_PROXY}
+     * parameter value.
+     *
+     * @param params    the parameters in which to set the value
+     * @param proxy     the value to set, may be <code>null</code>.
+     *                  Note that {@link #NO_HOST} will be mapped to
+     *                  <code>null</code> by {@link #getDefaultProxy},
+     *                  to allow for explicit unsetting in hierarchies.
+     */
+    public static void setDefaultProxy(HttpParams params,
+                                             HttpHost proxy) {
+        if (params == null) {
+            throw new IllegalArgumentException("Parameters must not be null.");
+        }
+        params.setParameter(DEFAULT_PROXY, proxy);
+    }
+
+
+    /**
+     * Obtains the {@link ConnRoutePNames#FORCED_ROUTE FORCED_ROUTE}
+     * parameter value.
+     * {@link #NO_ROUTE} will be mapped to <code>null</code>,
+     * to allow unsetting in a hierarchy.
+     *
+     * @param params    the parameters in which to look up
+     *
+     * @return  the forced route set in the argument parameters, or
+     *          <code>null</code> if not set
+     */
+    public static HttpRoute getForcedRoute(HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("Parameters must not be null.");
+        }
+        HttpRoute route = (HttpRoute)
+            params.getParameter(FORCED_ROUTE);
+        if ((route != null) && NO_ROUTE.equals(route)) {
+            // value is explicitly unset
+            route = null;
+        }
+        return route;
+    }
+
+
+    /**
+     * Sets the {@link ConnRoutePNames#FORCED_ROUTE FORCED_ROUTE}
+     * parameter value.
+     *
+     * @param params    the parameters in which to set the value
+     * @param route     the value to set, may be <code>null</code>.
+     *                  Note that {@link #NO_ROUTE} will be mapped to
+     *                  <code>null</code> by {@link #getForcedRoute},
+     *                  to allow for explicit unsetting in hierarchies.
+     */
+    public static void setForcedRoute(HttpParams params,
+                                            HttpRoute route) {
+        if (params == null) {
+            throw new IllegalArgumentException("Parameters must not be null.");
+        }
+        params.setParameter(FORCED_ROUTE, route);
+    }
+
+
+    /**
+     * Obtains the {@link ConnRoutePNames#LOCAL_ADDRESS LOCAL_ADDRESS}
+     * parameter value.
+     * There is no special value that would automatically be mapped to
+     * <code>null</code>. You can use the wildcard address (0.0.0.0 for IPv4,
+     * :: for IPv6) to override a specific local address in a hierarchy.
+     *
+     * @param params    the parameters in which to look up
+     *
+     * @return  the local address set in the argument parameters, or
+     *          <code>null</code> if not set
+     */
+    public static InetAddress getLocalAddress(HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("Parameters must not be null.");
+        }
+        InetAddress local = (InetAddress)
+            params.getParameter(LOCAL_ADDRESS);
+        // no explicit unsetting
+        return local;
+    }
+
+
+    /**
+     * Sets the {@link ConnRoutePNames#LOCAL_ADDRESS LOCAL_ADDRESS}
+     * parameter value.
+     *
+     * @param params    the parameters in which to set the value
+     * @param local     the value to set, may be <code>null</code>
+     */
+    public static void setLocalAddress(HttpParams params,
+                                             InetAddress local) {
+        if (params == null) {
+            throw new IllegalArgumentException("Parameters must not be null.");
+        }
+        params.setParameter(LOCAL_ADDRESS, local);
+    }
+    
+}
+
diff --git a/src/org/apache/http/conn/params/package.html b/src/org/apache/http/conn/params/package.html
new file mode 100644
index 0000000..9b80420
--- /dev/null
+++ b/src/org/apache/http/conn/params/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Parameters for configuring <i>HttpConn</i>.
+
+</body>
+</html>
diff --git a/src/org/apache/http/conn/routing/BasicRouteDirector.java b/src/org/apache/http/conn/routing/BasicRouteDirector.java
new file mode 100644
index 0000000..a3714ec
--- /dev/null
+++ b/src/org/apache/http/conn/routing/BasicRouteDirector.java
@@ -0,0 +1,181 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/routing/BasicRouteDirector.java $
+ * $Revision: 620255 $
+ * $Date: 2008-02-10 02:23:55 -0800 (Sun, 10 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.routing;
+
+
+
+/**
+ * Basic implementation of an {@link HttpRouteDirector HttpRouteDirector}.
+ * This implementation is stateless and therefore thread-safe.
+ * 
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 620255 $
+ *
+ * @since 4.0
+ */
+public class BasicRouteDirector implements HttpRouteDirector {
+
+    // public default constructor
+
+
+    /**
+     * Provides the next step.
+     *
+     * @param plan      the planned route
+     * @param fact      the currently established route, or
+     *                  <code>null</code> if nothing is established
+     *
+     * @return  one of the constants defined in this class, indicating
+     *          either the next step to perform, or success, or failure.
+     *          0 is for success, a negative value for failure.
+     */
+    public int nextStep(RouteInfo plan, RouteInfo fact) {
+        if (plan == null) {
+            throw new IllegalArgumentException
+                ("Planned route may not be null.");
+        }
+
+        int step = UNREACHABLE;
+
+        if ((fact == null) || (fact.getHopCount() < 1))
+            step = firstStep(plan);
+        else if (plan.getHopCount() > 1)
+            step = proxiedStep(plan, fact);
+        else
+            step = directStep(plan, fact);
+
+        return step;
+
+    } // nextStep
+
+
+    /**
+     * Determines the first step to establish a route.
+     *
+     * @param plan      the planned route
+     *
+     * @return  the first step
+     */
+    protected int firstStep(RouteInfo plan) {
+
+        return (plan.getHopCount() > 1) ?
+            CONNECT_PROXY : CONNECT_TARGET;
+    }
+
+
+    /**
+     * Determines the next step to establish a direct connection.
+     *
+     * @param plan      the planned route
+     * @param fact      the currently established route
+     *
+     * @return  one of the constants defined in this class, indicating
+     *          either the next step to perform, or success, or failure
+     */
+    protected int directStep(RouteInfo plan, RouteInfo fact) {
+
+        if (fact.getHopCount() > 1)
+            return UNREACHABLE;
+        if (!plan.getTargetHost().equals(fact.getTargetHost()))
+            return UNREACHABLE;
+        // If the security is too low, we could now suggest to layer
+        // a secure protocol on the direct connection. Layering on direct
+        // connections has not been supported in HttpClient 3.x, we don't
+        // consider it here until there is a real-life use case for it.
+
+        // Should we tolerate if security is better than planned?
+        // (plan.isSecure() && !fact.isSecure())
+        if (plan.isSecure() != fact.isSecure())
+            return UNREACHABLE;
+
+        // Local address has to match only if the plan specifies one.
+        if ((plan.getLocalAddress() != null) &&
+            !plan.getLocalAddress().equals(fact.getLocalAddress())
+            )
+            return UNREACHABLE;
+
+        return COMPLETE;
+    }
+
+
+    /**
+     * Determines the next step to establish a connection via proxy.
+     *
+     * @param plan      the planned route
+     * @param fact      the currently established route
+     *
+     * @return  one of the constants defined in this class, indicating
+     *          either the next step to perform, or success, or failure
+     */
+    protected int proxiedStep(RouteInfo plan, RouteInfo fact) {
+
+        if (fact.getHopCount() <= 1)
+            return UNREACHABLE;
+        if (!plan.getTargetHost().equals(fact.getTargetHost()))
+            return UNREACHABLE;
+        final int phc = plan.getHopCount();
+        final int fhc = fact.getHopCount();
+        if (phc < fhc)
+            return UNREACHABLE;
+
+        for (int i=0; i<fhc-1; i++) {
+            if (!plan.getHopTarget(i).equals(fact.getHopTarget(i)))
+                return UNREACHABLE;
+        }
+        // now we know that the target matches and proxies so far are the same
+        if (phc > fhc)
+            return TUNNEL_PROXY; // need to extend the proxy chain
+            
+        // proxy chain and target are the same, check tunnelling and layering
+        if ((fact.isTunnelled() && !plan.isTunnelled()) ||
+            (fact.isLayered()   && !plan.isLayered()))
+            return UNREACHABLE;
+
+        if (plan.isTunnelled() && !fact.isTunnelled())
+            return TUNNEL_TARGET;
+        if (plan.isLayered() && !fact.isLayered())
+            return LAYER_PROTOCOL;
+
+        // tunnel and layering are the same, remains to check the security
+        // Should we tolerate if security is better than planned?
+        // (plan.isSecure() && !fact.isSecure())
+        if (plan.isSecure() != fact.isSecure())
+            return UNREACHABLE;
+
+        return COMPLETE;
+    }
+
+
+} // class BasicRouteDirector
diff --git a/src/org/apache/http/conn/routing/HttpRoute.java b/src/org/apache/http/conn/routing/HttpRoute.java
new file mode 100644
index 0000000..1e870b8
--- /dev/null
+++ b/src/org/apache/http/conn/routing/HttpRoute.java
@@ -0,0 +1,443 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/routing/HttpRoute.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.routing;
+
+import java.net.InetAddress;
+
+import org.apache.http.HttpHost;
+
+/**
+ * The route for a request.
+ * Instances of this class are unmodifiable and therefore suitable
+ * for use as lookup keys.
+ * 
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 653041 $
+ *
+ * @since 4.0
+ */
+public final class HttpRoute implements RouteInfo, Cloneable {
+
+    /** The target host to connect to. */
+    private final HttpHost targetHost;
+
+    /**
+     * The local address to connect from.
+     * <code>null</code> indicates that the default should be used.
+     */
+    private final InetAddress localAddress;
+
+    /** The proxy servers, if any. */
+    private final HttpHost[] proxyChain;
+
+    /** Whether the the route is tunnelled through the proxy. */
+    private final TunnelType tunnelled;
+
+    /** Whether the route is layered. */
+    private final LayerType layered;
+
+    /** Whether the route is (supposed to be) secure. */
+    private final boolean secure;
+
+
+    /**
+     * Internal, fully-specified constructor.
+     * This constructor does <i>not</i> clone the proxy chain array,
+     * nor test it for <code>null</code> elements. This conversion and
+     * check is the responsibility of the public constructors.
+     * The order of arguments here is different from the similar public
+     * constructor, as required by Java.
+     *
+     * @param local     the local address to route from, or
+     *                  <code>null</code> for the default
+     * @param target    the host to which to route
+     * @param proxies   the proxy chain to use, or
+     *                  <code>null</code> for a direct route
+     * @param secure    <code>true</code> if the route is (to be) secure,
+     *                  <code>false</code> otherwise
+     * @param tunnelled the tunnel type of this route, or
+     *                  <code>null</code> for PLAIN
+     * @param layered   the layering type of this route, or
+     *                  <code>null</code> for PLAIN
+     */
+    private HttpRoute(InetAddress local,
+                      HttpHost target, HttpHost[] proxies,
+                      boolean secure,
+                      TunnelType tunnelled, LayerType layered) {
+        if (target == null) {
+            throw new IllegalArgumentException
+                ("Target host may not be null.");
+        }
+        if ((tunnelled == TunnelType.TUNNELLED) && (proxies == null)) {
+            throw new IllegalArgumentException
+                ("Proxy required if tunnelled.");
+        }
+
+        // tunnelled is already checked above, that is in line with the default
+        if (tunnelled == null)
+            tunnelled = TunnelType.PLAIN;
+        if (layered == null)
+            layered = LayerType.PLAIN;
+
+        this.targetHost   = target;
+        this.localAddress = local;
+        this.proxyChain   = proxies;
+        this.secure       = secure;
+        this.tunnelled    = tunnelled;
+        this.layered      = layered;
+    }
+
+
+    /**
+     * Creates a new route with all attributes specified explicitly.
+     *
+     * @param target    the host to which to route
+     * @param local     the local address to route from, or
+     *                  <code>null</code> for the default
+     * @param proxies   the proxy chain to use, or
+     *                  <code>null</code> for a direct route
+     * @param secure    <code>true</code> if the route is (to be) secure,
+     *                  <code>false</code> otherwise
+     * @param tunnelled the tunnel type of this route
+     * @param layered   the layering type of this route
+     */
+    public HttpRoute(HttpHost target, InetAddress local, HttpHost[] proxies,
+                     boolean secure, TunnelType tunnelled, LayerType layered) {
+        this(local, target, toChain(proxies), secure, tunnelled, layered);
+    }
+
+
+    /**
+     * Creates a new route with at most one proxy.
+     *
+     * @param target    the host to which to route
+     * @param local     the local address to route from, or
+     *                  <code>null</code> for the default
+     * @param proxy     the proxy to use, or
+     *                  <code>null</code> for a direct route
+     * @param secure    <code>true</code> if the route is (to be) secure,
+     *                  <code>false</code> otherwise
+     * @param tunnelled <code>true</code> if the route is (to be) tunnelled
+     *                  via the proxy,
+     *                  <code>false</code> otherwise
+     * @param layered   <code>true</code> if the route includes a
+     *                  layered protocol,
+     *                  <code>false</code> otherwise
+     */
+    public HttpRoute(HttpHost target, InetAddress local, HttpHost proxy,
+                     boolean secure, TunnelType tunnelled, LayerType layered) {
+        this(local, target, toChain(proxy), secure, tunnelled, layered);
+    }
+
+
+    /**
+     * Creates a new direct route.
+     * That is a route without a proxy.
+     *
+     * @param target    the host to which to route
+     * @param local     the local address to route from, or
+     *                  <code>null</code> for the default
+     * @param secure    <code>true</code> if the route is (to be) secure,
+     *                  <code>false</code> otherwise
+     */
+    public HttpRoute(HttpHost target, InetAddress local, boolean secure) {
+        this(local, target, null, secure, TunnelType.PLAIN, LayerType.PLAIN);
+    }
+
+
+    /**
+     * Creates a new direct insecure route.
+     *
+     * @param target    the host to which to route
+     */
+    public HttpRoute(HttpHost target) {
+        this(null, target, null, false, TunnelType.PLAIN, LayerType.PLAIN);
+    }
+
+
+    /**
+     * Creates a new route through a proxy.
+     * When using this constructor, the <code>proxy</code> MUST be given.
+     * For convenience, it is assumed that a secure connection will be
+     * layered over a tunnel through the proxy.
+     *
+     * @param target    the host to which to route
+     * @param local     the local address to route from, or
+     *                  <code>null</code> for the default
+     * @param proxy     the proxy to use
+     * @param secure    <code>true</code> if the route is (to be) secure,
+     *                  <code>false</code> otherwise
+     */
+    public HttpRoute(HttpHost target, InetAddress local, HttpHost proxy,
+                     boolean secure) {
+        this(local, target, toChain(proxy), secure,
+             secure ? TunnelType.TUNNELLED : TunnelType.PLAIN,
+             secure ? LayerType.LAYERED    : LayerType.PLAIN);
+        if (proxy == null) {
+            throw new IllegalArgumentException
+                ("Proxy host may not be null.");
+        }
+    }
+
+
+    /**
+     * Helper to convert a proxy to a proxy chain.
+     *
+     * @param proxy     the only proxy in the chain, or <code>null</code>
+     *
+     * @return  a proxy chain array, or <code>null</code>
+     */
+    private static HttpHost[] toChain(HttpHost proxy) {
+        if (proxy == null)
+            return null;
+
+        return new HttpHost[]{ proxy };
+    }
+
+
+    /**
+     * Helper to duplicate and check a proxy chain.
+     * An empty proxy chain is converted to <code>null</code>.
+     *
+     * @param proxies   the proxy chain to duplicate, or <code>null</code>
+     *
+     * @return  a new proxy chain array, or <code>null</code>
+     */
+    private static HttpHost[] toChain(HttpHost[] proxies) {
+        if ((proxies == null) || (proxies.length < 1))
+            return null;
+
+        for (HttpHost proxy : proxies) {
+            if (proxy == null)
+                throw new IllegalArgumentException
+                        ("Proxy chain may not contain null elements.");
+        }
+
+        // copy the proxy chain, the traditional way
+        HttpHost[] result = new HttpHost[proxies.length];
+        System.arraycopy(proxies, 0, result, 0, proxies.length);
+
+        return result;
+    }
+
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final HttpHost getTargetHost() {
+        return this.targetHost;
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final InetAddress getLocalAddress() {
+        return this.localAddress;
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final int getHopCount() {
+        return (proxyChain == null) ? 1 : (proxyChain.length+1);
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final HttpHost getHopTarget(int hop) {
+        if (hop < 0)
+            throw new IllegalArgumentException
+                ("Hop index must not be negative: " + hop);
+        final int hopcount = getHopCount();
+        if (hop >= hopcount)
+            throw new IllegalArgumentException
+                ("Hop index " + hop +
+                 " exceeds route length " + hopcount);
+
+        HttpHost result = null;
+        if (hop < hopcount-1)
+            result = this.proxyChain[hop];
+        else
+            result = this.targetHost;
+
+        return result;
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final HttpHost getProxyHost() {
+        return (this.proxyChain == null) ? null : this.proxyChain[0];
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final TunnelType getTunnelType() {
+        return this.tunnelled;
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final boolean isTunnelled() {
+        return (this.tunnelled == TunnelType.TUNNELLED);
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final LayerType getLayerType() {
+        return this.layered;
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final boolean isLayered() {
+        return (this.layered == LayerType.LAYERED);
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final boolean isSecure() {
+        return this.secure;
+    }
+
+
+    /**
+     * Compares this route to another.
+     *
+     * @param o         the object to compare with
+     *
+     * @return  <code>true</code> if the argument is the same route,
+     *          <code>false</code>
+     */
+    @Override
+    public final boolean equals(Object o) {
+        if (o == this)
+            return true;
+        if (!(o instanceof HttpRoute))
+            return false;
+
+        HttpRoute that = (HttpRoute) o;
+        boolean equal = this.targetHost.equals(that.targetHost);
+        equal &=
+            ( this.localAddress == that.localAddress) ||
+            ((this.localAddress != null) &&
+              this.localAddress.equals(that.localAddress));
+        equal &=
+            ( this.proxyChain        == that.proxyChain) ||
+            ((this.proxyChain        != null) &&
+             (that.proxyChain        != null) &&
+             (this.proxyChain.length == that.proxyChain.length));
+        // comparison of actual proxies follows below
+        equal &=
+            (this.secure    == that.secure) &&
+            (this.tunnelled == that.tunnelled) &&
+            (this.layered   == that.layered);
+
+        // chain length has been compared above, now check the proxies
+        if (equal && (this.proxyChain != null)) {
+            for (int i=0; equal && (i<this.proxyChain.length); i++)
+                equal = this.proxyChain[i].equals(that.proxyChain[i]);
+        }
+
+        return equal;
+    }
+
+
+    /**
+     * Generates a hash code for this route.
+     *
+     * @return  the hash code
+     */
+    @Override
+    public final int hashCode() {
+
+        int hc = this.targetHost.hashCode();
+
+        if (this.localAddress != null)
+            hc ^= localAddress.hashCode();
+        if (this.proxyChain != null) {
+            hc ^= proxyChain.length;
+            for (HttpHost aProxyChain : proxyChain) hc ^= aProxyChain.hashCode();
+        }
+
+        if (this.secure)
+            hc ^= 0x11111111;
+
+        hc ^= this.tunnelled.hashCode();
+        hc ^= this.layered.hashCode();
+
+        return hc;
+    }
+
+
+    /**
+     * Obtains a description of this route.
+     *
+     * @return  a human-readable representation of this route
+     */
+    @Override
+    public final String toString() {
+        StringBuilder cab = new StringBuilder(50 + getHopCount()*30);
+
+        cab.append("HttpRoute[");
+        if (this.localAddress != null) {
+            cab.append(this.localAddress);
+            cab.append("->");
+        }
+        cab.append('{');
+        if (this.tunnelled == TunnelType.TUNNELLED)
+            cab.append('t');
+        if (this.layered == LayerType.LAYERED)
+            cab.append('l');
+        if (this.secure)
+            cab.append('s');
+        cab.append("}->");
+        if (this.proxyChain != null) {
+            for (HttpHost aProxyChain : this.proxyChain) {
+                cab.append(aProxyChain);
+                cab.append("->");
+            }
+        }
+        cab.append(this.targetHost);
+        cab.append(']');
+
+        return cab.toString();
+    }
+
+
+    // default implementation of clone() is sufficient
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+
+
+} // class HttpRoute
diff --git a/src/org/apache/http/conn/routing/HttpRouteDirector.java b/src/org/apache/http/conn/routing/HttpRouteDirector.java
new file mode 100644
index 0000000..8cfcf67
--- /dev/null
+++ b/src/org/apache/http/conn/routing/HttpRouteDirector.java
@@ -0,0 +1,88 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/routing/HttpRouteDirector.java $
+ * $Revision: 620255 $
+ * $Date: 2008-02-10 02:23:55 -0800 (Sun, 10 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.routing;
+
+
+
+/**
+ * Provides directions on establishing a route.
+ * Implementations of this interface compare a planned route with
+ * a tracked route and indicate the next step required.
+ * 
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 620255 $
+ *
+ * @since 4.0
+ */
+public interface HttpRouteDirector {
+
+    /** Indicates that the route can not be established at all. */
+    public final static int UNREACHABLE = -1;
+
+    /** Indicates that the route is complete. */
+    public final static int COMPLETE = 0;
+
+    /** Step: open connection to target. */
+    public final static int CONNECT_TARGET = 1;
+
+    /** Step: open connection to proxy. */
+    public final static int CONNECT_PROXY = 2;
+
+    /** Step: tunnel through proxy to target. */
+    public final static int TUNNEL_TARGET = 3;
+
+    /** Step: tunnel through proxy to other proxy. */
+    public final static int TUNNEL_PROXY = 4;
+
+    /** Step: layer protocol (over tunnel). */
+    public final static int LAYER_PROTOCOL = 5;
+
+
+    /**
+     * Provides the next step.
+     *
+     * @param plan      the planned route
+     * @param fact      the currently established route, or
+     *                  <code>null</code> if nothing is established
+     *
+     * @return  one of the constants defined in this interface, indicating
+     *          either the next step to perform, or success, or failure.
+     *          0 is for success, a negative value for failure.
+     */
+    public int nextStep(RouteInfo plan, RouteInfo fact)
+        ;
+
+
+} // interface HttpRouteDirector
diff --git a/src/org/apache/http/conn/routing/HttpRoutePlanner.java b/src/org/apache/http/conn/routing/HttpRoutePlanner.java
new file mode 100644
index 0000000..489702a
--- /dev/null
+++ b/src/org/apache/http/conn/routing/HttpRoutePlanner.java
@@ -0,0 +1,69 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/routing/HttpRoutePlanner.java $
+ * $Revision: 613654 $
+ * $Date: 2008-01-20 11:00:19 -0800 (Sun, 20 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.routing;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.protocol.HttpContext;
+
+
+
+/**
+ * Encapsulates logic to compute a {@link HttpRoute} to a target host.
+ * Implementations may for example be based on parameters, or on the
+ * standard Java system properties.
+ */
+public interface HttpRoutePlanner {
+    
+    /**
+     * Determines the route for a request.
+     *
+     * @param target    the target host for the request.
+     *                  Implementations may accept <code>null</code>
+     *                  if they can still determine a route, for example
+     *                  to a default target or by inspecting the request.
+     * @param request   the request to execute
+     * @param context   the context to use for the subsequent execution.
+     *                  Implementations may accept <code>null</code>.
+     *
+     * @return  the route that the request should take
+     *
+     * @throws HttpException    in case of a problem
+     */        
+    public HttpRoute determineRoute(HttpHost target,
+                                    HttpRequest request,
+                                    HttpContext context)
+        throws HttpException
+        ;
+    
+}
diff --git a/src/org/apache/http/conn/routing/RouteInfo.java b/src/org/apache/http/conn/routing/RouteInfo.java
new file mode 100644
index 0000000..3449cb1
--- /dev/null
+++ b/src/org/apache/http/conn/routing/RouteInfo.java
@@ -0,0 +1,194 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/routing/RouteInfo.java $
+ * $Revision: 652200 $
+ * $Date: 2008-04-29 17:22:43 -0700 (Tue, 29 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.routing;
+
+import java.net.InetAddress;
+
+import org.apache.http.HttpHost;
+
+
+/**
+ * Read-only interface for route information.
+ * 
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 652200 $
+ *
+ * @since 4.0
+ */
+public interface RouteInfo {
+
+    /**
+     * The tunnelling type of a route.
+     * Plain routes are established by connecting to the target or
+     * the first proxy.
+     * Tunnelled routes are established by connecting to the first proxy
+     * and tunnelling through all proxies to the target.
+     * Routes without a proxy cannot be tunnelled.
+     */
+    public enum TunnelType { PLAIN, TUNNELLED }
+
+    /**
+     * The layering type of a route.
+     * Plain routes are established by connecting or tunnelling.
+     * Layered routes are established by layering a protocol such as TLS/SSL
+     * over an existing connection.
+     * Protocols can only be layered over a tunnel to the target, or
+     * or over a direct connection without proxies.
+     * <br/>
+     * Layering a protocol
+     * over a direct connection makes little sense, since the connection
+     * could be established with the new protocol in the first place.
+     * But we don't want to exclude that use case.
+     */
+    public enum LayerType  { PLAIN, LAYERED }
+
+
+
+    /**
+     * Obtains the target host.
+     * 
+     * @return the target host
+     */
+    HttpHost getTargetHost()
+        ;
+
+
+    /**
+     * Obtains the local address to connect from.
+     * 
+     * @return  the local address,
+     *          or <code>null</code>
+     */
+    InetAddress getLocalAddress()
+        ;
+
+
+    /**
+     * Obtains the number of hops in this route.
+     * A direct route has one hop. A route through a proxy has two hops.
+     * A route through a chain of <i>n</i> proxies has <i>n+1</i> hops.
+     *
+     * @return  the number of hops in this route
+     */
+    int getHopCount()
+        ;
+
+
+    /**
+     * Obtains the target of a hop in this route.
+     * The target of the last hop is the {@link #getTargetHost target host},
+     * the target of previous hops is the respective proxy in the chain.
+     * For a route through exactly one proxy, target of hop 0 is the proxy
+     * and target of hop 1 is the target host.
+     *
+     * @param hop       index of the hop for which to get the target,
+     *                  0 for first
+     *
+     * @return  the target of the given hop
+     *
+     * @throws IllegalArgumentException
+     *  if the argument is negative or not less than
+     *  {@link #getHopCount getHopCount()}
+     */
+    HttpHost getHopTarget(int hop)
+        ;
+
+
+    /**
+     * Obtains the first proxy host.
+     * 
+     * @return the first proxy in the proxy chain, or
+     *         <code>null</code> if this route is direct
+     */
+    HttpHost getProxyHost()
+        ;
+
+
+    /**
+     * Obtains the tunnel type of this route.
+     * If there is a proxy chain, only end-to-end tunnels are considered.
+     *
+     * @return  the tunnelling type
+     */
+    TunnelType getTunnelType()
+        ;
+
+
+    /**
+     * Checks whether this route is tunnelled through a proxy.
+     * If there is a proxy chain, only end-to-end tunnels are considered.
+     *
+     * @return  <code>true</code> if tunnelled end-to-end through at least
+     *          one proxy,
+     *          <code>false</code> otherwise
+     */
+    boolean isTunnelled()
+        ;
+
+
+    /**
+     * Obtains the layering type of this route.
+     * In the presence of proxies, only layering over an end-to-end tunnel
+     * is considered.
+     *
+     * @return  the layering type
+     */
+    LayerType getLayerType()
+        ;
+
+
+    /**
+     * Checks whether this route includes a layered protocol.
+     * In the presence of proxies, only layering over an end-to-end tunnel
+     * is considered.
+     *
+     * @return  <code>true</code> if layered,
+     *          <code>false</code> otherwise
+     */
+    boolean isLayered()
+        ;
+
+
+    /**
+     * Checks whether this route is secure.
+     *
+     * @return  <code>true</code> if secure,
+     *          <code>false</code> otherwise
+     */
+    boolean isSecure()
+        ;
+
+
+} // interface RouteInfo
diff --git a/src/org/apache/http/conn/routing/RouteTracker.java b/src/org/apache/http/conn/routing/RouteTracker.java
new file mode 100644
index 0000000..ba8213e
--- /dev/null
+++ b/src/org/apache/http/conn/routing/RouteTracker.java
@@ -0,0 +1,439 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/routing/RouteTracker.java $
+ * $Revision: 620254 $
+ * $Date: 2008-02-10 02:18:48 -0800 (Sun, 10 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.routing;
+
+import java.net.InetAddress;
+
+import org.apache.http.HttpHost;
+
+
+/**
+ * Helps tracking the steps in establishing a route.
+ * 
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 620254 $
+ *
+ * @since 4.0
+ */
+public final class RouteTracker implements RouteInfo, Cloneable {
+
+    /** The target host to connect to. */
+    private final HttpHost targetHost;
+
+    /**
+     * The local address to connect from.
+     * <code>null</code> indicates that the default should be used.
+     */
+    private final InetAddress localAddress;
+
+    // the attributes above are fixed at construction time
+    // now follow attributes that indicate the established route
+
+    /** Whether the first hop of the route is established. */
+    private boolean connected;
+
+    /** The proxy chain, if any. */
+    private HttpHost[] proxyChain;
+
+    /** Whether the the route is tunnelled end-to-end through proxies. */
+    private TunnelType tunnelled;
+
+    /** Whether the route is layered over a tunnel. */
+    private LayerType layered;
+
+    /** Whether the route is secure. */
+    private boolean secure;
+
+
+    /**
+     * Creates a new route tracker.
+     * The target and origin need to be specified at creation time.
+     *
+     * @param target    the host to which to route
+     * @param local     the local address to route from, or
+     *                  <code>null</code> for the default
+     */
+    public RouteTracker(HttpHost target, InetAddress local) {
+        if (target == null) {
+            throw new IllegalArgumentException("Target host may not be null.");
+        }
+        this.targetHost   = target;
+        this.localAddress = local;
+        this.tunnelled    = TunnelType.PLAIN;
+        this.layered      = LayerType.PLAIN;
+    }
+
+
+    /**
+     * Creates a new tracker for the given route.
+     * Only target and origin are taken from the route,
+     * everything else remains to be tracked.
+     *
+     * @param route     the route to track
+     */
+    public RouteTracker(HttpRoute route) {
+        this(route.getTargetHost(), route.getLocalAddress());
+    }
+
+
+    /**
+     * Tracks connecting to the target.
+     *
+     * @param secure    <code>true</code> if the route is secure,
+     *                  <code>false</code> otherwise
+     */
+    public final void connectTarget(boolean secure) {
+        if (this.connected) {
+            throw new IllegalStateException("Already connected.");
+        }
+        this.connected = true;
+        this.secure = secure;
+    }
+
+
+    /**
+     * Tracks connecting to the first proxy.
+     *
+     * @param proxy     the proxy connected to
+     * @param secure    <code>true</code> if the route is secure,
+     *                  <code>false</code> otherwise
+     */
+    public final void connectProxy(HttpHost proxy, boolean secure) {
+        if (proxy == null) {
+            throw new IllegalArgumentException("Proxy host may not be null.");
+        }
+        if (this.connected) {
+            throw new IllegalStateException("Already connected.");
+        }
+        this.connected  = true;
+        this.proxyChain = new HttpHost[]{ proxy };
+        this.secure     = secure;
+    }
+
+
+    /**
+     * Tracks tunnelling to the target.
+     *
+     * @param secure    <code>true</code> if the route is secure,
+     *                  <code>false</code> otherwise
+     */
+    public final void tunnelTarget(boolean secure) {
+        if (!this.connected) {
+            throw new IllegalStateException("No tunnel unless connected.");
+        }
+        if (this.proxyChain == null) {
+            throw new IllegalStateException("No tunnel without proxy.");
+        }
+        this.tunnelled = TunnelType.TUNNELLED;
+        this.secure    = secure;
+    }
+
+
+    /**
+     * Tracks tunnelling to a proxy in a proxy chain.
+     * This will extend the tracked proxy chain, but it does not mark
+     * the route as tunnelled. Only end-to-end tunnels are considered there.
+     *
+     * @param proxy     the proxy tunnelled to
+     * @param secure    <code>true</code> if the route is secure,
+     *                  <code>false</code> otherwise
+     */
+    public final void tunnelProxy(HttpHost proxy, boolean secure) {
+        if (proxy == null) {
+            throw new IllegalArgumentException("Proxy host may not be null.");
+        }
+        if (!this.connected) {
+            throw new IllegalStateException("No tunnel unless connected.");
+        }
+        if (this.proxyChain == null) {
+            throw new IllegalStateException("No proxy tunnel without proxy.");
+        }
+
+        // prepare an extended proxy chain
+        HttpHost[] proxies = new HttpHost[this.proxyChain.length+1];
+        System.arraycopy(this.proxyChain, 0,
+                         proxies, 0, this.proxyChain.length);
+        proxies[proxies.length-1] = proxy;
+
+        this.proxyChain = proxies;
+        this.secure     = secure;
+    }
+
+
+    /**
+     * Tracks layering a protocol.
+     *
+     * @param secure    <code>true</code> if the route is secure,
+     *                  <code>false</code> otherwise
+     */
+    public final void layerProtocol(boolean secure) {
+        // it is possible to layer a protocol over a direct connection,
+        // although this case is probably not considered elsewhere
+        if (!this.connected) {
+            throw new IllegalStateException
+                ("No layered protocol unless connected.");
+        }
+        this.layered = LayerType.LAYERED;
+        this.secure  = secure;
+    }
+
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final HttpHost getTargetHost() {
+        return this.targetHost;
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final InetAddress getLocalAddress() {
+        return this.localAddress;
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final int getHopCount() {
+        int hops = 0;
+        if (this.connected) {
+            if (proxyChain == null)
+                hops = 1;
+            else
+                hops = proxyChain.length + 1;
+        }
+        return hops;
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final HttpHost getHopTarget(int hop) {
+        if (hop < 0)
+            throw new IllegalArgumentException
+                ("Hop index must not be negative: " + hop);
+        final int hopcount = getHopCount();
+        if (hop >= hopcount) {
+            throw new IllegalArgumentException
+                ("Hop index " + hop +
+                 " exceeds tracked route length " + hopcount +".");
+        }
+
+        HttpHost result = null;
+        if (hop < hopcount-1)
+            result = this.proxyChain[hop];
+        else
+            result = this.targetHost;
+
+        return result;
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final HttpHost getProxyHost() {
+        return (this.proxyChain == null) ? null : this.proxyChain[0];
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final boolean isConnected() {
+        return this.connected;
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final TunnelType getTunnelType() {
+        return this.tunnelled;
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final boolean isTunnelled() {
+        return (this.tunnelled == TunnelType.TUNNELLED);
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final LayerType getLayerType() {
+        return this.layered;
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final boolean isLayered() {
+        return (this.layered == LayerType.LAYERED);
+    }
+
+
+    // non-JavaDoc, see interface RouteInfo
+    public final boolean isSecure() {
+        return this.secure;
+    }
+
+
+    /**
+     * Obtains the tracked route.
+     * If a route has been tracked, it is {@link #isConnected connected}.
+     * If not connected, nothing has been tracked so far.
+     *
+     * @return  the tracked route, or
+     *          <code>null</code> if nothing has been tracked so far
+     */
+    public final HttpRoute toRoute() {
+        return !this.connected ?
+            null : new HttpRoute(this.targetHost, this.localAddress,
+                                 this.proxyChain, this.secure,
+                                 this.tunnelled, this.layered);
+    }
+
+
+    /**
+     * Compares this tracked route to another.
+     *
+     * @param o         the object to compare with
+     *
+     * @return  <code>true</code> if the argument is the same tracked route,
+     *          <code>false</code>
+     */
+    @Override
+    public final boolean equals(Object o) {
+        if (o == this)
+            return true;
+        if (!(o instanceof RouteTracker))
+            return false;
+
+        RouteTracker that = (RouteTracker) o;
+        boolean equal = this.targetHost.equals(that.targetHost);
+        equal &=
+            ( this.localAddress == that.localAddress) ||
+            ((this.localAddress != null) &&
+              this.localAddress.equals(that.localAddress));
+        equal &=
+            ( this.proxyChain        == that.proxyChain) ||
+            ((this.proxyChain        != null) &&
+             (that.proxyChain        != null) &&
+             (this.proxyChain.length == that.proxyChain.length));
+        // comparison of actual proxies follows below
+        equal &=
+            (this.connected == that.connected) &&
+            (this.secure    == that.secure) &&
+            (this.tunnelled == that.tunnelled) &&
+            (this.layered   == that.layered);
+
+        // chain length has been compared above, now check the proxies
+        if (equal && (this.proxyChain != null)) {
+            for (int i=0; equal && (i<this.proxyChain.length); i++)
+                equal = this.proxyChain[i].equals(that.proxyChain[i]);
+        }
+
+        return equal;
+    }
+
+
+    /**
+     * Generates a hash code for this tracked route.
+     * Route trackers are modifiable and should therefore not be used
+     * as lookup keys. Use {@link #toRoute toRoute} to obtain an
+     * unmodifiable representation of the tracked route.
+     *
+     * @return  the hash code
+     */
+    @Override
+    public final int hashCode() {
+
+        int hc = this.targetHost.hashCode();
+
+        if (this.localAddress != null)
+            hc ^= localAddress.hashCode();
+        if (this.proxyChain != null) {
+            hc ^= proxyChain.length;
+            for (int i=0; i<proxyChain.length; i++)
+                hc ^= proxyChain[i].hashCode();
+        }
+
+        if (this.connected)
+            hc ^= 0x11111111;
+        if (this.secure)
+            hc ^= 0x22222222;
+
+        hc ^= this.tunnelled.hashCode();
+        hc ^= this.layered.hashCode();
+
+        return hc;
+    }
+
+
+    /**
+     * Obtains a description of the tracked route.
+     *
+     * @return  a human-readable representation of the tracked route
+     */
+    @Override
+    public final String toString() {
+        StringBuilder cab = new StringBuilder(50 + getHopCount()*30);
+
+        cab.append("RouteTracker[");
+        if (this.localAddress != null) {
+            cab.append(this.localAddress);
+            cab.append("->");
+        }
+        cab.append('{');
+        if (this.connected)
+            cab.append('c');
+        if (this.tunnelled == TunnelType.TUNNELLED)
+            cab.append('t');
+        if (this.layered == LayerType.LAYERED)
+            cab.append('l');
+        if (this.secure)
+            cab.append('s');
+        cab.append("}->");
+        if (this.proxyChain != null) {
+            for (int i=0; i<this.proxyChain.length; i++) {
+                cab.append(this.proxyChain[i]);
+                cab.append("->");
+            }
+        }
+        cab.append(this.targetHost);
+        cab.append(']');
+
+        return cab.toString();
+    }
+
+
+    // default implementation of clone() is sufficient
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+
+
+} // class RouteTracker
diff --git a/src/org/apache/http/conn/routing/package.html b/src/org/apache/http/conn/routing/package.html
new file mode 100644
index 0000000..b50f97c
--- /dev/null
+++ b/src/org/apache/http/conn/routing/package.html
@@ -0,0 +1,62 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/routing/package.html $
+ * $Revision: 613656 $
+ * $Date: 2008-01-20 11:06:56 -0800 (Sun, 20 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+The client-side route representation and tracking API, part of <i>HttpConn</i>.
+
+<p>
+An {@link org.apache.http.conn.routing.HttpRoute HttpRoute}
+is the path along which a request has to be sent to the server.
+The route starts at a local network address and may pass
+through one or more proxies before reaching the target.
+Routes through proxies can be tunnelled, and a layered protocol (TLS/SSL)
+might be put on top of the tunnel.
+The {@link org.apache.http.conn.routing.RouteTracker RouteTracker}
+helps in tracking the steps for establishing a route, while an
+{@link org.apache.http.conn.routing.HttpRouteDirector HttpRouteDirector}
+determines the next step to take.
+</p>
+
+
+<p>
+The {@link org.apache.http.conn.routing.HttpRoutePlanner HttpRoutePlanner}
+is responsible for determining a route to a given target host.
+Implementations must know about proxies to use, and about exemptions
+for hosts that should be contacted directly without a proxy.
+</p>
+
+
+</body>
+</html>
diff --git a/src/org/apache/http/conn/scheme/HostNameResolver.java b/src/org/apache/http/conn/scheme/HostNameResolver.java
new file mode 100644
index 0000000..ca6615c
--- /dev/null
+++ b/src/org/apache/http/conn/scheme/HostNameResolver.java
@@ -0,0 +1,41 @@
+/*
+ * $HeadURL:$
+ * $Revision:$
+ * $Date:$
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.scheme;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+public interface HostNameResolver {
+
+    InetAddress resolve (String hostname) throws IOException;
+
+}
diff --git a/src/org/apache/http/conn/scheme/LayeredSocketFactory.java b/src/org/apache/http/conn/scheme/LayeredSocketFactory.java
new file mode 100644
index 0000000..8dc6c6c
--- /dev/null
+++ b/src/org/apache/http/conn/scheme/LayeredSocketFactory.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/scheme/LayeredSocketFactory.java $
+ * $Revision: 645850 $
+ * $Date: 2008-04-08 04:08:52 -0700 (Tue, 08 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.scheme;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+/**
+ * A {@link SocketFactory SocketFactory} for layered sockets (SSL/TLS).
+ * See there for things to consider when implementing a socket factory.
+ * 
+ * @author Michael Becke
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @since 4.0
+ */
+public interface LayeredSocketFactory extends SocketFactory {
+
+    /**
+     * Returns a socket connected to the given host that is layered over an
+     * existing socket.  Used primarily for creating secure sockets through
+     * proxies.
+     * 
+     * @param socket the existing socket 
+     * @param host the host name/IP
+     * @param port the port on the host
+     * @param autoClose a flag for closing the underling socket when the created
+     * socket is closed
+     * 
+     * @return Socket a new socket
+     * 
+     * @throws IOException if an I/O error occurs while creating the socket
+     * @throws UnknownHostException if the IP address of the host cannot be
+     * determined
+     */
+    Socket createSocket(
+        Socket socket, 
+        String host, 
+        int port, 
+        boolean autoClose
+    ) throws IOException, UnknownHostException;              
+
+}
diff --git a/src/org/apache/http/conn/scheme/PlainSocketFactory.java b/src/org/apache/http/conn/scheme/PlainSocketFactory.java
new file mode 100644
index 0000000..acc13f7
--- /dev/null
+++ b/src/org/apache/http/conn/scheme/PlainSocketFactory.java
@@ -0,0 +1,182 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/scheme/PlainSocketFactory.java $
+ * $Revision: 659194 $
+ * $Date: 2008-05-22 11:33:47 -0700 (Thu, 22 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.scheme;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+/**
+ * The default class for creating sockets.
+ * 
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author Michael Becke
+ */
+public final class PlainSocketFactory implements SocketFactory {
+
+    /**
+     * The factory singleton.
+     */
+    private static final
+        PlainSocketFactory DEFAULT_FACTORY = new PlainSocketFactory();
+
+    private final HostNameResolver nameResolver;
+    
+    /**
+     * Gets the singleton instance of this class.
+     * @return the one and only plain socket factory
+     */
+    public static PlainSocketFactory getSocketFactory() {
+        return DEFAULT_FACTORY;
+    }
+
+    public PlainSocketFactory(final HostNameResolver nameResolver) {
+        super();
+        this.nameResolver = nameResolver;
+    }
+
+
+    public PlainSocketFactory() {
+        this(null);
+    }
+
+    // non-javadoc, see interface org.apache.http.conn.SocketFactory
+    public Socket createSocket() {
+        return new Socket();
+    }
+
+    // non-javadoc, see interface org.apache.http.conn.SocketFactory
+    public Socket connectSocket(Socket sock, String host, int port, 
+                                InetAddress localAddress, int localPort,
+                                HttpParams params)
+        throws IOException {
+
+        if (host == null) {
+            throw new IllegalArgumentException("Target host may not be null.");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException("Parameters may not be null.");
+        }
+
+        if (sock == null)
+            sock = createSocket();
+
+        if ((localAddress != null) || (localPort > 0)) {
+
+            // we need to bind explicitly
+            if (localPort < 0)
+                localPort = 0; // indicates "any"
+
+            InetSocketAddress isa =
+                new InetSocketAddress(localAddress, localPort);
+            sock.bind(isa);
+        }
+
+        int timeout = HttpConnectionParams.getConnectionTimeout(params);
+
+        InetSocketAddress remoteAddress;
+        if (this.nameResolver != null) {
+            remoteAddress = new InetSocketAddress(this.nameResolver.resolve(host), port); 
+        } else {
+            remoteAddress = new InetSocketAddress(host, port);            
+        }
+        
+        sock.connect(remoteAddress, timeout);
+
+        return sock;
+
+    } // connectSocket
+
+
+    /**
+     * Checks whether a socket connection is secure.
+     * This factory creates plain socket connections
+     * which are not considered secure.
+     *
+     * @param sock      the connected socket
+     *
+     * @return  <code>false</code>
+     *
+     * @throws IllegalArgumentException if the argument is invalid
+     */
+    public final boolean isSecure(Socket sock)
+        throws IllegalArgumentException {
+
+        if (sock == null) {
+            throw new IllegalArgumentException("Socket may not be null.");
+        }
+        // This class check assumes that createSocket() calls the constructor
+        // directly. If it was using javax.net.SocketFactory, we couldn't make
+        // an assumption about the socket class here.
+        if (sock.getClass() != Socket.class) {
+            throw new IllegalArgumentException
+                ("Socket not created by this factory.");
+        }
+        // This check is performed last since it calls a method implemented
+        // by the argument object. getClass() is final in java.lang.Object.
+        if (sock.isClosed()) {
+            throw new IllegalArgumentException("Socket is closed.");
+        }
+
+        return false;
+
+    } // isSecure
+
+
+    /**
+     * Compares this factory with an object.
+     * There is only one instance of this class.
+     *
+     * @param obj       the object to compare with
+     *
+     * @return  iff the argument is this object
+     */
+    @Override
+    public boolean equals(Object obj) {
+        return (obj == this);
+    }
+
+    /**
+     * Obtains a hash code for this object.
+     * All instances of this class have the same hash code.
+     * There is only one instance of this class.
+     */
+    @Override
+    public int hashCode() {
+        return PlainSocketFactory.class.hashCode();
+    }
+
+}
diff --git a/src/org/apache/http/conn/scheme/Scheme.java b/src/org/apache/http/conn/scheme/Scheme.java
new file mode 100644
index 0000000..590d59d
--- /dev/null
+++ b/src/org/apache/http/conn/scheme/Scheme.java
@@ -0,0 +1,223 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/scheme/Scheme.java $
+ * $Revision: 652950 $
+ * $Date: 2008-05-02 16:49:48 -0700 (Fri, 02 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.conn.scheme;
+
+import java.util.Locale;
+
+import org.apache.http.util.LangUtils;
+
+/**
+ * Encapsulates specifics of a protocol scheme such as "http" or "https".
+ * Schemes are identified by lowercase names.
+ * Supported schemes are typically collected in a
+ * {@link SchemeRegistry SchemeRegistry}.
+ *
+ * <p>
+ * For example, to configure support for "https://" URLs,
+ * you could write code like the following:
+ * </p>
+ * <pre>
+ * Scheme https = new Scheme("https", new MySecureSocketFactory(), 443);
+ * SchemeRegistry.DEFAULT.register(https);
+ * </pre>
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author Michael Becke 
+ * @author Jeff Dever
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ */
+public final class Scheme {
+
+    /** The name of this scheme, in lowercase. (e.g. http, https) */
+    private final String name;
+    
+    /** The socket factory for this scheme */
+    private final SocketFactory socketFactory;
+    
+    /** The default port for this scheme */
+    private final int defaultPort;
+    
+    /** Indicates whether this scheme allows for layered connections */
+    private final boolean layered;
+
+
+    /** A string representation, for {@link #toString toString}. */
+    private String stringRep;
+
+
+    /**
+     * Creates a new scheme.
+     * Whether the created scheme allows for layered connections
+     * depends on the class of <code>factory</code>.
+     *
+     * @param name      the scheme name, for example "http".
+     *                  The name will be converted to lowercase.
+     * @param factory   the factory for creating sockets for communication
+     *                  with this scheme
+     * @param port      the default port for this scheme
+     */
+    public Scheme(final String name,
+                  final SocketFactory factory,
+                  final int port) {
+
+        if (name == null) {
+            throw new IllegalArgumentException
+                ("Scheme name may not be null");
+        }
+        if (factory == null) {
+            throw new IllegalArgumentException
+                ("Socket factory may not be null");
+        }
+        if ((port <= 0) || (port > 0xffff)) {
+            throw new IllegalArgumentException
+                ("Port is invalid: " + port);
+        }
+
+        this.name = name.toLowerCase(Locale.ENGLISH);
+        this.socketFactory = factory;
+        this.defaultPort = port;
+        this.layered = (factory instanceof LayeredSocketFactory);
+    }
+
+
+    /**
+     * Obtains the default port.
+     *
+     * @return  the default port for this scheme
+     */
+    public final int getDefaultPort() {
+        return defaultPort;
+    }
+
+
+    /**
+     * Obtains the socket factory.
+     * If this scheme is {@link #isLayered layered}, the factory implements
+     * {@link LayeredSocketFactory LayeredSocketFactory}.
+     *
+     * @return  the socket factory for this scheme
+     */
+    public final SocketFactory getSocketFactory() {
+        return socketFactory;
+    }
+
+
+    /**
+     * Obtains the scheme name.
+     *
+     * @return  the name of this scheme, in lowercase
+     */
+    public final String getName() {
+        return name;
+    }
+
+
+    /**
+     * Indicates whether this scheme allows for layered connections.
+     *
+     * @return <code>true</code> if layered connections are possible,
+     *         <code>false</code> otherwise
+     */
+    public final boolean isLayered() {
+        return layered;
+    }
+
+
+    /**
+     * Resolves the correct port for this scheme.
+     * Returns the given port if it is valid, the default port otherwise.
+     * 
+     * @param port      the port to be resolved,
+     *                  a negative number to obtain the default port
+     * 
+     * @return the given port or the defaultPort
+     */
+    public final int resolvePort(int port) {
+        return ((port <= 0) || (port > 0xffff)) ? defaultPort : port;
+    }
+
+
+    /**
+     * Return a string representation of this object.
+     *
+     * @return  a human-readable string description of this scheme
+     */
+    @Override
+    public final String toString() {
+        if (stringRep == null) {
+            StringBuilder buffer = new StringBuilder();
+            buffer.append(this.name);
+            buffer.append(':');
+            buffer.append(Integer.toString(this.defaultPort));
+            stringRep = buffer.toString();
+        }
+        return stringRep;
+    }
+
+
+    /**
+     * Compares this scheme to an object.
+     *
+     * @param obj       the object to compare with
+     *
+     * @return  <code>true</code> iff the argument is equal to this scheme
+     */
+    @Override
+    public final boolean equals(Object obj) {
+        if (obj == null) return false;
+        if (this == obj) return true;
+        if (!(obj instanceof Scheme)) return false;
+
+        Scheme s = (Scheme) obj;
+        return (name.equals(s.name) &&
+                defaultPort == s.defaultPort &&
+                layered == s.layered &&
+                socketFactory.equals(s.socketFactory)
+                );
+    } // equals
+
+
+    /**
+     * Obtains a hash code for this scheme.
+     *
+     * @return  the hash code
+     */
+    @Override
+    public int hashCode() {
+        int hash = LangUtils.HASH_SEED;
+        hash = LangUtils.hashCode(hash, this.defaultPort);
+        hash = LangUtils.hashCode(hash, this.name);
+        hash = LangUtils.hashCode(hash, this.layered);
+        hash = LangUtils.hashCode(hash, this.socketFactory);
+        return hash;
+    }
+
+} // class Scheme
diff --git a/src/org/apache/http/conn/scheme/SchemeRegistry.java b/src/org/apache/http/conn/scheme/SchemeRegistry.java
new file mode 100644
index 0000000..2ee8685
--- /dev/null
+++ b/src/org/apache/http/conn/scheme/SchemeRegistry.java
@@ -0,0 +1,187 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/scheme/SchemeRegistry.java $
+ * $Revision: 648356 $
+ * $Date: 2008-04-15 10:57:53 -0700 (Tue, 15 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.conn.scheme;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.http.HttpHost;
+
+/**
+ * A set of supported protocol {@link Scheme schemes}.
+ * Schemes are identified by lowercase names.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version   $Revision: 648356 $ $Date: 2008-04-15 10:57:53 -0700 (Tue, 15 Apr 2008) $
+ *
+ * @since 4.0
+ */
+public final class SchemeRegistry {
+
+    /** The available schemes in this registry. */
+    private final Map<String,Scheme> registeredSchemes;
+
+
+    /**
+     * Creates a new, empty scheme registry.
+     */
+    public SchemeRegistry() {
+        super();
+        registeredSchemes = new LinkedHashMap<String,Scheme>();
+    }
+
+
+    /**
+     * Obtains a scheme by name.
+     *
+     * @param name      the name of the scheme to look up (in lowercase)
+     *
+     * @return  the scheme, never <code>null</code>
+     *
+     * @throws IllegalStateException
+     *          if the scheme with the given name is not registered
+     */
+    public synchronized final Scheme getScheme(String name) {
+        Scheme found = get(name);
+        if (found == null) {
+            throw new IllegalStateException
+                ("Scheme '"+name+"' not registered.");
+        }
+        return found;
+    }
+
+
+    /**
+     * Obtains the scheme for a host.
+     * Convenience method for <code>getScheme(host.getSchemeName())</pre>
+     *
+     * @param host      the host for which to obtain the scheme
+     *
+     * @return  the scheme for the given host, never <code>null</code>
+     *
+     * @throws IllegalStateException
+     *          if a scheme with the respective name is not registered
+     */
+    public synchronized final Scheme getScheme(HttpHost host) {
+        if (host == null) {
+            throw new IllegalArgumentException("Host must not be null.");
+        }
+        return getScheme(host.getSchemeName());
+    }
+
+
+    /**
+     * Obtains a scheme by name, if registered.
+     *
+     * @param name      the name of the scheme to look up (in lowercase)
+     *
+     * @return  the scheme, or
+     *          <code>null</code> if there is none by this name
+     */
+    public synchronized final Scheme get(String name) {
+        if (name == null)
+            throw new IllegalArgumentException("Name must not be null.");
+
+        // leave it to the caller to use the correct name - all lowercase
+        //name = name.toLowerCase();
+        Scheme found = registeredSchemes.get(name);
+        return found;
+    }
+
+
+    /**
+     * Registers a scheme.
+     * The scheme can later be retrieved by its name
+     * using {@link #getScheme(String) getScheme} or {@link #get get}.
+     *
+     * @param sch       the scheme to register
+     *
+     * @return  the scheme previously registered with that name, or
+     *          <code>null</code> if none was registered
+     */
+    public synchronized final Scheme register(Scheme sch) {
+        if (sch == null)
+            throw new IllegalArgumentException("Scheme must not be null.");
+
+        Scheme old = registeredSchemes.put(sch.getName(), sch);
+        return old;
+    }
+
+
+    /**
+     * Unregisters a scheme.
+     *
+     * @param name      the name of the scheme to unregister (in lowercase)
+     *
+     * @return  the unregistered scheme, or
+     *          <code>null</code> if there was none
+     */
+    public synchronized final Scheme unregister(String name) {
+        if (name == null)
+            throw new IllegalArgumentException("Name must not be null.");
+
+        // leave it to the caller to use the correct name - all lowercase
+        //name = name.toLowerCase();
+        Scheme gone = registeredSchemes.remove(name);
+        return gone;
+    }
+
+
+    /**
+     * Obtains the names of the registered schemes in their default order.
+     *
+     * @return  List containing registered scheme names.
+     */
+    public synchronized final List<String> getSchemeNames() {
+        return new ArrayList<String>(registeredSchemes.keySet());
+    }
+
+    /**
+     * Populates the internal collection of registered {@link Scheme protocol schemes} 
+     * with the content of the map passed as a parameter.
+     * 
+     * @param map protocol schemes
+     */
+    public synchronized void setItems(final Map<String, Scheme> map) {
+        if (map == null) {
+            return;
+        }
+        registeredSchemes.clear();
+        registeredSchemes.putAll(map);
+    }
+
+} // class SchemeRegistry
+
diff --git a/src/org/apache/http/conn/scheme/SocketFactory.java b/src/org/apache/http/conn/scheme/SocketFactory.java
new file mode 100644
index 0000000..bb553b2
--- /dev/null
+++ b/src/org/apache/http/conn/scheme/SocketFactory.java
@@ -0,0 +1,138 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/scheme/SocketFactory.java $
+ * $Revision: 645850 $
+ * $Date: 2008-04-08 04:08:52 -0700 (Tue, 08 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.scheme;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.params.HttpParams;
+
+/**
+ * A factory for creating and connecting sockets.
+ * The factory encapsulates the logic for establishing a socket connection.
+ * <br/>
+ * Both {@link java.lang.Object#equals(java.lang.Object) Object.equals()}
+ * and {@link java.lang.Object#hashCode() Object.hashCode()}
+ * must be overridden for the correct operation of some connection managers.
+ * 
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author Michael Becke
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ */
+public interface SocketFactory {
+
+    /**
+     * Creates a new, unconnected socket.
+     * The socket should subsequently be passed to
+     * {@link #connectSocket connectSocket}.
+     *
+     * @return  a new socket
+     * 
+     * @throws IOException if an I/O error occurs while creating the socket
+     */
+    Socket createSocket()
+        throws IOException
+        ;
+
+
+    /**
+     * Connects a socket to the given host.
+     * 
+     * @param sock      the socket to connect, as obtained from
+     *                  {@link #createSocket createSocket}.
+     *                  <code>null</code> indicates that a new socket
+     *                  should be created and connected.
+     * @param host      the host to connect to
+     * @param port      the port to connect to on the host
+     * @param localAddress the local address to bind the socket to, or
+     *                  <code>null</code> for any
+     * @param localPort the port on the local machine,
+     *                  0 or a negative number for any
+     * @param params    additional {@link HttpParams parameters} for connecting
+     * 
+     * @return  the connected socket. The returned object may be different
+     *          from the <code>sock</code> argument if this factory supports
+     *          a layered protocol.
+     * 
+     * @throws IOException if an I/O error occurs
+     * @throws UnknownHostException if the IP address of the target host
+     *          can not be determined
+     * @throws ConnectTimeoutException if the socket cannot be connected
+     *          within the time limit defined in the <code>params</code>
+     */
+    Socket connectSocket(
+        Socket sock,
+        String host, 
+        int port, 
+        InetAddress localAddress, 
+        int localPort,
+        HttpParams params
+    ) throws IOException, UnknownHostException, ConnectTimeoutException;
+
+
+    /**
+     * Checks whether a socket provides a secure connection.
+     * The socket must be {@link #connectSocket connected}
+     * by this factory.
+     * The factory will <i>not</i> perform I/O operations
+     * in this method.
+     * <br/>
+     * As a rule of thumb, plain sockets are not secure and
+     * TLS/SSL sockets are secure. However, there may be
+     * application specific deviations. For example, a plain
+     * socket to a host in the same intranet ("trusted zone")
+     * could be considered secure. On the other hand, a
+     * TLS/SSL socket could be considered insecure based on
+     * the cypher suite chosen for the connection.
+     *
+     * @param sock      the connected socket to check
+     *
+     * @return  <code>true</code> if the connection of the socket
+     *          should be considered secure, or
+     *          <code>false</code> if it should not
+     *
+     * @throws IllegalArgumentException
+     *  if the argument is invalid, for example because it is
+     *  not a connected socket or was created by a different
+     *  socket factory.
+     *  Note that socket factories are <i>not</i> required to
+     *  check these conditions, they may simply return a default
+     *  value when called with an invalid socket argument.
+     */
+    boolean isSecure(Socket sock)
+        throws IllegalArgumentException
+        ;
+
+}
diff --git a/src/org/apache/http/conn/ssl/AbstractVerifier.java b/src/org/apache/http/conn/ssl/AbstractVerifier.java
new file mode 100644
index 0000000..5195e58
--- /dev/null
+++ b/src/org/apache/http/conn/ssl/AbstractVerifier.java
@@ -0,0 +1,343 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.ssl;
+
+import org.apache.http.conn.util.InetAddressUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.StringTokenizer;
+import java.util.logging.Logger;
+import java.util.logging.Level;
+
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+
+/**
+ * Abstract base class for all standard {@link X509HostnameVerifier} 
+ * implementations.
+ * 
+ * @author Julius Davies
+ */
+public abstract class AbstractVerifier implements X509HostnameVerifier {
+
+    /**
+     * This contains a list of 2nd-level domains that aren't allowed to
+     * have wildcards when combined with country-codes.
+     * For example: [*.co.uk].
+     * <p/>
+     * The [*.co.uk] problem is an interesting one.  Should we just hope
+     * that CA's would never foolishly allow such a certificate to happen?
+     * Looks like we're the only implementation guarding against this.
+     * Firefox, Curl, Sun Java 1.4, 5, 6 don't bother with this check.
+     */
+    private final static String[] BAD_COUNTRY_2LDS =
+          { "ac", "co", "com", "ed", "edu", "go", "gouv", "gov", "info",
+            "lg", "ne", "net", "or", "org" };
+
+    static {
+        // Just in case developer forgot to manually sort the array.  :-)
+        Arrays.sort(BAD_COUNTRY_2LDS);
+    }
+
+    public AbstractVerifier() {
+        super();
+    }
+
+    public final void verify(String host, SSLSocket ssl)
+          throws IOException {
+        if(host == null) {
+            throw new NullPointerException("host to verify is null");
+        }
+
+        ssl.startHandshake();
+        SSLSession session = ssl.getSession();
+        if(session == null) {
+            // In our experience this only happens under IBM 1.4.x when
+            // spurious (unrelated) certificates show up in the server'
+            // chain.  Hopefully this will unearth the real problem:
+            InputStream in = ssl.getInputStream();
+            in.available();
+            /*
+              If you're looking at the 2 lines of code above because
+              you're running into a problem, you probably have two
+              options:
+
+                #1.  Clean up the certificate chain that your server
+                     is presenting (e.g. edit "/etc/apache2/server.crt"
+                     or wherever it is your server's certificate chain
+                     is defined).
+
+                                           OR
+
+                #2.   Upgrade to an IBM 1.5.x or greater JVM, or switch
+                      to a non-IBM JVM.
+            */
+
+            // If ssl.getInputStream().available() didn't cause an
+            // exception, maybe at least now the session is available?
+            session = ssl.getSession();
+            if(session == null) {
+                // If it's still null, probably a startHandshake() will
+                // unearth the real problem.
+                ssl.startHandshake();
+
+                // Okay, if we still haven't managed to cause an exception,
+                // might as well go for the NPE.  Or maybe we're okay now?
+                session = ssl.getSession();
+            }
+        }
+
+        Certificate[] certs = session.getPeerCertificates();
+        X509Certificate x509 = (X509Certificate) certs[0];
+        verify(host, x509);
+    }
+
+    public final boolean verify(String host, SSLSession session) {
+        try {
+            Certificate[] certs = session.getPeerCertificates();
+            X509Certificate x509 = (X509Certificate) certs[0];
+            verify(host, x509);
+            return true;
+        }
+        catch(SSLException e) {
+            return false;
+        }
+    }
+
+    public final void verify(String host, X509Certificate cert)
+          throws SSLException {
+        String[] cns = getCNs(cert);
+        String[] subjectAlts = getDNSSubjectAlts(cert);
+        verify(host, cns, subjectAlts);
+    }
+
+    public final void verify(final String host, final String[] cns,
+                             final String[] subjectAlts,
+                             final boolean strictWithSubDomains)
+          throws SSLException {
+
+        // Build the list of names we're going to check.  Our DEFAULT and
+        // STRICT implementations of the HostnameVerifier only use the
+        // first CN provided.  All other CNs are ignored.
+        // (Firefox, wget, curl, Sun Java 1.4, 5, 6 all work this way).
+        LinkedList<String> names = new LinkedList<String>();
+        if(cns != null && cns.length > 0 && cns[0] != null) {
+            names.add(cns[0]);
+        }
+        if(subjectAlts != null) {
+            for (String subjectAlt : subjectAlts) {
+                if (subjectAlt != null) {
+                    names.add(subjectAlt);
+                }
+            }
+        }
+
+        if(names.isEmpty()) {
+            String msg = "Certificate for <" + host + "> doesn't contain CN or DNS subjectAlt";
+            throw new SSLException(msg);
+        }
+
+        // StringBuffer for building the error message.
+        StringBuffer buf = new StringBuffer();
+
+        // We're can be case-insensitive when comparing the host we used to
+        // establish the socket to the hostname in the certificate.
+        String hostName = host.trim().toLowerCase(Locale.ENGLISH);
+        boolean match = false;
+        for(Iterator<String> it = names.iterator(); it.hasNext();) {
+            // Don't trim the CN, though!
+            String cn = it.next();
+            cn = cn.toLowerCase(Locale.ENGLISH);
+            // Store CN in StringBuffer in case we need to report an error.
+            buf.append(" <");
+            buf.append(cn);
+            buf.append('>');
+            if(it.hasNext()) {
+                buf.append(" OR");
+            }
+
+            // The CN better have at least two dots if it wants wildcard
+            // action.  It also can't be [*.co.uk] or [*.co.jp] or
+            // [*.org.uk], etc...
+            boolean doWildcard = cn.startsWith("*.") &&
+                                 cn.lastIndexOf('.') >= 0 &&
+                                 acceptableCountryWildcard(cn) &&
+                                 !InetAddressUtils.isIPv4Address(host);
+
+            if(doWildcard) {
+                match = hostName.endsWith(cn.substring(1));
+                if(match && strictWithSubDomains) {
+                    // If we're in strict mode, then [*.foo.com] is not
+                    // allowed to match [a.b.foo.com]
+                    match = countDots(hostName) == countDots(cn);
+                }
+            } else {
+                match = hostName.equals(cn);
+            }
+            if(match) {
+                break;
+            }
+        }
+        if(!match) {
+            throw new SSLException("hostname in certificate didn't match: <" + host + "> !=" + buf);
+        }
+    }
+
+    public static boolean acceptableCountryWildcard(String cn) {
+        int cnLen = cn.length();
+        if(cnLen >= 7 && cnLen <= 9) {
+            // Look for the '.' in the 3rd-last position:
+            if(cn.charAt(cnLen - 3) == '.') {
+                // Trim off the [*.] and the [.XX].
+                String s = cn.substring(2, cnLen - 3);
+                // And test against the sorted array of bad 2lds:
+                int x = Arrays.binarySearch(BAD_COUNTRY_2LDS, s);
+                return x < 0;
+            }
+        }
+        return true;
+    }
+
+    public static String[] getCNs(X509Certificate cert) {
+        LinkedList<String> cnList = new LinkedList<String>();
+        /*
+          Sebastian Hauer's original StrictSSLProtocolSocketFactory used
+          getName() and had the following comment:
+
+                Parses a X.500 distinguished name for the value of the
+                "Common Name" field.  This is done a bit sloppy right
+                 now and should probably be done a bit more according to
+                <code>RFC 2253</code>.
+
+           I've noticed that toString() seems to do a better job than
+           getName() on these X500Principal objects, so I'm hoping that
+           addresses Sebastian's concern.
+
+           For example, getName() gives me this:
+           1.2.840.113549.1.9.1=#16166a756c6975736461766965734063756362632e636f6d
+
+           whereas toString() gives me this:
+           EMAILADDRESS=juliusdavies@cucbc.com
+
+           Looks like toString() even works with non-ascii domain names!
+           I tested it with "&#x82b1;&#x5b50;.co.jp" and it worked fine.
+        */
+        String subjectPrincipal = cert.getSubjectX500Principal().toString();
+        StringTokenizer st = new StringTokenizer(subjectPrincipal, ",");
+        while(st.hasMoreTokens()) {
+            String tok = st.nextToken();
+            int x = tok.indexOf("CN=");
+            if(x >= 0) {
+                cnList.add(tok.substring(x + 3));
+            }
+        }
+        if(!cnList.isEmpty()) {
+            String[] cns = new String[cnList.size()];
+            cnList.toArray(cns);
+            return cns;
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Extracts the array of SubjectAlt DNS names from an X509Certificate.
+     * Returns null if there aren't any.
+     * <p/>
+     * Note:  Java doesn't appear able to extract international characters
+     * from the SubjectAlts.  It can only extract international characters
+     * from the CN field.
+     * <p/>
+     * (Or maybe the version of OpenSSL I'm using to test isn't storing the
+     * international characters correctly in the SubjectAlts?).
+     *
+     * @param cert X509Certificate
+     * @return Array of SubjectALT DNS names stored in the certificate.
+     */
+    public static String[] getDNSSubjectAlts(X509Certificate cert) {
+        LinkedList<String> subjectAltList = new LinkedList<String>();
+        Collection<List<?>> c = null;
+        try {
+            c = cert.getSubjectAlternativeNames();
+        }
+        catch(CertificateParsingException cpe) {
+            Logger.getLogger(AbstractVerifier.class.getName())
+                    .log(Level.FINE, "Error parsing certificate.", cpe);
+        }
+        if(c != null) {
+            for (List<?> aC : c) {
+                List<?> list = aC;
+                int type = ((Integer) list.get(0)).intValue();
+                // If type is 2, then we've got a dNSName
+                if (type == 2) {
+                    String s = (String) list.get(1);
+                    subjectAltList.add(s);
+                }
+            }
+        }
+        if(!subjectAltList.isEmpty()) {
+            String[] subjectAlts = new String[subjectAltList.size()];
+            subjectAltList.toArray(subjectAlts);
+            return subjectAlts;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Counts the number of dots "." in a string.
+     * @param s  string to count dots from
+     * @return  number of dots
+     */
+    public static int countDots(final String s) {
+        int count = 0;
+        for(int i = 0; i < s.length(); i++) {
+            if(s.charAt(i) == '.') {
+                count++;
+            }
+        }
+        return count;
+    }
+    
+}
diff --git a/src/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java b/src/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java
new file mode 100644
index 0000000..05828fb
--- /dev/null
+++ b/src/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java
@@ -0,0 +1,54 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java $
+ * $Revision: 617642 $
+ * $Date: 2008-02-01 12:54:07 -0800 (Fri, 01 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.ssl;
+
+/**
+ * The ALLOW_ALL HostnameVerifier essentially turns hostname verification
+ * off. This implementation is a no-op, and never throws the SSLException.
+ * 
+ * @author Julius Davies
+ */
+public class AllowAllHostnameVerifier extends AbstractVerifier {
+
+    public final void verify(
+            final String host, 
+            final String[] cns,
+            final String[] subjectAlts) {
+        // Allow everything - so never blowup.
+    }
+
+    @Override
+    public final String toString() { 
+        return "ALLOW_ALL"; 
+    }
+    
+}
diff --git a/src/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java b/src/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java
new file mode 100644
index 0000000..f4129d6
--- /dev/null
+++ b/src/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java
@@ -0,0 +1,62 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java $
+ * $Revision: 617642 $
+ * $Date: 2008-02-01 12:54:07 -0800 (Fri, 01 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.ssl;
+
+import javax.net.ssl.SSLException;
+
+/**
+ * The HostnameVerifier that works the same way as Curl and Firefox.
+ * <p/>
+ * The hostname must match either the first CN, or any of the subject-alts.
+ * A wildcard can occur in the CN, and in any of the subject-alts.
+ * <p/>
+ * The only difference between BROWSER_COMPATIBLE and STRICT is that a wildcard 
+ * (such as "*.foo.com") with BROWSER_COMPATIBLE matches all subdomains, 
+ * including "a.b.foo.com".
+ * 
+ * @author Julius Davies
+ */
+public class BrowserCompatHostnameVerifier extends AbstractVerifier {
+
+    public final void verify(
+            final String host, 
+            final String[] cns,
+            final String[] subjectAlts) throws SSLException {
+        verify(host, cns, subjectAlts, false);
+    }
+
+    @Override
+    public final String toString() { 
+        return "BROWSER_COMPATIBLE"; 
+    }
+    
+}
diff --git a/src/org/apache/http/conn/ssl/SSLSocketFactory.java b/src/org/apache/http/conn/ssl/SSLSocketFactory.java
new file mode 100644
index 0000000..1be6c3a
--- /dev/null
+++ b/src/org/apache/http/conn/ssl/SSLSocketFactory.java
@@ -0,0 +1,397 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java $
+ * $Revision: 659194 $
+ * $Date: 2008-05-22 11:33:47 -0700 (Thu, 22 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.ssl;
+
+import org.apache.http.conn.scheme.HostNameResolver;
+import org.apache.http.conn.scheme.LayeredSocketFactory;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+
+/**
+ * Layered socket factory for TLS/SSL connections, based on JSSE.
+ *.
+ * <p>
+ * SSLSocketFactory can be used to validate the identity of the HTTPS 
+ * server against a list of trusted certificates and to authenticate to
+ * the HTTPS server using a private key. 
+ * </p>
+ * 
+ * <p>
+ * SSLSocketFactory will enable server authentication when supplied with
+ * a {@link KeyStore truststore} file containg one or several trusted
+ * certificates. The client secure socket will reject the connection during
+ * the SSL session handshake if the target HTTPS server attempts to
+ * authenticate itself with a non-trusted certificate.
+ * </p>
+ * 
+ * <p>
+ * Use JDK keytool utility to import a trusted certificate and generate a truststore file:    
+ *    <pre>
+ *     keytool -import -alias "my server cert" -file server.crt -keystore my.truststore
+ *    </pre>
+ * </p>
+ * 
+ * <p>
+ * SSLSocketFactory will enable client authentication when supplied with
+ * a {@link KeyStore keystore} file containg a private key/public certificate
+ * pair. The client secure socket will use the private key to authenticate
+ * itself to the target HTTPS server during the SSL session handshake if
+ * requested to do so by the server.
+ * The target HTTPS server will in its turn verify the certificate presented
+ * by the client in order to establish client's authenticity
+ * </p>
+ * 
+ * <p>
+ * Use the following sequence of actions to generate a keystore file
+ * </p>
+ *   <ul>
+ *     <li>
+ *      <p>
+ *      Use JDK keytool utility to generate a new key
+ *      <pre>keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore</pre>
+ *      For simplicity use the same password for the key as that of the keystore
+ *      </p>
+ *     </li>
+ *     <li>
+ *      <p>
+ *      Issue a certificate signing request (CSR)
+ *      <pre>keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore</pre>
+ *     </p>
+ *     </li>
+ *     <li>
+ *      <p>
+ *      Send the certificate request to the trusted Certificate Authority for signature. 
+ *      One may choose to act as her own CA and sign the certificate request using a PKI 
+ *      tool, such as OpenSSL.
+ *      </p>
+ *     </li>
+ *     <li>
+ *      <p>
+ *       Import the trusted CA root certificate
+ *       <pre>keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore</pre> 
+ *      </p>
+ *     </li>
+ *     <li>
+ *      <p>
+ *       Import the PKCS#7 file containg the complete certificate chain
+ *       <pre>keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore</pre> 
+ *      </p>
+ *     </li>
+ *     <li>
+ *      <p>
+ *       Verify the content the resultant keystore file
+ *       <pre>keytool -list -v -keystore my.keystore</pre> 
+ *      </p>
+ *     </li>
+ *   </ul>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author Julius Davies
+ */
+
+public class SSLSocketFactory implements LayeredSocketFactory {
+
+    public static final String TLS   = "TLS";
+    public static final String SSL   = "SSL";
+    public static final String SSLV2 = "SSLv2";
+    
+    public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER 
+        = new AllowAllHostnameVerifier();
+    
+    public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER 
+        = new BrowserCompatHostnameVerifier();
+    
+    public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER 
+        = new StrictHostnameVerifier();
+    /**
+     * The factory using the default JVM settings for secure connections.
+     */
+    private static final SSLSocketFactory DEFAULT_FACTORY = new SSLSocketFactory();
+    
+    /**
+     * Gets an singleton instance of the SSLProtocolSocketFactory.
+     * @return a SSLProtocolSocketFactory
+     */
+    public static SSLSocketFactory getSocketFactory() {
+        return DEFAULT_FACTORY;
+    }
+    
+    private final SSLContext sslcontext;
+    private final javax.net.ssl.SSLSocketFactory socketfactory;
+    private final HostNameResolver nameResolver;
+    private X509HostnameVerifier hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
+
+    public SSLSocketFactory(
+        String algorithm, 
+        final KeyStore keystore, 
+        final String keystorePassword, 
+        final KeyStore truststore,
+        final SecureRandom random,
+        final HostNameResolver nameResolver) 
+        throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
+    {
+        super();
+        if (algorithm == null) {
+            algorithm = TLS;
+        }
+        KeyManager[] keymanagers = null;
+        if (keystore != null) {
+            keymanagers = createKeyManagers(keystore, keystorePassword);
+        }
+        TrustManager[] trustmanagers = null;
+        if (truststore != null) {
+            trustmanagers = createTrustManagers(truststore);
+        }
+        this.sslcontext = SSLContext.getInstance(algorithm);
+        this.sslcontext.init(keymanagers, trustmanagers, random);
+        this.socketfactory = this.sslcontext.getSocketFactory();
+        this.nameResolver = nameResolver;
+    }
+
+    public SSLSocketFactory(
+            final KeyStore keystore, 
+            final String keystorePassword, 
+            final KeyStore truststore) 
+            throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
+    {
+        this(TLS, keystore, keystorePassword, truststore, null, null);
+    }
+
+    public SSLSocketFactory(final KeyStore keystore, final String keystorePassword) 
+            throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
+    {
+        this(TLS, keystore, keystorePassword, null, null, null);
+    }
+
+    public SSLSocketFactory(final KeyStore truststore) 
+            throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
+    {
+        this(TLS, null, null, truststore, null, null);
+    }
+
+    /**
+     * Constructs an HttpClient SSLSocketFactory backed by the given JSSE
+     * SSLSocketFactory.
+     *
+     * @hide
+     */
+    public SSLSocketFactory(javax.net.ssl.SSLSocketFactory socketfactory) {
+        super();
+        this.sslcontext = null;
+        this.socketfactory = socketfactory;
+        this.nameResolver = null;
+    }
+
+    /**
+     * Creates the default SSL socket factory.
+     * This constructor is used exclusively to instantiate the factory for
+     * {@link #getSocketFactory getSocketFactory}.
+     */
+    private SSLSocketFactory() {
+        super();
+        this.sslcontext = null;
+        this.socketfactory = HttpsURLConnection.getDefaultSSLSocketFactory();
+        this.nameResolver = null;
+    }
+
+    private static KeyManager[] createKeyManagers(final KeyStore keystore, final String password)
+        throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
+        if (keystore == null) {
+            throw new IllegalArgumentException("Keystore may not be null");
+        }
+        KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
+            KeyManagerFactory.getDefaultAlgorithm());
+        kmfactory.init(keystore, password != null ? password.toCharArray(): null);
+        return kmfactory.getKeyManagers(); 
+    }
+
+    private static TrustManager[] createTrustManagers(final KeyStore keystore)
+        throws KeyStoreException, NoSuchAlgorithmException { 
+        if (keystore == null) {
+            throw new IllegalArgumentException("Keystore may not be null");
+        }
+        TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(
+            TrustManagerFactory.getDefaultAlgorithm());
+        tmfactory.init(keystore);
+        return tmfactory.getTrustManagers();
+    }
+
+
+    // non-javadoc, see interface org.apache.http.conn.SocketFactory
+    public Socket createSocket()
+        throws IOException {
+
+        // the cast makes sure that the factory is working as expected
+        return (SSLSocket) this.socketfactory.createSocket();
+    }
+
+
+    // non-javadoc, see interface org.apache.http.conn.SocketFactory
+    public Socket connectSocket(
+        final Socket sock,
+        final String host,
+        final int port,
+        final InetAddress localAddress,
+        int localPort,
+        final HttpParams params
+    ) throws IOException {
+
+        if (host == null) {
+            throw new IllegalArgumentException("Target host may not be null.");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException("Parameters may not be null.");
+        }
+
+        SSLSocket sslsock = (SSLSocket)
+            ((sock != null) ? sock : createSocket());
+
+        if ((localAddress != null) || (localPort > 0)) {
+
+            // we need to bind explicitly
+            if (localPort < 0)
+                localPort = 0; // indicates "any"
+
+            InetSocketAddress isa =
+                new InetSocketAddress(localAddress, localPort);
+            sslsock.bind(isa);
+        }
+
+        int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
+        int soTimeout = HttpConnectionParams.getSoTimeout(params);
+
+        InetSocketAddress remoteAddress;
+        if (this.nameResolver != null) {
+            remoteAddress = new InetSocketAddress(this.nameResolver.resolve(host), port); 
+        } else {
+            remoteAddress = new InetSocketAddress(host, port);            
+        }
+        
+        sslsock.connect(remoteAddress, connTimeout);
+
+        sslsock.setSoTimeout(soTimeout);
+        try {
+            hostnameVerifier.verify(host, sslsock);
+            // verifyHostName() didn't blowup - good!
+        } catch (IOException iox) {
+            // close the socket before re-throwing the exception
+            try { sslsock.close(); } catch (Exception x) { /*ignore*/ }
+            throw iox;
+        }
+
+        return sslsock;
+    }
+
+
+    /**
+     * Checks whether a socket connection is secure.
+     * This factory creates TLS/SSL socket connections
+     * which, by default, are considered secure.
+     * <br/>
+     * Derived classes may override this method to perform
+     * runtime checks, for example based on the cypher suite.
+     *
+     * @param sock      the connected socket
+     *
+     * @return  <code>true</code>
+     *
+     * @throws IllegalArgumentException if the argument is invalid
+     */
+    public boolean isSecure(Socket sock)
+        throws IllegalArgumentException {
+
+        if (sock == null) {
+            throw new IllegalArgumentException("Socket may not be null.");
+        }
+        // This instanceof check is in line with createSocket() above.
+        if (!(sock instanceof SSLSocket)) {
+            throw new IllegalArgumentException
+                ("Socket not created by this factory.");
+        }
+        // This check is performed last since it calls the argument object.
+        if (sock.isClosed()) {
+            throw new IllegalArgumentException("Socket is closed.");
+        }
+
+        return true;
+
+    } // isSecure
+
+
+    // non-javadoc, see interface LayeredSocketFactory
+    public Socket createSocket(
+        final Socket socket,
+        final String host,
+        final int port,
+        final boolean autoClose
+    ) throws IOException, UnknownHostException {
+        SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket(
+              socket,
+              host,
+              port,
+              autoClose
+        );
+        hostnameVerifier.verify(host, sslSocket);
+        // verifyHostName() didn't blowup - good!
+        return sslSocket;
+    }
+
+    public void setHostnameVerifier(X509HostnameVerifier hostnameVerifier) {
+        if ( hostnameVerifier == null ) {
+            throw new IllegalArgumentException("Hostname verifier may not be null");
+        }
+        this.hostnameVerifier = hostnameVerifier;
+    }
+
+    public X509HostnameVerifier getHostnameVerifier() {
+        return hostnameVerifier;
+    }
+
+}
diff --git a/src/org/apache/http/conn/ssl/StrictHostnameVerifier.java b/src/org/apache/http/conn/ssl/StrictHostnameVerifier.java
new file mode 100644
index 0000000..5eb0d96
--- /dev/null
+++ b/src/org/apache/http/conn/ssl/StrictHostnameVerifier.java
@@ -0,0 +1,69 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/StrictHostnameVerifier.java $
+ * $Revision: 617642 $
+ * $Date: 2008-02-01 12:54:07 -0800 (Fri, 01 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.ssl;
+
+import javax.net.ssl.SSLException;
+
+/**
+ * The Strict HostnameVerifier works the same way as Sun Java 1.4, Sun
+ * Java 5, Sun Java 6-rc.  It's also pretty close to IE6.  This
+ * implementation appears to be compliant with RFC 2818 for dealing with
+ * wildcards.
+ * <p/>
+ * The hostname must match either the first CN, or any of the subject-alts.
+ * A wildcard can occur in the CN, and in any of the subject-alts.  The
+ * one divergence from IE6 is how we only check the first CN.  IE6 allows
+ * a match against any of the CNs present.  We decided to follow in
+ * Sun Java 1.4's footsteps and only check the first CN.  (If you need
+ * to check all the CN's, feel free to write your own implementation!).
+ * <p/>
+ * A wildcard such as "*.foo.com" matches only subdomains in the same
+ * level, for example "a.foo.com".  It does not match deeper subdomains
+ * such as "a.b.foo.com".
+ * 
+ * @author Julius Davies
+ */
+public class StrictHostnameVerifier extends AbstractVerifier {
+
+    public final void verify(
+            final String host, 
+            final String[] cns,
+            final String[] subjectAlts) throws SSLException {
+        verify(host, cns, subjectAlts, true);
+    }
+
+    @Override
+    public final String toString() { 
+        return "STRICT"; 
+    }
+    
+}
diff --git a/src/org/apache/http/conn/ssl/X509HostnameVerifier.java b/src/org/apache/http/conn/ssl/X509HostnameVerifier.java
new file mode 100644
index 0000000..05ad04d
--- /dev/null
+++ b/src/org/apache/http/conn/ssl/X509HostnameVerifier.java
@@ -0,0 +1,86 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/X509HostnameVerifier.java $
+ * $Revision: 618365 $
+ * $Date: 2008-02-04 10:20:08 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.ssl;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+
+/**
+ * Interface for checking if a hostname matches the names stored inside the
+ * server's X.509 certificate.  Implements javax.net.ssl.HostnameVerifier, but
+ * we don't actually use that interface.  Instead we added some methods that
+ * take String parameters (instead of javax.net.ssl.HostnameVerifier's
+ * SSLSession).  JUnit is a lot easier this way!  :-)
+ * <p/>
+ * We provide the HostnameVerifier.DEFAULT, HostnameVerifier.STRICT, and
+ * HostnameVerifier.ALLOW_ALL implementations.  But feel free to define
+ * your own implementation!
+ * <p/>
+ * Inspired by Sebastian Hauer's original StrictSSLProtocolSocketFactory in the
+ * HttpClient "contrib" repository.
+ *
+ * @author Julius Davies
+ * @author <a href="mailto:hauer@psicode.com">Sebastian Hauer</a>
+ *
+ * @since 4.0 (8-Dec-2006)
+ */
+public interface X509HostnameVerifier extends HostnameVerifier {
+
+    boolean verify(String host, SSLSession session);
+
+    void verify(String host, SSLSocket ssl) throws IOException;
+
+    void verify(String host, X509Certificate cert) throws SSLException;
+
+    /**
+     * Checks to see if the supplied hostname matches any of the supplied CNs
+     * or "DNS" Subject-Alts.  Most implementations only look at the first CN,
+     * and ignore any additional CNs.  Most implementations do look at all of
+     * the "DNS" Subject-Alts. The CNs or Subject-Alts may contain wildcards
+     * according to RFC 2818.
+     *
+     * @param cns         CN fields, in order, as extracted from the X.509
+     *                    certificate.
+     * @param subjectAlts Subject-Alt fields of type 2 ("DNS"), as extracted
+     *                    from the X.509 certificate.
+     * @param host        The hostname to verify.
+     * @throws SSLException If verification failed.
+     */
+    void verify(String host, String[] cns, String[] subjectAlts)
+          throws SSLException;
+
+
+}
diff --git a/src/org/apache/http/conn/ssl/package.html b/src/org/apache/http/conn/ssl/package.html
new file mode 100644
index 0000000..a5c737f
--- /dev/null
+++ b/src/org/apache/http/conn/ssl/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+TLS/SSL specific parts of the <i>HttpConn</i> API.
+
+</body>
+</html>
diff --git a/src/org/apache/http/conn/util/InetAddressUtils.java b/src/org/apache/http/conn/util/InetAddressUtils.java
new file mode 100644
index 0000000..71f2190
--- /dev/null
+++ b/src/org/apache/http/conn/util/InetAddressUtils.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/util/InetAddressUtils.java $
+ * $Revision: 652020 $
+ * $Date: 2008-04-27 14:23:31 -0700 (Sun, 27 Apr 2008) $
+ * 
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.util;
+
+import java.util.regex.Pattern;
+
+/**
+ * A collection of utilities relating to InetAddresses.
+ */
+public class InetAddressUtils {
+
+    private InetAddressUtils() {
+    }
+    
+    private static final Pattern IPV4_PATTERN = 
+        Pattern.compile(
+                "^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$");
+
+    private static final Pattern IPV6_STD_PATTERN = 
+        Pattern.compile(
+                "^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$");
+
+    private static final Pattern IPV6_HEX_COMPRESSED_PATTERN = 
+        Pattern.compile(
+                "^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$");
+
+    public static boolean isIPv4Address(final String input) {
+        return IPV4_PATTERN.matcher(input).matches();
+    }
+
+    public static boolean isIPv6StdAddress(final String input) {
+        return IPV6_STD_PATTERN.matcher(input).matches();
+    }
+    
+    public static boolean isIPv6HexCompressedAddress(final String input) {
+        return IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches();
+    }
+    
+    public static boolean isIPv6Address(final String input) {
+        return isIPv6StdAddress(input) || isIPv6HexCompressedAddress(input); 
+    }
+
+}
diff --git a/src/org/apache/http/cookie/ClientCookie.java b/src/org/apache/http/cookie/ClientCookie.java
new file mode 100644
index 0000000..96edec9
--- /dev/null
+++ b/src/org/apache/http/cookie/ClientCookie.java
@@ -0,0 +1,67 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/ClientCookie.java $
+ * $Revision: 578403 $
+ * $Date: 2007-09-22 03:56:04 -0700 (Sat, 22 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+/**
+ * ClientCookie extends the standard {@link Cookie} interface with 
+ * additional client specific functionality such ability to retrieve 
+ * original cookie attributes exactly as they were specified by the 
+ * origin server. This is important for generating the <tt>Cookie</tt> 
+ * header because some cookie specifications require that the 
+ * <tt>Cookie</tt> header should include certain attributes only if 
+ * they were specified in the <tt>Set-Cookie</tt> header.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public interface ClientCookie extends Cookie {
+
+    // RFC2109 attributes
+    public static final String VERSION_ATTR    = "version";
+    public static final String PATH_ATTR       = "path";
+    public static final String DOMAIN_ATTR     = "domain";
+    public static final String MAX_AGE_ATTR    = "max-age";
+    public static final String SECURE_ATTR     = "secure";
+    public static final String COMMENT_ATTR    = "comment";
+    public static final String EXPIRES_ATTR    = "expires";
+    
+    // RFC2965 attributes
+    public static final String PORT_ATTR       = "port";
+    public static final String COMMENTURL_ATTR = "commenturl";
+    public static final String DISCARD_ATTR    = "discard";
+    
+    String getAttribute(String name);
+    
+    boolean containsAttribute(String name);
+    
+}
\ No newline at end of file
diff --git a/src/org/apache/http/cookie/Cookie.java b/src/org/apache/http/cookie/Cookie.java
new file mode 100644
index 0000000..5eae9d5
--- /dev/null
+++ b/src/org/apache/http/cookie/Cookie.java
@@ -0,0 +1,139 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/Cookie.java $
+ * $Revision: 578403 $
+ * $Date: 2007-09-22 03:56:04 -0700 (Sat, 22 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+import java.util.Date;
+
+/**
+ * HTTP "magic-cookie" represents a piece of state information
+ * that the HTTP agent and the target server can exchange to maintain 
+ * a session.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public interface Cookie {
+
+    /**
+     * Returns the name.
+     *
+     * @return String name The name
+     */
+    String getName();
+
+    /**
+     * Returns the value.
+     *
+     * @return String value The current value.
+     */
+    String getValue();
+
+    /**
+     * Returns the comment describing the purpose of this cookie, or
+     * <tt>null</tt> if no such comment has been defined.
+     * 
+     * @return comment 
+     */
+    String getComment();
+
+    /**
+     * If a user agent (web browser) presents this cookie to a user, the
+     * cookie's purpose will be described by the information at this URL.
+     */
+    String getCommentURL();    
+    
+    /**
+     * Returns the expiration {@link Date} of the cookie, or <tt>null</tt>
+     * if none exists.
+     * <p><strong>Note:</strong> the object returned by this method is 
+     * considered immutable. Changing it (e.g. using setTime()) could result
+     * in undefined behaviour. Do so at your peril. </p>
+     * @return Expiration {@link Date}, or <tt>null</tt>.
+     */
+    Date getExpiryDate();
+
+    /**
+     * Returns <tt>false</tt> if the cookie should be discarded at the end
+     * of the "session"; <tt>true</tt> otherwise.
+     *
+     * @return <tt>false</tt> if the cookie should be discarded at the end
+     *         of the "session"; <tt>true</tt> otherwise
+     */
+    boolean isPersistent();
+
+    /**
+     * Returns domain attribute of the cookie.
+     * 
+     * @return the value of the domain attribute
+     */
+    String getDomain();
+
+    /**
+     * Returns the path attribute of the cookie
+     * 
+     * @return The value of the path attribute.
+     */
+    String getPath();
+
+    /**
+     * Get the Port attribute. It restricts the ports to which a cookie
+     * may be returned in a Cookie request header.
+     */
+    int[] getPorts();
+
+    /**
+     * Indicates whether this cookie requires a secure connection.
+     *
+     * @return  <code>true</code> if this cookie should only be sent
+     *          over secure connections, <code>false</code> otherwise.
+     */
+    boolean isSecure();
+
+    /**
+     * Returns the version of the cookie specification to which this
+     * cookie conforms.
+     *
+     * @return the version of the cookie.
+     */
+    int getVersion();
+
+    /**
+     * Returns true if this cookie has expired.
+     * @param date Current time
+     * 
+     * @return <tt>true</tt> if the cookie has expired.
+     */
+    boolean isExpired(final Date date);
+
+}
+
diff --git a/src/org/apache/http/cookie/CookieAttributeHandler.java b/src/org/apache/http/cookie/CookieAttributeHandler.java
new file mode 100644
index 0000000..a79d115
--- /dev/null
+++ b/src/org/apache/http/cookie/CookieAttributeHandler.java
@@ -0,0 +1,78 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/CookieAttributeHandler.java $
+ * $Revision: 558519 $
+ * $Date: 2007-07-22 11:19:49 -0700 (Sun, 22 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.cookie;
+
+/**
+ * Ths interface represents a cookie attribute handler responsible
+ * for parsing, validating, and matching a specific cookie attribute, 
+ * such as path, domain, port, etc.
+ *
+ * Different cookie specifications can provide a specific
+ * implementation for this class based on their cookie handling
+ * rules.
+ *
+ * @author jain.samit@gmail.com (Samit Jain)
+ * 
+ * @since 4.0
+ */
+public interface CookieAttributeHandler {
+
+  /**
+   * Parse the given cookie attribute value and update the corresponding
+   * {@link org.apache.http.cookie.Cookie} property.
+   *
+   * @param cookie {@link org.apache.http.cookie.Cookie} to be updated
+   * @param value cookie attribute value from the cookie response header
+   */
+  void parse(SetCookie cookie, String value)
+          throws MalformedCookieException;
+
+  /**
+   * Peforms cookie validation for the given attribute value.
+   *
+   * @param cookie {@link org.apache.http.cookie.Cookie} to validate
+   * @param origin the cookie source to validate against
+   * @throws MalformedCookieException if cookie validation fails for this attribute
+   */
+  void validate(Cookie cookie, CookieOrigin origin)
+          throws MalformedCookieException;
+
+  /**
+   * Matches the given value (property of the destination host where request is being
+   * submitted) with the corresponding cookie attribute.
+   *
+   * @param cookie {@link org.apache.http.cookie.Cookie} to match
+   * @param origin the cookie source to match against
+   * @return <tt>true</tt> if the match is successful; <tt>false</tt> otherwise
+   */
+  boolean match(Cookie cookie, CookieOrigin origin);
+
+}
diff --git a/src/org/apache/http/cookie/CookieIdentityComparator.java b/src/org/apache/http/cookie/CookieIdentityComparator.java
new file mode 100644
index 0000000..4fc701c
--- /dev/null
+++ b/src/org/apache/http/cookie/CookieIdentityComparator.java
@@ -0,0 +1,68 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/CookieIdentityComparator.java $
+ * $Revision: 618308 $
+ * $Date: 2008-02-04 07:51:19 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * This cookie comparator can be used to compare identity of cookies.
+ *  
+ * <p>
+ *  Cookies are considered identical if their names are equal and 
+ *  their domain attributes match ignoring case.
+ * </p>
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public class CookieIdentityComparator implements Serializable, Comparator<Cookie> {
+
+    private static final long serialVersionUID = 4466565437490631532L;
+
+    public int compare(final Cookie c1, final Cookie c2) {
+        int res = c1.getName().compareTo(c2.getName());
+        if (res == 0) {
+            // do not differentiate empty and null domains 
+            String d1 = c1.getDomain();
+            if (d1 == null) {
+                d1 = "";
+            }
+            String d2 = c2.getDomain();
+            if (d2 == null) {
+                d2 = "";
+            }
+            res = d1.compareToIgnoreCase(d2);
+        }
+        return res;
+    }
+
+}
diff --git a/src/org/apache/http/cookie/CookieOrigin.java b/src/org/apache/http/cookie/CookieOrigin.java
new file mode 100644
index 0000000..ad0448a
--- /dev/null
+++ b/src/org/apache/http/cookie/CookieOrigin.java
@@ -0,0 +1,108 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/CookieOrigin.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.cookie;
+
+import java.util.Locale;
+
+/**
+ * CookieOrigin class incapsulates details of an origin server that 
+ * are relevant when parsing, validating or matching HTTP cookies.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public final class CookieOrigin {
+
+    private final String host;
+    private final int port;
+    private final String path;
+    private final boolean secure;
+    
+    public CookieOrigin(final String host, int port, final String path, boolean secure) {
+        super();
+        if (host == null) {
+            throw new IllegalArgumentException(
+                    "Host of origin may not be null");
+        }
+        if (host.trim().length() == 0) {
+            throw new IllegalArgumentException(
+                    "Host of origin may not be blank");
+        }
+        if (port < 0) {
+            throw new IllegalArgumentException("Invalid port: " + port);
+        }
+        if (path == null) {
+            throw new IllegalArgumentException(
+                    "Path of origin may not be null.");
+        }
+        this.host = host.toLowerCase(Locale.ENGLISH);
+        this.port = port;
+        if (path.trim().length() != 0) {
+            this.path = path;
+        } else {
+            this.path = "/";
+        }
+        this.secure = secure;
+    }
+
+    public String getHost() {
+        return this.host;
+    }
+
+    public String getPath() {
+        return this.path;
+    }
+
+    public int getPort() {
+        return this.port;
+    }
+
+    public boolean isSecure() {
+        return this.secure;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder buffer = new StringBuilder();
+        buffer.append('[');
+        if (this.secure) {
+            buffer.append("(secure)");
+        }
+        buffer.append(this.host);
+        buffer.append(':');
+        buffer.append(Integer.toString(this.port));
+        buffer.append(this.path);
+        buffer.append(']');
+        return buffer.toString();
+    }
+    
+}
diff --git a/src/org/apache/http/cookie/CookiePathComparator.java b/src/org/apache/http/cookie/CookiePathComparator.java
new file mode 100644
index 0000000..f5f0a66
--- /dev/null
+++ b/src/org/apache/http/cookie/CookiePathComparator.java
@@ -0,0 +1,81 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/CookiePathComparator.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * This cookie comparator ensures that multiple cookies satisfying 
+ * a common criteria are ordered in the <tt>Cookie</tt> header such
+ * that those with more specific Path attributes precede those with
+ * less specific.
+ *  
+ * <p>
+ * This comparator assumes that Path attributes of two cookies 
+ * path-match a commmon request-URI. Otherwise, the result of the
+ * comparison is undefined.
+ * </p>
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public class CookiePathComparator implements Serializable, Comparator<Cookie> {
+
+    private static final long serialVersionUID = 7523645369616405818L;
+
+    private String normalizePath(final Cookie cookie) {
+        String path = cookie.getPath();
+        if (path == null) {
+            path = "/";
+        }
+        if (!path.endsWith("/")) {
+            path = path + '/';
+        }
+        return path;
+    }
+    
+    public int compare(final Cookie c1, final Cookie c2) {
+        String path1 = normalizePath(c1);
+        String path2 = normalizePath(c2);
+        if (path1.equals(path2)) {
+            return 0;
+        } else if (path1.startsWith(path2)) {
+            return -1;
+        } else if (path2.startsWith(path1)) {
+            return 1;
+        } else {
+            // Does not really matter
+            return 0;
+        }
+    }
+
+}
diff --git a/src/org/apache/http/cookie/CookieSpec.java b/src/org/apache/http/cookie/CookieSpec.java
new file mode 100644
index 0000000..1eb9f26
--- /dev/null
+++ b/src/org/apache/http/cookie/CookieSpec.java
@@ -0,0 +1,115 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/CookieSpec.java $
+ * $Revision: 603563 $
+ * $Date: 2007-12-12 03:17:55 -0800 (Wed, 12 Dec 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+import java.util.List;
+
+import org.apache.http.Header;
+
+/**
+ * Defines the cookie management specification.
+ * <p>Cookie management specification must define
+ * <ul>
+ *   <li> rules of parsing "Set-Cookie" header
+ *   <li> rules of validation of parsed cookies
+ *   <li>  formatting of "Cookie" header 
+ * </ul>
+ * for a given host, port and path of origin
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ *
+ * @since 4.0
+ */
+public interface CookieSpec {    
+
+    /**
+     * Returns version of the state management this cookie specification
+     * conforms to.
+     * 
+     * @return version of the state management specification
+     */
+    int getVersion();
+    
+    /**
+      * Parse the <tt>"Set-Cookie"</tt> Header into an array of Cookies.
+      *
+      * <p>This method will not perform the validation of the resultant
+      * {@link Cookie}s</p> 
+      *
+      * @see #validate
+      *
+      * @param header the <tt>Set-Cookie</tt> received from the server
+      * @param origin details of the cookie origin
+      * @return an array of <tt>Cookie</tt>s parsed from the header
+      * @throws MalformedCookieException if an exception occurs during parsing
+      */
+    List<Cookie> parse(Header header, CookieOrigin origin) throws MalformedCookieException;
+
+    /**
+      * Validate the cookie according to validation rules defined by the 
+      *  cookie specification.
+      *
+      * @param cookie the Cookie to validate
+      * @param origin details of the cookie origin
+      * @throws MalformedCookieException if the cookie is invalid
+      */
+    void validate(Cookie cookie, CookieOrigin origin) throws MalformedCookieException;
+    
+    /**
+     * Determines if a Cookie matches the target location.
+     *
+     * @param cookie the Cookie to be matched
+     * @param origin the target to test against
+     * 
+     * @return <tt>true</tt> if the cookie should be submitted with a request 
+     *  with given attributes, <tt>false</tt> otherwise.
+     */
+    boolean match(Cookie cookie, CookieOrigin origin);
+
+    /**
+     * Create <tt>"Cookie"</tt> headers for an array of Cookies.
+     *
+     * @param cookies the Cookies format into a Cookie header
+     * @return a Header for the given Cookies.
+     * @throws IllegalArgumentException if an input parameter is illegal
+     */
+    List<Header> formatCookies(List<Cookie> cookies);
+
+    /**
+     * Returns a request header identifying what version of the state management 
+     * specification is understood. May be <code>null</code> if the cookie 
+     * specification does not support <tt>Cookie2</tt> header.
+     */
+    Header getVersionHeader();
+    
+}
diff --git a/src/org/apache/http/cookie/CookieSpecFactory.java b/src/org/apache/http/cookie/CookieSpecFactory.java
new file mode 100644
index 0000000..9d5c21d
--- /dev/null
+++ b/src/org/apache/http/cookie/CookieSpecFactory.java
@@ -0,0 +1,46 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/CookieSpecFactory.java $
+ * $Revision: 489636 $
+ * $Date: 2006-12-22 04:34:57 -0800 (Fri, 22 Dec 2006) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public interface CookieSpecFactory {    
+
+    CookieSpec newInstance(HttpParams params);
+
+}
diff --git a/src/org/apache/http/cookie/CookieSpecRegistry.java b/src/org/apache/http/cookie/CookieSpecRegistry.java
new file mode 100644
index 0000000..64b9c8b
--- /dev/null
+++ b/src/org/apache/http/cookie/CookieSpecRegistry.java
@@ -0,0 +1,160 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/CookieSpecRegistry.java $
+ * $Revision: 652950 $
+ * $Date: 2008-05-02 16:49:48 -0700 (Fri, 02 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ * Cookie specification registry that can be used to obtain the corresponding
+ * cookie specification implementation for a given type of type or version of 
+ * cookie. 
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ *
+ * @since 4.0
+ */
+public final class CookieSpecRegistry {
+
+    private final Map<String,CookieSpecFactory> registeredSpecs;
+    
+    public CookieSpecRegistry() {
+        super();
+        this.registeredSpecs = new LinkedHashMap<String,CookieSpecFactory>();
+    }
+    
+    /**
+     * Registers a {@link CookieSpecFactory} with the given identifier. 
+     * If a specification with the given name already exists it will be overridden.  
+     * This nameis the same one used to retrieve the {@link CookieSpecFactory} 
+     * from {@link #getCookieSpec(String)}.
+     * 
+     * @param name the identifier for this specification
+     * @param factory the {@link CookieSpecFactory} class to register
+     * 
+     * @see #getCookieSpec(String)
+     */
+    public synchronized void register(final String name, final CookieSpecFactory factory) {
+         if (name == null) {
+             throw new IllegalArgumentException("Name may not be null");
+         }
+        if (factory == null) {
+            throw new IllegalArgumentException("Cookie spec factory may not be null");
+        }
+        registeredSpecs.put(name.toLowerCase(Locale.ENGLISH), factory);
+    }
+
+    /**
+     * Unregisters the {@link CookieSpecFactory} with the given ID.
+     * 
+     * @param id the identifier of the {@link CookieSpec cookie specification} to unregister
+     */
+    public synchronized void unregister(final String id) {
+         if (id == null) {
+             throw new IllegalArgumentException("Id may not be null");
+         }
+         registeredSpecs.remove(id.toLowerCase(Locale.ENGLISH));
+    }
+
+    /**
+     * Gets the {@link CookieSpec cookie specification} with the given ID.
+     * 
+     * @param name the {@link CookieSpec cookie specification} identifier
+     * @param params the {@link HttpParams HTTP parameters} for the cookie
+     *  specification. 
+     * 
+     * @return {@link CookieSpec cookie specification}
+     * 
+     * @throws IllegalStateException if a policy with the given name cannot be found
+     */
+    public synchronized CookieSpec getCookieSpec(final String name, final HttpParams params) 
+        throws IllegalStateException {
+
+        if (name == null) {
+            throw new IllegalArgumentException("Name may not be null");
+        }
+        CookieSpecFactory factory = registeredSpecs.get(name.toLowerCase(Locale.ENGLISH));
+        if (factory != null) {
+            return factory.newInstance(params);
+        } else {
+            throw new IllegalStateException("Unsupported cookie spec: " + name);
+        }
+    } 
+
+    /**
+     * Gets the {@link CookieSpec cookie specification} with the given name.
+     * 
+     * @param name the {@link CookieSpec cookie specification} identifier
+     * 
+     * @return {@link CookieSpec cookie specification}
+     * 
+     * @throws IllegalStateException if a policy with the given name cannot be found
+     */
+    public synchronized CookieSpec getCookieSpec(final String name) 
+        throws IllegalStateException {
+        return getCookieSpec(name, null);
+    } 
+
+    /**
+     * Obtains a list containing names of all registered {@link CookieSpec cookie 
+     * specs} in their default order.
+     * 
+     * Note that the DEFAULT policy (if present) is likely to be the same
+     * as one of the other policies, but does not have to be.
+     * 
+     * @return list of registered cookie spec names
+     */
+    public synchronized List<String> getSpecNames(){
+        return new ArrayList<String>(registeredSpecs.keySet()); 
+    }
+    
+    /**
+     * Populates the internal collection of registered {@link CookieSpec cookie 
+     * specs} with the content of the map passed as a parameter.
+     * 
+     * @param map cookie specs
+     */
+    public synchronized void setItems(final Map<String, CookieSpecFactory> map) {
+        if (map == null) {
+            return;
+        }
+        registeredSpecs.clear();
+        registeredSpecs.putAll(map);
+    }
+
+}
diff --git a/src/org/apache/http/cookie/MalformedCookieException.java b/src/org/apache/http/cookie/MalformedCookieException.java
new file mode 100644
index 0000000..e3f30a9
--- /dev/null
+++ b/src/org/apache/http/cookie/MalformedCookieException.java
@@ -0,0 +1,74 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/MalformedCookieException.java $
+ * $Revision: 508891 $
+ * $Date: 2007-02-18 02:08:48 -0800 (Sun, 18 Feb 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+import org.apache.http.ProtocolException;
+
+/**
+ * Signals that a cookie is in some way invalid or illegal in a given
+ * context
+ *
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public class MalformedCookieException extends ProtocolException {
+
+    private static final long serialVersionUID = -6695462944287282185L;
+
+    /**
+     * Creates a new MalformedCookieException with a <tt>null</tt> detail message.
+     */
+    public MalformedCookieException() {
+        super();
+    }
+     
+    /** 
+     * Creates a new MalformedCookieException with a specified message string.
+     * 
+     * @param message The exception detail message
+     */
+    public MalformedCookieException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new MalformedCookieException with the specified detail message and cause.
+     * 
+     * @param message the exception detail message
+     * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+     * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+     */
+    public MalformedCookieException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/org/apache/http/cookie/SM.java b/src/org/apache/http/cookie/SM.java
new file mode 100644
index 0000000..a7047d5
--- /dev/null
+++ b/src/org/apache/http/cookie/SM.java
@@ -0,0 +1,48 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/SM.java $
+ * $Revision: 582602 $
+ * $Date: 2007-10-07 02:35:48 -0700 (Sun, 07 Oct 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+/**
+ * Constants and static helpers related to the HTTP state management.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public interface SM {
+
+    public static final String COOKIE            = "Cookie";
+    public static final String COOKIE2          = "Cookie2";
+    public static final String SET_COOKIE        = "Set-Cookie";
+    public static final String SET_COOKIE2       = "Set-Cookie2";
+       
+}
diff --git a/src/org/apache/http/cookie/SetCookie.java b/src/org/apache/http/cookie/SetCookie.java
new file mode 100644
index 0000000..d207c48
--- /dev/null
+++ b/src/org/apache/http/cookie/SetCookie.java
@@ -0,0 +1,115 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/SetCookie.java $
+ * $Revision: 617193 $
+ * $Date: 2008-01-31 11:26:47 -0800 (Thu, 31 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+import java.util.Date;
+
+/**
+ * This interface represents a <code>SetCookie</code> response header sent by the 
+ * origin server to the HTTP agent in order to maintain a conversational state.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public interface SetCookie extends Cookie {
+
+    void setValue(String value);
+    
+    /**
+     * If a user agent (web browser) presents this cookie to a user, the
+     * cookie's purpose will be described using this comment.
+     * 
+     * @param comment
+     *  
+     * @see #getComment()
+     */
+    void setComment(String comment);
+
+    /**
+     * Sets expiration date.
+     * <p><strong>Note:</strong> the object returned by this method is considered
+     * immutable. Changing it (e.g. using setTime()) could result in undefined 
+     * behaviour. Do so at your peril.</p>
+     *
+     * @param expiryDate the {@link Date} after which this cookie is no longer valid.
+     *
+     * @see Cookie#getExpiryDate
+     *
+     */
+    void setExpiryDate (Date expiryDate);
+
+    /**
+     * Sets the domain attribute.
+     * 
+     * @param domain The value of the domain attribute
+     *
+     * @see Cookie#getDomain
+     */
+    void setDomain(String domain);
+
+    /**
+     * Sets the path attribute.
+     *
+     * @param path The value of the path attribute
+     *
+     * @see Cookie#getPath
+     *
+     */
+    void setPath(String path);
+
+    /**
+     * Sets the secure attribute of the cookie.
+     * <p>
+     * When <tt>true</tt> the cookie should only be sent
+     * using a secure protocol (https).  This should only be set when
+     * the cookie's originating server used a secure protocol to set the
+     * cookie's value.
+     *
+     * @param secure The value of the secure attribute
+     * 
+     * @see #isSecure()
+     */
+    void setSecure (boolean secure);
+
+    /**
+     * Sets the version of the cookie specification to which this
+     * cookie conforms. 
+     *
+     * @param version the version of the cookie.
+     * 
+     * @see Cookie#getVersion
+     */
+    void setVersion(int version);
+
+}
+
diff --git a/src/org/apache/http/cookie/SetCookie2.java b/src/org/apache/http/cookie/SetCookie2.java
new file mode 100644
index 0000000..cd0420e
--- /dev/null
+++ b/src/org/apache/http/cookie/SetCookie2.java
@@ -0,0 +1,66 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/SetCookie2.java $
+ * $Revision: 578408 $
+ * $Date: 2007-09-22 04:53:57 -0700 (Sat, 22 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+/**
+ * This interface represents a <code>SetCookie2</code> response header sent by the 
+ * origin server to the HTTP agent in order to maintain a conversational state.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public interface SetCookie2 extends SetCookie {
+
+    /**
+     * If a user agent (web browser) presents this cookie to a user, the
+     * cookie's purpose will be described by the information at this URL.
+     */
+    void setCommentURL(String commentURL);
+    
+    /**
+     * Sets the Port attribute. It restricts the ports to which a cookie
+     * may be returned in a Cookie request header.
+     */
+    void setPorts(int[] ports);
+    
+    /**
+     * Set the Discard attribute.
+     *
+     * Note: <tt>Discard</tt> attribute overrides <tt>Max-age</tt>.
+     *
+     * @see #isPersistent()
+     */
+    void setDiscard(boolean discard);
+    
+}
+
diff --git a/src/org/apache/http/cookie/package.html b/src/org/apache/http/cookie/package.html
new file mode 100644
index 0000000..891d9c3
--- /dev/null
+++ b/src/org/apache/http/cookie/package.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+The API for client-side state management via cookies,
+commonly referred to as <i>HttpCookie</i>.
+
+</body>
+</html>
diff --git a/src/org/apache/http/cookie/params/CookieSpecPNames.java b/src/org/apache/http/cookie/params/CookieSpecPNames.java
new file mode 100644
index 0000000..6a6f6d0
--- /dev/null
+++ b/src/org/apache/http/cookie/params/CookieSpecPNames.java
@@ -0,0 +1,68 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/params/CookieSpecPNames.java $
+ * $Revision: 578403 $
+ * $Date: 2007-09-22 03:56:04 -0700 (Sat, 22 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie.params;
+
+/**
+ * Parameter names for cookie specifications in HttpCookie.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Revision: 578403 $
+ * 
+ * @since 4.0
+ */
+public interface CookieSpecPNames {
+
+    /**
+     * Parameter for the date patterns used for parsing.
+     * <p>
+     * This parameter expects a value of type {@link java.util.Collection}.
+     * The collection elements are of type {@link String}
+     * and must be compatible with the syntax of
+     * {@link java.text.SimpleDateFormat}.
+     * </p>
+     */
+    public static final String DATE_PATTERNS = "http.protocol.cookie-datepatterns";
+    
+    /**
+     * Parameter for Cookie header formatting.
+     * Defines whether {@link org.apache.http.cookie.Cookie cookies}
+     * should be put on 
+     * a single {@link org.apache.http.Header request header}.
+     * If not, each cookie is formatted in a seperate Cookie header.
+     * <p>
+     * This parameter expects a value of type {@link Boolean}.
+     * </p>
+     */
+    public static final String SINGLE_COOKIE_HEADER = "http.protocol.single-cookie-header"; 
+
+}
diff --git a/src/org/apache/http/cookie/params/CookieSpecParamBean.java b/src/org/apache/http/cookie/params/CookieSpecParamBean.java
new file mode 100644
index 0000000..6016022
--- /dev/null
+++ b/src/org/apache/http/cookie/params/CookieSpecParamBean.java
@@ -0,0 +1,53 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/params/CookieSpecParamBean.java $
+ * $Revision: 632313 $
+ * $Date: 2008-02-29 05:19:50 -0800 (Fri, 29 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie.params;
+
+import java.util.Collection;
+
+import org.apache.http.params.HttpAbstractParamBean;
+import org.apache.http.params.HttpParams;
+
+public class CookieSpecParamBean extends HttpAbstractParamBean {
+
+    public CookieSpecParamBean (final HttpParams params) {
+        super(params);
+    }
+
+    public void setDatePatterns (final Collection <String> patterns) {
+        params.setParameter(CookieSpecPNames.DATE_PATTERNS, patterns);
+    }
+
+    public void setSingleHeader (final boolean singleHeader) {
+        params.setBooleanParameter(CookieSpecPNames.SINGLE_COOKIE_HEADER, singleHeader);
+    }
+    
+}
diff --git a/src/org/apache/http/cookie/params/package.html b/src/org/apache/http/cookie/params/package.html
new file mode 100644
index 0000000..e6fb7cd
--- /dev/null
+++ b/src/org/apache/http/cookie/params/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/params/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Parameters for configuring <i>HttpCookie</i>.
+
+</body>
+</html>
diff --git a/src/org/apache/http/entity/AbstractHttpEntity.java b/src/org/apache/http/entity/AbstractHttpEntity.java
new file mode 100644
index 0000000..0fce6eb
--- /dev/null
+++ b/src/org/apache/http/entity/AbstractHttpEntity.java
@@ -0,0 +1,213 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/AbstractHttpEntity.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.IOException;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * Abstract base class for entities.
+ * Provides the commonly used attributes for streamed and self-contained
+ * implementations of {@link HttpEntity HttpEntity}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 496070 $
+ * 
+ * @since 4.0
+ */
+public abstract class AbstractHttpEntity implements HttpEntity {
+
+    /**
+     * The Content-Type header.
+     * Returned by {@link #getContentType getContentType},
+     * unless that method is overridden.
+     */
+    protected Header contentType;
+
+    /**
+     * The Content-Encoding header.
+     * Returned by {@link #getContentEncoding getContentEncoding},
+     * unless that method is overridden.
+     */
+    protected Header contentEncoding;
+
+    /**
+     * The 'chunked' flag.
+     * Returned by {@link #isChunked isChunked},
+     * unless that method is overridden.
+     */
+    protected boolean chunked;
+
+
+    /**
+     * Protected default constructor.
+     * The attributes of the created object remain
+     * <code>null</code> and <code>false</code>, respectively.
+     */
+    protected AbstractHttpEntity() {
+        super();
+    }
+
+
+    /**
+     * Obtains the Content-Type header.
+     * The default implementation returns the value of the
+     * {@link #contentType contentType} attribute.
+     *
+     * @return  the Content-Type header, or <code>null</code>
+     */
+    public Header getContentType() {
+        return this.contentType;
+    }
+
+    
+    /**
+     * Obtains the Content-Encoding header.
+     * The default implementation returns the value of the
+     * {@link #contentEncoding contentEncoding} attribute.
+     *
+     * @return  the Content-Encoding header, or <code>null</code>
+     */
+    public Header getContentEncoding() {
+        return this.contentEncoding;
+    }
+
+    /**
+     * Obtains the 'chunked' flag.
+     * The default implementation returns the value of the
+     * {@link #chunked chunked} attribute.
+     *
+     * @return  the 'chunked' flag
+     */
+    public boolean isChunked() {
+        return this.chunked;
+    }
+
+    
+    /**
+     * Specifies the Content-Type header.
+     * The default implementation sets the value of the
+     * {@link #contentType contentType} attribute.
+     *
+     * @param contentType       the new Content-Encoding header, or
+     *                          <code>null</code> to unset
+     */
+    public void setContentType(final Header contentType) {
+        this.contentType = contentType;
+    }
+
+    /**
+     * Specifies the Content-Type header, as a string.
+     * The default implementation calls
+     * {@link #setContentType(Header) setContentType(Header)}.
+     *
+     * @param ctString     the new Content-Type header, or
+     *                     <code>null</code> to unset
+     */
+    public void setContentType(final String ctString) {
+        Header h = null;
+        if (ctString != null) {
+            h = new BasicHeader(HTTP.CONTENT_TYPE, ctString);
+        }
+        setContentType(h);
+    }
+    
+
+    /**
+     * Specifies the Content-Encoding header.
+     * The default implementation sets the value of the
+     * {@link #contentEncoding contentEncoding} attribute.
+     *
+     * @param contentEncoding   the new Content-Encoding header, or
+     *                          <code>null</code> to unset
+     */
+    public void setContentEncoding(final Header contentEncoding) {
+        this.contentEncoding = contentEncoding;
+    }
+
+    /**
+     * Specifies the Content-Encoding header, as a string.
+     * The default implementation calls
+     * {@link #setContentEncoding(Header) setContentEncoding(Header)}.
+     *
+     * @param ceString     the new Content-Encoding header, or
+     *                     <code>null</code> to unset
+     */
+    public void setContentEncoding(final String ceString) {
+        Header h = null;
+        if (ceString != null) {
+            h = new BasicHeader(HTTP.CONTENT_ENCODING, ceString);
+        }
+        setContentEncoding(h);
+    }
+
+
+    /**
+     * Specifies the 'chunked' flag.
+     * The default implementation sets the value of the
+     * {@link #chunked chunked} attribute.
+     *
+     * @param b         the new 'chunked' flag
+     */
+    public void setChunked(boolean b) {
+        this.chunked = b;
+    }
+
+
+    /**
+     * Does not consume anything.
+     * The default implementation does nothing if
+     * {@link HttpEntity#isStreaming isStreaming}
+     * returns <code>false</code>, and throws an exception
+     * if it returns <code>true</code>.
+     * This removes the burden of implementing
+     * an empty method for non-streaming entities.
+     *
+     * @throws IOException      in case of an I/O problem
+     * @throws UnsupportedOperationException
+     *          if a streaming subclass does not override this method
+     */
+    public void consumeContent()
+        throws IOException, UnsupportedOperationException{
+        if (isStreaming()) {
+            throw new UnsupportedOperationException
+                ("streaming entity does not implement consumeContent()");
+        }
+    } // consumeContent
+
+    
+} // class AbstractHttpEntity
diff --git a/src/org/apache/http/entity/BasicHttpEntity.java b/src/org/apache/http/entity/BasicHttpEntity.java
new file mode 100644
index 0000000..df3c07c
--- /dev/null
+++ b/src/org/apache/http/entity/BasicHttpEntity.java
@@ -0,0 +1,146 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/BasicHttpEntity.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * A generic streamed entity being received on a connection.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 496070 $
+ * 
+ * @since 4.0
+ */
+public class BasicHttpEntity extends AbstractHttpEntity {
+
+    private InputStream content;
+    private boolean contentObtained;
+    private long length;
+
+    /**
+     * Creates a new basic entity.
+     * The content is initially missing, the content length
+     * is set to a negative number.
+     */
+    public BasicHttpEntity() {
+        super();
+        this.length = -1;
+    }
+
+    // non-javadoc, see interface HttpEntity
+    public long getContentLength() {
+        return this.length;
+    }
+
+    /**
+     * Obtains the content, once only.
+     *
+     * @return  the content, if this is the first call to this method
+     *          since {@link #setContent setContent} has been called
+     *
+     * @throws IllegalStateException
+     *          if the content has been obtained before, or
+     *          has not yet been provided
+     */
+    public InputStream getContent()
+        throws IllegalStateException {
+        if (this.content == null) {
+            throw new IllegalStateException("Content has not been provided");
+        }
+        if (this.contentObtained) {
+            throw new IllegalStateException("Content has been consumed");
+        }
+        this.contentObtained = true;
+        return this.content;
+
+    } // getContent
+
+    /**
+     * Tells that this entity is not repeatable.
+     *
+     * @return <code>false</code>
+     */
+    public boolean isRepeatable() {
+        return false;
+    }
+
+    /**
+     * Specifies the length of the content.
+     *
+     * @param len       the number of bytes in the content, or
+     *                  a negative number to indicate an unknown length
+     */
+    public void setContentLength(long len) {
+        this.length = len;
+    }
+
+    /**
+     * Specifies the content.
+     *
+     * @param instream          the stream to return with the next call to
+     *                          {@link #getContent getContent}
+     */
+    public void setContent(final InputStream instream) {
+        this.content = instream;
+        this.contentObtained = false; 
+    }
+
+    // non-javadoc, see interface HttpEntity
+    public void writeTo(final OutputStream outstream) throws IOException {
+        if (outstream == null) {
+            throw new IllegalArgumentException("Output stream may not be null");
+        }
+        InputStream instream = getContent();
+        int l;
+        byte[] tmp = new byte[2048];
+        while ((l = instream.read(tmp)) != -1) {
+            outstream.write(tmp, 0, l);
+        }
+    }
+
+    // non-javadoc, see interface HttpEntity
+    public boolean isStreaming() {
+        return !this.contentObtained && this.content != null;
+    }
+
+    // non-javadoc, see interface HttpEntity
+    public void consumeContent() throws IOException {
+        if (content != null) {
+            content.close(); // reads to the end of the entity
+        }
+    }
+    
+} // class BasicHttpEntity
diff --git a/src/org/apache/http/entity/BufferedHttpEntity.java b/src/org/apache/http/entity/BufferedHttpEntity.java
new file mode 100644
index 0000000..9888797
--- /dev/null
+++ b/src/org/apache/http/entity/BufferedHttpEntity.java
@@ -0,0 +1,120 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/BufferedHttpEntity.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.util.EntityUtils;
+
+/**
+ * A wrapping entity that buffers it content if necessary.
+ * The buffered entity is always repeatable.
+ * If the wrapped entity is repeatable itself, calls are passed through.
+ * If the wrapped entity is not repeatable, the content is read into a
+ * buffer once and provided from there as often as required.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 496070 $
+ * 
+ * @since 4.0
+ */
+public class BufferedHttpEntity extends HttpEntityWrapper {
+      
+    private final byte[] buffer;
+      
+    public BufferedHttpEntity(final HttpEntity entity) throws IOException {
+        super(entity);
+        if (!entity.isRepeatable() || entity.getContentLength() < 0) {
+            this.buffer = EntityUtils.toByteArray(entity);
+        } else {
+            this.buffer = null;
+        }
+    }
+
+    public long getContentLength() {
+        if (this.buffer != null) {
+            return this.buffer.length;
+        } else {
+            return wrappedEntity.getContentLength();
+        }
+    }
+    
+    public InputStream getContent() throws IOException {
+        if (this.buffer != null) {
+            return new ByteArrayInputStream(this.buffer);
+        } else {
+            return wrappedEntity.getContent();
+        }
+    }
+
+    /**
+     * Tells that this entity does not have to be chunked.
+     *
+     * @return  <code>false</code>
+     */
+    public boolean isChunked() {
+        return (buffer == null) && wrappedEntity.isChunked();
+    }
+    
+    /**
+     * Tells that this entity is repeatable.
+     *
+     * @return  <code>true</code>
+     */
+    public boolean isRepeatable() {
+        return true;
+    }
+
+    
+    public void writeTo(final OutputStream outstream) throws IOException {
+        if (outstream == null) {
+            throw new IllegalArgumentException("Output stream may not be null");
+        }
+        if (this.buffer != null) {
+            outstream.write(this.buffer);
+        } else {
+            wrappedEntity.writeTo(outstream);
+        }
+    }
+
+
+    // non-javadoc, see interface HttpEntity
+    public boolean isStreaming() {
+        return (buffer == null) && wrappedEntity.isStreaming();
+    }
+    
+} // class BufferedHttpEntity
diff --git a/src/org/apache/http/entity/ByteArrayEntity.java b/src/org/apache/http/entity/ByteArrayEntity.java
new file mode 100644
index 0000000..c7257f7
--- /dev/null
+++ b/src/org/apache/http/entity/ByteArrayEntity.java
@@ -0,0 +1,94 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/ByteArrayEntity.java $
+ * $Revision: 604625 $
+ * $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ *  An entity whose content is retrieved from a byte array.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 604625 $
+ * 
+ * @since 4.0
+ */
+public class ByteArrayEntity extends AbstractHttpEntity implements Cloneable {
+
+    protected final byte[] content;
+
+    public ByteArrayEntity(final byte[] b) {
+        super();        
+        if (b == null) {
+            throw new IllegalArgumentException("Source byte array may not be null");
+        }
+        this.content = b;
+    }
+
+    public boolean isRepeatable() {
+        return true;
+    }
+
+    public long getContentLength() {
+        return this.content.length;
+    }
+
+    public InputStream getContent() {
+        return new ByteArrayInputStream(this.content);
+    }
+    
+    public void writeTo(final OutputStream outstream) throws IOException {
+        if (outstream == null) {
+            throw new IllegalArgumentException("Output stream may not be null");
+        }
+        outstream.write(this.content);
+        outstream.flush();
+    }
+
+
+    /**
+     * Tells that this entity is not streaming.
+     *
+     * @return <code>false</code>
+     */
+    public boolean isStreaming() {
+        return false;
+    }
+
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+
+} // class ByteArrayEntity
diff --git a/src/org/apache/http/entity/ContentLengthStrategy.java b/src/org/apache/http/entity/ContentLengthStrategy.java
new file mode 100644
index 0000000..cc4ab7d
--- /dev/null
+++ b/src/org/apache/http/entity/ContentLengthStrategy.java
@@ -0,0 +1,54 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/ContentLengthStrategy.java $
+ * $Revision: 613298 $
+ * $Date: 2008-01-18 14:09:22 -0800 (Fri, 18 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+
+/**
+ * Represents a strategy to determine the content length based on the properties
+ * of an HTTP message.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 613298 $
+ * 
+ * @since 4.0
+ */
+public interface ContentLengthStrategy {
+
+    public static final int IDENTITY         = -1;
+    public static final int CHUNKED          = -2;
+    
+    long determineLength(HttpMessage message) throws HttpException;
+            
+}
diff --git a/src/org/apache/http/entity/ContentProducer.java b/src/org/apache/http/entity/ContentProducer.java
new file mode 100644
index 0000000..456eae3
--- /dev/null
+++ b/src/org/apache/http/entity/ContentProducer.java
@@ -0,0 +1,53 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/ContentProducer.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An abstract entity content producer.
+ *
+ *<p>Content producers are expected to be able to produce their 
+ * content multiple times</p>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 496070 $
+ * 
+ * @since 4.0
+ */
+public interface ContentProducer {
+
+    void writeTo(OutputStream outstream) throws IOException;
+
+}
diff --git a/src/org/apache/http/entity/EntityTemplate.java b/src/org/apache/http/entity/EntityTemplate.java
new file mode 100644
index 0000000..0c6002e
--- /dev/null
+++ b/src/org/apache/http/entity/EntityTemplate.java
@@ -0,0 +1,86 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/EntityTemplate.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Entity that delegates the process of content generation to an abstract
+ * content producer.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 496070 $
+ * 
+ * @since 4.0
+ */
+public class EntityTemplate extends AbstractHttpEntity {
+
+    private final ContentProducer contentproducer;
+    
+    public EntityTemplate(final ContentProducer contentproducer) {
+        super();
+        if (contentproducer == null) {
+            throw new IllegalArgumentException("Content producer may not be null");
+        }
+        this.contentproducer = contentproducer; 
+    }
+
+    public long getContentLength() {
+        return -1;
+    }
+
+    public InputStream getContent() {
+        throw new UnsupportedOperationException("Entity template does not implement getContent()");
+    }
+
+    public boolean isRepeatable() {
+        return true;
+    }
+
+    public void writeTo(final OutputStream outstream) throws IOException {
+        if (outstream == null) {
+            throw new IllegalArgumentException("Output stream may not be null");
+        }
+        this.contentproducer.writeTo(outstream);
+    }
+
+    public boolean isStreaming() {
+        return true;
+    }
+
+    public void consumeContent() throws IOException {
+    }
+    
+}
diff --git a/src/org/apache/http/entity/FileEntity.java b/src/org/apache/http/entity/FileEntity.java
new file mode 100644
index 0000000..a991058
--- /dev/null
+++ b/src/org/apache/http/entity/FileEntity.java
@@ -0,0 +1,106 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/FileEntity.java $
+ * $Revision: 604625 $
+ * $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * An entity whose content is retrieved from a file.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 604625 $
+ * 
+ * @since 4.0
+ */
+public class FileEntity extends AbstractHttpEntity implements Cloneable {
+
+    protected final File file; 
+
+    public FileEntity(final File file, final String contentType) {
+        super();
+        if (file == null) {
+            throw new IllegalArgumentException("File may not be null");
+        }
+        this.file = file;
+        setContentType(contentType);
+    }
+
+    public boolean isRepeatable() {
+        return true;
+    }
+
+    public long getContentLength() {
+        return this.file.length();
+    }
+    
+    public InputStream getContent() throws IOException {
+        return new FileInputStream(this.file);
+    }
+    
+    public void writeTo(final OutputStream outstream) throws IOException {
+        if (outstream == null) {
+            throw new IllegalArgumentException("Output stream may not be null");
+        }
+        InputStream instream = new FileInputStream(this.file);
+        try {
+            byte[] tmp = new byte[4096];
+            int l;
+            while ((l = instream.read(tmp)) != -1) {
+                outstream.write(tmp, 0, l);
+            }
+            outstream.flush();
+        } finally {
+            instream.close();
+        }
+    }
+
+    /**
+     * Tells that this entity is not streaming.
+     *
+     * @return <code>false</code>
+     */
+    public boolean isStreaming() {
+        return false;
+    }
+
+    public Object clone() throws CloneNotSupportedException {
+        // File instance is considered immutable
+        // No need to make a copy of it
+        return super.clone();
+    }
+
+} // class FileEntity
diff --git a/src/org/apache/http/entity/HttpEntityWrapper.java b/src/org/apache/http/entity/HttpEntityWrapper.java
new file mode 100644
index 0000000..17a4149
--- /dev/null
+++ b/src/org/apache/http/entity/HttpEntityWrapper.java
@@ -0,0 +1,113 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/HttpEntityWrapper.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+
+/**
+ * Base class for wrapping entities.
+ * Keeps a {@link #wrappedEntity wrappedEntity} and delegates all
+ * calls to it. Implementations of wrapping entities can derive
+ * from this class and need to override only those methods that
+ * should not be delegated to the wrapped entity.
+ *
+ * @version $Revision: 496070 $
+ * 
+ * @since 4.0
+ */
+public class HttpEntityWrapper implements HttpEntity {
+
+    /** The wrapped entity. */
+    protected HttpEntity wrappedEntity;
+
+    /**
+     * Creates a new entity wrapper.
+     *
+     * @param wrapped   the entity to wrap
+     */
+    public HttpEntityWrapper(HttpEntity wrapped) {
+        super();
+
+        if (wrapped == null) {
+            throw new IllegalArgumentException
+                ("wrapped entity must not be null");
+        }
+        wrappedEntity = wrapped;
+
+    } // constructor
+
+
+    public boolean isRepeatable() {
+        return wrappedEntity.isRepeatable();
+    }
+
+    public boolean isChunked() {
+        return wrappedEntity.isChunked();
+    }
+
+    public long getContentLength() {
+        return wrappedEntity.getContentLength();
+    }
+
+    public Header getContentType() {
+        return wrappedEntity.getContentType();
+    }
+
+    public Header getContentEncoding() {
+        return wrappedEntity.getContentEncoding();
+    }
+
+    public InputStream getContent()
+        throws IOException {
+        return wrappedEntity.getContent();
+    }
+
+    public void writeTo(OutputStream outstream)
+        throws IOException {
+        wrappedEntity.writeTo(outstream);
+    }
+
+    public boolean isStreaming() {
+        return wrappedEntity.isStreaming();
+    }
+
+    public void consumeContent()
+        throws IOException {
+        wrappedEntity.consumeContent();
+    }
+
+} // class HttpEntityWrapper
diff --git a/src/org/apache/http/entity/InputStreamEntity.java b/src/org/apache/http/entity/InputStreamEntity.java
new file mode 100644
index 0000000..6d33fe4
--- /dev/null
+++ b/src/org/apache/http/entity/InputStreamEntity.java
@@ -0,0 +1,116 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/InputStreamEntity.java $
+ * $Revision: 617591 $
+ * $Date: 2008-02-01 10:21:17 -0800 (Fri, 01 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+ 
+/**
+ * A streamed entity obtaining content from an {@link InputStream InputStream}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 617591 $
+ * 
+ * @since 4.0
+ */
+public class InputStreamEntity extends AbstractHttpEntity {
+
+    private final static int BUFFER_SIZE = 2048;
+
+    private final InputStream content;
+    private final long length;
+    private boolean consumed = false;
+
+    public InputStreamEntity(final InputStream instream, long length) {
+        super();        
+        if (instream == null) {
+            throw new IllegalArgumentException("Source input stream may not be null");
+        }
+        this.content = instream;
+        this.length = length;
+    }
+
+    public boolean isRepeatable() {
+        return false;
+    }
+
+    public long getContentLength() {
+        return this.length;
+    }
+
+    public InputStream getContent() throws IOException {
+        return this.content;
+    }
+        
+    public void writeTo(final OutputStream outstream) throws IOException {
+        if (outstream == null) {
+            throw new IllegalArgumentException("Output stream may not be null");
+        }
+        InputStream instream = this.content;
+        byte[] buffer = new byte[BUFFER_SIZE];
+        int l;
+        if (this.length < 0) {
+            // consume until EOF
+            while ((l = instream.read(buffer)) != -1) {
+                outstream.write(buffer, 0, l);
+            }
+        } else {
+            // consume no more than length
+            long remaining = this.length;
+            while (remaining > 0) {
+                l = instream.read(buffer, 0, (int)Math.min(BUFFER_SIZE, remaining));
+                if (l == -1) {
+                    break;
+                }
+                outstream.write(buffer, 0, l);
+                remaining -= l;
+            }
+        }
+        this.consumed = true;
+    }
+
+    // non-javadoc, see interface HttpEntity
+    public boolean isStreaming() {
+        return !this.consumed;
+    }
+
+    // non-javadoc, see interface HttpEntity
+    public void consumeContent() throws IOException {
+        this.consumed = true;
+        // If the input stream is from a connection, closing it will read to
+        // the end of the content. Otherwise, we don't care what it does.
+        this.content.close();
+    }
+    
+} // class InputStreamEntity
diff --git a/src/org/apache/http/entity/SerializableEntity.java b/src/org/apache/http/entity/SerializableEntity.java
new file mode 100644
index 0000000..171977b
--- /dev/null
+++ b/src/org/apache/http/entity/SerializableEntity.java
@@ -0,0 +1,107 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/SerializableEntity.java $
+ * $Revision: 647816 $
+ * $Date: 2008-04-14 07:37:13 -0700 (Mon, 14 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+
+public class SerializableEntity extends AbstractHttpEntity {
+    
+    private byte[] objSer;
+    
+    private Serializable objRef;
+    
+    public SerializableEntity(Serializable ser, boolean bufferize) throws IOException {
+        super();
+        if (ser == null) {
+            throw new IllegalArgumentException("Source object may not be null");
+        }
+        
+        if (bufferize) {
+            createBytes(ser);
+        } else {
+            this.objRef = ser;
+        }
+    }
+    
+    private void createBytes(Serializable ser) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream out = new ObjectOutputStream(baos);
+        out.writeObject(ser);
+        out.flush();	
+        this.objSer = baos.toByteArray();
+    }
+    
+    public InputStream getContent() throws IOException, IllegalStateException {
+        if (this.objSer == null) {
+            createBytes(this.objRef);
+        }
+        return new ByteArrayInputStream(this.objSer);
+    }
+
+    public long getContentLength() {
+        if (this.objSer ==  null) { 
+            return -1;
+        } else {
+            return this.objSer.length;
+        }
+    }
+
+    public boolean isRepeatable() {
+        return true;
+    }
+
+    public boolean isStreaming() {
+        return this.objSer == null;
+    }
+
+    public void writeTo(OutputStream outstream) throws IOException {
+        if (outstream == null) {
+            throw new IllegalArgumentException("Output stream may not be null");
+        }
+        
+        if (this.objSer == null) {
+            ObjectOutputStream out = new ObjectOutputStream(outstream);
+            out.writeObject(this.objRef);
+            out.flush();
+        } else {
+            outstream.write(this.objSer);
+            outstream.flush();
+        }
+    }
+
+}
diff --git a/src/org/apache/http/entity/StringEntity.java b/src/org/apache/http/entity/StringEntity.java
new file mode 100644
index 0000000..cbc382b
--- /dev/null
+++ b/src/org/apache/http/entity/StringEntity.java
@@ -0,0 +1,106 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/StringEntity.java $
+ * $Revision: 618367 $
+ * $Date: 2008-02-04 10:26:06 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.http.protocol.HTTP;
+
+/**
+ *  An entity whose content is retrieved from a string.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618367 $
+ * 
+ * @since 4.0
+ */
+public class StringEntity extends AbstractHttpEntity implements Cloneable {
+
+    protected final byte[] content;
+
+    public StringEntity(final String s, String charset) 
+            throws UnsupportedEncodingException {
+        super();
+        if (s == null) {
+            throw new IllegalArgumentException("Source string may not be null");
+        }
+        if (charset == null) {
+            charset = HTTP.DEFAULT_CONTENT_CHARSET;
+        }
+        this.content = s.getBytes(charset);
+        setContentType(HTTP.PLAIN_TEXT_TYPE + HTTP.CHARSET_PARAM + charset);
+    }
+
+    public StringEntity(final String s) 
+            throws UnsupportedEncodingException {
+        this(s, null);
+    }
+
+    public boolean isRepeatable() {
+        return true;
+    }
+
+    public long getContentLength() {
+        return this.content.length;
+    }
+    
+    public InputStream getContent() throws IOException {
+        return new ByteArrayInputStream(this.content);
+    }
+    
+    public void writeTo(final OutputStream outstream) throws IOException {
+        if (outstream == null) {
+            throw new IllegalArgumentException("Output stream may not be null");
+        }
+        outstream.write(this.content);
+        outstream.flush();
+    }
+
+    /**
+     * Tells that this entity is not streaming.
+     *
+     * @return <code>false</code>
+     */
+    public boolean isStreaming() {
+        return false;
+    }
+
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+
+} // class StringEntity
diff --git a/src/org/apache/http/entity/package.html b/src/org/apache/http/entity/package.html
new file mode 100644
index 0000000..11d491e
--- /dev/null
+++ b/src/org/apache/http/entity/package.html
@@ -0,0 +1,58 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/package.html $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Representations for HTTP message entities.
+
+An {@link org.apache.http.HttpEntity entity} is the optional content of a
+{@link org.apache.http.HttpMessage message}.
+You'll find a basic selection of entity implementations here.
+If you need to send an entity, you can provide it for example as a
+{@link org.apache.http.entity.ByteArrayEntity byte array},
+{@link org.apache.http.entity.StringEntity string},
+{@link org.apache.http.entity.FileEntity file}, or through an arbitrary
+{@link org.apache.http.entity.InputStreamEntity input stream}.
+If you receive a message with an entity, you typically get that as a
+{@link org.apache.http.entity.BasicHttpEntity basic} entity.
+Entity implementations can be
+{@link org.apache.http.entity.HttpEntityWrapper wrapped},
+for example to
+{@link org.apache.http.entity.BufferedHttpEntity buffer}
+the content in memory.
+
+
+
+</body>
+</html>
diff --git a/src/org/apache/http/impl/AbstractHttpClientConnection.java b/src/org/apache/http/impl/AbstractHttpClientConnection.java
new file mode 100644
index 0000000..ebfaabb
--- /dev/null
+++ b/src/org/apache/http/impl/AbstractHttpClientConnection.java
@@ -0,0 +1,212 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/AbstractHttpClientConnection.java $
+ * $Revision: 627457 $
+ * $Date: 2008-02-13 07:14:19 -0800 (Wed, 13 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.io.IOException;
+
+import org.apache.http.HttpClientConnection;
+import org.apache.http.HttpConnectionMetrics;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseFactory;
+import org.apache.http.impl.entity.EntityDeserializer;
+import org.apache.http.impl.entity.EntitySerializer;
+import org.apache.http.impl.entity.LaxContentLengthStrategy;
+import org.apache.http.impl.entity.StrictContentLengthStrategy;
+import org.apache.http.impl.io.HttpRequestWriter;
+import org.apache.http.impl.io.HttpResponseParser;
+import org.apache.http.io.HttpMessageParser;
+import org.apache.http.io.HttpMessageWriter;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Abstract client-side HTTP connection capable of transmitting and receiving data
+ * using arbitrary {@link SessionInputBuffer} and {@link SessionOutputBuffer}
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 627457 $
+ * 
+ * @since 4.0
+ */
+public abstract class AbstractHttpClientConnection implements HttpClientConnection {
+
+    private final EntitySerializer entityserializer;
+    private final EntityDeserializer entitydeserializer;
+    
+    private SessionInputBuffer inbuffer = null;
+    private SessionOutputBuffer outbuffer = null;
+    private HttpMessageParser responseParser = null;
+    private HttpMessageWriter requestWriter = null;
+    private HttpConnectionMetricsImpl metrics = null;
+
+
+
+    public AbstractHttpClientConnection() {
+        super();
+        this.entityserializer = createEntitySerializer();
+        this.entitydeserializer = createEntityDeserializer();
+    }
+    
+    protected abstract void assertOpen() throws IllegalStateException;
+
+    protected EntityDeserializer createEntityDeserializer() {
+        return new EntityDeserializer(new LaxContentLengthStrategy());
+    }
+
+    protected EntitySerializer createEntitySerializer() {
+        return new EntitySerializer(new StrictContentLengthStrategy());
+    }
+
+    protected HttpResponseFactory createHttpResponseFactory() {
+        return new DefaultHttpResponseFactory();
+    }
+
+    protected HttpMessageParser createResponseParser(
+            final SessionInputBuffer buffer,
+            final HttpResponseFactory responseFactory,
+            final HttpParams params) {
+        // override in derived class to specify a line parser
+        return new HttpResponseParser(buffer, null, responseFactory, params);
+    }
+
+    protected HttpMessageWriter createRequestWriter(
+            final SessionOutputBuffer buffer,
+            final HttpParams params) {
+        // override in derived class to specify a line formatter
+        return new HttpRequestWriter(buffer, null, params);
+    }
+
+    protected void init(
+            final SessionInputBuffer inbuffer,
+            final SessionOutputBuffer outbuffer,
+            final HttpParams params) {
+        if (inbuffer == null) {
+            throw new IllegalArgumentException("Input session buffer may not be null");
+        }
+        if (outbuffer == null) {
+            throw new IllegalArgumentException("Output session buffer may not be null");
+        }
+        this.inbuffer = inbuffer;
+        this.outbuffer = outbuffer;
+        this.responseParser = createResponseParser(
+                inbuffer, 
+                createHttpResponseFactory(), 
+                params);
+        this.requestWriter = createRequestWriter(
+                outbuffer, params);
+        this.metrics = new HttpConnectionMetricsImpl(
+                inbuffer.getMetrics(),
+                outbuffer.getMetrics());
+    }
+    
+    public boolean isResponseAvailable(int timeout) throws IOException {
+        assertOpen();
+        return this.inbuffer.isDataAvailable(timeout);
+    }
+
+    public void sendRequestHeader(final HttpRequest request) 
+            throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        assertOpen();
+        this.requestWriter.write(request);
+        this.metrics.incrementRequestCount();
+    }
+
+    public void sendRequestEntity(final HttpEntityEnclosingRequest request) 
+            throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        assertOpen();
+        if (request.getEntity() == null) {
+            return;
+        }
+        this.entityserializer.serialize(
+                this.outbuffer,
+                request,
+                request.getEntity());
+    }
+
+    protected void doFlush() throws IOException {
+        this.outbuffer.flush();
+    }
+    
+    public void flush() throws IOException {
+        assertOpen();
+        doFlush();
+    }
+    
+    public HttpResponse receiveResponseHeader() 
+            throws HttpException, IOException {
+        assertOpen();
+        HttpResponse response = (HttpResponse) this.responseParser.parse();
+        if (response.getStatusLine().getStatusCode() >= 200) {
+            this.metrics.incrementResponseCount();
+        }
+        return response;
+    }
+    
+    public void receiveResponseEntity(final HttpResponse response)
+            throws HttpException, IOException {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        assertOpen();
+        HttpEntity entity = this.entitydeserializer.deserialize(this.inbuffer, response);
+        response.setEntity(entity);
+    }
+    
+    public boolean isStale() {
+        if (!isOpen()) {
+            return true;
+        }
+        try {
+            this.inbuffer.isDataAvailable(1);
+            return false;
+        } catch (IOException ex) {
+            return true;
+        }
+    }
+    
+    public HttpConnectionMetrics getMetrics() {
+        return this.metrics;
+    }
+
+}
diff --git a/src/org/apache/http/impl/AbstractHttpServerConnection.java b/src/org/apache/http/impl/AbstractHttpServerConnection.java
new file mode 100644
index 0000000..ef68ed3
--- /dev/null
+++ b/src/org/apache/http/impl/AbstractHttpServerConnection.java
@@ -0,0 +1,202 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/AbstractHttpServerConnection.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.io.IOException;
+
+import org.apache.http.HttpConnectionMetrics;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestFactory;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpServerConnection;
+import org.apache.http.impl.entity.EntityDeserializer;
+import org.apache.http.impl.entity.EntitySerializer;
+import org.apache.http.impl.entity.LaxContentLengthStrategy;
+import org.apache.http.impl.entity.StrictContentLengthStrategy;
+import org.apache.http.impl.io.HttpRequestParser;
+import org.apache.http.impl.io.HttpResponseWriter;
+import org.apache.http.io.HttpMessageParser;
+import org.apache.http.io.HttpMessageWriter;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Abstract server-side HTTP connection capable of transmitting and receiving data
+ * using arbitrary {@link SessionInputBuffer} and {@link SessionOutputBuffer}
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618017 $
+ * 
+ * @since 4.0
+ */
+public abstract class AbstractHttpServerConnection implements HttpServerConnection {
+
+    private final EntitySerializer entityserializer;
+    private final EntityDeserializer entitydeserializer;
+    
+    private SessionInputBuffer inbuffer = null;
+    private SessionOutputBuffer outbuffer = null;
+    private HttpMessageParser requestParser = null;
+    private HttpMessageWriter responseWriter = null;
+    private HttpConnectionMetricsImpl metrics = null;
+
+
+
+    public AbstractHttpServerConnection() {
+        super();
+        this.entityserializer = createEntitySerializer();
+        this.entitydeserializer = createEntityDeserializer();
+    }
+    
+    protected abstract void assertOpen() throws IllegalStateException;
+
+    protected EntityDeserializer createEntityDeserializer() {
+        return new EntityDeserializer(new LaxContentLengthStrategy());
+    }
+
+    protected EntitySerializer createEntitySerializer() {
+        return new EntitySerializer(new StrictContentLengthStrategy());
+    }
+
+    protected HttpRequestFactory createHttpRequestFactory() {
+        return new DefaultHttpRequestFactory();
+    }
+
+    protected HttpMessageParser createRequestParser(
+            final SessionInputBuffer buffer,
+            final HttpRequestFactory requestFactory,
+            final HttpParams params) {
+        // override in derived class to specify a line parser
+        return new HttpRequestParser(buffer, null, requestFactory, params);
+    }
+    
+    protected HttpMessageWriter createResponseWriter(
+            final SessionOutputBuffer buffer,
+            final HttpParams params) {
+        // override in derived class to specify a line formatter
+        return new HttpResponseWriter(buffer, null, params);
+    }
+
+
+    protected void init(
+            final SessionInputBuffer inbuffer,
+            final SessionOutputBuffer outbuffer,
+            final HttpParams params) {
+        if (inbuffer == null) {
+            throw new IllegalArgumentException("Input session buffer may not be null");
+        }
+        if (outbuffer == null) {
+            throw new IllegalArgumentException("Output session buffer may not be null");
+        }
+        this.inbuffer = inbuffer;
+        this.outbuffer = outbuffer;
+        this.requestParser = createRequestParser(
+                inbuffer, 
+                createHttpRequestFactory(), 
+                params);
+        this.responseWriter = createResponseWriter(
+                outbuffer, params);
+        this.metrics = new HttpConnectionMetricsImpl(
+                inbuffer.getMetrics(),
+                outbuffer.getMetrics());
+    }
+    
+    public HttpRequest receiveRequestHeader() 
+            throws HttpException, IOException {
+        assertOpen();
+        HttpRequest request = (HttpRequest) this.requestParser.parse();
+        this.metrics.incrementRequestCount();
+        return request;
+    }
+    
+    public void receiveRequestEntity(final HttpEntityEnclosingRequest request) 
+            throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        assertOpen();
+        HttpEntity entity = this.entitydeserializer.deserialize(this.inbuffer, request);
+        request.setEntity(entity);
+    }
+
+    protected void doFlush() throws IOException  {
+        this.outbuffer.flush();
+    }
+    
+    public void flush() throws IOException {
+        assertOpen();
+        doFlush();
+    }
+    
+    public void sendResponseHeader(final HttpResponse response) 
+            throws HttpException, IOException {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        assertOpen();
+        this.responseWriter.write(response);
+        if (response.getStatusLine().getStatusCode() >= 200) {
+            this.metrics.incrementResponseCount();
+        }
+    }
+
+    public void sendResponseEntity(final HttpResponse response) 
+            throws HttpException, IOException {
+        if (response.getEntity() == null) {
+            return;
+        }
+        this.entityserializer.serialize(
+                this.outbuffer,
+                response,
+                response.getEntity());
+    }
+    
+    public boolean isStale() {
+        assertOpen();
+        try {
+            this.inbuffer.isDataAvailable(1);
+            return false;
+        } catch (IOException ex) {
+            return true;
+        }
+    }
+    
+    public HttpConnectionMetrics getMetrics() {
+        return this.metrics;
+    }
+
+}
diff --git a/src/org/apache/http/impl/DefaultConnectionReuseStrategy.java b/src/org/apache/http/impl/DefaultConnectionReuseStrategy.java
new file mode 100644
index 0000000..da1d5fd
--- /dev/null
+++ b/src/org/apache/http/impl/DefaultConnectionReuseStrategy.java
@@ -0,0 +1,182 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/DefaultConnectionReuseStrategy.java $
+ * $Revision: 602537 $
+ * $Date: 2007-12-08 11:42:06 -0800 (Sat, 08 Dec 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpConnection;
+import org.apache.http.HeaderIterator;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
+import org.apache.http.ParseException;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.TokenIterator;
+import org.apache.http.message.BasicTokenIterator;
+
+/**
+ * Default implementation of a strategy deciding about connection re-use.
+ * The default implementation first checks some basics, for example
+ * whether the connection is still open or whether the end of the
+ * request entity can be determined without closing the connection.
+ * If these checks pass, the tokens in the "Connection" header will
+ * be examined. In the absence of a "Connection" header, the
+ * non-standard but commonly used "Proxy-Connection" header takes
+ * it's role. A token "close" indicates that the connection cannot
+ * be reused. If there is no such token, a token "keep-alive" indicates
+ * that the connection should be re-used. If neither token is found,
+ * or if there are no "Connection" headers, the default policy for
+ * the HTTP version is applied. Since HTTP/1.1, connections are re-used
+ * by default. Up until HTTP/1.0, connections are not re-used by default.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ * @version $Revision: 602537 $
+ * 
+ * @since 4.0
+ */
+public class DefaultConnectionReuseStrategy
+    implements ConnectionReuseStrategy {
+
+    public DefaultConnectionReuseStrategy() {
+        super();
+    }
+
+    // see interface ConnectionReuseStrategy
+    public boolean keepAlive(final HttpResponse response,
+                             final HttpContext context) {
+        if (response == null) {
+            throw new IllegalArgumentException
+                ("HTTP response may not be null.");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException
+                ("HTTP context may not be null.");
+        }
+        
+        HttpConnection conn = (HttpConnection)
+            context.getAttribute(ExecutionContext.HTTP_CONNECTION);
+
+        if (conn != null && !conn.isOpen())
+            return false;
+        // do NOT check for stale connection, that is an expensive operation
+
+        // Check for a self-terminating entity. If the end of the entity will
+        // be indicated by closing the connection, there is no keep-alive.
+        HttpEntity entity = response.getEntity();
+        ProtocolVersion ver = response.getStatusLine().getProtocolVersion();
+        if (entity != null) {
+            if (entity.getContentLength() < 0) {
+                if (!entity.isChunked() ||
+                    ver.lessEquals(HttpVersion.HTTP_1_0)) {
+                    // if the content length is not known and is not chunk
+                    // encoded, the connection cannot be reused
+                    return false;
+                }
+            }
+        }
+
+        // Check for the "Connection" header. If that is absent, check for
+        // the "Proxy-Connection" header. The latter is an unspecified and
+        // broken but unfortunately common extension of HTTP.
+        HeaderIterator hit = response.headerIterator(HTTP.CONN_DIRECTIVE);
+        if (!hit.hasNext())
+            hit = response.headerIterator("Proxy-Connection");
+
+        // Experimental usage of the "Connection" header in HTTP/1.0 is
+        // documented in RFC 2068, section 19.7.1. A token "keep-alive" is
+        // used to indicate that the connection should be persistent.
+        // Note that the final specification of HTTP/1.1 in RFC 2616 does not
+        // include this information. Neither is the "Connection" header
+        // mentioned in RFC 1945, which informally describes HTTP/1.0.
+        //
+        // RFC 2616 specifies "close" as the only connection token with a
+        // specific meaning: it disables persistent connections.
+        //
+        // The "Proxy-Connection" header is not formally specified anywhere,
+        // but is commonly used to carry one token, "close" or "keep-alive".
+        // The "Connection" header, on the other hand, is defined as a
+        // sequence of tokens, where each token is a header name, and the
+        // token "close" has the above-mentioned additional meaning.
+        //
+        // To get through this mess, we treat the "Proxy-Connection" header
+        // in exactly the same way as the "Connection" header, but only if
+        // the latter is missing. We scan the sequence of tokens for both
+        // "close" and "keep-alive". As "close" is specified by RFC 2068,
+        // it takes precedence and indicates a non-persistent connection.
+        // If there is no "close" but a "keep-alive", we take the hint.
+
+        if (hit.hasNext()) {
+            try {
+                TokenIterator ti = createTokenIterator(hit);
+                boolean keepalive = false;
+                while (ti.hasNext()) {
+                    final String token = ti.nextToken();
+                    if (HTTP.CONN_CLOSE.equalsIgnoreCase(token)) {
+                        return false;
+                    } else if (HTTP.CONN_KEEP_ALIVE.equalsIgnoreCase(token)) {
+                        // continue the loop, there may be a "close" afterwards
+                        keepalive = true;
+                    }
+                }
+                if (keepalive)
+                    return true;
+                // neither "close" nor "keep-alive", use default policy
+
+            } catch (ParseException px) {
+                // invalid connection header means no persistent connection
+                // we don't have logging in HttpCore, so the exception is lost
+                return false;
+            }
+        }
+
+        // default since HTTP/1.1 is persistent, before it was non-persistent
+        return !ver.lessEquals(HttpVersion.HTTP_1_0);
+    }
+
+
+    /**
+     * Creates a token iterator from a header iterator.
+     * This method can be overridden to replace the implementation of
+     * the token iterator.
+     *
+     * @param hit       the header iterator
+     *
+     * @return  the token iterator
+     */
+    protected TokenIterator createTokenIterator(HeaderIterator hit) {
+        return new BasicTokenIterator(hit);
+    }
+}
diff --git a/src/org/apache/http/impl/DefaultHttpClientConnection.java b/src/org/apache/http/impl/DefaultHttpClientConnection.java
new file mode 100644
index 0000000..c0a96f5
--- /dev/null
+++ b/src/org/apache/http/impl/DefaultHttpClientConnection.java
@@ -0,0 +1,87 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/DefaultHttpClientConnection.java $
+ * $Revision: 561083 $
+ * $Date: 2007-07-30 11:31:17 -0700 (Mon, 30 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Default implementation of a client-side HTTP connection.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 561083 $
+ * 
+ * @since 4.0
+ */
+public class DefaultHttpClientConnection extends SocketHttpClientConnection {
+
+    public DefaultHttpClientConnection() {
+        super();
+    }
+    
+    public void bind(
+            final Socket socket, 
+            final HttpParams params) throws IOException {
+        if (socket == null) {
+            throw new IllegalArgumentException("Socket may not be null");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        assertNotOpen();
+        socket.setTcpNoDelay(HttpConnectionParams.getTcpNoDelay(params));
+        socket.setSoTimeout(HttpConnectionParams.getSoTimeout(params));
+        
+        int linger = HttpConnectionParams.getLinger(params);
+        if (linger >= 0) {
+            socket.setSoLinger(linger > 0, linger);
+        }
+        super.bind(socket, params);
+    }
+
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("[");
+        if (isOpen()) {
+            buffer.append(getRemotePort());
+        } else {
+            buffer.append("closed");
+        }
+        buffer.append("]");
+        return buffer.toString();
+    }
+    
+}
diff --git a/src/org/apache/http/impl/DefaultHttpRequestFactory.java b/src/org/apache/http/impl/DefaultHttpRequestFactory.java
new file mode 100644
index 0000000..dee36c9
--- /dev/null
+++ b/src/org/apache/http/impl/DefaultHttpRequestFactory.java
@@ -0,0 +1,113 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/DefaultHttpRequestFactory.java $
+ * $Revision: 618367 $
+ * $Date: 2008-02-04 10:26:06 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestFactory;
+import org.apache.http.MethodNotSupportedException;
+import org.apache.http.RequestLine;
+import org.apache.http.message.BasicHttpEntityEnclosingRequest;
+import org.apache.http.message.BasicHttpRequest;
+
+/**
+ * Default implementation of a factory for creating request objects.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618367 $
+ * 
+ * @since 4.0
+ */
+public class DefaultHttpRequestFactory implements HttpRequestFactory {
+    
+    private static final String[] RFC2616_COMMON_METHODS = {
+        "GET"
+    };
+    
+    private static final String[] RFC2616_ENTITY_ENC_METHODS = {
+        "POST",
+        "PUT"
+    };
+
+    private static final String[] RFC2616_SPECIAL_METHODS = {
+        "HEAD",
+        "OPTIONS",
+        "DELETE",
+        "TRACE"
+    };
+    
+    
+    public DefaultHttpRequestFactory() {
+        super();
+    }
+    
+    private static boolean isOneOf(final String[] methods, final String method) {
+        for (int i = 0; i < methods.length; i++) {
+            if (methods[i].equalsIgnoreCase(method)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public HttpRequest newHttpRequest(final RequestLine requestline)
+            throws MethodNotSupportedException {
+        if (requestline == null) {
+            throw new IllegalArgumentException("Request line may not be null");
+        }
+        String method = requestline.getMethod();
+        if (isOneOf(RFC2616_COMMON_METHODS, method)) {
+            return new BasicHttpRequest(requestline); 
+        } else if (isOneOf(RFC2616_ENTITY_ENC_METHODS, method)) {
+            return new BasicHttpEntityEnclosingRequest(requestline); 
+        } else if (isOneOf(RFC2616_SPECIAL_METHODS, method)) {
+            return new BasicHttpRequest(requestline); 
+        } else { 
+            throw new MethodNotSupportedException(method +  " method not supported");
+        }
+    }
+
+    public HttpRequest newHttpRequest(final String method, final String uri)
+            throws MethodNotSupportedException {
+        if (isOneOf(RFC2616_COMMON_METHODS, method)) {
+            return new BasicHttpRequest(method, uri); 
+        } else if (isOneOf(RFC2616_ENTITY_ENC_METHODS, method)) {
+            return new BasicHttpEntityEnclosingRequest(method, uri); 
+        } else if (isOneOf(RFC2616_SPECIAL_METHODS, method)) {
+            return new BasicHttpRequest(method, uri); 
+        } else {
+            throw new MethodNotSupportedException(method
+                    + " method not supported");
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/impl/DefaultHttpResponseFactory.java b/src/org/apache/http/impl/DefaultHttpResponseFactory.java
new file mode 100644
index 0000000..40a2c9a
--- /dev/null
+++ b/src/org/apache/http/impl/DefaultHttpResponseFactory.java
@@ -0,0 +1,121 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/DefaultHttpResponseFactory.java $
+ * $Revision: 618367 $
+ * $Date: 2008-02-04 10:26:06 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.util.Locale;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseFactory;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.StatusLine;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.message.BasicStatusLine;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.ReasonPhraseCatalog;
+import org.apache.http.impl.EnglishReasonPhraseCatalog;
+
+/**
+ * Default implementation of a factory for creating response objects.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618367 $
+ * 
+ * @since 4.0
+ */
+public class DefaultHttpResponseFactory implements HttpResponseFactory {
+
+    /** The catalog for looking up reason phrases. */
+    protected final ReasonPhraseCatalog reasonCatalog;
+
+
+    /**
+     * Creates a new response factory with the given catalog.
+     *
+     * @param catalog   the catalog of reason phrases
+     */
+    public DefaultHttpResponseFactory(ReasonPhraseCatalog catalog) {
+        if (catalog == null) {
+            throw new IllegalArgumentException
+                ("Reason phrase catalog must not be null.");
+        }
+        this.reasonCatalog = catalog;
+    }
+
+    /**
+     * Creates a new response factory with the default catalog.
+     * The default catalog is
+     * {@link EnglishReasonPhraseCatalog EnglishReasonPhraseCatalog}.
+     */
+    public DefaultHttpResponseFactory() {
+        this(EnglishReasonPhraseCatalog.INSTANCE);
+    }
+
+
+    // non-javadoc, see interface HttpResponseFactory
+    public HttpResponse newHttpResponse(final ProtocolVersion ver,
+                                        final int status,
+                                        HttpContext context) {
+        if (ver == null) {
+            throw new IllegalArgumentException("HTTP version may not be null");
+        }
+        final Locale loc      = determineLocale(context);
+        final String reason   = reasonCatalog.getReason(status, loc);
+        StatusLine statusline = new BasicStatusLine(ver, status, reason);
+        return new BasicHttpResponse(statusline, reasonCatalog, loc); 
+    }
+
+
+    // non-javadoc, see interface HttpResponseFactory
+    public HttpResponse newHttpResponse(final StatusLine statusline,
+                                        HttpContext context) {
+        if (statusline == null) {
+            throw new IllegalArgumentException("Status line may not be null");
+        }
+        final Locale loc = determineLocale(context);
+        return new BasicHttpResponse(statusline, reasonCatalog, loc);
+    }
+
+
+    /**
+     * Determines the locale of the response.
+     * The implementation in this class always returns the default locale.
+     *
+     * @param context   the context from which to determine the locale, or
+     *                  <code>null</code> to use the default locale
+     *
+     * @return  the locale for the response, never <code>null</code>
+     */
+    protected Locale determineLocale(HttpContext context) {
+        return Locale.getDefault();
+    }
+}
diff --git a/src/org/apache/http/impl/DefaultHttpServerConnection.java b/src/org/apache/http/impl/DefaultHttpServerConnection.java
new file mode 100644
index 0000000..d296fc8
--- /dev/null
+++ b/src/org/apache/http/impl/DefaultHttpServerConnection.java
@@ -0,0 +1,85 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/DefaultHttpServerConnection.java $
+ * $Revision: 561083 $
+ * $Date: 2007-07-30 11:31:17 -0700 (Mon, 30 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Default implementation of a server-side HTTP connection.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 561083 $
+ * 
+ * @since 4.0
+ */
+public class DefaultHttpServerConnection extends SocketHttpServerConnection {
+
+    public DefaultHttpServerConnection() {
+        super();
+    }
+    
+    public void bind(final Socket socket, final HttpParams params) throws IOException {
+        if (socket == null) {
+            throw new IllegalArgumentException("Socket may not be null");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        assertNotOpen();
+        socket.setTcpNoDelay(HttpConnectionParams.getTcpNoDelay(params));
+        socket.setSoTimeout(HttpConnectionParams.getSoTimeout(params));
+        
+        int linger = HttpConnectionParams.getLinger(params);
+        if (linger >= 0) {
+            socket.setSoLinger(linger > 0, linger);
+        }
+        super.bind(socket, params);
+    }
+
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("[");
+        if (isOpen()) {
+            buffer.append(getRemotePort());
+        } else {
+            buffer.append("closed");
+        }
+        buffer.append("]");
+        return buffer.toString();
+    }
+    
+}
diff --git a/src/org/apache/http/impl/EnglishReasonPhraseCatalog.java b/src/org/apache/http/impl/EnglishReasonPhraseCatalog.java
new file mode 100644
index 0000000..f1aeee1
--- /dev/null
+++ b/src/org/apache/http/impl/EnglishReasonPhraseCatalog.java
@@ -0,0 +1,234 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/EnglishReasonPhraseCatalog.java $
+ * $Revision: 505744 $
+ * $Date: 2007-02-10 10:58:45 -0800 (Sat, 10 Feb 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.util.Locale;
+
+import org.apache.http.HttpStatus;
+import org.apache.http.ReasonPhraseCatalog;
+
+
+/**
+ * English reason phrases for HTTP status codes.
+ * All status codes defined in RFC1945 (HTTP/1.0), RFC2616 (HTTP/1.1), and
+ * RFC2518 (WebDAV) are supported.
+ * 
+ * @author Unascribed
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * 
+ * @version $Revision: 505744 $
+ */
+public class EnglishReasonPhraseCatalog
+    implements ReasonPhraseCatalog {
+
+    // static array with english reason phrases defined below
+
+    /**
+     * The default instance of this catalog.
+     * This catalog is thread safe, so there typically
+     * is no need to create other instances.
+     */
+    public final static EnglishReasonPhraseCatalog INSTANCE =
+        new EnglishReasonPhraseCatalog();
+
+
+    /**
+     * Restricted default constructor, for derived classes.
+     * If you need an instance of this class, use {@link #INSTANCE INSTANCE}.
+     */
+    protected EnglishReasonPhraseCatalog() {
+        // no body
+    }
+
+
+    /**
+     * Obtains the reason phrase for a status code.
+     *
+     * @param status    the status code, in the range 100-599
+     * @param loc       ignored
+     *
+     * @return  the reason phrase, or <code>null</code>
+     */
+    public String getReason(int status, Locale loc) {
+        if ((status < 100) || (status >= 600)) {
+            throw new IllegalArgumentException
+                ("Unknown category for status code " + status + ".");
+        }
+
+        final int category = status / 100;
+        final int subcode  = status - 100*category;
+
+        String reason = null;
+        if (REASON_PHRASES[category].length > subcode)
+            reason = REASON_PHRASES[category][subcode];
+
+        return reason;
+    }
+
+
+    /** Reason phrases lookup table. */
+    private static final String[][] REASON_PHRASES = new String[][]{
+        null,
+        new String[3],  // 1xx
+        new String[8],  // 2xx
+        new String[8],  // 3xx
+        new String[25], // 4xx
+        new String[8]   // 5xx
+    };
+
+
+
+    /**
+     * Stores the given reason phrase, by status code.
+     * Helper method to initialize the static lookup table.
+     *
+     * @param status    the status code for which to define the phrase
+     * @param reason    the reason phrase for this status code
+     */
+    private static void setReason(int status, String reason) {
+        final int category = status / 100;
+        final int subcode  = status - 100*category;
+        REASON_PHRASES[category][subcode] = reason;
+    }
+
+
+    // ----------------------------------------------------- Static Initializer
+
+    /** Set up status code to "reason phrase" map. */
+    static {
+        // HTTP 1.0 Server status codes -- see RFC 1945
+        setReason(HttpStatus.SC_OK,
+                  "OK");
+        setReason(HttpStatus.SC_CREATED,
+                  "Created");
+        setReason(HttpStatus.SC_ACCEPTED,
+                  "Accepted");
+        setReason(HttpStatus.SC_NO_CONTENT,
+                  "No Content");
+        setReason(HttpStatus.SC_MOVED_PERMANENTLY,
+                  "Moved Permanently");
+        setReason(HttpStatus.SC_MOVED_TEMPORARILY,
+                  "Moved Temporarily");
+        setReason(HttpStatus.SC_NOT_MODIFIED,
+                  "Not Modified");
+        setReason(HttpStatus.SC_BAD_REQUEST,
+                  "Bad Request");
+        setReason(HttpStatus.SC_UNAUTHORIZED,
+                  "Unauthorized");
+        setReason(HttpStatus.SC_FORBIDDEN,
+                  "Forbidden");
+        setReason(HttpStatus.SC_NOT_FOUND,
+                  "Not Found");
+        setReason(HttpStatus.SC_INTERNAL_SERVER_ERROR,
+                  "Internal Server Error");
+        setReason(HttpStatus.SC_NOT_IMPLEMENTED,
+                  "Not Implemented");
+        setReason(HttpStatus.SC_BAD_GATEWAY,
+                  "Bad Gateway");
+        setReason(HttpStatus.SC_SERVICE_UNAVAILABLE,
+                  "Service Unavailable");
+
+        // HTTP 1.1 Server status codes -- see RFC 2048
+        setReason(HttpStatus.SC_CONTINUE,
+                  "Continue");
+        setReason(HttpStatus.SC_TEMPORARY_REDIRECT,
+                  "Temporary Redirect");
+        setReason(HttpStatus.SC_METHOD_NOT_ALLOWED,
+                  "Method Not Allowed");
+        setReason(HttpStatus.SC_CONFLICT,
+                  "Conflict");
+        setReason(HttpStatus.SC_PRECONDITION_FAILED,
+                  "Precondition Failed");
+        setReason(HttpStatus.SC_REQUEST_TOO_LONG,
+                  "Request Too Long");
+        setReason(HttpStatus.SC_REQUEST_URI_TOO_LONG,
+                  "Request-URI Too Long");
+        setReason(HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE,
+                  "Unsupported Media Type");
+        setReason(HttpStatus.SC_MULTIPLE_CHOICES,
+                  "Multiple Choices");
+        setReason(HttpStatus.SC_SEE_OTHER,
+                  "See Other");
+        setReason(HttpStatus.SC_USE_PROXY,
+                  "Use Proxy");
+        setReason(HttpStatus.SC_PAYMENT_REQUIRED,
+                  "Payment Required");
+        setReason(HttpStatus.SC_NOT_ACCEPTABLE,
+                  "Not Acceptable");
+        setReason(HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED, 
+                  "Proxy Authentication Required");
+        setReason(HttpStatus.SC_REQUEST_TIMEOUT, 
+                  "Request Timeout");
+
+        setReason(HttpStatus.SC_SWITCHING_PROTOCOLS,
+                  "Switching Protocols");
+        setReason(HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION,
+                  "Non Authoritative Information");
+        setReason(HttpStatus.SC_RESET_CONTENT,
+                  "Reset Content");
+        setReason(HttpStatus.SC_PARTIAL_CONTENT,
+                  "Partial Content");
+        setReason(HttpStatus.SC_GATEWAY_TIMEOUT,
+                  "Gateway Timeout");
+        setReason(HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED,
+                  "Http Version Not Supported");
+        setReason(HttpStatus.SC_GONE,
+                  "Gone");
+        setReason(HttpStatus.SC_LENGTH_REQUIRED,
+                  "Length Required");
+        setReason(HttpStatus.SC_REQUESTED_RANGE_NOT_SATISFIABLE,
+                  "Requested Range Not Satisfiable");
+        setReason(HttpStatus.SC_EXPECTATION_FAILED,
+                  "Expectation Failed");
+
+        // WebDAV Server-specific status codes
+        setReason(HttpStatus.SC_PROCESSING,
+                  "Processing");
+        setReason(HttpStatus.SC_MULTI_STATUS,
+                  "Multi-Status");
+        setReason(HttpStatus.SC_UNPROCESSABLE_ENTITY,
+                  "Unprocessable Entity");
+        setReason(HttpStatus.SC_INSUFFICIENT_SPACE_ON_RESOURCE,
+                  "Insufficient Space On Resource");
+        setReason(HttpStatus.SC_METHOD_FAILURE,
+                  "Method Failure");
+        setReason(HttpStatus.SC_LOCKED,
+                  "Locked");
+        setReason(HttpStatus.SC_INSUFFICIENT_STORAGE,
+                  "Insufficient Storage");
+        setReason(HttpStatus.SC_FAILED_DEPENDENCY,
+                  "Failed Dependency");
+    }
+
+
+}
diff --git a/src/org/apache/http/impl/HttpConnectionMetricsImpl.java b/src/org/apache/http/impl/HttpConnectionMetricsImpl.java
new file mode 100644
index 0000000..4f4eacf
--- /dev/null
+++ b/src/org/apache/http/impl/HttpConnectionMetricsImpl.java
@@ -0,0 +1,146 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/HttpConnectionMetricsImpl.java $
+ * $Revision: 548031 $
+ * $Date: 2007-06-17 04:28:38 -0700 (Sun, 17 Jun 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.util.HashMap;
+import org.apache.http.HttpConnectionMetrics;
+import org.apache.http.io.HttpTransportMetrics;
+
+/**
+ * Implementation of the metrics interface.
+ */
+public class HttpConnectionMetricsImpl implements HttpConnectionMetrics {
+    
+    public static final String REQUEST_COUNT = "http.request-count";
+    public static final String RESPONSE_COUNT = "http.response-count";
+    public static final String SENT_BYTES_COUNT = "http.sent-bytes-count";
+    public static final String RECEIVED_BYTES_COUNT = "http.received-bytes-count";
+    
+    private final HttpTransportMetrics inTransportMetric;
+    private final HttpTransportMetrics outTransportMetric;
+    private long requestCount = 0;
+    private long responseCount = 0;
+    
+    /**
+     * The cache map for all metrics values.
+     */
+    private HashMap metricsCache;
+    
+    public HttpConnectionMetricsImpl(
+            final HttpTransportMetrics inTransportMetric,
+            final HttpTransportMetrics outTransportMetric) {
+        super();
+        this.inTransportMetric = inTransportMetric;
+        this.outTransportMetric = outTransportMetric;
+    }
+    
+    /* ------------------  Public interface method -------------------------- */
+
+    public long getReceivedBytesCount() {
+        if (this.inTransportMetric != null) {
+            return this.inTransportMetric.getBytesTransferred();
+        } else {
+            return -1;
+        }
+    }
+
+    public long getSentBytesCount() {
+        if (this.outTransportMetric != null) {
+            return this.outTransportMetric.getBytesTransferred();
+        } else {
+            return -1;
+        }
+    }
+    
+    public long getRequestCount() {
+        return this.requestCount;
+    }
+    
+    public void incrementRequestCount() {
+        this.requestCount++;
+    }
+    
+    public long getResponseCount() {
+        return this.responseCount;
+    }
+    
+    public void incrementResponseCount() {
+        this.responseCount++;
+    }
+    
+    public Object getMetric(final String metricName) {
+        Object value = null;
+        if (this.metricsCache != null) {
+            value = this.metricsCache.get(metricName);
+        }
+        if (value == null) {
+            if (REQUEST_COUNT.equals(metricName)) {
+                value = new Long(requestCount);
+            } else if (RESPONSE_COUNT.equals(metricName)) {
+                value = new Long(responseCount);
+            } else if (RECEIVED_BYTES_COUNT.equals(metricName)) {
+                if (this.inTransportMetric != null) {
+                    return new Long(this.inTransportMetric.getBytesTransferred());
+                } else {
+                    return null;
+                }
+            } else if (SENT_BYTES_COUNT.equals(metricName)) {
+                if (this.outTransportMetric != null) {
+                    return new Long(this.outTransportMetric.getBytesTransferred());
+                } else {
+                    return null;
+                }
+            }
+        }
+        return value;
+    }
+    
+    public void setMetric(final String metricName, Object obj) {
+        if (this.metricsCache == null) {
+            this.metricsCache = new HashMap();
+        }
+        this.metricsCache.put(metricName, obj);
+    }
+    
+    public void reset() {
+        if (this.outTransportMetric != null) {
+            this.outTransportMetric.reset();
+        }
+        if (this.inTransportMetric != null) {
+            this.inTransportMetric.reset();
+        }
+        this.requestCount = 0;
+        this.responseCount = 0;
+        this.metricsCache = null;
+    }
+
+}
diff --git a/src/org/apache/http/impl/NoConnectionReuseStrategy.java b/src/org/apache/http/impl/NoConnectionReuseStrategy.java
new file mode 100644
index 0000000..c7a5f73
--- /dev/null
+++ b/src/org/apache/http/impl/NoConnectionReuseStrategy.java
@@ -0,0 +1,65 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/NoConnectionReuseStrategy.java $
+ * $Revision: 502684 $
+ * $Date: 2007-02-02 10:25:38 -0800 (Fri, 02 Feb 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpResponse;
+import org.apache.http.protocol.HttpContext;
+
+
+/**
+ * A strategy that never re-uses a connection.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ * @version $Revision: 502684 $
+ * 
+ * @since 4.0
+ */
+public class NoConnectionReuseStrategy implements ConnectionReuseStrategy {
+
+    // default constructor
+
+
+    // non-JavaDoc, see interface ConnectionReuseStrategy
+    public boolean keepAlive(final HttpResponse response, final HttpContext context) {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+
+        return false;
+    }
+            
+}
diff --git a/src/org/apache/http/impl/SocketHttpClientConnection.java b/src/org/apache/http/impl/SocketHttpClientConnection.java
new file mode 100644
index 0000000..1e551e0
--- /dev/null
+++ b/src/org/apache/http/impl/SocketHttpClientConnection.java
@@ -0,0 +1,208 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/SocketHttpClientConnection.java $
+ * $Revision: 561083 $
+ * $Date: 2007-07-30 11:31:17 -0700 (Mon, 30 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+
+import org.apache.http.HttpInetConnection;
+import org.apache.http.impl.io.SocketInputBuffer;
+import org.apache.http.impl.io.SocketOutputBuffer;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Implementation of a client-side HTTP connection that can be bound to a 
+ * network Socket in order to receive and transmit data.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 561083 $
+ * 
+ * @since 4.0
+ */
+public class SocketHttpClientConnection 
+        extends AbstractHttpClientConnection implements HttpInetConnection {
+
+    private volatile boolean open;
+    private Socket socket = null;
+    
+    public SocketHttpClientConnection() {
+        super();
+    }
+    
+    protected void assertNotOpen() {
+        if (this.open) {
+            throw new IllegalStateException("Connection is already open");
+        }
+    }
+    
+    protected void assertOpen() {
+        if (!this.open) {
+            throw new IllegalStateException("Connection is not open");
+        }
+    }
+
+    protected SessionInputBuffer createSessionInputBuffer(
+            final Socket socket, 
+            int buffersize,
+            final HttpParams params) throws IOException {
+        return new SocketInputBuffer(socket, buffersize, params);
+    }
+    
+    protected SessionOutputBuffer createSessionOutputBuffer(
+            final Socket socket, 
+            int buffersize,
+            final HttpParams params) throws IOException {
+        return new SocketOutputBuffer(socket, buffersize, params);
+    }
+    
+    protected void bind(
+            final Socket socket, 
+            final HttpParams params) throws IOException {
+        if (socket == null) {
+            throw new IllegalArgumentException("Socket may not be null");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        this.socket = socket;
+
+        int buffersize = HttpConnectionParams.getSocketBufferSize(params);
+
+        init(
+                createSessionInputBuffer(socket, buffersize, params), 
+                createSessionOutputBuffer(socket, buffersize, params),
+                params);
+        
+        this.open = true;
+    }
+
+    public boolean isOpen() {
+        return this.open;
+    }
+    
+    protected Socket getSocket() {
+        return this.socket;
+    }
+
+    public InetAddress getLocalAddress() {
+        if (this.socket != null) {
+            return this.socket.getLocalAddress();
+        } else {
+            return null;
+        }
+    }
+
+    public int getLocalPort() {
+        if (this.socket != null) {
+            return this.socket.getLocalPort();
+        } else {
+            return -1;
+        }
+    }
+
+    public InetAddress getRemoteAddress() {
+        if (this.socket != null) {
+            return this.socket.getInetAddress();
+        } else {
+            return null;
+        }
+    }
+
+    public int getRemotePort() {
+        if (this.socket != null) {
+            return this.socket.getPort();
+        } else {
+            return -1;
+        }
+    }
+
+    public void setSocketTimeout(int timeout) {
+        assertOpen();
+        if (this.socket != null) {
+            try {
+                this.socket.setSoTimeout(timeout);
+            } catch (SocketException ignore) {
+                // It is not quite clear from the Sun's documentation if there are any 
+                // other legitimate cases for a socket exception to be thrown when setting 
+                // SO_TIMEOUT besides the socket being already closed
+            }
+        }
+    }
+    
+    public int getSocketTimeout() {
+        if (this.socket != null) {
+            try {
+                return this.socket.getSoTimeout();
+            } catch (SocketException ignore) {
+                return -1;
+            }
+        } else {
+            return -1;
+        }
+    }
+
+    public void shutdown() throws IOException {
+        this.open = false;
+        Socket tmpsocket = this.socket;
+        if (tmpsocket != null) {
+            tmpsocket.close();
+        }
+    }
+    
+    public void close() throws IOException {
+        if (!this.open) {
+            return;
+        }
+        this.open = false;
+        doFlush();
+        try {
+            try {
+                this.socket.shutdownOutput();
+            } catch (IOException ignore) {
+            }
+            try {
+                this.socket.shutdownInput();
+            } catch (IOException ignore) {
+            }
+        } catch (UnsupportedOperationException ignore) {
+            // if one isn't supported, the other one isn't either
+        }
+        this.socket.close();
+    }
+    
+}
diff --git a/src/org/apache/http/impl/SocketHttpServerConnection.java b/src/org/apache/http/impl/SocketHttpServerConnection.java
new file mode 100644
index 0000000..cfa2bf9
--- /dev/null
+++ b/src/org/apache/http/impl/SocketHttpServerConnection.java
@@ -0,0 +1,202 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/SocketHttpServerConnection.java $
+ * $Revision: 561083 $
+ * $Date: 2007-07-30 11:31:17 -0700 (Mon, 30 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+
+import org.apache.http.HttpInetConnection;
+import org.apache.http.impl.io.SocketInputBuffer;
+import org.apache.http.impl.io.SocketOutputBuffer;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Implementation of a server-side HTTP connection that can be bound to a 
+ * network Socket in order to receive and transmit data.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 561083 $
+ * 
+ * @since 4.0
+ */
+public class SocketHttpServerConnection extends 
+        AbstractHttpServerConnection implements HttpInetConnection {
+
+    private volatile boolean open;
+    private Socket socket = null;
+    
+    public SocketHttpServerConnection() {
+        super();
+    }
+
+    protected void assertNotOpen() {
+        if (this.open) {
+            throw new IllegalStateException("Connection is already open");
+        }
+    }
+    
+    protected void assertOpen() {
+        if (!this.open) {
+            throw new IllegalStateException("Connection is not open");
+        }
+    }
+    
+    protected SessionInputBuffer createHttpDataReceiver(
+            final Socket socket, 
+            int buffersize,
+            final HttpParams params) throws IOException {
+        return new SocketInputBuffer(socket, buffersize, params);
+    }
+    
+    protected SessionOutputBuffer createHttpDataTransmitter(
+            final Socket socket, 
+            int buffersize,
+            final HttpParams params) throws IOException {
+        return new SocketOutputBuffer(socket, buffersize, params);
+    }
+    
+    protected void bind(final Socket socket, final HttpParams params) throws IOException {
+        if (socket == null) {
+            throw new IllegalArgumentException("Socket may not be null");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        this.socket = socket;
+        
+        int buffersize = HttpConnectionParams.getSocketBufferSize(params);
+        
+        init(
+                createHttpDataReceiver(socket, buffersize, params), 
+                createHttpDataTransmitter(socket, buffersize, params),
+                params);
+        
+        this.open = true;
+    }
+
+    protected Socket getSocket() {
+        return this.socket;
+    }
+
+    public boolean isOpen() {
+        return this.open;
+    }
+
+    public InetAddress getLocalAddress() {
+        if (this.socket != null) {
+            return this.socket.getLocalAddress();
+        } else {
+            return null;
+        }
+    }
+
+    public int getLocalPort() {
+        if (this.socket != null) {
+            return this.socket.getLocalPort();
+        } else {
+            return -1;
+        }
+    }
+
+    public InetAddress getRemoteAddress() {
+        if (this.socket != null) {
+            return this.socket.getInetAddress();
+        } else {
+            return null;
+        }
+    }
+
+    public int getRemotePort() {
+        if (this.socket != null) {
+            return this.socket.getPort();
+        } else {
+            return -1;
+        }
+    }
+
+    public void setSocketTimeout(int timeout) {
+        assertOpen();
+        if (this.socket != null) {
+            try {
+                this.socket.setSoTimeout(timeout);
+            } catch (SocketException ignore) {
+                // It is not quite clear from the Sun's documentation if there are any 
+                // other legitimate cases for a socket exception to be thrown when setting 
+                // SO_TIMEOUT besides the socket being already closed
+            }
+        }
+    }
+    
+    public int getSocketTimeout() {
+        if (this.socket != null) {
+            try {
+                return this.socket.getSoTimeout();
+            } catch (SocketException ignore) {
+                return -1;
+            }
+        } else {
+            return -1;
+        }
+    }
+
+    public void shutdown() throws IOException {
+        this.open = false;
+        Socket tmpsocket = this.socket;
+        if (tmpsocket != null) {
+            tmpsocket.close();
+        }
+    }
+    
+    public void close() throws IOException {
+        if (!this.open) {
+            return;
+        }
+        this.open = false;
+        doFlush();
+        try {
+            this.socket.shutdownOutput();
+        } catch (IOException ignore) {
+        }
+        try {
+            this.socket.shutdownInput();
+        } catch (IOException ignore) {
+        }
+        this.socket.close();
+    }
+    
+}
diff --git a/src/org/apache/http/impl/auth/AuthSchemeBase.java b/src/org/apache/http/impl/auth/AuthSchemeBase.java
new file mode 100644
index 0000000..689ce5d
--- /dev/null
+++ b/src/org/apache/http/impl/auth/AuthSchemeBase.java
@@ -0,0 +1,128 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/AuthSchemeBase.java $
+ * $Revision: 653867 $
+ * $Date: 2008-05-06 11:17:29 -0700 (Tue, 06 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import org.apache.http.FormattedHeader;
+import org.apache.http.Header;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Abstract authentication scheme class that serves as a basis
+ * for all authentication schemes supported by HttpClient. This class
+ * defines the generic way of parsing an authentication challenge. It 
+ * does not make any assumptions regarding the format of the challenge 
+ * nor does it impose any specific way of responding to that challenge.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+*/
+public abstract class AuthSchemeBase implements AuthScheme {
+
+    /**
+     * Flag whether authenticating against a proxy.
+     */
+    private boolean proxy;
+    
+    public AuthSchemeBase() {
+        super();
+    }
+
+    /**
+     * 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 header the challenge header
+     * 
+     * @throws MalformedChallengeException is thrown if the authentication challenge
+     * is malformed
+     */
+    public void processChallenge(final Header header) throws MalformedChallengeException {
+        if (header == null) {
+            throw new IllegalArgumentException("Header may not be null");
+        }
+        String authheader = header.getName();
+        if (authheader.equalsIgnoreCase(AUTH.WWW_AUTH)) {
+            this.proxy = false;
+        } else if (authheader.equalsIgnoreCase(AUTH.PROXY_AUTH)) {
+            this.proxy = true;
+        } else {
+            throw new MalformedChallengeException("Unexpected header name: " + authheader);
+        }
+
+        CharArrayBuffer buffer;
+        int pos;
+        if (header instanceof FormattedHeader) {
+            buffer = ((FormattedHeader) header).getBuffer();
+            pos = ((FormattedHeader) header).getValuePos();
+        } else {
+            String s = header.getValue();
+            if (s == null) {
+                throw new MalformedChallengeException("Header value is null");
+            }
+            buffer = new CharArrayBuffer(s.length());
+            buffer.append(s);
+            pos = 0;
+        }
+        while (pos < buffer.length() && HTTP.isWhitespace(buffer.charAt(pos))) {
+            pos++;
+        }
+        int beginIndex = pos;
+        while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) {
+            pos++;
+        }
+        int endIndex = pos;
+        String s = buffer.substring(beginIndex, endIndex);
+        if (!s.equalsIgnoreCase(getSchemeName())) {
+            throw new MalformedChallengeException("Invalid scheme identifier: " + s);
+        }
+        
+        parseChallenge(buffer, pos, buffer.length());
+    }
+
+    protected abstract void parseChallenge(
+            CharArrayBuffer buffer, int pos, int len) throws MalformedChallengeException;
+
+    /**
+     * Returns <code>true</code> if authenticating against a proxy, <code>false</code>
+     * otherwise.
+     *  
+     * @return <code>true</code> if authenticating against a proxy, <code>false</code>
+     * otherwise
+     */
+    public boolean isProxy() {
+        return this.proxy;
+    }
+    
+}
diff --git a/src/org/apache/http/impl/auth/BasicScheme.java b/src/org/apache/http/impl/auth/BasicScheme.java
new file mode 100644
index 0000000..88ea110
--- /dev/null
+++ b/src/org/apache/http/impl/auth/BasicScheme.java
@@ -0,0 +1,185 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/BasicScheme.java $
+ * $Revision: 658430 $
+ * $Date: 2008-05-20 14:04:27 -0700 (Tue, 20 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.http.Header;
+import org.apache.http.HttpRequest;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.auth.params.AuthParams;
+import org.apache.http.message.BufferedHeader;
+import org.apache.http.util.CharArrayBuffer;
+import org.apache.http.util.EncodingUtils;
+
+/**
+ * <p>
+ * Basic authentication scheme as defined in RFC 2617.
+ * </p>
+ * 
+ * @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 Glueck
+ * @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 at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+
+public class BasicScheme extends RFC2617Scheme {
+    
+    /** Whether the basic authentication process is complete */
+    private boolean complete;
+    
+    /**
+     * Default constructor for the basic authetication scheme.
+     */
+    public BasicScheme() {
+        super();
+        this.complete = false;
+    }
+
+    /**
+     * Returns textual designation of the basic authentication scheme.
+     * 
+     * @return <code>basic</code>
+     */
+    public String getSchemeName() {
+        return "basic";
+    }
+
+    /**
+     * Processes the Basic challenge.
+     *  
+     * @param header the challenge header
+     * 
+     * @throws MalformedChallengeException is thrown if the authentication challenge
+     * is malformed
+     */
+    @Override
+    public void processChallenge(
+            final Header header) throws MalformedChallengeException {
+        super.processChallenge(header);
+        this.complete = true;
+    }
+
+    /**
+     * Tests if the Basic authentication process has been completed.
+     * 
+     * @return <tt>true</tt> if Basic authorization has been processed,
+     *   <tt>false</tt> otherwise.
+     */
+    public boolean isComplete() {
+        return this.complete;
+    }
+
+    /**
+     * Returns <tt>false</tt>. Basic authentication scheme is request based.
+     * 
+     * @return <tt>false</tt>.
+     */
+    public boolean isConnectionBased() {
+        return false;    
+    }
+
+    /**
+     * Produces basic authorization header for the given set of {@link Credentials}.
+     * 
+     * @param credentials The set of credentials to be used for athentication
+     * @param request The request being authenticated
+     * @throws org.apache.http.auth.InvalidCredentialsException if authentication credentials
+     *         are not valid or not applicable for this authentication scheme
+     * @throws AuthenticationException if authorization string cannot 
+     *   be generated due to an authentication failure
+     * 
+     * @return a basic authorization string
+     */
+    public Header authenticate(
+            final Credentials credentials, 
+            final HttpRequest request) throws AuthenticationException {
+
+        if (credentials == null) {
+            throw new IllegalArgumentException("Credentials may not be null");
+        }
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        
+        String charset = AuthParams.getCredentialCharset(request.getParams());
+        return authenticate(credentials, charset, isProxy());
+    }
+    
+    /**
+     * Returns a basic <tt>Authorization</tt> header value for the given 
+     * {@link Credentials} and charset.
+     * 
+     * @param credentials The credentials to encode.
+     * @param charset The charset to use for encoding the credentials
+     * 
+     * @return a basic authorization header
+     */
+    public static Header authenticate(
+            final Credentials credentials, 
+            final String charset, 
+            boolean proxy) {
+        if (credentials == null) {
+            throw new IllegalArgumentException("Credentials may not be null"); 
+        }
+        if (charset == null) {
+            throw new IllegalArgumentException("charset may not be null");
+        }
+
+        StringBuilder tmp = new StringBuilder();
+        tmp.append(credentials.getUserPrincipal().getName());
+        tmp.append(":");
+        tmp.append((credentials.getPassword() == null) ? "null" : credentials.getPassword());
+
+        byte[] base64password = Base64.encodeBase64(
+                EncodingUtils.getBytes(tmp.toString(), charset));
+        
+        CharArrayBuffer buffer = new CharArrayBuffer(32);
+        if (proxy) {
+            buffer.append(AUTH.PROXY_AUTH_RESP);
+        } else {
+            buffer.append(AUTH.WWW_AUTH_RESP);
+        }
+        buffer.append(": Basic ");
+        buffer.append(base64password, 0, base64password.length);
+        
+        return new BufferedHeader(buffer);
+    }
+    
+}
diff --git a/src/org/apache/http/impl/auth/BasicSchemeFactory.java b/src/org/apache/http/impl/auth/BasicSchemeFactory.java
new file mode 100644
index 0000000..c5d28b0
--- /dev/null
+++ b/src/org/apache/http/impl/auth/BasicSchemeFactory.java
@@ -0,0 +1,50 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/BasicSchemeFactory.java $
+ * $Revision: 534839 $
+ * $Date: 2007-05-03 06:03:41 -0700 (Thu, 03 May 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthSchemeFactory;
+import org.apache.http.params.HttpParams;
+
+/**
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class BasicSchemeFactory implements AuthSchemeFactory {    
+
+    public AuthScheme newInstance(final HttpParams params) {
+        return new BasicScheme();
+    }
+
+}
diff --git a/src/org/apache/http/impl/auth/DigestScheme.java b/src/org/apache/http/impl/auth/DigestScheme.java
new file mode 100644
index 0000000..803807b
--- /dev/null
+++ b/src/org/apache/http/impl/auth/DigestScheme.java
@@ -0,0 +1,484 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/DigestScheme.java $
+ * $Revision: 659595 $
+ * $Date: 2008-05-23 09:47:14 -0700 (Fri, 23 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.apache.http.Header;
+import org.apache.http.HttpRequest;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.auth.params.AuthParams;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.message.BasicHeaderValueFormatter;
+import org.apache.http.message.BufferedHeader;
+import org.apache.http.util.CharArrayBuffer;
+import org.apache.http.util.EncodingUtils;
+
+/**
+ * <p>
+ * Digest authentication scheme as defined in RFC 2617.
+ * Both MD5 (default) and MD5-sess are supported.
+ * Currently only qop=auth or no qop is supported. qop=auth-int
+ * is unsupported. If auth and auth-int are provided, auth is
+ * used.
+ * </p>
+ * <p>
+ * Credential charset is configured via the 
+ * {@link org.apache.http.auth.params.AuthPNames#CREDENTIAL_CHARSET
+ *        credential charset} parameter.
+ * Since the digest username is included as clear text in the generated 
+ * Authentication header, the charset of the username must be compatible
+ * with the 
+ * {@link org.apache.http.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET
+ *        http element charset}.
+ * </p>
+ * 
+ * @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 Glueck
+ * @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 at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+
+public class DigestScheme extends RFC2617Scheme {
+    
+    /**
+     * Hexa values used when creating 32 character long digest in HTTP DigestScheme
+     * in case of authentication.
+     * 
+     * @see #encode(byte[])
+     */
+    private static final char[] HEXADECIMAL = {
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 
+        'e', 'f'
+    };
+    
+    /** Whether the digest authentication process is complete */
+    private boolean complete;
+    
+    //TODO: supply a real nonce-count, currently a server will interprete a repeated request as a replay  
+    private static final String NC = "00000001"; //nonce-count is always 1
+    private static final int QOP_MISSING = 0;
+    private static final int QOP_AUTH_INT = 1;
+    private static final int QOP_AUTH = 2;
+
+    private int qopVariant = QOP_MISSING;
+    private String cnonce;
+
+    /**
+     * Default constructor for the digest authetication scheme.
+     */
+    public DigestScheme() {
+        super();
+        this.complete = false;
+    }
+
+    /**
+     * Processes the Digest challenge.
+     *  
+     * @param header the challenge header
+     * 
+     * @throws MalformedChallengeException is thrown if the authentication challenge
+     * is malformed
+     */
+    @Override
+    public void processChallenge(
+            final Header header) throws MalformedChallengeException {
+        super.processChallenge(header);
+        
+        if (getParameter("realm") == null) {
+            throw new MalformedChallengeException("missing realm in challange");
+        }
+        if (getParameter("nonce") == null) {
+            throw new MalformedChallengeException("missing nonce in challange");   
+        }
+        
+        boolean unsupportedQop = false;
+        // qop parsing
+        String qop = getParameter("qop");
+        if (qop != null) {
+            StringTokenizer tok = new StringTokenizer(qop,",");
+            while (tok.hasMoreTokens()) {
+                String variant = tok.nextToken().trim();
+                if (variant.equals("auth")) {
+                    qopVariant = QOP_AUTH;
+                    break; //that's our favourite, because auth-int is unsupported
+                } else if (variant.equals("auth-int")) {
+                    qopVariant = QOP_AUTH_INT;               
+                } else {
+                    unsupportedQop = true;
+                }     
+            }
+        }        
+        
+        if (unsupportedQop && (qopVariant == QOP_MISSING)) {
+            throw new MalformedChallengeException("None of the qop methods is supported");   
+        }
+        // Reset cnonce
+        this.cnonce = null;
+        this.complete = true;
+    }
+
+    /**
+     * Tests if the Digest authentication process has been completed.
+     * 
+     * @return <tt>true</tt> if Digest authorization has been processed,
+     *   <tt>false</tt> otherwise.
+     */
+    public boolean isComplete() {
+        String s = getParameter("stale");
+        if ("true".equalsIgnoreCase(s)) {
+            return false;
+        } else {
+            return this.complete;
+        }
+    }
+
+    /**
+     * Returns textual designation of the digest authentication scheme.
+     * 
+     * @return <code>digest</code>
+     */
+    public String getSchemeName() {
+        return "digest";
+    }
+
+    /**
+     * Returns <tt>false</tt>. Digest authentication scheme is request based.
+     * 
+     * @return <tt>false</tt>.
+     */
+    public boolean isConnectionBased() {
+        return false;    
+    }
+
+    public void overrideParamter(final String name, final String value) {
+        getParameters().put(name, value);
+    }
+    
+    private String getCnonce() {
+        if (this.cnonce == null) {
+            this.cnonce = createCnonce();
+        }
+        return this.cnonce; 
+    }
+    
+    /**
+     * Produces a digest authorization string for the given set of 
+     * {@link Credentials}, method name and URI.
+     * 
+     * @param credentials A set of credentials to be used for athentication
+     * @param request    The request being authenticated
+     * 
+     * @throws org.apache.http.auth.InvalidCredentialsException if authentication credentials
+     *         are not valid or not applicable for this authentication scheme
+     * @throws AuthenticationException if authorization string cannot 
+     *   be generated due to an authentication failure
+     * 
+     * @return a digest authorization string
+     */
+    public Header authenticate(
+            final Credentials credentials, 
+            final HttpRequest request) throws AuthenticationException {
+
+        if (credentials == null) {
+            throw new IllegalArgumentException("Credentials may not be null");
+        }
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        
+        // Add method name and request-URI to the parameter map
+        getParameters().put("methodname", request.getRequestLine().getMethod());
+        getParameters().put("uri", request.getRequestLine().getUri());
+        String charset = getParameter("charset");
+        if (charset == null) {
+            charset = AuthParams.getCredentialCharset(request.getParams());
+            getParameters().put("charset", charset);
+        }
+        String digest = createDigest(credentials);
+        return createDigestHeader(credentials, digest);
+    }
+    
+    private static MessageDigest createMessageDigest(
+            final String digAlg) throws UnsupportedDigestAlgorithmException {
+        try {
+            return MessageDigest.getInstance(digAlg);
+        } catch (Exception e) {
+            throw new UnsupportedDigestAlgorithmException(
+              "Unsupported algorithm in HTTP Digest authentication: "
+               + digAlg);
+        }
+    }
+    
+    /**
+     * Creates an MD5 response digest.
+     * 
+     * @return The created digest as string. This will be the response tag's
+     *         value in the Authentication HTTP header.
+     * @throws AuthenticationException when MD5 is an unsupported algorithm
+     */
+    private String createDigest(final Credentials credentials) throws AuthenticationException {
+        // Collecting required tokens
+        String uri = getParameter("uri");
+        String realm = getParameter("realm");
+        String nonce = getParameter("nonce");
+        String method = getParameter("methodname");
+        String algorithm = getParameter("algorithm");
+        if (uri == null) {
+            throw new IllegalStateException("URI may not be null");
+        }
+        if (realm == null) {
+            throw new IllegalStateException("Realm may not be null");
+        }
+        if (nonce == null) {
+            throw new IllegalStateException("Nonce may not be null");
+        }
+        // If an algorithm is not specified, default to MD5.
+        if (algorithm == null) {
+            algorithm = "MD5";
+        }
+        // If an charset is not specified, default to ISO-8859-1.
+        String charset = getParameter("charset");
+        if (charset == null) {
+            charset = "ISO-8859-1";
+        }
+
+        if (qopVariant == QOP_AUTH_INT) {
+            throw new AuthenticationException(
+                "Unsupported qop in HTTP Digest authentication");   
+        }
+
+        MessageDigest md5Helper = createMessageDigest("MD5");
+
+        String uname = credentials.getUserPrincipal().getName();
+        String pwd = credentials.getPassword();
+        
+        // 3.2.2.2: Calculating digest
+        StringBuilder tmp = new StringBuilder(uname.length() + realm.length() + pwd.length() + 2);
+        tmp.append(uname);
+        tmp.append(':');
+        tmp.append(realm);
+        tmp.append(':');
+        tmp.append(pwd);
+        // unq(username-value) ":" unq(realm-value) ":" passwd
+        String a1 = tmp.toString();
+        
+        //a1 is suitable for MD5 algorithm
+        if(algorithm.equals("MD5-sess")) {
+            // H( unq(username-value) ":" unq(realm-value) ":" passwd )
+            //      ":" unq(nonce-value)
+            //      ":" unq(cnonce-value)
+
+            String cnonce = getCnonce();
+            
+            String tmp2=encode(md5Helper.digest(EncodingUtils.getBytes(a1, charset)));
+            StringBuilder tmp3 = new StringBuilder(tmp2.length() + nonce.length() + cnonce.length() + 2);
+            tmp3.append(tmp2);
+            tmp3.append(':');
+            tmp3.append(nonce);
+            tmp3.append(':');
+            tmp3.append(cnonce);
+            a1 = tmp3.toString();
+        } else if (!algorithm.equals("MD5")) {
+            throw new AuthenticationException("Unhandled algorithm " + algorithm + " requested");
+        }
+        String md5a1 = encode(md5Helper.digest(EncodingUtils.getBytes(a1, charset)));
+
+        String a2 = null;
+        if (qopVariant == QOP_AUTH_INT) {
+            // Unhandled qop auth-int
+            //we do not have access to the entity-body or its hash
+            //TODO: add Method ":" digest-uri-value ":" H(entity-body)      
+        } else {
+            a2 = method + ':' + uri;
+        }
+        String md5a2 = encode(md5Helper.digest(EncodingUtils.getAsciiBytes(a2)));
+
+        // 3.2.2.1
+        String serverDigestValue;
+        if (qopVariant == QOP_MISSING) {
+            StringBuilder tmp2 = new StringBuilder(md5a1.length() + nonce.length() + md5a2.length());
+            tmp2.append(md5a1);
+            tmp2.append(':');
+            tmp2.append(nonce);
+            tmp2.append(':');
+            tmp2.append(md5a2);
+            serverDigestValue = tmp2.toString();
+        } else {
+            String qopOption = getQopVariantString();
+            String cnonce = getCnonce();
+            
+            StringBuilder tmp2 = new StringBuilder(md5a1.length() + nonce.length()
+                + NC.length() + cnonce.length() + qopOption.length() + md5a2.length() + 5);
+            tmp2.append(md5a1);
+            tmp2.append(':');
+            tmp2.append(nonce);
+            tmp2.append(':');
+            tmp2.append(NC);
+            tmp2.append(':');
+            tmp2.append(cnonce);
+            tmp2.append(':');
+            tmp2.append(qopOption);
+            tmp2.append(':');
+            tmp2.append(md5a2); 
+            serverDigestValue = tmp2.toString();
+        }
+
+        String serverDigest =
+            encode(md5Helper.digest(EncodingUtils.getAsciiBytes(serverDigestValue)));
+
+        return serverDigest;
+    }
+
+    /**
+     * Creates digest-response header as defined in RFC2617.
+     * 
+     * @param credentials User credentials
+     * @param digest The response tag's value as String.
+     * 
+     * @return The digest-response as String.
+     */
+    private Header createDigestHeader(
+            final Credentials credentials, 
+            final String digest) throws AuthenticationException {
+        
+        CharArrayBuffer buffer = new CharArrayBuffer(128);
+        if (isProxy()) {
+            buffer.append(AUTH.PROXY_AUTH_RESP);
+        } else {
+            buffer.append(AUTH.WWW_AUTH_RESP);
+        }
+        buffer.append(": Digest ");
+        
+        String uri = getParameter("uri");
+        String realm = getParameter("realm");
+        String nonce = getParameter("nonce");
+        String opaque = getParameter("opaque");
+        String response = digest;
+        String algorithm = getParameter("algorithm");
+
+        String uname = credentials.getUserPrincipal().getName();
+        
+        List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>(20);
+        params.add(new BasicNameValuePair("username", uname));
+        params.add(new BasicNameValuePair("realm", realm));
+        params.add(new BasicNameValuePair("nonce", nonce));
+        params.add(new BasicNameValuePair("uri", uri));
+        params.add(new BasicNameValuePair("response", response));
+        
+        if (qopVariant != QOP_MISSING) {
+            params.add(new BasicNameValuePair("qop", getQopVariantString()));
+            params.add(new BasicNameValuePair("nc", NC));
+            params.add(new BasicNameValuePair("cnonce", getCnonce()));
+        }
+        if (algorithm != null) {
+            params.add(new BasicNameValuePair("algorithm", algorithm));
+        }    
+        if (opaque != null) {
+            params.add(new BasicNameValuePair("opaque", opaque));
+        }
+
+        for (int i = 0; i < params.size(); i++) {
+            BasicNameValuePair param = params.get(i);
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            boolean noQuotes = "nc".equals(param.getName()) ||
+                               "qop".equals(param.getName());
+            BasicHeaderValueFormatter.DEFAULT
+                .formatNameValuePair(buffer, param, !noQuotes);
+        }
+        return new BufferedHeader(buffer);
+    }
+
+    private String getQopVariantString() {
+        String qopOption;
+        if (qopVariant == QOP_AUTH_INT) {
+            qopOption = "auth-int";   
+        } else {
+            qopOption = "auth";
+        }
+        return qopOption;            
+    }
+
+    /**
+     * Encodes the 128 bit (16 bytes) MD5 digest into a 32 characters long 
+     * <CODE>String</CODE> according to RFC 2617.
+     * 
+     * @param binaryData array containing the digest
+     * @return encoded MD5, or <CODE>null</CODE> if encoding failed
+     */
+    private static String encode(byte[] binaryData) {
+        if (binaryData.length != 16) {
+            return null;
+        } 
+
+        char[] buffer = new char[32];
+        for (int i = 0; i < 16; i++) {
+            int low = (binaryData[i] & 0x0f);
+            int high = ((binaryData[i] & 0xf0) >> 4);
+            buffer[i * 2] = HEXADECIMAL[high];
+            buffer[(i * 2) + 1] = HEXADECIMAL[low];
+        }
+
+        return new String(buffer);
+    }
+
+
+    /**
+     * Creates a random cnonce value based on the current time.
+     * 
+     * @return The cnonce value as String.
+     * @throws UnsupportedDigestAlgorithmException if MD5 algorithm is not supported.
+     */
+    public static String createCnonce() {
+        String cnonce;
+
+        MessageDigest md5Helper = createMessageDigest("MD5");
+
+        cnonce = Long.toString(System.currentTimeMillis());
+        cnonce = encode(md5Helper.digest(EncodingUtils.getAsciiBytes(cnonce)));
+
+        return cnonce;
+    }
+}
diff --git a/src/org/apache/http/impl/auth/DigestSchemeFactory.java b/src/org/apache/http/impl/auth/DigestSchemeFactory.java
new file mode 100644
index 0000000..38f2e12
--- /dev/null
+++ b/src/org/apache/http/impl/auth/DigestSchemeFactory.java
@@ -0,0 +1,50 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/DigestSchemeFactory.java $
+ * $Revision: 534839 $
+ * $Date: 2007-05-03 06:03:41 -0700 (Thu, 03 May 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthSchemeFactory;
+import org.apache.http.params.HttpParams;
+
+/**
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class DigestSchemeFactory implements AuthSchemeFactory {    
+
+    public AuthScheme newInstance(final HttpParams params) {
+        return new DigestScheme();
+    }
+
+}
diff --git a/src/org/apache/http/impl/auth/NTLMEngine.java b/src/org/apache/http/impl/auth/NTLMEngine.java
new file mode 100644
index 0000000..7b6bf42
--- /dev/null
+++ b/src/org/apache/http/impl/auth/NTLMEngine.java
@@ -0,0 +1,76 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/NTLMEngine.java $
+ * $Revision: 659788 $
+ * $Date: 2008-05-24 03:42:23 -0700 (Sat, 24 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+/**
+ * Abstract NTLM authentication engine. The engine can be used to
+ * generate Type1 messages and Type3 messages in response to a 
+ * Type2 challenge.
+ * <p/>
+ * For details see <a href="http://davenport.sourceforge.net/ntlm.html">this resource</a>
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+*/
+public interface NTLMEngine {
+
+    /**
+     * Generates a Type1 message given the domain and workstation.
+     * 
+     * @param domain Optional Windows domain name. Can be <code>null</code>.
+     * @param workstation Optional Windows workstation name. Can be 
+     *  <code>null</code>.
+     * @return Type1 message
+     * @throws NTLMEngineException
+     */
+    String generateType1Msg(
+            String domain, 
+            String workstation) throws NTLMEngineException;
+    
+    /**
+     * Generates a Type3 message given the user credentials and the 
+     * authentication challenge.
+     *  
+     * @param username Windows user name
+     * @param password Password
+     * @param domain Windows domain name
+     * @param workstation Windows workstation name
+     * @param challenge Type2 challenge.
+     * @return Type3 response.
+     * @throws NTLMEngineException
+     */
+    String generateType3Msg(
+            String username,
+            String password,
+            String domain, 
+            String workstation,
+            String challenge) throws NTLMEngineException;
+    
+}
diff --git a/src/org/apache/http/impl/auth/NTLMEngineException.java b/src/org/apache/http/impl/auth/NTLMEngineException.java
new file mode 100644
index 0000000..73baabc
--- /dev/null
+++ b/src/org/apache/http/impl/auth/NTLMEngineException.java
@@ -0,0 +1,70 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/NTLMEngineException.java $
+ * $Revision: 655048 $
+ * $Date: 2008-05-10 04:22:12 -0700 (Sat, 10 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import org.apache.http.auth.AuthenticationException;
+
+/**
+ * Signals NTLM protocol failure.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public class NTLMEngineException extends AuthenticationException {
+
+    private static final long serialVersionUID = 6027981323731768824L;
+
+    public NTLMEngineException() {
+        super();
+    }
+
+    /**
+     * Creates a new NTLMEngineException with the specified message.
+     * 
+     * @param message the exception detail message
+     */
+    public NTLMEngineException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new NTLMEngineException with the specified detail message and cause.
+     * 
+     * @param message the exception detail message
+     * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+     * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+     */
+    public NTLMEngineException(String message, Throwable cause) {
+        super(message, cause);
+    }
+    
+}
diff --git a/src/org/apache/http/impl/auth/NTLMScheme.java b/src/org/apache/http/impl/auth/NTLMScheme.java
new file mode 100644
index 0000000..8dfdbba
--- /dev/null
+++ b/src/org/apache/http/impl/auth/NTLMScheme.java
@@ -0,0 +1,149 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/NTLMScheme.java $
+ * $Revision: 655048 $
+ * $Date: 2008-05-10 04:22:12 -0700 (Sat, 10 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import org.apache.http.Header;
+import org.apache.http.HttpRequest;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.InvalidCredentialsException;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.auth.NTCredentials;
+import org.apache.http.impl.auth.AuthSchemeBase;
+import org.apache.http.message.BufferedHeader;
+import org.apache.http.util.CharArrayBuffer;
+
+public class NTLMScheme extends AuthSchemeBase {
+
+    enum State {
+        UNINITIATED,
+        CHALLENGE_RECEIVED,
+        MSG_TYPE1_GENERATED,
+        MSG_TYPE2_RECEVIED,
+        MSG_TYPE3_GENERATED,
+        FAILED,
+    }
+    
+    private final NTLMEngine engine;
+    
+    private State state;
+    private String challenge;
+    
+    public NTLMScheme(final NTLMEngine engine) {
+        super();
+        if (engine == null) {
+            throw new IllegalArgumentException("NTLM engine may not be null");
+        }
+        this.engine = engine;
+        this.state = State.UNINITIATED;
+        this.challenge = null;
+    }
+    
+    public String getSchemeName() {
+        return "ntlm";
+    }
+
+    public String getParameter(String name) {
+        // String parameters not supported
+        return null;
+    }
+
+    public String getRealm() {
+        // NTLM does not support the concept of an authentication realm
+        return null;
+    }
+
+    public boolean isConnectionBased() {
+        return true;
+    }
+
+    @Override
+    protected void parseChallenge(
+            final CharArrayBuffer buffer, int pos, int len) throws MalformedChallengeException {
+        String challenge = buffer.substringTrimmed(pos, len);
+        if (challenge.length() == 0) {
+            if (this.state == State.UNINITIATED) {
+                this.state = State.CHALLENGE_RECEIVED;
+            } else {
+                this.state = State.FAILED;
+            }
+            this.challenge = null;
+        } else {
+            this.state = State.MSG_TYPE2_RECEVIED;
+            this.challenge = challenge;
+        }
+    }
+
+    public Header authenticate(
+            final Credentials credentials, 
+            final HttpRequest request) throws AuthenticationException {
+        NTCredentials ntcredentials = null;
+        try {
+            ntcredentials = (NTCredentials) credentials;
+        } catch (ClassCastException e) {
+            throw new InvalidCredentialsException(
+             "Credentials cannot be used for NTLM authentication: " 
+              + credentials.getClass().getName());
+        }
+        String response = null;
+        if (this.state == State.CHALLENGE_RECEIVED || this.state == State.FAILED) {
+            response = this.engine.generateType1Msg(
+                    ntcredentials.getDomain(), 
+                    ntcredentials.getWorkstation());
+            this.state = State.MSG_TYPE1_GENERATED;
+        } else if (this.state == State.MSG_TYPE2_RECEVIED) {
+            response = this.engine.generateType3Msg(
+                    ntcredentials.getUserName(), 
+                    ntcredentials.getPassword(), 
+                    ntcredentials.getDomain(), 
+                    ntcredentials.getWorkstation(),
+                    this.challenge);
+            this.state = State.MSG_TYPE3_GENERATED;
+        } else {
+            throw new AuthenticationException("Unexpected state: " + this.state);
+        }
+        CharArrayBuffer buffer = new CharArrayBuffer(32);
+        if (isProxy()) {
+            buffer.append(AUTH.PROXY_AUTH_RESP);
+        } else {
+            buffer.append(AUTH.WWW_AUTH_RESP);
+        }
+        buffer.append(": NTLM ");
+        buffer.append(response);
+        return new BufferedHeader(buffer);
+    }
+
+    public boolean isComplete() {
+        return this.state == State.MSG_TYPE3_GENERATED || this.state == State.FAILED;
+    }
+
+}
diff --git a/src/org/apache/http/impl/auth/RFC2617Scheme.java b/src/org/apache/http/impl/auth/RFC2617Scheme.java
new file mode 100644
index 0000000..0ed0a28
--- /dev/null
+++ b/src/org/apache/http/impl/auth/RFC2617Scheme.java
@@ -0,0 +1,119 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/RFC2617Scheme.java $
+ * $Revision: 659595 $
+ * $Date: 2008-05-23 09:47:14 -0700 (Fri, 23 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.http.HeaderElement;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.message.BasicHeaderValueParser;
+import org.apache.http.message.HeaderValueParser;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Abstract authentication scheme class that lays foundation for all
+ * RFC 2617 compliant authetication schemes and provides capabilities common 
+ * to all authentication schemes defined in RFC 2617.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+*/
+public abstract class RFC2617Scheme extends AuthSchemeBase {
+
+    /**
+     * Authentication parameter map.
+     */
+    private Map<String, String> params;
+
+    /**
+     * Default constructor for RFC2617 compliant authetication schemes.
+     */
+    public RFC2617Scheme() {
+        super();
+    }
+
+    @Override
+    protected void parseChallenge(
+            final CharArrayBuffer buffer, int pos, int len) throws MalformedChallengeException {
+        HeaderValueParser parser = BasicHeaderValueParser.DEFAULT;
+        ParserCursor cursor = new ParserCursor(pos, buffer.length()); 
+        HeaderElement[] elements = parser.parseElements(buffer, cursor);
+        if (elements.length == 0) {
+            throw new MalformedChallengeException("Authentication challenge is empty");
+        }
+        
+        this.params = new HashMap<String, String>(elements.length);
+        for (HeaderElement element : elements) {
+            this.params.put(element.getName(), element.getValue());
+        }
+    }
+
+    /**
+     * Returns authentication parameters map. Keys in the map are lower-cased.
+     * 
+     * @return the map of authentication parameters
+     */
+    protected Map<String, String> getParameters() {
+        if (this.params == null) {
+            this.params = new HashMap<String, String>();
+        }
+        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(final String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("Parameter name may not be null"); 
+        }
+        if (this.params == null) {
+            return null;
+        }
+        return this.params.get(name.toLowerCase(Locale.ENGLISH));
+    }
+
+    /**
+     * Returns authentication realm. The realm may not be null.
+     * 
+     * @return the authentication realm
+     */
+    public String getRealm() {
+        return getParameter("realm");
+    }
+
+}
diff --git a/src/org/apache/http/impl/auth/UnsupportedDigestAlgorithmException.java b/src/org/apache/http/impl/auth/UnsupportedDigestAlgorithmException.java
new file mode 100644
index 0000000..abd0a66
--- /dev/null
+++ b/src/org/apache/http/impl/auth/UnsupportedDigestAlgorithmException.java
@@ -0,0 +1,71 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/UnsupportedDigestAlgorithmException.java $
+ * $Revision: 527479 $
+ * $Date: 2007-04-11 05:55:12 -0700 (Wed, 11 Apr 2007) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+/**
+ * Authentication credentials required to respond to a authentication 
+ * challenge are invalid
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public class UnsupportedDigestAlgorithmException extends RuntimeException {
+
+    private static final long serialVersionUID = 319558534317118022L;
+
+    /**
+     * Creates a new UnsupportedAuthAlgoritmException with a <tt>null</tt> detail message. 
+     */
+    public UnsupportedDigestAlgorithmException() {
+        super();
+    }
+
+    /**
+     * Creates a new UnsupportedAuthAlgoritmException with the specified message.
+     * 
+     * @param message the exception detail message
+     */
+    public UnsupportedDigestAlgorithmException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new UnsupportedAuthAlgoritmException with the specified detail message and cause.
+     * 
+     * @param message the exception detail message
+     * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+     * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+     */
+    public UnsupportedDigestAlgorithmException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/org/apache/http/impl/auth/package.html b/src/org/apache/http/impl/auth/package.html
new file mode 100644
index 0000000..e301283
--- /dev/null
+++ b/src/org/apache/http/impl/auth/package.html
@@ -0,0 +1,4 @@
+<body>
+
+</body>
+
diff --git a/src/org/apache/http/impl/client/AbstractAuthenticationHandler.java b/src/org/apache/http/impl/client/AbstractAuthenticationHandler.java
new file mode 100644
index 0000000..57699d5
--- /dev/null
+++ b/src/org/apache/http/impl/client/AbstractAuthenticationHandler.java
@@ -0,0 +1,165 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/AbstractAuthenticationHandler.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.FormattedHeader;
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthSchemeRegistry;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.client.AuthenticationHandler;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public abstract class AbstractAuthenticationHandler implements AuthenticationHandler {
+
+    private final Log log = LogFactory.getLog(getClass());
+    
+    private static final List<String> DEFAULT_SCHEME_PRIORITY = Arrays.asList(new String[] {
+            "ntlm",
+            "digest",
+            "basic"
+    });
+    
+    public AbstractAuthenticationHandler() {
+        super();
+    }
+    
+    protected Map<String, Header> parseChallenges(
+            final Header[] headers) throws MalformedChallengeException {
+        
+        Map<String, Header> map = new HashMap<String, Header>(headers.length);
+        for (Header header : headers) {
+            CharArrayBuffer buffer;
+            int pos;
+            if (header instanceof FormattedHeader) {
+                buffer = ((FormattedHeader) header).getBuffer();
+                pos = ((FormattedHeader) header).getValuePos();
+            } else {
+                String s = header.getValue();
+                if (s == null) {
+                    throw new MalformedChallengeException("Header value is null");
+                }
+                buffer = new CharArrayBuffer(s.length());
+                buffer.append(s);
+                pos = 0;
+            }
+            while (pos < buffer.length() && HTTP.isWhitespace(buffer.charAt(pos))) {
+                pos++;
+            }
+            int beginIndex = pos;
+            while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) {
+                pos++;
+            }
+            int endIndex = pos;
+            String s = buffer.substring(beginIndex, endIndex);
+            map.put(s.toLowerCase(Locale.ENGLISH), header);
+        }
+        return map;
+    }
+    
+    protected List<String> getAuthPreferences() {
+        return DEFAULT_SCHEME_PRIORITY;
+    }
+    
+    public AuthScheme selectScheme(
+            final Map<String, Header> challenges, 
+            final HttpResponse response,
+            final HttpContext context) throws AuthenticationException {
+        
+        AuthSchemeRegistry registry = (AuthSchemeRegistry) context.getAttribute(
+                ClientContext.AUTHSCHEME_REGISTRY);
+        if (registry == null) {
+            throw new IllegalStateException("AuthScheme registry not set in HTTP context");
+        }
+        
+        List<?> authPrefs = (List<?>) context.getAttribute(
+                ClientContext.AUTH_SCHEME_PREF);
+        if (authPrefs == null) {
+            authPrefs = getAuthPreferences();
+        }
+        
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("Authentication schemes in the order of preference: " 
+                + authPrefs);
+        }
+
+        AuthScheme authScheme = null;
+        for (int i = 0; i < authPrefs.size(); i++) {
+            String id = (String) authPrefs.get(i);
+            Header challenge = challenges.get(id.toLowerCase(Locale.ENGLISH)); 
+
+            if (challenge != null) {
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug(id + " authentication scheme selected");
+                }
+                try {
+                    authScheme = registry.getAuthScheme(id, response.getParams());
+                    break;
+                } catch (IllegalStateException e) {
+                    if (this.log.isWarnEnabled()) {
+                        this.log.warn("Authentication scheme " + id + " not supported");
+                        // Try again
+                    }
+                }
+            } else {
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("Challenge for " + id + " authentication scheme not available");
+                    // Try again
+                }
+            }
+        }
+        if (authScheme == null) {
+            // If none selected, something is wrong
+            throw new AuthenticationException(
+                "Unable to respond to any of these challenges: "
+                    + challenges);
+        }
+        return authScheme;
+    }
+
+}
diff --git a/src/org/apache/http/impl/client/AbstractHttpClient.java b/src/org/apache/http/impl/client/AbstractHttpClient.java
new file mode 100644
index 0000000..3a1b838
--- /dev/null
+++ b/src/org/apache/http/impl/client/AbstractHttpClient.java
@@ -0,0 +1,697 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java $
+ * $Revision: 677250 $
+ * $Date: 2008-07-16 04:45:47 -0700 (Wed, 16 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.io.IOException;
+import java.net.URI;
+import java.lang.reflect.UndeclaredThrowableException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.HttpEntity;
+import org.apache.http.auth.AuthSchemeRegistry;
+import org.apache.http.client.AuthenticationHandler;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.RequestDirector;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.HttpRequestRetryHandler;
+import org.apache.http.client.RedirectHandler;
+import org.apache.http.client.UserTokenHandler;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.cookie.CookieSpecRegistry;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.BasicHttpProcessor;
+import org.apache.http.protocol.DefaultedHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.HttpRequestExecutor;
+
+/**
+ * Convenience base class for HTTP client implementations.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version   $Revision: 677250 $
+ *
+ * @since 4.0
+ */
+public abstract class AbstractHttpClient implements HttpClient {
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    /** The parameters. */
+    private HttpParams defaultParams;
+
+    /** The request executor. */
+    private HttpRequestExecutor requestExec;
+
+    /** The connection manager. */
+    private ClientConnectionManager connManager;
+
+    /** The connection re-use strategy. */
+    private ConnectionReuseStrategy reuseStrategy;
+    
+    /** The connection keep-alive strategy. */
+    private ConnectionKeepAliveStrategy keepAliveStrategy;
+
+    /** The cookie spec registry. */
+    private CookieSpecRegistry supportedCookieSpecs;
+
+    /** The authentication scheme registry. */
+    private AuthSchemeRegistry supportedAuthSchemes;
+    
+    /** The HTTP processor. */
+    private BasicHttpProcessor httpProcessor;
+
+    /** The request retry handler. */
+    private HttpRequestRetryHandler retryHandler;
+
+    /** The redirect handler. */
+    private RedirectHandler redirectHandler;
+
+    /** The target authentication handler. */
+    private AuthenticationHandler targetAuthHandler;
+
+    /** The proxy authentication handler. */
+    private AuthenticationHandler proxyAuthHandler;
+
+    /** The cookie store. */
+    private CookieStore cookieStore;
+
+    /** The credentials provider. */
+    private CredentialsProvider credsProvider;
+    
+    /** The route planner. */
+    private HttpRoutePlanner routePlanner;
+
+    /** The user token handler. */
+    private UserTokenHandler userTokenHandler;
+
+
+    /**
+     * Creates a new HTTP client.
+     *
+     * @param conman    the connection manager
+     * @param params    the parameters
+     */
+    protected AbstractHttpClient(
+            final ClientConnectionManager conman,
+            final HttpParams params) {
+        defaultParams        = params;
+        connManager          = conman;
+    } // constructor
+
+    protected abstract HttpParams createHttpParams();
+
+    
+    protected abstract HttpContext createHttpContext();
+
+    
+    protected abstract HttpRequestExecutor createRequestExecutor();
+
+
+    protected abstract ClientConnectionManager createClientConnectionManager();
+
+
+    protected abstract AuthSchemeRegistry createAuthSchemeRegistry();
+
+    
+    protected abstract CookieSpecRegistry createCookieSpecRegistry();
+
+    
+    protected abstract ConnectionReuseStrategy createConnectionReuseStrategy();
+
+    
+    protected abstract ConnectionKeepAliveStrategy createConnectionKeepAliveStrategy();
+
+    
+    protected abstract BasicHttpProcessor createHttpProcessor();
+
+    
+    protected abstract HttpRequestRetryHandler createHttpRequestRetryHandler();
+
+    
+    protected abstract RedirectHandler createRedirectHandler();
+
+    
+    protected abstract AuthenticationHandler createTargetAuthenticationHandler();
+
+    
+    protected abstract AuthenticationHandler createProxyAuthenticationHandler();
+
+    
+    protected abstract CookieStore createCookieStore();
+    
+    
+    protected abstract CredentialsProvider createCredentialsProvider();
+    
+    
+    protected abstract HttpRoutePlanner createHttpRoutePlanner();
+
+    
+    protected abstract UserTokenHandler createUserTokenHandler();
+
+    
+    // non-javadoc, see interface HttpClient
+    public synchronized final HttpParams getParams() {
+        if (defaultParams == null) {
+            defaultParams = createHttpParams();
+        }
+        return defaultParams;
+    }
+
+
+    /**
+     * Replaces the parameters.
+     * The implementation here does not update parameters of dependent objects.
+     *
+     * @param params    the new default parameters
+     */
+    public synchronized void setParams(HttpParams params) {
+        defaultParams = params;
+    }
+
+
+    public synchronized final ClientConnectionManager getConnectionManager() {
+        if (connManager == null) {
+            connManager = createClientConnectionManager();
+        }
+        return connManager;
+    }
+
+
+    public synchronized final HttpRequestExecutor getRequestExecutor() {
+        if (requestExec == null) {
+            requestExec = createRequestExecutor();
+        }
+        return requestExec;
+    }
+
+
+    public synchronized final AuthSchemeRegistry getAuthSchemes() {
+        if (supportedAuthSchemes == null) {
+            supportedAuthSchemes = createAuthSchemeRegistry();
+        }
+        return supportedAuthSchemes;
+    }
+
+
+    public synchronized void setAuthSchemes(final AuthSchemeRegistry authSchemeRegistry) {
+        supportedAuthSchemes = authSchemeRegistry;
+    }
+
+
+    public synchronized final CookieSpecRegistry getCookieSpecs() {
+        if (supportedCookieSpecs == null) {
+            supportedCookieSpecs = createCookieSpecRegistry();
+        }
+        return supportedCookieSpecs;
+    }
+
+
+    public synchronized void setCookieSpecs(final CookieSpecRegistry cookieSpecRegistry) {
+        supportedCookieSpecs = cookieSpecRegistry;
+    }
+
+    
+    public synchronized final ConnectionReuseStrategy getConnectionReuseStrategy() {
+        if (reuseStrategy == null) {
+            reuseStrategy = createConnectionReuseStrategy();
+        }
+        return reuseStrategy;
+    }
+
+
+    public synchronized void setReuseStrategy(final ConnectionReuseStrategy reuseStrategy) {
+        this.reuseStrategy = reuseStrategy;
+    }
+
+    
+    public synchronized final ConnectionKeepAliveStrategy getConnectionKeepAliveStrategy() {
+        if (keepAliveStrategy == null) {
+            keepAliveStrategy = createConnectionKeepAliveStrategy();
+        }
+        return keepAliveStrategy;
+    }
+
+    
+    public synchronized void setKeepAliveStrategy(final ConnectionKeepAliveStrategy keepAliveStrategy) {
+        this.keepAliveStrategy = keepAliveStrategy;
+    }
+
+
+    public synchronized final HttpRequestRetryHandler getHttpRequestRetryHandler() {
+        if (retryHandler == null) {
+            retryHandler = createHttpRequestRetryHandler();
+        }
+        return retryHandler;
+    }
+
+
+    public synchronized void setHttpRequestRetryHandler(final HttpRequestRetryHandler retryHandler) {
+        this.retryHandler = retryHandler;
+    }
+
+
+    public synchronized final RedirectHandler getRedirectHandler() {
+        if (redirectHandler == null) {
+            redirectHandler = createRedirectHandler();
+        }
+        return redirectHandler;
+    }
+
+
+    public synchronized void setRedirectHandler(final RedirectHandler redirectHandler) {
+        this.redirectHandler = redirectHandler;
+    }
+
+
+    public synchronized final AuthenticationHandler getTargetAuthenticationHandler() {
+        if (targetAuthHandler == null) {
+            targetAuthHandler = createTargetAuthenticationHandler();
+        }
+        return targetAuthHandler;
+    }
+
+
+    public synchronized void setTargetAuthenticationHandler(
+            final AuthenticationHandler targetAuthHandler) {
+        this.targetAuthHandler = targetAuthHandler;
+    }
+
+
+    public synchronized final AuthenticationHandler getProxyAuthenticationHandler() {
+        if (proxyAuthHandler == null) {
+            proxyAuthHandler = createProxyAuthenticationHandler();
+        }
+        return proxyAuthHandler;
+    }
+
+
+    public synchronized void setProxyAuthenticationHandler(
+            final AuthenticationHandler proxyAuthHandler) {
+        this.proxyAuthHandler = proxyAuthHandler;
+    }
+
+
+    public synchronized final CookieStore getCookieStore() {
+        if (cookieStore == null) {
+            cookieStore = createCookieStore();
+        }
+        return cookieStore;
+    }
+
+
+    public synchronized void setCookieStore(final CookieStore cookieStore) {
+        this.cookieStore = cookieStore;
+    }
+
+
+    public synchronized final CredentialsProvider getCredentialsProvider() {
+        if (credsProvider == null) {
+            credsProvider = createCredentialsProvider();
+        }
+        return credsProvider;
+    }
+
+
+    public synchronized void setCredentialsProvider(final CredentialsProvider credsProvider) {
+        this.credsProvider = credsProvider;
+    }
+
+
+    public synchronized final HttpRoutePlanner getRoutePlanner() {
+        if (this.routePlanner == null) {
+            this.routePlanner = createHttpRoutePlanner();
+        }
+        return this.routePlanner;
+    }
+
+
+    public synchronized void setRoutePlanner(final HttpRoutePlanner routePlanner) {
+        this.routePlanner = routePlanner;
+    }
+    
+    
+    public synchronized final UserTokenHandler getUserTokenHandler() {
+        if (this.userTokenHandler == null) {
+            this.userTokenHandler = createUserTokenHandler();
+        }
+        return this.userTokenHandler;
+    }
+
+
+    public synchronized void setUserTokenHandler(final UserTokenHandler userTokenHandler) {
+        this.userTokenHandler = userTokenHandler;
+    }
+    
+    
+    protected synchronized final BasicHttpProcessor getHttpProcessor() {
+        if (httpProcessor == null) {
+            httpProcessor = createHttpProcessor();
+        }
+        return httpProcessor;
+    }
+
+
+    public synchronized void addResponseInterceptor(final HttpResponseInterceptor itcp) {
+        getHttpProcessor().addInterceptor(itcp);
+    }
+
+
+    public synchronized void addResponseInterceptor(final HttpResponseInterceptor itcp, int index) {
+        getHttpProcessor().addInterceptor(itcp, index);
+    }
+
+
+    public synchronized HttpResponseInterceptor getResponseInterceptor(int index) {
+        return getHttpProcessor().getResponseInterceptor(index);
+    }
+
+
+    public synchronized int getResponseInterceptorCount() {
+        return getHttpProcessor().getResponseInterceptorCount();
+    }
+
+
+    public synchronized void clearResponseInterceptors() {
+        getHttpProcessor().clearResponseInterceptors();
+    }
+
+
+    public void removeResponseInterceptorByClass(Class<? extends HttpResponseInterceptor> clazz) {
+        getHttpProcessor().removeResponseInterceptorByClass(clazz);
+    }
+
+    
+    public synchronized void addRequestInterceptor(final HttpRequestInterceptor itcp) {
+        getHttpProcessor().addInterceptor(itcp);
+    }
+
+
+    public synchronized void addRequestInterceptor(final HttpRequestInterceptor itcp, int index) {
+        getHttpProcessor().addInterceptor(itcp, index);
+    }
+
+
+    public synchronized HttpRequestInterceptor getRequestInterceptor(int index) {
+        return getHttpProcessor().getRequestInterceptor(index);
+    }
+
+
+    public synchronized int getRequestInterceptorCount() {
+        return getHttpProcessor().getRequestInterceptorCount();
+    }
+
+
+    public synchronized void clearRequestInterceptors() {
+        getHttpProcessor().clearRequestInterceptors();
+    }
+
+
+    public void removeRequestInterceptorByClass(Class<? extends HttpRequestInterceptor> clazz) {
+        getHttpProcessor().removeRequestInterceptorByClass(clazz);
+    }
+
+
+    // non-javadoc, see interface HttpClient
+    public final HttpResponse execute(HttpUriRequest request)
+        throws IOException, ClientProtocolException {
+
+        return execute(request, (HttpContext) null);
+    }
+
+
+    /**
+     * Maps to {@link HttpClient#execute(HttpHost,HttpRequest,HttpContext)
+     *                           execute(target, request, context)}.
+     * The target is determined from the URI of the request.
+     *
+     * @param request   the request to execute
+     * @param context   the request-specific execution context,
+     *                  or <code>null</code> to use a default context
+     */
+    public final HttpResponse execute(HttpUriRequest request,
+                                      HttpContext context)
+        throws IOException, ClientProtocolException {
+
+        if (request == null) {
+            throw new IllegalArgumentException
+                ("Request must not be null.");
+        }
+
+        return execute(determineTarget(request), request, context);
+    }
+
+    private HttpHost determineTarget(HttpUriRequest request) {
+        // A null target may be acceptable if there is a default target.
+        // Otherwise, the null target is detected in the director.
+        HttpHost target = null;
+
+        URI requestURI = request.getURI();
+        if (requestURI.isAbsolute()) {
+            target = new HttpHost(
+                    requestURI.getHost(),
+                    requestURI.getPort(),
+                    requestURI.getScheme());
+        }
+        return target;
+    }
+
+    // non-javadoc, see interface HttpClient
+    public final HttpResponse execute(HttpHost target, HttpRequest request)
+        throws IOException, ClientProtocolException {
+
+        return execute(target, request, (HttpContext) null);
+    }
+
+
+    // non-javadoc, see interface HttpClient
+    public final HttpResponse execute(HttpHost target, HttpRequest request,
+                                      HttpContext context)
+        throws IOException, ClientProtocolException {
+
+        if (request == null) {
+            throw new IllegalArgumentException
+                ("Request must not be null.");
+        }
+        // a null target may be acceptable, this depends on the route planner
+        // a null context is acceptable, default context created below
+
+        HttpContext execContext = null;
+        RequestDirector director = null;
+        
+        // Initialize the request execution context making copies of 
+        // all shared objects that are potentially threading unsafe.
+        synchronized (this) {
+
+            HttpContext defaultContext = createHttpContext();
+            if (context == null) {
+                execContext = defaultContext;
+            } else {
+                execContext = new DefaultedHttpContext(context, defaultContext);
+            }
+            // Create a director for this request
+            director = createClientRequestDirector(
+                    getRequestExecutor(),
+                    getConnectionManager(),
+                    getConnectionReuseStrategy(),
+                    getConnectionKeepAliveStrategy(),
+                    getRoutePlanner(),
+                    getHttpProcessor().copy(),
+                    getHttpRequestRetryHandler(),
+                    getRedirectHandler(),
+                    getTargetAuthenticationHandler(),
+                    getProxyAuthenticationHandler(),
+                    getUserTokenHandler(),
+                    determineParams(request));
+        }
+
+        try {
+            return director.execute(target, request, execContext);
+        } catch(HttpException httpException) {
+            throw new ClientProtocolException(httpException);
+        }
+    } // execute
+
+    
+    protected RequestDirector createClientRequestDirector(
+            final HttpRequestExecutor requestExec,
+            final ClientConnectionManager conman,
+            final ConnectionReuseStrategy reustrat,
+            final ConnectionKeepAliveStrategy kastrat,
+            final HttpRoutePlanner rouplan,
+            final HttpProcessor httpProcessor,
+            final HttpRequestRetryHandler retryHandler,
+            final RedirectHandler redirectHandler,
+            final AuthenticationHandler targetAuthHandler,
+            final AuthenticationHandler proxyAuthHandler,
+            final UserTokenHandler stateHandler,
+            final HttpParams params) {
+        return new DefaultRequestDirector(
+                requestExec,
+                conman,
+                reustrat,
+                kastrat,
+                rouplan,
+                httpProcessor,
+                retryHandler,
+                redirectHandler,
+                targetAuthHandler,
+                proxyAuthHandler,
+                stateHandler,
+                params);
+    }
+
+    /**
+     * Obtains parameters for executing a request.
+     * The default implementation in this class creates a new
+     * {@link ClientParamsStack} from the request parameters
+     * and the client parameters.
+     * <br/>
+     * This method is called by the default implementation of
+     * {@link #execute(HttpHost,HttpRequest,HttpContext)}
+     * to obtain the parameters for the
+     * {@link DefaultRequestDirector}.
+     *
+     * @param req    the request that will be executed
+     *
+     * @return  the parameters to use
+     */
+    protected HttpParams determineParams(HttpRequest req) {
+        return new ClientParamsStack
+            (null, getParams(), req.getParams(), null);
+    }
+
+
+    // non-javadoc, see interface HttpClient
+    public <T> T execute(
+            final HttpUriRequest request, 
+            final ResponseHandler<? extends T> responseHandler) 
+                throws IOException, ClientProtocolException {
+        return execute(request, responseHandler, null);
+    }
+
+
+    // non-javadoc, see interface HttpClient
+    public <T> T execute(
+            final HttpUriRequest request,
+            final ResponseHandler<? extends T> responseHandler, 
+            final HttpContext context)
+                throws IOException, ClientProtocolException {
+        HttpHost target = determineTarget(request);
+        return execute(target, request, responseHandler, context);
+    }
+
+
+    // non-javadoc, see interface HttpClient
+    public <T> T execute(
+            final HttpHost target, 
+            final HttpRequest request,
+            final ResponseHandler<? extends T> responseHandler) 
+                throws IOException, ClientProtocolException {
+        return execute(target, request, responseHandler, null);
+    }
+
+
+    // non-javadoc, see interface HttpClient
+    public <T> T execute(
+            final HttpHost target, 
+            final HttpRequest request,
+            final ResponseHandler<? extends T> responseHandler, 
+            final HttpContext context) 
+                throws IOException, ClientProtocolException {
+        if (responseHandler == null) {
+            throw new IllegalArgumentException
+                ("Response handler must not be null.");
+        }
+
+        HttpResponse response = execute(target, request, context);
+
+        T result;
+        try {
+            result = responseHandler.handleResponse(response);
+        } catch (Throwable t) {
+            HttpEntity entity = response.getEntity();
+            if (entity != null) {
+                try {
+                    entity.consumeContent();
+                } catch (Throwable t2) {
+                    // Log this exception. The original exception is more
+                    // important and will be thrown to the caller.
+                    this.log.warn("Error consuming content after an exception.", t2);
+                }
+            }
+
+            if (t instanceof Error) {
+                throw (Error) t;
+            }
+
+            if (t instanceof RuntimeException) {
+                throw (RuntimeException) t;
+            }
+
+            if (t instanceof IOException) {
+                throw (IOException) t;
+            }
+            
+            throw new UndeclaredThrowableException(t);
+        }
+
+        // Handling the response was successful. Ensure that the content has
+        // been fully consumed.
+        HttpEntity entity = response.getEntity();
+        if (entity != null) {
+            // Let this exception go to the caller.
+            entity.consumeContent();
+        }
+
+        return result;
+    }
+
+
+} // class AbstractHttpClient
diff --git a/src/org/apache/http/impl/client/BasicCookieStore.java b/src/org/apache/http/impl/client/BasicCookieStore.java
new file mode 100644
index 0000000..9970961
--- /dev/null
+++ b/src/org/apache/http/impl/client/BasicCookieStore.java
@@ -0,0 +1,162 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/BasicCookieStore.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.http.client.CookieStore;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieIdentityComparator;
+
+/**
+ * Default implementation of {@link CookieStore}
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author Rodney Waldhoff
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author Sean C. Sullivan
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:adrian@intencha.com">Adrian Sutton</a>
+ * 
+ * @since 4.0
+ */
+public class BasicCookieStore implements CookieStore {
+
+    private final ArrayList<Cookie> cookies;
+
+    private final Comparator<Cookie> cookieComparator;
+    
+    // -------------------------------------------------------- Class Variables
+
+    /**
+     * Default constructor.
+     */
+    public BasicCookieStore() {
+        super();
+        this.cookies = new ArrayList<Cookie>();
+        this.cookieComparator = new CookieIdentityComparator();
+    }
+
+    /**
+     * Adds an {@link Cookie HTTP cookie}, replacing any existing equivalent cookies.
+     * If the given cookie has already expired it will not be added, but existing 
+     * values will still be removed.
+     * 
+     * @param cookie the {@link Cookie cookie} to be added
+     * 
+     * @see #addCookies(Cookie[])
+     * 
+     */
+    public synchronized void addCookie(Cookie cookie) {
+        if (cookie != null) {
+            // first remove any old cookie that is equivalent
+            for (Iterator<Cookie> it = cookies.iterator(); it.hasNext();) {
+                if (cookieComparator.compare(cookie, it.next()) == 0) {
+                    it.remove();
+                    break;
+                }
+            }
+            if (!cookie.isExpired(new Date())) {
+                cookies.add(cookie);
+            }
+        }
+    }
+
+    /**
+     * Adds an array of {@link Cookie HTTP cookies}. Cookies are added individually and 
+     * in the given array order. If any of the given cookies has already expired it will 
+     * not be added, but existing values will still be removed.
+     * 
+     * @param cookies the {@link Cookie cookies} to be added
+     * 
+     * @see #addCookie(Cookie)
+     * 
+     */
+    public synchronized void addCookies(Cookie[] cookies) {
+        if (cookies != null) {
+            for (Cookie cooky : cookies) {
+                this.addCookie(cooky);
+            }
+        }
+    }
+
+    /**
+     * Returns an immutable array of {@link Cookie cookies} that this HTTP
+     * state currently contains.
+     * 
+     * @return an array of {@link Cookie cookies}.
+     */
+    public synchronized List<Cookie> getCookies() {
+        return Collections.unmodifiableList(this.cookies);
+    }
+
+    /**
+     * Removes all of {@link Cookie cookies} in this HTTP state
+     * that have expired by the specified {@link java.util.Date date}. 
+     * 
+     * @return true if any cookies were purged.
+     * 
+     * @see Cookie#isExpired(Date)
+     */
+    public synchronized boolean clearExpired(final Date date) {
+        if (date == null) {
+            return false;
+        }
+        boolean removed = false;
+        for (Iterator<Cookie> it = cookies.iterator(); it.hasNext();) {
+            if (it.next().isExpired(date)) {
+                it.remove();
+                removed = true;
+            }
+        }
+        return removed;
+    }
+
+    @Override
+    public String toString() {
+        return cookies.toString();
+    }
+    
+    /**
+     * Clears all cookies.
+     */
+    public synchronized void clear() {
+        cookies.clear();
+    }
+    
+}
diff --git a/src/org/apache/http/impl/client/BasicCredentialsProvider.java b/src/org/apache/http/impl/client/BasicCredentialsProvider.java
new file mode 100644
index 0000000..02427ea
--- /dev/null
+++ b/src/org/apache/http/impl/client/BasicCredentialsProvider.java
@@ -0,0 +1,143 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/BasicCredentialsProvider.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.util.HashMap;
+
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.client.CredentialsProvider;
+
+/**
+ * Default implementation of {@link CredentialsProvider}
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author Rodney Waldhoff
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author Sean C. Sullivan
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:adrian@intencha.com">Adrian Sutton</a>
+ * 
+ * @since 4.0
+ */
+public class BasicCredentialsProvider implements CredentialsProvider {
+
+    private final HashMap<AuthScope, Credentials> credMap;
+
+    /**
+     * Default constructor.
+     */
+    public BasicCredentialsProvider() {
+        super();
+        this.credMap = new HashMap<AuthScope, Credentials>();
+    }
+
+    /** 
+     * Sets the {@link Credentials credentials} for the given authentication 
+     * scope. Any previous credentials for the given scope will be overwritten.
+     * 
+     * @param authscope the {@link AuthScope authentication scope}
+     * @param credentials the authentication {@link Credentials credentials} 
+     * for the given scope.
+     * 
+     * @see #getCredentials(AuthScope)
+     */
+    public synchronized void setCredentials(
+            final AuthScope authscope, 
+            final Credentials credentials) {
+        if (authscope == null) {
+            throw new IllegalArgumentException("Authentication scope may not be null");
+        }
+        credMap.put(authscope, credentials);
+    }
+
+    /**
+     * Find matching {@link Credentials credentials} for the given authentication scope.
+     *
+     * @param map the credentials hash map
+     * @param authscope the {@link AuthScope authentication scope}
+     * @return the credentials 
+     * 
+     */
+    private static Credentials matchCredentials(
+            final HashMap<AuthScope, Credentials> map, 
+            final AuthScope authscope) {
+        // see if we get a direct hit
+        Credentials creds = map.get(authscope);
+        if (creds == null) {
+            // Nope.
+            // Do a full scan
+            int bestMatchFactor  = -1;
+            AuthScope bestMatch  = null;
+            for (AuthScope current: map.keySet()) {
+                int factor = authscope.match(current);
+                if (factor > bestMatchFactor) {
+                    bestMatchFactor = factor;
+                    bestMatch = current;
+                }
+            }
+            if (bestMatch != null) {
+                creds = map.get(bestMatch);
+            }
+        }
+        return creds;
+    }
+    
+    /**
+     * Get the {@link Credentials credentials} for the given authentication scope.
+     *
+     * @param authscope the {@link AuthScope authentication scope}
+     * @return the credentials 
+     * 
+     * @see #setCredentials(AuthScope, Credentials)
+     */
+    public synchronized Credentials getCredentials(final AuthScope authscope) {
+        if (authscope == null) {
+            throw new IllegalArgumentException("Authentication scope may not be null");
+        }
+        return matchCredentials(this.credMap, authscope);
+    }
+
+    @Override
+    public String toString() {
+        return credMap.toString();
+    }
+    
+    /**
+     * Clears all credentials.
+     */
+    public synchronized void clear() {
+        this.credMap.clear();
+    }
+    
+}
diff --git a/src/org/apache/http/impl/client/BasicResponseHandler.java b/src/org/apache/http/impl/client/BasicResponseHandler.java
new file mode 100644
index 0000000..f17d30d
--- /dev/null
+++ b/src/org/apache/http/impl/client/BasicResponseHandler.java
@@ -0,0 +1,79 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/BasicResponseHandler.java $
+ * $Revision: 677240 $
+ * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.io.IOException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.HttpResponseException;
+import org.apache.http.util.EntityUtils;
+
+/**
+ * A {@link ResponseHandler} that returns the response body as a String
+ * for successful (2xx) responses. If the response code was >= 300, the response
+ * body is consumed and an {@link HttpResponseException} is thrown.
+ * 
+ * If this is used with
+ * {@link org.apache.http.client.HttpClient#execute(
+ *  org.apache.http.client.methods.HttpUriRequest, ResponseHandler),
+ * HttpClient may handle redirects (3xx responses) internally.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Revision: 677240 $
+ * 
+ * @since 4.0
+ */
+public class BasicResponseHandler implements ResponseHandler<String> {
+
+    /**
+     * Returns the response body as a String if the response was successful (a
+     * 2xx status code). If no response body exists, this returns null. If the
+     * response was unsuccessful (>= 300 status code), throws an
+     * {@link HttpResponseException}.
+     */
+    public String handleResponse(final HttpResponse response)
+            throws HttpResponseException, IOException {
+        StatusLine statusLine = response.getStatusLine();
+        if (statusLine.getStatusCode() >= 300) {
+            throw new HttpResponseException(statusLine.getStatusCode(),
+                    statusLine.getReasonPhrase());
+        }
+
+        HttpEntity entity = response.getEntity();
+        return entity == null ? null : EntityUtils.toString(entity);
+    }
+
+}
diff --git a/src/org/apache/http/impl/client/ClientParamsStack.java b/src/org/apache/http/impl/client/ClientParamsStack.java
new file mode 100644
index 0000000..a017e5d
--- /dev/null
+++ b/src/org/apache/http/impl/client/ClientParamsStack.java
@@ -0,0 +1,282 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/ClientParamsStack.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.AbstractHttpParams;
+
+
+/**
+ * Represents a stack of parameter collections.
+ * When retrieving a parameter, the stack is searched in a fixed order
+ * and the first match returned. Setting parameters via the stack is
+ * not supported. To minimize overhead, the stack has a fixed size and
+ * does not maintain an internal array.
+ * The supported stack entries, sorted by increasing priority, are:
+ * <ol>
+ * <li>Application parameters:
+ *     expected to be the same for all clients used by an application.
+ *     These provide "global", that is application-wide, defaults.
+ *     </li>
+ * <li>Client parameters:
+ *     specific to an instance of
+ *     {@link org.apache.http.client.HttpClient HttpClient}.
+ *     These provide client specific defaults.
+ *     </li>
+ * <li>Request parameters:
+ *     specific to a single request execution.
+ *     For overriding client and global defaults.
+ *     </li>
+ * <li>Override parameters:
+ *     specific to an instance of
+ *     {@link org.apache.http.client.HttpClient HttpClient}.
+ *     These can be used to set parameters that cannot be overridden
+ *     on a per-request basis.
+ *     </li>
+ * </ol>
+ * Each stack entry may be <code>null</code>. That is preferable over
+ * an empty params collection, since it avoids searching the empty collection
+ * when looking up parameters.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * @version $Revision: 673450 $
+ */
+public class ClientParamsStack extends AbstractHttpParams {
+
+    private final Log log = LogFactory.getLog(getClass());
+    
+    /** The application parameter collection, or <code>null</code>. */
+    protected final HttpParams applicationParams;
+
+    /** The client parameter collection, or <code>null</code>. */
+    protected final HttpParams clientParams;
+
+    /** The request parameter collection, or <code>null</code>. */
+    protected final HttpParams requestParams;
+
+    /** The override parameter collection, or <code>null</code>. */
+    protected final HttpParams overrideParams;
+
+
+    /**
+     * Creates a new parameter stack from elements.
+     * The arguments will be stored as-is, there is no copying to
+     * prevent modification.
+     *
+     * @param aparams   application parameters, or <code>null</code>
+     * @param cparams   client parameters, or <code>null</code>
+     * @param rparams   request parameters, or <code>null</code>
+     * @param oparams   override parameters, or <code>null</code>
+     */
+    public ClientParamsStack(HttpParams aparams, HttpParams cparams,
+                             HttpParams rparams, HttpParams oparams) {
+        applicationParams = aparams;
+        clientParams      = cparams;
+        requestParams     = rparams;
+        overrideParams    = oparams;
+    }
+
+
+    /**
+     * Creates a copy of a parameter stack.
+     * The new stack will have the exact same entries as the argument stack.
+     * There is no copying of parameters.
+     *
+     * @param stack     the stack to copy
+     */
+    public ClientParamsStack(ClientParamsStack stack) {
+        this(stack.getApplicationParams(),
+             stack.getClientParams(),
+             stack.getRequestParams(),
+             stack.getOverrideParams());
+    }
+
+
+    /**
+     * Creates a modified copy of a parameter stack.
+     * The new stack will contain the explicitly passed elements.
+     * For elements where the explicit argument is <code>null</code>,
+     * the corresponding element from the argument stack is used.
+     * There is no copying of parameters.
+     *
+     * @param stack     the stack to modify
+     * @param aparams   application parameters, or <code>null</code>
+     * @param cparams   client parameters, or <code>null</code>
+     * @param rparams   request parameters, or <code>null</code>
+     * @param oparams   override parameters, or <code>null</code>
+     */
+    public ClientParamsStack(ClientParamsStack stack,
+                             HttpParams aparams, HttpParams cparams,
+                             HttpParams rparams, HttpParams oparams) {
+        this((aparams != null) ? aparams : stack.getApplicationParams(),
+             (cparams != null) ? cparams : stack.getClientParams(),
+             (rparams != null) ? rparams : stack.getRequestParams(),
+             (oparams != null) ? oparams : stack.getOverrideParams());
+    }
+
+
+    /**
+     * Obtains the application parameters of this stack.
+     *
+     * @return  the application parameters, or <code>null</code>
+     */
+    public final HttpParams getApplicationParams() {
+        return applicationParams;
+    }
+
+    /**
+     * Obtains the client parameters of this stack.
+     *
+     * @return  the client parameters, or <code>null</code>
+     */
+    public final HttpParams getClientParams() {
+        return clientParams;
+    }
+
+    /**
+     * Obtains the request parameters of this stack.
+     *
+     * @return  the request parameters, or <code>null</code>
+     */
+    public final HttpParams getRequestParams() {
+        return requestParams;
+    }
+
+    /**
+     * Obtains the override parameters of this stack.
+     *
+     * @return  the override parameters, or <code>null</code>
+     */
+    public final HttpParams getOverrideParams() {
+        return overrideParams;
+    }
+
+
+    /**
+     * Obtains a parameter from this stack.
+     * See class comment for search order.
+     *
+     * @param name      the name of the parameter to obtain
+     *
+     * @return  the highest-priority value for that parameter, or
+     *          <code>null</code> if it is not set anywhere in this stack
+     */
+    public Object getParameter(String name) {
+        if (name == null) {
+            throw new IllegalArgumentException
+                ("Parameter name must not be null.");
+        }
+
+        Object result = null;
+
+        if (overrideParams != null) {
+            result = overrideParams.getParameter(name);
+        }
+        if ((result == null) && (requestParams != null)) {
+            result = requestParams.getParameter(name);
+        }
+        if ((result == null) && (clientParams != null)) {
+            result = clientParams.getParameter(name);
+        }
+        if ((result == null) && (applicationParams != null)) {
+            result = applicationParams.getParameter(name);
+        }
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("'" + name + "': " + result);
+        }
+
+        return result;
+    }
+
+    /**
+     * Does <i>not</i> set a parameter.
+     * Parameter stacks are read-only. It is possible, though discouraged,
+     * to access and modify specific stack entries.
+     * Derived classes may change this behavior.
+     *
+     * @param name      ignored
+     * @param value     ignored
+     *
+     * @return  nothing
+     *
+     * @throws UnsupportedOperationException    always
+     */
+    public HttpParams setParameter(String name, Object value)
+        throws UnsupportedOperationException {
+
+        throw new UnsupportedOperationException
+            ("Setting parameters in a stack is not supported.");
+    }
+
+
+    /**
+     * Does <i>not</i> remove a parameter.
+     * Parameter stacks are read-only. It is possible, though discouraged,
+     * to access and modify specific stack entries.
+     * Derived classes may change this behavior.
+     *
+     * @param name      ignored
+     *
+     * @return  nothing
+     *
+     * @throws UnsupportedOperationException    always
+     */
+    public boolean removeParameter(String name) {
+        throw new UnsupportedOperationException
+        ("Removing parameters in a stack is not supported.");
+    }
+
+
+    /**
+     * Does <i>not</i> copy parameters.
+     * Parameter stacks are lightweight objects, expected to be instantiated
+     * as needed and to be used only in a very specific context. On top of
+     * that, they are read-only. The typical copy operation to prevent
+     * accidental modification of parameters passed by the application to
+     * a framework object is therefore pointless and disabled.
+     * Create a new stack if you really need a copy.
+     * <br/>
+     * Derived classes may change this behavior.
+     *
+     * @return <code>this</code> parameter stack
+     */
+    public HttpParams copy() {
+        return this;
+    }
+
+
+}
diff --git a/src/org/apache/http/impl/client/DefaultConnectionKeepAliveStrategy.java b/src/org/apache/http/impl/client/DefaultConnectionKeepAliveStrategy.java
new file mode 100644
index 0000000..c7641d2
--- /dev/null
+++ b/src/org/apache/http/impl/client/DefaultConnectionKeepAliveStrategy.java
@@ -0,0 +1,76 @@
+/*
+ * $HeadURL: $
+ * $Revision: $
+ * $Date: $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client;
+
+import org.apache.http.HeaderElement;
+import org.apache.http.HeaderElementIterator;
+import org.apache.http.HttpResponse;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.message.BasicHeaderElementIterator;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * Default implementation of a strategy deciding duration
+ * that a connection can remain idle.
+ * 
+ * The default implementation looks solely at the 'Keep-Alive'
+ * header's timeout token.
+ *
+ * @author <a href="mailto:sberlin at gmail.com">Sam Berlin</a>
+ *
+ * @version $Revision: $
+ * 
+ * @since 4.0
+ */
+public class DefaultConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy {
+    
+    public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        HeaderElementIterator it = new BasicHeaderElementIterator(
+                response.headerIterator(HTTP.CONN_KEEP_ALIVE));
+        while (it.hasNext()) {
+            HeaderElement he = it.nextElement();
+            String param = he.getName(); 
+            String value = he.getValue();
+            if (value != null && param.equalsIgnoreCase("timeout")) {
+                try {
+                    return Long.parseLong(value) * 1000;
+                } catch(NumberFormatException ignore) {
+                }
+            }
+        }
+        return -1;
+    }
+
+}
diff --git a/src/org/apache/http/impl/client/DefaultHttpClient.java b/src/org/apache/http/impl/client/DefaultHttpClient.java
new file mode 100644
index 0000000..f0b694e
--- /dev/null
+++ b/src/org/apache/http/impl/client/DefaultHttpClient.java
@@ -0,0 +1,332 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultHttpClient.java $
+ * $Revision: 677250 $
+ * $Date: 2008-07-16 04:45:47 -0700 (Wed, 16 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpVersion;
+import org.apache.http.auth.AuthSchemeRegistry;
+import org.apache.http.client.AuthenticationHandler;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.HttpRequestRetryHandler;
+import org.apache.http.client.RedirectHandler;
+import org.apache.http.client.UserTokenHandler;
+import org.apache.http.client.params.AuthPolicy;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.client.params.CookiePolicy;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.client.protocol.RequestAddCookies;
+import org.apache.http.client.protocol.RequestDefaultHeaders;
+import org.apache.http.client.protocol.RequestProxyAuthentication;
+import org.apache.http.client.protocol.RequestTargetAuthentication;
+import org.apache.http.client.protocol.ResponseProcessCookies;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionManagerFactory;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.cookie.CookieSpecRegistry;
+import org.apache.http.impl.DefaultConnectionReuseStrategy;
+import org.apache.http.impl.auth.BasicSchemeFactory;
+import org.apache.http.impl.auth.DigestSchemeFactory;
+import org.apache.http.impl.conn.DefaultHttpRoutePlanner;
+import org.apache.http.impl.conn.SingleClientConnManager;
+import org.apache.http.impl.cookie.BestMatchSpecFactory;
+import org.apache.http.impl.cookie.BrowserCompatSpecFactory;
+import org.apache.http.impl.cookie.NetscapeDraftSpecFactory;
+import org.apache.http.impl.cookie.RFC2109SpecFactory;
+import org.apache.http.impl.cookie.RFC2965SpecFactory;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.BasicHttpProcessor;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestExecutor;
+import org.apache.http.protocol.RequestConnControl;
+import org.apache.http.protocol.RequestContent;
+import org.apache.http.protocol.RequestExpectContinue;
+import org.apache.http.protocol.RequestTargetHost;
+import org.apache.http.protocol.RequestUserAgent;
+import org.apache.http.util.VersionInfo;
+
+
+
+/**
+ * Default implementation of an HTTP client.
+ * <br/>
+ * This class replaces <code>HttpClient</code> in HttpClient 3.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version   $Revision: 677250 $
+ *
+ * @since 4.0
+ */
+public class DefaultHttpClient extends AbstractHttpClient {
+
+
+    /**
+     * Creates a new HTTP client from parameters and a connection manager.
+     *
+     * @param params    the parameters
+     * @param conman    the connection manager
+     */
+    public DefaultHttpClient(
+            final ClientConnectionManager conman,
+            final HttpParams params) {
+        super(conman, params);
+    }
+    
+       
+    public DefaultHttpClient(final HttpParams params) {
+        super(null, params);
+    }
+
+    
+    public DefaultHttpClient() {
+        super(null, null);
+    }
+
+    
+    @Override
+    protected HttpParams createHttpParams() {
+        HttpParams params = new BasicHttpParams();
+        HttpProtocolParams.setVersion(params, 
+                HttpVersion.HTTP_1_1);
+        HttpProtocolParams.setContentCharset(params, 
+                HTTP.DEFAULT_CONTENT_CHARSET);
+        HttpProtocolParams.setUseExpectContinue(params, 
+                true);
+
+        // determine the release version from packaged version info
+        final VersionInfo vi = VersionInfo.loadVersionInfo
+            ("org.apache.http.client", getClass().getClassLoader());
+        final String release = (vi != null) ?
+            vi.getRelease() : VersionInfo.UNAVAILABLE;
+        HttpProtocolParams.setUserAgent(params, 
+                "Apache-HttpClient/" + release + " (java 1.4)");
+
+        return params;
+    }
+
+    
+    @Override
+    protected HttpRequestExecutor createRequestExecutor() {
+        return new HttpRequestExecutor();
+    }
+
+
+    @Override
+    protected ClientConnectionManager createClientConnectionManager() {
+        SchemeRegistry registry = new SchemeRegistry();
+        registry.register(
+                new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
+        registry.register(
+                new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
+
+        ClientConnectionManager connManager = null;     
+        HttpParams params = getParams();
+        
+        ClientConnectionManagerFactory factory = null;
+
+        // Try first getting the factory directly as an object.
+        factory = (ClientConnectionManagerFactory) params
+                .getParameter(ClientPNames.CONNECTION_MANAGER_FACTORY);
+        if (factory == null) { // then try getting its class name.
+            String className = (String) params.getParameter(
+                    ClientPNames.CONNECTION_MANAGER_FACTORY_CLASS_NAME);
+            if (className != null) {
+                try {
+                    Class<?> clazz = Class.forName(className);
+                    factory = (ClientConnectionManagerFactory) clazz.newInstance();
+                } catch (ClassNotFoundException ex) {
+                    throw new IllegalStateException("Invalid class name: " + className);
+                } catch (IllegalAccessException ex) {
+                    throw new IllegalAccessError(ex.getMessage());
+                } catch (InstantiationException ex) {
+                    throw new InstantiationError(ex.getMessage());
+                }
+            }
+        }
+        
+        if(factory != null) {
+            connManager = factory.newInstance(params, registry);
+        } else {
+            connManager = new SingleClientConnManager(getParams(), registry); 
+        }
+        
+        return connManager;
+    }
+
+
+    @Override
+    protected HttpContext createHttpContext() {
+        HttpContext context = new BasicHttpContext();
+        context.setAttribute(
+                ClientContext.AUTHSCHEME_REGISTRY, 
+                getAuthSchemes());
+        context.setAttribute(
+                ClientContext.COOKIESPEC_REGISTRY, 
+                getCookieSpecs());
+        context.setAttribute(
+                ClientContext.COOKIE_STORE, 
+                getCookieStore());
+        context.setAttribute(
+                ClientContext.CREDS_PROVIDER, 
+                getCredentialsProvider());
+        return context;
+    }
+
+    
+    @Override
+    protected ConnectionReuseStrategy createConnectionReuseStrategy() {
+        return new DefaultConnectionReuseStrategy();
+    }
+    
+    @Override
+    protected ConnectionKeepAliveStrategy createConnectionKeepAliveStrategy() {
+        return new DefaultConnectionKeepAliveStrategy();
+    }
+    
+
+    @Override
+    protected AuthSchemeRegistry createAuthSchemeRegistry() {
+        AuthSchemeRegistry registry = new AuthSchemeRegistry(); 
+        registry.register(
+                AuthPolicy.BASIC, 
+                new BasicSchemeFactory());
+        registry.register(
+                AuthPolicy.DIGEST, 
+                new DigestSchemeFactory());
+        return registry;
+    }
+
+
+    @Override
+    protected CookieSpecRegistry createCookieSpecRegistry() {
+        CookieSpecRegistry registry = new CookieSpecRegistry();
+        registry.register(
+                CookiePolicy.BEST_MATCH, 
+                new BestMatchSpecFactory());
+        registry.register(
+                CookiePolicy.BROWSER_COMPATIBILITY, 
+                new BrowserCompatSpecFactory());
+        registry.register(
+                CookiePolicy.NETSCAPE, 
+                new NetscapeDraftSpecFactory());
+        registry.register(
+                CookiePolicy.RFC_2109, 
+                new RFC2109SpecFactory());
+        registry.register(
+                CookiePolicy.RFC_2965, 
+                new RFC2965SpecFactory());
+        return registry;
+    }
+
+
+    @Override
+    protected BasicHttpProcessor createHttpProcessor() {
+        BasicHttpProcessor httpproc = new BasicHttpProcessor();
+        httpproc.addInterceptor(new RequestDefaultHeaders());
+        // Required protocol interceptors
+        httpproc.addInterceptor(new RequestContent());
+        httpproc.addInterceptor(new RequestTargetHost());
+        // Recommended protocol interceptors
+        httpproc.addInterceptor(new RequestConnControl());
+        httpproc.addInterceptor(new RequestUserAgent());
+        httpproc.addInterceptor(new RequestExpectContinue());
+        // HTTP state management interceptors
+        httpproc.addInterceptor(new RequestAddCookies());
+        httpproc.addInterceptor(new ResponseProcessCookies());
+        // HTTP authentication interceptors
+        httpproc.addInterceptor(new RequestTargetAuthentication());
+        httpproc.addInterceptor(new RequestProxyAuthentication());
+        return httpproc;
+    }
+
+
+    @Override
+    protected HttpRequestRetryHandler createHttpRequestRetryHandler() {
+        return new DefaultHttpRequestRetryHandler();
+    }
+
+
+    @Override
+    protected RedirectHandler createRedirectHandler() {
+        return new DefaultRedirectHandler();
+    }
+
+
+    @Override
+    protected AuthenticationHandler createTargetAuthenticationHandler() {
+        return new DefaultTargetAuthenticationHandler();
+    }
+
+
+    @Override
+    protected AuthenticationHandler createProxyAuthenticationHandler() {
+        return new DefaultProxyAuthenticationHandler();
+    }
+
+
+    @Override
+    protected CookieStore createCookieStore() {
+        return new BasicCookieStore();
+    }
+
+
+    @Override
+    protected CredentialsProvider createCredentialsProvider() {
+        return new BasicCredentialsProvider();
+    }
+
+
+    @Override
+    protected HttpRoutePlanner createHttpRoutePlanner() {
+        return new DefaultHttpRoutePlanner
+            (getConnectionManager().getSchemeRegistry());
+    }
+
+
+    @Override
+    protected UserTokenHandler createUserTokenHandler() {
+        return new DefaultUserTokenHandler();
+    }
+    
+} // class DefaultHttpClient
diff --git a/src/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.java b/src/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.java
new file mode 100644
index 0000000..7f66990
--- /dev/null
+++ b/src/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.java
@@ -0,0 +1,134 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.java $
+ * $Revision: 652726 $
+ * $Date: 2008-05-01 18:16:51 -0700 (Thu, 01 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.UnknownHostException;
+
+import javax.net.ssl.SSLHandshakeException;
+
+import org.apache.http.NoHttpResponseException;
+import org.apache.http.client.HttpRequestRetryHandler;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.ExecutionContext;
+
+/**
+ * The default {@link HttpRequestRetryHandler} used by request executors.
+ * 
+ * @author Michael Becke
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public class DefaultHttpRequestRetryHandler implements HttpRequestRetryHandler {
+
+    /** the number of times a method will be retried */
+    private final int retryCount;
+    
+    /** Whether or not methods that have successfully sent their request will be retried */
+    private final boolean requestSentRetryEnabled;
+    
+    /**
+     * Default constructor
+     */
+    public DefaultHttpRequestRetryHandler(int retryCount, boolean requestSentRetryEnabled) {
+        super();
+        this.retryCount = retryCount;
+        this.requestSentRetryEnabled = requestSentRetryEnabled;
+    }
+    
+    /**
+     * Default constructor
+     */
+    public DefaultHttpRequestRetryHandler() {
+        this(3, false);
+    }
+    /** 
+     * Used <code>retryCount</code> and <code>requestSentRetryEnabled</code> to determine
+     * if the given method should be retried.
+     */
+    public boolean retryRequest(
+            final IOException exception, 
+            int executionCount,
+            final HttpContext context) {
+        if (exception == null) {
+            throw new IllegalArgumentException("Exception parameter may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+        if (executionCount > this.retryCount) {
+            // Do not retry if over max retry count
+            return false;
+        }
+        if (exception instanceof NoHttpResponseException) {
+            // Retry if the server dropped connection on us
+            return true;
+        }
+        if (exception instanceof InterruptedIOException) {
+            // Timeout
+            return false;
+        }
+        if (exception instanceof UnknownHostException) {
+            // Unknown host
+            return false;
+        }
+        if (exception instanceof SSLHandshakeException) {
+            // SSL handshake exception
+            return false;
+        }
+        Boolean b = (Boolean)
+            context.getAttribute(ExecutionContext.HTTP_REQ_SENT);
+        boolean sent = (b != null && b.booleanValue());
+        if (!sent || this.requestSentRetryEnabled) {
+            // Retry if the request has not been sent fully or
+            // if it's OK to retry methods that have been sent
+            return true;
+        }
+        // otherwise do not retry
+        return false;
+    }
+    
+    /**
+     * @return <code>true</code> if this handler will retry methods that have 
+     * successfully sent their request, <code>false</code> otherwise
+     */
+    public boolean isRequestSentRetryEnabled() {
+        return requestSentRetryEnabled;
+    }
+
+    /**
+     * @return the maximum number of times a method will be retried
+     */
+    public int getRetryCount() {
+        return retryCount;
+    }
+}
diff --git a/src/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java b/src/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java
new file mode 100644
index 0000000..c188da8
--- /dev/null
+++ b/src/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java $
+ * $Revision: 603615 $
+ * $Date: 2007-12-12 06:03:21 -0800 (Wed, 12 Dec 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.util.Map;
+
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public class DefaultProxyAuthenticationHandler extends AbstractAuthenticationHandler {
+
+    public DefaultProxyAuthenticationHandler() {
+        super();
+    }
+    
+    public boolean isAuthenticationRequested(
+            final HttpResponse response, 
+            final HttpContext context) {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        int status = response.getStatusLine().getStatusCode();
+        return status == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED;
+    }
+
+    public Map<String, Header> getChallenges(
+            final HttpResponse response, 
+            final HttpContext context) throws MalformedChallengeException {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        Header[] headers = response.getHeaders(AUTH.PROXY_AUTH);
+        return parseChallenges(headers);
+    }
+
+}
diff --git a/src/org/apache/http/impl/client/DefaultRedirectHandler.java b/src/org/apache/http/impl/client/DefaultRedirectHandler.java
new file mode 100644
index 0000000..0811b28
--- /dev/null
+++ b/src/org/apache/http/impl/client/DefaultRedirectHandler.java
@@ -0,0 +1,183 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultRedirectHandler.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.ProtocolException;
+import org.apache.http.client.CircularRedirectException;
+import org.apache.http.client.RedirectHandler;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.client.utils.URIUtils;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.ExecutionContext;
+
+
+/**
+ * Default implementation of {@link RedirectHandler}.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 673450 $
+ *
+ * @since 4.0
+ */
+public class DefaultRedirectHandler implements RedirectHandler {
+
+    private final Log log = LogFactory.getLog(getClass());
+    
+    private static final String REDIRECT_LOCATIONS = "http.protocol.redirect-locations";
+
+    public DefaultRedirectHandler() {
+        super();
+    }
+    
+    public boolean isRedirectRequested(
+            final HttpResponse response,
+            final HttpContext context) {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        int statusCode = response.getStatusLine().getStatusCode();
+        switch (statusCode) {
+        case HttpStatus.SC_MOVED_TEMPORARILY:
+        case HttpStatus.SC_MOVED_PERMANENTLY:
+        case HttpStatus.SC_SEE_OTHER:
+        case HttpStatus.SC_TEMPORARY_REDIRECT:
+            return true;
+        default:
+            return false;
+        } //end of switch
+    }
+ 
+    public URI getLocationURI(
+            final HttpResponse response, 
+            final HttpContext context) throws ProtocolException {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        //get the location header to find out where to redirect to
+        Header locationHeader = response.getFirstHeader("location");
+        if (locationHeader == null) {
+            // got a redirect response, but no location header
+            throw new ProtocolException(
+                    "Received redirect response " + response.getStatusLine()
+                    + " but no location header");
+        }
+        String location = locationHeader.getValue();
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("Redirect requested to location '" + location + "'");
+        }
+
+        URI uri;
+        try {
+            uri = new URI(location);            
+        } catch (URISyntaxException ex) {
+            throw new ProtocolException("Invalid redirect URI: " + location, ex);
+        }
+
+        HttpParams params = response.getParams();
+        // rfc2616 demands the location value be a complete URI
+        // Location       = "Location" ":" absoluteURI
+        if (!uri.isAbsolute()) {
+            if (params.isParameterTrue(ClientPNames.REJECT_RELATIVE_REDIRECT)) {
+                throw new ProtocolException("Relative redirect location '" 
+                        + uri + "' not allowed");
+            }
+            // Adjust location URI
+            HttpHost target = (HttpHost) context.getAttribute(
+                    ExecutionContext.HTTP_TARGET_HOST);
+            if (target == null) {
+                throw new IllegalStateException("Target host not available " +
+                        "in the HTTP context");
+            }
+            
+            HttpRequest request = (HttpRequest) context.getAttribute(
+                    ExecutionContext.HTTP_REQUEST);
+            
+            try {
+                URI requestURI = new URI(request.getRequestLine().getUri());
+                URI absoluteRequestURI = URIUtils.rewriteURI(requestURI, target, true);
+                uri = URIUtils.resolve(absoluteRequestURI, uri); 
+            } catch (URISyntaxException ex) {
+                throw new ProtocolException(ex.getMessage(), ex);
+            }
+        }
+        
+        if (params.isParameterFalse(ClientPNames.ALLOW_CIRCULAR_REDIRECTS)) {
+            
+            RedirectLocations redirectLocations = (RedirectLocations) context.getAttribute(
+                    REDIRECT_LOCATIONS);
+            
+            if (redirectLocations == null) {
+                redirectLocations = new RedirectLocations();
+                context.setAttribute(REDIRECT_LOCATIONS, redirectLocations);
+            }
+            
+            URI redirectURI;
+            if (uri.getFragment() != null) {
+                try {
+                    HttpHost target = new HttpHost(
+                            uri.getHost(), 
+                            uri.getPort(),
+                            uri.getScheme());
+                    redirectURI = URIUtils.rewriteURI(uri, target, true);
+                } catch (URISyntaxException ex) {
+                    throw new ProtocolException(ex.getMessage(), ex);
+                }
+            } else {
+                redirectURI = uri;
+            }
+            
+            if (redirectLocations.contains(redirectURI)) {
+                throw new CircularRedirectException("Circular redirect to '" +
+                        redirectURI + "'");
+            } else {
+                redirectLocations.add(redirectURI);
+            }
+        }
+        
+        return uri;
+    }
+
+}
diff --git a/src/org/apache/http/impl/client/DefaultRequestDirector.java b/src/org/apache/http/impl/client/DefaultRequestDirector.java
new file mode 100644
index 0000000..511f8a0
--- /dev/null
+++ b/src/org/apache/http/impl/client/DefaultRequestDirector.java
@@ -0,0 +1,1088 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultRequestDirector.java $
+ * $Revision: 676023 $
+ * $Date: 2008-07-11 09:40:56 -0700 (Fri, 11 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.ProtocolException;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.AuthState;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.client.AuthenticationHandler;
+import org.apache.http.client.RequestDirector;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.HttpRequestRetryHandler;
+import org.apache.http.client.NonRepeatableRequestException;
+import org.apache.http.client.RedirectException;
+import org.apache.http.client.RedirectHandler;
+import org.apache.http.client.UserTokenHandler;
+import org.apache.http.client.methods.AbortableHttpRequest;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.client.params.HttpClientParams;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.client.utils.URIUtils;
+import org.apache.http.conn.BasicManagedEntity;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionRequest;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.params.ConnManagerParams;
+import org.apache.http.conn.routing.BasicRouteDirector;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.HttpRouteDirector;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.entity.BufferedHttpEntity;
+import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.HttpRequestExecutor;
+
+/**
+ * Default implementation of {@link RequestDirector}.
+ * <br/>
+ * This class replaces the <code>HttpMethodDirector</code> in HttpClient 3.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 676023 $
+ *
+ * @since 4.0
+ */
+public class DefaultRequestDirector implements RequestDirector {
+
+    private final Log log = LogFactory.getLog(getClass());
+    
+    /** The connection manager. */
+    protected final ClientConnectionManager connManager;
+
+    /** The route planner. */
+    protected final HttpRoutePlanner routePlanner;
+
+    /** The connection re-use strategy. */
+    protected final ConnectionReuseStrategy reuseStrategy;
+    
+    /** The keep-alive duration strategy. */
+    protected final ConnectionKeepAliveStrategy keepAliveStrategy;
+
+    /** The request executor. */
+    protected final HttpRequestExecutor requestExec;
+
+    /** The HTTP protocol processor. */
+    protected final HttpProcessor httpProcessor;
+    
+    /** The request retry handler. */
+    protected final HttpRequestRetryHandler retryHandler;
+    
+    /** The redirect handler. */
+    protected final RedirectHandler redirectHandler;
+    
+    /** The target authentication handler. */
+    private final AuthenticationHandler targetAuthHandler;
+    
+    /** The proxy authentication handler. */
+    private final AuthenticationHandler proxyAuthHandler;
+    
+    /** The user token handler. */
+    private final UserTokenHandler userTokenHandler;
+    
+    /** The HTTP parameters. */
+    protected final HttpParams params;
+    
+    /** The currently allocated connection. */
+    protected ManagedClientConnection managedConn;
+
+    private int redirectCount;
+
+    private int maxRedirects;
+    
+    private final AuthState targetAuthState;
+    
+    private final AuthState proxyAuthState;
+    
+    public DefaultRequestDirector(
+            final HttpRequestExecutor requestExec,
+            final ClientConnectionManager conman,
+            final ConnectionReuseStrategy reustrat,
+            final ConnectionKeepAliveStrategy kastrat,
+            final HttpRoutePlanner rouplan,
+            final HttpProcessor httpProcessor,
+            final HttpRequestRetryHandler retryHandler,
+            final RedirectHandler redirectHandler,
+            final AuthenticationHandler targetAuthHandler,
+            final AuthenticationHandler proxyAuthHandler,
+            final UserTokenHandler userTokenHandler,
+            final HttpParams params) {
+
+        if (requestExec == null) {
+            throw new IllegalArgumentException
+                ("Request executor may not be null.");
+        }
+        if (conman == null) {
+            throw new IllegalArgumentException
+                ("Client connection manager may not be null.");
+        }
+        if (reustrat == null) {
+            throw new IllegalArgumentException
+                ("Connection reuse strategy may not be null.");
+        }
+        if (kastrat == null) {
+            throw new IllegalArgumentException
+                ("Connection keep alive strategy may not be null.");
+        }
+        if (rouplan == null) {
+            throw new IllegalArgumentException
+                ("Route planner may not be null.");
+        }
+        if (httpProcessor == null) {
+            throw new IllegalArgumentException
+                ("HTTP protocol processor may not be null.");
+        }
+        if (retryHandler == null) {
+            throw new IllegalArgumentException
+                ("HTTP request retry handler may not be null.");
+        }
+        if (redirectHandler == null) {
+            throw new IllegalArgumentException
+                ("Redirect handler may not be null.");
+        }
+        if (targetAuthHandler == null) {
+            throw new IllegalArgumentException
+                ("Target authentication handler may not be null.");
+        }
+        if (proxyAuthHandler == null) {
+            throw new IllegalArgumentException
+                ("Proxy authentication handler may not be null.");
+        }
+        if (userTokenHandler == null) {
+            throw new IllegalArgumentException
+                ("User token handler may not be null.");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException
+                ("HTTP parameters may not be null");
+        }
+        this.requestExec       = requestExec;
+        this.connManager       = conman;
+        this.reuseStrategy     = reustrat;
+        this.keepAliveStrategy = kastrat;
+        this.routePlanner      = rouplan;
+        this.httpProcessor     = httpProcessor;
+        this.retryHandler      = retryHandler;
+        this.redirectHandler   = redirectHandler;
+        this.targetAuthHandler = targetAuthHandler;
+        this.proxyAuthHandler  = proxyAuthHandler;
+        this.userTokenHandler  = userTokenHandler; 
+        this.params            = params;
+
+        this.managedConn       = null;
+        
+        this.redirectCount = 0;
+        this.maxRedirects = this.params.getIntParameter(ClientPNames.MAX_REDIRECTS, 100);
+        this.targetAuthState = new AuthState();
+        this.proxyAuthState = new AuthState();
+    } // constructor
+
+
+    private RequestWrapper wrapRequest(
+            final HttpRequest request) throws ProtocolException {
+        if (request instanceof HttpEntityEnclosingRequest) {
+            return new EntityEnclosingRequestWrapper(
+                    (HttpEntityEnclosingRequest) request);
+        } else {
+            return new RequestWrapper(
+                    request);
+        }
+    }
+    
+    
+    protected void rewriteRequestURI(
+            final RequestWrapper request,
+            final HttpRoute route) throws ProtocolException {
+        try {
+            
+            URI uri = request.getURI();
+            if (route.getProxyHost() != null && !route.isTunnelled()) {
+                // Make sure the request URI is absolute
+                if (!uri.isAbsolute()) {
+                    HttpHost target = route.getTargetHost();
+                    uri = URIUtils.rewriteURI(uri, target);
+                    request.setURI(uri);
+                }
+            } else {
+                // Make sure the request URI is relative
+                if (uri.isAbsolute()) {
+                    uri = URIUtils.rewriteURI(uri, null);
+                    request.setURI(uri);
+                }
+            }
+            
+        } catch (URISyntaxException ex) {
+            throw new ProtocolException("Invalid URI: " + 
+                    request.getRequestLine().getUri(), ex);
+        }
+    }
+    
+    
+    // non-javadoc, see interface ClientRequestDirector
+    public HttpResponse execute(HttpHost target, HttpRequest request,
+                                HttpContext context)
+        throws HttpException, IOException {
+
+        HttpRequest orig = request;
+        RequestWrapper origWrapper = wrapRequest(orig);
+        origWrapper.setParams(params);
+        HttpRoute origRoute = determineRoute(target, origWrapper, context);
+
+        RoutedRequest roureq = new RoutedRequest(origWrapper, origRoute); 
+
+        long timeout = ConnManagerParams.getTimeout(params);
+        
+        int execCount = 0;
+        
+        boolean reuse = false;
+        HttpResponse response = null;
+        boolean done = false;
+        try {
+            while (!done) {
+                // In this loop, the RoutedRequest may be replaced by a
+                // followup request and route. The request and route passed
+                // in the method arguments will be replaced. The original
+                // request is still available in 'orig'.
+
+                RequestWrapper wrapper = roureq.getRequest();
+                HttpRoute route = roureq.getRoute();
+                
+                // See if we have a user token bound to the execution context
+                Object userToken = context.getAttribute(ClientContext.USER_TOKEN);
+                
+                // Allocate connection if needed
+                if (managedConn == null) {
+                    ClientConnectionRequest connRequest = connManager.requestConnection(
+                            route, userToken);
+                    if (orig instanceof AbortableHttpRequest) {
+                        ((AbortableHttpRequest) orig).setConnectionRequest(connRequest);
+                    }
+                    
+                    try {
+                        managedConn = connRequest.getConnection(timeout, TimeUnit.MILLISECONDS);
+                    } catch(InterruptedException interrupted) {
+                        InterruptedIOException iox = new InterruptedIOException();
+                        iox.initCause(interrupted);
+                        throw iox;
+                    }
+
+                    if (HttpConnectionParams.isStaleCheckingEnabled(params)) {
+                        // validate connection
+                        this.log.debug("Stale connection check");
+                        if (managedConn.isStale()) {
+                            this.log.debug("Stale connection detected");
+                            managedConn.close();
+                        }
+                    }
+                }
+
+                if (orig instanceof AbortableHttpRequest) {
+                    ((AbortableHttpRequest) orig).setReleaseTrigger(managedConn);
+                }
+
+                // Reopen connection if needed
+                if (!managedConn.isOpen()) {
+                    managedConn.open(route, context, params);
+                } 
+                
+                try {
+                    establishRoute(route, context);
+                } catch (TunnelRefusedException ex) {
+                    if (this.log.isDebugEnabled()) {
+                        this.log.debug(ex.getMessage());
+                    }
+                    response = ex.getResponse();
+                    break;
+                }
+
+                // Reset headers on the request wrapper
+                wrapper.resetHeaders();
+                
+                // Re-write request URI if needed
+                rewriteRequestURI(wrapper, route);
+
+                // Use virtual host if set
+                target = (HttpHost) wrapper.getParams().getParameter(
+                        ClientPNames.VIRTUAL_HOST);
+
+                if (target == null) {
+                    target = route.getTargetHost();
+                }
+
+                HttpHost proxy = route.getProxyHost();
+
+                // Populate the execution context
+                context.setAttribute(ExecutionContext.HTTP_TARGET_HOST,
+                        target);
+                context.setAttribute(ExecutionContext.HTTP_PROXY_HOST,
+                        proxy);
+                context.setAttribute(ExecutionContext.HTTP_CONNECTION,
+                        managedConn);
+                context.setAttribute(ClientContext.TARGET_AUTH_STATE,
+                        targetAuthState);
+                context.setAttribute(ClientContext.PROXY_AUTH_STATE,
+                        proxyAuthState);
+                
+                // Run request protocol interceptors
+                requestExec.preProcess(wrapper, httpProcessor, context);
+                
+                context.setAttribute(ExecutionContext.HTTP_REQUEST,
+                        wrapper);
+
+                boolean retrying = true;
+                while (retrying) {
+                    // Increment total exec count (with redirects)
+                    execCount++;
+                    // Increment exec count for this particular request
+                    wrapper.incrementExecCount();
+                    if (wrapper.getExecCount() > 1 && !wrapper.isRepeatable()) {
+                        throw new NonRepeatableRequestException("Cannot retry request " +
+                                "with a non-repeatable request entity");
+                    }
+                    
+                    try {
+                        if (this.log.isDebugEnabled()) {
+                            this.log.debug("Attempt " + execCount + " to execute request");
+                        }
+                        response = requestExec.execute(wrapper, managedConn, context);
+                        retrying = false;
+                        
+                    } catch (IOException ex) {
+                        this.log.debug("Closing the connection.");
+                        managedConn.close();
+                        if (retryHandler.retryRequest(ex, execCount, context)) {
+                            if (this.log.isInfoEnabled()) {
+                                this.log.info("I/O exception ("+ ex.getClass().getName() + 
+                                        ") caught when processing request: "
+                                        + ex.getMessage());
+                            }
+                            if (this.log.isDebugEnabled()) {
+                                this.log.debug(ex.getMessage(), ex);
+                            }
+                            this.log.info("Retrying request");
+                        } else {
+                            throw ex;
+                        }
+
+                        // If we have a direct route to the target host
+                        // just re-open connection and re-try the request
+                        if (route.getHopCount() == 1) {
+                            this.log.debug("Reopening the direct connection.");
+                            managedConn.open(route, context, params);
+                        } else {
+                            // otherwise give up
+                            retrying = false;
+                        }
+                        
+                    }
+
+                }
+
+                // Run response protocol interceptors
+                response.setParams(params);
+                requestExec.postProcess(response, httpProcessor, context);
+                
+
+                // The connection is in or can be brought to a re-usable state.
+                reuse = reuseStrategy.keepAlive(response, context);
+                if(reuse) {
+                    // Set the idle duration of this connection
+                    long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
+                    managedConn.setIdleDuration(duration, TimeUnit.MILLISECONDS);
+                }
+                
+                RoutedRequest followup = handleResponse(roureq, response, context);
+                if (followup == null) {
+                    done = true;
+                } else {
+                    if (reuse) {
+                        this.log.debug("Connection kept alive");
+                        // Make sure the response body is fully consumed, if present
+                        HttpEntity entity = response.getEntity();
+                        if (entity != null) {
+                            entity.consumeContent();
+                        }
+                        // entity consumed above is not an auto-release entity,
+                        // need to mark the connection re-usable explicitly
+                        managedConn.markReusable();
+                    } else {
+                        managedConn.close();
+                    }
+                    // check if we can use the same connection for the followup
+                    if (!followup.getRoute().equals(roureq.getRoute())) {
+                        releaseConnection();
+                    }
+                    roureq = followup;
+                }
+                
+                userToken = this.userTokenHandler.getUserToken(context);
+                context.setAttribute(ClientContext.USER_TOKEN, userToken);
+                if (managedConn != null) {
+                    managedConn.setState(userToken);
+                }
+            } // while not done
+
+
+            // check for entity, release connection if possible
+            if ((response == null) || (response.getEntity() == null) ||
+                !response.getEntity().isStreaming()) {
+                // connection not needed and (assumed to be) in re-usable state
+                if (reuse)
+                    managedConn.markReusable();
+                releaseConnection();
+            } else {
+                // install an auto-release entity
+                HttpEntity entity = response.getEntity();
+                entity = new BasicManagedEntity(entity, managedConn, reuse);
+                response.setEntity(entity);
+            }
+
+            return response;
+            
+        } catch (HttpException ex) {
+            abortConnection();
+            throw ex;
+        } catch (IOException ex) {
+            abortConnection();
+            throw ex;
+        } catch (RuntimeException ex) {
+            abortConnection();
+            throw ex;
+        }
+    } // execute
+
+    /**
+     * Returns the connection back to the connection manager
+     * and prepares for retrieving a new connection during
+     * the next request.
+     */
+    protected void releaseConnection() {
+        // Release the connection through the ManagedConnection instead of the
+        // ConnectionManager directly.  This lets the connection control how
+        // it is released.
+        try {
+            managedConn.releaseConnection();
+        } catch(IOException ignored) {
+            this.log.debug("IOException releasing connection", ignored);
+        }
+        managedConn = null;
+    }
+
+    /**
+     * Determines the route for a request.
+     * Called by {@link #execute}
+     * to determine the route for either the original or a followup request.
+     *
+     * @param target    the target host for the request.
+     *                  Implementations may accept <code>null</code>
+     *                  if they can still determine a route, for example
+     *                  to a default target or by inspecting the request.
+     * @param request   the request to execute
+     * @param context   the context to use for the execution,
+     *                  never <code>null</code>
+     *
+     * @return  the route the request should take
+     *
+     * @throws HttpException    in case of a problem
+     */
+    protected HttpRoute determineRoute(HttpHost    target,
+                                           HttpRequest request,
+                                           HttpContext context)
+        throws HttpException {
+
+        if (target == null) {
+            target = (HttpHost) request.getParams().getParameter(
+                ClientPNames.DEFAULT_HOST);
+        }
+        if (target == null) {
+            throw new IllegalStateException
+                ("Target host must not be null, or set in parameters.");
+        }
+
+        return this.routePlanner.determineRoute(target, request, context);
+    }
+
+
+    /**
+     * Establishes the target route.
+     *
+     * @param route     the route to establish
+     * @param context   the context for the request execution
+     *
+     * @throws HttpException    in case of a problem
+     * @throws IOException      in case of an IO problem
+     */
+    protected void establishRoute(HttpRoute route, HttpContext context)
+        throws HttpException, IOException {
+
+        //@@@ how to handle CONNECT requests for tunnelling?
+        //@@@ refuse to send external CONNECT via director? special handling?
+
+        //@@@ should the request parameters already be used below?
+        //@@@ probably yes, but they're not linked yet
+        //@@@ will linking above cause problems with linking in reqExec?
+        //@@@ probably not, because the parent is replaced
+        //@@@ just make sure we don't link parameters to themselves
+
+        HttpRouteDirector rowdy = new BasicRouteDirector();
+        int step;
+        do {
+            HttpRoute fact = managedConn.getRoute();
+            step = rowdy.nextStep(route, fact);
+
+            switch (step) {
+
+            case HttpRouteDirector.CONNECT_TARGET:
+            case HttpRouteDirector.CONNECT_PROXY:
+                managedConn.open(route, context, this.params);
+                break;
+
+            case HttpRouteDirector.TUNNEL_TARGET: {
+                boolean secure = createTunnelToTarget(route, context);
+                this.log.debug("Tunnel to target created.");
+                managedConn.tunnelTarget(secure, this.params);
+            }   break;
+
+            case HttpRouteDirector.TUNNEL_PROXY: {
+                // The most simple example for this case is a proxy chain
+                // of two proxies, where P1 must be tunnelled to P2.
+                // route: Source -> P1 -> P2 -> Target (3 hops)
+                // fact:  Source -> P1 -> Target       (2 hops)
+                final int hop = fact.getHopCount()-1; // the hop to establish
+                boolean secure = createTunnelToProxy(route, hop, context);
+                this.log.debug("Tunnel to proxy created.");
+                managedConn.tunnelProxy(route.getHopTarget(hop),
+                                        secure, this.params);
+            }   break;
+
+
+            case HttpRouteDirector.LAYER_PROTOCOL:
+                managedConn.layerProtocol(context, this.params);
+                break;
+
+            case HttpRouteDirector.UNREACHABLE:
+                throw new IllegalStateException
+                    ("Unable to establish route." +
+                     "\nplanned = " + route +
+                     "\ncurrent = " + fact);
+
+            case HttpRouteDirector.COMPLETE:
+                // do nothing
+                break;
+
+            default:
+                throw new IllegalStateException
+                    ("Unknown step indicator "+step+" from RouteDirector.");
+            } // switch
+
+        } while (step > HttpRouteDirector.COMPLETE);
+
+    } // establishConnection
+
+
+    /**
+     * Creates a tunnel to the target server.
+     * The connection must be established to the (last) proxy.
+     * A CONNECT request for tunnelling through the proxy will
+     * be created and sent, the response received and checked.
+     * This method does <i>not</i> update the connection with
+     * information about the tunnel, that is left to the caller.
+     *
+     * @param route     the route to establish
+     * @param context   the context for request execution
+     *
+     * @return  <code>true</code> if the tunnelled route is secure,
+     *          <code>false</code> otherwise.
+     *          The implementation here always returns <code>false</code>,
+     *          but derived classes may override.
+     *
+     * @throws HttpException    in case of a problem
+     * @throws IOException      in case of an IO problem
+     */
+    protected boolean createTunnelToTarget(HttpRoute route,
+                                           HttpContext context)
+        throws HttpException, IOException {
+
+        HttpHost proxy = route.getProxyHost();
+        HttpHost target = route.getTargetHost();
+        HttpResponse response = null;
+        
+        boolean done = false;
+        while (!done) {
+
+            done = true;
+            
+            if (!this.managedConn.isOpen()) {
+                this.managedConn.open(route, context, this.params);
+            }
+            
+            HttpRequest connect = createConnectRequest(route, context);
+            
+            String agent = HttpProtocolParams.getUserAgent(params);
+            if (agent != null) {
+                connect.addHeader(HTTP.USER_AGENT, agent);
+            }
+            connect.addHeader(HTTP.TARGET_HOST, target.toHostString());
+            
+            AuthScheme authScheme = this.proxyAuthState.getAuthScheme();
+            AuthScope authScope = this.proxyAuthState.getAuthScope();
+            Credentials creds = this.proxyAuthState.getCredentials();
+            if (creds != null) {
+                if (authScope != null || !authScheme.isConnectionBased()) {
+                    try {
+                        connect.addHeader(authScheme.authenticate(creds, connect));
+                    } catch (AuthenticationException ex) {
+                        if (this.log.isErrorEnabled()) {
+                            this.log.error("Proxy authentication error: " + ex.getMessage());
+                        }
+                    }
+                }
+            }
+            
+            response = requestExec.execute(connect, this.managedConn, context);
+            
+            int status = response.getStatusLine().getStatusCode();
+            if (status < 200) {
+                throw new HttpException("Unexpected response to CONNECT request: " +
+                        response.getStatusLine());
+            }
+            
+            CredentialsProvider credsProvider = (CredentialsProvider)
+                context.getAttribute(ClientContext.CREDS_PROVIDER);
+            
+            if (credsProvider != null && HttpClientParams.isAuthenticating(params)) {
+                if (this.proxyAuthHandler.isAuthenticationRequested(response, context)) {
+
+                    this.log.debug("Proxy requested authentication");
+                    Map<String, Header> challenges = this.proxyAuthHandler.getChallenges(
+                            response, context);
+                    try {
+                        processChallenges(
+                                challenges, this.proxyAuthState, this.proxyAuthHandler, 
+                                response, context);
+                    } catch (AuthenticationException ex) {
+                        if (this.log.isWarnEnabled()) {
+                            this.log.warn("Authentication error: " +  ex.getMessage());
+                            break;
+                        }
+                    }
+                    updateAuthState(this.proxyAuthState, proxy, credsProvider);
+                    
+                    if (this.proxyAuthState.getCredentials() != null) {
+                        done = false;
+
+                        // Retry request
+                        if (this.reuseStrategy.keepAlive(response, context)) {
+                            this.log.debug("Connection kept alive");
+                            // Consume response content
+                            HttpEntity entity = response.getEntity();
+                            if (entity != null) {
+                                entity.consumeContent();
+                            }                
+                        } else {
+                            this.managedConn.close();
+                        }
+                        
+                    }
+                    
+                } else {
+                    // Reset proxy auth scope
+                    this.proxyAuthState.setAuthScope(null);
+                }
+            }
+        }
+        
+        int status = response.getStatusLine().getStatusCode();
+
+        if (status > 299) {
+
+            // Buffer response content
+            HttpEntity entity = response.getEntity();
+            if (entity != null) {
+                response.setEntity(new BufferedHttpEntity(entity));
+            }                
+            
+            this.managedConn.close();
+            throw new TunnelRefusedException("CONNECT refused by proxy: " +
+                    response.getStatusLine(), response);
+        }
+
+        this.managedConn.markReusable();
+        
+        // How to decide on security of the tunnelled connection?
+        // The socket factory knows only about the segment to the proxy.
+        // Even if that is secure, the hop to the target may be insecure.
+        // Leave it to derived classes, consider insecure by default here.
+        return false;
+
+    } // createTunnelToTarget
+
+
+
+    /**
+     * Creates a tunnel to an intermediate proxy.
+     * This method is <i>not</i> implemented in this class.
+     * It just throws an exception here.
+     *
+     * @param route     the route to establish
+     * @param hop       the hop in the route to establish now.
+     *                  <code>route.getHopTarget(hop)</code>
+     *                  will return the proxy to tunnel to.
+     * @param context   the context for request execution
+     *
+     * @return  <code>true</code> if the partially tunnelled connection
+     *          is secure, <code>false</code> otherwise.
+     *
+     * @throws HttpException    in case of a problem
+     * @throws IOException      in case of an IO problem
+     */
+    protected boolean createTunnelToProxy(HttpRoute route, int hop,
+                                          HttpContext context)
+        throws HttpException, IOException {
+
+        // Have a look at createTunnelToTarget and replicate the parts
+        // you need in a custom derived class. If your proxies don't require
+        // authentication, it is not too hard. But for the stock version of
+        // HttpClient, we cannot make such simplifying assumptions and would
+        // have to include proxy authentication code. The HttpComponents team
+        // is currently not in a position to support rarely used code of this
+        // complexity. Feel free to submit patches that refactor the code in
+        // createTunnelToTarget to facilitate re-use for proxy tunnelling.
+
+        throw new UnsupportedOperationException
+            ("Proxy chains are not supported.");
+    }
+
+
+
+    /**
+     * Creates the CONNECT request for tunnelling.
+     * Called by {@link #createTunnelToTarget createTunnelToTarget}.
+     *
+     * @param route     the route to establish
+     * @param context   the context for request execution
+     *
+     * @return  the CONNECT request for tunnelling
+     */
+    protected HttpRequest createConnectRequest(HttpRoute route,
+                                               HttpContext context) {
+        // see RFC 2817, section 5.2 and 
+        // INTERNET-DRAFT: Tunneling TCP based protocols through 
+        // Web proxy servers
+            
+        HttpHost target = route.getTargetHost();
+        
+        String host = target.getHostName();
+        int port = target.getPort();
+        if (port < 0) {
+            Scheme scheme = connManager.getSchemeRegistry().
+                getScheme(target.getSchemeName());
+            port = scheme.getDefaultPort();
+        }
+        
+        StringBuilder buffer = new StringBuilder(host.length() + 6);
+        buffer.append(host);
+        buffer.append(':');
+        buffer.append(Integer.toString(port));
+        
+        String authority = buffer.toString();
+        ProtocolVersion ver = HttpProtocolParams.getVersion(params);
+        HttpRequest req = new BasicHttpRequest
+            ("CONNECT", authority, ver);
+
+        return req;
+    }
+
+
+    /**
+     * Analyzes a response to check need for a followup.
+     *
+     * @param roureq    the request and route. 
+     * @param response  the response to analayze
+     * @param context   the context used for the current request execution
+     *
+     * @return  the followup request and route if there is a followup, or
+     *          <code>null</code> if the response should be returned as is
+     *
+     * @throws HttpException    in case of a problem
+     * @throws IOException      in case of an IO problem
+     */
+    protected RoutedRequest handleResponse(RoutedRequest roureq,
+                                           HttpResponse response,
+                                           HttpContext context)
+        throws HttpException, IOException {
+
+        HttpRoute route = roureq.getRoute();
+        HttpHost proxy = route.getProxyHost();
+        RequestWrapper request = roureq.getRequest();
+        
+        HttpParams params = request.getParams();
+        if (HttpClientParams.isRedirecting(params) && 
+                this.redirectHandler.isRedirectRequested(response, context)) {
+
+            if (redirectCount >= maxRedirects) {
+                throw new RedirectException("Maximum redirects ("
+                        + maxRedirects + ") exceeded");
+            }
+            redirectCount++;
+            
+            URI uri = this.redirectHandler.getLocationURI(response, context);
+
+            HttpHost newTarget = new HttpHost(
+                    uri.getHost(), 
+                    uri.getPort(),
+                    uri.getScheme());
+            
+            HttpGet redirect = new HttpGet(uri);
+            
+            HttpRequest orig = request.getOriginal();
+            redirect.setHeaders(orig.getAllHeaders());
+            
+            RequestWrapper wrapper = new RequestWrapper(redirect);
+            wrapper.setParams(params);
+            
+            HttpRoute newRoute = determineRoute(newTarget, wrapper, context);
+            RoutedRequest newRequest = new RoutedRequest(wrapper, newRoute);
+            
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("Redirecting to '" + uri + "' via " + newRoute);
+            }
+            
+            return newRequest;
+        }
+
+        CredentialsProvider credsProvider = (CredentialsProvider)
+            context.getAttribute(ClientContext.CREDS_PROVIDER);
+    
+        if (credsProvider != null && HttpClientParams.isAuthenticating(params)) {
+
+            if (this.targetAuthHandler.isAuthenticationRequested(response, context)) {
+
+                HttpHost target = (HttpHost)
+                    context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
+                if (target == null) {
+                    target = route.getTargetHost();
+                }
+                
+                this.log.debug("Target requested authentication");
+                Map<String, Header> challenges = this.targetAuthHandler.getChallenges(
+                        response, context); 
+                try {
+                    processChallenges(challenges, 
+                            this.targetAuthState, this.targetAuthHandler,
+                            response, context);
+                } catch (AuthenticationException ex) {
+                    if (this.log.isWarnEnabled()) {
+                        this.log.warn("Authentication error: " +  ex.getMessage());
+                        return null;
+                    }
+                }
+                updateAuthState(this.targetAuthState, target, credsProvider);
+                
+                if (this.targetAuthState.getCredentials() != null) {
+                    // Re-try the same request via the same route
+                    return roureq;
+                } else {
+                    return null;
+                }
+            } else {
+                // Reset target auth scope
+                this.targetAuthState.setAuthScope(null);
+            }
+            
+            if (this.proxyAuthHandler.isAuthenticationRequested(response, context)) {
+
+                this.log.debug("Proxy requested authentication");
+                Map<String, Header> challenges = this.proxyAuthHandler.getChallenges(
+                        response, context);
+                try {
+                    processChallenges(challenges, 
+                            this.proxyAuthState, this.proxyAuthHandler, 
+                            response, context);
+                } catch (AuthenticationException ex) {
+                    if (this.log.isWarnEnabled()) {
+                        this.log.warn("Authentication error: " +  ex.getMessage());
+                        return null;
+                    }
+                }
+                updateAuthState(this.proxyAuthState, proxy, credsProvider);
+                
+                if (this.proxyAuthState.getCredentials() != null) {
+                    // Re-try the same request via the same route
+                    return roureq;
+                } else {
+                    return null;
+                }
+            } else {
+                // Reset proxy auth scope
+                this.proxyAuthState.setAuthScope(null);
+            }
+        }
+        return null;
+    } // handleResponse
+
+
+    /**
+     * Shuts down the connection.
+     * This method is called from a <code>catch</code> block in
+     * {@link #execute execute} during exception handling.
+     */
+    private void abortConnection() {
+        ManagedClientConnection mcc = managedConn;
+        if (mcc != null) {
+            // we got here as the result of an exception
+            // no response will be returned, release the connection
+            managedConn = null;
+            try {
+                mcc.abortConnection();
+            } catch (IOException ex) {
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug(ex.getMessage(), ex);
+                }
+            }
+            // ensure the connection manager properly releases this connection
+            try {
+                mcc.releaseConnection();
+            } catch(IOException ignored) {
+                this.log.debug("Error releasing connection", ignored);
+            }
+        }
+    } // abortConnection
+
+
+    private void processChallenges(
+            final Map<String, Header> challenges, 
+            final AuthState authState,
+            final AuthenticationHandler authHandler,
+            final HttpResponse response, 
+            final HttpContext context) 
+                throws MalformedChallengeException, AuthenticationException {
+        
+        AuthScheme authScheme = authState.getAuthScheme();
+        if (authScheme == null) {
+            // Authentication not attempted before
+            authScheme = authHandler.selectScheme(challenges, response, context);
+            authState.setAuthScheme(authScheme);
+        }
+        String id = authScheme.getSchemeName();
+
+        Header challenge = challenges.get(id.toLowerCase(Locale.ENGLISH));
+        if (challenge == null) {
+            throw new AuthenticationException(id + 
+                " authorization challenge expected, but not found");
+        }
+        authScheme.processChallenge(challenge);
+        this.log.debug("Authorization challenge processed");
+    }
+    
+    
+    private void updateAuthState(
+            final AuthState authState, 
+            final HttpHost host,
+            final CredentialsProvider credsProvider) {
+        
+        if (!authState.isValid()) {
+            return;
+        }
+        
+        String hostname = host.getHostName();
+        int port = host.getPort();
+        if (port < 0) {
+            Scheme scheme = connManager.getSchemeRegistry().getScheme(host);
+            port = scheme.getDefaultPort();
+        }
+        
+        AuthScheme authScheme = authState.getAuthScheme();
+        AuthScope authScope = new AuthScope(
+                hostname,
+                port,
+                authScheme.getRealm(), 
+                authScheme.getSchemeName());  
+        
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("Authentication scope: " + authScope);
+        }
+        Credentials creds = authState.getCredentials();
+        if (creds == null) {
+            creds = credsProvider.getCredentials(authScope);
+            if (this.log.isDebugEnabled()) {
+                if (creds != null) {
+                    this.log.debug("Found credentials");
+                } else {
+                    this.log.debug("Credentials not found");
+                }
+            }
+        } else {
+            if (authScheme.isComplete()) {
+                this.log.debug("Authentication failed");
+                creds = null;
+            }
+        }
+        authState.setAuthScope(authScope);
+        authState.setCredentials(creds);
+    }
+    
+} // class DefaultClientRequestDirector
diff --git a/src/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java b/src/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java
new file mode 100644
index 0000000..5794549
--- /dev/null
+++ b/src/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java $
+ * $Revision: 603615 $
+ * $Date: 2007-12-12 06:03:21 -0800 (Wed, 12 Dec 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.util.Map;
+
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public class DefaultTargetAuthenticationHandler extends AbstractAuthenticationHandler {
+
+    public DefaultTargetAuthenticationHandler() {
+        super();
+    }
+    
+    public boolean isAuthenticationRequested(
+            final HttpResponse response, 
+            final HttpContext context) {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        int status = response.getStatusLine().getStatusCode();
+        return status == HttpStatus.SC_UNAUTHORIZED;
+    }
+
+    public Map<String, Header> getChallenges(
+            final HttpResponse response, 
+            final HttpContext context) throws MalformedChallengeException {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        Header[] headers = response.getHeaders(AUTH.WWW_AUTH);
+        return parseChallenges(headers);
+    }
+
+}
diff --git a/src/org/apache/http/impl/client/DefaultUserTokenHandler.java b/src/org/apache/http/impl/client/DefaultUserTokenHandler.java
new file mode 100644
index 0000000..c8a409f
--- /dev/null
+++ b/src/org/apache/http/impl/client/DefaultUserTokenHandler.java
@@ -0,0 +1,88 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultUserTokenHandler.java $
+ * $Revision: 659971 $
+ * $Date: 2008-05-25 05:01:22 -0700 (Sun, 25 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.security.Principal;
+
+import javax.net.ssl.SSLSession;
+
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthState;
+import org.apache.http.auth.Credentials;
+import org.apache.http.client.UserTokenHandler;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.protocol.HttpContext;
+
+public class DefaultUserTokenHandler implements UserTokenHandler {
+
+    public Object getUserToken(final HttpContext context) {
+        
+        Principal userPrincipal = null;
+        
+        AuthState targetAuthState = (AuthState) context.getAttribute(
+                ClientContext.TARGET_AUTH_STATE);
+        if (targetAuthState != null) {
+            userPrincipal = getAuthPrincipal(targetAuthState);
+            if (userPrincipal == null) {
+                AuthState proxyAuthState = (AuthState) context.getAttribute(
+                        ClientContext.PROXY_AUTH_STATE);
+                userPrincipal = getAuthPrincipal(proxyAuthState);
+            }
+        }
+        
+        if (userPrincipal == null) {
+            ManagedClientConnection conn = (ManagedClientConnection) context.getAttribute(
+                    ExecutionContext.HTTP_CONNECTION);
+            if (conn.isOpen()) {
+                SSLSession sslsession = conn.getSSLSession();
+                if (sslsession != null) {
+                    userPrincipal = sslsession.getLocalPrincipal();
+                }
+            }
+        }
+        
+        return userPrincipal;
+    }
+
+    private static Principal getAuthPrincipal(final AuthState authState) {
+        AuthScheme scheme = authState.getAuthScheme();
+        if (scheme != null && scheme.isComplete() && scheme.isConnectionBased()) {
+            Credentials creds = authState.getCredentials();
+            if (creds != null) {
+                return creds.getUserPrincipal(); 
+            }
+        }
+        return null;
+    }
+    
+}
diff --git a/src/org/apache/http/impl/client/EntityEnclosingRequestWrapper.java b/src/org/apache/http/impl/client/EntityEnclosingRequestWrapper.java
new file mode 100644
index 0000000..05098cf
--- /dev/null
+++ b/src/org/apache/http/impl/client/EntityEnclosingRequestWrapper.java
@@ -0,0 +1,83 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/EntityEnclosingRequestWrapper.java $
+ * $Revision: 674186 $
+ * $Date: 2008-07-05 05:18:54 -0700 (Sat, 05 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.ProtocolException;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * A wrapper class for {@link HttpEntityEnclosingRequest}s that can 
+ * be used to change properties of the current request without 
+ * modifying the original object.
+ * </p>
+ * This class is also capable of resetting the request headers to
+ * the state of the original request.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 674186 $
+ * 
+ * @since 4.0
+ */
+public class EntityEnclosingRequestWrapper extends RequestWrapper 
+    implements HttpEntityEnclosingRequest {
+    
+    private HttpEntity entity;
+    
+    public EntityEnclosingRequestWrapper(final HttpEntityEnclosingRequest request) 
+        throws ProtocolException {
+        super(request);
+        this.entity = request.getEntity();
+    }
+
+    public HttpEntity getEntity() {
+        return this.entity;
+    }
+
+    public void setEntity(final HttpEntity entity) {
+        this.entity = entity;
+    }
+    
+    public boolean expectContinue() {
+        Header expect = getFirstHeader(HTTP.EXPECT_DIRECTIVE);
+        return expect != null && HTTP.EXPECT_CONTINUE.equalsIgnoreCase(expect.getValue());
+    }
+
+    @Override
+    public boolean isRepeatable() {
+        return this.entity == null || this.entity.isRepeatable();
+    }
+    
+}
diff --git a/src/org/apache/http/impl/client/RedirectLocations.java b/src/org/apache/http/impl/client/RedirectLocations.java
new file mode 100644
index 0000000..d5c47e7
--- /dev/null
+++ b/src/org/apache/http/impl/client/RedirectLocations.java
@@ -0,0 +1,71 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/RedirectLocations.java $
+ * $Revision: 652020 $
+ * $Date: 2008-04-27 14:23:31 -0700 (Sun, 27 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A collection of URIs that were used as redirects.
+ */
+public class RedirectLocations {
+
+    private final Set<URI> uris;
+    
+    public RedirectLocations() {
+        super();
+        this.uris = new HashSet<URI>();
+    }
+    
+    /**
+     * Returns true if this collection contains the given URI.
+     */
+    public boolean contains(final URI uri) {
+        return this.uris.contains(uri);
+    }
+    
+    /**
+     * Adds a new URI to the list of redirects.
+     */
+    public void add(final URI uri) {
+        this.uris.add(uri);
+    }
+
+    /**
+     * Removes a URI from the list of redirects.
+     */
+    public boolean remove(final URI uri) {
+        return this.uris.remove(uri);
+    }
+
+}
diff --git a/src/org/apache/http/impl/client/RequestWrapper.java b/src/org/apache/http/impl/client/RequestWrapper.java
new file mode 100644
index 0000000..04a641d
--- /dev/null
+++ b/src/org/apache/http/impl/client/RequestWrapper.java
@@ -0,0 +1,170 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/RequestWrapper.java $
+ * $Revision: 674186 $
+ * $Date: 2008-07-05 05:18:54 -0700 (Sat, 05 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.http.HttpRequest;
+import org.apache.http.ProtocolException;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.RequestLine;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.message.AbstractHttpMessage;
+import org.apache.http.message.BasicRequestLine;
+import org.apache.http.params.HttpProtocolParams;
+
+/**
+ * A wrapper class for {@link HttpRequest}s that can be used to change
+ * properties of the current request without modifying the original
+ * object.
+ * </p>
+ * This class is also capable of resetting the request headers to
+ * the state of the original request.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 674186 $
+ * 
+ * @since 4.0
+ */
+public class RequestWrapper extends AbstractHttpMessage implements HttpUriRequest {
+    
+    private final HttpRequest original;
+
+    private URI uri;
+    private String method;
+    private ProtocolVersion version;
+    private int execCount;
+    
+    public RequestWrapper(final HttpRequest request) throws ProtocolException {
+        super();
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        this.original = request;
+        setParams(request.getParams());
+        // Make a copy of the original URI 
+        if (request instanceof HttpUriRequest) {
+            this.uri = ((HttpUriRequest) request).getURI();
+            this.method = ((HttpUriRequest) request).getMethod();
+            this.version = null;
+        } else {
+            RequestLine requestLine = request.getRequestLine();
+            try {
+                this.uri = new URI(requestLine.getUri());
+            } catch (URISyntaxException ex) {
+                throw new ProtocolException("Invalid request URI: " 
+                        + requestLine.getUri(), ex);
+            }
+            this.method = requestLine.getMethod();
+            this.version = request.getProtocolVersion();
+        }
+        this.execCount = 0;
+    }
+
+    public void resetHeaders() {
+        // Make a copy of original headers
+        this.headergroup.clear();
+        setHeaders(this.original.getAllHeaders());
+    }
+    
+    public String getMethod() {
+        return this.method;
+    }
+
+    public void setMethod(final String method) {
+        if (method == null) {
+            throw new IllegalArgumentException("Method name may not be null");
+        }
+        this.method = method;
+    }
+
+    public ProtocolVersion getProtocolVersion() {
+        if (this.version != null) {
+            return this.version;
+        } else {
+            return HttpProtocolParams.getVersion(getParams());
+        }
+    }
+
+    public void setProtocolVersion(final ProtocolVersion version) {
+        this.version = version;
+    }
+
+
+    public URI getURI() {
+        return this.uri;
+    }
+    
+    public void setURI(final URI uri) {
+        this.uri = uri;
+    }
+
+    public RequestLine getRequestLine() {
+        String method = getMethod();
+        ProtocolVersion ver = getProtocolVersion();
+        String uritext = null;
+        if (uri != null) {
+            uritext = uri.toASCIIString();
+        }
+        if (uritext == null || uritext.length() == 0) {
+            uritext = "/";
+        }
+        return new BasicRequestLine(method, uritext, ver);
+    }
+
+    public void abort() throws UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean isAborted() {
+        return false;
+    }
+
+    public HttpRequest getOriginal() {
+        return this.original;
+    }
+    
+    public boolean isRepeatable() {
+        return true;
+    }
+
+    public int getExecCount() {
+        return this.execCount;
+    }
+    
+    public void incrementExecCount() {
+        this.execCount++;
+    }
+    
+}
diff --git a/src/org/apache/http/impl/client/RoutedRequest.java b/src/org/apache/http/impl/client/RoutedRequest.java
new file mode 100644
index 0000000..954ebe5
--- /dev/null
+++ b/src/org/apache/http/impl/client/RoutedRequest.java
@@ -0,0 +1,73 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/RoutedRequest.java $
+ * $Revision: 645846 $
+ * $Date: 2008-04-08 03:53:39 -0700 (Tue, 08 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import org.apache.http.conn.routing.HttpRoute;
+
+
+/**
+ * A request with the route along which it should be sent.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 645846 $
+ *
+ * @since 4.0
+ */
+public class RoutedRequest {
+
+    protected final RequestWrapper request;
+    protected final HttpRoute route;
+
+    /**
+     * Creates a new routed request.
+     *
+     * @param req   the request
+     * @param route   the route
+     */
+    public RoutedRequest(final RequestWrapper req, final HttpRoute route) {
+        super();
+        this.request = req;
+        this.route   = route;
+    }
+
+    public final RequestWrapper getRequest() {
+        return request;
+    }
+
+    public final HttpRoute getRoute() {
+        return route;
+    }
+
+} // interface RoutedRequest
diff --git a/src/org/apache/http/impl/client/TunnelRefusedException.java b/src/org/apache/http/impl/client/TunnelRefusedException.java
new file mode 100644
index 0000000..601626f
--- /dev/null
+++ b/src/org/apache/http/impl/client/TunnelRefusedException.java
@@ -0,0 +1,52 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/TunnelRefusedException.java $
+ * $Revision: 537650 $
+ * $Date: 2007-05-13 12:58:22 -0700 (Sun, 13 May 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+
+public class TunnelRefusedException extends HttpException {
+
+    private static final long serialVersionUID = -8646722842745617323L;
+
+    private final HttpResponse response;
+    
+    public TunnelRefusedException(final String message, final HttpResponse response) {
+        super(message);
+        this.response = response;
+    }
+
+    public HttpResponse getResponse() {
+        return this.response;
+    }
+
+}
diff --git a/src/org/apache/http/impl/client/package.html b/src/org/apache/http/impl/client/package.html
new file mode 100644
index 0000000..e301283
--- /dev/null
+++ b/src/org/apache/http/impl/client/package.html
@@ -0,0 +1,4 @@
+<body>
+
+</body>
+
diff --git a/src/org/apache/http/impl/conn/AbstractClientConnAdapter.java b/src/org/apache/http/impl/conn/AbstractClientConnAdapter.java
new file mode 100644
index 0000000..5cbe010
--- /dev/null
+++ b/src/org/apache/http/impl/conn/AbstractClientConnAdapter.java
@@ -0,0 +1,399 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/AbstractClientConnAdapter.java $
+ * $Revision: 672969 $
+ * $Date: 2008-06-30 18:09:50 -0700 (Mon, 30 Jun 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSession;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpConnectionMetrics;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.ClientConnectionManager;
+
+
+/**
+ * Abstract adapter from {@link OperatedClientConnection operated} to
+ * {@link ManagedClientConnection managed} client connections.
+ * Read and write methods are delegated to the wrapped connection.
+ * Operations affecting the connection state have to be implemented
+ * by derived classes. Operations for querying the connection state
+ * are delegated to the wrapped connection if there is one, or
+ * return a default value if there is none.
+ * <br/>
+ * This adapter tracks the checkpoints for reusable communication states,
+ * as indicated by {@link #markReusable markReusable} and queried by
+ * {@link #isMarkedReusable isMarkedReusable}.
+ * All send and receive operations will automatically clear the mark.
+ * <br/>
+ * Connection release calls are delegated to the connection manager,
+ * if there is one. {@link #abortConnection abortConnection} will
+ * clear the reusability mark first. The connection manager is
+ * expected to tolerate multiple calls to the release method.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version   $Revision: 672969 $ $Date: 2008-06-30 18:09:50 -0700 (Mon, 30 Jun 2008) $
+ *
+ * @since 4.0
+ */
+public abstract class AbstractClientConnAdapter
+    implements ManagedClientConnection {
+
+    /** Thread that requested this connection. */
+    private final Thread executionThread; 
+    
+    /**
+     * The connection manager, if any.
+     * This attribute MUST NOT be final, so the adapter can be detached
+     * from the connection manager without keeping a hard reference there.
+     */
+    private volatile ClientConnectionManager connManager;
+
+    /** The wrapped connection. */
+    private volatile OperatedClientConnection wrappedConnection;
+
+    /** The reusability marker. */
+    private volatile boolean markedReusable;
+
+    /** True if the connection has been aborted. */
+    private volatile boolean aborted;
+    
+    /** The duration this is valid for while idle (in ms). */
+    private volatile long duration;
+
+    /**
+     * Creates a new connection adapter.
+     * The adapter is initially <i>not</i>
+     * {@link #isMarkedReusable marked} as reusable.
+     *
+     * @param mgr       the connection manager, or <code>null</code>
+     * @param conn      the connection to wrap, or <code>null</code>
+     */
+    protected AbstractClientConnAdapter(ClientConnectionManager mgr,
+                                        OperatedClientConnection conn) {
+        super();
+        executionThread = Thread.currentThread();
+        connManager = mgr;
+        wrappedConnection = conn;
+        markedReusable = false;
+        aborted = false;
+        duration = Long.MAX_VALUE;
+    } // <constructor>
+
+
+    /**
+     * Detaches this adapter from the wrapped connection.
+     * This adapter becomes useless.
+     */
+    protected void detach() {
+        wrappedConnection = null;
+        connManager = null; // base class attribute
+        duration = Long.MAX_VALUE;
+    }
+
+    protected OperatedClientConnection getWrappedConnection() {
+        return wrappedConnection;
+    }
+    
+    protected ClientConnectionManager getManager() {
+        return connManager;
+    }
+    
+    /**
+     * Asserts that the connection has not been aborted.
+     *
+     * @throws InterruptedIOException   if the connection has been aborted
+     */
+    protected final void assertNotAborted() throws InterruptedIOException {
+        if (aborted) {
+            throw new InterruptedIOException("Connection has been shut down.");
+        }
+    }
+
+    /**
+     * Asserts that there is a wrapped connection to delegate to.
+     *
+     * @throws IllegalStateException    if there is no wrapped connection
+     *                                  or connection has been aborted
+     */
+    protected final void assertValid(
+            final OperatedClientConnection wrappedConn) {
+        if (wrappedConn == null) {
+            throw new IllegalStateException("No wrapped connection.");
+        }
+    }
+
+    // non-javadoc, see interface HttpConnection
+    public boolean isOpen() {
+        OperatedClientConnection conn = getWrappedConnection();
+        if (conn == null)
+            return false;
+
+        return conn.isOpen();
+    }
+
+
+    // non-javadoc, see interface HttpConnection
+    public boolean isStale() {
+        if (aborted)
+            return true;
+        OperatedClientConnection conn = getWrappedConnection();
+        if (conn == null)
+            return true;
+
+        return conn.isStale();
+    }
+
+
+    // non-javadoc, see interface HttpConnection
+    public void setSocketTimeout(int timeout) {
+        OperatedClientConnection conn = getWrappedConnection();
+        assertValid(conn);
+        conn.setSocketTimeout(timeout);
+    }
+
+
+    // non-javadoc, see interface HttpConnection
+    public int getSocketTimeout() {
+        OperatedClientConnection conn = getWrappedConnection();
+        assertValid(conn);
+        return conn.getSocketTimeout();
+    }
+
+
+    // non-javadoc, see interface HttpConnection
+    public HttpConnectionMetrics getMetrics() {
+        OperatedClientConnection conn = getWrappedConnection();
+        assertValid(conn);
+        return conn.getMetrics();
+    }
+
+
+    // non-javadoc, see interface HttpClientConnection
+    public void flush()
+        throws IOException {
+
+        assertNotAborted();
+        OperatedClientConnection conn = getWrappedConnection();
+        assertValid(conn);
+
+        conn.flush();
+    }
+
+
+    // non-javadoc, see interface HttpClientConnection
+    public boolean isResponseAvailable(int timeout)
+        throws IOException {
+
+        assertNotAborted();
+        OperatedClientConnection conn = getWrappedConnection();
+        assertValid(conn);
+
+        return conn.isResponseAvailable(timeout);
+    }
+
+
+    // non-javadoc, see interface HttpClientConnection
+    public void receiveResponseEntity(HttpResponse response)
+        throws HttpException, IOException {
+
+        assertNotAborted();
+        OperatedClientConnection conn = getWrappedConnection();
+        assertValid(conn);
+
+        unmarkReusable();
+        conn.receiveResponseEntity(response);
+    }
+
+
+    // non-javadoc, see interface HttpClientConnection
+    public HttpResponse receiveResponseHeader()
+        throws HttpException, IOException {
+
+        assertNotAborted();
+        OperatedClientConnection conn = getWrappedConnection();
+        assertValid(conn);
+
+        unmarkReusable();
+        return conn.receiveResponseHeader();
+    }
+
+
+    // non-javadoc, see interface HttpClientConnection
+    public void sendRequestEntity(HttpEntityEnclosingRequest request)
+        throws HttpException, IOException {
+
+        assertNotAborted();
+        OperatedClientConnection conn = getWrappedConnection();
+        assertValid(conn);
+
+        unmarkReusable();
+        conn.sendRequestEntity(request);
+    }
+
+
+    // non-javadoc, see interface HttpClientConnection
+    public void sendRequestHeader(HttpRequest request)
+        throws HttpException, IOException {
+
+        assertNotAborted();
+        OperatedClientConnection conn = getWrappedConnection();
+        assertValid(conn);
+        
+        unmarkReusable();
+        conn.sendRequestHeader(request);
+    }
+
+
+    // non-javadoc, see interface HttpInetConnection
+    public InetAddress getLocalAddress() {
+        OperatedClientConnection conn = getWrappedConnection();
+        assertValid(conn);
+        return conn.getLocalAddress();
+    }
+
+    // non-javadoc, see interface HttpInetConnection
+    public int getLocalPort() {
+        OperatedClientConnection conn = getWrappedConnection();
+        assertValid(conn);
+        return conn.getLocalPort();
+    }
+
+
+    // non-javadoc, see interface HttpInetConnection
+    public InetAddress getRemoteAddress() {
+        OperatedClientConnection conn = getWrappedConnection();
+        assertValid(conn);
+        return conn.getRemoteAddress();
+    }
+
+    // non-javadoc, see interface HttpInetConnection
+    public int getRemotePort() {
+        OperatedClientConnection conn = getWrappedConnection();
+        assertValid(conn);
+        return conn.getRemotePort();
+    }
+
+    // non-javadoc, see interface ManagedClientConnection
+    public boolean isSecure() {
+        OperatedClientConnection conn = getWrappedConnection();
+        assertValid(conn);
+        return conn.isSecure();
+    }
+
+    // non-javadoc, see interface ManagedClientConnection
+    public SSLSession getSSLSession() {
+        OperatedClientConnection conn = getWrappedConnection();
+        assertValid(conn);
+        if (!isOpen())
+            return null;
+
+        SSLSession result = null;
+        Socket    sock    = conn.getSocket();
+        if (sock instanceof SSLSocket) {
+            result = ((SSLSocket)sock).getSession();
+        }
+        return result;
+    }
+
+    // non-javadoc, see interface ManagedClientConnection
+    public void markReusable() {
+        markedReusable = true;
+    }
+
+    // non-javadoc, see interface ManagedClientConnection
+    public void unmarkReusable() {
+        markedReusable = false;
+    }
+
+    // non-javadoc, see interface ManagedClientConnection
+    public boolean isMarkedReusable() {
+        return markedReusable;
+    }
+    
+    public void setIdleDuration(long duration, TimeUnit unit) {
+        if(duration > 0) {
+            this.duration = unit.toMillis(duration);
+        } else {
+            this.duration = -1;
+        }
+    }
+
+    // non-javadoc, see interface ConnectionReleaseTrigger
+    public void releaseConnection() {
+        if (connManager != null) {
+            connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS);
+        }
+    }
+
+    // non-javadoc, see interface ConnectionReleaseTrigger
+    public void abortConnection() {
+        if (aborted) {
+            return;
+        }
+        aborted = true;
+        unmarkReusable();
+        try {
+            shutdown();
+        } catch (IOException ignore) {
+        }
+        // Usually #abortConnection() is expected to be called from 
+        // a helper thread in order to unblock the main execution thread 
+        // blocked in an I/O operation. It may be unsafe to call 
+        // #releaseConnection() from the helper thread, so we have to rely
+        // on an IOException thrown by the closed socket on the main thread 
+        // to trigger the release of the connection back to the 
+        // connection manager.
+        // 
+        // However, if this method is called from the main execution thread 
+        // it should be safe to release the connection immediately. Besides, 
+        // this also helps ensure the connection gets released back to the 
+        // manager if #abortConnection() is called from the main execution 
+        // thread while there is no blocking I/O operation.
+        if (executionThread.equals(Thread.currentThread())) {
+            releaseConnection();
+        }
+    }
+
+} // class AbstractClientConnAdapter
diff --git a/src/org/apache/http/impl/conn/AbstractPoolEntry.java b/src/org/apache/http/impl/conn/AbstractPoolEntry.java
new file mode 100644
index 0000000..0e7d95f
--- /dev/null
+++ b/src/org/apache/http/impl/conn/AbstractPoolEntry.java
@@ -0,0 +1,322 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/AbstractPoolEntry.java $
+ * $Revision: 658775 $
+ * $Date: 2008-05-21 10:30:45 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+
+import java.io.IOException;
+
+import org.apache.http.HttpHost;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.RouteTracker;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.OperatedClientConnection;
+
+
+
+/**
+ * A pool entry for use by connection manager implementations.
+ * Pool entries work in conjunction with an
+ * {@link AbstractClientConnAdapter adapter}.
+ * The adapter is handed out to applications that obtain a connection.
+ * The pool entry stores the underlying connection and tracks the
+ * {@link HttpRoute route} established.
+ * The adapter delegates methods for establishing the route to
+ * it's pool entry.
+ * <br/>
+ * If the managed connections is released or revoked, the adapter
+ * gets disconnected, but the pool entry still contains the
+ * underlying connection and the established route.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version   $Revision: 658775 $
+ *
+ * @since 4.0
+ */
+public abstract class AbstractPoolEntry {
+
+    /** The connection operator. */
+    protected final ClientConnectionOperator connOperator;
+
+    /** The underlying connection being pooled or used. */
+    protected final OperatedClientConnection connection;
+
+    /** The route for which this entry gets allocated. */
+    //@@@ currently accessed from connection manager(s) as attribute
+    //@@@ avoid that, derived classes should decide whether update is allowed
+    //@@@ SCCM: yes, TSCCM: no
+    protected volatile HttpRoute route;
+    
+    /** Connection state object */
+    protected volatile Object state;
+    
+    /** The tracked route, or <code>null</code> before tracking starts. */
+    protected volatile RouteTracker tracker;
+
+
+    /**
+     * Creates a new pool entry.
+     *
+     * @param connOperator     the Connection Operator for this entry
+     * @param route   the planned route for the connection,
+     *                or <code>null</code>
+     */
+    protected AbstractPoolEntry(ClientConnectionOperator connOperator,
+                                HttpRoute route) {
+        super();
+        if (connOperator == null) {
+            throw new IllegalArgumentException("Connection operator may not be null");
+        }
+        this.connOperator = connOperator;
+        this.connection = connOperator.createConnection();
+        this.route = route;
+        this.tracker = null;
+    }
+
+    /**
+     * Returns the state object associated with this pool entry.
+     * 
+     * @return The state object
+     */
+    public Object getState() {
+        return state;
+    }
+    
+    /**
+     * Assigns a state object to this pool entry.
+     * 
+     * @param state The state object
+     */
+    public void setState(final Object state) {
+        this.state = state;
+    }
+    
+    /**
+     * Opens the underlying connection.
+     *
+     * @param route         the route along which to open the connection
+     * @param context       the context for opening the connection
+     * @param params        the parameters for opening the connection
+     *
+     * @throws IOException  in case of a problem
+     */
+    public void open(HttpRoute route,
+                     HttpContext context, HttpParams params)
+        throws IOException {
+
+        if (route == null) {
+            throw new IllegalArgumentException
+                ("Route must not be null.");
+        }
+        //@@@ is context allowed to be null? depends on operator?
+        if (params == null) {
+            throw new IllegalArgumentException
+                ("Parameters must not be null.");
+        }
+        if ((this.tracker != null) && this.tracker.isConnected()) {
+            throw new IllegalStateException("Connection already open.");
+        }
+
+        // - collect the arguments
+        // - call the operator
+        // - update the tracking data
+        // In this order, we can be sure that only a successful
+        // opening of the connection will be tracked.
+
+        //@@@ verify route against planned route?
+
+        this.tracker = new RouteTracker(route);
+        final HttpHost proxy  = route.getProxyHost();
+
+        connOperator.openConnection
+            (this.connection,
+             (proxy != null) ? proxy : route.getTargetHost(),
+             route.getLocalAddress(),
+             context, params);
+
+        RouteTracker localTracker = tracker; // capture volatile        
+
+        // If this tracker was reset while connecting,
+        // fail early.
+        if (localTracker == null) {
+            throw new IOException("Request aborted");
+        }
+
+        if (proxy == null) {
+            localTracker.connectTarget(this.connection.isSecure());
+        } else {
+            localTracker.connectProxy(proxy, this.connection.isSecure());
+        }
+
+    } // open
+
+
+    /**
+     * Tracks tunnelling of the connection to the target.
+     * The tunnel has to be established outside by sending a CONNECT
+     * request to the (last) proxy.
+     *
+     * @param secure    <code>true</code> if the tunnel should be
+     *                  considered secure, <code>false</code> otherwise
+     * @param params    the parameters for tunnelling the connection
+     *
+     * @throws IOException  in case of a problem
+     */
+    public void tunnelTarget(boolean secure, HttpParams params)
+        throws IOException {
+
+        if (params == null) {
+            throw new IllegalArgumentException
+                ("Parameters must not be null.");
+        }
+
+        //@@@ check for proxy in planned route?
+        if ((this.tracker == null) || !this.tracker.isConnected()) {
+            throw new IllegalStateException("Connection not open.");
+        }
+        if (this.tracker.isTunnelled()) {
+            throw new IllegalStateException
+                ("Connection is already tunnelled.");
+        }
+
+        // LOG.debug?
+
+        this.connection.update(null, tracker.getTargetHost(),
+                               secure, params);
+        this.tracker.tunnelTarget(secure);
+
+    } // tunnelTarget
+
+
+    /**
+     * Tracks tunnelling of the connection to a chained proxy.
+     * The tunnel has to be established outside by sending a CONNECT
+     * request to the previous proxy.
+     *
+     * @param next      the proxy to which the tunnel was established.
+     *  See {@link org.apache.http.conn.ManagedClientConnection#tunnelProxy
+     *                                  ManagedClientConnection.tunnelProxy}
+     *                  for details.
+     * @param secure    <code>true</code> if the tunnel should be
+     *                  considered secure, <code>false</code> otherwise
+     * @param params    the parameters for tunnelling the connection
+     *
+     * @throws IOException  in case of a problem
+     */
+    public void tunnelProxy(HttpHost next, boolean secure, HttpParams params)
+        throws IOException {
+
+        if (next == null) {
+            throw new IllegalArgumentException
+                ("Next proxy must not be null.");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException
+                ("Parameters must not be null.");
+        }
+
+        //@@@ check for proxy in planned route?
+        if ((this.tracker == null) || !this.tracker.isConnected()) {
+            throw new IllegalStateException("Connection not open.");
+        }
+
+        // LOG.debug?
+
+        this.connection.update(null, next, secure, params);
+        this.tracker.tunnelProxy(next, secure);
+
+    } // tunnelProxy
+
+
+    /**
+     * Layers a protocol on top of an established tunnel.
+     *
+     * @param context   the context for layering
+     * @param params    the parameters for layering
+     *
+     * @throws IOException  in case of a problem
+     */
+    public void layerProtocol(HttpContext context, HttpParams params)
+        throws IOException {
+
+        //@@@ is context allowed to be null? depends on operator?
+        if (params == null) {
+            throw new IllegalArgumentException
+                ("Parameters must not be null.");
+        }
+
+        if ((this.tracker == null) || !this.tracker.isConnected()) {
+            throw new IllegalStateException("Connection not open.");
+        }
+        if (!this.tracker.isTunnelled()) {
+            //@@@ allow this?
+            throw new IllegalStateException
+                ("Protocol layering without a tunnel not supported.");
+        }
+        if (this.tracker.isLayered()) {
+            throw new IllegalStateException
+                ("Multiple protocol layering not supported.");
+        }
+
+        // - collect the arguments
+        // - call the operator
+        // - update the tracking data
+        // In this order, we can be sure that only a successful
+        // layering on top of the connection will be tracked.
+
+        final HttpHost target = tracker.getTargetHost();
+
+        connOperator.updateSecureConnection(this.connection, target,
+                                             context, params);
+
+        this.tracker.layerProtocol(this.connection.isSecure());
+
+    } // layerProtocol
+
+
+    /**
+     * Shuts down the entry.
+     * 
+     * If {@link #open(HttpRoute, HttpContext, HttpParams)} is in progress,
+     * this will cause that open to possibly throw an {@link IOException}.
+     */
+    protected void shutdownEntry() { 
+        tracker = null;
+    }
+
+
+} // class AbstractPoolEntry
+
diff --git a/src/org/apache/http/impl/conn/AbstractPooledConnAdapter.java b/src/org/apache/http/impl/conn/AbstractPooledConnAdapter.java
new file mode 100644
index 0000000..2c5fd30
--- /dev/null
+++ b/src/org/apache/http/impl/conn/AbstractPooledConnAdapter.java
@@ -0,0 +1,188 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/AbstractPooledConnAdapter.java $
+ * $Revision: 658775 $
+ * $Date: 2008-05-21 10:30:45 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+
+import java.io.IOException;
+
+import org.apache.http.HttpHost;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.OperatedClientConnection;
+
+
+
+/**
+ * Abstract adapter from pool {@link AbstractPoolEntry entries} to
+ * {@link org.apache.http.conn.ManagedClientConnection managed}
+ * client connections.
+ * The connection in the pool entry is used to initialize the base class.
+ * In addition, methods to establish a route are delegated to the
+ * pool entry. {@link #shutdown shutdown} and {@link #close close}
+ * will clear the tracked route in the pool entry and call the
+ * respective method of the wrapped connection.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version   $Revision: 658775 $ $Date: 2008-05-21 10:30:45 -0700 (Wed, 21 May 2008) $
+ *
+ * @since 4.0
+ */
+public abstract class AbstractPooledConnAdapter extends AbstractClientConnAdapter {
+
+    /** The wrapped pool entry. */
+    protected volatile AbstractPoolEntry poolEntry;
+
+
+    /**
+     * Creates a new connection adapter.
+     *
+     * @param manager   the connection manager
+     * @param entry     the pool entry for the connection being wrapped
+     */
+    protected AbstractPooledConnAdapter(ClientConnectionManager manager,
+                                        AbstractPoolEntry entry) {
+        super(manager, entry.connection);
+        this.poolEntry = entry;
+    }
+
+
+    /**
+     * Asserts that this adapter is still attached.
+     *
+     * @throws IllegalStateException
+     *      if it is {@link #detach detach}ed
+     */
+    protected final void assertAttached() {
+        if (poolEntry == null) {
+            throw new IllegalStateException("Adapter is detached.");
+        }
+    }
+
+    /**
+     * Detaches this adapter from the wrapped connection.
+     * This adapter becomes useless.
+     */
+    @Override
+    protected void detach() {
+        super.detach();
+        poolEntry = null;
+    }
+
+
+    // non-javadoc, see interface ManagedHttpConnection
+    public HttpRoute getRoute() {
+
+        assertAttached();
+        return (poolEntry.tracker == null) ?
+            null : poolEntry.tracker.toRoute();
+    }
+
+    // non-javadoc, see interface ManagedHttpConnection
+    public void open(HttpRoute route,
+                     HttpContext context, HttpParams params)
+        throws IOException {
+
+        assertAttached();
+        poolEntry.open(route, context, params);
+    }
+
+
+    // non-javadoc, see interface ManagedHttpConnection
+    public void tunnelTarget(boolean secure, HttpParams params)
+        throws IOException {
+
+        assertAttached();
+        poolEntry.tunnelTarget(secure, params);
+    }
+
+
+    // non-javadoc, see interface ManagedHttpConnection
+    public void tunnelProxy(HttpHost next, boolean secure, HttpParams params)
+        throws IOException {
+
+        assertAttached();
+        poolEntry.tunnelProxy(next, secure, params);
+    }
+
+
+    // non-javadoc, see interface ManagedHttpConnection
+    public void layerProtocol(HttpContext context, HttpParams params)
+        throws IOException {
+
+        assertAttached();
+        poolEntry.layerProtocol(context, params);
+    }
+
+
+
+    // non-javadoc, see interface HttpConnection        
+    public void close() throws IOException {
+        if (poolEntry != null)
+            poolEntry.shutdownEntry();
+
+        OperatedClientConnection conn = getWrappedConnection();
+        if (conn != null) {
+            conn.close();
+        }
+    }
+
+    // non-javadoc, see interface HttpConnection        
+    public void shutdown() throws IOException {
+        if (poolEntry != null)
+            poolEntry.shutdownEntry();
+
+        OperatedClientConnection conn = getWrappedConnection();
+        if (conn != null) {
+            conn.shutdown();
+        }
+    }
+
+    
+    // non-javadoc, see interface ManagedClientConnection        
+    public Object getState() {
+        assertAttached();
+        return poolEntry.getState();
+    }
+
+
+    // non-javadoc, see interface ManagedClientConnection        
+    public void setState(final Object state) {
+        assertAttached();
+        poolEntry.setState(state);
+    }
+    
+
+} // class AbstractPooledConnAdapter
diff --git a/src/org/apache/http/impl/conn/DefaultClientConnection.java b/src/org/apache/http/impl/conn/DefaultClientConnection.java
new file mode 100644
index 0000000..a41f57a
--- /dev/null
+++ b/src/org/apache/http/impl/conn/DefaultClientConnection.java
@@ -0,0 +1,259 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/DefaultClientConnection.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+
+import java.io.IOException;
+import java.net.Socket;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseFactory;
+import org.apache.http.params.HttpParams;
+import org.apache.http.impl.SocketHttpClientConnection;
+import org.apache.http.io.HttpMessageParser;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.io.SessionOutputBuffer;
+
+import org.apache.http.conn.OperatedClientConnection;
+
+
+/**
+ * Default implementation of an operated client connection.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version   $Revision: 673450 $ $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * @since 4.0
+ */
+public class DefaultClientConnection extends SocketHttpClientConnection
+    implements OperatedClientConnection {
+
+    private final Log log = LogFactory.getLog(getClass());
+    private final Log headerLog = LogFactory.getLog("org.apache.http.headers");
+    private final Log wireLog = LogFactory.getLog("org.apache.http.wire");
+
+    /** The unconnected socket */
+    private volatile Socket socket;
+
+    /** The target host of this connection. */
+    private HttpHost targetHost;
+
+    /** Whether this connection is secure. */
+    private boolean connSecure;
+    
+    /** True if this connection was shutdown. */
+    private volatile boolean shutdown;
+
+    public DefaultClientConnection() {
+        super();
+    }
+
+
+    // non-javadoc, see interface OperatedClientConnection
+    public final HttpHost getTargetHost() {
+        return this.targetHost;
+    }
+
+
+    // non-javadoc, see interface OperatedClientConnection
+    public final boolean isSecure() {
+        return this.connSecure;
+    }
+
+
+    @Override
+    public final Socket getSocket() {
+        return this.socket;
+    }
+
+
+    public void opening(Socket sock, HttpHost target) throws IOException {
+        assertNotOpen();        
+        this.socket = sock;
+        this.targetHost = target;
+        
+        // Check for shutdown after assigning socket, so that 
+        if (this.shutdown) {
+            sock.close(); // allow this to throw...
+            // ...but if it doesn't, explicitly throw one ourselves.
+            throw new IOException("Connection already shutdown");
+        }
+    }
+
+    
+    public void openCompleted(boolean secure, HttpParams params) throws IOException {
+        assertNotOpen();
+        if (params == null) {
+            throw new IllegalArgumentException
+                ("Parameters must not be null.");
+        }
+        this.connSecure = secure;
+        bind(this.socket, params);
+    }
+
+    /**
+     * Force-closes this connection.
+     * If the connection is still in the process of being open (the method 
+     * {@link #opening opening} was already called but 
+     * {@link #openCompleted openCompleted} was not), the associated 
+     * socket that is being connected to a remote address will be closed. 
+     * That will interrupt a thread that is blocked on connecting 
+     * the socket.
+     * If the connection is not yet open, this will prevent the connection
+     * from being opened.
+     *
+     * @throws IOException      in case of a problem
+     */
+    @Override
+    public void shutdown() throws IOException {
+        log.debug("Connection shut down");
+        shutdown = true;
+        
+        super.shutdown();        
+        Socket sock = this.socket; // copy volatile attribute
+        if (sock != null)
+            sock.close();
+
+    } // shutdown
+
+    
+    @Override
+    public void close() throws IOException {
+        log.debug("Connection closed");
+        super.close();
+    }
+
+
+    @Override
+    protected SessionInputBuffer createSessionInputBuffer(
+            final Socket socket,
+            int buffersize,
+            final HttpParams params) throws IOException {
+        SessionInputBuffer inbuffer = super.createSessionInputBuffer(
+                socket, 
+                buffersize,
+                params);
+        if (wireLog.isDebugEnabled()) {
+            inbuffer = new LoggingSessionInputBuffer(inbuffer, new Wire(wireLog));
+        }
+        return inbuffer;
+    }
+
+    
+    @Override
+    protected SessionOutputBuffer createSessionOutputBuffer(
+            final Socket socket,
+            int buffersize,
+            final HttpParams params) throws IOException {
+        SessionOutputBuffer outbuffer = super.createSessionOutputBuffer(
+                socket,
+                buffersize,
+                params);
+        if (wireLog.isDebugEnabled()) {
+            outbuffer = new LoggingSessionOutputBuffer(outbuffer, new Wire(wireLog));
+        }
+        return outbuffer;
+    }
+
+    
+    @Override
+    protected HttpMessageParser createResponseParser(
+            final SessionInputBuffer buffer,
+            final HttpResponseFactory responseFactory, 
+            final HttpParams params) {
+        // override in derived class to specify a line parser
+        return new DefaultResponseParser
+            (buffer, null, responseFactory, params);
+    }
+
+
+    // non-javadoc, see interface OperatedClientConnection
+    public void update(Socket sock, HttpHost target,
+                       boolean secure, HttpParams params)
+        throws IOException {
+
+        assertOpen();
+        if (target == null) {
+            throw new IllegalArgumentException
+                ("Target host must not be null.");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException
+                ("Parameters must not be null.");
+        }
+
+        if (sock != null) {
+            this.socket = sock;
+            bind(sock, params);
+        }
+        targetHost = target;
+        connSecure = secure;
+
+    } // update
+
+
+    @Override
+    public HttpResponse receiveResponseHeader() throws HttpException, IOException {
+        HttpResponse response = super.receiveResponseHeader();
+        if (headerLog.isDebugEnabled()) {
+            headerLog.debug("<< " + response.getStatusLine().toString());
+            Header[] headers = response.getAllHeaders();
+            for (Header header : headers) {
+                headerLog.debug("<< " + header.toString());
+            }
+        }
+        return response;
+    }
+
+
+    @Override
+    public void sendRequestHeader(HttpRequest request) throws HttpException, IOException {
+        super.sendRequestHeader(request);
+        if (headerLog.isDebugEnabled()) {
+            headerLog.debug(">> " + request.getRequestLine().toString());
+            Header[] headers = request.getAllHeaders();
+            for (Header header : headers) {
+                headerLog.debug(">> " + header.toString());
+            }
+        }
+    }
+
+} // class DefaultClientConnection
diff --git a/src/org/apache/http/impl/conn/DefaultClientConnectionOperator.java b/src/org/apache/http/impl/conn/DefaultClientConnectionOperator.java
new file mode 100644
index 0000000..41488e1
--- /dev/null
+++ b/src/org/apache/http/impl/conn/DefaultClientConnectionOperator.java
@@ -0,0 +1,216 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/DefaultClientConnectionOperator.java $
+ * $Revision: 652193 $
+ * $Date: 2008-04-29 17:10:36 -0700 (Tue, 29 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.Socket;
+import java.net.InetAddress;
+
+import org.apache.http.HttpHost;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.protocol.HttpContext;
+
+import org.apache.http.conn.HttpHostConnectException;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.scheme.LayeredSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.scheme.SocketFactory;
+
+
+/**
+ * Default implementation of a
+ * {@link ClientConnectionOperator ClientConnectionOperator}.
+ * It uses a {@link SchemeRegistry SchemeRegistry} to look up
+ * {@link SocketFactory SocketFactory} objects.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version   $Revision: 652193 $ $Date: 2008-04-29 17:10:36 -0700 (Tue, 29 Apr 2008) $
+ *
+ * @since 4.0
+ */
+public class DefaultClientConnectionOperator
+    implements ClientConnectionOperator {
+
+
+    /** The scheme registry for looking up socket factories. */
+    protected SchemeRegistry schemeRegistry;
+
+
+    /**
+     * Creates a new client connection operator for the given scheme registry.
+     *
+     * @param schemes   the scheme registry
+     */
+    public DefaultClientConnectionOperator(SchemeRegistry schemes) {
+        if (schemes == null) {
+            throw new IllegalArgumentException
+                ("Scheme registry must not be null.");
+        }
+        schemeRegistry = schemes;
+    }
+
+
+    // non-javadoc, see interface ClientConnectionOperator
+    public OperatedClientConnection createConnection() {
+        return new DefaultClientConnection();
+    }
+
+
+    // non-javadoc, see interface ClientConnectionOperator
+    public void openConnection(OperatedClientConnection conn,
+                               HttpHost target,
+                               InetAddress local,
+                               HttpContext context,
+                               HttpParams params)
+        throws IOException {
+
+        if (conn == null) {
+            throw new IllegalArgumentException
+                ("Connection must not be null.");
+        }
+        if (target == null) {
+            throw new IllegalArgumentException
+                ("Target host must not be null.");
+        }
+        // local address may be null
+        //@@@ is context allowed to be null?
+        if (params == null) {
+            throw new IllegalArgumentException
+                ("Parameters must not be null.");
+        }
+        if (conn.isOpen()) {
+            throw new IllegalArgumentException
+                ("Connection must not be open.");
+        }
+
+        final Scheme schm = schemeRegistry.getScheme(target.getSchemeName());
+        final SocketFactory sf = schm.getSocketFactory();
+
+        Socket sock = sf.createSocket();
+        conn.opening(sock, target);
+
+        try {
+            sock = sf.connectSocket(sock, target.getHostName(),
+                    schm.resolvePort(target.getPort()),
+                    local, 0, params);
+        } catch (ConnectException ex) {
+            throw new HttpHostConnectException(target, ex);
+        }
+        prepareSocket(sock, context, params);
+        conn.openCompleted(sf.isSecure(sock), params);
+    } // openConnection
+
+
+    // non-javadoc, see interface ClientConnectionOperator
+    public void updateSecureConnection(OperatedClientConnection conn,
+                                       HttpHost target,
+                                       HttpContext context,
+                                       HttpParams params)
+        throws IOException {
+
+
+        if (conn == null) {
+            throw new IllegalArgumentException
+                ("Connection must not be null.");
+        }
+        if (target == null) {
+            throw new IllegalArgumentException
+                ("Target host must not be null.");
+        }
+        //@@@ is context allowed to be null?
+        if (params == null) {
+            throw new IllegalArgumentException
+                ("Parameters must not be null.");
+        }
+        if (!conn.isOpen()) {
+            throw new IllegalArgumentException
+                ("Connection must be open.");
+        }
+
+        final Scheme schm = schemeRegistry.getScheme(target.getSchemeName());
+        if (!(schm.getSocketFactory() instanceof LayeredSocketFactory)) {
+            throw new IllegalArgumentException
+                ("Target scheme (" + schm.getName() +
+                 ") must have layered socket factory.");
+        }
+
+        final LayeredSocketFactory lsf = (LayeredSocketFactory) schm.getSocketFactory();
+        final Socket sock; 
+        try {
+            sock = lsf.createSocket
+                (conn.getSocket(), target.getHostName(), target.getPort(), true);
+        } catch (ConnectException ex) {
+            throw new HttpHostConnectException(target, ex);
+        }
+        prepareSocket(sock, context, params);
+        conn.update(sock, target, lsf.isSecure(sock), params);
+        //@@@ error handling: close the layered socket in case of exception?
+
+    } // updateSecureConnection
+
+
+    /**
+     * Performs standard initializations on a newly created socket.
+     *
+     * @param sock      the socket to prepare
+     * @param context   the context for the connection
+     * @param params    the parameters from which to prepare the socket
+     *
+     * @throws IOException      in case of an IO problem
+     */
+    protected void prepareSocket(Socket sock, HttpContext context,
+                                 HttpParams params)
+        throws IOException {
+
+        // context currently not used, but derived classes may need it
+        //@@@ is context allowed to be null?
+
+        sock.setTcpNoDelay(HttpConnectionParams.getTcpNoDelay(params));
+        sock.setSoTimeout(HttpConnectionParams.getSoTimeout(params));
+
+        int linger = HttpConnectionParams.getLinger(params);
+        if (linger >= 0) {
+            sock.setSoLinger(linger > 0, linger);
+        }
+
+    } // prepareSocket
+
+
+} // class DefaultClientConnectionOperator
+
diff --git a/src/org/apache/http/impl/conn/DefaultHttpRoutePlanner.java b/src/org/apache/http/impl/conn/DefaultHttpRoutePlanner.java
new file mode 100644
index 0000000..90fd55f
--- /dev/null
+++ b/src/org/apache/http/impl/conn/DefaultHttpRoutePlanner.java
@@ -0,0 +1,121 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/DefaultHttpRoutePlanner.java $
+ * $Revision: 658785 $
+ * $Date: 2008-05-21 10:47:40 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+
+import java.net.InetAddress;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.protocol.HttpContext;
+
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+
+import org.apache.http.conn.params.ConnRouteParams;
+
+
+/**
+ * Default implementation of an {@link HttpRoutePlanner}.
+ * This implementation is based on
+ * {@link org.apache.http.conn.params.ConnRoutePNames parameters}.
+ * It will not make use of any Java system properties,
+ * nor of system or browser proxy settings.
+ */
+public class DefaultHttpRoutePlanner implements HttpRoutePlanner {
+
+    /** The scheme registry. */
+    protected SchemeRegistry schemeRegistry;
+
+
+    /**
+     * Creates a new default route planner.
+     *
+     * @param schreg    the scheme registry
+     */
+    public DefaultHttpRoutePlanner(SchemeRegistry schreg) {
+        if (schreg == null) {
+            throw new IllegalArgumentException
+                ("SchemeRegistry must not be null.");
+        }
+        schemeRegistry = schreg;
+    }
+
+
+    // non-javadoc, see interface HttpRoutePlanner
+    public HttpRoute determineRoute(HttpHost target,
+                                    HttpRequest request,
+                                    HttpContext context)
+        throws HttpException {
+
+        if (request == null) {
+            throw new IllegalStateException
+                ("Request must not be null.");
+        }
+
+        // If we have a forced route, we can do without a target.
+        HttpRoute route =
+            ConnRouteParams.getForcedRoute(request.getParams());
+        if (route != null)
+            return route;
+
+        // If we get here, there is no forced route.
+        // So we need a target to compute a route.
+
+        if (target == null) {
+            throw new IllegalStateException
+                ("Target host must not be null.");
+        }
+
+        final InetAddress local =
+            ConnRouteParams.getLocalAddress(request.getParams());
+        final HttpHost proxy =
+            ConnRouteParams.getDefaultProxy(request.getParams());
+
+        final Scheme schm = schemeRegistry.getScheme(target.getSchemeName());
+        // as it is typically used for TLS/SSL, we assume that
+        // a layered scheme implies a secure connection
+        final boolean secure = schm.isLayered();
+
+        if (proxy == null) {
+            route = new HttpRoute(target, local, secure);
+        } else {
+            route = new HttpRoute(target, local, proxy, secure);
+        }
+        return route;
+    }
+    
+    
+}
diff --git a/src/org/apache/http/impl/conn/DefaultResponseParser.java b/src/org/apache/http/impl/conn/DefaultResponseParser.java
new file mode 100644
index 0000000..f817a10
--- /dev/null
+++ b/src/org/apache/http/impl/conn/DefaultResponseParser.java
@@ -0,0 +1,103 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/DefaultResponseParser.java $
+ * $Revision: 617638 $
+ * $Date: 2008-02-01 12:49:26 -0800 (Fri, 01 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.HttpResponseFactory;
+import org.apache.http.NoHttpResponseException;
+import org.apache.http.ProtocolException;
+import org.apache.http.StatusLine;
+import org.apache.http.conn.params.ConnConnectionPNames;
+import org.apache.http.impl.io.AbstractMessageParser;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.message.LineParser;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.CharArrayBuffer;
+
+public class DefaultResponseParser extends AbstractMessageParser {
+    
+    private final HttpResponseFactory responseFactory;
+    private final CharArrayBuffer lineBuf;
+    private final int maxGarbageLines;
+    
+    public DefaultResponseParser(
+            final SessionInputBuffer buffer,
+            final LineParser parser,
+            final HttpResponseFactory responseFactory,
+            final HttpParams params) {
+        super(buffer, parser, params);
+        if (responseFactory == null) {
+            throw new IllegalArgumentException
+                ("Response factory may not be null");
+        }
+        this.responseFactory = responseFactory;
+        this.lineBuf = new CharArrayBuffer(128);
+        this.maxGarbageLines = params.getIntParameter(
+            ConnConnectionPNames.MAX_STATUS_LINE_GARBAGE, Integer.MAX_VALUE);
+    }
+
+
+    @Override
+    protected HttpMessage parseHead(
+            final SessionInputBuffer sessionBuffer) throws IOException, HttpException {
+        // clear the buffer
+        this.lineBuf.clear();
+        //read out the HTTP status string
+        int count = 0;
+        ParserCursor cursor = null;
+        do {
+            int i = sessionBuffer.readLine(this.lineBuf);
+            if (i == -1 && count == 0) {
+                // The server just dropped connection on us
+                throw new NoHttpResponseException("The target server failed to respond");
+            }
+            cursor = new ParserCursor(0, this.lineBuf.length());
+            if (lineParser.hasProtocolVersion(this.lineBuf, cursor)) {
+                // Got one
+                break;
+            } else if (i == -1 || count >= this.maxGarbageLines) {
+                // Giving up
+                throw new ProtocolException("The server failed to respond with a " +
+                        "valid HTTP response");
+            }
+            count++;
+        } while(true);
+        //create the status line from the status string
+        StatusLine statusline = lineParser.parseStatusLine(this.lineBuf, cursor);
+        return this.responseFactory.newHttpResponse(statusline, null);
+    }
+
+}
diff --git a/src/org/apache/http/impl/conn/IdleConnectionHandler.java b/src/org/apache/http/impl/conn/IdleConnectionHandler.java
new file mode 100644
index 0000000..2cacda3
--- /dev/null
+++ b/src/org/apache/http/impl/conn/IdleConnectionHandler.java
@@ -0,0 +1,190 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/IdleConnectionHandler.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpConnection;
+
+
+/**
+ * A helper class for connection managers to track idle connections.
+ * 
+ * <p>This class is not synchronized.</p>
+ * 
+ * @see org.apache.http.conn.ClientConnectionManager#closeIdleConnections
+ * 
+ * @since 4.0
+ */
+public class IdleConnectionHandler {
+
+    private final Log log = LogFactory.getLog(getClass());
+    
+    /** Holds connections and the time they were added. */
+    private final Map<HttpConnection,TimeValues> connectionToTimes;
+    
+
+    public IdleConnectionHandler() {
+        super();
+        connectionToTimes = new HashMap<HttpConnection,TimeValues>();
+    }
+    
+    /**
+     * Registers the given connection with this handler.  The connection will be held until 
+     * {@link #remove} or {@link #closeIdleConnections} is called.
+     * 
+     * @param connection the connection to add
+     * 
+     * @see #remove
+     */
+    public void add(HttpConnection connection, long validDuration, TimeUnit unit) {
+        
+        Long timeAdded = Long.valueOf(System.currentTimeMillis());
+        
+        if (log.isDebugEnabled()) {
+            log.debug("Adding connection at: " + timeAdded);
+        }
+        
+        connectionToTimes.put(connection, new TimeValues(timeAdded, validDuration, unit));
+    }
+    
+    /**
+     * Removes the given connection from the list of connections to be closed when idle.
+     * This will return true if the connection is still valid, and false
+     * if the connection should be considered expired and not used.
+     * 
+     * @param connection
+     * @return True if the connection is still valid.
+     */
+    public boolean remove(HttpConnection connection) {
+        TimeValues times = connectionToTimes.remove(connection);
+        if(times == null) {
+            log.warn("Removing a connection that never existed!");
+            return true;
+        } else {
+            return System.currentTimeMillis() <= times.timeExpires;
+        }
+    }
+
+    /**
+     * Removes all connections referenced by this handler.
+     */
+    public void removeAll() {
+        this.connectionToTimes.clear();
+    }
+    
+    /**
+     * Closes connections that have been idle for at least the given amount of time.
+     * 
+     * @param idleTime the minimum idle time, in milliseconds, for connections to be closed
+     */
+    //@@@ add TimeUnit argument here?
+    public void closeIdleConnections(long idleTime) {
+        
+        // the latest time for which connections will be closed
+        long idleTimeout = System.currentTimeMillis() - idleTime;
+
+        if (log.isDebugEnabled()) {
+            log.debug("Checking for connections, idleTimeout: "  + idleTimeout);
+        }
+        
+        Iterator<HttpConnection> connectionIter =
+            connectionToTimes.keySet().iterator();
+        
+        while (connectionIter.hasNext()) {
+            HttpConnection conn = connectionIter.next();
+            TimeValues times = connectionToTimes.get(conn);
+            Long connectionTime = times.timeAdded;
+            if (connectionTime.longValue() <= idleTimeout) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Closing connection, connection time: "  + connectionTime);
+                }
+                connectionIter.remove();
+                try {
+                    conn.close();
+                } catch (IOException ex) {
+                    log.debug("I/O error closing connection", ex);
+                }
+            }
+        }
+    }
+    
+
+    public void closeExpiredConnections() {
+        long now = System.currentTimeMillis();
+        if (log.isDebugEnabled()) {
+            log.debug("Checking for expired connections, now: "  + now);
+        }
+        
+        Iterator<HttpConnection> connectionIter =
+            connectionToTimes.keySet().iterator();
+        
+        while (connectionIter.hasNext()) {
+            HttpConnection conn = connectionIter.next();
+            TimeValues times = connectionToTimes.get(conn);
+            if(times.timeExpires <= now) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Closing connection, expired @: "  + times.timeExpires);
+                }
+                connectionIter.remove();
+                try {
+                    conn.close();
+                } catch (IOException ex) {
+                    log.debug("I/O error closing connection", ex);
+                }
+            }
+        }        
+    }
+    
+    private static class TimeValues {
+        private final long timeAdded;
+        private final long timeExpires;
+
+        /**
+         * @param now The current time in milliseconds
+         * @param validDuration The duration this connection is valid for
+         * @param validUnit The unit of time the duration is specified in.
+         */
+        TimeValues(long now, long validDuration, TimeUnit validUnit) {
+            this.timeAdded = now;
+            if(validDuration > 0) {
+                this.timeExpires = now + validUnit.toMillis(validDuration);
+            } else {
+                this.timeExpires = Long.MAX_VALUE;
+            }
+        }
+    }
+}
diff --git a/src/org/apache/http/impl/conn/LoggingSessionInputBuffer.java b/src/org/apache/http/impl/conn/LoggingSessionInputBuffer.java
new file mode 100644
index 0000000..4f6477e
--- /dev/null
+++ b/src/org/apache/http/impl/conn/LoggingSessionInputBuffer.java
@@ -0,0 +1,117 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/LoggingSessionInputBuffer.java $
+ * $Revision: 674186 $
+ * $Date: 2008-07-05 05:18:54 -0700 (Sat, 05 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+
+import org.apache.http.io.HttpTransportMetrics;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Logs all data read to the wire LOG.
+ *
+ * @author Ortwin Glueck
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public class LoggingSessionInputBuffer implements SessionInputBuffer {
+     
+    /** Original session input buffer. */
+    private final SessionInputBuffer in;
+
+    /** The wire log to use for writing. */
+    private final Wire wire;
+    
+    /**
+     * Create an instance that wraps the specified session input buffer.
+     * @param in The session input buffer.
+     * @param wire The wire log to use.
+     */
+    public LoggingSessionInputBuffer(final SessionInputBuffer in, final Wire wire) {
+        super();
+        this.in = in;
+        this.wire = wire;
+    }
+
+    public boolean isDataAvailable(int timeout) throws IOException {
+        return this.in.isDataAvailable(timeout);
+    }
+
+    public int read(byte[] b, int off, int len) throws IOException {
+        int l = this.in.read(b,  off,  len);
+        if (this.wire.enabled() && l > 0) {
+            this.wire.input(b, off, l);
+        }
+        return l;
+    }
+
+    public int read() throws IOException {
+        int l = this.in.read();
+        if (this.wire.enabled() && l > 0) { 
+            this.wire.input(l);
+        }
+        return l;
+    }
+
+    public int read(byte[] b) throws IOException {
+        int l = this.in.read(b);
+        if (this.wire.enabled() && l > 0) {
+            this.wire.input(b, 0, l);
+        }
+        return l;
+    }
+
+    public String readLine() throws IOException {
+        String s = this.in.readLine();
+        if (this.wire.enabled() && s != null) {
+            this.wire.input(s + "[EOL]");
+        }
+        return s;
+    }
+
+    public int readLine(final CharArrayBuffer buffer) throws IOException {
+        int l = this.in.readLine(buffer);
+        if (this.wire.enabled() && l > 0) {
+            int pos = buffer.length() - l;
+            String s = new String(buffer.buffer(), pos, l);
+            this.wire.input(s + "[EOL]");
+        }
+        return l;
+    }
+
+    public HttpTransportMetrics getMetrics() {
+        return this.in.getMetrics();
+    }
+
+}
diff --git a/src/org/apache/http/impl/conn/LoggingSessionOutputBuffer.java b/src/org/apache/http/impl/conn/LoggingSessionOutputBuffer.java
new file mode 100644
index 0000000..1afab7d
--- /dev/null
+++ b/src/org/apache/http/impl/conn/LoggingSessionOutputBuffer.java
@@ -0,0 +1,109 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/LoggingSessionOutputBuffer.java $
+ * $Revision: 674186 $
+ * $Date: 2008-07-05 05:18:54 -0700 (Sat, 05 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+
+import org.apache.http.io.HttpTransportMetrics;
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Logs all data written to the wire LOG.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public class LoggingSessionOutputBuffer implements SessionOutputBuffer {
+
+    /** Original data transmitter. */
+    private final SessionOutputBuffer out;
+    
+    /** The wire log to use. */
+    private final Wire wire;
+
+    /**
+     * Create an instance that wraps the specified session output buffer.
+     * @param out The session output buffer.
+     * @param wire The Wire log to use.
+     */
+    public LoggingSessionOutputBuffer(final SessionOutputBuffer out, final Wire wire) {
+        super();
+        this.out = out;
+        this.wire = wire;
+    }
+    
+    public void write(byte[] b, int off, int len) throws IOException {
+        this.out.write(b,  off,  len);
+        if (this.wire.enabled()) {
+            this.wire.output(b, off, len);
+        }
+    }
+
+    public void write(int b) throws IOException {
+        this.out.write(b);
+        if (this.wire.enabled()) {
+            this.wire.output(b);
+        }
+    }
+
+    public void write(byte[] b) throws IOException {
+        this.out.write(b);
+        if (this.wire.enabled()) {
+            this.wire.output(b);
+        }
+    }
+
+    public void flush() throws IOException {
+        this.out.flush();
+    }
+
+    public void writeLine(final CharArrayBuffer buffer) throws IOException {
+        this.out.writeLine(buffer);
+        if (this.wire.enabled()) {
+            String s = new String(buffer.buffer(), 0, buffer.length());
+            this.wire.output(s + "[EOL]");
+        }
+    }
+
+    public void writeLine(final String s) throws IOException {
+        this.out.writeLine(s);
+        if (this.wire.enabled()) {
+            this.wire.output(s + "[EOL]");
+        }
+    }
+
+    public HttpTransportMetrics getMetrics() {
+        return this.out.getMetrics();
+    }
+    
+}
diff --git a/src/org/apache/http/impl/conn/ProxySelectorRoutePlanner.java b/src/org/apache/http/impl/conn/ProxySelectorRoutePlanner.java
new file mode 100644
index 0000000..136caf4
--- /dev/null
+++ b/src/org/apache/http/impl/conn/ProxySelectorRoutePlanner.java
@@ -0,0 +1,290 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/ProxySelectorRoutePlanner.java $
+ * $Revision: 658785 $
+ * $Date: 2008-05-21 10:47:40 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.protocol.HttpContext;
+
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+
+import org.apache.http.conn.params.ConnRouteParams;
+
+
+/**
+ * Default implementation of an {@link HttpRoutePlanner}.
+ * This implementation is based on {@link java.net.ProxySelector}.
+ * By default, it will pick up the proxy settings of the JVM, either
+ * from system properties or from the browser running the application.
+ * Additionally, it interprets some
+ * {@link org.apache.http.conn.params.ConnRoutePNames parameters},
+ * though not the {@link
+ * org.apache.http.conn.params.ConnRoutePNames#DEFAULT_PROXY DEFAULT_PROXY}.
+ */
+public class ProxySelectorRoutePlanner implements HttpRoutePlanner {
+    
+    /** The scheme registry. */
+    protected SchemeRegistry schemeRegistry;
+
+    /** The proxy selector to use, or <code>null</code> for system default. */
+    protected ProxySelector proxySelector;
+
+
+    /**
+     * Creates a new proxy selector route planner.
+     *
+     * @param schreg    the scheme registry
+     * @param prosel    the proxy selector, or
+     *                  <code>null</code> for the system default
+     */
+    public ProxySelectorRoutePlanner(SchemeRegistry schreg,
+                                     ProxySelector prosel) {
+
+        if (schreg == null) {
+            throw new IllegalArgumentException
+                ("SchemeRegistry must not be null.");
+        }
+        schemeRegistry = schreg;
+        proxySelector  = prosel;
+    }
+
+
+    /**
+     * Obtains the proxy selector to use.
+     *
+     * @return the proxy selector, or <code>null</code> for the system default
+     */
+    public ProxySelector getProxySelector() {
+        return this.proxySelector;
+    }
+
+
+    /**
+     * Sets the proxy selector to use.
+     *
+     * @param prosel    the proxy selector, or
+     *                  <code>null</code> to use the system default
+     */
+    public void setProxySelector(ProxySelector prosel) {
+        this.proxySelector = prosel;
+    }
+
+
+
+    // non-javadoc, see interface HttpRoutePlanner
+    public HttpRoute determineRoute(HttpHost target,
+                                    HttpRequest request,
+                                    HttpContext context)
+        throws HttpException {
+
+        if (request == null) {
+            throw new IllegalStateException
+                ("Request must not be null.");
+        }
+
+        // If we have a forced route, we can do without a target.
+        HttpRoute route =
+            ConnRouteParams.getForcedRoute(request.getParams());
+        if (route != null)
+            return route;
+
+        // If we get here, there is no forced route.
+        // So we need a target to compute a route.
+
+        if (target == null) {
+            throw new IllegalStateException
+                ("Target host must not be null.");
+        }
+
+        final InetAddress local =
+            ConnRouteParams.getLocalAddress(request.getParams());
+        final HttpHost proxy = determineProxy(target, request, context);
+
+        final Scheme schm =
+            this.schemeRegistry.getScheme(target.getSchemeName());
+        // as it is typically used for TLS/SSL, we assume that
+        // a layered scheme implies a secure connection
+        final boolean secure = schm.isLayered();
+
+        if (proxy == null) {
+            route = new HttpRoute(target, local, secure);
+        } else {
+            route = new HttpRoute(target, local, proxy, secure);
+        }
+        return route;
+    }
+
+
+    /**
+     * Determines a proxy for the given target.
+     *
+     * @param target    the planned target, never <code>null</code>
+     * @param request   the request to be sent, never <code>null</code>
+     * @param context   the context, or <code>null</code>
+     *
+     * @return  the proxy to use, or <code>null</code> for a direct route
+     *
+     * @throws HttpException
+     *         in case of system proxy settings that cannot be handled
+     */
+    protected HttpHost determineProxy(HttpHost    target,
+                                      HttpRequest request,
+                                      HttpContext context)
+        throws HttpException {
+
+        // the proxy selector can be 'unset', so we better deal with null here
+        ProxySelector psel = this.proxySelector;
+        if (psel == null)
+            psel = ProxySelector.getDefault();
+        if (psel == null)
+            return null;
+
+        URI targetURI = null;
+        try {
+            targetURI = new URI(target.toURI());
+        } catch (URISyntaxException usx) {
+            throw new HttpException
+                ("Cannot convert host to URI: " + target, usx);
+        }
+        List<Proxy> proxies = psel.select(targetURI);
+
+        Proxy p = chooseProxy(proxies, target, request, context);
+
+        HttpHost result = null;
+        if (p.type() == Proxy.Type.HTTP) {
+            // convert the socket address to an HttpHost
+            if (!(p.address() instanceof InetSocketAddress)) {
+                throw new HttpException
+                    ("Unable to handle non-Inet proxy address: "+p.address());
+            }
+            final InetSocketAddress isa = (InetSocketAddress) p.address();
+            // assume default scheme (http)
+            result = new HttpHost(getHost(isa), isa.getPort());
+        }
+
+        return result;
+    }
+
+
+    /**
+     * Obtains a host from an {@link InetSocketAddress}.
+     *
+     * @param isa       the socket address
+     *
+     * @return  a host string, either as a symbolic name or
+     *          as a literal IP address string
+     * <br/>
+     * (TODO: determine format for IPv6 addresses, with or without [brackets])
+     */
+    protected String getHost(InetSocketAddress isa) {
+
+        //@@@ Will this work with literal IPv6 addresses, or do we
+        //@@@ need to wrap these in [] for the string representation?
+        //@@@ Having it in this method at least allows for easy workarounds.
+       return isa.isUnresolved() ?
+            isa.getHostName() : isa.getAddress().getHostAddress();
+ 
+    }
+
+
+    /*
+     * Chooses a proxy from a list of available proxies.
+     * The default implementation just picks the first non-SOCKS proxy
+     * from the list. If there are only SOCKS proxies,
+     * {@link Proxy#NO_PROXY Proxy.NO_PROXY} is returned.
+     * Derived classes may implement more advanced strategies,
+     * such as proxy rotation if there are multiple options.
+     *
+     * @param proxies   the list of proxies to choose from,
+     *                  never <code>null</code> or empty
+     * @param target    the planned target, never <code>null</code>
+     * @param request   the request to be sent, never <code>null</code>
+     * @param context   the context, or <code>null</code>
+     *
+     * @return  a proxy of type {@link Proxy.Type#DIRECT DIRECT}
+     *          or {@link Proxy.Type#HTTP HTTP}, never <code>null</code>
+     */
+    protected Proxy chooseProxy(List<Proxy> proxies,
+                                HttpHost    target,
+                                HttpRequest request,
+                                HttpContext context) {
+
+        if ((proxies == null) || proxies.isEmpty()) {
+            throw new IllegalArgumentException
+                ("Proxy list must not be empty.");
+        }
+
+        Proxy result = null;
+
+        // check the list for one we can use
+        for (int i=0; (result == null) && (i < proxies.size()); i++) {
+
+            Proxy p = proxies.get(i);
+            switch (p.type()) {
+
+            case DIRECT:
+            case HTTP:
+                result = p;
+                break;
+
+            case SOCKS:
+                // SOCKS hosts are not handled on the route level.
+                // The socket may make use of the SOCKS host though.
+                break;
+            }
+        }
+
+        if (result == null) {
+            //@@@ log as warning or info that only a socks proxy is available?
+            // result can only be null if all proxies are socks proxies
+            // socks proxies are not handled on the route planning level
+            result = Proxy.NO_PROXY;
+        }
+
+        return result;
+    }
+
+} // class ProxySelectorRoutePlanner
+
diff --git a/src/org/apache/http/impl/conn/SingleClientConnManager.java b/src/org/apache/http/impl/conn/SingleClientConnManager.java
new file mode 100644
index 0000000..7999f3e
--- /dev/null
+++ b/src/org/apache/http/impl/conn/SingleClientConnManager.java
@@ -0,0 +1,444 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/SingleClientConnManager.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.ClientConnectionRequest;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.RouteTracker;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.params.HttpParams;
+
+
+/**
+ * A connection "manager" for a single connection.
+ * This manager is good only for single-threaded use.
+ * Allocation <i>always</i> returns the connection immediately,
+ * even if it has not been released after the previous allocation.
+ * In that case, a {@link #MISUSE_MESSAGE warning} is logged
+ * and the previously issued connection is revoked.
+ * <p>
+ * This class is derived from <code>SimpleHttpConnectionManager</code>
+ * in HttpClient 3. See there for original authors.
+ * </p>
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version   $Revision: 673450 $
+ *
+ * @since 4.0
+ */
+public class SingleClientConnManager implements ClientConnectionManager {
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    /** The message to be logged on multiple allocation. */
+    public final static String MISUSE_MESSAGE =
+    "Invalid use of SingleClientConnManager: connection still allocated.\n" +
+    "Make sure to release the connection before allocating another one.";
+
+
+    /** The schemes supported by this connection manager. */
+    protected SchemeRegistry schemeRegistry; 
+    
+    /** The operator for opening and updating connections. */
+    protected ClientConnectionOperator connOperator;
+
+    /** The one and only entry in this pool. */
+    protected PoolEntry uniquePoolEntry;
+
+    /** The currently issued managed connection, if any. */
+    protected ConnAdapter managedConn;
+
+    /** The time of the last connection release, or -1. */
+    protected long lastReleaseTime;
+    
+    /** The time the last released connection expires and shouldn't be reused. */
+    protected long connectionExpiresTime;
+
+    /** Whether the connection should be shut down  on release. */
+    protected boolean alwaysShutDown;
+
+    /** Indicates whether this connection manager is shut down. */
+    protected volatile boolean isShutDown;
+
+
+
+
+    /**
+     * Creates a new simple connection manager.
+     *
+     * @param params    the parameters for this manager
+     * @param schreg    the scheme registry, or
+     *                  <code>null</code> for the default registry
+     */
+    public SingleClientConnManager(HttpParams params,
+                                   SchemeRegistry schreg) {
+
+        if (schreg == null) {
+            throw new IllegalArgumentException
+                ("Scheme registry must not be null.");
+        }
+        this.schemeRegistry  = schreg;
+        this.connOperator    = createConnectionOperator(schreg);
+        this.uniquePoolEntry = new PoolEntry();
+        this.managedConn     = null;
+        this.lastReleaseTime = -1L;
+        this.alwaysShutDown  = false; //@@@ from params? as argument?
+        this.isShutDown      = false;
+
+    } // <constructor>
+
+
+    @Override
+    protected void finalize() throws Throwable {
+        shutdown();
+        super.finalize();
+    }
+
+
+    // non-javadoc, see interface ClientConnectionManager
+    public SchemeRegistry getSchemeRegistry() {
+        return this.schemeRegistry;
+    }
+
+    
+    /**
+     * Hook for creating the connection operator.
+     * It is called by the constructor.
+     * Derived classes can override this method to change the
+     * instantiation of the operator.
+     * The default implementation here instantiates
+     * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}.
+     *
+     * @param schreg    the scheme registry to use, or <code>null</code>
+     *
+     * @return  the connection operator to use
+     */
+    protected ClientConnectionOperator
+        createConnectionOperator(SchemeRegistry schreg) {
+
+        return new DefaultClientConnectionOperator(schreg);
+    }
+
+
+    /**
+     * Asserts that this manager is not shut down.
+     *
+     * @throws IllegalStateException    if this manager is shut down
+     */
+    protected final void assertStillUp()
+        throws IllegalStateException {
+
+        if (this.isShutDown)
+            throw new IllegalStateException("Manager is shut down.");
+    }
+
+
+    public final ClientConnectionRequest requestConnection(
+            final HttpRoute route,
+            final Object state) {
+        
+        return new ClientConnectionRequest() {
+            
+            public void abortRequest() {
+                // Nothing to abort, since requests are immediate.
+            }
+            
+            public ManagedClientConnection getConnection(
+                    long timeout, TimeUnit tunit) {
+                return SingleClientConnManager.this.getConnection(
+                        route, state);
+            }
+            
+        };
+    }
+
+
+    /**
+     * Obtains a connection.
+     * This method does not block.
+     *
+     * @param route     where the connection should point to
+     *
+     * @return  a connection that can be used to communicate
+     *          along the given route
+     */
+    public ManagedClientConnection getConnection(HttpRoute route, Object state) {
+
+        if (route == null) {
+            throw new IllegalArgumentException("Route may not be null.");
+        }
+        assertStillUp();
+
+        if (log.isDebugEnabled()) {
+            log.debug("Get connection for route " + route);
+        }
+
+        if (managedConn != null)
+            revokeConnection();
+
+        // check re-usability of the connection
+        boolean recreate = false;
+        boolean shutdown = false;
+        
+        // Kill the connection if it expired.
+        closeExpiredConnections();
+        
+        if (uniquePoolEntry.connection.isOpen()) {
+            RouteTracker tracker = uniquePoolEntry.tracker;
+            shutdown = (tracker == null || // can happen if method is aborted
+                        !tracker.toRoute().equals(route));
+        } else {
+            // If the connection is not open, create a new PoolEntry,
+            // as the connection may have been marked not reusable,
+            // due to aborts -- and the PoolEntry should not be reused
+            // either.  There's no harm in recreating an entry if
+            // the connection is closed.
+            recreate = true;
+        }
+
+        if (shutdown) {
+            recreate = true;
+            try {
+                uniquePoolEntry.shutdown();
+            } catch (IOException iox) {
+                log.debug("Problem shutting down connection.", iox);
+            }
+        }
+        
+        if (recreate)
+            uniquePoolEntry = new PoolEntry();
+
+        managedConn = new ConnAdapter(uniquePoolEntry, route);
+
+        return managedConn;
+    }
+
+
+    // non-javadoc, see interface ClientConnectionManager
+    public void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit) {
+        assertStillUp();
+
+        if (!(conn instanceof ConnAdapter)) {
+            throw new IllegalArgumentException
+                ("Connection class mismatch, " +
+                 "connection not obtained from this manager.");
+        }
+        
+        if (log.isDebugEnabled()) {
+            log.debug("Releasing connection " + conn);
+        }
+
+        ConnAdapter sca = (ConnAdapter) conn;
+        if (sca.poolEntry == null)
+            return; // already released
+        ClientConnectionManager manager = sca.getManager();
+        if (manager != null && manager != this) {
+            throw new IllegalArgumentException
+                ("Connection not obtained from this manager.");
+        }
+
+        try {
+            // make sure that the response has been read completely
+            if (sca.isOpen() && (this.alwaysShutDown ||
+                                 !sca.isMarkedReusable())
+                ) {
+                if (log.isDebugEnabled()) {
+                    log.debug
+                        ("Released connection open but not reusable.");
+                }
+
+                // make sure this connection will not be re-used
+                // we might have gotten here because of a shutdown trigger
+                // shutdown of the adapter also clears the tracked route
+                sca.shutdown();
+            }
+        } catch (IOException iox) {
+            //@@@ log as warning? let pass?
+            if (log.isDebugEnabled())
+                log.debug("Exception shutting down released connection.",
+                          iox);
+        } finally {
+            sca.detach();
+            managedConn = null;
+            lastReleaseTime = System.currentTimeMillis();
+            if(validDuration > 0)
+                connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime;
+            else
+                connectionExpiresTime = Long.MAX_VALUE;
+        }
+    } // releaseConnection
+    
+    public void closeExpiredConnections() {
+        if(System.currentTimeMillis() >= connectionExpiresTime) {
+            closeIdleConnections(0, TimeUnit.MILLISECONDS);
+        }
+    }
+
+
+    // non-javadoc, see interface ClientConnectionManager
+    public void closeIdleConnections(long idletime, TimeUnit tunit) {
+        assertStillUp();
+
+        // idletime can be 0 or negative, no problem there
+        if (tunit == null) {
+            throw new IllegalArgumentException("Time unit must not be null.");
+        }
+
+        if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) {
+            final long cutoff =
+                System.currentTimeMillis() - tunit.toMillis(idletime);
+            if (lastReleaseTime <= cutoff) {
+                try {
+                    uniquePoolEntry.close();
+                } catch (IOException iox) {
+                    // ignore
+                    log.debug("Problem closing idle connection.", iox);
+                }
+            }
+        }
+    }
+
+
+    // non-javadoc, see interface ClientConnectionManager
+    public void shutdown() {
+
+        this.isShutDown = true;
+
+        if (managedConn != null)
+            managedConn.detach();
+
+        try {
+            if (uniquePoolEntry != null) // and connection open?
+                uniquePoolEntry.shutdown();
+        } catch (IOException iox) {
+            // ignore
+            log.debug("Problem while shutting down manager.", iox);
+        } finally {
+            uniquePoolEntry = null;
+        }
+    }
+
+
+    /**
+     * Revokes the currently issued connection.
+     * The adapter gets disconnected, the connection will be shut down.
+     */
+    protected void revokeConnection() {
+        if (managedConn == null)
+            return;
+
+        log.warn(MISUSE_MESSAGE);
+
+        managedConn.detach();
+
+        try {
+            uniquePoolEntry.shutdown();
+        } catch (IOException iox) {
+            // ignore
+            log.debug("Problem while shutting down connection.", iox);
+        }
+    }
+
+    
+    /**
+     * The pool entry for this connection manager.
+     */
+    protected class PoolEntry extends AbstractPoolEntry {
+
+        /**
+         * Creates a new pool entry.
+         *
+         */
+        protected PoolEntry() {
+            super(SingleClientConnManager.this.connOperator, null);
+        }
+
+        /**
+         * Closes the connection in this pool entry.
+         */
+        protected void close()
+            throws IOException {
+
+            shutdownEntry();
+            if (connection.isOpen())
+                connection.close();
+        }
+
+
+        /**
+         * Shuts down the connection in this pool entry.
+         */
+        protected void shutdown()
+            throws IOException {
+
+            shutdownEntry();
+            if (connection.isOpen())
+                connection.shutdown();
+        }
+
+    } // class PoolEntry
+
+
+
+    /**
+     * The connection adapter used by this manager.
+     */
+    protected class ConnAdapter extends AbstractPooledConnAdapter {
+
+        /**
+         * Creates a new connection adapter.
+         *
+         * @param entry   the pool entry for the connection being wrapped
+         * @param route   the planned route for this connection
+         */
+        protected ConnAdapter(PoolEntry entry, HttpRoute route) {
+            super(SingleClientConnManager.this, entry);
+            markReusable();
+            entry.route = route;
+        }
+
+    }
+
+
+} // class SingleClientConnManager
diff --git a/src/org/apache/http/impl/conn/Wire.java b/src/org/apache/http/impl/conn/Wire.java
new file mode 100644
index 0000000..147b7f5
--- /dev/null
+++ b/src/org/apache/http/impl/conn/Wire.java
@@ -0,0 +1,160 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/Wire.java,v 1.9 2004/06/24 21:39:52 mbecke Exp $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ByteArrayInputStream;
+import org.apache.commons.logging.Log;
+
+/**
+ * Logs data to the wire LOG.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public class Wire {
+
+    private final Log log;
+    
+    public Wire(Log log) {
+        this.log = log;
+    }
+    
+    private void wire(String header, InputStream instream)
+      throws IOException {
+        StringBuilder buffer = new StringBuilder();
+        int ch;
+        while ((ch = instream.read()) != -1) {
+            if (ch == 13) {
+                buffer.append("[\\r]");
+            } else if (ch == 10) {
+                    buffer.append("[\\n]\"");
+                    buffer.insert(0, "\"");
+                    buffer.insert(0, header);
+                    log.debug(buffer.toString());
+                    buffer.setLength(0);
+            } else if ((ch < 32) || (ch > 127)) {
+                buffer.append("[0x");
+                buffer.append(Integer.toHexString(ch));
+                buffer.append("]");
+            } else {
+                buffer.append((char) ch);
+            }
+        } 
+        if (buffer.length() > 0) {
+            buffer.append('\"');
+            buffer.insert(0, '\"');
+            buffer.insert(0, header);
+            log.debug(buffer.toString());
+        }
+    }
+
+
+    public boolean enabled() {
+        return log.isDebugEnabled();
+    }    
+    
+    public void output(InputStream outstream)
+      throws IOException {
+        if (outstream == null) {
+            throw new IllegalArgumentException("Output may not be null"); 
+        }
+        wire(">> ", outstream);
+    }
+
+    public void input(InputStream instream)
+      throws IOException {
+        if (instream == null) {
+            throw new IllegalArgumentException("Input may not be null"); 
+        }
+        wire("<< ", instream);
+    }
+
+    public void output(byte[] b, int off, int len)
+      throws IOException {
+        if (b == null) {
+            throw new IllegalArgumentException("Output may not be null"); 
+        }
+        wire(">> ", new ByteArrayInputStream(b, off, len));
+    }
+
+    public void input(byte[] b, int off, int len)
+      throws IOException {
+        if (b == null) {
+            throw new IllegalArgumentException("Input may not be null"); 
+        }
+        wire("<< ", new ByteArrayInputStream(b, off, len));
+    }
+
+    public void output(byte[] b)
+      throws IOException {
+        if (b == null) {
+            throw new IllegalArgumentException("Output may not be null"); 
+        }
+        wire(">> ", new ByteArrayInputStream(b));
+    }
+
+    public void input(byte[] b)
+      throws IOException {
+        if (b == null) {
+            throw new IllegalArgumentException("Input may not be null"); 
+        }
+        wire("<< ", new ByteArrayInputStream(b));
+    }
+
+    public void output(int b)
+      throws IOException {
+        output(new byte[] {(byte) b});
+    }
+
+    public void input(int b)
+      throws IOException {
+        input(new byte[] {(byte) b});
+    }
+
+    public void output(final String s)
+      throws IOException {
+        if (s == null) {
+            throw new IllegalArgumentException("Output may not be null"); 
+        }
+        output(s.getBytes());
+    }
+
+    public void input(final String s)
+      throws IOException {
+        if (s == null) {
+            throw new IllegalArgumentException("Input may not be null"); 
+        }
+        input(s.getBytes());
+    }
+}
diff --git a/src/org/apache/http/impl/conn/package.html b/src/org/apache/http/impl/conn/package.html
new file mode 100644
index 0000000..54eb3c2
--- /dev/null
+++ b/src/org/apache/http/impl/conn/package.html
@@ -0,0 +1,5 @@
+<body>
+
+
+</body>
+
diff --git a/src/org/apache/http/impl/conn/tsccm/AbstractConnPool.java b/src/org/apache/http/impl/conn/tsccm/AbstractConnPool.java
new file mode 100644
index 0000000..2b37d72
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/AbstractConnPool.java
@@ -0,0 +1,332 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/AbstractConnPool.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+import java.io.IOException;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.conn.ConnectionPoolTimeoutException;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.impl.conn.IdleConnectionHandler;
+
+
+/**
+ * An abstract connection pool.
+ * It is used by the {@link ThreadSafeClientConnManager}.
+ * The abstract pool includes a {@link #poolLock}, which is used to
+ * synchronize access to the internal pool datastructures.
+ * Don't use <code>synchronized</code> for that purpose!
+ */
+public abstract class AbstractConnPool implements RefQueueHandler {
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    /**
+     * The global lock for this pool.
+     */
+    protected final Lock poolLock;
+
+
+    /**
+     * References to issued connections.
+     * Objects in this set are of class
+     * {@link BasicPoolEntryRef BasicPoolEntryRef},
+     * and point to the pool entry for the issued connection.
+     * GCed connections are detected by the missing pool entries.
+     */
+    protected Set<BasicPoolEntryRef> issuedConnections;
+
+    /** The handler for idle connections. */
+    protected IdleConnectionHandler idleConnHandler;
+
+    /** The current total number of connections. */
+    protected int numConnections;
+
+    /**
+     * A reference queue to track loss of pool entries to GC.
+     * The same queue is used to track loss of the connection manager,
+     * so we cannot specialize the type.
+     */
+    protected ReferenceQueue<Object> refQueue;
+
+    /** A worker (thread) to track loss of pool entries to GC. */
+    private RefQueueWorker refWorker;
+
+
+    /** Indicates whether this pool is shut down. */
+    protected volatile boolean isShutDown;
+
+    /**
+     * Creates a new connection pool.
+     */
+    protected AbstractConnPool() {
+        issuedConnections = new HashSet<BasicPoolEntryRef>();
+        idleConnHandler = new IdleConnectionHandler();
+
+        boolean fair = false; //@@@ check parameters to decide
+        poolLock = new ReentrantLock(fair);
+    }
+
+
+    /**
+     * Enables connection garbage collection (GC).
+     * This method must be called immediately after creating the
+     * connection pool. It is not possible to enable connection GC
+     * after pool entries have been created. Neither is it possible
+     * to disable connection GC.
+     *
+     * @throws IllegalStateException
+     *         if connection GC is already enabled, or if it cannot be
+     *         enabled because there already are pool entries
+     */
+    public void enableConnectionGC()
+        throws IllegalStateException {
+
+        if (refQueue != null) {
+            throw new IllegalStateException("Connection GC already enabled.");
+        }
+        poolLock.lock();
+        try {
+            if (numConnections > 0) { //@@@ is this check sufficient?
+                throw new IllegalStateException("Pool already in use.");
+            }
+        } finally {
+            poolLock.unlock();
+        }
+
+        refQueue  = new ReferenceQueue<Object>();
+        refWorker = new RefQueueWorker(refQueue, this);
+        Thread t = new Thread(refWorker); //@@@ use a thread factory
+        t.setDaemon(true);
+        t.setName("RefQueueWorker@" + this);
+        t.start();
+    }
+
+
+    /**
+     * Obtains a pool entry with a connection within the given timeout.
+     *
+     * @param route     the route for which to get the connection
+     * @param timeout   the timeout, 0 or negative for no timeout
+     * @param tunit     the unit for the <code>timeout</code>,
+     *                  may be <code>null</code> only if there is no timeout
+     *
+     * @return  pool entry holding a connection for the route
+     *
+     * @throws ConnectionPoolTimeoutException
+     *         if the timeout expired
+     * @throws InterruptedException
+     *         if the calling thread was interrupted
+     */
+    public final
+        BasicPoolEntry getEntry(
+                HttpRoute route, 
+                Object state,
+                long timeout, 
+                TimeUnit tunit)
+                    throws ConnectionPoolTimeoutException, InterruptedException {
+        return requestPoolEntry(route, state).getPoolEntry(timeout, tunit);
+    }
+    
+    /**
+     * Returns a new {@link PoolEntryRequest}, from which a {@link BasicPoolEntry}
+     * can be obtained, or the request can be aborted.
+     */
+    public abstract PoolEntryRequest requestPoolEntry(HttpRoute route, Object state);
+
+
+    /**
+     * Returns an entry into the pool.
+     * The connection of the entry is expected to be in a suitable state,
+     * either open and re-usable, or closed. The pool will not make any
+     * attempt to determine whether it can be re-used or not.
+     *
+     * @param entry     the entry for the connection to release
+     * @param reusable  <code>true</code> if the entry is deemed 
+     *                  reusable, <code>false</code> otherwise.
+     * @param validDuration The duration that the entry should remain free and reusable.
+     * @param timeUnit The unit of time the duration is measured in.
+     */
+    public abstract void freeEntry(BasicPoolEntry entry, boolean reusable, long validDuration, TimeUnit timeUnit)
+        ;
+
+
+
+    // non-javadoc, see interface RefQueueHandler
+// BEGIN android-changed
+    public void handleReference(Reference ref) {
+// END android-changed
+        poolLock.lock();
+        try {
+
+            if (ref instanceof BasicPoolEntryRef) {
+                // check if the GCed pool entry was still in use
+                //@@@ find a way to detect this without lookup
+                //@@@ flag in the BasicPoolEntryRef, to be reset when freed?
+                final boolean lost = issuedConnections.remove(ref);
+                if (lost) {
+                    final HttpRoute route =
+                        ((BasicPoolEntryRef)ref).getRoute();
+                    if (log.isDebugEnabled()) {
+                        log.debug("Connection garbage collected. " + route);
+                    }
+                    handleLostEntry(route);
+                }
+            }
+
+        } finally {
+            poolLock.unlock();
+        }
+    }
+
+
+    /**
+     * Handles cleaning up for a lost pool entry with the given route.
+     * A lost pool entry corresponds to a connection that was
+     * garbage collected instead of being properly released.
+     *
+     * @param route     the route of the pool entry that was lost
+     */
+    protected abstract void handleLostEntry(HttpRoute route)
+        ;
+
+
+    /**
+     * Closes idle connections.
+     *
+     * @param idletime  the time the connections should have been idle
+     *                  in order to be closed now
+     * @param tunit     the unit for the <code>idletime</code>
+     */
+    public void closeIdleConnections(long idletime, TimeUnit tunit) {
+
+        // idletime can be 0 or negative, no problem there
+        if (tunit == null) {
+            throw new IllegalArgumentException("Time unit must not be null.");
+        }
+
+        poolLock.lock();
+        try {
+            idleConnHandler.closeIdleConnections(tunit.toMillis(idletime));
+        } finally {
+            poolLock.unlock();
+        }
+    }
+    
+    public void closeExpiredConnections() {
+        poolLock.lock();
+        try {
+            idleConnHandler.closeExpiredConnections();
+        } finally {
+            poolLock.unlock();
+        }
+    }
+
+        
+    //@@@ revise this cleanup stuff (closeIdle+deleteClosed), it's not good
+
+    /**
+     * Deletes all entries for closed connections.
+     */
+    public abstract void deleteClosedConnections()
+        ;
+
+
+    /**
+     * Shuts down this pool and all associated resources.
+     * Overriding methods MUST call the implementation here!
+     */
+    public void shutdown() {
+
+        poolLock.lock();
+        try {
+
+            if (isShutDown)
+                return;
+
+            // no point in monitoring GC anymore
+            if (refWorker != null)
+                refWorker.shutdown();
+
+            // close all connections that are issued to an application
+            Iterator<BasicPoolEntryRef> iter = issuedConnections.iterator();
+            while (iter.hasNext()) {
+                BasicPoolEntryRef per = iter.next();
+                iter.remove();
+                BasicPoolEntry entry = per.get();
+                if (entry != null) {
+                    closeConnection(entry.getConnection());
+                }
+            }
+
+            // remove all references to connections
+            //@@@ use this for shutting them down instead?
+            idleConnHandler.removeAll();
+
+            isShutDown = true;
+
+        } finally {
+            poolLock.unlock();
+        }
+    }
+
+
+    /**
+     * Closes a connection from this pool.
+     *
+     * @param conn      the connection to close, or <code>null</code>
+     */
+    protected void closeConnection(final OperatedClientConnection conn) {
+        if (conn != null) {
+            try {
+                conn.close();
+            } catch (IOException ex) {
+                log.debug("I/O error closing connection", ex);
+            }
+        }
+    }
+
+
+
+
+
+} // class AbstractConnPool
+
diff --git a/src/org/apache/http/impl/conn/tsccm/BasicPoolEntry.java b/src/org/apache/http/impl/conn/tsccm/BasicPoolEntry.java
new file mode 100644
index 0000000..dded360
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/BasicPoolEntry.java
@@ -0,0 +1,88 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/BasicPoolEntry.java $
+ * $Revision: 652721 $
+ * $Date: 2008-05-01 17:32:20 -0700 (Thu, 01 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+
+import java.lang.ref.ReferenceQueue;
+
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.impl.conn.AbstractPoolEntry;
+
+
+
+/**
+ * Basic implementation of a connection pool entry.
+ */
+public class BasicPoolEntry extends AbstractPoolEntry {
+
+    /**
+     * A weak reference to <code>this</code> used to detect GC of entries.
+     * Pool entries can only be GCed when they are allocated by an application
+     * and therefore not referenced with a hard link in the manager.
+     */
+    private final BasicPoolEntryRef reference;
+
+    /**
+     * Creates a new pool entry.
+     *
+     * @param op      the connection operator
+     * @param route   the planned route for the connection
+     * @param queue   the reference queue for tracking GC of this entry,
+     *                or <code>null</code>
+     */
+    public BasicPoolEntry(ClientConnectionOperator op,
+                          HttpRoute route,
+                          ReferenceQueue<Object> queue) {
+        super(op, route);
+        if (route == null) {
+            throw new IllegalArgumentException("HTTP route may not be null");
+        }
+        this.reference = new BasicPoolEntryRef(this, queue);
+    }
+
+    protected final OperatedClientConnection getConnection() {
+        return super.connection;
+    }
+
+    protected final HttpRoute getPlannedRoute() {
+        return super.route;
+    }
+
+    protected final BasicPoolEntryRef getWeakRef() {
+        return this.reference;
+    }
+
+
+} // class BasicPoolEntry
+
+
diff --git a/src/org/apache/http/impl/conn/tsccm/BasicPoolEntryRef.java b/src/org/apache/http/impl/conn/tsccm/BasicPoolEntryRef.java
new file mode 100644
index 0000000..32df8a5
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/BasicPoolEntryRef.java
@@ -0,0 +1,80 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/BasicPoolEntryRef.java $
+ * $Revision: 674186 $
+ * $Date: 2008-07-05 05:18:54 -0700 (Sat, 05 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+
+import java.lang.ref.WeakReference;
+import java.lang.ref.ReferenceQueue;
+
+import org.apache.http.conn.routing.HttpRoute;
+
+
+
+/**
+ * A weak reference to a {@link BasicPoolEntry BasicPoolEntry}.
+ * This reference explicitly keeps the planned route, so the connection
+ * can be reclaimed if it is lost to garbage collection.
+ */
+public class BasicPoolEntryRef extends WeakReference<BasicPoolEntry> {
+
+    /** The planned route of the entry. */
+    private final HttpRoute route;
+
+
+    /**
+     * Creates a new reference to a pool entry.
+     *
+     * @param entry   the pool entry, must not be <code>null</code>
+     * @param queue   the reference queue, or <code>null</code>
+     */
+    public BasicPoolEntryRef(BasicPoolEntry entry,
+                             ReferenceQueue<Object> queue) {
+        super(entry, queue);
+        if (entry == null) {
+            throw new IllegalArgumentException
+                ("Pool entry must not be null.");
+        }
+        route = entry.getPlannedRoute();
+    }
+
+
+    /**
+     * Obtain the planned route for the referenced entry.
+     * The planned route is still available, even if the entry is gone.
+     *
+     * @return      the planned route
+     */
+    public final HttpRoute getRoute() {
+        return this.route;
+    }
+
+} // class BasicPoolEntryRef
+
diff --git a/src/org/apache/http/impl/conn/tsccm/BasicPooledConnAdapter.java b/src/org/apache/http/impl/conn/tsccm/BasicPooledConnAdapter.java
new file mode 100644
index 0000000..29455d0
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/BasicPooledConnAdapter.java
@@ -0,0 +1,83 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/BasicPooledConnAdapter.java $
+ * $Revision: 653214 $
+ * $Date: 2008-05-04 07:12:13 -0700 (Sun, 04 May 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.impl.conn.AbstractPoolEntry;
+import org.apache.http.impl.conn.AbstractPooledConnAdapter;
+
+
+
+/**
+ * A connection wrapper and callback handler.
+ * All connections given out by the manager are wrappers which
+ * can be {@link #detach detach}ed to prevent further use on release.
+ */
+public class BasicPooledConnAdapter extends AbstractPooledConnAdapter {
+
+    /**
+     * Creates a new adapter.
+     *
+     * @param tsccm   the connection manager
+     * @param entry   the pool entry for the connection being wrapped
+     */
+    protected BasicPooledConnAdapter(ThreadSafeClientConnManager tsccm,
+                               AbstractPoolEntry entry) {
+        super(tsccm, entry);
+        markReusable();
+    }
+
+
+    @Override
+    protected ClientConnectionManager getManager() {
+        // override needed only to make method visible in this package
+        return super.getManager();
+    }
+
+
+    /**
+     * Obtains the pool entry.
+     *
+     * @return  the pool entry, or <code>null</code> if detached
+     */
+    protected AbstractPoolEntry getPoolEntry() {
+        return super.poolEntry;
+    }
+
+
+    // non-javadoc, see base class
+    @Override
+    protected void detach() {
+        // override needed only to make method visible in this package
+        super.detach();
+    }
+}
diff --git a/src/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java b/src/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java
new file mode 100644
index 0000000..cf59129
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java
@@ -0,0 +1,698 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java $
+ * $Revision: 677240 $
+ * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Queue;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.ConnectionPoolTimeoutException;
+import org.apache.http.conn.params.ConnPerRoute;
+import org.apache.http.conn.params.ConnManagerParams;
+import org.apache.http.params.HttpParams;
+
+
+/**
+ * A connection pool that maintains connections by route.
+ * This class is derived from <code>MultiThreadedHttpConnectionManager</code>
+ * in HttpClient 3.x, see there for original authors. It implements the same
+ * algorithm for connection re-use and connection-per-host enforcement:
+ * <ul>
+ * <li>connections are re-used only for the exact same route</li>
+ * <li>connection limits are enforced per route rather than per host</li>
+ * </ul>
+ * Note that access to the pool datastructures is synchronized via the
+ * {@link AbstractConnPool#poolLock poolLock} in the base class,
+ * not via <code>synchronized</code> methods.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ * @author and others
+ */
+public class ConnPoolByRoute extends AbstractConnPool {
+        
+    private final Log log = LogFactory.getLog(getClass());
+
+    /** Connection operator for this pool */
+    protected final ClientConnectionOperator operator;
+    
+    /** The list of free connections */
+    protected Queue<BasicPoolEntry> freeConnections;
+
+    /** The list of WaitingThreads waiting for a connection */
+    protected Queue<WaitingThread> waitingThreads;
+
+    /**
+     * A map of route-specific pools.
+     * Keys are of class {@link HttpRoute},
+     * values of class {@link RouteSpecificPool}.
+     */
+    protected final Map<HttpRoute, RouteSpecificPool> routeToPool;
+
+    protected final int maxTotalConnections;
+    
+    private final ConnPerRoute connPerRoute;
+    
+    /**
+     * Creates a new connection pool, managed by route.
+     */
+    public ConnPoolByRoute(final ClientConnectionOperator operator, final HttpParams params) {
+        super();
+        if (operator == null) {
+            throw new IllegalArgumentException("Connection operator may not be null");
+        }
+        this.operator = operator;
+        
+        freeConnections = createFreeConnQueue();
+        waitingThreads  = createWaitingThreadQueue();
+        routeToPool     = createRouteToPoolMap();
+        maxTotalConnections = ConnManagerParams
+            .getMaxTotalConnections(params);
+        connPerRoute = ConnManagerParams
+            .getMaxConnectionsPerRoute(params);
+    }
+
+
+    /**
+     * Creates the queue for {@link #freeConnections}.
+     * Called once by the constructor.
+     *
+     * @return  a queue
+     */
+    protected Queue<BasicPoolEntry> createFreeConnQueue() {
+        return new LinkedList<BasicPoolEntry>();
+    }
+
+    /**
+     * Creates the queue for {@link #waitingThreads}.
+     * Called once by the constructor.
+     *
+     * @return  a queue
+     */
+    protected Queue<WaitingThread> createWaitingThreadQueue() {
+        return new LinkedList<WaitingThread>();
+    }
+
+    /**
+     * Creates the map for {@link #routeToPool}.
+     * Called once by the constructor.
+     *
+     * @return  a map
+     */
+    protected Map<HttpRoute, RouteSpecificPool> createRouteToPoolMap() {
+        return new HashMap<HttpRoute, RouteSpecificPool>();
+    }
+
+
+    /**
+     * Creates a new route-specific pool.
+     * Called by {@link #getRoutePool} when necessary.
+     *
+     * @param route     the route
+     *
+     * @return  the new pool
+     */
+    protected RouteSpecificPool newRouteSpecificPool(HttpRoute route) {
+        return new RouteSpecificPool(route, connPerRoute.getMaxForRoute(route));
+    }
+
+
+    /**
+     * Creates a new waiting thread.
+     * Called by {@link #getRoutePool} when necessary.
+     *
+     * @param cond      the condition to wait for
+     * @param rospl     the route specific pool, or <code>null</code>
+     *
+     * @return  a waiting thread representation
+     */
+    protected WaitingThread newWaitingThread(Condition cond,
+                                             RouteSpecificPool rospl) {
+        return new WaitingThread(cond, rospl);
+    }
+
+
+    /**
+     * Get a route-specific pool of available connections.
+     *
+     * @param route   the route
+     * @param create    whether to create the pool if it doesn't exist
+     *
+     * @return  the pool for the argument route,
+     *     never <code>null</code> if <code>create</code> is <code>true</code>
+     */
+    protected RouteSpecificPool getRoutePool(HttpRoute route,
+                                             boolean create) {
+        RouteSpecificPool rospl = null;
+        poolLock.lock();
+        try {
+
+            rospl = routeToPool.get(route);
+            if ((rospl == null) && create) {
+                // no pool for this route yet (or anymore)
+                rospl = newRouteSpecificPool(route);
+                routeToPool.put(route, rospl);
+            }
+
+        } finally {
+            poolLock.unlock();
+        }
+
+        return rospl;
+    }
+
+
+    //@@@ consider alternatives for gathering statistics
+    public int getConnectionsInPool(HttpRoute route) {
+
+        poolLock.lock();
+        try {
+            // don't allow a pool to be created here!
+            RouteSpecificPool rospl = getRoutePool(route, false);
+            return (rospl != null) ? rospl.getEntryCount() : 0;
+
+        } finally {
+            poolLock.unlock();
+        }
+    }
+    
+    @Override
+    public PoolEntryRequest requestPoolEntry(
+            final HttpRoute route,
+            final Object state) {
+        
+        final WaitingThreadAborter aborter = new WaitingThreadAborter();
+        
+        return new PoolEntryRequest() {
+        
+            public void abortRequest() {
+                poolLock.lock();
+                try {
+                    aborter.abort();
+                } finally {
+                    poolLock.unlock();
+                }
+            }
+            
+            public BasicPoolEntry getPoolEntry(
+                    long timeout,
+                    TimeUnit tunit)
+                        throws InterruptedException, ConnectionPoolTimeoutException {
+                return getEntryBlocking(route, state, timeout, tunit, aborter);
+            }
+            
+        };
+    }
+
+    /**
+     * Obtains a pool entry with a connection within the given timeout.
+     * If a {@link WaitingThread} is used to block, {@link WaitingThreadAborter#setWaitingThread(WaitingThread)}
+     * must be called before blocking, to allow the thread to be interrupted.
+     *
+     * @param route     the route for which to get the connection
+     * @param timeout   the timeout, 0 or negative for no timeout
+     * @param tunit     the unit for the <code>timeout</code>,
+     *                  may be <code>null</code> only if there is no timeout
+     * @param aborter   an object which can abort a {@link WaitingThread}.
+     *
+     * @return  pool entry holding a connection for the route
+     *
+     * @throws ConnectionPoolTimeoutException
+     *         if the timeout expired
+     * @throws InterruptedException
+     *         if the calling thread was interrupted
+     */
+    protected BasicPoolEntry getEntryBlocking(
+                                   HttpRoute route, Object state,
+                                   long timeout, TimeUnit tunit,
+                                   WaitingThreadAborter aborter)
+        throws ConnectionPoolTimeoutException, InterruptedException {
+
+        Date deadline = null;
+        if (timeout > 0) {
+            deadline = new Date
+                (System.currentTimeMillis() + tunit.toMillis(timeout));
+        }
+
+        BasicPoolEntry entry = null;
+        poolLock.lock();
+        try {
+
+            RouteSpecificPool rospl = getRoutePool(route, true);
+            WaitingThread waitingThread = null;
+
+            while (entry == null) {
+
+                if (isShutDown) {
+                    throw new IllegalStateException
+                        ("Connection pool shut down.");
+                }
+
+                if (log.isDebugEnabled()) {
+                    log.debug("Total connections kept alive: " + freeConnections.size()); 
+                    log.debug("Total issued connections: " + issuedConnections.size()); 
+                    log.debug("Total allocated connection: " + numConnections + " out of " + maxTotalConnections);
+                }
+                
+                // the cases to check for:
+                // - have a free connection for that route
+                // - allowed to create a free connection for that route
+                // - can delete and replace a free connection for another route
+                // - need to wait for one of the things above to come true
+
+                entry = getFreeEntry(rospl, state);
+                if (entry != null) {
+                    break;
+                }
+                
+                boolean hasCapacity = rospl.getCapacity() > 0; 
+                
+                if (log.isDebugEnabled()) {
+                    log.debug("Available capacity: " + rospl.getCapacity() 
+                            + " out of " + rospl.getMaxEntries()
+                            + " [" + route + "][" + state + "]");
+                }
+                
+                if (hasCapacity && numConnections < maxTotalConnections) {
+
+                    entry = createEntry(rospl, operator);
+
+                } else if (hasCapacity && !freeConnections.isEmpty()) {
+
+                    deleteLeastUsedEntry();
+                    entry = createEntry(rospl, operator);
+
+                } else {
+
+                    if (log.isDebugEnabled()) {
+                        log.debug("Need to wait for connection" +
+                                " [" + route + "][" + state + "]");
+                    }
+
+                    if (waitingThread == null) {
+                        waitingThread =
+                            newWaitingThread(poolLock.newCondition(), rospl);
+                        aborter.setWaitingThread(waitingThread);
+                    }
+
+                    boolean success = false;
+                    try {
+                        rospl.queueThread(waitingThread);
+                        waitingThreads.add(waitingThread);
+                        success = waitingThread.await(deadline);
+
+                    } finally {
+                        // In case of 'success', we were woken up by the
+                        // connection pool and should now have a connection
+                        // waiting for us, or else we're shutting down.
+                        // Just continue in the loop, both cases are checked.
+                        rospl.removeThread(waitingThread);
+                        waitingThreads.remove(waitingThread);
+                    }
+
+                    // check for spurious wakeup vs. timeout
+                    if (!success && (deadline != null) &&
+                        (deadline.getTime() <= System.currentTimeMillis())) {
+                        throw new ConnectionPoolTimeoutException
+                            ("Timeout waiting for connection");
+                    }
+                }
+            } // while no entry
+
+        } finally {
+            poolLock.unlock();
+        }
+
+        return entry;
+
+    } // getEntry
+
+
+    // non-javadoc, see base class AbstractConnPool
+    @Override
+    public void freeEntry(BasicPoolEntry entry, boolean reusable, long validDuration, TimeUnit timeUnit) {
+
+        HttpRoute route = entry.getPlannedRoute();
+        if (log.isDebugEnabled()) {
+            log.debug("Freeing connection" +                                 
+                    " [" + route + "][" + entry.getState() + "]");
+        }
+
+        poolLock.lock();
+        try {
+            if (isShutDown) {
+                // the pool is shut down, release the
+                // connection's resources and get out of here
+                closeConnection(entry.getConnection());
+                return;
+            }
+
+            // no longer issued, we keep a hard reference now
+            issuedConnections.remove(entry.getWeakRef());
+
+            RouteSpecificPool rospl = getRoutePool(route, true);
+
+            if (reusable) {
+                rospl.freeEntry(entry);
+                freeConnections.add(entry);
+                idleConnHandler.add(entry.getConnection(), validDuration, timeUnit);
+            } else {
+                rospl.dropEntry();
+                numConnections--;
+            }
+
+            notifyWaitingThread(rospl);
+
+        } finally {
+            poolLock.unlock();
+        }
+
+    } // freeEntry
+
+
+
+    /**
+     * If available, get a free pool entry for a route.
+     *
+     * @param rospl       the route-specific pool from which to get an entry
+     *
+     * @return  an available pool entry for the given route, or
+     *          <code>null</code> if none is available
+     */
+    protected BasicPoolEntry getFreeEntry(RouteSpecificPool rospl, Object state) {
+
+        BasicPoolEntry entry = null;
+        poolLock.lock();
+        try {
+            boolean done = false;
+            while(!done) {
+
+                entry = rospl.allocEntry(state);
+    
+                if (entry != null) {
+                    if (log.isDebugEnabled()) {
+                        log.debug("Getting free connection" 
+                                + " [" + rospl.getRoute() + "][" + state + "]");
+    
+                    }
+                    freeConnections.remove(entry);
+                    boolean valid = idleConnHandler.remove(entry.getConnection());
+                    if(!valid) {
+                        // If the free entry isn't valid anymore, get rid of it
+                        // and loop to find another one that might be valid.
+                        if(log.isDebugEnabled())
+                            log.debug("Closing expired free connection"
+                                    + " [" + rospl.getRoute() + "][" + state + "]");
+                        closeConnection(entry.getConnection());
+                        // We use dropEntry instead of deleteEntry because the entry
+                        // is no longer "free" (we just allocated it), and deleteEntry
+                        // can only be used to delete free entries.
+                        rospl.dropEntry();
+                        numConnections--;
+                    } else {
+                        issuedConnections.add(entry.getWeakRef());
+                        done = true;
+                    }
+    
+                } else {
+                    done = true;
+                    if (log.isDebugEnabled()) {
+                        log.debug("No free connections" 
+                                + " [" + rospl.getRoute() + "][" + state + "]");
+                    }
+                }
+            }
+        } finally {
+            poolLock.unlock();
+        }
+
+        return entry;
+    }
+
+
+    /**
+     * Creates a new pool entry.
+     * This method assumes that the new connection will be handed
+     * out immediately.
+     *
+     * @param rospl       the route-specific pool for which to create the entry
+     * @param op        the operator for creating a connection
+     *
+     * @return  the new pool entry for a new connection
+     */
+    protected BasicPoolEntry createEntry(RouteSpecificPool rospl,
+                                         ClientConnectionOperator op) {
+
+        if (log.isDebugEnabled()) {
+            log.debug("Creating new connection [" + rospl.getRoute() + "]");
+        }
+
+        // the entry will create the connection when needed
+        BasicPoolEntry entry =
+            new BasicPoolEntry(op, rospl.getRoute(), refQueue);
+
+        poolLock.lock();
+        try {
+
+            rospl.createdEntry(entry);
+            numConnections++;
+
+            issuedConnections.add(entry.getWeakRef());
+
+        } finally {
+            poolLock.unlock();
+        }
+
+        return entry;
+    }
+
+        
+    /**
+     * Deletes a given pool entry.
+     * This closes the pooled connection and removes all references,
+     * so that it can be GCed.
+     * 
+     * <p><b>Note:</b> Does not remove the entry from the freeConnections list.
+     * It is assumed that the caller has already handled this step.</p>
+     * <!-- @@@ is that a good idea? or rather fix it? -->
+     * 
+     * @param entry         the pool entry for the connection to delete
+     */
+    protected void deleteEntry(BasicPoolEntry entry) {
+
+        HttpRoute route = entry.getPlannedRoute();
+
+        if (log.isDebugEnabled()) {
+            log.debug("Deleting connection" 
+                    + " [" + route + "][" + entry.getState() + "]");
+        }
+
+        poolLock.lock();
+        try {
+
+            closeConnection(entry.getConnection());
+
+            RouteSpecificPool rospl = getRoutePool(route, true);
+            rospl.deleteEntry(entry);
+            numConnections--;
+            if (rospl.isUnused()) {
+                routeToPool.remove(route);
+            }
+
+            idleConnHandler.remove(entry.getConnection());// not idle, but dead
+
+        } finally {
+            poolLock.unlock();
+        }
+    }
+
+
+    /**
+     * Delete an old, free pool entry to make room for a new one.
+     * Used to replace pool entries with ones for a different route.
+     */
+    protected void deleteLeastUsedEntry() {
+
+        try {
+            poolLock.lock();
+
+            //@@@ with get() instead of remove, we could
+            //@@@ leave the removing to deleteEntry()
+            BasicPoolEntry entry = freeConnections.remove();
+
+            if (entry != null) {
+                deleteEntry(entry);
+            } else if (log.isDebugEnabled()) {
+                log.debug("No free connection to delete.");
+            }
+
+        } finally {
+            poolLock.unlock();
+        }
+    }
+
+
+    // non-javadoc, see base class AbstractConnPool
+    @Override
+    protected void handleLostEntry(HttpRoute route) {
+
+        poolLock.lock();
+        try {
+
+            RouteSpecificPool rospl = getRoutePool(route, true);
+            rospl.dropEntry();
+            if (rospl.isUnused()) {
+                routeToPool.remove(route);
+            }
+
+            numConnections--;
+            notifyWaitingThread(rospl);
+
+        } finally {
+            poolLock.unlock();
+        }
+    }
+
+
+    /**
+     * Notifies a waiting thread that a connection is available.
+     * This will wake a thread waiting in the specific route pool,
+     * if there is one.
+     * Otherwise, a thread in the connection pool will be notified.
+     * 
+     * @param rospl     the pool in which to notify, or <code>null</code>
+     */
+    protected void notifyWaitingThread(RouteSpecificPool rospl) {
+
+        //@@@ while this strategy provides for best connection re-use,
+        //@@@ is it fair? only do this if the connection is open?
+        // Find the thread we are going to notify. We want to ensure that
+        // each waiting thread is only interrupted once, so we will remove
+        // it from all wait queues before interrupting.
+        WaitingThread waitingThread = null;
+
+        poolLock.lock();
+        try {
+
+            if ((rospl != null) && rospl.hasThread()) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Notifying thread waiting on pool" +
+                            " [" + rospl.getRoute() + "]");
+                }
+                waitingThread = rospl.nextThread();
+            } else if (!waitingThreads.isEmpty()) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Notifying thread waiting on any pool");
+                }
+                waitingThread = waitingThreads.remove();
+            } else if (log.isDebugEnabled()) {
+                log.debug("Notifying no-one, there are no waiting threads");
+            }
+
+            if (waitingThread != null) {
+                waitingThread.wakeup();
+            }
+
+        } finally {
+            poolLock.unlock();
+        }
+    }
+
+
+    //@@@ revise this cleanup stuff
+    //@@@ move method to base class when deleteEntry() is fixed
+    // non-javadoc, see base class AbstractConnPool
+    @Override
+    public void deleteClosedConnections() {
+
+        poolLock.lock();
+        try {
+
+            Iterator<BasicPoolEntry>  iter = freeConnections.iterator();
+            while (iter.hasNext()) {
+                BasicPoolEntry entry = iter.next();
+                if (!entry.getConnection().isOpen()) {
+                    iter.remove();
+                    deleteEntry(entry);
+                }
+            }
+
+        } finally {
+            poolLock.unlock();
+        }
+    }
+
+
+    // non-javadoc, see base class AbstractConnPool
+    @Override
+    public void shutdown() {
+
+        poolLock.lock();
+        try {
+
+            super.shutdown();
+
+            // close all free connections
+            //@@@ move this to base class?
+            Iterator<BasicPoolEntry> ibpe = freeConnections.iterator();
+            while (ibpe.hasNext()) {
+                BasicPoolEntry entry = ibpe.next();
+                ibpe.remove();
+                closeConnection(entry.getConnection());
+            }
+
+            // wake up all waiting threads
+            Iterator<WaitingThread> iwth = waitingThreads.iterator();
+            while (iwth.hasNext()) {
+                WaitingThread waiter = iwth.next();
+                iwth.remove();
+                waiter.wakeup();
+            }
+
+            routeToPool.clear();
+
+        } finally {
+            poolLock.unlock();
+        }
+    }
+
+
+} // class ConnPoolByRoute
+
diff --git a/src/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java b/src/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java
new file mode 100644
index 0000000..faf5e3b
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java
@@ -0,0 +1,68 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java $
+ * $Revision: 652020 $
+ * $Date: 2008-04-27 14:23:31 -0700 (Sun, 27 Apr 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.http.conn.ConnectionPoolTimeoutException;
+
+/**
+ * Encapsulates a request for a {@link BasicPoolEntry}.
+ */
+public interface PoolEntryRequest {
+
+    /**
+     * Obtains a pool entry with a connection within the given timeout.
+     * If {@link #abortRequest()} is called before this completes
+     * an {@link InterruptedException} is thrown.
+     *
+     * @param timeout   the timeout, 0 or negative for no timeout
+     * @param tunit     the unit for the <code>timeout</code>,
+     *                  may be <code>null</code> only if there is no timeout
+     *
+     * @return  pool entry holding a connection for the route
+     *
+     * @throws ConnectionPoolTimeoutException
+     *         if the timeout expired
+     * @throws InterruptedException
+     *         if the calling thread was interrupted or the request was aborted
+     */
+    BasicPoolEntry getPoolEntry(
+            long timeout, 
+            TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException;
+
+    /**
+     * Aborts the active or next call to
+     * {@link #getPoolEntry(long, TimeUnit)}.
+     */
+    void abortRequest();
+    
+}
diff --git a/src/org/apache/http/impl/conn/tsccm/RefQueueHandler.java b/src/org/apache/http/impl/conn/tsccm/RefQueueHandler.java
new file mode 100644
index 0000000..3af28cc
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/RefQueueHandler.java
@@ -0,0 +1,48 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/RefQueueHandler.java $
+ * $Revision: 603874 $
+ * $Date: 2007-12-13 02:42:41 -0800 (Thu, 13 Dec 2007) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+import java.lang.ref.Reference;
+
+
+/**
+ * Callback handler for {@link RefQueueWorker RefQueueWorker}.
+ */
+public interface RefQueueHandler {
+
+    /**
+     * Invoked when a reference is found on the queue.
+     *
+     * @param ref       the reference to handle
+     */
+    public void handleReference(Reference<?> ref)
+        ;
+}
diff --git a/src/org/apache/http/impl/conn/tsccm/RefQueueWorker.java b/src/org/apache/http/impl/conn/tsccm/RefQueueWorker.java
new file mode 100644
index 0000000..9ad5c77
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/RefQueueWorker.java
@@ -0,0 +1,139 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/RefQueueWorker.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+
+/**
+ * A worker thread for processing queued references.
+ * {@link Reference Reference}s can be
+ * {@link ReferenceQueue queued}
+ * automatically by the garbage collector.
+ * If that feature is used, a daemon thread should be executing
+ * this worker. It will pick up the queued references and pass them
+ * on to a handler for appropriate processing.
+ */
+public class RefQueueWorker implements Runnable {
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    /** The reference queue to monitor. */
+    protected final ReferenceQueue<?> refQueue;
+
+    /** The handler for the references found. */
+    protected final RefQueueHandler refHandler;
+
+
+    /**
+     * The thread executing this handler.
+     * This attribute is also used as a shutdown indicator.
+     */
+    protected volatile Thread workerThread;
+
+
+    /**
+     * Instantiates a new worker to listen for lost connections.
+     *
+     * @param queue     the queue on which to wait for references
+     * @param handler   the handler to pass the references to
+     */
+    public RefQueueWorker(ReferenceQueue<?> queue, RefQueueHandler handler) {
+        if (queue == null) {
+            throw new IllegalArgumentException("Queue must not be null.");
+        }
+        if (handler == null) {
+            throw new IllegalArgumentException("Handler must not be null.");
+        }
+
+        refQueue   = queue;
+        refHandler = handler;
+    }
+
+
+    /**
+     * The main loop of this worker.
+     * If initialization succeeds, this method will only return
+     * after {@link #shutdown shutdown()}. Only one thread can
+     * execute the main loop at any time.
+     */
+    public void run() {
+
+        if (this.workerThread == null) {
+            this.workerThread = Thread.currentThread();
+        }
+
+        while (this.workerThread == Thread.currentThread()) {
+            try {
+                // remove the next reference and process it
+                Reference<?> ref = refQueue.remove();
+                refHandler.handleReference(ref);
+            } catch (InterruptedException e) {
+                //@@@ is logging really necessary? this here is the
+                //@@@ only reason for having a log in this class
+                if (log.isDebugEnabled()) {
+                    log.debug(this.toString() + " interrupted", e);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Shuts down this worker.
+     * It can be re-started afterwards by another call to {@link #run run()}.
+     */
+    public void shutdown() {
+        Thread wt = this.workerThread;
+        if (wt != null) {
+            this.workerThread = null; // indicate shutdown
+            wt.interrupt();
+        }
+    }
+
+
+    /**
+     * Obtains a description of this worker.
+     *
+     * @return  a descriptive string for this worker
+     */
+    @Override
+    public String toString() {
+        return "RefQueueWorker::" + this.workerThread;
+    }
+
+} // class RefQueueWorker
+
diff --git a/src/org/apache/http/impl/conn/tsccm/RouteSpecificPool.java b/src/org/apache/http/impl/conn/tsccm/RouteSpecificPool.java
new file mode 100644
index 0000000..5c63933
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/RouteSpecificPool.java
@@ -0,0 +1,301 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/RouteSpecificPool.java $
+ * $Revision: 677240 $
+ * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+import java.io.IOException;
+import java.util.ListIterator;
+import java.util.Queue;
+import java.util.LinkedList;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.util.LangUtils;
+
+
+/**
+ * A connection sub-pool for a specific route, used by {@link ConnPoolByRoute}.
+ * The methods in this class are unsynchronized. It is expected that the
+ * containing pool takes care of synchronization.
+ */
+public class RouteSpecificPool {
+
+    private final Log log = LogFactory.getLog(getClass());
+    
+    /** The route this pool is for. */
+    protected final HttpRoute route;
+
+    /** the maximum number of entries allowed for this pool */
+    protected final int maxEntries;
+    
+    /**
+     * The list of free entries.
+     * This list is managed LIFO, to increase idle times and
+     * allow for closing connections that are not really needed.
+     */
+    protected final LinkedList<BasicPoolEntry> freeEntries;
+
+    /** The list of threads waiting for this pool. */
+    protected final Queue<WaitingThread> waitingThreads;
+
+    /** The number of created entries. */
+    protected int numEntries;
+
+
+    /**
+     * Creates a new route-specific pool.
+     *
+     * @param route the route for which to pool
+     * @param maxEntries the maximum number of entries allowed for this pool
+     */
+    public RouteSpecificPool(HttpRoute route, int maxEntries) {
+        this.route = route;
+        this.maxEntries = maxEntries;
+        this.freeEntries = new LinkedList<BasicPoolEntry>();
+        this.waitingThreads = new LinkedList<WaitingThread>();
+        this.numEntries = 0;
+    }
+
+
+    /**
+     * Obtains the route for which this pool is specific.
+     *
+     * @return  the route
+     */
+    public final HttpRoute getRoute() {
+        return route;
+    }
+
+    
+    /**
+     * Obtains the maximum number of entries allowed for this pool.
+     *
+     * @return  the max entry number
+     */
+    public final int getMaxEntries() {
+        return maxEntries;
+    }
+    
+    
+    /**
+     * Indicates whether this pool is unused.
+     * A pool is unused if there is neither an entry nor a waiting thread.
+     * All entries count, not only the free but also the allocated ones.
+     *
+     * @return  <code>true</code> if this pool is unused,
+     *          <code>false</code> otherwise
+     */
+    public boolean isUnused() {
+        return (numEntries < 1) && waitingThreads.isEmpty();
+    }
+
+
+    /**
+     * Return remaining capacity of this pool
+     * 
+     * @return capacity
+     */
+    public int getCapacity() {
+        return maxEntries - numEntries;
+    }
+    
+    
+    /**
+     * Obtains the number of entries.
+     * This includes not only the free entries, but also those that
+     * have been created and are currently issued to an application.
+     *
+     * @return  the number of entries for the route of this pool
+     */
+    public final int getEntryCount() {
+        return numEntries;
+    }
+
+
+    /**
+     * Obtains a free entry from this pool, if one is available.
+     *
+     * @return an available pool entry, or <code>null</code> if there is none
+     */
+    public BasicPoolEntry allocEntry(final Object state) {
+        if (!freeEntries.isEmpty()) {
+            ListIterator<BasicPoolEntry> it = freeEntries.listIterator(freeEntries.size());
+            while (it.hasPrevious()) {
+                BasicPoolEntry entry = it.previous();
+                if (LangUtils.equals(state, entry.getState())) {
+                    it.remove();
+                    return entry;
+                }
+            }
+        }
+        if (!freeEntries.isEmpty()) {
+            BasicPoolEntry entry = freeEntries.remove();   
+            entry.setState(null);
+            OperatedClientConnection conn = entry.getConnection();
+            try {
+                conn.close();
+            } catch (IOException ex) {
+                log.debug("I/O error closing connection", ex);
+            }
+            return entry;
+        }
+        return null;
+    }
+
+
+    /**
+     * Returns an allocated entry to this pool.
+     *
+     * @param entry     the entry obtained from {@link #allocEntry allocEntry}
+     *                  or presented to {@link #createdEntry createdEntry}
+     */
+    public void freeEntry(BasicPoolEntry entry) {
+
+        if (numEntries < 1) {
+            throw new IllegalStateException
+                ("No entry created for this pool. " + route);
+        }
+        if (numEntries <= freeEntries.size()) {
+            throw new IllegalStateException
+                ("No entry allocated from this pool. " + route);
+        }
+        freeEntries.add(entry);
+    }
+
+
+    /**
+     * Indicates creation of an entry for this pool.
+     * The entry will <i>not</i> be added to the list of free entries,
+     * it is only recognized as belonging to this pool now. It can then
+     * be passed to {@link #freeEntry freeEntry}.
+     *
+     * @param entry     the entry that was created for this pool
+     */
+    public void createdEntry(BasicPoolEntry entry) {
+
+        if (!route.equals(entry.getPlannedRoute())) {
+            throw new IllegalArgumentException
+                ("Entry not planned for this pool." +
+                 "\npool: " + route +
+                 "\nplan: " + entry.getPlannedRoute());
+        }
+
+        numEntries++;
+    }
+
+
+    /**
+     * Deletes an entry from this pool.
+     * Only entries that are currently free in this pool can be deleted.
+     * Allocated entries can not be deleted.
+     *
+     * @param entry     the entry to delete from this pool
+     *
+     * @return  <code>true</code> if the entry was found and deleted, or
+     *          <code>false</code> if the entry was not found
+     */
+    public boolean deleteEntry(BasicPoolEntry entry) {
+
+        final boolean found = freeEntries.remove(entry);
+        if (found)
+            numEntries--;
+        return found;
+    }
+
+
+    /**
+     * Forgets about an entry from this pool.
+     * This method is used to indicate that an entry
+     * {@link #allocEntry allocated}
+     * from this pool has been lost and will not be returned.
+     */
+    public void dropEntry() {
+        if (numEntries < 1) {
+            throw new IllegalStateException
+                ("There is no entry that could be dropped.");
+        }
+        numEntries--;
+    }
+
+
+    /**
+     * Adds a waiting thread.
+     * This pool makes no attempt to match waiting threads with pool entries.
+     * It is the caller's responsibility to check that there is no entry
+     * before adding a waiting thread.
+     *
+     * @param wt        the waiting thread
+     */
+    public void queueThread(WaitingThread wt) {
+        if (wt == null) {
+            throw new IllegalArgumentException
+                ("Waiting thread must not be null.");
+        }
+        this.waitingThreads.add(wt);
+    }
+
+
+    /**
+     * Checks whether there is a waiting thread in this pool.
+     *
+     * @return  <code>true</code> if there is a waiting thread,
+     *          <code>false</code> otherwise
+     */
+    public boolean hasThread() {
+        return !this.waitingThreads.isEmpty();
+    }
+
+
+    /**
+     * Returns the next thread in the queue.
+     *
+     * @return  a waiting thread, or <code>null</code> if there is none
+     */
+    public WaitingThread nextThread() {
+        return this.waitingThreads.peek();
+    }
+
+
+    /**
+     * Removes a waiting thread, if it is queued.
+     *
+     * @param wt        the waiting thread
+     */
+    public void removeThread(WaitingThread wt) {
+        if (wt == null)
+            return;
+
+        this.waitingThreads.remove(wt);
+    }
+
+
+} // class RouteSpecificPool
diff --git a/src/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java b/src/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java
new file mode 100644
index 0000000..0781e05
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java
@@ -0,0 +1,282 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.ClientConnectionRequest;
+import org.apache.http.conn.ConnectionPoolTimeoutException;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.params.HttpParams;
+import org.apache.http.impl.conn.DefaultClientConnectionOperator;
+
+
+
+/**
+ * Manages a pool of {@link OperatedClientConnection client connections}.
+ * <p>
+ * This class is derived from <code>MultiThreadedHttpConnectionManager</code>
+ * in HttpClient 3. See there for original authors.
+ * </p>
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 673450 $ $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * @since 4.0
+ */
+public class ThreadSafeClientConnManager implements ClientConnectionManager {
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    /** The schemes supported by this connection manager. */
+    protected SchemeRegistry schemeRegistry; 
+    
+    /** The pool of connections being managed. */
+    protected final AbstractConnPool connectionPool;
+
+    /** The operator for opening and updating connections. */
+    protected ClientConnectionOperator connOperator;
+    
+
+
+    /**
+     * Creates a new thread safe connection manager.
+     *
+     * @param params    the parameters for this manager
+     * @param schreg    the scheme registry, or
+     *                  <code>null</code> for the default registry
+     */
+    public ThreadSafeClientConnManager(HttpParams params,
+                                       SchemeRegistry schreg) {
+
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        this.schemeRegistry = schreg;
+        this.connOperator   = createConnectionOperator(schreg);
+        this.connectionPool = createConnectionPool(params);
+
+    } // <constructor>
+
+    
+    @Override
+    protected void finalize() throws Throwable {
+        shutdown();
+        super.finalize();
+    }
+
+
+    /**
+     * Hook for creating the connection pool.
+     *
+     * @return  the connection pool to use
+     */
+    protected AbstractConnPool createConnectionPool(final HttpParams params) {
+
+        AbstractConnPool acp = new ConnPoolByRoute(connOperator, params);
+        boolean conngc = true; //@@@ check parameters to decide
+        if (conngc) {
+            acp.enableConnectionGC();
+        }
+        return acp;
+    }
+
+
+    /**
+     * Hook for creating the connection operator.
+     * It is called by the constructor.
+     * Derived classes can override this method to change the
+     * instantiation of the operator.
+     * The default implementation here instantiates
+     * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}.
+     *
+     * @param schreg    the scheme registry to use, or <code>null</code>
+     *
+     * @return  the connection operator to use
+     */
+    protected ClientConnectionOperator
+        createConnectionOperator(SchemeRegistry schreg) {
+
+        return new DefaultClientConnectionOperator(schreg);
+    }
+
+
+    // non-javadoc, see interface ClientConnectionManager
+    public SchemeRegistry getSchemeRegistry() {
+        return this.schemeRegistry;
+    }
+
+    
+    public ClientConnectionRequest requestConnection(
+            final HttpRoute route, 
+            final Object state) {
+        
+        final PoolEntryRequest poolRequest = connectionPool.requestPoolEntry(
+                route, state);
+        
+        return new ClientConnectionRequest() {
+            
+            public void abortRequest() {
+                poolRequest.abortRequest();
+            }
+            
+            public ManagedClientConnection getConnection(
+                    long timeout, TimeUnit tunit) throws InterruptedException,
+                    ConnectionPoolTimeoutException {
+                if (route == null) {
+                    throw new IllegalArgumentException("Route may not be null.");
+                }
+
+                if (log.isDebugEnabled()) {
+                    log.debug("ThreadSafeClientConnManager.getConnection: "
+                        + route + ", timeout = " + timeout);
+                }
+
+                BasicPoolEntry entry = poolRequest.getPoolEntry(timeout, tunit);
+                return new BasicPooledConnAdapter(ThreadSafeClientConnManager.this, entry);
+            }
+            
+        };
+        
+    }
+
+    
+    // non-javadoc, see interface ClientConnectionManager
+    public void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit) {
+
+        if (!(conn instanceof BasicPooledConnAdapter)) {
+            throw new IllegalArgumentException
+                ("Connection class mismatch, " +
+                 "connection not obtained from this manager.");
+        }
+        BasicPooledConnAdapter hca = (BasicPooledConnAdapter) conn;
+        if ((hca.getPoolEntry() != null) && (hca.getManager() != this)) {
+            throw new IllegalArgumentException
+                ("Connection not obtained from this manager.");
+        }
+
+        try {
+            // make sure that the response has been read completely
+            if (hca.isOpen() && !hca.isMarkedReusable()) {
+                if (log.isDebugEnabled()) {
+                    log.debug
+                        ("Released connection open but not marked reusable.");
+                }
+                // In MTHCM, there would be a call to
+                // SimpleHttpConnectionManager.finishLastResponse(conn);
+                // Consuming the response is handled outside in 4.0.
+
+                // make sure this connection will not be re-used
+                // Shut down rather than close, we might have gotten here
+                // because of a shutdown trigger.
+                // Shutdown of the adapter also clears the tracked route.
+                hca.shutdown();
+            }
+        } catch (IOException iox) {
+            //@@@ log as warning? let pass?
+            if (log.isDebugEnabled())
+                log.debug("Exception shutting down released connection.",
+                          iox);
+        } finally {
+            BasicPoolEntry entry = (BasicPoolEntry) hca.getPoolEntry();
+            boolean reusable = hca.isMarkedReusable();
+            hca.detach();
+            if (entry != null) {
+                connectionPool.freeEntry(entry, reusable, validDuration, timeUnit);
+            }
+        }
+    }
+
+
+    // non-javadoc, see interface ClientConnectionManager
+    public void shutdown() {
+        connectionPool.shutdown();
+    }
+
+
+    /**
+     * Gets the total number of pooled connections for the given route.
+     * This is the total number of connections that have been created and
+     * are still in use by this connection manager for the route.
+     * This value will not exceed the maximum number of connections per host.
+     * 
+     * @param route     the route in question
+     *
+     * @return  the total number of pooled connections for that route
+     */
+    public int getConnectionsInPool(HttpRoute route) {
+        return ((ConnPoolByRoute)connectionPool).getConnectionsInPool(
+                route);
+    }
+
+
+    /**
+     * Gets the total number of pooled connections.  This is the total number of 
+     * connections that have been created and are still in use by this connection 
+     * manager.  This value will not exceed the maximum number of connections
+     * in total.
+     * 
+     * @return the total number of pooled connections
+     */
+    public int getConnectionsInPool() {
+        synchronized (connectionPool) {
+            return connectionPool.numConnections; //@@@
+        }
+    }
+
+
+    // non-javadoc, see interface ClientConnectionManager
+    public void closeIdleConnections(long idleTimeout, TimeUnit tunit) {
+        // combine these two in a single call?
+        connectionPool.closeIdleConnections(idleTimeout, tunit);
+        connectionPool.deleteClosedConnections();
+    }
+    
+    public void closeExpiredConnections() {
+        connectionPool.closeExpiredConnections();
+        connectionPool.deleteClosedConnections();
+    }
+
+
+} // class ThreadSafeClientConnManager
+
diff --git a/src/org/apache/http/impl/conn/tsccm/WaitingThread.java b/src/org/apache/http/impl/conn/tsccm/WaitingThread.java
new file mode 100644
index 0000000..a50e11f
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/WaitingThread.java
@@ -0,0 +1,197 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThread.java $
+ * $Revision: 649217 $
+ * $Date: 2008-04-17 11:32:32 -0700 (Thu, 17 Apr 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+
+import java.util.Date;
+import java.util.concurrent.locks.Condition;
+
+
+/**
+ * Represents a thread waiting for a connection.
+ * This class implements throwaway objects. It is instantiated whenever
+ * a thread needs to wait. Instances are not re-used, except if the
+ * waiting thread experiences a spurious wakeup and continues to wait.
+ * <br/>
+ * All methods assume external synchronization on the condition
+ * passed to the constructor.
+ * Instances of this class do <i>not</i> synchronize access!
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ */
+public class WaitingThread {
+
+    /** The condition on which the thread is waiting. */
+    private final Condition cond;
+
+    /** The route specific pool on which the thread is waiting. */
+    //@@@ replace with generic pool interface
+    private final RouteSpecificPool pool;
+
+    /** The thread that is waiting for an entry. */
+    private Thread waiter;
+    
+    /** True if this was interrupted. */
+    private boolean aborted;
+
+
+    /**
+     * Creates a new entry for a waiting thread.
+     *
+     * @param cond      the condition for which to wait
+     * @param pool      the pool on which the thread will be waiting,
+     *                  or <code>null</code>
+     */
+    public WaitingThread(Condition cond, RouteSpecificPool pool) {
+
+        if (cond == null) {
+            throw new IllegalArgumentException("Condition must not be null.");
+        }
+
+        this.cond = cond;
+        this.pool = pool;
+    }
+
+
+    /**
+     * Obtains the condition.
+     *
+     * @return  the condition on which to wait, never <code>null</code>
+     */
+    public final Condition getCondition() {
+        // not synchronized
+        return this.cond;
+    }
+
+
+    /**
+     * Obtains the pool, if there is one.
+     *
+     * @return  the pool on which a thread is or was waiting,
+     *          or <code>null</code>
+     */
+    public final RouteSpecificPool getPool() {
+        // not synchronized
+        return this.pool;
+    }
+
+
+    /**
+     * Obtains the thread, if there is one.
+     *
+     * @return  the thread which is waiting, or <code>null</code>
+     */
+    public final Thread getThread() {
+        // not synchronized
+        return this.waiter;
+    }
+
+
+    /**
+     * Blocks the calling thread.
+     * This method returns when the thread is notified or interrupted,
+     * if a timeout occurrs, or if there is a spurious wakeup.
+     * <br/>
+     * This method assumes external synchronization.
+     *
+     * @param deadline  when to time out, or <code>null</code> for no timeout
+     *
+     * @return  <code>true</code> if the condition was satisfied,
+     *          <code>false</code> in case of a timeout.
+     *          Typically, a call to {@link #wakeup} is used to indicate
+     *          that the condition was satisfied. Since the condition is
+     *          accessible outside, this cannot be guaranteed though.
+     *
+     * @throws InterruptedException     if the waiting thread was interrupted
+     *
+     * @see #wakeup
+     */
+    public boolean await(Date deadline)
+        throws InterruptedException {
+
+        // This is only a sanity check. We cannot synchronize here,
+        // the lock would not be released on calling cond.await() below.
+        if (this.waiter != null) {
+            throw new IllegalStateException
+                ("A thread is already waiting on this object." +
+                 "\ncaller: " + Thread.currentThread() +
+                 "\nwaiter: " + this.waiter);
+        }
+
+        if (aborted)
+            throw new InterruptedException("Operation interrupted");
+        
+        this.waiter = Thread.currentThread();
+
+        boolean success = false;
+        try {
+            if (deadline != null) {
+                success = this.cond.awaitUntil(deadline);
+            } else {
+                this.cond.await();
+                success = true;
+            }
+            if (aborted)
+                throw new InterruptedException("Operation interrupted");
+        } finally {
+            this.waiter = null;
+        }
+        return success;
+
+    } // await
+
+
+    /**
+     * Wakes up the waiting thread.
+     * <br/>
+     * This method assumes external synchronization.
+     */
+    public void wakeup() {
+
+        // If external synchronization and pooling works properly,
+        // this cannot happen. Just a sanity check.
+        if (this.waiter == null) {
+            throw new IllegalStateException
+                ("Nobody waiting on this object.");
+        }
+
+        // One condition might be shared by several WaitingThread instances.
+        // It probably isn't, but just in case: wake all, not just one.
+        this.cond.signalAll();
+    }
+    
+    public void interrupt() {
+        aborted = true;
+        this.cond.signalAll();
+    }
+
+
+} // class WaitingThread
diff --git a/src/org/apache/http/impl/conn/tsccm/WaitingThreadAborter.java b/src/org/apache/http/impl/conn/tsccm/WaitingThreadAborter.java
new file mode 100644
index 0000000..1844457
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/WaitingThreadAborter.java
@@ -0,0 +1,62 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThreadAborter.java $
+ * $Revision: 649220 $
+ * $Date: 2008-04-17 11:40:24 -0700 (Thu, 17 Apr 2008) $
+ *
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+/** A simple class that can interrupt a {@link WaitingThread}. */
+public class WaitingThreadAborter {
+    
+    private WaitingThread waitingThread;
+    private boolean aborted;
+    
+    /**
+     * If a waiting thread has been set, interrupts it.
+     */
+    public void abort() {
+        aborted = true;
+        
+        if (waitingThread != null)
+            waitingThread.interrupt();
+        
+    }
+    
+    /**
+     * Sets the waiting thread.  If this has already been aborted,
+     * the waiting thread is immediately interrupted.
+     * 
+     * @param waitingThread The thread to interrupt when aborting.
+     */
+    public void setWaitingThread(WaitingThread waitingThread) {
+        this.waitingThread = waitingThread;
+        if (aborted)
+            waitingThread.interrupt();
+    }
+
+}
diff --git a/src/org/apache/http/impl/conn/tsccm/doc-files/tsccm-structure.png b/src/org/apache/http/impl/conn/tsccm/doc-files/tsccm-structure.png
new file mode 100644
index 0000000..2e2820d
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/doc-files/tsccm-structure.png
Binary files differ
diff --git a/src/org/apache/http/impl/conn/tsccm/package.html b/src/org/apache/http/impl/conn/tsccm/package.html
new file mode 100644
index 0000000..5aca5d4
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/package.html
@@ -0,0 +1,205 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/package.html $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+
+The implementation of a thread-safe client connection manager.
+
+<center>
+<img src="doc-files/tsccm-structure.png" alt="Relation Diagram"/>
+</center>
+
+<p>
+The implementation is structured into three areas, as illustrated
+by the diagram above.
+Facing the application is the <i>Manager</i> (green), which internally
+maintains a <i>Pool</i> (yellow) of connections and waiting threads.
+Both Manager and Pool rely on <i>Operations</i> (cyan) to provide the
+actual connections.
+</p>
+<p>
+In order to allow connection garbage collection, it is
+imperative that hard object references between the areas are
+restricted to the relations indicated by arrows in the diagram:
+</p>
+<ul>
+<li>Applications reference only the Manager objects.</li>
+<li>Manager objects reference Pool objects, but not vice versa.</li>
+<li>Operations objects do not reference either Manager or Pool objects.</li>
+</ul>
+
+<p>
+The following table shows a selection of classes and interfaces,
+and their assignment to the three areas.
+</p>
+<center>
+<table border="1">
+<colgroup>
+  <col width="50%"/>
+  <col width="50%"/>
+</colgroup>
+
+<tr>
+<td style="text-align: center; background-color: #00ff00;">
+{@link org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager}
+</td>
+<td style="text-align: center; background-color: #ffff00;">
+{@link org.apache.http.impl.conn.tsccm.AbstractConnPool}
+</td>
+</tr>
+
+<tr>
+<td style="text-align: center; background-color: #00ff00;">
+{@link org.apache.http.impl.conn.tsccm.BasicPooledConnAdapter}
+</td>
+<td style="text-align: center; background-color: #ffff00;">
+{@link org.apache.http.impl.conn.tsccm.ConnPoolByRoute}
+</td>
+</tr>
+
+<!-- appears on both sides! -->
+
+<tr>
+<td style="text-align: right; background-color: #00ff00;">
+{@link org.apache.http.impl.conn.tsccm.BasicPoolEntry}
+</td>
+<td style="text-align: left; background-color: #ffff00;">
+{@link org.apache.http.impl.conn.tsccm.BasicPoolEntry}
+</td>
+</tr>
+
+<!-- ====================== -->
+
+<tr style="border-width: 5px;">
+</tr>
+
+<tr>
+<td colspan="2" style="text-align: center; background-color: #00ffff;">
+{@link org.apache.http.conn.ClientConnectionOperator}
+</td>
+</tr>
+
+<tr>
+<td colspan="2" style="text-align: center; background-color: #00ffff;">
+{@link org.apache.http.conn.OperatedClientConnection}
+</td>
+</tr>
+
+</table>
+</center>
+
+<p>
+The Manager area has implementations for the connection management
+interfaces {@link org.apache.http.conn.ClientConnectionManager}
+and {@link org.apache.http.conn.ManagedClientConnection}.
+The latter is an adapter from managed to operated connections, based on a
+{@link org.apache.http.impl.conn.tsccm.BasicPoolEntry}.
+<br/>
+The Pool area shows an abstract pool class
+{@link org.apache.http.impl.conn.tsccm.AbstractConnPool}
+and a concrete implementation
+{@link org.apache.http.impl.conn.tsccm.ConnPoolByRoute}
+which uses the same basic algorithm as the
+<code>MultiThreadedHttpConnectionManager</code>
+in HttpClient 3.x.
+A pool contains instances of
+{@link org.apache.http.impl.conn.tsccm.BasicPoolEntry}.
+Most other classes in this package also belong to the Pool area.
+<br/>
+In the Operations area, you will find only the interfaces for
+operated connections as defined in the org.apache.http.conn package.
+The connection manager will work with all correct implementations
+of these interfaces. This package therefore does not define anything
+specific to the Operations area.
+</p>
+
+<p>
+As you have surely noticed, the
+{@link org.apache.http.impl.conn.tsccm.BasicPoolEntry}
+appears in both the Manager and Pool areas.
+This is where things get tricky for connection garbage collection.
+<br/>
+A connection pool may start a background thread to implement cleanup.
+In that case, the connection pool will not be garbage collected until
+it is shut down, since the background thread keeps a hard reference
+to the pool. The pool itself keeps hard references to the pooled entries,
+which in turn reference idle connections. Neither of these is subject
+to garbage collection.
+Only the shutdown of the pool will stop the background thread,
+thereby enabling garbage collection of the pool objects.
+<br/>
+A pool entry that is passed to an application by means of a connection
+adapter will move from the Pool area to the Manager area. When the
+connection is released by the application, the manager returns the
+entry back to the pool. With that step, the pool entry moves from
+the Manager area back to the Pool area.
+While the entry is in the Manager area, the pool MUST NOT keep a
+hard reference to it.
+</p>
+
+<p>
+The purpose of connection garbage collection is to detect when an
+application fails to return a connection. In order to achieve this,
+the only hard reference to the pool entry in the Manager area is
+in the connection wrapper. The manager will not keep a hard reference
+to the connection wrapper either, since that wrapper is effectively
+moving to the Application area.
+If the application drops it's reference to the connection wrapper,
+that wrapper will be garbage collected, and with it the pool entry.
+<br/>
+In order to detect garbage collection of pool entries handed out
+to the application, the pool keeps a <i>weak reference</i> to the
+entry. Instances of
+{@link org.apache.http.impl.conn.tsccm.BasicPoolEntryRef}
+combine the weak reference with information about the route for
+which the pool entry was allocated. If one of these entry references
+becomes stale, the pool can accommodate for the lost connection.
+This is triggered either by a background thread waiting for the
+references to be queued by the garbage collector, or by the
+application calling a {@link
+    org.apache.http.conn.ClientConnectionManager#closeIdleConnections cleanup}
+method of the connection manager.
+<br/>
+Basically the same trick is used for detecting garbage collection
+of the connection manager itself. The pool keeps a weak reference
+to the connection manager that created it. However, this will work
+only if there is a background thread to detect when that reference
+is queued by the garbage collector. Otherwise, a finalizer of the
+connection manager will shut down the pool and release it's resources.
+</p>
+
+
+</body>
+</html>
diff --git a/src/org/apache/http/impl/cookie/AbstractCookieAttributeHandler.java b/src/org/apache/http/impl/cookie/AbstractCookieAttributeHandler.java
new file mode 100644
index 0000000..1aa4d2c
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/AbstractCookieAttributeHandler.java
@@ -0,0 +1,50 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/AbstractCookieAttributeHandler.java $
+ * $Revision: 503525 $
+ * $Date: 2007-02-04 17:15:08 -0800 (Sun, 04 Feb 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */ 
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+
+public abstract class AbstractCookieAttributeHandler implements CookieAttributeHandler {
+
+    public void validate(final Cookie cookie, final CookieOrigin origin) 
+            throws MalformedCookieException {
+        // Do nothing
+    }
+    
+    public boolean match(final Cookie cookie, final CookieOrigin origin) {
+        // Always match
+        return true;
+    }
+    
+}
diff --git a/src/org/apache/http/impl/cookie/AbstractCookieSpec.java b/src/org/apache/http/impl/cookie/AbstractCookieSpec.java
new file mode 100644
index 0000000..3e47a4d
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/AbstractCookieSpec.java
@@ -0,0 +1,110 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/AbstractCookieSpec.java $
+ * $Revision: 617207 $
+ * $Date: 2008-01-31 12:14:12 -0800 (Thu, 31 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */ 
+
+package org.apache.http.impl.cookie;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieSpec;
+
+/**
+ * Abstract cookie specification which can delegate the job of parsing,
+ * validation or matching cookie attributes to a number of arbitrary 
+ * {@link CookieAttributeHandler}s.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0 
+ */
+public abstract class AbstractCookieSpec implements CookieSpec {
+    
+    /**
+    * Stores attribute name -> attribute handler mappings
+    */
+    private final Map<String, CookieAttributeHandler> attribHandlerMap;
+
+    /** 
+     * Default constructor 
+     * */
+    public AbstractCookieSpec() {
+        super();
+        this.attribHandlerMap = new HashMap<String, CookieAttributeHandler>(10);        
+    }
+
+    public void registerAttribHandler(
+            final String name, final CookieAttributeHandler handler) {
+        if (name == null) {
+            throw new IllegalArgumentException("Attribute name may not be null");
+        }
+        if (handler == null) {
+            throw new IllegalArgumentException("Attribute handler may not be null");
+        }
+        this.attribHandlerMap.put(name, handler);
+    }
+    
+    /**
+     * Finds an attribute handler {@link CookieAttributeHandler} for the
+     * given attribute. Returns <tt>null</tt> if no attribute handler is
+     * found for the specified attribute.
+     *
+     * @param name attribute name. e.g. Domain, Path, etc.
+     * @return an attribute handler or <tt>null</tt>
+     */
+    protected CookieAttributeHandler findAttribHandler(final String name) {
+        return this.attribHandlerMap.get(name);
+    }
+    
+    /**
+     * Gets attribute handler {@link CookieAttributeHandler} for the
+     * given attribute.
+     *
+     * @param name attribute name. e.g. Domain, Path, etc.
+     * @throws IllegalStateException if handler not found for the
+     *          specified attribute.
+     */
+    protected CookieAttributeHandler getAttribHandler(final String name) {
+        CookieAttributeHandler handler = findAttribHandler(name);
+        if (handler == null) {
+            throw new IllegalStateException("Handler not registered for " +
+                                            name + " attribute.");
+        } else {
+            return handler;
+        }
+    }
+
+    protected Collection<CookieAttributeHandler> getAttribHandlers() {
+        return this.attribHandlerMap.values();
+    }
+    
+}
diff --git a/src/org/apache/http/impl/cookie/BasicClientCookie.java b/src/org/apache/http/impl/cookie/BasicClientCookie.java
new file mode 100644
index 0000000..6ec6c2b
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BasicClientCookie.java
@@ -0,0 +1,376 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BasicClientCookie.java $
+ * $Revision: 659191 $
+ * $Date: 2008-05-22 11:26:53 -0700 (Thu, 22 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.SetCookie;
+
+/**
+ * HTTP "magic-cookie" represents a piece of state information
+ * that the HTTP agent and the target server can exchange to maintain 
+ * a session.
+ * 
+ * @author B.C. Holmes
+ * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
+ * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
+ * @author Rod Waldhoff
+ * @author dIon Gillard
+ * @author Sean C. Sullivan
+ * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
+ * @author Marc A. Saegesser
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * 
+ * @version $Revision: 659191 $
+ */
+public class BasicClientCookie implements SetCookie, ClientCookie, Cloneable {
+
+    /**
+     * Default Constructor taking a name and a value. The value may be null.
+     * 
+     * @param name The name.
+     * @param value The value.
+     */
+    public BasicClientCookie(final String name, final String value) {
+        super();
+        if (name == null) {
+            throw new IllegalArgumentException("Name may not be null");
+        }
+        this.name = name;
+        this.attribs = new HashMap<String, String>();
+        this.value = value;
+    }
+
+    /**
+     * Returns the name.
+     *
+     * @return String name The name
+     */
+    public String getName() {
+        return this.name;
+    }
+
+    /**
+     * Returns the value.
+     *
+     * @return String value The current value.
+     */
+    public String getValue() {
+        return this.value;
+    }
+
+    /**
+     * Sets the value
+     * 
+     * @param value
+     */
+    public void setValue(final String value) {
+        this.value = value;
+    }
+
+    /**
+     * Returns the comment describing the purpose of this cookie, or
+     * <tt>null</tt> if no such comment has been defined.
+     * 
+     * @return comment 
+     *
+     * @see #setComment(String)
+     */
+    public String getComment() {
+        return cookieComment;
+    }
+
+    /**
+     * If a user agent (web browser) presents this cookie to a user, the
+     * cookie's purpose will be described using this comment.
+     * 
+     * @param comment
+     *  
+     * @see #getComment()
+     */
+    public void setComment(String comment) {
+        cookieComment = comment;
+    }
+
+    
+    /**
+     * Returns null. Cookies prior to RFC2965 do not set this attribute
+     */
+    public String getCommentURL() {
+        return null;
+    }
+
+    
+    /**
+     * Returns the expiration {@link Date} of the cookie, or <tt>null</tt>
+     * if none exists.
+     * <p><strong>Note:</strong> the object returned by this method is 
+     * considered immutable. Changing it (e.g. using setTime()) could result
+     * in undefined behaviour. Do so at your peril. </p>
+     * @return Expiration {@link Date}, or <tt>null</tt>.
+     *
+     * @see #setExpiryDate(java.util.Date)
+     *
+     */
+    public Date getExpiryDate() {
+        return cookieExpiryDate;
+    }
+
+    /**
+     * Sets expiration date.
+     * <p><strong>Note:</strong> the object returned by this method is considered
+     * immutable. Changing it (e.g. using setTime()) could result in undefined 
+     * behaviour. Do so at your peril.</p>
+     *
+     * @param expiryDate the {@link Date} after which this cookie is no longer valid.
+     *
+     * @see #getExpiryDate
+     *
+     */
+    public void setExpiryDate (Date expiryDate) {
+        cookieExpiryDate = expiryDate;
+    }
+
+
+    /**
+     * Returns <tt>false</tt> if the cookie should be discarded at the end
+     * of the "session"; <tt>true</tt> otherwise.
+     *
+     * @return <tt>false</tt> if the cookie should be discarded at the end
+     *         of the "session"; <tt>true</tt> otherwise
+     */
+    public boolean isPersistent() {
+        return (null != cookieExpiryDate);
+    }
+
+
+    /**
+     * Returns domain attribute of the cookie.
+     * 
+     * @return the value of the domain attribute
+     *
+     * @see #setDomain(java.lang.String)
+     */
+    public String getDomain() {
+        return cookieDomain;
+    }
+
+    /**
+     * Sets the domain attribute.
+     * 
+     * @param domain The value of the domain attribute
+     *
+     * @see #getDomain
+     */
+    public void setDomain(String domain) {
+        if (domain != null) {
+            cookieDomain = domain.toLowerCase(Locale.ENGLISH);
+        } else {
+            cookieDomain = null;
+        }
+    }
+
+
+    /**
+     * Returns the path attribute of the cookie
+     * 
+     * @return The value of the path attribute.
+     * 
+     * @see #setPath(java.lang.String)
+     */
+    public String getPath() {
+        return cookiePath;
+    }
+
+    /**
+     * Sets the path attribute.
+     *
+     * @param path The value of the path attribute
+     *
+     * @see #getPath
+     *
+     */
+    public void setPath(String path) {
+        cookiePath = path;
+    }
+
+    /**
+     * @return <code>true</code> if this cookie should only be sent over secure connections.
+     * @see #setSecure(boolean)
+     */
+    public boolean isSecure() {
+        return isSecure;
+    }
+
+    /**
+     * Sets the secure attribute of the cookie.
+     * <p>
+     * When <tt>true</tt> the cookie should only be sent
+     * using a secure protocol (https).  This should only be set when
+     * the cookie's originating server used a secure protocol to set the
+     * cookie's value.
+     *
+     * @param secure The value of the secure attribute
+     * 
+     * @see #isSecure()
+     */
+    public void setSecure (boolean secure) {
+        isSecure = secure;
+    }
+
+
+    /**
+     * Returns null. Cookies prior to RFC2965 do not set this attribute
+     */
+    public int[] getPorts() {
+        return null;
+    }
+
+    
+    /**
+     * Returns the version of the cookie specification to which this
+     * cookie conforms.
+     *
+     * @return the version of the cookie.
+     * 
+     * @see #setVersion(int)
+     *
+     */
+    public int getVersion() {
+        return cookieVersion;
+    }
+
+    /**
+     * Sets the version of the cookie specification to which this
+     * cookie conforms. 
+     *
+     * @param version the version of the cookie.
+     * 
+     * @see #getVersion
+     */
+    public void setVersion(int version) {
+        cookieVersion = version;
+    }
+
+    /**
+     * Returns true if this cookie has expired.
+     * @param date Current time
+     * 
+     * @return <tt>true</tt> if the cookie has expired.
+     */
+    public boolean isExpired(final Date date) {
+        if (date == null) {
+            throw new IllegalArgumentException("Date may not be null");
+        }
+        return (cookieExpiryDate != null  
+            && cookieExpiryDate.getTime() <= date.getTime());
+    }
+
+    public void setAttribute(final String name, final String value) {
+        this.attribs.put(name, value);
+    }
+    
+    public String getAttribute(final String name) {
+        return this.attribs.get(name);
+    }
+
+    public boolean containsAttribute(final String name) {
+        return this.attribs.get(name) != null;
+    }
+    
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        BasicClientCookie clone = (BasicClientCookie) super.clone();
+        clone.attribs = new HashMap<String, String>(this.attribs);
+        return clone;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder buffer = new StringBuilder();
+        buffer.append("[version: ");
+        buffer.append(Integer.toString(this.cookieVersion));
+        buffer.append("]");
+        buffer.append("[name: ");
+        buffer.append(this.name);
+        buffer.append("]");
+        buffer.append("[value: ");
+        buffer.append(this.value);
+        buffer.append("]");
+        buffer.append("[domain: ");
+        buffer.append(this.cookieDomain);
+        buffer.append("]");
+        buffer.append("[path: ");
+        buffer.append(this.cookiePath);
+        buffer.append("]");
+        buffer.append("[expiry: ");
+        buffer.append(this.cookieExpiryDate);
+        buffer.append("]");
+        return buffer.toString();
+    }
+    
+   // ----------------------------------------------------- Instance Variables
+
+    /** Cookie name */
+    private final String name;
+
+    /** Cookie attributes as specified by the origin server */
+    private Map<String, String> attribs;
+   
+    /** Cookie value */
+    private String value;
+
+    /** Comment attribute. */
+    private String  cookieComment;
+
+    /** Domain attribute. */
+    private String  cookieDomain;
+
+    /** Expiration {@link Date}. */
+    private Date cookieExpiryDate;
+
+    /** Path attribute. */
+    private String cookiePath;
+
+    /** My secure flag. */
+    private boolean isSecure;
+
+    /** The version of the cookie specification I was created from. */
+    private int cookieVersion;
+
+}
+
diff --git a/src/org/apache/http/impl/cookie/BasicClientCookie2.java b/src/org/apache/http/impl/cookie/BasicClientCookie2.java
new file mode 100644
index 0000000..86ec60d
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BasicClientCookie2.java
@@ -0,0 +1,101 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BasicClientCookie2.java $
+ * $Revision: 659191 $
+ * $Date: 2008-05-22 11:26:53 -0700 (Thu, 22 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.Date;
+
+import org.apache.http.cookie.SetCookie2;
+
+/**
+ * HTTP "magic-cookie" represents a piece of state information
+ * that the HTTP agent and the target server can exchange to maintain 
+ * a session as specified by RFC2965.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public class BasicClientCookie2 extends BasicClientCookie implements SetCookie2 {
+
+    private String commentURL;
+    private int[] ports;
+    private boolean discard;
+    
+    /**
+     * Default Constructor taking a name and a value. The value may be null.
+     * 
+     * @param name The name.
+     * @param value The value.
+     */
+    public BasicClientCookie2(final String name, final String value) {
+        super(name, value);
+    }
+
+    @Override
+    public int[] getPorts() {
+        return this.ports;
+    }
+
+    public void setPorts(final int[] ports) {
+        this.ports = ports;
+    }
+    
+    @Override
+    public String getCommentURL() {
+        return this.commentURL;
+    }
+
+    public void setCommentURL(final String commentURL) {
+        this.commentURL = commentURL;
+    }
+
+    public void setDiscard(boolean discard) {
+        this.discard = discard;
+    }
+
+    @Override
+    public boolean isPersistent() {
+        return !this.discard && super.isPersistent();
+    }
+
+    @Override
+    public boolean isExpired(final Date date) {
+        return this.discard || super.isExpired(date);
+    }
+ 
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        BasicClientCookie2 clone = (BasicClientCookie2) super.clone();
+        clone.ports = this.ports.clone();
+        return clone;
+    }
+    
+}
+
diff --git a/src/org/apache/http/impl/cookie/BasicCommentHandler.java b/src/org/apache/http/impl/cookie/BasicCommentHandler.java
new file mode 100644
index 0000000..ce8baea
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BasicCommentHandler.java
@@ -0,0 +1,50 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BasicCommentHandler.java $
+ * $Revision: 558519 $
+ * $Date: 2007-07-22 11:19:49 -0700 (Sun, 22 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */ 
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+public class BasicCommentHandler extends AbstractCookieAttributeHandler {
+
+    public BasicCommentHandler() {
+        super();
+    }
+    
+    public void parse(final SetCookie cookie, final String value) 
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        cookie.setComment(value);
+    }
+    
+}
diff --git a/src/org/apache/http/impl/cookie/BasicDomainHandler.java b/src/org/apache/http/impl/cookie/BasicDomainHandler.java
new file mode 100644
index 0000000..267faf8
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BasicDomainHandler.java
@@ -0,0 +1,122 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BasicDomainHandler.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */ 
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+public class BasicDomainHandler implements CookieAttributeHandler {
+
+    public BasicDomainHandler() {
+        super();
+    }
+    
+    public void parse(final SetCookie cookie, final String value) 
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (value == null) {
+            throw new MalformedCookieException("Missing value for domain attribute");
+        }
+        if (value.trim().length() == 0) {
+            throw new MalformedCookieException("Blank value for domain attribute");
+        }
+        cookie.setDomain(value);
+    }
+
+    public void validate(final Cookie cookie, final CookieOrigin origin) 
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        // Validate the cookies domain attribute.  NOTE:  Domains without 
+        // any dots are allowed to support hosts on private LANs that don't 
+        // have DNS names.  Since they have no dots, to domain-match the 
+        // request-host and domain must be identical for the cookie to sent 
+        // back to the origin-server.
+        String host = origin.getHost();
+        String domain = cookie.getDomain();
+        if (domain == null) {
+            throw new MalformedCookieException("Cookie domain may not be null");
+        }
+        if (host.contains(".")) {
+            // Not required to have at least two dots.  RFC 2965.
+            // A Set-Cookie2 with Domain=ajax.com will be accepted.
+
+            // domain must match host
+            if (!host.endsWith(domain)) {
+                if (domain.startsWith(".")) {
+                    domain = domain.substring(1, domain.length());
+                }
+                if (!host.equals(domain)) { 
+                    throw new MalformedCookieException(
+                        "Illegal domain attribute \"" + domain 
+                        + "\". Domain of origin: \"" + host + "\"");
+                }
+            }
+        } else {
+            if (!host.equals(domain)) {
+                throw new MalformedCookieException(
+                    "Illegal domain attribute \"" + domain 
+                    + "\". Domain of origin: \"" + host + "\"");
+            }
+        }
+    }
+    
+    public boolean match(final Cookie cookie, final CookieOrigin origin) {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        String host = origin.getHost();
+        String domain = cookie.getDomain();
+        if (domain == null) {
+            return false;
+        }
+        if (host.equals(domain)) {
+            return true;
+        }
+        if (!domain.startsWith(".")) {
+            domain = '.' + domain;
+        }
+        return host.endsWith(domain) || host.equals(domain.substring(1));
+    }
+    
+}
diff --git a/src/org/apache/http/impl/cookie/BasicExpiresHandler.java b/src/org/apache/http/impl/cookie/BasicExpiresHandler.java
new file mode 100644
index 0000000..a53519e
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BasicExpiresHandler.java
@@ -0,0 +1,65 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BasicExpiresHandler.java $
+ * $Revision: 558519 $
+ * $Date: 2007-07-22 11:19:49 -0700 (Sun, 22 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */ 
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+
+public class BasicExpiresHandler extends AbstractCookieAttributeHandler {
+
+    /** Valid date patterns */
+    private final String[] datepatterns;
+
+    public BasicExpiresHandler(final String[] datepatterns) {
+        if (datepatterns == null) {
+            throw new IllegalArgumentException("Array of date patterns may not be null");
+        }
+        this.datepatterns = datepatterns;
+    }
+
+    public void parse(final SetCookie cookie, final String value) 
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (value == null) {
+            throw new MalformedCookieException("Missing value for expires attribute");
+        }
+        try {
+            cookie.setExpiryDate(DateUtils.parseDate(value, this.datepatterns));
+        } catch (DateParseException dpe) {
+            throw new MalformedCookieException("Unable to parse expires attribute: " 
+                + value);
+        }
+    }
+
+}
diff --git a/src/org/apache/http/impl/cookie/BasicMaxAgeHandler.java b/src/org/apache/http/impl/cookie/BasicMaxAgeHandler.java
new file mode 100644
index 0000000..92a5c7d
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BasicMaxAgeHandler.java
@@ -0,0 +1,66 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BasicMaxAgeHandler.java $
+ * $Revision: 581953 $
+ * $Date: 2007-10-04 08:53:50 -0700 (Thu, 04 Oct 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */ 
+package org.apache.http.impl.cookie;
+
+import java.util.Date;
+
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+public class BasicMaxAgeHandler extends AbstractCookieAttributeHandler {
+
+    public BasicMaxAgeHandler() {
+        super();
+    }
+    
+    public void parse(final SetCookie cookie, final String value) 
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (value == null) {
+            throw new MalformedCookieException("Missing value for max-age attribute");
+        }
+        int age;
+        try {
+            age = Integer.parseInt(value);
+        } catch (NumberFormatException e) {
+            throw new MalformedCookieException ("Invalid max-age attribute: " 
+                    + value);
+        }
+        if (age < 0) {
+            throw new MalformedCookieException ("Negative max-age attribute: " 
+                    + value);
+        }
+        cookie.setExpiryDate(new Date(System.currentTimeMillis() + age * 1000L));
+    }
+    
+}
diff --git a/src/org/apache/http/impl/cookie/BasicPathHandler.java b/src/org/apache/http/impl/cookie/BasicPathHandler.java
new file mode 100644
index 0000000..43a12c8
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BasicPathHandler.java
@@ -0,0 +1,91 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BasicPathHandler.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */ 
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+public class BasicPathHandler implements CookieAttributeHandler {
+
+    public BasicPathHandler() {
+        super();
+    }
+    
+    public void parse(final SetCookie cookie, String value) 
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (value == null || value.trim().length() == 0) {
+            value = "/";
+        }
+        cookie.setPath(value);
+    }
+
+    public void validate(final Cookie cookie, final CookieOrigin origin) 
+            throws MalformedCookieException {
+        if (!match(cookie, origin)) {
+            throw new MalformedCookieException(
+                "Illegal path attribute \"" + cookie.getPath() 
+                + "\". Path of origin: \"" + origin.getPath() + "\"");
+        }
+    }
+    
+    public boolean match(final Cookie cookie, final CookieOrigin origin) {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        String targetpath = origin.getPath();
+        String topmostPath = cookie.getPath();
+        if (topmostPath == null) {
+            topmostPath = "/";
+        }
+        if (topmostPath.length() > 1 && topmostPath.endsWith("/")) {
+            topmostPath = topmostPath.substring(0, topmostPath.length() - 1);
+        }
+        boolean match = targetpath.startsWith (topmostPath);
+        // if there is a match and these values are not exactly the same we have
+        // to make sure we're not matcing "/foobar" and "/foo"
+        if (match && targetpath.length() != topmostPath.length()) {
+            if (!topmostPath.endsWith("/")) {
+                match = (targetpath.charAt(topmostPath.length()) == '/');
+            }
+        }
+        return match;
+    }
+    
+}
diff --git a/src/org/apache/http/impl/cookie/BasicSecureHandler.java b/src/org/apache/http/impl/cookie/BasicSecureHandler.java
new file mode 100644
index 0000000..9100b9c
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BasicSecureHandler.java
@@ -0,0 +1,63 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BasicSecureHandler.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */ 
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+public class BasicSecureHandler extends AbstractCookieAttributeHandler {
+
+    public BasicSecureHandler() {
+        super();
+    }
+    
+    public void parse(final SetCookie cookie, final String value) 
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        cookie.setSecure(true);
+    }
+    
+    @Override
+    public boolean match(final Cookie cookie, final CookieOrigin origin) {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        return !cookie.isSecure() || origin.isSecure();
+    }
+    
+}
diff --git a/src/org/apache/http/impl/cookie/BestMatchSpec.java b/src/org/apache/http/impl/cookie/BestMatchSpec.java
new file mode 100644
index 0000000..e33fec3
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BestMatchSpec.java
@@ -0,0 +1,186 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BestMatchSpec.java $
+ * $Revision: 657334 $
+ * $Date: 2008-05-17 04:44:16 -0700 (Sat, 17 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */ 
+
+package org.apache.http.impl.cookie;
+
+import java.util.List;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.MalformedCookieException;
+
+/**
+ * 'Meta' cookie specification that selects a cookie policy depending
+ * on the format of the cookie(s)
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class BestMatchSpec implements CookieSpec {
+
+    private final String[] datepatterns;
+    private final boolean oneHeader;
+    
+    private RFC2965Spec strict;
+    private BrowserCompatSpec compat;
+    private NetscapeDraftSpec netscape;
+
+    public BestMatchSpec(final String[] datepatterns, boolean oneHeader) {
+        super();
+        this.datepatterns = datepatterns;
+        this.oneHeader = oneHeader;
+    }
+
+    public BestMatchSpec() {
+        this(null, false);
+    }
+
+    private RFC2965Spec getStrict() {
+        if (this.strict == null) {
+             this.strict = new RFC2965Spec(this.datepatterns, this.oneHeader);
+        }
+        return strict;
+    }
+
+    private BrowserCompatSpec getCompat() {
+        if (this.compat == null) {
+            this.compat = new BrowserCompatSpec(this.datepatterns);
+        }
+        return compat;
+    }
+
+    private NetscapeDraftSpec getNetscape() {
+        if (this.netscape == null) {
+            String[] patterns = this.datepatterns;
+            if (patterns == null) {
+                patterns = BrowserCompatSpec.DATE_PATTERNS;
+            }
+            this.netscape = new NetscapeDraftSpec(patterns);
+        }
+        return netscape;
+    }
+
+    public List<Cookie> parse(
+            final Header header, 
+            final CookieOrigin origin) throws MalformedCookieException {
+        if (header == null) {
+            throw new IllegalArgumentException("Header may not be null");
+        }
+        if (origin == null) {
+           throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        HeaderElement[] helems = header.getElements();
+        boolean versioned = false;
+        boolean netscape = false;
+        for (HeaderElement helem: helems) {
+            if (helem.getParameterByName("version") != null) {
+                versioned = true;
+            }
+            if (helem.getParameterByName("expires") != null) {
+               netscape = true;
+            }
+        }
+        if (netscape) {
+            
+        }
+        // Do we have a cookie with a version attribute?
+        if (versioned) {
+            return getStrict().parse(helems, origin);
+        } else if (netscape) {
+            // Need to parse the header again,
+            // because Netscape draft cannot handle
+            // comma separators
+            return getNetscape().parse(header, origin);
+        } else {
+            return getCompat().parse(helems, origin);
+        }
+    }
+
+    public void validate(
+            final Cookie cookie, 
+            final CookieOrigin origin) throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        if (cookie.getVersion() > 0) {
+            getStrict().validate(cookie, origin);
+        } else {
+            getCompat().validate(cookie, origin);
+        }
+    }
+
+    public boolean match(final Cookie cookie, final CookieOrigin origin) {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        if (cookie.getVersion() > 0) {
+            return getStrict().match(cookie, origin);
+        } else {
+            return getCompat().match(cookie, origin);
+        }
+    }
+
+    public List<Header> formatCookies(final List<Cookie> cookies) {
+        if (cookies == null) {
+            throw new IllegalArgumentException("List of cookie may not be null");
+        }
+        int version = Integer.MAX_VALUE;
+        for (Cookie cookie: cookies) {
+            if (cookie.getVersion() < version) {
+                version = cookie.getVersion();
+            }
+        }
+        if (version > 0) {
+            return getStrict().formatCookies(cookies);
+        } else {
+            return getCompat().formatCookies(cookies);
+        }
+    }
+
+    public int getVersion() {
+        return getStrict().getVersion();
+    }
+
+    public Header getVersionHeader() {
+        return getStrict().getVersionHeader();
+    }
+
+}
\ No newline at end of file
diff --git a/src/org/apache/http/impl/cookie/BestMatchSpecFactory.java b/src/org/apache/http/impl/cookie/BestMatchSpecFactory.java
new file mode 100644
index 0000000..cb632bb
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BestMatchSpecFactory.java
@@ -0,0 +1,57 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BestMatchSpecFactory.java $
+ * $Revision: 613707 $
+ * $Date: 2008-01-20 16:28:37 -0800 (Sun, 20 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.CookieSpecFactory;
+import org.apache.http.cookie.params.CookieSpecPNames;
+import org.apache.http.params.HttpParams;
+
+/**
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class BestMatchSpecFactory implements CookieSpecFactory {    
+
+    public CookieSpec newInstance(final HttpParams params) {
+        if (params != null) {
+            return new BestMatchSpec(
+                    (String []) params.getParameter(CookieSpecPNames.DATE_PATTERNS), 
+                    params.getBooleanParameter(CookieSpecPNames.SINGLE_COOKIE_HEADER, false));
+        } else {
+            return new BestMatchSpec();
+        }
+    }
+
+}
diff --git a/src/org/apache/http/impl/cookie/BrowserCompatSpec.java b/src/org/apache/http/impl/cookie/BrowserCompatSpec.java
new file mode 100644
index 0000000..d7bc0da
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BrowserCompatSpec.java
@@ -0,0 +1,188 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BrowserCompatSpec.java $
+ * $Revision: 657334 $
+ * $Date: 2008-05-17 04:44:16 -0700 (Sat, 17 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */ 
+
+package org.apache.http.impl.cookie;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.http.FormattedHeader;
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SM;
+import org.apache.http.message.BufferedHeader;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Cookie specification that strives to closely mimic (mis)behavior of 
+ * common web browser applications such as Microsoft Internet Explorer
+ * and Mozilla FireFox.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0 
+ */
+public class BrowserCompatSpec extends CookieSpecBase {
+    
+    /** Valid date patterns used per default */
+    protected static final String[] DATE_PATTERNS = new String[] {
+            DateUtils.PATTERN_RFC1123,
+            DateUtils.PATTERN_RFC1036,
+            DateUtils.PATTERN_ASCTIME,
+            "EEE, dd-MMM-yyyy HH:mm:ss z",
+            "EEE, dd-MMM-yyyy HH-mm-ss z",
+            "EEE, dd MMM yy HH:mm:ss z",
+            "EEE dd-MMM-yyyy HH:mm:ss z",
+            "EEE dd MMM yyyy HH:mm:ss z",
+            "EEE dd-MMM-yyyy HH-mm-ss z",
+            "EEE dd-MMM-yy HH:mm:ss z",
+            "EEE dd MMM yy HH:mm:ss z",
+            "EEE,dd-MMM-yy HH:mm:ss z",
+            "EEE,dd-MMM-yyyy HH:mm:ss z",
+            "EEE, dd-MM-yyyy HH:mm:ss z",                
+        };
+
+    private final String[] datepatterns; 
+    
+    /** Default constructor */
+    public BrowserCompatSpec(final String[] datepatterns) {
+        super();
+        if (datepatterns != null) {
+            this.datepatterns = datepatterns.clone();
+        } else {
+            this.datepatterns = DATE_PATTERNS;
+        }
+        registerAttribHandler(ClientCookie.PATH_ATTR, new BasicPathHandler());
+        registerAttribHandler(ClientCookie.DOMAIN_ATTR, new BasicDomainHandler());
+        registerAttribHandler(ClientCookie.MAX_AGE_ATTR, new BasicMaxAgeHandler());
+        registerAttribHandler(ClientCookie.SECURE_ATTR, new BasicSecureHandler());
+        registerAttribHandler(ClientCookie.COMMENT_ATTR, new BasicCommentHandler());
+        registerAttribHandler(ClientCookie.EXPIRES_ATTR, new BasicExpiresHandler(
+                this.datepatterns));
+    }
+
+    /** Default constructor */
+    public BrowserCompatSpec() {
+        this(null);
+    }
+    
+    public List<Cookie> parse(final Header header, final CookieOrigin origin) 
+            throws MalformedCookieException {
+        if (header == null) {
+            throw new IllegalArgumentException("Header may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        String headervalue = header.getValue();
+        boolean isNetscapeCookie = false; 
+        int i1 = headervalue.toLowerCase(Locale.ENGLISH).indexOf("expires=");
+        if (i1 != -1) {
+            i1 += "expires=".length();
+            int i2 = headervalue.indexOf(';', i1);
+            if (i2 == -1) {
+                i2 = headervalue.length(); 
+            }
+            try {
+                DateUtils.parseDate(headervalue.substring(i1, i2), this.datepatterns);
+                isNetscapeCookie = true; 
+            } catch (DateParseException e) {
+                // Does not look like a valid expiry date
+            }
+        }
+        HeaderElement[] elems = null;
+        if (isNetscapeCookie) {
+            NetscapeDraftHeaderParser parser = NetscapeDraftHeaderParser.DEFAULT;
+            CharArrayBuffer buffer;
+            ParserCursor cursor;
+            if (header instanceof FormattedHeader) {
+                buffer = ((FormattedHeader) header).getBuffer();
+                cursor = new ParserCursor(
+                        ((FormattedHeader) header).getValuePos(), 
+                        buffer.length());
+            } else {
+                String s = header.getValue();
+                if (s == null) {
+                    throw new MalformedCookieException("Header value is null");
+                }
+                buffer = new CharArrayBuffer(s.length());
+                buffer.append(s);
+                cursor = new ParserCursor(0, buffer.length());
+            }
+            elems = new HeaderElement[] { parser.parseHeader(buffer, cursor) };
+        } else {
+            elems = header.getElements();
+        }
+        return parse(elems, origin);
+    }
+
+    public List<Header> formatCookies(final List<Cookie> cookies) {
+        if (cookies == null) {
+            throw new IllegalArgumentException("List of cookies may not be null");
+        }
+        if (cookies.isEmpty()) {
+            throw new IllegalArgumentException("List of cookies may not be empty");
+        }
+        CharArrayBuffer buffer = new CharArrayBuffer(20 * cookies.size());
+        buffer.append(SM.COOKIE);
+        buffer.append(": ");
+        for (int i = 0; i < cookies.size(); i++) {
+            Cookie cookie = cookies.get(i);
+            if (i > 0) {
+                buffer.append("; ");
+            }
+            buffer.append(cookie.getName());
+            buffer.append("=");
+            String s = cookie.getValue();
+            if (s != null) {
+                buffer.append(s);
+            }
+        }
+        List<Header> headers = new ArrayList<Header>(1);
+        headers.add(new BufferedHeader(buffer));
+        return headers;
+    }
+
+    public int getVersion() {
+        return 0;
+    }
+
+    public Header getVersionHeader() {
+        return null;
+    }
+    
+}
diff --git a/src/org/apache/http/impl/cookie/BrowserCompatSpecFactory.java b/src/org/apache/http/impl/cookie/BrowserCompatSpecFactory.java
new file mode 100644
index 0000000..71c0c05
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BrowserCompatSpecFactory.java
@@ -0,0 +1,56 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BrowserCompatSpecFactory.java $
+ * $Revision: 576068 $
+ * $Date: 2007-09-16 03:25:01 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.CookieSpecFactory;
+import org.apache.http.cookie.params.CookieSpecPNames;
+import org.apache.http.params.HttpParams;
+
+/**
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class BrowserCompatSpecFactory implements CookieSpecFactory {    
+
+    public CookieSpec newInstance(final HttpParams params) {
+        if (params != null) {
+            return new BrowserCompatSpec(
+                    (String []) params.getParameter(CookieSpecPNames.DATE_PATTERNS));
+        } else {
+            return new BrowserCompatSpec();
+        }
+    }
+
+}
diff --git a/src/org/apache/http/impl/cookie/CookieSpecBase.java b/src/org/apache/http/impl/cookie/CookieSpecBase.java
new file mode 100644
index 0000000..8e70bb1
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/CookieSpecBase.java
@@ -0,0 +1,131 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/CookieSpecBase.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */ 
+
+package org.apache.http.impl.cookie;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+
+/**
+ * Cookie management functions shared by all specification.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0 
+ */
+public abstract class CookieSpecBase extends AbstractCookieSpec {
+    
+    protected static String getDefaultPath(final CookieOrigin origin) {
+        String defaultPath = origin.getPath();    
+        int lastSlashIndex = defaultPath.lastIndexOf('/');
+        if (lastSlashIndex >= 0) {
+            if (lastSlashIndex == 0) {
+                //Do not remove the very first slash
+                lastSlashIndex = 1;
+            }
+            defaultPath = defaultPath.substring(0, lastSlashIndex);
+        }
+        return defaultPath;
+    }
+
+    protected static String getDefaultDomain(final CookieOrigin origin) {
+        return origin.getHost();
+    }
+    
+    protected List<Cookie> parse(final HeaderElement[] elems, final CookieOrigin origin)
+                throws MalformedCookieException {
+        List<Cookie> cookies = new ArrayList<Cookie>(elems.length);
+        for (HeaderElement headerelement : elems) {
+            String name = headerelement.getName();
+            String value = headerelement.getValue();
+            if (name == null || name.length() == 0) {
+                throw new MalformedCookieException("Cookie name may not be empty");
+            }
+
+            BasicClientCookie cookie = new BasicClientCookie(name, value);
+            cookie.setPath(getDefaultPath(origin));
+            cookie.setDomain(getDefaultDomain(origin));
+
+            // cycle through the parameters
+            NameValuePair[] attribs = headerelement.getParameters();
+            for (int j = attribs.length - 1; j >= 0; j--) {
+                NameValuePair attrib = attribs[j];
+                String s = attrib.getName().toLowerCase(Locale.ENGLISH);
+
+                cookie.setAttribute(s, attrib.getValue());
+
+                CookieAttributeHandler handler = findAttribHandler(s);
+                if (handler != null) {
+                    handler.parse(cookie, attrib.getValue());
+                }
+            }
+            cookies.add(cookie);
+        }
+        return cookies;
+    }
+
+    public void validate(final Cookie cookie, final CookieOrigin origin)
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        for (CookieAttributeHandler handler: getAttribHandlers()) {
+            handler.validate(cookie, origin);
+        }
+    }
+
+    public boolean match(final Cookie cookie, final CookieOrigin origin) {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        for (CookieAttributeHandler handler: getAttribHandlers()) {
+            if (!handler.match(cookie, origin)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+}
diff --git a/src/org/apache/http/impl/cookie/DateParseException.java b/src/org/apache/http/impl/cookie/DateParseException.java
new file mode 100644
index 0000000..c80b669
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/DateParseException.java
@@ -0,0 +1,60 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/DateParseException.java $
+ * $Revision: 609105 $
+ * $Date: 2008-01-05 00:55:00 -0800 (Sat, 05 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+
+/**
+ * An exception to indicate an error parsing a date string.
+ * 
+ * @see DateUtils
+ * 
+ * @author Michael Becke
+ */
+public class DateParseException extends Exception {
+
+    private static final long serialVersionUID = 4417696455000643370L;
+    
+    /**
+     * 
+     */
+    public DateParseException() {
+        super();
+    }
+
+    /**
+     * @param message the exception message
+     */
+    public DateParseException(String message) {
+        super(message);
+    }
+
+}
diff --git a/src/org/apache/http/impl/cookie/DateUtils.java b/src/org/apache/http/impl/cookie/DateUtils.java
new file mode 100644
index 0000000..a0a056c
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/DateUtils.java
@@ -0,0 +1,261 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/DateUtils.java $
+ * $Revision: 677240 $
+ * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.lang.ref.SoftReference;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+/**
+ * A utility class for parsing and formatting HTTP dates as used in cookies and 
+ * other headers.  This class handles dates as defined by RFC 2616 section 
+ * 3.3.1 as well as some other common non-standard formats.
+ * 
+ * @author Christopher Brown
+ * @author Michael Becke
+ */
+public final class DateUtils {
+
+    /**
+     * Date format pattern used to parse HTTP date headers in RFC 1123 format.
+     */
+    public static final String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz";
+
+    /**
+     * Date format pattern used to parse HTTP date headers in RFC 1036 format.
+     */
+    public static final String PATTERN_RFC1036 = "EEEE, dd-MMM-yy HH:mm:ss zzz";
+
+    /**
+     * Date format pattern used to parse HTTP date headers in ANSI C 
+     * <code>asctime()</code> format.
+     */
+    public static final String PATTERN_ASCTIME = "EEE MMM d HH:mm:ss yyyy";
+
+    private static final String[] DEFAULT_PATTERNS = new String[] {
+    	PATTERN_RFC1036,
+    	PATTERN_RFC1123,
+        PATTERN_ASCTIME
+    };
+
+    private static final Date DEFAULT_TWO_DIGIT_YEAR_START;
+    
+    public static final TimeZone GMT = TimeZone.getTimeZone("GMT");
+    
+    static {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTimeZone(GMT);
+        calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        DEFAULT_TWO_DIGIT_YEAR_START = calendar.getTime(); 
+    }
+    
+    /**
+     * Parses a date value.  The formats used for parsing the date value are retrieved from
+     * the default http params.
+     *
+     * @param dateValue the date value to parse
+     * 
+     * @return the parsed date
+     *
+     * @throws DateParseException if the value could not be parsed using any of the 
+     * supported date formats
+     */
+    public static Date parseDate(String dateValue) throws DateParseException {
+        return parseDate(dateValue, null, null);
+    }
+    
+    /**
+     * Parses the date value using the given date formats.
+     * 
+     * @param dateValue the date value to parse
+     * @param dateFormats the date formats to use
+     * 
+     * @return the parsed date
+     * 
+     * @throws DateParseException if none of the dataFormats could parse the dateValue
+     */
+    public static Date parseDate(final String dateValue, String[] dateFormats) 
+        throws DateParseException {
+        return parseDate(dateValue, dateFormats, null);
+    }
+    
+    /**
+     * Parses the date value using the given date formats.
+     * 
+     * @param dateValue the date value to parse
+     * @param dateFormats the date formats to use
+     * @param startDate During parsing, two digit years will be placed in the range 
+     * <code>startDate</code> to <code>startDate + 100 years</code>. This value may 
+     * be <code>null</code>. When <code>null</code> is given as a parameter, year 
+     * <code>2000</code> will be used. 
+     * 
+     * @return the parsed date
+     * 
+     * @throws DateParseException if none of the dataFormats could parse the dateValue
+     */
+    public static Date parseDate(
+        String dateValue, 
+        String[] dateFormats,
+        Date startDate 
+    ) throws DateParseException {
+        
+        if (dateValue == null) {
+            throw new IllegalArgumentException("dateValue is null");
+        }
+        if (dateFormats == null) {
+            dateFormats = DEFAULT_PATTERNS;
+        }
+        if (startDate == null) {
+            startDate = DEFAULT_TWO_DIGIT_YEAR_START;
+        }
+        // trim single quotes around date if present
+        // see issue #5279
+        if (dateValue.length() > 1 
+            && dateValue.startsWith("'") 
+            && dateValue.endsWith("'")
+        ) {
+            dateValue = dateValue.substring (1, dateValue.length() - 1);
+        }
+
+        for (String dateFormat : dateFormats) {
+            SimpleDateFormat dateParser = DateFormatHolder.formatFor(dateFormat);
+            dateParser.set2DigitYearStart(startDate);
+
+            try {
+                return dateParser.parse(dateValue);
+            } catch (ParseException pe) {
+                // ignore this exception, we will try the next format
+            }
+        }
+        
+        // we were unable to parse the date
+        throw new DateParseException("Unable to parse the date " + dateValue);        
+    }
+
+    /**
+     * Formats the given date according to the RFC 1123 pattern.
+     * 
+     * @param date The date to format.
+     * @return An RFC 1123 formatted date string.
+     * 
+     * @see #PATTERN_RFC1123
+     */
+    public static String formatDate(Date date) {
+        return formatDate(date, PATTERN_RFC1123);
+    }
+    
+    /**
+     * Formats the given date according to the specified pattern.  The pattern
+     * must conform to that used by the {@link SimpleDateFormat simple date
+     * format} class.
+     * 
+     * @param date The date to format.
+     * @param pattern The pattern to use for formatting the date.  
+     * @return A formatted date string.
+     * 
+     * @throws IllegalArgumentException If the given date pattern is invalid.
+     * 
+     * @see SimpleDateFormat
+     */
+    public static String formatDate(Date date, String pattern) {
+        if (date == null) throw new IllegalArgumentException("date is null");
+        if (pattern == null) throw new IllegalArgumentException("pattern is null");
+        
+        SimpleDateFormat formatter = DateFormatHolder.formatFor(pattern);
+        return formatter.format(date);
+    }
+    
+    /** This class should not be instantiated. */    
+    private DateUtils() { 
+    }
+    
+    /**
+     * A factory for {@link SimpleDateFormat}s. The instances are stored in a
+     * threadlocal way because SimpleDateFormat is not threadsafe as noted in
+     * {@link SimpleDateFormat its javadoc}.
+     * 
+     * @author Daniel Mueller
+     */
+    final static class DateFormatHolder {
+
+        private static final ThreadLocal<SoftReference<Map<String, SimpleDateFormat>>> 
+            THREADLOCAL_FORMATS = new ThreadLocal<SoftReference<Map<String, SimpleDateFormat>>>() {
+
+            @Override
+            protected SoftReference<Map<String, SimpleDateFormat>> initialValue() {
+                return new SoftReference<Map<String, SimpleDateFormat>>(
+                        new HashMap<String, SimpleDateFormat>());
+            }
+            
+        };
+
+        /**
+         * creates a {@link SimpleDateFormat} for the requested format string.
+         * 
+         * @param pattern
+         *            a non-<code>null</code> format String according to
+         *            {@link SimpleDateFormat}. The format is not checked against
+         *            <code>null</code> since all paths go through
+         *            {@link DateUtils}.
+         * @return the requested format. This simple dateformat should not be used
+         *         to {@link SimpleDateFormat#applyPattern(String) apply} to a
+         *         different pattern.
+         */
+        public static SimpleDateFormat formatFor(String pattern) {
+            SoftReference<Map<String, SimpleDateFormat>> ref = THREADLOCAL_FORMATS.get();
+            Map<String, SimpleDateFormat> formats = ref.get();
+            if (formats == null) {
+                formats = new HashMap<String, SimpleDateFormat>();
+                THREADLOCAL_FORMATS.set(
+                        new SoftReference<Map<String, SimpleDateFormat>>(formats));    
+            }
+
+            SimpleDateFormat format = formats.get(pattern);
+            if (format == null) {
+                format = new SimpleDateFormat(pattern, Locale.US);
+                format.setTimeZone(TimeZone.getTimeZone("GMT"));
+                formats.put(pattern, format);
+            }
+
+            return format;
+        }
+        
+    }
+    
+}
diff --git a/src/org/apache/http/impl/cookie/NetscapeDomainHandler.java b/src/org/apache/http/impl/cookie/NetscapeDomainHandler.java
new file mode 100644
index 0000000..8b785ae
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/NetscapeDomainHandler.java
@@ -0,0 +1,106 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/NetscapeDomainHandler.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */ 
+package org.apache.http.impl.cookie;
+
+import java.util.Locale;
+import java.util.StringTokenizer;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+
+public class NetscapeDomainHandler extends BasicDomainHandler {
+
+    public NetscapeDomainHandler() {
+        super();
+    }
+
+    @Override
+    public void validate(final Cookie cookie, final CookieOrigin origin) 
+            throws MalformedCookieException {
+        super.validate(cookie, origin);
+        // Perform Netscape Cookie draft specific validation
+        String host = origin.getHost();
+        String domain = cookie.getDomain();
+        if (host.contains(".")) {
+            int domainParts = new StringTokenizer(domain, ".").countTokens();
+
+            if (isSpecialDomain(domain)) {
+                if (domainParts < 2) {
+                    throw new MalformedCookieException("Domain attribute \""
+                        + domain 
+                        + "\" violates the Netscape cookie specification for "
+                        + "special domains");
+                }
+            } else {
+                if (domainParts < 3) {
+                    throw new MalformedCookieException("Domain attribute \""
+                        + domain 
+                        + "\" violates the Netscape cookie specification");
+                }            
+            }
+        }
+    }
+
+   /**
+    * Checks if the given domain is in one of the seven special
+    * top level domains defined by the Netscape cookie specification.
+    * @param domain The domain.
+    * @return True if the specified domain is "special"
+    */
+   private static boolean isSpecialDomain(final String domain) {
+       final String ucDomain = domain.toUpperCase(Locale.ENGLISH);
+       return ucDomain.endsWith(".COM")
+               || ucDomain.endsWith(".EDU")
+               || ucDomain.endsWith(".NET")
+               || ucDomain.endsWith(".GOV")
+               || ucDomain.endsWith(".MIL")
+               || ucDomain.endsWith(".ORG")
+               || ucDomain.endsWith(".INT");
+   }
+
+   @Override
+public boolean match(Cookie cookie, CookieOrigin origin) {
+       if (cookie == null) {
+           throw new IllegalArgumentException("Cookie may not be null");
+       }
+       if (origin == null) {
+           throw new IllegalArgumentException("Cookie origin may not be null");
+       }
+       String host = origin.getHost();
+       String domain = cookie.getDomain();
+       if (domain == null) {
+           return false;
+       }
+       return host.endsWith(domain);
+   }
+
+}
diff --git a/src/org/apache/http/impl/cookie/NetscapeDraftHeaderParser.java b/src/org/apache/http/impl/cookie/NetscapeDraftHeaderParser.java
new file mode 100644
index 0000000..ca6b7fa
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/NetscapeDraftHeaderParser.java
@@ -0,0 +1,78 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/NetscapeDraftHeaderParser.java $
+ * $Revision: 603563 $
+ * $Date: 2007-12-12 03:17:55 -0800 (Wed, 12 Dec 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.ParseException;
+import org.apache.http.message.BasicHeaderElement;
+import org.apache.http.message.BasicHeaderValueParser;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.util.CharArrayBuffer;
+
+public class NetscapeDraftHeaderParser {
+
+    public final static NetscapeDraftHeaderParser DEFAULT = new NetscapeDraftHeaderParser();
+    
+    private final static char[] DELIMITERS = new char[] { ';' };
+    
+    private final BasicHeaderValueParser nvpParser;
+    
+    public NetscapeDraftHeaderParser() {
+        super();
+        this.nvpParser = BasicHeaderValueParser.DEFAULT;
+    }
+    
+    public HeaderElement parseHeader(
+            final CharArrayBuffer buffer,
+            final ParserCursor cursor) throws ParseException {
+        if (buffer == null) {
+            throw new IllegalArgumentException("Char array buffer may not be null");
+        }
+        if (cursor == null) {
+            throw new IllegalArgumentException("Parser cursor may not be null");
+        }
+        NameValuePair nvp = this.nvpParser.parseNameValuePair(buffer, cursor, DELIMITERS);
+        List<NameValuePair> params = new ArrayList<NameValuePair>(); 
+        while (!cursor.atEnd()) {
+            NameValuePair param = this.nvpParser.parseNameValuePair(buffer, cursor, DELIMITERS);
+            params.add(param);
+        }
+        return new BasicHeaderElement(
+                nvp.getName(), 
+                nvp.getValue(), params.toArray(new NameValuePair[params.size()]));
+    }
+
+}
diff --git a/src/org/apache/http/impl/cookie/NetscapeDraftSpec.java b/src/org/apache/http/impl/cookie/NetscapeDraftSpec.java
new file mode 100644
index 0000000..3bc4f9f
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/NetscapeDraftSpec.java
@@ -0,0 +1,182 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/NetscapeDraftSpec.java $
+ * $Revision: 677240 $
+ * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 Jul 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.http.FormattedHeader;
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SM;
+import org.apache.http.message.BufferedHeader;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Netscape cookie draft compliant cookie policy
+ *
+ * @author  B.C. Holmes
+ * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
+ * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
+ * @author Rod Waldhoff
+ * @author dIon Gillard
+ * @author Sean C. Sullivan
+ * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
+ * @author Marc A. Saegesser
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * 
+ * @since 4.0 
+ */
+public class NetscapeDraftSpec extends CookieSpecBase {
+
+    protected static final String EXPIRES_PATTERN = "EEE, dd-MMM-yyyy HH:mm:ss z";
+    
+    private final String[] datepatterns; 
+    
+    /** Default constructor */
+    public NetscapeDraftSpec(final String[] datepatterns) {
+        super();
+        if (datepatterns != null) {
+            this.datepatterns = datepatterns.clone();
+        } else {
+            this.datepatterns = new String[] { EXPIRES_PATTERN };
+        }
+        registerAttribHandler(ClientCookie.PATH_ATTR, new BasicPathHandler());
+        registerAttribHandler(ClientCookie.DOMAIN_ATTR, new NetscapeDomainHandler());
+        registerAttribHandler(ClientCookie.MAX_AGE_ATTR, new BasicMaxAgeHandler());
+        registerAttribHandler(ClientCookie.SECURE_ATTR, new BasicSecureHandler());
+        registerAttribHandler(ClientCookie.COMMENT_ATTR, new BasicCommentHandler());
+        registerAttribHandler(ClientCookie.EXPIRES_ATTR, new BasicExpiresHandler(
+                this.datepatterns));
+    }
+
+    /** Default constructor */
+    public NetscapeDraftSpec() {
+        this(null);
+    }
+    
+    /**
+      * Parses the Set-Cookie value into an array of <tt>Cookie</tt>s.
+      *
+      * <p>Syntax of the Set-Cookie HTTP Response Header:</p>
+      * 
+      * <p>This is the format a CGI script would use to add to 
+      * the HTTP headers a new piece of data which is to be stored by 
+      * the client for later retrieval.</p>
+      *  
+      * <PRE>
+      *  Set-Cookie: NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure
+      * </PRE>
+      *
+      * <p>Please note that Netscape draft specification does not fully 
+      * conform to the HTTP header format. Netscape draft does not specify 
+      * whether multiple cookies may be sent in one header. Hence, comma 
+      * character may be present in unquoted cookie value or unquoted 
+      * parameter value.</p>
+      * 
+      * @see <a href="http://wp.netscape.com/newsref/std/cookie_spec.html">
+      *  The Cookie Spec.</a>
+      *
+      * @param header the <tt>Set-Cookie</tt> received from the server
+      * @return an array of <tt>Cookie</tt>s parsed from the Set-Cookie value
+      * @throws MalformedCookieException if an exception occurs during parsing
+      */
+    public List<Cookie> parse(final Header header, final CookieOrigin origin) 
+            throws MalformedCookieException {
+        if (header == null) {
+            throw new IllegalArgumentException("Header may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        NetscapeDraftHeaderParser parser = NetscapeDraftHeaderParser.DEFAULT;
+        CharArrayBuffer buffer;
+        ParserCursor cursor;
+        if (header instanceof FormattedHeader) {
+            buffer = ((FormattedHeader) header).getBuffer();
+            cursor = new ParserCursor(
+                    ((FormattedHeader) header).getValuePos(), 
+                    buffer.length());
+        } else {
+            String s = header.getValue();
+            if (s == null) {
+                throw new MalformedCookieException("Header value is null");
+            }
+            buffer = new CharArrayBuffer(s.length());
+            buffer.append(s);
+            cursor = new ParserCursor(0, buffer.length());
+        }
+        return parse(new HeaderElement[] { parser.parseHeader(buffer, cursor) }, origin);
+    }
+
+    public List<Header> formatCookies(final List<Cookie> cookies) {
+        if (cookies == null) {
+            throw new IllegalArgumentException("List of cookies may not be null");
+        }
+        if (cookies.isEmpty()) {
+            throw new IllegalArgumentException("List of cookies may not be empty");
+        }
+        CharArrayBuffer buffer = new CharArrayBuffer(20 * cookies.size());
+        buffer.append(SM.COOKIE);
+        buffer.append(": ");
+        for (int i = 0; i < cookies.size(); i++) {
+            Cookie cookie = cookies.get(i);
+            if (i > 0) {
+                buffer.append("; ");
+            }
+            buffer.append(cookie.getName());
+            String s = cookie.getValue();
+            if (s != null) {
+                buffer.append("=");
+                buffer.append(s);
+            }
+        }
+        List<Header> headers = new ArrayList<Header>(1);
+        headers.add(new BufferedHeader(buffer));
+        return headers;
+    }
+
+    public int getVersion() {
+        return 0;
+    }
+
+    public Header getVersionHeader() {
+        return null;
+    }
+    
+}
diff --git a/src/org/apache/http/impl/cookie/NetscapeDraftSpecFactory.java b/src/org/apache/http/impl/cookie/NetscapeDraftSpecFactory.java
new file mode 100644
index 0000000..0dcb187
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/NetscapeDraftSpecFactory.java
@@ -0,0 +1,56 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/NetscapeDraftSpecFactory.java $
+ * $Revision: 657334 $
+ * $Date: 2008-05-17 04:44:16 -0700 (Sat, 17 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.CookieSpecFactory;
+import org.apache.http.cookie.params.CookieSpecPNames;
+import org.apache.http.params.HttpParams;
+
+/**
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class NetscapeDraftSpecFactory implements CookieSpecFactory {    
+
+    public CookieSpec newInstance(final HttpParams params) {
+        if (params != null) {
+            return new NetscapeDraftSpec(
+                    (String []) params.getParameter(CookieSpecPNames.DATE_PATTERNS));
+        } else {
+            return new NetscapeDraftSpec();
+        }
+    }
+
+}
diff --git a/src/org/apache/http/impl/cookie/RFC2109DomainHandler.java b/src/org/apache/http/impl/cookie/RFC2109DomainHandler.java
new file mode 100644
index 0000000..9cfd484
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2109DomainHandler.java
@@ -0,0 +1,126 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2109DomainHandler.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */ 
+package org.apache.http.impl.cookie;
+
+import java.util.Locale;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+public class RFC2109DomainHandler implements CookieAttributeHandler {
+
+    public RFC2109DomainHandler() {
+        super();
+    }
+    
+    public void parse(final SetCookie cookie, final String value) 
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (value == null) {
+            throw new MalformedCookieException("Missing value for domain attribute");
+        }
+        if (value.trim().length() == 0) {
+            throw new MalformedCookieException("Blank value for domain attribute");
+        }
+        cookie.setDomain(value);
+    }
+
+    public void validate(final Cookie cookie, final CookieOrigin origin) 
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        String host = origin.getHost();
+        String domain = cookie.getDomain();
+        if (domain == null) {
+            throw new MalformedCookieException("Cookie domain may not be null");
+        }
+        if (!domain.equals(host)) {
+            int dotIndex = domain.indexOf('.');
+            if (dotIndex == -1) {
+                throw new MalformedCookieException("Domain attribute \"" 
+                        + domain 
+                        + "\" does not match the host \"" 
+                        + host + "\"");
+            }
+            // domain must start with dot
+            if (!domain.startsWith(".")) {
+                throw new MalformedCookieException("Domain attribute \"" 
+                    + domain 
+                    + "\" violates RFC 2109: domain must start with a dot");
+            }
+            // domain must have at least one embedded dot
+            dotIndex = domain.indexOf('.', 1);
+            if (dotIndex < 0 || dotIndex == domain.length() - 1) {
+                throw new MalformedCookieException("Domain attribute \"" 
+                    + domain 
+                    + "\" violates RFC 2109: domain must contain an embedded dot");
+            }
+            host = host.toLowerCase(Locale.ENGLISH);
+            if (!host.endsWith(domain)) {
+                throw new MalformedCookieException(
+                    "Illegal domain attribute \"" + domain 
+                    + "\". Domain of origin: \"" + host + "\"");
+            }
+            // host minus domain may not contain any dots
+            String hostWithoutDomain = host.substring(0, host.length() - domain.length());
+            if (hostWithoutDomain.indexOf('.') != -1) {
+                throw new MalformedCookieException("Domain attribute \"" 
+                    + domain 
+                    + "\" violates RFC 2109: host minus domain may not contain any dots");
+            }
+        }
+    }
+    
+    public boolean match(final Cookie cookie, final CookieOrigin origin) {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        String host = origin.getHost();
+        String domain = cookie.getDomain();
+        if (domain == null) {
+            return false;
+        }
+        return host.equals(domain) || (domain.startsWith(".") && host.endsWith(domain));
+    }
+    
+}
diff --git a/src/org/apache/http/impl/cookie/RFC2109Spec.java b/src/org/apache/http/impl/cookie/RFC2109Spec.java
new file mode 100644
index 0000000..9e45408
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2109Spec.java
@@ -0,0 +1,246 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2109Spec.java $
+ * $Revision: 677240 $
+ * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 Jul 2008) $
+ * 
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.CookiePathComparator;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SM;
+import org.apache.http.message.BufferedHeader;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * RFC 2109 compliant cookie policy
+ *
+ * @author  B.C. Holmes
+ * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
+ * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
+ * @author Rod Waldhoff
+ * @author dIon Gillard
+ * @author Sean C. Sullivan
+ * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
+ * @author Marc A. Saegesser
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * 
+ * @since 4.0 
+ */
+
+public class RFC2109Spec extends CookieSpecBase {
+
+    private final static CookiePathComparator PATH_COMPARATOR = new CookiePathComparator(); 
+    
+    private final static String[] DATE_PATTERNS = {
+        DateUtils.PATTERN_RFC1123,
+        DateUtils.PATTERN_RFC1036,
+        DateUtils.PATTERN_ASCTIME 
+    }; 
+    
+    private final String[] datepatterns; 
+    private final boolean oneHeader;
+    
+    /** Default constructor */
+    public RFC2109Spec(final String[] datepatterns, boolean oneHeader) {
+        super();
+        if (datepatterns != null) {
+            this.datepatterns = datepatterns.clone();
+        } else {
+            this.datepatterns = DATE_PATTERNS;
+        }
+        this.oneHeader = oneHeader;
+        registerAttribHandler(ClientCookie.VERSION_ATTR, new RFC2109VersionHandler());
+        registerAttribHandler(ClientCookie.PATH_ATTR, new BasicPathHandler());
+        registerAttribHandler(ClientCookie.DOMAIN_ATTR, new RFC2109DomainHandler());
+        registerAttribHandler(ClientCookie.MAX_AGE_ATTR, new BasicMaxAgeHandler());
+        registerAttribHandler(ClientCookie.SECURE_ATTR, new BasicSecureHandler());
+        registerAttribHandler(ClientCookie.COMMENT_ATTR, new BasicCommentHandler());
+        registerAttribHandler(ClientCookie.EXPIRES_ATTR, new BasicExpiresHandler(
+                this.datepatterns));
+    }
+
+    /** Default constructor */
+    public RFC2109Spec() {
+        this(null, false);
+    }
+    
+    public List<Cookie> parse(final Header header, final CookieOrigin origin) 
+            throws MalformedCookieException {
+        if (header == null) {
+            throw new IllegalArgumentException("Header may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        HeaderElement[] elems = header.getElements();
+        return parse(elems, origin);
+    }
+
+    @Override
+    public void validate(final Cookie cookie, final CookieOrigin origin) 
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        String name = cookie.getName();
+        if (name.indexOf(' ') != -1) {
+            throw new MalformedCookieException("Cookie name may not contain blanks");
+        }
+        if (name.startsWith("$")) {
+            throw new MalformedCookieException("Cookie name may not start with $");
+        }
+        super.validate(cookie, origin);
+    }
+
+    public List<Header> formatCookies(List<Cookie> cookies) {
+        if (cookies == null) {
+            throw new IllegalArgumentException("List of cookies may not be null");
+        }
+        if (cookies.isEmpty()) {
+            throw new IllegalArgumentException("List of cookies may not be empty");
+        }
+        if (cookies.size() > 1) {
+            // Create a mutable copy and sort the copy.
+            cookies = new ArrayList<Cookie>(cookies);
+            Collections.sort(cookies, PATH_COMPARATOR);
+        }
+        if (this.oneHeader) {
+            return doFormatOneHeader(cookies);
+        } else {
+            return doFormatManyHeaders(cookies);
+        }
+    }
+
+    private List<Header> doFormatOneHeader(final List<Cookie> cookies) {
+        int version = Integer.MAX_VALUE;
+        // Pick the lowest common denominator
+        for (Cookie cookie : cookies) {
+            if (cookie.getVersion() < version) {
+                version = cookie.getVersion();
+            }
+        }
+        CharArrayBuffer buffer = new CharArrayBuffer(40 * cookies.size());
+        buffer.append(SM.COOKIE);
+        buffer.append(": ");
+        buffer.append("$Version=");
+        buffer.append(Integer.toString(version));
+        for (Cookie cooky : cookies) {
+            buffer.append("; ");
+            Cookie cookie = cooky;
+            formatCookieAsVer(buffer, cookie, version);
+        }
+        List<Header> headers = new ArrayList<Header>(1);
+        headers.add(new BufferedHeader(buffer));
+        return headers;
+    }
+
+    private List<Header> doFormatManyHeaders(final List<Cookie> cookies) {
+        List<Header> headers = new ArrayList<Header>(cookies.size());
+        for (Cookie cookie : cookies) {
+            int version = cookie.getVersion();
+            CharArrayBuffer buffer = new CharArrayBuffer(40);
+            buffer.append("Cookie: ");
+            buffer.append("$Version=");
+            buffer.append(Integer.toString(version));
+            buffer.append("; ");
+            formatCookieAsVer(buffer, cookie, version);
+            headers.add(new BufferedHeader(buffer));
+        }
+        return headers;
+    }
+    
+    /**
+     * Return a name/value string suitable for sending in a <tt>"Cookie"</tt>
+     * header as defined in RFC 2109 for backward compatibility with cookie
+     * version 0
+     * @param buffer The char array buffer to use for output
+     * @param name The cookie name
+     * @param value The cookie value
+     * @param version The cookie version 
+     */
+    protected void formatParamAsVer(final CharArrayBuffer buffer, 
+            final String name, final String value, int version) {
+        buffer.append(name);
+        buffer.append("=");
+        if (value != null) {
+            if (version > 0) {
+                buffer.append('\"');
+                buffer.append(value);
+                buffer.append('\"');
+            } else {
+                buffer.append(value);
+            }
+        }
+    }
+
+    /**
+     * Return a string suitable for sending in a <tt>"Cookie"</tt> header 
+     * as defined in RFC 2109 for backward compatibility with cookie version 0
+     * @param buffer The char array buffer to use for output
+     * @param cookie The {@link Cookie} to be formatted as string
+     * @param version The version to use.
+     */
+    protected void formatCookieAsVer(final CharArrayBuffer buffer, 
+            final Cookie cookie, int version) {
+        formatParamAsVer(buffer, cookie.getName(), cookie.getValue(), version);
+        if (cookie.getPath() != null) {
+            if (cookie instanceof ClientCookie 
+                    && ((ClientCookie) cookie).containsAttribute(ClientCookie.PATH_ATTR)) {
+                buffer.append("; ");
+                formatParamAsVer(buffer, "$Path", cookie.getPath(), version);
+            }
+        }
+        if (cookie.getDomain() != null) {
+            if (cookie instanceof ClientCookie 
+                    && ((ClientCookie) cookie).containsAttribute(ClientCookie.DOMAIN_ATTR)) {
+                buffer.append("; ");
+                formatParamAsVer(buffer, "$Domain", cookie.getDomain(), version);
+            }
+        }
+    }
+
+    public int getVersion() {
+        return 1;
+    }
+
+    public Header getVersionHeader() {
+        return null;
+    }
+    
+}
diff --git a/src/org/apache/http/impl/cookie/RFC2109SpecFactory.java b/src/org/apache/http/impl/cookie/RFC2109SpecFactory.java
new file mode 100644
index 0000000..35c506e
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2109SpecFactory.java
@@ -0,0 +1,57 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2109SpecFactory.java $
+ * $Revision: 576068 $
+ * $Date: 2007-09-16 03:25:01 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.CookieSpecFactory;
+import org.apache.http.cookie.params.CookieSpecPNames;
+import org.apache.http.params.HttpParams;
+
+/**
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class RFC2109SpecFactory implements CookieSpecFactory {    
+
+    public CookieSpec newInstance(final HttpParams params) {
+        if (params != null) {
+            return new RFC2109Spec(
+                    (String []) params.getParameter(CookieSpecPNames.DATE_PATTERNS), 
+                    params.getBooleanParameter(CookieSpecPNames.SINGLE_COOKIE_HEADER, false));
+        } else {
+            return new RFC2109Spec();
+        }
+    }
+
+}
diff --git a/src/org/apache/http/impl/cookie/RFC2109VersionHandler.java b/src/org/apache/http/impl/cookie/RFC2109VersionHandler.java
new file mode 100644
index 0000000..d2c4955
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2109VersionHandler.java
@@ -0,0 +1,74 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2109VersionHandler.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */ 
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+public class RFC2109VersionHandler extends AbstractCookieAttributeHandler {
+
+    public RFC2109VersionHandler() {
+        super();
+    }
+    
+    public void parse(final SetCookie cookie, final String value) 
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (value == null) {
+            throw new MalformedCookieException("Missing value for version attribute");
+        }
+        if (value.trim().length() == 0) {
+            throw new MalformedCookieException("Blank value for version attribute");
+        }
+        try {
+           cookie.setVersion(Integer.parseInt(value));
+        } catch (NumberFormatException e) {
+            throw new MalformedCookieException("Invalid version: " 
+                + e.getMessage());
+        }
+    }
+
+    @Override
+    public void validate(final Cookie cookie, final CookieOrigin origin) 
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (cookie.getVersion() < 0) {
+            throw new MalformedCookieException("Cookie version may not be negative");
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/impl/cookie/RFC2965CommentUrlAttributeHandler.java b/src/org/apache/http/impl/cookie/RFC2965CommentUrlAttributeHandler.java
new file mode 100644
index 0000000..aa3a1c5
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2965CommentUrlAttributeHandler.java
@@ -0,0 +1,66 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965CommentUrlAttributeHandler.java $
+ * $Revision: 590695 $
+ * $Date: 2007-10-31 07:55:41 -0700 (Wed, 31 Oct 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+import org.apache.http.cookie.SetCookie2;
+
+/**
+   * <tt>"CommantURL"</tt> cookie attribute handler for RFC 2965 cookie spec.
+   */
+  public class RFC2965CommentUrlAttributeHandler implements CookieAttributeHandler {
+
+      public RFC2965CommentUrlAttributeHandler() {
+          super();
+      }
+      
+      public void parse(final SetCookie cookie, final String commenturl)
+              throws MalformedCookieException {
+          if (cookie instanceof SetCookie2) {
+              SetCookie2 cookie2 = (SetCookie2) cookie;
+              cookie2.setCommentURL(commenturl);
+          }
+      }
+
+      public void validate(final Cookie cookie, final CookieOrigin origin)
+              throws MalformedCookieException {
+      }
+
+      public boolean match(final Cookie cookie, final CookieOrigin origin) {
+          return true;
+      }
+      
+  }
\ No newline at end of file
diff --git a/src/org/apache/http/impl/cookie/RFC2965DiscardAttributeHandler.java b/src/org/apache/http/impl/cookie/RFC2965DiscardAttributeHandler.java
new file mode 100644
index 0000000..aa81145
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2965DiscardAttributeHandler.java
@@ -0,0 +1,66 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965DiscardAttributeHandler.java $
+ * $Revision: 590695 $
+ * $Date: 2007-10-31 07:55:41 -0700 (Wed, 31 Oct 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+import org.apache.http.cookie.SetCookie2;
+
+/**
+   * <tt>"Discard"</tt> cookie attribute handler for RFC 2965 cookie spec.
+   */
+  public class RFC2965DiscardAttributeHandler implements CookieAttributeHandler {
+
+      public RFC2965DiscardAttributeHandler() {
+          super();
+      }
+      
+      public void parse(final SetCookie cookie, final String commenturl)
+              throws MalformedCookieException {
+          if (cookie instanceof SetCookie2) {
+              SetCookie2 cookie2 = (SetCookie2) cookie;
+              cookie2.setDiscard(true);
+          }
+      }
+
+      public void validate(final Cookie cookie, final CookieOrigin origin)
+              throws MalformedCookieException {
+      }
+
+      public boolean match(final Cookie cookie, final CookieOrigin origin) {
+          return true;
+      }
+      
+  }
\ No newline at end of file
diff --git a/src/org/apache/http/impl/cookie/RFC2965DomainAttributeHandler.java b/src/org/apache/http/impl/cookie/RFC2965DomainAttributeHandler.java
new file mode 100644
index 0000000..b07e5e9
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2965DomainAttributeHandler.java
@@ -0,0 +1,195 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965DomainAttributeHandler.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.Locale;
+
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+/**
+ * <tt>"Domain"</tt> cookie attribute handler for RFC 2965 cookie spec.
+ * 
+ * @author jain.samit@gmail.com (Samit Jain)
+ *
+ * @since 3.1
+ */
+public class RFC2965DomainAttributeHandler implements CookieAttributeHandler {
+
+    public RFC2965DomainAttributeHandler() {
+        super();
+    }
+
+    /**
+     * Parse cookie domain attribute.
+     */
+    public void parse(final SetCookie cookie, String domain)
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (domain == null) {
+            throw new MalformedCookieException(
+                    "Missing value for domain attribute");
+        }
+        if (domain.trim().length() == 0) {
+            throw new MalformedCookieException(
+                    "Blank value for domain attribute");
+        }
+        domain = domain.toLowerCase(Locale.ENGLISH);
+        if (!domain.startsWith(".")) {
+            // Per RFC 2965 section 3.2.2
+            // "... If an explicitly specified value does not start with
+            // a dot, the user agent supplies a leading dot ..."
+            // That effectively implies that the domain attribute 
+            // MAY NOT be an IP address of a host name
+            domain = '.' + domain;
+        }
+        cookie.setDomain(domain);
+    }
+
+    /**
+     * Performs domain-match as defined by the RFC2965.
+     * <p>
+     * Host A's name domain-matches host B's if
+     * <ol>
+     *   <ul>their host name strings string-compare equal; or</ul>
+     *   <ul>A is a HDN string and has the form NB, where N is a non-empty
+     *       name string, B has the form .B', and B' is a HDN string.  (So,
+     *       x.y.com domain-matches .Y.com but not Y.com.)</ul>
+     * </ol>
+     *
+     * @param host host name where cookie is received from or being sent to.
+     * @param domain The cookie domain attribute.
+     * @return true if the specified host matches the given domain.
+     */
+    public boolean domainMatch(String host, String domain) {
+        boolean match = host.equals(domain)
+                        || (domain.startsWith(".") && host.endsWith(domain));
+
+        return match;
+    }
+
+    /**
+     * Validate cookie domain attribute.
+     */
+    public void validate(final Cookie cookie, final CookieOrigin origin)
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        String host = origin.getHost().toLowerCase(Locale.ENGLISH);
+        if (cookie.getDomain() == null) {
+            throw new MalformedCookieException("Invalid cookie state: " +
+                                               "domain not specified");
+        }
+        String cookieDomain = cookie.getDomain().toLowerCase(Locale.ENGLISH);
+
+        if (cookie instanceof ClientCookie 
+                && ((ClientCookie) cookie).containsAttribute(ClientCookie.DOMAIN_ATTR)) {
+            // Domain attribute must start with a dot
+            if (!cookieDomain.startsWith(".")) {
+                throw new MalformedCookieException("Domain attribute \"" +
+                    cookie.getDomain() + "\" violates RFC 2109: domain must start with a dot");
+            }
+
+            // Domain attribute must contain at least one embedded dot,
+            // or the value must be equal to .local.
+            int dotIndex = cookieDomain.indexOf('.', 1);
+            if (((dotIndex < 0) || (dotIndex == cookieDomain.length() - 1))
+                && (!cookieDomain.equals(".local"))) {
+                throw new MalformedCookieException(
+                        "Domain attribute \"" + cookie.getDomain()
+                        + "\" violates RFC 2965: the value contains no embedded dots "
+                        + "and the value is not .local");
+            }
+
+            // The effective host name must domain-match domain attribute.
+            if (!domainMatch(host, cookieDomain)) {
+                throw new MalformedCookieException(
+                        "Domain attribute \"" + cookie.getDomain()
+                        + "\" violates RFC 2965: effective host name does not "
+                        + "domain-match domain attribute.");
+            }
+
+            // effective host name minus domain must not contain any dots
+            String effectiveHostWithoutDomain = host.substring(
+                    0, host.length() - cookieDomain.length());
+            if (effectiveHostWithoutDomain.indexOf('.') != -1) {
+                throw new MalformedCookieException("Domain attribute \""
+                                                   + cookie.getDomain() + "\" violates RFC 2965: "
+                                                   + "effective host minus domain may not contain any dots");
+            }
+        } else {
+            // Domain was not specified in header. In this case, domain must
+            // string match request host (case-insensitive).
+            if (!cookie.getDomain().equals(host)) {
+                throw new MalformedCookieException("Illegal domain attribute: \""
+                                                   + cookie.getDomain() + "\"."
+                                                   + "Domain of origin: \""
+                                                   + host + "\"");
+            }
+        }
+    }
+
+    /**
+     * Match cookie domain attribute.
+     */
+    public boolean match(final Cookie cookie, final CookieOrigin origin) {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        String host = origin.getHost().toLowerCase(Locale.ENGLISH);
+        String cookieDomain = cookie.getDomain();
+
+        // The effective host name MUST domain-match the Domain
+        // attribute of the cookie.
+        if (!domainMatch(host, cookieDomain)) {
+            return false;
+        }
+        // effective host name minus domain must not contain any dots
+        String effectiveHostWithoutDomain = host.substring(
+                0, host.length() - cookieDomain.length());
+        return effectiveHostWithoutDomain.indexOf('.') == -1;
+    }
+
+}
\ No newline at end of file
diff --git a/src/org/apache/http/impl/cookie/RFC2965PortAttributeHandler.java b/src/org/apache/http/impl/cookie/RFC2965PortAttributeHandler.java
new file mode 100644
index 0000000..b881cda
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2965PortAttributeHandler.java
@@ -0,0 +1,168 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965PortAttributeHandler.java $
+ * $Revision: 590695 $
+ * $Date: 2007-10-31 07:55:41 -0700 (Wed, 31 Oct 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.StringTokenizer;
+
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+import org.apache.http.cookie.SetCookie2;
+
+/**
+ * <tt>"Port"</tt> cookie attribute handler for RFC 2965 cookie spec.
+ */
+public class RFC2965PortAttributeHandler implements CookieAttributeHandler {
+
+    public RFC2965PortAttributeHandler() {
+        super();
+    }
+
+    /**
+     * Parses the given Port attribute value (e.g. "8000,8001,8002")
+     * into an array of ports.
+     *
+     * @param portValue port attribute value
+     * @return parsed array of ports
+     * @throws MalformedCookieException if there is a problem in
+     *          parsing due to invalid portValue.
+     */
+    private static int[] parsePortAttribute(final String portValue)
+            throws MalformedCookieException {
+        StringTokenizer st = new StringTokenizer(portValue, ",");
+        int[] ports = new int[st.countTokens()];
+        try {
+            int i = 0;
+            while(st.hasMoreTokens()) {
+                ports[i] = Integer.parseInt(st.nextToken().trim());
+                if (ports[i] < 0) {
+                  throw new MalformedCookieException ("Invalid Port attribute.");
+                }
+                ++i;
+            }
+        } catch (NumberFormatException e) {
+            throw new MalformedCookieException ("Invalid Port "
+                                                + "attribute: " + e.getMessage());
+        }
+        return ports;
+    }
+
+    /**
+     * Returns <tt>true</tt> if the given port exists in the given
+     * ports list.
+     *
+     * @param port port of host where cookie was received from or being sent to.
+     * @param ports port list
+     * @return true returns <tt>true</tt> if the given port exists in
+     *         the given ports list; <tt>false</tt> otherwise.
+     */
+    private static boolean portMatch(int port, int[] ports) {
+        boolean portInList = false;
+        for (int i = 0, len = ports.length; i < len; i++) {
+            if (port == ports[i]) {
+                portInList = true;
+                break;
+            }
+        }
+        return portInList;
+    }
+
+    /**
+     * Parse cookie port attribute.
+     */
+    public void parse(final SetCookie cookie, final String portValue)
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (cookie instanceof SetCookie2) {
+            SetCookie2 cookie2 = (SetCookie2) cookie;
+            if (portValue != null && portValue.trim().length() > 0) {
+                int[] ports = parsePortAttribute(portValue);
+                cookie2.setPorts(ports);
+            }
+        }
+    }
+
+    /**
+     * Validate cookie port attribute. If the Port attribute was specified
+     * in header, the request port must be in cookie's port list.
+     */
+    public void validate(final Cookie cookie, final CookieOrigin origin)
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        int port = origin.getPort();
+        if (cookie instanceof ClientCookie 
+                && ((ClientCookie) cookie).containsAttribute(ClientCookie.PORT_ATTR)) {
+            if (!portMatch(port, cookie.getPorts())) {
+                throw new MalformedCookieException(
+                        "Port attribute violates RFC 2965: "
+                        + "Request port not found in cookie's port list.");
+            }
+        }
+    }
+
+    /**
+     * Match cookie port attribute. If the Port attribute is not specified
+     * in header, the cookie can be sent to any port. Otherwise, the request port
+     * must be in the cookie's port list.
+     */
+    public boolean match(final Cookie cookie, final CookieOrigin origin) {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        int port = origin.getPort();
+        if (cookie instanceof ClientCookie 
+                && ((ClientCookie) cookie).containsAttribute(ClientCookie.PORT_ATTR)) {
+            if (cookie.getPorts() == null) {
+                // Invalid cookie state: port not specified
+                return false;
+            }
+            if (!portMatch(port, cookie.getPorts())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+}
diff --git a/src/org/apache/http/impl/cookie/RFC2965Spec.java b/src/org/apache/http/impl/cookie/RFC2965Spec.java
new file mode 100644
index 0000000..9422fdf
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2965Spec.java
@@ -0,0 +1,259 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965Spec.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ * 
+ * ====================================================================
+ *
+ *  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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SM;
+import org.apache.http.message.BufferedHeader;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * <p>RFC 2965 specific cookie management functions.</p>
+ * 
+ * @author jain.samit@gmail.com (Samit Jain)
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 3.1
+ */
+public class RFC2965Spec extends RFC2109Spec {
+
+    /** 
+     * Default constructor 
+     * 
+     */
+    public RFC2965Spec() {
+        this(null, false);
+    }
+    
+    public RFC2965Spec(final String[] datepatterns, boolean oneHeader) {
+        super(datepatterns, oneHeader);
+        registerAttribHandler(ClientCookie.DOMAIN_ATTR, new RFC2965DomainAttributeHandler());
+        registerAttribHandler(ClientCookie.PORT_ATTR, new RFC2965PortAttributeHandler());
+        registerAttribHandler(ClientCookie.COMMENTURL_ATTR, new RFC2965CommentUrlAttributeHandler());
+        registerAttribHandler(ClientCookie.DISCARD_ATTR, new RFC2965DiscardAttributeHandler());
+        registerAttribHandler(ClientCookie.VERSION_ATTR, new RFC2965VersionAttributeHandler());
+    }
+    
+    private BasicClientCookie createCookie(
+            final String name, final String value, final CookieOrigin origin) {
+        BasicClientCookie cookie = new BasicClientCookie(name, value);
+        cookie.setPath(getDefaultPath(origin));
+        cookie.setDomain(getDefaultDomain(origin));
+        return cookie;
+    }
+    
+    private BasicClientCookie createCookie2(
+            final String name, final String value, final CookieOrigin origin) {
+        BasicClientCookie2 cookie = new BasicClientCookie2(name, value);
+        cookie.setPath(getDefaultPath(origin));
+        cookie.setDomain(getDefaultDomain(origin));
+        cookie.setPorts(new int [] { origin.getPort() });
+        return cookie;
+    }
+    
+    @Override
+    public List<Cookie> parse(
+            final Header header, 
+            CookieOrigin origin) throws MalformedCookieException {
+        if (header == null) {
+            throw new IllegalArgumentException("Header may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        
+        origin = adjustEffectiveHost(origin);
+        
+        HeaderElement[] elems = header.getElements();
+
+        List<Cookie> cookies = new ArrayList<Cookie>(elems.length);
+        for (HeaderElement headerelement : elems) {
+            String name = headerelement.getName();
+            String value = headerelement.getValue();
+            if (name == null || name.length() == 0) {
+                throw new MalformedCookieException("Cookie name may not be empty");
+            }
+
+            BasicClientCookie cookie;
+            if (header.getName().equals(SM.SET_COOKIE2)) {
+                cookie = createCookie2(name, value, origin);
+            } else {
+                cookie = createCookie(name, value, origin);
+            }
+
+            // cycle through the parameters
+            NameValuePair[] attribs = headerelement.getParameters();
+
+            // Eliminate duplicate attributes. The first occurrence takes precedence
+            // See RFC2965: 3.2  Origin Server Role
+            Map<String, NameValuePair> attribmap =
+                    new HashMap<String, NameValuePair>(attribs.length);
+            for (int j = attribs.length - 1; j >= 0; j--) {
+                NameValuePair param = attribs[j];
+                attribmap.put(param.getName().toLowerCase(Locale.ENGLISH), param);
+            }
+            for (Map.Entry<String, NameValuePair> entry : attribmap.entrySet()) {
+                NameValuePair attrib = entry.getValue();
+                String s = attrib.getName().toLowerCase(Locale.ENGLISH);
+
+                cookie.setAttribute(s, attrib.getValue());
+
+                CookieAttributeHandler handler = findAttribHandler(s);
+                if (handler != null) {
+                    handler.parse(cookie, attrib.getValue());
+                }
+            }
+            cookies.add(cookie);
+        }
+        return cookies;
+    }
+
+    @Override
+    public void validate(final Cookie cookie, CookieOrigin origin)
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        origin = adjustEffectiveHost(origin);
+        super.validate(cookie, origin);
+    }
+
+    @Override
+    public boolean match(final Cookie cookie, CookieOrigin origin) {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (origin == null) {
+            throw new IllegalArgumentException("Cookie origin may not be null");
+        }
+        origin = adjustEffectiveHost(origin);
+        return super.match(cookie, origin);
+    }
+
+    /**
+     * Adds valid Port attribute value, e.g. "8000,8001,8002"
+     */
+    @Override
+    protected void formatCookieAsVer(final CharArrayBuffer buffer, 
+            final Cookie cookie, int version) {
+        super.formatCookieAsVer(buffer, cookie, version);
+        // format port attribute
+        if (cookie instanceof ClientCookie) {
+            // Test if the port attribute as set by the origin server is not blank
+            String s = ((ClientCookie) cookie).getAttribute(ClientCookie.PORT_ATTR);
+            if (s != null) {
+                buffer.append("; $Port");
+                buffer.append("=\"");
+                if (s.trim().length() > 0) {
+                    int[] ports = cookie.getPorts();
+                    if (ports != null) {
+                        for (int i = 0, len = ports.length; i < len; i++) {
+                            if (i > 0) {
+                                buffer.append(",");
+                            }
+                            buffer.append(Integer.toString(ports[i]));
+                        }
+                    }
+                }
+                buffer.append("\"");
+            }
+        }
+    }
+    
+    /**
+     * Set 'effective host name' as defined in RFC 2965.
+     * <p>
+     * If a host name contains no dots, the effective host name is
+     * that name with the string .local appended to it.  Otherwise
+     * the effective host name is the same as the host name.  Note
+     * that all effective host names contain at least one dot.
+     *
+     * @param origin origin where cookie is received from or being sent to.
+     * @return
+     */
+    private static CookieOrigin adjustEffectiveHost(final CookieOrigin origin) {
+        String host = origin.getHost();
+        
+        // Test if the host name appears to be a fully qualified DNS name, 
+        // IPv4 address or IPv6 address
+        boolean isLocalHost = true;
+        for (int i = 0; i < host.length(); i++) {
+            char ch = host.charAt(i);
+            if (ch == '.' || ch == ':') {
+                isLocalHost = false;
+                break;
+            }
+        }
+        if (isLocalHost) {
+            host += ".local";
+            return new CookieOrigin(
+                    host, 
+                    origin.getPort(), 
+                    origin.getPath(), 
+                    origin.isSecure());
+        } else {
+            return origin;
+        }
+    }
+
+    @Override
+    public int getVersion() {
+        return 1;
+    }
+
+    @Override
+    public Header getVersionHeader() {
+        CharArrayBuffer buffer = new CharArrayBuffer(40);
+        buffer.append(SM.COOKIE2);
+        buffer.append(": ");
+        buffer.append("$Version=");
+        buffer.append(Integer.toString(getVersion()));
+        return new BufferedHeader(buffer);
+    }
+    
+}
+
diff --git a/src/org/apache/http/impl/cookie/RFC2965SpecFactory.java b/src/org/apache/http/impl/cookie/RFC2965SpecFactory.java
new file mode 100644
index 0000000..4b3cc4d
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2965SpecFactory.java
@@ -0,0 +1,57 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965SpecFactory.java $
+ * $Revision: 581953 $
+ * $Date: 2007-10-04 08:53:50 -0700 (Thu, 04 Oct 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.CookieSpecFactory;
+import org.apache.http.cookie.params.CookieSpecPNames;
+import org.apache.http.params.HttpParams;
+
+/**
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class RFC2965SpecFactory implements CookieSpecFactory {    
+
+    public CookieSpec newInstance(final HttpParams params) {
+        if (params != null) {
+            return new RFC2965Spec(
+                    (String []) params.getParameter(CookieSpecPNames.DATE_PATTERNS), 
+                    params.getBooleanParameter(CookieSpecPNames.SINGLE_COOKIE_HEADER, false));
+        } else {
+            return new RFC2965Spec();
+        }
+    }
+
+}
diff --git a/src/org/apache/http/impl/cookie/RFC2965VersionAttributeHandler.java b/src/org/apache/http/impl/cookie/RFC2965VersionAttributeHandler.java
new file mode 100644
index 0000000..8ea8481
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2965VersionAttributeHandler.java
@@ -0,0 +1,96 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965VersionAttributeHandler.java $
+ * $Revision: 590695 $
+ * $Date: 2007-10-31 07:55:41 -0700 (Wed, 31 Oct 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+import org.apache.http.cookie.SetCookie2;
+
+/**
+ * <tt>"Version"</tt> cookie attribute handler for RFC 2965 cookie spec.
+ */
+public class RFC2965VersionAttributeHandler implements CookieAttributeHandler {
+
+    public RFC2965VersionAttributeHandler() {
+        super();
+    }
+    
+    /**
+     * Parse cookie version attribute.
+     */
+    public void parse(final SetCookie cookie, final String value)
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (value == null) {
+            throw new MalformedCookieException(
+                    "Missing value for version attribute");
+        }
+        int version = -1;
+        try {
+            version = Integer.parseInt(value);
+        } catch (NumberFormatException e) {
+            version = -1;
+        }
+        if (version < 0) {
+            throw new MalformedCookieException("Invalid cookie version.");
+        }
+        cookie.setVersion(version);
+    }
+
+    /**
+     * validate cookie version attribute. Version attribute is REQUIRED.
+     */
+    public void validate(final Cookie cookie, final CookieOrigin origin)
+            throws MalformedCookieException {
+        if (cookie == null) {
+            throw new IllegalArgumentException("Cookie may not be null");
+        }
+        if (cookie instanceof SetCookie2) {
+            if (cookie instanceof ClientCookie 
+                    && !((ClientCookie) cookie).containsAttribute(ClientCookie.VERSION_ATTR)) {
+                throw new MalformedCookieException(
+                        "Violates RFC 2965. Version attribute is required.");
+            }
+        }
+    }
+
+    public boolean match(final Cookie cookie, final CookieOrigin origin) {
+        return true;
+    }
+
+}
\ No newline at end of file
diff --git a/src/org/apache/http/impl/cookie/package.html b/src/org/apache/http/impl/cookie/package.html
new file mode 100644
index 0000000..e301283
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/package.html
@@ -0,0 +1,4 @@
+<body>
+
+</body>
+
diff --git a/src/org/apache/http/impl/entity/EntityDeserializer.java b/src/org/apache/http/impl/entity/EntityDeserializer.java
new file mode 100644
index 0000000..12c4756
--- /dev/null
+++ b/src/org/apache/http/impl/entity/EntityDeserializer.java
@@ -0,0 +1,115 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/entity/EntityDeserializer.java $
+ * $Revision: 560358 $
+ * $Date: 2007-07-27 12:30:42 -0700 (Fri, 27 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.entity;
+
+import java.io.IOException;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.entity.BasicHttpEntity;
+import org.apache.http.entity.ContentLengthStrategy;
+import org.apache.http.impl.io.ChunkedInputStream;
+import org.apache.http.impl.io.ContentLengthInputStream;
+import org.apache.http.impl.io.IdentityInputStream;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * Default implementation of an entity deserializer.
+ * <p>
+ * This entity deserializer currently supports only "chunked" and "identitiy" transfer-coding</a>
+ * </p>
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560358 $
+ * 
+ * @since 4.0
+ */
+public class EntityDeserializer {
+
+    private final ContentLengthStrategy lenStrategy;
+    
+    public EntityDeserializer(final ContentLengthStrategy lenStrategy) {
+        super();
+        if (lenStrategy == null) {
+            throw new IllegalArgumentException("Content length strategy may not be null");
+        }
+        this.lenStrategy = lenStrategy;
+    }
+
+    protected BasicHttpEntity doDeserialize(
+            final SessionInputBuffer inbuffer,
+            final HttpMessage message) throws HttpException, IOException {
+        BasicHttpEntity entity = new BasicHttpEntity();
+        
+        long len = this.lenStrategy.determineLength(message);
+        if (len == ContentLengthStrategy.CHUNKED) {
+            entity.setChunked(true);
+            entity.setContentLength(-1);
+            entity.setContent(new ChunkedInputStream(inbuffer));
+        } else if (len == ContentLengthStrategy.IDENTITY) {
+            entity.setChunked(false);
+            entity.setContentLength(-1);
+            entity.setContent(new IdentityInputStream(inbuffer));                            
+        } else {
+            entity.setChunked(false);
+            entity.setContentLength(len);
+            entity.setContent(new ContentLengthInputStream(inbuffer, len));
+        }
+        
+        Header contentTypeHeader = message.getFirstHeader(HTTP.CONTENT_TYPE);
+        if (contentTypeHeader != null) {
+            entity.setContentType(contentTypeHeader);    
+        }
+        Header contentEncodingHeader = message.getFirstHeader(HTTP.CONTENT_ENCODING);
+        if (contentEncodingHeader != null) {
+            entity.setContentEncoding(contentEncodingHeader);    
+        }
+        return entity;
+    }
+        
+    public HttpEntity deserialize(
+            final SessionInputBuffer inbuffer,
+            final HttpMessage message) throws HttpException, IOException {
+        if (inbuffer == null) {
+            throw new IllegalArgumentException("Session input buffer may not be null");
+        }
+        if (message == null) {
+            throw new IllegalArgumentException("HTTP message may not be null");
+        }
+        return doDeserialize(inbuffer, message);
+    }
+    
+}
diff --git a/src/org/apache/http/impl/entity/EntitySerializer.java b/src/org/apache/http/impl/entity/EntitySerializer.java
new file mode 100644
index 0000000..3221980
--- /dev/null
+++ b/src/org/apache/http/impl/entity/EntitySerializer.java
@@ -0,0 +1,101 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/entity/EntitySerializer.java $
+ * $Revision: 560343 $
+ * $Date: 2007-07-27 11:18:19 -0700 (Fri, 27 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.entity;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.entity.ContentLengthStrategy;
+import org.apache.http.impl.io.ChunkedOutputStream;
+import org.apache.http.impl.io.ContentLengthOutputStream;
+import org.apache.http.impl.io.IdentityOutputStream;
+import org.apache.http.io.SessionOutputBuffer;
+
+/**
+ * Default implementation of an entity serializer.
+ * <p>
+ * This entity serializer currently supports only "chunked" and "identitiy" transfer-coding</a>
+ * </p>
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560343 $
+ * 
+ * @since 4.0
+ */
+public class EntitySerializer {
+
+    private final ContentLengthStrategy lenStrategy;
+    
+    public EntitySerializer(final ContentLengthStrategy lenStrategy) {
+        super();
+        if (lenStrategy == null) {
+            throw new IllegalArgumentException("Content length strategy may not be null");
+        }
+        this.lenStrategy = lenStrategy;
+    }
+
+    protected OutputStream doSerialize(
+            final SessionOutputBuffer outbuffer,
+            final HttpMessage message) throws HttpException, IOException {
+        long len = this.lenStrategy.determineLength(message);
+        if (len == ContentLengthStrategy.CHUNKED) {
+            return new ChunkedOutputStream(outbuffer);
+        } else if (len == ContentLengthStrategy.IDENTITY) {
+            return new IdentityOutputStream(outbuffer);
+        } else {
+            return new ContentLengthOutputStream(outbuffer, len);
+        }
+    }
+
+    public void serialize(
+            final SessionOutputBuffer outbuffer,
+            final HttpMessage message,
+            final HttpEntity entity) throws HttpException, IOException {
+        if (outbuffer == null) {
+            throw new IllegalArgumentException("Session output buffer may not be null");
+        }
+        if (message == null) {
+            throw new IllegalArgumentException("HTTP message may not be null");
+        }
+        if (entity == null) {
+            throw new IllegalArgumentException("HTTP entity may not be null");
+        }
+        OutputStream outstream = doSerialize(outbuffer, message);
+        entity.writeTo(outstream);
+        outstream.close();
+    }
+    
+}
diff --git a/src/org/apache/http/impl/entity/LaxContentLengthStrategy.java b/src/org/apache/http/impl/entity/LaxContentLengthStrategy.java
new file mode 100644
index 0000000..9a0d238
--- /dev/null
+++ b/src/org/apache/http/impl/entity/LaxContentLengthStrategy.java
@@ -0,0 +1,260 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/entity/LaxContentLengthStrategy.java $
+ * $Revision: 576073 $
+ * $Date: 2007-09-16 03:53:13 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.entity;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.ParseException;
+import org.apache.http.ProtocolException;
+import org.apache.http.entity.ContentLengthStrategy;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * The lax implementation of the content length strategy.
+ * <p>
+ * This strategy conforms to the entity transfer rules outlined in  
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec4.4">Section 4.4</a>, 
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">Section 3.6</a>, 
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.41">Section 14.41</a>
+ * and <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec14.13">Section 14.13</a>
+ * of <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>, but is lenient 
+ * about unsupported transfer codecs and malformed content-length headers.
+ * </p>
+ * <h>4.4 Message Length</h>
+ * <p>
+ * The transfer-length of a message is the length of the message-body as it appears in the 
+ * message; that is, after any transfer-codings have been applied. When a message-body is 
+ * included with a message, the transfer-length of that body is determined by one of the 
+ * following (in order of precedence):
+ * </p>
+ * <p>
+ * 1.Any response message which "MUST NOT" include a message-body (such as the 1xx, 204, 
+ * and 304 responses and any response to a HEAD request) is always terminated by the first 
+ * empty line after the header fields, regardless of the entity-header fields present in the 
+ * message.
+ * </p>
+ * <p>
+ * 2.If a Transfer-Encoding header field (section 14.41) is present and has any value other 
+ * than "identity", then the transfer-length is defined by use of the "chunked" transfer-
+ * coding (section 3.6), unless the message is terminated by closing the connection.
+ * </p>
+ * <p>
+ * 3.If a Content-Length header field (section 14.13) is present, its decimal value in 
+ * OCTETs represents both the entity-length and the transfer-length. The Content-Length 
+ * header field MUST NOT be sent if these two lengths are different (i.e., if a 
+ * Transfer-Encoding
+ * </p>
+ * <pre>
+ *    header field is present). If a message is received with both a
+ *    Transfer-Encoding header field and a Content-Length header field,
+ *    the latter MUST be ignored.
+ * </pre>
+ * <p>
+ * 4.If the message uses the media type "multipart/byteranges", and the ransfer-length is not 
+ * otherwise specified, then this self- elimiting media type defines the transfer-length. 
+ * This media type UST NOT be used unless the sender knows that the recipient can arse it; the 
+ * presence in a request of a Range header with ultiple byte- range specifiers from a 1.1 
+ * client implies that the lient can parse multipart/byteranges responses.
+ * </p>
+ * <pre>
+ *     A range header might be forwarded by a 1.0 proxy that does not
+ *     understand multipart/byteranges; in this case the server MUST
+ *     delimit the message using methods defined in items 1,3 or 5 of
+ *     this section.
+ * </pre>
+ * <p>
+ * 5.By the server closing the connection. (Closing the connection cannot be used to indicate 
+ * the end of a request body, since that would leave no possibility for the server to send back 
+ * a response.)
+ * </p>
+ * <p>
+ * For compatibility with HTTP/1.0 applications, HTTP/1.1 requests containing a message-body 
+ * MUST include a valid Content-Length header field unless the server is known to be HTTP/1.1 
+ * compliant. If a request contains a message-body and a Content-Length is not given, the 
+ * server SHOULD respond with 400 (bad request) if it cannot determine the length of the 
+ * message, or with 411 (length required) if it wishes to insist on receiving a valid 
+ * Content-Length.
+ * </p>
+ * <p>All HTTP/1.1 applications that receive entities MUST accept the "chunked" transfer-coding 
+ * (section 3.6), thus allowing this mechanism to be used for messages when the message 
+ * length cannot be determined in advance. 
+ * </p>
+ * <h>3.6 Transfer Codings</h>
+ * <p>
+ * Transfer-coding values are used to indicate an encoding transformation that 
+ * has been, can be, or may need to be applied to an entity-body in order to ensure 
+ * "safe transport" through the network. This differs from a content coding in that 
+ * the transfer-coding is a property of the message, not of the original entity.
+ * </p>
+ * <pre>
+ * transfer-coding         = "chunked" | transfer-extension
+ * transfer-extension      = token *( ";" parameter )
+ * </pre>
+ * <p>
+ * Parameters are in the form of attribute/value pairs.
+ * </p>
+ * <pre>
+ * parameter               = attribute "=" value
+ * attribute               = token
+ * value                   = token | quoted-string
+ * </pre>
+ * <p>
+ * All transfer-coding values are case-insensitive. HTTP/1.1 uses transfer-coding values in 
+ * the TE header field (section 14.39) and in the Transfer-Encoding header field (section 14.41).
+ * </p>
+ * <p>
+ * Whenever a transfer-coding is applied to a message-body, the set of transfer-codings MUST 
+ * include "chunked", unless the message is terminated by closing the connection. When the 
+ * "chunked" transfer-coding is used, it MUST be the last transfer-coding applied to the 
+ * message-body. The "chunked" transfer-coding MUST NOT be applied more than once to a 
+ * message-body. These rules allow the recipient to determine the transfer-length of the 
+ * message (section 4.4).
+ * </p>
+ * <h>14.41 Transfer-Encoding</h>
+ * <p>
+ * The Transfer-Encoding general-header field indicates what (if any) type of transformation has 
+ * been applied to the message body in order to safely transfer it between the sender and the 
+ * recipient. This differs from the content-coding in that the transfer-coding is a property of 
+ * the message, not of the entity.
+ * </p>
+ * <pre>
+ *   Transfer-Encoding       = "Transfer-Encoding" ":" 1#transfer-coding
+ * </pre>
+ * <p>
+ * If multiple encodings have been applied to an entity, the transfer- codings MUST be listed in 
+ * the order in which they were applied. Additional information about the encoding parameters 
+ * MAY be provided by other entity-header fields not defined by this specification.
+ * </p> 
+ * <h>14.13 Content-Length</h>
+ * <p>
+ * The Content-Length entity-header field indicates the size of the entity-body, in decimal 
+ * number of OCTETs, sent to the recipient or, in the case of the HEAD method, the size of 
+ * the entity-body that would have been sent had the request been a GET.
+ * </p>
+ * <pre>
+ *   Content-Length    = "Content-Length" ":" 1*DIGIT
+ * </pre>
+ * <p>
+ * Applications SHOULD use this field to indicate the transfer-length of the message-body, 
+ * unless this is prohibited by the rules in section 4.4. 
+ * </p>
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 576073 $
+ * 
+ * @since 4.0
+ */
+public class LaxContentLengthStrategy implements ContentLengthStrategy {
+
+    public LaxContentLengthStrategy() {
+        super();
+    }
+
+    public long determineLength(final HttpMessage message) throws HttpException {
+        if (message == null) {
+            throw new IllegalArgumentException("HTTP message may not be null");
+        }
+
+        HttpParams params = message.getParams(); 
+        boolean strict = params.isParameterTrue(CoreProtocolPNames.STRICT_TRANSFER_ENCODING);
+        
+        Header transferEncodingHeader = message.getFirstHeader(HTTP.TRANSFER_ENCODING);
+        Header contentLengthHeader = message.getFirstHeader(HTTP.CONTENT_LEN);
+        // We use Transfer-Encoding if present and ignore Content-Length.
+        // RFC2616, 4.4 item number 3
+        if (transferEncodingHeader != null) {
+            HeaderElement[] encodings = null;
+            try {
+                encodings = transferEncodingHeader.getElements();
+            } catch (ParseException px) {
+                throw new ProtocolException
+                    ("Invalid Transfer-Encoding header value: " +
+                     transferEncodingHeader, px);
+            }
+            if (strict) {
+                // Currently only chunk and identity are supported
+                for (int i = 0; i < encodings.length; i++) {
+                    String encoding = encodings[i].getName();
+                    if (encoding != null && encoding.length() > 0 
+                        && !encoding.equalsIgnoreCase(HTTP.CHUNK_CODING)
+                        && !encoding.equalsIgnoreCase(HTTP.IDENTITY_CODING)) {
+                        throw new ProtocolException("Unsupported transfer encoding: " + encoding);
+                    }
+                }
+            }
+            // The chunked encoding must be the last one applied RFC2616, 14.41
+            int len = encodings.length;
+            if (HTTP.IDENTITY_CODING.equalsIgnoreCase(transferEncodingHeader.getValue())) {
+                return IDENTITY;                            
+            } else if ((len > 0) && (HTTP.CHUNK_CODING.equalsIgnoreCase(
+                    encodings[len - 1].getName()))) { 
+                return CHUNKED;
+            } else {
+                if (strict) {
+                    throw new ProtocolException("Chunk-encoding must be the last one applied");
+                }
+                return IDENTITY;                            
+            }
+        } else if (contentLengthHeader != null) {
+            long contentlen = -1;
+            Header[] headers = message.getHeaders(HTTP.CONTENT_LEN);
+            if (strict && headers.length > 1) {
+                throw new ProtocolException("Multiple content length headers");
+            }
+            for (int i = headers.length - 1; i >= 0; i--) {
+                Header header = headers[i];
+                try {
+                    contentlen = Long.parseLong(header.getValue());
+                    break;
+                } catch (NumberFormatException e) {
+                    if (strict) {
+                        throw new ProtocolException("Invalid content length: " + header.getValue());
+                    }
+                }
+                // See if we can have better luck with another header, if present
+            }
+            if (contentlen >= 0) {
+                return contentlen;
+            } else {
+                return IDENTITY;
+            }
+        } else {
+            return IDENTITY;
+        }
+    }
+        
+}
diff --git a/src/org/apache/http/impl/entity/StrictContentLengthStrategy.java b/src/org/apache/http/impl/entity/StrictContentLengthStrategy.java
new file mode 100644
index 0000000..30be8e2
--- /dev/null
+++ b/src/org/apache/http/impl/entity/StrictContentLengthStrategy.java
@@ -0,0 +1,220 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/entity/StrictContentLengthStrategy.java $
+ * $Revision: 573949 $
+ * $Date: 2007-09-08 22:46:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.entity;
+
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolException;
+import org.apache.http.entity.ContentLengthStrategy;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * The strict implementation of the content length strategy.
+ * <p>
+ * This entity generator comforms to the entity transfer rules outlined in the 
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec4.4">Section 4.4</a>, 
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">Section 3.6</a>, 
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.41">Section 14.41</a>
+ * and <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec14.13">Section 14.13</a>
+ * of <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>
+ * </p>
+ * <h>4.4 Message Length</h>
+ * <p>
+ * The transfer-length of a message is the length of the message-body as it appears in the 
+ * message; that is, after any transfer-codings have been applied. When a message-body is 
+ * included with a message, the transfer-length of that body is determined by one of the 
+ * following (in order of precedence):
+ * </p>
+ * <p>
+ * 1.Any response message which "MUST NOT" include a message-body (such as the 1xx, 204, 
+ * and 304 responses and any response to a HEAD request) is always terminated by the first 
+ * empty line after the header fields, regardless of the entity-header fields present in the 
+ * message.
+ * </p>
+ * <p>
+ * 2.If a Transfer-Encoding header field (section 14.41) is present and has any value other 
+ * than "identity", then the transfer-length is defined by use of the "chunked" transfer-
+ * coding (section 3.6), unless the message is terminated by closing the connection.
+ * </p>
+ * <p>
+ * 3.If a Content-Length header field (section 14.13) is present, its decimal value in 
+ * OCTETs represents both the entity-length and the transfer-length. The Content-Length 
+ * header field MUST NOT be sent if these two lengths are different (i.e., if a 
+ * Transfer-Encoding
+ * </p>
+ * <pre>
+ *    header field is present). If a message is received with both a
+ *    Transfer-Encoding header field and a Content-Length header field,
+ *    the latter MUST be ignored.
+ * </pre>
+ * <p>
+ * 4.If the message uses the media type "multipart/byteranges", and the ransfer-length is not 
+ * otherwise specified, then this self- elimiting media type defines the transfer-length. 
+ * This media type UST NOT be used unless the sender knows that the recipient can arse it; the 
+ * presence in a request of a Range header with ultiple byte- range specifiers from a 1.1 
+ * client implies that the lient can parse multipart/byteranges responses.
+ * </p>
+ * <pre>
+ *     A range header might be forwarded by a 1.0 proxy that does not
+ *     understand multipart/byteranges; in this case the server MUST
+ *     delimit the message using methods defined in items 1,3 or 5 of
+ *     this section.
+ * </pre>
+ * <p>
+ * 5.By the server closing the connection. (Closing the connection cannot be used to indicate 
+ * the end of a request body, since that would leave no possibility for the server to send back 
+ * a response.)
+ * </p>
+ * <p>
+ * For compatibility with HTTP/1.0 applications, HTTP/1.1 requests containing a message-body 
+ * MUST include a valid Content-Length header field unless the server is known to be HTTP/1.1 
+ * compliant. If a request contains a message-body and a Content-Length is not given, the 
+ * server SHOULD respond with 400 (bad request) if it cannot determine the length of the 
+ * message, or with 411 (length required) if it wishes to insist on receiving a valid 
+ * Content-Length.
+ * </p>
+ * <p>All HTTP/1.1 applications that receive entities MUST accept the "chunked" transfer-coding 
+ * (section 3.6), thus allowing this mechanism to be used for messages when the message 
+ * length cannot be determined in advance. 
+ * </p>
+ * <h>3.6 Transfer Codings</h>
+ * <p>
+ * Transfer-coding values are used to indicate an encoding transformation that 
+ * has been, can be, or may need to be applied to an entity-body in order to ensure 
+ * "safe transport" through the network. This differs from a content coding in that 
+ * the transfer-coding is a property of the message, not of the original entity.
+ * </p>
+ * <pre>
+ * transfer-coding         = "chunked" | transfer-extension
+ * transfer-extension      = token *( ";" parameter )
+ * </pre>
+ * <p>
+ * Parameters are in the form of attribute/value pairs.
+ * </p>
+ * <pre>
+ * parameter               = attribute "=" value
+ * attribute               = token
+ * value                   = token | quoted-string
+ * </pre>
+ * <p>
+ * All transfer-coding values are case-insensitive. HTTP/1.1 uses transfer-coding values in 
+ * the TE header field (section 14.39) and in the Transfer-Encoding header field (section 14.41).
+ * </p>
+ * <p>
+ * Whenever a transfer-coding is applied to a message-body, the set of transfer-codings MUST 
+ * include "chunked", unless the message is terminated by closing the connection. When the 
+ * "chunked" transfer-coding is used, it MUST be the last transfer-coding applied to the 
+ * message-body. The "chunked" transfer-coding MUST NOT be applied more than once to a 
+ * message-body. These rules allow the recipient to determine the transfer-length of the 
+ * message (section 4.4).
+ * </p>
+ * <h>14.41 Transfer-Encoding</h>
+ * <p>
+ * The Transfer-Encoding general-header field indicates what (if any) type of transformation has 
+ * been applied to the message body in order to safely transfer it between the sender and the 
+ * recipient. This differs from the content-coding in that the transfer-coding is a property of 
+ * the message, not of the entity.
+ * </p>
+ * <pre>
+ *   Transfer-Encoding       = "Transfer-Encoding" ":" 1#transfer-coding
+ * </pre>
+ * <p>
+ * If multiple encodings have been applied to an entity, the transfer- codings MUST be listed in 
+ * the order in which they were applied. Additional information about the encoding parameters 
+ * MAY be provided by other entity-header fields not defined by this specification.
+ * </p> 
+ * <h>14.13 Content-Length</h>
+ * <p>
+ * The Content-Length entity-header field indicates the size of the entity-body, in decimal 
+ * number of OCTETs, sent to the recipient or, in the case of the HEAD method, the size of 
+ * the entity-body that would have been sent had the request been a GET.
+ * </p>
+ * <pre>
+ *   Content-Length    = "Content-Length" ":" 1*DIGIT
+ * </pre>
+ * <p>
+ * Applications SHOULD use this field to indicate the transfer-length of the message-body, 
+ * unless this is prohibited by the rules in section 4.4. 
+ * </p>
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573949 $
+ * 
+ * @since 4.0
+ */
+public class StrictContentLengthStrategy implements ContentLengthStrategy {
+
+    public StrictContentLengthStrategy() {
+        super();
+    }
+    
+    public long determineLength(final HttpMessage message) throws HttpException {
+        if (message == null) {
+            throw new IllegalArgumentException("HTTP message may not be null");
+        }
+        // Although Transfer-Encoding is specified as a list, in practice
+        // it is either missing or has the single value "chunked". So we
+        // treat it as a single-valued header here.
+        Header transferEncodingHeader = message.getFirstHeader(HTTP.TRANSFER_ENCODING);
+        Header contentLengthHeader = message.getFirstHeader(HTTP.CONTENT_LEN);
+        if (transferEncodingHeader != null) {
+            String s = transferEncodingHeader.getValue();
+            if (HTTP.CHUNK_CODING.equalsIgnoreCase(s)) {
+                if (message.getProtocolVersion().lessEquals(HttpVersion.HTTP_1_0)) {
+                    throw new ProtocolException(
+                            "Chunked transfer encoding not allowed for " + 
+                            message.getProtocolVersion());
+                }
+                return CHUNKED;
+            } else if (HTTP.IDENTITY_CODING.equalsIgnoreCase(s)) {
+                return IDENTITY;
+            } else {
+                throw new ProtocolException(
+                        "Unsupported transfer encoding: " + s);
+            }
+        } else if (contentLengthHeader != null) {
+            String s = contentLengthHeader.getValue();
+            try {
+                long len = Long.parseLong(s);
+                return len;
+            } catch (NumberFormatException e) {
+                throw new ProtocolException("Invalid content length: " + s);
+            }
+        } else {
+            return IDENTITY; 
+        }
+    }
+
+}
diff --git a/src/org/apache/http/impl/entity/package.html b/src/org/apache/http/impl/entity/package.html
new file mode 100644
index 0000000..9ac4481
--- /dev/null
+++ b/src/org/apache/http/impl/entity/package.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/entity/package.html $
+ * $Revision: 496072 $
+ * $Date: 2007-01-14 04:22:55 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Default implementations for interfaces in
+{@link org.apache.http.entity org.apache.http.entity}.
+
+</body>
+</html>
diff --git a/src/org/apache/http/impl/io/AbstractMessageParser.java b/src/org/apache/http/impl/io/AbstractMessageParser.java
new file mode 100644
index 0000000..679bcd1
--- /dev/null
+++ b/src/org/apache/http/impl/io/AbstractMessageParser.java
@@ -0,0 +1,187 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/AbstractMessageParser.java $
+ * $Revision: 576077 $
+ * $Date: 2007-09-16 04:50:22 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.ParseException;
+import org.apache.http.ProtocolException;
+import org.apache.http.io.HttpMessageParser;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.message.LineParser;
+import org.apache.http.message.BasicLineParser;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Message parser base class.
+ * 
+ * @author Michael Becke
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public abstract class AbstractMessageParser implements HttpMessageParser {
+
+    private final SessionInputBuffer sessionBuffer;
+    private final int maxHeaderCount;
+    private final int maxLineLen;
+    protected final LineParser lineParser;
+
+
+    public AbstractMessageParser(
+            final SessionInputBuffer buffer,
+            final LineParser parser,
+            final HttpParams params) {
+        super();
+        if (buffer == null) {
+            throw new IllegalArgumentException("Session input buffer may not be null");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        this.sessionBuffer = buffer;
+        this.maxHeaderCount = params.getIntParameter(
+                CoreConnectionPNames.MAX_HEADER_COUNT, -1);
+        this.maxLineLen = params.getIntParameter(
+                CoreConnectionPNames.MAX_LINE_LENGTH, -1);
+        this.lineParser = (parser != null) ? parser : BasicLineParser.DEFAULT;
+    }
+
+    /**
+     * Parses HTTP headers from the data receiver stream according to the generic 
+     * format as given in Section 3.1 of RFC 822, RFC-2616 Section 4 and 19.3.
+     *  
+     * @param inbuffer Session input buffer
+     * @param maxHeaderCount maximum number of headers allowed. If the number
+     *  of headers received from the data stream exceeds maxCount value, an
+     *  IOException will be thrown. Setting this parameter to a negative value
+     *  or zero  will disable the check.
+     * @param maxLineLen maximum number of characters for a header line,
+     *                   including the continuation lines
+     * @return array of HTTP headers
+     * 
+     * @throws HttpException
+     * @throws IOException
+     */
+    public static Header[] parseHeaders(
+            final SessionInputBuffer inbuffer,
+            int maxHeaderCount,
+            int maxLineLen,
+            LineParser parser)
+        throws HttpException, IOException {
+
+        if (inbuffer == null) {
+            throw new IllegalArgumentException("Session input buffer may not be null");
+        }
+        if (parser == null)
+            parser = BasicLineParser.DEFAULT;
+
+        ArrayList headerLines = new ArrayList();
+
+        CharArrayBuffer current = null;
+        CharArrayBuffer previous = null;
+        for (;;) {
+            if (current == null) {
+                current = new CharArrayBuffer(64);
+            } else {
+                current.clear();
+            }
+            int l = inbuffer.readLine(current);
+            if (l == -1 || current.length() < 1) {
+                break;
+            }
+            // Parse the header name and value
+            // Check for folded headers first
+            // Detect LWS-char see HTTP/1.0 or HTTP/1.1 Section 2.2
+            // discussion on folded headers
+            if ((current.charAt(0) == ' ' || current.charAt(0) == '\t') && previous != null) {
+                // we have continuation folded header
+                // so append value
+                int i = 0;
+                while (i < current.length()) {
+                    char ch = current.charAt(i);
+                    if (ch != ' ' && ch != '\t') {
+                        break;
+                    }
+                    i++;
+                }
+                if (maxLineLen > 0 
+                        && previous.length() + 1 + current.length() - i > maxLineLen) {
+                    throw new IOException("Maximum line length limit exceeded");
+                }
+                previous.append(' ');
+                previous.append(current, i, current.length() - i);
+            } else {
+                headerLines.add(current);
+                previous = current;
+                current = null;
+            }
+            if (maxHeaderCount > 0 && headerLines.size() >= maxHeaderCount) {
+                throw new IOException("Maximum header count exceeded");
+            }
+        }
+        Header[] headers = new Header[headerLines.size()];
+        for (int i = 0; i < headerLines.size(); i++) {
+            CharArrayBuffer buffer = (CharArrayBuffer) headerLines.get(i);
+            try {
+                headers[i] = parser.parseHeader(buffer);
+            } catch (ParseException ex) {
+                throw new ProtocolException(ex.getMessage());
+            }
+        }
+        return headers;
+    }
+
+    protected abstract HttpMessage parseHead(SessionInputBuffer sessionBuffer) 
+        throws IOException, HttpException, ParseException;
+
+    public HttpMessage parse() throws IOException, HttpException {
+        HttpMessage message = null;
+        try {
+            message = parseHead(this.sessionBuffer);
+        } catch (ParseException px) {
+            throw new ProtocolException(px.getMessage(), px);
+        }
+        Header[] headers = AbstractMessageParser.parseHeaders(
+                this.sessionBuffer, 
+                this.maxHeaderCount,
+                this.maxLineLen,
+                this.lineParser);
+        message.setHeaders(headers);
+        return message;
+    }
+    
+}
diff --git a/src/org/apache/http/impl/io/AbstractMessageWriter.java b/src/org/apache/http/impl/io/AbstractMessageWriter.java
new file mode 100644
index 0000000..f9644ce
--- /dev/null
+++ b/src/org/apache/http/impl/io/AbstractMessageWriter.java
@@ -0,0 +1,85 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/AbstractMessageWriter.java $
+ * $Revision: 569673 $
+ * $Date: 2007-08-25 06:58:51 -0700 (Sat, 25 Aug 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.io.HttpMessageWriter;
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.message.LineFormatter;
+import org.apache.http.message.BasicLineFormatter;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.CharArrayBuffer;
+
+public abstract class AbstractMessageWriter implements HttpMessageWriter {
+    
+    protected final SessionOutputBuffer sessionBuffer;    
+    protected final CharArrayBuffer lineBuf;
+    protected final LineFormatter lineFormatter;
+
+    public AbstractMessageWriter(final SessionOutputBuffer buffer,
+                                 final LineFormatter formatter,
+                                 final HttpParams params) {
+        super();
+        if (buffer == null) {
+            throw new IllegalArgumentException("Session input buffer may not be null");
+        }
+        this.sessionBuffer = buffer;
+        this.lineBuf = new CharArrayBuffer(128);
+        this.lineFormatter = (formatter != null) ?
+            formatter : BasicLineFormatter.DEFAULT;
+    }
+    
+    protected abstract void writeHeadLine(HttpMessage message)
+        throws IOException
+        ;
+
+    public void write(
+            final HttpMessage message) throws IOException, HttpException {
+        if (message == null) {
+            throw new IllegalArgumentException("HTTP message may not be null");
+        }
+        writeHeadLine(message);
+        for (Iterator it = message.headerIterator(); it.hasNext(); ) {
+            Header header = (Header) it.next();
+            this.sessionBuffer.writeLine
+                (lineFormatter.formatHeader(this.lineBuf, header));
+        }
+        this.lineBuf.clear();
+        this.sessionBuffer.writeLine(this.lineBuf);
+    }
+    
+}
diff --git a/src/org/apache/http/impl/io/AbstractSessionInputBuffer.java b/src/org/apache/http/impl/io/AbstractSessionInputBuffer.java
new file mode 100644
index 0000000..eb007a9
--- /dev/null
+++ b/src/org/apache/http/impl/io/AbstractSessionInputBuffer.java
@@ -0,0 +1,271 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/AbstractSessionInputBuffer.java $
+ * $Revision: 576077 $
+ * $Date: 2007-09-16 04:50:22 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.io.HttpTransportMetrics;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.ByteArrayBuffer;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Abstract base class for session input buffers that stream data 
+ * from a {@link InputStream}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ */
+public abstract class AbstractSessionInputBuffer implements SessionInputBuffer {
+
+    private InputStream instream;
+    private byte[] buffer;
+    private int bufferpos;
+    private int bufferlen;
+    
+    private ByteArrayBuffer linebuffer = null;
+    
+    private String charset = HTTP.US_ASCII;
+    private boolean ascii = true;
+    private int maxLineLen = -1;
+    
+    private HttpTransportMetricsImpl metrics;
+    
+    protected void init(final InputStream instream, int buffersize, final HttpParams params) {
+        if (instream == null) {
+            throw new IllegalArgumentException("Input stream may not be null");
+        }
+        if (buffersize <= 0) {
+            throw new IllegalArgumentException("Buffer size may not be negative or zero");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        this.instream = instream;
+        this.buffer = new byte[buffersize];
+        this.bufferpos = 0;
+        this.bufferlen = 0;
+        this.linebuffer = new ByteArrayBuffer(buffersize);
+        this.charset = HttpProtocolParams.getHttpElementCharset(params);
+        this.ascii = this.charset.equalsIgnoreCase(HTTP.US_ASCII)
+                     || this.charset.equalsIgnoreCase(HTTP.ASCII);
+        this.maxLineLen = params.getIntParameter(CoreConnectionPNames.MAX_LINE_LENGTH, -1);
+        this.metrics = new HttpTransportMetricsImpl();
+    }
+    
+    protected int fillBuffer() throws IOException {
+        // compact the buffer if necessary
+        if (this.bufferpos > 0) {
+            int len = this.bufferlen - this.bufferpos;
+            if (len > 0) {
+                System.arraycopy(this.buffer, this.bufferpos, this.buffer, 0, len);
+            }
+            this.bufferpos = 0;
+            this.bufferlen = len;
+        }
+        int l;
+        int off = this.bufferlen;
+        int len = this.buffer.length - off;
+        l = this.instream.read(this.buffer, off, len);
+        if (l == -1) {
+            return -1;
+        } else {
+            this.bufferlen = off + l;
+            this.metrics.incrementBytesTransferred(l);
+            return l;
+        }
+    }
+
+    protected boolean hasBufferedData() {
+        return this.bufferpos < this.bufferlen;
+    }
+    
+    public int read() throws IOException {
+        int noRead = 0;
+        while (!hasBufferedData()) {
+            noRead = fillBuffer();
+            if (noRead == -1) {
+                return -1;
+            }
+        }
+        return this.buffer[this.bufferpos++] & 0xff;
+    }
+    
+    public int read(final byte[] b, int off, int len) throws IOException {
+        if (b == null) {
+            return 0;
+        }
+        int noRead = 0;
+        while (!hasBufferedData()) {
+            noRead = fillBuffer();
+            if (noRead == -1) {
+                return -1;
+            }
+        }
+        int chunk = this.bufferlen - this.bufferpos;
+        if (chunk > len) {
+            chunk = len;
+        }
+        System.arraycopy(this.buffer, this.bufferpos, b, off, chunk);
+        this.bufferpos += chunk;
+        return chunk;
+    }
+    
+    public int read(final byte[] b) throws IOException {
+        if (b == null) {
+            return 0;
+        }
+        return read(b, 0, b.length);
+    }
+    
+    private int locateLF() {
+        for (int i = this.bufferpos; i < this.bufferlen; i++) {
+            if (this.buffer[i] == HTTP.LF) {
+                return i;
+            }
+        }
+        return -1;
+    }
+    
+    public int readLine(final CharArrayBuffer charbuffer) throws IOException {
+        if (charbuffer == null) {
+            throw new IllegalArgumentException("Char array buffer may not be null");
+        }
+        this.linebuffer.clear();
+        int noRead = 0;
+        boolean retry = true;
+        while (retry) {
+            // attempt to find end of line (LF)
+            int i = locateLF();
+            if (i != -1) {
+                // end of line found. 
+                if (this.linebuffer.isEmpty()) {
+                    // the entire line is preset in the read buffer
+                    return lineFromReadBuffer(charbuffer, i);
+                }
+                retry = false;
+                int len = i + 1 - this.bufferpos;
+                this.linebuffer.append(this.buffer, this.bufferpos, len);
+                this.bufferpos = i + 1;
+            } else {
+                // end of line not found
+                if (hasBufferedData()) {
+                    int len = this.bufferlen - this.bufferpos;
+                    this.linebuffer.append(this.buffer, this.bufferpos, len);
+                    this.bufferpos = this.bufferlen;
+                }
+                noRead = fillBuffer();
+                if (noRead == -1) {
+                    retry = false;
+                }
+            }
+            if (this.maxLineLen > 0 && this.linebuffer.length() >= this.maxLineLen) {
+                throw new IOException("Maximum line length limit exceeded");
+            }
+        }
+        if (noRead == -1 && this.linebuffer.isEmpty()) {
+            // indicate the end of stream
+            return -1;
+        }
+        return lineFromLineBuffer(charbuffer);
+    }
+    
+    private int lineFromLineBuffer(final CharArrayBuffer charbuffer) 
+            throws IOException {
+        // discard LF if found
+        int l = this.linebuffer.length(); 
+        if (l > 0) {
+            if (this.linebuffer.byteAt(l - 1) == HTTP.LF) {
+                l--;
+                this.linebuffer.setLength(l);
+            }
+            // discard CR if found
+            if (l > 0) {
+                if (this.linebuffer.byteAt(l - 1) == HTTP.CR) {
+                    l--;
+                    this.linebuffer.setLength(l);
+                }
+            }
+        }
+        l = this.linebuffer.length(); 
+        if (this.ascii) {
+            charbuffer.append(this.linebuffer, 0, l);
+        } else {
+            // This is VERY memory inefficient, BUT since non-ASCII charsets are 
+            // NOT meant to be used anyway, there's no point optimizing it
+            String s = new String(this.linebuffer.buffer(), 0, l, this.charset);
+            charbuffer.append(s);
+        }
+        return l;
+    }
+    
+    private int lineFromReadBuffer(final CharArrayBuffer charbuffer, int pos) 
+            throws IOException {
+        int off = this.bufferpos;
+        int len;
+        this.bufferpos = pos + 1;
+        if (pos > 0 && this.buffer[pos - 1] == HTTP.CR) {
+            // skip CR if found
+            pos--;
+        }
+        len = pos - off;
+        if (this.ascii) {
+            charbuffer.append(this.buffer, off, len);
+        } else {
+            // This is VERY memory inefficient, BUT since non-ASCII charsets are 
+            // NOT meant to be used anyway, there's no point optimizing it
+            String s = new String(this.buffer, off, len, this.charset);
+            charbuffer.append(s);
+        }
+        return len;
+    }
+    
+    public String readLine() throws IOException {
+        CharArrayBuffer charbuffer = new CharArrayBuffer(64);
+        int l = readLine(charbuffer);
+        if (l != -1) {
+            return charbuffer.toString();
+        } else {
+            return null;
+        }
+    }
+    
+    public HttpTransportMetrics getMetrics() {
+        return this.metrics;
+    }
+    
+}
diff --git a/src/org/apache/http/impl/io/AbstractSessionOutputBuffer.java b/src/org/apache/http/impl/io/AbstractSessionOutputBuffer.java
new file mode 100644
index 0000000..bf4e56e
--- /dev/null
+++ b/src/org/apache/http/impl/io/AbstractSessionOutputBuffer.java
@@ -0,0 +1,179 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/AbstractSessionOutputBuffer.java $
+ * $Revision: 652091 $
+ * $Date: 2008-04-29 13:41:07 -0700 (Tue, 29 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.io.HttpTransportMetrics;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.ByteArrayBuffer;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Abstract base class for session output buffers that stream data
+ * to an {@link OutputStream}. 
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ */
+public abstract class AbstractSessionOutputBuffer implements SessionOutputBuffer {
+
+    private static final byte[] CRLF = new byte[] {HTTP.CR, HTTP.LF};
+    
+    private static final int MAX_CHUNK = 256;
+    
+    private OutputStream outstream;
+    private ByteArrayBuffer buffer;
+        
+    private String charset = HTTP.US_ASCII;
+    private boolean ascii = true;
+    
+    private HttpTransportMetricsImpl metrics;
+    
+    protected void init(final OutputStream outstream, int buffersize, final HttpParams params) {
+        if (outstream == null) {
+            throw new IllegalArgumentException("Input stream may not be null");
+        }
+        if (buffersize <= 0) {
+            throw new IllegalArgumentException("Buffer size may not be negative or zero");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        this.outstream = outstream;
+        this.buffer = new ByteArrayBuffer(buffersize);
+        this.charset = HttpProtocolParams.getHttpElementCharset(params); 
+        this.ascii = this.charset.equalsIgnoreCase(HTTP.US_ASCII)
+                     || this.charset.equalsIgnoreCase(HTTP.ASCII);
+        this.metrics = new HttpTransportMetricsImpl();
+    }
+    
+    protected void flushBuffer() throws IOException {
+        int len = this.buffer.length();
+        if (len > 0) {
+            this.outstream.write(this.buffer.buffer(), 0, len);
+            this.buffer.clear();
+            this.metrics.incrementBytesTransferred(len);
+        }
+    }
+    
+    public void flush() throws IOException {
+        flushBuffer();
+        this.outstream.flush();
+    }
+    
+    public void write(final byte[] b, int off, int len) throws IOException {
+        if (b == null) {
+            return;
+        }
+        // Do not want to buffer largish chunks
+        // if the byte array is larger then MAX_CHUNK
+        // write it directly to the output stream
+        if (len > MAX_CHUNK || len > this.buffer.capacity()) {
+            // flush the buffer
+            flushBuffer();
+            // write directly to the out stream
+            this.outstream.write(b, off, len);
+            this.metrics.incrementBytesTransferred(len);
+        } else {
+            // Do not let the buffer grow unnecessarily
+            int freecapacity = this.buffer.capacity() - this.buffer.length();
+            if (len > freecapacity) {
+                // flush the buffer
+                flushBuffer();
+            }
+            // buffer
+            this.buffer.append(b, off, len);
+        }
+    }
+    
+    public void write(final byte[] b) throws IOException {
+        if (b == null) {
+            return;
+        }
+        write(b, 0, b.length);
+    }
+    
+    public void write(int b) throws IOException {
+        if (this.buffer.isFull()) {
+            flushBuffer();
+        }
+        this.buffer.append(b);
+    }
+    
+    public void writeLine(final String s) throws IOException {
+        if (s == null) {
+            return;
+        }
+        if (s.length() > 0) {
+            write(s.getBytes(this.charset));
+        }
+        write(CRLF);
+    }
+    
+    public void writeLine(final CharArrayBuffer s) throws IOException {
+        if (s == null) {
+            return;
+        }
+        if (this.ascii) {
+            int off = 0;
+            int remaining = s.length();
+            while (remaining > 0) {
+                int chunk = this.buffer.capacity() - this.buffer.length();
+                chunk = Math.min(chunk, remaining);
+                if (chunk > 0) {
+                    this.buffer.append(s, off, chunk);
+                }
+                if (this.buffer.isFull()) {
+                    flushBuffer();
+                }
+                off += chunk;
+                remaining -= chunk;
+            }
+        } else {
+            // This is VERY memory inefficient, BUT since non-ASCII charsets are 
+            // NOT meant to be used anyway, there's no point optimizing it
+            byte[] tmp = s.toString().getBytes(this.charset);
+            write(tmp);
+        }
+        write(CRLF);
+    }
+    
+    public HttpTransportMetrics getMetrics() {
+        return this.metrics;
+    }
+    
+}
diff --git a/src/org/apache/http/impl/io/ChunkedInputStream.java b/src/org/apache/http/impl/io/ChunkedInputStream.java
new file mode 100644
index 0000000..60cae90
--- /dev/null
+++ b/src/org/apache/http/impl/io/ChunkedInputStream.java
@@ -0,0 +1,294 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/ChunkedInputStream.java $
+ * $Revision: 569843 $
+ * $Date: 2007-08-26 10:05:40 -0700 (Sun, 26 Aug 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.MalformedChunkCodingException;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.CharArrayBuffer;
+import org.apache.http.util.ExceptionUtils;
+
+/**
+ * Implements chunked transfer coding.
+ * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>,
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">section 3.6.1</a>.
+ * It transparently coalesces chunks of a HTTP stream that uses chunked
+ * transfer coding. After the stream is read to the end, it provides access
+ * to the trailers, if any.
+ * <p>
+ * Note that this class NEVER closes the underlying stream, even when close
+ * gets called.  Instead, it will read until the "end" of its chunking on
+ * close, which allows for the seamless execution of subsequent HTTP 1.1
+ * requests, while not requiring the client to remember to read the entire
+ * contents of the response.
+ * </p>
+ *
+ * @author Ortwin Glueck
+ * @author Sean C. Sullivan
+ * @author Martin Elwin
+ * @author Eric Johnson
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author Michael Becke
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ *
+ */
+public class ChunkedInputStream extends InputStream {
+
+    /** The session input buffer */
+    private SessionInputBuffer in;
+
+    private final CharArrayBuffer buffer;
+    
+    /** The chunk size */
+    private int chunkSize;
+
+    /** The current position within the current chunk */
+    private int pos;
+
+    /** True if we'are at the beginning of stream */
+    private boolean bof = true;
+
+    /** True if we've reached the end of stream */
+    private boolean eof = false;
+
+    /** True if this stream is closed */
+    private boolean closed = false;
+    
+    private Header[] footers = new Header[] {};
+
+    public ChunkedInputStream(final SessionInputBuffer in) {
+        super();
+        if (in == null) {
+            throw new IllegalArgumentException("Session input buffer may not be null");
+        }
+        this.in = in;
+        this.pos = 0;
+        this.buffer = new CharArrayBuffer(16);
+    }
+
+    /**
+     * <p> Returns all the data in a chunked stream in coalesced form. A chunk
+     * is followed by a CRLF. The method returns -1 as soon as a chunksize of 0
+     * is detected.</p>
+     * 
+     * <p> Trailer headers are read automcatically at the end of the stream and
+     * can be obtained with the getResponseFooters() method.</p>
+     *
+     * @return -1 of the end of the stream has been reached or the next data
+     * byte
+     * @throws IOException If an IO problem occurs
+     */
+    public int read() throws IOException {
+        if (this.closed) {
+            throw new IOException("Attempted read from closed stream.");
+        }
+        if (this.eof) {
+            return -1;
+        } 
+        if (this.pos >= this.chunkSize) {
+            nextChunk();
+            if (this.eof) { 
+                return -1;
+            }
+        }
+        pos++;
+        return in.read();
+    }
+
+    /**
+     * Read some bytes from the stream.
+     * @param b The byte array that will hold the contents from the stream.
+     * @param off The offset into the byte array at which bytes will start to be
+     * placed.
+     * @param len the maximum number of bytes that can be returned.
+     * @return The number of bytes returned or -1 if the end of stream has been
+     * reached.
+     * @see java.io.InputStream#read(byte[], int, int)
+     * @throws IOException if an IO problem occurs.
+     */
+    public int read (byte[] b, int off, int len) throws IOException {
+
+        if (closed) {
+            throw new IOException("Attempted read from closed stream.");
+        }
+
+        if (eof) { 
+            return -1;
+        }
+        if (pos >= chunkSize) {
+            nextChunk();
+            if (eof) { 
+                return -1;
+            }
+        }
+        len = Math.min(len, chunkSize - pos);
+        int count = in.read(b, off, len);
+        pos += count;
+        return count;
+    }
+
+    /**
+     * Read some bytes from the stream.
+     * @param b The byte array that will hold the contents from the stream.
+     * @return The number of bytes returned or -1 if the end of stream has been
+     * reached.
+     * @see java.io.InputStream#read(byte[])
+     * @throws IOException if an IO problem occurs.
+     */
+    public int read (byte[] b) throws IOException {
+        return read(b, 0, b.length);
+    }
+
+    /**
+     * Read the next chunk.
+     * @throws IOException If an IO error occurs.
+     */
+    private void nextChunk() throws IOException {
+        chunkSize = getChunkSize();
+        if (chunkSize < 0) {
+            throw new MalformedChunkCodingException("Negative chunk size");
+        }
+        bof = false;
+        pos = 0;
+        if (chunkSize == 0) {
+            eof = true;
+            parseTrailerHeaders();
+        }
+    }
+
+    /**
+     * Expects the stream to start with a chunksize in hex with optional
+     * comments after a semicolon. The line must end with a CRLF: "a3; some
+     * comment\r\n" Positions the stream at the start of the next line.
+     *
+     * @param in The new input stream.
+     * @param required <tt>true<tt/> if a valid chunk must be present,
+     *                 <tt>false<tt/> otherwise.
+     * 
+     * @return the chunk size as integer
+     * 
+     * @throws IOException when the chunk size could not be parsed
+     */
+    private int getChunkSize() throws IOException {
+        // skip CRLF
+        if (!bof) {
+            int cr = in.read();
+            int lf = in.read();
+            if ((cr != HTTP.CR) || (lf != HTTP.LF)) { 
+                throw new MalformedChunkCodingException(
+                    "CRLF expected at end of chunk");
+            }
+        }
+        //parse data
+        this.buffer.clear();
+        int i = this.in.readLine(this.buffer);
+        if (i == -1) {
+            throw new MalformedChunkCodingException(
+                    "Chunked stream ended unexpectedly");
+        }
+        int separator = this.buffer.indexOf(';');
+        if (separator < 0) {
+            separator = this.buffer.length();
+        }
+        try {
+            return Integer.parseInt(this.buffer.substringTrimmed(0, separator), 16);
+        } catch (NumberFormatException e) {
+            throw new MalformedChunkCodingException("Bad chunk header");
+        }
+    }
+
+    /**
+     * Reads and stores the Trailer headers.
+     * @throws IOException If an IO problem occurs
+     */
+    private void parseTrailerHeaders() throws IOException {
+        try {
+            this.footers = AbstractMessageParser.parseHeaders
+                (in, -1, -1, null);
+        } catch (HttpException e) {
+            IOException ioe = new MalformedChunkCodingException("Invalid footer: " 
+                    + e.getMessage());
+            ExceptionUtils.initCause(ioe, e); 
+            throw ioe;
+        }
+    }
+
+    /**
+     * Upon close, this reads the remainder of the chunked message,
+     * leaving the underlying socket at a position to start reading the
+     * next response without scanning.
+     * @throws IOException If an IO problem occurs.
+     */
+    public void close() throws IOException {
+        if (!closed) {
+            try {
+                if (!eof) {
+                    exhaustInputStream(this);
+                }
+            } finally {
+                eof = true;
+                closed = true;
+            }
+        }
+    }
+
+    public Header[] getFooters() {
+        return (Header[])this.footers.clone();
+    }
+    
+    /**
+     * Exhaust an input stream, reading until EOF has been encountered.
+     *
+     * <p>Note that this function is intended as a non-public utility.
+     * This is a little weird, but it seemed silly to make a utility
+     * class for this one function, so instead it is just static and
+     * shared that way.</p>
+     *
+     * @param inStream The {@link InputStream} to exhaust.
+     * @throws IOException If an IO problem occurs
+     */
+    static void exhaustInputStream(final InputStream inStream) throws IOException {
+        // read and discard the remainder of the message
+        byte buffer[] = new byte[1024];
+        while (inStream.read(buffer) >= 0) {
+            ;
+        }
+    }
+
+}
diff --git a/src/org/apache/http/impl/io/ChunkedOutputStream.java b/src/org/apache/http/impl/io/ChunkedOutputStream.java
new file mode 100644
index 0000000..5ee7dd6
--- /dev/null
+++ b/src/org/apache/http/impl/io/ChunkedOutputStream.java
@@ -0,0 +1,191 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/ChunkedOutputStream.java $
+ * $Revision: 645081 $
+ * $Date: 2008-04-05 04:36:42 -0700 (Sat, 05 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.http.io.SessionOutputBuffer;
+
+/**
+ * Implements chunked transfer coding.
+ * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>,
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">section 3.6.1</a>.
+ * Writes are buffered to an internal buffer (2048 default size).
+ * 
+ * @author Mohammad Rezaei (Goldman, Sachs &amp; Co.)
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public class ChunkedOutputStream extends OutputStream {
+
+    // ----------------------------------------------------- Instance Variables
+    private final SessionOutputBuffer out;
+
+    private byte[] cache;
+
+    private int cachePosition = 0;
+
+    private boolean wroteLastChunk = false;
+
+    /** True if the stream is closed. */
+    private boolean closed = false;
+    
+    // ----------------------------------------------------------- Constructors
+    /**
+     * Wraps a session output buffer and chunks the output.
+     * @param out the session output buffer to wrap
+     * @param bufferSize minimum chunk size (excluding last chunk)
+     * @throws IOException
+     */
+    public ChunkedOutputStream(final SessionOutputBuffer out, int bufferSize)
+            throws IOException {
+        super();
+        this.cache = new byte[bufferSize];
+        this.out = out;
+    }
+
+    /**
+     * Wraps a session output buffer and chunks the output. The default buffer 
+     * size of 2048 was chosen because the chunk overhead is less than 0.5%
+     *
+     * @param out       the output buffer to wrap
+     * @throws IOException
+     */
+    public ChunkedOutputStream(final SessionOutputBuffer out) 
+            throws IOException {
+        this(out, 2048);
+    }
+
+    // ----------------------------------------------------------- Internal methods
+    /**
+     * Writes the cache out onto the underlying stream
+     * @throws IOException
+     */
+    protected void flushCache() throws IOException {
+        if (this.cachePosition > 0) {
+            this.out.writeLine(Integer.toHexString(this.cachePosition));
+            this.out.write(this.cache, 0, this.cachePosition);
+            this.out.writeLine("");
+            this.cachePosition = 0;
+        }
+    }
+
+    /**
+     * Writes the cache and bufferToAppend to the underlying stream
+     * as one large chunk
+     * @param bufferToAppend
+     * @param off
+     * @param len
+     * @throws IOException
+     */
+    protected void flushCacheWithAppend(byte bufferToAppend[], int off, int len) throws IOException {
+        this.out.writeLine(Integer.toHexString(this.cachePosition + len));
+        this.out.write(this.cache, 0, this.cachePosition);
+        this.out.write(bufferToAppend, off, len);
+        this.out.writeLine("");
+        this.cachePosition = 0;
+    }
+
+    protected void writeClosingChunk() throws IOException {
+        // Write the final chunk.
+        this.out.writeLine("0");
+        this.out.writeLine("");
+    }
+
+    // ----------------------------------------------------------- Public Methods
+    /**
+     * Must be called to ensure the internal cache is flushed and the closing chunk is written.
+     * @throws IOException
+     */
+    public void finish() throws IOException {
+        if (!this.wroteLastChunk) {
+            flushCache();
+            writeClosingChunk();
+            this.wroteLastChunk = true;
+        }
+    }
+
+    // -------------------------------------------- OutputStream Methods
+    public void write(int b) throws IOException {
+        if (this.closed) {
+            throw new IOException("Attempted write to closed stream.");
+        }
+        this.cache[this.cachePosition] = (byte) b;
+        this.cachePosition++;
+        if (this.cachePosition == this.cache.length) flushCache();
+    }
+
+    /**
+     * Writes the array. If the array does not fit within the buffer, it is
+     * not split, but rather written out as one large chunk.
+     * @param b
+     * @throws IOException
+     */
+    public void write(byte b[]) throws IOException {
+        write(b, 0, b.length);
+    }
+
+    public void write(byte src[], int off, int len) throws IOException {
+        if (this.closed) {
+            throw new IOException("Attempted write to closed stream.");
+        }
+        if (len >= this.cache.length - this.cachePosition) {
+            flushCacheWithAppend(src, off, len);
+        } else {
+            System.arraycopy(src, off, cache, this.cachePosition, len);
+            this.cachePosition += len;
+        }
+    }
+
+    /**
+     * Flushes the content buffer and the underlying stream.
+     * @throws IOException
+     */
+    public void flush() throws IOException {
+        flushCache();
+        this.out.flush();
+    }
+
+    /**
+     * Finishes writing to the underlying stream, but does NOT close the underlying stream.
+     * @throws IOException
+     */
+    public void close() throws IOException {
+        if (!this.closed) {
+            this.closed = true;
+            finish();
+            this.out.flush();
+        }
+    }
+}
diff --git a/src/org/apache/http/impl/io/ContentLengthInputStream.java b/src/org/apache/http/impl/io/ContentLengthInputStream.java
new file mode 100644
index 0000000..3b19c5b
--- /dev/null
+++ b/src/org/apache/http/impl/io/ContentLengthInputStream.java
@@ -0,0 +1,220 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/ContentLengthInputStream.java $
+ * $Revision: 652091 $
+ * $Date: 2008-04-29 13:41:07 -0700 (Tue, 29 Apr 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.http.io.SessionInputBuffer;
+
+/**
+ * Stream that cuts off after a specified number of bytes.
+ * Note that this class NEVER closes the underlying stream, even when close
+ * gets called.  Instead, it will read until the "end" of its chunking on
+ * close, which allows for the seamless execution of subsequent HTTP 1.1
+ * requests, while not requiring the client to remember to read the entire
+ * contents of the response.
+ *
+ * <p>Implementation note: Choices abound. One approach would pass
+ * through the {@link InputStream#mark} and {@link InputStream#reset} calls to
+ * the underlying stream.  That's tricky, though, because you then have to
+ * start duplicating the work of keeping track of how much a reset rewinds.
+ * Further, you have to watch out for the "readLimit", and since the semantics
+ * for the readLimit leave room for differing implementations, you might get
+ * into a lot of trouble.</p>
+ *
+ * <p>Alternatively, you could make this class extend
+ * {@link java.io.BufferedInputStream}
+ * and then use the protected members of that class to avoid duplicated effort.
+ * That solution has the side effect of adding yet another possible layer of
+ * buffering.</p>
+ *
+ * <p>Then, there is the simple choice, which this takes - simply don't
+ * support {@link InputStream#mark} and {@link InputStream#reset}.  That choice
+ * has the added benefit of keeping this class very simple.</p>
+ *
+ * @author Ortwin Glueck
+ * @author Eric Johnson
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ *
+ * @since 4.0
+ */
+public class ContentLengthInputStream extends InputStream {
+    
+    private static final int BUFFER_SIZE = 2048;
+    /**
+     * The maximum number of bytes that can be read from the stream. Subsequent
+     * read operations will return -1.
+     */
+    private long contentLength;
+
+    /** The current position */
+    private long pos = 0;
+
+    /** True if the stream is closed. */
+    private boolean closed = false;
+
+    /**
+     * Wrapped input stream that all calls are delegated to.
+     */
+    private SessionInputBuffer in = null;
+
+    /**
+     * Creates a new length limited stream
+     *
+     * @param in The session input buffer to wrap
+     * @param contentLength The maximum number of bytes that can be read from
+     * the stream. Subsequent read operations will return -1.
+     */
+    public ContentLengthInputStream(final SessionInputBuffer in, long contentLength) {
+        super();
+        if (in == null) {
+            throw new IllegalArgumentException("Input stream may not be null");
+        }
+        if (contentLength < 0) {
+            throw new IllegalArgumentException("Content length may not be negative");
+        }
+        this.in = in;
+        this.contentLength = contentLength;
+    }
+
+    /**
+     * <p>Reads until the end of the known length of content.</p>
+     *
+     * <p>Does not close the underlying socket input, but instead leaves it
+     * primed to parse the next response.</p>
+     * @throws IOException If an IO problem occurs.
+     */
+    public void close() throws IOException {
+        if (!closed) {
+            try {
+                byte buffer[] = new byte[BUFFER_SIZE];
+                while (read(buffer) >= 0) {
+                }
+            } finally {
+                // close after above so that we don't throw an exception trying
+                // to read after closed!
+                closed = true;
+            }
+        }
+    }
+
+
+    /**
+     * Read the next byte from the stream
+     * @return The next byte or -1 if the end of stream has been reached.
+     * @throws IOException If an IO problem occurs
+     * @see java.io.InputStream#read()
+     */
+    public int read() throws IOException {
+        if (closed) {
+            throw new IOException("Attempted read from closed stream.");
+        }
+
+        if (pos >= contentLength) {
+            return -1;
+        }
+        pos++;
+        return this.in.read();
+    }
+
+    /**
+     * Does standard {@link InputStream#read(byte[], int, int)} behavior, but
+     * also notifies the watcher when the contents have been consumed.
+     *
+     * @param b     The byte array to fill.
+     * @param off   Start filling at this position.
+     * @param len   The number of bytes to attempt to read.
+     * @return The number of bytes read, or -1 if the end of content has been
+     *  reached.
+     *
+     * @throws java.io.IOException Should an error occur on the wrapped stream.
+     */
+    public int read (byte[] b, int off, int len) throws java.io.IOException {
+        if (closed) {
+            throw new IOException("Attempted read from closed stream.");
+        }
+
+        if (pos >= contentLength) {
+            return -1;
+        }
+
+        if (pos + len > contentLength) {
+            len = (int) (contentLength - pos);
+        }
+        int count = this.in.read(b, off, len);
+        pos += count;
+        return count;
+    }
+
+
+    /**
+     * Read more bytes from the stream.
+     * @param b The byte array to put the new data in.
+     * @return The number of bytes read into the buffer.
+     * @throws IOException If an IO problem occurs
+     * @see java.io.InputStream#read(byte[])
+     */
+    public int read(byte[] b) throws IOException {
+        return read(b, 0, b.length);
+    }
+
+    /**
+     * Skips and discards a number of bytes from the input stream.
+     * @param n The number of bytes to skip.
+     * @return The actual number of bytes skipped. <= 0 if no bytes
+     * are skipped.
+     * @throws IOException If an error occurs while skipping bytes.
+     * @see InputStream#skip(long)
+     */
+    public long skip(long n) throws IOException {
+        if (n <= 0) {
+            return 0;
+        }
+        byte[] buffer = new byte[BUFFER_SIZE];
+        // make sure we don't skip more bytes than are 
+        // still available
+        long remaining = Math.min(n, this.contentLength - this.pos); 
+        // skip and keep track of the bytes actually skipped
+        long count = 0;
+        while (remaining > 0) {
+            int l = read(buffer, 0, (int)Math.min(BUFFER_SIZE, remaining));
+            if (l == -1) {
+                break;
+            }
+            count += l;
+            remaining -= l;
+        }
+        this.pos += count;
+        return count;
+    }
+}
diff --git a/src/org/apache/http/impl/io/ContentLengthOutputStream.java b/src/org/apache/http/impl/io/ContentLengthOutputStream.java
new file mode 100644
index 0000000..afcb883
--- /dev/null
+++ b/src/org/apache/http/impl/io/ContentLengthOutputStream.java
@@ -0,0 +1,132 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/ContentLengthOutputStream.java $
+ * $Revision: 560343 $
+ * $Date: 2007-07-27 11:18:19 -0700 (Fri, 27 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.http.io.SessionOutputBuffer;
+
+/**
+ * A stream wrapper that closes itself after a defined number of bytes.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560343 $
+ * 
+ * @since 4.0
+ */
+public class ContentLengthOutputStream extends OutputStream {
+    
+    /**
+     * Wrapped session outbut buffer.
+     */
+    private final SessionOutputBuffer out;
+
+    /**
+     * The maximum number of bytes that can be written the stream. Subsequent
+     * write operations will be ignored.
+     */
+    private final long contentLength;
+
+    /** Total bytes written */
+    private long total = 0;
+
+    /** True if the stream is closed. */
+    private boolean closed = false;
+
+    /**
+     * Creates a new length limited stream
+     *
+     * @param out The data transmitter to wrap
+     * @param contentLength The maximum number of bytes that can be written to
+     * the stream. Subsequent write operations will be ignored.
+     * 
+     * @since 4.0
+     */
+    public ContentLengthOutputStream(final SessionOutputBuffer out, long contentLength) {
+        super();
+        if (out == null) {
+            throw new IllegalArgumentException("Session output buffer may not be null");
+        }
+        if (contentLength < 0) {
+            throw new IllegalArgumentException("Content length may not be negative");
+        }
+        this.out = out;
+        this.contentLength = contentLength;
+    }
+
+    /**
+     * <p>Does not close the underlying socket output.</p>
+     * 
+     * @throws IOException If an I/O problem occurs.
+     */
+    public void close() throws IOException {
+        if (!this.closed) {
+            this.closed = true;
+            this.out.flush();
+        }
+    }
+
+    public void flush() throws IOException {
+        this.out.flush();
+    }
+
+    public void write(byte[] b, int off, int len) throws IOException {
+        if (this.closed) {
+            throw new IOException("Attempted write to closed stream.");
+        }
+        if (this.total < this.contentLength) {
+            long max = this.contentLength - this.total;
+            if (len > max) {
+                len = (int) max;
+            }
+            this.out.write(b, off, len);
+            this.total += len;
+        }
+    }
+
+    public void write(byte[] b) throws IOException {
+        write(b, 0, b.length);
+    }
+
+    public void write(int b) throws IOException {
+        if (this.closed) {
+            throw new IOException("Attempted write to closed stream.");
+        }
+        if (this.total < this.contentLength) {
+            this.out.write(b);
+            this.total++;
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/impl/io/HttpRequestParser.java b/src/org/apache/http/impl/io/HttpRequestParser.java
new file mode 100644
index 0000000..a7bae6d
--- /dev/null
+++ b/src/org/apache/http/impl/io/HttpRequestParser.java
@@ -0,0 +1,80 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/HttpRequestParser.java $
+ * $Revision: 589374 $
+ * $Date: 2007-10-28 09:25:07 -0700 (Sun, 28 Oct 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+
+import org.apache.http.ConnectionClosedException;
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.HttpRequestFactory;
+import org.apache.http.RequestLine;
+import org.apache.http.ParseException;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.message.LineParser;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.CharArrayBuffer;
+
+public class HttpRequestParser extends AbstractMessageParser {
+    
+    private final HttpRequestFactory requestFactory;
+    private final CharArrayBuffer lineBuf;
+    
+    public HttpRequestParser(
+            final SessionInputBuffer buffer, 
+            final LineParser parser,
+            final HttpRequestFactory requestFactory,
+            final HttpParams params) {
+        super(buffer, parser, params);
+        if (requestFactory == null) {
+            throw new IllegalArgumentException("Request factory may not be null");
+        }
+        this.requestFactory = requestFactory;
+        this.lineBuf = new CharArrayBuffer(128);
+    }
+
+    protected HttpMessage parseHead(
+            final SessionInputBuffer sessionBuffer)
+        throws IOException, HttpException, ParseException {
+
+        this.lineBuf.clear();
+        int i = sessionBuffer.readLine(this.lineBuf);
+        if (i == -1) {
+            throw new ConnectionClosedException("Client closed connection"); 
+        }
+        ParserCursor cursor = new ParserCursor(0, this.lineBuf.length());
+        RequestLine requestline = this.lineParser.parseRequestLine(this.lineBuf, cursor);
+        return this.requestFactory.newHttpRequest(requestline);
+    }
+    
+}
diff --git a/src/org/apache/http/impl/io/HttpRequestWriter.java b/src/org/apache/http/impl/io/HttpRequestWriter.java
new file mode 100644
index 0000000..b784e2d
--- /dev/null
+++ b/src/org/apache/http/impl/io/HttpRequestWriter.java
@@ -0,0 +1,59 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/HttpRequestWriter.java $
+ * $Revision: 569673 $
+ * $Date: 2007-08-25 06:58:51 -0700 (Sat, 25 Aug 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+
+import org.apache.http.HttpMessage;
+import org.apache.http.HttpRequest;
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.message.LineFormatter;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.CharArrayBuffer;
+
+public class HttpRequestWriter extends AbstractMessageWriter {
+
+    public HttpRequestWriter(final SessionOutputBuffer buffer,
+                             final LineFormatter formatter,
+                             final HttpParams params) {
+        super(buffer, formatter, params);
+    }
+    
+    protected void writeHeadLine(final HttpMessage message)
+        throws IOException {
+
+        final CharArrayBuffer buffer = lineFormatter.formatRequestLine
+            (this.lineBuf, ((HttpRequest) message).getRequestLine());
+        this.sessionBuffer.writeLine(buffer);
+    }
+
+}
diff --git a/src/org/apache/http/impl/io/HttpResponseParser.java b/src/org/apache/http/impl/io/HttpResponseParser.java
new file mode 100644
index 0000000..575aa18
--- /dev/null
+++ b/src/org/apache/http/impl/io/HttpResponseParser.java
@@ -0,0 +1,81 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/HttpResponseParser.java $
+ * $Revision: 589374 $
+ * $Date: 2007-10-28 09:25:07 -0700 (Sun, 28 Oct 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.HttpResponseFactory;
+import org.apache.http.NoHttpResponseException;
+import org.apache.http.StatusLine;
+import org.apache.http.ParseException;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.message.LineParser;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.CharArrayBuffer;
+
+public class HttpResponseParser extends AbstractMessageParser {
+    
+    private final HttpResponseFactory responseFactory;
+    private final CharArrayBuffer lineBuf;
+    
+    public HttpResponseParser(
+            final SessionInputBuffer buffer,
+            final LineParser parser,
+            final HttpResponseFactory responseFactory,
+            final HttpParams params) {
+        super(buffer, parser, params);
+        if (responseFactory == null) {
+            throw new IllegalArgumentException("Response factory may not be null");
+        }
+        this.responseFactory = responseFactory;
+        this.lineBuf = new CharArrayBuffer(128);
+    }
+
+    protected HttpMessage parseHead(
+            final SessionInputBuffer sessionBuffer)
+        throws IOException, HttpException, ParseException {
+
+        this.lineBuf.clear();
+        int i = sessionBuffer.readLine(this.lineBuf);
+        if (i == -1) {
+            throw new NoHttpResponseException("The target server failed to respond");
+        }
+        //create the status line from the status string
+        ParserCursor cursor = new ParserCursor(0, this.lineBuf.length());
+        StatusLine statusline = lineParser.parseStatusLine(this.lineBuf, cursor);
+        return this.responseFactory.newHttpResponse(statusline, null);
+    }
+
+}
diff --git a/src/org/apache/http/impl/io/HttpResponseWriter.java b/src/org/apache/http/impl/io/HttpResponseWriter.java
new file mode 100644
index 0000000..f88791e
--- /dev/null
+++ b/src/org/apache/http/impl/io/HttpResponseWriter.java
@@ -0,0 +1,59 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/HttpResponseWriter.java $
+ * $Revision: 569673 $
+ * $Date: 2007-08-25 06:58:51 -0700 (Sat, 25 Aug 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+
+import org.apache.http.HttpMessage;
+import org.apache.http.HttpResponse;
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.message.LineFormatter;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.CharArrayBuffer;
+
+public class HttpResponseWriter extends AbstractMessageWriter {
+
+    public HttpResponseWriter(final SessionOutputBuffer buffer,
+                              final LineFormatter formatter,
+                              final HttpParams params) {
+        super(buffer, formatter, params);
+    }
+    
+    protected void writeHeadLine(final HttpMessage message)
+        throws IOException {
+
+        final CharArrayBuffer buffer = lineFormatter.formatStatusLine
+            (this.lineBuf, ((HttpResponse) message).getStatusLine());
+        this.sessionBuffer.writeLine(buffer);
+    }
+
+}
diff --git a/src/org/apache/http/impl/io/HttpTransportMetricsImpl.java b/src/org/apache/http/impl/io/HttpTransportMetricsImpl.java
new file mode 100644
index 0000000..53e6772
--- /dev/null
+++ b/src/org/apache/http/impl/io/HttpTransportMetricsImpl.java
@@ -0,0 +1,63 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/HttpTransportMetricsImpl.java $
+ * $Revision: 539755 $
+ * $Date: 2007-05-19 07:05:02 -0700 (Sat, 19 May 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import org.apache.http.io.HttpTransportMetrics;
+
+/**
+ * Default implementation of {@link HttpTransportMetrics}.
+ */
+public class HttpTransportMetricsImpl implements HttpTransportMetrics {
+
+    private long bytesTransferred = 0;
+    
+    public HttpTransportMetricsImpl() {
+        super();
+    }
+    
+    public long getBytesTransferred() {
+        return this.bytesTransferred;
+    }
+
+    public void setBytesTransferred(long count) {
+        this.bytesTransferred = count;
+    }
+
+    public void incrementBytesTransferred(long count) {
+        this.bytesTransferred += count;
+    }
+
+    public void reset() {
+        this.bytesTransferred = 0;
+    }
+    
+}
diff --git a/src/org/apache/http/impl/io/IdentityInputStream.java b/src/org/apache/http/impl/io/IdentityInputStream.java
new file mode 100644
index 0000000..390d5b7
--- /dev/null
+++ b/src/org/apache/http/impl/io/IdentityInputStream.java
@@ -0,0 +1,90 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/IdentityInputStream.java $
+ * $Revision: 560358 $
+ * $Date: 2007-07-27 12:30:42 -0700 (Fri, 27 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.http.io.SessionInputBuffer;
+
+/**
+ * A stream for reading from a {@link SessionInputBuffer session input buffer}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560358 $
+ * 
+ * @since 4.0
+ */
+public class IdentityInputStream extends InputStream {
+    
+    private final SessionInputBuffer in;
+    
+    private boolean closed = false;
+    
+    public IdentityInputStream(final SessionInputBuffer in) {
+        super();
+        if (in == null) {
+            throw new IllegalArgumentException("Session input buffer may not be null");
+        }
+        this.in = in;
+    }
+    
+    public int available() throws IOException {
+        if (!this.closed && this.in.isDataAvailable(10)) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+    
+    public void close() throws IOException {
+        this.closed = true;
+    }
+
+    public int read() throws IOException {
+        if (this.closed) {
+            return -1;
+        } else {
+            return this.in.read();
+        }
+    }
+    
+    public int read(final byte[] b, int off, int len) throws IOException {
+        if (this.closed) {
+            return -1;
+        } else {
+            return this.in.read(b, off, len);
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/impl/io/IdentityOutputStream.java b/src/org/apache/http/impl/io/IdentityOutputStream.java
new file mode 100644
index 0000000..10b64f7
--- /dev/null
+++ b/src/org/apache/http/impl/io/IdentityOutputStream.java
@@ -0,0 +1,100 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/IdentityOutputStream.java $
+ * $Revision: 560343 $
+ * $Date: 2007-07-27 11:18:19 -0700 (Fri, 27 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.http.io.SessionOutputBuffer;
+
+/**
+ * A stream for writing with an "identity" transport encoding.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560343 $
+ * 
+ * @since 4.0
+ */
+public class IdentityOutputStream extends OutputStream {
+    
+    /**
+     * Wrapped session output buffer.
+     */
+    private final SessionOutputBuffer out;
+
+    /** True if the stream is closed. */
+    private boolean closed = false;
+
+    public IdentityOutputStream(final SessionOutputBuffer out) {
+        super();
+        if (out == null) {
+            throw new IllegalArgumentException("Session output buffer may not be null");
+        }
+        this.out = out;
+    }
+
+    /**
+     * <p>Does not close the underlying socket output.</p>
+     * 
+     * @throws IOException If an I/O problem occurs.
+     */
+    public void close() throws IOException {
+        if (!this.closed) {
+            this.closed = true;
+            this.out.flush();
+        }
+    }
+
+    public void flush() throws IOException {
+        this.out.flush();
+    }
+
+    public void write(byte[] b, int off, int len) throws IOException {
+        if (this.closed) {
+            throw new IOException("Attempted write to closed stream.");
+        }
+        this.out.write(b, off, len);
+    }
+
+    public void write(byte[] b) throws IOException {
+        write(b, 0, b.length);
+    }
+
+    public void write(int b) throws IOException {
+        if (this.closed) {
+            throw new IOException("Attempted write to closed stream.");
+        }
+        this.out.write(b);
+    }
+    
+}
diff --git a/src/org/apache/http/impl/io/SocketInputBuffer.java b/src/org/apache/http/impl/io/SocketInputBuffer.java
new file mode 100644
index 0000000..925e80a
--- /dev/null
+++ b/src/org/apache/http/impl/io/SocketInputBuffer.java
@@ -0,0 +1,115 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/SocketInputBuffer.java $
+ * $Revision: 560358 $
+ * $Date: 2007-07-27 12:30:42 -0700 (Fri, 27 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.Socket;
+
+import org.apache.http.params.HttpParams;
+
+
+/**
+ * {@link Socket} bound session input buffer.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560358 $
+ * 
+ * @since 4.0
+ */
+public class SocketInputBuffer extends AbstractSessionInputBuffer {
+
+    static private final Class SOCKET_TIMEOUT_CLASS = SocketTimeoutExceptionClass();
+
+    /**
+     * Returns <code>SocketTimeoutExceptionClass<code> or <code>null</code> if the class
+     * does not exist.
+     * 
+     * @return <code>SocketTimeoutExceptionClass<code>, or <code>null</code> if unavailable.
+     */ 
+    static private Class SocketTimeoutExceptionClass() {
+        try {
+            return Class.forName("java.net.SocketTimeoutException");
+        } catch (ClassNotFoundException e) {
+            return null;
+        }
+    }
+
+    private static boolean isSocketTimeoutException(final InterruptedIOException e) {
+        if (SOCKET_TIMEOUT_CLASS != null) {
+            return SOCKET_TIMEOUT_CLASS.isInstance(e);
+        } else {
+            return true;
+        }
+    }
+    
+    private final Socket socket;
+    
+    public SocketInputBuffer(
+            final Socket socket, 
+            int buffersize, 
+            final HttpParams params) throws IOException {
+        super();
+        if (socket == null) {
+            throw new IllegalArgumentException("Socket may not be null");
+        }
+        this.socket = socket;
+        if (buffersize < 0) {
+            buffersize = socket.getReceiveBufferSize();
+        }
+        if (buffersize < 1024) {
+            buffersize = 1024;
+        }
+        init(socket.getInputStream(), buffersize, params);
+    }
+    
+    public boolean isDataAvailable(int timeout) throws IOException {
+        boolean result = hasBufferedData();
+        if (!result) {
+            int oldtimeout = this.socket.getSoTimeout();
+            try {
+                this.socket.setSoTimeout(timeout);
+                fillBuffer();
+                result = hasBufferedData();
+            } catch (InterruptedIOException e) {
+                if (!isSocketTimeoutException(e)) {
+                    throw e;
+                }
+            } finally {
+                socket.setSoTimeout(oldtimeout);
+            }
+        }
+        return result;
+    }    
+        
+}
diff --git a/src/org/apache/http/impl/io/SocketOutputBuffer.java b/src/org/apache/http/impl/io/SocketOutputBuffer.java
new file mode 100644
index 0000000..efb91e9
--- /dev/null
+++ b/src/org/apache/http/impl/io/SocketOutputBuffer.java
@@ -0,0 +1,79 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/SocketOutputBuffer.java $
+ * $Revision: 560358 $
+ * $Date: 2007-07-27 12:30:42 -0700 (Fri, 27 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import org.apache.http.params.HttpParams;
+
+
+/**
+ * {@link Socket} bound session output buffer.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560358 $
+ * 
+ * @since 4.0
+ */
+public class SocketOutputBuffer extends AbstractSessionOutputBuffer {
+
+    public SocketOutputBuffer(
+            final Socket socket, 
+            int buffersize,
+            final HttpParams params) throws IOException {
+        super();
+        if (socket == null) {
+            throw new IllegalArgumentException("Socket may not be null");
+        }
+        if (buffersize < 0) {
+            buffersize = socket.getReceiveBufferSize();
+// BEGIN android-changed
+            // Workaround for http://b/issue?id=1083103.
+            if (buffersize > 8096) {
+                buffersize = 8096;
+            }
+// END android-changed
+        }
+        if (buffersize < 1024) {
+            buffersize = 1024;
+        }
+
+// BEGIN android-changed
+        socket.setSendBufferSize(buffersize * 3);
+// END andrdoid-changed
+
+        init(socket.getOutputStream(), buffersize, params);
+    }
+    
+}
diff --git a/src/org/apache/http/impl/io/package.html b/src/org/apache/http/impl/io/package.html
new file mode 100644
index 0000000..48eb2c1
--- /dev/null
+++ b/src/org/apache/http/impl/io/package.html
@@ -0,0 +1,48 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/package.html $
+ * $Revision: 567360 $
+ * $Date: 2007-08-18 23:49:21 -0700 (Sat, 18 Aug 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Default implementations for interfaces in
+{@link org.apache.http.io org.apache.http.io}.
+
+<br/>
+
+There are implementations of the transport encodings used by HTTP,
+in particular the chunked encoding for
+{@link org.apache.http.impl.io.ChunkedOutputStream sending} and
+{@link org.apache.http.impl.io.ChunkedInputStream receiving} entities.
+
+</body>
+</html>
diff --git a/src/org/apache/http/impl/package.html b/src/org/apache/http/impl/package.html
new file mode 100644
index 0000000..6cec586
--- /dev/null
+++ b/src/org/apache/http/impl/package.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/package.html $
+ * $Revision: 496072 $
+ * $Date: 2007-01-14 04:22:55 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Default implementations for interfaces in
+{@link org.apache.http org.apache.http}.
+
+</body>
+</html>
diff --git a/src/org/apache/http/io/HttpMessageParser.java b/src/org/apache/http/io/HttpMessageParser.java
new file mode 100644
index 0000000..5c24736
--- /dev/null
+++ b/src/org/apache/http/io/HttpMessageParser.java
@@ -0,0 +1,53 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/io/HttpMessageParser.java $
+ * $Revision: 567370 $
+ * $Date: 2007-08-19 01:13:21 -0700 (Sun, 19 Aug 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.io;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+
+/**
+ * Generic message parser interface.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 567370 $
+ * 
+ * @since 4.0
+ */
+public interface HttpMessageParser {
+    
+    HttpMessage parse()
+        throws IOException, HttpException;
+
+}
diff --git a/src/org/apache/http/io/HttpMessageWriter.java b/src/org/apache/http/io/HttpMessageWriter.java
new file mode 100644
index 0000000..b6ac7c1
--- /dev/null
+++ b/src/org/apache/http/io/HttpMessageWriter.java
@@ -0,0 +1,53 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/io/HttpMessageWriter.java $
+ * $Revision: 567370 $
+ * $Date: 2007-08-19 01:13:21 -0700 (Sun, 19 Aug 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.io;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+
+/**
+ * Generic message writer interface.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 567370 $
+ * 
+ * @since 4.0
+ */
+public interface HttpMessageWriter {
+    
+    void write(HttpMessage message)
+        throws IOException, HttpException;
+    
+}
diff --git a/src/org/apache/http/io/HttpTransportMetrics.java b/src/org/apache/http/io/HttpTransportMetrics.java
new file mode 100644
index 0000000..f88e036
--- /dev/null
+++ b/src/org/apache/http/io/HttpTransportMetrics.java
@@ -0,0 +1,46 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/io/HttpTransportMetrics.java $
+ * $Revision: 536667 $
+ * $Date: 2007-05-09 14:48:41 -0700 (Wed, 09 May 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.io;
+
+public interface HttpTransportMetrics {
+    
+    /**
+     * Returns the number of bytes trasferred.
+     */
+    long getBytesTransferred(); 
+    
+    /**
+     * Resets the counts
+     */
+    void reset();
+    
+}
diff --git a/src/org/apache/http/io/SessionInputBuffer.java b/src/org/apache/http/io/SessionInputBuffer.java
new file mode 100644
index 0000000..d7824d9
--- /dev/null
+++ b/src/org/apache/http/io/SessionInputBuffer.java
@@ -0,0 +1,63 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/io/SessionInputBuffer.java $
+ * $Revision: 560528 $
+ * $Date: 2007-07-28 04:34:17 -0700 (Sat, 28 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.io;
+
+import java.io.IOException;
+
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Session input buffer for blocking connections.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560528 $
+ * 
+ * @since 4.0
+ */
+public interface SessionInputBuffer {
+    
+    int read(byte[] b, int off, int len) throws IOException; 
+    
+    int read(byte[] b) throws IOException; 
+    
+    int read() throws IOException; 
+    
+    int readLine(CharArrayBuffer buffer) throws IOException;
+    
+    String readLine() throws IOException;
+    
+    boolean isDataAvailable(int timeout) throws IOException; 
+
+    HttpTransportMetrics getMetrics();
+    
+}
diff --git a/src/org/apache/http/io/SessionOutputBuffer.java b/src/org/apache/http/io/SessionOutputBuffer.java
new file mode 100644
index 0000000..6587a26
--- /dev/null
+++ b/src/org/apache/http/io/SessionOutputBuffer.java
@@ -0,0 +1,63 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/io/SessionOutputBuffer.java $
+ * $Revision: 560528 $
+ * $Date: 2007-07-28 04:34:17 -0700 (Sat, 28 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.io;
+
+import java.io.IOException;
+
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Session output buffer for blocking connections.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560528 $
+ * 
+ * @since 4.0
+ */
+public interface SessionOutputBuffer {
+
+    void write(byte[] b, int off, int len) throws IOException;
+    
+    void write(byte[] b) throws IOException;
+    
+    void write(int b) throws IOException;
+    
+    void writeLine(String s) throws IOException;
+    
+    void writeLine(CharArrayBuffer buffer) throws IOException;
+    
+    void flush() throws IOException;
+    
+    HttpTransportMetrics getMetrics();
+    
+}
diff --git a/src/org/apache/http/io/package.html b/src/org/apache/http/io/package.html
new file mode 100644
index 0000000..da9fabf
--- /dev/null
+++ b/src/org/apache/http/io/package.html
@@ -0,0 +1,47 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/io/package.html $
+ * $Revision: 503192 $
+ * $Date: 2007-02-03 03:55:49 -0800 (Sat, 03 Feb 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+The transport layer abstraction of the HTTP components.
+
+This layer is used to transfer messages over connections.
+Connections are typically based on sockets: plain or secure,
+direct or via SOCKS hosts, with bandwidth throttling, or
+whatever else you might think of.
+However, opening connections is not within the responsibility
+of HttpCore.
+
+</body>
+</html>
diff --git a/src/org/apache/http/message/AbstractHttpMessage.java b/src/org/apache/http/message/AbstractHttpMessage.java
new file mode 100644
index 0000000..d8a6962
--- /dev/null
+++ b/src/org/apache/http/message/AbstractHttpMessage.java
@@ -0,0 +1,166 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/AbstractHttpMessage.java $
+ * $Revision: 620287 $
+ * $Date: 2008-02-10 07:15:53 -0800 (Sun, 10 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import java.util.Iterator;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.HttpMessage;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.BasicHttpParams;
+
+/**
+ * Basic implementation of an HTTP message that can be modified.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 620287 $
+ * 
+ * @since 4.0
+ */
+public abstract class AbstractHttpMessage implements HttpMessage {
+    
+    protected HeaderGroup headergroup;
+    
+    protected HttpParams params;
+    
+    protected AbstractHttpMessage(final HttpParams params) {
+        super();
+        this.headergroup = new HeaderGroup();
+        this.params = params;
+    }
+
+    protected AbstractHttpMessage() {
+        this(null);
+    }
+
+    // non-javadoc, see interface HttpMessage
+    public boolean containsHeader(String name) {
+        return this.headergroup.containsHeader(name);
+    }
+    
+    // non-javadoc, see interface HttpMessage
+    public Header[] getHeaders(final String name) {
+        return this.headergroup.getHeaders(name);
+    }
+
+    // non-javadoc, see interface HttpMessage
+    public Header getFirstHeader(final String name) {
+        return this.headergroup.getFirstHeader(name);
+    }
+
+    // non-javadoc, see interface HttpMessage
+    public Header getLastHeader(final String name) {
+        return this.headergroup.getLastHeader(name);
+    }
+
+    // non-javadoc, see interface HttpMessage
+    public Header[] getAllHeaders() {
+        return this.headergroup.getAllHeaders();
+    }
+    
+    // non-javadoc, see interface HttpMessage
+    public void addHeader(final Header header) {
+        this.headergroup.addHeader(header);
+    }
+
+    // non-javadoc, see interface HttpMessage
+    public void addHeader(final String name, final String value) {
+        if (name == null) {
+            throw new IllegalArgumentException("Header name may not be null");
+        }
+        this.headergroup.addHeader(new BasicHeader(name, value));
+    }
+
+    // non-javadoc, see interface HttpMessage
+    public void setHeader(final Header header) {
+        this.headergroup.updateHeader(header);
+    }
+
+    // non-javadoc, see interface HttpMessage
+    public void setHeader(final String name, final String value) {
+        if (name == null) {
+            throw new IllegalArgumentException("Header name may not be null");
+        }
+        this.headergroup.updateHeader(new BasicHeader(name, value));
+    }
+
+    // non-javadoc, see interface HttpMessage
+    public void setHeaders(final Header[] headers) {
+        this.headergroup.setHeaders(headers);
+    }
+
+    // non-javadoc, see interface HttpMessage
+    public void removeHeader(final Header header) {
+        this.headergroup.removeHeader(header);
+    }
+    
+    // non-javadoc, see interface HttpMessage
+    public void removeHeaders(final String name) {
+        if (name == null) {
+            return;
+        }
+        for (Iterator i = this.headergroup.iterator(); i.hasNext(); ) {
+            Header header = (Header) i.next();
+            if (name.equalsIgnoreCase(header.getName())) {
+                i.remove();
+            }
+        }
+    }
+    
+    // non-javadoc, see interface HttpMessage
+    public HeaderIterator headerIterator() {
+        return this.headergroup.iterator();
+    }
+
+    // non-javadoc, see interface HttpMessage
+    public HeaderIterator headerIterator(String name) {
+        return this.headergroup.iterator(name);
+    }
+    
+    // non-javadoc, see interface HttpMessage
+    public HttpParams getParams() {
+        if (this.params == null) {
+            this.params = new BasicHttpParams();
+        }
+        return this.params;
+    }
+    
+    // non-javadoc, see interface HttpMessage
+    public void setParams(final HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        this.params = params;
+    }
+}
diff --git a/src/org/apache/http/message/BasicHeader.java b/src/org/apache/http/message/BasicHeader.java
new file mode 100644
index 0000000..f134d8d
--- /dev/null
+++ b/src/org/apache/http/message/BasicHeader.java
@@ -0,0 +1,143 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHeader.java $
+ * $Revision: 652956 $
+ * $Date: 2008-05-02 17:13:05 -0700 (Fri, 02 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.ParseException;
+
+/**
+ * Represents an HTTP header field.
+ * 
+ * <p>The HTTP header fields follow the same generic format as
+ * that given in Section 3.1 of RFC 822. Each header field consists
+ * of a name followed by a colon (":") and the field value. Field names
+ * are case-insensitive. The field value MAY be preceded by any amount
+ * of LWS, though a single SP is preferred. 
+ *
+ *<pre>
+ *     message-header = field-name ":" [ field-value ]
+ *     field-name     = token
+ *     field-value    = *( field-content | LWS )
+ *     field-content  = &lt;the OCTETs making up the field-value
+ *                      and consisting of either *TEXT or combinations
+ *                      of token, separators, and quoted-string&gt;
+ *</pre>
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 652956 $ $Date: 2008-05-02 17:13:05 -0700 (Fri, 02 May 2008) $
+ * 
+ * @since 4.0
+ */
+public class BasicHeader implements Header, Cloneable {
+
+    /**
+     * Header name.
+     */
+    private final String name;
+    
+    /**
+     * Header value.
+     */
+    private final String value;
+    
+    /**
+     * Constructor with name and value
+     *
+     * @param name the header name
+     * @param value the header value
+     */
+    public BasicHeader(final String name, final String value) {
+        super();
+        if (name == null) {
+            throw new IllegalArgumentException("Name may not be null");
+        }
+        this.name = name;
+        this.value = value;
+    }
+
+    /**
+     * Returns the header name.
+     *
+     * @return String name The name
+     */
+    public String getName() {
+        return this.name;
+    }
+
+    /**
+     * Returns the header value.
+     *
+     * @return String value The current value.
+     */
+    public String getValue() {
+        return this.value;
+    }
+
+    /**
+     * Returns a {@link String} representation of the header.
+     *
+     * @return a string
+     */
+    public String toString() {
+        // no need for non-default formatting in toString()
+        return BasicLineFormatter.DEFAULT.formatHeader(null, this).toString();
+    }
+
+    /**
+     * Returns an array of {@link HeaderElement}s constructed from my value.
+     *
+     * @see BasicHeaderValueParser#parseElements(String, HeaderValueParser)
+     * 
+     * @return an array of header elements
+     *
+     * @throws ParseException   in case of a parse error
+     */
+    public HeaderElement[] getElements() throws ParseException {
+        if (this.value != null) {
+            // result intentionally not cached, it's probably not used again
+            return BasicHeaderValueParser.parseElements(this.value, null);
+        } else {
+            return new HeaderElement[] {}; 
+        }
+    }
+
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+ 
+}
diff --git a/src/org/apache/http/message/BasicHeaderElement.java b/src/org/apache/http/message/BasicHeaderElement.java
new file mode 100644
index 0000000..19a40c6
--- /dev/null
+++ b/src/org/apache/http/message/BasicHeaderElement.java
@@ -0,0 +1,243 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHeaderElement.java $
+ * $Revision: 604625 $
+ * $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.util.CharArrayBuffer;
+import org.apache.http.util.LangUtils;
+
+/**
+ * One element of an HTTP header's value.
+ * <p>
+ * Some HTTP headers (such as the set-cookie header) have values that
+ * can be decomposed into multiple elements.  Such headers must be in the
+ * following form:
+ * </p>
+ * <pre>
+ * header  = [ element ] *( "," [ element ] )
+ * element = name [ "=" [ value ] ] *( ";" [ param ] )
+ * param   = name [ "=" [ value ] ]
+ *
+ * name    = token
+ * value   = ( token | quoted-string )
+ *
+ * token         = 1*&lt;any char except "=", ",", ";", &lt;"&gt; and
+ *                       white space&gt;
+ * quoted-string = &lt;"&gt; *( text | quoted-char ) &lt;"&gt;
+ * text          = any char except &lt;"&gt;
+ * quoted-char   = "\" char
+ * </pre>
+ * <p>
+ * Any amount of white space is allowed between any part of the
+ * header, element or param and is ignored. A missing value in any
+ * element or param will be stored as the empty {@link String};
+ * if the "=" is also missing <var>null</var> will be stored instead.
+ * </p>
+ * <p>
+ * This class represents an individual header element, containing
+ * both a name/value pair (value may be <tt>null</tt>) and optionally
+ * a set of additional parameters.
+ * </p>
+ *
+ * @author <a href="mailto:bcholmes@interlog.com">B.C. Holmes</a>
+ * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 604625 $ $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ * 
+ * @since 4.0
+ */
+public class BasicHeaderElement implements HeaderElement, Cloneable {
+
+    private final String name;
+    private final String value;
+    private final NameValuePair[] parameters;
+
+    /**
+     * Constructor with name, value and parameters.
+     *
+     * @param name header element name
+     * @param value header element value. May be <tt>null</tt>
+     * @param parameters header element parameters. May be <tt>null</tt>.
+     *   Parameters are copied by reference, not by value
+     */
+    public BasicHeaderElement(
+            final String name, 
+            final String value,
+            final NameValuePair[] parameters) {
+        super();
+        if (name == null) {
+            throw new IllegalArgumentException("Name may not be null");
+        }
+        this.name = name;
+        this.value = value;
+        if (parameters != null) {
+            this.parameters = parameters;
+        } else {
+            this.parameters = new NameValuePair[] {};
+        }
+    }
+
+    /**
+     * Constructor with name and value.
+     * 
+     * @param name header element name
+     * @param value header element value. May be <tt>null</tt>
+     */
+    public BasicHeaderElement(final String name, final String value) {
+       this(name, value, null);
+    }
+
+    /**
+     * Returns the name.
+     *
+     * @return String name The name
+     */
+    public String getName() {
+        return this.name;
+    }
+
+    /**
+     * Returns the value.
+     *
+     * @return String value The current value.
+     */
+    public String getValue() {
+        return this.value;
+    }
+
+    /**
+     * Get parameters, if any.
+     * The returned array is created for each invocation and can
+     * be modified by the caller without affecting this header element.
+     *
+     * @return parameters as an array of {@link NameValuePair}s
+     */
+    public NameValuePair[] getParameters() {
+        return (NameValuePair[])this.parameters.clone();
+    }
+
+
+    /**
+     * Obtains the number of parameters.
+     *
+     * @return  the number of parameters
+     */
+    public int getParameterCount() {
+        return this.parameters.length;
+    }
+
+
+    /**
+     * Obtains the parameter with the given index.
+     *
+     * @param index     the index of the parameter, 0-based
+     *
+     * @return  the parameter with the given index
+     */
+    public NameValuePair getParameter(int index) {
+        // ArrayIndexOutOfBoundsException is appropriate
+        return this.parameters[index];
+    }
+
+
+    /**
+     * Returns parameter with the given name, if found. Otherwise null 
+     * is returned
+     *
+     * @param name The name to search by.
+     * @return NameValuePair parameter with the given name
+     */
+    public NameValuePair getParameterByName(final String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("Name may not be null");
+        } 
+        NameValuePair found = null;
+        for (int i = 0; i < this.parameters.length; i++) {
+            NameValuePair current = this.parameters[ i ];
+            if (current.getName().equalsIgnoreCase(name)) {
+                found = current;
+                break;
+            }
+        }
+        return found;
+    }
+
+    public boolean equals(final Object object) {
+        if (object == null) return false;
+        if (this == object) return true;
+        if (object instanceof HeaderElement) {
+            BasicHeaderElement that = (BasicHeaderElement) object;
+            return this.name.equals(that.name)
+                && LangUtils.equals(this.value, that.value)
+                && LangUtils.equals(this.parameters, that.parameters);
+        } else {
+            return false;
+        }
+    }
+
+    public int hashCode() {
+        int hash = LangUtils.HASH_SEED;
+        hash = LangUtils.hashCode(hash, this.name);
+        hash = LangUtils.hashCode(hash, this.value);
+        for (int i = 0; i < this.parameters.length; i++) {
+            hash = LangUtils.hashCode(hash, this.parameters[i]);
+        }
+        return hash;
+    }
+    
+    public String toString() {
+        CharArrayBuffer buffer = new CharArrayBuffer(64);
+        buffer.append(this.name);
+        if (this.value != null) {
+            buffer.append("=");
+            buffer.append(this.value);
+        }
+        for (int i = 0; i < this.parameters.length; i++) {
+            buffer.append("; ");
+            buffer.append(this.parameters[i]);
+        }
+        return buffer.toString();
+    }
+    
+    public Object clone() throws CloneNotSupportedException {
+        // parameters array is considered immutable
+        // no need to make a copy of it
+        return super.clone();
+    }
+ 
+}
+
diff --git a/src/org/apache/http/message/BasicHeaderElementIterator.java b/src/org/apache/http/message/BasicHeaderElementIterator.java
new file mode 100644
index 0000000..46f53a8
--- /dev/null
+++ b/src/org/apache/http/message/BasicHeaderElementIterator.java
@@ -0,0 +1,161 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHeaderElementIterator.java $
+ * $Revision: 592088 $
+ * $Date: 2007-11-05 09:03:39 -0800 (Mon, 05 Nov 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import java.util.NoSuchElementException;
+
+import org.apache.http.FormattedHeader;
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.HeaderElementIterator;
+import org.apache.http.HeaderIterator;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Basic implementation of a {@link HeaderElementIterator}.
+ * 
+ * @version $Revision: 592088 $
+ * 
+ * @author Andrea Selva <selva.andre at gmail.com>
+ * @author Oleg Kalnichevski <oleg at ural.ru>
+ */
+public class BasicHeaderElementIterator implements HeaderElementIterator {
+    
+    private final HeaderIterator headerIt;
+    private final HeaderValueParser parser;
+    
+    private HeaderElement currentElement = null;
+    private CharArrayBuffer buffer = null;
+    private ParserCursor cursor = null;
+    
+    /**
+     * Creates a new instance of BasicHeaderElementIterator
+     */
+    public BasicHeaderElementIterator(
+            final HeaderIterator headerIterator,
+            final HeaderValueParser parser) {
+        if (headerIterator == null) {
+            throw new IllegalArgumentException("Header iterator may not be null");
+        }
+        if (parser == null) {
+            throw new IllegalArgumentException("Parser may not be null");
+        }
+        this.headerIt = headerIterator;
+        this.parser = parser;
+    }
+
+    
+    public BasicHeaderElementIterator(final HeaderIterator headerIterator) {
+        this(headerIterator, BasicHeaderValueParser.DEFAULT);
+    }
+
+    
+    private void bufferHeaderValue() {
+        this.cursor = null;
+        this.buffer = null;
+        while (this.headerIt.hasNext()) {
+            Header h = this.headerIt.nextHeader();
+            if (h instanceof FormattedHeader) {
+                this.buffer = ((FormattedHeader) h).getBuffer();
+                this.cursor = new ParserCursor(0, this.buffer.length());
+                this.cursor.updatePos(((FormattedHeader) h).getValuePos());
+                break;
+            } else {
+                String value = h.getValue();
+                if (value != null) {
+                    this.buffer = new CharArrayBuffer(value.length());
+                    this.buffer.append(value);
+                    this.cursor = new ParserCursor(0, this.buffer.length());
+                    break;
+                }
+            }
+        }
+    }
+
+    private void parseNextElement() {
+        // loop while there are headers left to parse
+        while (this.headerIt.hasNext() || this.cursor != null) {
+            if (this.cursor == null || this.cursor.atEnd()) {
+                // get next header value
+                bufferHeaderValue();
+            }
+            // Anything buffered?
+            if (this.cursor != null) {
+                // loop while there is data in the buffer 
+                while (!this.cursor.atEnd()) {
+                    HeaderElement e = this.parser.parseHeaderElement(this.buffer, this.cursor);
+                    if (!(e.getName().length() == 0 && e.getValue() == null)) {
+                        // Found something
+                        this.currentElement = e;
+                        return;
+                    }
+                }
+                // if at the end of the buffer
+                if (this.cursor.atEnd()) {
+                    // discard it
+                    this.cursor = null;
+                    this.buffer = null;
+                }
+            }
+        }
+    }
+    
+    public boolean hasNext() {
+        if (this.currentElement == null) {
+            parseNextElement();
+        }
+        return this.currentElement != null;
+    }
+
+    public HeaderElement nextElement() throws NoSuchElementException {
+        if (this.currentElement == null) {
+            parseNextElement();
+        }
+
+        if (this.currentElement == null) {
+            throw new NoSuchElementException("No more header elements available");
+        }
+
+        HeaderElement element = this.currentElement;
+        this.currentElement = null;
+        return element;
+    }
+
+    public final Object next() throws NoSuchElementException {
+        return nextElement();
+    }
+
+    public void remove() throws UnsupportedOperationException {
+        throw new UnsupportedOperationException("Remove not supported");
+    }
+
+}
\ No newline at end of file
diff --git a/src/org/apache/http/message/BasicHeaderIterator.java b/src/org/apache/http/message/BasicHeaderIterator.java
new file mode 100644
index 0000000..32cd1c8
--- /dev/null
+++ b/src/org/apache/http/message/BasicHeaderIterator.java
@@ -0,0 +1,180 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHeaderIterator.java $
+ * $Revision: 581981 $
+ * $Date: 2007-10-04 11:26:26 -0700 (Thu, 04 Oct 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+
+import java.util.NoSuchElementException;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+
+
+/**
+ * Basic implementation of a {@link HeaderIterator}.
+ * 
+ * @version $Revision: 581981 $
+ */
+public class BasicHeaderIterator implements HeaderIterator {
+
+    /**
+     * An array of headers to iterate over.
+     * Not all elements of this array are necessarily part of the iteration.
+     * This array will never be modified by the iterator.
+     * Derived implementations are expected to adhere to this restriction.
+     */
+    protected final Header[] allHeaders;
+
+
+    /**
+     * The position of the next header in {@link #allHeaders allHeaders}.
+     * Negative if the iteration is over.
+     */
+    protected int currentIndex;
+
+
+    /**
+     * The header name to filter by.
+     * <code>null</code> to iterate over all headers in the array.
+     */
+    protected String headerName;
+
+
+
+    /**
+     * Creates a new header iterator.
+     *
+     * @param headers   an array of headers over which to iterate
+     * @param name      the name of the headers over which to iterate, or
+     *                  <code>null</code> for any
+     */
+    public BasicHeaderIterator(Header[] headers, String name) {
+        if (headers == null) {
+            throw new IllegalArgumentException
+                ("Header array must not be null.");
+        }
+
+        this.allHeaders = headers;
+        this.headerName = name;
+        this.currentIndex = findNext(-1);
+    }
+
+
+    /**
+     * Determines the index of the next header.
+     *
+     * @param from      one less than the index to consider first,
+     *                  -1 to search for the first header
+     *
+     * @return  the index of the next header that matches the filter name,
+     *          or negative if there are no more headers
+     */
+    protected int findNext(int from) {
+        if (from < -1)
+            return -1;
+
+        final int to = this.allHeaders.length-1;
+        boolean found = false;
+        while (!found && (from < to)) {
+            from++;
+            found = filterHeader(from);
+        }
+        return found ? from : -1;
+    }
+
+
+    /**
+     * Checks whether a header is part of the iteration.
+     *
+     * @param index     the index of the header to check
+     *
+     * @return  <code>true</code> if the header should be part of the
+     *          iteration, <code>false</code> to skip
+     */
+    protected boolean filterHeader(int index) {
+        return (this.headerName == null) ||
+            this.headerName.equalsIgnoreCase(this.allHeaders[index].getName());
+    }
+
+
+    // non-javadoc, see interface HeaderIterator
+    public boolean hasNext() {
+        return (this.currentIndex >= 0);
+    }
+
+
+    /**
+     * Obtains the next header from this iteration.
+     *
+     * @return  the next header in this iteration
+     *
+     * @throws NoSuchElementException   if there are no more headers
+     */
+    public Header nextHeader()
+        throws NoSuchElementException {
+
+        final int current = this.currentIndex;
+        if (current < 0) {
+            throw new NoSuchElementException("Iteration already finished.");
+        }
+
+        this.currentIndex = findNext(current);
+
+        return this.allHeaders[current];
+    }
+
+
+    /**
+     * Returns the next header.
+     * Same as {@link #nextHeader nextHeader}, but not type-safe.
+     *
+     * @return  the next header in this iteration
+     *
+     * @throws NoSuchElementException   if there are no more headers
+     */
+    public final Object next()
+        throws NoSuchElementException {
+        return nextHeader();
+    }
+
+
+    /**
+     * Removing headers is not supported.
+     *
+     * @throws UnsupportedOperationException    always
+     */
+    public void remove()
+        throws UnsupportedOperationException {
+
+        throw new UnsupportedOperationException
+            ("Removing headers is not supported.");
+    }
+}
diff --git a/src/org/apache/http/message/BasicHeaderValueFormatter.java b/src/org/apache/http/message/BasicHeaderValueFormatter.java
new file mode 100644
index 0000000..b63bdf7
--- /dev/null
+++ b/src/org/apache/http/message/BasicHeaderValueFormatter.java
@@ -0,0 +1,441 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHeaderValueFormatter.java $
+ * $Revision: 574185 $
+ * $Date: 2007-09-10 02:19:47 -0700 (Mon, 10 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.util.CharArrayBuffer;
+
+
+/**
+ * Basic implementation for formatting header value elements.
+ * Instances of this class are stateless and thread-safe.
+ * Derived classes are expected to maintain these properties.
+ * 
+ * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a>
+ * @author and others
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 574185 $
+ *
+ * @since 4.0
+ */
+public class BasicHeaderValueFormatter implements HeaderValueFormatter {
+
+    /**
+     * A default instance of this class, for use as default or fallback.
+     * Note that {@link BasicHeaderValueFormatter} is not a singleton, there
+     * can be many instances of the class itself and of derived classes.
+     * The instance here provides non-customized, default behavior.
+     */
+    public final static
+        BasicHeaderValueFormatter DEFAULT = new BasicHeaderValueFormatter();
+
+
+    /**
+     * 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 .
+     */
+    public final static String SEPARATORS = " ;,:@()<>\\\"/[]?={}\t";
+
+
+    /**
+     * Unsafe special characters that must be escaped using the backslash
+     * character
+     */
+    public final static String UNSAFE_CHARS = "\"\\";
+
+
+
+    // public default constructor
+
+
+
+    /**
+     * Formats an array of header elements.
+     *
+     * @param elems     the header elements to format
+     * @param quote     <code>true</code> to always format with quoted values,
+     *                  <code>false</code> to use quotes only when necessary
+     * @param formatter         the formatter to use, or <code>null</code>
+     *                          for the {@link #DEFAULT default}
+     *
+     * @return  the formatted header elements
+     */
+    public final static
+        String formatElements(final HeaderElement[] elems,
+                              final boolean quote,
+                              HeaderValueFormatter formatter) {
+        if (formatter == null)
+            formatter = BasicHeaderValueFormatter.DEFAULT;
+        return formatter.formatElements(null, elems, quote).toString();
+    }
+
+
+    // non-javadoc, see interface HeaderValueFormatter
+    public CharArrayBuffer formatElements(CharArrayBuffer buffer,
+                                          final HeaderElement[] elems,
+                                          final boolean quote) {
+        if (elems == null) {
+            throw new IllegalArgumentException
+                ("Header element array must not be null.");
+        }
+
+        int len = estimateElementsLen(elems);
+        if (buffer == null) {
+            buffer = new CharArrayBuffer(len);
+        } else {
+            buffer.ensureCapacity(len);
+        }
+
+        for (int i=0; i<elems.length; i++) {
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            formatHeaderElement(buffer, elems[i], quote);
+        }
+
+        return buffer;
+    }
+
+
+    /**
+     * Estimates the length of formatted header elements.
+     *
+     * @param elems     the header elements to format, or <code>null</code>
+     *
+     * @return  a length estimate, in number of characters
+     */
+    protected int estimateElementsLen(final HeaderElement[] elems) {
+        if ((elems == null) || (elems.length < 1))
+            return 0;
+
+        int result = (elems.length-1) * 2; // elements separated by ", "
+        for (int i=0; i<elems.length; i++) {
+            result += estimateHeaderElementLen(elems[i]);
+        }
+
+        return result;
+    }
+
+
+
+    /**
+     * Formats a header element.
+     *
+     * @param elem      the header element to format
+     * @param quote     <code>true</code> to always format with quoted values,
+     *                  <code>false</code> to use quotes only when necessary
+     * @param formatter         the formatter to use, or <code>null</code>
+     *                          for the {@link #DEFAULT default}
+     *
+     * @return  the formatted header element
+     */
+    public final static
+        String formatHeaderElement(final HeaderElement elem,
+                                   boolean quote,
+                                   HeaderValueFormatter formatter) {
+        if (formatter == null)
+            formatter = BasicHeaderValueFormatter.DEFAULT;
+        return formatter.formatHeaderElement(null, elem, quote).toString();
+    }
+
+
+    // non-javadoc, see interface HeaderValueFormatter
+    public CharArrayBuffer formatHeaderElement(CharArrayBuffer buffer,
+                                               final HeaderElement elem,
+                                               final boolean quote) {
+        if (elem == null) {
+            throw new IllegalArgumentException
+                ("Header element must not be null.");
+        }
+
+        int len = estimateHeaderElementLen(elem);
+        if (buffer == null) {
+            buffer = new CharArrayBuffer(len);
+        } else {
+            buffer.ensureCapacity(len);
+        }
+
+        buffer.append(elem.getName());
+        final String value = elem.getValue();
+        if (value != null) {
+            buffer.append('=');
+            doFormatValue(buffer, value, quote);
+        }
+
+        final int parcnt = elem.getParameterCount();
+        if (parcnt > 0) {
+            for (int i=0; i<parcnt; i++) {
+                buffer.append("; ");
+                formatNameValuePair(buffer, elem.getParameter(i), quote);
+            }
+        }
+
+        return buffer;
+    }
+
+
+    /**
+     * Estimates the length of a formatted header element.
+     *
+     * @param elem      the header element to format, or <code>null</code>
+     *
+     * @return  a length estimate, in number of characters
+     */
+    protected int estimateHeaderElementLen(final HeaderElement elem) {
+        if (elem == null)
+            return 0;
+
+        int result = elem.getName().length(); // name
+        final String value = elem.getValue();
+        if (value != null) {
+            // assume quotes, but no escaped characters
+            result += 3 + value.length(); // ="value"
+        }
+
+        final int parcnt = elem.getParameterCount();
+        if (parcnt > 0) {
+            for (int i=0; i<parcnt; i++) {
+                result += 2 +                   // ; <param>
+                    estimateNameValuePairLen(elem.getParameter(i));
+            }
+        }
+
+        return result;
+    }
+
+
+
+
+    /**
+     * Formats a set of parameters.
+     *
+     * @param nvps      the parameters to format
+     * @param quote     <code>true</code> to always format with quoted values,
+     *                  <code>false</code> to use quotes only when necessary
+     * @param formatter         the formatter to use, or <code>null</code>
+     *                          for the {@link #DEFAULT default}
+     *
+     * @return  the formatted parameters
+     */
+    public final static
+        String formatParameters(final NameValuePair[] nvps,
+                                final boolean quote,
+                                HeaderValueFormatter formatter) {
+        if (formatter == null)
+            formatter = BasicHeaderValueFormatter.DEFAULT;
+        return formatter.formatParameters(null, nvps, quote).toString();
+    }
+
+
+    // non-javadoc, see interface HeaderValueFormatter
+    public CharArrayBuffer formatParameters(CharArrayBuffer buffer,
+                                            NameValuePair[] nvps,
+                                            boolean quote) {
+        if (nvps == null) {
+            throw new IllegalArgumentException
+                ("Parameters must not be null.");
+        }
+
+        int len = estimateParametersLen(nvps);
+        if (buffer == null) {
+            buffer = new CharArrayBuffer(len);
+        } else {
+            buffer.ensureCapacity(len);
+        }
+
+        for (int i = 0; i < nvps.length; i++) {
+            if (i > 0) {
+                buffer.append("; ");
+            }
+            formatNameValuePair(buffer, nvps[i], quote);
+        }
+
+        return buffer;
+    }
+
+
+    /**
+     * Estimates the length of formatted parameters.
+     *
+     * @param nvps      the parameters to format, or <code>null</code>
+     *
+     * @return  a length estimate, in number of characters
+     */
+    protected int estimateParametersLen(final NameValuePair[] nvps) {
+        if ((nvps == null) || (nvps.length < 1))
+            return 0;
+
+        int result = (nvps.length-1) * 2; // "; " between the parameters
+        for (int i=0; i<nvps.length; i++) {
+            result += estimateNameValuePairLen(nvps[i]);
+        }
+
+        return result;
+    }
+
+
+    /**
+     * Formats a name-value pair.
+     *
+     * @param nvp       the name-value pair to format
+     * @param quote     <code>true</code> to always format with a quoted value,
+     *                  <code>false</code> to use quotes only when necessary
+     * @param formatter         the formatter to use, or <code>null</code>
+     *                          for the {@link #DEFAULT default}
+     *
+     * @return  the formatted name-value pair
+     */
+    public final static
+        String formatNameValuePair(final NameValuePair nvp,
+                                   final boolean quote,
+                                   HeaderValueFormatter formatter) {
+        if (formatter == null)
+            formatter = BasicHeaderValueFormatter.DEFAULT;
+        return formatter.formatNameValuePair(null, nvp, quote).toString();
+    }
+
+
+    // non-javadoc, see interface HeaderValueFormatter
+    public CharArrayBuffer formatNameValuePair(CharArrayBuffer buffer,
+                                               final NameValuePair nvp,
+                                               final boolean quote) {
+        if (nvp == null) {
+            throw new IllegalArgumentException
+                ("NameValuePair must not be null.");
+        }
+
+        int len = estimateNameValuePairLen(nvp);
+        if (buffer == null) {
+            buffer = new CharArrayBuffer(len);
+        } else {
+            buffer.ensureCapacity(len);
+        }
+
+        buffer.append(nvp.getName());
+        final String value = nvp.getValue();
+        if (value != null) {
+            buffer.append('=');
+            doFormatValue(buffer, value, quote);
+        }
+
+        return buffer;
+    }
+
+
+    /**
+     * Estimates the length of a formatted name-value pair.
+     *
+     * @param nvp       the name-value pair to format, or <code>null</code>
+     *
+     * @return  a length estimate, in number of characters
+     */
+    protected int estimateNameValuePairLen(final NameValuePair nvp) {
+        if (nvp == null)
+            return 0;
+
+        int result = nvp.getName().length(); // name
+        final String value = nvp.getValue();
+        if (value != null) {
+            // assume quotes, but no escaped characters
+            result += 3 + value.length(); // ="value"
+        }
+        return result;
+    }
+
+
+    /**
+     * Actually formats the value of a name-value pair.
+     * This does not include a leading = character.
+     * Called from {@link #formatNameValuePair formatNameValuePair}.
+     *
+     * @param buffer    the buffer to append to, never <code>null</code>
+     * @param value     the value to append, never <code>null</code>
+     * @param quote     <code>true</code> to always format with quotes,
+     *                  <code>false</code> to use quotes only when necessary
+     */
+    protected void doFormatValue(final CharArrayBuffer buffer,
+                                 final String value,
+                                 boolean quote) {
+
+        if (!quote) {
+            for (int i = 0; (i < value.length()) && !quote; i++) {
+                quote = isSeparator(value.charAt(i));
+            }
+        }
+
+        if (quote) {
+            buffer.append('"');
+        }
+        for (int i = 0; i < value.length(); i++) {
+            char ch = value.charAt(i);
+            if (isUnsafe(ch)) {
+                buffer.append('\\');
+            }
+            buffer.append(ch);
+        }
+        if (quote) {
+            buffer.append('"');
+        }
+    }
+
+
+    /**
+     * Checks whether a character is a {@link #SEPARATORS separator}.
+     *
+     * @param ch        the character to check
+     *
+     * @return  <code>true</code> if the character is a separator,
+     *          <code>false</code> otherwise
+     */
+    protected boolean isSeparator(char ch) {
+        return SEPARATORS.indexOf(ch) >= 0;
+    }
+
+
+    /**
+     * Checks whether a character is {@link #UNSAFE_CHARS unsafe}.
+     *
+     * @param ch        the character to check
+     *
+     * @return  <code>true</code> if the character is unsafe,
+     *          <code>false</code> otherwise
+     */
+    protected boolean isUnsafe(char ch) {
+        return UNSAFE_CHARS.indexOf(ch) >= 0;
+    }
+
+
+} // class BasicHeaderValueFormatter
diff --git a/src/org/apache/http/message/BasicHeaderValueParser.java b/src/org/apache/http/message/BasicHeaderValueParser.java
new file mode 100644
index 0000000..5216196
--- /dev/null
+++ b/src/org/apache/http/message/BasicHeaderValueParser.java
@@ -0,0 +1,420 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHeaderValueParser.java $
+ * $Revision: 595670 $
+ * $Date: 2007-11-16 06:15:01 -0800 (Fri, 16 Nov 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+
+import java.util.List;
+import java.util.ArrayList;
+
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.ParseException;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.CharArrayBuffer;
+
+
+
+/**
+ * Basic implementation for parsing header values into elements.
+ * Instances of this class are stateless and thread-safe.
+ * Derived classes are expected to maintain these properties.
+ *
+ * @author <a href="mailto:bcholmes@interlog.com">B.C. Holmes</a>
+ * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a>
+ * @author and others
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 595670 $
+ *
+ * @since 4.0
+ */
+public class BasicHeaderValueParser implements HeaderValueParser {
+
+    /**
+     * A default instance of this class, for use as default or fallback.
+     * Note that {@link BasicHeaderValueParser} is not a singleton, there
+     * can be many instances of the class itself and of derived classes.
+     * The instance here provides non-customized, default behavior.
+     */
+    public final static
+        BasicHeaderValueParser DEFAULT = new BasicHeaderValueParser();
+
+    private final static char PARAM_DELIMITER                = ';';
+    private final static char ELEM_DELIMITER                 = ',';
+    private final static char[] ALL_DELIMITERS               = new char[] {
+                                                                PARAM_DELIMITER, 
+                                                                ELEM_DELIMITER
+                                                                };  
+    
+    // public default constructor
+
+
+    /**
+     * Parses elements with the given parser.
+     *
+     * @param value     the header value to parse
+     * @param parser    the parser to use, or <code>null</code> for default
+     *
+     * @return  array holding the header elements, never <code>null</code>
+     */
+    public final static
+        HeaderElement[] parseElements(final String value,
+                                      HeaderValueParser parser)
+        throws ParseException {
+
+        if (value == null) {
+            throw new IllegalArgumentException
+                ("Value to parse may not be null");
+        }
+
+        if (parser == null)
+            parser = BasicHeaderValueParser.DEFAULT;
+
+        CharArrayBuffer buffer = new CharArrayBuffer(value.length());
+        buffer.append(value);
+        ParserCursor cursor = new ParserCursor(0, value.length());
+        return parser.parseElements(buffer, cursor);
+    }
+
+
+    // non-javadoc, see interface HeaderValueParser
+    public HeaderElement[] parseElements(final CharArrayBuffer buffer,
+                                         final ParserCursor cursor) {
+
+        if (buffer == null) {
+            throw new IllegalArgumentException("Char array buffer may not be null");
+        }
+        if (cursor == null) {
+            throw new IllegalArgumentException("Parser cursor may not be null");
+        }
+
+        List elements = new ArrayList(); 
+        while (!cursor.atEnd()) {
+            HeaderElement element = parseHeaderElement(buffer, cursor);
+            if (!(element.getName().length() == 0 && element.getValue() == null)) {
+                elements.add(element);
+            }
+        }
+        return (HeaderElement[])
+            elements.toArray(new HeaderElement[elements.size()]);
+    }
+
+
+    /**
+     * Parses an element with the given parser.
+     *
+     * @param value     the header element to parse
+     * @param parser    the parser to use, or <code>null</code> for default
+     *
+     * @return  the parsed header element
+     */
+    public final static
+        HeaderElement parseHeaderElement(final String value,
+                                         HeaderValueParser parser)
+        throws ParseException {
+
+        if (value == null) {
+            throw new IllegalArgumentException
+                ("Value to parse may not be null");
+        }
+
+        if (parser == null)
+            parser = BasicHeaderValueParser.DEFAULT;
+
+        CharArrayBuffer buffer = new CharArrayBuffer(value.length());
+        buffer.append(value);
+        ParserCursor cursor = new ParserCursor(0, value.length());
+        return parser.parseHeaderElement(buffer, cursor);
+    }
+
+
+    // non-javadoc, see interface HeaderValueParser
+    public HeaderElement parseHeaderElement(final CharArrayBuffer buffer,
+                                            final ParserCursor cursor) {
+
+        if (buffer == null) {
+            throw new IllegalArgumentException("Char array buffer may not be null");
+        }
+        if (cursor == null) {
+            throw new IllegalArgumentException("Parser cursor may not be null");
+        }
+        
+        NameValuePair nvp = parseNameValuePair(buffer, cursor);
+        NameValuePair[] params = null;
+        if (!cursor.atEnd()) {
+            char ch = buffer.charAt(cursor.getPos() - 1); 
+            if (ch != ELEM_DELIMITER) {
+                params = parseParameters(buffer, cursor);
+            }
+        }
+        return createHeaderElement(nvp.getName(), nvp.getValue(), params);
+    }
+
+
+    /**
+     * Creates a header element.
+     * Called from {@link #parseHeaderElement}.
+     *
+     * @return  a header element representing the argument
+     */
+    protected HeaderElement createHeaderElement(
+            final String name, 
+            final String value, 
+            final NameValuePair[] params) {
+        return new BasicHeaderElement(name, value, params);
+    }
+
+
+    /**
+     * Parses parameters with the given parser.
+     *
+     * @param value     the parameter list to parse
+     * @param parser    the parser to use, or <code>null</code> for default
+     *
+     * @return  array holding the parameters, never <code>null</code>
+     */
+    public final static
+        NameValuePair[] parseParameters(final String value,
+                                        HeaderValueParser parser)
+        throws ParseException {
+
+        if (value == null) {
+            throw new IllegalArgumentException
+                ("Value to parse may not be null");
+        }
+
+        if (parser == null)
+            parser = BasicHeaderValueParser.DEFAULT;
+
+        CharArrayBuffer buffer = new CharArrayBuffer(value.length());
+        buffer.append(value);
+        ParserCursor cursor = new ParserCursor(0, value.length());
+        return parser.parseParameters(buffer, cursor);
+    }
+
+
+
+    // non-javadoc, see interface HeaderValueParser
+    public NameValuePair[] parseParameters(final CharArrayBuffer buffer,
+                                           final ParserCursor cursor) {
+
+        if (buffer == null) {
+            throw new IllegalArgumentException("Char array buffer may not be null");
+        }
+        if (cursor == null) {
+            throw new IllegalArgumentException("Parser cursor may not be null");
+        }
+        
+        int pos = cursor.getPos();
+        int indexTo = cursor.getUpperBound();
+        
+        while (pos < indexTo) {
+            char ch = buffer.charAt(pos);
+            if (HTTP.isWhitespace(ch)) {
+                pos++;
+            } else {
+                break;
+            }
+        }
+        cursor.updatePos(pos);
+        if (cursor.atEnd()) {
+            return new NameValuePair[] {};
+        }
+        
+        List params = new ArrayList(); 
+        while (!cursor.atEnd()) {
+            NameValuePair param = parseNameValuePair(buffer, cursor);
+            params.add(param);
+            char ch = buffer.charAt(cursor.getPos() - 1);
+            if (ch == ELEM_DELIMITER) {
+                break;
+            }
+        }
+        
+        return (NameValuePair[])
+            params.toArray(new NameValuePair[params.size()]);
+    }
+
+    /**
+     * Parses a name-value-pair with the given parser.
+     *
+     * @param value     the NVP to parse
+     * @param parser    the parser to use, or <code>null</code> for default
+     *
+     * @return  the parsed name-value pair
+     */
+    public final static
+       NameValuePair parseNameValuePair(final String value,
+                                        HeaderValueParser parser)
+        throws ParseException {
+
+        if (value == null) {
+            throw new IllegalArgumentException
+                ("Value to parse may not be null");
+        }
+
+        if (parser == null)
+            parser = BasicHeaderValueParser.DEFAULT;
+
+        CharArrayBuffer buffer = new CharArrayBuffer(value.length());
+        buffer.append(value);
+        ParserCursor cursor = new ParserCursor(0, value.length());
+        return parser.parseNameValuePair(buffer, cursor);
+    }
+
+
+    // non-javadoc, see interface HeaderValueParser
+    public NameValuePair parseNameValuePair(final CharArrayBuffer buffer,
+                                            final ParserCursor cursor) {
+        return parseNameValuePair(buffer, cursor, ALL_DELIMITERS);
+    }
+    
+    private static boolean isOneOf(final char ch, final char[] chs) {
+        if (chs != null) {
+            for (int i = 0; i < chs.length; i++) {
+                if (ch == chs[i]) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+    
+    public NameValuePair parseNameValuePair(final CharArrayBuffer buffer,
+                                            final ParserCursor cursor,
+                                            final char[] delimiters) {
+
+        if (buffer == null) {
+            throw new IllegalArgumentException("Char array buffer may not be null");
+        }
+        if (cursor == null) {
+            throw new IllegalArgumentException("Parser cursor may not be null");
+        }
+
+        boolean terminated = false;
+        
+        int pos = cursor.getPos();
+        int indexFrom = cursor.getPos();
+        int indexTo = cursor.getUpperBound();
+        
+        // Find name
+        String name = null;
+        while (pos < indexTo) {
+            char ch = buffer.charAt(pos);
+            if (ch == '=') {
+                break;
+            }
+            if (isOneOf(ch, delimiters)) {
+                terminated = true;
+                break;
+            }
+            pos++;
+        }
+        
+        if (pos == indexTo) {
+            terminated = true;
+            name = buffer.substringTrimmed(indexFrom, indexTo);
+        } else {
+            name = buffer.substringTrimmed(indexFrom, pos);
+            pos++;
+        }
+        
+        if (terminated) {
+            cursor.updatePos(pos);
+            return createNameValuePair(name, null);
+        }
+
+        // Find value
+        String value = null;
+        int i1 = pos;
+        
+        boolean qouted = false;
+        boolean escaped = false;
+        while (pos < indexTo) {
+            char ch = buffer.charAt(pos);
+            if (ch == '"' && !escaped) {
+                qouted = !qouted;
+            }
+            if (!qouted && !escaped && isOneOf(ch, delimiters)) {
+                terminated = true;
+                break;
+            }
+            if (escaped) {
+                escaped = false;
+            } else {
+                escaped = qouted && ch == '\\';
+            }
+            pos++;
+        }
+        
+        int i2 = pos;
+        // Trim leading white spaces
+        while (i1 < i2 && (HTTP.isWhitespace(buffer.charAt(i1)))) {
+            i1++;
+        }
+        // Trim trailing white spaces
+        while ((i2 > i1) && (HTTP.isWhitespace(buffer.charAt(i2 - 1)))) {
+            i2--;
+        }
+        // Strip away quotes if necessary
+        if (((i2 - i1) >= 2) 
+            && (buffer.charAt(i1) == '"') 
+            && (buffer.charAt(i2 - 1) == '"')) {
+            i1++;
+            i2--;
+        }
+        value = buffer.substring(i1, i2);
+        if (terminated) {
+            pos++;
+        }
+        cursor.updatePos(pos);
+        return createNameValuePair(name, value);
+    }
+
+    /**
+     * Creates a name-value pair.
+     * Called from {@link #parseNameValuePair}.
+     *
+     * @param name      the name
+     * @param value     the value, or <code>null</code>
+     *
+     * @return  a name-value pair representing the arguments
+     */
+    protected NameValuePair createNameValuePair(final String name, final String value) {
+        return new BasicNameValuePair(name, value);
+    }
+
+}
+
diff --git a/src/org/apache/http/message/BasicHttpEntityEnclosingRequest.java b/src/org/apache/http/message/BasicHttpEntityEnclosingRequest.java
new file mode 100644
index 0000000..dbb70c8
--- /dev/null
+++ b/src/org/apache/http/message/BasicHttpEntityEnclosingRequest.java
@@ -0,0 +1,81 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHttpEntityEnclosingRequest.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.RequestLine;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * Basic implementation of a request with an entity that can be modified.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618017 $
+ * 
+ * @since 4.0
+ */
+public class BasicHttpEntityEnclosingRequest 
+            extends BasicHttpRequest implements HttpEntityEnclosingRequest {
+    
+    private HttpEntity entity;
+    
+    public BasicHttpEntityEnclosingRequest(final String method, final String uri) {
+        super(method, uri);
+    }
+
+    public BasicHttpEntityEnclosingRequest(final String method, final String uri, 
+            final ProtocolVersion ver) {
+        this(new BasicRequestLine(method, uri, ver));
+    }
+
+    public BasicHttpEntityEnclosingRequest(final RequestLine requestline) {
+        super(requestline);
+    }
+
+    public HttpEntity getEntity() {
+        return this.entity;
+    }
+
+    public void setEntity(final HttpEntity entity) {
+        this.entity = entity;
+    }
+    
+    public boolean expectContinue() {
+        Header expect = getFirstHeader(HTTP.EXPECT_DIRECTIVE);
+        return expect != null && HTTP.EXPECT_CONTINUE.equalsIgnoreCase(expect.getValue());
+    }
+    
+}
diff --git a/src/org/apache/http/message/BasicHttpRequest.java b/src/org/apache/http/message/BasicHttpRequest.java
new file mode 100644
index 0000000..eedf8bc
--- /dev/null
+++ b/src/org/apache/http/message/BasicHttpRequest.java
@@ -0,0 +1,98 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHttpRequest.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.HttpRequest;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.RequestLine;
+import org.apache.http.params.HttpProtocolParams;
+
+/**
+ * Basic implementation of an HTTP request that can be modified.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573864 $
+ * 
+ * @since 4.0
+ */
+public class BasicHttpRequest extends AbstractHttpMessage implements HttpRequest {
+    
+    private final RequestLine requestline;
+    private final String method;
+    private final String uri;
+        
+    public BasicHttpRequest(final String method, final String uri) {
+        super();
+        if (method == null) {
+            throw new IllegalArgumentException("Method name may not be null");
+        }
+        if (uri == null) {
+            throw new IllegalArgumentException("Request URI may not be null");
+        }
+        this.method = method;
+        this.uri = uri;
+        this.requestline = null;
+    }
+
+    public BasicHttpRequest(final String method, final String uri, final ProtocolVersion ver) {
+        this(new BasicRequestLine(method, uri, ver));
+    }
+
+    public BasicHttpRequest(final RequestLine requestline) {
+        super();
+        if (requestline == null) {
+            throw new IllegalArgumentException("Request line may not be null");
+        }
+        this.requestline = requestline;
+        this.method = requestline.getMethod();
+        this.uri = requestline.getUri();
+    }
+
+    public ProtocolVersion getProtocolVersion() {
+        if (this.requestline != null) {
+            return this.requestline.getProtocolVersion();
+        } else {
+            return HttpProtocolParams.getVersion(getParams());
+        }
+    }
+    
+    public RequestLine getRequestLine() {
+        if (this.requestline != null) {
+            return this.requestline;
+        } else {
+            ProtocolVersion ver = HttpProtocolParams.getVersion(getParams());
+            return new BasicRequestLine(this.method, this.uri, ver);
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/message/BasicHttpResponse.java b/src/org/apache/http/message/BasicHttpResponse.java
new file mode 100644
index 0000000..7da4bea
--- /dev/null
+++ b/src/org/apache/http/message/BasicHttpResponse.java
@@ -0,0 +1,204 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHttpResponse.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import java.util.Locale;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.StatusLine;
+import org.apache.http.ReasonPhraseCatalog;
+
+
+/**
+ * Basic implementation of an HTTP response that can be modified.
+ * This implementation makes sure that there always is a status line.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573864 $
+ * 
+ * @since 4.0
+ */
+public class BasicHttpResponse extends AbstractHttpMessage
+    implements HttpResponse {
+
+    private StatusLine          statusline;
+    private HttpEntity          entity;
+    private ReasonPhraseCatalog reasonCatalog;
+    private Locale              locale;
+
+
+    /**
+     * Creates a new response.
+     * This is the constructor to which all others map.
+     *
+     * @param statusline        the status line
+     * @param catalog           the reason phrase catalog, or
+     *                          <code>null</code> to disable automatic
+     *                          reason phrase lookup
+     * @param locale            the locale for looking up reason phrases, or
+     *                          <code>null</code> for the system locale
+     */
+    public BasicHttpResponse(final StatusLine statusline,
+                             final ReasonPhraseCatalog catalog,
+                             final Locale locale) {
+        super();
+        if (statusline == null) {
+            throw new IllegalArgumentException("Status line may not be null.");
+        }
+        this.statusline    = statusline;
+        this.reasonCatalog = catalog;
+        this.locale        = (locale != null) ? locale : Locale.getDefault();
+    }
+
+    /**
+     * Creates a response from a status line.
+     * The response will not have a reason phrase catalog and
+     * use the system default locale.
+     *
+     * @param statusline        the status line
+     */
+    public BasicHttpResponse(final StatusLine statusline) {
+        this(statusline, null, null);
+    }
+
+    /**
+     * Creates a response from elements of a status line.
+     * The response will not have a reason phrase catalog and
+     * use the system default locale.
+     *
+     * @param ver       the protocol version of the response
+     * @param code      the status code of the response
+     * @param reason    the reason phrase to the status code, or
+     *                  <code>null</code>
+     */
+    public BasicHttpResponse(final ProtocolVersion ver,
+                             final int code,
+                             final String reason) {
+        this(new BasicStatusLine(ver, code, reason), null, null);
+    }
+
+
+    // non-javadoc, see interface HttpMessage
+    public ProtocolVersion getProtocolVersion() {
+        return this.statusline.getProtocolVersion();
+    }
+
+    // non-javadoc, see interface HttpResponse
+    public StatusLine getStatusLine() {
+        return this.statusline; 
+    }
+
+    // non-javadoc, see interface HttpResponse
+    public HttpEntity getEntity() {
+        return this.entity;
+    }
+
+    // non-javadoc, see interface HttpResponse
+    public Locale getLocale() {
+        return this.locale;
+    }
+
+    // non-javadoc, see interface HttpResponse
+    public void setStatusLine(final StatusLine statusline) {
+        if (statusline == null) {
+            throw new IllegalArgumentException("Status line may not be null");
+        }
+        this.statusline = statusline;
+    }
+
+    // non-javadoc, see interface HttpResponse
+    public void setStatusLine(final ProtocolVersion ver, final int code) {
+        // arguments checked in BasicStatusLine constructor
+        this.statusline = new BasicStatusLine(ver, code, getReason(code));
+    }
+
+    // non-javadoc, see interface HttpResponse
+    public void setStatusLine(final ProtocolVersion ver, final int code,
+                              final String reason) {
+        // arguments checked in BasicStatusLine constructor
+        this.statusline = new BasicStatusLine(ver, code, reason);
+    }
+
+    // non-javadoc, see interface HttpResponse
+    public void setStatusCode(int code) {
+        // argument checked in BasicStatusLine constructor
+        ProtocolVersion ver = this.statusline.getProtocolVersion();
+        this.statusline = new BasicStatusLine(ver, code, getReason(code));
+    }
+
+    // non-javadoc, see interface HttpResponse
+    public void setReasonPhrase(String reason) {
+
+        if ((reason != null) && ((reason.indexOf('\n') >= 0) ||
+                                 (reason.indexOf('\r') >= 0))
+            ) {
+            throw new IllegalArgumentException("Line break in reason phrase.");
+        }
+        this.statusline = new BasicStatusLine(this.statusline.getProtocolVersion(),
+                                              this.statusline.getStatusCode(),
+                                              reason);
+    }
+
+    // non-javadoc, see interface HttpResponse
+    public void setEntity(final HttpEntity entity) {
+        this.entity = entity;
+    }
+
+    // non-javadoc, see interface HttpResponse
+    public void setLocale(Locale loc) {
+        if (loc == null) {
+            throw new IllegalArgumentException("Locale may not be null.");
+        }
+        this.locale = loc;
+        final int code = this.statusline.getStatusCode();
+        this.statusline = new BasicStatusLine
+            (this.statusline.getProtocolVersion(), code, getReason(code));
+    }
+
+    /**
+     * Looks up a reason phrase.
+     * This method evaluates the currently set catalog and locale.
+     * It also handles a missing catalog.
+     *
+     * @param code      the status code for which to look up the reason
+     *
+     * @return  the reason phrase, or <code>null</code> if there is none
+     */
+    protected String getReason(int code) {
+        return (this.reasonCatalog == null) ?
+            null : this.reasonCatalog.getReason(code, this.locale);
+    }
+
+}
diff --git a/src/org/apache/http/message/BasicLineFormatter.java b/src/org/apache/http/message/BasicLineFormatter.java
new file mode 100644
index 0000000..7c3bbc4
--- /dev/null
+++ b/src/org/apache/http/message/BasicLineFormatter.java
@@ -0,0 +1,346 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicLineFormatter.java $
+ * $Revision: 574185 $
+ * $Date: 2007-09-10 02:19:47 -0700 (Mon, 10 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+
+import org.apache.http.ProtocolVersion;
+import org.apache.http.RequestLine;
+import org.apache.http.StatusLine;
+import org.apache.http.Header;
+import org.apache.http.FormattedHeader;
+import org.apache.http.util.CharArrayBuffer;
+
+
+/**
+ * Interface for formatting elements of the HEAD section of an HTTP message.
+ * This is the complement to {@link LineParser}.
+ * There are individual methods for formatting a request line, a
+ * status line, or a header line. The formatting does <i>not</i> include the
+ * trailing line break sequence CR-LF.
+ * The formatted lines are returned in memory, the formatter does not depend
+ * on any specific IO mechanism.
+ * Instances of this interface are expected to be stateless and thread-safe.
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author and others
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 574185 $
+ *
+ * @since 4.0
+ */
+public class BasicLineFormatter implements LineFormatter {
+
+    /**
+     * A default instance of this class, for use as default or fallback.
+     * Note that {@link BasicLineFormatter} is not a singleton, there can
+     * be many instances of the class itself and of derived classes.
+     * The instance here provides non-customized, default behavior.
+     */
+    public final static BasicLineFormatter DEFAULT = new BasicLineFormatter();
+
+
+
+    // public default constructor
+
+
+    /**
+     * Obtains a buffer for formatting.
+     *
+     * @param buffer    a buffer already available, or <code>null</code>
+     *
+     * @return  the cleared argument buffer if there is one, or
+     *          a new empty buffer that can be used for formatting
+     */
+    protected CharArrayBuffer initBuffer(CharArrayBuffer buffer) {
+        if (buffer != null) {
+            buffer.clear();
+        } else {
+            buffer = new CharArrayBuffer(64);
+        }
+        return buffer;
+    }
+
+
+    /**
+     * Formats a protocol version.
+     *
+     * @param version           the protocol version to format
+     * @param formatter         the formatter to use, or
+     *                          <code>null</code> for the
+     *                          {@link #DEFAULT default}
+     *
+     * @return  the formatted protocol version
+     */
+    public final static
+        String formatProtocolVersion(final ProtocolVersion version,
+                                     LineFormatter formatter) {
+        if (formatter == null)
+            formatter = BasicLineFormatter.DEFAULT;
+        return formatter.appendProtocolVersion(null, version).toString();
+    }
+
+
+    // non-javadoc, see interface LineFormatter
+    public CharArrayBuffer appendProtocolVersion(final CharArrayBuffer buffer,
+                                                 final ProtocolVersion version) {
+        if (version == null) {
+            throw new IllegalArgumentException
+                ("Protocol version may not be null");
+        }
+
+        // can't use initBuffer, that would clear the argument!
+        CharArrayBuffer result = buffer;
+        final int len = estimateProtocolVersionLen(version);
+        if (result == null) {
+            result = new CharArrayBuffer(len);
+        } else {
+            result.ensureCapacity(len);
+        }
+
+        result.append(version.getProtocol());
+        result.append('/'); 
+        result.append(Integer.toString(version.getMajor())); 
+        result.append('.'); 
+        result.append(Integer.toString(version.getMinor())); 
+
+        return result;
+    }
+
+
+    /**
+     * Guesses the length of a formatted protocol version.
+     * Needed to guess the length of a formatted request or status line.
+     *
+     * @param version   the protocol version to format, or <code>null</code>
+     *
+     * @return  the estimated length of the formatted protocol version,
+     *          in characters
+     */
+    protected int estimateProtocolVersionLen(final ProtocolVersion version) {
+        return version.getProtocol().length() + 4; // room for "HTTP/1.1"
+    }
+
+
+    /**
+     * Formats a request line.
+     *
+     * @param reqline           the request line to format
+     * @param formatter         the formatter to use, or
+     *                          <code>null</code> for the
+     *                          {@link #DEFAULT default}
+     *
+     * @return  the formatted request line
+     */
+    public final static String formatRequestLine(final RequestLine reqline,
+                                                 LineFormatter formatter) {
+        if (formatter == null)
+            formatter = BasicLineFormatter.DEFAULT;
+        return formatter.formatRequestLine(null, reqline).toString();
+    }
+
+
+    // non-javadoc, see interface LineFormatter
+    public CharArrayBuffer formatRequestLine(CharArrayBuffer buffer,
+                                             RequestLine reqline) {
+        if (reqline == null) {
+            throw new IllegalArgumentException
+                ("Request line may not be null");
+        }
+
+        CharArrayBuffer result = initBuffer(buffer);
+        doFormatRequestLine(result, reqline);
+
+        return result;
+    }
+
+
+    /**
+     * Actually formats a request line.
+     * Called from {@link #formatRequestLine}.
+     *
+     * @param buffer    the empty buffer into which to format,
+     *                  never <code>null</code>
+     * @param reqline   the request line to format, never <code>null</code>
+     */
+    protected void doFormatRequestLine(final CharArrayBuffer buffer,
+                                       final RequestLine reqline) {
+        final String method = reqline.getMethod();
+        final String uri    = reqline.getUri();
+
+        // room for "GET /index.html HTTP/1.1"
+        int len = method.length() + 1 + uri.length() + 1 + 
+            estimateProtocolVersionLen(reqline.getProtocolVersion());
+        buffer.ensureCapacity(len);
+
+        buffer.append(method);
+        buffer.append(' ');
+        buffer.append(uri);
+        buffer.append(' ');
+        appendProtocolVersion(buffer, reqline.getProtocolVersion());
+    }
+
+
+
+    /**
+     * Formats a status line.
+     *
+     * @param statline          the status line to format
+     * @param formatter         the formatter to use, or
+     *                          <code>null</code> for the
+     *                          {@link #DEFAULT default}
+     *
+     * @return  the formatted status line
+     */
+    public final static String formatStatusLine(final StatusLine statline,
+                                                LineFormatter formatter) {
+        if (formatter == null)
+            formatter = BasicLineFormatter.DEFAULT;
+        return formatter.formatStatusLine(null, statline).toString();
+    }
+
+
+    // non-javadoc, see interface LineFormatter
+    public CharArrayBuffer formatStatusLine(final CharArrayBuffer buffer,
+                                            final StatusLine statline) {
+        if (statline == null) {
+            throw new IllegalArgumentException
+                ("Status line may not be null");
+        }
+
+        CharArrayBuffer result = initBuffer(buffer);
+        doFormatStatusLine(result, statline);
+
+        return result;
+    }
+
+
+    /**
+     * Actually formats a status line.
+     * Called from {@link #formatStatusLine}.
+     *
+     * @param buffer    the empty buffer into which to format,
+     *                  never <code>null</code>
+     * @param statline  the status line to format, never <code>null</code>
+     */
+    protected void doFormatStatusLine(final CharArrayBuffer buffer,
+                                      final StatusLine statline) {
+
+        int len = estimateProtocolVersionLen(statline.getProtocolVersion())
+            + 1 + 3 + 1; // room for "HTTP/1.1 200 "
+        final String reason = statline.getReasonPhrase();
+        if (reason != null) {
+            len += reason.length();
+        }
+        buffer.ensureCapacity(len);
+
+        appendProtocolVersion(buffer, statline.getProtocolVersion());
+        buffer.append(' ');
+        buffer.append(Integer.toString(statline.getStatusCode()));
+        buffer.append(' '); // keep whitespace even if reason phrase is empty
+        if (reason != null) {
+            buffer.append(reason);
+        }
+    }
+
+
+    /**
+     * Formats a header.
+     *
+     * @param header            the header to format
+     * @param formatter         the formatter to use, or
+     *                          <code>null</code> for the
+     *                          {@link #DEFAULT default}
+     *
+     * @return  the formatted header
+     */
+    public final static String formatHeader(final Header header,
+                                            LineFormatter formatter) {
+        if (formatter == null)
+            formatter = BasicLineFormatter.DEFAULT;
+        return formatter.formatHeader(null, header).toString();
+    }
+
+
+    // non-javadoc, see interface LineFormatter
+    public CharArrayBuffer formatHeader(CharArrayBuffer buffer,
+                                        Header header) {
+        if (header == null) {
+            throw new IllegalArgumentException
+                ("Header may not be null");
+        }
+        CharArrayBuffer result = null;
+
+        if (header instanceof FormattedHeader) {
+            // If the header is backed by a buffer, re-use the buffer
+            result = ((FormattedHeader)header).getBuffer();
+        } else {
+            result = initBuffer(buffer);
+            doFormatHeader(result, header);
+        }
+        return result;
+
+    } // formatHeader
+
+
+    /**
+     * Actually formats a header.
+     * Called from {@link #formatHeader}.
+     *
+     * @param buffer    the empty buffer into which to format,
+     *                  never <code>null</code>
+     * @param header    the header to format, never <code>null</code>
+     */
+    protected void doFormatHeader(final CharArrayBuffer buffer,
+                                  final Header header) {
+        final String name = header.getName();
+        final String value = header.getValue();
+
+        int len = name.length() + 2;
+        if (value != null) {
+            len += value.length();
+        }
+        buffer.ensureCapacity(len);
+
+        buffer.append(name);
+        buffer.append(": ");
+        if (value != null) {
+            buffer.append(value);
+        }
+    }
+
+
+} // class BasicLineFormatter
diff --git a/src/org/apache/http/message/BasicLineParser.java b/src/org/apache/http/message/BasicLineParser.java
new file mode 100644
index 0000000..c5e9ddb
--- /dev/null
+++ b/src/org/apache/http/message/BasicLineParser.java
@@ -0,0 +1,504 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicLineParser.java $
+ * $Revision: 591798 $
+ * $Date: 2007-11-04 08:19:29 -0800 (Sun, 04 Nov 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.ParseException;
+import org.apache.http.RequestLine;
+import org.apache.http.StatusLine;
+import org.apache.http.Header;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.CharArrayBuffer;
+
+
+/**
+ * Basic parser for lines in the head section of an HTTP message.
+ * There are individual methods for parsing a request line, a
+ * status line, or a header line.
+ * The lines to parse are passed in memory, the parser does not depend
+ * on any specific IO mechanism.
+ * Instances of this class are stateless and thread-safe.
+ * Derived classes MUST maintain these properties.
+ *
+ * <p>
+ * Note: This class was created by refactoring parsing code located in
+ * various other classes. The author tags from those other classes have
+ * been replicated here, although the association with the parsing code
+ * taken from there has not been traced.
+ * </p>
+ *
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ * @author and others
+ */
+public class BasicLineParser implements LineParser {
+
+    /**
+     * A default instance of this class, for use as default or fallback.
+     * Note that {@link BasicLineParser} is not a singleton, there can
+     * be many instances of the class itself and of derived classes.
+     * The instance here provides non-customized, default behavior.
+     */
+    public final static BasicLineParser DEFAULT = new BasicLineParser();
+
+
+    /**
+     * A version of the protocol to parse.
+     * The version is typically not relevant, but the protocol name.
+     */
+    protected final ProtocolVersion protocol;
+
+
+    /**
+     * Creates a new line parser for the given HTTP-like protocol.
+     *
+     * @param proto     a version of the protocol to parse, or
+     *                  <code>null</code> for HTTP. The actual version
+     *                  is not relevant, only the protocol name.
+     */
+    public BasicLineParser(ProtocolVersion proto) {
+        if (proto == null) {
+            proto = HttpVersion.HTTP_1_1;
+        }
+        this.protocol = proto;
+    }
+
+
+    /**
+     * Creates a new line parser for HTTP.
+     */
+    public BasicLineParser() {
+        this(null);
+    }
+
+
+
+    public final static
+        ProtocolVersion parseProtocolVersion(String value,
+                                             LineParser parser)
+        throws ParseException {
+
+        if (value == null) {
+            throw new IllegalArgumentException
+                ("Value to parse may not be null.");
+        }
+
+        if (parser == null)
+            parser = BasicLineParser.DEFAULT;
+
+        CharArrayBuffer buffer = new CharArrayBuffer(value.length());
+        buffer.append(value);
+        ParserCursor cursor = new ParserCursor(0, value.length());
+        return parser.parseProtocolVersion(buffer, cursor);
+    }
+
+
+    // non-javadoc, see interface LineParser
+    public ProtocolVersion parseProtocolVersion(final CharArrayBuffer buffer,
+                                                final ParserCursor cursor) 
+        throws ParseException {
+
+        if (buffer == null) {
+            throw new IllegalArgumentException("Char array buffer may not be null");
+        }
+        if (cursor == null) {
+            throw new IllegalArgumentException("Parser cursor may not be null");
+        }
+
+        final String protoname = this.protocol.getProtocol();
+        final int protolength  = protoname.length();
+
+        int indexFrom = cursor.getPos();
+        int indexTo = cursor.getUpperBound();
+        
+        skipWhitespace(buffer, cursor);
+
+        int i = cursor.getPos();
+        
+        // long enough for "HTTP/1.1"?
+        if (i + protolength + 4 > indexTo) {
+            throw new ParseException
+                ("Not a valid protocol version: " +
+                 buffer.substring(indexFrom, indexTo));
+        }
+
+        // check the protocol name and slash
+        boolean ok = true;
+        for (int j=0; ok && (j<protolength); j++) {
+            ok = (buffer.charAt(i+j) == protoname.charAt(j));
+        }
+        if (ok) {
+            ok = (buffer.charAt(i+protolength) == '/');
+        }
+        if (!ok) {
+            throw new ParseException
+                ("Not a valid protocol version: " +
+                 buffer.substring(indexFrom, indexTo));
+        }
+
+        i += protolength+1;
+
+        int period = buffer.indexOf('.', i, indexTo);
+        if (period == -1) {
+            throw new ParseException
+                ("Invalid protocol version number: " + 
+                 buffer.substring(indexFrom, indexTo));
+        }
+        int major;
+        try {
+            major = Integer.parseInt(buffer.substringTrimmed(i, period)); 
+        } catch (NumberFormatException e) {
+            throw new ParseException
+                ("Invalid protocol major version number: " + 
+                 buffer.substring(indexFrom, indexTo));
+        }
+        i = period + 1;
+        
+        int blank = buffer.indexOf(' ', i, indexTo);
+        if (blank == -1) {
+            blank = indexTo;
+        }
+        int minor;
+        try {
+            minor = Integer.parseInt(buffer.substringTrimmed(i, blank)); 
+        } catch (NumberFormatException e) {
+            throw new ParseException(
+                "Invalid protocol minor version number: " + 
+                buffer.substring(indexFrom, indexTo));
+        }
+        
+        cursor.updatePos(blank);
+
+        return createProtocolVersion(major, minor);
+
+    } // parseProtocolVersion
+
+
+    /**
+     * Creates a protocol version.
+     * Called from {@link #parseProtocolVersion}.
+     *
+     * @param major     the major version number, for example 1 in HTTP/1.0
+     * @param minor     the minor version number, for example 0 in HTTP/1.0
+     *
+     * @return  the protocol version
+     */
+    protected ProtocolVersion createProtocolVersion(int major, int minor) {
+        return protocol.forVersion(major, minor);
+    }
+
+
+
+    // non-javadoc, see interface LineParser
+    public boolean hasProtocolVersion(final CharArrayBuffer buffer,
+                                      final ParserCursor cursor) {
+
+        if (buffer == null) {
+            throw new IllegalArgumentException("Char array buffer may not be null");
+        }
+        if (cursor == null) {
+            throw new IllegalArgumentException("Parser cursor may not be null");
+        }
+        int index = cursor.getPos();
+
+        final String protoname = this.protocol.getProtocol();
+        final int  protolength = protoname.length();
+
+        if (buffer.length() < protolength+4)
+            return false; // not long enough for "HTTP/1.1"
+
+        if (index < 0) {
+            // end of line, no tolerance for trailing whitespace
+            // this works only for single-digit major and minor version
+            index = buffer.length() -4 -protolength;
+        } else if (index == 0) {
+            // beginning of line, tolerate leading whitespace
+            while ((index < buffer.length()) &&
+                    HTTP.isWhitespace(buffer.charAt(index))) {
+                 index++;
+             }
+        } // else within line, don't tolerate whitespace
+
+
+        if (index + protolength + 4 > buffer.length())
+            return false;
+
+
+        // just check protocol name and slash, no need to analyse the version
+        boolean ok = true;
+        for (int j=0; ok && (j<protolength); j++) {
+            ok = (buffer.charAt(index+j) == protoname.charAt(j));
+        }
+        if (ok) {
+            ok = (buffer.charAt(index+protolength) == '/');
+        }
+
+        return ok;
+    }
+
+
+
+    public final static
+        RequestLine parseRequestLine(final String value,
+                                     LineParser parser)
+        throws ParseException {
+
+        if (value == null) {
+            throw new IllegalArgumentException
+                ("Value to parse may not be null.");
+        }
+
+        if (parser == null)
+            parser = BasicLineParser.DEFAULT;
+
+        CharArrayBuffer buffer = new CharArrayBuffer(value.length());
+        buffer.append(value);
+        ParserCursor cursor = new ParserCursor(0, value.length());
+        return parser.parseRequestLine(buffer, cursor);
+    }
+
+
+    /**
+     * Parses a request line.
+     *
+     * @param buffer    a buffer holding the line to parse
+     *
+     * @return  the parsed request line
+     *
+     * @throws ParseException        in case of a parse error
+     */
+    public RequestLine parseRequestLine(final CharArrayBuffer buffer,
+                                        final ParserCursor cursor)
+        throws ParseException {
+
+        if (buffer == null) {
+            throw new IllegalArgumentException("Char array buffer may not be null");
+        }
+        if (cursor == null) {
+            throw new IllegalArgumentException("Parser cursor may not be null");
+        }
+
+        int indexFrom = cursor.getPos();
+        int indexTo = cursor.getUpperBound();
+        
+        try {
+            skipWhitespace(buffer, cursor);
+            int i = cursor.getPos();
+            
+            int blank = buffer.indexOf(' ', i, indexTo);
+            if (blank < 0) {
+                throw new ParseException("Invalid request line: " + 
+                        buffer.substring(indexFrom, indexTo));
+            }
+            String method = buffer.substringTrimmed(i, blank);
+            cursor.updatePos(blank);
+
+            skipWhitespace(buffer, cursor);
+            i = cursor.getPos();
+
+            blank = buffer.indexOf(' ', i, indexTo);
+            if (blank < 0) {
+                throw new ParseException("Invalid request line: " + 
+                        buffer.substring(indexFrom, indexTo));
+            }
+            String uri = buffer.substringTrimmed(i, blank);
+            cursor.updatePos(blank);
+
+            ProtocolVersion ver = parseProtocolVersion(buffer, cursor);
+            
+            skipWhitespace(buffer, cursor);
+            if (!cursor.atEnd()) {
+                throw new ParseException("Invalid request line: " + 
+                        buffer.substring(indexFrom, indexTo));
+            }
+            
+            return createRequestLine(method, uri, ver);
+        } catch (IndexOutOfBoundsException e) {
+            throw new ParseException("Invalid request line: " + 
+                                     buffer.substring(indexFrom, indexTo)); 
+        }
+    } // parseRequestLine
+
+
+    /**
+     * Instantiates a new request line.
+     * Called from {@link #parseRequestLine}.
+     *
+     * @param method    the request method
+     * @param uri       the requested URI
+     * @param ver       the protocol version
+     *
+     * @return  a new status line with the given data
+     */
+    protected RequestLine createRequestLine(final String method,
+                                            final String uri,
+                                            final ProtocolVersion ver) {
+        return new BasicRequestLine(method, uri, ver);
+    }
+
+
+
+    public final static
+        StatusLine parseStatusLine(final String value,
+                                   LineParser parser)
+        throws ParseException {
+
+        if (value == null) {
+            throw new IllegalArgumentException
+                ("Value to parse may not be null.");
+        }
+
+        if (parser == null)
+            parser = BasicLineParser.DEFAULT;
+
+        CharArrayBuffer buffer = new CharArrayBuffer(value.length());
+        buffer.append(value);
+        ParserCursor cursor = new ParserCursor(0, value.length());
+        return parser.parseStatusLine(buffer, cursor);
+    }
+
+
+    // non-javadoc, see interface LineParser
+    public StatusLine parseStatusLine(final CharArrayBuffer buffer,
+                                      final ParserCursor cursor) 
+        throws ParseException {
+
+        if (buffer == null) {
+            throw new IllegalArgumentException("Char array buffer may not be null");
+        }
+        if (cursor == null) {
+            throw new IllegalArgumentException("Parser cursor may not be null");
+        }
+
+        int indexFrom = cursor.getPos();
+        int indexTo = cursor.getUpperBound();
+        
+        try {
+            // handle the HTTP-Version
+            ProtocolVersion ver = parseProtocolVersion(buffer, cursor);
+
+            // handle the Status-Code
+            skipWhitespace(buffer, cursor);
+            int i = cursor.getPos();
+            
+            int blank = buffer.indexOf(' ', i, indexTo);
+            if (blank < 0) {
+                blank = indexTo;
+            }
+            int statusCode = 0;
+            try {
+                statusCode =
+                    Integer.parseInt(buffer.substringTrimmed(i, blank));
+            } catch (NumberFormatException e) {
+                throw new ParseException(
+                    "Unable to parse status code from status line: " 
+                    + buffer.substring(indexFrom, indexTo));
+            }
+            //handle the Reason-Phrase
+            i = blank;
+            String reasonPhrase = null;
+            if (i < indexTo) {
+                reasonPhrase = buffer.substringTrimmed(i, indexTo);
+            } else {
+                reasonPhrase = "";
+            }
+            return createStatusLine(ver, statusCode, reasonPhrase);
+
+        } catch (IndexOutOfBoundsException e) {
+            throw new ParseException("Invalid status line: " + 
+                                     buffer.substring(indexFrom, indexTo)); 
+        }
+    } // parseStatusLine
+
+
+    /**
+     * Instantiates a new status line.
+     * Called from {@link #parseStatusLine}.
+     *
+     * @param ver       the protocol version
+     * @param status    the status code
+     * @param reason    the reason phrase
+     *
+     * @return  a new status line with the given data
+     */
+    protected StatusLine createStatusLine(final ProtocolVersion ver,
+                                          final int status, 
+                                          final String reason) {
+        return new BasicStatusLine(ver, status, reason);
+    }
+
+
+
+    public final static
+        Header parseHeader(final String value, 
+                           LineParser parser)
+        throws ParseException {
+
+        if (value == null) {
+            throw new IllegalArgumentException
+                ("Value to parse may not be null");
+        }
+
+        if (parser == null)
+            parser = BasicLineParser.DEFAULT;
+
+        CharArrayBuffer buffer = new CharArrayBuffer(value.length());
+        buffer.append(value);
+        return parser.parseHeader(buffer);
+    }
+
+
+    // non-javadoc, see interface LineParser
+    public Header parseHeader(CharArrayBuffer buffer)
+        throws ParseException {
+
+        // the actual parser code is in the constructor of BufferedHeader
+        return new BufferedHeader(buffer);
+    }
+
+
+    /**
+     * Helper to skip whitespace.
+     */
+    protected void skipWhitespace(final CharArrayBuffer buffer, final ParserCursor cursor) {
+        int pos = cursor.getPos();
+        int indexTo = cursor.getUpperBound();
+        while ((pos < indexTo) &&
+               HTTP.isWhitespace(buffer.charAt(pos))) {
+            pos++;
+        }
+        cursor.updatePos(pos);
+    }
+
+} // class BasicLineParser
diff --git a/src/org/apache/http/message/BasicListHeaderIterator.java b/src/org/apache/http/message/BasicListHeaderIterator.java
new file mode 100644
index 0000000..69b8c06
--- /dev/null
+++ b/src/org/apache/http/message/BasicListHeaderIterator.java
@@ -0,0 +1,196 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicListHeaderIterator.java $
+ * $Revision: 584542 $
+ * $Date: 2007-10-14 06:29:34 -0700 (Sun, 14 Oct 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+
+
+/**
+ * Implementation of a {@link HeaderIterator} based on a {@link List}.
+ * For use by {@link HeaderGroup}.
+ * 
+ * @version $Revision: 584542 $
+ */
+public class BasicListHeaderIterator implements HeaderIterator {
+
+    /**
+     * A list of headers to iterate over.
+     * Not all elements of this array are necessarily part of the iteration.
+     */
+    protected final List allHeaders;
+
+
+    /**
+     * The position of the next header in {@link #allHeaders allHeaders}.
+     * Negative if the iteration is over.
+     */
+    protected int currentIndex;
+
+
+    /**
+     * The position of the last returned header.
+     * Negative if none has been returned so far.
+     */
+    protected int lastIndex;
+
+
+    /**
+     * The header name to filter by.
+     * <code>null</code> to iterate over all headers in the array.
+     */
+    protected String headerName;
+
+
+
+    /**
+     * Creates a new header iterator.
+     *
+     * @param headers   a list of headers over which to iterate
+     * @param name      the name of the headers over which to iterate, or
+     *                  <code>null</code> for any
+     */
+    public BasicListHeaderIterator(List headers, String name) {
+        if (headers == null) {
+            throw new IllegalArgumentException
+                ("Header list must not be null.");
+        }
+
+        this.allHeaders = headers;
+        this.headerName = name;
+        this.currentIndex = findNext(-1);
+        this.lastIndex = -1;
+    }
+
+
+    /**
+     * Determines the index of the next header.
+     *
+     * @param from      one less than the index to consider first,
+     *                  -1 to search for the first header
+     *
+     * @return  the index of the next header that matches the filter name,
+     *          or negative if there are no more headers
+     */
+    protected int findNext(int from) {
+        if (from < -1)
+            return -1;
+
+        final int to = this.allHeaders.size()-1;
+        boolean found = false;
+        while (!found && (from < to)) {
+            from++;
+            found = filterHeader(from);
+        }
+        return found ? from : -1;
+    }
+
+
+    /**
+     * Checks whether a header is part of the iteration.
+     *
+     * @param index     the index of the header to check
+     *
+     * @return  <code>true</code> if the header should be part of the
+     *          iteration, <code>false</code> to skip
+     */
+    protected boolean filterHeader(int index) {
+        if (this.headerName == null)
+            return true;
+
+        // non-header elements, including null, will trigger exceptions
+        final String name = ((Header)this.allHeaders.get(index)).getName();
+
+        return this.headerName.equalsIgnoreCase(name);
+    }
+
+
+    // non-javadoc, see interface HeaderIterator
+    public boolean hasNext() {
+        return (this.currentIndex >= 0);
+    }
+
+
+    /**
+     * Obtains the next header from this iteration.
+     *
+     * @return  the next header in this iteration
+     *
+     * @throws NoSuchElementException   if there are no more headers
+     */
+    public Header nextHeader()
+        throws NoSuchElementException {
+
+        final int current = this.currentIndex;
+        if (current < 0) {
+            throw new NoSuchElementException("Iteration already finished.");
+        }
+
+        this.lastIndex    = current;
+        this.currentIndex = findNext(current);
+
+        return (Header) this.allHeaders.get(current);
+    }
+
+
+    /**
+     * Returns the next header.
+     * Same as {@link #nextHeader nextHeader}, but not type-safe.
+     *
+     * @return  the next header in this iteration
+     *
+     * @throws NoSuchElementException   if there are no more headers
+     */
+    public final Object next()
+        throws NoSuchElementException {
+        return nextHeader();
+    }
+
+
+    /**
+     * Removes the header that was returned last.
+     */
+    public void remove()
+        throws UnsupportedOperationException {
+
+        if (this.lastIndex < 0) {
+            throw new IllegalStateException("No header to remove.");
+        }
+        this.allHeaders.remove(this.lastIndex);
+        this.lastIndex = -1;
+        this.currentIndex--; // adjust for the removed element
+    }
+}
diff --git a/src/org/apache/http/message/BasicNameValuePair.java b/src/org/apache/http/message/BasicNameValuePair.java
new file mode 100644
index 0000000..59fcb42
--- /dev/null
+++ b/src/org/apache/http/message/BasicNameValuePair.java
@@ -0,0 +1,189 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicNameValuePair.java $
+ * $Revision: 604625 $
+ * $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.util.CharArrayBuffer;
+import org.apache.http.util.LangUtils;
+
+/**
+ * A simple class encapsulating an attribute/value pair.
+ * <p>
+ *  This class comforms to the generic grammar and formatting rules outlined in the 
+ *  <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2">Section 2.2</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.2 Basic Rules</h>
+ * <p>
+ *  The following rules are used throughout this specification to describe basic parsing constructs. 
+ *  The US-ASCII coded character set is defined by ANSI X3.4-1986.
+ * </p>
+ * <pre>
+ *     OCTET          = <any 8-bit sequence of data>
+ *     CHAR           = <any US-ASCII character (octets 0 - 127)>
+ *     UPALPHA        = <any US-ASCII uppercase letter "A".."Z">
+ *     LOALPHA        = <any US-ASCII lowercase letter "a".."z">
+ *     ALPHA          = UPALPHA | LOALPHA
+ *     DIGIT          = <any US-ASCII digit "0".."9">
+ *     CTL            = <any US-ASCII control character
+ *                      (octets 0 - 31) and DEL (127)>
+ *     CR             = <US-ASCII CR, carriage return (13)>
+ *     LF             = <US-ASCII LF, linefeed (10)>
+ *     SP             = <US-ASCII SP, space (32)>
+ *     HT             = <US-ASCII HT, horizontal-tab (9)>
+ *     <">            = <US-ASCII double-quote mark (34)>
+ * </pre>
+ * <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.com">Oleg Kalnichevski</a>
+ * 
+ */
+public class BasicNameValuePair implements NameValuePair, Cloneable {
+
+    private final String name;
+    private final String value;
+
+    /**
+     * Default Constructor taking a name and a value. The value may be null.
+     * 
+     * @param name The name.
+     * @param value The value.
+     */
+    public BasicNameValuePair(final String name, final String value) {
+        super();
+        if (name == null) {
+            throw new IllegalArgumentException("Name may not be null");
+        }
+        this.name = name;
+        this.value = value;
+    }
+
+    /**
+     * Returns the name.
+     *
+     * @return String name The name
+     */
+    public String getName() {
+        return this.name;
+    }
+
+    /**
+     * Returns the value.
+     *
+     * @return String value The current value.
+     */
+    public String getValue() {
+        return this.value;
+    }
+
+    
+    /**
+     * Get a string representation of this pair.
+     * 
+     * @return A string representation.
+     */
+    public String toString() {
+        // don't call complex default formatting for a simple toString
+
+        int len = this.name.length();
+        if (this.value != null)
+            len += 1 + this.value.length();
+        CharArrayBuffer buffer = new CharArrayBuffer(len);
+
+        buffer.append(this.name);
+        if (this.value != null) {
+            buffer.append("=");
+            buffer.append(this.value);
+        }
+        return buffer.toString();
+    }
+
+    public boolean equals(final Object object) {
+        if (object == null) return false;
+        if (this == object) return true;
+        if (object instanceof NameValuePair) {
+            BasicNameValuePair that = (BasicNameValuePair) object;
+            return this.name.equals(that.name)
+                  && LangUtils.equals(this.value, that.value);
+        } else {
+            return false;
+        }
+    }
+
+    public int hashCode() {
+        int hash = LangUtils.HASH_SEED;
+        hash = LangUtils.hashCode(hash, this.name);
+        hash = LangUtils.hashCode(hash, this.value);
+        return hash;
+    }
+    
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+ 
+}
diff --git a/src/org/apache/http/message/BasicRequestLine.java b/src/org/apache/http/message/BasicRequestLine.java
new file mode 100644
index 0000000..b826064
--- /dev/null
+++ b/src/org/apache/http/message/BasicRequestLine.java
@@ -0,0 +1,99 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicRequestLine.java $
+ * $Revision: 604625 $
+ * $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.ProtocolVersion;
+import org.apache.http.RequestLine;
+
+/**
+ * The first line of an {@link org.apache.http.HttpRequest HttpRequest}.
+ * It contains the method, URI, and HTTP version of the request.
+ * For details, see RFC 2616.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 604625 $
+ * 
+ * @since 4.0
+ */
+public class BasicRequestLine implements RequestLine, Cloneable {
+
+    private final ProtocolVersion protoversion;
+    private final String method;
+    private final String uri;
+
+    public BasicRequestLine(final String method,
+                            final String uri,
+                            final ProtocolVersion version) {
+        super();
+        if (method == null) {
+            throw new IllegalArgumentException
+                ("Method must not be null.");
+        }
+        if (uri == null) {
+            throw new IllegalArgumentException
+                ("URI must not be null.");
+        }
+        if (version == null) {
+            throw new IllegalArgumentException
+                ("Protocol version must not be null.");
+        }
+        this.method = method;
+        this.uri = uri;
+        this.protoversion = version;
+    }
+
+    public String getMethod() {
+        return this.method;
+    }
+
+    public ProtocolVersion getProtocolVersion() {
+        return this.protoversion;
+    }
+
+    public String getUri() {
+        return this.uri;
+    }
+
+    public String toString() {
+        // no need for non-default formatting in toString()
+        return BasicLineFormatter.DEFAULT
+            .formatRequestLine(null, this).toString();
+    }
+    
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+    
+}
diff --git a/src/org/apache/http/message/BasicStatusLine.java b/src/org/apache/http/message/BasicStatusLine.java
new file mode 100644
index 0000000..c34cefe
--- /dev/null
+++ b/src/org/apache/http/message/BasicStatusLine.java
@@ -0,0 +1,124 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicStatusLine.java $
+ * $Revision: 604625 $
+ * $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.HttpStatus;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.StatusLine;
+
+
+
+/**
+ * Represents a status line as returned from a HTTP server.
+ * See <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a> section 6.1.
+ * This class is immutable and therefore inherently thread safe.
+ *
+ * @see HttpStatus
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ *
+ * @version $Id: BasicStatusLine.java 604625 2007-12-16 14:11:11Z olegk $
+ * 
+ * @since 4.0
+ */
+public class BasicStatusLine implements StatusLine, Cloneable {
+
+    // ----------------------------------------------------- Instance Variables
+
+    /** The protocol version. */
+    private final ProtocolVersion protoVersion;
+
+    /** The status code. */
+    private final int statusCode;
+
+    /** The reason phrase. */
+    private final String reasonPhrase;
+
+    // ----------------------------------------------------------- Constructors
+    /**
+     * Creates a new status line with the given version, status, and reason.
+     *
+     * @param version           the protocol version of the response
+     * @param statusCode        the status code of the response
+     * @param reasonPhrase      the reason phrase to the status code, or
+     *                          <code>null</code>
+     */
+    public BasicStatusLine(final ProtocolVersion version, int statusCode,
+                           final String reasonPhrase) {
+        super();
+        if (version == null) {
+            throw new IllegalArgumentException
+                ("Protocol version may not be null.");
+        }
+        if (statusCode < 0) {
+            throw new IllegalArgumentException
+                ("Status code may not be negative.");
+        }
+        this.protoVersion = version;
+        this.statusCode   = statusCode;
+        this.reasonPhrase = reasonPhrase;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * @return the Status-Code
+     */
+    public int getStatusCode() {
+        return this.statusCode;
+    }
+
+    /**
+     * @return the HTTP-Version
+     */
+    public ProtocolVersion getProtocolVersion() {
+        return this.protoVersion;
+    }
+
+    /**
+     * @return the Reason-Phrase
+     */
+    public String getReasonPhrase() {
+        return this.reasonPhrase;
+    }
+
+    public String toString() {
+        // no need for non-default formatting in toString()
+        return BasicLineFormatter.DEFAULT
+            .formatStatusLine(null, this).toString();
+    }
+    
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+    
+}
diff --git a/src/org/apache/http/message/BasicTokenIterator.java b/src/org/apache/http/message/BasicTokenIterator.java
new file mode 100644
index 0000000..5fbf5ba
--- /dev/null
+++ b/src/org/apache/http/message/BasicTokenIterator.java
@@ -0,0 +1,429 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicTokenIterator.java $
+ * $Revision: 602520 $
+ * $Date: 2007-12-08 09:42:26 -0800 (Sat, 08 Dec 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import java.util.NoSuchElementException;
+
+import org.apache.http.HeaderIterator;
+import org.apache.http.ParseException;
+import org.apache.http.TokenIterator;
+
+/**
+ * Basic implementation of a {@link TokenIterator}.
+ * This implementation parses <tt>#token<tt> sequences as
+ * defined by RFC 2616, section 2.
+ * It extends that definition somewhat beyond US-ASCII.
+ * 
+ * @version $Revision: 602520 $
+ */
+public class BasicTokenIterator implements TokenIterator {
+
+    /** The HTTP separator characters. Defined in RFC 2616, section 2.2. */
+    // the order of the characters here is adjusted to put the
+    // most likely candidates at the beginning of the collection
+    public final static String HTTP_SEPARATORS = " ,;=()<>@:\\\"/[]?{}\t";
+
+
+    /** The iterator from which to obtain the next header. */
+    protected final HeaderIterator headerIt;
+
+    /**
+     * The value of the current header.
+     * This is the header value that includes {@link #currentToken}.
+     * Undefined if the iteration is over.
+     */
+    protected String currentHeader;
+
+    /**
+     * The token to be returned by the next call to {@link #currentToken}.
+     * <code>null</code> if the iteration is over.
+     */
+    protected String currentToken;
+
+    /**
+     * The position after {@link #currentToken} in {@link #currentHeader}.
+     * Undefined if the iteration is over.
+     */
+    protected int searchPos;
+
+
+    /**
+     * Creates a new instance of {@link BasicTokenIterator}.
+     *
+     * @param headerIterator    the iterator for the headers to tokenize
+     */
+    public BasicTokenIterator(final HeaderIterator headerIterator) {
+        if (headerIterator == null) {
+            throw new IllegalArgumentException
+                ("Header iterator must not be null.");
+        }
+
+        this.headerIt = headerIterator;
+        this.searchPos = findNext(-1);
+    }
+
+
+    // non-javadoc, see interface TokenIterator
+    public boolean hasNext() {
+        return (this.currentToken != null);
+    }
+
+
+    /**
+     * Obtains the next token from this iteration.
+     *
+     * @return  the next token in this iteration
+     *
+     * @throws NoSuchElementException   if the iteration is already over
+     * @throws ParseException   if an invalid header value is encountered
+     */
+    public String nextToken()
+        throws NoSuchElementException, ParseException {
+
+        if (this.currentToken == null) {
+            throw new NoSuchElementException("Iteration already finished.");
+        }
+
+        final String result = this.currentToken;
+        // updates currentToken, may trigger ParseException:
+        this.searchPos = findNext(this.searchPos);
+
+        return result;
+    }
+
+
+    /**
+     * Returns the next token.
+     * Same as {@link #nextToken}, but with generic return type.
+     *
+     * @return  the next token in this iteration
+     *
+     * @throws NoSuchElementException   if there are no more tokens
+     * @throws ParseException   if an invalid header value is encountered
+     */
+    public final Object next()
+        throws NoSuchElementException, ParseException {
+        return nextToken();
+    }
+
+
+    /**
+     * Removing tokens is not supported.
+     *
+     * @throws UnsupportedOperationException    always
+     */
+    public final void remove()
+        throws UnsupportedOperationException {
+
+        throw new UnsupportedOperationException
+            ("Removing tokens is not supported.");
+    }
+
+
+    /**
+     * Determines the next token.
+     * If found, the token is stored in {@link #currentToken}.
+     * The return value indicates the position after the token
+     * in {@link #currentHeader}. If necessary, the next header
+     * will be obtained from {@link #headerIt}.
+     * If not found, {@link #currentToken} is set to <code>null</code>.
+     *
+     * @param from      the position in the current header at which to
+     *                  start the search, -1 to search in the first header
+     *
+     * @return  the position after the found token in the current header, or
+     *          negative if there was no next token
+     *
+     * @throws ParseException   if an invalid header value is encountered
+     */
+    protected int findNext(int from)
+        throws ParseException {
+
+        if (from < 0) {
+            // called from the constructor, initialize the first header
+            if (!this.headerIt.hasNext()) {
+                return -1;
+            }
+            this.currentHeader = this.headerIt.nextHeader().getValue();
+            from = 0;
+        } else {
+            // called after a token, make sure there is a separator
+            from = findTokenSeparator(from);
+        }
+
+        int start = findTokenStart(from);
+        if (start < 0) {
+            this.currentToken = null;
+            return -1; // nothing found
+        }
+
+        int end = findTokenEnd(start);
+        this.currentToken = createToken(this.currentHeader, start, end);
+        return end;
+    }
+
+
+    /**
+     * Creates a new token to be returned.
+     * Called from {@link #findNext findNext} after the token is identified.
+     * The default implementation simply calls
+     * {@link java.lang.String#substring String.substring}.
+     * <br/>
+     * If header values are significantly longer than tokens, and some
+     * tokens are permanently referenced by the application, there can
+     * be problems with garbage collection. A substring will hold a
+     * reference to the full characters of the original string and
+     * therefore occupies more memory than might be expected.
+     * To avoid this, override this method and create a new string
+     * instead of a substring.
+     *
+     * @param value     the full header value from which to create a token
+     * @param start     the index of the first token character
+     * @param end       the index after the last token character
+     *
+     * @return  a string representing the token identified by the arguments
+     */
+    protected String createToken(String value, int start, int end) {
+        return value.substring(start, end);
+    }
+
+
+    /**
+     * Determines the starting position of the next token.
+     * This method will iterate over headers if necessary.
+     *
+     * @param from      the position in the current header at which to
+     *                  start the search
+     *
+     * @return  the position of the token start in the current header,
+     *          negative if no token start could be found
+     */
+    protected int findTokenStart(int from) {
+        if (from < 0) {
+            throw new IllegalArgumentException
+                ("Search position must not be negative: " + from);
+        }
+
+        boolean found = false;
+        while (!found && (this.currentHeader != null)) {
+
+            final int to = this.currentHeader.length();
+            while (!found && (from < to)) {
+
+                final char ch = this.currentHeader.charAt(from);
+                if (isTokenSeparator(ch) || isWhitespace(ch)) {
+                    // whitspace and token separators are skipped
+                    from++;
+                } else if (isTokenChar(this.currentHeader.charAt(from))) {
+                    // found the start of a token
+                    found = true;
+                } else {
+                    throw new ParseException
+                        ("Invalid character before token (pos " + from +
+                         "): " + this.currentHeader);
+                }
+            }
+            if (!found) {
+                if (this.headerIt.hasNext()) {
+                    this.currentHeader = this.headerIt.nextHeader().getValue();
+                    from = 0;
+                } else {
+                    this.currentHeader = null;
+                }
+            }
+        } // while headers
+
+        return found ? from : -1;
+    }
+
+
+    /**
+     * Determines the position of the next token separator.
+     * Because of multi-header joining rules, the end of a
+     * header value is a token separator. This method does
+     * therefore not need to iterate over headers.
+     *
+     * @param from      the position in the current header at which to
+     *                  start the search
+     *
+     * @return  the position of a token separator in the current header,
+     *          or at the end
+     *
+     * @throws ParseException
+     *         if a new token is found before a token separator.
+     *         RFC 2616, section 2.1 explicitly requires a comma between
+     *         tokens for <tt>#</tt>.
+     */
+    protected int findTokenSeparator(int from) {
+        if (from < 0) {
+            throw new IllegalArgumentException
+                ("Search position must not be negative: " + from);
+        }
+
+        boolean found = false;
+        final int to = this.currentHeader.length();
+        while (!found && (from < to)) {
+            final char ch = this.currentHeader.charAt(from);
+            if (isTokenSeparator(ch)) {
+                found = true;
+            } else if (isWhitespace(ch)) {
+                from++;
+            } else if (isTokenChar(ch)) {
+                throw new ParseException
+                    ("Tokens without separator (pos " + from +
+                     "): " + this.currentHeader);
+            } else {
+                throw new ParseException
+                    ("Invalid character after token (pos " + from +
+                     "): " + this.currentHeader);
+            }
+        }
+
+        return from;
+    }
+
+
+    /**
+     * Determines the ending position of the current token.
+     * This method will not leave the current header value,
+     * since the end of the header value is a token boundary.
+     *
+     * @param from      the position of the first character of the token
+     *
+     * @return  the position after the last character of the token.
+     *          The behavior is undefined if <code>from</code> does not
+     *          point to a token character in the current header value.
+     */
+    protected int findTokenEnd(int from) {
+        if (from < 0) {
+            throw new IllegalArgumentException
+                ("Token start position must not be negative: " + from);
+        }
+
+        final int to = this.currentHeader.length();
+        int end = from+1;
+        while ((end < to) && isTokenChar(this.currentHeader.charAt(end))) {
+            end++;
+        }
+
+        return end;
+    }
+
+
+    /**
+     * Checks whether a character is a token separator.
+     * RFC 2616, section 2.1 defines comma as the separator for
+     * <tt>#token</tt> sequences. The end of a header value will
+     * also separate tokens, but that is not a character check.
+     *
+     * @param ch        the character to check
+     *
+     * @return  <code>true</code> if the character is a token separator,
+     *          <code>false</code> otherwise
+     */
+    protected boolean isTokenSeparator(char ch) {
+        return (ch == ',');
+    }
+
+
+    /**
+     * Checks whether a character is a whitespace character.
+     * RFC 2616, section 2.2 defines space and horizontal tab as whitespace.
+     * The optional preceeding line break is irrelevant, since header
+     * continuation is handled transparently when parsing messages.
+     *
+     * @param ch        the character to check
+     *
+     * @return  <code>true</code> if the character is whitespace,
+     *          <code>false</code> otherwise
+     */
+    protected boolean isWhitespace(char ch) {
+
+        // we do not use Character.isWhitspace(ch) here, since that allows
+        // many control characters which are not whitespace as per RFC 2616
+        return ((ch == '\t') || Character.isSpaceChar(ch));
+    }
+
+
+    /**
+     * Checks whether a character is a valid token character.
+     * Whitespace, control characters, and HTTP separators are not
+     * valid token characters. The HTTP specification (RFC 2616, section 2.2)
+     * defines tokens only for the US-ASCII character set, this
+     * method extends the definition to other character sets.
+     *
+     * @param ch        the character to check
+     *
+     * @return  <code>true</code> if the character is a valid token start,
+     *          <code>false</code> otherwise
+     */
+    protected boolean isTokenChar(char ch) {
+
+        // common sense extension of ALPHA + DIGIT
+        if (Character.isLetterOrDigit(ch))
+            return true;
+
+        // common sense extension of CTL
+        if (Character.isISOControl(ch))
+            return false;
+
+        // no common sense extension for this
+        if (isHttpSeparator(ch))
+            return false;
+
+        // RFC 2616, section 2.2 defines a token character as
+        // "any CHAR except CTLs or separators". The controls
+        // and separators are included in the checks above.
+        // This will yield unexpected results for Unicode format characters.
+        // If that is a problem, overwrite isHttpSeparator(char) to filter
+        // out the false positives.
+        return true;
+    }
+
+
+    /**
+     * Checks whether a character is an HTTP separator.
+     * The implementation in this class checks only for the HTTP separators
+     * defined in RFC 2616, section 2.2. If you need to detect other
+     * separators beyond the US-ASCII character set, override this method.
+     *
+     * @param ch        the character to check
+     *
+     * @return  <code>true</code> if the character is an HTTP separator
+     */
+    protected boolean isHttpSeparator(char ch) {
+        return (HTTP_SEPARATORS.indexOf(ch) >= 0);
+    }
+
+
+} // class BasicTokenIterator
+    
diff --git a/src/org/apache/http/message/BufferedHeader.java b/src/org/apache/http/message/BufferedHeader.java
new file mode 100644
index 0000000..35c5cfc
--- /dev/null
+++ b/src/org/apache/http/message/BufferedHeader.java
@@ -0,0 +1,133 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BufferedHeader.java $
+ * $Revision: 604625 $
+ * $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.FormattedHeader;
+import org.apache.http.HeaderElement;
+import org.apache.http.ParseException;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * This class represents a raw HTTP header whose content is parsed 'on demand'
+ * only when the header value needs to be consumed.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 604625 $ $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ */
+public class BufferedHeader implements FormattedHeader, Cloneable {
+
+    /**
+     * Header name.
+     */
+    private final String name;
+
+    /**
+     * The buffer containing the entire header line.
+     */
+    private final CharArrayBuffer buffer;
+    
+    /**
+     * The beginning of the header value in the buffer
+     */
+    private final int valuePos;
+
+
+    /**
+     * Creates a new header from a buffer.
+     * The name of the header will be parsed immediately,
+     * the value only if it is accessed.
+     *
+     * @param buffer    the buffer containing the header to represent
+     *
+     * @throws ParseException   in case of a parse error
+     */
+    public BufferedHeader(final CharArrayBuffer buffer)
+        throws ParseException {
+
+        super();
+        if (buffer == null) {
+            throw new IllegalArgumentException
+                ("Char array buffer may not be null");
+        }
+        int colon = buffer.indexOf(':');
+        if (colon == -1) {
+            throw new ParseException
+                ("Invalid header: " + buffer.toString());
+        }
+        String s = buffer.substringTrimmed(0, colon);
+        if (s.length() == 0) {
+            throw new ParseException
+                ("Invalid header: " + buffer.toString());
+        }
+        this.buffer = buffer;
+        this.name = s;
+        this.valuePos = colon + 1;
+    }
+
+
+    public String getName() {
+        return this.name;
+    }
+
+    public String getValue() {
+        return this.buffer.substringTrimmed(this.valuePos, this.buffer.length());
+    }
+
+    public HeaderElement[] getElements() throws ParseException {
+        ParserCursor cursor = new ParserCursor(0, this.buffer.length());
+        cursor.updatePos(this.valuePos);
+        return BasicHeaderValueParser.DEFAULT
+            .parseElements(this.buffer, cursor);
+    }
+
+    public int getValuePos() {
+        return this.valuePos;
+    }
+
+    public CharArrayBuffer getBuffer() {
+        return this.buffer;
+    }
+
+    public String toString() {
+        return this.buffer.toString();
+    }
+
+    public Object clone() throws CloneNotSupportedException {
+        // buffer is considered immutable
+        // no need to make a copy of it
+        return super.clone();
+    }
+ 
+}
diff --git a/src/org/apache/http/message/HeaderGroup.java b/src/org/apache/http/message/HeaderGroup.java
new file mode 100644
index 0000000..4e40db1
--- /dev/null
+++ b/src/org/apache/http/message/HeaderGroup.java
@@ -0,0 +1,295 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/HeaderGroup.java $
+ * $Revision: 659185 $
+ * $Date: 2008-05-22 11:07:36 -0700 (Thu, 22 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * A class for combining a set of headers.
+ * This class allows for multiple headers with the same name and
+ * keeps track of the order in which headers were added.
+ * 
+ * @author Michael Becke
+ *
+ * @since 4.0
+ */
+public class HeaderGroup implements Cloneable {
+
+    /** The list of headers for this group, in the order in which they were added */
+    private List headers;
+
+    /**
+     * Constructor for HeaderGroup.
+     */
+    public HeaderGroup() {
+        this.headers = new ArrayList(16);
+    }
+    
+    /**
+     * Removes any contained headers.
+     */
+    public void clear() {
+        headers.clear();
+    }
+    
+    /**
+     * Adds the given header to the group.  The order in which this header was
+     * added is preserved.
+     * 
+     * @param header the header to add
+     */
+    public void addHeader(Header header) {
+        if (header == null) {
+            return;
+        }
+        headers.add(header);
+    }
+    
+    /**
+     * Removes the given header.
+     *
+     * @param header the header to remove
+     */
+    public void removeHeader(Header header) {
+        if (header == null) {
+            return;
+        }
+        headers.remove(header);
+    }
+
+    /**
+     * Replaces the first occurence of the header with the same name. If no header with 
+     * the same name is found the given header is added to the end of the list.
+     * 
+     * @param header the new header that should replace the first header with the same 
+     * name if present in the list.
+     */
+    public void updateHeader(Header header) {
+        if (header == null) {
+            return;
+        }
+        for (int i = 0; i < this.headers.size(); i++) {
+            Header current = (Header) this.headers.get(i);
+            if (current.getName().equalsIgnoreCase(header.getName())) {
+                this.headers.set(i, header);
+                return;
+            }
+        }
+        this.headers.add(header);
+    }
+
+    /**
+     * Sets all of the headers contained within this group overriding any
+     * existing headers. The headers are added in the order in which they appear
+     * in the array.
+     * 
+     * @param headers the headers to set
+     */
+    public void setHeaders(Header[] headers) {
+        clear();
+        if (headers == null) {
+            return;
+        }
+        for (int i = 0; i < headers.length; i++) {
+            this.headers.add(headers[i]);
+        }
+    }
+    
+    /**
+     * Gets a header representing all of the header values with the given name.
+     * If more that one header with the given name exists the values will be
+     * combined with a "," as per RFC 2616.
+     * 
+     * <p>Header name comparison is case insensitive.
+     * 
+     * @param name the name of the header(s) to get
+     * @return a header with a condensed value or <code>null</code> if no
+     * headers by the given name are present
+     */
+    public Header getCondensedHeader(String name) {
+        Header[] headers = getHeaders(name);
+        
+        if (headers.length == 0) {
+            return null;   
+        } else if (headers.length == 1) {
+            return headers[0];
+        } else {
+            CharArrayBuffer valueBuffer = new CharArrayBuffer(128);
+            valueBuffer.append(headers[0].getValue());
+            for (int i = 1; i < headers.length; i++) {
+                valueBuffer.append(", ");
+                valueBuffer.append(headers[i].getValue());
+            }
+            
+            return new BasicHeader(name.toLowerCase(Locale.ENGLISH), valueBuffer.toString());
+        }
+    }
+    
+    /**
+     * Gets all of the headers with the given name.  The returned array
+     * maintains the relative order in which the headers were added.  
+     * 
+     * <p>Header name comparison is case insensitive.
+     * 
+     * @param name the name of the header(s) to get
+     * 
+     * @return an array of length >= 0
+     */
+    public Header[] getHeaders(String name) {
+        ArrayList headersFound = new ArrayList();
+        
+        for (int i = 0; i < headers.size(); i++) {
+            Header header = (Header) headers.get(i);
+            if (header.getName().equalsIgnoreCase(name)) {
+                headersFound.add(header);
+            }
+        }
+        
+        return (Header[]) headersFound.toArray(new Header[headersFound.size()]);
+    }
+    
+    /**
+     * Gets the first header with the given name.
+     * 
+     * <p>Header name comparison is case insensitive.
+     * 
+     * @param name the name of the header to get
+     * @return the first header or <code>null</code>
+     */
+    public Header getFirstHeader(String name) {
+        for (int i = 0; i < headers.size(); i++) {
+            Header header = (Header) headers.get(i);
+            if (header.getName().equalsIgnoreCase(name)) {
+                return header;
+            }
+        }
+        return null;                
+    }
+    
+    /**
+     * Gets the last header with the given name.
+     *
+     * <p>Header name comparison is case insensitive.
+     *
+     * @param name the name of the header to get
+     * @return the last header or <code>null</code>
+     */
+    public Header getLastHeader(String name) {
+        // start at the end of the list and work backwards
+        for (int i = headers.size() - 1; i >= 0; i--) {
+            Header header = (Header) headers.get(i);
+            if (header.getName().equalsIgnoreCase(name)) {
+                return header;
+            }            
+        }
+        
+        return null;        
+    }
+    
+    /**
+     * Gets all of the headers contained within this group.
+     * 
+     * @return an array of length >= 0
+     */
+    public Header[] getAllHeaders() {
+        return (Header[]) headers.toArray(new Header[headers.size()]);
+    }
+    
+    /**
+     * Tests if headers with the given name are contained within this group.
+     * 
+     * <p>Header name comparison is case insensitive.
+     * 
+     * @param name the header name to test for
+     * @return <code>true</code> if at least one header with the name is
+     * contained, <code>false</code> otherwise
+     */
+    public boolean containsHeader(String name) {
+        for (int i = 0; i < headers.size(); i++) {
+            Header header = (Header) headers.get(i);
+            if (header.getName().equalsIgnoreCase(name)) {
+                return true;
+            }
+        }
+        
+        return false;
+    }
+
+    /**
+     * Returns an iterator over this group of headers.
+     * 
+     * @return iterator over this group of headers.
+     * 
+     * @since 4.0
+     */
+    public HeaderIterator iterator() {
+        return new BasicListHeaderIterator(this.headers, null); 
+    }
+
+    /**
+     * Returns an iterator over the headers with a given name in this group.
+     *
+     * @param name      the name of the headers over which to iterate, or
+     *                  <code>null</code> for all headers
+     *
+     * @return iterator over some headers in this group.
+     * 
+     * @since 4.0
+     */
+    public HeaderIterator iterator(final String name) {
+        return new BasicListHeaderIterator(this.headers, name);
+    }
+    
+    /**
+     * Returns a copy of this object
+     * 
+     * @return copy of this object
+     */
+    public HeaderGroup copy() {
+        HeaderGroup clone = new HeaderGroup();
+        clone.headers.addAll(this.headers);
+        return clone;
+    }
+    
+    public Object clone() throws CloneNotSupportedException {
+        HeaderGroup clone = (HeaderGroup) super.clone();
+        clone.headers = new ArrayList(this.headers);
+        return clone;
+    }
+    
+}
diff --git a/src/org/apache/http/message/HeaderValueFormatter.java b/src/org/apache/http/message/HeaderValueFormatter.java
new file mode 100644
index 0000000..4f6351f
--- /dev/null
+++ b/src/org/apache/http/message/HeaderValueFormatter.java
@@ -0,0 +1,141 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/HeaderValueFormatter.java $
+ * $Revision: 571954 $
+ * $Date: 2007-09-02 04:05:21 -0700 (Sun, 02 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.util.CharArrayBuffer;
+
+
+
+/**
+ * Interface for formatting elements of a header value.
+ * This is the complement to {@link HeaderValueParser}.
+ * Instances of this interface are expected to be stateless and thread-safe.
+ *
+ * <p>
+ * All formatting methods accept an optional buffer argument.
+ * If a buffer is passed in, the formatted element will be appended
+ * and the modified buffer is returned. If no buffer is passed in,
+ * a new buffer will be created and filled with the formatted element.
+ * In both cases, the caller is allowed to modify the returned buffer.
+ * </p>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 571954 $
+ *
+ * @since 4.0
+ */
+public interface HeaderValueFormatter {
+
+    /**
+     * Formats an array of header elements.
+     *
+     * @param buffer    the buffer to append to, or
+     *                  <code>null</code> to create a new buffer
+     * @param elems     the header elements to format
+     * @param quote     <code>true</code> to always format with quoted values,
+     *                  <code>false</code> to use quotes only when necessary
+     *
+     * @return  a buffer with the formatted header elements.
+     *          If the <code>buffer</code> argument was not <code>null</code>,
+     *          that buffer will be used and returned.
+     */
+    CharArrayBuffer formatElements(CharArrayBuffer buffer,
+                                   HeaderElement[] elems,
+                                   boolean quote)
+        ;
+
+
+    /**
+     * Formats one header element.
+     *
+     * @param buffer    the buffer to append to, or
+     *                  <code>null</code> to create a new buffer
+     * @param elem      the header element to format
+     * @param quote     <code>true</code> to always format with quoted values,
+     *                  <code>false</code> to use quotes only when necessary
+     *
+     * @return  a buffer with the formatted header element.
+     *          If the <code>buffer</code> argument was not <code>null</code>,
+     *          that buffer will be used and returned.
+     */
+    CharArrayBuffer formatHeaderElement(CharArrayBuffer buffer,
+                                        HeaderElement elem,
+                                        boolean quote)
+        ;
+
+
+
+    /**
+     * Formats the parameters of a header element.
+     * That's a list of name-value pairs, to be separated by semicolons.
+     * This method will <i>not</i> generate a leading semicolon.
+     *
+     * @param buffer    the buffer to append to, or
+     *                  <code>null</code> to create a new buffer
+     * @param nvps      the parameters (name-value pairs) to format
+     * @param quote     <code>true</code> to always format with quoted values,
+     *                  <code>false</code> to use quotes only when necessary
+     *
+     * @return  a buffer with the formatted parameters.
+     *          If the <code>buffer</code> argument was not <code>null</code>,
+     *          that buffer will be used and returned.
+     */
+    CharArrayBuffer formatParameters(CharArrayBuffer buffer,
+                                     NameValuePair[] nvps,
+                                     boolean quote)
+        ;
+
+
+    /**
+     * Formats one name-value pair, where the value is optional.
+     *
+     * @param buffer    the buffer to append to, or
+     *                  <code>null</code> to create a new buffer
+     * @param nvp       the name-value pair to format
+     * @param quote     <code>true</code> to always format with a quoted value,
+     *                  <code>false</code> to use quotes only when necessary
+     *
+     * @return  a buffer with the formatted name-value pair.
+     *          If the <code>buffer</code> argument was not <code>null</code>,
+     *          that buffer will be used and returned.
+     */
+    CharArrayBuffer formatNameValuePair(CharArrayBuffer buffer,
+                                        NameValuePair nvp,
+                                        boolean quote)
+        ;
+
+}
+
diff --git a/src/org/apache/http/message/HeaderValueParser.java b/src/org/apache/http/message/HeaderValueParser.java
new file mode 100644
index 0000000..74bb93c
--- /dev/null
+++ b/src/org/apache/http/message/HeaderValueParser.java
@@ -0,0 +1,214 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/HeaderValueParser.java $
+ * $Revision: 589325 $
+ * $Date: 2007-10-28 03:37:56 -0700 (Sun, 28 Oct 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.ParseException;
+import org.apache.http.util.CharArrayBuffer;
+
+
+
+/**
+ * Interface for parsing header values into elements.
+ * Instances of this interface are expected to be stateless and thread-safe.
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 589325 $ $Date: 2007-10-28 03:37:56 -0700 (Sun, 28 Oct 2007) $
+ *
+ * @since 4.0
+ */
+public interface HeaderValueParser {
+
+    /**
+     * Parses a header value into elements.
+     * Parse errors are indicated as <code>RuntimeException</code>.
+     * <p>
+     * Some HTTP headers (such as the set-cookie header) have values that
+     * can be decomposed into multiple elements. In order to be processed
+     * by this parser, such headers must be in the following form:
+     * </p>
+     * <pre>
+     * header  = [ element ] *( "," [ element ] )
+     * element = name [ "=" [ value ] ] *( ";" [ param ] )
+     * param   = name [ "=" [ value ] ]
+     *
+     * name    = token
+     * value   = ( token | quoted-string )
+     *
+     * token         = 1*&lt;any char except "=", ",", ";", &lt;"&gt; and
+     *                       white space&gt;
+     * quoted-string = &lt;"&gt; *( text | quoted-char ) &lt;"&gt;
+     * text          = any char except &lt;"&gt;
+     * quoted-char   = "\" char
+     * </pre>
+     * <p>
+     * Any amount of white space is allowed between any part of the
+     * header, element or param and is ignored. A missing value in any
+     * element or param will be stored as the empty {@link String};
+     * if the "=" is also missing <var>null</var> will be stored instead.
+     * </p>
+     *
+     * @param buffer    buffer holding the header value to parse
+     * @param cursor    the parser cursor containing the current position and 
+     *                  the bounds within the buffer for the parsing operation
+     *
+     * @return  an array holding all elements of the header value
+     *
+     * @throws ParseException        in case of a parse error
+     */
+    HeaderElement[] parseElements(
+            CharArrayBuffer buffer,
+            ParserCursor cursor) throws ParseException;
+
+    /**
+     * Parses a single header element.
+     * A header element consist of a semicolon-separate list
+     * of name=value definitions.
+     *
+     * @param buffer    buffer holding the element to parse
+     * @param cursor    the parser cursor containing the current position and 
+     *                  the bounds within the buffer for the parsing operation
+     *
+     * @return  the parsed element
+     *
+     * @throws ParseException        in case of a parse error
+     */
+    HeaderElement parseHeaderElement(
+            CharArrayBuffer buffer,
+            ParserCursor cursor) throws ParseException;
+
+    /**
+     * Parses a list of name-value pairs.
+     * These lists are used to specify parameters to a header element.
+     * Parse errors are indicated as <code>RuntimeException</code>.
+     * <p>
+     * This method comforms to the generic grammar and formatting rules
+     * outlined in the 
+     * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2"
+     *   >Section 2.2</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.2 Basic Rules</h>
+     * <p>
+     * The following rules are used throughout this specification to
+     * describe basic parsing constructs. 
+     * The US-ASCII coded character set is defined by ANSI X3.4-1986.
+     * </p>
+     * <pre>
+     *     OCTET          = <any 8-bit sequence of data>
+     *     CHAR           = <any US-ASCII character (octets 0 - 127)>
+     *     UPALPHA        = <any US-ASCII uppercase letter "A".."Z">
+     *     LOALPHA        = <any US-ASCII lowercase letter "a".."z">
+     *     ALPHA          = UPALPHA | LOALPHA
+     *     DIGIT          = <any US-ASCII digit "0".."9">
+     *     CTL            = <any US-ASCII control character
+     *                      (octets 0 - 31) and DEL (127)>
+     *     CR             = <US-ASCII CR, carriage return (13)>
+     *     LF             = <US-ASCII LF, linefeed (10)>
+     *     SP             = <US-ASCII SP, space (32)>
+     *     HT             = <US-ASCII HT, horizontal-tab (9)>
+     *     <">            = <US-ASCII double-quote mark (34)>
+     * </pre>
+     * <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> 
+     *
+     * @param buffer    buffer holding the name-value list to parse
+     * @param cursor    the parser cursor containing the current position and 
+     *                  the bounds within the buffer for the parsing operation
+     *
+     * @return  an array holding all items of the name-value list
+     *
+     * @throws ParseException        in case of a parse error
+     */
+    NameValuePair[] parseParameters(
+            CharArrayBuffer buffer,
+            ParserCursor cursor) throws ParseException;
+
+
+    /**
+     * Parses a name=value specification, where the = and value are optional.
+     *
+     * @param buffer    the buffer holding the name-value pair to parse
+     * @param cursor    the parser cursor containing the current position and 
+     *                  the bounds within the buffer for the parsing operation
+     *
+     * @return  the name-value pair, where the value is <code>null</code>
+     *          if no value is specified
+     */
+    NameValuePair parseNameValuePair(
+            CharArrayBuffer buffer,
+            ParserCursor cursor) throws ParseException;
+    
+}
+
diff --git a/src/org/apache/http/message/LineFormatter.java b/src/org/apache/http/message/LineFormatter.java
new file mode 100644
index 0000000..ccc603c
--- /dev/null
+++ b/src/org/apache/http/message/LineFormatter.java
@@ -0,0 +1,153 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/LineFormatter.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+
+import org.apache.http.ProtocolVersion;
+import org.apache.http.RequestLine;
+import org.apache.http.StatusLine;
+import org.apache.http.Header;
+import org.apache.http.util.CharArrayBuffer;
+
+
+/**
+ * Interface for formatting elements of the HEAD section of an HTTP message.
+ * This is the complement to {@link LineParser}.
+ * There are individual methods for formatting a request line, a
+ * status line, or a header line. The formatting does <i>not</i> include the
+ * trailing line break sequence CR-LF.
+ * Instances of this interface are expected to be stateless and thread-safe.
+ *
+ * <p>
+ * The formatted lines are returned in memory, the formatter does not depend
+ * on any specific IO mechanism.
+ * In order to avoid unnecessary creation of temporary objects,
+ * a buffer can be passed as argument to all formatting methods.
+ * The implementation may or may not actually use that buffer for formatting.
+ * If it is used, the buffer will first be cleared by the
+ * <code>formatXXX</code> methods.
+ * The argument buffer can always be re-used after the call. The buffer
+ * returned as the result, if it is different from the argument buffer,
+ * MUST NOT be modified.
+ * </p>
+ *
+ *
+ * @author <a href="mailto:rolandw AT apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 573864 $ $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * @since 4.0
+ */
+public interface LineFormatter {
+
+
+
+    /**
+     * Formats a protocol version.
+     * This method does <i>not</i> follow the general contract for
+     * <code>buffer</code> arguments.
+     * It does <i>not</i> clear the argument buffer, but appends instead.
+     * The returned buffer can always be modified by the caller.
+     * Because of these differing conventions, it is not named
+     * <code>formatProtocolVersion</code>.
+     *
+     * @param buffer    a buffer to which to append, or <code>null</code>
+     * @param version   the protocol version to format
+     *
+     * @return  a buffer with the formatted protocol version appended.
+     *          The caller is allowed to modify the result buffer.
+     *          If the <code>buffer</code> argument is not <code>null</code>,
+     *          the returned buffer is the argument buffer.
+     */
+    CharArrayBuffer appendProtocolVersion(CharArrayBuffer buffer,
+                                          ProtocolVersion version)
+        ;
+
+
+    /**
+     * Formats a request line.
+     *
+     * @param buffer    a buffer available for formatting, or
+     *                  <code>null</code>.
+     *                  The buffer will be cleared before use.
+     * @param reqline   the request line to format
+     *
+     * @return  the formatted request line
+     */
+    CharArrayBuffer formatRequestLine(CharArrayBuffer buffer,
+                                      RequestLine reqline) 
+        ;
+
+
+    /**
+     * Formats a status line.
+     *
+     * @param buffer    a buffer available for formatting, or
+     *                  <code>null</code>.
+     *                  The buffer will be cleared before use.
+     * @param statline  the status line to format
+     *
+     * @return  the formatted status line
+     *
+     * @throws ParseException        in case of a parse error
+     */
+    CharArrayBuffer formatStatusLine(CharArrayBuffer buffer,
+                                     StatusLine statline) 
+        ;
+
+
+    /**
+     * Formats a header.
+     * Due to header continuation, the result may be multiple lines.
+     * In order to generate well-formed HTTP, the lines in the result
+     * must be separated by the HTTP line break sequence CR-LF.
+     * There is <i>no</i> trailing CR-LF in the result.
+     * <br/>
+     * See the class comment for details about the buffer argument.
+     *
+     * @param buffer    a buffer available for formatting, or
+     *                  <code>null</code>.
+     *                  The buffer will be cleared before use.
+     * @param header    the header to format
+     *
+     * @return  a buffer holding the formatted header, never <code>null</code>.
+     *          The returned buffer may be different from the argument buffer.
+     *
+     * @throws ParseException        in case of a parse error
+     */
+    CharArrayBuffer formatHeader(CharArrayBuffer buffer,
+                                 Header header)
+        ;
+
+}
diff --git a/src/org/apache/http/message/LineParser.java b/src/org/apache/http/message/LineParser.java
new file mode 100644
index 0000000..d1bcd15
--- /dev/null
+++ b/src/org/apache/http/message/LineParser.java
@@ -0,0 +1,156 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/LineParser.java $
+ * $Revision: 589374 $
+ * $Date: 2007-10-28 09:25:07 -0700 (Sun, 28 Oct 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+
+import org.apache.http.ProtocolVersion;
+import org.apache.http.ParseException;
+import org.apache.http.RequestLine;
+import org.apache.http.StatusLine;
+import org.apache.http.Header;
+import org.apache.http.util.CharArrayBuffer;
+
+
+/**
+ * Interface for parsing lines in the HEAD section of an HTTP message.
+ * There are individual methods for parsing a request line, a
+ * status line, or a header line.
+ * The lines to parse are passed in memory, the parser does not depend
+ * on any specific IO mechanism.
+ * Instances of this interface are expected to be stateless and thread-safe.
+ *
+ * @author <a href="mailto:rolandw AT apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 589374 $ $Date: 2007-10-28 09:25:07 -0700 (Sun, 28 Oct 2007) $
+ *
+ * @since 4.0
+ */
+public interface LineParser {
+
+
+    /**
+     * Parses the textual representation of a protocol version.
+     * This is needed for parsing request lines (last element)
+     * as well as status lines (first element).
+     *
+     * @param buffer    a buffer holding the protocol version to parse
+     * @param cursor    the parser cursor containing the current position and 
+     *                  the bounds within the buffer for the parsing operation
+     * 
+     * @return  the parsed protocol version
+     *
+     * @throws ParseException        in case of a parse error
+     */
+    ProtocolVersion parseProtocolVersion(
+            CharArrayBuffer buffer,
+            ParserCursor cursor) throws ParseException;
+
+
+    /**
+     * Checks whether there likely is a protocol version in a line.
+     * This method implements a <i>heuristic</i> to check for a
+     * likely protocol version specification. It does <i>not</i>
+     * guarantee that {@link #parseProtocolVersion} would not
+     * detect a parse error.
+     * This can be used to detect garbage lines before a request
+     * or status line.
+     *
+     * @param buffer    a buffer holding the line to inspect
+     * @param cursor    the cursor at which to check for a protocol version, or
+     *                  negative for "end of line". Whether the check tolerates
+     *                  whitespace before or after the protocol version is
+     *                  implementation dependent.
+     *
+     * @return  <code>true</code> if there is a protocol version at the
+     *          argument index (possibly ignoring whitespace),
+     *          <code>false</code> otherwise
+     */
+    boolean hasProtocolVersion(
+            CharArrayBuffer buffer, 
+            ParserCursor cursor);
+
+
+    /**
+     * Parses a request line.
+     *
+     * @param buffer    a buffer holding the line to parse
+     * @param cursor    the parser cursor containing the current position and 
+     *                  the bounds within the buffer for the parsing operation
+     *
+     * @return  the parsed request line
+     *
+     * @throws ParseException        in case of a parse error
+     */
+    RequestLine parseRequestLine(
+            CharArrayBuffer buffer,
+            ParserCursor cursor) throws ParseException;
+
+
+    /**
+     * Parses a status line.
+     *
+     * @param buffer    a buffer holding the line to parse
+     * @param cursor    the parser cursor containing the current position and 
+     *                  the bounds within the buffer for the parsing operation
+     *
+     * @return  the parsed status line
+     *
+     * @throws ParseException        in case of a parse error
+     */
+    StatusLine parseStatusLine(
+            CharArrayBuffer buffer,
+            ParserCursor cursor) throws ParseException;
+
+
+    /**
+     * Creates a header from a line.
+     * The full header line is expected here. Header continuation lines
+     * must be joined by the caller before invoking this method.
+     *
+     * @param buffer    a buffer holding the full header line.
+     *                  This buffer MUST NOT be re-used afterwards, since
+     *                  the returned object may reference the contents later.
+     *
+     * @return  the header in the argument buffer.
+     *          The returned object MAY be a wrapper for the argument buffer.
+     *          The argument buffer MUST NOT be re-used or changed afterwards.
+     *
+     * @throws ParseException        in case of a parse error
+     */
+    Header parseHeader(CharArrayBuffer buffer)
+        throws ParseException
+        ;
+
+
+}
diff --git a/src/org/apache/http/message/ParserCursor.java b/src/org/apache/http/message/ParserCursor.java
new file mode 100644
index 0000000..d030675
--- /dev/null
+++ b/src/org/apache/http/message/ParserCursor.java
@@ -0,0 +1,102 @@
+/*
+ * $HeadURL:https://svn.apache.org/repos/asf/jakarta/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/ParserCursor.java $
+ * $Revision:589325 $
+ * $Date:2007-10-28 11:37:56 +0100 (Sun, 28 Oct 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * This class represents a context of a parsing operation: 
+ * <ul>
+ *  <li>the current position the parsing operation is expected to start at</li>
+ *  <li>the bounds limiting the scope of the parsing operation</li>
+ * </ul>
+ * 
+ * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a>
+ */
+public class ParserCursor {
+
+    private final int lowerBound;
+    private final int upperBound;
+    private int pos;
+    
+    public ParserCursor(int lowerBound, int upperBound) {
+        super();
+        if (lowerBound < 0) {
+            throw new IndexOutOfBoundsException("Lower bound cannot be negative");
+        }
+        if (lowerBound > upperBound) {
+            throw new IndexOutOfBoundsException("Lower bound cannot be greater then upper bound");
+        }
+        this.lowerBound = lowerBound;
+        this.upperBound = upperBound;
+        this.pos = lowerBound;
+    }
+
+    public int getLowerBound() {
+        return this.lowerBound;
+    }
+
+    public int getUpperBound() {
+        return this.upperBound;
+    }
+
+    public int getPos() {
+        return this.pos;
+    }
+
+    public void updatePos(int pos) {
+        if (pos < this.lowerBound) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (pos > this.upperBound) {
+            throw new IndexOutOfBoundsException();
+        }
+        this.pos = pos;
+    }
+    
+    public boolean atEnd() {
+        return this.pos >= this.upperBound;
+    }
+
+    public String toString() {
+        CharArrayBuffer buffer = new CharArrayBuffer(16);
+        buffer.append('[');
+        buffer.append(Integer.toString(this.lowerBound));
+        buffer.append('>');
+        buffer.append(Integer.toString(this.pos));
+        buffer.append('>');
+        buffer.append(Integer.toString(this.upperBound));
+        buffer.append(']');
+        return buffer.toString();
+    }
+    
+}
diff --git a/src/org/apache/http/message/package.html b/src/org/apache/http/message/package.html
new file mode 100644
index 0000000..88ee617
--- /dev/null
+++ b/src/org/apache/http/message/package.html
@@ -0,0 +1,49 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/package.html $
+ * $Revision: 539755 $
+ * $Date: 2007-05-19 07:05:02 -0700 (Sat, 19 May 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+A selection of HTTP
+{@link org.apache.http.message.AbstractHttpMessage message}
+implementations.
+
+There are basic implementations for HTTP requests
+{@link org.apache.http.message.BasicHttpEntityEnclosingRequest with}
+and {@link org.apache.http.message.BasicHttpRequest without}
+an entity, and for
+{@link org.apache.http.message.BasicHttpResponse responses}.
+
+
+</body>
+</html>
diff --git a/src/org/apache/http/package.html b/src/org/apache/http/package.html
new file mode 100644
index 0000000..e9647b4
--- /dev/null
+++ b/src/org/apache/http/package.html
@@ -0,0 +1,51 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/package.html $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+The core interfaces and classes of the HTTP components.
+
+These deal with the fundamental things required for using the
+HTTP protocol, such as representing a
+{@link org.apache.http.HttpMessage message} including it's
+{@link org.apache.http.Header headers} and optional
+{@link org.apache.http.HttpEntity entity}, and
+{@link org.apache.http.HttpConnection connections}
+over which messages are sent. In order to prepare messages
+before sending or after receiving, there are interceptors for
+{@link org.apache.http.HttpRequestInterceptor requests} and
+{@link org.apache.http.HttpResponseInterceptor responses}.
+
+</body>
+</html>
diff --git a/src/org/apache/http/params/AbstractHttpParams.java b/src/org/apache/http/params/AbstractHttpParams.java
new file mode 100644
index 0000000..91631fc
--- /dev/null
+++ b/src/org/apache/http/params/AbstractHttpParams.java
@@ -0,0 +1,116 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/AbstractHttpParams.java $
+ * $Revision: 542224 $
+ * $Date: 2007-05-28 06:34:04 -0700 (Mon, 28 May 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+import org.apache.http.params.HttpParams;
+
+
+/**
+ * Abstract base class for parameter collections.
+ * Type specific setters and getters are mapped to the abstract,
+ * generic getters and setters.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * 
+ * @version $Revision: 542224 $
+ */
+public abstract class AbstractHttpParams implements HttpParams {
+
+    /**
+     * Instantiates parameters.
+     */
+    protected AbstractHttpParams() {
+        super();
+    }
+
+    public long getLongParameter(final String name, long defaultValue) { 
+        Object param = getParameter(name);
+        if (param == null) {
+            return defaultValue;
+        }
+        return ((Long)param).longValue();
+    }
+    
+    public HttpParams setLongParameter(final String name, long value) {
+        setParameter(name, new Long(value));
+        return this;
+    }
+
+    public int getIntParameter(final String name, int defaultValue) { 
+        Object param = getParameter(name);
+        if (param == null) {
+            return defaultValue;
+        }
+        return ((Integer)param).intValue();
+    }
+    
+    public HttpParams setIntParameter(final String name, int value) {
+        setParameter(name, new Integer(value));
+        return this;
+    }
+
+    public double getDoubleParameter(final String name, double defaultValue) { 
+        Object param = getParameter(name);
+        if (param == null) {
+            return defaultValue;
+        }
+        return ((Double)param).doubleValue();
+    }
+    
+    public HttpParams setDoubleParameter(final String name, double value) {
+        setParameter(name, new Double(value));
+        return this;
+    }
+
+    public boolean getBooleanParameter(final String name, boolean defaultValue) { 
+        Object param = getParameter(name);
+        if (param == null) {
+            return defaultValue;
+        }
+        return ((Boolean)param).booleanValue();
+    }
+    
+    public HttpParams setBooleanParameter(final String name, boolean value) {
+        setParameter(name, value ? Boolean.TRUE : Boolean.FALSE);
+        return this;
+    }
+        
+    public boolean isParameterTrue(final String name) {
+        return getBooleanParameter(name, false);
+    }
+        
+    public boolean isParameterFalse(final String name) {
+        return !getBooleanParameter(name, false);
+    }
+
+} // class AbstractHttpParams
diff --git a/src/org/apache/http/params/BasicHttpParams.java b/src/org/apache/http/params/BasicHttpParams.java
new file mode 100644
index 0000000..70e6605
--- /dev/null
+++ b/src/org/apache/http/params/BasicHttpParams.java
@@ -0,0 +1,160 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/BasicHttpParams.java $
+ * $Revision: 610464 $
+ * $Date: 2008-01-09 09:10:55 -0800 (Wed, 09 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ * This class represents a collection of HTTP protocol parameters.
+ * Protocol parameters may be linked together to form a hierarchy.
+ * If a particular parameter value has not been explicitly defined
+ * in the collection itself, its value will be drawn from the parent 
+ * collection of parameters.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Revision: 610464 $
+ */
+public final class BasicHttpParams extends AbstractHttpParams
+    implements Serializable, Cloneable {
+
+    private static final long serialVersionUID = -7086398485908701455L;
+
+    /** Map of HTTP parameters that this collection contains. */
+    private HashMap parameters;
+
+    public BasicHttpParams() {
+        super();
+    }
+
+    public Object getParameter(final String name) {
+        // See if the parameter has been explicitly defined
+        Object param = null;
+        if (this.parameters != null) {
+            param = this.parameters.get(name);
+        }    
+        return param;
+    }
+
+    public HttpParams setParameter(final String name, final Object value) {
+        if (this.parameters == null) {
+            this.parameters = new HashMap();
+        }
+        this.parameters.put(name, value);
+        return this;
+    }
+    
+    public boolean removeParameter(String name) {
+        if (this.parameters == null) {
+            return false;
+        }
+        //this is to avoid the case in which the key has a null value
+        if (this.parameters.containsKey(name)) {
+            this.parameters.remove(name);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    
+    /**
+     * Assigns the value to all the parameter with the given names
+     * 
+     * @param names array of parameter name
+     * @param value parameter value
+     */ 
+    public void setParameters(final String[] names, final Object value) {
+        for (int i = 0; i < names.length; i++) {
+            setParameter(names[i], value);
+        }
+    }
+
+    public boolean isParameterSet(final String name) {
+        return getParameter(name) != null;
+    }
+        
+    public boolean isParameterSetLocally(final String name) {
+        return this.parameters != null && this.parameters.get(name) != null;
+    }
+        
+    /**
+     * Removes all parameters from this collection.
+     */
+    public void clear() {
+        this.parameters = null;
+    }
+
+    /**
+     * Creates a copy of these parameters.
+     * The implementation here instantiates {@link BasicHttpParams}, 
+     * then calls {@link #copyParams(HttpParams)} to populate the copy.
+     *
+     * @return  a new set of params holding a copy of the
+     *          <i>local</i> parameters in this object.
+     */
+    public HttpParams copy() {
+        BasicHttpParams clone = new BasicHttpParams();
+        copyParams(clone);
+        return clone;
+    }
+
+    public Object clone() throws CloneNotSupportedException {
+        BasicHttpParams clone = (BasicHttpParams) super.clone();
+        copyParams(clone);
+        return clone;
+    }
+ 
+    /**
+     * Copies the locally defined parameters to the argument parameters.
+     * This method is called from {@link #copy()}.
+     *
+     * @param target    the parameters to which to copy
+     */
+    protected void copyParams(HttpParams target) {
+        if (this.parameters == null)
+            return;
+
+        Iterator iter = parameters.entrySet().iterator();
+        while (iter.hasNext()) {
+            Map.Entry me = (Map.Entry) iter.next();
+            if (me.getKey() instanceof String)
+                target.setParameter((String)me.getKey(), me.getValue());
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/params/CoreConnectionPNames.java b/src/org/apache/http/params/CoreConnectionPNames.java
new file mode 100644
index 0000000..a2dec8b
--- /dev/null
+++ b/src/org/apache/http/params/CoreConnectionPNames.java
@@ -0,0 +1,131 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/CoreConnectionPNames.java $
+ * $Revision: 576077 $
+ * $Date: 2007-09-16 04:50:22 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+
+/**
+ * Defines parameter names for connections in HttpCore.
+ * 
+ * @version $Revision: 576077 $
+ * 
+ * @since 4.0
+ */
+public interface CoreConnectionPNames {
+
+    /**
+     * Defines the default socket timeout (<tt>SO_TIMEOUT</tt>) in milliseconds which is the 
+     * timeout for waiting for data. A timeout value of zero is interpreted as an infinite 
+     * timeout. This value is used when no socket timeout is set in the 
+     * method parameters. 
+     * <p>
+     * This parameter expects a value of type {@link Integer}.
+     * </p>
+     * @see java.net.SocketOptions#SO_TIMEOUT
+     */
+    public static final String SO_TIMEOUT = "http.socket.timeout"; 
+
+    /**
+     * Determines whether Nagle's algorithm is to be used. The Nagle's algorithm 
+     * tries to conserve bandwidth by minimizing the number of segments that are 
+     * sent. When applications wish to decrease network latency and increase 
+     * performance, they can disable Nagle's algorithm (that is enable TCP_NODELAY). 
+     * Data will be sent earlier, at the cost of an increase in bandwidth consumption. 
+     * <p>
+     * This parameter expects a value of type {@link Boolean}.
+     * </p>
+     * @see java.net.SocketOptions#TCP_NODELAY
+     */
+    public static final String TCP_NODELAY = "http.tcp.nodelay"; 
+
+    /**
+     * Determines the size of the internal socket buffer used to buffer data
+     * while receiving / transmitting HTTP messages.
+     * <p>
+     * This parameter expects a value of type {@link Integer}.
+     * </p>
+     */
+    public static final String SOCKET_BUFFER_SIZE = "http.socket.buffer-size"; 
+
+    /**
+     * Sets SO_LINGER with the specified linger time in seconds. The maximum timeout 
+     * value is platform specific. Value <tt>0</tt> implies that the option is disabled.
+     * Value <tt>-1</tt> implies that the JRE default is used. The setting only affects 
+     * socket close.  
+     * <p>
+     * This parameter expects a value of type {@link Integer}.
+     * </p>
+     * @see java.net.SocketOptions#SO_LINGER
+     */
+    public static final String SO_LINGER = "http.socket.linger"; 
+
+    /**
+     * Determines the timeout until a connection is etablished. A value of zero 
+     * means the timeout is not used. The default value is zero.
+     * <p>
+     * This parameter expects a value of type {@link Integer}.
+     * </p>
+     */
+    public static final String CONNECTION_TIMEOUT = "http.connection.timeout"; 
+
+    /**
+     * Determines whether stale connection check is to be used. Disabling 
+     * stale connection check may result in slight performance improvement 
+     * at the risk of getting an I/O error when executing a request over a
+     * connection that has been closed at the server side. 
+     * <p>
+     * This parameter expects a value of type {@link Boolean}.
+     * </p>
+     */
+    public static final String STALE_CONNECTION_CHECK = "http.connection.stalecheck"; 
+
+    /**
+     * Determines the maximum line length limit. If set to a positive value, any HTTP 
+     * line exceeding this limit will cause an IOException. A negative or zero value
+     * will effectively disable the check.
+     * <p>
+     * This parameter expects a value of type {@link Integer}.
+     * </p>
+     */
+    public static final String MAX_LINE_LENGTH = "http.connection.max-line-length";
+    
+    /**
+     * Determines the maximum HTTP header count allowed. If set to a positive value, 
+     * the number of HTTP headers received from the data stream exceeding this limit 
+     * will cause an IOException. A negative or zero value will effectively disable 
+     * the check. 
+     * <p>
+     * This parameter expects a value of type {@link Integer}.
+     * </p>
+     */
+    public static final String MAX_HEADER_COUNT = "http.connection.max-header-count";
+
+}
diff --git a/src/org/apache/http/params/CoreProtocolPNames.java b/src/org/apache/http/params/CoreProtocolPNames.java
new file mode 100644
index 0000000..a42c5de
--- /dev/null
+++ b/src/org/apache/http/params/CoreProtocolPNames.java
@@ -0,0 +1,132 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/CoreProtocolPNames.java $
+ * $Revision: 576077 $
+ * $Date: 2007-09-16 04:50:22 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+
+/**
+ * Defines parameter names for protocol execution in HttpCore.
+ * 
+ * @version $Revision: 576077 $
+ * 
+ * @since 4.0
+ */
+public interface CoreProtocolPNames {
+
+    /**
+     * Defines the {@link org.apache.http.ProtocolVersion protocol version}
+     * used per default.
+     * <p>
+     * This parameter expects a value of type
+     * {@link org.apache.http.ProtocolVersion}.
+     * </p>
+     */
+    public static final String PROTOCOL_VERSION = "http.protocol.version"; 
+
+    /**
+     * Defines the charset to be used for encoding HTTP protocol elements.
+     * <p>
+     * This parameter expects a value of type {@link String}.
+     * </p>
+     */
+    public static final String HTTP_ELEMENT_CHARSET = "http.protocol.element-charset"; 
+    
+    /**
+     * Defines the charset to be used per default for encoding content body.
+     * <p>
+     * This parameter expects a value of type {@link String}.
+     * </p>
+     */
+    public static final String HTTP_CONTENT_CHARSET = "http.protocol.content-charset"; 
+    
+    /**
+     * Defines the content of the <tt>User-Agent</tt> header.
+     * <p>
+     * This parameter expects a value of type {@link String}.
+     * </p>
+     */
+    public static final String USER_AGENT = "http.useragent"; 
+
+    /**
+     * Defines the content of the <tt>Server</tt> header.
+     * <p>
+     * This parameter expects a value of type {@link String}.
+     * </p>
+     */
+    public static final String ORIGIN_SERVER = "http.origin-server"; 
+
+    /**
+     * Defines whether responses with an invalid <tt>Transfer-Encoding</tt> header should be 
+     * rejected.
+     * <p>
+     * This parameter expects a value of type {@link Boolean}.
+     * </p>
+     */
+    public static final String STRICT_TRANSFER_ENCODING = "http.protocol.strict-transfer-encoding"; 
+
+    /**
+     * <p>
+     * Activates 'Expect: 100-Continue' handshake for the 
+     * entity enclosing methods. The purpose of the 'Expect: 100-Continue'
+     * handshake to allow a client that is sending a request message with 
+     * a request body to determine if the origin server is willing to 
+     * accept the request (based on the request headers) before the client
+     * sends the request body.
+     * </p>
+     * 
+     * <p>
+     * The use of the 'Expect: 100-continue' handshake can result in 
+     * noticable peformance improvement for entity enclosing requests
+     * (such as POST and PUT) that require the target server's 
+     * authentication.
+     * </p>
+     * 
+     * <p>
+     * 'Expect: 100-continue' handshake should be used with 
+     * caution, as it may cause problems with HTTP servers and 
+     * proxies that do not support HTTP/1.1 protocol.
+     * </p>
+     * 
+     * This parameter expects a value of type {@link Boolean}.
+     */
+    public static final String USE_EXPECT_CONTINUE = "http.protocol.expect-continue"; 
+
+    /**
+     * <p>
+     * Defines the maximum period of time in milliseconds the client should spend
+     * waiting for a 100-continue response.
+     * </p>
+     * 
+     * This parameter expects a value of type {@link Integer}.
+     */
+    public static final String WAIT_FOR_CONTINUE = "http.protocol.wait-for-continue";
+    
+}
diff --git a/src/org/apache/http/params/DefaultedHttpParams.java b/src/org/apache/http/params/DefaultedHttpParams.java
new file mode 100644
index 0000000..ce33247
--- /dev/null
+++ b/src/org/apache/http/params/DefaultedHttpParams.java
@@ -0,0 +1,101 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/DefaultedHttpParams.java $
+ * $Revision: 610763 $
+ * $Date: 2008-01-10 04:01:13 -0800 (Thu, 10 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ * {@link HttpParams} implementation that delegates resolution of a parameter
+ * to the given default {@link HttpParams} instance if the parameter is not 
+ * present in the local one. The state of the local collection can be mutated,
+ * whereas the default collection is treated as read-only.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Revision: 610763 $
+ */
+public final class DefaultedHttpParams extends AbstractHttpParams {
+
+    private final HttpParams local;
+    private final HttpParams defaults;
+    
+    public DefaultedHttpParams(final HttpParams local, final HttpParams defaults) {
+        super();
+        if (local == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        this.local = local;
+        this.defaults = defaults;
+    }
+
+    /**
+     * Creates a copy of the local collection with the same default
+     */
+    public HttpParams copy() {
+        HttpParams clone = this.local.copy();
+        return new DefaultedHttpParams(clone, this.defaults);
+    }
+
+    /**
+     * Retrieves the value of the parameter from the local collection and, if the 
+     * parameter is not set locally, delegates its resolution to the default 
+     * collection.
+     */
+    public Object getParameter(final String name) {
+        Object obj = this.local.getParameter(name);
+        if (obj == null && this.defaults != null) {
+            obj = this.defaults.getParameter(name);
+        }
+        return obj;
+    }
+
+    /**
+     * Attempts to remove the parameter from the local collection. This method 
+     * <i>does not</i> modify the default collection.
+     */
+    public boolean removeParameter(final String name) {
+        return this.local.removeParameter(name);
+    }
+
+    /**
+     * Sets the parameter in the local collection. This method <i>does not</i> 
+     * modify the default collection.
+     */
+    public HttpParams setParameter(final String name, final Object value) {
+        return this.local.setParameter(name, value);
+    }
+
+    public HttpParams getDefaults() {
+        return this.defaults;
+    }
+    
+}
diff --git a/src/org/apache/http/params/HttpAbstractParamBean.java b/src/org/apache/http/params/HttpAbstractParamBean.java
new file mode 100644
index 0000000..8701a99
--- /dev/null
+++ b/src/org/apache/http/params/HttpAbstractParamBean.java
@@ -0,0 +1,44 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpAbstractParamBean.java $
+ * $Revision: 593937 $
+ * $Date: 2007-11-11 10:44:12 -0800 (Sun, 11 Nov 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+public abstract class HttpAbstractParamBean {
+    
+    protected final HttpParams params;
+
+    public HttpAbstractParamBean (final HttpParams params) {
+        if (params == null)
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        this.params = params;
+    }
+    
+}
diff --git a/src/org/apache/http/params/HttpConnectionParamBean.java b/src/org/apache/http/params/HttpConnectionParamBean.java
new file mode 100644
index 0000000..0b61346
--- /dev/null
+++ b/src/org/apache/http/params/HttpConnectionParamBean.java
@@ -0,0 +1,64 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpConnectionParamBean.java $
+ * $Revision: 593937 $
+ * $Date: 2007-11-11 10:44:12 -0800 (Sun, 11 Nov 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+public class HttpConnectionParamBean extends HttpAbstractParamBean {
+    
+    public HttpConnectionParamBean (final HttpParams params) {
+        super(params);
+    }
+
+    public void setSoTimeout (int soTimeout) {
+        HttpConnectionParams.setSoTimeout(params, soTimeout);
+    }
+
+    public void setTcpNoDelay (boolean tcpNoDelay) {
+        HttpConnectionParams.setTcpNoDelay(params, tcpNoDelay);
+    }
+
+    public void setSocketBufferSize (int socketBufferSize) {
+        HttpConnectionParams.setSocketBufferSize(params, socketBufferSize);
+    }
+
+    public void setLinger (int linger) {
+        HttpConnectionParams.setLinger(params, linger);
+    }
+
+    public void setConnectionTimeout (int connectionTimeout) {
+        HttpConnectionParams.setConnectionTimeout(params, connectionTimeout);
+    }
+
+    public void setStaleCheckingEnabled (boolean staleCheckingEnabled) {
+        HttpConnectionParams.setStaleCheckingEnabled(params, staleCheckingEnabled);
+    }
+    
+}
diff --git a/src/org/apache/http/params/HttpConnectionParams.java b/src/org/apache/http/params/HttpConnectionParams.java
new file mode 100644
index 0000000..7918a66
--- /dev/null
+++ b/src/org/apache/http/params/HttpConnectionParams.java
@@ -0,0 +1,224 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpConnectionParams.java $
+ * $Revision: 576089 $
+ * $Date: 2007-09-16 05:39:56 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+/**
+ * An adaptor for accessing connection parameters in {@link HttpParams}.
+ * <br/>
+ * Note that the <i>implements</i> relation to {@link CoreConnectionPNames}
+ * is for compatibility with existing application code only. References to
+ * the parameter names should use the interface, not this class.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Revision: 576089 $
+ * 
+ * @since 4.0
+ */
+public final class HttpConnectionParams implements CoreConnectionPNames {
+
+    /**
+     */
+    private HttpConnectionParams() {
+        super();
+    }
+
+    /**
+     * Returns the default socket timeout (<tt>SO_TIMEOUT</tt>) in milliseconds which is the 
+     * timeout for waiting for data. A timeout value of zero is interpreted as an infinite 
+     * timeout. This value is used when no socket timeout is set in the 
+     * method parameters. 
+     *
+     * @return timeout in milliseconds
+     */
+    public static int getSoTimeout(final HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        return params.getIntParameter(CoreConnectionPNames.SO_TIMEOUT, 0);
+    }
+
+    /**
+     * Sets the default socket timeout (<tt>SO_TIMEOUT</tt>) in milliseconds which is the 
+     * timeout for waiting for data. A timeout value of zero is interpreted as an infinite 
+     * timeout. This value is used when no socket timeout is set in the 
+     * method parameters. 
+     *
+     * @param timeout Timeout in milliseconds
+     */
+    public static void setSoTimeout(final HttpParams params, int timeout) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, timeout);
+        
+    }
+
+    /**
+     * Tests if Nagle's algorithm is to be used.  
+     *
+     * @return <tt>true</tt> if the Nagle's algorithm is to NOT be used
+     *   (that is enable TCP_NODELAY), <tt>false</tt> otherwise.
+     */
+    public static boolean getTcpNoDelay(final HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        return params.getBooleanParameter
+            (CoreConnectionPNames.TCP_NODELAY, true);
+    }
+
+    /**
+     * Determines whether Nagle's algorithm is to be used. The Nagle's algorithm 
+     * tries to conserve bandwidth by minimizing the number of segments that are 
+     * sent. When applications wish to decrease network latency and increase 
+     * performance, they can disable Nagle's algorithm (that is enable TCP_NODELAY). 
+     * Data will be sent earlier, at the cost of an increase in bandwidth consumption. 
+     *
+     * @param value <tt>true</tt> if the Nagle's algorithm is to NOT be used
+     *   (that is enable TCP_NODELAY), <tt>false</tt> otherwise.
+     */
+    public static void setTcpNoDelay(final HttpParams params, boolean value) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        params.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, value);
+    }
+
+    public static int getSocketBufferSize(final HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        return params.getIntParameter
+            (CoreConnectionPNames.SOCKET_BUFFER_SIZE, -1);
+    }
+    
+    public static void setSocketBufferSize(final HttpParams params, int size) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        params.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, size);
+    }
+
+    /**
+     * Returns linger-on-close timeout. Value <tt>0</tt> implies that the option is 
+     * disabled. Value <tt>-1</tt> implies that the JRE default is used.
+     * 
+     * @return the linger-on-close timeout
+     */
+    public static int getLinger(final HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        return params.getIntParameter(CoreConnectionPNames.SO_LINGER, -1);
+    }
+
+    /**
+     * Returns linger-on-close timeout. This option disables/enables immediate return 
+     * from a close() of a TCP Socket. Enabling this option with a non-zero Integer 
+     * timeout means that a close() will block pending the transmission and 
+     * acknowledgement of all data written to the peer, at which point the socket is 
+     * closed gracefully. Value <tt>0</tt> implies that the option is 
+     * disabled. Value <tt>-1</tt> implies that the JRE default is used.
+     *
+     * @param value the linger-on-close timeout
+     */
+    public static void setLinger(final HttpParams params, int value) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        params.setIntParameter(CoreConnectionPNames.SO_LINGER, value);
+    }
+
+    /**
+     * Returns the timeout until a connection is etablished. A value of zero 
+     * means the timeout is not used. The default value is zero.
+     * 
+     * @return timeout in milliseconds.
+     */
+    public static int getConnectionTimeout(final HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        return params.getIntParameter
+            (CoreConnectionPNames.CONNECTION_TIMEOUT, 0);
+    }
+
+    /**
+     * Sets the timeout until a connection is etablished. A value of zero 
+     * means the timeout is not used. The default value is zero.
+     * 
+     * @param timeout Timeout in milliseconds.
+     */
+    public static void setConnectionTimeout(final HttpParams params, int timeout) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        params.setIntParameter
+            (CoreConnectionPNames.CONNECTION_TIMEOUT, timeout);
+    }
+    
+    /**
+     * Tests whether stale connection check is to be used. Disabling 
+     * stale connection check may result in slight performance improvement 
+     * at the risk of getting an I/O error when executing a request over a
+     * connection that has been closed at the server side. 
+     * 
+     * @return <tt>true</tt> if stale connection check is to be used, 
+     *   <tt>false</tt> otherwise.
+     */
+    public static boolean isStaleCheckingEnabled(final HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        return params.getBooleanParameter
+            (CoreConnectionPNames.STALE_CONNECTION_CHECK, true);
+    }
+
+    /**
+     * Defines whether stale connection check is to be used. Disabling 
+     * stale connection check may result in slight performance improvement 
+     * at the risk of getting an I/O error when executing a request over a
+     * connection that has been closed at the server side. 
+     * 
+     * @param value <tt>true</tt> if stale connection check is to be used, 
+     *   <tt>false</tt> otherwise.
+     */
+    public static void setStaleCheckingEnabled(final HttpParams params, boolean value) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        params.setBooleanParameter
+            (CoreConnectionPNames.STALE_CONNECTION_CHECK, value);
+    }
+    
+}
diff --git a/src/org/apache/http/params/HttpParams.java b/src/org/apache/http/params/HttpParams.java
new file mode 100644
index 0000000..ba901a2
--- /dev/null
+++ b/src/org/apache/http/params/HttpParams.java
@@ -0,0 +1,187 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpParams.java $
+ * $Revision: 610763 $
+ * $Date: 2008-01-10 04:01:13 -0800 (Thu, 10 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+/**
+ * Represents a collection of HTTP protocol and framework parameters.
+ *   
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Revision: 610763 $
+ *
+ * @since 4.0
+ */
+public interface HttpParams {
+
+    /** 
+     * Obtains the value of the given parameter.
+     * 
+     * @param name the parent name.
+     * 
+     * @return  an object that represents the value of the parameter,
+     *          <code>null</code> if the parameter is not set or if it
+     *          is explicitly set to <code>null</code>
+     * 
+     * @see #setParameter(String, Object)
+     */
+    Object getParameter(String name);
+
+    /**
+     * Assigns the value to the parameter with the given name.
+     *
+     * @param name parameter name
+     * @param value parameter value
+     */
+    HttpParams setParameter(String name, Object value);
+
+    /**
+     * Creates a copy of these parameters.
+     *
+     * @return  a new set of parameters holding the same values as this one
+     */
+    HttpParams copy();
+    
+    /**
+     * Removes the parameter with the specified name.
+     * 
+     * @param name parameter name
+     * 
+     * @return true if the parameter existed and has been removed, false else.
+     */
+    boolean removeParameter(String name);
+
+    /** 
+     * Returns a {@link Long} parameter value with the given name. 
+     * If the parameter is not explicitly set, the default value is returned.  
+     * 
+     * @param name the parent name.
+     * @param defaultValue the default value.
+     * 
+     * @return a {@link Long} that represents the value of the parameter.
+     * 
+     * @see #setLongParameter(String, long)
+     */
+    long getLongParameter(String name, long defaultValue);
+
+    /**
+     * Assigns a {@link Long} to the parameter with the given name
+     * 
+     * @param name parameter name
+     * @param value parameter value
+     */ 
+    HttpParams setLongParameter(String name, long value);
+
+    /** 
+     * Returns an {@link Integer} parameter value with the given name. 
+     * If the parameter is not explicitly set, the default value is returned.  
+     * 
+     * @param name the parent name.
+     * @param defaultValue the default value.
+     * 
+     * @return a {@link Integer} that represents the value of the parameter.
+     * 
+     * @see #setIntParameter(String, int)
+     */
+    int getIntParameter(String name, int defaultValue);
+
+    /**
+     * Assigns an {@link Integer} to the parameter with the given name
+     * 
+     * @param name parameter name
+     * @param value parameter value
+     */ 
+    HttpParams setIntParameter(String name, int value);
+
+    /** 
+     * Returns a {@link Double} parameter value with the given name. 
+     * If the parameter is not explicitly set, the default value is returned.  
+     *
+     * @param name the parent name.
+     * @param defaultValue the default value.
+     * 
+     * @return a {@link Double} that represents the value of the parameter.
+     * 
+     * @see #setDoubleParameter(String, double)
+     */
+    double getDoubleParameter(String name, double defaultValue);
+
+    /**
+     * Assigns a {@link Double} to the parameter with the given name
+     * 
+     * @param name parameter name
+     * @param value parameter value
+     */ 
+    HttpParams setDoubleParameter(String name, double value);
+
+    /** 
+     * Returns a {@link Boolean} parameter value with the given name. 
+     * If the parameter is not explicitly set, the default value is returned.  
+     * 
+     * @param name the parent name.
+     * @param defaultValue the default value.
+     * 
+     * @return a {@link Boolean} that represents the value of the parameter.
+     * 
+     * @see #setBooleanParameter(String, boolean)
+     */
+    boolean getBooleanParameter(String name, boolean defaultValue);
+
+    /**
+     * Assigns a {@link Boolean} to the parameter with the given name
+     * 
+     * @param name parameter name
+     * @param value parameter value
+     */ 
+    HttpParams setBooleanParameter(String name, boolean value);
+
+    /**
+     * Checks if a boolean parameter is set to <code>true</code>.
+     * 
+     * @param name parameter name
+     * 
+     * @return <tt>true</tt> if the parameter is set to value <tt>true</tt>,
+     *         <tt>false</tt> if it is not set or set to <code>false</code>
+     */
+    boolean isParameterTrue(String name);
+
+    /**
+     * Checks if a boolean parameter is not set or <code>false</code>.
+     * 
+     * @param name parameter name
+     * 
+     * @return <tt>true</tt> if the parameter is either not set or
+     *         set to value <tt>false</tt>,
+     *         <tt>false</tt> if it is set to <code>true</code>
+     */
+    boolean isParameterFalse(String name);
+
+}
diff --git a/src/org/apache/http/params/HttpProtocolParamBean.java b/src/org/apache/http/params/HttpProtocolParamBean.java
new file mode 100644
index 0000000..6273430
--- /dev/null
+++ b/src/org/apache/http/params/HttpProtocolParamBean.java
@@ -0,0 +1,62 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpProtocolParamBean.java $
+ * $Revision: 593937 $
+ * $Date: 2007-11-11 10:44:12 -0800 (Sun, 11 Nov 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+import org.apache.http.HttpVersion;
+
+public class HttpProtocolParamBean extends HttpAbstractParamBean {
+    
+    public HttpProtocolParamBean (final HttpParams params) {
+        super(params);
+    }
+
+    public void setHttpElementCharset (final String httpElementCharset) {
+        HttpProtocolParams.setHttpElementCharset(params, httpElementCharset);
+    }
+
+    public void setContentCharset (final String contentCharset) {
+        HttpProtocolParams.setContentCharset(params, contentCharset);
+    }
+
+    public void setVersion (final HttpVersion version) {
+        HttpProtocolParams.setVersion(params, version);
+    }
+
+    public void setUserAgent (final String userAgent) {
+        HttpProtocolParams.setUserAgent(params, userAgent);
+    }
+
+    public void setUseExpectContinue (boolean useExpectContinue) {
+        HttpProtocolParams.setUseExpectContinue(params, useExpectContinue);
+    }
+    
+}
diff --git a/src/org/apache/http/params/HttpProtocolParams.java b/src/org/apache/http/params/HttpProtocolParams.java
new file mode 100644
index 0000000..71e9c67
--- /dev/null
+++ b/src/org/apache/http/params/HttpProtocolParams.java
@@ -0,0 +1,176 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpProtocolParams.java $
+ * $Revision: 576089 $
+ * $Date: 2007-09-16 05:39:56 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * This class implements an adaptor around the {@link HttpParams} interface
+ * to simplify manipulation of the HTTP protocol specific parameters.
+ * <br/>
+ * Note that the <i>implements</i> relation to {@link CoreProtocolPNames}
+ * is for compatibility with existing application code only. References to
+ * the parameter names should use the interface, not this class.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Revision: 576089 $
+ * 
+ * @since 4.0
+ *
+ * @see CoreProtocolPNames
+ */
+public final class HttpProtocolParams implements CoreProtocolPNames {
+    
+    /**
+     */
+    private HttpProtocolParams() {
+        super();
+    }
+
+    /**
+     * Returns the charset to be used for writing HTTP headers.
+     * @return The charset
+     */
+    public static String getHttpElementCharset(final HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        String charset = (String) params.getParameter
+            (CoreProtocolPNames.HTTP_ELEMENT_CHARSET);
+        if (charset == null) {
+            charset = HTTP.DEFAULT_PROTOCOL_CHARSET;
+        }
+        return charset;
+    }
+    
+    /**
+     * Sets the charset to be used for writing HTTP headers.
+     * @param charset The charset
+     */
+    public static void setHttpElementCharset(final HttpParams params, final String charset) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        params.setParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET, charset);
+    }
+
+    /**
+     * Returns the default charset to be used for writing content body, 
+     * when no charset explicitly specified.
+     * @return The charset
+     */
+    public static String getContentCharset(final HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        String charset = (String) params.getParameter
+            (CoreProtocolPNames.HTTP_CONTENT_CHARSET);
+        if (charset == null) {
+            charset = HTTP.DEFAULT_CONTENT_CHARSET;
+        }
+        return charset;
+    }
+    
+    /**
+     * Sets the default charset to be used for writing content body,
+     * when no charset explicitly specified.
+     * @param charset The charset
+     */
+    public static void setContentCharset(final HttpParams params, final String charset) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        params.setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, charset);
+    }
+
+    /**
+     * Returns {@link ProtocolVersion protocol version} to be used per default. 
+     *
+     * @return {@link ProtocolVersion protocol version}
+     */
+    public static ProtocolVersion getVersion(final HttpParams params) { 
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        Object param = params.getParameter
+            (CoreProtocolPNames.PROTOCOL_VERSION);
+        if (param == null) {
+            return HttpVersion.HTTP_1_1;
+        }
+        return (ProtocolVersion)param;
+    }
+    
+    /**
+     * Assigns the {@link ProtocolVersion protocol version} to be used by the 
+     * HTTP methods that this collection of parameters applies to. 
+     *
+     * @param version the {@link ProtocolVersion protocol version}
+     */
+    public static void setVersion(final HttpParams params, final ProtocolVersion version) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, version);
+    }
+
+    public static String getUserAgent(final HttpParams params) { 
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        return (String) params.getParameter(CoreProtocolPNames.USER_AGENT);
+    }
+    
+    public static void setUserAgent(final HttpParams params, final String useragent) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        params.setParameter(CoreProtocolPNames.USER_AGENT, useragent);
+    }
+
+    public static boolean useExpectContinue(final HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        return params.getBooleanParameter
+            (CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
+    }
+    
+    public static void setUseExpectContinue(final HttpParams params, boolean b) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        params.setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, b);
+    }
+}
diff --git a/src/org/apache/http/params/package.html b/src/org/apache/http/params/package.html
new file mode 100644
index 0000000..3943053
--- /dev/null
+++ b/src/org/apache/http/params/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/package.html $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+The parameterization framework for HTTP components.
+
+</body>
+</html>
diff --git a/src/org/apache/http/protocol/BasicHttpContext.java b/src/org/apache/http/protocol/BasicHttpContext.java
new file mode 100644
index 0000000..9b4e2b3
--- /dev/null
+++ b/src/org/apache/http/protocol/BasicHttpContext.java
@@ -0,0 +1,95 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/BasicHttpContext.java $
+ * $Revision: 654882 $
+ * $Date: 2008-05-09 09:58:59 -0700 (Fri, 09 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Default implementation of the {@link HttpContext HttpContext}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 654882 $
+ * 
+ * @since 4.0
+ */
+public class BasicHttpContext implements HttpContext {
+    
+    private final HttpContext parentContext;
+    private Map map = null;
+    
+    public BasicHttpContext() {
+        this(null);
+    }
+    
+    public BasicHttpContext(final HttpContext parentContext) {
+        super();
+        this.parentContext = parentContext;
+    }
+    
+    public Object getAttribute(final String id) {
+        if (id == null) {
+            throw new IllegalArgumentException("Id may not be null");
+        }
+        Object obj = null;
+        if (this.map != null) {
+            obj = this.map.get(id);
+        }
+        if (obj == null && this.parentContext != null) {
+            obj = this.parentContext.getAttribute(id);
+        }
+        return obj;
+    }
+
+    public void setAttribute(final String id, final Object obj) {
+        if (id == null) {
+            throw new IllegalArgumentException("Id may not be null");
+        }
+        if (this.map == null) {
+            this.map = new HashMap();
+        }
+        this.map.put(id, obj);
+    }
+    
+    public Object removeAttribute(final String id) {
+        if (id == null) {
+            throw new IllegalArgumentException("Id may not be null");
+        }
+        if (this.map != null) {
+            return this.map.remove(id);
+        } else {
+            return null;
+        }
+    }
+
+}
diff --git a/src/org/apache/http/protocol/BasicHttpProcessor.java b/src/org/apache/http/protocol/BasicHttpProcessor.java
new file mode 100644
index 0000000..3caec72
--- /dev/null
+++ b/src/org/apache/http/protocol/BasicHttpProcessor.java
@@ -0,0 +1,337 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/BasicHttpProcessor.java $
+ * $Revision: 613298 $
+ * $Date: 2008-01-18 14:09:22 -0800 (Fri, 18 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+
+/**
+ * Keeps lists of interceptors for processing requests and responses.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author Andrea Selva
+ *
+ * @version $Revision: 613298 $
+ * 
+ * @since 4.0
+ */
+public final class BasicHttpProcessor implements
+    HttpProcessor, HttpRequestInterceptorList, HttpResponseInterceptorList, Cloneable {
+
+    protected List requestInterceptors = null; 
+    protected List responseInterceptors = null;
+
+
+    // non-Javadoc, see interface HttpRequestInterceptorList
+    public void addRequestInterceptor(final HttpRequestInterceptor itcp) {
+
+        if (itcp == null) {
+            return;
+        }
+        if (this.requestInterceptors == null) {
+            this.requestInterceptors = new ArrayList();
+        }
+        this.requestInterceptors.add(itcp);
+    }
+    
+    // non-Javadoc, see interface HttpRequestInterceptorList
+    public void addRequestInterceptor(final HttpRequestInterceptor itcp,
+                                      int index) {
+        if (index < 0) {
+            throw new IndexOutOfBoundsException(String.valueOf(index));
+        }
+        if (itcp == null) {
+            return;
+        }
+
+        if (this.requestInterceptors == null) {
+            if (index > 0) {
+                throw new IndexOutOfBoundsException(String.valueOf(index));
+            }
+            this.requestInterceptors = new ArrayList();
+        }
+        this.requestInterceptors.add(index, itcp);
+    }
+    
+
+    public void addResponseInterceptor(HttpResponseInterceptor itcp,
+                                       int index) {
+        if (index < 0) {
+            throw new IndexOutOfBoundsException(String.valueOf(index));
+        }
+        if (itcp == null) {
+            return;
+        }
+
+        if (this.responseInterceptors == null) {
+            if (index > 0) {
+                throw new IndexOutOfBoundsException(String.valueOf(index));
+            }
+            this.responseInterceptors = new ArrayList();
+        }
+        this.responseInterceptors.add(index, itcp);
+    }
+    
+    
+    // non-Javadoc, see interface HttpRequestInterceptorList
+    public void removeRequestInterceptorByClass(final Class clazz) {
+        if (this.requestInterceptors == null) {
+            return;
+        }
+        for (Iterator it = this.requestInterceptors.iterator();
+             it.hasNext(); ) {
+            Object request = it.next();
+            if (request.getClass().equals(clazz)) {
+                it.remove();
+            }
+        }
+    }
+    
+    // non-Javadoc, see interface HttpResponseInterceptorList
+    public void removeResponseInterceptorByClass(final Class clazz) {
+        if (this.responseInterceptors == null) {
+            return;
+        }
+        for (Iterator it = this.responseInterceptors.iterator();
+             it.hasNext(); ) {
+            Object request = it.next();
+            if (request.getClass().equals(clazz)) {
+                it.remove();
+            }
+        }
+    }
+    
+    /**
+     * Same as {@link #addRequestInterceptor(HttpRequestInterceptor) addRequestInterceptor}.
+     *
+     * @param interceptor       the interceptor to add
+     */
+    public final
+            void addInterceptor(final HttpRequestInterceptor interceptor) {
+        addRequestInterceptor(interceptor);
+    }
+    
+     public final
+            void addInterceptor(final HttpRequestInterceptor interceptor,
+                                int index) {
+        addRequestInterceptor(interceptor, index);
+    }
+    
+    
+    // non-Javadoc, see interface HttpRequestInterceptorList
+    public int getRequestInterceptorCount() {
+        return (this.requestInterceptors == null) ?
+            0 : this.requestInterceptors.size();
+    }
+    
+    
+    // non-Javadoc, see interface HttpRequestInterceptorList
+    public HttpRequestInterceptor getRequestInterceptor(int index) {
+        
+        if ((this.requestInterceptors == null) ||
+                (index < 0) || (index >= this.requestInterceptors.size()))
+            return null;
+        
+        return (HttpRequestInterceptor) this.requestInterceptors.get(index);
+    }
+    
+    
+    // non-Javadoc, see interface HttpRequestInterceptorList
+    public void clearRequestInterceptors() {
+        this.requestInterceptors = null;
+    }
+    
+    
+    
+    // non-Javadoc, see interface HttpResponseInterceptorList
+    public void addResponseInterceptor(final HttpResponseInterceptor itcp) {
+        if (itcp == null) {
+            return;
+        }
+        if (this.responseInterceptors == null) {
+            this.responseInterceptors = new ArrayList();
+        }
+        this.responseInterceptors.add(itcp);
+    }
+    
+    /**
+     * Same as {@link #addResponseInterceptor(HttpResponseInterceptor) addResponseInterceptor}.
+     *
+     * @param interceptor       the interceptor to add
+     */
+    public final
+            void addInterceptor(final HttpResponseInterceptor interceptor) {
+        addResponseInterceptor(interceptor);
+    }
+    
+    public final void addInterceptor(final HttpResponseInterceptor interceptor,
+                                     int index) {
+        addResponseInterceptor(interceptor, index);
+    }
+      
+    
+    
+    // non-Javadoc, see interface HttpResponseInterceptorList
+    public int getResponseInterceptorCount() {
+        return (this.responseInterceptors == null) ?
+            0 : this.responseInterceptors.size();
+    }
+    
+    
+    // non-Javadoc, see interface HttpResponseInterceptorList
+    public HttpResponseInterceptor getResponseInterceptor(int index) {
+        
+        if ((this.responseInterceptors == null) ||
+                (index < 0) || (index >= this.responseInterceptors.size()))
+            return null;
+        
+        return (HttpResponseInterceptor) this.responseInterceptors.get(index);
+    }
+    
+    
+    // non-Javadoc, see interface HttpResponseInterceptorList
+    public void clearResponseInterceptors() {
+        this.responseInterceptors = null;
+    }
+    
+    
+    /**
+     * Sets the interceptor lists.
+     * First, both interceptor lists maintained by this processor
+     * will be cleared.
+     * Subsequently,
+     * elements of the argument list that are request interceptors will be
+     * added to the request interceptor list.
+     * Elements that are response interceptors will be
+     * added to the response interceptor list.
+     * Elements that are both request and response interceptor will be
+     * added to both lists.
+     * Elements that are neither request nor response interceptor
+     * will be ignored.
+     *
+     * @param list      the list of request and response interceptors
+     *                  from which to initialize
+     */
+    public void setInterceptors(final List list) {
+        if (list == null) {
+            throw new IllegalArgumentException("List must not be null.");
+        }
+        if (this.requestInterceptors != null) {
+            this.requestInterceptors.clear();
+        }
+        if (this.responseInterceptors != null) {
+            this.responseInterceptors.clear();
+        }
+        for (int i = 0; i < list.size(); i++) {
+            Object obj = list.get(i);
+            if (obj instanceof HttpRequestInterceptor) {
+                addInterceptor((HttpRequestInterceptor)obj);
+            }
+            if (obj instanceof HttpResponseInterceptor) {
+                addInterceptor((HttpResponseInterceptor)obj);
+            }
+        }
+    }
+    
+    /**
+     * Clears both interceptor lists maintained by this processor.
+     */
+    public void clearInterceptors() {
+        clearRequestInterceptors();
+        clearResponseInterceptors();
+    }
+    
+    // non-Javadoc, see interface HttpRequestInterceptor (via HttpProcessor)
+    public void process(
+            final HttpRequest request,
+            final HttpContext context)
+            throws IOException, HttpException {
+        if (this.requestInterceptors != null) {
+            for (int i = 0; i < this.requestInterceptors.size(); i++) {
+                HttpRequestInterceptor interceptor =
+                    (HttpRequestInterceptor) this.requestInterceptors.get(i);
+                interceptor.process(request, context);
+            }
+        }
+    }
+    
+    // non-Javadoc, see interface HttpResponseInterceptor (via HttpProcessor)
+    public void process(
+            final HttpResponse response,
+            final HttpContext context)
+            throws IOException, HttpException {
+        if (this.responseInterceptors != null) {
+            for (int i = 0; i < this.responseInterceptors.size(); i++) {
+                HttpResponseInterceptor interceptor =
+                    (HttpResponseInterceptor) this.responseInterceptors.get(i);
+                interceptor.process(response, context);
+            }
+        }
+    }
+
+    protected void copyInterceptors(final BasicHttpProcessor target) {
+        if (this.requestInterceptors != null) {
+            target.requestInterceptors =
+                new ArrayList(this.requestInterceptors);
+        }
+        if (this.responseInterceptors != null) {
+            target.responseInterceptors =
+                new ArrayList(this.responseInterceptors);
+        }
+    }
+    
+    /**
+     * Creates a copy of this instance
+     *
+     * @return new instance of the BasicHttpProcessor
+     */
+    public BasicHttpProcessor copy() {
+        BasicHttpProcessor clone = new BasicHttpProcessor();
+        copyInterceptors(clone);
+        return clone;
+    }
+    
+    public Object clone() throws CloneNotSupportedException {
+        BasicHttpProcessor clone = (BasicHttpProcessor) super.clone();
+        copyInterceptors(clone);
+        return clone;
+    }
+ 
+}
diff --git a/src/org/apache/http/protocol/DefaultedHttpContext.java b/src/org/apache/http/protocol/DefaultedHttpContext.java
new file mode 100644
index 0000000..986f1a6
--- /dev/null
+++ b/src/org/apache/http/protocol/DefaultedHttpContext.java
@@ -0,0 +1,79 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/DefaultedHttpContext.java $
+ * $Revision: 654882 $
+ * $Date: 2008-05-09 09:58:59 -0700 (Fri, 09 May 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+/**
+ * {@link HttpContext} implementation that delegates resolution of an attribute
+ * to the given default {@link HttpContext} instance if the attribute is not 
+ * present in the local one. The state of the local context can be mutated,
+ * whereas the default context is treated as read-only.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Revision: 654882 $
+ */
+public final class DefaultedHttpContext implements HttpContext {
+
+    private final HttpContext local;
+    private final HttpContext defaults;
+    
+    public DefaultedHttpContext(final HttpContext local, final HttpContext defaults) {
+        super();
+        if (local == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+        this.local = local;
+        this.defaults = defaults;
+    }
+
+    public Object getAttribute(final String id) {
+        Object obj = this.local.getAttribute(id);
+        if (obj == null) {
+            return this.defaults.getAttribute(id);
+        } else {
+            return obj;
+        }
+    }
+
+    public Object removeAttribute(final String id) {
+        return this.local.removeAttribute(id);
+    }
+
+    public void setAttribute(final String id, final Object obj) {
+        this.local.setAttribute(id, obj);
+    }
+
+    public HttpContext getDefaults() {
+        return this.defaults;
+    }
+    
+}
diff --git a/src/org/apache/http/protocol/ExecutionContext.java b/src/org/apache/http/protocol/ExecutionContext.java
new file mode 100644
index 0000000..d14acb5
--- /dev/null
+++ b/src/org/apache/http/protocol/ExecutionContext.java
@@ -0,0 +1,52 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/ExecutionContext.java $
+ * $Revision: 558154 $
+ * $Date: 2007-07-20 14:29:02 -0700 (Fri, 20 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+/**
+ * {@link HttpContext Context} attribute names for protocol execution.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 558154 $
+ * 
+ * @since 4.0
+ */
+public interface ExecutionContext {
+    
+    public static final String HTTP_CONNECTION  = "http.connection"; 
+    public static final String HTTP_REQUEST     = "http.request"; 
+    public static final String HTTP_RESPONSE    = "http.response"; 
+    public static final String HTTP_TARGET_HOST = "http.target_host"; 
+    public static final String HTTP_PROXY_HOST  = "http.proxy_host"; 
+    public static final String HTTP_REQ_SENT    = "http.request_sent"; 
+
+}
diff --git a/src/org/apache/http/protocol/HTTP.java b/src/org/apache/http/protocol/HTTP.java
new file mode 100644
index 0000000..de76ca6
--- /dev/null
+++ b/src/org/apache/http/protocol/HTTP.java
@@ -0,0 +1,99 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HTTP.java $
+ * $Revision: 555989 $
+ * $Date: 2007-07-13 06:33:39 -0700 (Fri, 13 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+/**
+ * Constants and static helpers related to the HTTP protocol.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 555989 $
+ * 
+ * @since 4.0
+ */
+public final class HTTP {
+
+    public static final int CR = 13; // <US-ASCII CR, carriage return (13)>
+    public static final int LF = 10; // <US-ASCII LF, linefeed (10)>
+    public static final int SP = 32; // <US-ASCII SP, space (32)>
+    public static final int HT = 9;  // <US-ASCII HT, horizontal-tab (9)>
+
+    /** HTTP header definitions */ 
+    public static final String TRANSFER_ENCODING = "Transfer-Encoding";
+    public static final String CONTENT_LEN  = "Content-Length";
+    public static final String CONTENT_TYPE = "Content-Type";
+    public static final String CONTENT_ENCODING = "Content-Encoding";
+    public static final String EXPECT_DIRECTIVE = "Expect";
+    public static final String CONN_DIRECTIVE = "Connection";
+    public static final String TARGET_HOST = "Host";
+    public static final String USER_AGENT = "User-Agent";
+    public static final String DATE_HEADER = "Date";
+    public static final String SERVER_HEADER = "Server";
+    
+    /** HTTP expectations */
+    public static final String EXPECT_CONTINUE = "100-Continue";
+
+    /** HTTP connection control */
+    public static final String CONN_CLOSE = "Close";
+    public static final String CONN_KEEP_ALIVE = "Keep-Alive";
+    
+    /** Transfer encoding definitions */
+    public static final String CHUNK_CODING = "chunked";
+    public static final String IDENTITY_CODING = "identity";
+    
+    /** Common charset definitions */
+    public static final String UTF_8 = "UTF-8";
+    public static final String UTF_16 = "UTF-16";
+    public static final String US_ASCII = "US-ASCII";
+    public static final String ASCII = "ASCII";
+    public static final String ISO_8859_1 = "ISO-8859-1";
+
+    /** Default charsets */
+    public static final String DEFAULT_CONTENT_CHARSET = ISO_8859_1;
+    public static final String DEFAULT_PROTOCOL_CHARSET = US_ASCII;
+
+    /** Content type definitions */
+    public final static String OCTET_STREAM_TYPE = "application/octet-stream";
+    public final static String PLAIN_TEXT_TYPE = "text/plain";
+    public final static String CHARSET_PARAM = "; charset=";
+
+    /** Default content type */
+    public final static String DEFAULT_CONTENT_TYPE = OCTET_STREAM_TYPE;
+
+    public static boolean isWhitespace(char ch) {
+        return ch == SP || ch == HT || ch == CR || ch == LF; 
+    }
+    
+    private HTTP() {
+    }
+       
+}
diff --git a/src/org/apache/http/protocol/HttpContext.java b/src/org/apache/http/protocol/HttpContext.java
new file mode 100644
index 0000000..bcf36fd
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpContext.java
@@ -0,0 +1,58 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpContext.java $
+ * $Revision: 558111 $
+ * $Date: 2007-07-20 13:01:50 -0700 (Fri, 20 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+/**
+ * A context for executing a request.
+ * The context is used to tie together the request, the response,
+ * and optional application data. It is also used for internal data.
+ * Attribute names starting with the prefix "http." are
+ * {@link #RESERVED_PREFIX reserved} for internal data.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 558111 $
+ * 
+ * @since 4.0
+ */
+public interface HttpContext {
+
+    /** The prefix reserved for use by HTTP components. "http." */
+    public static final String RESERVED_PREFIX  = "http.";
+    
+    Object getAttribute(String id);
+
+    void setAttribute(String id, Object obj);
+
+    Object removeAttribute(String id);
+    
+}
diff --git a/src/org/apache/http/protocol/HttpDateGenerator.java b/src/org/apache/http/protocol/HttpDateGenerator.java
new file mode 100644
index 0000000..bfb0863
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpDateGenerator.java
@@ -0,0 +1,81 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpDateGenerator.java $
+ * $Revision: 548066 $
+ * $Date: 2007-06-17 09:51:55 -0700 (Sun, 17 Jun 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+
+/**
+ * Generates a date in the format required by the HTTP protocol.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 548066 $
+ * 
+ * @since 4.0
+ */
+public class HttpDateGenerator {
+
+    /** Date format pattern used to generate the header in RFC 1123 format. */
+    public static final
+        String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz";
+
+    /** The time zone to use in the date header. */
+    public static final TimeZone GMT = TimeZone.getTimeZone("GMT");
+
+
+    private final DateFormat dateformat;
+    
+    private long dateAsLong = 0L;
+    private String dateAsText = null;
+
+    public HttpDateGenerator() {
+        super();
+        this.dateformat = new SimpleDateFormat(PATTERN_RFC1123, Locale.US);
+        this.dateformat.setTimeZone(GMT);
+    }
+    
+    public synchronized String getCurrentDate() {
+        long now = System.currentTimeMillis();
+        if (now - this.dateAsLong > 1000) {
+            // Generate new date string
+            this.dateAsText = this.dateformat.format(new Date(now));
+            this.dateAsLong = now;
+        }
+        return this.dateAsText;
+    }
+    
+}
diff --git a/src/org/apache/http/protocol/HttpExpectationVerifier.java b/src/org/apache/http/protocol/HttpExpectationVerifier.java
new file mode 100644
index 0000000..9fa4316
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpExpectationVerifier.java
@@ -0,0 +1,73 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpExpectationVerifier.java $
+ * $Revision: 613298 $
+ * $Date: 2008-01-18 14:09:22 -0800 (Fri, 18 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+
+/**
+ * Defines an interface to verify whether an incoming HTTP request meets 
+ * the target server's expectations. 
+ *<p>
+ * The Expect request-header field is used to indicate that particular
+ * server behaviors are required by the client.
+ *</p>
+ *<pre>
+ *    Expect       =  "Expect" ":" 1#expectation
+ *
+ *    expectation  =  "100-continue" | expectation-extension
+ *    expectation-extension =  token [ "=" ( token | quoted-string )
+ *                             *expect-params ]
+ *    expect-params =  ";" token [ "=" ( token | quoted-string ) ]
+ *</pre>
+ *<p>
+ * A server that does not understand or is unable to comply with any of
+ * the expectation values in the Expect field of a request MUST respond
+ * with appropriate error status. The server MUST respond with a 417
+ * (Expectation Failed) status if any of the expectations cannot be met
+ * or, if there are other problems with the request, some other 4xx
+ * status.
+ *</p>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 613298 $
+ * 
+ * @since 4.0
+ */
+public interface HttpExpectationVerifier {
+
+    void verify(HttpRequest request, HttpResponse response, HttpContext context)
+            throws HttpException;
+    
+}
diff --git a/src/org/apache/http/protocol/HttpProcessor.java b/src/org/apache/http/protocol/HttpProcessor.java
new file mode 100644
index 0000000..489220d
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpProcessor.java
@@ -0,0 +1,56 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpProcessor.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpResponseInterceptor;
+
+/**
+ * Performs interceptor processing of requests and responses.
+ * Specific interceptors typically interpret or update message headers,
+ * and they may wrap the message entity for example to implement a
+ * specific transport or content encoding.
+ * A <code>HttpProcessor</code> typically maintains a list of
+ * interceptors that will be applied to a request or response.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ * @version $Revision: 496070 $
+ * 
+ * @since 4.0
+ */
+public interface HttpProcessor
+    extends HttpRequestInterceptor, HttpResponseInterceptor {
+
+    // no additional methods
+}
diff --git a/src/org/apache/http/protocol/HttpRequestExecutor.java b/src/org/apache/http/protocol/HttpRequestExecutor.java
new file mode 100644
index 0000000..71fa75a
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpRequestExecutor.java
@@ -0,0 +1,322 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpRequestExecutor.java $
+ * $Revision: 576073 $
+ * $Date: 2007-09-16 03:53:13 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+import java.net.ProtocolException;
+
+import org.apache.http.HttpClientConnection;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.params.CoreProtocolPNames;
+
+/**
+ * Sends HTTP requests and receives the responses.
+ * Takes care of request preprocessing and response postprocessing
+ * by the respective interceptors.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 576073 $
+ * 
+ * @since 4.0
+ */
+public class HttpRequestExecutor {
+
+    /**
+     * Create a new request executor.
+     */
+    public HttpRequestExecutor() {
+        super();
+    }
+
+    /**
+     * Decide whether a response comes with an entity.
+     * The implementation in this class is based on RFC 2616.
+     * Unknown methods and response codes are supposed to
+     * indicate responses with an entity.
+     * <br/>
+     * Derived executors can override this method to handle
+     * methods and response codes not specified in RFC 2616.
+     *
+     * @param request   the request, to obtain the executed method
+     * @param response  the response, to obtain the status code
+     */
+    protected boolean canResponseHaveBody(final HttpRequest request,
+                                          final HttpResponse response) {
+
+        if ("HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) {
+            return false;
+        }
+        int status = response.getStatusLine().getStatusCode(); 
+        return status >= HttpStatus.SC_OK 
+            && status != HttpStatus.SC_NO_CONTENT 
+            && status != HttpStatus.SC_NOT_MODIFIED
+            && status != HttpStatus.SC_RESET_CONTENT; 
+    }
+
+    /**
+     * Synchronously send a request and obtain the response.
+     *
+     * @param request   the request to send. It will be preprocessed.
+     * @param conn      the open connection over which to send
+     *
+     * @return  the response to the request, postprocessed
+     *
+     * @throws HttpException      in case of a protocol or processing problem
+     * @throws IOException        in case of an I/O problem
+     */    
+    public HttpResponse execute(
+            final HttpRequest request,
+            final HttpClientConnection conn,
+            final HttpContext context) 
+                throws IOException, HttpException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (conn == null) {
+            throw new IllegalArgumentException("Client connection may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+
+        try {
+            HttpResponse response = doSendRequest(request, conn, context);
+            if (response == null) {
+                response = doReceiveResponse(request, conn, context);
+            }
+            return response;
+        } catch (IOException ex) {
+            conn.close();
+            throw ex;
+        } catch (HttpException ex) {
+            conn.close();
+            throw ex;
+        } catch (RuntimeException ex) {
+            conn.close();
+            throw ex;
+        }
+    }
+
+    /**
+     * Prepare a request for sending.
+     *
+     * @param request   the request to prepare
+     * @param processor the processor to use
+     * @param context   the context for sending the request
+     *
+     * @throws HttpException      in case of a protocol or processing problem
+     * @throws IOException        in case of an I/O problem
+     */
+    public void preProcess(
+            final HttpRequest request,
+            final HttpProcessor processor,
+            final HttpContext context)
+                throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (processor == null) {
+            throw new IllegalArgumentException("HTTP processor may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+        processor.process(request, context);
+    }
+
+    /**
+     * Send a request over a connection.
+     * This method also handles the expect-continue handshake if necessary.
+     * If it does not have to handle an expect-continue handshake, it will
+     * not use the connection for reading or anything else that depends on
+     * data coming in over the connection.
+     *
+     * @param request   the request to send, already
+     *                  {@link #preProcess preprocessed}
+     * @param conn      the connection over which to send the request,
+     *                  already established
+     * @param context   the context for sending the request
+     *
+     * @return  a terminal response received as part of an expect-continue
+     *          handshake, or
+     *          <code>null</code> if the expect-continue handshake is not used
+     *
+     * @throws HttpException      in case of a protocol or processing problem
+     * @throws IOException        in case of an I/O problem
+     */
+    protected HttpResponse doSendRequest(
+            final HttpRequest request,
+            final HttpClientConnection conn,
+            final HttpContext context)
+                throws IOException, HttpException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (conn == null) {
+            throw new IllegalArgumentException("HTTP connection may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+
+        HttpResponse response = null;
+        context.setAttribute(ExecutionContext.HTTP_REQ_SENT, Boolean.FALSE);
+
+        conn.sendRequestHeader(request);
+        if (request instanceof HttpEntityEnclosingRequest) {
+            // Check for expect-continue handshake. We have to flush the
+            // headers and wait for an 100-continue response to handle it.
+            // If we get a different response, we must not send the entity.
+            boolean sendentity = true;
+            final ProtocolVersion ver =
+                request.getRequestLine().getProtocolVersion();
+            if (((HttpEntityEnclosingRequest) request).expectContinue() &&
+                !ver.lessEquals(HttpVersion.HTTP_1_0)) {
+
+                conn.flush();
+                // As suggested by RFC 2616 section 8.2.3, we don't wait for a
+                // 100-continue response forever. On timeout, send the entity.
+                int tms = request.getParams().getIntParameter(
+                        CoreProtocolPNames.WAIT_FOR_CONTINUE, 2000);
+                
+                if (conn.isResponseAvailable(tms)) {
+                    response = conn.receiveResponseHeader();
+                    if (canResponseHaveBody(request, response)) {
+                        conn.receiveResponseEntity(response);
+                    }
+                    int status = response.getStatusLine().getStatusCode();
+                    if (status < 200) {
+                        if (status != HttpStatus.SC_CONTINUE) {
+                            throw new ProtocolException(
+                                    "Unexpected response: " + response.getStatusLine());
+                        }
+                        // discard 100-continue
+                        response = null;
+                    } else {
+                        sendentity = false;
+                    }
+                }
+            }
+            if (sendentity) {
+                conn.sendRequestEntity((HttpEntityEnclosingRequest) request);
+            }
+        }
+        conn.flush();
+        context.setAttribute(ExecutionContext.HTTP_REQ_SENT, Boolean.TRUE);
+        return response;
+    } 
+
+    /**
+     * Wait for and receive a response.
+     * This method will automatically ignore intermediate responses
+     * with status code 1xx.
+     *
+     * @param request   the request for which to obtain the response
+     * @param conn      the connection over which the request was sent
+     * @param context   the context for receiving the response
+     *
+     * @return  the final response, not yet post-processed
+     *
+     * @throws HttpException      in case of a protocol or processing problem
+     * @throws IOException        in case of an I/O problem
+     */
+    protected HttpResponse doReceiveResponse(
+            final HttpRequest          request,
+            final HttpClientConnection conn,
+            final HttpContext          context)
+                throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (conn == null) {
+            throw new IllegalArgumentException("HTTP connection may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+
+        HttpResponse response = null;
+        int statuscode = 0;
+
+        while (response == null || statuscode < HttpStatus.SC_OK) {
+
+            response = conn.receiveResponseHeader();
+            if (canResponseHaveBody(request, response)) {
+                conn.receiveResponseEntity(response);
+            }
+            statuscode = response.getStatusLine().getStatusCode();
+
+        } // while intermediate response
+
+        return response;
+
+    }
+
+    /**
+     * Finish a response.
+     * This includes post-processing of the response object.
+     * It does <i>not</i> read the response entity, if any.
+     * It does <i>not</i> allow for immediate re-use of the
+     * connection over which the response is coming in.
+     *
+     * @param response  the response object to finish
+     * @param processor the processor to use
+     * @param context   the context for post-processing the response
+     *
+     * @throws HttpException      in case of a protocol or processing problem
+     * @throws IOException        in case of an I/O problem
+     */
+    public void postProcess(
+            final HttpResponse response,
+            final HttpProcessor processor,
+            final HttpContext context)
+                throws HttpException, IOException {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        if (processor == null) {
+            throw new IllegalArgumentException("HTTP processor may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+        processor.process(response, context);
+    }
+
+} // class HttpRequestExecutor
diff --git a/src/org/apache/http/protocol/HttpRequestHandler.java b/src/org/apache/http/protocol/HttpRequestHandler.java
new file mode 100644
index 0000000..7494353
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpRequestHandler.java
@@ -0,0 +1,53 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpRequestHandler.java $
+ * $Revision: 613298 $
+ * $Date: 2008-01-18 14:09:22 -0800 (Fri, 18 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+
+/**
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 613298 $
+ * 
+ * @since 4.0
+ */
+public interface HttpRequestHandler {
+
+    void handle(HttpRequest request, HttpResponse response, HttpContext context) 
+            throws HttpException, IOException;
+    
+}
diff --git a/src/org/apache/http/protocol/HttpRequestHandlerRegistry.java b/src/org/apache/http/protocol/HttpRequestHandlerRegistry.java
new file mode 100644
index 0000000..668d748
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpRequestHandlerRegistry.java
@@ -0,0 +1,82 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpRequestHandlerRegistry.java $
+ * $Revision: 630662 $
+ * $Date: 2008-02-24 11:40:51 -0800 (Sun, 24 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.util.Map;
+
+/**
+ * Maintains a map of HTTP request handlers keyed by a request URI pattern.
+ * {@link HttpRequestHandler} instances can be looked up by request URI
+ * using the {@link HttpRequestHandlerResolver} interface.<br/>
+ * Patterns may have three formats:
+ * <ul>
+ *   <li><code>*</code></li>
+ *   <li><code>*&lt;uri&gt;</code></li>
+ *   <li><code>&lt;uri&gt;*</code></li>
+ * </ul>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 630662 $
+ */
+public class HttpRequestHandlerRegistry implements HttpRequestHandlerResolver {
+
+    private final UriPatternMatcher matcher;
+
+    public HttpRequestHandlerRegistry() {
+        matcher = new UriPatternMatcher();
+    }
+
+    public void register(final String pattern, final HttpRequestHandler handler) {
+        matcher.register(pattern, handler);
+    }
+
+    public void unregister(final String pattern) {
+        matcher.unregister(pattern);
+    }
+
+    public void setHandlers(final Map map) {
+        matcher.setHandlers(map);
+    }
+
+    public HttpRequestHandler lookup(final String requestURI) {
+        return (HttpRequestHandler) matcher.lookup(requestURI);
+    }
+
+    /**
+     * @deprecated
+     */
+    protected boolean matchUriRequestPattern(final String pattern, final String requestUri) {
+        return matcher.matchUriRequestPattern(pattern, requestUri);
+    }
+
+}
diff --git a/src/org/apache/http/protocol/HttpRequestHandlerResolver.java b/src/org/apache/http/protocol/HttpRequestHandlerResolver.java
new file mode 100644
index 0000000..be92deb
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpRequestHandlerResolver.java
@@ -0,0 +1,46 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpRequestHandlerResolver.java $
+ * $Revision: 613298 $
+ * $Date: 2008-01-18 14:09:22 -0800 (Fri, 18 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+/**
+ * Interface to be implemented by objects that can resolve 
+ * {@link HttpRequestHandler} instances by request URI.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 613298 $
+ */
+public interface HttpRequestHandlerResolver {
+
+    HttpRequestHandler lookup(String requestURI);
+    
+}
diff --git a/src/org/apache/http/protocol/HttpRequestInterceptorList.java b/src/org/apache/http/protocol/HttpRequestInterceptorList.java
new file mode 100644
index 0000000..84ec761
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpRequestInterceptorList.java
@@ -0,0 +1,120 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpRequestInterceptorList.java $
+ * $Revision: 554903 $
+ * $Date: 2007-07-10 03:54:17 -0700 (Tue, 10 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.util.List;
+
+import org.apache.http.HttpRequestInterceptor;
+
+/**
+ * Provides access to an ordered list of request interceptors.
+ * Lists are expected to be built upfront and used read-only afterwards
+ * for {@link HttpProcessor processing}.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ * @version $Revision: 554903 $
+ * 
+ * @since 4.0
+ */
+public interface HttpRequestInterceptorList {
+
+    /**
+     * Appends a request interceptor to this list.
+     *
+     * @param itcp      the request interceptor to add
+     */
+    void addRequestInterceptor(HttpRequestInterceptor itcp)
+        ;
+
+
+    /**
+     * Inserts a request interceptor at the specified index.
+     *
+     * @param itcp      the request interceptor to add
+     * @param index     the index to insert the interceptor at
+     */
+    void addRequestInterceptor(HttpRequestInterceptor itcp, int index);
+    
+    
+    /**
+     * Obtains the current size of this list.
+     *
+     * @return  the number of request interceptors in this list
+     */
+    int getRequestInterceptorCount()
+        ;
+
+
+    /**
+     * Obtains a request interceptor from this list.
+     *
+     * @param index     the index of the interceptor to obtain,
+     *                  0 for first
+     *
+     * @return  the interceptor at the given index, or
+     *          <code>null</code> if the index is out of range
+     */
+    HttpRequestInterceptor getRequestInterceptor(int index)
+        ;
+
+
+    /**
+     * Removes all request interceptors from this list.
+     */
+    void clearRequestInterceptors()
+        ;
+
+
+    /**
+     * Removes all request interceptor of the specified class
+     *
+     * @param clazz  the class of the instances to be removed.
+     */
+    void removeRequestInterceptorByClass(Class clazz);
+    
+    
+    /**
+     * Sets the request interceptors in this list.
+     * This list will be cleared and re-initialized to contain
+     * all request interceptors from the argument list.
+     * If the argument list includes elements that are not request
+     * interceptors, the behavior is implementation dependent.
+     *
+     * @param itcps     the list of request interceptors
+     */
+    void setInterceptors(List itcps)
+        ;
+
+
+} // interface HttpRequestInterceptorList
+
diff --git a/src/org/apache/http/protocol/HttpResponseInterceptorList.java b/src/org/apache/http/protocol/HttpResponseInterceptorList.java
new file mode 100644
index 0000000..8b5811b
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpResponseInterceptorList.java
@@ -0,0 +1,121 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpResponseInterceptorList.java $
+ * $Revision: 554903 $
+ * $Date: 2007-07-10 03:54:17 -0700 (Tue, 10 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+
+import java.util.List;
+
+import org.apache.http.HttpResponseInterceptor;
+
+
+/**
+ * Provides access to an ordered list of response interceptors.
+ * Lists are expected to be built upfront and used read-only afterwards
+ * for {@link HttpProcessor processing}.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ * @version $Revision: 554903 $
+ * 
+ * @since 4.0
+ */
+public interface HttpResponseInterceptorList {
+
+    /**
+     * Appends a response interceptor to this list.
+     *
+     * @param itcp      the response interceptor to add
+     */
+    void addResponseInterceptor(HttpResponseInterceptor itcp)
+        ;
+
+    /**
+     * Inserts a response interceptor at the specified index.
+     *
+     * @param itcp      the response interceptor to add
+     * @param index     the index to insert the interceptor at
+     */
+    void addResponseInterceptor(HttpResponseInterceptor itcp, int index);
+    
+    
+    /**
+     * Obtains the current size of this list.
+     *
+     * @return  the number of response interceptors in this list
+     */
+    int getResponseInterceptorCount()
+        ;
+
+
+    /**
+     * Obtains a response interceptor from this list.
+     *
+     * @param index     the index of the interceptor to obtain,
+     *                  0 for first
+     *
+     * @return  the interceptor at the given index, or
+     *          <code>null</code> if the index is out of range
+     */
+    HttpResponseInterceptor getResponseInterceptor(int index)
+        ;
+
+
+    /**
+     * Removes all response interceptors from this list.
+     */
+    void clearResponseInterceptors()
+        ;
+
+    
+    /**
+     * Removes all response interceptor of the specified class
+     *
+     * @param clazz  the class of the instances to be removed.
+     */
+    void removeResponseInterceptorByClass(Class clazz);
+
+    
+    /**
+     * Sets the response interceptors in this list.
+     * This list will be cleared and re-initialized to contain
+     * all response interceptors from the argument list.
+     * If the argument list includes elements that are not response
+     * interceptors, the behavior is implementation dependent.
+     *
+     * @param itcps     the list of response interceptors
+     */
+    void setInterceptors(List itcps)
+        ;
+
+
+} // interface HttpResponseInterceptorList
+
diff --git a/src/org/apache/http/protocol/HttpService.java b/src/org/apache/http/protocol/HttpService.java
new file mode 100644
index 0000000..991e931
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpService.java
@@ -0,0 +1,249 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpService.java $
+ * $Revision: 610763 $
+ * $Date: 2008-01-10 04:01:13 -0800 (Thu, 10 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseFactory;
+import org.apache.http.HttpServerConnection;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.MethodNotSupportedException;
+import org.apache.http.ProtocolException;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.UnsupportedHttpVersionException;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.DefaultedHttpParams;
+import org.apache.http.util.EncodingUtils;
+
+/**
+ * Minimalistic server-side implementation of an HTTP processor.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 610763 $
+ */
+public class HttpService {
+
+    private HttpParams params = null;
+    private HttpProcessor processor = null;
+    private HttpRequestHandlerResolver handlerResolver = null;
+    private ConnectionReuseStrategy connStrategy = null;
+    private HttpResponseFactory responseFactory = null;
+    private HttpExpectationVerifier expectationVerifier = null;
+    
+    /**
+     * Create a new HTTP service.
+     *
+     * @param proc             the processor to use on requests and responses
+     * @param connStrategy     the connection reuse strategy
+     * @param responseFactory  the response factory
+     */
+    public HttpService(
+            final HttpProcessor proc,
+            final ConnectionReuseStrategy connStrategy,
+            final HttpResponseFactory responseFactory) {
+        super();
+        setHttpProcessor(proc);
+        setConnReuseStrategy(connStrategy);
+        setResponseFactory(responseFactory);
+    }
+    
+    public void setHttpProcessor(final HttpProcessor processor) {
+        if (processor == null) {
+            throw new IllegalArgumentException("HTTP processor may not be null.");
+        }
+        this.processor = processor;
+    }
+
+    public void setConnReuseStrategy(final ConnectionReuseStrategy connStrategy) {
+        if (connStrategy == null) {
+            throw new IllegalArgumentException("Connection reuse strategy may not be null");
+        }
+        this.connStrategy = connStrategy;
+    }
+
+    public void setResponseFactory(final HttpResponseFactory responseFactory) {
+        if (responseFactory == null) {
+            throw new IllegalArgumentException("Response factory may not be null");
+        }
+        this.responseFactory = responseFactory;
+    }
+    
+    public void setHandlerResolver(final HttpRequestHandlerResolver handlerResolver) {
+        this.handlerResolver = handlerResolver;
+    }
+
+    public void setExpectationVerifier(final HttpExpectationVerifier expectationVerifier) {
+        this.expectationVerifier = expectationVerifier;
+    }
+
+    public HttpParams getParams() {
+        return this.params;
+    }
+    
+    public void setParams(final HttpParams params) {
+        this.params = params;
+    }
+    
+    public void handleRequest(
+            final HttpServerConnection conn, 
+            final HttpContext context) throws IOException, HttpException { 
+        
+        context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
+
+        HttpResponse response = null;
+        
+        try {
+
+            HttpRequest request = conn.receiveRequestHeader();
+            request.setParams(
+                    new DefaultedHttpParams(request.getParams(), this.params));
+            
+            ProtocolVersion ver =
+                request.getRequestLine().getProtocolVersion();
+            if (!ver.lessEquals(HttpVersion.HTTP_1_1)) {
+                // Downgrade protocol version if greater than HTTP/1.1 
+                ver = HttpVersion.HTTP_1_1;
+            }
+
+            if (request instanceof HttpEntityEnclosingRequest) {
+
+                if (((HttpEntityEnclosingRequest) request).expectContinue()) {
+                    response = this.responseFactory.newHttpResponse(ver, 
+                            HttpStatus.SC_CONTINUE, context);
+                    response.setParams(
+                            new DefaultedHttpParams(response.getParams(), this.params));
+                    
+                    if (this.expectationVerifier != null) {
+                        try {
+                            this.expectationVerifier.verify(request, response, context);
+                        } catch (HttpException ex) {
+                            response = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_0, 
+                                    HttpStatus.SC_INTERNAL_SERVER_ERROR, context);
+                            response.setParams(
+                                    new DefaultedHttpParams(response.getParams(), this.params));
+                            handleException(ex, response);
+                        }
+                    }
+                    if (response.getStatusLine().getStatusCode() < 200) {
+                        // Send 1xx response indicating the server expections
+                        // have been met
+                        conn.sendResponseHeader(response);
+                        conn.flush();
+                        response = null;
+                        conn.receiveRequestEntity((HttpEntityEnclosingRequest) request);
+                    }
+                } else {
+                    conn.receiveRequestEntity((HttpEntityEnclosingRequest) request);
+                }
+            }
+
+            if (response == null) {
+                response = this.responseFactory.newHttpResponse(ver, HttpStatus.SC_OK, context);
+                response.setParams(
+                        new DefaultedHttpParams(response.getParams(), this.params));
+
+                context.setAttribute(ExecutionContext.HTTP_REQUEST, request);
+                context.setAttribute(ExecutionContext.HTTP_RESPONSE, response);
+
+                this.processor.process(request, context);
+                doService(request, response, context);
+            }
+            
+            // Make sure the request content is fully consumed
+            if (request instanceof HttpEntityEnclosingRequest) {
+                HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity();
+                if (entity != null) {
+                    entity.consumeContent();
+                }
+            }
+            
+        } catch (HttpException ex) {
+            response = this.responseFactory.newHttpResponse
+                (HttpVersion.HTTP_1_0, HttpStatus.SC_INTERNAL_SERVER_ERROR,
+                 context);
+            response.setParams(
+                    new DefaultedHttpParams(response.getParams(), this.params));
+            handleException(ex, response);
+        }
+        
+        this.processor.process(response, context);
+        conn.sendResponseHeader(response);
+        conn.sendResponseEntity(response);
+        conn.flush();
+        
+        if (!this.connStrategy.keepAlive(response, context)) {
+            conn.close();
+        }
+    }
+    
+    protected void handleException(final HttpException ex, final HttpResponse response) {
+        if (ex instanceof MethodNotSupportedException) {
+            response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED);
+        } else if (ex instanceof UnsupportedHttpVersionException) {
+            response.setStatusCode(HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED);
+        } else if (ex instanceof ProtocolException) {
+            response.setStatusCode(HttpStatus.SC_BAD_REQUEST);
+        } else {
+            response.setStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
+        }
+        byte[] msg = EncodingUtils.getAsciiBytes(ex.getMessage());
+        ByteArrayEntity entity = new ByteArrayEntity(msg);
+        entity.setContentType("text/plain; charset=US-ASCII");
+        response.setEntity(entity);
+    }
+    
+    protected void doService(
+            final HttpRequest request, 
+            final HttpResponse response,
+            final HttpContext context) throws HttpException, IOException {
+        HttpRequestHandler handler = null;
+        if (this.handlerResolver != null) {
+            String requestURI = request.getRequestLine().getUri();
+            handler = this.handlerResolver.lookup(requestURI);
+        }
+        if (handler != null) {
+            handler.handle(request, response, context);
+        } else {
+            response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED);
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/protocol/RequestConnControl.java b/src/org/apache/http/protocol/RequestConnControl.java
new file mode 100644
index 0000000..0a7088c
--- /dev/null
+++ b/src/org/apache/http/protocol/RequestConnControl.java
@@ -0,0 +1,67 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/RequestConnControl.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+
+/**
+ * A request interceptor that suggests connection keep-alive to the server.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 496070 $
+ * 
+ * @since 4.0
+ */
+public class RequestConnControl implements HttpRequestInterceptor {
+
+    public RequestConnControl() {
+        super();
+    }
+    
+    public void process(final HttpRequest request, final HttpContext context) 
+            throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (!request.containsHeader(HTTP.CONN_DIRECTIVE)) {
+            // Default policy is to keep connection alive
+            // whenever possible
+            request.addHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_KEEP_ALIVE);
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/protocol/RequestContent.java b/src/org/apache/http/protocol/RequestContent.java
new file mode 100644
index 0000000..745f604
--- /dev/null
+++ b/src/org/apache/http/protocol/RequestContent.java
@@ -0,0 +1,101 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/RequestContent.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.ProtocolException;
+
+/**
+ * A request interceptor that decides about the transport encoding.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573864 $
+ * 
+ * @since 4.0
+ */
+public class RequestContent implements HttpRequestInterceptor {
+
+    public RequestContent() {
+        super();
+    }
+    
+    public void process(final HttpRequest request, final HttpContext context) 
+            throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (request instanceof HttpEntityEnclosingRequest) {
+            if (request.containsHeader(HTTP.TRANSFER_ENCODING)) {
+                throw new ProtocolException("Transfer-encoding header already present");
+            }
+            if (request.containsHeader(HTTP.CONTENT_LEN)) {
+                throw new ProtocolException("Content-Length header already present");
+            }
+            ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
+            HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity();
+            if (entity == null) {
+                request.addHeader(HTTP.CONTENT_LEN, "0");
+                return;
+            }
+            // Must specify a transfer encoding or a content length 
+            if (entity.isChunked() || entity.getContentLength() < 0) {
+                if (ver.lessEquals(HttpVersion.HTTP_1_0)) {
+                    throw new ProtocolException(
+                            "Chunked transfer encoding not allowed for " + ver);
+                }
+                request.addHeader(HTTP.TRANSFER_ENCODING, HTTP.CHUNK_CODING);
+            } else {
+                request.addHeader(HTTP.CONTENT_LEN, Long.toString(entity.getContentLength()));
+            }
+            // Specify a content type if known
+            if (entity.getContentType() != null && !request.containsHeader(
+                    HTTP.CONTENT_TYPE )) {
+                request.addHeader(entity.getContentType()); 
+            }
+            // Specify a content encoding if known
+            if (entity.getContentEncoding() != null && !request.containsHeader(
+                    HTTP.CONTENT_ENCODING)) {
+                request.addHeader(entity.getContentEncoding()); 
+            }
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/protocol/RequestDate.java b/src/org/apache/http/protocol/RequestDate.java
new file mode 100644
index 0000000..6462906
--- /dev/null
+++ b/src/org/apache/http/protocol/RequestDate.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/RequestDate.java $
+ * $Revision: 555989 $
+ * $Date: 2007-07-13 06:33:39 -0700 (Fri, 13 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpRequestInterceptor;
+
+/**
+ * A request interceptor that adds a Date header.
+ * For use on the client side.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 555989 $
+ * 
+ * @since 4.0
+ */
+public class RequestDate implements HttpRequestInterceptor {
+
+    private static final HttpDateGenerator DATE_GENERATOR = new HttpDateGenerator(); 
+    
+    public RequestDate() {
+        super();
+    }
+
+    public void process(final HttpRequest request, final HttpContext context) 
+            throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException
+                ("HTTP request may not be null.");
+        }
+        if ((request instanceof HttpEntityEnclosingRequest) &&
+            !request.containsHeader(HTTP.DATE_HEADER)) {
+            String httpdate = DATE_GENERATOR.getCurrentDate();
+            request.setHeader(HTTP.DATE_HEADER, httpdate); 
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/protocol/RequestExpectContinue.java b/src/org/apache/http/protocol/RequestExpectContinue.java
new file mode 100644
index 0000000..0799849
--- /dev/null
+++ b/src/org/apache/http/protocol/RequestExpectContinue.java
@@ -0,0 +1,78 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/RequestExpectContinue.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.params.HttpProtocolParams;
+
+/**
+ * A request interceptor that enables the expect-continue handshake.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573864 $
+ * 
+ * @since 4.0
+ */
+public class RequestExpectContinue implements HttpRequestInterceptor {
+
+    public RequestExpectContinue() {
+        super();
+    }
+    
+    public void process(final HttpRequest request, final HttpContext context) 
+            throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (request instanceof HttpEntityEnclosingRequest) {
+            HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity();
+            // Do not send the expect header if request body is known to be empty
+            if (entity != null && entity.getContentLength() != 0) { 
+                ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
+                if (HttpProtocolParams.useExpectContinue(request.getParams()) 
+                        && !ver.lessEquals(HttpVersion.HTTP_1_0)) {
+                    request.addHeader(HTTP.EXPECT_DIRECTIVE, HTTP.EXPECT_CONTINUE);
+                }
+            }
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/protocol/RequestTargetHost.java b/src/org/apache/http/protocol/RequestTargetHost.java
new file mode 100644
index 0000000..9349a8a
--- /dev/null
+++ b/src/org/apache/http/protocol/RequestTargetHost.java
@@ -0,0 +1,98 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/RequestTargetHost.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+import org.apache.http.HttpConnection;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpInetConnection;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.ProtocolException;
+
+/**
+ * A request interceptor that sets the Host header for HTTP/1.1 requests.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573864 $
+ * 
+ * @since 4.0
+ */
+public class RequestTargetHost implements HttpRequestInterceptor {
+
+    public RequestTargetHost() {
+        super();
+    }
+    
+    public void process(final HttpRequest request, final HttpContext context) 
+            throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+        if (!request.containsHeader(HTTP.TARGET_HOST)) {
+            HttpHost targethost = (HttpHost) context
+                .getAttribute(ExecutionContext.HTTP_TARGET_HOST);
+            if (targethost == null) {
+                HttpConnection conn = (HttpConnection) context
+                    .getAttribute(ExecutionContext.HTTP_CONNECTION);
+                if (conn instanceof HttpInetConnection) {
+                    // Populate the context with a default HTTP host based on the 
+                    // inet address of the target host
+                    InetAddress address = ((HttpInetConnection) conn).getRemoteAddress();
+                    int port = ((HttpInetConnection) conn).getRemotePort();
+                    if (address != null) {
+                        targethost = new HttpHost(address.getHostName(), port);
+                    }
+                }
+                if (targethost == null) {
+                    ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
+                    if (ver.lessEquals(HttpVersion.HTTP_1_0)) {
+                        return;
+                    } else {
+                        throw new ProtocolException("Target host missing");
+                    }
+                }
+            }
+            request.addHeader(HTTP.TARGET_HOST, targethost.toHostString());
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/protocol/RequestUserAgent.java b/src/org/apache/http/protocol/RequestUserAgent.java
new file mode 100644
index 0000000..5a3145f
--- /dev/null
+++ b/src/org/apache/http/protocol/RequestUserAgent.java
@@ -0,0 +1,69 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/RequestUserAgent.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.params.HttpProtocolParams;
+
+/**
+ * A request interceptor that adds a User-Agent header.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 496070 $
+ * 
+ * @since 4.0
+ */
+public class RequestUserAgent implements HttpRequestInterceptor {
+
+    public RequestUserAgent() {
+        super();
+    }
+    
+    public void process(final HttpRequest request, final HttpContext context) 
+        throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (!request.containsHeader(HTTP.USER_AGENT)) {
+            String useragent = HttpProtocolParams.getUserAgent(request.getParams());
+            if (useragent != null) {
+                request.addHeader(HTTP.USER_AGENT, useragent);
+            }
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/protocol/ResponseConnControl.java b/src/org/apache/http/protocol/ResponseConnControl.java
new file mode 100644
index 0000000..2e535fe
--- /dev/null
+++ b/src/org/apache/http/protocol/ResponseConnControl.java
@@ -0,0 +1,104 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/ResponseConnControl.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolVersion;
+
+/**
+ * A response interceptor that suggests connection keep-alive to the client.
+ * For use on the server side.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618017 $
+ * 
+ * @since 4.0
+ */
+public class ResponseConnControl implements HttpResponseInterceptor {
+
+    public ResponseConnControl() {
+        super();
+    }
+    
+    public void process(final HttpResponse response, final HttpContext context) 
+            throws HttpException, IOException {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+        // Always drop connection after certain type of responses
+        int status = response.getStatusLine().getStatusCode();
+        if (status == HttpStatus.SC_BAD_REQUEST ||
+                status == HttpStatus.SC_REQUEST_TIMEOUT ||
+                status == HttpStatus.SC_LENGTH_REQUIRED ||
+                status == HttpStatus.SC_REQUEST_TOO_LONG ||
+                status == HttpStatus.SC_REQUEST_URI_TOO_LONG ||
+                status == HttpStatus.SC_SERVICE_UNAVAILABLE ||
+                status == HttpStatus.SC_NOT_IMPLEMENTED) {
+            response.setHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE);
+            return;
+        }
+        // Always drop connection for HTTP/1.0 responses and below
+        // if the content body cannot be correctly delimited
+        HttpEntity entity = response.getEntity();
+        if (entity != null) {
+            ProtocolVersion ver = response.getStatusLine().getProtocolVersion();
+            if (entity.getContentLength() < 0 && 
+                    (!entity.isChunked() || ver.lessEquals(HttpVersion.HTTP_1_0))) {
+                response.setHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE);
+                return;
+            }
+        }
+        // Drop connection if requested by the client
+        HttpRequest request = (HttpRequest)
+            context.getAttribute(ExecutionContext.HTTP_REQUEST);
+        if (request != null) {
+            Header header = request.getFirstHeader(HTTP.CONN_DIRECTIVE);
+            if (header != null) {
+                response.setHeader(HTTP.CONN_DIRECTIVE, header.getValue());
+            }
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/protocol/ResponseContent.java b/src/org/apache/http/protocol/ResponseContent.java
new file mode 100644
index 0000000..d1ac054
--- /dev/null
+++ b/src/org/apache/http/protocol/ResponseContent.java
@@ -0,0 +1,101 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/ResponseContent.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.ProtocolException;
+
+/**
+ * A response interceptor that sets up entity-related headers.
+ * For use on the server side.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573864 $
+ * 
+ * @since 4.0
+ */
+public class ResponseContent implements HttpResponseInterceptor {
+
+    public ResponseContent() {
+        super();
+    }
+    
+    public void process(final HttpResponse response, final HttpContext context) 
+            throws HttpException, IOException {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (response.containsHeader(HTTP.TRANSFER_ENCODING)) {
+            throw new ProtocolException("Transfer-encoding header already present");
+        }
+        if (response.containsHeader(HTTP.CONTENT_LEN)) {
+            throw new ProtocolException("Content-Length header already present");
+        }
+        ProtocolVersion ver = response.getStatusLine().getProtocolVersion();
+        HttpEntity entity = response.getEntity();
+        if (entity != null) {
+            long len = entity.getContentLength();
+            if (entity.isChunked() && !ver.lessEquals(HttpVersion.HTTP_1_0)) {
+                response.addHeader(HTTP.TRANSFER_ENCODING, HTTP.CHUNK_CODING);
+            } else if (len >= 0) {
+                response.addHeader(HTTP.CONTENT_LEN, Long.toString(entity.getContentLength()));
+            }
+            // Specify a content type if known
+            if (entity.getContentType() != null && !response.containsHeader(
+                    HTTP.CONTENT_TYPE )) {
+                response.addHeader(entity.getContentType()); 
+            }
+            // Specify a content encoding if known
+            if (entity.getContentEncoding() != null && !response.containsHeader(
+                    HTTP.CONTENT_ENCODING)) {
+                response.addHeader(entity.getContentEncoding()); 
+            }
+        } else {
+            int status = response.getStatusLine().getStatusCode();
+            if (status != HttpStatus.SC_NO_CONTENT 
+                    && status != HttpStatus.SC_NOT_MODIFIED
+                    && status != HttpStatus.SC_RESET_CONTENT) {
+                response.addHeader(HTTP.CONTENT_LEN, "0");
+            }
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/protocol/ResponseDate.java b/src/org/apache/http/protocol/ResponseDate.java
new file mode 100644
index 0000000..431dc19
--- /dev/null
+++ b/src/org/apache/http/protocol/ResponseDate.java
@@ -0,0 +1,73 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/ResponseDate.java $
+ * $Revision: 555989 $
+ * $Date: 2007-07-13 06:33:39 -0700 (Fri, 13 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.HttpStatus;
+
+/**
+ * A response interceptor that adds a Date header.
+ * For use on the server side.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 555989 $
+ * 
+ * @since 4.0
+ */
+public class ResponseDate implements HttpResponseInterceptor {
+
+    private static final HttpDateGenerator DATE_GENERATOR = new HttpDateGenerator(); 
+    
+    public ResponseDate() {
+        super();
+    }
+
+    public void process(final HttpResponse response, final HttpContext context) 
+            throws HttpException, IOException {
+        if (response == null) {
+            throw new IllegalArgumentException
+                ("HTTP response may not be null.");
+        }
+        int status = response.getStatusLine().getStatusCode();
+        if ((status >= HttpStatus.SC_OK) &&
+            !response.containsHeader(HTTP.DATE_HEADER)) {
+            String httpdate = DATE_GENERATOR.getCurrentDate();
+            response.setHeader(HTTP.DATE_HEADER, httpdate); 
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/protocol/ResponseServer.java b/src/org/apache/http/protocol/ResponseServer.java
new file mode 100644
index 0000000..44df593
--- /dev/null
+++ b/src/org/apache/http/protocol/ResponseServer.java
@@ -0,0 +1,71 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/ResponseServer.java $
+ * $Revision: 576073 $
+ * $Date: 2007-09-16 03:53:13 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.params.CoreProtocolPNames;
+
+/**
+ * A response interceptor that adds a Server header.
+ * For use on the server side.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 576073 $
+ * 
+ * @since 4.0
+ */
+public class ResponseServer implements HttpResponseInterceptor {
+
+    public ResponseServer() {
+        super();
+    }
+
+    public void process(final HttpResponse response, final HttpContext context) 
+            throws HttpException, IOException {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (!response.containsHeader(HTTP.SERVER_HEADER)) {
+            String s = (String) response.getParams().getParameter(
+                    CoreProtocolPNames.ORIGIN_SERVER);
+            if (s != null) {
+                response.addHeader(HTTP.SERVER_HEADER, s);
+            }
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/protocol/SyncBasicHttpContext.java b/src/org/apache/http/protocol/SyncBasicHttpContext.java
new file mode 100644
index 0000000..b1a408b
--- /dev/null
+++ b/src/org/apache/http/protocol/SyncBasicHttpContext.java
@@ -0,0 +1,61 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/SyncBasicHttpContext.java $
+ * $Revision: 613298 $
+ * $Date: 2008-01-18 14:09:22 -0800 (Fri, 18 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+/**
+ * Thread-safe extension of the {@link BasicHttpContext}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 613298 $
+ * 
+ * @since 4.0
+ */
+public class SyncBasicHttpContext extends BasicHttpContext {
+    
+    public SyncBasicHttpContext(final HttpContext parentContext) {
+        super(parentContext);
+    }
+    
+    public synchronized Object getAttribute(final String id) {
+        return super.getAttribute(id);
+    }
+
+    public synchronized void setAttribute(final String id, final Object obj) {
+        super.setAttribute(id, obj);
+    }
+    
+    public synchronized Object removeAttribute(final String id) {
+        return super.removeAttribute(id);
+    }
+
+}
diff --git a/src/org/apache/http/protocol/UriPatternMatcher.java b/src/org/apache/http/protocol/UriPatternMatcher.java
new file mode 100644
index 0000000..2870d99
--- /dev/null
+++ b/src/org/apache/http/protocol/UriPatternMatcher.java
@@ -0,0 +1,127 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/UriPatternMatcher.java $
+ * $Revision: 630662 $
+ * $Date: 2008-02-24 11:40:51 -0800 (Sun, 24 Feb 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Maintains a map of objects keyed by a request URI pattern.
+ * Instances can be looked up by request URI.<br/>
+ * Patterns may have three formats:
+ * <ul>
+ *   <li><code>*</code></li>
+ *   <li><code>*&lt;uri&gt;</code></li>
+ *   <li><code>&lt;uri&gt;*</code></li>
+ * </ul>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 630662 $
+ */
+public class UriPatternMatcher {
+
+    private final Map handlerMap;
+
+    public UriPatternMatcher() {
+        super();
+        this.handlerMap = new HashMap();
+    }
+
+    public void register(final String pattern, final Object handler) {
+        if (pattern == null) {
+            throw new IllegalArgumentException("URI request pattern may not be null");
+        }
+        if (handler == null) {
+            throw new IllegalArgumentException("HTTP request handelr may not be null");
+        }
+        this.handlerMap.put(pattern, handler);
+    }
+
+    public void unregister(final String pattern) {
+        if (pattern == null) {
+            return;
+        }
+        this.handlerMap.remove(pattern);
+    }
+
+    public void setHandlers(final Map map) {
+        if (map == null) {
+            throw new IllegalArgumentException("Map of handlers may not be null");
+        }
+        this.handlerMap.clear();
+        this.handlerMap.putAll(map);
+    }
+
+    public Object lookup(String requestURI) {
+        if (requestURI == null) {
+            throw new IllegalArgumentException("Request URI may not be null");
+        }
+        //Strip away the query part part if found
+        int index = requestURI.indexOf("?");
+        if (index != -1) {
+            requestURI = requestURI.substring(0, index);
+        }
+
+        // direct match?
+        Object handler = this.handlerMap.get(requestURI);
+        if (handler == null) {
+            // pattern match?
+            String bestMatch = null;
+            for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {
+                String pattern = (String) it.next();
+                if (matchUriRequestPattern(pattern, requestURI)) {
+                    // we have a match. is it any better?
+                    if (bestMatch == null
+                            || (bestMatch.length() < pattern.length())
+                            || (bestMatch.length() == pattern.length() && pattern.endsWith("*"))) {
+                        handler = this.handlerMap.get(pattern);
+                        bestMatch = pattern;
+                    }
+                }
+            }
+        }
+        return handler;
+    }
+
+    protected boolean matchUriRequestPattern(final String pattern, final String requestUri) {
+        if (pattern.equals("*")) {
+            return true;
+        } else {
+            return
+            (pattern.endsWith("*") && requestUri.startsWith(pattern.substring(0, pattern.length() - 1))) ||
+            (pattern.startsWith("*") && requestUri.endsWith(pattern.substring(1, pattern.length())));
+        }
+    }
+
+}
diff --git a/src/org/apache/http/protocol/package.html b/src/org/apache/http/protocol/package.html
new file mode 100644
index 0000000..5056bcc
--- /dev/null
+++ b/src/org/apache/http/protocol/package.html
@@ -0,0 +1,112 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/package.html $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+HTTP protocol execution framework.
+
+Apart from simply sending and receiving messages, there are a lot
+of things to consider when communicating with HTTP. Many details
+such as transport encodings or connection management are handled
+by setting up or interpreting
+{@link org.apache.http.Header headers} in the messages.
+In order to relieve applications from the responsibility of
+implementing these nitty-gritty details of the protocol,
+HTTP components provides an execution framework that sets up
+some of the headers before sending a message, and interprets
+headers when a message has been received.
+<br/>
+An HTTP {@link org.apache.http.protocol.HttpProcessor processor}
+typically keeps lists of so-called interceptors that will be executed
+before a message is sent and after it has been received.
+An application should initialize a processor, set up the lists
+with the required and desired processors, and then communicate
+through that processor. There are four kinds of interceptors,
+depending on whether they act on
+{@link org.apache.http.HttpRequestInterceptor requests} or
+{@link org.apache.http.HttpResponseInterceptor responses},
+on the client or server side:
+
+<table border="1" cellpadding="5">
+<tr><th></th>
+    <th>Client</th>
+    <th>Server</th>
+</tr>
+<tr><th>Request</th>
+    <td>prepares headers before a request is sent</td>
+    <td>interprets headers when a request is received</td>
+</tr>
+<tr><th>Response</th>
+    <td>interprets headers when a response is received</td>
+    <td>prepares headers before a response is sent</td>
+</tr>
+</table>
+
+<p>
+{@link org.apache.http.protocol.HttpRequestExecutor HttpRequestExecutor}
+is a processor for the client side,
+{@link org.apache.http.protocol.HttpService HttpService}
+for the server side.
+On the client side, a {@link org.apache.http.protocol.HttpContext context}
+is used to tie together a request, the response to it, and other data
+that might be associated with the request execution. It is passed to
+the request executor whenever needed.
+</p>
+
+<p>
+<font size="+1">
+<i>
+Information about required and recommended interceptors for the
+client side will be provided elsewhere. For the time being, please
+refer to the comments in the example applications or ask on
+one of the mailing lists.
+</i>
+</font>
+</p>
+
+<p>
+<b>Note:</b>
+If you want to develop a server-side application, we recommend that
+you implement your application as a servlet running in a servlet engine
+like <a href="http://tomcat.apache.org/">Tomcat</a> or full-blown
+JSEE container like <a href="http://geronimo.apache.org/">Geronimo</a>.
+If you prefer to implement a server-side application based on our
+{@link org.apache.http.protocol.HttpService HttpService}, we'll
+assume that you know what you're doing and that you don't need
+help in figuring out which interceptors need to be configured.
+</p>
+
+
+</body>
+</html>
diff --git a/src/org/apache/http/svn.info b/src/org/apache/http/svn.info
new file mode 100644
index 0000000..100deb5
--- /dev/null
+++ b/src/org/apache/http/svn.info
@@ -0,0 +1,8 @@
+Repository Root: http://svn.apache.org/repos/asf
+Repository UUID: 13f79535-47bb-0310-9956-ffa450edef68
+Revision: 677354
+Node Kind: directory
+Schedule: normal
+Last Changed Author: olegk
+Last Changed Rev: 677250
+Last Changed Date: 2008-07-16 04:45:47 -0700 (Wed, 16 Jul 2008)
diff --git a/src/org/apache/http/util/ByteArrayBuffer.java b/src/org/apache/http/util/ByteArrayBuffer.java
new file mode 100644
index 0000000..01d6577
--- /dev/null
+++ b/src/org/apache/http/util/ByteArrayBuffer.java
@@ -0,0 +1,162 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/util/ByteArrayBuffer.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.util;
+
+/**
+ * A resizable byte array.
+ *
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Revision: 496070 $
+ * 
+ * @since 4.0
+ */
+public final class ByteArrayBuffer  {
+    
+    private byte[] buffer;
+    private int len;
+
+    public ByteArrayBuffer(int capacity) {
+        super();
+        if (capacity < 0) {
+            throw new IllegalArgumentException("Buffer capacity may not be negative");
+        }
+        this.buffer = new byte[capacity]; 
+    }
+
+    private void expand(int newlen) {
+        byte newbuffer[] = new byte[Math.max(this.buffer.length << 1, newlen)];
+        System.arraycopy(this.buffer, 0, newbuffer, 0, this.len);
+        this.buffer = newbuffer;
+    }
+    
+    public void append(final byte[] b, int off, int len) {
+        if (b == null) {
+            return;
+        }
+        if ((off < 0) || (off > b.length) || (len < 0) ||
+                ((off + len) < 0) || ((off + len) > b.length)) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (len == 0) {
+            return;
+        }
+        int newlen = this.len + len;
+        if (newlen > this.buffer.length) {
+            expand(newlen);
+        }
+        System.arraycopy(b, off, this.buffer, this.len, len);
+        this.len = newlen;
+    }
+
+    public void append(int b) {
+        int newlen = this.len + 1;
+        if (newlen > this.buffer.length) {
+            expand(newlen);
+        }
+        this.buffer[this.len] = (byte)b;
+        this.len = newlen;
+    }
+
+    public void append(final char[] b, int off, int len) {
+        if (b == null) {
+            return;
+        }
+        if ((off < 0) || (off > b.length) || (len < 0) ||
+                ((off + len) < 0) || ((off + len) > b.length)) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (len == 0) {
+            return;
+        }
+        int oldlen = this.len;
+        int newlen = oldlen + len;
+        if (newlen > this.buffer.length) {
+            expand(newlen);
+        }
+        for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) {
+            this.buffer[i2] = (byte) b[i1];
+        }
+        this.len = newlen;
+    }
+
+    public void append(final CharArrayBuffer b, int off, int len) {
+        if (b == null) {
+            return;
+        }
+        append(b.buffer(), off, len);
+    }
+    
+    public void clear() {
+        this.len = 0;
+    }
+    
+    public byte[] toByteArray() {
+        byte[] b = new byte[this.len]; 
+        if (this.len > 0) {
+            System.arraycopy(this.buffer, 0, b, 0, this.len);
+        }
+        return b;
+    }
+    
+    public int byteAt(int i) {
+        return this.buffer[i];
+    }
+    
+    public int capacity() {
+        return this.buffer.length;
+    }
+    
+    public int length() {
+        return this.len;
+    }
+
+    public byte[] buffer() {
+        return this.buffer;
+    }
+        
+    public void setLength(int len) {
+        if (len < 0 || len > this.buffer.length) {
+            throw new IndexOutOfBoundsException();
+        }
+        this.len = len;
+    }
+    
+    public boolean isEmpty() {
+        return this.len == 0; 
+    }
+    
+    public boolean isFull() {
+        return this.len == this.buffer.length; 
+    }
+    
+}
diff --git a/src/org/apache/http/util/CharArrayBuffer.java b/src/org/apache/http/util/CharArrayBuffer.java
new file mode 100644
index 0000000..b89f5d1
--- /dev/null
+++ b/src/org/apache/http/util/CharArrayBuffer.java
@@ -0,0 +1,264 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/util/CharArrayBuffer.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.util;
+
+import org.apache.http.protocol.HTTP;
+
+/**
+ * A resizable char array.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Revision: 496070 $
+ * 
+ * @since 4.0
+ */
+public final class CharArrayBuffer  {
+    
+    private char[] buffer;
+    private int len;
+
+    public CharArrayBuffer(int capacity) {
+        super();
+        if (capacity < 0) {
+            throw new IllegalArgumentException("Buffer capacity may not be negative");
+        }
+        this.buffer = new char[capacity]; 
+    }
+
+    private void expand(int newlen) {
+        char newbuffer[] = new char[Math.max(this.buffer.length << 1, newlen)];
+        System.arraycopy(this.buffer, 0, newbuffer, 0, this.len);
+        this.buffer = newbuffer;
+    }
+    
+    public void append(final char[] b, int off, int len) {
+        if (b == null) {
+            return;
+        }
+        if ((off < 0) || (off > b.length) || (len < 0) ||
+                ((off + len) < 0) || ((off + len) > b.length)) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (len == 0) {
+            return;
+        }
+        int newlen = this.len + len;
+        if (newlen > this.buffer.length) {
+            expand(newlen);
+        }
+        System.arraycopy(b, off, this.buffer, this.len, len);
+        this.len = newlen;
+    }
+    
+    public void append(String str) {
+        if (str == null) {
+            str = "null";
+        }
+        int strlen = str.length();
+        int newlen = this.len + strlen;
+        if (newlen > this.buffer.length) {
+            expand(newlen);
+        }
+        str.getChars(0, strlen, this.buffer, this.len);
+        this.len = newlen;
+    }
+
+    public void append(final CharArrayBuffer b, int off, int len) {
+        if (b == null) {
+            return;
+        }
+        append(b.buffer, off, len);
+    }
+        
+    public void append(final CharArrayBuffer b) {
+        if (b == null) {
+            return;
+        }
+        append(b.buffer,0, b.len);
+    }
+        
+    public void append(char ch) {
+        int newlen = this.len + 1;
+        if (newlen > this.buffer.length) {
+            expand(newlen);
+        }
+        this.buffer[this.len] = ch;
+        this.len = newlen;
+    }
+
+    public void append(final byte[] b, int off, int len) {
+        if (b == null) {
+            return;
+        }
+        if ((off < 0) || (off > b.length) || (len < 0) ||
+                ((off + len) < 0) || ((off + len) > b.length)) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (len == 0) {
+            return;
+        }
+        int oldlen = this.len;
+        int newlen = oldlen + len;
+        if (newlen > this.buffer.length) {
+            expand(newlen);
+        }
+        for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) {
+            int ch = b[i1]; 
+            if (ch < 0) {
+                ch = 256 + ch;
+            }
+            this.buffer[i2] = (char) ch;
+        }
+        this.len = newlen;
+    }
+    
+    public void append(final ByteArrayBuffer b, int off, int len) {
+        if (b == null) {
+            return;
+        }
+        append(b.buffer(), off, len);
+    }
+    
+    public void append(final Object obj) {
+        append(String.valueOf(obj));
+    }
+    
+    public void clear() {
+        this.len = 0;
+    }
+    
+    public char[] toCharArray() {
+        char[] b = new char[this.len]; 
+        if (this.len > 0) {
+            System.arraycopy(this.buffer, 0, b, 0, this.len);
+        }
+        return b;
+    }
+    
+    public char charAt(int i) {
+        return this.buffer[i];
+    }
+    
+    public char[] buffer() {
+        return this.buffer;
+    }
+    
+    public int capacity() {
+        return this.buffer.length;
+    }
+    
+    public int length() {
+        return this.len;
+    }
+
+    public void ensureCapacity(int required) {
+        int available = this.buffer.length - this.len;
+        if (required > available) {
+            expand(this.len + required);
+        }
+    }
+    
+    public void setLength(int len) {
+        if (len < 0 || len > this.buffer.length) {
+            throw new IndexOutOfBoundsException();
+        }
+        this.len = len;
+    }
+    
+    public boolean isEmpty() {
+        return this.len == 0; 
+    }
+    
+    public boolean isFull() {
+        return this.len == this.buffer.length; 
+    }
+    
+    public int indexOf(int ch, int beginIndex, int endIndex) {
+        if (beginIndex < 0) {
+            beginIndex = 0;
+        }
+        if (endIndex > this.len) {
+            endIndex = this.len;
+        }
+        if (beginIndex > endIndex) {
+            return -1;
+        }
+        for (int i = beginIndex; i < endIndex; i++) {
+            if (this.buffer[i] == ch) {
+                return i;
+            }
+        }
+        return -1;
+    }
+    
+    public int indexOf(int ch) {
+        return indexOf(ch, 0, this.len);
+    }
+
+    public String substring(int beginIndex, int endIndex) {
+        if (beginIndex < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (endIndex > this.len) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (beginIndex > endIndex) {
+            throw new IndexOutOfBoundsException();
+        }
+        return new String(this.buffer, beginIndex, endIndex - beginIndex);
+    }
+    
+    public String substringTrimmed(int beginIndex, int endIndex) {
+        if (beginIndex < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (endIndex > this.len) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (beginIndex > endIndex) {
+            throw new IndexOutOfBoundsException();
+        }
+        while (beginIndex < endIndex && HTTP.isWhitespace(this.buffer[beginIndex])) {
+            beginIndex++;
+        }
+        while (endIndex > beginIndex && HTTP.isWhitespace(this.buffer[endIndex - 1])) {
+            endIndex--;
+        }
+        return new String(this.buffer, beginIndex, endIndex - beginIndex);
+    }
+    
+    public String toString() {
+        return new String(this.buffer, 0, this.len);
+    }
+    
+}
diff --git a/src/org/apache/http/util/EncodingUtils.java b/src/org/apache/http/util/EncodingUtils.java
new file mode 100644
index 0000000..a1b3f44
--- /dev/null
+++ b/src/org/apache/http/util/EncodingUtils.java
@@ -0,0 +1,185 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/util/EncodingUtils.java $
+ * $Revision: 503413 $
+ * $Date: 2007-02-04 06:22:14 -0800 (Sun, 04 Feb 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.util;
+
+import java.io.UnsupportedEncodingException;
+
+import org.apache.http.protocol.HTTP;
+
+/**
+ * The home for utility methods that handle various encoding tasks.
+ * 
+ * @author Michael Becke
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public final class EncodingUtils {
+
+    /**
+     * Converts the byte array of HTTP content characters to a string. If
+     * the specified charset is not supported, default system encoding
+     * is used.
+     *
+     * @param data the byte array to be encoded
+     * @param offset the index of the first byte to encode
+     * @param length the number of bytes to encode 
+     * @param charset the desired character encoding
+     * @return The result of the conversion.
+     */
+    public static String getString(
+        final byte[] data, 
+        int offset, 
+        int length, 
+        String charset
+    ) {
+
+        if (data == null) {
+            throw new IllegalArgumentException("Parameter may not be null");
+        }
+
+        if (charset == null || charset.length() == 0) {
+            throw new IllegalArgumentException("charset may not be null or empty");
+        }
+
+        try {
+            return new String(data, offset, length, charset);
+        } catch (UnsupportedEncodingException e) {
+            return new String(data, offset, length);
+        }
+    }
+
+
+    /**
+     * Converts the byte array of HTTP content characters to a string. If
+     * the specified charset is not supported, default system encoding
+     * is used.
+     *
+     * @param data the byte array to be encoded
+     * @param charset the desired character encoding
+     * @return The result of the conversion.
+     */
+    public static String getString(final byte[] data, final String charset) {
+        if (data == null) {
+            throw new IllegalArgumentException("Parameter may not be null");
+        }
+        return getString(data, 0, data.length, charset);
+    }
+
+    /**
+     * Converts the specified string to a byte array.  If the charset is not supported the
+     * default system charset is used.
+     *
+     * @param data the string to be encoded
+     * @param charset the desired character encoding
+     * @return The resulting byte array.
+     */
+    public static byte[] getBytes(final String data, final String charset) {
+
+        if (data == null) {
+            throw new IllegalArgumentException("data may not be null");
+        }
+
+        if (charset == null || charset.length() == 0) {
+            throw new IllegalArgumentException("charset may not be null or empty");
+        }
+
+        try {
+            return data.getBytes(charset);
+        } catch (UnsupportedEncodingException e) {
+            return data.getBytes();
+        }
+    }    
+    
+    /**
+     * Converts the specified string to byte array of ASCII characters.
+     *
+     * @param data the string to be encoded
+     * @return The string as a byte array.
+     */
+    public static byte[] getAsciiBytes(final String data) {
+
+        if (data == null) {
+            throw new IllegalArgumentException("Parameter may not be null");
+        }
+
+        try {
+            return data.getBytes(HTTP.US_ASCII);
+        } catch (UnsupportedEncodingException e) {
+            throw new Error("HttpClient requires ASCII support");
+        }
+    }
+
+    /**
+     * Converts the byte array of ASCII characters to a string. This method is
+     * to be used when decoding content of HTTP elements (such as response
+     * headers)
+     *
+     * @param data the byte array to be encoded
+     * @param offset the index of the first byte to encode
+     * @param length the number of bytes to encode 
+     * @return The string representation of the byte array
+     */
+    public static String getAsciiString(final byte[] data, int offset, int length) {
+
+        if (data == null) {
+            throw new IllegalArgumentException("Parameter may not be null");
+        }
+
+        try {
+            return new String(data, offset, length, HTTP.US_ASCII);
+        } catch (UnsupportedEncodingException e) {
+            throw new Error("HttpClient requires ASCII support");
+        }
+    }
+
+    /**
+     * Converts the byte array of ASCII characters to a string. This method is
+     * to be used when decoding content of HTTP elements (such as response
+     * headers)
+     *
+     * @param data the byte array to be encoded
+     * @return The string representation of the byte array
+     */
+    public static String getAsciiString(final byte[] data) {
+        if (data == null) {
+            throw new IllegalArgumentException("Parameter may not be null");
+        }
+        return getAsciiString(data, 0, data.length);
+    }
+
+    /**
+     * This class should not be instantiated.
+     */
+    private EncodingUtils() {
+    }
+
+}
diff --git a/src/org/apache/http/util/EntityUtils.java b/src/org/apache/http/util/EntityUtils.java
new file mode 100644
index 0000000..f9a7cf3
--- /dev/null
+++ b/src/org/apache/http/util/EntityUtils.java
@@ -0,0 +1,149 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/util/EntityUtils.java $
+ * $Revision: 569637 $
+ * $Date: 2007-08-25 00:36:59 -0700 (Sat, 25 Aug 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+import org.apache.http.HeaderElement;
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.ParseException;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * Static helpers for dealing with {@link HttpEntity entities}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 569637 $
+ * 
+ * @since 4.0
+ */
+public final class EntityUtils {
+
+    /** Disabled default constructor. */
+    private EntityUtils() {
+    }
+    
+    public static byte[] toByteArray(final HttpEntity entity) throws IOException {
+        if (entity == null) {
+            throw new IllegalArgumentException("HTTP entity may not be null");
+        }
+        InputStream instream = entity.getContent();
+        if (instream == null) {
+            return new byte[] {};
+        }
+        if (entity.getContentLength() > Integer.MAX_VALUE) {
+            throw new IllegalArgumentException("HTTP entity too large to be buffered in memory");
+        }
+        int i = (int)entity.getContentLength();
+        if (i < 0) {
+            i = 4096;
+        }
+        ByteArrayBuffer buffer = new ByteArrayBuffer(i);
+        try {
+            byte[] tmp = new byte[4096];
+            int l;
+            while((l = instream.read(tmp)) != -1) {
+                buffer.append(tmp, 0, l);
+            }
+        } finally {
+            instream.close();
+        }
+        return buffer.toByteArray();
+    }
+        
+    public static String getContentCharSet(final HttpEntity entity)
+        throws ParseException {
+
+        if (entity == null) {
+            throw new IllegalArgumentException("HTTP entity may not be null");
+        }
+        String charset = null;
+        if (entity.getContentType() != null) { 
+            HeaderElement values[] = entity.getContentType().getElements();
+            if (values.length > 0) {
+                NameValuePair param = values[0].getParameterByName("charset");
+                if (param != null) {
+                    charset = param.getValue();
+                }
+            }
+        }
+        return charset;
+    }
+
+    public static String toString(
+            final HttpEntity entity, final String defaultCharset) throws IOException, ParseException {
+        if (entity == null) {
+            throw new IllegalArgumentException("HTTP entity may not be null");
+        }
+        InputStream instream = entity.getContent();
+        if (instream == null) {
+            return "";
+        }
+        if (entity.getContentLength() > Integer.MAX_VALUE) {
+            throw new IllegalArgumentException("HTTP entity too large to be buffered in memory");
+        }
+        int i = (int)entity.getContentLength();
+        if (i < 0) {
+            i = 4096;
+        }
+        String charset = getContentCharSet(entity);
+        if (charset == null) {
+            charset = defaultCharset;
+        }
+        if (charset == null) {
+            charset = HTTP.DEFAULT_CONTENT_CHARSET;
+        }
+        Reader reader = new InputStreamReader(instream, charset);
+        CharArrayBuffer buffer = new CharArrayBuffer(i); 
+        try {
+            char[] tmp = new char[1024];
+            int l;
+            while((l = reader.read(tmp)) != -1) {
+                buffer.append(tmp, 0, l);
+            }
+        } finally {
+            reader.close();
+        }
+        return buffer.toString();
+    }
+
+    public static String toString(final HttpEntity entity)
+        throws IOException, ParseException {
+        return toString(entity, null);
+    }
+    
+}
diff --git a/src/org/apache/http/util/ExceptionUtils.java b/src/org/apache/http/util/ExceptionUtils.java
new file mode 100644
index 0000000..c7fdccd
--- /dev/null
+++ b/src/org/apache/http/util/ExceptionUtils.java
@@ -0,0 +1,85 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/util/ExceptionUtils.java $
+ * $Revision: 613003 $
+ * $Date: 2008-01-17 15:00:42 -0800 (Thu, 17 Jan 2008) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.util;
+
+import java.lang.reflect.Method;
+
+/**
+ * The home for utility methods that handle various exception-related tasks.
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:laura@lwerner.org">Laura Werner</a>
+ * 
+ * @since 4.0
+ */
+public final class ExceptionUtils {
+
+    /** A reference to Throwable's initCause method, or null if it's not there in this JVM */
+    static private final Method INIT_CAUSE_METHOD = getInitCauseMethod();
+
+    /**
+     * Returns a <code>Method<code> allowing access to
+     * {@link Throwable#initCause(Throwable) initCause} method of {@link Throwable},
+     * or <code>null</code> if the method
+     * does not exist.
+     * 
+     * @return A <code>Method<code> for <code>Throwable.initCause</code>, or
+     * <code>null</code> if unavailable.
+     */ 
+    static private Method getInitCauseMethod() {
+        try {
+            Class[] paramsClasses = new Class[] { Throwable.class };
+            return Throwable.class.getMethod("initCause", paramsClasses);
+        } catch (NoSuchMethodException e) {
+            return null;
+        }
+    }
+    
+    /** 
+     * If we're running on JDK 1.4 or later, initialize the cause for the given throwable.
+     * 
+     * @param  throwable The throwable.
+     * @param  cause     The cause of the throwable.
+     */
+    public static void initCause(Throwable throwable, Throwable cause) {
+        if (INIT_CAUSE_METHOD != null) {
+            try {
+                INIT_CAUSE_METHOD.invoke(throwable, new Object[] { cause });
+            } catch (Exception e) {
+                // Well, with no logging, the only option is to munch the exception
+            }
+        }
+    }
+
+    private ExceptionUtils() {
+    }
+    
+}
diff --git a/src/org/apache/http/util/LangUtils.java b/src/org/apache/http/util/LangUtils.java
new file mode 100644
index 0000000..a1dee8f
--- /dev/null
+++ b/src/org/apache/http/util/LangUtils.java
@@ -0,0 +1,88 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/util/LangUtils.java $
+ * $Revision: 503413 $
+ * $Date: 2007-02-04 06:22:14 -0800 (Sun, 04 Feb 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.util;
+
+/**
+ * A set of utility methods to help produce consistent
+ * {@link Object#equals equals} and {@link Object#hashCode hashCode} methods.
+ *  
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @since 4.0
+ */
+public final class LangUtils {
+    
+    public static final int HASH_SEED = 17;
+    public static final int HASH_OFFSET = 37;
+
+    /** Disabled default constructor. */
+    private LangUtils() {
+    }
+
+    public static int hashCode(final int seed, final int hashcode) {
+        return seed * HASH_OFFSET + hashcode;
+    }
+
+    public static int hashCode(final int seed, final boolean b) {
+        return hashCode(seed, b ? 1 : 0);
+    }
+    
+    public static int hashCode(final int seed, final Object obj) {
+        return hashCode(seed, obj != null ? obj.hashCode() : 0);
+    }
+    
+    public static boolean equals(final Object obj1, final Object obj2) {
+        return obj1 == null ? obj2 == null : obj1.equals(obj2);
+    }
+
+    public static boolean equals(final Object[] a1, final Object[] a2) {
+        if (a1 == null) {
+            if (a2 == null) {
+                return true;
+            } else {
+                return false;
+            }
+        } else {
+            if (a2 != null && a1.length == a2.length) {
+                for (int i = 0; i < a1.length; i++) {
+                    if (!equals(a1[i], a2[i])) {
+                        return false;
+                    }
+                }
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+    
+}
diff --git a/src/org/apache/http/util/VersionInfo.java b/src/org/apache/http/util/VersionInfo.java
new file mode 100644
index 0000000..0c95594
--- /dev/null
+++ b/src/org/apache/http/util/VersionInfo.java
@@ -0,0 +1,317 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/util/VersionInfo.java $
+ * $Revision: 554888 $
+ * $Date: 2007-07-10 02:46:36 -0700 (Tue, 10 Jul 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Properties;
+import java.util.ArrayList;
+
+
+/**
+ * Provides access to version information for HTTP components.
+ * Instances of this class provide version information for a single module
+ * or informal unit, as explained
+ * <a href="http://wiki.apache.org/jakarta-httpclient/HttpComponents">here</a>.
+ * Static methods are used to extract version information from property
+ * files that are automatically packaged with HTTP component release JARs.
+ * <br/>
+ * All available version information is provided in strings, where
+ * the string format is informal and subject to change without notice.
+ * Version information is provided for debugging output and interpretation
+ * by humans, not for automated processing in applications.
+ *
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ * @author and others
+ */
+public class VersionInfo {
+
+    /** A string constant for unavailable information. */
+    public final static String UNAVAILABLE = "UNAVAILABLE";
+
+    /** The filename of the version information files. */
+    public final static String VERSION_PROPERTY_FILE = "version.properties";
+
+    // the property names
+    public final static String PROPERTY_MODULE    = "info.module";
+    public final static String PROPERTY_RELEASE   = "info.release";
+    public final static String PROPERTY_TIMESTAMP = "info.timestamp";
+
+
+    /** The package that contains the version information. */
+    private final String infoPackage;
+
+    /** The module from the version info. */
+    private final String infoModule;
+
+    /** The release from the version info. */
+    private final String infoRelease;
+
+    /** The timestamp from the version info. */
+    private final String infoTimestamp;
+
+    /** The classloader from which the version info was obtained. */
+    private final String infoClassloader;
+
+
+    /**
+     * Instantiates version information.
+     *
+     * @param pckg      the package
+     * @param module    the module, or <code>null</code>
+     * @param release   the release, or <code>null</code>
+     * @param time      the build time, or <code>null</code>
+     * @param clsldr    the class loader, or <code>null</code>
+     */
+    protected VersionInfo(String pckg, String module,
+                          String release, String time, String clsldr) {
+        if (pckg == null) {
+            throw new IllegalArgumentException
+                ("Package identifier must not be null.");
+        }
+
+        infoPackage     = pckg;
+        infoModule      = (module  != null) ? module  : UNAVAILABLE;
+        infoRelease     = (release != null) ? release : UNAVAILABLE;
+        infoTimestamp   = (time    != null) ? time    : UNAVAILABLE;
+        infoClassloader = (clsldr  != null) ? clsldr  : UNAVAILABLE;
+    }
+
+
+    /**
+     * Obtains the package name.
+     * The package name identifies the module or informal unit.
+     *
+     * @return  the package name, never <code>null</code>
+     */
+    public final String getPackage() {
+        return infoPackage;
+    }
+
+    /**
+     * Obtains the name of the versioned module or informal unit.
+     * This data is read from the version information for the package.
+     *
+     * @return  the module name, never <code>null</code>
+     */
+    public final String getModule() {
+        return infoModule;
+    }
+
+    /**
+     * Obtains the release of the versioned module or informal unit.
+     * This data is read from the version information for the package.
+     *
+     * @return  the release version, never <code>null</code>
+     */
+    public final String getRelease() {
+        return infoRelease;
+    }
+
+    /**
+     * Obtains the timestamp of the versioned module or informal unit.
+     * This data is read from the version information for the package.
+     *
+     * @return  the timestamp, never <code>null</code>
+     */
+    public final String getTimestamp() {
+        return infoTimestamp;
+    }
+
+    /**
+     * Obtains the classloader used to read the version information.
+     * This is just the <code>toString</code> output of the classloader,
+     * since the version information should not keep a reference to
+     * the classloader itself. That could prevent garbage collection.
+     *
+     * @return  the classloader description, never <code>null</code>
+     */
+    public final String getClassloader() {
+        return infoClassloader;
+    }
+
+
+    /**
+     * Provides the version information in human-readable format.
+     *
+     * @return  a string holding this version information
+     */
+    public String toString() {
+        StringBuffer sb = new StringBuffer
+            (20 + infoPackage.length() + infoModule.length() +
+             infoRelease.length() + infoTimestamp.length() +
+             infoClassloader.length());
+
+        sb.append("VersionInfo(")
+            .append(infoPackage).append(':').append(infoModule);
+
+        // If version info is missing, a single "UNAVAILABLE" for the module
+        // is sufficient. Everything else just clutters the output.
+        if (!UNAVAILABLE.equals(infoRelease))
+            sb.append(':').append(infoRelease);
+        if (!UNAVAILABLE.equals(infoTimestamp))
+            sb.append(':').append(infoTimestamp);
+
+        sb.append(')');
+
+        if (!UNAVAILABLE.equals(infoClassloader))
+            sb.append('@').append(infoClassloader);
+
+        return sb.toString();
+    }
+
+
+    /**
+     * Loads version information for a list of packages.
+     *
+     * @param pckgs     the packages for which to load version info
+     * @param clsldr    the classloader to load from, or
+     *                  <code>null</code> for the thread context classloader
+     *
+     * @return  the version information for all packages found,
+     *          never <code>null</code>
+     */
+    public final static VersionInfo[] loadVersionInfo(String[] pckgs,
+                                                      ClassLoader clsldr) {
+        if (pckgs == null) {
+            throw new IllegalArgumentException
+                ("Package identifier list must not be null.");
+        }
+
+        ArrayList vil = new ArrayList(pckgs.length);
+        for (int i=0; i<pckgs.length; i++) {
+            VersionInfo vi = loadVersionInfo(pckgs[i], clsldr);
+            if (vi != null)
+                vil.add(vi);
+        }
+
+        return (VersionInfo[]) vil.toArray(new VersionInfo[vil.size()]);
+    }
+
+
+    /**
+     * Loads version information for a package.
+     *
+     * @param pckg      the package for which to load version information,
+     *                  for example "org.apache.http".
+     *                  The package name should NOT end with a dot.
+     * @param clsldr    the classloader to load from, or
+     *                  <code>null</code> for the thread context classloader
+     *
+     * @return  the version information for the argument package, or
+     *          <code>null</code> if not available
+     */
+    public final static VersionInfo loadVersionInfo(final String pckg,
+                                                    ClassLoader clsldr) {
+        if (pckg == null) {
+            throw new IllegalArgumentException
+                ("Package identifier must not be null.");
+        }
+
+        if (clsldr == null)
+            clsldr = Thread.currentThread().getContextClassLoader();
+
+        Properties vip = null; // version info properties, if available
+        try {
+            // org.apache.http      becomes
+            // org/apache/http/version.properties
+            InputStream is = clsldr.getResourceAsStream
+                (pckg.replace('.', '/') + "/" + VERSION_PROPERTY_FILE);
+            if (is != null) {
+                try {
+                    Properties props = new Properties();
+                    props.load(is);
+                    vip = props;
+                } finally {
+                    is.close();
+                }
+            }
+        } catch (IOException ex) {
+            // shamelessly munch this exception
+        }
+
+        VersionInfo result = null;
+        if (vip != null)
+            result = fromMap(pckg, vip, clsldr);
+
+        return result;
+    }
+
+
+    /**
+     * Instantiates version information from properties.
+     *
+     * @param pckg      the package for the version information
+     * @param info      the map from string keys to string values,
+     *                  for example {@link java.util.Properties}
+     * @param clsldr    the classloader, or <code>null</code>
+     *
+     * @return  the version information
+     */
+    protected final static VersionInfo fromMap(String pckg, Map info,
+                                               ClassLoader clsldr) {
+        if (pckg == null) {
+            throw new IllegalArgumentException
+                ("Package identifier must not be null.");
+        }
+
+        String module = null;
+        String release = null;
+        String timestamp = null;
+
+        if (info != null) {
+            module = (String) info.get(PROPERTY_MODULE);
+            if ((module != null) && (module.length() < 1))
+                module = null;
+
+            release = (String) info.get(PROPERTY_RELEASE);
+            if ((release != null) && ((release.length() < 1) ||
+                                      (release.equals("${pom.version}"))))
+                release = null;
+
+            timestamp = (String) info.get(PROPERTY_TIMESTAMP);
+            if ((timestamp != null) &&
+                ((timestamp.length() < 1) ||
+                 (timestamp.equals("${mvn.timestamp}")))
+                )
+                timestamp = null;
+        } // if info
+
+        String clsldrstr = null;
+        if (clsldr != null)
+            clsldrstr = clsldr.toString();
+
+        return new VersionInfo(pckg, module, release, timestamp, clsldrstr);
+    }
+
+} // class VersionInfo
diff --git a/src/org/apache/http/util/package.html b/src/org/apache/http/util/package.html
new file mode 100644
index 0000000..19d97b3
--- /dev/null
+++ b/src/org/apache/http/util/package.html
@@ -0,0 +1,45 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/util/package.html $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Mostly utility classes with static helper methods for various purposes.
+
+The helper classes for resizable
+{@link org.apache.http.util.ByteArrayBuffer byte} and
+{@link org.apache.http.util.CharArrayBuffer char} arrays
+do not fall into this category.
+
+</body>
+</html>