You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2016/10/08 11:40:09 UTC

svn commit: r1763892 [1/2] - in /httpcomponents/httpcore/trunk/httpcore5-testing/src: main/java/org/apache/hc/core5/testing/classic/ main/java/org/apache/hc/core5/testing/framework/ test/java/org/apache/hc/core5/testing/framework/

Author: olegk
Date: Sat Oct  8 11:40:09 2016
New Revision: 1763892

URL: http://svn.apache.org/viewvc?rev=1763892&view=rev
Log:
[HTTPCLIENT-1759] Contribute new tests that use the in-process http server.
Contributed by John W. Lewis <johnw.lewis at sas.com>

Added:
    httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/
    httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientAdapter.java   (with props)
    httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientTestingAdapter.java   (with props)
    httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientPOJOAdapter.java   (with props)
    httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientTestingAdapter.java   (with props)
    httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/FrameworkTest.java   (with props)
    httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFramework.java   (with props)
    httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkException.java   (with props)
    httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkRequestHandler.java   (with props)
    httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/framework/
    httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/framework/TestClassicTestClientTestingAdapter.java   (with props)
    httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/framework/TestClientPOJOAdapter.java   (with props)
    httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/framework/TestClientTestingAdapter.java   (with props)
    httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/framework/TestFrameworkTest.java   (with props)
    httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/framework/TestTestingFramework.java   (with props)
    httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/framework/TestTestingFrameworkRequestHandler.java   (with props)
Modified:
    httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java

Modified: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java?rev=1763892&r1=1763891&r2=1763892&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java Sat Oct  8 11:40:09 2016
@@ -110,10 +110,14 @@ public class ClassicTestServer {
     }
 
     public void shutdown() {
+        shutdown(5, TimeUnit.SECONDS);
+    }
+
+    public void shutdown(final long gracePeriod, final TimeUnit timeUnit) {
         final org.apache.hc.core5.http.bootstrap.io.HttpServer local = this.server;
         this.server = null;
         if (local != null) {
-            local.shutdown(5, TimeUnit.SECONDS);
+            local.shutdown(gracePeriod, timeUnit);
         }
     }
 

Added: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientAdapter.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientAdapter.java?rev=1763892&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientAdapter.java (added)
+++ httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientAdapter.java Sat Oct  8 11:40:09 2016
@@ -0,0 +1,181 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.testing.framework;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.ProtocolVersion;
+import org.apache.hc.core5.http.entity.ContentType;
+import org.apache.hc.core5.http.entity.EntityUtils;
+import org.apache.hc.core5.http.entity.StringEntity;
+import org.apache.hc.core5.http.impl.io.DefaultBHttpClientConnection;
+import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
+import org.apache.hc.core5.testing.classic.ClassicTestClient;
+
+/**
+ * Implementation of {@link ClientPOJOAdapter} for ClassicTestClient.
+ *
+ * @since 5.0
+ */
+public class ClassicTestClientAdapter extends ClientPOJOAdapter {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Map<String, Object> execute(final String defaultURI, final Map<String, Object> request) throws Exception {
+        // check the request for missing items.
+        if (defaultURI == null) {
+            throw new HttpException("defaultURL cannot be null");
+        }
+        if (request == null) {
+            throw new HttpException("request cannot be null");
+        }
+        if (! request.containsKey(PATH)) {
+            throw new HttpException("Request path should be set.");
+        }
+        if (! request.containsKey(METHOD)) {
+            throw new HttpException("Request method should be set.");
+        }
+
+        final ClassicTestClient client = new ClassicTestClient();
+
+        // Append the path to the defaultURI.
+        String tempDefaultURI = defaultURI;
+        if (! defaultURI.endsWith("/")) {
+            tempDefaultURI += "/";
+        }
+        final URI startingURI = new URI(tempDefaultURI + request.get(PATH));
+        final URI uri;
+
+        // append each parameter in the query to the uri.
+        @SuppressWarnings("unchecked")
+        final Map<String, String> queryMap = (Map<String, String>) request.get(QUERY);
+        if (queryMap != null) {
+            final String existingQuery = startingURI.getRawQuery();
+            final StringBuilder newQuery = new StringBuilder(existingQuery == null ? "" : existingQuery);
+
+            // append each parm to the query
+            for (Entry<String, String> parm : queryMap.entrySet()) {
+                newQuery.append("&" + parm.getKey() + "=" + parm.getValue());
+            }
+            // create a uri with the new query.
+            uri = new URI(
+                    startingURI.getRawSchemeSpecificPart(),
+                    startingURI.getRawUserInfo(),
+                    startingURI.getHost(),
+                    startingURI.getPort(),
+                    startingURI.getRawPath(),
+                    newQuery.toString(),
+                    startingURI.getRawFragment());
+        } else {
+            uri = startingURI;
+        }
+
+        final BasicClassicHttpRequest httpRequest = new BasicClassicHttpRequest(request.get(METHOD).toString(), uri);
+
+        if (request.containsKey(PROTOCOL_VERSION)) {
+            httpRequest.setVersion((ProtocolVersion) request.get(PROTOCOL_VERSION));
+        }
+
+        // call addHeader for each header in headers.
+        @SuppressWarnings("unchecked")
+        final Map<String, String> headersMap = (Map<String, String>) request.get(HEADERS);
+        if (headersMap != null) {
+            for (Entry<String, String> header : headersMap.entrySet()) {
+                httpRequest.addHeader(header.getKey(), header.getValue());
+            }
+        }
+
+        // call setEntity if a body is specified.
+        final String requestBody = (String) request.get(BODY);
+        if (requestBody != null) {
+            final String requestContentType = (String) request.get(CONTENT_TYPE);
+            final StringEntity entity = requestContentType != null ?
+                                          new StringEntity(requestBody, ContentType.parse(requestContentType)) :
+                                          new StringEntity(requestBody);
+            httpRequest.setEntity(entity);
+        }
+
+        // timeout
+        if (request.containsKey(TIMEOUT)) {
+            final long timeout = (long) request.get(TIMEOUT);
+            client.setTimeout((int) timeout);
+        }
+
+        // Now execute the request.
+        final DefaultBHttpClientConnection conn = client.createConnection();
+        final HttpHost host = new HttpHost(uri.getHost(), uri.getPort());
+
+        if (!conn.isOpen()) {
+            client.connect(host, conn);
+        }
+
+        final ClassicHttpResponse response = client.execute(httpRequest, host, conn);
+
+        // Prepare the response.  It will contain status, body, headers, and contentType.
+        final HttpEntity entity = response.getEntity();
+        final String body = entity == null ? null : EntityUtils.toString(entity);
+        final String contentType = entity == null ? null : entity.getContentType();
+
+        if (!client.keepAlive(httpRequest, response)) {
+            conn.close();
+        }
+
+        // prepare the returned information
+        final Map<String, Object> ret = new HashMap<String, Object>();
+        ret.put(STATUS, response.getCode());
+
+        // convert the headers to a Map
+        final Map<String, Object> headerMap = new HashMap<String, Object>();
+        for (Header header : response.getAllHeaders()) {
+            headerMap.put(header.getName(), header.getValue());
+        }
+        ret.put(HEADERS, headerMap);
+        ret.put(BODY, body);
+        ret.put(CONTENT_TYPE, contentType);
+
+        return ret ;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getClientName() {
+        return "ClassicTestClient";
+    }
+}

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientAdapter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientAdapter.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientAdapter.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientTestingAdapter.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientTestingAdapter.java?rev=1763892&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientTestingAdapter.java (added)
+++ httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientTestingAdapter.java Sat Oct  8 11:40:09 2016
@@ -0,0 +1,40 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.testing.framework;
+
+/**
+ * Implementation of {@link ClientTestingAdapter} for the ClassicTestClient.
+ *
+ * @since 5.0
+ */
+public class ClassicTestClientTestingAdapter extends ClientTestingAdapter {
+
+    public ClassicTestClientTestingAdapter() {
+        super(new ClassicTestClientAdapter());
+    }
+}

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientTestingAdapter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientTestingAdapter.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientTestingAdapter.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientPOJOAdapter.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientPOJOAdapter.java?rev=1763892&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientPOJOAdapter.java (added)
+++ httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientPOJOAdapter.java Sat Oct  8 11:40:09 2016
@@ -0,0 +1,155 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.testing.framework;
+
+import java.util.Map;
+
+/**
+ *
+ * <p>This adapter expects a request to be made up of POJOs such as Maps and Lists.  In Groovy
+ * the request could be expressed like this:</p>
+ *
+ * <pre>
+ *
+ * def request = [
+ *                   path    : "a/path",
+ *                   method  : "GET",
+ *                   query   : [
+ *                                parm1 : "1",
+ *                                parm2 : "2",
+ *                             ]
+ *                   headers : [
+ *                                header1 : "stuff",
+ *                                header2 : "more_stuff",
+ *                             ]
+ *                   contentType : "application/json",
+ *                   body        : '{"location" : "home" }',
+ *                ]
+ * </pre>
+ *
+ * <p>The adapter will translate this request into calls specific to a particular HTTP client.</p>
+ *
+ * <p>The response is then returned with POJOs with this structure:</p>
+ *
+ * <pre>
+ *
+ * def response = [
+ *                    status      : 200,
+ *                    headers     : [
+ *                                      header1 : "response_stuff",
+ *                                  ]
+ *                    contentType : "application/json",
+ *                    body        : '{"location" : "work" }',
+ *                ]
+ * </pre>
+ * @since 5.0
+ */
+public abstract class ClientPOJOAdapter {
+    public static final String BODY = "body";
+    public static final String CONTENT_TYPE = "contentType";
+    public static final String HEADERS = "headers";
+    public static final String METHOD = "method";
+    public static final String NAME = "name";
+    public static final String PATH = "path";
+    public static final String PROTOCOL_VERSION = "protocolVersion";
+    public static final String QUERY = "query";
+    public static final String REQUEST = "request";
+    public static final String RESPONSE = "response";
+    public static final String STATUS = "status";
+    public static final String TIMEOUT = "timeout";
+
+    /**
+     * Name of the HTTP Client that this adapter uses.
+     *
+     * @return name of the HTTP Client.
+     */
+    public abstract String getClientName();
+
+    /**
+     * Execute an HTTP request.
+     *
+     * @param defaultURI   the URI used by default.  The path in the request is
+     *                     usually appended to it.
+     * @param request      the request as specified above.
+     *
+     * @return the response to the request as specified above.
+     *
+     * @throws Exception in case of a problem
+     */
+    public abstract Map<String, Object> execute(String defaultURI, Map<String, Object> request) throws Exception;
+
+    /**
+     * <p>Check if a request is supported.</p>
+     *
+     * <p>Usually called directly by a testing framework.  If an HTTP client does not support
+     * a particular request, a non-null reason should be returned.  Otherwise, if
+     * the request is supported, return null.</p>
+     *
+     * <p>If this method is overridden, then the execute method should probably call
+     * assertRequestSupported() at the beginning.</p>
+     *
+     * @param request the request as specified above.
+     *
+     * @return null if the request is supported;  Otherwise, return a reason.
+     */
+    public String checkRequestSupport(final Map<String, Object> request) {
+        return null;
+    }
+
+    /**
+     * <p>Assert that the request is supported</p>
+     *
+     * <p>Usually called by the execute method.  Throws an exception if the request
+     * is not supported.</p>
+     *
+     * @param request the request as specified above.
+     * @throws Exception if the request is not supported.
+     */
+    public void assertRequestSupported(final Map<String, Object> request) throws Exception {
+        final String reason = checkRequestSupport(request);
+        if (reason != null) {
+            throw new Exception(reason);
+        }
+    }
+
+    /**
+     * <p>Modify the request.</p>
+     *
+     * <p>In a testing context, a testing framework can call this method to allow
+     * the adapter to change the request.  The request is then given to a
+     * special request handler of the in-process HttpServer which will later check
+     * an actual HTTP request against what is expected.</p>
+     *
+     * <p>In a production context, this is called by the execute method (if at all).</p>
+     *
+     * @param request the request as specified above.
+     * @return the same request or a modification of it.
+     */
+    public Map<String, Object> modifyRequest(final Map<String, Object> request) {
+        return request;
+    };
+}

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientPOJOAdapter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientPOJOAdapter.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientPOJOAdapter.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientTestingAdapter.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientTestingAdapter.java?rev=1763892&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientTestingAdapter.java (added)
+++ httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientTestingAdapter.java Sat Oct  8 11:40:09 2016
@@ -0,0 +1,186 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.testing.framework;
+
+import java.util.Map;
+
+/**
+*
+* <p>This adapter assists the testing of an HTTP client.  This adapter in turn uses an
+* {@link ClientPOJOAdapter} to actually use the HTTP client to make the request.
+* See {@link ClientPOJOAdapter} to see the format of the request and the returned
+* response.  The format of the returned response is also the format of the parameter
+* called responseExpectations.</p>
+*
+* <p>This adapter will generally call the {@link ClientPOJOAdapter} methods of the same
+* name when these methods are called:</p>
+*
+* <pre>
+*
+* isRequestSupported
+* modifyRequest
+* execute
+*
+* </pre>
+*
+* <p>See these method's documentation in {@link ClientPOJOAdapter} for details.</p>
+*
+* <p>The value that this adapter adds is with the modifyResponseExpectations method.  Each
+* test will specify the response that is expected.  The HttpClient5 adapter is able
+* to use these unmodified expectations, but if a different HTTP client (such as Groovy's
+* RESTClient which uses HttpClient) for some reason needs to modify the expectations,
+* it would be done in the modifyResponseExpectations method.</p>
+*
+* @since 5.0
+*/
+public class ClientTestingAdapter {
+    /**
+     * This adapter will perform the HTTP request and return the response in the
+     * expected format.
+     */
+    protected ClientPOJOAdapter adapter;
+
+    /*
+     * The following is not expected to be changed to true, but it is to highlight
+     * where the execute method can call the requestHandler's assertNothingThrown()
+     * method if desired.  Since this adapter's execute method does not check
+     * the response, there is no need to call it.
+     */
+    protected boolean callAssertNothingThrown;
+
+    public ClientTestingAdapter() {
+    }
+
+    public ClientTestingAdapter(final ClientPOJOAdapter adapter) {
+        this.adapter = adapter;
+    }
+
+    /**
+     * See the documentation for the same method in {@link ClientPOJOAdapter}.  This
+     * method will typically call it.  However, this method also has access to the
+     * test's response expectations if that is needed for some reason.  Furthermore,
+     * this method also has access to the {@link TestingFrameworkRequestHandler} so
+     * it can optionally call assertNothingThrown() before checking the response
+     * further.  It is optional because the test framework will call it later.
+     *
+     * @param defaultURI           See execute method of {@link ClientPOJOAdapter}.
+     * @param request              See execute method of {@link ClientPOJOAdapter}.
+     * @param requestHandler       The request handler that checks the received HTTP request
+     *                             with the request that was intended.  If there is a
+     *                             mismatch of expectations, then the requestHandler will
+     *                             throw an exception.  If this execute method does not want
+     *                             to make further checks of the response in the case
+     *                             the responseHandler threw, then the assertNothingThrown()
+     *                             method should be called before doing further checks.
+     * @param responseExpectations The response expectations of the test.
+     * @return See return of the execute method of {@link ClientPOJOAdapter}.
+     * @throws TestingFrameworkException in the case of a problem.
+     */
+    public Map<String, Object> execute(final String defaultURI, final Map<String, Object> request,
+            final TestingFrameworkRequestHandler requestHandler,
+            final Map<String, Object> responseExpectations) throws TestingFrameworkException {
+
+        try {
+            if (adapter == null) {
+                throw new TestingFrameworkException("adapter cannot be null");
+            }
+            // Call the adapter's execute method to actually make the HTTP request.
+            final Map<String, Object> response = adapter.execute(defaultURI, request);
+
+            /*
+             * Adapters may call assertNothingThrown() if they would like.  This would be to
+             * make sure the following code is not executed in the event there was something
+             * thrown in the request handler.
+             *
+             * Otherwise, the framework will call it when this method returns.  So, it is
+             * optional.
+             */
+            if (callAssertNothingThrown) {
+                if (requestHandler == null) {
+                    throw new TestingFrameworkException("requestHandler cannot be null");
+                }
+                requestHandler.assertNothingThrown();
+            }
+
+            return response;
+        } catch (TestingFrameworkException e) {
+            throw e;
+        } catch (Exception ex) {
+            throw new TestingFrameworkException(ex);
+        }
+    }
+
+    /**
+     * See the documentation for the same method in {@link ClientPOJOAdapter}.
+     *
+     * @param request
+     * @return
+     */
+    public boolean isRequestSupported(final Map<String, Object> request) {
+        return (adapter == null) ? true : adapter.checkRequestSupport(request) == null;
+    };
+
+    /**
+     * See the documentation for the same method in {@link ClientPOJOAdapter}.
+     *
+     * @param request
+     * @return
+     */
+    public Map<String, Object> modifyRequest(final Map<String, Object> request) {
+       return (adapter == null) ? request : adapter.modifyRequest(request);
+    };
+
+    /**
+     * Generally a test's response expectations should not need to be modified, but
+     * if a particular HTTP client (such as Groovy's RESTClient which uses HttpClient)
+     * needs to modify the response expectations, it should do so here.  After this
+     * method returns, the {@link TestingFrameworkRequestHandler} is sent the
+     * expectations so the request handler will return a response that matches the
+     * expectations.  When the HTTP response is obtained, the received response
+     * is matched against the expectations.
+     *
+     * @param request for the format, see the documentation for {@link ClientPOJOAdapter}.
+     * @param responseExpectations for the format, see the documentation for {@link ClientPOJOAdapter}.
+     * @return the same or modified response expectations.
+     */
+    public Map<String, Object> modifyResponseExpectations(final Map<String, Object> request,
+                                                          final Map<String, Object> responseExpectations) {
+        return responseExpectations;
+    }
+
+    /**
+     * Getter for the {@link ClientPOJOAdapter} that is actually used to make the
+     * HTTP request.
+     *
+     * @return the {@link ClientPOJOAdapter}.
+     */
+    public ClientPOJOAdapter getClientPOJOAdapter() {
+        return adapter;
+    }
+
+}

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientTestingAdapter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientTestingAdapter.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientTestingAdapter.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/FrameworkTest.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/FrameworkTest.java?rev=1763892&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/FrameworkTest.java (added)
+++ httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/FrameworkTest.java Sat Oct  8 11:40:09 2016
@@ -0,0 +1,172 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.testing.framework;
+
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.BODY;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.CONTENT_TYPE;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.HEADERS;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.METHOD;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.PATH;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.PROTOCOL_VERSION;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.QUERY;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.REQUEST;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.RESPONSE;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.STATUS;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.util.URLEncodedUtils;
+
+
+
+/**
+ * <p>This class is not expected to be used directly by the user, but its job is to
+ * supply helpful defaults for tests.</p>
+ *
+ * <p>A test is made up of an HTTP request that the HTTP client will send as well
+ * as a response that is expected.</p>
+ *
+ * <p>See {@link ClientPOJOAdapter} for details on the request and response.</p>
+ *
+ * <p>Generally, if the request does not specify a method, it is assumed to be a GET.
+ * There are also defaults for headers, query parameters, body, contentType, etc.</p>
+ *
+ * @since 5.0
+ */
+public class FrameworkTest {
+    private Map<String, Object> request = new HashMap<String, Object>();
+    private Map<String, Object> response = new HashMap<String, Object>();
+
+    /**
+     * Constructs a test with default values.
+     */
+    public FrameworkTest() {
+        this(null);
+    }
+
+    /**
+     * Constructs a test with values that are passed in as well as defaults
+     * for values that are not passed in.
+     *
+     * @param test Contains a REQUEST and an expected RESPONSE.
+     *             See {@link ClientPOJOAdapter} for details.
+     */
+    @SuppressWarnings("unchecked")
+    public FrameworkTest(final Map<String, Object> test) {
+        if (test != null) {
+            if (test.containsKey(REQUEST)) {
+                request = (Map<String, Object>) test.get(REQUEST);
+            }
+            if (test.containsKey(RESPONSE)) {
+                response = (Map<String, Object>) test.get(RESPONSE);
+            }
+        }
+    }
+
+    /**
+     * Returns a request with defaults for any parameter that is not specified.
+     *
+     * @return a REQUEST map.
+     * @throws TestingFrameworkException a problem such as an invalid URL
+     */
+    public Map<String, Object> initRequest() throws TestingFrameworkException {
+        // initialize to some helpful defaults
+        final Map<String, Object> ret = new HashMap<String, Object>();
+        ret.put(PATH, TestingFramework.DEFAULT_REQUEST_PATH);
+        ret.put(BODY, TestingFramework.DEFAULT_REQUEST_BODY);
+        ret.put(CONTENT_TYPE, TestingFramework.DEFAULT_REQUEST_CONTENT_TYPE);
+        ret.put(QUERY, new HashMap<String, String>(TestingFramework.DEFAULT_REQUEST_QUERY));
+        ret.put(HEADERS, new HashMap<String, String>(TestingFramework.DEFAULT_REQUEST_HEADERS));
+        ret.put(PROTOCOL_VERSION, TestingFramework.DEFAULT_REQUEST_PROTOCOL_VERSION);
+
+        // GET is the default method.
+        if (! request.containsKey(METHOD)) {
+            request.put(METHOD, "GET");
+        }
+        ret.putAll(request);
+
+        moveAnyParametersInPathToQuery(ret);
+
+        return ret;
+    }
+
+    private void moveAnyParametersInPathToQuery(final Map<String, Object> request) throws TestingFrameworkException {
+        try {
+            final String path = (String) request.get(PATH);
+            if (path != null) {
+                final URI uri = path.startsWith("/") ? new URI("http://localhost:8080" + path) :
+                                                 new URI("http://localhost:8080/");
+                final List<NameValuePair> params = URLEncodedUtils.parse(uri, StandardCharsets.UTF_8);
+                @SuppressWarnings("unchecked")
+                final Map<String, Object> queryMap = (Map<String, Object>) request.get(QUERY);
+                for (NameValuePair param : params) {
+                    queryMap.put(param.getName(), param.getValue());
+                }
+                if (! params.isEmpty()) {
+                    request.put(PATH, uri.getPath());
+                }
+            }
+        } catch (URISyntaxException e) {
+            throw new TestingFrameworkException(e);
+        }
+    }
+
+    /**
+     * Returns an expected response with defaults for any parameter that is not specified.
+     *
+     * @return the RESPONSE map.
+     */
+    public Map<String, Object> initResponseExpectations() {
+        // 200 is the default status.
+        if (! response.containsKey(STATUS)) {
+            response.put(STATUS, 200);
+        }
+
+        final Map<String, Object> responseExpectations = new HashMap<String, Object>();
+        // initialize to some helpful defaults
+        responseExpectations.put(BODY, TestingFramework.DEFAULT_RESPONSE_BODY);
+        responseExpectations.put(CONTENT_TYPE, TestingFramework.DEFAULT_RESPONSE_CONTENT_TYPE);
+        responseExpectations.put(HEADERS, new HashMap<String, String>(TestingFramework.DEFAULT_RESPONSE_HEADERS));
+
+        // Now override any defaults with what is requested.
+        responseExpectations.putAll(response);
+
+        return responseExpectations;
+    }
+
+    @Override
+    public String toString() {
+        return "request: " + request + "\nresponse: " + response;
+    }
+}

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/FrameworkTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/FrameworkTest.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/FrameworkTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFramework.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFramework.java?rev=1763892&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFramework.java (added)
+++ httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFramework.java Sat Oct  8 11:40:09 2016
@@ -0,0 +1,492 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.testing.framework;
+
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.BODY;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.CONTENT_TYPE;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.HEADERS;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.METHOD;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.REQUEST;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.RESPONSE;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.STATUS;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.hc.core5.http.HttpVersion;
+import org.apache.hc.core5.http.ProtocolVersion;
+import org.apache.hc.core5.http.bootstrap.io.HttpServer;
+import org.apache.hc.core5.http.bootstrap.io.ServerBootstrap;
+import org.apache.hc.core5.http.config.SocketConfig;
+
+/**
+ * <p>This testing framework starts an in-process {@link HttpServer} which will use an
+ * {@link TestingFrameworkRequestHandler} to check HTTP requests that are sent
+ * to it.  Before the request is sent, the handler is told what request to expect.
+ * If the received request does not match the request expectations, an exception
+ * is thrown.</p>
+ *
+ * <p>The handler is also told what response to return.  This testing framework will
+ * then check the response it receives with what it desired.  If they do not
+ * match, an exception is thrown.</p>
+ *
+ * <p>This has been designed to work with any HTTP client.  So, for instance, Groovy's
+ * HttpBuilder or RESTClient which uses Apache HttpClient can also be tested with this
+ * testing framework.  A different {@link ClientTestingAdapter} is used with
+ * different HTTP clients.  If testing Apache HttpClient5, the {@link ClassicTestClientTestingAdapter}
+ * is used.  Since use of this testing framework with other projects is desired,
+ * the testframework package has been placed outside the test directory.  Care has
+ * been taken to make sure no testing dependency such as JUnit or EasyMock is used
+ * in the framework.</p>
+ *
+ * <p>The {@link ClassicTestClientTestingAdapter} that is used is either passed into the
+ * constructor or set with setAdapter().</p>
+ *
+ * <p>By default, this framework will go through a series of tests that will exercise
+ * all HTTP methods.  If the default tests are not desired, then the deleteTests()
+ * method can be called.  Then, custom tests can be added with the addTest() methods.
+ * Of course additional tests can be added with the addTest() method without first
+ * calling deleteTests().  In that case, the default tests and the additional tests
+ * will all run.</p>
+ *
+ * <p>Since this framework has been designed to be used with any HTTP client, the test
+ * is specified with POJO's such as Map, List, and primitives.  The test is a Map with
+ * two keys - request and response.  See {@link ClientPOJOAdapter} for details
+ * on the format of the request and response.</p>
+ *
+ * <p>Once any additional tests have been added, the runTests() method is called to
+ * actually do the testing.</p>
+ *
+ * @since 5.0
+ *
+ */
+public class TestingFramework {
+    /**
+     * Use the ALL_METHODS list to conveniently cycle through all HTTP methods.
+     */
+    public static final List<String> ALL_METHODS = Arrays.asList("HEAD", "GET", "DELETE", "POST", "PUT", "PATCH");
+
+    /**
+     * If an {@link ClassicTestClientTestingAdapter} is unable to return a response in
+     * the format this testing framework is needing, then it will need to check the
+     * item in the response (such as body, status, headers, or contentType) itself and set
+     * the returned value of the item as ALREADY_CHECKED.
+     */
+    public static final Object ALREADY_CHECKED = new Object();
+
+    /**
+     * If a test does not specify a path, this one is used.
+     */
+    public static final String DEFAULT_REQUEST_PATH = "a/path";
+
+    /**
+     * If a test does not specify a body, this one is used.
+     */
+    public static final String DEFAULT_REQUEST_BODY = "{\"location\":\"home\"}";
+
+    /**
+     * If a test does not specify a request contentType, this one is used.
+     */
+    public static final String DEFAULT_REQUEST_CONTENT_TYPE = "application/json";
+
+    /**
+     * If a test does not specify query parameters, these are used.
+     */
+    public static final Map<String, String> DEFAULT_REQUEST_QUERY;
+
+    /**
+     * If a test does not specify a request headers, these are used.
+     */
+    public static final Map<String, String> DEFAULT_REQUEST_HEADERS;
+
+    /**
+     * If a test does not specify a protocol version, this one is used.
+     */
+    public static final ProtocolVersion DEFAULT_REQUEST_PROTOCOL_VERSION = HttpVersion.HTTP_1_1;
+
+    /**
+     * If a test does not specify an expected response status, this one is used.
+     */
+    public static final int DEFAULT_RESPONSE_STATUS = 200;
+
+    /**
+     * If a test does not specify an expected response body, this one is used.
+     */
+    public static final String DEFAULT_RESPONSE_BODY = "{\"location\":\"work\"}";
+
+    /**
+     * If a test does not specify an expected response contentType, this one is used.
+     */
+    public static final String DEFAULT_RESPONSE_CONTENT_TYPE = "application/json";
+
+    /**
+     * If a test does not specify expected response headers, these are used.
+     */
+    public static final Map<String, String> DEFAULT_RESPONSE_HEADERS;
+
+    static {
+        final Map<String, String> request = new HashMap<String, String>();
+        request.put("p1", "this");
+        request.put("p2", "that");
+        DEFAULT_REQUEST_QUERY = Collections.unmodifiableMap(request);
+
+        Map<String, String> headers = new HashMap<String, String>();
+        headers.put("header1", "stuff");
+        headers.put("header2", "more stuff");
+        DEFAULT_REQUEST_HEADERS = Collections.unmodifiableMap(headers);
+
+        headers = new HashMap<String, String>();
+        headers.put("header3", "header_three");
+        headers.put("header4", "header_four");
+        DEFAULT_RESPONSE_HEADERS = Collections.unmodifiableMap(headers);
+    }
+
+    private ClientTestingAdapter adapter;
+    private TestingFrameworkRequestHandler requestHandler = new TestingFrameworkRequestHandler();
+    private List<FrameworkTest> tests = new ArrayList<FrameworkTest>();
+
+    private HttpServer server;
+    private int port;
+
+    public TestingFramework() throws TestingFrameworkException {
+        this(null);
+    }
+
+    public TestingFramework(final ClientTestingAdapter adapter) throws TestingFrameworkException {
+        this.adapter = adapter;
+
+        /*
+         * By default, a set of tests that will exercise each HTTP method are pre-loaded.
+         */
+        for (String method : ALL_METHODS) {
+            final List<Integer> statusList = Arrays.asList(200, 201);
+            for (Integer status : statusList) {
+                final Map<String, Object> request = new HashMap<String, Object>();
+                request.put(METHOD, method);
+
+                final Map<String, Object> response = new HashMap<String, Object>();
+                response.put(STATUS, status);
+
+                final Map<String, Object> test = new HashMap<String, Object>();
+                test.put(REQUEST, request);
+                test.put(RESPONSE, response);
+
+                addTest(test);
+            }
+        }
+    }
+
+    /**
+     * This is not likely to be used except during the testing of this class.
+     * It is used to inject a mocked request handler.
+     *
+     * @param requestHandler
+     */
+    public void setRequestHandler(final TestingFrameworkRequestHandler requestHandler) {
+        this.requestHandler = requestHandler;
+    }
+
+    /**
+     * Run the tests that have been previously added.  First, an in-process {@link HttpServer} is
+     * started.  Then, all the tests are completed by passing each test to the adapter
+     * which will make the HTTP request.
+     *
+     * @throws TestingFrameworkException if there is a test failure or unexpected problem.
+     */
+    public void runTests() throws TestingFrameworkException {
+        if (adapter == null) {
+            throw new TestingFrameworkException("adapter should not be null");
+        }
+
+        startServer();
+
+        try {
+            for (FrameworkTest test : tests) {
+                try {
+                    callAdapter(test);
+                } catch (Throwable t) {
+                    processThrowable(t, test);
+                }
+            }
+        } finally {
+            stopServer();
+        }
+    }
+
+    private void processThrowable(final Throwable t, final FrameworkTest test) throws TestingFrameworkException {
+        final TestingFrameworkException e;
+        if (t instanceof TestingFrameworkException) {
+            e = (TestingFrameworkException) t;
+        } else {
+            e = new TestingFrameworkException(t);
+        }
+        e.setAdapter(adapter);
+        e.setTest(test);
+        throw e;
+    }
+
+    private void startServer() throws TestingFrameworkException {
+        /*
+         * Start an in-process server and handle all HTTP requests
+         * with the requestHandler.
+         */
+        final SocketConfig socketConfig = SocketConfig.custom()
+                                          .setSoTimeout(15000)
+                                          .build();
+
+        final ServerBootstrap serverBootstrap = ServerBootstrap.bootstrap()
+                                          .setSocketConfig(socketConfig)
+                                          .registerHandler("/*", requestHandler);
+
+        server = serverBootstrap.create();
+        try {
+            server.start();
+        } catch (IOException e) {
+            throw new TestingFrameworkException(e);
+        }
+
+        port = server.getLocalPort();
+    }
+
+    private void stopServer() {
+        if (server != null) {
+            server.shutdown(0, TimeUnit.SECONDS);
+            server = null;
+        }
+    }
+
+    private void callAdapter(final FrameworkTest test) throws TestingFrameworkException {
+        Map<String, Object> request = test.initRequest();
+
+        /*
+         * If the adapter does not support the particular request, skip the test.
+         */
+        if (! adapter.isRequestSupported(request)) {
+            return;
+        }
+
+        /*
+         * Allow the adapter to modify the request before the request expectations
+         * are given to the requestHandler.  Typically, adapters should not have
+         * to modify the request.
+         */
+        request = adapter.modifyRequest(request);
+
+        // Tell the request handler what to expect in the request.
+        requestHandler.setRequestExpectations(request);
+
+        Map<String, Object> responseExpectations = test.initResponseExpectations();
+        /*
+         * Allow the adapter to modify the response expectations before the handler
+         * is told what to return.  Typically, adapters should not have to modify
+         * the response expectations.
+         */
+        responseExpectations = adapter.modifyResponseExpectations(request, responseExpectations);
+
+        // Tell the request handler what response to return.
+        requestHandler.setDesiredResponse(responseExpectations);
+
+        /*
+         * Use the adapter to make the HTTP call.  Make sure the responseExpectations are not changed
+         * since they have already been sent to the request handler and they will later be used
+         * to check the response.
+         */
+        final String defaultURI = getDefaultURI();
+        final Map<String, Object> response = adapter.execute(
+                                                defaultURI,
+                                                request,
+                                                requestHandler,
+                                                Collections.unmodifiableMap(responseExpectations));
+        /*
+         * The adapter is welcome to call assertNothingThrown() earlier, but we will
+         * do it here to make sure it is done.  If the handler threw any exception
+         * while checking the request it received, it will be re-thrown here.
+         */
+        requestHandler.assertNothingThrown();
+
+        assertResponseMatchesExpectation(request.get(METHOD), response, responseExpectations);
+    }
+
+    @SuppressWarnings("unchecked")
+    private void assertResponseMatchesExpectation(final Object method, final Map<String, Object> actualResponse,
+                                                  final Map<String, Object> expectedResponse)
+                                                  throws TestingFrameworkException {
+        if (actualResponse == null) {
+            throw new TestingFrameworkException("response should not be null");
+        }
+        /*
+         * Now check the items in the response unless the adapter says they
+         * already checked something.
+         */
+        if (actualResponse.get(STATUS) != TestingFramework.ALREADY_CHECKED) {
+            assertStatusMatchesExpectation(actualResponse.get(STATUS), expectedResponse.get(STATUS));
+        }
+        if (! method.equals("HEAD")) {
+            if (actualResponse.get(BODY) != TestingFramework.ALREADY_CHECKED) {
+                assertBodyMatchesExpectation(actualResponse.get(BODY), expectedResponse.get(BODY));
+            }
+            if (actualResponse.get(CONTENT_TYPE) != TestingFramework.ALREADY_CHECKED) {
+                assertContentTypeMatchesExpectation(actualResponse.get(CONTENT_TYPE), expectedResponse.get(CONTENT_TYPE));
+            }
+        }
+        if (actualResponse.get(HEADERS) != TestingFramework.ALREADY_CHECKED) {
+            assertHeadersMatchExpectation((Map<String, String>) actualResponse.get(HEADERS),
+                                          (Map<String, String>) expectedResponse.get(HEADERS));
+        }
+    }
+
+    private void assertStatusMatchesExpectation(final Object actualStatus, final Object expectedStatus)
+            throws TestingFrameworkException {
+        if (actualStatus == null) {
+            throw new TestingFrameworkException("Returned status is null.");
+        }
+        if ((expectedStatus != null) && (! actualStatus.equals(expectedStatus))) {
+            throw new TestingFrameworkException("Expected status not found. expected="
+                                                  + expectedStatus + "; actual=" + actualStatus);
+        }
+    }
+
+    private void assertBodyMatchesExpectation(final Object actualBody, final Object expectedBody)
+        throws TestingFrameworkException {
+        if (actualBody == null) {
+            throw new TestingFrameworkException("Returned body is null.");
+        }
+        if ((expectedBody != null) && (! actualBody.equals(expectedBody))) {
+            throw new TestingFrameworkException("Expected body not found. expected="
+                                    + expectedBody + "; actual=" + actualBody);
+        }
+    }
+
+    private void assertContentTypeMatchesExpectation(final Object actualContentType, final Object expectedContentType)
+        throws TestingFrameworkException {
+        if (expectedContentType != null) {
+            if (actualContentType == null) {
+                throw new TestingFrameworkException("Returned contentType is null.");
+            }
+            if (! actualContentType.equals(expectedContentType)) {
+                throw new TestingFrameworkException("Expected content type not found.  expected="
+                                    + expectedContentType + "; actual=" + actualContentType);
+            }
+        }
+    }
+
+    private void assertHeadersMatchExpectation(final Map<String, String> actualHeaders,
+                                               final Map<String, String>  expectedHeaders)
+            throws TestingFrameworkException {
+        if (expectedHeaders == null) {
+            return;
+        }
+        for (Map.Entry<String, String> expectedHeader : ((Map<String, String>) expectedHeaders).entrySet()) {
+            final String expectedHeaderName = expectedHeader.getKey();
+            if (! actualHeaders.containsKey(expectedHeaderName)) {
+                throw new TestingFrameworkException("Expected header not found: name=" + expectedHeaderName);
+            }
+            if (! actualHeaders.get(expectedHeaderName).equals(expectedHeaders.get(expectedHeaderName))) {
+                throw new TestingFrameworkException("Header value not expected: name=" + expectedHeaderName
+                        + "; expected=" + expectedHeaders.get(expectedHeaderName)
+                        + "; actual=" + actualHeaders.get(expectedHeaderName));
+            }
+        }
+    }
+
+    private String getDefaultURI() {
+        return "http://localhost:" + port  + "/";
+    }
+
+    /**
+     * Sets the {@link ClientTestingAdapter}.
+     *
+     * @param adapter
+     */
+    public void setAdapter(final ClientTestingAdapter adapter) {
+        this.adapter = adapter;
+    }
+
+    /**
+     * Deletes all tests.
+     */
+    public void deleteTests() {
+        tests = new ArrayList<FrameworkTest>();
+    }
+
+    /**
+     * Call to add a test with defaults.
+     *
+     * @throws TestingFrameworkException
+     */
+    public void addTest() throws TestingFrameworkException {
+        addTest(null);
+    }
+
+    /**
+     * Call to add a test.  The test is a map with a REQUEST and a RESPONSE key.
+     * See {@link ClientPOJOAdapter} for details on the format of the request and response.
+     *
+     * @param test Map with a REQUEST and a RESPONSE key.
+     * @throws TestingFrameworkException
+     */
+    @SuppressWarnings("unchecked")
+    public void addTest(final Map<String, Object> test) throws TestingFrameworkException {
+        final Map<String, Object> testCopy = (Map<String, Object>) deepcopy(test);
+
+        tests.add(new FrameworkTest(testCopy));
+    }
+
+    /**
+     * Used to make a "deep" copy of an object.  This testing framework makes deep copies
+     * of tests that are added as well as requestExpectations Maps and response Maps.
+     *
+     * @param orig a serializable object.
+     * @return a deep copy of the orig object.
+     * @throws TestingFrameworkException
+     */
+    public static Object deepcopy(final Object orig) throws TestingFrameworkException {
+        try {
+            // this is from http://stackoverflow.com/questions/13155127/deep-copy-map-in-groovy
+            final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            final ObjectOutputStream oos = new ObjectOutputStream(bos);
+            oos.writeObject(orig);
+            oos.flush();
+            final ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray());
+            final ObjectInputStream ois = new ObjectInputStream(bin);
+            return ois.readObject();
+        } catch (ClassNotFoundException | IOException e) {
+            throw new TestingFrameworkException(e);
+        }
+    }
+}

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFramework.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFramework.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFramework.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkException.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkException.java?rev=1763892&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkException.java (added)
+++ httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkException.java Sat Oct  8 11:40:09 2016
@@ -0,0 +1,91 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.testing.framework;
+
+
+/**
+ * <p>Signals a problem or an assertion failure while using the {@link TestingFramework}.</p>
+ *
+ * <p>Optionally, an adapter and a test can be added to the exception.  If this is done,
+ * the adapter name and the test information is appended to the exception message to help
+ * determine what test is having a problem.</p>
+ *
+ * @since 5.0
+ */
+public class TestingFrameworkException extends Exception {
+    public static final String NO_HTTP_CLIENT = "none";
+
+    private ClientTestingAdapter adapter;
+
+    private FrameworkTest test;
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = -1010516169283589675L;
+
+    /**
+     * Creates a WebServerTestingFrameworkException with the specified detail message.
+     */
+    public TestingFrameworkException(final String message) {
+        super(message);
+    }
+
+    public TestingFrameworkException(final Throwable cause) {
+        super(cause);
+    }
+
+    @Override
+    public String getMessage() {
+        String message = super.getMessage();
+        if (adapter != null) {
+            final ClientPOJOAdapter pojoAdapter = adapter.getClientPOJOAdapter();
+            final String tempHttpClient = pojoAdapter == null ? null : pojoAdapter.getClientName();
+            final String httpClient = tempHttpClient == null ? NO_HTTP_CLIENT : tempHttpClient;
+            if (message == null) {
+                message = "null";
+            }
+            message += "\nHTTP Client=" + httpClient;
+        }
+        if (test != null) {
+            if (message == null) {
+                message = "null";
+            }
+            message += "\ntest:\n" + test;
+        }
+        return message;
+    }
+
+    public void setAdapter(final ClientTestingAdapter adapter) {
+        this.adapter = adapter;
+    }
+
+    public void setTest(final FrameworkTest test) {
+        this.test = test;
+    }
+}

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkException.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkRequestHandler.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkRequestHandler.java?rev=1763892&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkRequestHandler.java (added)
+++ httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkRequestHandler.java Sat Oct  8 11:40:09 2016
@@ -0,0 +1,267 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.testing.framework;
+
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.BODY;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.CONTENT_TYPE;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.HEADERS;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.METHOD;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.PROTOCOL_VERSION;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.QUERY;
+import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.STATUS;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.ProtocolVersion;
+import org.apache.hc.core5.http.entity.ContentType;
+import org.apache.hc.core5.http.entity.EntityUtils;
+import org.apache.hc.core5.http.entity.StringEntity;
+import org.apache.hc.core5.http.io.HttpRequestHandler;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.util.URLEncodedUtils;
+
+/**
+ * <p>This request handler is used with an in-process instance of HttpServer during testing.
+ * The handler is told what to expect in the request.  If the request does not match
+ * the expectations, the handler will throw an exception which is then caught and
+ * saved in the "thrown" member.  The testing framework will later call assertNothingThrown().
+ * If something was thrown earlier by the handler, an exception will be thrown by the method.</p>
+ *
+ * <p>The handler is also told what response to return.</p>
+ *
+ * <p>See {@link ClientPOJOAdapter} for details on the format of the request and response.</p>
+ *
+ * @since 5.0
+ *
+ */
+public class TestingFrameworkRequestHandler implements HttpRequestHandler {
+    protected Throwable thrown;
+    protected Map<String, Object> requestExpectations;
+    protected Map<String, Object> desiredResponse;
+
+    /**
+     * Sets the request expectations.
+     *
+     * @param requestExpectations the expected values of the request.
+     * @throws TestingFrameworkException
+     */
+    @SuppressWarnings("unchecked")
+    public void setRequestExpectations(final Map<String, Object> requestExpectations) throws TestingFrameworkException {
+        this.requestExpectations = (Map<String, Object>) TestingFramework.deepcopy(requestExpectations);
+    }
+
+    /**
+     * Sets the desired response.  The handler will return a response that matches this.
+     *
+     * @param desiredResponse the desired response.
+     * @throws TestingFrameworkException
+     */
+    @SuppressWarnings("unchecked")
+    public void setDesiredResponse(final Map<String, Object> desiredResponse) throws TestingFrameworkException {
+        this.desiredResponse = (Map<String, Object>) TestingFramework.deepcopy(desiredResponse);
+    }
+
+    /**
+     * After the handler returns the response, any exception or failed assertion will be
+     * in the member called "thrown".  A testing framework can later call this method
+     * which will rethrow the exception that was thrown before.
+     *
+     * @throws TestingFrameworkException
+     */
+    public void assertNothingThrown() throws TestingFrameworkException {
+        if (thrown != null) {
+            final TestingFrameworkException e = (thrown instanceof TestingFrameworkException ?
+                                                          (TestingFrameworkException) thrown :
+                                                          new TestingFrameworkException(thrown));
+            thrown = null;
+            throw e;
+        }
+    }
+
+    /**
+     * <p>Checks the HTTP request against the requestExpectations that it was previously given.
+     * If there is a mismatch, an exception will be saved in the "thrown" member.</p>
+     *
+     * <p>Also, a response will be returned that matches the desiredResponse.</p>
+     */
+    @Override
+    public void handle(final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context)
+            throws HttpException, IOException {
+
+        try {
+            /*
+             * Check the method against the method in the requestExpectations.
+             */
+            final String actualMethod = request.getMethod();
+            final String expectedMethod = (String) requestExpectations.get(METHOD);
+            if (! actualMethod.equals(expectedMethod)) {
+                throw new TestingFrameworkException("Method not expected. " +
+                    " expected=" + expectedMethod + "; actual=" + actualMethod);
+            }
+
+            /*
+             * Set the status to the status that is in the desiredResponse.
+             */
+            final Object desiredStatus = desiredResponse.get(STATUS);
+            if (desiredStatus != null) {
+                response.setCode((int) desiredStatus);
+            }
+
+            /*
+             * Check the query parameters against the parameters in requestExpectations.
+             */
+            @SuppressWarnings("unchecked")
+            final Map<String, String> expectedQuery = (Map<String, String>) requestExpectations.get(QUERY);
+            if (expectedQuery != null) {
+                final URI uri = request.getUri();
+                final List<NameValuePair> actualParams = URLEncodedUtils.parse(uri, StandardCharsets.UTF_8);
+                final Map<String, String> actualParamsMap = new HashMap<String, String>();
+                for (NameValuePair actualParam : actualParams) {
+                    actualParamsMap.put(actualParam.getName(), actualParam.getValue());
+                }
+                for (Map.Entry<String, String> expectedParam : expectedQuery.entrySet()) {
+                    final String key = expectedParam.getKey();
+                    if (! actualParamsMap.containsKey(key)) {
+                        throw new TestingFrameworkException("Expected parameter not found: " + key);
+                    }
+                    final String actualParamValue = actualParamsMap.get(key);
+                    final String expectedParamValue = expectedParam.getValue();
+                    if (! actualParamValue.equals(expectedParamValue)) {
+                        throw new TestingFrameworkException("Expected parameter value not found. " +
+                            " Parameter=" + key + "; expected=" + expectedParamValue + "; actual=" + actualParamValue);
+                    }
+                }
+            }
+
+            /*
+             * Check the headers against the headers in requestExpectations.
+             */
+            @SuppressWarnings("unchecked")
+            final Map<String, String> expectedHeaders = (Map<String, String>) requestExpectations.get(HEADERS);
+            if (expectedHeaders != null) {
+                final Map<String, String> actualHeadersMap = new HashMap<String, String>();
+                final Header[] actualHeaders = request.getAllHeaders();
+                for (Header header : actualHeaders) {
+                    actualHeadersMap.put(header.getName(), header.getValue());
+                }
+                for (Entry<String, String> expectedHeader : expectedHeaders.entrySet()) {
+                    final String key = expectedHeader.getKey();
+                    if (! actualHeadersMap.containsKey(key)) {
+                        throw new TestingFrameworkException("Expected header not found: " + key);
+                    }
+                    final String actualHeaderValue = actualHeadersMap.get(key);
+                    final String expectedHeaderValue = expectedHeader.getValue();
+                    if (! actualHeaderValue.equals(expectedHeaderValue)) {
+                        throw new TestingFrameworkException("Expected header value not found. " +
+                                " Name=" + key + "; expected=" + expectedHeaderValue + "; actual=" + actualHeaderValue);
+                    }
+                }
+            }
+
+            /*
+             * Check the body.
+             */
+            final String expectedBody = (String) requestExpectations.get(BODY);
+            if (expectedBody != null) {
+                final HttpEntity entity = request.getEntity();
+                final String data = EntityUtils.toString(entity);
+                if (! data.equals(expectedBody)) {
+                    throw new TestingFrameworkException("Expected body not found. " +
+                            " Body=" + data + "; expected=" + expectedBody);
+                }
+            }
+
+            /*
+             * Check the contentType of the request.
+             */
+            final String requestContentType = (String) requestExpectations.get(CONTENT_TYPE);
+            if (requestContentType != null) {
+                final HttpEntity entity = request.getEntity();
+                final String contentType = entity.getContentType();
+                final String expectedContentType = (String) requestExpectations.get(CONTENT_TYPE);
+                if (! contentType.equals(expectedContentType)) {
+                    throw new TestingFrameworkException("Expected request content type not found. " +
+                            " Content Type=" + contentType + "; expected=" + expectedContentType);
+                }
+            }
+
+            /*
+             * Check the protocolVersion.
+             */
+            if (requestExpectations.containsKey(PROTOCOL_VERSION)) {
+                final ProtocolVersion protocolVersion = request.getVersion();
+                final ProtocolVersion expectedProtocolVersion = (ProtocolVersion) requestExpectations.get(PROTOCOL_VERSION);
+                if (! protocolVersion.equals(expectedProtocolVersion)) {
+                    throw new TestingFrameworkException("Expected request protocol version not found. " +
+                            " Protocol Version=" + protocolVersion + "; expected=" + expectedProtocolVersion);
+                }
+            }
+
+            /*
+             * Return the body in desiredResponse using the contentType in desiredResponse.
+             */
+            final String desiredBody = (String) desiredResponse.get(BODY);
+            if (desiredBody != null) {
+                final String desiredContentType = (String) desiredResponse.get(CONTENT_TYPE);
+                final StringEntity entity = desiredContentType != null ?
+                                new StringEntity(desiredBody, ContentType.parse(desiredContentType)) :
+                                new StringEntity(desiredBody);
+                response.setEntity(entity);
+            }
+
+            /*
+             * Return the headers in desiredResponse.
+             */
+            @SuppressWarnings("unchecked")
+            final Map<String, String> desiredHeaders = (Map<String, String>) desiredResponse.get(HEADERS);
+            if (desiredHeaders != null) {
+                for (Entry<String, String> entry : desiredHeaders.entrySet()) {
+                    response.setHeader(entry.getKey(), entry.getValue());
+                }
+            }
+
+        } catch (Throwable t) {
+            /*
+             * Save the throwable to be later retrieved by a call to assertNothingThrown().
+             */
+            thrown = t;
+        }
+    }
+}

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkRequestHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkRequestHandler.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkRequestHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain