Not getting response with Http Async Client - apache

I am stuck with this weird situation where sometimes my HTTP requests don't go out or I don't get a HTTP response to my request sporadically. My application makes several (100s) http requests to other 3rd party service periodically most of which work absolutely fine.
I use the CloseableHttpAsyncClient (Version 4.0) with a custom HttpRequestIntercerptor and HttpResponseInterceptor. These were mainly added for debugging purpose with the RequestInterceptor is the last interceptor in the chain and the ResponseInterceptor is the first one. The idea was to log each http request at the last stage before it sends the actual request and to log each http response when it is first received.
I have the following pattern to setup the async client:
HttpAsyncClientBuilder asyncClientBuilder = HttpAsyncClientBuilder.create();
asyncClientBuilder.addInterceptorLast(new MyHttpRequestInterceptor());
asyncClientBuilder.addInterceptorFirst(new MyHttpResponseInterceptor());
IOReactorConfig reactorConfig = IOReactorConfig.DEFAULT;
reactorConfig.setConnectTimeout(5 * 60 * 1000); // 5 mins
reactorConfig.setSoTimeout(5 * 60 * 1000); // 5 mins
asyncClientBuilder.setDefaultIOReactorConfig(reactorConfig);
System.setProperty("http.maxConnections", "100");
this.asyncHttpClient = asyncClientBuilder.useSystemProperties().build();
this.asyncHttpClient.start();
To make the request I do:
HttpGet httpGet = new HttpGet("some url");
asyncHttpClient.execute(httpGet, new AsyncHTTPResponseHandler(requestMetadata));
Here is my AsyncHTTPResponseHandler class:
class AsyncHTTPResponseHandler implements FutureCallback<HttpResponse> {
// local copy of the request for reference while processing the response.
private RequestMetadata requestMetadata;
public AsyncHTTPResponseHandler(final RequestMetadata requestMetadata) {
this.setRequestMetadata(requestMetadata);
Thread.currentThread().setUncaughtExceptionHandler(new HttpUncaughtExceptionHandler(requestMetadata));
}
#Override
public void cancelled() {
logger.error("AsyncHTTPResponseHandler#Http request id: {} cancelled",
requestMetadata.getRequestId()));
}
#Override
public void completed(HttpResponse response) {
logger.debug("Received HTTP Response for request id: {}",
requestMetadata.getRequestId());
//handleHttpResponse(requestMetadata, response);
}
#Override
public void failed(Exception e) {
logger.error("AsyncHTTPResponseHandler#Error in Http request id: " + requestMetadata.getRequestId(), e);
}
}
Based on this setup, I see the following cases based on my interceptors logs:
1. My application http request triggers an asyncclient HttpRequest and I get the HttpResponse -- Success.
2. My application http request triggers an asyncclient HttpRequest (the interceptor logs it) and I don't get the HttpResponse for this request --- Don't know why?
3. My application http request does not trigger an asyncclient HttpRequest (the interceptor does not log it) and I don't get the HttpResponse for this request --- Don't know why?
Any tips or suggestions on what I can do fix this or debug this problem further?
Thanks!!

So, thought I will share my findings and solution here.
We were experiencing symptoms similar to this bug: https://issues.apache.org/jira/browse/HTTPASYNC-79
If you enable DEBUG logging for "org.apache.http.impl.nio" package, then you can see the exchanges. Note: The logs will be very verbose.
The issue was resolved by upgrading the HttpAsyncClient library from 4.0 to 4.0.2. I have also enabled socket and Connection timeouts. You should see timeout exceptions in the log files with this.
Here is how my HttpAsyncClient instance looks now:
HttpAsyncClientBuilder asyncClientBuilder = HttpAsyncClientBuilder.create();
asyncClientBuilder.addInterceptorLast(new MyHttpRequestInterceptor());
asyncClientBuilder.addInterceptorFirst(new MyHttpResponseInterceptor());
// reactor config
IOReactorConfig reactorConfig = IOReactorConfig.custom()
.setConnectTimeout(TIMEOUT_5_MINS_IN_MILLIS)
.setSoTimeout(TIMEOUT_5_MINS_IN_MILLIS).build();
asyncClientBuilder.setDefaultIOReactorConfig(reactorConfig);
// request config
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(TIMEOUT_5_MINS_IN_MILLIS)
.setConnectionRequestTimeout(TIMEOUT_5_MINS_IN_MILLIS)
.setSocketTimeout(TIMEOUT_5_MINS_IN_MILLIS).build();
asyncClientBuilder.setDefaultRequestConfig(requestConfig);
// connection config
ConnectionConfig connectionConfig = ConnectionConfig.custom()
.setMalformedInputAction(CodingErrorAction.IGNORE)
.setUnmappableInputAction(CodingErrorAction.IGNORE)
.build();
asyncClientBuilder.setDefaultConnectionConfig(connectionConfig);
System.setProperty("http.maxConnections", "100");
System.setProperty("http.conn-manager.timeout", "300000"); // 5 mins
this.asyncHttpClient = asyncClientBuilder.useSystemProperties().build();

Related

HTTPClient intermittently locking up server

I have a .NET Core 2.2 app which has a controller acting as a proxy to my APIs.
JS makes a fetch to the proxy, proxy forwards call onto API's and returns response.
I am experiencing intermittent lock ups on the proxy app when its awaiting the response from the HttpClient. When this happens it locks up the entire server. No more requests will be processed.
According to the logs of the API that is being proxied to it is returning fine.
To reproduce this is i have to make 100+ requests in a loop on the client through the proxy. Then i have to reload the page multiple times, reloading it whilst the 100 requests are in flight. It usually takes around 5 hits before things start slowing down.
The proxy will lock up waiting for an awaited request to resolve. Sometimes it comes back after a 4 - 5 second delay, other times after a minuet. Most of the time i haven't waited longer then 10 min before giving up and killing the proxy.
I've distilled the code down to the following block that will reproduce the issue.
I believe im following best practices, its async all the way down, im using IHttpClientFactory to enable sharing of HttpClient instances, im implementing using where i believe it is required.
The implementation was based on this: https://github.com/aspnet/AspLabs/tree/master/src/Proxy
I'm hoping im making a rather obvious mistake that others with more experience can pin point!
Any help would be greatly appreciated.
namespace Controllers
{
[Route("/proxy")]
public class ProxyController : Controller
{
private readonly IHttpClientFactory _factory;
public ProxyController(IHttpClientFactory factory)
{
_factory = factory ?? throw new ArgumentNullException(nameof(factory));
}
[HttpGet]
[Route("api")]
async public Task ProxyApi(CancellationToken requestAborted)
{
// Build API specific URI
var uri = new Uri("");
// Get headers frpm request
var headers = Request.Headers.ToDictionary(x => x.Key, y => y.Value);
headers.Add(HeaderNames.Authorization, $"Bearer {await HttpContext.GetTokenAsync("access_token")}");
// Build proxy request method. This is within a service
var message = new HttpRequestMessage();
foreach(var header in headers) {
message.Headers.Add(header.Key, header.Value.ToArray());
}
message.RequestUri = uri;
message.Headers.Host = uri.Authority;
message.Method = new HttpMethod(Request.Method);
requestAborted.ThrowIfCancellationRequested();
// Generate client and issue request
using(message)
using(var client = _factory.CreateClient())
// **Always hangs here when it does hang**
using(var result = await client.SendAsync(message, requestAborted).ConfigureAwait(false))
{
// Appy data from request onto response - Again this is within a service
Response.StatusCode = (int)result.StatusCode;
foreach (var header in result.Headers)
{
Response.Headers[header.Key] = header.Value.ToArray();
}
// SendAsync removes chunking from the response. This removes the header so it doesn't expect a chunked response.
Response.Headers.Remove("transfer-encoding");
requestAborted.ThrowIfCancellationRequested();
using (var responseStream = await result.Content.ReadAsStreamAsync())
{
await responseStream.CopyToAsync(responseStream, 81920);
}
}
}
}
}
EDIT
So modified the code to remove the usings and return the proxied response directly as a string instead of streaming and still getting the same issues.
When running netstat i do see a lot of logs for the url of the proxied API.
4 rows mention the IP of the API being proxied to, probably about another 20 rows mentions the IP of the proxy site. Those numbers dont seem odd to me but i don't have much experience using netstat (first time ive ever fired it up).
Also i have left the proxy running for about 20 min. Its it technically still alive. Responses are coming back. Just taking a very long time between the API being proxied to returning data and the HttpClient resolving. However it wont service any new requests, they just sit there hanging.

IllegalArgumentException: "Auth scheme may not be null" in CloseableHttpAsyncClient

I'm running some asynchronous GET requests using a proxy with authentication. When doing HTTPS requests, I'm always running into an exception after 2 successful asyncronous requests:
java.lang.IllegalArgumentException: Auth scheme may not be null
When executing the GET requests without a proxy, or using http instead of https, the exception never occurred.
Example from Apache HttpAsyncClient Examples
HttpHost proxy = new HttpHost("proxyname", 3128);
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(proxy), new UsernamePasswordCredentials("proxyuser", "proxypass"));
CloseableHttpAsyncClient httpClient = HttpAsyncClients.custom().setDefaultCredentialsProvider(credsProvider).build();
httpClient.start();
RequestConfig config = RequestConfig.custom().setProxy(proxy).build();
for (int i = 0; i < 3; i++) {
HttpGet httpGet = new HttpGet(url);
httpGet.setConfig(config);
httpClient.execute(httpGet, new FutureCallback<HttpResponse>() {
public void failed(Exception ex) {
ex.printStackTrace(); // Exception occures here afther 2nd iteration
}
public void completed(HttpResponse result) {
// works for the first and second iteration
}
public void cancelled() {
}
});
}
httpClient.close();
If I run the code above with 'http://httpbin.org/get', there is no exception, but if I run it with 'https://httpbin.org/get', I get the following exception after 2 successful requests:
java.lang.IllegalArgumentException: Auth scheme may not be null
at org.apache.http.util.Args.notNull(Args.java:54)
at org.apache.http.impl.client.AuthenticationStrategyImpl.authSucceeded(AuthenticationStrategyImpl.java:215)
at org.apache.http.impl.client.ProxyAuthenticationStrategy.authSucceeded(ProxyAuthenticationStrategy.java:44)
at org.apache.http.impl.auth.HttpAuthenticator.isAuthenticationRequested(HttpAuthenticator.java:88)
at org.apache.http.impl.nio.client.MainClientExec.needAuthentication(MainClientExec.java:629)
at org.apache.http.impl.nio.client.MainClientExec.handleResponse(MainClientExec.java:569)
at org.apache.http.impl.nio.client.MainClientExec.responseReceived(MainClientExec.java:309)
at org.apache.http.impl.nio.client.DefaultClientExchangeHandlerImpl.responseReceived(DefaultClientExchangeHandlerImpl.java:151)
at org.apache.http.nio.protocol.HttpAsyncRequestExecutor.responseReceived(HttpAsyncRequestExecutor.java:315)
at org.apache.http.impl.nio.DefaultNHttpClientConnection.consumeInput(DefaultNHttpClientConnection.java:255)
at org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:81)
at org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:39)
at org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:121)
at org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:162)
at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:337)
at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:315)
at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:276)
at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104)
at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:591)
at java.lang.Thread.run(Thread.java:748)
Note: I'm using httpasyncclient 4.1.4
If this is the exact code you have been executing then the problem is quite apparent. Welcome to the world of even-driven programming.
Essentially what happens is the following:
The client initiates 3 message exchanges by submitting 3 requests to the client execution pipeline in a tight loop
3 message exchanges get queued up for execution
The loop exits
Client shutdown is initiated
Now the client is racing to execute 3 initiated message exchanges and to shut itself down at the same time
If one is lucky and the target server is fast enough one might get all 3 exchanges before the client shuts down its i/o event processing threads
If unlucky or when the request execution is relatively slow, for instance due, to the use of TLS transport security, some of message exchanges might get terminated in the middle of the process. This is the reason you are seeing the failure when using https scheme but not http.

IBM MobileFirst 8.0 JavaAdapter successStateExpirationSec callback

Hi my resource adapter is like this
#Path("/branches")
public class MyResourceAdapter {
....
#GET
#Produces(MediaType.TEXT_PLAIN)
#Path("/getDetails")
#OAuthSecurity(scope = "getDetailsAuthScope")
public String getDetails() throws Exception {
String url = "...some url which returns data---";
HttpGet request = new HttpGet(url);
CloseableHttpClient client = HttpClients.createDefault();
CloseableHttpResponse response = client.execute(request);
HttpEntity entity = response.getEntity();
String responseString = EntityUtils.toString(entity);
return responseString;
}
}
I have set the successStateExpirationSec as 40 seconds. And in my javascript i am invoking the adapter like
function getData(type) {
alert(type);
var req = new WLResourceRequest('/adapters/LoginAdapter/branches/getDetails', WLResourceRequest.GET);
return req.send().then(function (response) {
alert(JSON.stringify(response));
return response.responseJSON;
});
}
Just to test this if have
setTimeout(function () {
getData('60 timeout');
}, 60000);
setTimeout(function () {
getData('20 timeout');
}, 20000);
During logout
WLAuthorizationManager.logout(securityCheckName);
In brief, After 20 seconds i am making a Http request and i am getting the response. After 40 seconds the session expires. After 60 seconds i am making the Http request again and i did not get any response which is as expected.
But i need to find a way to logout the app when the session expires. Is there any callbacks for this? Also after logging in i get the response from the previous hit.
Can anyone help me on this?
If you knew the session expiration time is 40 seconds, You can set the security check expiration time to the same. So, you don't need to perform logout operation as the security check expired after the specified amount of time.
The following are steps to override the default expiration time of security check.
Open MFP Operations console & login.
Navigate to your app for a particular version and open security tab
In Security-Check Configurations, select the security check name and override the expiration time out to desired time out value as shown in the below figure.

Apache Http Client Put Request Error

I'm trying to upload a file using the Apache Http Client's PUT method. The code is as below;
def putFile(resource: String, file: File): (Int, String) = {
val httpClient = new DefaultHttpClient(connManager)
httpClient.getCredentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(un, pw))
val url = address + "/" + resource
val put = new HttpPut(url)
put.setEntity(new FileEntity(file, "application/xml"))
executeHttp(httpClient, put) match {
case Success(answer) => (answer.getStatusLine.getStatusCode, "Successfully uploaded file")
case Failure(e) => {
e.printStackTrace()
(-1, e.getMessage)
}
}
}
When I tried running the method, I get to see the following error:
org.apache.http.NoHttpResponseException: The target server failed to respond
at org.apache.http.impl.conn.DefaultResponseParser.parseHead(DefaultResponseParser.java:101)
at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:252)
at org.apache.http.impl.AbstractHttpClientConnection.receiveResponseHeader(AbstractHttpClientConnection.java:281)
at org.apache.http.impl.conn.DefaultClientConnection.receiveResponseHeader(DefaultClientConnection.java:247)
at org.apache.http.impl.conn.AbstractClientConnAdapter.receiveResponseHeader(AbstractClientConnAdapter.java:219)
at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:298)
at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
at org.apache.http.impl.client.DefaultRequestDirector.tryExecute(DefaultRequestDirector.java:633)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:454)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820)
I do not know what has gone wrong? I'm able to do GET requests, but PUT seems not to work! Any clues as to where I should look for?
Look on the server. If GET Works, but PUT does not, then you have to figure out the receiving end.
Also, you may want to write a simple HTML File that has a form with PUT Method in it to rule out your Java Part.
As a sidenode: Its technically possible that something in between stops the request from going through or the response reaching you. Best setup a dummy HTTP Server to do the testing against.
Maybe its also a timeout issue, so the server takes to long to process your PUT.
The connection you are trying to use is a stale connection and therefore the request is failing.
But why are you only seeing an error for the PUT request and you are not seeing it for the GET request?
If you check the DefaultHttpRequestRetryHandler class you will see that by default HttpClient attempts to automatically recover from I/O exceptions. The default auto-recovery mechanism is limited to just a few exceptions that are known to be safe.
HttpClient will make no attempt to recover from any logical or HTTP protocol errors (those derived from HttpException class).
HttpClient will automatically retry those methods that are assumed to be idempotent. Your GET request, but not your PUT request!!
HttpClient will automatically retry those methods that fail with a transport exception while the HTTP request is still being transmitted to the target server (i.e. the request has not been fully transmitted to the server).
This is why you don't notice any error with your GET request, because the retry mechanism handles it.
You should define a CustomHttpRequestRetryHandler extending the DefaultHttpRequestRetryHandler. Something like this:
public class CustomHttpRequestRetryHandler extends DefaultHttpRequestRetryHandler {
#Override
public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
if(exception instanceof NoHttpResponseException) {
return true;
}
return super.retryRequest(exception, executionCount, context);
}
}
Then just assign your CustomHttpRequestRetryHandler
final HttpClientBuilder httpClientBuilder = HttpClients.custom();
httpClientBuilder.setRetryHandler(new CustomHttpRequestRetryHandler());
And that's it, now your PUT request is handled by your new RetryHandler (like the GET was by the default one)

Get response and raw request when using proxies and resteasy client side framework 3.0.4

// Some setup steps
ResteasyProviderFactory factory = new ResteasyProviderFactory();
factory.registerProvider(com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider.class);
Client client = ClientBuilder.newClient(new ClientConfiguration(factory));
WebTarget target = client.target(webappURL.toURI() + "api/v1");
resteasyWebTarget = (ResteasyWebTarget) target;
// the real request
MyApiController myApiController = resteasyWebTarget.proxy(MyApiController.class);
ClientResponse response = (ClientResponse) myApiController.doSomeStuff();
The code above works great, but I want to really know what is going on in terms of real http request and real http response when
myApiController.doSomeStuff();
is executed.
I am wondering what the best way is to catch and log the "raw" request and a catch and log the "raw" http response. I am only interested in solutions for resteasy-client 3.0.2.Final or similar...
Thanks!
Not sure how to get it if everything went well (response code 200), but in case the server returned anything else, a sub type of ClientErrorException which gives you access to the response / status code / entity (message body) etc is thrown.
try {
myApiController.doSomeStuff();
} catch (BadRequestException ce) {
// Handle
} catch (ClientErrorException e) {
MyErrorObject obj = ce.getResponse().readEntity(MyErrorObject.class);
// Handle
}