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.Header;
49  import org.apache.hc.core5.http.HttpEntity;
50  import org.apache.hc.core5.http.HttpException;
51  import org.apache.hc.core5.http.NameValuePair;
52  import org.apache.hc.core5.http.ProtocolVersion;
53  import org.apache.hc.core5.http.io.HttpRequestHandler;
54  import org.apache.hc.core5.http.ContentType;
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.URLEncodedUtils;
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 List<NameValuePair> actualParams = URLEncodedUtils.parse(uri, StandardCharsets.UTF_8);
141                 final Map<String, String> actualParamsMap = new HashMap<>();
142                 for (final NameValuePair actualParam : actualParams) {
143                     actualParamsMap.put(actualParam.getName(), actualParam.getValue());
144                 }
145                 for (final Map.Entry<String, String> expectedParam : expectedQuery.entrySet()) {
146                     final String key = expectedParam.getKey();
147                     if (! actualParamsMap.containsKey(key)) {
148                         throw new TestingFrameworkException("Expected parameter not found: " + key);
149                     }
150                     final String actualParamValue = actualParamsMap.get(key);
151                     final String expectedParamValue = expectedParam.getValue();
152                     if (! actualParamValue.equals(expectedParamValue)) {
153                         throw new TestingFrameworkException("Expected parameter value not found. " +
154                             " Parameter=" + key + "; expected=" + expectedParamValue + "; actual=" + actualParamValue);
155                     }
156                 }
157             }
158 
159             /*
160              * Check the headers against the headers in requestExpectations.
161              */
162             @SuppressWarnings("unchecked")
163             final Map<String, String> expectedHeaders = (Map<String, String>) requestExpectations.get(HEADERS);
164             if (expectedHeaders != null) {
165                 final Map<String, String> actualHeadersMap = new HashMap<>();
166                 final Header[] actualHeaders = request.getHeaders();
167                 for (final Header header : actualHeaders) {
168                     actualHeadersMap.put(header.getName(), header.getValue());
169                 }
170                 for (final Entry<String, String> expectedHeader : expectedHeaders.entrySet()) {
171                     final String key = expectedHeader.getKey();
172                     if (! actualHeadersMap.containsKey(key)) {
173                         throw new TestingFrameworkException("Expected header not found: " + key);
174                     }
175                     final String actualHeaderValue = actualHeadersMap.get(key);
176                     final String expectedHeaderValue = expectedHeader.getValue();
177                     if (! actualHeaderValue.equals(expectedHeaderValue)) {
178                         throw new TestingFrameworkException("Expected header value not found. " +
179                                 " Name=" + key + "; expected=" + expectedHeaderValue + "; actual=" + actualHeaderValue);
180                     }
181                 }
182             }
183 
184             /*
185              * Check the body.
186              */
187             final String expectedBody = (String) requestExpectations.get(BODY);
188             if (expectedBody != null) {
189                 final HttpEntity entity = request.getEntity();
190                 final String data = EntityUtils.toString(entity);
191                 if (! data.equals(expectedBody)) {
192                     throw new TestingFrameworkException("Expected body not found. " +
193                             " Body=" + data + "; expected=" + expectedBody);
194                 }
195             }
196 
197             /*
198              * Check the contentType of the request.
199              */
200             final String requestContentType = (String) requestExpectations.get(CONTENT_TYPE);
201             if (requestContentType != null) {
202                 final HttpEntity entity = request.getEntity();
203                 final String contentType = entity.getContentType();
204                 final String expectedContentType = (String) requestExpectations.get(CONTENT_TYPE);
205                 if (! contentType.equals(expectedContentType)) {
206                     throw new TestingFrameworkException("Expected request content type not found. " +
207                             " Content Type=" + contentType + "; expected=" + expectedContentType);
208                 }
209             }
210 
211             /*
212              * Check the protocolVersion.
213              */
214             if (requestExpectations.containsKey(PROTOCOL_VERSION)) {
215                 final ProtocolVersion protocolVersion = request.getVersion();
216                 final ProtocolVersionhe/hc/core5/http/ProtocolVersion.html#ProtocolVersion">ProtocolVersion expectedProtocolVersion = (ProtocolVersion) requestExpectations.get(PROTOCOL_VERSION);
217                 if (! protocolVersion.equals(expectedProtocolVersion)) {
218                     throw new TestingFrameworkException("Expected request protocol version not found. " +
219                             " Protocol Version=" + protocolVersion + "; expected=" + expectedProtocolVersion);
220                 }
221             }
222 
223             /*
224              * Return the body in desiredResponse using the contentType in desiredResponse.
225              */
226             final String desiredBody = (String) desiredResponse.get(BODY);
227             if (desiredBody != null) {
228                 final String desiredContentType = (String) desiredResponse.get(CONTENT_TYPE);
229                 final StringEntity entity = desiredContentType != null ?
230                                 new StringEntity(desiredBody, ContentType.parse(desiredContentType)) :
231                                 new StringEntity(desiredBody);
232                 response.setEntity(entity);
233             }
234 
235             /*
236              * Return the headers in desiredResponse.
237              */
238             @SuppressWarnings("unchecked")
239             final Map<String, String> desiredHeaders = (Map<String, String>) desiredResponse.get(HEADERS);
240             if (desiredHeaders != null) {
241                 for (final Entry<String, String> entry : desiredHeaders.entrySet()) {
242                     response.setHeader(entry.getKey(), entry.getValue());
243                 }
244             }
245 
246         } catch (final Throwable t) {
247             /*
248              * Save the throwable to be later retrieved by a call to assertNothingThrown().
249              */
250             thrown = t;
251         }
252     }
253 }