use of com.cloudant.http.HttpConnectionResponseInterceptor in project java-cloudant by cloudant.
the class HttpTest method testHttpConnectionRetries.
/**
* Test the global number of retries
*
* @throws Exception
*/
@TestTemplate
public void testHttpConnectionRetries() throws Exception {
// Just return 200 OK
mockWebServer.setDispatcher(new MockWebServerResources.ConstantResponseDispatcher(200));
CloudantClient c = CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer).interceptors(new HttpConnectionResponseInterceptor() {
@Override
public HttpConnectionInterceptorContext interceptResponse(HttpConnectionInterceptorContext context) {
// At least do something with the connection, otherwise we risk breaking it
try {
context.connection.getConnection().getResponseCode();
} catch (IOException e) {
fail("IOException getting response code");
}
// Set to always replay
context.replayRequest = true;
return context;
}
}).build();
String response = c.executeRequest(Http.GET(c.getBaseUri()).setNumberOfRetries(5)).responseAsString();
assertTrue(response.isEmpty(), "There should be no response body on the mock response");
assertEquals(5, mockWebServer.getRequestCount(), "There should be 5 request attempts");
}
use of com.cloudant.http.HttpConnectionResponseInterceptor in project java-cloudant by cloudant.
the class Utils method removeReplicatorTestDoc.
public static void removeReplicatorTestDoc(CloudantClient account, String replicatorDocId) throws Exception {
// Grab replicator doc revision using HTTP HEAD command
String replicatorDb = "_replicator";
URI uri = new URIBase(account.getBaseUri()).path(replicatorDb).path(replicatorDocId).build();
HttpConnection head = Http.HEAD(uri);
// add a response interceptor to allow us to retrieve the ETag revision header
final AtomicReference<String> revisionRef = new AtomicReference<String>();
head.responseInterceptors.add(new HttpConnectionResponseInterceptor() {
@Override
public HttpConnectionInterceptorContext interceptResponse(HttpConnectionInterceptorContext context) {
revisionRef.set(context.connection.getConnection().getHeaderField("ETag"));
return context;
}
});
account.executeRequest(head);
String revision = revisionRef.get();
assertNotNull("The revision should not be null", revision);
Database replicator = account.database(replicatorDb, false);
Response removeResponse = replicator.remove(replicatorDocId, revision.replaceAll("\"", ""));
assertThat(removeResponse.getError(), is(nullValue()));
}
use of com.cloudant.http.HttpConnectionResponseInterceptor in project java-cloudant by cloudant.
the class HttpTest method testInputStreamRetry.
private void testInputStreamRetry(HttpConnection request, byte[] expectedContent) throws Exception {
final MockResponse retry = new MockResponse().setResponseCode(444);
mockWebServer.enqueue(retry);
mockWebServer.enqueue(new MockResponse());
HttpConnection response = CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer).interceptors(new HttpConnectionResponseInterceptor() {
// This interceptor responds to our 444 request with a retry
@Override
public HttpConnectionInterceptorContext interceptResponse(HttpConnectionInterceptorContext context) {
try {
if (444 == context.connection.getConnection().getResponseCode()) {
context.replayRequest = true;
// Close the error stream
InputStream errors = context.connection.getConnection().getErrorStream();
if (errors != null) {
IOUtils.closeQuietly(errors);
}
}
} catch (IOException e) {
e.printStackTrace();
fail("IOException getting response code in test interceptor");
}
return context;
}
}).build().executeRequest(request);
String responseStr = response.responseAsString();
assertTrue(responseStr.isEmpty(), "There should be no response body on the mock response");
assertEquals(200, response.getConnection().getResponseCode(), "The final response code should be 200");
// We want the second request
assertEquals(2, mockWebServer.getRequestCount(), "There should have been two requests");
MockWebServerResources.takeRequestWithTimeout(mockWebServer);
RecordedRequest rr = MockWebServerResources.takeRequestWithTimeout(mockWebServer);
assertNotNull(rr, "The request should have been recorded");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream((int) rr.getBodySize());
rr.getBody().copyTo(byteArrayOutputStream);
assertArrayEquals(expectedContent, byteArrayOutputStream.toByteArray(), "The body bytes should have matched after a " + "retry");
}
use of com.cloudant.http.HttpConnectionResponseInterceptor in project java-cloudant by cloudant.
the class ClientBuilder method build.
/**
* Build the {@link CloudantClient} instance based on the endpoint used to construct this
* client builder and the options that have been set on it before calling this method.
*
* @return the {@link CloudantClient} instance for the specified end point and options
*/
public CloudantClient build() {
logger.config("Building client using URL: " + url);
// Build properties and couchdb client
CouchDbProperties props = new CouchDbProperties(url);
props.addRequestInterceptors(USER_AGENT_INTERCEPTOR);
if (this.iamApiKey != null) {
// Create IAM cookie interceptor and set in HttpConnection interceptors
IamCookieInterceptor cookieInterceptor = new IamCookieInterceptor(this.iamApiKey, this.url.toString());
props.addRequestInterceptors(cookieInterceptor);
props.addResponseInterceptors(cookieInterceptor);
logger.config("Added IAM cookie interceptor");
} else // Create cookie interceptor
if (this.username != null && this.password != null) {
// make interceptor if both username and password are not null
// Create cookie interceptor and set in HttpConnection interceptors
CookieInterceptor cookieInterceptor = new CookieInterceptor(username, password, this.url.toString());
props.addRequestInterceptors(cookieInterceptor);
props.addResponseInterceptors(cookieInterceptor);
logger.config("Added cookie interceptor");
} else {
// If username or password is null, throw an exception
if (username != null || password != null) {
// Username and password both have to contain values
throw new CouchDbException("Either a username and password must be provided, or " + "both values must be null. Please check the credentials and try again.");
}
}
// If setter methods for read and connection timeout are not called, default values
// are used.
logger.config(String.format("Connect timeout: %s %s", connectTimeout, connectTimeoutUnit));
logger.config(String.format("Read timeout: %s %s", readTimeout, readTimeoutUnit));
// Log a warning if the DNS cache time is too long
try {
boolean shouldLogValueWarning = false;
boolean isUsingDefaultTTLValue = true;
String ttlString = Security.getProperty("networkaddress.cache.ttl");
// Was able to access the property
if (ttlString != null) {
try {
int ttl = Integer.parseInt(ttlString);
isUsingDefaultTTLValue = false;
logger.finest("networkaddress.cache.ttl was " + ttl);
if (ttl > 30 || ttl < 0) {
shouldLogValueWarning = true;
}
} catch (NumberFormatException nfe) {
// Suppress the exception, this will result in the default being used
logger.finest("networkaddress.cache.ttl was not an int.");
}
}
if (isUsingDefaultTTLValue && System.getSecurityManager() != null) {
// If we're using a default value and there is a SecurityManager we need to warn
shouldLogValueWarning = true;
}
if (shouldLogValueWarning) {
logger.warning("DNS cache lifetime may be too long. DNS cache lifetimes in excess" + " of 30 seconds may impede client operation during cluster failover.");
}
} catch (SecurityException e) {
// Couldn't access the property; log a warning
logger.warning("Permission denied to check Java DNS cache TTL. If the cache " + "lifetime is too long cluster failover will be impeded.");
}
props.addRequestInterceptors(new TimeoutCustomizationInterceptor(connectTimeout, connectTimeoutUnit, readTimeout, readTimeoutUnit));
// Set connect options
props.setMaxConnections(maxConnections);
props.setProxyURL(proxyURL);
if (proxyUser != null) {
// if there was proxy auth information set up proxy auth
if ("http".equals(url.getProtocol())) {
// If we are using http, create an interceptor to add the Proxy-Authorization header
props.addRequestInterceptors(new ProxyAuthInterceptor(proxyUser, proxyPassword));
logger.config("Added proxy auth interceptor");
} else {
// Set up an authenticator
props.setProxyAuthentication(new PasswordAuthentication(proxyUser, proxyPassword.toCharArray()));
}
}
if (isSSLAuthenticationDisabled) {
props.addRequestInterceptors(SSLCustomizerInterceptor.SSL_AUTH_DISABLED_INTERCEPTOR);
logger.config("SSL authentication is disabled");
}
if (authenticatedModeSSLSocketFactory != null) {
props.addRequestInterceptors(new SSLCustomizerInterceptor(authenticatedModeSSLSocketFactory));
logger.config("Added custom SSL socket factory");
}
// Set http connection interceptors
if (requestInterceptors != null) {
for (HttpConnectionRequestInterceptor requestInterceptor : requestInterceptors) {
props.addRequestInterceptors(requestInterceptor);
logger.config("Added request interceptor: " + requestInterceptor.getClass().getName());
}
}
if (responseInterceptors != null) {
for (HttpConnectionResponseInterceptor responseInterceptor : responseInterceptors) {
props.addResponseInterceptors(responseInterceptor);
logger.config("Added response interceptor: " + responseInterceptor.getClass().getName());
}
}
// if no gsonBuilder has been provided, create a new one
if (gsonBuilder == null) {
gsonBuilder = new GsonBuilder();
logger.config("Using default GSON builder");
} else {
logger.config("Using custom GSON builder");
}
// always register additional TypeAdapaters for derserializing some Cloudant specific
// types before constructing the CloudantClient
gsonBuilder.registerTypeAdapter(DeserializationTypes.SHARDS, new ShardDeserializer()).registerTypeAdapter(DeserializationTypes.INDICES, new IndexDeserializer()).registerTypeAdapter(DeserializationTypes.PERMISSIONS_MAP, new SecurityDeserializer()).registerTypeAdapter(Key.ComplexKey.class, new Key.ComplexKeyDeserializer());
return new CloudantClient(props, gsonBuilder);
}
Aggregations