HttpClient 4.4 RC1 tag

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/tags/4.4-RC1@1654497 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/NOTICE.txt b/NOTICE.txt
index 6ce1939..6a8629b 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -1,5 +1,5 @@
 Apache HttpComponents Client
-Copyright 1999-2014 The Apache Software Foundation
+Copyright 1999-2015 The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt
index 67b26a0..142e1e7 100644
--- a/RELEASE_NOTES.txt
+++ b/RELEASE_NOTES.txt
@@ -1,3 +1,79 @@
+Release 4.4 Final
+-------------------
+
+This is the first stable (GA) release of HttpClient 4.4. Notable features and enhancements included
+in 4.4 series are:
+
+* Support for the latest HTTP state management specification (RFC 6265). Please note that the old 
+cookie policy is still used by default for compatibility reasons. RFC 6265 compliant cookie 
+policies need to be explicitly configured by the user. Please also note that as of next feature 
+release support for Netscape draft, RFC 2109 and RFC 2965 cookie policies will be deprecated 
+and disabled by default. It is recommended to use RFC 6265 compliant policies for new applications 
+unless compatibility with RFC 2109 and RFC 2965 is required and to migrate existing applications 
+to the default cookie policy.
+
+* Enhanced, redesigned and rewritten default SSL hostname verifier with improved RFC 2818
+compliance
+
+* Default SSL hostname verifier and default cookie policy now validate certificate identity 
+and cookie domain of origin against the public suffix list maintained by Mozilla.org
+<https://publicsuffix.org/list>
+
+* More efficient stale connection checking: indiscriminate connection checking which results
+in approximately 20 to 50 ms overhead per request has been deprecated in favor of conditional
+connection state validation (persistent connections are to be re-validated only if a specified
+period inactivity has elapsed)
+
+* Authentication cache thread-safety: authentication cache used by HttpClient is now thread-safe
+and can be shared by multiple threads in order to re-use authentication state for subsequent
+requests
+
+* Native Windows Negotiate and NTLM via SSPI through JNA: when running on Windows OS HttpClient
+configured to use native NTLM or SPNEGO authentication schemes can make use of platform specific
+functionality via JNA and current user credentials. This functionality is still considered
+experimental, known to have compatibility issues and subject to change without prior notice.
+Use at your discretion.
+
+This release also includes all fixes from the stable 4.3.x release branch.
+
+Please note that as of 4.4 HttpClient requires Java 1.6 or newer.
+
+
+Changelog:
+-------------------
+
+* Support for the latest HTTP state management specification (RFC 6265).
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1515] Caching of responses to HEAD requests
+  Contributed by Tyrone Cutajar <tj.cutajar at gmail.com> and 
+  Francois-Xavier Bonnet <fx at apache.org> 
+
+* [HTTPCLIENT-1560] Native Windows auth improvements
+  Contributed by Michael Osipov <michaelo at apache.org>
+
+* Update Apache Commons Logging version from 1.1.3 to 1.2. 
+  Contributed by Gary Gregory <ggregory at apache.org>
+
+* Update Apache Commons Codec version from 1.6 to 1.9. 
+  Contributed by Gary Gregory <ggregory at apache.org>
+
+* Update Ehcache version from 2.2.0 to 2.6.9. 
+  Contributed by Gary Gregory <ggregory at apache.org>
+
+* Update Ehcache version from 2.2.0 to 2.6.9. 
+  Contributed by Gary Gregory <ggregory at apache.org>
+
+* Update Spymemcached version from 2.6 to 2.11.4. 
+  Contributed by Gary Gregory <ggregory at apache.org>
+
+* Update SLF4J version from 1.5.11 to 1.7.7. 
+  Contributed by Gary Gregory <ggregory at apache.org>
+
+
+
+
+
 Release 4.4 BETA1
 -------------------
 
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/Content.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/Content.java
index 5d752e1..2be4c3f 100644
--- a/fluent-hc/src/main/java/org/apache/http/client/fluent/Content.java
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/Content.java
@@ -33,6 +33,10 @@
 import org.apache.http.Consts;
 import org.apache.http.entity.ContentType;
 
+/**
+ * This class represents arbitrary content of a specfic type that can be consumed
+ * multiple times and requires no explicit deallocation.
+ */
 public class Content {
 
     public static final Content NO_CONTENT = new Content(new byte[] {}, ContentType.DEFAULT_BINARY);
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/ContentResponseHandler.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/ContentResponseHandler.java
index 206bd87..ebcbccc 100644
--- a/fluent-hc/src/main/java/org/apache/http/client/fluent/ContentResponseHandler.java
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/ContentResponseHandler.java
@@ -29,31 +29,26 @@
 import java.io.IOException;
 
 import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.StatusLine;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.HttpResponseException;
-import org.apache.http.client.ResponseHandler;
 import org.apache.http.entity.ContentType;
+import org.apache.http.impl.client.AbstractResponseHandler;
 import org.apache.http.util.EntityUtils;
 
-class ContentResponseHandler implements ResponseHandler<Content> {
+/**
+ * {@link org.apache.http.client.ResponseHandler} implementation that converts
+ * {@link org.apache.http.HttpResponse} messages to {@link org.apache.http.client.fluent.Content}
+ * instances.
+ *
+ * @see org.apache.http.client.fluent.Content
+ *
+ * @since 4.4
+ */
+public class ContentResponseHandler extends AbstractResponseHandler<Content> {
 
     @Override
-    public Content handleResponse(
-            final HttpResponse response) throws ClientProtocolException, IOException {
-        final StatusLine statusLine = response.getStatusLine();
-        final HttpEntity entity = response.getEntity();
-        if (statusLine.getStatusCode() >= 300) {
-            throw new HttpResponseException(statusLine.getStatusCode(),
-                    statusLine.getReasonPhrase());
-        }
-        if (entity != null) {
-            return new Content(
-                    EntityUtils.toByteArray(entity),
-                    ContentType.getOrDefault(entity));
-        }
-        return Content.NO_CONTENT;
+    public Content handleEntity(final HttpEntity entity) throws IOException {
+        return entity != null ?
+                new Content(EntityUtils.toByteArray(entity), ContentType.getOrDefault(entity)) :
+                Content.NO_CONTENT;
     }
 
 }
diff --git a/httpclient-cache/pom.xml b/httpclient-cache/pom.xml
index f9e74c3..75b83f7 100644
--- a/httpclient-cache/pom.xml
+++ b/httpclient-cache/pom.xml
@@ -64,7 +64,7 @@
       <optional>true</optional>
     </dependency>
     <dependency>
-      <groupId>spy</groupId>
+      <groupId>net.spy</groupId>
       <artifactId>spymemcached</artifactId>
       <scope>compile</scope>
       <optional>true</optional>
@@ -124,16 +124,16 @@
         </executions>
       </plugin>
       <plugin>
-       <groupId>org.apache.maven.plugins</groupId>
-       <artifactId>maven-jar-plugin</artifactId>
-       <executions>
-         <execution>
-           <goals>
-             <goal>test-jar</goal>
-           </goals>
-         </execution>
-       </executions>
-     </plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
     </plugins>
   </build>
 
@@ -142,7 +142,7 @@
 
       <plugin>
         <artifactId>maven-javadoc-plugin</artifactId>
-         <version>${hc.javadoc.version}</version>
+        <version>${hc.javadoc.version}</version>
         <configuration>
           <!-- reduce console output. Can override with -Dquiet=false -->
           <quiet>true</quiet>
diff --git a/httpclient-cache/src/main/java-deprecated/org/apache/http/impl/client/cache/CachingHttpClient.java b/httpclient-cache/src/main/java-deprecated/org/apache/http/impl/client/cache/CachingHttpClient.java
index 1c154fd..a54ff19 100644
--- a/httpclient-cache/src/main/java-deprecated/org/apache/http/impl/client/cache/CachingHttpClient.java
+++ b/httpclient-cache/src/main/java-deprecated/org/apache/http/impl/client/cache/CachingHttpClient.java
@@ -595,7 +595,7 @@
                 || request.containsHeader(HeaderConstants.IF_MODIFIED_SINCE)) {
             cachedResponse = responseGenerator.generateNotModifiedResponse(entry);
         } else {
-            cachedResponse = responseGenerator.generateResponse(entry);
+            cachedResponse = responseGenerator.generateResponse(request, entry);
         }
         setResponseStatus(context, CacheResponseStatus.CACHE_HIT);
         if (validityPolicy.getStalenessSecs(entry, now) > 0L) {
@@ -609,7 +609,7 @@
         if (staleResponseNotAllowed(request, entry, now)) {
             return generateGatewayTimeout(context);
         } else {
-            return unvalidatedCacheHit(context, entry);
+            return unvalidatedCacheHit(request, context, entry);
         }
     }
 
@@ -619,9 +619,11 @@
                 HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout");
     }
 
-    private HttpResponse unvalidatedCacheHit(final HttpContext context,
+    private HttpResponse unvalidatedCacheHit(
+            final HttpRequestWrapper request,
+            final HttpContext context,
             final HttpCacheEntry entry) {
-        final HttpResponse cachedResponse = responseGenerator.generateResponse(entry);
+        final HttpResponse cachedResponse = responseGenerator.generateResponse(request, entry);
         setResponseStatus(context, CacheResponseStatus.CACHE_HIT);
         cachedResponse.addHeader(HeaderConstants.WARNING, "111 localhost \"Revalidation failed\"");
         return cachedResponse;
@@ -819,7 +821,7 @@
                 conditionalRequest, requestDate, responseDate, backendResponse,
                 matchingVariant, matchedEntry);
 
-        final HttpResponse resp = responseGenerator.generateResponse(responseEntry);
+        final HttpResponse resp = responseGenerator.generateResponse(request, responseEntry);
         tryToUpdateVariantMap(target, request, matchingVariant);
 
         if (shouldSendNotModifiedResponse(request, responseEntry)) {
@@ -901,13 +903,13 @@
                     && suitabilityChecker.allConditionalsMatch(request, updatedEntry, new Date())) {
                 return responseGenerator.generateNotModifiedResponse(updatedEntry);
             }
-            return responseGenerator.generateResponse(updatedEntry);
+            return responseGenerator.generateResponse(request, updatedEntry);
         }
 
         if (staleIfErrorAppliesTo(statusCode)
             && !staleResponseNotAllowed(request, cacheEntry, getCurrentDate())
             && validityPolicy.mayReturnStaleIfError(request, cacheEntry, responseDate)) {
-            final HttpResponse cachedResponse = responseGenerator.generateResponse(cacheEntry);
+            final HttpResponse cachedResponse = responseGenerator.generateResponse(request, cacheEntry);
             cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
             final HttpEntity errorBody = backendResponse.getEntity();
             if (errorBody != null) {
diff --git a/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheEntry.java b/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheEntry.java
index 38ef49f..d8bf81b 100644
--- a/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheEntry.java
+++ b/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheEntry.java
@@ -33,6 +33,7 @@
 import java.util.Map;
 
 import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
 import org.apache.http.ProtocolVersion;
 import org.apache.http.StatusLine;
 import org.apache.http.annotation.Immutable;
@@ -54,6 +55,7 @@
 public class HttpCacheEntry implements Serializable {
 
     private static final long serialVersionUID = -6300496422359477413L;
+    private static final String REQUEST_METHOD_HEADER_NAME = "Hc-Request-Method";
 
     private final Date requestDate;
     private final Date responseDate;
@@ -80,6 +82,7 @@
      *   of this parent entry; this maps a "variant key" (derived
      *   from the varying request headers) to a "cache key" (where
      *   in the cache storage the particular variant is located)
+     * @param requestMethod HTTP method used when the request was made
      */
     public HttpCacheEntry(
             final Date requestDate,
@@ -87,7 +90,8 @@
             final StatusLine statusLine,
             final Header[] responseHeaders,
             final Resource resource,
-            final Map<String,String> variantMap) {
+            final Map<String,String> variantMap,
+            final String requestMethod) {
         super();
         Args.notNull(requestDate, "Request date");
         Args.notNull(responseDate, "Response date");
@@ -106,6 +110,35 @@
     }
 
     /**
+     * Create a new {@link HttpCacheEntry} with variants.
+     * @param requestDate
+     *          Date/time when the request was made (Used for age
+     *            calculations)
+     * @param responseDate
+     *          Date/time that the response came back (Used for age
+     *            calculations)
+     * @param statusLine
+     *          HTTP status line from origin response
+     * @param responseHeaders
+     *          Header[] from original HTTP Response
+     * @param resource representing origin response body
+     * @param variantMap describing cache entries that are variants
+     *   of this parent entry; this maps a "variant key" (derived
+     *   from the varying request headers) to a "cache key" (where
+     *   in the cache storage the particular variant is located)
+     */
+    public HttpCacheEntry(
+            final Date requestDate,
+            final Date responseDate,
+            final StatusLine statusLine,
+            final Header[] responseHeaders,
+            final Resource resource,
+            final Map<String,String> variantMap) {
+        this(requestDate, responseDate, statusLine, responseHeaders, resource,
+                variantMap, null);
+    }
+
+    /**
      * Create a new {@link HttpCacheEntry}.
      *
      * @param requestDate
@@ -127,6 +160,28 @@
     }
 
     /**
+     * Create a new {@link HttpCacheEntry}.
+     *
+     * @param requestDate
+     *          Date/time when the request was made (Used for age
+     *            calculations)
+     * @param responseDate
+     *          Date/time that the response came back (Used for age
+     *            calculations)
+     * @param statusLine
+     *          HTTP status line from origin response
+     * @param responseHeaders
+     *          Header[] from original HTTP Response
+     * @param resource representing origin response body
+     * @param requestMethod HTTP method used when the request was made
+     */
+    public HttpCacheEntry(final Date requestDate, final Date responseDate, final StatusLine statusLine,
+            final Header[] responseHeaders, final Resource resource, final String requestMethod) {
+        this(requestDate, responseDate, statusLine, responseHeaders, resource,
+                new HashMap<String,String>(),requestMethod);
+    }
+
+    /**
      * Find the "Date" response header and parse it into a java.util.Date
      * @return the Date value of the header or null if the header is not present
      */
@@ -191,7 +246,15 @@
      * Returns all the headers that were on the origin response.
      */
     public Header[] getAllHeaders() {
-        return responseHeaders.getAllHeaders();
+        final HeaderGroup filteredHeaders = new HeaderGroup();
+        for (final HeaderIterator iterator = responseHeaders.iterator(); iterator
+                .hasNext();) {
+            final Header header = (Header) iterator.next();
+            if (!REQUEST_METHOD_HEADER_NAME.equals(header.getName())) {
+                filteredHeaders.addHeader(header);
+            }
+        }
+        return filteredHeaders.getAllHeaders();
     }
 
     /**
@@ -199,6 +262,9 @@
      * name.
      */
     public Header getFirstHeader(final String name) {
+        if (REQUEST_METHOD_HEADER_NAME.equalsIgnoreCase(name)) {
+            return null;
+        }
         return responseHeaders.getFirstHeader(name);
     }
 
@@ -207,6 +273,9 @@
      * response.
      */
     public Header[] getHeaders(final String name) {
+        if (REQUEST_METHOD_HEADER_NAME.equalsIgnoreCase(name)) {
+            return new Header[0];
+        }
         return responseHeaders.getHeaders(name);
     }
 
@@ -251,6 +320,21 @@
     }
 
     /**
+     * Returns the HTTP request method that was used to create the cached
+     * response entry.
+     *
+     * @since 4.4
+     */
+    public String getRequestMethod() {
+        final Header requestMethodHeader = responseHeaders
+                .getFirstHeader(REQUEST_METHOD_HEADER_NAME);
+        if (requestMethodHeader != null) {
+            return requestMethodHeader.getValue();
+        }
+        return HeaderConstants.GET_METHOD;
+    }
+
+    /**
      * Provides a string representation of this instance suitable for
      * human consumption.
      */
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java
index 91e692d..2a03f60 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java
@@ -51,6 +51,7 @@
 import org.apache.http.client.cache.Resource;
 import org.apache.http.client.cache.ResourceFactory;
 import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpRequestWrapper;
 import org.apache.http.entity.ByteArrayEntity;
 import org.apache.http.message.BasicHttpResponse;
 import org.apache.http.protocol.HTTP;
@@ -208,6 +209,9 @@
         } catch (final NumberFormatException nfe) {
             return false;
         }
+        if (resource == null) {
+            return false;
+        }
         return (resource.length() < contentLength);
     }
 
@@ -219,7 +223,7 @@
         error.setHeader("Content-Type","text/plain;charset=UTF-8");
         final String msg = String.format("Received incomplete response " +
                 "with Content-Length %d but actual body length %d",
-                contentLength, Long.valueOf(resource.length()));
+                contentLength, resource.length());
         final byte[] msgBytes = msg.getBytes();
         error.setHeader("Content-Length", Integer.toString(msgBytes.length));
         error.setEntity(new ByteArrayEntity(msgBytes));
@@ -249,7 +253,8 @@
                 src.getStatusLine(),
                 src.getAllHeaders(),
                 resource,
-                variantMap);
+                variantMap,
+                src.getRequestMethod());
     }
 
     @Override
@@ -317,9 +322,10 @@
                     responseReceived,
                     originResponse.getStatusLine(),
                     originResponse.getAllHeaders(),
-                    resource);
+                    resource,
+                    request.getRequestLine().getMethod());
             storeInCache(host, request, entry);
-            return responseGenerator.generateResponse(entry);
+            return responseGenerator.generateResponse(HttpRequestWrapper.wrap(request, host), entry);
         } finally {
             if (closeOriginResponse) {
                 originResponse.close();
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheEntryUpdater.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheEntryUpdater.java
index ffc528b..4801b9d 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheEntryUpdater.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheEntryUpdater.java
@@ -96,7 +96,8 @@
                 responseDate,
                 entry.getStatusLine(),
                 mergedHeaders,
-                resource);
+                resource,
+                entry.getRequestMethod());
     }
 
     protected Header[] mergeHeaders(final HttpCacheEntry entry, final HttpResponse response) {
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheInvalidator.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheInvalidator.java
index 7413372..57c2d91 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheInvalidator.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheInvalidator.java
@@ -82,15 +82,11 @@
      */
     @Override
     public void flushInvalidatedCacheEntries(final HttpHost host, final HttpRequest req)  {
-        if (requestShouldNotBeCached(req)) {
-            log.debug("Request should not be cached");
+        final String theUri = cacheKeyGenerator.getURI(host, req);
+        final HttpCacheEntry parent = getEntry(theUri);
 
-            final String theUri = cacheKeyGenerator.getURI(host, req);
-
-            final HttpCacheEntry parent = getEntry(theUri);
-
-            log.debug("parent entry: " + parent);
-
+        if (requestShouldNotBeCached(req) || shouldInvalidateHeadCacheEntry(req, parent)) {
+            log.debug("Invalidating parent cache entry: " + parent);
             if (parent != null) {
                 for (final String variantURI : parent.getVariantMap().values()) {
                     flushEntry(variantURI);
@@ -116,6 +112,18 @@
         }
     }
 
+    private boolean shouldInvalidateHeadCacheEntry(final HttpRequest req, final HttpCacheEntry parentCacheEntry) {
+        return requestIsGet(req) && isAHeadCacheEntry(parentCacheEntry);
+    }
+
+    private boolean requestIsGet(final HttpRequest req) {
+        return req.getRequestLine().getMethod().equals((HeaderConstants.GET_METHOD));
+    }
+
+    private boolean isAHeadCacheEntry(final HttpCacheEntry parentCacheEntry) {
+        return parentCacheEntry != null && parentCacheEntry.getRequestMethod().equals(HeaderConstants.HEAD_METHOD);
+    }
+
     private void flushEntry(final String uri) {
         try {
             storage.removeEntry(uri);
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheValidityPolicy.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheValidityPolicy.java
index 05aa493..7690e8a 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheValidityPolicy.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheValidityPolicy.java
@@ -214,7 +214,8 @@
      * @return boolean indicating whether actual length matches Content-Length
      */
     protected boolean contentLengthHeaderMatchesActualLength(final HttpCacheEntry entry) {
-        return !hasContentLengthHeader(entry) || getContentLengthValue(entry) == entry.getResource().length();
+        return !hasContentLengthHeader(entry) ||
+                (entry.getResource() != null && getContentLengthValue(entry) == entry.getResource().length());
     }
 
     protected long getApparentAgeSecs(final HttpCacheEntry entry) {
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheableRequestPolicy.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheableRequestPolicy.java
index f10ff3a..d787703 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheableRequestPolicy.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheableRequestPolicy.java
@@ -62,8 +62,9 @@
             return false;
         }
 
-        if (!method.equals(HeaderConstants.GET_METHOD)) {
-            log.trace("non-GET request was not serveable from cache");
+        if (!(method.equals(HeaderConstants.GET_METHOD) || method
+                .equals(HeaderConstants.HEAD_METHOD))) {
+            log.trace("non-GET or non-HEAD request was not serveable from cache");
             return false;
         }
 
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedHttpResponseGenerator.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedHttpResponseGenerator.java
index 523c28f..f74a2e4 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedHttpResponseGenerator.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedHttpResponseGenerator.java
@@ -37,6 +37,7 @@
 import org.apache.http.client.cache.HeaderConstants;
 import org.apache.http.client.cache.HttpCacheEntry;
 import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpRequestWrapper;
 import org.apache.http.client.utils.DateUtils;
 import org.apache.http.message.BasicHeader;
 import org.apache.http.message.BasicHttpResponse;
@@ -64,19 +65,18 @@
     /**
      * If I was able to use a {@link CacheEntity} to response to the {@link org.apache.http.HttpRequest} then
      * generate an {@link HttpResponse} based on the cache entry.
-     * @param entry
-     *            {@link CacheEntity} to transform into an {@link HttpResponse}
+     * @param request {@link HttpRequestWrapper} to generate the response for
+     * @param entry {@link CacheEntity} to transform into an {@link HttpResponse}
      * @return {@link HttpResponse} that was constructed
      */
-    CloseableHttpResponse generateResponse(final HttpCacheEntry entry) {
-
+    CloseableHttpResponse generateResponse(final HttpRequestWrapper request, final HttpCacheEntry entry) {
         final Date now = new Date();
         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, entry
                 .getStatusCode(), entry.getReasonPhrase());
 
         response.setHeaders(entry.getAllHeaders());
 
-        if (entry.getResource() != null) {
+        if (responseShouldContainEntity(request, entry)) {
             final HttpEntity entity = new CacheEntity(entry);
             addMissingContentLengthHeader(response, entity);
             response.setEntity(entity);
@@ -163,4 +163,10 @@
         final Header hdr = response.getFirstHeader(HTTP.TRANSFER_ENCODING);
         return hdr != null;
     }
+
+    private boolean responseShouldContainEntity(final HttpRequestWrapper request, final HttpCacheEntry cacheEntry) {
+        return request.getRequestLine().getMethod().equals(HeaderConstants.GET_METHOD) &&
+               cacheEntry.getResource() != null;
+    }
+
 }
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedResponseSuitabilityChecker.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedResponseSuitabilityChecker.java
index 7391a72..e7c940c 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedResponseSuitabilityChecker.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedResponseSuitabilityChecker.java
@@ -143,13 +143,12 @@
      * @return boolean yes/no answer
      */
     public boolean canCachedResponseBeUsed(final HttpHost host, final HttpRequest request, final HttpCacheEntry entry, final Date now) {
-
         if (!isFreshEnough(entry, request, now)) {
             log.trace("Cache entry was not fresh enough");
             return false;
         }
 
-        if (!validityStrategy.contentLengthHeaderMatchesActualLength(entry)) {
+        if (isGet(request) && !validityStrategy.contentLengthHeaderMatchesActualLength(entry)) {
             log.debug("Cache entry Content-Length and header information do not match");
             return false;
         }
@@ -160,13 +159,19 @@
         }
 
         if (!isConditional(request) && entry.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
-        return false;
+            return false;
         }
 
         if (isConditional(request) && !allConditionalsMatch(request, entry, now)) {
             return false;
         }
 
+        if (hasUnsupportedCacheEntryForGet(request, entry)) {
+            log.debug("HEAD response caching enabled but the cache entry does not contain a " +
+                      "request method, entity or a 204 response");
+            return false;
+        }
+
         for (final Header ccHdr : request.getHeaders(HeaderConstants.CACHE_CONTROL)) {
             for (final HeaderElement elt : ccHdr.getElements()) {
                 if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equals(elt.getName())) {
@@ -233,6 +238,22 @@
         return true;
     }
 
+    private boolean isGet(final HttpRequest request) {
+        return request.getRequestLine().getMethod().equals(HeaderConstants.GET_METHOD);
+    }
+
+    private boolean entryIsNotA204Response(final HttpCacheEntry entry) {
+        return entry.getStatusCode() != HttpStatus.SC_NO_CONTENT;
+    }
+
+    private boolean cacheEntryDoesNotContainMethodAndEntity(final HttpCacheEntry entry) {
+        return entry.getRequestMethod() == null && entry.getResource() == null;
+    }
+
+    private boolean hasUnsupportedCacheEntryForGet(final HttpRequest request, final HttpCacheEntry entry) {
+        return isGet(request) && cacheEntryDoesNotContainMethodAndEntity(entry) && entryIsNotA204Response(entry);
+    }
+
     /**
      * Is this request the type of conditional request we support?
      * @param request The current httpRequest being made
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java
index 822c340..d9c63dd 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java
@@ -430,7 +430,7 @@
                 || request.containsHeader(HeaderConstants.IF_MODIFIED_SINCE)) {
             cachedResponse = responseGenerator.generateNotModifiedResponse(entry);
         } else {
-            cachedResponse = responseGenerator.generateResponse(entry);
+            cachedResponse = responseGenerator.generateResponse(request, entry);
         }
         setResponseStatus(context, CacheResponseStatus.CACHE_HIT);
         if (validityPolicy.getStalenessSecs(entry, now) > 0L) {
@@ -447,7 +447,7 @@
         if (staleResponseNotAllowed(request, entry, now)) {
             return generateGatewayTimeout(context);
         } else {
-            return unvalidatedCacheHit(context, entry);
+            return unvalidatedCacheHit(request, context, entry);
         }
     }
 
@@ -460,8 +460,10 @@
     }
 
     private CloseableHttpResponse unvalidatedCacheHit(
-            final HttpContext context, final HttpCacheEntry entry) {
-        final CloseableHttpResponse cachedResponse = responseGenerator.generateResponse(entry);
+            final HttpRequestWrapper request,
+            final HttpContext context,
+            final HttpCacheEntry entry) {
+        final CloseableHttpResponse cachedResponse = responseGenerator.generateResponse(request, entry);
         setResponseStatus(context, CacheResponseStatus.CACHE_HIT);
         cachedResponse.addHeader(HeaderConstants.WARNING, "111 localhost \"Revalidation failed\"");
         return cachedResponse;
@@ -526,8 +528,8 @@
         final String release = (vi != null) ? vi.getRelease() : VersionInfo.UNAVAILABLE;
 
         String value;
-        final Integer major = Integer.valueOf(pv.getMajor());
-        final Integer minor = Integer.valueOf(pv.getMinor());
+        final int major = pv.getMajor();
+        final int minor = pv.getMinor();
         if ("http".equalsIgnoreCase(pv.getProtocol())) {
             value = String.format("%d.%d localhost (Apache-HttpClient/%s (cache))", major, minor,
                     release);
@@ -676,7 +678,7 @@
                     backendResponse, matchingVariant, matchedEntry);
             backendResponse.close();
 
-            final CloseableHttpResponse resp = responseGenerator.generateResponse(responseEntry);
+            final CloseableHttpResponse resp = responseGenerator.generateResponse(request, responseEntry);
             tryToUpdateVariantMap(context.getTargetHost(), request, matchingVariant);
 
             if (shouldSendNotModifiedResponse(request, responseEntry)) {
@@ -788,14 +790,14 @@
                 return responseGenerator
                         .generateNotModifiedResponse(updatedEntry);
             }
-            return responseGenerator.generateResponse(updatedEntry);
+            return responseGenerator.generateResponse(request, updatedEntry);
         }
 
         if (staleIfErrorAppliesTo(statusCode)
             && !staleResponseNotAllowed(request, cacheEntry, getCurrentDate())
             && validityPolicy.mayReturnStaleIfError(request, cacheEntry, responseDate)) {
             try {
-                final CloseableHttpResponse cachedResponse = responseGenerator.generateResponse(cacheEntry);
+                final CloseableHttpResponse cachedResponse = responseGenerator.generateResponse(request, cacheEntry);
                 cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
                 return cachedResponse;
             } finally {
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClientBuilder.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClientBuilder.java
index ef32df0..e5b66b7 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClientBuilder.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClientBuilder.java
@@ -162,7 +162,6 @@
         return null;
     }
 
-    @SuppressWarnings("resource")
     private SchedulingStrategy createSchedulingStrategy(final CacheConfig config) {
         return schedulingStrategy != null ? schedulingStrategy : new ImmediateSchedulingStrategy(config);
     }
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseCachingPolicy.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseCachingPolicy.java
index 1a9a6d3..ae7a08b 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseCachingPolicy.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseCachingPolicy.java
@@ -67,6 +67,7 @@
                 HttpStatus.SC_MOVED_PERMANENTLY,
                 HttpStatus.SC_GONE));
     private final Set<Integer> uncacheableStatuses;
+
     /**
      * Define a cache policy that limits the size of things that should be stored
      * in the cache to a maximum of {@link HttpResponse} bytes in size.
@@ -104,7 +105,8 @@
     public boolean isResponseCacheable(final String httpMethod, final HttpResponse response) {
         boolean cacheable = false;
 
-        if (!HeaderConstants.GET_METHOD.equals(httpMethod)) {
+        if (!(HeaderConstants.GET_METHOD.equals(httpMethod) ||
+                HeaderConstants.HEAD_METHOD.equals(httpMethod))) {
             log.debug("Response was not cacheable.");
             return false;
         }
diff --git a/httpclient-cache/src/test/java/org/apache/http/client/cache/TestHttpCacheEntry.java b/httpclient-cache/src/test/java/org/apache/http/client/cache/TestHttpCacheEntry.java
index 737d9e9..197cfd5 100644
--- a/httpclient-cache/src/test/java/org/apache/http/client/cache/TestHttpCacheEntry.java
+++ b/httpclient-cache/src/test/java/org/apache/http/client/cache/TestHttpCacheEntry.java
@@ -70,7 +70,7 @@
 
     private HttpCacheEntry makeEntry(final Header[] headers) {
         return new HttpCacheEntry(elevenSecondsAgo, nineSecondsAgo,
-                statusLine, headers, mockResource);
+                statusLine, headers, mockResource, HeaderConstants.GET_METHOD);
     }
 
     @Test
@@ -148,7 +148,7 @@
     public void mustProvideRequestDate() {
         try {
             new HttpCacheEntry(null, new Date(), statusLine,
-                    new Header[]{}, mockResource);
+                    new Header[]{}, mockResource, HeaderConstants.GET_METHOD);
             fail("Should have thrown exception");
         } catch (final IllegalArgumentException expected) {
         }
@@ -159,7 +159,7 @@
     public void mustProvideResponseDate() {
         try {
             new HttpCacheEntry(new Date(), null, statusLine,
-                    new Header[]{}, mockResource);
+                    new Header[]{}, mockResource, HeaderConstants.GET_METHOD);
             fail("Should have thrown exception");
         } catch (final IllegalArgumentException expected) {
         }
@@ -170,7 +170,7 @@
     public void mustProvideStatusLine() {
         try {
             new HttpCacheEntry(new Date(), new Date(), null,
-                    new Header[]{}, mockResource);
+                    new Header[]{}, mockResource, HeaderConstants.GET_METHOD);
             fail("Should have thrown exception");
         } catch (final IllegalArgumentException expected) {
         }
@@ -181,7 +181,7 @@
     public void mustProvideResponseHeaders() {
         try {
             new HttpCacheEntry(new Date(), new Date(), statusLine,
-                    null, mockResource);
+                    null, mockResource, HeaderConstants.GET_METHOD);
             fail("Should have thrown exception");
         } catch (final IllegalArgumentException expected) {
         }
@@ -190,14 +190,14 @@
     @Test
     public void canRetrieveOriginalStatusLine() {
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
-                new Header[]{}, mockResource);
+                new Header[]{}, mockResource, HeaderConstants.GET_METHOD);
         assertSame(statusLine, entry.getStatusLine());
     }
 
     @Test
     public void protocolVersionComesFromOriginalStatusLine() {
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
-                new Header[]{}, mockResource);
+                new Header[]{}, mockResource, HeaderConstants.GET_METHOD);
         assertSame(statusLine.getProtocolVersion(),
                 entry.getProtocolVersion());
     }
@@ -205,14 +205,14 @@
     @Test
     public void reasonPhraseComesFromOriginalStatusLine() {
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
-                new Header[]{}, mockResource);
+                new Header[]{}, mockResource, HeaderConstants.GET_METHOD);
         assertSame(statusLine.getReasonPhrase(), entry.getReasonPhrase());
     }
 
     @Test
     public void statusCodeComesFromOriginalStatusLine() {
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
-                new Header[]{}, mockResource);
+                new Header[]{}, mockResource, HeaderConstants.GET_METHOD);
         assertEquals(statusLine.getStatusCode(), entry.getStatusCode());
     }
 
@@ -220,7 +220,7 @@
     public void canGetOriginalRequestDate() {
         final Date requestDate = new Date();
         entry = new HttpCacheEntry(requestDate, new Date(), statusLine,
-                new Header[]{}, mockResource);
+                new Header[]{}, mockResource, HeaderConstants.GET_METHOD);
         assertSame(requestDate, entry.getRequestDate());
     }
 
@@ -228,14 +228,14 @@
     public void canGetOriginalResponseDate() {
         final Date responseDate = new Date();
         entry = new HttpCacheEntry(new Date(), responseDate, statusLine,
-                new Header[]{}, mockResource);
+                new Header[]{}, mockResource, HeaderConstants.GET_METHOD);
         assertSame(responseDate, entry.getResponseDate());
     }
 
     @Test
     public void canGetOriginalResource() {
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
-                new Header[]{}, mockResource);
+                new Header[]{}, mockResource, HeaderConstants.GET_METHOD);
         assertSame(mockResource, entry.getResource());
     }
 
@@ -246,7 +246,7 @@
                 new BasicHeader("Date", DateUtils.formatDate(now))
         };
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
-                headers, mockResource);
+                headers, mockResource, HeaderConstants.GET_METHOD);
         final Header[] result = entry.getAllHeaders();
         assertEquals(headers.length, result.length);
         for(int i=0; i<headers.length; i++) {
@@ -258,7 +258,7 @@
     @Test
     public void canConstructWithoutVariants() {
         new HttpCacheEntry(new Date(), new Date(), statusLine,
-                new Header[]{}, mockResource);
+                new Header[]{}, mockResource, HeaderConstants.GET_METHOD);
     }
 
     @SuppressWarnings("unused")
@@ -266,7 +266,7 @@
     public void canProvideVariantMap() {
         new HttpCacheEntry(new Date(), new Date(), statusLine,
                 new Header[]{}, mockResource,
-                new HashMap<String,String>());
+                new HashMap<String,String>(), HeaderConstants.GET_METHOD);
     }
 
     @Test
@@ -276,7 +276,7 @@
         variantMap.put("C","D");
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
                 new Header[]{}, mockResource,
-                variantMap);
+                variantMap, HeaderConstants.GET_METHOD);
         final Map<String,String> result = entry.getVariantMap();
         assertEquals(2, result.size());
         assertEquals("B", result.get("A"));
@@ -290,7 +290,7 @@
         variantMap.put("C","D");
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
                 new Header[]{}, mockResource,
-                variantMap);
+                variantMap, HeaderConstants.GET_METHOD);
         final Map<String,String> result = entry.getVariantMap();
         try {
             result.remove("A");
@@ -307,7 +307,7 @@
     @Test
     public void canConvertToString() {
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
-                new Header[]{}, mockResource);
+                new Header[]{}, mockResource, HeaderConstants.GET_METHOD);
         assertNotNull(entry.toString());
         assertFalse("".equals(entry.toString()));
     }
@@ -316,7 +316,7 @@
     public void testMissingDateHeaderIsIgnored() {
         final Header[] headers = new Header[] {};
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
-                                   headers, mockResource);
+                                   headers, mockResource, HeaderConstants.GET_METHOD);
         assertNull(entry.getDate());
     }
 
@@ -324,7 +324,7 @@
     public void testMalformedDateHeaderIsIgnored() {
         final Header[] headers = new Header[] { new BasicHeader("Date", "asdf") };
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
-                                   headers, mockResource);
+                                   headers, mockResource, HeaderConstants.GET_METHOD);
         assertNull(entry.getDate());
     }
 
@@ -335,10 +335,19 @@
         final Date date = new Date(nowMs - (nowMs % 1000L));
         final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatDate(date)) };
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
-                                   headers, mockResource);
+                                   headers, mockResource, HeaderConstants.GET_METHOD);
         final Date dateHeaderValue = entry.getDate();
         assertNotNull(dateHeaderValue);
         assertEquals(date.getTime(), dateHeaderValue.getTime());
     }
 
+    @Test
+    public void testGetMethodReturnsCorrectRequestMethod() {
+        final Header[] headers = { new BasicHeader("foo", "fooValue"),
+                new BasicHeader("bar", "barValue1"),
+                new BasicHeader("bar", "barValue2")
+        };
+        entry = makeEntry(headers);
+        assertEquals(HeaderConstants.GET_METHOD, entry.getRequestMethod());
+    }
 }
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/AbstractProtocolTest.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/AbstractProtocolTest.java
index 5072143..9f85fcd 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/AbstractProtocolTest.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/AbstractProtocolTest.java
@@ -79,7 +79,7 @@
 
     @Before
     public void setUp() {
-        host = new HttpHost("foo.example.com");
+        host = new HttpHost("foo.example.com", 80);
 
         route = new HttpRoute(host);
 
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/HttpTestUtils.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/HttpTestUtils.java
index 3d9fb54..d1df505 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/HttpTestUtils.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/HttpTestUtils.java
@@ -28,6 +28,7 @@
 
 import java.io.InputStream;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Random;
 
@@ -41,6 +42,7 @@
 import org.apache.http.HttpVersion;
 import org.apache.http.RequestLine;
 import org.apache.http.StatusLine;
+import org.apache.http.client.cache.HeaderConstants;
 import org.apache.http.client.cache.HttpCacheEntry;
 import org.apache.http.client.utils.DateUtils;
 import org.apache.http.entity.ByteArrayEntity;
@@ -299,8 +301,7 @@
     public static HttpCacheEntry makeCacheEntry(final Date requestDate,
             final Date responseDate, final Header[] headers, final byte[] bytes,
             final Map<String,String> variantMap) {
-        final StatusLine statusLine = new BasicStatusLine(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
-        return new HttpCacheEntry(requestDate, responseDate, statusLine, headers, new HeapResource(bytes), variantMap);
+        return new HttpCacheEntry(requestDate, responseDate, makeStatusLine(), headers, new HeapResource(bytes), variantMap, HeaderConstants.GET_METHOD);
     }
 
     public static HttpCacheEntry makeCacheEntry(final Header[] headers, final byte[] bytes) {
@@ -321,6 +322,39 @@
         return makeCacheEntry(now, now);
     }
 
+    public static HttpCacheEntry makeCacheEntryWithNoRequestMethodOrEntity(final Header[] headers) {
+        final Date now = new Date();
+        return new HttpCacheEntry(now, now, makeStatusLine(), headers, null, null, null);
+    }
+
+    public static HttpCacheEntry makeCacheEntryWithNoRequestMethod(final Header[] headers) {
+        final Date now = new Date();
+        return new HttpCacheEntry(now, now, makeStatusLine(), headers, new HeapResource(getRandomBytes(128)), null, null);
+    }
+
+    public static HttpCacheEntry make204CacheEntryWithNoRequestMethod(final Header[] headers) {
+        final Date now = new Date();
+        return new HttpCacheEntry(now, now, make204StatusLine(), headers, null, null, HeaderConstants.HEAD_METHOD);
+    }
+
+    public static HttpCacheEntry makeHeadCacheEntry(final Header[] headers) {
+        final Date now = new Date();
+        return new HttpCacheEntry(now, now, makeStatusLine(), headers, null, null, HeaderConstants.HEAD_METHOD);
+    }
+
+    public static HttpCacheEntry makeHeadCacheEntryWithNoRequestMethod(final Header[] headers) {
+        final Date now = new Date();
+        return new HttpCacheEntry(now, now, makeStatusLine(), headers, null, null, null);
+    }
+
+    public static StatusLine makeStatusLine() {
+        return new BasicStatusLine(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
+    }
+
+    public static StatusLine make204StatusLine() {
+        return new BasicStatusLine(HttpVersion.HTTP_1_1, HttpStatus.SC_NO_CONTENT, "OK");
+    }
+
     public static HttpResponse make200Response() {
         final HttpResponse out = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
         out.setHeader("Date", DateUtils.formatDate(new Date()));
@@ -356,8 +390,19 @@
         return new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1);
     }
 
+    public static HttpRequest makeDefaultHEADRequest() {
+        return new BasicHttpRequest("HEAD","/",HttpVersion.HTTP_1_1);
+    }
+
     public static HttpResponse make500Response() {
         return new BasicHttpResponse(HttpVersion.HTTP_1_1,
                 HttpStatus.SC_INTERNAL_SERVER_ERROR, "Internal Server Error");
     }
+
+    public static Map<String, String> makeDefaultVariantMap(final String key, final String value) {
+        final Map<String, String> variants = new HashMap<String, String>();
+        variants.put(key, value);
+
+        return variants;
+    }
 }
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidationRequest.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidationRequest.java
index 6df2a62..a61d431 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidationRequest.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidationRequest.java
@@ -62,7 +62,7 @@
     public void setUp() {
         mockParent = EasyMock.createNiceMock(AsynchronousValidator.class);
         mockClient = EasyMock.createNiceMock(CachingExec.class);
-        route = new HttpRoute(new HttpHost("foo.example.com"));
+        route = new HttpRoute(new HttpHost("foo.example.com", 80));
         request = HttpRequestWrapper.wrap(new HttpGet("/"));
         context = HttpClientContext.create();
         mockExecAware = EasyMock.createNiceMock(HttpExecutionAware.class);
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidator.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidator.java
index 890c36f..1ca1b3c 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidator.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidator.java
@@ -64,7 +64,7 @@
     @Before
     public void setUp() {
         mockClient = EasyMock.createNiceMock(CachingExec.class);
-        route = new HttpRoute(new HttpHost("foo.example.com"));
+        route = new HttpRoute(new HttpHost("foo.example.com", 80));
         request = HttpRequestWrapper.wrap(new HttpGet("/"));
         context = HttpClientContext.create();
         context.setTargetHost(new HttpHost("foo.example.com"));
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestBasicHttpCache.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestBasicHttpCache.java
index 6652c16..8549ab1 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestBasicHttpCache.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestBasicHttpCache.java
@@ -263,6 +263,15 @@
     }
 
     @Test
+    public void testNullResourcesAreComplete()
+        throws Exception {
+        final HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
+        resp.setHeader("Content-Length","256");
+
+        assertFalse(impl.isIncompleteResponse(resp, null));
+    }
+
+    @Test
     public void testIncompleteResponseErrorProvidesPlainTextErrorMessage()
         throws Exception {
         final HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheInvalidator.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheInvalidator.java
index 9e9994f..af412b6 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheInvalidator.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheInvalidator.java
@@ -222,6 +222,81 @@
     }
 
     @Test
+    public void testInvalidatesHEADCacheEntryIfSubsequentGETRequestsAreMadeToTheSameURI() throws Exception {
+        impl = new CacheInvalidator(cacheKeyGenerator, mockStorage);
+        final String theURI = "http://foo.example.com:80/";
+        request = new BasicHttpRequest("GET", theURI,HTTP_1_1);
+
+        cacheEntryisForMethod("HEAD");
+        cacheEntryHasVariantMap(new HashMap<String, String>());
+        cacheReturnsEntryForUri(theURI);
+        entryIsRemoved(theURI);
+
+        replayMocks();
+        impl.flushInvalidatedCacheEntries(host, request);
+        verifyMocks();
+    }
+
+    @Test
+    public void testInvalidatesVariantHEADCacheEntriesIfSubsequentGETRequestsAreMadeToTheSameURI() throws Exception {
+        impl = new CacheInvalidator(cacheKeyGenerator, mockStorage);
+        final String theURI = "http://foo.example.com:80/";
+        request = new BasicHttpRequest("GET", theURI,HTTP_1_1);
+        final String theVariantKey = "{Accept-Encoding=gzip%2Cdeflate&User-Agent=Apache-HttpClient}";
+        final String theVariantURI = "{Accept-Encoding=gzip%2Cdeflate&User-Agent=Apache-HttpClient}http://foo.example.com:80/";
+        final Map<String, String> variants = HttpTestUtils.makeDefaultVariantMap(theVariantKey, theVariantURI);
+
+        cacheEntryisForMethod("HEAD");
+        cacheEntryHasVariantMap(variants);
+        cacheReturnsEntryForUri(theURI);
+        entryIsRemoved(theURI);
+        entryIsRemoved(theVariantURI);
+
+        replayMocks();
+        impl.flushInvalidatedCacheEntries(host, request);
+        verifyMocks();
+    }
+
+    @Test
+    public void testDoesNotInvalidateHEADCacheEntry() throws Exception {
+        final String theURI = "http://foo.example.com:80/";
+        request = new BasicHttpRequest("HEAD", theURI,HTTP_1_1);
+
+        cacheReturnsEntryForUri(theURI);
+
+        replayMocks();
+        impl.flushInvalidatedCacheEntries(host, request);
+        verifyMocks();
+    }
+
+    @Test
+    public void testDoesNotInvalidateHEADCacheEntryIfSubsequentHEADRequestsAreMadeToTheSameURI() throws Exception {
+        impl = new CacheInvalidator(cacheKeyGenerator, mockStorage);
+        final String theURI = "http://foo.example.com:80/";
+        request = new BasicHttpRequest("HEAD", theURI,HTTP_1_1);
+
+        cacheReturnsEntryForUri(theURI);
+
+        replayMocks();
+        impl.flushInvalidatedCacheEntries(host, request);
+        verifyMocks();
+    }
+
+    @Test
+    public void testDoesNotInvalidateGETCacheEntryIfSubsequentGETRequestsAreMadeToTheSameURI() throws Exception {
+        impl = new CacheInvalidator(cacheKeyGenerator, mockStorage);
+        final String theURI = "http://foo.example.com:80/";
+        request = new BasicHttpRequest("GET", theURI,HTTP_1_1);
+
+        cacheEntryisForMethod("GET");
+        cacheReturnsEntryForUri(theURI);
+
+        replayMocks();
+        impl.flushInvalidatedCacheEntries(host, request);
+        verifyMocks();
+    }
+
+    @Test
     public void testDoesNotInvalidateRequestsWithClientCacheControlHeaders() throws Exception {
         request = new BasicHttpRequest("GET","/",HTTP_1_1);
         request.setHeader("Cache-Control","no-cache");
@@ -244,9 +319,7 @@
         request = new BasicHttpRequest("POST","/",HTTP_1_1);
         final String theUri = "http://foo.example.com:80/";
         final String variantUri = "theVariantURI";
-
-        final Map<String,String> mapOfURIs = new HashMap<String,String>();
-        mapOfURIs.put(variantUri,variantUri);
+        final Map<String,String> mapOfURIs = HttpTestUtils.makeDefaultVariantMap(variantUri, variantUri);
 
         cacheReturnsEntryForUri(theUri);
         cacheEntryHasVariantMap(mapOfURIs);
@@ -651,4 +724,8 @@
         mockStorage.removeEntry(theUri);
     }
 
+    private void cacheEntryisForMethod(final String httpMethod) {
+        expect(mockEntry.getRequestMethod()).andReturn(httpMethod);
+    }
+
 }
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheValidityPolicy.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheValidityPolicy.java
index 5357276..5e8e5e1 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheValidityPolicy.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheValidityPolicy.java
@@ -355,6 +355,14 @@
     }
 
     @Test
+    public void testNullResourceInvalidatesEntry() {
+        final int contentLength = 128;
+        final Header[] headers = {new BasicHeader(HTTP.CONTENT_LEN, Integer.toString(contentLength))};
+        final HttpCacheEntry entry = HttpTestUtils.makeHeadCacheEntry(headers);
+        assertFalse(impl.contentLengthHeaderMatchesActualLength(entry));
+    }
+
+    @Test
     public void testMalformedContentLengthReturnsNegativeOne() {
         final Header[] headers = new Header[] { new BasicHeader("Content-Length", "asdf") };
         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheableRequestPolicy.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheableRequestPolicy.java
index 927a7cd..8b06ca2 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheableRequestPolicy.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheableRequestPolicy.java
@@ -45,7 +45,6 @@
         final BasicHttpRequest request = new BasicHttpRequest("GET", "someUri");
 
         Assert.assertTrue(policy.isServableFromCache(request));
-
     }
 
     @Test
@@ -83,10 +82,56 @@
     }
 
     @Test
-    public void testIsArbitraryMethodServableFromCache() {
-
+    public void testIsHeadServableFromCache() {
         BasicHttpRequest request = new BasicHttpRequest("HEAD", "someUri");
 
+        Assert.assertTrue(policy.isServableFromCache(request));
+
+        request = new BasicHttpRequest("HEAD", "someUri");
+        request.addHeader("Cache-Control", "public");
+        request.addHeader("Cache-Control", "max-age=20");
+
+        Assert.assertTrue(policy.isServableFromCache(request));
+    }
+
+    @Test
+    public void testIsHeadWithCacheControlServableFromCache() {
+        BasicHttpRequest request = new BasicHttpRequest("HEAD", "someUri");
+        request.addHeader("Cache-Control", "no-cache");
+
+        Assert.assertFalse(policy.isServableFromCache(request));
+
+        request = new BasicHttpRequest("HEAD", "someUri");
+        request.addHeader("Cache-Control", "no-store");
+        request.addHeader("Cache-Control", "max-age=20");
+
+        Assert.assertFalse(policy.isServableFromCache(request));
+
+        request = new BasicHttpRequest("HEAD", "someUri");
+        request.addHeader("Cache-Control", "public");
+        request.addHeader("Cache-Control", "no-store, max-age=20");
+
+        Assert.assertFalse(policy.isServableFromCache(request));
+    }
+
+    @Test
+    public void testIsHeadWithPragmaServableFromCache() {
+        BasicHttpRequest request = new BasicHttpRequest("HEAD", "someUri");
+        request.addHeader("Pragma", "no-cache");
+
+        Assert.assertFalse(policy.isServableFromCache(request));
+
+        request = new BasicHttpRequest("HEAD", "someUri");
+        request.addHeader("Pragma", "value1");
+        request.addHeader("Pragma", "value2");
+
+        Assert.assertFalse(policy.isServableFromCache(request));
+    }
+
+    @Test
+    public void testIsArbitraryMethodServableFromCache() {
+        BasicHttpRequest request = new BasicHttpRequest("TRACE", "someUri");
+
         Assert.assertFalse(policy.isServableFromCache(request));
 
         request = new BasicHttpRequest("get", "someUri");
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachedHttpResponseGenerator.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachedHttpResponseGenerator.java
index d3b8cf5..1f5556a 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachedHttpResponseGenerator.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachedHttpResponseGenerator.java
@@ -27,10 +27,12 @@
 package org.apache.http.impl.client.cache;
 
 import java.util.Date;
+import java.util.HashMap;
 
 import org.apache.http.Header;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.cache.HttpCacheEntry;
+import org.apache.http.client.methods.HttpRequestWrapper;
 import org.apache.http.client.utils.DateUtils;
 import org.apache.http.message.BasicHeader;
 import org.easymock.classextension.EasyMock;
@@ -42,6 +44,7 @@
 public class TestCachedHttpResponseGenerator {
 
     private HttpCacheEntry entry;
+    private HttpRequestWrapper request;
     private CacheValidityPolicy mockValidityPolicy;
     private CachedHttpResponseGenerator impl;
     private Date now;
@@ -49,15 +52,14 @@
     @Before
     public void setUp() {
         now = new Date();
-        final Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
         final Date eightSecondsAgo = new Date(now.getTime() - 8 * 1000L);
-        final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
         final Date tenSecondsFromNow = new Date(now.getTime() + 10 * 1000L);
         final Header[] hdrs = { new BasicHeader("Date", DateUtils.formatDate(eightSecondsAgo)),
                 new BasicHeader("Expires", DateUtils.formatDate(tenSecondsFromNow)),
                 new BasicHeader("Content-Length", "150") };
 
-        entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, sixSecondsAgo, hdrs);
+        entry = HttpTestUtils.makeCacheEntry(new HashMap<String, String>());
+        request = HttpRequestWrapper.wrap(HttpTestUtils.makeDefaultRequest());
         mockValidityPolicy = EasyMock.createNiceMock(CacheValidityPolicy.class);
         impl = new CachedHttpResponseGenerator(mockValidityPolicy);
     }
@@ -71,7 +73,7 @@
         final byte[] buf = new byte[] { 1, 2, 3, 4, 5 };
         final HttpCacheEntry entry1 = HttpTestUtils.makeCacheEntry(buf);
 
-        final HttpResponse response = impl.generateResponse(entry1);
+        final HttpResponse response = impl.generateResponse(request, entry1);
 
         final Header length = response.getFirstHeader("Content-Length");
         Assert.assertNotNull("Content-Length Header is missing", length);
@@ -87,7 +89,7 @@
         final byte[] buf = new byte[] { 1, 2, 3, 4, 5 };
         final HttpCacheEntry entry1 = HttpTestUtils.makeCacheEntry(hdrs, buf);
 
-        final HttpResponse response = impl.generateResponse(entry1);
+        final HttpResponse response = impl.generateResponse(request, entry1);
 
         final Header length = response.getFirstHeader("Content-Length");
 
@@ -96,7 +98,7 @@
 
     @Test
     public void testResponseMatchesCacheEntry() {
-        final HttpResponse response = impl.generateResponse(entry);
+        final HttpResponse response = impl.generateResponse(request, entry);
 
         Assert.assertTrue(response.containsHeader("Content-Length"));
 
@@ -107,7 +109,7 @@
 
     @Test
     public void testResponseStatusCodeMatchesCacheEntry() {
-        final HttpResponse response = impl.generateResponse(entry);
+        final HttpResponse response = impl.generateResponse(request, entry);
 
         Assert.assertEquals(entry.getStatusCode(), response.getStatusLine().getStatusCode());
     }
@@ -117,7 +119,7 @@
         currentAge(10L);
         replayMocks();
 
-        final HttpResponse response = impl.generateResponse(entry);
+        final HttpResponse response = impl.generateResponse(request, entry);
 
         final Header ageHdr = response.getFirstHeader("Age");
         Assert.assertNotNull(ageHdr);
@@ -129,7 +131,7 @@
         currentAge(0L);
         replayMocks();
 
-        final HttpResponse response = impl.generateResponse(entry);
+        final HttpResponse response = impl.generateResponse(request, entry);
 
         final Header ageHdr = response.getFirstHeader("Age");
         Assert.assertNull(ageHdr);
@@ -140,7 +142,7 @@
         currentAge(CacheValidityPolicy.MAX_AGE + 1L);
         replayMocks();
 
-        final HttpResponse response = impl.generateResponse(entry);
+        final HttpResponse response = impl.generateResponse(request, entry);
 
         final Header ageHdr = response.getFirstHeader("Age");
         Assert.assertNotNull(ageHdr);
@@ -153,4 +155,19 @@
                         EasyMock.isA(Date.class))).andReturn(sec);
     }
 
+    @Test
+    public void testResponseContainsEntityToServeGETRequestIfEntryContainsResource() throws Exception {
+        final HttpResponse response = impl.generateResponse(request, entry);
+
+        Assert.assertNotNull(response.getEntity());
+    }
+
+    @Test
+    public void testResponseDoesNotContainEntityToServeHEADRequestIfEntryContainsResource() throws Exception {
+        final HttpRequestWrapper headRequest = HttpRequestWrapper.wrap(HttpTestUtils.makeDefaultHEADRequest());
+        final HttpResponse response = impl.generateResponse(headRequest, entry);
+
+        Assert.assertNull(response.getEntity());
+    }
+
 }
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachedResponseSuitabilityChecker.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachedResponseSuitabilityChecker.java
index 0735026..7ab8e6c 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachedResponseSuitabilityChecker.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachedResponseSuitabilityChecker.java
@@ -261,4 +261,81 @@
 
         Assert.assertTrue(impl.canCachedResponseBeUsed(host, request, entry, now));
     }
+
+    @Test
+    public void testSuitableIfRequestMethodisHEAD() {
+        final HttpRequest headRequest = new BasicHttpRequest("HEAD", "/foo", HttpVersion.HTTP_1_1);
+        final Header[] headers = {
+                new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
+                new BasicHeader("Cache-Control", "max-age=3600"),
+                new BasicHeader("Content-Length","128")
+        };
+        entry = getEntry(headers);
+
+        Assert.assertTrue(impl.canCachedResponseBeUsed(host, headRequest, entry, now));
+    }
+
+    @Test
+    public void testNotSuitableIfRequestMethodIsGETAndEntryResourceIsNull() {
+        final Header[] headers = {
+                new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
+                new BasicHeader("Cache-Control", "max-age=3600"),
+                new BasicHeader("Content-Length","128")
+        };
+        entry = HttpTestUtils.makeHeadCacheEntry(headers);
+
+        Assert.assertFalse(impl.canCachedResponseBeUsed(host, request, entry, now));
+    }
+
+    @Test
+    public void testNotSuitableForGETIfEntryDoesNotSpecifyARequestMethodOrEntity() {
+        impl = new CachedResponseSuitabilityChecker(CacheConfig.custom().build());
+        final Header[] headers = {
+                new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
+                new BasicHeader("Cache-Control", "max-age=3600"),
+                new BasicHeader("Content-Length","128")
+        };
+        entry = HttpTestUtils.makeCacheEntryWithNoRequestMethodOrEntity(headers);
+
+        Assert.assertFalse(impl.canCachedResponseBeUsed(host, request, entry, now));
+    }
+
+    @Test
+    public void testSuitableForGETIfEntryDoesNotSpecifyARequestMethodButContainsEntity() {
+        impl = new CachedResponseSuitabilityChecker(CacheConfig.custom().build());
+        final Header[] headers = {
+                new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
+                new BasicHeader("Cache-Control", "max-age=3600"),
+                new BasicHeader("Content-Length","128")
+        };
+        entry = HttpTestUtils.makeCacheEntryWithNoRequestMethod(headers);
+
+        Assert.assertTrue(impl.canCachedResponseBeUsed(host, request, entry, now));
+    }
+
+    @Test
+    public void testSuitableForGETIfHeadResponseCachingEnabledAndEntryDoesNotSpecifyARequestMethodButContains204Response() {
+        impl = new CachedResponseSuitabilityChecker(CacheConfig.custom().build());
+        final Header[] headers = {
+                new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
+                new BasicHeader("Cache-Control", "max-age=3600")
+        };
+        entry = HttpTestUtils.make204CacheEntryWithNoRequestMethod(headers);
+
+        Assert.assertTrue(impl.canCachedResponseBeUsed(host, request, entry, now));
+    }
+
+    @Test
+    public void testSuitableForHEADIfHeadResponseCachingEnabledAndEntryDoesNotSpecifyARequestMethod() {
+        final HttpRequest headRequest = new BasicHttpRequest("HEAD", "/foo", HttpVersion.HTTP_1_1);
+        impl = new CachedResponseSuitabilityChecker(CacheConfig.custom().build());
+        final Header[] headers = {
+                new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
+                new BasicHeader("Cache-Control", "max-age=3600"),
+                new BasicHeader("Content-Length","128")
+        };
+        entry = HttpTestUtils.makeHeadCacheEntryWithNoRequestMethod(headers);
+
+        Assert.assertTrue(impl.canCachedResponseBeUsed(host, headRequest, entry, now));
+    }
 }
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingExecChain.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingExecChain.java
index aaa605f..852c1ca 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingExecChain.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingExecChain.java
@@ -139,7 +139,7 @@
         config = CacheConfig.DEFAULT;
         asyncValidator = new AsynchronousValidator(config);
 
-        host = new HttpHost("foo.example.com");
+        host = new HttpHost("foo.example.com", 80);
         route = new HttpRoute(host);
         request = HttpRequestWrapper.wrap(new BasicHttpRequest("GET", "/stuff",
             HttpVersion.HTTP_1_1));
@@ -1781,8 +1781,9 @@
     }
 
     protected void responseIsGeneratedFromCache() {
-        expect(mockResponseGenerator.generateResponse((HttpCacheEntry) anyObject())).andReturn(
-            mockCachedResponse);
+        expect(
+            mockResponseGenerator.generateResponse((HttpRequestWrapper) anyObject(), (HttpCacheEntry) anyObject()))
+            .andReturn(mockCachedResponse);
     }
 
 }
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestExponentialBackingOffSchedulingStrategy.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestExponentialBackingOffSchedulingStrategy.java
index 7d5c366..5005118 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestExponentialBackingOffSchedulingStrategy.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestExponentialBackingOffSchedulingStrategy.java
@@ -164,7 +164,7 @@
         final ClientExecChain clientExecChain = EasyMock.createNiceMock(ClientExecChain.class);
         final CachingExec cachingHttpClient = new CachingExec(clientExecChain);
         final AsynchronousValidator mockValidator = new AsynchronousValidator(impl);
-        final HttpRoute httpRoute = new HttpRoute(new HttpHost("foo.example.com"));
+        final HttpRoute httpRoute = new HttpRoute(new HttpHost("foo.example.com", 80));
         final HttpRequestWrapper httpRequestWrapper = HttpRequestWrapper.wrap(new BasicHttpRequest("GET", "/"));
         final HttpClientContext httpClientContext = new HttpClientContext();
         return new AsynchronousValidationRequest(mockValidator, cachingHttpClient, httpRoute, httpRequestWrapper,
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpCacheEntrySerializers.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpCacheEntrySerializers.java
index 4e8abed..188f08c 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpCacheEntrySerializers.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpCacheEntrySerializers.java
@@ -42,6 +42,7 @@
 import org.apache.http.Header;
 import org.apache.http.ProtocolVersion;
 import org.apache.http.StatusLine;
+import org.apache.http.client.cache.HeaderConstants;
 import org.apache.http.client.cache.HttpCacheEntry;
 import org.apache.http.client.cache.HttpCacheEntrySerializer;
 import org.apache.http.client.cache.Resource;
@@ -93,7 +94,7 @@
         variantMap.put("test variant 2","true");
         final HttpCacheEntry cacheEntry = new HttpCacheEntry(new Date(), new Date(),
                 slObj, headers, new HeapResource(Base64.decodeBase64(body
-                        .getBytes(UTF8))), variantMap);
+                        .getBytes(UTF8))), variantMap, HeaderConstants.GET_METHOD);
 
         return cacheEntry;
     }
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpCacheJiraNumber1147.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpCacheJiraNumber1147.java
index 60a310f..ce82393 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpCacheJiraNumber1147.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpCacheJiraNumber1147.java
@@ -90,7 +90,7 @@
         final ClientExecChain backend = EasyMock.createNiceMock(ClientExecChain.class);
         final HttpRequestWrapper get = HttpRequestWrapper.wrap(new HttpGet("http://somehost/"));
         final HttpClientContext context = HttpClientContext.create();
-        final HttpHost target = new HttpHost("somehost");
+        final HttpHost target = new HttpHost("somehost", 80);
         final HttpRoute route = new HttpRoute(target);
 
         context.setTargetHost(target);
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolDeviations.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolDeviations.java
index e5e7063..53d0d66 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolDeviations.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolDeviations.java
@@ -93,7 +93,7 @@
 
     @Before
     public void setUp() {
-        host = new HttpHost("foo.example.com");
+        host = new HttpHost("foo.example.com", 80);
 
         route = new HttpRoute(host);
 
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRequirements.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRequirements.java
index 8211b1f..3378083 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRequirements.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRequirements.java
@@ -4806,7 +4806,7 @@
     protected void testUnsafeMethodDoesNotInvalidateCacheForHeaderUri(
             final HttpRequestWrapper unsafeReq) throws Exception, IOException {
 
-        final HttpHost otherHost = new HttpHost("bar.example.com");
+        final HttpHost otherHost = new HttpHost("bar.example.com", 80);
         final HttpRoute otherRoute = new HttpRoute(otherHost);
         final HttpRequestWrapper req1 = HttpRequestWrapper.wrap(
                 new BasicHttpRequest("GET", "/content", HttpVersion.HTTP_1_1));
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCachingPolicy.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCachingPolicy.java
index 5734053..8bab0a5 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCachingPolicy.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCachingPolicy.java
@@ -76,6 +76,12 @@
     }
 
     @Test
+    public void testIsHeadCacheable() {
+        policy = new ResponseCachingPolicy(0, true, false, false);
+        Assert.assertTrue(policy.isResponseCacheable("HEAD", response));
+    }
+
+    @Test
     public void testResponsesToRequestsWithAuthorizationHeadersAreNotCacheableBySharedCache() {
         request = new BasicHttpRequest("GET","/",HTTP_1_1);
         request.setHeader("Authorization","Basic dXNlcjpwYXNzd2Q=");
@@ -268,6 +274,13 @@
     }
 
     @Test
+    public void testIsHeadWithNoCacheCacheable() {
+        response.addHeader("Cache-Control", "no-cache");
+
+        Assert.assertFalse(policy.isResponseCacheable("HEAD", response));
+    }
+
+    @Test
     public void testIsGetWithNoStoreCacheable() {
         response.addHeader("Cache-Control", "no-store");
 
@@ -275,6 +288,13 @@
     }
 
     @Test
+    public void testIsHeadWithNoStoreCacheable() {
+        response.addHeader("Cache-Control", "no-store");
+
+        Assert.assertFalse(policy.isResponseCacheable("HEAD", response));
+    }
+
+    @Test
     public void testIsGetWithNoStoreEmbeddedInListCacheable() {
         response.addHeader("Cache-Control", "public, no-store");
 
@@ -282,6 +302,13 @@
     }
 
     @Test
+    public void testIsHeadWithNoStoreEmbeddedInListCacheable() {
+        response.addHeader("Cache-Control", "public, no-store");
+
+        Assert.assertFalse(policy.isResponseCacheable("HEAD", response));
+    }
+
+    @Test
     public void testIsGetWithNoCacheEmbeddedInListCacheable() {
         response.addHeader("Cache-Control", "public, no-cache");
 
@@ -289,6 +316,13 @@
     }
 
     @Test
+    public void testIsHeadWithNoCacheEmbeddedInListCacheable() {
+        response.addHeader("Cache-Control", "public, no-cache");
+
+        Assert.assertFalse(policy.isResponseCacheable("HEAD", response));
+    }
+
+    @Test
     public void testIsGetWithNoCacheEmbeddedInListAfterFirstHeaderCacheable() {
         response.addHeader("Cache-Control", "max-age=20");
         response.addHeader("Cache-Control", "public, no-cache");
@@ -297,6 +331,14 @@
     }
 
     @Test
+    public void testIsHeadWithNoCacheEmbeddedInListAfterFirstHeaderCacheable() {
+        response.addHeader("Cache-Control", "max-age=20");
+        response.addHeader("Cache-Control", "public, no-cache");
+
+        Assert.assertFalse(policy.isResponseCacheable("HEAD", response));
+    }
+
+    @Test
     public void testIsGetWithNoStoreEmbeddedInListAfterFirstHeaderCacheable() {
         response.addHeader("Cache-Control", "max-age=20");
         response.addHeader("Cache-Control", "public, no-store");
@@ -305,6 +347,14 @@
     }
 
     @Test
+    public void testIsHeadWithNoStoreEmbeddedInListAfterFirstHeaderCacheable() {
+        response.addHeader("Cache-Control", "max-age=20");
+        response.addHeader("Cache-Control", "public, no-store");
+
+        Assert.assertFalse(policy.isResponseCacheable("HEAD", response));
+    }
+
+    @Test
     public void testIsGetWithAnyCacheControlCacheable() {
         response.addHeader("Cache-Control", "max=10");
 
@@ -320,6 +370,22 @@
     }
 
     @Test
+    public void testIsHeadWithAnyCacheControlCacheable() {
+        policy = new ResponseCachingPolicy(0, true, false, false);
+        response.addHeader("Cache-Control", "max=10");
+
+        Assert.assertTrue(policy.isResponseCacheable("HEAD", response));
+
+        response = new BasicHttpResponse(
+                new BasicStatusLine(HTTP_1_1, HttpStatus.SC_OK, ""));
+        response.setHeader("Date", DateUtils.formatDate(new Date()));
+        response.addHeader("Cache-Control", "no-transform");
+        response.setHeader("Content-Length", "0");
+
+        Assert.assertTrue(policy.isResponseCacheable("HEAD", response));
+    }
+
+    @Test
     public void testIsGetWithout200Cacheable() {
         HttpResponse response404 = new BasicHttpResponse(new BasicStatusLine(HTTP_1_1,
                 HttpStatus.SC_NOT_FOUND, ""));
@@ -333,6 +399,19 @@
     }
 
     @Test
+    public void testIsHeadWithout200Cacheable() {
+        HttpResponse response404 = new BasicHttpResponse(new BasicStatusLine(HTTP_1_1,
+                HttpStatus.SC_NOT_FOUND, ""));
+
+        Assert.assertFalse(policy.isResponseCacheable("HEAD", response404));
+
+        response404 = new BasicHttpResponse(new BasicStatusLine(HTTP_1_1,
+                HttpStatus.SC_GATEWAY_TIMEOUT, ""));
+
+        Assert.assertFalse(policy.isResponseCacheable("HEAD", response404));
+    }
+
+    @Test
     public void testVaryStarIsNotCacheable() {
         response.setHeader("Vary", "*");
         Assert.assertFalse(policy.isResponseCacheable("GET", response));
@@ -355,6 +434,13 @@
     }
 
     @Test
+    public void testIsHeadWithVaryHeaderCacheable() {
+        policy = new ResponseCachingPolicy(0, true, false, false);
+        response.addHeader("Vary", "Accept-Encoding");
+        Assert.assertTrue(policy.isResponseCacheable("HEAD", response));
+    }
+
+    @Test
     public void testIsArbitraryMethodCacheable() {
 
         Assert.assertFalse(policy.isResponseCacheable("PUT", response));
@@ -486,6 +572,12 @@
     }
 
     @Test
+    public void testResponsesToHEADWithQueryParamsButNoExplicitCachingAreNotCacheable() {
+        request = new BasicHttpRequest("HEAD", "/foo?s=bar");
+        Assert.assertFalse(policy.isResponseCacheable(request, response));
+    }
+
+    @Test
     public void testResponsesToGETWithQueryParamsButNoExplicitCachingAreNotCacheableEvenWhen1_0QueryCachingDisabled() {
         policy = new ResponseCachingPolicy(0, true, true, false);
         request = new BasicHttpRequest("GET", "/foo?s=bar");
@@ -493,6 +585,13 @@
     }
 
     @Test
+    public void testResponsesToHEADWithQueryParamsButNoExplicitCachingAreNotCacheableEvenWhen1_0QueryCachingDisabled() {
+        policy = new ResponseCachingPolicy(0, true, true, false);
+        request = new BasicHttpRequest("HEAD", "/foo?s=bar");
+        Assert.assertFalse(policy.isResponseCacheable(request, response));
+    }
+
+    @Test
     public void testResponsesToGETWithQueryParamsAndExplicitCachingAreCacheable() {
         request = new BasicHttpRequest("GET", "/foo?s=bar");
         response.setHeader("Date", DateUtils.formatDate(now));
@@ -501,6 +600,15 @@
     }
 
     @Test
+    public void testResponsesToHEADWithQueryParamsAndExplicitCachingAreCacheable() {
+        policy = new ResponseCachingPolicy(0, true, false, false);
+        request = new BasicHttpRequest("HEAD", "/foo?s=bar");
+        response.setHeader("Date", DateUtils.formatDate(now));
+        response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+        Assert.assertTrue(policy.isResponseCacheable(request, response));
+    }
+
+    @Test
     public void testResponsesToGETWithQueryParamsAndExplicitCachingAreCacheableEvenWhen1_0QueryCachingDisabled() {
         policy = new ResponseCachingPolicy(0, true, true, false);
         request = new BasicHttpRequest("GET", "/foo?s=bar");
@@ -510,6 +618,15 @@
     }
 
     @Test
+    public void testResponsesToHEADWithQueryParamsAndExplicitCachingAreCacheableEvenWhen1_0QueryCachingDisabled() {
+        policy = new ResponseCachingPolicy(0, true, true, false);
+        request = new BasicHttpRequest("HEAD", "/foo?s=bar");
+        response.setHeader("Date", DateUtils.formatDate(now));
+        response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+        Assert.assertTrue(policy.isResponseCacheable(request, response));
+    }
+
+    @Test
     public void getsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheable() {
         request = new BasicHttpRequest("GET", "/foo?s=bar");
         response = new BasicHttpResponse(HttpVersion.HTTP_1_0, HttpStatus.SC_OK, "OK");
@@ -517,6 +634,13 @@
     }
 
     @Test
+    public void headsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheable() {
+        request = new BasicHttpRequest("HEAD", "/foo?s=bar");
+        response = new BasicHttpResponse(HttpVersion.HTTP_1_0, HttpStatus.SC_OK, "OK");
+        Assert.assertFalse(policy.isResponseCacheable(request, response));
+    }
+
+    @Test
     public void getsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheableEvenWithSetting() {
         policy = new ResponseCachingPolicy(0, true, true, false);
         request = new BasicHttpRequest("GET", "/foo?s=bar");
@@ -525,6 +649,14 @@
     }
 
     @Test
+    public void headsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheableEvenWithSetting() {
+        policy = new ResponseCachingPolicy(0, true, true, false);
+        request = new BasicHttpRequest("HEAD", "/foo?s=bar");
+        response = new BasicHttpResponse(HttpVersion.HTTP_1_0, HttpStatus.SC_OK, "OK");
+        Assert.assertFalse(policy.isResponseCacheable(request, response));
+    }
+
+    @Test
     public void getsWithQueryParametersDirectlyFrom1_0OriginsAreCacheableWithExpires() {
         request = new BasicHttpRequest("GET", "/foo?s=bar");
         response = new BasicHttpResponse(HttpVersion.HTTP_1_0, HttpStatus.SC_OK, "OK");
@@ -534,6 +666,16 @@
     }
 
     @Test
+    public void headsWithQueryParametersDirectlyFrom1_0OriginsAreCacheableWithExpires() {
+        policy = new ResponseCachingPolicy(0, true, false, false);
+        request = new BasicHttpRequest("HEAD", "/foo?s=bar");
+        response = new BasicHttpResponse(HttpVersion.HTTP_1_0, HttpStatus.SC_OK, "OK");
+        response.setHeader("Date", DateUtils.formatDate(now));
+        response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+        Assert.assertTrue(policy.isResponseCacheable(request, response));
+    }
+
+    @Test
     public void getsWithQueryParametersDirectlyFrom1_0OriginsCanBeNotCacheableEvenWithExpires() {
         policy = new ResponseCachingPolicy(0, true, true, false);
         request = new BasicHttpRequest("GET", "/foo?s=bar");
@@ -544,6 +686,16 @@
     }
 
     @Test
+    public void headsWithQueryParametersDirectlyFrom1_0OriginsCanBeNotCacheableEvenWithExpires() {
+        policy = new ResponseCachingPolicy(0, true, true, false);
+        request = new BasicHttpRequest("HEAD", "/foo?s=bar");
+        response = new BasicHttpResponse(HttpVersion.HTTP_1_0, HttpStatus.SC_OK, "OK");
+        response.setHeader("Date", DateUtils.formatDate(now));
+        response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+        Assert.assertFalse(policy.isResponseCacheable(request, response));
+    }
+
+    @Test
     public void getsWithQueryParametersFrom1_0OriginsViaProxiesAreNotCacheable() {
         request = new BasicHttpRequest("GET", "/foo?s=bar");
         response.setHeader("Via", "1.0 someproxy");
@@ -551,6 +703,13 @@
     }
 
     @Test
+    public void headsWithQueryParametersFrom1_0OriginsViaProxiesAreNotCacheable() {
+        request = new BasicHttpRequest("HEAD", "/foo?s=bar");
+        response.setHeader("Via", "1.0 someproxy");
+        Assert.assertFalse(policy.isResponseCacheable(request, response));
+    }
+
+    @Test
     public void getsWithQueryParametersFrom1_0OriginsViaProxiesAreCacheableWithExpires() {
         request = new BasicHttpRequest("GET", "/foo?s=bar");
         response.setHeader("Date", DateUtils.formatDate(now));
@@ -560,6 +719,16 @@
     }
 
     @Test
+    public void headsWithQueryParametersFrom1_0OriginsViaProxiesAreCacheableWithExpires() {
+        policy = new ResponseCachingPolicy(0, true, false, false);
+        request = new BasicHttpRequest("HEAD", "/foo?s=bar");
+        response.setHeader("Date", DateUtils.formatDate(now));
+        response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+        response.setHeader("Via", "1.0 someproxy");
+        Assert.assertTrue(policy.isResponseCacheable(request, response));
+    }
+
+    @Test
     public void getsWithQueryParametersFrom1_0OriginsViaProxiesCanNotBeCacheableEvenWithExpires() {
         policy = new ResponseCachingPolicy(0, true, true, true);
         request = new BasicHttpRequest("GET", "/foo?s=bar");
@@ -570,6 +739,16 @@
     }
 
     @Test
+    public void headsWithQueryParametersFrom1_0OriginsViaProxiesCanNotBeCacheableEvenWithExpires() {
+        policy = new ResponseCachingPolicy(0, true, true, true);
+        request = new BasicHttpRequest("HEAD", "/foo?s=bar");
+        response.setHeader("Date", DateUtils.formatDate(now));
+        response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+        response.setHeader("Via", "1.0 someproxy");
+        Assert.assertFalse(policy.isResponseCacheable(request, response));
+    }
+
+    @Test
     public void getsWithQueryParametersFrom1_0OriginsViaExplicitProxiesAreCacheableWithExpires() {
         request = new BasicHttpRequest("GET", "/foo?s=bar");
         response.setHeader("Date", DateUtils.formatDate(now));
@@ -579,6 +758,16 @@
     }
 
     @Test
+    public void headsWithQueryParametersFrom1_0OriginsViaExplicitProxiesAreCacheableWithExpires() {
+        policy = new ResponseCachingPolicy(0, true, false, false);
+        request = new BasicHttpRequest("HEAD", "/foo?s=bar");
+        response.setHeader("Date", DateUtils.formatDate(now));
+        response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+        response.setHeader("Via", "HTTP/1.0 someproxy");
+        Assert.assertTrue(policy.isResponseCacheable(request, response));
+    }
+
+    @Test
     public void getsWithQueryParametersFrom1_0OriginsViaExplicitProxiesCanNotBeCacheableEvenWithExpires() {
         policy = new ResponseCachingPolicy(0, true, true, true);
         request = new BasicHttpRequest("GET", "/foo?s=bar");
@@ -589,6 +778,16 @@
     }
 
     @Test
+    public void headsWithQueryParametersFrom1_0OriginsViaExplicitProxiesCanNotBeCacheableEvenWithExpires() {
+        policy = new ResponseCachingPolicy(0, true, true, true);
+        request = new BasicHttpRequest("HEAD", "/foo?s=bar");
+        response.setHeader("Date", DateUtils.formatDate(now));
+        response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+        response.setHeader("Via", "HTTP/1.0 someproxy");
+        Assert.assertFalse(policy.isResponseCacheable(request, response));
+    }
+
+    @Test
     public void getsWithQueryParametersFrom1_1OriginsVia1_0ProxiesAreCacheableWithExpires() {
         request = new BasicHttpRequest("GET", "/foo?s=bar");
         response = new BasicHttpResponse(HttpVersion.HTTP_1_0, HttpStatus.SC_OK, "OK");
@@ -599,6 +798,17 @@
     }
 
     @Test
+    public void headsWithQueryParametersFrom1_1OriginsVia1_0ProxiesAreCacheableWithExpires() {
+        policy = new ResponseCachingPolicy(0, true, false, false);
+        request = new BasicHttpRequest("HEAD", "/foo?s=bar");
+        response = new BasicHttpResponse(HttpVersion.HTTP_1_0, HttpStatus.SC_OK, "OK");
+        response.setHeader("Date", DateUtils.formatDate(now));
+        response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
+        response.setHeader("Via", "1.1 someproxy");
+        Assert.assertTrue(policy.isResponseCacheable(request, response));
+    }
+
+    @Test
     public void notCacheableIfExpiresEqualsDateAndNoCacheControl() {
         response.setHeader("Date", DateUtils.formatDate(now));
         response.setHeader("Expires", DateUtils.formatDate(now));
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseProtocolCompliance.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseProtocolCompliance.java
index 24ca14f..6460e85 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseProtocolCompliance.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseProtocolCompliance.java
@@ -26,8 +26,8 @@
  */
 package org.apache.http.impl.client.cache;
 
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.assertTrue;
 
 import java.io.ByteArrayInputStream;
 import java.util.Date;
@@ -151,4 +151,5 @@
         }
         assertTrue(closed.set || bais.read() == -1);
     }
+
 }
diff --git a/httpclient-osgi/pom.xml b/httpclient-osgi/pom.xml
index f839e11..9c74fa4 100644
--- a/httpclient-osgi/pom.xml
+++ b/httpclient-osgi/pom.xml
@@ -54,7 +54,7 @@
     <dependency>
       <groupId>org.apache.httpcomponents</groupId>
       <artifactId>httpcore</artifactId>
-      <scope>compile</scope>
+      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>commons-codec</groupId>
@@ -133,6 +133,8 @@
             javax.crypto,
             javax.crypto.spec,
             javax.naming,
+            javax.naming.directory,
+            javax.naming.ldap,
             javax.net,
             javax.net.ssl,
             javax.security.auth.x500,
diff --git a/httpclient-win/pom.xml b/httpclient-win/pom.xml
index 5928785..f88e96a 100644
--- a/httpclient-win/pom.xml
+++ b/httpclient-win/pom.xml
@@ -74,7 +74,7 @@
 
       <plugin>
         <artifactId>maven-javadoc-plugin</artifactId>
-         <version>${hc.javadoc.version}</version>
+        <version>${hc.javadoc.version}</version>
         <configuration>
           <!-- reduce console output. Can override with -Dquiet=false -->
           <quiet>true</quiet>
diff --git a/httpclient-win/src/main/java/org/apache/http/impl/auth/win/WindowsNegotiateScheme.java b/httpclient-win/src/main/java/org/apache/http/impl/auth/win/WindowsNegotiateScheme.java
index 5b3c7b8..29a6b42 100644
--- a/httpclient-win/src/main/java/org/apache/http/impl/auth/win/WindowsNegotiateScheme.java
+++ b/httpclient-win/src/main/java/org/apache/http/impl/auth/win/WindowsNegotiateScheme.java
@@ -77,7 +77,7 @@
     private final String servicePrincipalName;
 
     private CredHandle clientCred;
-    private CtxtHandle sppicontext;
+    private CtxtHandle sspiContext;
     private boolean continueNeeded;
     private String challenge;
 
@@ -101,15 +101,15 @@
                 throw new Win32Exception(rc);
             }
         }
-        if (sppicontext != null && !sppicontext.isNull()) {
-            final int rc = Secur32.INSTANCE.DeleteSecurityContext(sppicontext);
+        if (sspiContext != null && !sspiContext.isNull()) {
+            final int rc = Secur32.INSTANCE.DeleteSecurityContext(sspiContext);
             if (WinError.SEC_E_OK != rc) {
                 throw new Win32Exception(rc);
             }
         }
         continueNeeded = true; // waiting
         clientCred = null;
-        sppicontext = null;
+        sspiContext = null;
     }
 
     @Override
@@ -205,7 +205,7 @@
                 final SecBufferDesc continueTokenBuffer = new SecBufferDesc(
                         Sspi.SECBUFFER_TOKEN, continueTokenBytes);
                 final String targetName = getServicePrincipalName(context);
-                response = getToken(this.sppicontext, continueTokenBuffer, targetName);
+                response = getToken(this.sspiContext, continueTokenBuffer, targetName);
             } catch (RuntimeException ex) {
                 failAuthCleanup();
                 if (ex instanceof Win32Exception) {
@@ -273,10 +273,10 @@
         final SecBufferDesc token = new SecBufferDesc(
                 Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE);
 
-        sppicontext = new CtxtHandle();
+        sspiContext = new CtxtHandle();
         final int rc = Secur32.INSTANCE.InitializeSecurityContext(clientCred,
-                continueCtx, targetName, Sspi.ISC_REQ_CONNECTION | Sspi.ISC_REQ_DELEGATE, 0,
-                Sspi.SECURITY_NATIVE_DREP, continueToken, 0, sppicontext, token,
+                continueCtx, targetName, Sspi.ISC_REQ_DELEGATE | Sspi.ISC_REQ_MUTUAL_AUTH, 0,
+                Sspi.SECURITY_NATIVE_DREP, continueToken, 0, sspiContext, token,
                 attr, null);
         switch (rc) {
             case WinError.SEC_I_CONTINUE_NEEDED:
diff --git a/httpclient-win/src/test/java/org/apache/http/impl/auth/win/TestWindowsNegotiateScheme.java b/httpclient-win/src/test/java/org/apache/http/impl/auth/win/TestWindowsNegotiateScheme.java
index a89eb40..6ff2d74 100644
--- a/httpclient-win/src/test/java/org/apache/http/impl/auth/win/TestWindowsNegotiateScheme.java
+++ b/httpclient-win/src/test/java/org/apache/http/impl/auth/win/TestWindowsNegotiateScheme.java
@@ -91,7 +91,7 @@
         Assume.assumeTrue("Test can only be run on Windows", WinHttpClients.isWinAuthAvailable());
 
         // HTTPCLIENT-1545
-        // If a service principle name (SPN) from outside your Windows domain tree (e.g., HTTP/EXAMPLE.COM) is used,
+        // If a service principle name (SPN) from outside your Windows domain tree (e.g., HTTP/example.com) is used,
         // InitializeSecurityContext will return SEC_E_DOWNGRADE_DETECTED (decimal: -2146892976, hex: 0x80090350).
         // Because WindowsNegotiateScheme wasn't setting the completed state correctly when authentication fails,
         // HttpClient goes into an infinite loop, constantly retrying the negotiate authentication to kingdom
@@ -101,7 +101,7 @@
         final Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
             .register(AuthSchemes.SPNEGO, new AuthSchemeProvider() {
                 public AuthScheme create(final HttpContext context) {
-                    return new WindowsNegotiateSchemeGetTokenFail(AuthSchemes.SPNEGO, "HTTP/EXAMPLE.COM");
+                    return new WindowsNegotiateSchemeGetTokenFail(AuthSchemes.SPNEGO, "HTTP/example.com");
                 }
             }).build();
         final CredentialsProvider credsProvider =
@@ -129,7 +129,10 @@
         @Override
         String getToken(final CtxtHandle continueCtx, final SecBufferDesc continueToken, final String targetName) {
             dispose();
-            throw new Win32Exception(WinError.SEC_E_DOWNGRADE_DETECTED);
+            /* We will rather throw SEC_E_TARGET_UNKNOWN because SEC_E_DOWNGRADE_DETECTED is not
+             * available on Windows XP and this unit test always fails.
+             */
+            throw new Win32Exception(WinError.SEC_E_TARGET_UNKNOWN);
         }
 
     }
diff --git a/httpclient/pom.xml b/httpclient/pom.xml
index a693c73..c2fa14f 100644
--- a/httpclient/pom.xml
+++ b/httpclient/pom.xml
@@ -133,7 +133,7 @@
 
       <plugin>
         <artifactId>maven-javadoc-plugin</artifactId>
-         <version>${hc.javadoc.version}</version>
+        <version>${hc.javadoc.version}</version>
         <configuration>
           <!-- reduce console output. Can override with -Dquiet=false -->
           <quiet>true</quiet>
diff --git a/httpclient/src/examples/org/apache/http/examples/client/ClientConfiguration.java b/httpclient/src/examples/org/apache/http/examples/client/ClientConfiguration.java
index 6f7d27a..ac6aa39 100644
--- a/httpclient/src/examples/org/apache/http/examples/client/ClientConfiguration.java
+++ b/httpclient/src/examples/org/apache/http/examples/client/ClientConfiguration.java
@@ -61,7 +61,6 @@
 import org.apache.http.conn.socket.ConnectionSocketFactory;
 import org.apache.http.conn.socket.PlainConnectionSocketFactory;
 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
-import org.apache.http.conn.ssl.SSLContexts;
 import org.apache.http.impl.DefaultHttpResponseFactory;
 import org.apache.http.impl.client.BasicCookieStore;
 import org.apache.http.impl.client.BasicCredentialsProvider;
@@ -80,6 +79,7 @@
 import org.apache.http.message.BasicHeader;
 import org.apache.http.message.BasicLineParser;
 import org.apache.http.message.LineParser;
+import org.apache.http.ssl.SSLContexts;
 import org.apache.http.util.CharArrayBuffer;
 
 /**
diff --git a/httpclient/src/examples/org/apache/http/examples/client/ClientCustomPublicSuffixList.java b/httpclient/src/examples/org/apache/http/examples/client/ClientCustomPublicSuffixList.java
new file mode 100644
index 0000000..0246caf
--- /dev/null
+++ b/httpclient/src/examples/org/apache/http/examples/client/ClientCustomPublicSuffixList.java
@@ -0,0 +1,95 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.examples.client;
+
+import java.net.URL;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.client.config.CookieSpecs;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.config.Lookup;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.ssl.DefaultHostnameVerifier;
+import org.apache.http.conn.util.PublicSuffixMatcher;
+import org.apache.http.conn.util.PublicSuffixMatcherLoader;
+import org.apache.http.cookie.CookieSpecProvider;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.cookie.RFC6265CookieSpecProvider;
+import org.apache.http.util.EntityUtils;
+
+/**
+ * This example demonstrates how to use a custom public suffix list.
+ */
+public class ClientCustomPublicSuffixList {
+
+    public final static void main(String[] args) throws Exception {
+
+        // Use PublicSuffixMatcherLoader to load public suffix list from a file,
+        // resource or from an arbitrary URL
+        PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.load(
+                new URL("https://publicsuffix.org/list/effective_tld_names.dat"));
+
+        // Please use the publicsuffix.org URL to download the list no more than once per day !!!
+        // Please consider making a local copy !!!
+
+        DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher);
+
+        RFC6265CookieSpecProvider cookieSpecProvider = new RFC6265CookieSpecProvider(publicSuffixMatcher);
+        Lookup<CookieSpecProvider> cookieSpecRegistry = RegistryBuilder.<CookieSpecProvider>create()
+                .register(CookieSpecs.DEFAULT, cookieSpecProvider)
+                .register(CookieSpecs.STANDARD, cookieSpecProvider)
+                .register(CookieSpecs.STANDARD_STRICT, cookieSpecProvider)
+                .build();
+
+        CloseableHttpClient httpclient = HttpClients.custom()
+                .setSSLHostnameVerifier(hostnameVerifier)
+                .setDefaultCookieSpecRegistry(cookieSpecRegistry)
+                .build();
+        try {
+
+            HttpGet httpget = new HttpGet("https://remotehost/");
+
+            System.out.println("executing request " + httpget.getRequestLine());
+
+            CloseableHttpResponse response = httpclient.execute(httpget);
+            try {
+                HttpEntity entity = response.getEntity();
+
+                System.out.println("----------------------------------------");
+                System.out.println(response.getStatusLine());
+                EntityUtils.consume(entity);
+            } finally {
+                response.close();
+            }
+        } finally {
+            httpclient.close();
+        }
+    }
+
+}
diff --git a/httpclient/src/examples/org/apache/http/examples/client/ClientCustomSSL.java b/httpclient/src/examples/org/apache/http/examples/client/ClientCustomSSL.java
index ec3a565..eb16287 100644
--- a/httpclient/src/examples/org/apache/http/examples/client/ClientCustomSSL.java
+++ b/httpclient/src/examples/org/apache/http/examples/client/ClientCustomSSL.java
@@ -27,8 +27,6 @@
 package org.apache.http.examples.client;
 
 import java.io.File;
-import java.io.FileInputStream;
-import java.security.KeyStore;
 
 import javax.net.ssl.SSLContext;
 
@@ -36,10 +34,10 @@
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
-import org.apache.http.conn.ssl.SSLContexts;
 import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContexts;
 import org.apache.http.util.EntityUtils;
 
 /**
@@ -49,17 +47,10 @@
 public class ClientCustomSSL {
 
     public final static void main(String[] args) throws Exception {
-        KeyStore trustStore  = KeyStore.getInstance(KeyStore.getDefaultType());
-        FileInputStream instream = new FileInputStream(new File("my.keystore"));
-        try {
-            trustStore.load(instream, "nopassword".toCharArray());
-        } finally {
-            instream.close();
-        }
-
         // Trust own CA and all self-signed certs
         SSLContext sslcontext = SSLContexts.custom()
-                .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
+                .loadTrustMaterial(new File("my.keystore"), "nopassword".toCharArray(),
+                        new TrustSelfSignedStrategy())
                 .build();
         // Allow TLSv1 protocol only
         SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
@@ -74,7 +65,7 @@
 
             HttpGet httpget = new HttpGet("https://localhost/");
 
-            System.out.println("executing request" + httpget.getRequestLine());
+            System.out.println("executing request " + httpget.getRequestLine());
 
             CloseableHttpResponse response = httpclient.execute(httpget);
             try {
@@ -82,9 +73,6 @@
 
                 System.out.println("----------------------------------------");
                 System.out.println(response.getStatusLine());
-                if (entity != null) {
-                    System.out.println("Response content length: " + entity.getContentLength());
-                }
                 EntityUtils.consume(entity);
             } finally {
                 response.close();
diff --git a/httpclient/src/examples/org/apache/http/examples/client/ClientEvictExpiredConnections.java b/httpclient/src/examples/org/apache/http/examples/client/ClientEvictExpiredConnections.java
index 3c734d1..b349709 100644
--- a/httpclient/src/examples/org/apache/http/examples/client/ClientEvictExpiredConnections.java
+++ b/httpclient/src/examples/org/apache/http/examples/client/ClientEvictExpiredConnections.java
@@ -30,10 +30,10 @@
 
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
-import org.apache.http.conn.HttpClientConnectionManager;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.pool.PoolStats;
 import org.apache.http.util.EntityUtils;
 
 /**
@@ -47,6 +47,8 @@
         cm.setMaxTotal(100);
         CloseableHttpClient httpclient = HttpClients.custom()
                 .setConnectionManager(cm)
+                .evictExpiredConnections()
+                .evictIdleConnections(5L, TimeUnit.SECONDS)
                 .build();
         try {
             // create an array of URIs to perform GETs on
@@ -56,9 +58,6 @@
                 "http://hc.apache.org/httpcomponents-client-ga/",
             };
 
-            IdleConnectionEvictor connEvictor = new IdleConnectionEvictor(cm);
-            connEvictor.start();
-
             for (int i = 0; i < urisToGet.length; i++) {
                 String requestURI = urisToGet[i];
                 HttpGet request = new HttpGet(requestURI);
@@ -75,54 +74,18 @@
                 }
             }
 
-            // Sleep 10 sec and let the connection evictor do its job
-            Thread.sleep(20000);
+            PoolStats stats1 = cm.getTotalStats();
+            System.out.println("Connections kept alive: " + stats1.getAvailable());
 
-            // Shut down the evictor thread
-            connEvictor.shutdown();
-            connEvictor.join();
+            // Sleep 10 sec and let the connection evictor do its job
+            Thread.sleep(10000);
+
+            PoolStats stats2 = cm.getTotalStats();
+            System.out.println("Connections kept alive: " + stats2.getAvailable());
 
         } finally {
             httpclient.close();
         }
     }
 
-    public static class IdleConnectionEvictor extends Thread {
-
-        private final HttpClientConnectionManager connMgr;
-
-        private volatile boolean shutdown;
-
-        public IdleConnectionEvictor(HttpClientConnectionManager connMgr) {
-            super();
-            this.connMgr = connMgr;
-        }
-
-        @Override
-        public void run() {
-            try {
-                while (!shutdown) {
-                    synchronized (this) {
-                        wait(5000);
-                        // Close expired connections
-                        connMgr.closeExpiredConnections();
-                        // Optionally, close connections
-                        // that have been idle longer than 5 sec
-                        connMgr.closeIdleConnections(5, TimeUnit.SECONDS);
-                    }
-                }
-            } catch (InterruptedException ex) {
-                // terminate
-            }
-        }
-
-        public void shutdown() {
-            shutdown = true;
-            synchronized (this) {
-                notifyAll();
-            }
-        }
-
-    }
-
 }
diff --git a/httpclient/src/examples/org/apache/http/examples/client/ClientExecuteSOCKS.java b/httpclient/src/examples/org/apache/http/examples/client/ClientExecuteSOCKS.java
index 0010088..3cf62d7 100644
--- a/httpclient/src/examples/org/apache/http/examples/client/ClientExecuteSOCKS.java
+++ b/httpclient/src/examples/org/apache/http/examples/client/ClientExecuteSOCKS.java
@@ -71,7 +71,7 @@
             HttpGet request = new HttpGet("/");
 
             System.out.println("Executing request " + request + " to " + target + " via SOCKS proxy " + socksaddr);
-            CloseableHttpResponse response = httpclient.execute(target, request);
+            CloseableHttpResponse response = httpclient.execute(target, request, context);
             try {
                 System.out.println("----------------------------------------");
                 System.out.println(response.getStatusLine());
diff --git a/httpclient/src/main/java-deprecated/org/apache/http/client/params/AuthPolicy.java b/httpclient/src/main/java-deprecated/org/apache/http/client/params/AuthPolicy.java
index c04a6c1..768ddf9 100644
--- a/httpclient/src/main/java-deprecated/org/apache/http/client/params/AuthPolicy.java
+++ b/httpclient/src/main/java-deprecated/org/apache/http/client/params/AuthPolicy.java
@@ -67,7 +67,7 @@
      *
      * @since 4.1
      */
-    public static final String SPNEGO = "negotiate";
+    public static final String SPNEGO = "Negotiate";
 
     /**
      * Kerberos Authentication scheme.
diff --git a/httpclient/src/main/java/org/apache/http/conn/ssl/PrivateKeyDetails.java b/httpclient/src/main/java-deprecated/org/apache/http/conn/ssl/PrivateKeyDetails.java
similarity index 95%
rename from httpclient/src/main/java/org/apache/http/conn/ssl/PrivateKeyDetails.java
rename to httpclient/src/main/java-deprecated/org/apache/http/conn/ssl/PrivateKeyDetails.java
index 1abb449..ff94d4d 100644
--- a/httpclient/src/main/java/org/apache/http/conn/ssl/PrivateKeyDetails.java
+++ b/httpclient/src/main/java-deprecated/org/apache/http/conn/ssl/PrivateKeyDetails.java
@@ -35,7 +35,10 @@
  * Private key details.
  *
  * @since 4.3
+ *
+ * @deprecated (4.4) use {@link org.apache.http.ssl.PrivateKeyDetails}.
  */
+@Deprecated
 public final class PrivateKeyDetails {
 
     private final String type;
diff --git a/httpclient/src/main/java/org/apache/http/conn/ssl/PrivateKeyStrategy.java b/httpclient/src/main/java-deprecated/org/apache/http/conn/ssl/PrivateKeyStrategy.java
similarity index 94%
rename from httpclient/src/main/java/org/apache/http/conn/ssl/PrivateKeyStrategy.java
rename to httpclient/src/main/java-deprecated/org/apache/http/conn/ssl/PrivateKeyStrategy.java
index 4238e28..119869b 100644
--- a/httpclient/src/main/java/org/apache/http/conn/ssl/PrivateKeyStrategy.java
+++ b/httpclient/src/main/java-deprecated/org/apache/http/conn/ssl/PrivateKeyStrategy.java
@@ -33,7 +33,10 @@
  * A strategy allowing for a choice of an alias during SSL authentication.
  *
  * @since 4.3
+ *
+ * @deprecated (4.4) use {@link org.apache.http.ssl.PrivateKeyStrategy}.
  */
+@Deprecated
 public interface PrivateKeyStrategy {
 
     /**
diff --git a/httpclient/src/main/java/org/apache/http/conn/ssl/SSLContextBuilder.java b/httpclient/src/main/java-deprecated/org/apache/http/conn/ssl/SSLContextBuilder.java
similarity index 97%
rename from httpclient/src/main/java/org/apache/http/conn/ssl/SSLContextBuilder.java
rename to httpclient/src/main/java-deprecated/org/apache/http/conn/ssl/SSLContextBuilder.java
index c71810b..44aa397 100644
--- a/httpclient/src/main/java/org/apache/http/conn/ssl/SSLContextBuilder.java
+++ b/httpclient/src/main/java-deprecated/org/apache/http/conn/ssl/SSLContextBuilder.java
@@ -39,7 +39,7 @@
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.util.HashMap;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.Map;
 import java.util.Set;
 
@@ -57,8 +57,11 @@
  * Builder for {@link SSLContext} instances.
  *
  * @since 4.3
+ *
+ * @deprecated (4.4) use {@link org.apache.http.ssl.SSLContextBuilder}.
  */
 @NotThreadSafe
+@Deprecated
 public class SSLContextBuilder {
 
     static final String TLS   = "TLS";
@@ -71,8 +74,8 @@
 
     public SSLContextBuilder() {
         super();
-        this.keymanagers = new HashSet<KeyManager>();
-        this.trustmanagers = new HashSet<TrustManager>();
+        this.keymanagers = new LinkedHashSet<KeyManager>();
+        this.trustmanagers = new LinkedHashSet<TrustManager>();
     }
 
     public SSLContextBuilder useTLS() {
diff --git a/httpclient/src/main/java/org/apache/http/conn/ssl/SSLContexts.java b/httpclient/src/main/java-deprecated/org/apache/http/conn/ssl/SSLContexts.java
similarity index 97%
rename from httpclient/src/main/java/org/apache/http/conn/ssl/SSLContexts.java
rename to httpclient/src/main/java-deprecated/org/apache/http/conn/ssl/SSLContexts.java
index 3244611..26ab3e5 100644
--- a/httpclient/src/main/java/org/apache/http/conn/ssl/SSLContexts.java
+++ b/httpclient/src/main/java-deprecated/org/apache/http/conn/ssl/SSLContexts.java
@@ -38,8 +38,11 @@
  * {@link SSLContext} factory methods.
  *
  * @since 4.3
+ *
+ * @deprecated (4.4) use {@link org.apache.http.ssl.SSLContexts}.
  */
 @Immutable
+@Deprecated
 public class SSLContexts {
 
     /**
diff --git a/httpclient/src/main/java-deprecated/org/apache/http/impl/client/AbstractHttpClient.java b/httpclient/src/main/java-deprecated/org/apache/http/impl/client/AbstractHttpClient.java
index a2fe633..a1f7345 100644
--- a/httpclient/src/main/java-deprecated/org/apache/http/impl/client/AbstractHttpClient.java
+++ b/httpclient/src/main/java-deprecated/org/apache/http/impl/client/AbstractHttpClient.java
@@ -53,6 +53,7 @@
 import org.apache.http.client.RedirectStrategy;
 import org.apache.http.client.RequestDirector;
 import org.apache.http.client.UserTokenHandler;
+import org.apache.http.client.config.CookieSpecs;
 import org.apache.http.client.config.RequestConfig;
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.params.AuthPolicy;
@@ -372,6 +373,9 @@
     protected CookieSpecRegistry createCookieSpecRegistry() {
         final CookieSpecRegistry registry = new CookieSpecRegistry();
         registry.register(
+                CookieSpecs.DEFAULT,
+                new BestMatchSpecFactory());
+        registry.register(
                 CookiePolicy.BEST_MATCH,
                 new BestMatchSpecFactory());
         registry.register(
diff --git a/httpclient/src/main/java-deprecated/org/apache/http/impl/client/DefaultHttpClient.java b/httpclient/src/main/java-deprecated/org/apache/http/impl/client/DefaultHttpClient.java
index 8ef28a4..e64b008 100644
--- a/httpclient/src/main/java-deprecated/org/apache/http/impl/client/DefaultHttpClient.java
+++ b/httpclient/src/main/java-deprecated/org/apache/http/impl/client/DefaultHttpClient.java
@@ -110,7 +110,7 @@
  *
  * @since 4.0
  *
- * @deprecated (4.3) use {@link HttpClientBuilder}.
+ * @deprecated (4.3) use {@link HttpClientBuilder} see also {@link CloseableHttpClient}.
  */
 @ThreadSafe
 @Deprecated
diff --git a/httpclient/src/main/java/org/apache/http/client/config/AuthSchemes.java b/httpclient/src/main/java/org/apache/http/client/config/AuthSchemes.java
index 3b6d803..58f5d61 100644
--- a/httpclient/src/main/java/org/apache/http/client/config/AuthSchemes.java
+++ b/httpclient/src/main/java/org/apache/http/client/config/AuthSchemes.java
@@ -38,30 +38,33 @@
 public final class AuthSchemes {
 
     /**
-     * Basic authentication scheme as defined in RFC2617 (considered inherently
-     * insecure, but most widely supported)
+     * Basic authentication scheme as defined in RFC 2617 (considered inherently
+     * insecure, but most widely supported).
      */
     public static final String BASIC = "Basic";
 
     /**
-     * Digest authentication scheme as defined in RFC2617.
+     * Digest authentication scheme as defined in RFC 2617.
      */
     public static final String DIGEST = "Digest";
 
     /**
-     * The NTLM scheme is a proprietary Microsoft Windows Authentication
-     * protocol (considered to be the most secure among currently supported
-     * authentication schemes).
+     * The NTLM authentication scheme is a proprietary Microsoft Windows
+     * authentication protocol as defined in [MS-NLMP].
      */
     public static final String NTLM = "NTLM";
 
     /**
-     * SPNEGO Authentication scheme.
+     * SPNEGO authentication scheme as defined in RFC 4559 and RFC 4178
+     * (considered to be the most secure among currently supported
+     * authentication schemes if Kerberos is selected).
      */
-    public static final String SPNEGO = "negotiate";
+    public static final String SPNEGO = "Negotiate";
 
     /**
-     * Kerberos Authentication scheme.
+     * Kerberos authentication scheme as defined in RFC 4120
+     * (considered to be the most secure among currently supported
+     * authentication schemes).
      */
     public static final String KERBEROS = "Kerberos";
 
diff --git a/httpclient/src/main/java/org/apache/http/client/config/CookieSpecs.java b/httpclient/src/main/java/org/apache/http/client/config/CookieSpecs.java
index d821d23..86477f2 100644
--- a/httpclient/src/main/java/org/apache/http/client/config/CookieSpecs.java
+++ b/httpclient/src/main/java/org/apache/http/client/config/CookieSpecs.java
@@ -52,11 +52,18 @@
     public static final String NETSCAPE = "netscape";
 
     /**
-     * The RFC 2965 compliant policy (standard).
+     * The RFC 6265 compliant policy (interoprability profile).
      */
     public static final String STANDARD = "standard";
 
     /**
+     * The RFC 6265 compliant policy (strict profile).
+     *
+     * @since 4.4
+     */
+    public static final String STANDARD_STRICT = "standard-strict";
+
+    /**
      * The default 'best match' policy.
      *
      * @deprecated (4.4) use {link #DEFAULT}.
diff --git a/httpclient/src/main/java/org/apache/http/client/methods/RequestBuilder.java b/httpclient/src/main/java/org/apache/http/client/methods/RequestBuilder.java
index a8e4de2..763bcb5 100644
--- a/httpclient/src/main/java/org/apache/http/client/methods/RequestBuilder.java
+++ b/httpclient/src/main/java/org/apache/http/client/methods/RequestBuilder.java
@@ -142,6 +142,27 @@
         return new RequestBuilder(HttpHead.METHOD_NAME, uri);
     }
 
+    /**
+     * @since 4.4
+     */
+    public static RequestBuilder patch() {
+        return new RequestBuilder(HttpPatch.METHOD_NAME);
+    }
+
+    /**
+     * @since 4.4
+     */
+    public static RequestBuilder patch(final URI uri) {
+        return new RequestBuilder(HttpPatch.METHOD_NAME, uri);
+    }
+
+    /**
+     * @since 4.4
+     */
+    public static RequestBuilder patch(final String uri) {
+        return new RequestBuilder(HttpPatch.METHOD_NAME, uri);
+    }
+
     public static RequestBuilder post() {
         return new RequestBuilder(HttpPost.METHOD_NAME);
     }
diff --git a/httpclient/src/main/java/org/apache/http/client/protocol/RequestAddCookies.java b/httpclient/src/main/java/org/apache/http/client/protocol/RequestAddCookies.java
index 3f4c016..b548d04 100644
--- a/httpclient/src/main/java/org/apache/http/client/protocol/RequestAddCookies.java
+++ b/httpclient/src/main/java/org/apache/http/client/protocol/RequestAddCookies.java
@@ -52,7 +52,6 @@
 import org.apache.http.cookie.CookieOrigin;
 import org.apache.http.cookie.CookieSpec;
 import org.apache.http.cookie.CookieSpecProvider;
-import org.apache.http.cookie.SetCookie2;
 import org.apache.http.protocol.HttpContext;
 import org.apache.http.util.Args;
 import org.apache.http.util.TextUtils;
@@ -156,10 +155,11 @@
         }
         final CookieSpec cookieSpec = provider.create(clientContext);
         // Get all cookies available in the HTTP state
-        final List<Cookie> cookies = new ArrayList<Cookie>(cookieStore.getCookies());
+        final List<Cookie> cookies = cookieStore.getCookies();
         // Find cookies matching the given origin
         final List<Cookie> matchedCookies = new ArrayList<Cookie>();
         final Date now = new Date();
+        boolean expired = false;
         for (final Cookie cookie : cookies) {
             if (!cookie.isExpired(now)) {
                 if (cookieSpec.match(cookie, cookieOrigin)) {
@@ -172,8 +172,15 @@
                 if (this.log.isDebugEnabled()) {
                     this.log.debug("Cookie " + cookie + " expired");
                 }
+                expired = true;
             }
         }
+        // Per RFC 6265, 5.3
+        // The user agent must evict all expired cookies if, at any time, an expired cookie
+        // exists in the cookie store
+        if (expired) {
+            cookieStore.clearExpired(now);
+        }
         // Generate Cookie request headers
         if (!matchedCookies.isEmpty()) {
             final List<Header> headers = cookieSpec.formatCookies(matchedCookies);
@@ -184,19 +191,10 @@
 
         final int ver = cookieSpec.getVersion();
         if (ver > 0) {
-            boolean needVersionHeader = false;
-            for (final Cookie cookie : matchedCookies) {
-                if (ver != cookie.getVersion() || !(cookie instanceof SetCookie2)) {
-                    needVersionHeader = true;
-                }
-            }
-
-            if (needVersionHeader) {
-                final Header header = cookieSpec.getVersionHeader();
-                if (header != null) {
-                    // Advertise cookie version support
-                    request.addHeader(header);
-                }
+            final Header header = cookieSpec.getVersionHeader();
+            if (header != null) {
+                // Advertise cookie version support
+                request.addHeader(header);
             }
         }
 
diff --git a/httpclient/src/main/java/org/apache/http/client/utils/URLEncodedUtils.java b/httpclient/src/main/java/org/apache/http/client/utils/URLEncodedUtils.java
index 49576c5..958a98a 100644
--- a/httpclient/src/main/java/org/apache/http/client/utils/URLEncodedUtils.java
+++ b/httpclient/src/main/java/org/apache/http/client/utils/URLEncodedUtils.java
@@ -28,6 +28,9 @@
 package org.apache.http.client.utils;
 
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
 import java.net.URI;
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
@@ -45,12 +48,12 @@
 import org.apache.http.NameValuePair;
 import org.apache.http.annotation.Immutable;
 import org.apache.http.entity.ContentType;
-import org.apache.http.message.BasicHeaderValueParser;
 import org.apache.http.message.BasicNameValuePair;
 import org.apache.http.message.ParserCursor;
+import org.apache.http.message.TokenParser;
 import org.apache.http.protocol.HTTP;
+import org.apache.http.util.Args;
 import org.apache.http.util.CharArrayBuffer;
-import org.apache.http.util.EntityUtils;
 
 /**
  * A collection of utilities for encoding URLs.
@@ -87,17 +90,14 @@
     public static List <NameValuePair> parse(final URI uri, final String charset) {
         final String query = uri.getRawQuery();
         if (query != null && !query.isEmpty()) {
-            final List<NameValuePair> result = new ArrayList<NameValuePair>();
-            final Scanner scanner = new Scanner(query);
-            parse(result, scanner, QP_SEP_PATTERN, charset);
-            return result;
+            return parse(query, Charset.forName(charset));
         }
         return Collections.emptyList();
     }
 
     /**
-     * Returns a list of {@link NameValuePair NameValuePairs} as parsed from an {@link HttpEntity}. The encoding is
-     * taken from the entity's Content-Encoding header.
+     * 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.
      *
@@ -110,17 +110,33 @@
     public static List <NameValuePair> parse(
             final HttpEntity entity) throws IOException {
         final ContentType contentType = ContentType.get(entity);
-        if (contentType != null && contentType.getMimeType().equalsIgnoreCase(CONTENT_TYPE)) {
-            final String content = EntityUtils.toString(entity, Consts.ASCII);
-            if (content != null && !content.isEmpty()) {
-                Charset charset = contentType.getCharset();
-                if (charset == null) {
-                    charset = HTTP.DEF_CONTENT_CHARSET;
-                }
-                return parse(content, charset, QP_SEPS);
-            }
+        if (contentType == null || !contentType.getMimeType().equalsIgnoreCase(CONTENT_TYPE)) {
+            return Collections.emptyList();
         }
-        return Collections.emptyList();
+        final long len = entity.getContentLength();
+        Args.check(len <= Integer.MAX_VALUE, "HTTP entity is too large");
+        final Charset charset = contentType.getCharset() != null ? contentType.getCharset() : HTTP.DEF_CONTENT_CHARSET;
+        final InputStream instream = entity.getContent();
+        if (instream == null) {
+            return Collections.emptyList();
+        }
+        final CharArrayBuffer buf;
+        try {
+            buf = new CharArrayBuffer(len > 0 ? (int) len : 1024);
+            final Reader reader = new InputStreamReader(instream, charset);
+            final char[] tmp = new char[1024];
+            int l;
+            while((l = reader.read(tmp)) != -1) {
+                buf.append(tmp, 0, l);
+            }
+
+        } finally {
+            instream.close();
+        }
+        if (buf.length() == 0) {
+            return Collections.emptyList();
+        }
+        return parse(buf, charset, QP_SEP_A);
     }
 
     /**
@@ -151,12 +167,15 @@
      *            Input that contains the parameters to parse.
      * @param charset
      *            Encoding to use when decoding the parameters.
+     *
+     * @deprecated (4.4) use {@link #parse(String, java.nio.charset.Charset)}
      */
+    @Deprecated
     public static void parse(
-            final List <NameValuePair> parameters,
+            final List<NameValuePair> parameters,
             final Scanner scanner,
             final String charset) {
-        parse(parameters, scanner, QP_SEP_PATTERN, charset);
+        parse(parameters, scanner, "[" + QP_SEP_A + QP_SEP_S + "]", charset);
     }
 
     /**
@@ -174,7 +193,10 @@
      *            The Pattern string for parameter separators, by convention {@code "[&;]"}
      * @param charset
      *            Encoding to use when decoding the parameters.
+     *
+     * @deprecated (4.4) use {@link #parse(org.apache.http.util.CharArrayBuffer, java.nio.charset.Charset, char...)}
      */
+    @Deprecated
     public static void parse(
             final List <NameValuePair> parameters,
             final Scanner scanner,
@@ -182,8 +204,8 @@
             final String charset) {
         scanner.useDelimiter(parameterSepartorPattern);
         while (scanner.hasNext()) {
-            String name = null;
-            String value = null;
+            final String name;
+            final String value;
             final String token = scanner.next();
             final int i = token.indexOf(NAME_VALUE_SEPARATOR);
             if (i != -1) {
@@ -191,22 +213,13 @@
                 value = decodeFormFields(token.substring(i + 1).trim(), charset);
             } else {
                 name = decodeFormFields(token.trim(), charset);
+                value = null;
             }
             parameters.add(new BasicNameValuePair(name, value));
         }
     }
 
     /**
-     * Query parameter separators.
-     */
-    private static final char[] QP_SEPS = new char[] { QP_SEP_A, QP_SEP_S };
-
-    /**
-     * Query parameter separator pattern.
-     */
-    private static final String QP_SEP_PATTERN = "[" + new String(QP_SEPS) + "]";
-
-    /**
      * Returns a list of {@link NameValuePair NameValuePairs} as parsed from the given string using the given character
      * encoding. By convention, {@code '&'} and {@code ';'} are accepted as parameter separators.
      *
@@ -219,7 +232,9 @@
      * @since 4.2
      */
     public static List<NameValuePair> parse(final String s, final Charset charset) {
-        return parse(s, charset, QP_SEPS);
+        final CharArrayBuffer buffer = new CharArrayBuffer(s.length());
+        buffer.append(s);
+        return parse(buffer, charset, QP_SEP_A, QP_SEP_S);
     }
 
     /**
@@ -230,27 +245,64 @@
      *            text to parse.
      * @param charset
      *            Encoding to use when decoding the parameters.
-     * @param parameterSeparator
-     *            The characters used to separate parameters, by convention, {@code '&'} and {@code ';'}.
+     * @param separators
+     *            element separators.
      * @return a list of {@link NameValuePair} as built from the URI's query portion.
      *
      * @since 4.3
      */
-    public static List<NameValuePair> parse(final String s, final Charset charset, final char... parameterSeparator) {
+    public static List<NameValuePair> parse(final String s, final Charset charset, final char... separators) {
         if (s == null) {
             return Collections.emptyList();
         }
-        final BasicHeaderValueParser parser = BasicHeaderValueParser.INSTANCE;
         final CharArrayBuffer buffer = new CharArrayBuffer(s.length());
         buffer.append(s);
-        final ParserCursor cursor = new ParserCursor(0, buffer.length());
+        return parse(buffer, charset, separators);
+    }
+
+    /**
+     * Returns a list of {@link NameValuePair NameValuePairs} as parsed from the given string using
+     * the given character encoding.
+     *
+     * @param buf
+     *            text to parse.
+     * @param charset
+     *            Encoding to use when decoding the parameters.
+     * @param separators
+     *            element separators.
+     * @return a list of {@link NameValuePair} as built from the URI's query portion.
+     *
+     * @since 4.4
+     */
+    public static List<NameValuePair> parse(
+            final CharArrayBuffer buf, final Charset charset, final char... separators) {
+        Args.notNull(buf, "Char array buffer");
+        final TokenParser tokenParser = TokenParser.INSTANCE;
+        final BitSet delimSet = new BitSet();
+        for (char separator: separators) {
+            delimSet.set(separator);
+        }
+        final ParserCursor cursor = new ParserCursor(0, buf.length());
         final List<NameValuePair> list = new ArrayList<NameValuePair>();
         while (!cursor.atEnd()) {
-            final NameValuePair nvp = parser.parseNameValuePair(buffer, cursor, parameterSeparator);
-            if (!nvp.getName().isEmpty()) {
+            delimSet.set('=');
+            final String name = tokenParser.parseToken(buf, cursor, delimSet);
+            String value = null;
+            if (!cursor.atEnd()) {
+                final int delim = buf.charAt(cursor.getPos());
+                cursor.updatePos(cursor.getPos() + 1);
+                if (delim == '=') {
+                    delimSet.clear('=');
+                    value = tokenParser.parseValue(buf, cursor, delimSet);
+                    if (!cursor.atEnd()) {
+                        cursor.updatePos(cursor.getPos() + 1);
+                    }
+                }
+            }
+            if (!name.isEmpty()) {
                 list.add(new BasicNameValuePair(
-                        decodeFormFields(nvp.getName(), charset),
-                        decodeFormFields(nvp.getValue(), charset)));
+                        decodeFormFields(name, charset),
+                        decodeFormFields(value, charset)));
             }
         }
         return list;
@@ -554,7 +606,7 @@
      * Encode/escape www-url-form-encoded content.
      * <p>
      * Uses the {@link #URLENCODER} set of characters, rather than
-     * the {@link #UNRSERVED} set; this is for compatibilty with previous
+     * the {@link #UNRESERVED} set; this is for compatibilty with previous
      * releases, URLEncoder.encode() and most browsers.
      *
      * @param content the content to encode, will convert space to '+'
@@ -572,7 +624,7 @@
      * Encode/escape www-url-form-encoded content.
      * <p>
      * Uses the {@link #URLENCODER} set of characters, rather than
-     * the {@link #UNRSERVED} set; this is for compatibilty with previous
+     * the {@link #UNRESERVED} set; this is for compatibilty with previous
      * releases, URLEncoder.encode() and most browsers.
      *
      * @param content the content to encode, will convert space to '+'
diff --git a/httpclient/src/main/java/org/apache/http/conn/routing/HttpRoute.java b/httpclient/src/main/java/org/apache/http/conn/routing/HttpRoute.java
index 5817ce0..847fa7e 100644
--- a/httpclient/src/main/java/org/apache/http/conn/routing/HttpRoute.java
+++ b/httpclient/src/main/java/org/apache/http/conn/routing/HttpRoute.java
@@ -71,7 +71,7 @@
     private HttpRoute(final HttpHost target, final InetAddress local, final List<HttpHost> proxies,
                      final boolean secure, final TunnelType tunnelled, final LayerType layered) {
         Args.notNull(target, "Target host");
-        this.targetHost   = target;
+        this.targetHost = normalize(target);
         this.localAddress = local;
         if (proxies != null && !proxies.isEmpty()) {
             this.proxyChain = new ArrayList<HttpHost>(proxies);
@@ -86,6 +86,34 @@
         this.layered      = layered != null ? layered : LayerType.PLAIN;
     }
 
+    //TODO: to be removed in 5.0
+    private static int getDefaultPort(final String schemeName) {
+        if ("http".equalsIgnoreCase(schemeName)) {
+            return 80;
+        } else if ("https".equalsIgnoreCase(schemeName)) {
+            return 443;
+        } else {
+            return -1;
+        }
+
+    }
+
+    //TODO: to be removed in 5.0
+    private static HttpHost normalize(final HttpHost target) {
+        if (target.getPort() >= 0 ) {
+            return target;
+        } else {
+            final InetAddress address = target.getAddress();
+            final String schemeName = target.getSchemeName();
+            if (address != null) {
+                return new HttpHost(address, getDefaultPort(schemeName), schemeName);
+            } else {
+                final String hostName = target.getHostName();
+                return new HttpHost(hostName, getDefaultPort(schemeName), schemeName);
+            }
+        }
+    }
+
     /**
      * Creates a new route with all attributes specified explicitly.
      *
diff --git a/httpclient/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java b/httpclient/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java
index d29e0c9..81d4d7e 100644
--- a/httpclient/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java
+++ b/httpclient/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java
@@ -256,7 +256,13 @@
      * @return  number of dots
      */
     public static int countDots(final String s) {
-        return DefaultHostnameVerifier.countDots(s);
+        int count = 0;
+        for(int i = 0; i < s.length(); i++) {
+            if(s.charAt(i) == '.') {
+                count++;
+            }
+        }
+        return count;
     }
 
 }
diff --git a/httpclient/src/main/java/org/apache/http/conn/ssl/DefaultHostnameVerifier.java b/httpclient/src/main/java/org/apache/http/conn/ssl/DefaultHostnameVerifier.java
index fd117d2..03a1edb 100644
--- a/httpclient/src/main/java/org/apache/http/conn/ssl/DefaultHostnameVerifier.java
+++ b/httpclient/src/main/java/org/apache/http/conn/ssl/DefaultHostnameVerifier.java
@@ -166,25 +166,19 @@
         }
     }
 
+    static boolean matchDomainRoot(final String host, final String domainRoot) {
+        if (domainRoot == null) {
+            return false;
+        }
+        return host.endsWith(domainRoot) && (host.length() == domainRoot.length()
+                || host.charAt(host.length() - domainRoot.length() - 1) == '.');
+    }
+
     private static boolean matchIdentity(final String host, final String identity,
                                          final PublicSuffixMatcher publicSuffixMatcher,
                                          final boolean strict) {
-        if (host == null) {
-            return false;
-        }
-
-        if (publicSuffixMatcher != null) {
-            String domainRoot = publicSuffixMatcher.getDomainRoot(identity);
-            if (domainRoot == null) {
-                // Public domain
-                return false;
-            }
-            domainRoot = "." + domainRoot;
-            if (!host.endsWith(domainRoot)) {
-                // Domain root mismatch
-                return false;
-            }
-            if (strict && countDots(identity) != countDots(domainRoot)) {
+        if (publicSuffixMatcher != null && host.contains(".")) {
+            if (!matchDomainRoot(host, publicSuffixMatcher.getDomainRoot(identity))) {
                 return false;
             }
         }
@@ -217,16 +211,6 @@
         return host.equalsIgnoreCase(identity);
     }
 
-    static int countDots(final String s) {
-        int count = 0;
-        for(int i = 0; i < s.length(); i++) {
-            if(s.charAt(i) == '.') {
-                count++;
-            }
-        }
-        return count;
-    }
-
     static boolean matchIdentity(final String host, final String identity,
                                  final PublicSuffixMatcher publicSuffixMatcher) {
         return matchIdentity(host, identity, publicSuffixMatcher, false);
diff --git a/httpclient/src/main/java/org/apache/http/conn/ssl/SSLConnectionSocketFactory.java b/httpclient/src/main/java/org/apache/http/conn/ssl/SSLConnectionSocketFactory.java
index 9a51772..1ddbe0e 100644
--- a/httpclient/src/main/java/org/apache/http/conn/ssl/SSLConnectionSocketFactory.java
+++ b/httpclient/src/main/java/org/apache/http/conn/ssl/SSLConnectionSocketFactory.java
@@ -33,6 +33,10 @@
 import java.net.Socket;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
 
 import javax.net.SocketFactory;
 import javax.net.ssl.HostnameVerifier;
@@ -43,11 +47,14 @@
 import javax.net.ssl.SSLSocket;
 import javax.security.auth.x500.X500Principal;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.http.HttpHost;
 import org.apache.http.annotation.ThreadSafe;
 import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
 import org.apache.http.conn.util.PublicSuffixMatcherLoader;
 import org.apache.http.protocol.HttpContext;
+import org.apache.http.ssl.SSLContexts;
 import org.apache.http.util.Args;
 import org.apache.http.util.TextUtils;
 
@@ -145,6 +152,8 @@
     public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER
         = StrictHostnameVerifier.INSTANCE;
 
+    private final Log log = LogFactory.getLog(getClass());
+
     /**
      * @since 4.4
      */
@@ -323,6 +332,9 @@
             if (connectTimeout > 0 && sock.getSoTimeout() == 0) {
                 sock.setSoTimeout(connectTimeout);
             }
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("Connecting socket to " + remoteAddress + " with timeout " + connectTimeout);
+            }
             sock.connect(remoteAddress, connectTimeout);
         } catch (final IOException ex) {
             try {
@@ -334,6 +346,7 @@
         // Setup SSL layering if necessary
         if (sock instanceof SSLSocket) {
             final SSLSocket sslsock = (SSLSocket) sock;
+            this.log.debug("Starting handshake");
             sslsock.startHandshake();
             verifyHostname(sslsock, host.getHostName());
             return sock;
@@ -355,11 +368,30 @@
                 true);
         if (supportedProtocols != null) {
             sslsock.setEnabledProtocols(supportedProtocols);
+        } else {
+            // If supported protocols are not explicitly set, remove all SSL protocol versions
+            final String[] allProtocols = sslsock.getEnabledProtocols();
+            final List<String> enabledProtocols = new ArrayList<String>(allProtocols.length);
+            for (String protocol: allProtocols) {
+                if (!protocol.startsWith("SSL")) {
+                    enabledProtocols.add(protocol);
+                }
+            }
+            if (!enabledProtocols.isEmpty()) {
+                sslsock.setEnabledProtocols(enabledProtocols.toArray(new String[enabledProtocols.size()]));
+            }
         }
         if (supportedCipherSuites != null) {
             sslsock.setEnabledCipherSuites(supportedCipherSuites);
         }
+
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("Enabled protocols: " + Arrays.asList(sslsock.getEnabledProtocols()));
+            this.log.debug("Enabled cipher suites:" + Arrays.asList(sslsock.getEnabledCipherSuites()));
+        }
+
         prepareSocket(sslsock);
+        this.log.debug("Starting handshake");
         sslsock.startHandshake();
         verifyHostname(sslsock, target);
         return sslsock;
@@ -387,6 +419,46 @@
             if (session == null) {
                 throw new SSLHandshakeException("SSL session not available");
             }
+
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("Secure session established");
+                this.log.debug(" negotiated protocol: " + session.getProtocol());
+                this.log.debug(" negotiated cipher suite: " + session.getCipherSuite());
+
+                try {
+
+                    final Certificate[] certs = session.getPeerCertificates();
+                    final X509Certificate x509 = (X509Certificate) certs[0];
+                    final X500Principal peer = x509.getSubjectX500Principal();
+
+                    this.log.debug(" peer principal: " + peer.toString());
+                    final Collection<List<?>> altNames1 = x509.getSubjectAlternativeNames();
+                    if (altNames1 != null) {
+                        final List<String> altNames = new ArrayList<String>();
+                        for (final List<?> aC : altNames1) {
+                            if (!aC.isEmpty()) {
+                                altNames.add((String) aC.get(1));
+                            }
+                        }
+                        this.log.debug(" peer alternative names: " + altNames);
+                    }
+
+                    final X500Principal issuer = x509.getIssuerX500Principal();
+                    this.log.debug(" issuer principal: " + issuer.toString());
+                    final Collection<List<?>> altNames2 = x509.getIssuerAlternativeNames();
+                    if (altNames2 != null) {
+                        final List<String> altNames = new ArrayList<String>();
+                        for (final List<?> aC : altNames2) {
+                            if (!aC.isEmpty()) {
+                                altNames.add((String) aC.get(1));
+                            }
+                        }
+                        this.log.debug(" issuer alternative names: " + altNames);
+                    }
+                } catch (Exception ignore) {
+                }
+            }
+
             if (!this.hostnameVerifier.verify(hostname, session)) {
                 final Certificate[] certs = session.getPeerCertificates();
                 final X509Certificate x509 = (X509Certificate) certs[0];
diff --git a/httpclient/src/main/java/org/apache/http/conn/ssl/TrustStrategy.java b/httpclient/src/main/java/org/apache/http/conn/ssl/TrustStrategy.java
index c7e3dc1..53a8091 100644
--- a/httpclient/src/main/java/org/apache/http/conn/ssl/TrustStrategy.java
+++ b/httpclient/src/main/java/org/apache/http/conn/ssl/TrustStrategy.java
@@ -26,9 +26,6 @@
  */
 package org.apache.http.conn.ssl;
 
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-
 /**
  * A strategy to establish trustworthiness of certificates without consulting the trust manager
  * configured in the actual SSL context. This interface can be used to override the standard
@@ -36,22 +33,6 @@
  *
  * @since 4.1
  */
-public interface TrustStrategy {
-
-    /**
-     * Determines whether the certificate chain can be trusted without consulting the trust manager
-     * configured in the actual SSL context. This method can be used to override the standard JSSE
-     * certificate verification process.
-     * <p>
-     * Please note that, if this method returns {@code false}, the trust manager configured
-     * in the actual SSL context can still clear the certificate as trusted.
-     *
-     * @param chain the peer certificate chain
-     * @param authType the authentication type based on the client certificate
-     * @return {@code true} if the certificate can be trusted without verification by
-     *   the trust manager, {@code false} otherwise.
-     * @throws CertificateException thrown if the certificate is not trusted or invalid.
-     */
-    boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException;
+public interface TrustStrategy extends org.apache.http.ssl.TrustStrategy {
 
 }
diff --git a/httpclient/src/main/java/org/apache/http/conn/util/PublicSuffixMatcherLoader.java b/httpclient/src/main/java/org/apache/http/conn/util/PublicSuffixMatcherLoader.java
index 173763a..9017406 100644
--- a/httpclient/src/main/java/org/apache/http/conn/util/PublicSuffixMatcherLoader.java
+++ b/httpclient/src/main/java/org/apache/http/conn/util/PublicSuffixMatcherLoader.java
@@ -81,7 +81,7 @@
             synchronized (PublicSuffixMatcherLoader.class) {
                 if (DEFAULT_INSTANCE == null){
                     final URL url = PublicSuffixMatcherLoader.class.getResource(
-                            "mozilla/public-suffix-list.txt");
+                            "/mozilla/public-suffix-list.txt");
                     if (url != null) {
                         try {
                             DEFAULT_INSTANCE = load(url);
diff --git a/httpclient/src/main/java/org/apache/http/cookie/ClientCookie.java b/httpclient/src/main/java/org/apache/http/cookie/ClientCookie.java
index dde42d2..aa5fee8 100644
--- a/httpclient/src/main/java/org/apache/http/cookie/ClientCookie.java
+++ b/httpclient/src/main/java/org/apache/http/cookie/ClientCookie.java
@@ -27,6 +27,8 @@
 
 package org.apache.http.cookie;
 
+import org.apache.http.annotation.Obsolete;
+
 /**
  * ClientCookie extends the standard {@link Cookie} interface with
  * additional client specific functionality such ability to retrieve
@@ -35,24 +37,29 @@
  * header because some cookie specifications require that the
  * {@code Cookie} header should include certain attributes only if
  * they were specified in the {@code Set-Cookie} header.
- *
+ * <p>
+ * Please do not use attributes marked as @Obsolete. They have been rendered
+ * obsolete by RFC 6265.
  *
  * @since 4.0
  */
 public interface ClientCookie extends Cookie {
 
-    // RFC2109 attributes
+    @Obsolete
     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";
+    @Obsolete
     public static final String COMMENT_ATTR    = "comment";
     public static final String EXPIRES_ATTR    = "expires";
 
-    // RFC2965 attributes
+    @Obsolete
     public static final String PORT_ATTR       = "port";
+    @Obsolete
     public static final String COMMENTURL_ATTR = "commenturl";
+    @Obsolete
     public static final String DISCARD_ATTR    = "discard";
 
     String getAttribute(String name);
diff --git a/httpclient/src/main/java/org/apache/http/cookie/Cookie.java b/httpclient/src/main/java/org/apache/http/cookie/Cookie.java
index 36ed4c5..e259281 100644
--- a/httpclient/src/main/java/org/apache/http/cookie/Cookie.java
+++ b/httpclient/src/main/java/org/apache/http/cookie/Cookie.java
@@ -29,11 +29,16 @@
 
 import java.util.Date;
 
+import org.apache.http.annotation.Obsolete;
+
 /**
  * Cookie interface represents a token or short packet of state information
  * (also referred to as "magic-cookie") that the HTTP agent and the target
  * server can exchange to maintain a session. In its simples form an HTTP
  * cookie is merely a name / value pair.
+ * <p>
+ * Please do not use attributes marked as @Obsolete. They have been rendered
+ * obsolete by RFC 6265.
  *
  * @since 4.0
  */
@@ -59,12 +64,14 @@
      *
      * @return comment
      */
+    @Obsolete
     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.
      */
+    @Obsolete
     String getCommentURL();
 
     /**
@@ -107,6 +114,7 @@
      * Get the Port attribute. It restricts the ports to which a cookie
      * may be returned in a Cookie request header.
      */
+    @Obsolete
     int[] getPorts();
 
     /**
@@ -123,6 +131,7 @@
      *
      * @return the version of the cookie.
      */
+    @Obsolete
     int getVersion();
 
     /**
@@ -133,5 +142,7 @@
      */
     boolean isExpired(final Date date);
 
+    //TODO: RFC 6265 requires cookies to track their creation time; add #getCreationDate()
+
 }
 
diff --git a/httpclient/src/main/java/org/apache/http/cookie/CookieOrigin.java b/httpclient/src/main/java/org/apache/http/cookie/CookieOrigin.java
index 39fb65d..b46f6bd 100644
--- a/httpclient/src/main/java/org/apache/http/cookie/CookieOrigin.java
+++ b/httpclient/src/main/java/org/apache/http/cookie/CookieOrigin.java
@@ -30,6 +30,7 @@
 
 import org.apache.http.annotation.Immutable;
 import org.apache.http.util.Args;
+import org.apache.http.util.TextUtils;
 
 /**
  * CookieOrigin class encapsulates details of an origin server that
@@ -52,7 +53,7 @@
         Args.notNull(path, "Path");
         this.host = host.toLowerCase(Locale.ROOT);
         this.port = port;
-        if (!path.trim().isEmpty()) {
+        if (!TextUtils.isBlank(path)) {
             this.path = path;
         } else {
             this.path = "/";
diff --git a/httpclient/src/main/java/org/apache/http/cookie/CookiePathComparator.java b/httpclient/src/main/java/org/apache/http/cookie/CookiePathComparator.java
index 050a72d..f9e3e76 100644
--- a/httpclient/src/main/java/org/apache/http/cookie/CookiePathComparator.java
+++ b/httpclient/src/main/java/org/apache/http/cookie/CookiePathComparator.java
@@ -50,6 +50,8 @@
 @Immutable
 public class CookiePathComparator implements Serializable, Comparator<Cookie> {
 
+    public static final CookiePathComparator INSTANCE = new CookiePathComparator();
+
     private static final long serialVersionUID = 7523645369616405818L;
 
     private String normalizePath(final Cookie cookie) {
diff --git a/httpclient/src/main/java/org/apache/http/cookie/CookiePriorityComparator.java b/httpclient/src/main/java/org/apache/http/cookie/CookiePriorityComparator.java
new file mode 100644
index 0000000..77e4e01
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/cookie/CookiePriorityComparator.java
@@ -0,0 +1,69 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on 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.Comparator;
+import java.util.Date;
+
+import org.apache.http.annotation.Immutable;
+import org.apache.http.impl.cookie.BasicClientCookie;
+
+/**
+ * This cookie comparator ensures that cookies with longer paths take precedence over
+ * cookies with shorter path. Among cookies with equal path length cookies with ealier
+ * creation time take precedence over cookies with later creation time
+ *
+ * @since 4.4
+ */
+@Immutable
+public class CookiePriorityComparator implements Comparator<Cookie> {
+
+    public static final CookiePriorityComparator INSTANCE = new CookiePriorityComparator();
+
+    private int getPathLength(final Cookie cookie) {
+        final String path = cookie.getPath();
+        return path != null ? path.length() : 1;
+    }
+
+    @Override
+    public int compare(final Cookie c1, final Cookie c2) {
+        final int l1 = getPathLength(c1);
+        final int l2 = getPathLength(c2);
+        //TODO: update this class once Cookie interface has been expended with #getCreationTime method
+        final int result = l2 - l1;
+        if (result == 0 && c1 instanceof BasicClientCookie && c2 instanceof BasicClientCookie) {
+            final Date d1 = ((BasicClientCookie) c1).getCreationDate();
+            final Date d2 = ((BasicClientCookie) c2).getCreationDate();
+            if (d1 != null && d2 != null) {
+                return (int) (d1.getTime() - d2.getTime());
+            }
+        }
+        return result;
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/cookie/CookieSpec.java b/httpclient/src/main/java/org/apache/http/cookie/CookieSpec.java
index 2443e61..c3aceff 100644
--- a/httpclient/src/main/java/org/apache/http/cookie/CookieSpec.java
+++ b/httpclient/src/main/java/org/apache/http/cookie/CookieSpec.java
@@ -30,6 +30,7 @@
 import java.util.List;
 
 import org.apache.http.Header;
+import org.apache.http.annotation.Obsolete;
 
 /**
  * Defines the cookie management specification.
@@ -40,7 +41,9 @@
  *   <li>  formatting of "Cookie" header
  * </ul>
  * for a given host, port and path of origin
- *
+ * <p>
+ * Please do not use methods marked as @Obsolete. They have been rendered
+ * obsolete by RFC 6265.
  *
  * @since 4.0
  */
@@ -52,6 +55,7 @@
      *
      * @return version of the state management specification
      */
+    @Obsolete
     int getVersion();
 
     /**
@@ -104,6 +108,7 @@
      * specification is understood. May be {@code null} if the cookie
      * specification does not support {@code Cookie2} header.
      */
+    @Obsolete
     Header getVersionHeader();
 
 }
diff --git a/httpclient/src/main/java/org/apache/http/cookie/SetCookie.java b/httpclient/src/main/java/org/apache/http/cookie/SetCookie.java
index 432838d..7008bf5 100644
--- a/httpclient/src/main/java/org/apache/http/cookie/SetCookie.java
+++ b/httpclient/src/main/java/org/apache/http/cookie/SetCookie.java
@@ -29,9 +29,14 @@
 
 import java.util.Date;
 
+import org.apache.http.annotation.Obsolete;
+
 /**
  * This interface represents a {@code Set-Cookie} response header sent by the
  * origin server to the HTTP agent in order to maintain a conversational state.
+ * <p>
+ * Please do not use methods marked as @Obsolete. They have been rendered
+ * obsolete by RFC 6265
  *
  * @since 4.0
  */
@@ -47,6 +52,7 @@
      *
      * @see #getComment()
      */
+    @Obsolete
     void setComment(String comment);
 
     /**
@@ -103,6 +109,7 @@
      *
      * @see Cookie#getVersion
      */
+    @Obsolete
     void setVersion(int version);
 
 }
diff --git a/httpclient/src/main/java/org/apache/http/cookie/SetCookie2.java b/httpclient/src/main/java/org/apache/http/cookie/SetCookie2.java
index 3bf5672..2f07298 100644
--- a/httpclient/src/main/java/org/apache/http/cookie/SetCookie2.java
+++ b/httpclient/src/main/java/org/apache/http/cookie/SetCookie2.java
@@ -27,9 +27,14 @@
 
 package org.apache.http.cookie;
 
+import org.apache.http.annotation.Obsolete;
+
 /**
  * This interface represents a {@code Set-Cookie2} response header sent by the
  * origin server to the HTTP agent in order to maintain a conversational state.
+ * <p>
+ * Please do not use methods marked as @Obsolete. They have been rendered
+ * obsolete by RFC 6265
  *
  * @since 4.0
  */
@@ -39,12 +44,14 @@
      * 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.
      */
+    @Obsolete
     void setCommentURL(String commentURL);
 
     /**
      * Sets the Port attribute. It restricts the ports to which a cookie
      * may be returned in a Cookie request header.
      */
+    @Obsolete
     void setPorts(int[] ports);
 
     /**
@@ -54,6 +61,7 @@
      *
      * @see #isPersistent()
      */
+    @Obsolete
     void setDiscard(boolean discard);
 
 }
diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/NTLMEngineImpl.java b/httpclient/src/main/java/org/apache/http/impl/auth/NTLMEngineImpl.java
index 1281582..59451a3 100644
--- a/httpclient/src/main/java/org/apache/http/impl/auth/NTLMEngineImpl.java
+++ b/httpclient/src/main/java/org/apache/http/impl/auth/NTLMEngineImpl.java
@@ -51,7 +51,10 @@
 @NotThreadSafe
 final class NTLMEngineImpl implements NTLMEngine {
 
+    /** Unicode encoding */
     private static final Charset UNICODE_LITTLE_UNMARKED = CharsetUtils.lookup("UnicodeLittleUnmarked");
+    /** Character encoding */
+    private static final Charset DEFAULT_CHARSET = Consts.ASCII;
 
     // Flags we use; descriptions according to:
     // http://davenport.sourceforge.net/ntlm.html
@@ -85,9 +88,6 @@
         RND_GEN = rnd;
     }
 
-    /** Character encoding */
-    static final Charset DEFAULT_CHARSET = Consts.ASCII;
-
     /** The signature string as bytes in the default encoding */
     private static final byte[] SIGNATURE;
 
@@ -98,6 +98,8 @@
         SIGNATURE[bytesWithoutNull.length] = (byte) 0x00;
     }
 
+    private static final Type1Message TYPE_1_MESSAGE = new Type1Message();
+
     /**
      * Returns the response for the given message.
      *
@@ -115,7 +117,7 @@
      * @throws org.apache.http.HttpException
      *             If the messages cannot be retrieved.
      */
-    final String getResponseFor(final String message, final String username, final String password,
+    static String getResponseFor(final String message, final String username, final String password,
             final String host, final String domain) throws NTLMEngineException {
 
         final String response;
@@ -140,8 +142,10 @@
      *            The domain to authenticate with.
      * @return String the message to add to the HTTP request header.
      */
-    String getType1Message(final String host, final String domain) {
-        return new Type1Message(domain, host).getResponse();
+    static String getType1Message(final String host, final String domain) throws NTLMEngineException {
+        // For compatibility reason do not include domain and host in type 1 message
+        //return new Type1Message(domain, host).getResponse();
+        return TYPE_1_MESSAGE.getResponse();
     }
 
     /**
@@ -164,7 +168,7 @@
      * @throws NTLMEngineException
      *             If {@encrypt(byte[],byte[])} fails.
      */
-    String getType3Message(final String user, final String password, final String host, final String domain,
+    static String getType3Message(final String user, final String password, final String host, final String domain,
             final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation)
             throws NTLMEngineException {
         return new Type3Message(domain, host, user, password, nonce, type2Flags, target,
@@ -973,22 +977,31 @@
 
     /** Type 1 message assembly class */
     static class Type1Message extends NTLMMessage {
-        protected byte[] hostBytes;
-        protected byte[] domainBytes;
 
-        /** Constructor. Include the arguments the message will need */
-        Type1Message(final String domain, final String host) {
+        private final byte[] hostBytes;
+        private final byte[] domainBytes;
+
+        Type1Message(final String domain, final String host) throws NTLMEngineException {
             super();
+            if (UNICODE_LITTLE_UNMARKED == null) {
+                throw new NTLMEngineException("Unicode not supported");
+            }
             // Strip off domain name from the host!
             final String unqualifiedHost = convertHost(host);
             // Use only the base domain name!
             final String unqualifiedDomain = convertDomain(domain);
 
-            hostBytes = unqualifiedHost != null? unqualifiedHost.getBytes(Consts.ASCII) : null;
-            domainBytes = unqualifiedDomain != null ? unqualifiedDomain
-                    .toUpperCase(Locale.ROOT).getBytes(Consts.ASCII) : null;
+            hostBytes = unqualifiedHost != null ?
+                    unqualifiedHost.getBytes(UNICODE_LITTLE_UNMARKED) : null;
+            domainBytes = unqualifiedDomain != null ?
+                    unqualifiedDomain.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED) : null;
         }
 
+        Type1Message() {
+            super();
+            hostBytes = null;
+            domainBytes = null;
+        }
         /**
          * Getting the response involves building the message before returning
          * it
@@ -1049,13 +1062,14 @@
             // NTLM revision
             addUShort(0x0f00);
 
-
             // Host (workstation) String.
-            //addBytes(hostBytes);
-
+            if (hostBytes != null) {
+                addBytes(hostBytes);
+            }
             // Domain String.
-            //addBytes(domainBytes);
-
+            if (domainBytes != null) {
+                addBytes(domainBytes);
+            }
 
             return super.getResponse();
         }
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/CloseableHttpClient.java b/httpclient/src/main/java/org/apache/http/impl/client/CloseableHttpClient.java
index 647acae..16dfae2 100644
--- a/httpclient/src/main/java/org/apache/http/impl/client/CloseableHttpClient.java
+++ b/httpclient/src/main/java/org/apache/http/impl/client/CloseableHttpClient.java
@@ -29,7 +29,6 @@
 
 import java.io.Closeable;
 import java.io.IOException;
-import java.lang.reflect.UndeclaredThrowableException;
 import java.net.URI;
 
 import org.apache.commons.logging.Log;
@@ -37,7 +36,6 @@
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpRequest;
-import org.apache.http.HttpResponse;
 import org.apache.http.annotation.ThreadSafe;
 import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.HttpClient;
@@ -219,12 +217,14 @@
             throws IOException, ClientProtocolException {
         Args.notNull(responseHandler, "Response handler");
 
-        final HttpResponse response = execute(target, request, context);
-
-        final T result;
+        final CloseableHttpResponse response = execute(target, request, context);
         try {
-            result = responseHandler.handleResponse(response);
-        } catch (final Exception t) {
+            final T result = responseHandler.handleResponse(response);
+            final HttpEntity entity = response.getEntity();
+            EntityUtils.consume(entity);
+            return result;
+        } catch (final ClientProtocolException t) {
+            // Try to salvage the underlying connection in case of a protocol exception
             final HttpEntity entity = response.getEntity();
             try {
                 EntityUtils.consume(entity);
@@ -233,20 +233,10 @@
                 // important and will be thrown to the caller.
                 this.log.warn("Error consuming content after an exception.", t2);
             }
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            }
-            if (t instanceof IOException) {
-                throw (IOException) t;
-            }
-            throw new UndeclaredThrowableException(t);
+            throw t;
+        } finally {
+            response.close();
         }
-
-        // Handling the response was successful. Ensure that the content has
-        // been fully consumed.
-        final HttpEntity entity = response.getEntity();
-        EntityUtils.consume(entity);
-        return result;
     }
 
 }
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java b/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java
index 2c0d9c0..49264a6 100644
--- a/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java
+++ b/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java
@@ -83,7 +83,6 @@
 import org.apache.http.conn.socket.PlainConnectionSocketFactory;
 import org.apache.http.conn.ssl.DefaultHostnameVerifier;
 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
-import org.apache.http.conn.ssl.SSLContexts;
 import org.apache.http.conn.ssl.X509HostnameVerifier;
 import org.apache.http.conn.util.PublicSuffixMatcher;
 import org.apache.http.conn.util.PublicSuffixMatcherLoader;
@@ -103,7 +102,7 @@
 import org.apache.http.impl.cookie.DefaultCookieSpecProvider;
 import org.apache.http.impl.cookie.IgnoreSpecProvider;
 import org.apache.http.impl.cookie.NetscapeDraftSpecProvider;
-import org.apache.http.impl.cookie.RFC2965SpecProvider;
+import org.apache.http.impl.cookie.RFC6265CookieSpecProvider;
 import org.apache.http.impl.execchain.BackoffStrategyExec;
 import org.apache.http.impl.execchain.ClientExecChain;
 import org.apache.http.impl.execchain.MainClientExec;
@@ -118,6 +117,7 @@
 import org.apache.http.protocol.RequestContent;
 import org.apache.http.protocol.RequestTargetHost;
 import org.apache.http.protocol.RequestUserAgent;
+import org.apache.http.ssl.SSLContexts;
 import org.apache.http.util.TextUtils;
 import org.apache.http.util.VersionInfo;
 
@@ -197,6 +197,10 @@
     private SocketConfig defaultSocketConfig;
     private ConnectionConfig defaultConnectionConfig;
     private RequestConfig defaultRequestConfig;
+    private boolean evictExpiredConnections;
+    private boolean evictIdleConnections;
+    private long maxIdleTime;
+    private TimeUnit maxIdleTimeUnit;
 
     private boolean systemProperties;
     private boolean redirectHandlingDisabled;
@@ -414,8 +418,8 @@
     }
 
     /**
-     * Assigns {@link AuthenticationStrategy} instance for proxy
-     * authentication.
+     * Assigns {@link AuthenticationStrategy} instance for target
+     * host authentication.
      */
     public final HttpClientBuilder setTargetAuthenticationStrategy(
             final AuthenticationStrategy targetAuthStrategy) {
@@ -424,8 +428,8 @@
     }
 
     /**
-     * Assigns {@link AuthenticationStrategy} instance for target
-     * host authentication.
+     * Assigns {@link AuthenticationStrategy} instance for proxy
+     * authentication.
      */
     public final HttpClientBuilder setProxyAuthenticationStrategy(
             final AuthenticationStrategy proxyAuthStrategy) {
@@ -749,7 +753,60 @@
      * implementations.
      */
     public final HttpClientBuilder useSystemProperties() {
-        systemProperties = true;
+        this.systemProperties = true;
+        return this;
+    }
+
+    /**
+     * Makes this instance of HttpClient proactively evict expired connections from the
+     * connection pool using a background thread.
+     * <p>
+     * One MUST explicitly close HttpClient with {@link CloseableHttpClient#close()} in order
+     * to stop and release the background thread.
+     * <p>
+     * Please note this method has no effect if the instance of HttpClient is configuted to
+     * use a shared connection manager.
+     * <p>
+     * Please note this method may not be used when the instance of HttpClient is created
+     * inside an EJB container.
+     *
+     * @see #setConnectionManagerShared(boolean)
+     * @see org.apache.http.conn.HttpClientConnectionManager#closeExpiredConnections()
+     *
+     * @since 4.4
+     */
+    public final HttpClientBuilder evictExpiredConnections() {
+        evictExpiredConnections = true;
+        return this;
+    }
+
+    /**
+     * Makes this instance of HttpClient proactively evict idle connections from the
+     * connection pool using a background thread.
+     * <p>
+     * One MUST explicitly close HttpClient with {@link CloseableHttpClient#close()} in order
+     * to stop and release the background thread.
+     * <p>
+     * Please note this method has no effect if the instance of HttpClient is configuted to
+     * use a shared connection manager.
+     * <p>
+     * Please note this method may not be used when the instance of HttpClient is created
+     * inside an EJB container.
+     *
+     * @see #setConnectionManagerShared(boolean)
+     * @see org.apache.http.conn.HttpClientConnectionManager#closeExpiredConnections()
+     *
+     * @param maxIdleTime maxium time persistent connections can stay idle while kept alive
+     * in the connection pool. Connections whose inactivity period exceeds this value will
+     * get closed and evicted from the pool.
+     * @param maxIdleTimeUnit time unit for the above parameter.
+     *
+     * @since 4.4
+     */
+    public final HttpClientBuilder evictIdleConnections(final Long maxIdleTime, final TimeUnit maxIdleTimeUnit) {
+        this.evictIdleConnections = true;
+        this.maxIdleTime = maxIdleTime;
+        this.maxIdleTimeUnit = maxIdleTimeUnit;
         return this;
     }
 
@@ -1070,9 +1127,17 @@
         }
         Lookup<CookieSpecProvider> cookieSpecRegistryCopy = this.cookieSpecRegistry;
         if (cookieSpecRegistryCopy == null) {
+            final CookieSpecProvider defaultProvider = new DefaultCookieSpecProvider(publicSuffixMatcherCopy);
+            final CookieSpecProvider laxStandardProvider = new RFC6265CookieSpecProvider(
+                    RFC6265CookieSpecProvider.CompatibilityLevel.RELAXED, publicSuffixMatcherCopy);
+            final CookieSpecProvider strictStandardProvider = new RFC6265CookieSpecProvider(
+                    RFC6265CookieSpecProvider.CompatibilityLevel.STRICT, publicSuffixMatcherCopy);
             cookieSpecRegistryCopy = RegistryBuilder.<CookieSpecProvider>create()
-                .register(CookieSpecs.DEFAULT, new DefaultCookieSpecProvider(publicSuffixMatcherCopy))
-                .register(CookieSpecs.STANDARD, new RFC2965SpecProvider(publicSuffixMatcherCopy))
+                .register(CookieSpecs.DEFAULT, defaultProvider)
+                .register("best-match", defaultProvider)
+                .register("compatibility", defaultProvider)
+                .register(CookieSpecs.STANDARD, laxStandardProvider)
+                .register(CookieSpecs.STANDARD_STRICT, strictStandardProvider)
                 .register(CookieSpecs.NETSCAPE, new NetscapeDraftSpecProvider())
                 .register(CookieSpecs.IGNORE_COOKIES, new IgnoreSpecProvider())
                 .build();
@@ -1098,6 +1163,20 @@
                 closeablesCopy = new ArrayList<Closeable>(1);
             }
             final HttpClientConnectionManager cm = connManagerCopy;
+
+            if (evictExpiredConnections || evictIdleConnections) {
+                final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor(cm,
+                        maxIdleTime > 0 ? maxIdleTime : 10, maxIdleTimeUnit != null ? maxIdleTimeUnit : TimeUnit.SECONDS);
+                closeablesCopy.add(new Closeable() {
+
+                    @Override
+                    public void close() throws IOException {
+                        connectionEvictor.shutdown();
+                    }
+
+                });
+                connectionEvictor.start();
+            }
             closeablesCopy.add(new Closeable() {
 
                 @Override
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/IdleConnectionEvictor.java b/httpclient/src/main/java/org/apache/http/impl/client/IdleConnectionEvictor.java
new file mode 100644
index 0000000..6bd8c80
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/IdleConnectionEvictor.java
@@ -0,0 +1,123 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on 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.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.http.conn.HttpClientConnectionManager;
+import org.apache.http.util.Args;
+
+/**
+ * This class maintains a background thread to enforce an eviction policy for expired / idle
+ * persistent connections kept alive in the connection pool.
+ *
+ * @since 4.4
+ */
+public final class IdleConnectionEvictor {
+
+    private final HttpClientConnectionManager connectionManager;
+    private final ThreadFactory threadFactory;
+    private final Thread thread;
+    private final long sleepTimeMs;
+    private final long maxIdleTimeMs;
+
+    private volatile Exception exception;
+
+    public IdleConnectionEvictor(
+            final HttpClientConnectionManager connectionManager,
+            final ThreadFactory threadFactory,
+            final long sleepTime, final TimeUnit sleepTimeUnit,
+            final long maxIdleTime, final TimeUnit maxIdleTimeUnit) {
+        this.connectionManager = Args.notNull(connectionManager, "Connection manager");
+        this.threadFactory = threadFactory != null ? threadFactory : new DefaultThreadFactory();
+        this.sleepTimeMs = sleepTimeUnit != null ? sleepTimeUnit.toMillis(sleepTime) : sleepTime;
+        this.maxIdleTimeMs = maxIdleTimeUnit != null ? maxIdleTimeUnit.toMillis(maxIdleTime) : maxIdleTime;
+        this.thread = this.threadFactory.newThread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    while (!Thread.currentThread().isInterrupted()) {
+                        Thread.sleep(sleepTimeMs);
+                        connectionManager.closeExpiredConnections();
+                        if (maxIdleTimeMs > 0) {
+                            connectionManager.closeIdleConnections(maxIdleTimeMs, TimeUnit.MILLISECONDS);
+                        }
+                    }
+                } catch (Exception ex) {
+                    exception = ex;
+                }
+
+            }
+        });
+    }
+
+    public IdleConnectionEvictor(
+            final HttpClientConnectionManager connectionManager,
+            final long sleepTime, final TimeUnit sleepTimeUnit,
+            final long maxIdleTime, final TimeUnit maxIdleTimeUnit) {
+        this(connectionManager, null, sleepTime, sleepTimeUnit, maxIdleTime, maxIdleTimeUnit);
+    }
+
+    public IdleConnectionEvictor(
+            final HttpClientConnectionManager connectionManager,
+            final long maxIdleTime, final TimeUnit maxIdleTimeUnit) {
+        this(connectionManager, null,
+                maxIdleTime > 0 ? maxIdleTime : 5, maxIdleTimeUnit != null ? maxIdleTimeUnit : TimeUnit.SECONDS,
+                maxIdleTime, maxIdleTimeUnit);
+    }
+
+    public void start() {
+        thread.start();
+    }
+
+    public void shutdown() {
+        thread.interrupt();
+    }
+
+    public boolean isRunning() {
+        return thread.isAlive();
+    }
+
+    public void awaitTermination(final long time, final TimeUnit tunit) throws InterruptedException {
+        thread.join((tunit != null ? tunit : TimeUnit.MILLISECONDS).toMillis(time));
+    }
+
+    static class DefaultThreadFactory implements ThreadFactory {
+
+        @Override
+        public Thread newThread(final Runnable r) {
+            final Thread t = new Thread(r, "Connection evictor");
+            t.setDaemon(true);
+            return t;
+        }
+
+    };
+
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/InternalHttpClient.java b/httpclient/src/main/java/org/apache/http/impl/client/InternalHttpClient.java
index 34b0ab0..32bb5e0 100644
--- a/httpclient/src/main/java/org/apache/http/impl/client/InternalHttpClient.java
+++ b/httpclient/src/main/java/org/apache/http/impl/client/InternalHttpClient.java
@@ -74,7 +74,7 @@
  */
 @ThreadSafe
 @SuppressWarnings("deprecation")
-class InternalHttpClient extends CloseableHttpClient {
+class InternalHttpClient extends CloseableHttpClient implements Configurable {
 
     private final Log log = LogFactory.getLog(getClass());
 
@@ -188,6 +188,11 @@
     }
 
     @Override
+    public RequestConfig getConfig() {
+        return this.defaultConfig;
+    }
+
+    @Override
     public void close() {
         if (this.closeables != null) {
             for (final Closeable closeable: this.closeables) {
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/BasicClientCookie.java b/httpclient/src/main/java/org/apache/http/impl/cookie/BasicClientCookie.java
index 82e7396..dc261ab 100644
--- a/httpclient/src/main/java/org/apache/http/impl/cookie/BasicClientCookie.java
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/BasicClientCookie.java
@@ -304,6 +304,20 @@
             && cookieExpiryDate.getTime() <= date.getTime());
     }
 
+    /**
+     * @since 4.4
+     */
+    public Date getCreationDate() {
+        return creationDate;
+    }
+
+    /**
+     * @since 4.4
+     */
+    public void setCreationDate(final Date creationDate) {
+        this.creationDate = creationDate;
+    }
+
     public void setAttribute(final String name, final String value) {
         this.attribs.put(name, value);
     }
@@ -315,7 +329,14 @@
 
     @Override
     public boolean containsAttribute(final String name) {
-        return this.attribs.get(name) != null;
+        return this.attribs.containsKey(name);
+    }
+
+    /**
+     * @since 4.4
+     */
+    public boolean removeAttribute(final String name) {
+        return this.attribs.remove(name) != null;
     }
 
     @Override
@@ -378,5 +399,7 @@
     /** The version of the cookie specification I was created from. */
     private int cookieVersion;
 
+    private Date creationDate;
+
 }
 
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/BasicDomainHandler.java b/httpclient/src/main/java/org/apache/http/impl/cookie/BasicDomainHandler.java
index 6f77006..7503971 100644
--- a/httpclient/src/main/java/org/apache/http/impl/cookie/BasicDomainHandler.java
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/BasicDomainHandler.java
@@ -26,7 +26,10 @@
  */
 package org.apache.http.impl.cookie;
 
+import java.util.Locale;
+
 import org.apache.http.annotation.Immutable;
+import org.apache.http.conn.util.InetAddressUtils;
 import org.apache.http.cookie.ClientCookie;
 import org.apache.http.cookie.CommonCookieAttributeHandler;
 import org.apache.http.cookie.Cookie;
@@ -35,6 +38,7 @@
 import org.apache.http.cookie.MalformedCookieException;
 import org.apache.http.cookie.SetCookie;
 import org.apache.http.util.Args;
+import org.apache.http.util.TextUtils;
 
 /**
  *
@@ -51,13 +55,19 @@
     public void parse(final SetCookie cookie, final String value)
             throws MalformedCookieException {
         Args.notNull(cookie, "Cookie");
-        if (value == null) {
-            throw new MalformedCookieException("Missing value for domain attribute");
+        if (TextUtils.isBlank(value)) {
+            throw new MalformedCookieException("Blank or null value for domain attribute");
         }
-        if (value.trim().isEmpty()) {
-            throw new MalformedCookieException("Blank value for domain attribute");
+        // Ignore domain attributes ending with '.' per RFC 6265, 4.1.2.3
+        if (value.endsWith(".")) {
+            return;
         }
-        cookie.setDomain(value);
+        String domain = value;
+        if (domain.startsWith(".")) {
+            domain = domain.substring(1);
+        }
+        domain = domain.toLowerCase(Locale.ROOT);
+        cookie.setDomain(domain);
     }
 
     @Override
@@ -71,32 +81,32 @@
         // request-host and domain must be identical for the cookie to sent
         // back to the origin-server.
         final String host = origin.getHost();
-        String domain = cookie.getDomain();
+        final String domain = cookie.getDomain();
         if (domain == null) {
-            throw new CookieRestrictionViolationException("Cookie domain may not be null");
+            throw new CookieRestrictionViolationException("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.
+        if (!host.equals(domain) && !domainMatch(domain, host)) {
+            throw new CookieRestrictionViolationException(
+                    "Illegal 'domain' attribute \"" + domain + "\". Domain of origin: \"" + host + "\"");
+        }
+    }
 
-            // domain must match host
-            if (!host.endsWith(domain)) {
-                if (domain.startsWith(".")) {
-                    domain = domain.substring(1, domain.length());
-                }
-                if (!host.equals(domain)) {
-                    throw new CookieRestrictionViolationException(
-                        "Illegal domain attribute \"" + domain
-                        + "\". Domain of origin: \"" + host + "\"");
-                }
+    static boolean domainMatch(final String domain, final String host) {
+        if (InetAddressUtils.isIPv4Address(host) || InetAddressUtils.isIPv6Address(host)) {
+            return false;
+        }
+        final String normalizedDomain = domain.startsWith(".") ? domain.substring(1) : domain;
+        if (host.endsWith(normalizedDomain)) {
+            final int prefix = host.length() - normalizedDomain.length();
+            // Either a full match or a prefix endidng with a '.'
+            if (prefix == 0) {
+                return true;
             }
-        } else {
-            if (!host.equals(domain)) {
-                throw new CookieRestrictionViolationException(
-                    "Illegal domain attribute \"" + domain
-                    + "\". Domain of origin: \"" + host + "\"");
+            if (prefix > 1 && host.charAt(prefix - 1) == '.') {
+                return true;
             }
         }
+        return false;
     }
 
     @Override
@@ -108,13 +118,19 @@
         if (domain == null) {
             return false;
         }
+        if (domain.startsWith(".")) {
+            domain = domain.substring(1);
+        }
+        domain = domain.toLowerCase(Locale.ROOT);
         if (host.equals(domain)) {
             return true;
         }
-        if (!domain.startsWith(".")) {
-            domain = '.' + domain;
+        if (cookie instanceof ClientCookie) {
+            if (((ClientCookie) cookie).containsAttribute(ClientCookie.DOMAIN_ATTR)) {
+                return domainMatch(domain, host);
+            }
         }
-        return host.endsWith(domain) || host.equals(domain.substring(1));
+        return false;
     }
 
     @Override
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/BasicExpiresHandler.java b/httpclient/src/main/java/org/apache/http/impl/cookie/BasicExpiresHandler.java
index 42450d5..21a2876 100644
--- a/httpclient/src/main/java/org/apache/http/impl/cookie/BasicExpiresHandler.java
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/BasicExpiresHandler.java
@@ -56,11 +56,11 @@
             throws MalformedCookieException {
         Args.notNull(cookie, "Cookie");
         if (value == null) {
-            throw new MalformedCookieException("Missing value for expires attribute");
+            throw new MalformedCookieException("Missing value for 'expires' attribute");
         }
         final Date expiry = DateUtils.parseDate(value, this.datepatterns);
         if (expiry == null) {
-            throw new MalformedCookieException("Unable to parse expires attribute: "
+            throw new MalformedCookieException("Invalid 'expires' attribute: "
                     + value);
         }
         cookie.setExpiryDate(expiry);
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/BasicMaxAgeHandler.java b/httpclient/src/main/java/org/apache/http/impl/cookie/BasicMaxAgeHandler.java
index db28aa8..02ee8ba 100644
--- a/httpclient/src/main/java/org/apache/http/impl/cookie/BasicMaxAgeHandler.java
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/BasicMaxAgeHandler.java
@@ -51,17 +51,17 @@
             throws MalformedCookieException {
         Args.notNull(cookie, "Cookie");
         if (value == null) {
-            throw new MalformedCookieException("Missing value for max-age attribute");
+            throw new MalformedCookieException("Missing value for 'max-age' attribute");
         }
         final int age;
         try {
             age = Integer.parseInt(value);
         } catch (final NumberFormatException e) {
-            throw new MalformedCookieException ("Invalid max-age attribute: "
+            throw new MalformedCookieException ("Invalid 'max-age' attribute: "
                     + value);
         }
         if (age < 0) {
-            throw new MalformedCookieException ("Negative max-age attribute: "
+            throw new MalformedCookieException ("Negative 'max-age' attribute: "
                     + value);
         }
         cookie.setExpiryDate(new Date(System.currentTimeMillis() + age * 1000L));
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/BasicPathHandler.java b/httpclient/src/main/java/org/apache/http/impl/cookie/BasicPathHandler.java
index 4f10e47..c7b3875 100644
--- a/httpclient/src/main/java/org/apache/http/impl/cookie/BasicPathHandler.java
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/BasicPathHandler.java
@@ -60,32 +60,38 @@
             throws MalformedCookieException {
         if (!match(cookie, origin)) {
             throw new CookieRestrictionViolationException(
-                "Illegal path attribute \"" + cookie.getPath()
+                "Illegal 'path' attribute \"" + cookie.getPath()
                 + "\". Path of origin: \"" + origin.getPath() + "\"");
         }
     }
 
+    static boolean pathMatch(final String uriPath, final String cookiePath) {
+        String normalizedCookiePath = cookiePath;
+        if (normalizedCookiePath == null) {
+            normalizedCookiePath = "/";
+        }
+        if (normalizedCookiePath.length() > 1 && normalizedCookiePath.endsWith("/")) {
+            normalizedCookiePath = normalizedCookiePath.substring(0, normalizedCookiePath.length() - 1);
+        }
+        if (uriPath.startsWith(normalizedCookiePath)) {
+            if (normalizedCookiePath.equals("/")) {
+                return true;
+            }
+            if (uriPath.length() == normalizedCookiePath.length()) {
+                return true;
+            }
+            if (uriPath.charAt(normalizedCookiePath.length()) == '/') {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Override
     public boolean match(final Cookie cookie, final CookieOrigin origin) {
         Args.notNull(cookie, "Cookie");
         Args.notNull(origin, "Cookie origin");
-        final 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;
+        return pathMatch(origin.getPath(), cookie.getPath());
     }
 
     @Override
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/DefaultCookieSpec.java b/httpclient/src/main/java/org/apache/http/impl/cookie/DefaultCookieSpec.java
index 2dea1df..cb43078 100644
--- a/httpclient/src/main/java/org/apache/http/impl/cookie/DefaultCookieSpec.java
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/DefaultCookieSpec.java
@@ -210,7 +210,7 @@
 
     @Override
     public Header getVersionHeader() {
-        return strict.getVersionHeader();
+        return null;
     }
 
     @Override
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/DefaultCookieSpecProvider.java b/httpclient/src/main/java/org/apache/http/impl/cookie/DefaultCookieSpecProvider.java
index e9b894e..1fe8f03 100644
--- a/httpclient/src/main/java/org/apache/http/impl/cookie/DefaultCookieSpecProvider.java
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/DefaultCookieSpecProvider.java
@@ -38,7 +38,7 @@
 
 /**
  * {@link org.apache.http.cookie.CookieSpecProvider} implementation that provides an instance of
- * {@link BestMatchSpec}. The instance returned by this factory can
+ * {@link org.apache.http.impl.cookie.DefaultCookieSpec}. The instance returned by this factory can
  * be shared by multiple threads.
  *
  * @since 4.4
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/LaxExpiresHandler.java b/httpclient/src/main/java/org/apache/http/impl/cookie/LaxExpiresHandler.java
new file mode 100644
index 0000000..28e2b4f
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/LaxExpiresHandler.java
@@ -0,0 +1,220 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on 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.BitSet;
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.http.annotation.Immutable;
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.CommonCookieAttributeHandler;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.util.Args;
+
+/**
+ *
+ * @since 4.4
+ */
+@Immutable
+public class LaxExpiresHandler extends AbstractCookieAttributeHandler implements CommonCookieAttributeHandler {
+
+    static final TimeZone UTC = TimeZone.getTimeZone("UTC");
+
+    private static final BitSet DELIMS;
+    static {
+        final BitSet bitSet = new BitSet();
+        bitSet.set(0x9);
+        for (int b = 0x20; b <= 0x2f; b++) {
+            bitSet.set(b);
+        }
+        for (int b = 0x3b; b <= 0x40; b++) {
+            bitSet.set(b);
+        }
+        for (int b = 0x5b; b <= 0x60; b++) {
+            bitSet.set(b);
+        }
+        for (int b = 0x7b; b <= 0x7e; b++) {
+            bitSet.set(b);
+        }
+        DELIMS = bitSet;
+    }
+    private static final Map<String, Integer> MONTHS;
+    static {
+        final ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String, Integer>(12);
+        map.put("jan", Calendar.JANUARY);
+        map.put("feb", Calendar.FEBRUARY);
+        map.put("mar", Calendar.MARCH);
+        map.put("apr", Calendar.APRIL);
+        map.put("may", Calendar.MAY);
+        map.put("jun", Calendar.JUNE);
+        map.put("jul", Calendar.JULY);
+        map.put("aug", Calendar.AUGUST);
+        map.put("sep", Calendar.SEPTEMBER);
+        map.put("oct", Calendar.OCTOBER);
+        map.put("nov", Calendar.NOVEMBER);
+        map.put("dec", Calendar.DECEMBER);
+        MONTHS = map;
+    }
+
+    private final static Pattern TIME_PATTERN = Pattern.compile(
+            "^([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})([^0-9].*)?$");
+    private final static Pattern DAY_OF_MONTH_PATTERN = Pattern.compile(
+            "^([0-9]{1,2})([^0-9].*)?$");
+    private final static Pattern MONTH_PATTERN = Pattern.compile(
+            "^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)(.*)?$", Pattern.CASE_INSENSITIVE);
+    private final static Pattern YEAR_PATTERN = Pattern.compile(
+            "^([0-9]{2,4})([^0-9].*)?$");
+
+    public LaxExpiresHandler() {
+        super();
+    }
+
+    @Override
+    public void parse(final SetCookie cookie, final String value) throws MalformedCookieException {
+        Args.notNull(cookie, "Cookie");
+        final ParserCursor cursor = new ParserCursor(0, value.length());
+        final StringBuilder content = new StringBuilder();
+
+        int second = 0, minute = 0, hour = 0, day = 0, month = 0, year = 0;
+        boolean foundTime = false, foundDayOfMonth = false, foundMonth = false, foundYear = false;
+        try {
+            while (!cursor.atEnd()) {
+                skipDelims(value, cursor);
+                content.setLength(0);
+                copyContent(value, cursor, content);
+
+                if (content.length() == 0) {
+                    break;
+                }
+                if (!foundTime) {
+                    final Matcher matcher = TIME_PATTERN.matcher(content);
+                    if (matcher.matches()) {
+                        foundTime = true;
+                        hour = Integer.parseInt(matcher.group(1));
+                        minute = Integer.parseInt(matcher.group(2));
+                        second =Integer.parseInt(matcher.group(3));
+                        continue;
+                    }
+                }
+                if (!foundDayOfMonth) {
+                    final Matcher matcher = DAY_OF_MONTH_PATTERN.matcher(content);
+                    if (matcher.matches()) {
+                        foundDayOfMonth = true;
+                        day = Integer.parseInt(matcher.group(1));
+                        continue;
+                    }
+                }
+                if (!foundMonth) {
+                    final Matcher matcher = MONTH_PATTERN.matcher(content);
+                    if (matcher.matches()) {
+                        foundMonth = true;
+                        month = MONTHS.get(matcher.group(1).toLowerCase(Locale.ROOT));
+                        continue;
+                    }
+                }
+                if (!foundYear) {
+                    final Matcher matcher = YEAR_PATTERN.matcher(content);
+                    if (matcher.matches()) {
+                        foundYear = true;
+                        year = Integer.parseInt(matcher.group(1));
+                        continue;
+                    }
+                }
+            }
+        } catch (NumberFormatException ignore) {
+            throw new MalformedCookieException("Invalid 'expires' attribute: " + value);
+        }
+        if (!foundTime || !foundDayOfMonth || !foundMonth || !foundYear) {
+            throw new MalformedCookieException("Invalid 'expires' attribute: " + value);
+        }
+        if (year >= 70 && year <= 99) {
+            year = 1900 + year;
+        }
+        if (year >= 0 && year <= 69) {
+            year = 2000 + year;
+        }
+        if (day < 1 || day > 31 || year < 1601 || hour > 23 || minute > 59 || second > 59) {
+            throw new MalformedCookieException("Invalid 'expires' attribute: " + value);
+        }
+
+        final Calendar c = Calendar.getInstance();
+        c.setTimeZone(UTC);
+        c.setTimeInMillis(0L);
+        c.set(Calendar.SECOND, second);
+        c.set(Calendar.MINUTE, minute);
+        c.set(Calendar.HOUR_OF_DAY, hour);
+        c.set(Calendar.DAY_OF_MONTH, day);
+        c.set(Calendar.MONTH, month);
+        c.set(Calendar.YEAR, year);
+        cookie.setExpiryDate(c.getTime());
+    }
+
+    private void skipDelims(final CharSequence buf, final ParserCursor cursor) {
+        int pos = cursor.getPos();
+        final int indexFrom = cursor.getPos();
+        final int indexTo = cursor.getUpperBound();
+        for (int i = indexFrom; i < indexTo; i++) {
+            final char current = buf.charAt(i);
+            if (DELIMS.get(current)) {
+                pos++;
+            } else {
+                break;
+            }
+        }
+        cursor.updatePos(pos);
+    }
+
+    private void copyContent(final CharSequence buf, final ParserCursor cursor, final StringBuilder dst) {
+        int pos = cursor.getPos();
+        final int indexFrom = cursor.getPos();
+        final int indexTo = cursor.getUpperBound();
+        for (int i = indexFrom; i < indexTo; i++) {
+            final char current = buf.charAt(i);
+            if (DELIMS.get(current)) {
+                break;
+            } else {
+                pos++;
+                dst.append(current);
+            }
+        }
+        cursor.updatePos(pos);
+    }
+
+    @Override
+    public String getAttributeName() {
+        return ClientCookie.MAX_AGE_ATTR;
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/LaxMaxAgeHandler.java b/httpclient/src/main/java/org/apache/http/impl/cookie/LaxMaxAgeHandler.java
new file mode 100644
index 0000000..8615286
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/LaxMaxAgeHandler.java
@@ -0,0 +1,79 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on 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.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.http.annotation.Immutable;
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.CommonCookieAttributeHandler;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+import org.apache.http.util.Args;
+import org.apache.http.util.TextUtils;
+
+/**
+ *
+ * @since 4.4
+ */
+@Immutable
+public class LaxMaxAgeHandler extends AbstractCookieAttributeHandler implements CommonCookieAttributeHandler {
+
+    private final static Pattern MAX_AGE_PATTERN = Pattern.compile("^\\-?[0-9]+$");
+
+    public LaxMaxAgeHandler() {
+        super();
+    }
+
+    @Override
+    public void parse(final SetCookie cookie, final String value) throws MalformedCookieException {
+        Args.notNull(cookie, "Cookie");
+        if (TextUtils.isBlank(value)) {
+            return;
+        }
+        final Matcher matcher = MAX_AGE_PATTERN.matcher(value);
+        if (matcher.matches()) {
+            final int age;
+            try {
+                age = Integer.parseInt(value);
+            } catch (final NumberFormatException e) {
+                return;
+            }
+            final Date expiryDate = age >= 0 ? new Date(System.currentTimeMillis() + age * 1000L) :
+                    new Date(Long.MIN_VALUE);
+            cookie.setExpiryDate(expiryDate);
+        }
+    }
+
+    @Override
+    public String getAttributeName() {
+        return ClientCookie.MAX_AGE_ATTR;
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/NetscapeDomainHandler.java b/httpclient/src/main/java/org/apache/http/impl/cookie/NetscapeDomainHandler.java
index 6ff1193..1f92cc0 100644
--- a/httpclient/src/main/java/org/apache/http/impl/cookie/NetscapeDomainHandler.java
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/NetscapeDomainHandler.java
@@ -30,11 +30,14 @@
 import java.util.StringTokenizer;
 
 import org.apache.http.annotation.Immutable;
+import org.apache.http.cookie.ClientCookie;
 import org.apache.http.cookie.Cookie;
 import org.apache.http.cookie.CookieOrigin;
 import org.apache.http.cookie.CookieRestrictionViolationException;
 import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
 import org.apache.http.util.Args;
+import org.apache.http.util.TextUtils;
 
 /**
  *
@@ -48,12 +51,23 @@
     }
 
     @Override
+    public void parse(final SetCookie cookie, final String value) throws MalformedCookieException {
+        Args.notNull(cookie, "Cookie");
+        if (TextUtils.isBlank(value)) {
+            throw new MalformedCookieException("Blank or null value for domain attribute");
+        }
+        cookie.setDomain(value);
+    }
+
+    @Override
     public void validate(final Cookie cookie, final CookieOrigin origin)
             throws MalformedCookieException {
-        super.validate(cookie, origin);
-        // Perform Netscape Cookie draft specific validation
         final String host = origin.getHost();
         final String domain = cookie.getDomain();
+        if (!host.equals(domain) && !BasicDomainHandler.domainMatch(domain, host)) {
+            throw new CookieRestrictionViolationException(
+                    "Illegal domain attribute \"" + domain + "\". Domain of origin: \"" + host + "\"");
+        }
         if (host.contains(".")) {
             final int domainParts = new StringTokenizer(domain, ".").countTokens();
 
@@ -103,4 +117,9 @@
        return host.endsWith(domain);
    }
 
+    @Override
+    public String getAttributeName() {
+        return ClientCookie.DOMAIN_ATTR;
+    }
+
 }
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2109Spec.java b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2109Spec.java
index 581db54..3fd7f1a 100644
--- a/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2109Spec.java
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2109Spec.java
@@ -59,8 +59,6 @@
 @ThreadSafe
 public class RFC2109Spec extends CookieSpecBase {
 
-    private final static CookiePathComparator PATH_COMPARATOR = new CookiePathComparator();
-
     final static String[] DATE_PATTERNS = {
         DateUtils.PATTERN_RFC1123,
         DateUtils.PATTERN_RFC1036,
@@ -127,7 +125,7 @@
         if (cookies.size() > 1) {
             // Create a mutable copy and sort the copy.
             cookieList = new ArrayList<Cookie>(cookies);
-            Collections.sort(cookieList, PATH_COMPARATOR);
+            Collections.sort(cookieList, CookiePathComparator.INSTANCE);
         } else {
             cookieList = cookies;
         }
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/RFC6265CookieSpecBase.java b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC6265CookieSpecBase.java
new file mode 100644
index 0000000..bec959a
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC6265CookieSpecBase.java
@@ -0,0 +1,276 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on 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.BitSet;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.http.FormattedHeader;
+import org.apache.http.Header;
+import org.apache.http.annotation.ThreadSafe;
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.CommonCookieAttributeHandler;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.CookiePriorityComparator;
+import org.apache.http.cookie.CookieSpec;
+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.message.TokenParser;
+import org.apache.http.util.Args;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Cookie management functions shared by RFC C6265 compliant specification.
+ *
+ * @since 4.4
+ */
+@ThreadSafe
+class RFC6265CookieSpecBase implements CookieSpec {
+
+    private final static char PARAM_DELIMITER  = ';';
+    private final static char COMMA_CHAR       = ',';
+    private final static char EQUAL_CHAR       = '=';
+    private final static char DQUOTE_CHAR      = '"';
+    private final static char ESCAPE_CHAR      = '\\';
+
+    // IMPORTANT!
+    // These private static variables must be treated as immutable and never exposed outside this class
+    private static final BitSet TOKEN_DELIMS = TokenParser.INIT_BITSET(EQUAL_CHAR, PARAM_DELIMITER);
+    private static final BitSet VALUE_DELIMS = TokenParser.INIT_BITSET(PARAM_DELIMITER);
+    private static final BitSet SPECIAL_CHARS = TokenParser.INIT_BITSET(' ',
+            DQUOTE_CHAR, COMMA_CHAR, PARAM_DELIMITER, ESCAPE_CHAR);
+
+    private final CookieAttributeHandler[] attribHandlers;
+    private final Map<String, CookieAttributeHandler> attribHandlerMap;
+    private final TokenParser tokenParser;
+
+    RFC6265CookieSpecBase(final CommonCookieAttributeHandler... handlers) {
+        super();
+        this.attribHandlers = handlers.clone();
+        this.attribHandlerMap = new ConcurrentHashMap<String, CookieAttributeHandler>(handlers.length);
+        for (CommonCookieAttributeHandler handler: handlers) {
+            this.attribHandlerMap.put(handler.getAttributeName().toLowerCase(Locale.ROOT), handler);
+        }
+        this.tokenParser = TokenParser.INSTANCE;
+    }
+
+    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;
+    }
+
+    static String getDefaultDomain(final CookieOrigin origin) {
+        return origin.getHost();
+    }
+
+    @Override
+    public final List<Cookie> parse(final Header header, final CookieOrigin origin) throws MalformedCookieException {
+        Args.notNull(header, "Header");
+        Args.notNull(origin, "Cookie origin");
+        if (!header.getName().equalsIgnoreCase(SM.SET_COOKIE)) {
+            throw new MalformedCookieException("Unrecognized cookie header: '" + header.toString() + "'");
+        }
+        final CharArrayBuffer buffer;
+        final ParserCursor cursor;
+        if (header instanceof FormattedHeader) {
+            buffer = ((FormattedHeader) header).getBuffer();
+            cursor = new ParserCursor(((FormattedHeader) header).getValuePos(), buffer.length());
+        } else {
+            final 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());
+        }
+        final String name = tokenParser.parseToken(buffer, cursor, TOKEN_DELIMS);
+        if (name.length() == 0) {
+            throw new MalformedCookieException("Cookie name is invalid: '" + header.toString() + "'");
+        }
+        if (cursor.atEnd()) {
+            throw new MalformedCookieException("Cookie value is invalid: '" + header.toString() + "'");
+        }
+        final int valueDelim = buffer.charAt(cursor.getPos());
+        cursor.updatePos(cursor.getPos() + 1);
+        if (valueDelim != '=') {
+            throw new MalformedCookieException("Cookie value is invalid: '" + header.toString() + "'");
+        }
+        final String value = tokenParser.parseValue(buffer, cursor, VALUE_DELIMS);
+        if (!cursor.atEnd()) {
+            cursor.updatePos(cursor.getPos() + 1);
+        }
+        final BasicClientCookie cookie = new BasicClientCookie(name, value);
+        cookie.setPath(getDefaultPath(origin));
+        cookie.setDomain(getDefaultDomain(origin));
+        cookie.setCreationDate(new Date());
+
+        final Map<String, String> attribMap = new LinkedHashMap<String, String>();
+        while (!cursor.atEnd()) {
+            final String paramName = tokenParser.parseToken(buffer, cursor, TOKEN_DELIMS);
+            String paramValue = null;
+            if (!cursor.atEnd()) {
+                final int paramDelim = buffer.charAt(cursor.getPos());
+                cursor.updatePos(cursor.getPos() + 1);
+                if (paramDelim == EQUAL_CHAR) {
+                    paramValue = tokenParser.parseToken(buffer, cursor, VALUE_DELIMS);
+                    if (!cursor.atEnd()) {
+                        cursor.updatePos(cursor.getPos() + 1);
+                    }
+                }
+            }
+            cookie.setAttribute(paramName.toLowerCase(Locale.ROOT), paramValue);
+            attribMap.put(paramName, paramValue);
+        }
+        // Ignore 'Expires' if 'Max-Age' is present
+        if (attribMap.containsKey(ClientCookie.MAX_AGE_ATTR)) {
+            attribMap.remove(ClientCookie.EXPIRES_ATTR);
+        }
+
+        for (Map.Entry<String, String> entry: attribMap.entrySet()) {
+            final String paramName = entry.getKey();
+            final String paramValue = entry.getValue();
+            final CookieAttributeHandler handler = this.attribHandlerMap.get(paramName);
+            if (handler != null) {
+                handler.parse(cookie, paramValue);
+            }
+        }
+
+        return Collections.<Cookie>singletonList(cookie);
+    }
+
+    @Override
+    public final void validate(final Cookie cookie, final CookieOrigin origin)
+            throws MalformedCookieException {
+        Args.notNull(cookie, "Cookie");
+        Args.notNull(origin, "Cookie origin");
+        for (final CookieAttributeHandler handler: this.attribHandlers) {
+            handler.validate(cookie, origin);
+        }
+    }
+
+    @Override
+    public final boolean match(final Cookie cookie, final CookieOrigin origin) {
+        Args.notNull(cookie, "Cookie");
+        Args.notNull(origin, "Cookie origin");
+        for (final CookieAttributeHandler handler: this.attribHandlers) {
+            if (!handler.match(cookie, origin)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public List<Header> formatCookies(final List<Cookie> cookies) {
+        Args.notEmpty(cookies, "List of cookies");
+        final List<? extends Cookie> sortedCookies;
+        if (cookies.size() > 1) {
+            // Create a mutable copy and sort the copy.
+            sortedCookies = new ArrayList<Cookie>(cookies);
+            Collections.sort(sortedCookies, CookiePriorityComparator.INSTANCE);
+        } else {
+            sortedCookies = cookies;
+        }
+        final CharArrayBuffer buffer = new CharArrayBuffer(20 * sortedCookies.size());
+        buffer.append(SM.COOKIE);
+        buffer.append(": ");
+        for (int n = 0; n < sortedCookies.size(); n++) {
+            final Cookie cookie = sortedCookies.get(n);
+            if (n > 0) {
+                buffer.append(PARAM_DELIMITER);
+                buffer.append(' ');
+            }
+            buffer.append(cookie.getName());
+            final String s = cookie.getValue();
+            if (s != null) {
+                buffer.append(EQUAL_CHAR);
+                if (containsSpecialChar(s)) {
+                    buffer.append(DQUOTE_CHAR);
+                    for (int i = 0; i < s.length(); i++) {
+                        final char ch = s.charAt(i);
+                        if (ch == DQUOTE_CHAR || ch == ESCAPE_CHAR) {
+                            buffer.append(ESCAPE_CHAR);
+                        }
+                        buffer.append(ch);
+                    }
+                    buffer.append(DQUOTE_CHAR);
+                } else {
+                    buffer.append(s);
+                }
+            }
+        }
+        final List<Header> headers = new ArrayList<Header>(1);
+        headers.add(new BufferedHeader(buffer));
+        return headers;
+    }
+
+    boolean containsSpecialChar(final CharSequence s) {
+        return containsChars(s, SPECIAL_CHARS);
+    }
+
+    boolean containsChars(final CharSequence s, final BitSet chars) {
+        for (int i = 0; i < s.length(); i++) {
+            final char ch = s.charAt(i);
+            if (chars.get(ch)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public final int getVersion() {
+        return 0;
+    }
+
+    @Override
+    public final Header getVersionHeader() {
+        return null;
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/RFC6265CookieSpecProvider.java b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC6265CookieSpecProvider.java
new file mode 100644
index 0000000..695fabf
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC6265CookieSpecProvider.java
@@ -0,0 +1,120 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on 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.annotation.Immutable;
+import org.apache.http.conn.util.PublicSuffixMatcher;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.CookieSpecProvider;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * {@link org.apache.http.cookie.CookieSpecProvider} implementation that provides an instance of
+ * RFC 6265 conformant cookie policy. The instance returned by this factory can be shared by
+ * multiple threads.
+ *
+ * @since 4.4
+ */
+@Immutable
+public class RFC6265CookieSpecProvider implements CookieSpecProvider {
+
+    public enum CompatibilityLevel {
+        STRICT,
+        RELAXED,
+        IE_MEDIUM_SECURITY
+    }
+
+    private final CompatibilityLevel compatibilityLevel;
+    private final PublicSuffixMatcher publicSuffixMatcher;
+
+    private volatile CookieSpec cookieSpec;
+
+    public RFC6265CookieSpecProvider(
+            final CompatibilityLevel compatibilityLevel,
+            final PublicSuffixMatcher publicSuffixMatcher) {
+        super();
+        this.compatibilityLevel = compatibilityLevel != null ? compatibilityLevel : CompatibilityLevel.RELAXED;
+        this.publicSuffixMatcher = publicSuffixMatcher;
+    }
+
+    public RFC6265CookieSpecProvider(final PublicSuffixMatcher publicSuffixMatcher) {
+        this(CompatibilityLevel.RELAXED, publicSuffixMatcher);
+    }
+
+    public RFC6265CookieSpecProvider() {
+        this(CompatibilityLevel.RELAXED, null);
+    }
+
+    @Override
+    public CookieSpec create(final HttpContext context) {
+        if (cookieSpec == null) {
+            synchronized (this) {
+                if (cookieSpec == null) {
+                    switch (this.compatibilityLevel) {
+                        case STRICT:
+                            this.cookieSpec = new RFC6265StrictSpec(
+                                    new BasicPathHandler(),
+                                    PublicSuffixDomainFilter.decorate(
+                                            new BasicDomainHandler(), this.publicSuffixMatcher),
+                                    new BasicMaxAgeHandler(),
+                                    new BasicSecureHandler(),
+                                    new BasicExpiresHandler(RFC6265StrictSpec.DATE_PATTERNS));
+                        case IE_MEDIUM_SECURITY:
+                            this.cookieSpec = new RFC6265LaxSpec(
+                                    new BasicPathHandler() {
+                                        @Override
+                                        public void validate(
+                                                final Cookie cookie,
+                                                final CookieOrigin origin) throws MalformedCookieException {
+                                            // No validation
+                                        }
+                                    },
+                                    PublicSuffixDomainFilter.decorate(
+                                            new BasicDomainHandler(), this.publicSuffixMatcher),
+                                    new BasicMaxAgeHandler(),
+                                    new BasicSecureHandler(),
+                                    new BasicExpiresHandler(RFC6265StrictSpec.DATE_PATTERNS));
+                        default:
+                            this.cookieSpec = new RFC6265LaxSpec(
+                                    new BasicPathHandler(),
+                                    PublicSuffixDomainFilter.decorate(
+                                            new BasicDomainHandler(), this.publicSuffixMatcher),
+                                    new LaxMaxAgeHandler(),
+                                    new BasicSecureHandler(),
+                                    new LaxExpiresHandler());
+                    }
+                }
+            }
+        }
+        return this.cookieSpec;
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/RFC6265LaxSpec.java b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC6265LaxSpec.java
new file mode 100644
index 0000000..8455c83
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC6265LaxSpec.java
@@ -0,0 +1,61 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on 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.annotation.ThreadSafe;
+import org.apache.http.cookie.CommonCookieAttributeHandler;
+
+/**
+ * Standard {@link org.apache.http.cookie.CookieSpec} implementation that enforces a more relaxed
+ * interpretation of the HTTP state management specification (RFC 6265, section 5)
+ * for interoperability with existing servers that do not conform to the well behaved profile
+ * (RFC 6265, section 4).
+ *
+ * @since 4.4
+ */
+@ThreadSafe
+public class RFC6265LaxSpec extends RFC6265CookieSpecBase {
+
+    public RFC6265LaxSpec() {
+        super(new BasicPathHandler(),
+                new BasicDomainHandler(),
+                new LaxMaxAgeHandler(),
+                new BasicSecureHandler(),
+                new LaxExpiresHandler());
+    }
+
+    RFC6265LaxSpec(final CommonCookieAttributeHandler... handlers) {
+        super(handlers);
+    }
+
+    @Override
+    public String toString() {
+        return "rfc6265-lax";
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/RFC6265StrictSpec.java b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC6265StrictSpec.java
new file mode 100644
index 0000000..1765d27
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC6265StrictSpec.java
@@ -0,0 +1,67 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on 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.annotation.ThreadSafe;
+import org.apache.http.client.utils.DateUtils;
+import org.apache.http.cookie.CommonCookieAttributeHandler;
+
+/**
+ * Standard {@link org.apache.http.cookie.CookieSpec} implementation that enforces syntax
+ * and semantics of the well-behaved profile of the HTTP state management specification
+ * (RFC 6265, section 4).
+ *
+ * @since 4.4
+ */
+@ThreadSafe
+public class RFC6265StrictSpec extends RFC6265CookieSpecBase {
+
+    final static String[] DATE_PATTERNS = {
+        DateUtils.PATTERN_RFC1123,
+        DateUtils.PATTERN_RFC1036,
+        DateUtils.PATTERN_ASCTIME
+    };
+
+    public RFC6265StrictSpec() {
+        super(new BasicPathHandler(),
+                new BasicDomainHandler(),
+                new BasicMaxAgeHandler(),
+                new BasicSecureHandler(),
+                new BasicExpiresHandler(DATE_PATTERNS));
+    }
+
+    RFC6265StrictSpec(final CommonCookieAttributeHandler... handlers) {
+        super(handlers);
+    }
+
+    @Override
+    public String toString() {
+        return "rfc6265-strict";
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAddCookies.java b/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAddCookies.java
index 9bd8ece..1c8cf42 100644
--- a/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAddCookies.java
+++ b/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAddCookies.java
@@ -57,6 +57,7 @@
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mockito;
 
 public class TestRequestAddCookies {
 
@@ -371,10 +372,13 @@
         cookie3.setVersion(1);
         cookie3.setDomain("localhost.local");
         cookie3.setPath("/");
-        cookie3.setExpiryDate(new Date(System.currentTimeMillis() - (24 * 60 * 60 * 1000)));
-
+        cookie3.setExpiryDate(new Date(System.currentTimeMillis() + 100));
         this.cookieStore.addCookie(cookie3);
 
+        Assert.assertEquals(3, this.cookieStore.getCookies().size());
+
+        this.cookieStore = Mockito.spy(this.cookieStore);
+
         final HttpRoute route = new HttpRoute(this.target, null, false);
 
         final HttpClientContext context = HttpClientContext.create();
@@ -383,6 +387,9 @@
         context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore);
         context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry);
 
+        // Make sure the third cookie expires
+        Thread.sleep(200);
+
         final HttpRequestInterceptor interceptor = new RequestAddCookies();
         interceptor.process(request, context);
 
@@ -394,6 +401,8 @@
         final Header[] headers2 = request.getHeaders(SM.COOKIE2);
         Assert.assertNotNull(headers2);
         Assert.assertEquals(0, headers2.length);
+
+        Mockito.verify(this.cookieStore, Mockito.times(1)).clearExpired(Mockito.<Date>any());
     }
 
     @Test
@@ -463,41 +472,4 @@
         Assert.assertEquals("name1=value; name2=value; name3=value", headers1[0].getValue());
     }
 
-    @Test
-    public void testAddSpecVersionHeader() throws Exception {
-        final HttpRequest request = new BasicHttpRequest("GET", "/");
-
-        this.cookieStore.clear();
-        final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value1");
-        cookie1.setVersion(0);
-        cookie1.setDomain("localhost.local");
-        cookie1.setPath("/");
-        this.cookieStore.addCookie(cookie1);
-        final BasicClientCookie cookie2 = new BasicClientCookie("name2", "value2");
-        cookie2.setVersion(0);
-        cookie2.setDomain("localhost.local");
-        cookie2.setPath("/");
-        this.cookieStore.addCookie(cookie2);
-
-        final HttpRoute route = new HttpRoute(this.target, null, false);
-
-        final HttpClientContext context = HttpClientContext.create();
-        context.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, this.target);
-        context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
-        context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore);
-        context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry);
-
-        final HttpRequestInterceptor interceptor = new RequestAddCookies();
-        interceptor.process(request, context);
-
-        final Header[] headers1 = request.getHeaders(SM.COOKIE);
-        Assert.assertNotNull(headers1);
-        Assert.assertEquals(1, headers1.length);
-        Assert.assertEquals("name1=value1; name2=value2", headers1[0].getValue());
-        final Header[] headers2 = request.getHeaders(SM.COOKIE2);
-        Assert.assertNotNull(headers2);
-        Assert.assertEquals(1, headers2.length);
-        Assert.assertEquals("$Version=1", headers2[0].getValue());
-    }
-
 }
diff --git a/httpclient/src/test/java/org/apache/http/client/utils/TestURLEncodedUtils.java b/httpclient/src/test/java/org/apache/http/client/utils/TestURLEncodedUtils.java
index 6e7d136..60d1eaf 100644
--- a/httpclient/src/test/java/org/apache/http/client/utils/TestURLEncodedUtils.java
+++ b/httpclient/src/test/java/org/apache/http/client/utils/TestURLEncodedUtils.java
@@ -192,7 +192,7 @@
     };
 
     private static String constructString(final int [] unicodeChars) {
-        final StringBuffer buffer = new StringBuffer();
+        final StringBuilder buffer = new StringBuilder();
         if (unicodeChars != null) {
             for (final int unicodeChar : unicodeChars) {
                 buffer.append((char)unicodeChar);
diff --git a/httpclient/src/test/java/org/apache/http/conn/routing/TestHttpRoute.java b/httpclient/src/test/java/org/apache/http/conn/routing/TestHttpRoute.java
index ea3f0f2..206951a 100644
--- a/httpclient/src/test/java/org/apache/http/conn/routing/TestHttpRoute.java
+++ b/httpclient/src/test/java/org/apache/http/conn/routing/TestHttpRoute.java
@@ -45,7 +45,7 @@
 
     // a selection of constants for generating routes
     public final static
-        HttpHost TARGET1 = new HttpHost("target1.test.invalid");
+        HttpHost TARGET1 = new HttpHost("target1.test.invalid", 80);
     public final static
         HttpHost TARGET2 = new HttpHost("target2.test.invalid", 8080);
     // It is not necessary to have extra targets for https.
@@ -577,4 +577,49 @@
         Assert.assertEquals("route was modified", route3, route1);
     }
 
+    @Test
+    public void testTargetHostNormalizationHttp() {
+        final HttpHost target = new HttpHost("somehost", -1, "http");
+        final HttpRoute route = new HttpRoute(target);
+        final HttpHost targetHost = route.getTargetHost();
+        Assert.assertEquals("somehost", targetHost.getHostName());
+        Assert.assertEquals(80, targetHost.getPort());
+        Assert.assertEquals("http", targetHost.getSchemeName());
+        Assert.assertEquals(null, targetHost.getAddress());
+    }
+
+    @Test
+    public void testTargetHostNormalizationHttps() {
+        final HttpHost target = new HttpHost("somehost", -1, "https");
+        final HttpRoute route = new HttpRoute(target);
+        final HttpHost targetHost = route.getTargetHost();
+        Assert.assertEquals("somehost", targetHost.getHostName());
+        Assert.assertEquals(443, targetHost.getPort());
+        Assert.assertEquals("https", targetHost.getSchemeName());
+        Assert.assertEquals(null, targetHost.getAddress());
+    }
+
+    @Test
+    public void testTargetHostNormalizationUnknownPorotocol() {
+        final HttpHost target = new HttpHost("somehost", -1, "blah");
+        final HttpRoute route = new HttpRoute(target);
+        final HttpHost targetHost = route.getTargetHost();
+        Assert.assertEquals("somehost", targetHost.getHostName());
+        Assert.assertEquals(-1, targetHost.getPort());
+        Assert.assertEquals("blah", targetHost.getSchemeName());
+        Assert.assertEquals(null, targetHost.getAddress());
+    }
+
+    @Test
+    public void testTargetHostNormalizationAddress() throws Exception {
+        final InetAddress address = InetAddress.getByAddress(new byte[]{127, 0, 0, 1});
+        final HttpHost target = new HttpHost(address, -1, "http");
+        final HttpRoute route = new HttpRoute(target);
+        final HttpHost targetHost = route.getTargetHost();
+        Assert.assertEquals("localhost", targetHost.getHostName());
+        Assert.assertEquals(80, targetHost.getPort());
+        Assert.assertEquals("http", targetHost.getSchemeName());
+        Assert.assertEquals(address, targetHost.getAddress());
+    }
+
 }
diff --git a/httpclient/src/test/java/org/apache/http/conn/routing/TestRouteDirector.java b/httpclient/src/test/java/org/apache/http/conn/routing/TestRouteDirector.java
index e0d356f..802ab95 100644
--- a/httpclient/src/test/java/org/apache/http/conn/routing/TestRouteDirector.java
+++ b/httpclient/src/test/java/org/apache/http/conn/routing/TestRouteDirector.java
@@ -42,7 +42,7 @@
 
     // a selection of constants for generating routes
     public final static
-        HttpHost TARGET1 = new HttpHost("target1.test.invalid");
+        HttpHost TARGET1 = new HttpHost("target1.test.invalid", 80);
     public final static
         HttpHost TARGET2 = new HttpHost("target2.test.invalid", 8080);
     // It is not necessary to have extra targets for https.
@@ -50,7 +50,7 @@
     // for routes, they will not be determined from the scheme.
 
     public final static
-        HttpHost PROXY1 = new HttpHost("proxy1.test.invalid");
+        HttpHost PROXY1 = new HttpHost("proxy1.test.invalid", 80);
     public final static
         HttpHost PROXY2 = new HttpHost("proxy2.test.invalid", 1080);
     public final static
diff --git a/httpclient/src/test/java/org/apache/http/conn/routing/TestRouteTracker.java b/httpclient/src/test/java/org/apache/http/conn/routing/TestRouteTracker.java
index 37db9a6..4e87f85 100644
--- a/httpclient/src/test/java/org/apache/http/conn/routing/TestRouteTracker.java
+++ b/httpclient/src/test/java/org/apache/http/conn/routing/TestRouteTracker.java
@@ -45,7 +45,7 @@
 
     // a selection of constants for generating routes
     public final static
-        HttpHost TARGET1 = new HttpHost("target1.test.invalid");
+        HttpHost TARGET1 = new HttpHost("target1.test.invalid", 80);
     public final static
         HttpHost TARGET2 = new HttpHost("target2.test.invalid", 8080);
     // It is not necessary to have extra targets for https.
@@ -53,7 +53,7 @@
     // for routes, they will not be determined from the scheme.
 
     public final static
-        HttpHost PROXY1 = new HttpHost("proxy1.test.invalid");
+        HttpHost PROXY1 = new HttpHost("proxy1.test.invalid", 80);
     public final static
         HttpHost PROXY2 = new HttpHost("proxy2.test.invalid", 1080);
     public final static
diff --git a/httpclient/src/test/java/org/apache/http/conn/ssl/TestDefaultHostnameVerifier.java b/httpclient/src/test/java/org/apache/http/conn/ssl/TestDefaultHostnameVerifier.java
index a68b1ed..b809bcb 100644
--- a/httpclient/src/test/java/org/apache/http/conn/ssl/TestDefaultHostnameVerifier.java
+++ b/httpclient/src/test/java/org/apache/http/conn/ssl/TestDefaultHostnameVerifier.java
@@ -194,6 +194,16 @@
     }
 
     @Test
+    public void testDomainRootMatching() {
+
+        Assert.assertFalse(DefaultHostnameVerifier.matchDomainRoot("a.b.c", null));
+        Assert.assertTrue(DefaultHostnameVerifier.matchDomainRoot("a.b.c", "a.b.c"));
+        Assert.assertFalse(DefaultHostnameVerifier.matchDomainRoot("aa.b.c", "a.b.c"));
+        Assert.assertFalse(DefaultHostnameVerifier.matchDomainRoot("a.b.c", "aa.b.c"));
+        Assert.assertTrue(DefaultHostnameVerifier.matchDomainRoot("a.a.b.c", "a.b.c"));
+    }
+
+    @Test
     public void testIdentityMatching() {
 
         Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.b.c"));
diff --git a/httpclient/src/test/java/org/apache/http/conn/ssl/TestSSLContextBuilder.java b/httpclient/src/test/java/org/apache/http/conn/ssl/TestSSLContextBuilder.java
deleted file mode 100644
index cf906ba..0000000
--- a/httpclient/src/test/java/org/apache/http/conn/ssl/TestSSLContextBuilder.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on 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 java.io.InputStream;
-import java.net.URL;
-import java.security.KeyStore;
-import java.security.UnrecoverableKeyException;
-
-import org.junit.Test;
-
-/**
- * Unit tests for {@link org.apache.http.conn.ssl.SSLContextBuilder}.
- */
-public class TestSSLContextBuilder {
-
-    private static KeyStore load(final String res, final char[] passwd) throws Exception {
-        final KeyStore keystore  = KeyStore.getInstance("jks");
-        final ClassLoader cl = TestSSLContextBuilder.class.getClassLoader();
-        final URL url = cl.getResource(res);
-        final InputStream instream = url.openStream();
-        try {
-            keystore.load(instream, passwd);
-        } finally {
-            instream.close();
-        }
-        return keystore;
-    }
-
-    @Test
-    public void testBuildDefault() throws Exception {
-        new SSLContextBuilder().build();
-    }
-
-    @Test
-    public void testBuildAllNull() throws Exception {
-        new SSLContextBuilder()
-                .useProtocol(null)
-                .setSecureRandom(null)
-                .loadTrustMaterial(null)
-                .loadKeyMaterial(null, null)
-                .build();
-    }
-
-    @Test
-    public void testLoadTrustMultipleMaterial() throws Exception {
-        final KeyStore truststore1 = load("hc-test-1.truststore", "nopassword".toCharArray());
-        final KeyStore truststore2 = load("hc-test-2.truststore", "nopassword".toCharArray());
-        new SSLContextBuilder()
-                .loadTrustMaterial(truststore1)
-                .loadTrustMaterial(truststore2)
-                .build();
-    }
-
-    @Test
-    public void testKeyWithAlternatePassword() throws Exception {
-        final KeyStore keystore = load("test-keypasswd.keystore", "nopassword".toCharArray());
-        final String keyPassword = "password";
-        new SSLContextBuilder()
-                .loadKeyMaterial(keystore, keyPassword.toCharArray())
-                .loadTrustMaterial(keystore)
-                .build();
-    }
-
-    @Test(expected=UnrecoverableKeyException.class)
-    public void testKeyWithAlternatePasswordInvalid() throws Exception {
-        final KeyStore keystore = load("test-keypasswd.keystore", "nopassword".toCharArray());
-        final String keyPassword = "!password";
-        new SSLContextBuilder()
-                .loadKeyMaterial(keystore, keyPassword.toCharArray())
-                .loadTrustMaterial(keystore)
-                .build();
-    }
-
-}
diff --git a/httpclient/src/test/java/org/apache/http/conn/ssl/TestSSLSocketFactory.java b/httpclient/src/test/java/org/apache/http/conn/ssl/TestSSLSocketFactory.java
index 153e693..3a4cb95 100644
--- a/httpclient/src/test/java/org/apache/http/conn/ssl/TestSSLSocketFactory.java
+++ b/httpclient/src/test/java/org/apache/http/conn/ssl/TestSSLSocketFactory.java
@@ -32,25 +32,27 @@
 import java.net.Socket;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
-import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLServerSocket;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSocket;
 
 import org.apache.http.HttpHost;
 import org.apache.http.impl.bootstrap.HttpServer;
+import org.apache.http.impl.bootstrap.SSLServerSetupHandler;
 import org.apache.http.impl.bootstrap.ServerBootstrap;
 import org.apache.http.localserver.LocalServerTestBase;
 import org.apache.http.localserver.SSLTestContexts;
 import org.apache.http.protocol.BasicHttpContext;
 import org.apache.http.protocol.HttpContext;
+import org.apache.http.ssl.SSLContexts;
 import org.junit.After;
 import org.junit.Assert;
-import org.junit.Ignore;
 import org.junit.Test;
 
 /**
@@ -110,6 +112,30 @@
     }
 
     @Test
+    public void testBasicDefaultHostnameVerifier() throws Exception {
+        this.server = ServerBootstrap.bootstrap()
+                .setServerInfo(LocalServerTestBase.ORIGIN)
+                .setSslContext(SSLTestContexts.createServerSSLContext())
+                .create();
+        this.server.start();
+
+        final HttpContext context = new BasicHttpContext();
+        final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
+                SSLTestContexts.createClientSSLContext(), SSLConnectionSocketFactory.getDefaultHostnameVerifier());
+        final Socket socket = socketFactory.createSocket(context);
+        final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
+        final HttpHost target = new HttpHost("localhost", this.server.getLocalPort(), "https");
+        final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket(0, socket, target, remoteAddress, null, context);
+        try {
+            final SSLSession sslsession = sslSocket.getSession();
+
+            Assert.assertNotNull(sslsession);
+        } finally {
+            sslSocket.close();
+        }
+    }
+
+    @Test
     public void testClientAuthSSL() throws Exception {
         this.server = ServerBootstrap.bootstrap()
                 .setServerInfo(LocalServerTestBase.ORIGIN)
@@ -135,52 +161,19 @@
         }
     }
 
-    @Ignore("There is no way to force client auth with HttpServer in 4.4a1")
     @Test(expected=IOException.class)
     public void testClientAuthSSLFailure() throws Exception {
         this.server = ServerBootstrap.bootstrap()
                 .setServerInfo(LocalServerTestBase.ORIGIN)
                 .setSslContext(SSLTestContexts.createServerSSLContext())
-                .create();
-        this.server.start();
+                .setSslSetupHandler(new SSLServerSetupHandler() {
 
-        final HttpContext context = new BasicHttpContext();
-        final TestX509HostnameVerifier hostVerifier = new TestX509HostnameVerifier();
-        final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
-                SSLTestContexts.createClientSSLContext(), hostVerifier);
-        final Socket socket = socketFactory.createSocket(context);
-        final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
-        final HttpHost target = new HttpHost("localhost", this.server.getLocalPort(), "https");
-        final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket(0, socket, target, remoteAddress, null, context);
-        try {
-            final SSLSession sslsession = sslSocket.getSession();
+                    @Override
+                    public void initialize(final SSLServerSocket socket) throws SSLException {
+                        socket.setNeedClientAuth(true);
+                    }
 
-            Assert.assertNotNull(sslsession);
-            Assert.assertTrue(hostVerifier.isFired());
-        } finally {
-            sslSocket.close();
-        }
-    }
-
-    @Test
-    public void testClientAuthSSLAliasChoice() throws Exception {
-        // TODO unused - is there a bug in the test?
-        final PrivateKeyStrategy aliasStrategy = new PrivateKeyStrategy() {
-
-            @Override
-            public String chooseAlias(
-                    final Map<String, PrivateKeyDetails> aliases, final Socket socket) {
-                Assert.assertEquals(2, aliases.size());
-                Assert.assertTrue(aliases.containsKey("hc-test-key-1"));
-                Assert.assertTrue(aliases.containsKey("hc-test-key-2"));
-                return "hc-test-key-2";
-            }
-
-        };
-
-        this.server = ServerBootstrap.bootstrap()
-                .setServerInfo(LocalServerTestBase.ORIGIN)
-                .setSslContext(SSLTestContexts.createServerSSLContext())
+                })
                 .create();
         this.server.start();
 
@@ -257,4 +250,55 @@
         sslSocket.close();
     }
 
+    @Test
+    public void testTLSOnly() throws Exception {
+        this.server = ServerBootstrap.bootstrap()
+                .setServerInfo(LocalServerTestBase.ORIGIN)
+                .setSslContext(SSLTestContexts.createServerSSLContext())
+                .setSslSetupHandler(new SSLServerSetupHandler() {
+
+                    @Override
+                    public void initialize(final SSLServerSocket socket) throws SSLException {
+                        socket.setEnabledProtocols(new String[] {"TLSv1"});
+                    }
+
+                })
+                .create();
+        this.server.start();
+
+        final HttpContext context = new BasicHttpContext();
+        final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
+                SSLTestContexts.createClientSSLContext());
+        final Socket socket = socketFactory.createSocket(context);
+        final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
+        final HttpHost target = new HttpHost("localhost", this.server.getLocalPort(), "https");
+        final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket(0, socket, target, remoteAddress, null, context);
+        final SSLSession sslsession = sslSocket.getSession();
+        Assert.assertNotNull(sslsession);
+    }
+
+    @Test(expected=IOException.class)
+    public void testSSLDisabledByDefault() throws Exception {
+        this.server = ServerBootstrap.bootstrap()
+                .setServerInfo(LocalServerTestBase.ORIGIN)
+                .setSslContext(SSLTestContexts.createServerSSLContext())
+                .setSslSetupHandler(new SSLServerSetupHandler() {
+
+                    @Override
+                    public void initialize(final SSLServerSocket socket) throws SSLException {
+                        socket.setEnabledProtocols(new String[] {"SSLv3"});
+                    }
+
+                })
+                .create();
+        this.server.start();
+
+        final HttpContext context = new BasicHttpContext();
+        final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
+                SSLTestContexts.createClientSSLContext());
+        final Socket socket = socketFactory.createSocket(context);
+        final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
+        final HttpHost target = new HttpHost("localhost", this.server.getLocalPort(), "https");
+        socketFactory.connectSocket(0, socket, target, remoteAddress, null, context);
+    }
 }
diff --git a/httpclient/src/test/java/org/apache/http/cookie/TestCookiePriorityComparator.java b/httpclient/src/test/java/org/apache/http/cookie/TestCookiePriorityComparator.java
new file mode 100644
index 0000000..5878173
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/cookie/TestCookiePriorityComparator.java
@@ -0,0 +1,113 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on 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.Comparator;
+import java.util.Date;
+
+import org.apache.http.impl.cookie.BasicClientCookie;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test cases for {@link org.apache.http.cookie.CookiePriorityComparator}.
+ */
+public class TestCookiePriorityComparator {
+
+    private Comparator<Cookie> comparator;
+
+    @Before
+    public void setup() {
+        comparator = CookiePriorityComparator.INSTANCE;
+    }
+
+    @Test
+    public void testUnequality() {
+        final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value");
+        cookie1.setPath("/a/b/");
+        final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value");
+        cookie2.setPath("/a/");
+        Assert.assertTrue(comparator.compare(cookie1, cookie2) < 0);
+        Assert.assertTrue(comparator.compare(cookie2, cookie1) > 0);
+    }
+
+    @Test
+    public void testEquality() {
+        final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value");
+        cookie1.setPath("/a");
+        final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value");
+        cookie2.setPath("/a");
+        Assert.assertTrue(comparator.compare(cookie1, cookie2) == 0);
+        Assert.assertTrue(comparator.compare(cookie2, cookie1) == 0);
+    }
+
+    @Test
+    public void testUnequalityTrailingSlash() {
+        final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value");
+        cookie1.setPath("/a/");
+        final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value");
+        cookie2.setPath("/a");
+        Assert.assertTrue(comparator.compare(cookie1, cookie2) < 0);
+        Assert.assertTrue(comparator.compare(cookie2, cookie1) > 0);
+    }
+
+    @Test
+    public void testEqualityNullPath() {
+        final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value");
+        cookie1.setPath(null);
+        final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value");
+        cookie2.setPath("/");
+        Assert.assertTrue(comparator.compare(cookie1, cookie2) == 0);
+        Assert.assertTrue(comparator.compare(cookie2, cookie1) == 0);
+    }
+
+    @Test
+    public void testEqualitySameLength() {
+        final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value");
+        cookie1.setPath("/this");
+        final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value");
+        cookie2.setPath("/that");
+        Assert.assertTrue(comparator.compare(cookie1, cookie2) == 0);
+        Assert.assertTrue(comparator.compare(cookie2, cookie1) == 0);
+    }
+
+    @Test
+    public void testUnequalityCreationDate() {
+        final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value");
+        cookie1.setPath("/blah");
+        cookie1.setCreationDate(new Date(System.currentTimeMillis() - 200000));
+        final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value");
+        cookie2.setPath("/blah");
+        cookie2.setCreationDate(new Date(System.currentTimeMillis()));
+        Assert.assertTrue(comparator.compare(cookie1, cookie2) < 0);
+        Assert.assertTrue(comparator.compare(cookie2, cookie1) > 0);
+    }
+
+}
+
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestAIMDBackoffManager.java b/httpclient/src/test/java/org/apache/http/impl/client/TestAIMDBackoffManager.java
index 5ac3b86..06dec0f 100644
--- a/httpclient/src/test/java/org/apache/http/impl/client/TestAIMDBackoffManager.java
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestAIMDBackoffManager.java
@@ -47,7 +47,7 @@
     @Before
     public void setUp() {
         connPerRoute = new MockConnPoolControl();
-        route = new HttpRoute(new HttpHost("localhost:80"));
+        route = new HttpRoute(new HttpHost("localhost", 80));
         clock = new MockClock();
         impl = new AIMDBackoffManager(connPerRoute, clock);
         impl.setPerHostConnectionCap(10);
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestCloseableHttpClient.java b/httpclient/src/test/java/org/apache/http/impl/client/TestCloseableHttpClient.java
index 4dc389b..766f233 100644
--- a/httpclient/src/test/java/org/apache/http/impl/client/TestCloseableHttpClient.java
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestCloseableHttpClient.java
@@ -166,7 +166,7 @@
                     Mockito.eq(new HttpHost("somehost", 444, "https")),
                     Mockito.same(httpget),
                     (HttpContext) Mockito.isNull());
-            Mockito.verify(content).close();
+            Mockito.verify(response).close();
             throw ex;
         }
     }
@@ -190,7 +190,7 @@
                     Mockito.eq(new HttpHost("somehost", 444, "https")),
                     Mockito.same(httpget),
                     (HttpContext) Mockito.isNull());
-            Mockito.verify(content).close();
+            Mockito.verify(response).close();
             throw ex;
         }
     }
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestIdleConnectionEvictor.java b/httpclient/src/test/java/org/apache/http/impl/client/TestIdleConnectionEvictor.java
new file mode 100644
index 0000000..135f634
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestIdleConnectionEvictor.java
@@ -0,0 +1,80 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on 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.concurrent.TimeUnit;
+
+import org.apache.http.conn.HttpClientConnectionManager;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/**
+ * Unit tests for {@link org.apache.http.impl.client.IdleConnectionEvictor}.
+ */
+public class TestIdleConnectionEvictor {
+
+    @Test
+    public void testEvictExpiredAndIdle() throws Exception {
+        final HttpClientConnectionManager cm = Mockito.mock(HttpClientConnectionManager.class);
+        final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor(cm,
+                500, TimeUnit.MILLISECONDS, 3, TimeUnit.SECONDS);
+        connectionEvictor.start();
+
+        Thread.sleep(1000);
+
+        Mockito.verify(cm, Mockito.atLeast(1)).closeExpiredConnections();
+        Mockito.verify(cm, Mockito.atLeast(1)).closeIdleConnections(3000, TimeUnit.MILLISECONDS);
+
+        Assert.assertTrue(connectionEvictor.isRunning());
+
+        connectionEvictor.shutdown();
+        connectionEvictor.awaitTermination(1, TimeUnit.SECONDS);
+        Assert.assertFalse(connectionEvictor.isRunning());
+    }
+
+    @Test
+    public void testEvictExpiredOnly() throws Exception {
+        final HttpClientConnectionManager cm = Mockito.mock(HttpClientConnectionManager.class);
+        final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor(cm,
+                500, TimeUnit.MILLISECONDS, 0, TimeUnit.SECONDS);
+        connectionEvictor.start();
+
+        Thread.sleep(1000);
+
+        Mockito.verify(cm, Mockito.atLeast(1)).closeExpiredConnections();
+        Mockito.verify(cm, Mockito.never()).closeIdleConnections(Mockito.anyLong(), Mockito.<TimeUnit>any());
+
+        Assert.assertTrue(connectionEvictor.isRunning());
+
+        connectionEvictor.shutdown();
+        connectionEvictor.awaitTermination(1, TimeUnit.SECONDS);
+        Assert.assertFalse(connectionEvictor.isRunning());
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestInternalHttpClient.java b/httpclient/src/test/java/org/apache/http/impl/client/TestInternalHttpClient.java
index 200bd9b..9567073 100644
--- a/httpclient/src/test/java/org/apache/http/impl/client/TestInternalHttpClient.java
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestInternalHttpClient.java
@@ -94,7 +94,7 @@
     @Test
     public void testExecute() throws Exception {
         final HttpGet httpget = new HttpGet("http://somehost/stuff");
-        final HttpRoute route = new HttpRoute(new HttpHost("somehost"));
+        final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80));
 
         final ArgumentCaptor<HttpRequestWrapper> argcap = ArgumentCaptor.forClass(HttpRequestWrapper.class);
         Mockito.when(routePlanner.determineRoute(
@@ -117,7 +117,7 @@
     @Test(expected=ClientProtocolException.class)
     public void testExecuteHttpException() throws Exception {
         final HttpGet httpget = new HttpGet("http://somehost/stuff");
-        final HttpRoute route = new HttpRoute(new HttpHost("somehost"));
+        final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80));
 
         Mockito.when(routePlanner.determineRoute(
                 Mockito.eq(new HttpHost("somehost")),
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestCookie2Support.java b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestCookie2Support.java
deleted file mode 100644
index 6585850..0000000
--- a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestCookie2Support.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on 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.integration;
-
-import java.io.IOException;
-import java.util.List;
-
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpException;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.ProtocolVersion;
-import org.apache.http.client.CookieStore;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.protocol.HttpClientContext;
-import org.apache.http.cookie.Cookie;
-import org.apache.http.cookie.SM;
-import org.apache.http.cookie.SetCookie2;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.BasicCookieStore;
-import org.apache.http.localserver.LocalServerTestBase;
-import org.apache.http.message.BasicHeader;
-import org.apache.http.protocol.HttpContext;
-import org.apache.http.protocol.HttpRequestHandler;
-import org.apache.http.util.EntityUtils;
-import org.junit.Assert;
-import org.junit.Test;
-
-/**
- * Cookie2 support tests.
- */
-public class TestCookie2Support extends LocalServerTestBase {
-
-    private static class CookieVer0Service implements HttpRequestHandler {
-
-        @Override
-        public void handle(
-                final HttpRequest request,
-                final HttpResponse response,
-                final HttpContext context) throws HttpException, IOException {
-            final ProtocolVersion httpversion = request.getRequestLine().getProtocolVersion();
-            response.setStatusLine(httpversion, HttpStatus.SC_OK);
-            response.addHeader(new BasicHeader("Set-Cookie", "name1=value1; path=/test"));
-            final StringEntity entity = new StringEntity("whatever");
-            response.setEntity(entity);
-        }
-
-    }
-
-    @Test
-    public void testCookieVersionSupportHeader1() throws Exception {
-        this.serverBootstrap.registerHandler("*", new CookieVer0Service());
-
-        final HttpHost target = start();
-
-        final CookieStore cookieStore = new BasicCookieStore();
-        final HttpClientContext context = HttpClientContext.create();
-        context.setCookieStore(cookieStore);
-
-        final HttpGet httpget = new HttpGet("/test/");
-
-        final HttpResponse response1 = this.httpclient.execute(target, httpget, context);
-        final HttpEntity e1 = response1.getEntity();
-        EntityUtils.consume(e1);
-
-        final List<Cookie> cookies = cookieStore.getCookies();
-        Assert.assertNotNull(cookies);
-        Assert.assertEquals(1, cookies.size());
-
-        final HttpResponse response2 = this.httpclient.execute(target, httpget, context);
-        final HttpEntity e2 = response2.getEntity();
-        EntityUtils.consume(e2);
-
-        final HttpRequest reqWrapper = context.getRequest();
-
-        final Header cookiesupport = reqWrapper.getFirstHeader("Cookie2");
-        Assert.assertNotNull(cookiesupport);
-        Assert.assertEquals("$Version=1", cookiesupport.getValue());
-    }
-
-    private static class CookieVer1Service implements HttpRequestHandler {
-
-        @Override
-        public void handle(
-                final HttpRequest request,
-                final HttpResponse response,
-                final HttpContext context) throws HttpException, IOException {
-            final ProtocolVersion httpversion = request.getRequestLine().getProtocolVersion();
-            response.setStatusLine(httpversion, HttpStatus.SC_OK);
-            response.addHeader(new BasicHeader("Set-Cookie", "name1=value1; Path=\"/test\"; Version=1"));
-            response.addHeader(new BasicHeader("Set-Cookie2", "name2=value2; Path=\"/test\"; Version=1"));
-            final StringEntity entity = new StringEntity("whatever");
-            response.setEntity(entity);
-        }
-
-    }
-
-    @Test
-    public void testCookieVersionSupportHeader2() throws Exception {
-        this.serverBootstrap.registerHandler("*", new CookieVer1Service());
-
-        final HttpHost target = start();
-
-        final CookieStore cookieStore = new BasicCookieStore();
-        final HttpClientContext context = HttpClientContext.create();
-        context.setCookieStore(cookieStore);
-
-        final HttpGet httpget = new HttpGet("/test/");
-
-        final HttpResponse response1 = this.httpclient.execute(target, httpget, context);
-        final HttpEntity e1 = response1.getEntity();
-        EntityUtils.consume(e1);
-
-        final List<Cookie> cookies = cookieStore.getCookies();
-        Assert.assertNotNull(cookies);
-        Assert.assertEquals(2, cookies.size());
-
-        final HttpResponse response2 = this.httpclient.execute(target, httpget, context);
-        final HttpEntity e2 = response2.getEntity();
-        EntityUtils.consume(e2);
-
-        final HttpRequest reqWrapper = context.getRequest();
-
-        final Header cookiesupport = reqWrapper.getFirstHeader(SM.COOKIE2);
-        Assert.assertNotNull(cookiesupport);
-        Assert.assertEquals("$Version=1", cookiesupport.getValue());
-    }
-
-    private static class CookieVer2Service implements HttpRequestHandler {
-
-        @Override
-        public void handle(
-                final HttpRequest request,
-                final HttpResponse response,
-                final HttpContext context) throws HttpException, IOException {
-            final ProtocolVersion httpversion = request.getRequestLine().getProtocolVersion();
-            response.setStatusLine(httpversion, HttpStatus.SC_OK);
-            response.addHeader(new BasicHeader("Set-Cookie2", "name2=value2; Path=\"/test\"; Version=2"));
-            final StringEntity entity = new StringEntity("whatever");
-            response.setEntity(entity);
-        }
-
-    }
-
-    @Test
-    public void testCookieVersionSupportHeader3() throws Exception {
-        this.serverBootstrap.registerHandler("*", new CookieVer2Service());
-
-        final HttpHost target = start();
-
-        final CookieStore cookieStore = new BasicCookieStore();
-        final HttpClientContext context = HttpClientContext.create();
-        context.setCookieStore(cookieStore);
-
-        final HttpGet httpget = new HttpGet("/test/");
-
-        final HttpResponse response1 = this.httpclient.execute(target, httpget, context);
-        final HttpEntity e1 = response1.getEntity();
-        EntityUtils.consume(e1);
-
-        final List<Cookie> cookies = cookieStore.getCookies();
-        Assert.assertNotNull(cookies);
-        Assert.assertEquals(1, cookies.size());
-
-        final HttpResponse response2 = this.httpclient.execute(target, httpget, context);
-        final HttpEntity e2 = response2.getEntity();
-        EntityUtils.consume(e2);
-
-        final HttpRequest reqWrapper = context.getRequest();
-
-        final Header cookiesupport = reqWrapper.getFirstHeader("Cookie2");
-        Assert.assertNotNull(cookiesupport);
-        Assert.assertEquals("$Version=1", cookiesupport.getValue());
-    }
-
-    private static class SetCookieVersionMixService implements HttpRequestHandler {
-
-        @Override
-        public void handle(
-                final HttpRequest request,
-                final HttpResponse response,
-                final HttpContext context) throws HttpException, IOException {
-            final ProtocolVersion httpversion = request.getRequestLine().getProtocolVersion();
-            response.setStatusLine(httpversion, HttpStatus.SC_OK);
-            response.addHeader(new BasicHeader("Set-Cookie", "name=wrong; Path=/test"));
-            response.addHeader(new BasicHeader("Set-Cookie2", "name=right; Path=\"/test\"; Version=1"));
-            final StringEntity entity = new StringEntity("whatever");
-            response.setEntity(entity);
-        }
-
-    }
-
-    @Test
-    public void testSetCookieVersionMix() throws Exception {
-        this.serverBootstrap.registerHandler("*", new SetCookieVersionMixService());
-
-        final HttpHost target = start();
-
-        final CookieStore cookieStore = new BasicCookieStore();
-        final HttpClientContext context = HttpClientContext.create();
-        context.setCookieStore(cookieStore);
-
-        final HttpGet httpget = new HttpGet("/test/");
-
-        final HttpResponse response1 = this.httpclient.execute(target, httpget, context);
-        final HttpEntity e1 = response1.getEntity();
-        EntityUtils.consume(e1);
-
-        final List<Cookie> cookies = cookieStore.getCookies();
-        Assert.assertNotNull(cookies);
-        Assert.assertEquals(1, cookies.size());
-        Assert.assertEquals("right", cookies.get(0).getValue());
-        Assert.assertTrue(cookies.get(0) instanceof SetCookie2);
-    }
-
-}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestIdleConnectionEviction.java b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestIdleConnectionEviction.java
index 78972fe..eee9d36 100644
--- a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestIdleConnectionEviction.java
+++ b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestIdleConnectionEviction.java
@@ -35,7 +35,7 @@
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.conn.HttpClientConnectionManager;
+import org.apache.http.impl.client.IdleConnectionEvictor;
 import org.apache.http.localserver.LocalServerTestBase;
 import org.apache.http.util.EntityUtils;
 import org.junit.Test;
@@ -49,7 +49,8 @@
 
         final HttpHost target = start();
 
-        final IdleConnectionMonitor idleConnectionMonitor = new IdleConnectionMonitor(this.connManager);
+        final IdleConnectionEvictor idleConnectionMonitor = new IdleConnectionEvictor(
+                this.connManager, 50, TimeUnit.MILLISECONDS);
         idleConnectionMonitor.start();
 
         final HttpGet httpget = new HttpGet("/random/1024");
@@ -115,38 +116,4 @@
 
     }
 
-    public static class IdleConnectionMonitor extends Thread {
-
-        private final HttpClientConnectionManager cm;
-        private volatile boolean shutdown;
-
-        public IdleConnectionMonitor(final HttpClientConnectionManager cm) {
-            super();
-            this.cm = cm;
-            setDaemon(true);
-        }
-
-        @Override
-        public void run() {
-            try {
-                while (!this.shutdown) {
-                    synchronized (this) {
-                        wait(250);
-                        this.cm.closeIdleConnections(1, TimeUnit.MILLISECONDS);
-                    }
-                }
-            } catch (final InterruptedException ex) {
-                // terminate
-            }
-        }
-
-        public void shutdown() {
-            this.shutdown = true;
-            synchronized (this) {
-                notifyAll();
-            }
-        }
-
-    }
-
 }
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestBasicHttpClientConnectionManager.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestBasicHttpClientConnectionManager.java
index da6419d..b64bb7e 100644
--- a/httpclient/src/test/java/org/apache/http/impl/conn/TestBasicHttpClientConnectionManager.java
+++ b/httpclient/src/test/java/org/apache/http/impl/conn/TestBasicHttpClientConnectionManager.java
@@ -85,7 +85,7 @@
 
     @Test
     public void testLeaseReleaseNonReusable() throws Exception {
-        final HttpHost target = new HttpHost("localhost");
+        final HttpHost target = new HttpHost("localhost", 80);
         final HttpRoute route = new HttpRoute(target);
 
         Mockito.when(connFactory.create(
@@ -112,7 +112,7 @@
 
     @Test
     public void testLeaseReleaseReusable() throws Exception {
-        final HttpHost target = new HttpHost("somehost");
+        final HttpHost target = new HttpHost("somehost", 80);
         final HttpRoute route = new HttpRoute(target);
 
         Mockito.when(connFactory.create(Mockito.eq(route), Mockito.<ConnectionConfig>any())).thenReturn(conn);
@@ -142,7 +142,7 @@
 
     @Test
     public void testLeaseReleaseReusableWithState() throws Exception {
-        final HttpHost target = new HttpHost("somehost");
+        final HttpHost target = new HttpHost("somehost", 80);
         final HttpRoute route = new HttpRoute(target);
 
         Mockito.when(connFactory.create(
@@ -173,7 +173,7 @@
 
     @Test
     public void testLeaseDifferentRoute() throws Exception {
-        final HttpHost target1 = new HttpHost("somehost");
+        final HttpHost target1 = new HttpHost("somehost", 80);
         final HttpRoute route1 = new HttpRoute(target1);
 
         Mockito.when(connFactory.create(
@@ -193,7 +193,7 @@
         Assert.assertEquals(route1, mgr.getRoute());
         Assert.assertEquals(null, mgr.getState());
 
-        final HttpHost target2 = new HttpHost("otherhost");
+        final HttpHost target2 = new HttpHost("otherhost", 80);
         final HttpRoute route2 = new HttpRoute(target2);
         final ConnectionRequest connRequest2 = mgr.requestConnection(route2, null);
         final HttpClientConnection conn2 = connRequest2.get(0, TimeUnit.MILLISECONDS);
@@ -209,7 +209,7 @@
 
     @Test
     public void testLeaseExpired() throws Exception {
-        final HttpHost target = new HttpHost("somehost");
+        final HttpHost target = new HttpHost("somehost", 80);
         final HttpRoute route = new HttpRoute(target);
 
         Mockito.when(connFactory.create(
@@ -259,7 +259,7 @@
 
     @Test
     public void testShutdown() throws Exception {
-        final HttpHost target = new HttpHost("somehost");
+        final HttpHost target = new HttpHost("somehost", 80);
         final HttpRoute route = new HttpRoute(target);
 
         Mockito.when(connFactory.create(
@@ -297,7 +297,7 @@
 
     @Test
     public void testCloseExpired() throws Exception {
-        final HttpHost target = new HttpHost("somehost");
+        final HttpHost target = new HttpHost("somehost", 80);
         final HttpRoute route = new HttpRoute(target);
 
         Mockito.when(connFactory.create(
@@ -326,7 +326,7 @@
 
     @Test
     public void testCloseIdle() throws Exception {
-        final HttpHost target = new HttpHost("somehost");
+        final HttpHost target = new HttpHost("somehost", 80);
         final HttpRoute route = new HttpRoute(target);
 
         Mockito.when(connFactory.create(
@@ -355,7 +355,7 @@
 
     @Test(expected=IllegalStateException.class)
     public void testAlreadyLeased() throws Exception {
-        final HttpHost target = new HttpHost("somehost");
+        final HttpHost target = new HttpHost("somehost", 80);
         final HttpRoute route = new HttpRoute(target);
 
         Mockito.when(connFactory.create(
@@ -372,7 +372,7 @@
 
     @Test
     public void testTargetConnect() throws Exception {
-        final HttpHost target = new HttpHost("somehost", -1, "https");
+        final HttpHost target = new HttpHost("somehost", 443, "https");
         final InetAddress remote = InetAddress.getByAddress(new byte[] {10, 0, 0, 1});
         final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
         final HttpRoute route = new HttpRoute(target, local, true);
@@ -415,7 +415,7 @@
 
     @Test
     public void testProxyConnectAndUpgrade() throws Exception {
-        final HttpHost target = new HttpHost("somehost", -1, "https");
+        final HttpHost target = new HttpHost("somehost", 443, "https");
         final HttpHost proxy = new HttpHost("someproxy", 8080);
         final InetAddress remote = InetAddress.getByAddress(new byte[] {10, 0, 0, 1});
         final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingHttpClientConnectionManager.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingHttpClientConnectionManager.java
index 00601a6..5b379d3 100644
--- a/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingHttpClientConnectionManager.java
+++ b/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingHttpClientConnectionManager.java
@@ -92,7 +92,7 @@
 
     @Test
     public void testLeaseRelease() throws Exception {
-        final HttpHost target = new HttpHost("localhost");
+        final HttpHost target = new HttpHost("localhost", 80);
         final HttpRoute route = new HttpRoute(target);
 
         final CPoolEntry entry = new CPoolEntry(LogFactory.getLog(getClass()), "id", route, conn,
@@ -121,7 +121,7 @@
 
     @Test
     public void testReleaseRouteIncomplete() throws Exception {
-        final HttpHost target = new HttpHost("localhost");
+        final HttpHost target = new HttpHost("localhost", 80);
         final HttpRoute route = new HttpRoute(target);
 
         final CPoolEntry entry = new CPoolEntry(LogFactory.getLog(getClass()), "id", route, conn,
@@ -149,7 +149,7 @@
 
     @Test(expected=InterruptedException.class)
     public void testLeaseFutureCancelled() throws Exception {
-        final HttpHost target = new HttpHost("localhost");
+        final HttpHost target = new HttpHost("localhost", 80);
         final HttpRoute route = new HttpRoute(target);
 
         final CPoolEntry entry = new CPoolEntry(LogFactory.getLog(getClass()), "id", route, conn,
@@ -166,7 +166,7 @@
 
     @Test(expected=ConnectionPoolTimeoutException.class)
     public void testLeaseFutureTimeout() throws Exception {
-        final HttpHost target = new HttpHost("localhost");
+        final HttpHost target = new HttpHost("localhost", 80);
         final HttpRoute route = new HttpRoute(target);
 
         Mockito.when(future.isCancelled()).thenReturn(Boolean.TRUE);
@@ -179,7 +179,7 @@
 
     @Test
     public void testReleaseReusable() throws Exception {
-        final HttpHost target = new HttpHost("localhost");
+        final HttpHost target = new HttpHost("localhost", 80);
         final HttpRoute route = new HttpRoute(target);
 
         final CPoolEntry entry = Mockito.spy(new CPoolEntry(LogFactory.getLog(getClass()), "id", route, conn,
@@ -205,7 +205,7 @@
 
     @Test
     public void testReleaseNonReusable() throws Exception {
-        final HttpHost target = new HttpHost("localhost");
+        final HttpHost target = new HttpHost("localhost", 80);
         final HttpRoute route = new HttpRoute(target);
 
         final CPoolEntry entry = Mockito.spy(new CPoolEntry(LogFactory.getLog(getClass()), "id", route, conn,
@@ -231,7 +231,7 @@
 
     @Test
     public void testTargetConnect() throws Exception {
-        final HttpHost target = new HttpHost("somehost", -1, "https");
+        final HttpHost target = new HttpHost("somehost", 443, "https");
         final InetAddress remote = InetAddress.getByAddress(new byte[] {10, 0, 0, 1});
         final InetAddress local = InetAddress.getByAddress(new byte[]{127, 0, 0, 1});
         final HttpRoute route = new HttpRoute(target, local, true);
@@ -280,7 +280,7 @@
 
     @Test
     public void testProxyConnectAndUpgrade() throws Exception {
-        final HttpHost target = new HttpHost("somehost", -1, "https");
+        final HttpHost target = new HttpHost("somehost", 443, "https");
         final HttpHost proxy = new HttpHost("someproxy", 8080);
         final InetAddress remote = InetAddress.getByAddress(new byte[] {10, 0, 0, 1});
         final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
diff --git a/httpclient/src/test/java/org/apache/http/impl/cookie/TestAbstractCookieSpec.java b/httpclient/src/test/java/org/apache/http/impl/cookie/TestAbstractCookieSpec.java
deleted file mode 100644
index 0cfa76f..0000000
--- a/httpclient/src/test/java/org/apache/http/impl/cookie/TestAbstractCookieSpec.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on 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.Iterator;
-import java.util.List;
-
-import org.apache.http.Header;
-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.junit.Assert;
-import org.junit.Test;
-
-public class TestAbstractCookieSpec {
-
-    private static class DummyCookieSpec extends AbstractCookieSpec {
-
-        @Override
-        public List<Header> formatCookies(final List<Cookie> cookies) {
-            return null;
-        }
-
-        @Override
-        public boolean match(final Cookie cookie, final CookieOrigin origin) {
-            return true;
-        }
-
-        @Override
-        public List<Cookie> parse(final Header header, final CookieOrigin origin) throws MalformedCookieException {
-            return null;
-        }
-
-        @Override
-        public void validate(final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException {
-        }
-
-        @Override
-        public int getVersion() {
-            return 0;
-        }
-
-        @Override
-        public Header getVersionHeader() {
-            return null;
-        }
-
-    }
-
-    private static class DummyCookieAttribHandler implements CookieAttributeHandler {
-
-        @Override
-        public boolean match(final Cookie cookie, final CookieOrigin origin) {
-            return true;
-        }
-
-        @Override
-        public void parse(final SetCookie cookie, final String value) throws MalformedCookieException {
-        }
-
-        @Override
-        public void validate(final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException {
-        }
-
-    }
-
-    @Test
-    public void testSimpleRegisterAndGet() {
-        final CookieAttributeHandler h1 = new DummyCookieAttribHandler();
-        final CookieAttributeHandler h2 = new DummyCookieAttribHandler();
-
-        final AbstractCookieSpec cookiespec = new DummyCookieSpec();
-        cookiespec.registerAttribHandler("this", h1);
-        cookiespec.registerAttribHandler("that", h2);
-        cookiespec.registerAttribHandler("thistoo", h1);
-        cookiespec.registerAttribHandler("thattoo", h2);
-
-        Assert.assertTrue(h1 == cookiespec.getAttribHandler("this"));
-        Assert.assertTrue(h2 == cookiespec.getAttribHandler("that"));
-        Assert.assertTrue(h1 == cookiespec.getAttribHandler("thistoo"));
-        Assert.assertTrue(h2 == cookiespec.getAttribHandler("thattoo"));
-
-        final Iterator<CookieAttributeHandler> it = cookiespec.getAttribHandlers().iterator();
-        Assert.assertNotNull(it.next());
-        Assert.assertNotNull(it.next());
-        Assert.assertNotNull(it.next());
-        Assert.assertNotNull(it.next());
-        Assert.assertFalse(it.hasNext());
-    }
-
-    @Test(expected=IllegalStateException.class)
-    public void testInvalidHandler() {
-        final CookieAttributeHandler h1 = new DummyCookieAttribHandler();
-        final CookieAttributeHandler h2 = new DummyCookieAttribHandler();
-
-        final AbstractCookieSpec cookiespec = new DummyCookieSpec();
-        cookiespec.registerAttribHandler("this", h1);
-        cookiespec.registerAttribHandler("that", h2);
-
-        Assert.assertNull(cookiespec.findAttribHandler("whatever"));
-        cookiespec.getAttribHandler("whatever");
-    }
-
-    @Test(expected=IllegalArgumentException.class)
-    public void testBasicPathInvalidInput1() throws Exception {
-        final AbstractCookieSpec cookiespec = new DummyCookieSpec();
-        cookiespec.registerAttribHandler(null, null);
-    }
-
-    @Test(expected=IllegalArgumentException.class)
-    public void testBasicPathInvalidInput2() throws Exception {
-        final AbstractCookieSpec cookiespec = new DummyCookieSpec();
-        cookiespec.registerAttribHandler("whatever", null);
-    }
-
-}
diff --git a/httpclient/src/test/java/org/apache/http/impl/cookie/TestBasicCookieAttribHandlers.java b/httpclient/src/test/java/org/apache/http/impl/cookie/TestBasicCookieAttribHandlers.java
index ce6bd15..7b236fb 100644
--- a/httpclient/src/test/java/org/apache/http/impl/cookie/TestBasicCookieAttribHandlers.java
+++ b/httpclient/src/test/java/org/apache/http/impl/cookie/TestBasicCookieAttribHandlers.java
@@ -35,6 +35,7 @@
 
 import org.apache.http.client.utils.DateUtils;
 import org.apache.http.conn.util.PublicSuffixMatcher;
+import org.apache.http.cookie.ClientCookie;
 import org.apache.http.cookie.CookieAttributeHandler;
 import org.apache.http.cookie.CookieOrigin;
 import org.apache.http.cookie.MalformedCookieException;
@@ -140,6 +141,7 @@
         final CookieAttributeHandler h = new BasicDomainHandler();
 
         cookie.setDomain("somedomain.com");
+        cookie.setAttribute(ClientCookie.DOMAIN_ATTR, "somedomain.com");
         Assert.assertTrue(h.match(cookie, origin));
 
         cookie.setDomain(".somedomain.com");
@@ -153,6 +155,7 @@
         final CookieAttributeHandler h = new BasicDomainHandler();
 
         cookie.setDomain("somedomain.com");
+        cookie.setAttribute(ClientCookie.DOMAIN_ATTR, "somedomain.com");
         Assert.assertTrue(h.match(cookie, origin));
 
         cookie.setDomain(".somedomain.com");
@@ -163,6 +166,28 @@
     }
 
     @Test
+    public void testBasicDomainMatchOneLetterPrefix() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieOrigin origin = new CookieOrigin("a.somedomain.com", 80, "/", false);
+        final CookieAttributeHandler h = new BasicDomainHandler();
+
+        cookie.setDomain("somedomain.com");
+        cookie.setAttribute(ClientCookie.DOMAIN_ATTR, "somedomain.com");
+        Assert.assertTrue(h.match(cookie, origin));
+    }
+
+    @Test
+    public void testBasicDomainMatchMixedCase() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieOrigin origin = new CookieOrigin("a.SomeDomain.com", 80, "/", false);
+        final CookieAttributeHandler h = new BasicDomainHandler();
+
+        cookie.setDomain("somedoMain.Com");
+        cookie.setAttribute(ClientCookie.DOMAIN_ATTR, "somedoMain.Com");
+        Assert.assertTrue(h.match(cookie, origin));
+    }
+
+    @Test
     public void testBasicDomainInvalidInput() throws Exception {
         final CookieAttributeHandler h = new BasicDomainHandler();
         try {
diff --git a/httpclient/src/test/java/org/apache/http/impl/cookie/TestCookieRFC2109Spec.java b/httpclient/src/test/java/org/apache/http/impl/cookie/TestCookieRFC2109Spec.java
index bc1596f..e4eff79 100644
--- a/httpclient/src/test/java/org/apache/http/impl/cookie/TestCookieRFC2109Spec.java
+++ b/httpclient/src/test/java/org/apache/http/impl/cookie/TestCookieRFC2109Spec.java
@@ -399,8 +399,8 @@
         Assert.assertEquals(1, headers.size());
         Assert.assertEquals("$Version=0; name=; $Path=/", headers.get(0).getValue());
 
-        cookie.setAttribute(ClientCookie.DOMAIN_ATTR, null);
-        cookie.setAttribute(ClientCookie.PATH_ATTR, null);
+        cookie.removeAttribute(ClientCookie.DOMAIN_ATTR);
+        cookie.removeAttribute(ClientCookie.PATH_ATTR);
         cookies = new ArrayList<Cookie>();
         cookies.add(cookie);
         headers = cookiespec.formatCookies(cookies);
diff --git a/httpclient/src/test/java/org/apache/http/impl/cookie/TestLaxCookieAttribHandlers.java b/httpclient/src/test/java/org/apache/http/impl/cookie/TestLaxCookieAttribHandlers.java
new file mode 100644
index 0000000..d47d0ae
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/cookie/TestLaxCookieAttribHandlers.java
@@ -0,0 +1,317 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on 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.Calendar;
+import java.util.Date;
+
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.MalformedCookieException;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestLaxCookieAttribHandlers {
+
+    @Test
+    public void testParseMaxAge() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxMaxAgeHandler();
+        h.parse(cookie, "2000");
+        Assert.assertNotNull(cookie.getExpiryDate());
+    }
+
+    @Test
+    public void testParseMaxNegative() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxMaxAgeHandler();
+        h.parse(cookie, "-2000");
+        Assert.assertNotNull(cookie.getExpiryDate());
+    }
+
+    @Test
+    public void testParseMaxZero() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxMaxAgeHandler();
+        h.parse(cookie, "0000");
+        Assert.assertNotNull(cookie.getExpiryDate());
+    }
+
+    @Test
+    public void testBasicMaxAgeParseEmpty() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxMaxAgeHandler();
+        h.parse(cookie, "  ");
+        Assert.assertNull(cookie.getExpiryDate());
+    }
+
+    @Test
+    public void testBasicMaxAgeParseInvalid() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxMaxAgeHandler();
+        h.parse(cookie, "garbage");
+        Assert.assertNull(cookie.getExpiryDate());
+    }
+
+    @Test
+    public void testBasicMaxAgeInvalidInput() throws Exception {
+        final CookieAttributeHandler h = new LaxMaxAgeHandler();
+        try {
+            h.parse(null, "stuff");
+            Assert.fail("IllegalArgumentException must have been thrown");
+        } catch (final IllegalArgumentException ex) {
+            // expected
+        }
+    }
+
+    @Test(expected = MalformedCookieException.class)
+    public void testExpiryGarbage() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, ";;blah,blah;yada  ");
+    }
+
+    @Test
+    public void testParseExpiry() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "1:0:12 8-jan-2012");
+
+        final Date expiryDate = cookie.getExpiryDate();
+        Assert.assertNotNull(expiryDate);
+        final Calendar c = Calendar.getInstance();
+        c.setTimeZone(LaxExpiresHandler.UTC);
+        c.setTime(expiryDate);
+        Assert.assertEquals(2012, c.get(Calendar.YEAR));
+        Assert.assertEquals(Calendar.JANUARY, c.get(Calendar.MONTH));
+        Assert.assertEquals(8, c.get(Calendar.DAY_OF_MONTH));
+        Assert.assertEquals(1, c.get(Calendar.HOUR_OF_DAY));
+        Assert.assertEquals(0, c.get(Calendar.MINUTE));
+        Assert.assertEquals(12, c.get(Calendar.SECOND));
+        Assert.assertEquals(0, c.get(Calendar.MILLISECOND));
+    }
+
+    @Test(expected = MalformedCookieException.class)
+    public void testParseExpiryInvalidTime1() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "1:0:122 8 dec 1980");
+    }
+
+    @Test(expected = MalformedCookieException.class)
+    public void testParseExpiryInvalidTime2() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "24:00:00 8 dec 1980");
+    }
+
+    @Test(expected = MalformedCookieException.class)
+    public void testParseExpiryInvalidTime3() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "23:60:00 8 dec 1980");
+    }
+
+    @Test(expected = MalformedCookieException.class)
+    public void testParseExpiryInvalidTime4() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "23:00:60 8 dec 1980");
+    }
+
+    @Test
+    public void testParseExpiryFunnyTime() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "1:59:00blah; 8-feb-2000");
+
+        final Date expiryDate = cookie.getExpiryDate();
+        Assert.assertNotNull(expiryDate);
+        final Calendar c = Calendar.getInstance();
+        c.setTimeZone(LaxExpiresHandler.UTC);
+        c.setTime(expiryDate);
+        Assert.assertEquals(2000, c.get(Calendar.YEAR));
+        Assert.assertEquals(Calendar.FEBRUARY, c.get(Calendar.MONTH));
+        Assert.assertEquals(8, c.get(Calendar.DAY_OF_MONTH));
+        Assert.assertEquals(1, c.get(Calendar.HOUR_OF_DAY));
+        Assert.assertEquals(59, c.get(Calendar.MINUTE));
+        Assert.assertEquals(0, c.get(Calendar.SECOND));
+        Assert.assertEquals(0, c.get(Calendar.MILLISECOND));
+    }
+
+    @Test(expected = MalformedCookieException.class)
+    public void testParseExpiryInvalidDayOfMonth1() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "12:00:00 888 mar 1880");
+    }
+
+    @Test(expected = MalformedCookieException.class)
+    public void testParseExpiryInvalidDayOfMonth2() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "12:00:00 0 mar 1880");
+    }
+
+    @Test(expected = MalformedCookieException.class)
+    public void testParseExpiryInvalidDayOfMonth3() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "12:00:00 32 mar 1880");
+    }
+
+    @Test
+    public void testParseExpiryFunnyDayOfMonth() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "12:00:00 8blah;mar;1880");
+
+        final Date expiryDate = cookie.getExpiryDate();
+        Assert.assertNotNull(expiryDate);
+        final Calendar c = Calendar.getInstance();
+        c.setTimeZone(LaxExpiresHandler.UTC);
+        c.setTime(expiryDate);
+        Assert.assertEquals(1880, c.get(Calendar.YEAR));
+        Assert.assertEquals(Calendar.MARCH, c.get(Calendar.MONTH));
+        Assert.assertEquals(8, c.get(Calendar.DAY_OF_MONTH));
+        Assert.assertEquals(12, c.get(Calendar.HOUR_OF_DAY));
+        Assert.assertEquals(0, c.get(Calendar.MINUTE));
+        Assert.assertEquals(0, c.get(Calendar.SECOND));
+        Assert.assertEquals(0, c.get(Calendar.MILLISECOND));
+    }
+
+    @Test(expected = MalformedCookieException.class)
+    public void testParseExpiryInvalidMonth() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "1:00:00 8 dek 80");
+    }
+
+    @Test
+    public void testParseExpiryFunnyMonth() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "23:59:59; 1-ApriLLLLL-2008");
+
+        final Date expiryDate = cookie.getExpiryDate();
+        Assert.assertNotNull(expiryDate);
+        final Calendar c = Calendar.getInstance();
+        c.setTimeZone(LaxExpiresHandler.UTC);
+        c.setTime(expiryDate);
+        Assert.assertEquals(2008, c.get(Calendar.YEAR));
+        Assert.assertEquals(Calendar.APRIL, c.get(Calendar.MONTH));
+        Assert.assertEquals(1, c.get(Calendar.DAY_OF_MONTH));
+        Assert.assertEquals(23, c.get(Calendar.HOUR_OF_DAY));
+        Assert.assertEquals(59, c.get(Calendar.MINUTE));
+        Assert.assertEquals(59, c.get(Calendar.SECOND));
+        Assert.assertEquals(0, c.get(Calendar.MILLISECOND));
+    }
+
+    @Test(expected = MalformedCookieException.class)
+    public void testParseExpiryInvalidYearTooShort() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "1:00:00 8 dec 8");
+    }
+
+    @Test(expected = MalformedCookieException.class)
+    public void testParseExpiryInvalidYearTooLong() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "1:00:00 8 dec 88888");
+    }
+
+    @Test(expected = MalformedCookieException.class)
+    public void testParseExpiryInvalidYearTooLongAgo() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "1:00:00 8 dec 1600");
+    }
+
+    @Test
+    public void testParseExpiryFunnyYear() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "23:59:59; 1-Apr-2008blah");
+
+        final Date expiryDate = cookie.getExpiryDate();
+        Assert.assertNotNull(expiryDate);
+        final Calendar c = Calendar.getInstance();
+        c.setTimeZone(LaxExpiresHandler.UTC);
+        c.setTime(expiryDate);
+        Assert.assertEquals(2008, c.get(Calendar.YEAR));
+        Assert.assertEquals(Calendar.APRIL, c.get(Calendar.MONTH));
+        Assert.assertEquals(1, c.get(Calendar.DAY_OF_MONTH));
+        Assert.assertEquals(23, c.get(Calendar.HOUR_OF_DAY));
+        Assert.assertEquals(59, c.get(Calendar.MINUTE));
+        Assert.assertEquals(59, c.get(Calendar.SECOND));
+        Assert.assertEquals(0, c.get(Calendar.MILLISECOND));
+    }
+
+    @Test
+    public void testParseExpiryYearTwoDigit1() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "23:59:59; 1-Apr-70");
+
+        final Date expiryDate = cookie.getExpiryDate();
+        Assert.assertNotNull(expiryDate);
+        final Calendar c = Calendar.getInstance();
+        c.setTimeZone(LaxExpiresHandler.UTC);
+        c.setTime(expiryDate);
+        Assert.assertEquals(1970, c.get(Calendar.YEAR));
+    }
+
+    @Test
+    public void testParseExpiryYearTwoDigit2() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "23:59:59; 1-Apr-99");
+
+        final Date expiryDate = cookie.getExpiryDate();
+        Assert.assertNotNull(expiryDate);
+        final Calendar c = Calendar.getInstance();
+        c.setTimeZone(LaxExpiresHandler.UTC);
+        c.setTime(expiryDate);
+        Assert.assertEquals(1999, c.get(Calendar.YEAR));
+    }
+
+    @Test
+    public void testParseExpiryYearTwoDigit3() throws Exception {
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        final CookieAttributeHandler h = new LaxExpiresHandler();
+        h.parse(cookie, "23:59:59; 1-Apr-00");
+
+        final Date expiryDate = cookie.getExpiryDate();
+        Assert.assertNotNull(expiryDate);
+        final Calendar c = Calendar.getInstance();
+        c.setTimeZone(LaxExpiresHandler.UTC);
+        c.setTime(expiryDate);
+        Assert.assertEquals(2000, c.get(Calendar.YEAR));
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/cookie/TestRFC6265CookieSpecBase.java b/httpclient/src/test/java/org/apache/http/impl/cookie/TestRFC6265CookieSpecBase.java
new file mode 100644
index 0000000..b8f595d
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/cookie/TestRFC6265CookieSpecBase.java
@@ -0,0 +1,337 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on 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.Arrays;
+import java.util.List;
+
+import org.apache.http.Header;
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.CommonCookieAttributeHandler;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+import org.apache.http.message.BasicHeader;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class TestRFC6265CookieSpecBase {
+
+    @Test
+    public void testParseCookieBasics() throws Exception {
+        final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
+        Mockito.when(h1.getAttributeName()).thenReturn("this");
+        final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class);
+        Mockito.when(h2.getAttributeName()).thenReturn("that");
+
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase(h1, h2);
+
+        final Header header = new BasicHeader("Set-Cookie", "name = value ; this = stuff;");
+        final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
+        final List<Cookie> cookies = cookiespec.parse(header, origin);
+
+        Assert.assertEquals(1, cookies.size());
+        final Cookie cookie = cookies.get(0);
+        Assert.assertEquals("name", cookie.getName());
+        Assert.assertEquals("value", cookie.getValue());
+        Assert.assertEquals("/path", cookie.getPath());
+        Assert.assertEquals("host", cookie.getDomain());
+        Assert.assertTrue(cookie instanceof ClientCookie);
+        final ClientCookie clientCookie = (ClientCookie) cookie;
+        Assert.assertEquals("stuff", clientCookie.getAttribute("this"));
+        Assert.assertEquals(null, clientCookie.getAttribute("that"));
+
+        Mockito.verify(h1).parse(Mockito.<SetCookie>any(), Mockito.eq("stuff"));
+        Mockito.verify(h2, Mockito.never()).parse(Mockito.<SetCookie>any(), Mockito.anyString());
+    }
+
+    @Test
+    public void testParseCookieQuotedValue() throws Exception {
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase();
+
+        final Header header = new BasicHeader("Set-Cookie", "name = \" one, two, three; four \" ; this = stuff;");
+        final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
+        final List<Cookie> cookies = cookiespec.parse(header, origin);
+
+        Assert.assertEquals(1, cookies.size());
+        final Cookie cookie = cookies.get(0);
+        Assert.assertEquals("name", cookie.getName());
+        Assert.assertEquals(" one, two, three; four ", cookie.getValue());
+        Assert.assertTrue(cookie instanceof ClientCookie);
+        final ClientCookie clientCookie = (ClientCookie) cookie;
+        Assert.assertEquals("stuff", clientCookie.getAttribute("this"));
+    }
+
+    @Test(expected = MalformedCookieException.class)
+    public void testParseCookieWrongHeader() throws Exception {
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase();
+
+        final Header header = new BasicHeader("Set-Cookie2", "blah");
+        final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
+        cookiespec.parse(header, origin);
+    }
+
+    @Test(expected = MalformedCookieException.class)
+    public void testParseCookieMissingName() throws Exception {
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase();
+
+        final Header header = new BasicHeader("Set-Cookie", "=blah ; this = stuff;");
+        final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
+        cookiespec.parse(header, origin);
+    }
+
+    @Test(expected = MalformedCookieException.class)
+    public void testParseCookieMissingValue1() throws Exception {
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase();
+
+        final Header header = new BasicHeader("Set-Cookie", "blah");
+        final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
+        cookiespec.parse(header, origin);
+    }
+
+    @Test(expected = MalformedCookieException.class)
+    public void testParseCookieMissingValue2() throws Exception {
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase();
+
+        final Header header = new BasicHeader("Set-Cookie", "blah;");
+        final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
+        cookiespec.parse(header, origin);
+    }
+
+    @Test
+    public void testParseCookieEmptyValue() throws Exception {
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase();
+
+        final Header header = new BasicHeader("Set-Cookie", "blah=;");
+        final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
+        final List<Cookie> cookies = cookiespec.parse(header, origin);
+        Assert.assertEquals(1, cookies.size());
+        final Cookie cookie = cookies.get(0);
+        Assert.assertEquals("blah", cookie.getName());
+        Assert.assertEquals("", cookie.getValue());
+    }
+
+    @Test
+    public void testParseCookieWithAttributes() throws Exception {
+        final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
+        Mockito.when(h1.getAttributeName()).thenReturn("this");
+        final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class);
+        Mockito.when(h2.getAttributeName()).thenReturn("that");
+
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase(h1, h2);
+
+        final Header header = new BasicHeader("Set-Cookie", "name = value ; p1 = v ; p2 = v,0; p3 ; p4");
+        final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
+        final List<Cookie> cookies = cookiespec.parse(header, origin);
+
+        Assert.assertEquals(1, cookies.size());
+        final Cookie cookie = cookies.get(0);
+        Assert.assertEquals("name", cookie.getName());
+        Assert.assertEquals("value", cookie.getValue());
+        Assert.assertTrue(cookie instanceof ClientCookie);
+        final ClientCookie clientCookie = (ClientCookie) cookie;
+        Assert.assertEquals("v", clientCookie.getAttribute("p1"));
+        Assert.assertEquals("v,0", clientCookie.getAttribute("p2"));
+        Assert.assertTrue(clientCookie.containsAttribute("p3"));
+        Assert.assertTrue(clientCookie.containsAttribute("p4"));
+        Assert.assertFalse(clientCookie.containsAttribute("p5"));
+    }
+
+    @Test
+    public void testParseCookieWithAttributes2() throws Exception {
+        final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
+        Mockito.when(h1.getAttributeName()).thenReturn("this");
+        final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class);
+        Mockito.when(h2.getAttributeName()).thenReturn("that");
+
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase(h1, h2);
+
+        final Header header = new BasicHeader("Set-Cookie", "name = value ; p1 = v");
+        final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
+        final List<Cookie> cookies = cookiespec.parse(header, origin);
+
+        Assert.assertEquals(1, cookies.size());
+        final Cookie cookie = cookies.get(0);
+        Assert.assertEquals("name", cookie.getName());
+        Assert.assertEquals("value", cookie.getValue());
+        Assert.assertTrue(cookie instanceof ClientCookie);
+        final ClientCookie clientCookie = (ClientCookie) cookie;
+        Assert.assertEquals("v", clientCookie.getAttribute("p1"));
+    }
+
+    @Test
+    public void testParseCookieWithAttributes3() throws Exception {
+        final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
+        Mockito.when(h1.getAttributeName()).thenReturn("this");
+        final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class);
+        Mockito.when(h2.getAttributeName()).thenReturn("that");
+
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase(h1, h2);
+
+        final Header header = new BasicHeader("Set-Cookie", "name = value ; p1 =");
+        final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
+        final List<Cookie> cookies = cookiespec.parse(header, origin);
+
+        Assert.assertEquals(1, cookies.size());
+        final Cookie cookie = cookies.get(0);
+        Assert.assertEquals("name", cookie.getName());
+        Assert.assertEquals("value", cookie.getValue());
+        Assert.assertTrue(cookie instanceof ClientCookie);
+        final ClientCookie clientCookie = (ClientCookie) cookie;
+        Assert.assertEquals("", clientCookie.getAttribute("p1"));
+    }
+
+    @Test
+    public void testValidateCookieBasics() throws Exception {
+        final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
+        Mockito.when(h1.getAttributeName()).thenReturn("this");
+        final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class);
+        Mockito.when(h2.getAttributeName()).thenReturn("that");
+
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase(h1, h2);
+
+        final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        cookiespec.validate(cookie, origin);
+
+        Mockito.verify(h1).validate(cookie, origin);
+        Mockito.verify(h2).validate(cookie, origin);
+    }
+
+    @Test
+    public void testMatchCookie() throws Exception {
+        final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
+        Mockito.when(h1.getAttributeName()).thenReturn("this");
+        final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class);
+        Mockito.when(h2.getAttributeName()).thenReturn("that");
+
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase(h1, h2);
+
+        final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+
+        Mockito.when(h1.match(cookie, origin)).thenReturn(true);
+        Mockito.when(h2.match(cookie, origin)).thenReturn(true);
+
+        Assert.assertTrue(cookiespec.match(cookie, origin));
+
+        Mockito.verify(h1).match(cookie, origin);
+        Mockito.verify(h2).match(cookie, origin);
+    }
+
+    @Test
+    public void testMatchCookieNoMatch() throws Exception {
+        final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
+        Mockito.when(h1.getAttributeName()).thenReturn("this");
+        final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class);
+        Mockito.when(h2.getAttributeName()).thenReturn("that");
+
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase(h1, h2);
+
+        final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+
+        Mockito.when(h1.match(cookie, origin)).thenReturn(false);
+        Mockito.when(h2.match(cookie, origin)).thenReturn(false);
+
+        Assert.assertFalse(cookiespec.match(cookie, origin));
+
+        Mockito.verify(h1).match(cookie, origin);
+        Mockito.verify(h2, Mockito.never()).match(cookie, origin);
+    }
+
+    @Test
+    public void testLegacy() throws Exception {
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase();
+
+        Assert.assertEquals(0, cookiespec.getVersion());
+        Assert.assertEquals(null, cookiespec.getVersionHeader());
+    }
+
+    @Test
+    public void testFormatCookiesBasics() throws Exception {
+        final Cookie cookie1 = new BasicClientCookie("name1", "value");
+
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase();
+        final List<Header> headers = cookiespec.formatCookies(Arrays.asList(cookie1));
+        Assert.assertNotNull(headers);
+        Assert.assertEquals(1, headers.size());
+        final Header header = headers.get(0);
+        Assert.assertEquals("Cookie", header.getName());
+        Assert.assertEquals("name1=value", header.getValue());
+    }
+
+    @Test
+    public void testFormatCookiesIllegalCharsInValue() throws Exception {
+        final Cookie cookie1 = new BasicClientCookie("name1", "value");
+        final Cookie cookie2 = new BasicClientCookie("name2", "some value");
+        final Cookie cookie3 = new BasicClientCookie("name3", "\"\\\"");
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase();
+        final List<Header> headers = cookiespec.formatCookies(Arrays.asList(cookie1, cookie2, cookie3));
+        Assert.assertNotNull(headers);
+        Assert.assertEquals(1, headers.size());
+        final Header header = headers.get(0);
+        Assert.assertEquals("Cookie", header.getName());
+        Assert.assertEquals("name1=value; name2=\"some value\"; name3=\"\\\"\\\\\\\"\"", header.getValue());
+    }
+
+    @Test
+    public void testParseCookieMultipleAttributes() throws Exception {
+        final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
+        Mockito.when(h1.getAttributeName()).thenReturn("this");
+
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase(h1);
+
+        final Header header = new BasicHeader("Set-Cookie", "name = value ; this = stuff; this = morestuff;");
+        final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
+        cookiespec.parse(header, origin);
+
+        Mockito.verify(h1).parse(Mockito.<SetCookie>any(), Mockito.eq("morestuff"));
+        Mockito.verify(h1, Mockito.times(1)).parse(Mockito.<SetCookie>any(), Mockito.anyString());
+    }
+
+    @Test
+    public void testParseCookieMaxAgeOverExpires() throws Exception {
+        final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class);
+        Mockito.when(h1.getAttributeName()).thenReturn("Expires");
+        final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class);
+        Mockito.when(h2.getAttributeName()).thenReturn("Max-Age");
+
+        final RFC6265CookieSpecBase cookiespec = new RFC6265CookieSpecBase(h1, h2);
+
+        final Header header = new BasicHeader("Set-Cookie", "name = value ; expires = stuff; max-age = otherstuff;");
+        final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true);
+        cookiespec.parse(header, origin);
+
+        Mockito.verify(h1, Mockito.never()).parse(Mockito.<SetCookie>any(), Mockito.anyString());
+        Mockito.verify(h2).parse(Mockito.<SetCookie>any(), Mockito.eq("otherstuff"));
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/execchain/TestRedirectExec.java b/httpclient/src/test/java/org/apache/http/impl/execchain/TestRedirectExec.java
index fef2486..ed1546c 100644
--- a/httpclient/src/test/java/org/apache/http/impl/execchain/TestRedirectExec.java
+++ b/httpclient/src/test/java/org/apache/http/impl/execchain/TestRedirectExec.java
@@ -271,7 +271,7 @@
         Mockito.when(httpRoutePlanner.determineRoute(
                 Mockito.eq(target),
                 Mockito.<HttpRequestWrapper>any(),
-                Mockito.<HttpClientContext>any())).thenReturn(new HttpRoute(new HttpHost("otherhost")));
+                Mockito.<HttpClientContext>any())).thenReturn(new HttpRoute(new HttpHost("otherhost", 80)));
 
         redirectExec.execute(route, request, context, execAware);
 
diff --git a/httpclient/src/test/java/org/apache/http/localserver/SSLTestContexts.java b/httpclient/src/test/java/org/apache/http/localserver/SSLTestContexts.java
index c3332ea..2bfb5af 100644
--- a/httpclient/src/test/java/org/apache/http/localserver/SSLTestContexts.java
+++ b/httpclient/src/test/java/org/apache/http/localserver/SSLTestContexts.java
@@ -27,60 +27,26 @@
 
 package org.apache.http.localserver;
 
-import java.net.URL;
-import java.security.KeyStore;
-import java.security.NoSuchAlgorithmException;
-
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
+
+import org.apache.http.ssl.SSLContexts;
 
 public class SSLTestContexts {
 
-    private static KeyManagerFactory createKeyManagerFactory() throws NoSuchAlgorithmException {
-        final String algo = KeyManagerFactory.getDefaultAlgorithm();
-        try {
-            return KeyManagerFactory.getInstance(algo);
-        } catch (final NoSuchAlgorithmException ex) {
-            return KeyManagerFactory.getInstance("SunX509");
-        }
-    }
-
     public static SSLContext createServerSSLContext() throws Exception {
-        final ClassLoader cl = SSLTestContexts.class.getClassLoader();
-        final URL url = cl.getResource("test.keystore");
-        final KeyStore keystore  = KeyStore.getInstance("jks");
-        keystore.load(url.openStream(), "nopassword".toCharArray());
-        final KeyManagerFactory kmfactory = createKeyManagerFactory();
-        kmfactory.init(keystore, "nopassword".toCharArray());
-        final KeyManager[] keymanagers = kmfactory.getKeyManagers();
-        final SSLContext sslcontext = SSLContext.getInstance("TLS");
-        sslcontext.init(keymanagers, null, null);
-        return sslcontext;
-    }
-
-    private static TrustManagerFactory createTrustManagerFactory() throws NoSuchAlgorithmException {
-        final String algo = TrustManagerFactory.getDefaultAlgorithm();
-        try {
-            return TrustManagerFactory.getInstance(algo);
-        } catch (final NoSuchAlgorithmException ex) {
-            return TrustManagerFactory.getInstance("SunX509");
-        }
+        return SSLContexts.custom()
+                .loadTrustMaterial(SSLTestContexts.class.getResource("/test.keystore"),
+                        "nopassword".toCharArray())
+                .loadKeyMaterial(SSLTestContexts.class.getResource("/test.keystore"),
+                        "nopassword".toCharArray(), "nopassword".toCharArray())
+                .build();
     }
 
     public static SSLContext createClientSSLContext() throws Exception {
-        final ClassLoader cl = SSLTestContexts.class.getClassLoader();
-        final URL url = cl.getResource("test.keystore");
-        final KeyStore keystore  = KeyStore.getInstance("jks");
-        keystore.load(url.openStream(), "nopassword".toCharArray());
-        final TrustManagerFactory tmfactory = createTrustManagerFactory();
-        tmfactory.init(keystore);
-        final TrustManager[] trustmanagers = tmfactory.getTrustManagers();
-        final SSLContext sslcontext = SSLContext.getInstance("TLS");
-        sslcontext.init(null, trustmanagers, null);
-        return sslcontext;
+        return SSLContexts.custom()
+                .loadTrustMaterial(SSLTestContexts.class.getResource("/test.keystore"),
+                        "nopassword".toCharArray())
+                .build();
     }
 
 }
diff --git a/httpclient/src/test/resources/hc-test-1.truststore b/httpclient/src/test/resources/hc-test-1.truststore
deleted file mode 100644
index d8610f6..0000000
--- a/httpclient/src/test/resources/hc-test-1.truststore
+++ /dev/null
Binary files differ
diff --git a/httpclient/src/test/resources/hc-test-2.truststore b/httpclient/src/test/resources/hc-test-2.truststore
deleted file mode 100644
index 20c1964..0000000
--- a/httpclient/src/test/resources/hc-test-2.truststore
+++ /dev/null
Binary files differ
diff --git a/httpclient/src/test/resources/test-keypasswd.keystore b/httpclient/src/test/resources/test-keypasswd.keystore
deleted file mode 100644
index 01dd1bb..0000000
--- a/httpclient/src/test/resources/test-keypasswd.keystore
+++ /dev/null
Binary files differ
diff --git a/httpmime/pom.xml b/httpmime/pom.xml
index ddb0cac..3835ba9 100644
--- a/httpmime/pom.xml
+++ b/httpmime/pom.xml
@@ -97,7 +97,7 @@
 
       <plugin>
         <artifactId>maven-javadoc-plugin</artifactId>
-         <version>${hc.javadoc.version}</version>
+        <version>${hc.javadoc.version}</version>
         <configuration>
           <!-- reduce console output. Can override with -Dquiet=false -->
           <quiet>true</quiet>
@@ -133,12 +133,12 @@
 
       <plugin>
         <artifactId>maven-jxr-plugin</artifactId>
-         <version>${hc.jxr.version}</version>
+        <version>${hc.jxr.version}</version>
       </plugin>
 
       <plugin>
         <artifactId>maven-surefire-report-plugin</artifactId>
-         <version>${hc.surefire-report.version}</version>
+        <version>${hc.surefire-report.version}</version>
       </plugin>
 
     </plugins>
diff --git a/httpmime/src/main/java-deprecated/org/apache/http/entity/mime/HttpMultipart.java b/httpmime/src/main/java-deprecated/org/apache/http/entity/mime/HttpMultipart.java
index 0f7be7e..d756f03 100644
--- a/httpmime/src/main/java-deprecated/org/apache/http/entity/mime/HttpMultipart.java
+++ b/httpmime/src/main/java-deprecated/org/apache/http/entity/mime/HttpMultipart.java
@@ -48,6 +48,8 @@
     private final HttpMultipartMode mode;
     private final List<FormBodyPart> parts;
 
+    private final String subType;
+
     /**
      * Creates an instance with the specified settings.
      *
@@ -61,7 +63,8 @@
     public HttpMultipart(
             final String subType, final Charset charset, final String boundary,
             final HttpMultipartMode mode) {
-        super(subType, charset, boundary);
+        super(charset, boundary);
+        this.subType = subType;
         this.mode = mode;
         this.parts = new ArrayList<FormBodyPart>();
     }
@@ -123,4 +126,16 @@
         this.parts.add(part);
     }
 
+    public String getSubType() {
+        return this.subType;
+    }
+
+    public Charset getCharset() {
+        return this.charset;
+    }
+
+    public String getBoundary() {
+        return this.boundary;
+    }
+
 }
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/AbstractMultipartForm.java b/httpmime/src/main/java/org/apache/http/entity/mime/AbstractMultipartForm.java
index 90bd2b4..32249f6 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/AbstractMultipartForm.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/AbstractMultipartForm.java
@@ -93,50 +93,34 @@
     private static final ByteArrayBuffer CR_LF = encode(MIME.DEFAULT_CHARSET, "\r\n");
     private static final ByteArrayBuffer TWO_DASHES = encode(MIME.DEFAULT_CHARSET, "--");
 
-    private final String subType;
-    protected final Charset charset;
-    private final String boundary;
+    final Charset charset;
+    final String boundary;
 
     /**
      * Creates an instance with the specified settings.
      *
-     * @param subType MIME subtype - must not be {@code null}
      * @param charset the character set to use. May be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
      * @param boundary to use  - must not be {@code null}
      * @throws IllegalArgumentException if charset is null or boundary is null
      */
-    public AbstractMultipartForm(final String subType, final Charset charset, final String boundary) {
+    public AbstractMultipartForm(final Charset charset, final String boundary) {
         super();
-        Args.notNull(subType, "Multipart subtype");
         Args.notNull(boundary, "Multipart boundary");
-        this.subType = subType;
         this.charset = charset != null ? charset : MIME.DEFAULT_CHARSET;
         this.boundary = boundary;
     }
 
-    public AbstractMultipartForm(final String subType, final String boundary) {
-        this(subType, null, boundary);
-    }
-
-    public String getSubType() {
-        return this.subType;
-    }
-
-    public Charset getCharset() {
-        return this.charset;
+    public AbstractMultipartForm(final String boundary) {
+        this(null, boundary);
     }
 
     public abstract List<FormBodyPart> getBodyParts();
 
-    public String getBoundary() {
-        return this.boundary;
-    }
-
     void doWriteTo(
         final OutputStream out,
         final boolean writeContent) throws IOException {
 
-        final ByteArrayBuffer boundaryEncoded = encode(this.charset, getBoundary());
+        final ByteArrayBuffer boundaryEncoded = encode(this.charset, this.boundary);
         for (final FormBodyPart part: getBodyParts()) {
             writeBytes(TWO_DASHES, out);
             writeBytes(boundaryEncoded, out);
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/FormBodyPart.java b/httpmime/src/main/java/org/apache/http/entity/mime/FormBodyPart.java
index b7cb7b6..fbf7a53 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/FormBodyPart.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/FormBodyPart.java
@@ -43,9 +43,21 @@
 
     private final String name;
     private final Header header;
-
     private final ContentBody body;
 
+    FormBodyPart(final String name, final ContentBody body, final Header header) {
+        super();
+        Args.notNull(name, "Name");
+        Args.notNull(body, "Body");
+        this.name = name;
+        this.body = body;
+        this.header = header != null ? header : new Header();
+    }
+
+    /**
+     * @deprecated (4.4) use {@link org.apache.http.entity.mime.FormBodyPartBuilder}.
+     */
+    @Deprecated
     public FormBodyPart(final String name, final ContentBody body) {
         super();
         Args.notNull(name, "Name");
@@ -76,6 +88,10 @@
         this.header.addField(new MinimalField(name, value));
     }
 
+    /**
+     * @deprecated (4.4) use {@link org.apache.http.entity.mime.FormBodyPartBuilder}.
+     */
+    @Deprecated
     protected void generateContentDisp(final ContentBody body) {
         final StringBuilder buffer = new StringBuilder();
         buffer.append("form-data; name=\"");
@@ -89,6 +105,10 @@
         addField(MIME.CONTENT_DISPOSITION, buffer.toString());
     }
 
+    /**
+     * @deprecated (4.4) use {@link org.apache.http.entity.mime.FormBodyPartBuilder}.
+     */
+    @Deprecated
     protected void generateContentType(final ContentBody body) {
         final ContentType contentType;
         if (body instanceof AbstractContentBody) {
@@ -109,6 +129,10 @@
         }
     }
 
+    /**
+     * @deprecated (4.4) use {@link org.apache.http.entity.mime.FormBodyPartBuilder}.
+     */
+    @Deprecated
     protected void generateTransferEncoding(final ContentBody body) {
         addField(MIME.CONTENT_TRANSFER_ENC, body.getTransferEncoding()); // TE cannot be null
     }
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/FormBodyPartBuilder.java b/httpmime/src/main/java/org/apache/http/entity/mime/FormBodyPartBuilder.java
new file mode 100644
index 0000000..3fd282a
--- /dev/null
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/FormBodyPartBuilder.java
@@ -0,0 +1,141 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on 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.mime;
+
+import java.util.List;
+
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.content.AbstractContentBody;
+import org.apache.http.entity.mime.content.ContentBody;
+import org.apache.http.util.Args;
+import org.apache.http.util.Asserts;
+
+/**
+ * Builder for individual {@link org.apache.http.entity.mime.FormBodyPart}s.
+ *
+ * @since 4.4
+ */
+public class FormBodyPartBuilder {
+
+    private String name;
+    private ContentBody body;
+    private final Header header;
+
+    public static FormBodyPartBuilder create(final String name, final ContentBody body) {
+        return new FormBodyPartBuilder(name, body);
+    }
+
+    public static FormBodyPartBuilder create() {
+        return new FormBodyPartBuilder();
+    }
+
+    FormBodyPartBuilder(final String name, final ContentBody body) {
+        this();
+        this.name = name;
+        this.body = body;
+    }
+
+    FormBodyPartBuilder() {
+        this.header = new Header();
+    }
+
+    public FormBodyPartBuilder setName(final String name) {
+        this.name = name;
+        return this;
+    }
+
+    public FormBodyPartBuilder setBody(final ContentBody body) {
+        this.body = body;
+        return this;
+    }
+
+    public FormBodyPartBuilder addField(final String name, final String value) {
+        Args.notNull(name, "Field name");
+        this.header.addField(new MinimalField(name, value));
+        return this;
+    }
+
+    public FormBodyPartBuilder setField(final String name, final String value) {
+        Args.notNull(name, "Field name");
+        this.header.setField(new MinimalField(name, value));
+        return this;
+    }
+
+    public FormBodyPartBuilder removeFields(final String name) {
+        Args.notNull(name, "Field name");
+        this.header.removeFields(name);
+        return this;
+    }
+
+    public FormBodyPart build() {
+        Asserts.notBlank(this.name, "Name");
+        Asserts.notNull(this.body, "Content body");
+        final Header headerCopy = new Header();
+        final List<MinimalField> fields = this.header.getFields();
+        for (MinimalField field: fields) {
+            headerCopy.addField(field);
+        }
+        if (headerCopy.getField(MIME.CONTENT_DISPOSITION) == null) {
+            final StringBuilder buffer = new StringBuilder();
+            buffer.append("form-data; name=\"");
+            buffer.append(this.name);
+            buffer.append("\"");
+            if (this.body.getFilename() != null) {
+                buffer.append("; filename=\"");
+                buffer.append(this.body.getFilename());
+                buffer.append("\"");
+            }
+            headerCopy.addField(new MinimalField(MIME.CONTENT_DISPOSITION, buffer.toString()));
+        }
+        if (headerCopy.getField(MIME.CONTENT_TYPE) == null) {
+            final ContentType contentType;
+            if (body instanceof AbstractContentBody) {
+                contentType = ((AbstractContentBody) body).getContentType();
+            } else {
+                contentType = null;
+            }
+            if (contentType != null) {
+                headerCopy.addField(new MinimalField(MIME.CONTENT_TYPE, contentType.toString()));
+            } else {
+                final StringBuilder buffer = new StringBuilder();
+                buffer.append(this.body.getMimeType()); // MimeType cannot be null
+                if (this.body.getCharset() != null) { // charset may legitimately be null
+                    buffer.append("; charset=");
+                    buffer.append(this.body.getCharset());
+                }
+                headerCopy.addField(new MinimalField(MIME.CONTENT_TYPE, buffer.toString()));
+            }
+        }
+        if (headerCopy.getField(MIME.CONTENT_TRANSFER_ENC) == null) {
+            // TE cannot be null
+            headerCopy.addField(new MinimalField(MIME.CONTENT_TRANSFER_ENC, body.getTransferEncoding()));
+        }
+        return new FormBodyPart(this.name, this.body, headerCopy);
+    }
+
+}
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/HttpBrowserCompatibleMultipart.java b/httpmime/src/main/java/org/apache/http/entity/mime/HttpBrowserCompatibleMultipart.java
index 5ac01fc..426b3da 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/HttpBrowserCompatibleMultipart.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/HttpBrowserCompatibleMultipart.java
@@ -43,11 +43,10 @@
     private final List<FormBodyPart> parts;
 
     public HttpBrowserCompatibleMultipart(
-            final String subType,
             final Charset charset,
             final String boundary,
             final List<FormBodyPart> parts) {
-        super(subType, charset, boundary);
+        super(charset, boundary);
         this.parts = parts;
     }
 
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/HttpRFC6532Multipart.java b/httpmime/src/main/java/org/apache/http/entity/mime/HttpRFC6532Multipart.java
index a499151..a48b423 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/HttpRFC6532Multipart.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/HttpRFC6532Multipart.java
@@ -44,11 +44,10 @@
     private final List<FormBodyPart> parts;
 
     public HttpRFC6532Multipart(
-            final String subType,
             final Charset charset,
             final String boundary,
             final List<FormBodyPart> parts) {
-        super(subType, charset, boundary);
+        super(charset, boundary);
         this.parts = parts;
     }
 
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/HttpStrictMultipart.java b/httpmime/src/main/java/org/apache/http/entity/mime/HttpStrictMultipart.java
index 5e9510e..6b07c66 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/HttpStrictMultipart.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/HttpStrictMultipart.java
@@ -44,11 +44,10 @@
     private final List<FormBodyPart> parts;
 
     public HttpStrictMultipart(
-            final String subType,
             final Charset charset,
             final String boundary,
             final List<FormBodyPart> parts) {
-        super(subType, charset, boundary);
+        super(charset, boundary);
         this.parts = parts;
     }
 
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java b/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java
index 859cf1e..5bbb1db 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java
@@ -36,12 +36,14 @@
 import java.util.Random;
 
 import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
 import org.apache.http.entity.ContentType;
 import org.apache.http.entity.mime.content.ByteArrayBody;
 import org.apache.http.entity.mime.content.ContentBody;
 import org.apache.http.entity.mime.content.FileBody;
 import org.apache.http.entity.mime.content.InputStreamBody;
 import org.apache.http.entity.mime.content.StringBody;
+import org.apache.http.message.BasicNameValuePair;
 import org.apache.http.util.Args;
 
 /**
@@ -60,7 +62,7 @@
 
     private final static String DEFAULT_SUBTYPE = "form-data";
 
-    private String subType = DEFAULT_SUBTYPE;
+    private ContentType contentType;
     private HttpMultipartMode mode = HttpMultipartMode.STRICT;
     private String boundary = null;
     private Charset charset = null;
@@ -71,7 +73,6 @@
     }
 
     MultipartEntityBuilder() {
-        super();
     }
 
     public MultipartEntityBuilder setMode(final HttpMultipartMode mode) {
@@ -99,7 +100,16 @@
      */
     public MultipartEntityBuilder setMimeSubtype(final String subType) {
         Args.notBlank(subType, "MIME subtype");
-        this.subType = subType;
+        this.contentType = ContentType.create("multipart/" + subType);
+        return this;
+    }
+
+    /**
+     * @since 4.4
+     */
+    public MultipartEntityBuilder seContentType(final ContentType contentType) {
+        Args.notNull(contentType, "Content type");
+        this.contentType = contentType;
         return this;
     }
 
@@ -125,7 +135,7 @@
     public MultipartEntityBuilder addPart(final String name, final ContentBody contentBody) {
         Args.notNull(name, "Name");
         Args.notNull(contentBody, "Content body");
-        return addPart(new FormBodyPart(name, contentBody));
+        return addPart(FormBodyPartBuilder.create(name, contentBody).build());
     }
 
     public MultipartEntityBuilder addTextBody(
@@ -168,20 +178,6 @@
         return addBinaryBody(name, stream, ContentType.DEFAULT_BINARY, null);
     }
 
-    private String generateContentType(
-            final String boundary,
-            final String subType,
-            final Charset charset) {
-        final StringBuilder buffer = new StringBuilder();
-        buffer.append("multipart/").append(subType).append("; boundary=");
-        buffer.append(boundary);
-        if (charset != null) {
-            buffer.append("; charset=");
-            buffer.append(charset.name());
-        }
-        return buffer.toString();
-    }
-
     private String generateBoundary() {
         final StringBuilder buffer = new StringBuilder();
         final Random rand = new Random();
@@ -193,24 +189,41 @@
     }
 
     MultipartFormEntity buildEntity() {
-        final String st = subType != null ? subType : DEFAULT_SUBTYPE;
-        final Charset cs = charset;
-        final String b = boundary != null ? boundary : generateBoundary();
-        final List<FormBodyPart> bps = bodyParts != null ? new ArrayList<FormBodyPart>(bodyParts) :
+        String boundaryCopy = boundary;
+        if (boundaryCopy == null && contentType != null) {
+            boundaryCopy = contentType.getParameter("boundary");
+        }
+        if (boundaryCopy == null) {
+            boundaryCopy = generateBoundary();
+        }
+        Charset charsetCopy = charset;
+        if (charsetCopy == null && contentType != null) {
+            charsetCopy = contentType.getCharset();
+        }
+        final List<NameValuePair> paramsList = new ArrayList<NameValuePair>(2);
+        paramsList.add(new BasicNameValuePair("boundary", boundaryCopy));
+        if (charsetCopy != null) {
+            paramsList.add(new BasicNameValuePair("charset", charsetCopy.name()));
+        }
+        final NameValuePair[] params = paramsList.toArray(new NameValuePair[paramsList.size()]);
+        final ContentType contentTypeCopy = contentType != null ?
+                contentType.withParameters(params) :
+                ContentType.create("multipart/" + DEFAULT_SUBTYPE, params);
+        final List<FormBodyPart> bodyPartsCopy = bodyParts != null ? new ArrayList<FormBodyPart>(bodyParts) :
                 Collections.<FormBodyPart>emptyList();
-        final HttpMultipartMode m = mode != null ? mode : HttpMultipartMode.STRICT;
+        final HttpMultipartMode modeCopy = mode != null ? mode : HttpMultipartMode.STRICT;
         final AbstractMultipartForm form;
-        switch (m) {
+        switch (modeCopy) {
             case BROWSER_COMPATIBLE:
-                form = new HttpBrowserCompatibleMultipart(st, cs, b, bps);
+                form = new HttpBrowserCompatibleMultipart(charsetCopy, boundaryCopy, bodyPartsCopy);
                 break;
             case RFC6532:
-                form = new HttpRFC6532Multipart(st, cs, b, bps);
+                form = new HttpRFC6532Multipart(charsetCopy, boundaryCopy, bodyPartsCopy);
                 break;
             default:
-                form = new HttpStrictMultipart(st, cs, b, bps);
+                form = new HttpStrictMultipart(charsetCopy, boundaryCopy, bodyPartsCopy);
         }
-        return new MultipartFormEntity(form, generateContentType(b, st, cs), form.getTotalLength());
+        return new MultipartFormEntity(form, contentTypeCopy, form.getTotalLength());
     }
 
     public HttpEntity build() {
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/MultipartFormEntity.java b/httpmime/src/main/java/org/apache/http/entity/mime/MultipartFormEntity.java
index 701d428..9c6158a 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/MultipartFormEntity.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/MultipartFormEntity.java
@@ -29,6 +29,7 @@
 
 import org.apache.http.Header;
 import org.apache.http.HttpEntity;
+import org.apache.http.entity.ContentType;
 import org.apache.http.message.BasicHeader;
 import org.apache.http.protocol.HTTP;
 
@@ -44,11 +45,11 @@
 
     MultipartFormEntity(
             final AbstractMultipartForm multipart,
-            final String contentType,
+            final ContentType contentType,
             final long contentLength) {
         super();
         this.multipart = multipart;
-        this.contentType = new BasicHeader(HTTP.CONTENT_TYPE, contentType);
+        this.contentType = new BasicHeader(HTTP.CONTENT_TYPE, contentType.toString());
         this.contentLength = contentLength;
     }
 
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/content/FileBody.java b/httpmime/src/main/java/org/apache/http/entity/mime/content/FileBody.java
index 56c702e..877dcc4 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/content/FileBody.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/content/FileBody.java
@@ -103,7 +103,7 @@
      * @since 4.3
      */
     public FileBody(final File file, final ContentType contentType) {
-        this(file, contentType, null);
+        this(file, contentType, file != null ? file.getName() : null);
     }
 
     public InputStream getInputStream() throws IOException {
diff --git a/httpmime/src/test/java/org/apache/http/entity/mime/TestFormBodyPartBuilder.java b/httpmime/src/test/java/org/apache/http/entity/mime/TestFormBodyPartBuilder.java
new file mode 100644
index 0000000..49a6bd8
--- /dev/null
+++ b/httpmime/src/test/java/org/apache/http/entity/mime/TestFormBodyPartBuilder.java
@@ -0,0 +1,175 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on 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.mime;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.entity.mime.content.StringBody;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestFormBodyPartBuilder {
+
+    @Test
+    public void testBuildBodyPartBasics() throws Exception {
+        final StringBody stringBody = new StringBody("stuff", ContentType.TEXT_PLAIN);
+        final FormBodyPart bodyPart = FormBodyPartBuilder.create()
+                .setName("blah")
+                .setBody(stringBody)
+                .build();
+        Assert.assertNotNull(bodyPart);
+        Assert.assertEquals("blah", bodyPart.getName());
+        Assert.assertEquals(stringBody, bodyPart.getBody());
+        final Header header = bodyPart.getHeader();
+        Assert.assertNotNull(header);
+        assertFields(Arrays.asList(
+                        new MinimalField("Content-Disposition", "form-data; name=\"blah\""),
+                        new MinimalField("Content-Type", "text/plain; charset=ISO-8859-1"),
+                        new MinimalField("Content-Transfer-Encoding", "8bit")),
+                header.getFields());
+    }
+
+    @Test
+    public void testBuildBodyPartMultipleBuilds() throws Exception {
+        final StringBody stringBody = new StringBody("stuff", ContentType.TEXT_PLAIN);
+        final FormBodyPartBuilder builder = FormBodyPartBuilder.create();
+        final FormBodyPart bodyPart1 = builder
+                .setName("blah")
+                .setBody(stringBody)
+                .build();
+        Assert.assertNotNull(bodyPart1);
+        Assert.assertEquals("blah", bodyPart1.getName());
+        Assert.assertEquals(stringBody, bodyPart1.getBody());
+        final Header header1 = bodyPart1.getHeader();
+        Assert.assertNotNull(header1);
+        assertFields(Arrays.asList(
+                        new MinimalField("Content-Disposition", "form-data; name=\"blah\""),
+                        new MinimalField("Content-Type", "text/plain; charset=ISO-8859-1"),
+                        new MinimalField("Content-Transfer-Encoding", "8bit")),
+                header1.getFields());
+        final FileBody fileBody = new FileBody(new File("/path/stuff.bin"), ContentType.DEFAULT_BINARY);
+        final FormBodyPart bodyPart2 = builder
+                .setName("yada")
+                .setBody(fileBody)
+                .build();
+
+        Assert.assertNotNull(bodyPart2);
+        Assert.assertEquals("yada", bodyPart2.getName());
+        Assert.assertEquals(fileBody, bodyPart2.getBody());
+        final Header header2 = bodyPart2.getHeader();
+        Assert.assertNotNull(header2);
+        assertFields(Arrays.asList(
+                        new MinimalField("Content-Disposition", "form-data; name=\"yada\"; filename=\"stuff.bin\""),
+                        new MinimalField("Content-Type", "application/octet-stream"),
+                        new MinimalField("Content-Transfer-Encoding", "binary")),
+                header2.getFields());
+    }
+
+    @Test
+    public void testBuildBodyPartCustomHeaders() throws Exception {
+        final StringBody stringBody = new StringBody("stuff", ContentType.TEXT_PLAIN);
+        final FormBodyPartBuilder builder = FormBodyPartBuilder.create("blah", stringBody);
+        final FormBodyPart bodyPart1 = builder
+                .addField("header1", "blah")
+                .addField("header3", "blah")
+                .addField("header3", "blah")
+                .addField("header3", "blah")
+                .addField("header3", "blah")
+                .addField("header3", "blah")
+                .build();
+
+        Assert.assertNotNull(bodyPart1);
+        final Header header1 = bodyPart1.getHeader();
+        Assert.assertNotNull(header1);
+
+        assertFields(Arrays.asList(
+                new MinimalField("header1", "blah"),
+                new MinimalField("header3", "blah"),
+                new MinimalField("header3", "blah"),
+                new MinimalField("header3", "blah"),
+                new MinimalField("header3", "blah"),
+                new MinimalField("header3", "blah"),
+                new MinimalField("Content-Disposition", "form-data; name=\"blah\""),
+                new MinimalField("Content-Type", "text/plain; charset=ISO-8859-1"),
+                new MinimalField("Content-Transfer-Encoding", "8bit")),
+                header1.getFields());
+
+        final FormBodyPart bodyPart2 = builder
+                .setField("header2", "yada")
+                .removeFields("header3")
+                .build();
+
+        Assert.assertNotNull(bodyPart2);
+        final Header header2 = bodyPart2.getHeader();
+        Assert.assertNotNull(header2);
+
+        assertFields(Arrays.asList(
+                        new MinimalField("header1", "blah"),
+                        new MinimalField("header2", "yada"),
+                        new MinimalField("Content-Disposition", "form-data; name=\"blah\""),
+                        new MinimalField("Content-Type", "text/plain; charset=ISO-8859-1"),
+                        new MinimalField("Content-Transfer-Encoding", "8bit")),
+                header2.getFields());
+
+        final FormBodyPart bodyPart3 = builder
+                .addField("Content-Disposition", "disposition stuff")
+                .addField("Content-Type", "type stuff")
+                .addField("Content-Transfer-Encoding", "encoding stuff")
+                .build();
+
+        Assert.assertNotNull(bodyPart3);
+        final Header header3 = bodyPart3.getHeader();
+        Assert.assertNotNull(header3);
+
+        assertFields(Arrays.asList(
+                        new MinimalField("header1", "blah"),
+                        new MinimalField("header2", "yada"),
+                        new MinimalField("Content-Disposition", "disposition stuff"),
+                        new MinimalField("Content-Type", "type stuff"),
+                        new MinimalField("Content-Transfer-Encoding", "encoding stuff")),
+                header3.getFields());
+
+    }
+
+    private static void assertFields(final List<MinimalField> expected, final List<MinimalField> result) {
+        Assert.assertNotNull(result);
+        Assert.assertEquals(expected.size(), result.size());
+        for (int i = 0; i < expected.size(); i++) {
+            final MinimalField expectedField = expected.get(i);
+            final MinimalField resultField = result.get(i);
+            Assert.assertNotNull(resultField);
+            Assert.assertEquals(expectedField.getName(), resultField.getName());
+            Assert.assertEquals(expectedField.getBody(), resultField.getBody());
+        }
+    }
+
+}
diff --git a/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java b/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java
index f355b5b..5a9e9a3 100644
--- a/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java
+++ b/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java
@@ -32,6 +32,9 @@
 import java.util.List;
 
 import org.apache.http.Consts;
+import org.apache.http.Header;
+import org.apache.http.entity.ContentType;
+import org.apache.http.message.BasicNameValuePair;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -54,8 +57,8 @@
                 .buildEntity();
         Assert.assertNotNull(entity);
         Assert.assertTrue(entity.getMultipart() instanceof HttpBrowserCompatibleMultipart);
-        Assert.assertEquals("blah-blah", entity.getMultipart().getBoundary());
-        Assert.assertEquals(Consts.UTF_8, entity.getMultipart().getCharset());
+        Assert.assertEquals("blah-blah", entity.getMultipart().boundary);
+        Assert.assertEquals(Consts.UTF_8, entity.getMultipart().charset);
     }
 
     @Test
@@ -63,8 +66,8 @@
         final MultipartFormEntity entity = MultipartEntityBuilder.create()
                 .addTextBody("p1", "stuff")
                 .addBinaryBody("p2", new File("stuff"))
-                .addBinaryBody("p3", new byte[] {})
-                .addBinaryBody("p4", new ByteArrayInputStream(new byte[] {}))
+                .addBinaryBody("p3", new byte[]{})
+                .addBinaryBody("p4", new ByteArrayInputStream(new byte[]{}))
                 .buildEntity();
         Assert.assertNotNull(entity);
         final List<FormBodyPart> bodyParts = entity.getMultipart().getBodyParts();
@@ -72,4 +75,52 @@
         Assert.assertEquals(4, bodyParts.size());
     }
 
+    @Test
+    public void testMultipartCustomContentType() throws Exception {
+        final MultipartFormEntity entity = MultipartEntityBuilder.create()
+                .seContentType(ContentType.APPLICATION_XML)
+                .setBoundary("blah-blah")
+                .setCharset(Consts.UTF_8)
+                .setLaxMode()
+                .buildEntity();
+        Assert.assertNotNull(entity);
+        final Header contentType = entity.getContentType();
+        Assert.assertNotNull(contentType);
+        Assert.assertEquals("application/xml; boundary=blah-blah; charset=UTF-8", contentType.getValue());
+    }
+
+    @Test
+    public void testMultipartContentTypeParameter() throws Exception {
+        final MultipartFormEntity entity = MultipartEntityBuilder.create()
+                .seContentType(ContentType.MULTIPART_FORM_DATA.withParameters(
+                        new BasicNameValuePair("boundary", "yada-yada"),
+                        new BasicNameValuePair("charset", "ascii")))
+                .buildEntity();
+        Assert.assertNotNull(entity);
+        final Header contentType = entity.getContentType();
+        Assert.assertNotNull(contentType);
+        Assert.assertEquals("multipart/form-data; boundary=yada-yada; charset=US-ASCII",
+                contentType.getValue());
+        Assert.assertEquals("yada-yada", entity.getMultipart().boundary);
+        Assert.assertEquals(Consts.ASCII, entity.getMultipart().charset);
+    }
+
+    @Test
+    public void testMultipartCustomContentTypeParameterOverrides() throws Exception {
+        final MultipartFormEntity entity = MultipartEntityBuilder.create()
+                .seContentType(ContentType.MULTIPART_FORM_DATA.withParameters(
+                        new BasicNameValuePair("boundary", "yada-yada"),
+                        new BasicNameValuePair("charset", "ascii"),
+                        new BasicNameValuePair("my", "stuff")))
+                .setBoundary("blah-blah")
+                .setCharset(Consts.UTF_8)
+                .setLaxMode()
+                .buildEntity();
+        Assert.assertNotNull(entity);
+        final Header contentType = entity.getContentType();
+        Assert.assertNotNull(contentType);
+        Assert.assertEquals("multipart/form-data; boundary=blah-blah; charset=UTF-8; my=stuff",
+                contentType.getValue());
+    }
+
 }
diff --git a/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartForm.java b/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartForm.java
index f47e638..6ee3f39 100644
--- a/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartForm.java
+++ b/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartForm.java
@@ -57,17 +57,17 @@
 
     @Test
     public void testMultipartFormStringParts() throws Exception {
-        final FormBodyPart p1 = new FormBodyPart(
+        final FormBodyPart p1 = FormBodyPartBuilder.create(
                 "field1",
-                new StringBody("this stuff", ContentType.DEFAULT_TEXT));
-        final FormBodyPart p2 = new FormBodyPart(
+                new StringBody("this stuff", ContentType.DEFAULT_TEXT)).build();
+        final FormBodyPart p2 = FormBodyPartBuilder.create(
                 "field2",
                 new StringBody("that stuff", ContentType.create(
-                        ContentType.TEXT_PLAIN.getMimeType(), Consts.UTF_8)));
-        final FormBodyPart p3 = new FormBodyPart(
+                        ContentType.TEXT_PLAIN.getMimeType(), Consts.UTF_8))).build();
+        final FormBodyPart p3 = FormBodyPartBuilder.create(
                 "field3",
-                new StringBody("all kind of stuff", ContentType.DEFAULT_TEXT));
-        final HttpStrictMultipart multipart = new HttpStrictMultipart("form-data", null, "foo",
+                new StringBody("all kind of stuff", ContentType.DEFAULT_TEXT)).build();
+        final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
                 Arrays.asList(p1, p2, p3));
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -101,13 +101,13 @@
 
     @Test
     public void testMultipartFormCustomContentType() throws Exception {
-        final FormBodyPart p1 = new FormBodyPart(
+        final FormBodyPart p1 = FormBodyPartBuilder.create(
                 "field1",
-                new StringBody("this stuff", ContentType.DEFAULT_TEXT));
-        final FormBodyPart p2 = new FormBodyPart(
+                new StringBody("this stuff", ContentType.DEFAULT_TEXT)).build();
+        final FormBodyPart p2 = FormBodyPartBuilder.create(
                 "field2",
-                new StringBody("that stuff", ContentType.parse("stuff/plain; param=value")));
-        final HttpStrictMultipart multipart = new HttpStrictMultipart("form-data", null, "foo",
+                new StringBody("that stuff", ContentType.parse("stuff/plain; param=value"))).build();
+        final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
                 Arrays.asList(p1, p2));
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -143,14 +143,14 @@
             writer.close();
         }
 
-        final FormBodyPart p1 = new FormBodyPart(
+        final FormBodyPart p1 = FormBodyPartBuilder.create(
                 "field1",
-                new FileBody(tmpfile));
+                new FileBody(tmpfile)).build();
         @SuppressWarnings("resource")
-        final FormBodyPart p2 = new FormBodyPart(
+        final FormBodyPart p2 = FormBodyPartBuilder.create(
                 "field2",
-                new InputStreamBody(new FileInputStream(tmpfile), "file.tmp"));
-        final HttpStrictMultipart multipart = new HttpStrictMultipart("form-data", null, "foo",
+                new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build();
+        final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
                 Arrays.asList(p1, p2));
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -188,17 +188,17 @@
             writer.close();
         }
 
-        final FormBodyPart p1 = new FormBodyPart(
+        final FormBodyPart p1 = FormBodyPartBuilder.create(
                 "field1",
-                new FileBody(tmpfile));
-        final FormBodyPart p2 = new FormBodyPart(
+                new FileBody(tmpfile)).build();
+        final FormBodyPart p2 = FormBodyPartBuilder.create(
                 "field2",
-                new FileBody(tmpfile, ContentType.create("text/plain", "ANSI_X3.4-1968"), "test-file"));
+                new FileBody(tmpfile, ContentType.create("text/plain", "ANSI_X3.4-1968"), "test-file")).build();
         @SuppressWarnings("resource")
-        final FormBodyPart p3 = new FormBodyPart(
+        final FormBodyPart p3 = FormBodyPartBuilder.create(
                 "field3",
-                new InputStreamBody(new FileInputStream(tmpfile), "file.tmp"));
-        final HttpStrictMultipart multipart = new HttpStrictMultipart("form-data", null, "foo",
+                new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build();
+        final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
                 Arrays.asList(p1, p2, p3));
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -243,17 +243,17 @@
             writer.close();
         }
 
-        final FormBodyPart p1 = new FormBodyPart(
+        final FormBodyPart p1 = FormBodyPartBuilder.create(
                 "field1\u0414",
-                new FileBody(tmpfile));
-        final FormBodyPart p2 = new FormBodyPart(
+                new FileBody(tmpfile)).build();
+        final FormBodyPart p2 = FormBodyPartBuilder.create(
                 "field2",
-                new FileBody(tmpfile, ContentType.create("text/plain", "ANSI_X3.4-1968"), "test-file"));
+                new FileBody(tmpfile, ContentType.create("text/plain", "ANSI_X3.4-1968"), "test-file")).build();
         @SuppressWarnings("resource")
-        final FormBodyPart p3 = new FormBodyPart(
+        final FormBodyPart p3 = FormBodyPartBuilder.create(
                 "field3",
-                new InputStreamBody(new FileInputStream(tmpfile), "file.tmp"));
-        final HttpRFC6532Multipart multipart = new HttpRFC6532Multipart("form-data", null, "foo",
+                new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build();
+        final HttpRFC6532Multipart multipart = new HttpRFC6532Multipart(null, "foo",
                 Arrays.asList(p1, p2, p3));
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -321,15 +321,15 @@
         }
 
         @SuppressWarnings("resource")
-        final FormBodyPart p1 = new FormBodyPart(
+        final FormBodyPart p1 = FormBodyPartBuilder.create(
                 "field1",
-                new InputStreamBody(new FileInputStream(tmpfile), s1 + ".tmp"));
+                new InputStreamBody(new FileInputStream(tmpfile), s1 + ".tmp")).build();
         @SuppressWarnings("resource")
-        final FormBodyPart p2 = new FormBodyPart(
+        final FormBodyPart p2 = FormBodyPartBuilder.create(
                 "field2",
-                new InputStreamBody(new FileInputStream(tmpfile), s2 + ".tmp"));
+                new InputStreamBody(new FileInputStream(tmpfile), s2 + ".tmp")).build();
         final HttpBrowserCompatibleMultipart multipart = new HttpBrowserCompatibleMultipart(
-                "form-data", Consts.UTF_8, "foo",
+                Consts.UTF_8, "foo",
                 Arrays.asList(p1, p2));
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -360,13 +360,13 @@
         final String s1 = constructString(SWISS_GERMAN_HELLO);
         final String s2 = constructString(RUSSIAN_HELLO);
 
-        final FormBodyPart p1 = new FormBodyPart(
+        final FormBodyPart p1 = FormBodyPartBuilder.create(
                 "field1",
-                new StringBody(s1, ContentType.create("text/plain", Charset.forName("ISO-8859-1"))));
-        final FormBodyPart p2 = new FormBodyPart(
+                new StringBody(s1, ContentType.create("text/plain", Charset.forName("ISO-8859-1")))).build();
+        final FormBodyPart p2 = FormBodyPartBuilder.create(
                 "field2",
-                new StringBody(s2, ContentType.create("text/plain", Charset.forName("KOI8-R"))));
-        final HttpStrictMultipart multipart = new HttpStrictMultipart("form-data", null, "foo",
+                new StringBody(s2, ContentType.create("text/plain", Charset.forName("KOI8-R")))).build();
+        final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
                 Arrays.asList(p1, p2));
 
         final ByteArrayOutputStream out1 = new ByteArrayOutputStream();
diff --git a/pom.xml b/pom.xml
index 6c64d3a..4ed7529 100644
--- a/pom.xml
+++ b/pom.xml
@@ -66,12 +66,12 @@
   <properties>
     <maven.compiler.source>1.6</maven.compiler.source>
     <maven.compiler.target>1.6</maven.compiler.target>
-    <httpcore.version>4.4-beta1</httpcore.version>
-    <commons-logging.version>1.1.3</commons-logging.version>
-    <commons-codec.version>1.6</commons-codec.version>
-    <ehcache.version>2.2.0</ehcache.version>
-    <memcached.version>2.6</memcached.version>
-    <slf4j.version>1.5.11</slf4j.version>
+    <httpcore.version>4.4</httpcore.version>
+    <commons-logging.version>1.2</commons-logging.version>
+    <commons-codec.version>1.9</commons-codec.version>
+    <ehcache.version>2.6.9</ehcache.version>
+    <memcached.version>2.11.4</memcached.version>
+    <slf4j.version>1.7.7</slf4j.version>
     <junit.version>4.11</junit.version>
     <easymock.version>2.5.2</easymock.version>
     <mockito.version>1.8.5</mockito.version>
@@ -80,18 +80,6 @@
     <api.comparison.version>4.3</api.comparison.version>
   </properties>
 
-  <repositories>
-    <repository>
-      <id>spy</id>
-      <name>Spy Repository</name>
-      <layout>default</layout>
-      <url>http://files.couchbase.com/maven2/</url>
-      <snapshots>
-        <enabled>false</enabled>
-      </snapshots>
-    </repository>
-  </repositories>
-
   <dependencyManagement>
     <dependencies>
       <dependency>
@@ -120,7 +108,7 @@
         <version>${slf4j.version}</version>
       </dependency>
       <dependency>
-        <groupId>spy</groupId>
+        <groupId>net.spy</groupId>
         <artifactId>spymemcached</artifactId>
         <version>${memcached.version}</version>
       </dependency>
@@ -393,6 +381,7 @@
           <excludes>
             <exclude>src/docbkx/resources/**</exclude>
             <exclude>src/test/resources/*.truststore</exclude>
+            <exclude>.checkstyle</exclude>
           </excludes>
         </configuration>
       </plugin>
@@ -402,27 +391,27 @@
   <reporting>
     <plugins>
 
-       <plugin>
-         <artifactId>maven-project-info-reports-plugin</artifactId>
-         <version>${hc.project-info.version}</version>
-         <inherited>false</inherited>
-         <reportSets>
-           <reportSet>
-             <reports>
-               <report>dependency-management</report>
-               <report>issue-tracking</report>
-               <report>license</report>
-               <report>scm</report>
-               <report>summary</report>
-             </reports>
-           </reportSet>
-         </reportSets>
+      <plugin>
+        <artifactId>maven-project-info-reports-plugin</artifactId>
+        <version>${hc.project-info.version}</version>
+        <inherited>false</inherited>
+        <reportSets>
+          <reportSet>
+            <reports>
+              <report>dependency-management</report>
+              <report>issue-tracking</report>
+              <report>license</report>
+              <report>scm</report>
+              <report>summary</report>
+            </reports>
+          </reportSet>
+        </reportSets>
       </plugin>
 
       <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>clirr-maven-plugin</artifactId>
-         <version>${hc.clirr.version}</version>
+        <version>${hc.clirr.version}</version>
         <configuration>
           <comparisonVersion>${api.comparison.version}</comparisonVersion>
         </configuration>
diff --git a/src/docbkx/connmgmt.xml b/src/docbkx/connmgmt.xml
index 5a104f4..2a46b49 100644
--- a/src/docbkx/connmgmt.xml
+++ b/src/docbkx/connmgmt.xml
@@ -281,11 +281,11 @@
         <para>HttpClient tries to mitigate the problem by testing whether the connection is 'stale',
             that is no longer valid because it was closed on the server side, prior to using the
             connection for executing an HTTP request. The stale connection check is not 100%
-            reliable and adds 10 to 30 ms overhead to each request execution. The only feasible
-            solution that does not involve a one thread per socket model for idle connections is a
-            dedicated monitor thread used to evict connections that are considered expired due to a
-            long period of inactivity. The monitor thread can periodically call
-                <methodname>ClientConnectionManager#closeExpiredConnections()</methodname> method to
+            reliable. The only feasible solution that does not involve a one thread per socket
+            model for idle connections is a dedicated monitor thread used to evict connections
+            that are considered expired due to a long period of inactivity. The monitor thread can
+            periodically call
+            <methodname>ClientConnectionManager#closeExpiredConnections()</methodname> method to
             close all expired connections and evict closed connections from the pool. It can also
             optionally call <methodname>ClientConnectionManager#closeIdleConnections()</methodname>
             method to close all connections that have been idle over a given period of time.</para>
@@ -457,58 +457,66 @@
                 hostname matches the names stored inside the server's X.509 certificate, once the
                 connection has been established. This verification can provide additional guarantees
                 of authenticity of the server trust material.
-                The <interfacename>X509HostnameVerifier</interfacename> interface
-                represents a strategy for hostname verification. HttpClient ships with three
-                <interfacename>X509HostnameVerifier</interfacename> implementations.
+                The <interfacename>javax.net.ssl.HostnameVerifier</interfacename> interface
+                represents a strategy for hostname verification. HttpClient ships with two
+                <interfacename>javax.net.ssl.HostnameVerifier</interfacename> implementations.
                 Important: hostname verification should not be confused with
                 SSL trust verification.</para>
             <itemizedlist>
                 <listitem>
                     <formalpara>
-                        <title><classname>StrictHostnameVerifier</classname>:</title>
-                        <para>The strict hostname verifier works the same way as Sun Java 1.4, Sun
-                            Java 5, Sun Java 6. It's also pretty close to IE6. This implementation
-                            appears to be compliant with RFC 2818 for dealing with wildcards. The
-                            hostname must match either the first CN, or any of the subject-alts. A
+                        <title><classname>DefaultHostnameVerifier</classname>:</title>
+                        <para>The default implementation used by HttpClient is expected to be
+                            compliant with RFC 2818. The hostname must match any of alternative
+                            names specified by the certificate, or in case no alternative
+                            names are given the most specific CN of the certificate subject. A
                             wildcard can occur in the CN, and in any of the subject-alts.</para>
                     </formalpara>
                 </listitem>
                 <listitem>
                     <formalpara>
-                        <title><classname>BrowserCompatHostnameVerifier</classname>:</title>
-                        <para>This hostname verifier that works the same way as Curl and Firefox. 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 only
-                            difference between <classname>BrowserCompatHostnameVerifier</classname>
-                            and <classname>StrictHostnameVerifier</classname> is that a wildcard
-                            (such as "*.foo.com") with
-                            <classname>BrowserCompatHostnameVerifier</classname> matches all
-                            subdomains, including "a.b.foo.com".</para>
-                    </formalpara>
-                </listitem>
-                <listitem>
-                    <formalpara>
-                        <title><classname>AllowAllHostnameVerifier</classname>:</title>
+                        <title><classname>NoopHostnameVerifier</classname>:</title>
                         <para>This hostname verifier essentially turns hostname verification off.
-                            This implementation is a no-op, and never throws
-                            <exceptionname>javax.net.ssl.SSLException</exceptionname>.</para>
+                            It accepts any SSL session as valid and matching the target host.
+                        </para>
                     </formalpara>
                 </listitem>
             </itemizedlist>
-            <para>Per default HttpClient uses the <classname>BrowserCompatHostnameVerifier</classname>
+            <para>Per default HttpClient uses the <classname>DefaultHostnameVerifier</classname>
                 implementation. One can specify a different hostname verifier implementation if
                 desired</para>
             <programlisting><![CDATA[
 SSLContext sslContext = SSLContexts.createSystemDefault();
 SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
         sslContext,
-        SSLConnectionSocketFactory.STRICT_HOSTNAME_VERIFIER);
+        NoopHostnameVerifier.INSTANCE);
+]]></programlisting>
+            <para>As of version 4.4 HttpClient makes use the public suffix list kindly maintained
+                by Mozilla Foundation to make sure that wildcards in SSL certificates cannot be
+                misused to apply to multiple domains with a common top-level domain. HttpClient
+                ships with a copy of the list retrieved at the time of the release. The latest
+                revision of the list can found at
+                <ulink url="https://publicsuffix.org/list/effective_tld_names.dat">
+                    https://publicsuffix.org/list/</ulink>. It is highly adviseable to make a local
+                copy of the lsit and download the list no more than once per day from its original
+                location.
+            </para>
+            <programlisting><![CDATA[
+PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.load(
+    PublicSuffixMatcher.class.getResource("my-copy-effective_tld_names.dat"));
+DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher);
+]]></programlisting>
+            <para>One can disable verification against the public suffic list by using
+                <code>null</code> matcher.
+            </para>
+            <programlisting><![CDATA[
+DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(null);
 ]]></programlisting>
         </section>
     </section>
     <section>
         <title>HttpClient proxy configuration</title>
-        <para>Even though HttpClient is aware of complex routing scemes and proxy chaining, it
+        <para>Even though HttpClient is aware of complex routing schemes and proxy chaining, it
             supports only simple direct or one hop proxy connections out of the box.</para>
         <para>The simplest way to tell HttpClient to connect to the target host via a proxy is by
             setting the default proxy parameter:</para>
diff --git a/src/docbkx/preface.xml b/src/docbkx/preface.xml
index 8b7848b..b02e2ee 100644
--- a/src/docbkx/preface.xml
+++ b/src/docbkx/preface.xml
@@ -71,7 +71,7 @@
                 HttpClient is NOT a browser. It is a client side HTTP transport library. 
                 HttpClient's purpose is to transmit and receive HTTP messages. HttpClient will not 
                 attempt to process content, execute javascript embedded in HTML pages, try to guess
-                content type, if not explicitly set, or reformat request / redirect location URIs,
+                content type, if not explicitly set, or reformat request / rewrite location URIs,
                 or other functionality unrelated to the HTTP transport.
                 </para>
             </listitem>
diff --git a/src/docbkx/statemgmt.xml b/src/docbkx/statemgmt.xml
index b4da496..eb13511 100644
--- a/src/docbkx/statemgmt.xml
+++ b/src/docbkx/statemgmt.xml
@@ -45,63 +45,29 @@
             as a "magic cookie" and the name stuck.</para>
         <para>HttpClient uses the <interfacename>Cookie</interfacename> interface to represent an
             abstract cookie token. In its simplest form an HTTP cookie is merely a name / value pair.
-            Usually an HTTP cookie also contains a number of attributes such as version, a domain
-            for which is valid, a path that specifies the subset of URLs on the origin server to
-            which this cookie applies, and the maximum period of time for which the cookie is valid.</para>
+            Usually an HTTP cookie also contains a number of attributes such a domain for which is
+            valid, a path that specifies the subset of URLs on the origin server to which this
+            cookie applies, and the maximum period of time for which the cookie is valid.</para>
         <para>The <interfacename>SetCookie</interfacename> interface represents a
                 <literal>Set-Cookie</literal> response header sent by the origin server to the HTTP
-            agent in order to maintain a conversational state.
-            The <interfacename>SetCookie2</interfacename> interface extends SetCookie with
-                <literal>Set-Cookie2</literal> specific methods.</para>
-        <para>The <interfacename>ClientCookie</interfacename> interface extends
-                <interfacename>Cookie</interfacename> interface with additional client specific
-            functionality such as the ability to retrieve original cookie attributes exactly as they were
-            specified by the origin server. This is important for generating the
-                <literal>Cookie</literal> header because some cookie specifications require that the
-                <literal>Cookie</literal> header should include certain attributes only if they were
-            specified in the <literal>Set-Cookie</literal> or <literal>Set-Cookie2</literal>
-            header.</para>
-        <section>
-            <title>Cookie versions</title>
-            <para>Cookies compatible with Netscape draft specification but non-compliant with the
-                official specification are considered to be of version 0. Standard compliant cookies
-                are expected to have version 1. HttpClient may handle cookies differently depending
-                on the version.</para>
-            <para>Here is an example of re-creating a Netscape cookie:</para>
-            <programlisting><![CDATA[
-BasicClientCookie netscapeCookie = new BasicClientCookie("name", "value");
-netscapeCookie.setVersion(0);
-netscapeCookie.setDomain(".mycompany.com");
-netscapeCookie.setPath("/");
+            agent in order to maintain a conversational state.</para>
+        <para>The <interfacename>ClientCookie</interfacename> interface extends <interfacename>
+            Cookie</interfacename> interface with additional client specific functionality such
+            as the ability to retrieve original cookie attributes exactly as they were specified
+            by the origin server. This is important for generating the <literal>Cookie</literal>
+            header because some cookie specifications require that the  <literal>Cookie</literal>
+            header should include certain attributes only if they were specified in the
+            <literal>Set-Cookie</literal> header.</para>
+        <para>Here is an example of creating a client-side cookie object:</para>
+        <programlisting><![CDATA[
+BasicClientCookie cookie = new BasicClientCookie("name", "value");
+// Set effective domain and path attributes
+cookie.setDomain(".mycompany.com");
+cookie.setPath("/");
+// Set attributes exactly as sent by the server
+cookie.setAttribute(ClientCookie.PATH_ATTR, "/");
+cookie.setAttribute(ClientCookie.DOMAIN_ATTR, ".mycompany.com");
 ]]></programlisting>
-            <para>Here is an example of re-creating a standard cookie. Please note that standard
-                compliant cookie must retain all attributes as sent by the origin server:</para>
-            <programlisting><![CDATA[
-BasicClientCookie stdCookie = new BasicClientCookie("name", "value");
-stdCookie.setVersion(1);
-stdCookie.setDomain(".mycompany.com");
-stdCookie.setPath("/");
-stdCookie.setSecure(true);
-// Set attributes EXACTLY as sent by the server 
-stdCookie.setAttribute(ClientCookie.VERSION_ATTR, "1");
-stdCookie.setAttribute(ClientCookie.DOMAIN_ATTR, ".mycompany.com");
-]]></programlisting>
-            <para>Here is an example of re-creating a <literal>Set-Cookie2</literal> compliant
-                cookie. Please note that standard compliant cookie must retain all attributes as
-                sent by the origin server:</para>
-            <programlisting><![CDATA[
-BasicClientCookie2 stdCookie = new BasicClientCookie2("name", "value");
-stdCookie.setVersion(1);
-stdCookie.setDomain(".mycompany.com");
-stdCookie.setPorts(new int[] {80,8080});
-stdCookie.setPath("/");
-stdCookie.setSecure(true);
-// Set attributes EXACTLY as sent by the server 
-stdCookie.setAttribute(ClientCookie.VERSION_ATTR, "1");
-stdCookie.setAttribute(ClientCookie.DOMAIN_ATTR, ".mycompany.com");
-stdCookie.setAttribute(ClientCookie.PORT_ATTR, "80,8080");
-]]></programlisting>
-        </section>
     </section>
     <section>
         <title>Cookie specifications</title>
@@ -109,8 +75,7 @@
             specification. The cookie management specification is expected to enforce:</para>
         <itemizedlist>
             <listitem>
-                <para>rules of parsing <literal>Set-Cookie</literal> and optionally
-                        <literal>Set-Cookie2</literal> headers.</para>
+                <para>rules of parsing <literal>Set-Cookie</literal> headers.</para>
             </listitem>
             <listitem>
                 <para>rules of validation of parsed cookies.</para>
@@ -125,32 +90,60 @@
         <itemizedlist>
             <listitem>
                 <formalpara>
-                    <title>Netscape draft:</title>
-                    <para>This specification conforms to the original draft specification published
+                    <title>Standard strict:</title>
+                    <para>State management policy compliant with the syntax and semantics of
+                        the well-behaved profile defined by RFC 6265, section 4.</para>
+                </formalpara>
+            </listitem>
+            <listitem>
+                <formalpara>
+                    <title>Standard:</title>
+                    <para>State management policy compliant with a more relaxed profile defined
+                        by RFC 6265, section 4 intended for interoperability with existing servers
+                        that do not conform to the well behaved profile.</para>
+                </formalpara>
+            </listitem>
+            <listitem>
+                <formalpara>
+                    <title>Netscape draft (obsolete):</title>
+                    <para>This policy conforms to the original draft specification published
                         by Netscape Communications. It should be avoided unless absolutely necessary
                         for compatibility with legacy code.</para>
                 </formalpara>
             </listitem>
             <listitem>
                 <formalpara>
-                    <title>Standard:</title>
-                    <para>RFC 2965 HTTP state management specification.</para>
+                    <title>RFC 2965 (obsolete):</title>
+                    <para>State management policy compliant with the obsolete state management
+                        specification defined by RFC 2965. Please do not use in new applications.
+                    </para>
                 </formalpara>
             </listitem>
             <listitem>
                 <formalpara>
-                    <title>Browser compatibility:</title>
-                    <para>This implementation strives to closely mimic the (mis)behavior of common web
-                        browser applications such as Microsoft Internet Explorer and Mozilla
-                        FireFox.</para>
+                    <title>RFC 2109 (obsolete):</title>
+                    <para>State management policy compliant with the obsolete state management
+                        specification defined by RFC 2109. Please do not use in new applications.
+                    </para>
                 </formalpara>
             </listitem>
             <listitem>
                 <formalpara>
-                    <title>Best match:</title>
-                    <para>'Meta' cookie specification that picks up a cookie policy based on the
-                        format of cookies sent with the HTTP response. It basically aggregates all
-                        above implementations into one class.</para>
+                    <title>Browser compatibility (obsolete):</title>
+                    <para>This policy strives to closely mimic the (mis)behavior of older versions
+                        of browser applications such as Microsoft Internet Explorer and Mozilla
+                        FireFox. Please do not use in new applications.</para>
+                </formalpara>
+            </listitem>
+            <listitem>
+                <formalpara>
+                    <title>Default:</title>
+                    <para>Default cookie policy is a synthetic policy that picks up either RFC 2965,
+                        RFC 2109 or Netscape draft compliant implementation based on properties of
+                        cookies sent with the HTTP response (such as version attribute,
+                        now obsolete). This policy will be deprecated in favor of the
+                        standard (RFC 6265 compliant) implementation in the next minor release
+                        of HttpClient.</para>
                 </formalpara>
             </listitem>
             <listitem>
@@ -160,9 +153,11 @@
                 </formalpara>
             </listitem>
         </itemizedlist>
-        <para>It is strongly recommended to use the <literal>Best Match</literal> policy and let
-            HttpClient pick up an appropriate compliance level at runtime based on the execution
-            context.</para>
+        <para>It is strongly recommended to use either <literal>Standard</literal> or
+            <literal>Standard strict</literal> policy in new applications. Obsolete specifications
+            should be used for compatibility with legacy systems only. Support for obsolete
+            specifications will be removed in the next major release of HttpClient.
+        </para>
     </section>
     <section>
         <title>Choosing cookie policy</title>
@@ -170,13 +165,13 @@
             if required.</para>
         <programlisting><![CDATA[
 RequestConfig globalConfig = RequestConfig.custom()
-        .setCookieSpec(CookieSpecs.BEST_MATCH)
+        .setCookieSpec(CookieSpecs.DEFAULT)
         .build();
 CloseableHttpClient httpclient = HttpClients.custom()
         .setDefaultRequestConfig(globalConfig)
         .build();
 RequestConfig localConfig = RequestConfig.copy(globalConfig)
-        .setCookieSpec(CookieSpecs.BROWSER_COMPATIBILITY)
+        .setCookieSpec(CookieSpecs.STANDARD_STRICT)
         .build();
 HttpGet httpGet = new HttpGet("/");
 httpGet.setConfig(localConfig);
@@ -191,26 +186,14 @@
             HttpClient. Once the custom specification has been registered, it can be activated the
             same way as a standard cookie specification.</para>
         <programlisting><![CDATA[
-CookieSpecProvider easySpecProvider = new CookieSpecProvider() {
+PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.getDefault();
 
-    public CookieSpec create(HttpContext context) {
-
-        return new BrowserCompatSpec() {
-            @Override
-            public void validate(Cookie cookie, CookieOrigin origin)
-                    throws MalformedCookieException {
-                // Oh, I am easy
-            }
-        };
-    }
-
-};
 Registry<CookieSpecProvider> r = RegistryBuilder.<CookieSpecProvider>create()
-        .register(CookieSpecs.BEST_MATCH,
-            new BestMatchSpecFactory())
-        .register(CookieSpecs.BROWSER_COMPATIBILITY,
-            new BrowserCompatSpecFactory())
-        .register("easy", easySpecProvider)
+        .register(CookieSpecs.DEFAULT,
+                new DefaultCookieSpecProvider(publicSuffixMatcher))
+        .register(CookieSpecs.STANDARD,
+                new RFC6265CookieSpecProvider(publicSuffixMatcher))
+        .register("easy", new EasySpecProvider())
         .build();
 
 RequestConfig requestConfig = RequestConfig.custom()
@@ -238,7 +221,6 @@
 CookieStore cookieStore = new BasicCookieStore();
 // Populate cookies if needed
 BasicClientCookie cookie = new BasicClientCookie("name", "value");
-cookie.setVersion(0);
 cookie.setDomain(".mycompany.com");
 cookie.setPath("/");
 cookieStore.addCookie(cookie);