Change socket timeout for grails 3 RestBuilder client post request - grails

I have a grails 3 application using the grails.plugins.rest.client plugin to make calls to another API.The api performs some actions and responds in 40 to 50 seconds. The grails application timeout and returns a server error in 30 seconds. How can I change the timeout to wait for a response 60 seconds. My code is as follows:
import grails.plugins.rest.client.RestBuilder
import grails.plugins.rest.client.RestResponse
private RestBuilder rest = new RestBuilder()
RestResponse resp = rest.post(url) {
header 'Accept', "application/json"
json(data)
}
// more code

If you want to increase the socket timeout the grails rest client supports two options.
connectionTimeout
readTimeout
These options are to be set on the RestBuilder when it is instantiated. You cannot change this for request
types which is a shame.
To set them, you can use the below format. But bear in mind that it requires a change to the code and rebuilding the war file.
private RestBuilder rest = new RestBuilder(readTimeout: 180000, proxy: Proxy.NO_PROXY)
180000 = 180 seconds = 3 mins readTimeout
You can also set the connectionTimeout here, but most likely you will be limited by the connectiontimeOut property on the servlet container like Tomcat or CDN like cloudfront or cloudflare.
If you are also using tomcat, you may need to increase the async timeout like in the below example in directive. Hope this helps others looking to solve the same problem.
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
asyncTimeout="60000"
redirectPort="8443" />

Related

Flickering HttpClient sometimes throwing IOException

I'm using java.net.http.HttpClient.newHttpClient() under Java 19 (Temurin) and perform sendAsync(...) requests from different treads on the same instance. I assume this is ok, as the javadoc states:
Once built, an HttpClient is immutable...
However, some requests fail with:
java.io.IOException: HTTP/1.1 header parser received no bytes
The weird thing is, it depends on the speed of my requests:
Requests every 5 seconds: 30% failure
Requests every 3 seconds: 0% failure
I've written a test for it:
private final HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://..."))
.setHeader("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofByteArray("[]".getBytes()))
.build();
#ParameterizedTest
#ValueSource(ints = {3, 5})
void httpClientTest(int intervalSeconds) throws Exception {
HttpClient httpClient = HttpClient.newHttpClient();
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofByteArray()).get();
Thread.sleep(Duration.ofSeconds(intervalSeconds));
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofByteArray()).get();
Thread.sleep(Duration.ofSeconds(intervalSeconds));
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofByteArray()).get();
Thread.sleep(Duration.ofSeconds(intervalSeconds));
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofByteArray()).get();
Thread.sleep(Duration.ofSeconds(intervalSeconds));
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofByteArray()).get();
}
I've already tried the following:
Doing the same with curl on the command line. No requests fail whatever interval I try. So it's probably not a problem with the server.
Running the tests multiple times in parallel. Still the 5-second-intervals fail (then multiple times in parallel). So it's probably not a problem with the server.
Creating an HttpClient.newHttpClient() for every request. No requests fail whatever interval. So it's probably not a problem with the server but with an internal state of the HttpClient (although it claims to be immutable?).
Do you have an idea what I could do, without needing to create a new HttpClient for every request?
Here is the answer for the record: the java.net.HttpClient has a long default HTTP/1.1 keepAlive time, which is longer than what usual servers are configured with. This often results in the server closing idle HTTP/1.1 connections before the client does. If the server closes the connection at about the same time than the client tries to reuse it, some IOException might get raised.
If such exceptions are observed too frequently applications should consider adapting the default keepAlive time in the client to some value shorter than what the servers it connects to are using.
A default value for the HttpClient HTTP/1.1 keepAlive time can be specified on the command line with: -Djdk.httpclient.keepalive.timeout=duration-in-seconds
So for instance - if a server is configured with a keepAlive time of 5s, you could consider supplying -Djdk.httpclient.keepalive.timeout=3 or -Djdk.httpclient.keepalive.timeout=4 on the client's java command line.

RestSharp Timeout

I have an issue with RestClient response is coming back as
"StatusCode: 0, Content-Type: , Content-Length: )"
with the ErrorMessage of
"The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing."
Is this a timeout on the url's end or my httpclient? Is request.timeout correct? It might take 5+ minutes for this request even though it's only 170KB of data due to their end being poorly optimized.
var client = new RestClient(url);
RestRequest request = new RestRequest() { Method = Method.Get };
request.Timeout = 300000;
request.AddParameter("access_token", AccessToken);
request.AddParameter("start_date", StartDate.ToString("yyyy-MM-dd"));
request.AddParameter("end_date", EndDate.ToString("yyyy-MM-dd"));
request.AddParameter("offset", offset.ToString());
var response = await client.ExecuteAsync(request);
var responseWorkLoads = JObject.Parse(response.Content).SelectToken("worklogs");
There are two timeouts that RestSharp allows you to set.
When you create a new instance of RestClient, you can specify the HttpClient timeout that will override the default 100 ms using RestOptions:
var client = new RestClient(new RestClientOptions { Timeout = 300000 });
As the wrapped HttpClient is instantiated and configured once per RestClient instance, setting the request timeout doesn't override that setting, otherwise the client won't be thread-safe.
The request timeout, on the other hand, overrides the client timeout if it is less than the client timeout. RestSharp creates a cancellation token source using the request timeout, so the request will be cancelled when the linked cancellation token cancels.
I believe that currently RestClient also doesn't set the failure reason properly when the client times out, only if the token gets cancelled. I will create an issue for that.

Setting maximum http connections for Apache fluent executor

Apache fluent API allows simple one-line http calls like:
String content = Request.Get(url).execute().returnContent().asString();
Which is executed by a ...fluent.Executor, whose javadoc says:
A PoolingHttpClientConnectionManager with maximum 100 connections per route and a total maximum of 200 connections is used internally
I would like to change the maximum connection parameters to be used for a specific call, but I can't find a way to access the connection manager used by the above code. I have tried:
Executor.newInstance().execute(Request.Get("")).returnContent().asString();
but there is no way to change these parameters on the Executor returned by Executor.newInstance().
Is there is way to use the fluent API but with custom maximum connection values?
One can bind an instance of the fluent executor to an arbitrary HttpClient instance
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(cm)
.build();
cm.setDefaultMaxPerRoute(15);
Executor.newInstance(client).execute(Request.Get("/")).discardContent();

Jersey Client opens too many Connections

i run in some problems with my jersey rest api and a client.
This is how im using the methods on a server side:
#POST
#Path("/seed")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response addSeed(Seed seed) throws InterruptedException {
if (!Validator.isValidSeed(seed)) {
return Response.status(400).entity("{\"message\":\"Please verify your JSON!\", \"stat\":\"failed\"}")
.build();
}
save(seed);
return Response.status(200).build();
}
If i run a Jersey client in a while(true) loop, there are connections open and won't close. So im running into a problem i have a lot of connections open and my network crashes. So i can't use my server any more. After the connections are closed i can connect to the server.
This is a client:
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource service = client.resource(getBaseURI()).path("api/seed");
while (true) {
ClientResponse cr = service.header("Content-Type", "application/json").post(ClientResponse.class, seed);
System.out.println(cr);
cr.close();
My Questions are:
What can i do on the server side, to prevent clients open a new connection?
How can i specify a max number of connections?
And how should i implement the jersey client to reuse open connection?
I don't know of a way to limit Jersey resources at the web-app level. If you upgrade to GlassFish EE, you can make your resources EJBs #Stateless #StatelessDeployment(maxInstances=16)
The pile up of connections could be because of Keep-Alive settings. In Tomcat 6 there are two you can tune your connector with:
maxKeepAliveRequests, which defaults to 100. It's the maximum number of HTTP requests which can be pipelined until the connection is closed by the server. Setting this attribute to 1 will disable HTTP/1.0 keep-alive, as well as HTTP/1.1 keep-alive and pipelining. Setting this to -1 will allow an unlimited amount of pipelined or keep-alive HTTP requests.
keepAliveTimeout, which defaults to connectionTimeout which defaults to 60k ms. It it the number of milliseconds this Connector will wait for another HTTP request before closing the connection.

Supporting the "Expect: 100-continue" header with ASP.NET MVC

I'm implementing a REST API using ASP.NET MVC, and a little stumbling block has come up in the form of the Expect: 100-continue request header for requests with a post body.
RFC 2616 states that:
Upon receiving a request which
includes an Expect request-header
field with the "100-continue" expectation, an origin server MUST
either respond with 100 (Continue) status and continue to read
from the input stream, or respond with a final status code. The
origin server MUST NOT wait for the request body before sending
the 100 (Continue) response. If it responds with a final status
code, it MAY close the transport connection or it MAY continue
to read and discard the rest of the request. It MUST NOT
perform the requested method if it returns a final status code.
This sounds to me like I need to make two responses to the request, i.e. it needs to immediately send a HTTP 100 Continue response, and then continue reading from the original request stream (i.e. HttpContext.Request.InputStream) without ending the request, and then finally sending the resultant status code (for the sake of argument, lets say it's a 204 No Content result).
So, questions are:
Am I reading the specification right, that I need to make two responses to a request?
How can this be done in ASP.NET MVC?
w.r.t. (2) I have tried using the following code before proceeding to read the input stream...
HttpContext.Response.StatusCode = 100;
HttpContext.Response.Flush();
HttpContext.Response.Clear();
...but when I try to set the final 204 status code I get the error:
System.Web.HttpException: Server cannot set status after HTTP headers have been sent.
The .NET framework by default always sends the expect: 100-continue header for every HTTP 1.1 post. This behavior can be programmatically controlled per request via the System.Net.ServicePoint.Expect100Continue property like so:
HttpWebRequest httpReq = GetHttpWebRequestForPost();
httpReq.ServicePoint.Expect100Continue = false;
It can also be globally controlled programmatically:
System.Net.ServicePointManager.Expect100Continue = false;
...or globally through configuration:
<system.net>
<settings>
<servicePointManager expect100Continue="false"/>
</settings>
</system.net>
Thank you Lance Olson and Phil Haack for this info.
100-continue should be handled by IIS. Is there a reason why you want to do this explicitly?
IIS handles the 100.
That said, no it's not two responses. In HTTP, when the Expect: 100-continue comes in as part of the message headers, the client should be waiting until it receives the response before sending the content.
Because of the way asp.net is architected, you have little control over the output stream. Any data that gets written to the stream is automatically put in a 200 response with chunked encoding whenever you flush, be it that you're in buffered mode or not.
Sadly all this stuff is hidden away in internal methods all over the place, and the result is that if you rely on asp.net, as does MVC, you're pretty much unable to bypass it.
Wait till you try and access the input stream in a non-buffered way. A whole load of pain.
Seb

Resources