View Javadoc
1   package com.atlassian.sal.core.net;
2   
3   import com.atlassian.sal.api.net.Request;
4   import com.atlassian.sal.api.net.ResponseException;
5   import com.google.common.base.Throwables;
6   import org.apache.commons.codec.binary.Base64;
7   import org.apache.http.Header;
8   import org.apache.http.HttpClientConnection;
9   import org.apache.http.HttpEntityEnclosingRequest;
10  import org.apache.http.HttpException;
11  import org.apache.http.HttpHost;
12  import org.apache.http.HttpRequest;
13  import org.apache.http.HttpResponse;
14  import org.apache.http.HttpStatus;
15  import org.apache.http.NameValuePair;
16  import org.apache.http.ProtocolVersion;
17  import org.apache.http.auth.AUTH;
18  import org.apache.http.client.RedirectException;
19  import org.apache.http.client.config.AuthSchemes;
20  import org.apache.http.client.utils.URLEncodedUtils;
21  import org.apache.http.conn.ConnectionRequest;
22  import org.apache.http.conn.HttpClientConnectionManager;
23  import org.apache.http.conn.routing.HttpRoute;
24  import org.apache.http.entity.StringEntity;
25  import org.apache.http.message.BasicHttpResponse;
26  import org.apache.http.message.BasicNameValuePair;
27  import org.apache.http.protocol.HttpContext;
28  import org.apache.http.protocol.HttpRequestExecutor;
29  import org.hamcrest.FeatureMatcher;
30  import org.hamcrest.Matcher;
31  import org.hamcrest.Matchers;
32  import org.junit.Before;
33  import org.junit.Rule;
34  import org.junit.Test;
35  import org.junit.contrib.java.lang.system.RestoreSystemProperties;
36  import org.junit.rules.ExpectedException;
37  import org.junit.runner.RunWith;
38  import org.mockito.ArgumentCaptor;
39  import org.mockito.Mock;
40  import org.mockito.invocation.InvocationOnMock;
41  import org.mockito.junit.MockitoJUnitRunner;
42  import org.mockito.stubbing.Answer;
43  
44  import java.io.IOException;
45  import java.net.URI;
46  import java.nio.charset.StandardCharsets;
47  import java.text.MessageFormat;
48  import java.util.List;
49  import java.util.concurrent.ExecutionException;
50  import java.util.concurrent.TimeUnit;
51  
52  import static org.hamcrest.CoreMatchers.instanceOf;
53  import static org.hamcrest.CoreMatchers.is;
54  import static org.hamcrest.CoreMatchers.notNullValue;
55  import static org.hamcrest.MatcherAssert.assertThat;
56  import static org.hamcrest.Matchers.arrayContaining;
57  import static org.hamcrest.Matchers.contains;
58  import static org.hamcrest.Matchers.typeCompatibleWith;
59  import static org.hamcrest.core.IsEqual.equalTo;
60  import static org.junit.Assert.fail;
61  import static org.mockito.ArgumentMatchers.any;
62  import static org.mockito.ArgumentMatchers.anyInt;
63  import static org.mockito.ArgumentMatchers.anyLong;
64  import static org.mockito.Mockito.mock;
65  import static org.mockito.Mockito.times;
66  import static org.mockito.Mockito.verify;
67  import static org.mockito.Mockito.when;
68  
69  @RunWith(MockitoJUnitRunner.class)
70  public class TestHttpClientRequest {
71  
72      @Rule
73      public final RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
74  
75      private static final String DUMMY_HOST = "dummy.atlassian.test";
76      private static final String DUMMY_HTTP_URL = MessageFormat.format("http://{0}:8090/", DUMMY_HOST);
77  
78      private static final String DUMMY_PROXY_HOST = "dummy.proxy.atlassian.test";
79      private static final String DUMMY_PROXY_PORT = "12345";
80      private static final String DUMMY_PROXY_USER = "dummyproxyuser";
81      private static final String DUMMY_PROXY_PASSWORD = "dummyproxypassword";
82  
83      @Rule
84      public ExpectedException thrown = ExpectedException.none();
85  
86      @Mock
87      private HttpRequestExecutor mockRequestExecutor;
88  
89      @Mock
90      private HttpClientConnectionManager mockConnectionManager;
91  
92      private HttpClientRequestFactory requestFactory;
93      private ArgumentCaptor<HttpRequest> requestCaptor = ArgumentCaptor.forClass(HttpRequest.class);
94      private ArgumentCaptor<HttpRoute> routeCaptor = ArgumentCaptor.forClass(HttpRoute.class);
95  
96      @Before
97      public void setup() throws InterruptedException, ExecutionException, IOException, HttpException {
98          // Clear all system proxy settings
99          requestFactory = new HttpClientWithMockConnectionRequestFactory(mockConnectionManager, mockRequestExecutor);
100 
101         // Always respond with a 200/OK message
102         when(mockRequestExecutor.execute(any(HttpRequest.class), any(HttpClientConnection.class),
103                 any(HttpContext.class))).thenReturn(createOkResponse());
104 
105         // This allows us to hook in to the connection details that HttpClient would have made
106         final HttpClientConnection conn = mock(HttpClientConnection.class);
107         final ConnectionRequest connRequest = mock(ConnectionRequest.class);
108         when(connRequest.get(anyLong(), any(TimeUnit.class))).thenReturn(conn);
109         when(mockConnectionManager.requestConnection(any(HttpRoute.class), any())).thenReturn(connRequest);
110     }
111 
112     private static HttpResponse createOkResponse() {
113         final BasicHttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "OK");
114         response.setEntity(new StringEntity("test body", StandardCharsets.UTF_8));
115         return response;
116     }
117 
118     private static HttpResponse createRedirectResponse(String location) {
119         final BasicHttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_MOVED_PERMANENTLY, "Redirect");
120         response.setEntity(new StringEntity("Redirect", StandardCharsets.UTF_8));
121         response.setHeader("Location", location);
122         return response;
123     }
124 
125     private static HttpResponse createNoContentResponse() {
126         return new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_NO_CONTENT, "No Content");
127     }
128 
129     @Test
130     public void assertThatHeaderIsAddedToRequest() throws ResponseException, IOException, HttpException {
131         final HttpClientRequest request = requestFactory.createRequest(Request.MethodType.GET, DUMMY_HTTP_URL);
132 
133         final String testHeaderName = "foo";
134         final String testHeaderValue = "bar";
135         request.addHeader(testHeaderName, testHeaderValue);
136         request.execute();
137 
138         verify(mockRequestExecutor).execute(requestCaptor.capture(), any(HttpClientConnection.class), any(HttpContext.class));
139         final HttpRequest lastRequest = requestCaptor.getValue();
140         assertThat(lastRequest, notNullValue());
141 
142         final Header[] headers = lastRequest.getHeaders(testHeaderName);
143         //noinspection unchecked
144         assertThat(headers, arrayContaining(headerWithValue(equalTo(testHeaderValue))));
145     }
146 
147     @Test
148     public void assertThatMultiValuedHeadersAreAddedToRequest() throws ResponseException, IOException, HttpException {
149         final HttpClientRequest request = requestFactory.createRequest(Request.MethodType.GET, DUMMY_HTTP_URL);
150 
151         final String testHeaderName = "foo";
152         final String testHeaderValue1 = "bar1";
153         final String testHeaderValue2 = "bar2";
154         request.addHeader(testHeaderName, testHeaderValue1);
155         request.addHeader(testHeaderName, testHeaderValue2);
156         request.execute();
157 
158         verify(mockRequestExecutor).execute(requestCaptor.capture(), any(HttpClientConnection.class), any(HttpContext.class));
159         final HttpRequest lastRequest = requestCaptor.getValue();
160         assertThat(lastRequest, notNullValue());
161 
162         final Header[] headers = lastRequest.getHeaders(testHeaderName);
163         //noinspection unchecked
164         assertThat(headers, arrayContaining(
165                 headerWithValue(equalTo(testHeaderValue1)),
166                 headerWithValue(equalTo(testHeaderValue2))));
167     }
168 
169     @Test
170     public void testThatRouteIsNotCached() throws Exception {
171         System.setProperty(SystemPropertiesProxyConfig.PROXY_HOST_PROPERTY_NAME, DUMMY_PROXY_HOST);
172         System.setProperty(SystemPropertiesProxyConfig.PROXY_PORT_PROPERTY_NAME, DUMMY_PROXY_PORT);
173         final HttpClientRequestFactory factory = new HttpClientWithMockConnectionRequestFactory(mockConnectionManager, mockRequestExecutor);
174 
175         HttpClientRequest request = factory.createRequest(Request.MethodType.GET, DUMMY_HTTP_URL);
176         request.execute();
177 
178         System.setProperty(SystemPropertiesProxyConfig.PROXY_HOST_PROPERTY_NAME, "hostName");
179         System.setProperty(SystemPropertiesProxyConfig.PROXY_PORT_PROPERTY_NAME, "1234");
180 
181         request = requestFactory.createRequest(Request.MethodType.GET, DUMMY_HTTP_URL);
182         request.execute();
183 
184         verify(mockConnectionManager, times(2)).connect(any(HttpClientConnection.class), routeCaptor.capture(), anyInt(), any(HttpContext.class
185         ));
186         List<HttpRoute> routes = routeCaptor.getAllValues();
187         assertThat(routes.get(0), proxyHostIs(hostWithNameAndPort(DUMMY_PROXY_HOST, Integer.parseInt(DUMMY_PROXY_PORT))));
188         assertThat(routes.get(1), proxyHostIs(hostWithNameAndPort("hostName", Integer.parseInt("1234"))));
189     }
190 
191     @Test
192     public void assertThatParameterIsAddedToRequest() throws IOException, ResponseException, HttpException {
193         final HttpClientRequest request = requestFactory.createRequest(Request.MethodType.POST, DUMMY_HTTP_URL);
194 
195         final String testParameterName = "foo";
196         final String testParameterValue = "bar";
197 
198         request.addRequestParameters(testParameterName, testParameterValue);
199         request.execute();
200 
201         verify(mockRequestExecutor).execute(requestCaptor.capture(), any(HttpClientConnection.class), any(HttpContext.class));
202         final HttpRequest lastRequest = requestCaptor.getValue();
203 
204         assertThat(lastRequest, notNullValue());
205         assertThat(lastRequest.getClass(), is(typeCompatibleWith(HttpEntityEnclosingRequest.class)));
206         assertThat(lastRequest, requestParameters(contains(parameterWithNameAndValue(
207                 testParameterName, testParameterValue))));
208     }
209 
210     @Test
211     public void assertThatMultiValuedParametersAreAddedToRequest() throws IOException, ResponseException, HttpException {
212         final HttpClientRequest request = requestFactory.createRequest(Request.MethodType.POST, DUMMY_HTTP_URL);
213 
214         final String testParameterName = "foo";
215         final String testParameterValue1 = "bar1";
216         final String testParameterValue2 = "bar2";
217 
218         request.addRequestParameters(testParameterName, testParameterValue1, testParameterName, testParameterValue2);
219         request.execute();
220 
221         verify(mockRequestExecutor).execute(requestCaptor.capture(), any(HttpClientConnection.class), any(HttpContext.class));
222         final HttpRequest lastRequest = requestCaptor.getValue();
223 
224         assertThat(lastRequest, notNullValue());
225         assertThat(lastRequest.getClass(), is(typeCompatibleWith(HttpEntityEnclosingRequest.class)));
226         //noinspection unchecked
227         assertThat(lastRequest, requestParameters(contains(
228                 parameterWithNameAndValue(testParameterName, testParameterValue1),
229                 parameterWithNameAndValue(testParameterName, testParameterValue2)
230         )));
231     }
232 
233     @Test
234     public void assertThatParametersAreAddedWithMultipleCalls() throws IOException, ResponseException, HttpException {
235         final HttpClientRequest request = requestFactory.createRequest(Request.MethodType.POST, DUMMY_HTTP_URL);
236 
237         final String testParameterName = "foo";
238         final String testParameterValue1 = "bar1";
239         final String testParameterValue2 = "bar2";
240 
241         request.addRequestParameters(testParameterName, testParameterValue1);
242         request.addRequestParameters(testParameterName, testParameterValue2);
243         request.execute();
244 
245         verify(mockRequestExecutor).execute(requestCaptor.capture(), any(HttpClientConnection.class), any(HttpContext.class));
246         final HttpRequest lastRequest = requestCaptor.getValue();
247 
248         assertThat(lastRequest, notNullValue());
249         assertThat(lastRequest.getClass(), is(typeCompatibleWith(HttpEntityEnclosingRequest.class)));
250         //noinspection unchecked
251         assertThat(lastRequest, requestParameters(contains(
252                 parameterWithNameAndValue(testParameterName, testParameterValue1),
253                 parameterWithNameAndValue(testParameterName, testParameterValue2)
254         )));
255     }
256 
257     @Test
258     public void assertThatAddingParametersToGetRequestThrowsException() {
259         final HttpClientRequest request = requestFactory.createRequest(Request.MethodType.GET, DUMMY_HTTP_URL);
260         thrown.expect(IllegalStateException.class);
261         request.addRequestParameters("foo", "bar");
262     }
263 
264     @Test
265     public void assertThatSystemProxyHostSettingHonoured() throws IOException, ResponseException, HttpException {
266         System.setProperty(SystemPropertiesProxyConfig.PROXY_HOST_PROPERTY_NAME, DUMMY_PROXY_HOST);
267         System.setProperty(SystemPropertiesProxyConfig.PROXY_PORT_PROPERTY_NAME, DUMMY_PROXY_PORT);
268 
269         final HttpClientRequestFactory factory = new HttpClientWithMockConnectionRequestFactory(mockConnectionManager, mockRequestExecutor);
270         final HttpClientRequest request = factory.createRequest(Request.MethodType.GET, DUMMY_HTTP_URL);
271         request.execute();
272 
273         verify(mockConnectionManager).connect(any(HttpClientConnection.class), routeCaptor.capture(), anyInt(), any(HttpContext.class));
274         HttpRoute route = routeCaptor.getValue();
275         assertThat(route, proxyHostIs(hostWithNameAndPort(DUMMY_PROXY_HOST, Integer.parseInt(DUMMY_PROXY_PORT))));
276     }
277 
278     @Test
279     public void assertThatProxyNotUsedByDefault() throws IOException, ResponseException, HttpException {
280         final HttpClientRequestFactory factory = new HttpClientWithMockConnectionRequestFactory(mockConnectionManager, mockRequestExecutor);
281         final HttpClientRequest request = factory.createRequest(Request.MethodType.GET, DUMMY_HTTP_URL);
282         request.execute();
283 
284         verify(mockConnectionManager).connect(any(HttpClientConnection.class), routeCaptor.capture(), anyInt(), any(HttpContext.class));
285         final HttpRoute route = routeCaptor.getValue();
286         assertThat(route, proxyHostIs(Matchers.nullValue(HttpHost.class)));
287     }
288 
289     @Test
290     public void assertThatNonProxyHostsSystemPropertyHonoured() throws IOException, ResponseException, HttpException {
291         System.setProperty(SystemPropertiesProxyConfig.PROXY_HOST_PROPERTY_NAME, DUMMY_PROXY_HOST);
292         System.setProperty(SystemPropertiesProxyConfig.PROXY_PORT_PROPERTY_NAME, DUMMY_PROXY_PORT);
293         System.setProperty(SystemPropertiesProxyConfig.PROXY_NON_HOSTS_PROPERTY_NAME, "localhost");
294 
295         final HttpClientRequestFactory factory = new HttpClientWithMockConnectionRequestFactory(mockConnectionManager, mockRequestExecutor);
296 
297         // deliberatly set after factory is created to match
298         // how jira and confluence use it
299         System.setProperty(SystemPropertiesProxyConfig.PROXY_NON_HOSTS_PROPERTY_NAME, "*.notproxied.test|localhost");
300 
301         final HttpClientRequest request = factory.createRequest(Request.MethodType.GET, DUMMY_HTTP_URL);
302 
303         request.execute();
304         request.setUrl("http://www.notproxied.test").execute();
305 
306         verify(mockConnectionManager, times(2)).connect(any(HttpClientConnection.class), routeCaptor.capture(), anyInt(), any(HttpContext.class));
307 
308         final List<HttpRoute> routes = routeCaptor.getAllValues();
309 
310         //noinspection unchecked
311         assertThat(routes, contains(
312                 proxyHostIs(hostWithNameAndPort(DUMMY_PROXY_HOST, Integer.parseInt(DUMMY_PROXY_PORT))),
313                 proxyHostIs(Matchers.nullValue(HttpHost.class))
314         ));
315     }
316 
317     @Test
318     public void assertThatProxyAuthenticationSystemPropertyHonoured() throws IOException, ResponseException, HttpException {
319         System.setProperty(SystemPropertiesProxyConfig.PROXY_HOST_PROPERTY_NAME, DUMMY_PROXY_HOST);
320         System.setProperty(SystemPropertiesProxyConfig.PROXY_PORT_PROPERTY_NAME, DUMMY_PROXY_PORT);
321         System.setProperty(SystemPropertiesProxyConfig.PROXY_USER_PROPERTY_NAME, DUMMY_PROXY_USER);
322         System.setProperty(SystemPropertiesProxyConfig.PROXY_PASSWORD_PROPERTY_NAME, DUMMY_PROXY_PASSWORD);
323 
324         final HttpClientRequestFactory factory = new HttpClientWithMockConnectionRequestFactory(mockConnectionManager, mockRequestExecutor);
325         final HttpClientRequest request = factory.createRequest(Request.MethodType.GET, DUMMY_HTTP_URL);
326         final String username = "charlie";
327         final String password = "password123";
328         request.addBasicAuthentication(DUMMY_HOST, username, password);
329         request.execute();
330         verify(mockConnectionManager).connect(any(HttpClientConnection.class), routeCaptor.capture(), anyInt(), any(HttpContext.class));
331         final HttpRoute route = routeCaptor.getValue();
332         assertThat(route, proxyHostIs(hostWithNameAndPort(DUMMY_PROXY_HOST, Integer.parseInt(DUMMY_PROXY_PORT))));
333 
334         verify(mockRequestExecutor).execute(requestCaptor.capture(), any(HttpClientConnection.class), any(HttpContext.class));
335         final HttpRequest lastRequest = requestCaptor.getValue();
336         final Header[] headers = lastRequest.getHeaders(AUTH.PROXY_AUTH_RESP);
337 
338         //noinspection unchecked
339         assertThat(headers, arrayContaining(headerWithValue(
340                 basicAuthWithUsernameAndPassword(DUMMY_PROXY_USER, DUMMY_PROXY_PASSWORD))));
341     }
342 
343     private void assertThatBasicAuthenticationHeadersAdded(String url) throws Exception {
344         final HttpClientRequest request = requestFactory.createRequest(Request.MethodType.GET, url);
345 
346         final String username = "charlie";
347         final String password = "password123";
348         final URI uri = new URI(url);
349         request.addBasicAuthentication(uri.getHost(), username, password);
350         request.execute();
351 
352         verify(mockRequestExecutor).execute(requestCaptor.capture(), any(HttpClientConnection.class), any(HttpContext.class));
353         final HttpRequest lastRequest = requestCaptor.getValue();
354         final Header[] headers = lastRequest.getHeaders(AUTH.WWW_AUTH_RESP);
355 
356         //noinspection unchecked
357         assertThat(headers, arrayContaining(headerWithValue(basicAuthWithUsernameAndPassword(username, password))));
358     }
359 
360     @Test
361     public void assertThatBasicAuthenticationHeadersAddedDefaultPort() throws Exception {
362         assertThatBasicAuthenticationHeadersAdded(MessageFormat.format("http://{0}/", DUMMY_HOST));
363     }
364 
365     @Test
366     public void assertThatBasicAuthenticationHeadersAddedNotDefaultPort() throws Exception {
367         assertThatBasicAuthenticationHeadersAdded(MessageFormat.format("http://{0}:8090/", DUMMY_HOST));
368     }
369 
370     @Test
371     public void assertThatBasicAuthenticationHeadersAddedForHttpsNotDefaultPort() throws Exception {
372         assertThatBasicAuthenticationHeadersAdded(MessageFormat.format("https://{0}:8090/", DUMMY_HOST));
373     }
374 
375     @Test
376     public void assertThatBasicAuthenticationHeadersAddedForHttpsDefaultPort() throws Exception {
377         assertThatBasicAuthenticationHeadersAdded(MessageFormat.format("https://{0}/", DUMMY_HOST));
378     }
379 
380     @Test
381     public void assertThatExceptionThrownAfterDefaultMaximumRedirects() throws ResponseException, IOException, HttpException {
382 
383         when(mockRequestExecutor.execute(any(HttpRequest.class), any(HttpClientConnection.class),
384                 any(HttpContext.class))).thenAnswer(new Answer<HttpResponse>() {
385             int redirectCount = 0;
386 
387             @Override
388             public HttpResponse answer(final InvocationOnMock invocationOnMock) throws Throwable {
389                 // We just add a changing query string parameter here to avoid a circular redirect
390                 return createRedirectResponse(DUMMY_HTTP_URL + "?redirect_count=" + redirectCount++);
391             }
392         });
393 
394         final HttpClientRequest request = requestFactory.createRequest(Request.MethodType.GET, DUMMY_HTTP_URL);
395 
396         try {
397             request.execute();
398             fail("An exception should be thrown when maximum redirects is exceeded.");
399         } catch (ResponseException expectedException) {
400             // Although JUnit has an ExpectedException rule, we need to catch this both to ensure that a
401             // RedirectException is the cause, and to subsequently verify the execution method call count.
402             assertThat(Throwables.getCausalChain(expectedException), Matchers.<Throwable>hasItem(instanceOf(RedirectException.class)));
403         }
404 
405         verify(mockRequestExecutor, times(SystemPropertiesConnectionConfig.DEFAULT_MAX_REDIRECTS + 1))
406                 .execute(any(HttpRequest.class), any(HttpClientConnection.class), any(HttpContext.class));
407     }
408 
409     @Test
410     public void assertThatNoContentDoesNotThrowNPE() throws ResponseException, IOException, HttpException {
411         when(mockRequestExecutor.execute(any(HttpRequest.class), any(HttpClientConnection.class),
412                 any(HttpContext.class))).thenAnswer(new Answer<HttpResponse>() {
413             @Override
414             public HttpResponse answer(final InvocationOnMock invocationOnMock) throws Throwable {
415                 return createNoContentResponse();
416             }
417         });
418         final HttpClientRequest request = requestFactory.createRequest(Request.MethodType.GET, DUMMY_HTTP_URL);
419         request.execute();
420     }
421 
422     @Test
423     public void assertThatRequestParameterIsUTF8Encoded() throws ResponseException, IOException, HttpException
424     {
425         final HttpClientRequest request = requestFactory.createRequest(Request.MethodType.POST, DUMMY_HTTP_URL);
426         final String testParameterName = "test";
427         final String testParameterValue = "ั‚ะตัั‚";
428         request.addRequestParameters(testParameterName, testParameterValue);
429 
430         request.execute();
431 
432         verify(mockRequestExecutor).execute(requestCaptor.capture(), any(HttpClientConnection.class), any(HttpContext.class));
433         final HttpRequest lastRequest = requestCaptor.getValue();
434 
435         //noinspection unchecked
436         assertThat(lastRequest, requestParameters(contains(
437                 parameterWithNameAndValue(testParameterName, testParameterValue)
438         )));
439     }
440 
441     private static Matcher<Header> headerWithValue(final Matcher<String> valueMatcher) {
442         return new FeatureMatcher<Header, String>(valueMatcher, "header with value", "header value") {
443             @Override
444             protected String featureValueOf(final Header header) {
445                 return header.getValue();
446             }
447         };
448     }
449 
450     private static Matcher<HttpRequest> requestParameters(final Matcher<Iterable<? extends NameValuePair>> parametersMatcher) {
451         return new FeatureMatcher<HttpRequest, Iterable<? extends NameValuePair>>(
452                 parametersMatcher, "parameters with value", "parameters value") {
453             @Override
454             protected Iterable<? extends NameValuePair> featureValueOf(final HttpRequest httpRequest) {
455                 try {
456                     return URLEncodedUtils.parse(((HttpEntityEnclosingRequest) httpRequest).getEntity());
457                 } catch (IOException e) {
458                     throw new RuntimeException(e);
459                 }
460             }
461         };
462     }
463 
464     private static Matcher<NameValuePair> parameterWithNameAndValue(final String name, final String value) {
465 
466         final NameValuePair expectedParameter = new BasicNameValuePair(name, value);
467         return Matchers.equalTo(expectedParameter);
468     }
469 
470     private static Matcher<HttpRoute> proxyHostIs(final Matcher<HttpHost> routeMatcher) {
471         return new FeatureMatcher<HttpRoute, HttpHost>(routeMatcher, "proxy host for route", "proxy host") {
472             @Override
473             protected HttpHost featureValueOf(final HttpRoute route) {
474                 return route.getProxyHost();
475             }
476         };
477     }
478 
479     private static Matcher<HttpHost> hostWithNameAndPort(final String name, final int port) {
480 
481         final HttpHost expectedHost = new HttpHost(name, port);
482         return Matchers.equalTo(expectedHost);
483     }
484 
485     private static Matcher<String> basicAuthWithUsernameAndPassword(final String username, final String password) {
486 
487         final String basicAuthCreds = MessageFormat.format("{0}:{1}", username, password);
488         final String encodedBasicAuthCreds = Base64.encodeBase64String(basicAuthCreds.getBytes());
489         final String expectedBasicAuthHeader = MessageFormat.format("{0} {1}", AuthSchemes.BASIC, encodedBasicAuthCreds);
490 
491         return Matchers.equalTo(expectedBasicAuthHeader);
492     }
493 
494 }