Dart program will not exit if response body is unread - dart

import 'dart:convert';
import 'dart:io';
final url = Uri.http('127.0.0.1', '');
Future<void> main() async {
final httpClient = HttpClient();
httpClient.findProxy = null;
final req = await httpClient.getUrl(url);
final res = await req.close();
// await res.transform(utf8.decoder).join();
httpClient.close();
print('end of main');
}
The program will not terminate if the line is commented out.
Should we read the response body anyway if it is not important, or discard it somehow?

You can use force: true on your HttpClient object if you are sure you just want to kill all ongoing connections:
Shuts down the HTTP client.
If force is false (the default) the HttpClient will be kept alive until all active connections are done. If force is true any active connections will be closed to immediately release all resources. These closed connections will receive an error event to indicate that the client was shut down. In both cases trying to establish a new connection after calling close will throw an exception.
https://api.dart.dev/stable/2.15.1/dart-io/HttpClient/close.html
Alternative, you can use drain() on your HttpClientResponse if you just want to get the answer but throw it away immediately.

Related

Blazor-server scoped services, closed connections, garbage collection

If I have a scoped service:
services.AddSingleton<MyScopedService>();
And in that service, an HTTP request is made:
HttpClient client = _clientFactory.CreateClient();
StringContent formData = ...;
HttpResponseMessage response = await client.PostAsync(uri, formData);
string data = await response.Content.ReadAsStringAsync();
I read here that for an AddScoped service, the service scope is the SignalR connection.
If the user closes the browser tab before the response is returned, the MyScopedService code still completes.
Could someone explain what happens to that MyScopedService instance? When is it considered out of scope? After the code completes? Is the time until it's garbage collected predictable?
I have a Blazor-server project using scoped dependency injections (fluxor, and a CircuitHandler), and I'm noticing that the total app memory increases with each new connection (obviously), but takes a while (minutes) for the memory to come down after the browser tabs are closed.
Just wondering if this is expected, or if I could be doing something to let the memory usage recover more quickly. Or maybe I'm doing something wrong with my scoped services.
Add IDisposeAsync to your service then in your service :
public async ValueTask DisposeAsync() => await hubConnection.DisposeAsync();
This was copied from one of my own libraries I was facing the same issue. GC will not work if there are references to other objects...

efficient async function that needs result from another async function in dart (http client)

From here Dart - Request GET with cookie we have this example of doing a get request with dart's built in HTTP library:
exampleCall() {
HttpClient client = new HttpClient();
HttpClientRequest clientRequest =
await client.getUrl(Uri.parse("http: //www.example.com/"));
clientRequest.cookies.add(Cookie("sessionid", "asdasdasqqwd"));
HttpClientResponse clientResponse = await clientRequest.close();
}
As you can see, multiple awaits are needed. Which means that if I try to do multiple concurrent exampleCall calls, they won't happen at the same time.
I cannot return a future because I must wait the client.getUrl() in order to do the clientResponse.
I also couldn't find a good alternative to use cookies on http calls. Dio seems to only support storing cookies from the server. Anyways, I'd like to know how to do in this way, but if there's a better way I'd like to know.
As you can see, multiple awaits are needed. Which means that if I try to do multiple concurrent exampleCall calls, they won't happen at the same time.
Not really sure what you mean here. Dart is single threaded so the concept of things happen "at the same time" is a little vauge. But if you follow the example later you should be able to call exampleCall() multiple times without the need of waiting on each other.
I cannot return a future because I must wait the client.getUrl() in order to do the clientResponse.
Yes you can if you mark the method as async:
import 'dart:convert';
import 'dart:io';
Future<List<String>> exampleCall() async {
final client = HttpClient();
final clientRequest =
await client.getUrl(Uri.parse("http://www.example.com/"));
clientRequest.cookies.add(Cookie("sessionid", "asdasdasqqwd"));
final clientResponse = await clientRequest.close();
return clientResponse
.transform(utf8.decoder)
.transform(const LineSplitter())
.toList();
}
The whole point of async methods is the ability to easily bundle multiple asynchronous calls into a single Future. Notice, that async methods must always return a Future but your return statement should not necessarily return a Future object (if you return a normal object, it will automatically be packed into a Future).
I also couldn't find a good alternative to use cookies on http calls. Dio seems to only support storing cookies from the server. Anyways, I'd like to know how to do in this way, but if there's a better way I'd like to know.
Not really sure about the whole cookie situation. :)

How to close receivePort to get .toList() method working?

I am trying to get answer from Isolate as list. I wrote next code. The problem That it's not working. It's simply wait.
main() async
{
ReceivePort receivePort = ReceivePort();
Isolate.spawn(echo, receivePort.sendPort);
var d = await receivePort.toList();
receivePort.close();
print(d);
}
echo(SendPort sendPort) async
{
ReceivePort receivePort = ReceivePort();
sendPort.send("message");
}
The receivePort is a stream. When you call toList on the stream, it waits for the stream to complete. That never happens, so the toList call stalls forever.
If you know that the other end of the communication only sends one message, then you can instead do var d = await receivePort.first;. This only waits for the first message.
In general, when doing isolate communication, the sending isolate should send a special message when they are done, because the receiving isolate has no other way to know that. That is: You want a communication protocol, so the receiver can know whether there are going to be more messages. Maybe each message is an object which contains an isLast boolean, or the last message is null, or you wrap all messages in subclasses of a Message class that defines your protocol. What to do depends on the actual use-case.
The only rule is: You have to say when you are done.
You should use .listen() instead of await for, if you want to see output of the list before is stream closed. listen will register the handler and execution keeps continue.
instead of
var d = await receivePort.toList();
receivePort.close();
print(d);
try
receivePort.listen((data) => {print(data)});
You can find differences with listen and await here

HttpClient request is not working in asp .net Project

I am using HttpClient to make a request to an api. This code is located in a class library project shared with two aditional projects, a Console and a Asp.Net Mvc project. When I make a request from the Console project it works great, but in the asp project it blocks in the line
using(Stream responseStream = await response.Content.ReadAsStreamAsync()
this is my request code
private async Task<dynamic> ReadJson(string url)
{
HttpResponseMessage response = await httpClient.GetAsync(url);
if (response.StatusCode == System.Net.HttpStatusCode.NoContent)
throw new RateLimitException();
if (response.StatusCode == System.Net.HttpStatusCode.Forbidden)
throw new AccessDeniedException();
if (response.StatusCode != System.Net.HttpStatusCode.OK)
throw new Exception("Error: " + response.StatusCode);
using (Stream responseStream = await response.Content.ReadAsStreamAsync())
using (StreamReader sr = new StreamReader(responseStream, System.Text.Encoding.UTF8))
{
string json = sr.ReadToEnd();
return JObject.Parse(json);
}
}
I am making the same call to the method from the Console and the Asp.Net project. From the console works but the asp .net project blocks in the line when reads the response content
Most probably this deadlock occurs because the controller action that calls ReadJson function is synchronous. You need to make the action async. You can find an excellent explanation of this deadlock here. (All the credits go to Stephen Cleary)
Quick summary is:
/ My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
using (var client = new HttpClient())
{
var jsonString = await client.GetStringAsync(uri);
return JObject.Parse(jsonString);
}
}
// My "top-level" method.
public class MyController : ApiController
{
public string Get()
{
var jsonTask = GetJsonAsync(...);
return jsonTask.Result.ToString();
}
}
What Causes the Deadlock
The top-level method calls GetJsonAsync (within the UI/ASP.NET context). GetJsonAsync starts the REST request by calling
HttpClient.GetStringAsync (still within the context).
GetStringAsync returns an uncompleted Task, indicating the REST request is not complete.
GetJsonAsync awaits the Task returned by GetStringAsync. The context is captured and will be used to continue running the
GetJsonAsync method later. GetJsonAsync returns an uncompleted
Task, indicating that the GetJsonAsync method is not complete.
The top-level method synchronously blocks on the Task returned by GetJsonAsync. This blocks the context thread.
… Eventually, the REST request will complete. This completes the Task that was returned by GetStringAsync.
The continuation for GetJsonAsync is now ready to run, and it waits for the context to be available so it can execute in the context.
Deadlock. The top-level method is blocking the context thread, waiting for GetJsonAsync to complete, and GetJsonAsync is waiting for
the context to be free so it can complete.
Preventing the Deadlock
There are two best practices that avoid this situation:
In your “library” async methods, use ConfigureAwait(false) wherever possible.
Don’t block on Tasks; use async all the way down.

stomp disconnects its processing twice in channel interceptor and SimpleBrokerMessageHandler

I have modified to implement channel interceptor in spring-websocket-portfolio sample application (https://github.com/rstoyanchev/spring-websocket-portfolio). whenever the client disconnects, channel interceptor is processed twice. I have similar implementation in my production application. As it is being invoked twice so it has unwanted result for the 2nd invocation. I had put work around for the time being. But wondering why my channel interceptor is invoked twice? Any help would be highly appreciated.
modified items: WebSocketConfig.java:
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(channelInterceptor());
}
#Bean
public ChannelInterceptor channelInterceptor() {
return new ChannelInterceptor();
}
ChannelInterceptor :
package org.springframework.samples.portfolio.config;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptorAdapter;
public class ChannelInterceptor extends ChannelInterceptorAdapter {
#Override
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
StompHeaderAccessor sha = StompHeaderAccessor.wrap(message);
System.out.println(sha.getCommand() + " " + sha);
switch (sha.getCommand()) {
case CONNECT: {
System.out.println("connected:"+sha.getSessionId());
break;
}
case DISCONNECT: {
System.out.println("disconnected:"+sha.getSessionId());
break;
}
default:
System.out.println("default:"+sha.getCommand());
break;
}
}
}
logs:
**disconnected**:9k1hvln6
**disconnected**:9k1hvln6
Disconnect events may happen more than once for the same session, your interceptor should be idempotent and ignore duplicate events.
You may also consider using application events (SessionConnectEvent, SessionDisconnectEvent...) instead of a channel interceptor. Here's an example of an idempotent event listener: https://github.com/salmar/spring-websocket-chat/blob/master/src/main/java/com/sergialmar/wschat/event/PresenceEventListener.java
Generally a DISCONNECT frame comes the client side, is processed in the StompSubProtocolHandler, and is then propagated to the broker. However, a connection can also be closed or lost without a DISCONNECT frame. Regardless of how a connection is closed, the StompSubProtocolMessageHandler generates a DISCONNECT frame. So there is some redundancy on the server side to ensure the broker is aware the client connection is gone.
As Sergi mentioned you can either subscribe to listen for SessionDisconnectEvent (of which there should be only one) and other AbstractSubProtocol events or ensure your code is idempotent.

Resources