What can cause HTTP GET to localhost to timeout when the server thinks it responded? - apache-httpclient-4.x

I have a Java application running in AWS Beanstalk (Tomcat 8.5 + Apache httpd).
At one point the app calls a REST endpoint on localhost.
Occasionally I see a failure such as this in the log:
14:55:45 ... SEVERE: url[http://localhost/detail.api?id=200030599] timing=12.010 ...
That indicates that my CustomRestTemplate gave up waiting for a response, after 12 seconds.
However, looking a few lines up in the log, I see a log entry from the service endpoint:
{
"server_ts": "2020-08-19T14:55:33.890Z",
"remote_ip": "127.0.0.1",
"local_ip": "127.0.0.1",
"method": "GET",
"url": "/detail.api",
"query_string": "?id=200030599",
"protocol": "HTTP/1.1",
"http_status": 200,
"referer": null,
"user_agent": "Apache-HttpClient/4.5.2 (Java/1.8.0_252)",
"time_elapsed": 5,
"thread_name": "http-nio-8080-exec-20",
"host": "localhost",
}
That's my custom servlet logger showing a 5 millisecond response. This is logged from the outer wrapping Servlet Filter.
This problem is recurring but rare enough that I can't reproduce it. So I need to take an intellectual approach... develop a series of hypothesis and a test to disprove each one until the correct one is found.
What are some possible causes?
What have I tried so far
I wrote custom loggers so I could capture the timings shown above. Then I basically hit a brick wall as the timeout happens in some hidden dimension between the response being sent by the "server" (localhost endpoint) and the response being read by the client.
I see that the Apache log (from elasticbeanstack) also shows the local request:
127.0.0.1 (-) - - [19/Aug/2020:14:55:33 +0000] "GET /detail.api?id=200030599 HTTP/1.1" 200 4982 "-" "Apache-HttpClient/4.5.2 (Java/1.8.0_252)"

Finally I was able to reproduce the problem in a local test using JMeter.
Turns out the "hidden dimension" is lurking in a Servlet Filter:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
long started = System.currentTimeMillis();
chain.doFilter(request, response);
long elapsed = System.currentTimeMillis() - started;
log(request, response, elapsed);
}
The log() calls contains a synchronous database insert which slows down and eventually fails under load. I assume the servlet engine keeps the connection open until this filter returns. Investigation moves on to finding a fix now.

Related

Why is my RESTful web service returning error 401?

Update
Thanks to being nudged towards implementing debug logs, I can now see that the issue is that Spring is reporting an invalid CSRF token found for the notices controller. So far I've checked the headers postman generates and compared them to the ones generated through the fetch requests, and, found no difference. The token that was generated is successfully placed into the header of the request. Unfortunately there is nothing discernible in the Spring logs, so the debugging continues.
I'm working on learning Spring Security and currently connecting the frontend React portion to the Spring backend. I'm having trouble because when the POST request is made to the desired endpoint, it returns an error of 401. This is confusing to me because I believe I have correctly configured CORS and also marked the end points as permit all.
In short, the process calls an endpoint /token to get a CSRF token, then calls /notices and passes the token in as a header. If done with Postman, the process works as expected, so I had thought it was a CORS issue, however, I've tried running the frontend on a different port and it was blocked by CORS so I think the issue is somewhere else.
Some additional info:
/notices and /token are both POST operations.
Both the Spring backend and React frontend are ran off the same local machine.
Error code 401 is received
The code for the frontend JavaScript call is:
const debugNotices = () => {
let tokenData:any;
fetch('http://localhost:8080/token', {method:"POST"})
.then((response) => response.json())
.then((data) => tokenData = data).then((data:any) => fetch("http://localhost:8080/notices",
{
method:"POST",
headers: {
"X-XSRF-TOKEN": tokenData.token
}
}))
}
Spring security config:
#Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.cors()
.configurationSource(new CorsConfigurationSource() {
#Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
config.setAllowedMethods(Collections.singletonList(("*")));
config.setAllowCredentials(true);
config.setAllowedHeaders(Collections.singletonList("*"));
config.setMaxAge(3600L);
return config;
}
})
.and()
.csrf()
.ignoringRequestMatchers("/contact", "/register", "/token")
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.securityContext()
.requireExplicitSave(false)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.authorizeHttpRequests()
.requestMatchers("/myAccount", "/myBalance", "/myLoans", "/myCards", "/user").authenticated()
.requestMatchers("/notices", "/contact", "/register", "/test", "/token").permitAll()
.and()
.httpBasic()
.and()
.formLogin();
return http.build();
}
I've tried including
credentials:'include' in the request body, however it causes a login prompt that I don't believe is the direction I'm looking for.
I've also tried manually inserting the CSRF token instead of requesting the data from the server, with the same failed results.
I've also tested CORS as far as I know how to, accessing the endpoints from anything other than localhost:3000 gets denied with a CORS error as expected.
This issue only happens when the React frontend is accessed from localhost:portNumber, I was able to work around the issue completely by instead using my local IP address in place of localhost.
192.168.0.105:3000 for example.
I'm still unsure why there is an issue using localhost in the URL, and, would love to hear why this is happening if you know.

Exception filter NestJS does not work when delegating

I am using a gateway to emit events.
For clarity reasons, I created files that make the link between services and this gateway.
For example, in gateway :
#SubscribeMessage('createRoom')
async createRoom(client: Socket, channel: newChannelDto) {
this.chatService.createRoom(client, channel)
}
In Chat service :
async createRoom(client: Socket, channel: newChannelDto)
{
await this.channelService.createChannel(channel.chanName, client.data.user, channel.password, channel.private)
client.join(channel.chanName)
for (let [allUsers, socket] of this.gateway.activeUsers.entries())
this.gateway._server.to(socket.id).emit('rooms', " get rooms ", await this.channelService.getChannelsForUser(allUsers));
}
where :
gateway is the injected gateway, and _server, declared as
#WebSocketServer()
public _server : Server
the server contained in Gateway.
I made a WS/HTTP/Query failed exception filter, included on top of my Gateway via #UseFilters(new MyCustomExceptionsFilter()).
Before I move functions to the "children" files, my filter was able to catch everything, but since I moved them away from gateway, it does not work anymore ; I am still able to catch them manually, but they are not sent to front anymore.
Example of output in my terminal, that I was once able to catch/send as user-friendly error:
api | Error: this is still thrown if caught manually
api | at /api/src/websocket/chat.service.ts:67:28
api | at processTicksAndRejections (node:internal/process/task_queues:95:5)
api | at ChatService.createRoom (/api/src/websocket/chat.service.ts:66:3)
Without the await in the gateway call, the promise is not properly handled the the lifecycle of the request, as Nest sees it, ends, so when an error happens it is outside of the exception zone that Nest is responsible for. Add await to the this.chatService.createRoom(client, channel) and all should be good from there

How to set up Tomcat for one Database Connection per Request

I have a Sparkjava app which I have deployed on a Tomcat server. It uses SQL2O to interface with the MySQL-database. After some time I start to have trouble connecting to the database. I've tried connecting directly from SQL2O, connecting through HikariCP and connecting through JNDI. They all work for about a day, before I start getting Communications link failure. This app gets hit a handful of times a day at best, so performance is a complete non issue. I want to configure the app to use one database connection per request. How do I go about that?
The app doesn't come online again afterwards until I redeploy it (overwrite ROOT.war again). Restarting tomcat or the entire server does nothing.
Currently every request creates a new Sql2o object and executes the query using withConnection. I'd be highly surprised if I was leaking any connections.
Here's some example code (simplified).
public class UserRepositry {
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
protected Sql2o sql2o = new Sql2o("jdbc:mysql://mysql.server.name/dbname?serverTimezone=UTC", "username", "password");
public List<Users> getUsers() {
return sql2o.withConnection((c, o) -> {
return c.createQuery(
"SELECT\n" +
" id,\n" +
" name\n" +
"FROM users"
)
.executeAndFetch(User.class);
});
}
}
public class Main {
public static void main(String[] args) {
val gson = new Gson();
port(8080);
get("/users", (req, res) -> {
return new UserRepository().getUsers();
}, gson::toJson);
}
}
If you rely on Tomcat to provide the connection to you: It's coming from a pool. Just go with plain old JDBC and open that connection yourself (and make sure to close it as well) if you don't like that.
So much for the answer to your question, to the letter. Now for the spirit: There's nothing wrong with connections coming from a pool. In all cases, it's your responsibility to handle it properly: Get access to a connection and free it up (close) when you're done with it. It doesn't make a difference if the connection is coming from a pool or has been created manually.
As you say performance is not an issue: Note that the creation of a connection may take some time, so even if the computer is largely idle, creating a new connection per request may have a notable effect on the performance. Your server won't overheat, but it might add a second or two to the request turnaround time.
Check configurations for your pool - e.g. validationQuery (to detect communication failures) or limits for use per connection. And make sure that you don't run into those issues because of bugs in your code. You'll need to handle communication errors anyways. And, again, that handling doesn't differ whether you use pools or not.
Edit: And finally: Are you extra extra sure that there indeed is no communication link failure? Like: Database or router unplugged every night to connect the vacuum cleaner? (no pun intended), Firewall dropping/resetting connections etc?

why DeferredResult ends on setResult() on trying to use SSE

i am trying to implement a Server Sent Events (SSE) webpage which is powered by Spring. My test code does the following:
Browser uses EventSource(url) to connect to server. Spring accepts the request with the following controller code:
#RequestMapping(value="myurl", method = RequestMethod.GET, produces = "text/event-stream")
#ResponseBody
public DeferredResult<String> subscribe() throws Exception {
final DeferredResult<String> deferredResult = new DeferredResult<>();
resultList.add(deferredResult);
deferredResult.onCompletion(() -> {
logTimer.info("deferedResult "+deferredResult+" completion");
resultList.remove(deferredResult);
});
return deferredResult;
}
So mainly it puts the DeferredResult in a List and register a completion callback so that i can remove this thing from the List in case of completion.
Now i have a timer method, that will periodically output current timestamp to all registered "browser" via their DeferredResults.
#Scheduled(fixedRate=10000)
public void processQueues() {
Date d = new Date();
log.info("outputting to "+ LoginController.resultList.size()+ " connections");
LoginController.resultList.forEach(deferredResult -> deferredResult.setResult("data: "+d.getTime()+"\n\n"));
}
The data is sent to the browser and the following client code works:
var source = new EventSource('/myurl');
source.addEventListener('message', function (e) {
console.log(e.data);
$("#content").append(e.data).append("<br>");
});
Now the problem:
The completion callback on the DeferredResult is called on every setResult() call in the timer thread. So for some reason the connection is closed after the setResult() call. SSE in the browser reconnects as per spec and then same thing again. So on client side i have a polling behavior, but i want an kept open request where i can push data on the same DeferredResult over and over again.
Do i miss something here? Is DeferredResult not capable of sending multiple results? i put in a 10 seconds delay in the timer thread to see if the request only terminates after setResult(). So in the browser the request is kept open until the timer pushes the data but then its closed.
Thanks for any hint on that. One more note: I added async-supported to all filters/servlets in tomcat.
Indeed DeferredResult can be set only once (notice that setResult returns a boolean). It completes processing with the full range of Spring MVC processing options, i.e. meaning that all you know about what happens during a Spring MVC request remains more or less the same, except for the asynchronously produced return value.
What you need for SSE is something more focused, i.e. write each value to the response using an HttpMessageConverter. I've created a ticket for that https://jira.spring.io/browse/SPR-12212.
Note that Spring's SockJS support does have an SSE transport which takes care of a few extras such as cross-domain requests with cookies (important for IE). It's also used on top of a WebSocket API and WebSocket-style messaging (even if WebSocket is not available on either the client or the server side) which fully abstracts the details of HTTP long polling.
As a workaround you can also write directly to the Servlet response using an HttpMessageConverter.

Unable to send lenghty JSON string to ASP.NET HttpHandler using jquery ajax call (IIS 6)

BACKGROUND:
I have an Http handler which receives SIM/CDMA numbers ('8953502103000101242') in json string format from jQuery Ajax call using http POST method to activate them on server.
THE PROBLEM:
On Local Development Environment I can send up to 65K SIMS' Numbers (8953502103000101242 key/value pair in json string) BUT when I deploy to LIVE server then I faced the following problem.
If I send 2000 SIMS numbers separated by comma to Http handler then Http handlers receives http request successfully But when I send more than that (3000, 4000, 5000 SIM numbers in json string) then http request doesn’t reach to HttpHandler at server even after few hours.
If I send 5000 sim numbers in http POST using jquery then the total size request sent is 138.0 KB. and it should be passed to server because server maxRequestLength is 2048576. but it is not being sent to server when we deploy it on live server and it work fine on local development environment.
TRIED SOLUTION:
I tried to resolve the problem by editing httpRuntime configuration in web.config as follows
By using above httpRuntime configuration I noticed in Firefox firebug net state that It keeps waiting sending the request as executionTimeout="7200" but if executionTimeout="600" then it returns timeout error.
ITENDED SOLUTION:
I think if I try to sync above httpRuntime element in Machine.config file as well then it work fine.
REQUIRED SOLUIOTN
WHAT CAN THE BE PROBLEM AND HOW TO RESOLVE IT. PLEASE SUGGEST IN THIS REGARD. WAITING FOR A QUICK SOLUTION.
Jquery call to HttpHandler is as follows:
$.ajax({
type: "POST",
url: "../../HttpHandlers/PorthosCommonHandler.ashx",
data: { order: JSON.stringify(orderObject), method: "ValidateOrderInput", orgId: sCId, userId: uId, languageId: lId },
success: function(response) {
var orderResult = new dojo.data.ItemFileReadStore({
data: {
jsId: "jsorderResult",
id: "orderResult",
items: response.Data
}
});
});
UPDATE
I have diagnosed the problem. one problem was executionTimeout configuration in web.config. and the after making this change second problem was due to long operation time the request was being interrupted by internet (network communication). I made sure I have reliable internet connectivity and tested it again and it worked.
BUT Now i am facing another problem. My httphandler send request to a webservice and I am getting following exception in response.
The operation has timed out
I have fixed the problem.
one problem was executionTimeout configuration in web.config. and the after making this change second problem was due to long operation time the request was being interrupted by internet (network communication). I make sure I have reliable internet connectivity and tested it again.
configured the webserivces as follows.
<httpRuntime executionTimeout="7200" enable="true" maxRequestLength="2048576" useFullyQualifiedRedirectUrl="false" />
and
[WebMethod(Description = "Delete template",BufferResponse = false)]
Specifying "BufferResponse=false" indicates that .NET should begin sending the response to the client as soon as any part of the response becomes available, instead of waiting for the entire response to become available.