Detect client disconnect in dart http server - dart

I am using the dart http server as a mock server to test my flutter app interactions with a real server, I am doing something like this:
final _server = await HttpServer.bind(InternetAddress.anyIPv4, 0);
_server.listen(handleRequest, onError: (error) {
print("onError");
}, onDone: () {
print("Closed");
});
One of my test cases includes the user canceling a long-running request. I want to test if the request really got canceled and the connection closed.
So I need a way to detect on the server-side if a request has been canceled:
void handleRequest(HttpRequest request) async {
// This future is finished, when the test decides, that the client should have canceled the request
await waitFuture;
request.response
..statusCode
..close():
// How do I know here if the client has canceled the request in the meantime?
Unfortunately, the server seems to simply ignore any client disconnects.
So my question: Is there a way I can detect client disconnects using darts HTTP server?

Related

Hivemq Cloud - Persistent Session and Queuing Messages java vs android

I have a question / problem about Persistent Session and Queuing Messages.
Here is the scenario:
I have a publisher (java server) which is publish message and I have a receiver (android client). When android client it online it gets the messages which amazing, working very well.
However, when I kill the android app and keep sending message from server and when I open android app, android does not receive previous messages.
Server side:
final Mqtt5BlockingClient client = MqttClient.builder()
.useMqttVersion5()
.serverHost(host)
.serverPort(8883)
.sslWithDefaultConfig()
.buildBlocking();
// connect to HiveMQ Cloud with TLS and username/pw
client.connectWith()
.simpleAuth()
.username(username)
.password(UTF_8.encode(password))
.applySimpleAuth()
.noSessionExpiry()
.cleanStart(false)
.send();
// This code is running every 15 sec
String now = LocalDateTime.now().toString();
String message = String.format("Hello: %s", now);
// publish a message to the topic "my/test/topic"
client.publishWith()
.topic("hasan-device/sayHello")
.payload(UTF_8.encode(message))
.retain(true)
.qos(MqttQos.AT_LEAST_ONCE)
.noMessageExpiry()
.send();
Client side:
// create an MQTT client
final Mqtt5BlockingClient client = MqttClient.builder()
.identifier("my-device-1")
.useMqttVersion5()
.serverHost(host)
.serverPort(8883)
.sslWithDefaultConfig()
.automaticReconnectWithDefaultConfig()
.buildBlocking();
// connect to HiveMQ Cloud with TLS and username/pw
client.connectWith()
.simpleAuth()
.username(username)
.password(UTF_8.encode(password))
.applySimpleAuth()
.noSessionExpiry()
.cleanStart(false)
.send();
// subscribe to the topic "my/test/topic"
client.subscribeWith()
.topicFilter("hasan-device/sayHello")
.retainHandling(Mqtt5RetainHandling.SEND)
.send();
// set a callback that is called when a message is received (using the async API style)
client.toAsync().publishes(ALL, publish -> {
byte[] message = publish.getPayloadAsBytes();
LOGGER.info("Received message: {} -> {}, ", publish.getTopic(), new String(message, UTF_8));
});
Expecting to message arrive when device back to online
When the Android app restarts with the persistent session, brokers will send down pending messages immediately. This can happen before the application callbacks get initialised.
Here is an example from when I did some testing with this:
To fix, move this bit of code to execute just before the connectWith call:
// set a callback that is called when a message is received (using the async API style)
client.toAsync().publishes(ALL, publish -> {
byte[] message = publish.getPayloadAsBytes();
LOGGER.info("Received message: {} -> {}, ", publish.getTopic(), new String(message, UTF_8));
});

Is it possible calling other API in Cloud functions using firebase?

I'd like to make some push alarm on iOS
When user set Alarm on specific time, and Server sends Weather Data by calling Weather API (ex OpenweatherMap) that time.
is it possible by using cloud functions in Firebase
Yes, this can be done with the request module.
See Use firebase cloud function to send POST request to non-google server where you will find:
// import the module
var request = require('request');
// make the request
request('put your external url here', function (error, response, body) {
if (!error && response.statusCode == 200) {
//here put what you want to do with the request
}
})
Note that you will have to activate a paid plan, because the free plan (Spark plan) only allows "outbound network requests only to Google-owned services"
Note also that you have to install the request package before being able to call it as shown above.

Pattern for retrying URLSession dataTask?

I'm fairly new to iOS/Swift development and I'm working on an app that makes several requests to a REST API. Here's a sample of one of those calls which retrieves "messages":
func getMessages() {
let endpoint = "/api/outgoingMessages"
let parameters: [String: Any] = [
"limit" : 100,
"sortOrder" : "ASC"
]
guard let url = createURLWithComponents(endpoint: endpoint, parameters: parameters) else {
print("Failed to create URL!")
return
}
do {
var request = try URLRequest(url: url, method: .get)
let task = URLSession.shared.dataTask(with: request as URLRequest) { (data, response, error) in
if let error = error {
print("Request failed with error: \(error)")
// TODO: retry failed request
} else if let data = data, let response = response as? HTTPURLResponse {
if response.statusCode == 200 {
// process data here
} else {
// TODO: retry failed request
}
}
}
task.resume()
} catch {
print("Failed to construct URL: \(error)")
}
}
Of course, it's possible for this request to fail for a number of different reasons (server is unreachable, request timed out, server returns something other than 200, etc). If my request fails, I'd like to have the ability to retry it, perhaps even with a delay before the next attempt. I didn't see any guidance on this scenario in Apple's documentation but I found a couple of related discussions on SO. Unfortunately, both of those were a few years old and in Objective-C which I've never worked with. Are there any common patterns or implementations for doing something like this in Swift?
This question is airing on the side of opinion-based, and is rather broad, but I bet most are similar, so here goes.
For data updates that trigger UI changes:
(e.g. a table populated with data, or images loading) the general rule of thumb is to notify the user in a non-obstructing way, like so:
And then have a pull-to-refresh control or a refresh button.
For background data updates that don't impact the user's actions or behavior:
You could easily add a retry counter into your request result depending on the code - but I'd be careful with this one and build out some more intelligent logic. For example, given the following status codes, you might want to handle things differently:
5xx: Something is wrong with your server. You may want to delay the retry for 30s or a minute, but if it happens 3 or 4 times, you're going to want to stop hammering your back end.
401: The authenticated user may no longer be authorized to call your API. You're not going to want to retry this at all; instead, you'd probably want to log the user out so the next time they use your app they're prompted to re-authenticate.
Network time-out/lost connection: Retrying is irrelevant until connection is re-established. You could write some logic around your reachability handler to queue background requests for actioning the next time network connectivity is available.
And finally, as we touched on in the comments, you might want to look at notification-driven background app refreshing. This is where instead of polling your server for changes, you can send a notification to tell the app to update itself even when it's not running in the foreground. If you're clever enough, you can have your server repeat notifications to your app until the app has confirmed receipt - this solves for connectivity failures and a myriad of other server response error codes in a consistent way.
I'd categorize three methods for handling retry:
Reachability Retry
Reachability is a fancy way of saying "let me know when network connection has changed". Apple has some snippets for this, but they aren't fun to look at — my recommendation is to use something like Ashley Mill's Reachability replacement.
In addition to Reachability, Apple provides a waitsForConnectivity (iOS 11+) property that you can set on the URLSession configuration. By setting it, you are alerted via the URLSessionDataDelegate when a task is waiting for a network connection. You could use that opportunity to enable an offline mode or display something to the user.
Manual Retry
Let the user decide when to retry the request. I'd say this is most commonly implemented using a "pull to refresh" gesture/UI.
Timed/Auto Retry
Wait for a few second and try again.
Apple's Combine framework provides a convenient way to retry failed network requests. See Processing URL Session Data Task Results with Combine
From Apple Docs: Life Cycle of a URL Session (deprecated)... your app should not retry [a request] immediately, however. Instead, it should use reachability APIs to determine whether the server is reachable, and should make a new request only when it receives a notification that reachability has changed.

Problems subscribing to a room (socket) with Socket.IO-Client-Swift and Swift, Sails.js on the Server

On Swift, I use
socket.on("test") {data, ack in
print(data)
}
In order to subscribe to a room (socket) on my Sails.js API.
When I broadcast a message from the server, with
sails.sockets.broadcast('test', { text : 'ok' })
the socket.on handler is never called.
However, if I set "log" TRUE to config when connecting my socket.io client from swift, in Socket-IO logs the message arrives.
What's wrong?
Eventually, I found my mistake:
The whole process I did is right:
(The request to join the room is done by the server, with sails.sockets.join)
Wrong thing was using socket.on with the ROOM NAME parameter.
I will explain it better, for others having same problem:
From Swift you should subscribe by making a websocket request to an endpoint on the server that accepts websocket requests (GET, POST, PUT). For example, you can make a POST request, passing in the room name into the body.
socket.emitWithAck("post", [
"room": "testroom",
"url": "/api/v1.0/roomsubscribing"
]).timingOut(after: 0) {data in
print("Server responded with \(data)")
}
On server side, inside the room-subscribing endpoint, you should have the following code:
roomSubscribing: function(req, res) {
if (!req.isSocket) {
return res.badRequest();
}
sails.sockets.join(req, req.params('room'), function(err) {
if (err) {
return res.serverError(err);
}
});
}
When the server want to broadcast some data to subscribers of the "testroom" room, the following code must be used:
sails.sockets.broadcast('testroom', { message: 'testmessage' }
Now on the swift's side you must use:
socket.on("message") { data, ack in
print(data)
}
in order to get the message handler to work. I thought you should use room name, instead you should use the KEY of the KEY/VALUE entry you used in your server when you broadcasted the data (in this case, "message").
I only have a small amount of experience with sockets, but in case nobody else answers...
I think you are missing step one of the three step socket process:
A client sends a message to the server asking to subscribe to a particular room.
The client sets up a socket.on to handle particular events from that room.
The server broadcasts an event in a particular room. All subscribers/clients with a .on for that particular event will react.
I could be wrong, but it sounds from your description like you missed step one. Your client has to send a message with io.socket, something like here, then your server has to use the socket request to have them join the room, something like in the example here.
(the presence of log data without the socket.on firing would seem to confirm that the event was broadcast in the room, but that client was not subscribed)
Good luck!

SignalR, Messages are to be pushed by the Server without any event triggered by the client

There are running examples of SignalR, but in those, i have seen that the process is started by the client i.e. every piece of code contains following similar lines
$.connection.hub.start().done(function () {
$('#mybutton').click(function () {
notifier.server.doLongOperation();
});
});
The process on server starts on $('#mybutton').click and then responds.
Is my understanding correct? If yes then is it possible to start the process by Server? I mean Server will push messages to all clients without any triggering from the client side.
This didn't work
var context = GlobalHost.ConnectionManager.GetHubContext<Broadcast>();
context.Clients.All.Send(message);
My bad, method name on client side was incorrect. Problem solved
Yes it is possible to send server initiated "messages" from the server to clients. Note that you have to call a method on the client. Note that it's a RPC/Remoting type of communication.
On the server you'd have a code like this:
Clients.All.Say("Hello World!");
where the client needs to define a function:
myHub.client.say = function (message) {
console.log(message);
});
see the SignalR documentation

Resources