Requests to same url blocked in http nio thread pool - spring-boot

I simply created a RestController in springboot(1.5.4.RELEASE) application and test how it works when multiple requests come in. What confused me is:
same url : the 2nd request blocked until the 1st request executed
different url: non-block, 2 requests executed almost at the same time
My question is who is blocking the 2nd requst and why?
Test Code:
#GetMapping(value = "/sleep")
public String sleep(HttpServletRequest request, #RequestParam boolean status)
{
String requestId = request.toString();
logger.info("request [{}] in and status = {}.", requestId, status);
String result;
if (status)
{
Thread.currentThread().sleep(10 * 1000);
result = "slept";
}
else
{
result = "stay up";
}
logger.info("response [{}] out and result = [{}].", requestId, result);
return result;
}
Test Result:
Different url:do not block, almost start executing at the same time.
http://localhost:20002/sleep?status=false AND http://localhost:20002/sleep?status=true
2018-08-14 15:04:14.139 [http-nio-20002-exec-5]: connection [RequestFacade#46515328] in and status = true.
2018-08-14 15:04:16.452 [http-nio-20002-exec-6]: connection [RequestFacade#1140f857] in and status = false.
2018-08-14 15:04:16.452 [http-nio-20002-exec-6]: connection [RequestFacade#1140f857] out and result = [stay up].
2018-08-14 15:04:24.139 [http-nio-20002-exec-5]: connection [RequestFacade#46515328] out and result = [slept].
Same url: block, the 2nd requst doesn't execute until the 1st requst is done.
http://localhost:20002/sleep?status=true AND http://localhost:20002/sleep?status=true
2018-08-14 15:10:29.943 [http-nio-20002-exec-9]: connection [RequestFacade#46515328] in and status = true.
2018-08-14 15:10:39.944 [http-nio-20002-exec-9]: connection [RequestFacade#46515328] out and result = [slept].
2018-08-14 15:10:39.960 [http-nio-20002-exec-1]: connection [RequestFacade#1140f857] in and status = true.
2018-08-14 15:10:49.960 [http-nio-20002-exec-1]: connection [RequestFacade#1140f857] out and result = [slept].
I debug the code of tomcat-embed-core and find that when request with different url comes in, the Poller thread could catch it immediately and process it; while request with the same url come in, the Poller could not get it until the 1st connection is returned.

Thanks to #M.Deinum, the blocking has nothing to do with NIO. It's the chrome who blocked the 2nd request, and for reason ,I find some comment below:
this behavior is due to Chrome locking the cache and waiting to see the result of one
request before requesting the same resource again.
https://stackoverflow.com/a/27514611/10222882
And It's proved by chrome Network -> Timing, the 2nd request is in [stalled] status until the 1st response is returned.
connection_stalled

Related

How to configure request timeout and/or message TTL separately in MassTransit?

My goal is to make request-response from API Gateway to microservice. I want request to be durable (not expiring), but response should be expiring (if no consumer will receive response within let's say 30s then it should expire). I am using RequestClient in API GW request handler (request scope). By default timeout is set by MassTransit to 30s and after this time request is removed from the queue. It's here in MassTransit code: https://github.com/MassTransit/MassTransit/blob/5e2a416384f005c392ead139f5c4af34511c56db/src/MassTransit/RequestTimeout.cs#L7.
First of I tried to set timeout in my request client like that
(using RabbitMQ):
// on request client level
cfg.AddRequestClient<ICreateGroupPayload>(RequestTimeout.None);
// and/or
// on .GetResponse() level
var response = await _createGroupClient.GetResponse<ICreateGroupResult>(new
{
Name = payload.Name,
Description = payload.Description
}, new CancellationToken(), RequestTimeout.None);
But neither of those options work. Still message has default 30s TTL.
Why it does not work as expected? Is message TTL something different here than RequestTimeout? If yes then why its 30s?
Do I have to configure message TTL manually in my case?
I want request client to automatically timeout after 30s even when request message will never expire. Is this built in, or I have to write some custom code for timeout, or maybe it's enough if I will pass expiring CancellationToken to .getResponse()?
You can configure a default timeout when registering the request client:
cfg.AddRequestClient<ICreateGroupPayload>(RequestTimeout.After(s:20));
And then, when sending the request, you can override the TimeToLive using a header in the message initializer:
var response = await _createGroupClient.GetResponse<ICreateGroupResult>(new
{
Name = payload.Name,
Description = payload.Description,
__TimeToLive = TimeSpan.FromYears(1)
}, httpContext.CancellationToken);
That will replace the default value (which matches the request timeout, by default) with the new value.

Kestrel server slow on "bad request data"

I have an IOT device (black box, can't reprogram it) that sends http POST requests (136 bytes of JSON, a string) over wired ethernet to my .NET core 2.2 very simple server console application.
I just output the string to the console.
[HttpPost]
public void Post([FromBody] RootObject root)
{
string adv = root.prt;
Console.WriteLine(adv);
}
I get to display less than 1 line per second, sometimes 2.
Using Fiddler as a reverse proxy, instead I receive between 5 and 10 http req per second, that is the correct behavior of the device.
So I enabled Information logs, and get this, every second, with that "Invalid request line" error:
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 POST http://192.168.0.92/api/values application/json 136
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint 'BluepycWeb.Controllers.ValuesController.Post (BluepycWeb)'
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
Route matched with {action = "Post", controller = "Values"}. Executing action BluepycWeb.Controllers.ValuesController.Post (BluepycWeb)
02003F002293831000010033FF0006EFAA256B6D1A001E010201061AFF4C000215476C6F62616C2D54616700000000000000000000CD0101CC0001FF001FCC
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
Executing action method BluepycWeb.Controllers.ValuesController.Post (BluepycWeb) with arguments (BluepycWeb.Controllers.RootObject) - Validation state: Valid
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
Executed action method BluepycWeb.Controllers.ValuesController.Post (BluepycWeb), returned result Microsoft.AspNetCore.Mvc.EmptyResult in 0.2158ms.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
Executed action BluepycWeb.Controllers.ValuesController.Post (BluepycWeb) in 9.4032ms
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint 'BluepycWeb.Controllers.ValuesController.Post (BluepycWeb)'
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 24.5625ms 200
info: Microsoft.AspNetCore.Server.Kestrel[17]
Connection id "0HLMO99UNQBGM" bad request data: "Invalid request line: '\x0D\x0A'"
Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Invalid request line: '\x0D\x0A'
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpParser`1.RejectRequestLine(Byte* requestLine, Int32 length)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpParser`1.GetUnknownMethod(Byte* data, Int32 length, Int32& methodLength)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpParser`1.ParseRequestLine(TRequestHandler handler, Byte* data, Int32 length)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpParser`1.ParseRequestLine(TRequestHandler handler, ReadOnlySequence`1& buffer, SequencePosition& consumed, SequencePosition& examined)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpParser`1.Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.IHttpParser<TRequestHandler>.ParseRequestLine(TRequestHandler handler, ReadOnlySequence`1& buffer, SequencePosition& consumed, SequencePosition& examined)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1Connection.TakeStartLine(ReadOnlySequence`1 buffer, SequencePosition& consumed, SequencePosition& examined)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1Connection.ParseRequest(ReadOnlySequence`1 buffer, SequencePosition& consumed, SequencePosition& examined)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1Connection.TryParseRequest(ReadResult result, Boolean& endConnection)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequestsAsync[TContext](IHttpApplication`1 application)
If I send the same JSON paylod through Postman continuosly, no errors, no delays.
That error, that I don't know how to avoid nor by what is originated (but it seems it doesn't distub Fiddler) seems to hang my server for a second.
Any suggestion on how can I solve this problem?
Discard/correct the error?
Keep the error but not slowing down the data receive?
UPDATE:
Hosting with IISExpress, no problem, no "Invalid request line: '\x0D\x0A'" error, very fast.
Problem is only using Kestrel.
Thanks!
Yesterday I had same issue with my ASP .NETCore service.
Then realized I was sending HTTPS requests to my Kestrel server while it isn't configured to listen at HTTPS protocol, that's why Kestrel complaining.
If you want to use HTTPS in your kestrel you have multiple options to configure it, for example :
With assigned server certificate :
var host = new WebHostBuilder()
.UseKestrel(options =>
options.Listen(IPAddress.Any, 443, listenOptions =>
listenOptions.UseHttps("MyCert.pfx")))
With multiple hostnames and certificates :
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureKestrel((context, options) =>
{
options.ListenAnyIP(5005, listenOptions =>
{
listenOptions.UseHttps(httpsOptions =>
{
var localhostCert = CertificateLoader.LoadFromStoreCert(
"localhost", "My", StoreLocation.CurrentUser,
allowInvalid: true);
var exampleCert = CertificateLoader.LoadFromStoreCert(
"example.com", "My", StoreLocation.CurrentUser,
allowInvalid: true);
var subExampleCert = CertificateLoader.LoadFromStoreCert(
"sub.example.com", "My", StoreLocation.CurrentUser,
allowInvalid: true);
var certs = new Dictionary<string, X509Certificate2>(
StringComparer.OrdinalIgnoreCase);
certs["localhost"] = localhostCert;
certs["example.com"] = exampleCert;
certs["sub.example.com"] = subExampleCert;
httpsOptions.ServerCertificateSelector = (connectionContext, name) =>
{
if (name != null && certs.TryGetValue(name, out var cert))
{
return cert;
}
return exampleCert;
};
});
});
});

Spring + Angular: How to parse ResponseEntity in angular?

I'm using Spring Boot to create an API that needs to be consumed in Angular 4. Spring and Angular are on different ports.
The problem is that Spring's ResponseEntity raises an error in Angular.
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseEntity getFlow(#PathVariable int id) {
Flow flow = flowService.findById(id);
return new ResponseEntity(flow, HttpStatus.FOUND);
}
Now, I can perfectly use Postman to test the API and it works.
But when I make a request from Angular, it returns an error:
Strangely, it returns an error alongside the requested object.
Now, the cause of the problem is that the Spring Boot application returns a ResponseEntity and not a normal object (like String), and Angular doesn't know how to interpret it. If the controller returns just a Flow object, it works.
How can it be solved using ResponseEntity? Or, how else can I send the object alongside the HTTP status code?
Also, in #RequestMapping put produces = "application/json", and in get request in angular, add http options :
const httpOptions = {
headers: new HttpHeaders({
'Accept': 'application/json',
'Content-Type': 'application/json'
})
};
So your get request looks like this:
this.http.get(url, httpOptions)
As per the document mentioned here
https://docs.angularjs.org/api/ng/service/$http
A response status code between 200 and 299 is considered a success status and will result in the success callback being called. Any response status code outside of that range is considered an error status and will result in the error callback being called. Also, status codes less than -1 are normalized to zero. -1 usually means the request was aborted, e.g. using a config.timeout. Note that if the response is a redirect, XMLHttpRequest will transparently follow it, meaning that the outcome (success or error) will be determined by the final response status code.
As you are sending an instance of ResponseEntity(HttpStatus.Found) whose Http status code is 302 which doesnt fall under the success range thats why error callback is called.
Try returning the content like this
return new ResponseEntity(flow, HttpStatus.OK);

How to prioritize the reponse over request in ASP.NET Web Api

I have a code that looks something like this:
class SomeController {
HttpClient client = new HttpClient();
public Task<dynamic> SomeAction() {
Task.Run<dynamic>(() => {
var response = client.GetAsync(new Uri(someUrl));
return response.ReadAsAsync<dynamic>().Result;
});
}
}
Now, I call this api with many requests (around 300) and the 'someUrl' returns the response after about 200ms.
After adding some console logs I can see a behavior:
All the 200 requests arrive and request the someUrl resource
The first 2-3 requests to someUrl are handled and returned properly
Other responses are waiting for all the 300 requests to arrive and only then they are returned back...
I have heard that there is no prioritization of responses over incoming requests in situations like these but it seems weird to me. It seems like the requests coming to my server and the responses that are coming from the someUrl are on the same queue and until all the requests are sent no response can be handled.
Anyone else encountered this situation? Anyone knows how to handle it properly?
Thanks!

How to maintain session state between two request with Groovy HttpBuilder

I'm working on a integration test where authentication is needed.
Session state (ie. cookie) seems not to be maintain beetween requests.
Is there a CookieManager or something like that ?
#Test
public void whenAuthenticatedUserRequestAForbiddenUrlShouldObtain403() {
def client = new RESTClient('http://127.0.0.1:8080/app/')
def login = client .post(
path: 'api/login.json',
body: [j_username: 'user', j_password: 'test'],
requestContentType: ContentType.URLENC)
def resp = client .get(path: 'forbidden-url')
assert (resp.status == 403)
==> FAILS status = 200
}
It looks to me like the problem is not losing session state but rather the 'forbidden-url' might not be specified as secure in the first place. If it were, it does not seem that client request request should succeed EVEN IF you login. Try removing the login at the top and if you still get 200, you probably don't have the URL secured anyway.

Resources