View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.core5.testing.framework;
29  
30  import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.BODY;
31  import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.CONTENT_TYPE;
32  import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.HEADERS;
33  import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.METHOD;
34  import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.PROTOCOL_VERSION;
35  import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.QUERY;
36  import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.STATUS;
37  
38  import java.io.IOException;
39  import java.net.URI;
40  import java.nio.charset.StandardCharsets;
41  import java.util.HashMap;
42  import java.util.List;
43  import java.util.Map;
44  import java.util.Map.Entry;
45  
46  import org.apache.hc.core5.http.ClassicHttpRequest;
47  import org.apache.hc.core5.http.ClassicHttpResponse;
48  import org.apache.hc.core5.http.ContentType;
49  import org.apache.hc.core5.http.Header;
50  import org.apache.hc.core5.http.HttpEntity;
51  import org.apache.hc.core5.http.HttpException;
52  import org.apache.hc.core5.http.NameValuePair;
53  import org.apache.hc.core5.http.ProtocolVersion;
54  import org.apache.hc.core5.http.io.HttpRequestHandler;
55  import org.apache.hc.core5.http.io.entity.EntityUtils;
56  import org.apache.hc.core5.http.io.entity.StringEntity;
57  import org.apache.hc.core5.http.protocol.HttpContext;
58  import org.apache.hc.core5.net.URIBuilder;
59  
60  public class TestingFrameworkRequestHandler implements HttpRequestHandler {
61      protected Throwable thrown;
62      protected Map<String, Object> requestExpectations;
63      protected Map<String, Object> desiredResponse;
64  
65      /**
66       * Sets the request expectations.
67       *
68       * @param requestExpectations the expected values of the request.
69       * @throws TestingFrameworkException
70       */
71      @SuppressWarnings("unchecked")
72      public void setRequestExpectations(final Map<String, Object> requestExpectations) throws TestingFrameworkException {
73          this.requestExpectations = (Map<String, Object>) TestingFramework.deepcopy(requestExpectations);
74      }
75  
76      /**
77       * Sets the desired response.  The handler will return a response that matches this.
78       *
79       * @param desiredResponse the desired response.
80       * @throws TestingFrameworkException
81       */
82      @SuppressWarnings("unchecked")
83      public void setDesiredResponse(final Map<String, Object> desiredResponse) throws TestingFrameworkException {
84          this.desiredResponse = (Map<String, Object>) TestingFramework.deepcopy(desiredResponse);
85      }
86  
87      /**
88       * After the handler returns the response, any exception or failed assertion will be
89       * in the member called "thrown".  A testing framework can later call this method
90       * which will rethrow the exception that was thrown before.
91       *
92       * @throws TestingFrameworkException
93       */
94      public void assertNothingThrown() throws TestingFrameworkException {
95          if (thrown != null) {
96              final TestingFrameworkExceptioncore5/testing/framework/TestingFrameworkException.html#TestingFrameworkException">TestingFrameworkException e = (thrown instanceof TestingFrameworkException ?
97                                                            (TestingFrameworkException) thrown :
98                                                            new TestingFrameworkException(thrown));
99              thrown = null;
100             throw e;
101         }
102     }
103 
104     /**
105      * <p>Checks the HTTP request against the requestExpectations that it was previously given.
106      * If there is a mismatch, an exception will be saved in the "thrown" member.</p>
107      *
108      * <p>Also, a response will be returned that matches the desiredResponse.</p>
109      */
110     @Override
111     public void handle(final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context)
112             throws HttpException, IOException {
113 
114         try {
115             /*
116              * Check the method against the method in the requestExpectations.
117              */
118             final String actualMethod = request.getMethod();
119             final String expectedMethod = (String) requestExpectations.get(METHOD);
120             if (! actualMethod.equals(expectedMethod)) {
121                 throw new TestingFrameworkException("Method not expected. " +
122                     " expected=" + expectedMethod + "; actual=" + actualMethod);
123             }
124 
125             /*
126              * Set the status to the status that is in the desiredResponse.
127              */
128             final Object desiredStatus = desiredResponse.get(STATUS);
129             if (desiredStatus != null) {
130                 response.setCode((int) desiredStatus);
131             }
132 
133             /*
134              * Check the query parameters against the parameters in requestExpectations.
135              */
136             @SuppressWarnings("unchecked")
137             final Map<String, String> expectedQuery = (Map<String, String>) requestExpectations.get(QUERY);
138             if (expectedQuery != null) {
139                 final URI uri = request.getUri();
140                 final URIBuilderlder.html#URIBuilder">URIBuilder uriBuilder = new URIBuilder(uri, StandardCharsets.UTF_8);
141                 final List<NameValuePair> actualParams = uriBuilder.getQueryParams();
142                 final Map<String, String> actualParamsMap = new HashMap<>();
143                 for (final NameValuePair actualParam : actualParams) {
144                     actualParamsMap.put(actualParam.getName(), actualParam.getValue());
145                 }
146                 for (final Map.Entry<String, String> expectedParam : expectedQuery.entrySet()) {
147                     final String key = expectedParam.getKey();
148                     if (! actualParamsMap.containsKey(key)) {
149                         throw new TestingFrameworkException("Expected parameter not found: " + key);
150                     }
151                     final String actualParamValue = actualParamsMap.get(key);
152                     final String expectedParamValue = expectedParam.getValue();
153                     if (! actualParamValue.equals(expectedParamValue)) {
154                         throw new TestingFrameworkException("Expected parameter value not found. " +
155                             " Parameter=" + key + "; expected=" + expectedParamValue + "; actual=" + actualParamValue);
156                     }
157                 }
158             }
159 
160             /*
161              * Check the headers against the headers in requestExpectations.
162              */
163             @SuppressWarnings("unchecked")
164             final Map<String, String> expectedHeaders = (Map<String, String>) requestExpectations.get(HEADERS);
165             if (expectedHeaders != null) {
166                 final Map<String, String> actualHeadersMap = new HashMap<>();
167                 final Header[] actualHeaders = request.getHeaders();
168                 for (final Header header : actualHeaders) {
169                     actualHeadersMap.put(header.getName(), header.getValue());
170                 }
171                 for (final Entry<String, String> expectedHeader : expectedHeaders.entrySet()) {
172                     final String key = expectedHeader.getKey();
173                     if (! actualHeadersMap.containsKey(key)) {
174                         throw new TestingFrameworkException("Expected header not found: " + key);
175                     }
176                     final String actualHeaderValue = actualHeadersMap.get(key);
177                     final String expectedHeaderValue = expectedHeader.getValue();
178                     if (! actualHeaderValue.equals(expectedHeaderValue)) {
179                         throw new TestingFrameworkException("Expected header value not found. " +
180                                 " Name=" + key + "; expected=" + expectedHeaderValue + "; actual=" + actualHeaderValue);
181                     }
182                 }
183             }
184 
185             /*
186              * Check the body.
187              */
188             final String expectedBody = (String) requestExpectations.get(BODY);
189             if (expectedBody != null) {
190                 final HttpEntity entity = request.getEntity();
191                 final String data = EntityUtils.toString(entity);
192                 if (! data.equals(expectedBody)) {
193                     throw new TestingFrameworkException("Expected body not found. " +
194                             " Body=" + data + "; expected=" + expectedBody);
195                 }
196             }
197 
198             /*
199              * Check the contentType of the request.
200              */
201             final String requestContentType = (String) requestExpectations.get(CONTENT_TYPE);
202             if (requestContentType != null) {
203                 final HttpEntity entity = request.getEntity();
204                 final String contentType = entity.getContentType();
205                 final String expectedContentType = (String) requestExpectations.get(CONTENT_TYPE);
206                 if (! contentType.equals(expectedContentType)) {
207                     throw new TestingFrameworkException("Expected request content type not found. " +
208                             " Content Type=" + contentType + "; expected=" + expectedContentType);
209                 }
210             }
211 
212             /*
213              * Check the protocolVersion.
214              */
215             if (requestExpectations.containsKey(PROTOCOL_VERSION)) {
216                 final ProtocolVersion protocolVersion = request.getVersion();
217                 final ProtocolVersionhe/hc/core5/http/ProtocolVersion.html#ProtocolVersion">ProtocolVersion expectedProtocolVersion = (ProtocolVersion) requestExpectations.get(PROTOCOL_VERSION);
218                 if (! protocolVersion.equals(expectedProtocolVersion)) {
219                     throw new TestingFrameworkException("Expected request protocol version not found. " +
220                             " Protocol Version=" + protocolVersion + "; expected=" + expectedProtocolVersion);
221                 }
222             }
223 
224             /*
225              * Return the body in desiredResponse using the contentType in desiredResponse.
226              */
227             final String desiredBody = (String) desiredResponse.get(BODY);
228             if (desiredBody != null) {
229                 final String desiredContentType = (String) desiredResponse.get(CONTENT_TYPE);
230                 final StringEntity entity = desiredContentType != null ?
231                                 new StringEntity(desiredBody, ContentType.parse(desiredContentType)) :
232                                 new StringEntity(desiredBody);
233                 response.setEntity(entity);
234             }
235 
236             /*
237              * Return the headers in desiredResponse.
238              */
239             @SuppressWarnings("unchecked")
240             final Map<String, String> desiredHeaders = (Map<String, String>) desiredResponse.get(HEADERS);
241             if (desiredHeaders != null) {
242                 for (final Entry<String, String> entry : desiredHeaders.entrySet()) {
243                     response.setHeader(entry.getKey(), entry.getValue());
244                 }
245             }
246 
247         } catch (final Throwable t) {
248             /*
249              * Save the throwable to be later retrieved by a call to assertNothingThrown().
250              */
251             thrown = t;
252         }
253     }
254 }