@Test
  public void testExecRequestPersistentConnection() throws Exception {
    final HttpRoute route = new HttpRoute(target);
    final HttpClientContext context = new HttpClientContext();
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
    final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");

    Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
    Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
    Mockito.when(
            requestExecutor.execute(
                Mockito.same(request),
                Mockito.<HttpClientConnection>any(),
                Mockito.<HttpClientContext>any()))
        .thenReturn(response);
    Mockito.when(reuseStrategy.keepAlive(Mockito.same(response), Mockito.<HttpClientContext>any()))
        .thenReturn(Boolean.TRUE);
    Mockito.when(
            keepAliveStrategy.getKeepAliveDuration(
                Mockito.same(response), Mockito.<HttpClientContext>any()))
        .thenReturn(678L);

    final CloseableHttpResponse finalResponse =
        mainClientExec.execute(route, request, context, execAware);
    Mockito.verify(connManager).requestConnection(route, null);
    Mockito.verify(connRequest).get(0, TimeUnit.MILLISECONDS);
    Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context);
    Mockito.verify(connManager).releaseConnection(managedConn, null, 678L, TimeUnit.MILLISECONDS);
    Mockito.verify(managedConn, Mockito.never()).close();

    Assert.assertNotNull(finalResponse);
    Assert.assertTrue(finalResponse instanceof HttpResponseProxy);
  }
  @Test
  public void testEstablishRouteViaProxyTunnel() throws Exception {
    final AuthState authState = new AuthState();
    final HttpRoute route = new HttpRoute(target, null, proxy, true);
    final HttpClientContext context = new HttpClientContext();
    final RequestConfig config = RequestConfig.custom().setConnectTimeout(321).build();
    context.setRequestConfig(config);
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
    final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");

    Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
    Mockito.when(
            requestExecutor.execute(
                Mockito.<HttpRequest>any(),
                Mockito.<HttpClientConnection>any(),
                Mockito.<HttpClientContext>any()))
        .thenReturn(response);

    mainClientExec.establishRoute(authState, managedConn, route, request, context);

    Mockito.verify(connManager).connect(managedConn, route, 321, context);
    Mockito.verify(connManager).routeComplete(managedConn, route, context);
    final ArgumentCaptor<HttpRequest> reqCaptor = ArgumentCaptor.forClass(HttpRequest.class);
    Mockito.verify(requestExecutor)
        .execute(reqCaptor.capture(), Mockito.same(managedConn), Mockito.same(context));
    final HttpRequest connect = reqCaptor.getValue();
    Assert.assertNotNull(connect);
    Assert.assertEquals("CONNECT", connect.getRequestLine().getMethod());
    Assert.assertEquals(HttpVersion.HTTP_1_1, connect.getRequestLine().getProtocolVersion());
    Assert.assertEquals("foo:80", connect.getRequestLine().getUri());
  }
  @Test(expected = HttpException.class)
  public void testEstablishRouteViaProxyTunnelFailure() throws Exception {
    final AuthState authState = new AuthState();
    final HttpRoute route = new HttpRoute(target, null, proxy, true);
    final HttpClientContext context = new HttpClientContext();
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
    final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 500, "Boom");
    response.setEntity(new StringEntity("Ka-boom"));

    Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
    Mockito.when(
            requestExecutor.execute(
                Mockito.<HttpRequest>any(),
                Mockito.<HttpClientConnection>any(),
                Mockito.<HttpClientContext>any()))
        .thenReturn(response);

    try {
      mainClientExec.establishRoute(authState, managedConn, route, request, context);
    } catch (final TunnelRefusedException ex) {
      final HttpResponse r = ex.getResponse();
      Assert.assertEquals("Ka-boom", EntityUtils.toString(r.getEntity()));

      Mockito.verify(managedConn).close();

      throw ex;
    }
  }
  @Test
  public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengeNonPersistentConnection()
      throws Exception {
    final AuthState authState = new AuthState();
    final HttpRoute route = new HttpRoute(target, null, proxy, true);
    final HttpClientContext context = new HttpClientContext();
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
    final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?");
    final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
    response1.setEntity(EntityBuilder.create().setStream(instream1).build());
    final HttpResponse response2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");

    Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
    Mockito.when(
            proxyAuthStrategy.isAuthenticationRequested(
                Mockito.eq(proxy), Mockito.same(response1), Mockito.<HttpClientContext>any()))
        .thenReturn(Boolean.TRUE);
    Mockito.when(
            reuseStrategy.keepAlive(Mockito.<HttpResponse>any(), Mockito.<HttpClientContext>any()))
        .thenReturn(Boolean.FALSE);
    Mockito.when(
            requestExecutor.execute(
                Mockito.<HttpRequest>any(),
                Mockito.<HttpClientConnection>any(),
                Mockito.<HttpClientContext>any()))
        .thenReturn(response1, response2);

    mainClientExec.establishRoute(authState, managedConn, route, request, context);

    Mockito.verify(connManager).connect(managedConn, route, 0, context);
    Mockito.verify(connManager).routeComplete(managedConn, route, context);
    Mockito.verify(instream1, Mockito.never()).close();
    Mockito.verify(managedConn).close();
  }
  @Test
  public void testExecRequestPersistentStatefulConnection() throws Exception {
    final HttpRoute route = new HttpRoute(target);
    final HttpClientContext context = new HttpClientContext();
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
    final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");

    Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
    Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
    Mockito.when(
            requestExecutor.execute(
                Mockito.same(request),
                Mockito.<HttpClientConnection>any(),
                Mockito.<HttpClientContext>any()))
        .thenReturn(response);
    Mockito.when(reuseStrategy.keepAlive(Mockito.same(response), Mockito.<HttpClientContext>any()))
        .thenReturn(Boolean.TRUE);
    Mockito.when(userTokenHandler.getUserToken(Mockito.<HttpClientContext>any()))
        .thenReturn("this and that");

    mainClientExec.execute(route, request, context, execAware);
    Mockito.verify(connManager).requestConnection(route, null);
    Mockito.verify(connRequest).get(0, TimeUnit.MILLISECONDS);
    Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context);
    Mockito.verify(connManager)
        .releaseConnection(managedConn, "this and that", 0, TimeUnit.MILLISECONDS);
    Mockito.verify(managedConn, Mockito.never()).close();

    Assert.assertEquals("this and that", context.getUserToken());
  }
  @Test(expected = RequestAbortedException.class)
  public void testExecConnectionRequestFailed() throws Exception {
    final HttpRoute route = new HttpRoute(target);
    final HttpClientContext context = new HttpClientContext();
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));

    Mockito.when(connRequest.get(Mockito.anyInt(), Mockito.<TimeUnit>any()))
        .thenThrow(new ExecutionException("Opppsie", null));
    mainClientExec.execute(route, request, context, execAware);
  }
  @Test
  public void testEstablishRouteDirectProxy() throws Exception {
    final AuthState authState = new AuthState();
    final HttpRoute route = new HttpRoute(target, null, proxy, false);
    final HttpClientContext context = new HttpClientContext();
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));

    Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);

    mainClientExec.establishRoute(authState, managedConn, route, request, context);

    Mockito.verify(connManager).connect(managedConn, route, 0, context);
    Mockito.verify(connManager).routeComplete(managedConn, route, context);
  }
  @Test(expected = InterruptedIOException.class)
  public void testExecConnectionShutDown() throws Exception {
    final HttpRoute route = new HttpRoute(target);
    final HttpClientContext context = new HttpClientContext();
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));

    Mockito.when(
            requestExecutor.execute(
                Mockito.<HttpRequest>any(),
                Mockito.<HttpClientConnection>any(),
                Mockito.<HttpClientContext>any()))
        .thenThrow(new ConnectionShutdownException());

    mainClientExec.execute(route, request, context, execAware);
  }
  @Test(expected = RequestAbortedException.class)
  public void testExecAbortedPriorToConnectionLease() throws Exception {
    final HttpRoute route = new HttpRoute(target);
    final HttpClientContext context = new HttpClientContext();
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));

    Mockito.when(managedConn.isOpen()).thenReturn(Boolean.FALSE);
    Mockito.when(execAware.isAborted()).thenReturn(Boolean.TRUE);
    try {
      mainClientExec.execute(route, request, context, execAware);
    } catch (final IOException ex) {
      Mockito.verify(connRequest, Mockito.times(1)).cancel();
      throw ex;
    }
  }
  @Test
  public void testSocketTimeoutReset() throws Exception {
    final HttpRoute route = new HttpRoute(target);
    final HttpClientContext context = new HttpClientContext();
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
    final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
    Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
    Mockito.when(
            requestExecutor.execute(
                Mockito.same(request),
                Mockito.<HttpClientConnection>any(),
                Mockito.<HttpClientContext>any()))
        .thenReturn(response);

    mainClientExec.execute(route, request, context, execAware);
    Mockito.verify(managedConn, Mockito.never()).setSocketTimeout(Mockito.anyInt());
  }
  @Test
  public void testExecEntityEnclosingRequestRetryOnAuthChallenge() throws Exception {
    final HttpRoute route = new HttpRoute(target);
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
    final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?");
    final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
    response1.setEntity(EntityBuilder.create().setStream(instream1).build());
    final HttpResponse response2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
    final InputStream instream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {2, 3, 4}));
    response2.setEntity(EntityBuilder.create().setStream(instream2).build());

    final AuthState proxyAuthState = new AuthState();
    proxyAuthState.setState(AuthProtocolState.SUCCESS);
    proxyAuthState.update(new NTLMScheme(), new NTCredentials("user:pass"));

    final HttpClientContext context = new HttpClientContext();
    context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, proxyAuthState);

    Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
    Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
    Mockito.when(
            requestExecutor.execute(
                Mockito.same(request),
                Mockito.<HttpClientConnection>any(),
                Mockito.<HttpClientContext>any()))
        .thenReturn(response1, response2);
    Mockito.when(
            reuseStrategy.keepAlive(Mockito.<HttpResponse>any(), Mockito.<HttpClientContext>any()))
        .thenReturn(Boolean.FALSE);
    Mockito.when(
            targetAuthStrategy.isAuthenticationRequested(
                Mockito.eq(target), Mockito.same(response1), Mockito.<HttpClientContext>any()))
        .thenReturn(Boolean.TRUE);

    final CloseableHttpResponse finalResponse =
        mainClientExec.execute(route, request, context, execAware);
    Mockito.verify(requestExecutor, Mockito.times(2)).execute(request, managedConn, context);
    Mockito.verify(managedConn).close();
    Mockito.verify(instream2, Mockito.never()).close();

    Assert.assertNotNull(finalResponse);
    Assert.assertEquals(200, finalResponse.getStatusLine().getStatusCode());
    Assert.assertNull(proxyAuthState.getAuthScheme());
    Assert.assertNull(proxyAuthState.getCredentials());
  }
  @Test(expected = HttpException.class)
  public void testEstablishRouteViaProxyTunnelUnexpectedResponse() throws Exception {
    final AuthState authState = new AuthState();
    final HttpRoute route = new HttpRoute(target, null, proxy, true);
    final HttpClientContext context = new HttpClientContext();
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
    final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 101, "Lost");

    Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
    Mockito.when(
            requestExecutor.execute(
                Mockito.<HttpRequest>any(),
                Mockito.<HttpClientConnection>any(),
                Mockito.<HttpClientContext>any()))
        .thenReturn(response);

    mainClientExec.establishRoute(authState, managedConn, route, request, context);
  }
  @Test
  public void testSocketTimeoutExistingConnection() throws Exception {
    final HttpRoute route = new HttpRoute(target);
    final HttpClientContext context = new HttpClientContext();
    final RequestConfig config = RequestConfig.custom().setSocketTimeout(3000).build();
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
    context.setRequestConfig(config);
    final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
    Mockito.when(managedConn.isOpen()).thenReturn(true);
    Mockito.when(
            requestExecutor.execute(
                Mockito.same(request),
                Mockito.<HttpClientConnection>any(),
                Mockito.<HttpClientContext>any()))
        .thenReturn(response);

    mainClientExec.execute(route, request, context, execAware);
    Mockito.verify(managedConn).setSocketTimeout(3000);
  }
  @Test(expected = HttpException.class)
  public void testEstablishRouteViaProxyTunnelMultipleHops() throws Exception {
    final AuthState authState = new AuthState();
    final HttpHost proxy1 = new HttpHost("this", 8888);
    final HttpHost proxy2 = new HttpHost("that", 8888);
    final HttpRoute route =
        new HttpRoute(
            target,
            null,
            new HttpHost[] {proxy1, proxy2},
            true,
            RouteInfo.TunnelType.TUNNELLED,
            RouteInfo.LayerType.LAYERED);
    final HttpClientContext context = new HttpClientContext();
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));

    Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);

    mainClientExec.establishRoute(authState, managedConn, route, request, context);
  }
  @Test(expected = NonRepeatableRequestException.class)
  public void testExecEntityEnclosingRequest() throws Exception {
    final HttpRoute route = new HttpRoute(target);
    final HttpClientContext context = new HttpClientContext();
    final HttpPost post = new HttpPost("http://bar/test");
    final InputStream instream0 = new ByteArrayInputStream(new byte[] {1, 2, 3});
    post.setEntity(EntityBuilder.create().setStream(instream0).build());
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(post);

    final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?");
    final InputStream instream1 = new ByteArrayInputStream(new byte[] {1, 2, 3});
    response1.setEntity(EntityBuilder.create().setStream(instream1).build());

    Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
    Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
    Mockito.when(
            requestExecutor.execute(
                Mockito.same(request),
                Mockito.<HttpClientConnection>any(),
                Mockito.<HttpClientContext>any()))
        .thenAnswer(
            new Answer<HttpResponse>() {

              @Override
              public HttpResponse answer(final InvocationOnMock invocationOnMock) throws Throwable {
                final Object[] args = invocationOnMock.getArguments();
                final HttpEntityEnclosingRequest requestEE = (HttpEntityEnclosingRequest) args[0];
                requestEE.getEntity().writeTo(new ByteArrayOutputStream());
                return response1;
              }
            });
    Mockito.when(
            reuseStrategy.keepAlive(Mockito.<HttpResponse>any(), Mockito.<HttpClientContext>any()))
        .thenReturn(Boolean.TRUE);
    Mockito.when(
            targetAuthStrategy.isAuthenticationRequested(
                Mockito.eq(target), Mockito.same(response1), Mockito.<HttpClientContext>any()))
        .thenReturn(Boolean.TRUE);

    mainClientExec.execute(route, request, context, execAware);
  }
  @Test(expected = RequestAbortedException.class)
  public void testExecAbortedPriorToRequestExecution() throws Exception {
    final HttpRoute route = new HttpRoute(target);
    final HttpClientContext context = new HttpClientContext();
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));

    Mockito.when(managedConn.isOpen()).thenReturn(Boolean.FALSE);
    Mockito.when(execAware.isAborted()).thenReturn(Boolean.FALSE, Boolean.FALSE, Boolean.TRUE);
    try {
      mainClientExec.execute(route, request, context, execAware);
    } catch (final IOException ex) {
      Mockito.verify(connRequest, Mockito.times(1)).get(0, TimeUnit.MILLISECONDS);
      Mockito.verify(connManager, Mockito.times(1)).connect(managedConn, route, 0, context);
      Mockito.verify(requestExecutor, Mockito.never())
          .execute(
              Mockito.same(request),
              Mockito.<HttpClientConnection>any(),
              Mockito.<HttpClientContext>any());
      throw ex;
    }
  }
  @Test(expected = IOException.class)
  public void testExecIOException() throws Exception {
    final HttpRoute route = new HttpRoute(target);
    final HttpClientContext context = new HttpClientContext();
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));

    Mockito.when(
            requestExecutor.execute(
                Mockito.<HttpRequest>any(),
                Mockito.<HttpClientConnection>any(),
                Mockito.<HttpClientContext>any()))
        .thenThrow(new IOException("Ka-boom"));

    try {
      mainClientExec.execute(route, request, context, execAware);
    } catch (final Exception ex) {
      Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS);

      throw ex;
    }
  }
  @Test
  public void testExecRequestNonPersistentConnection() throws Exception {
    final HttpRoute route = new HttpRoute(target);
    final HttpClientContext context = new HttpClientContext();
    final RequestConfig config =
        RequestConfig.custom()
            .setConnectTimeout(123)
            .setSocketTimeout(234)
            .setConnectionRequestTimeout(345)
            .build();
    context.setRequestConfig(config);
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
    final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
    Mockito.when(
            requestExecutor.execute(
                Mockito.same(request),
                Mockito.<HttpClientConnection>any(),
                Mockito.<HttpClientContext>any()))
        .thenReturn(response);

    final CloseableHttpResponse finalResponse =
        mainClientExec.execute(route, request, context, execAware);
    Mockito.verify(connManager).requestConnection(route, null);
    Mockito.verify(connRequest).get(345, TimeUnit.MILLISECONDS);
    Mockito.verify(execAware, Mockito.times(1)).setCancellable(connRequest);
    Mockito.verify(execAware, Mockito.times(2)).setCancellable(Mockito.<Cancellable>any());
    Mockito.verify(connManager).connect(managedConn, route, 123, context);
    Mockito.verify(connManager).routeComplete(managedConn, route, context);
    Mockito.verify(managedConn).setSocketTimeout(234);
    Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context);
    Mockito.verify(managedConn, Mockito.times(1)).close();
    Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS);

    Assert.assertNotNull(context.getTargetAuthState());
    Assert.assertNotNull(context.getProxyAuthState());
    Assert.assertSame(managedConn, context.getConnection());
    Assert.assertNull(context.getUserToken());
    Assert.assertNotNull(finalResponse);
    Assert.assertTrue(finalResponse instanceof HttpResponseProxy);
  }
  @Test
  public void testExecRequestConnectionRelease() throws Exception {
    final HttpRoute route = new HttpRoute(target);
    final HttpClientContext context = new HttpClientContext();
    final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
    final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
    // The entity is streaming
    response.setEntity(
        EntityBuilder.create().setStream(new ByteArrayInputStream(new byte[] {})).build());

    Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
    Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
    Mockito.when(
            requestExecutor.execute(
                Mockito.same(request),
                Mockito.<HttpClientConnection>any(),
                Mockito.<HttpClientContext>any()))
        .thenReturn(response);
    Mockito.when(reuseStrategy.keepAlive(Mockito.same(response), Mockito.<HttpClientContext>any()))
        .thenReturn(Boolean.FALSE);

    final CloseableHttpResponse finalResponse =
        mainClientExec.execute(route, request, context, execAware);
    Mockito.verify(connManager).requestConnection(route, null);
    Mockito.verify(connRequest).get(0, TimeUnit.MILLISECONDS);
    Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context);
    Mockito.verify(connManager, Mockito.never())
        .releaseConnection(
            Mockito.same(managedConn), Mockito.any(), Mockito.anyInt(), Mockito.<TimeUnit>any());
    Mockito.verify(managedConn, Mockito.never()).close();

    Assert.assertNotNull(finalResponse);
    Assert.assertTrue(finalResponse instanceof HttpResponseProxy);
    finalResponse.close();

    Mockito.verify(connManager, Mockito.times(1))
        .releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS);
    Mockito.verify(managedConn, Mockito.times(1)).shutdown();
  }