diff --git a/connectors/apache-connector/src/test/java/org/glassfish/jersey/apache/connector/AuthTest.java b/connectors/apache-connector/src/test/java/org/glassfish/jersey/apache/connector/AuthTest.java index 8a524b2b32..a6c793a98c 100644 --- a/connectors/apache-connector/src/test/java/org/glassfish/jersey/apache/connector/AuthTest.java +++ b/connectors/apache-connector/src/test/java/org/glassfish/jersey/apache/connector/AuthTest.java @@ -58,17 +58,20 @@ import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature; +import org.glassfish.jersey.client.authentication.ResponseAuthenticationException; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.junit.Ignore; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; /** * @author Paul Sandoz @@ -229,6 +232,40 @@ public String deleteFilterWithEntity(@Context HttpHeaders h, String e) { return e; } + + @GET + @Path("content") + public String getWithContent(@Context HttpHeaders h) { + requestCount++; + String value = h.getRequestHeaders().getFirst("Authorization"); + if (value == null) { + assertEquals(1, requestCount); + throw new WebApplicationException( + Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"") + .entity("Forbidden").build()); + } else { + assertTrue(requestCount > 1); + } + + return "GET"; + } + + @GET + @Path("contentDigestAuth") + public String getWithContentDigestAuth(@Context HttpHeaders h) { + requestCount++; + String value = h.getRequestHeaders().getFirst("Authorization"); + if (value == null) { + assertEquals(1, requestCount); + throw new WebApplicationException( + Response.status(401).header("WWW-Authenticate", "Digest nonce=\"1234\"") + .entity("Forbidden").build()); + } else { + assertTrue(requestCount > 1); + } + + return "GET"; + } } @Test @@ -353,4 +390,50 @@ public void testAuthInteractivePost() { assertEquals("POST", r.request().post(Entity.text("POST"), String.class)); } + + @Test + public void testAuthGetWithBasicFilter() { + ClientConfig cc = new ClientConfig(); + PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); + cc.connectorProvider(new ApacheConnectorProvider()); + cc.property(ApacheClientProperties.CONNECTION_MANAGER, cm); + Client client = ClientBuilder.newClient(cc); + client.register(HttpAuthenticationFeature.universalBuilder().build()); + WebTarget r = client.target(getBaseUri()).path("test/content"); + + try { + assertEquals("GET", r.request().get(String.class)); + fail(); + } catch (ResponseAuthenticationException ex) { + // expected + } + + // Verify the connection that was used for the request is available for reuse + // and no connections are leased + assertEquals(cm.getTotalStats().getAvailable(), 1); + assertEquals(cm.getTotalStats().getLeased(), 0); + } + + @Test + public void testAuthGetWithDigestFilter() { + ClientConfig cc = new ClientConfig(); + PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); + cc.connectorProvider(new ApacheConnectorProvider()); + cc.property(ApacheClientProperties.CONNECTION_MANAGER, cm); + Client client = ClientBuilder.newClient(cc); + client.register(HttpAuthenticationFeature.universalBuilder().build()); + WebTarget r = client.target(getBaseUri()).path("test/contentDigestAuth"); + + try { + assertEquals("GET", r.request().get(String.class)); + fail(); + } catch (ResponseAuthenticationException ex) { + // expected + } + + // Verify the connection that was used for the request is available for reuse + // and no connections are leased + assertEquals(cm.getTotalStats().getAvailable(), 1); + assertEquals(cm.getTotalStats().getLeased(), 0); + } } diff --git a/core-client/src/main/java/org/glassfish/jersey/client/authentication/BasicAuthenticator.java b/core-client/src/main/java/org/glassfish/jersey/client/authentication/BasicAuthenticator.java index 0e692e700b..57733afed2 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/authentication/BasicAuthenticator.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/authentication/BasicAuthenticator.java @@ -40,6 +40,9 @@ package org.glassfish.jersey.client.authentication; +import java.io.IOException; +import java.io.InputStream; + import javax.ws.rs.client.ClientRequestContext; import javax.ws.rs.client.ClientResponseContext; import javax.ws.rs.core.HttpHeaders; @@ -120,6 +123,9 @@ public boolean filterResponseAndAuthenticate(ClientRequestContext request, Clien .getCredentials(request, defaultCredentials, HttpAuthenticationFilter.Type.BASIC); if (credentials == null) { + if (response.hasEntity()) { + discardInputAndClose(response.getEntityStream()); + } throw new ResponseAuthenticationException(null, LocalizationMessages.AUTHENTICATION_CREDENTIALS_MISSING_BASIC()); } @@ -127,4 +133,23 @@ public boolean filterResponseAndAuthenticate(ClientRequestContext request, Clien } return false; } + + private static void discardInputAndClose(InputStream is) { + byte[] buf = new byte[4096]; + try { + while (true) { + if (is.read(buf) <= 0) { + break; + } + } + } catch (IOException ex) { + // ignore + } finally { + try { + is.close(); + } catch (IOException ex) { + // ignore + } + } + } } diff --git a/core-client/src/main/java/org/glassfish/jersey/client/authentication/DigestAuthenticator.java b/core-client/src/main/java/org/glassfish/jersey/client/authentication/DigestAuthenticator.java index 494a1e9af5..68011ef272 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/authentication/DigestAuthenticator.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/authentication/DigestAuthenticator.java @@ -41,6 +41,7 @@ package org.glassfish.jersey.client.authentication; import java.io.IOException; +import java.io.InputStream; import java.net.URI; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -149,7 +150,9 @@ public boolean filterResponse(final ClientRequestContext request, final ClientRe final HttpAuthenticationFilter.Credentials cred = HttpAuthenticationFilter.getCredentials(request, this.credentials, HttpAuthenticationFilter.Type.DIGEST); if (cred == null) { - + if (response.hasEntity()) { + discardInputAndClose(response.getEntityStream()); + } throw new ResponseAuthenticationException(null, LocalizationMessages.AUTHENTICATION_CREDENTIALS_MISSING_DIGEST()); } @@ -373,6 +376,25 @@ private String randomBytes(final int nbBytes) { return bytesToHex(bytes); } + private static void discardInputAndClose(InputStream is) { + byte[] buf = new byte[4096]; + try { + while (true) { + if (is.read(buf) <= 0) { + break; + } + } + } catch (IOException ex) { + // ignore + } finally { + try { + is.close(); + } catch (IOException ex) { + // ignore + } + } + } + private enum QOP { UNSPECIFIED(null),