How to setup a Sockect.io server in aws EC2 and then connect remotely to the server? - amazon-web-services

Good morning.
I created a simple server for a chat using Socket.io. In the next screen, which would be the client interface, you can see some inputs
In localhost, in my computer works fine. Here are the logs messages from the server:
I uploaded the server to an EC2 instance with ubuntu. And it work fine, in the same way
Logs from the server in EC2
The main problem is when I tried to connect from my computer to the Socket.io server in EC2. I got this error from my client side file in my computer:
socket.io.js:3888 GET
https://mydomain:80/socket.io/?EIO=4&transport=polling&t=NQB59WG
Here is my server code:
const path = require("path");
const express = require("express");
const socketio = require("socket.io");
const app = express();
app.set('port', 3000);
//static files
app.use(express.static(path.join(__dirname, "public")));
//start the server
const server = app.listen(app.get('port'), () => {
console.log("server on port", app.get('port'));
});
const io = socketio(server);
//websockects
io.on('connection', (socket) => {
console.log("new connection " + socket.id);
socket.on('chat:message', (data) => {
console.log(data);
io.sockets.emit("chat:server", data);
});
socket.on('chat:typing', (data) => {
console.log(data);
socket.broadcast.emit("chat:typing", data);
});
});
Here is my client code:
const socket = io("https://domain:80");
//dom elements
let message = document.getElementById("message");
let username = document.getElementById("username");
let btn = document.getElementById("button");
let output = document.getElementById("output");
let actions = document.getElementById("actions");
btn.addEventListener('click', function() {
console.log({
username: username.value,
message: message.value
});
socket.emit("chat:message", {
username: username.value,
message: message.value
});
});
message.addEventListener('keypress', function() {
socket.emit("chat:typing", username.value);
});
socket.on("chat:server", function(data) {
console.log(data);
actions.innerHTML = "";
output.innerHTML += "<p><strong>" + data.username + "</strong>: " + data.message + " </p>";
});
socket.on("chat:typing", function(data) {
console.log(data);
actions.innerHTML = "<p><strong>" + data + "</strong> esta escribiendo </p>";
});
And here are the instance inboud rules for ports:
When i tried to connect from my computer to the EC2 instance, I tried with several ways to connect, like this:
const socket = io("https://url.org:80");
const socket = io("https://url.org");
const socket = io("https://ipaddres:80");
const socket = io("https://ipaddres");
const socket = io("ec2-xxxx.compute-1.amazonaws.com");
const socket = io("ec2-xxxx.compute-1.amazonaws.com:80");
Nothing works, Any help?

In my case the solution was to do two things:
I use my domaind without port
const socket = io("https://url.org");
The EC2 Instance work with nginx, I modified the header to add this property:
Access-Control-Allow-Origin: *
And it worked

Related

Problem with AWS API Gateway websocket- Lambda postToConnection

I've been trying to solve this problem all day, looking everywhere on the web, even in the official AWS documentation, why does this error keep appearing when I try to send a message to the client through this code?
PS. i use SDK v3 with node.js 18
import {
ApiGatewayManagementApiClient,
PostToConnectionCommand,
} from "#aws-sdk/client-apigatewaymanagementapi";
export const handler = async (event) => {
const domain = event.requestContext.domainName;
const stage = event.requestContext.stage;
const connectionId = event.requestContext.connectionId;
const callbackUrl = `https://${domain}/${stage}`;
const client = new ApiGatewayManagementApiClient({ endpoint: callbackUrl });
const requestParams = {
ConnectionId: connectionId,
Data: "Hello!",
};
const command = new PostToConnectionCommand(requestParams);
try {
await client.send(command);
} catch (error) {
console.log(error);
}
return {
statusCode: 200,
};
};
fef17825-58ce-4ca7-8f38-85857f1aef0a Task timed out after 3.01 seconds
i tried any online guide or video, can anyone help me?

Cannot POST when sending request in Postman

I am having a small issue. I am trying to send a request in Postman, but I receive "
Cannot POST /api/logTemp/1/25
"
Here is my app.js:
const express = require('express')
const bodyParser = require('body-parser')
const cors= require('cors')
const fs= require('fs')
const path= require('path')
const morgan = require('morgan')
const router = require('./routes/route')
const app = express()
app.use(cors())
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false}))
// parse application/json
app.use(bodyParser.json())
app.use(morgan('dev'))
//create a write stream (in append mode)
var accessLogStream = fs.createWriteStream(path.join(__dirname, '/logs/access.log'), {flags: 'a'})
//setup the logger
app.use(morgan('combined', {stream: accessLogStream}))
app.use(router)
app.get('/', (req, res) => {
res.send('Hello World!')
})
const port = 3000
//app.listen(process.env.PORT || port, (err) => {
app.listen(port, () => {
console.log('Server started running on :' + port)
})
and here is my controller file:
const { getEnabledCategories } = require('trace_events');
const mysql = require('../database/db')
class MainController {
async logTemp(req, res){
console.log(req.params.temperature)
console.log(req.params.deviceID)
if(req.params.deviceID != null && req.params.temperature != null){
let deviceID = req.params.deviceID
let temperature = req.params.temperature;
var sql = `insert into log_temp (log_date, device_id, temp) values (now(),${deviceID}, ${temperature});`
mysql.query(sql, (error,data,fields) => {
if(error){
res.status(500)
res.send(error.message)
} else{
console.log(data)
res.json({
status: 200,
message: "Log uploaded successfully",
affectedRows: data.affectedRows
})
}
})
} else {
res.send('Por favor llena todos los datos!')
}
}
async getLogs(req, res){
console.log("Get Logs")
console.log(req.params.deviceID)
if(req.params.deviceID!=null){
let deviceID = req.params.deviceID;
var sql = `SELECT * FROM log_temp where device_id=${deviceID}`
mysql.query(sql, (error, data, fields) => {
if(error) {
res.status(500)
res.send(error.message)
} else {
console.log(data)
res.json({
data
})
}
})
}
}
}
const tempController = new MainController()
module.exports = tempController;
The code above was made in Visual Studio. It is odd because getLogs does work but logTemp does not. What I intend to do with logTemp is add a new value (which is the value temperature) to MySQL database. The connection to the database worked just fine, as well as localhost. If you need any more info in order to help me find a solution, please let me know and I will be more than happy to provide it. Also, i'm sorry for any grammar errors, english is not my first language :)

How to fix aws-iot-device-sdk disconnect behaviour after switching internet connection?

I am trying to setup aws-iot-device-sdk-js with reconnect behaviour after wifi is switched and its taking around 20 mins to do so.I am not sure where i am wrong the docs doesn't have anything regarding the issue i am having as well.
I have tried going through the package docs and tried changing the keepalive time but its still showing the same output the offine is called only after 20 mins and reconnects.
const awsIot = require("aws-iot-device-sdk").device;
const certs = require("./certs_config");
const device = awsIot({
keyPath: certs.KEYPATH,
certPath: certs.CERTPATH,
caPath: certs.CAPATH,
deviceId: "rt.bottle.com.np",
host: "aot2wgmcbqwsa-ats.iot.ap-south-1.amazonaws.com",
region: "ap-south-1",
keepalive: 60
});
let delay = 4000;
let count = 0;
const minimumDelay = 250;
if ((Math.max(delay, minimumDelay)) !== delay) {
console.log('substituting ' + minimumDelay + 'ms delay for ' + delay + 'ms...');
}
setInterval(function () {
count++;
device.publish('topic', JSON.stringify({
count
}));
}, Math.max(delay, minimumDelay)); // clip to minimum
device
.on('connect', function () {
console.log('connect');
});
device
.on('close', function () {
console.log('close');
});
device
.on('reconnect', function () {
console.log('reconnect');
});
device
.on('offline', function () {
console.log('offline');
});
device
.on('error', function (error) {
console.log('error', error);
});
device
.on('message', function (topic, payload) {
console.log('message', topic, payload.toString());
});
In aws console i am getting this message:
Mqtt connection lost. Reconnect. Error code: 4. AMQJS0004E Ping timed out.
after around 1.5 mins of the network switch but in the node server setup as you can see in the code below it only receives the offline message in around 20 mins. I want to get the error/offline/disconnect as soon as its disconnects or goes offline.(i.e when receives the error on aws console) as expected.
i am currently using the simulateNetworkFailure Function to handle the network switch issue i was having i hope it helps others having the same issue.

Cloud Function (trigger by HTTP) that would publish a message to PubSub

I am trying to create HTTP API in Cloud Function - that eventually published a message t PubSub. Understood, that there is PubSub REST API - but it enforced me to set up the authentication (in client side) - that I would like to skip and move it to the server side.
Below code is deployed as Google Cloud Function with this command gcloud functions deploy helloGET --runtime nodejs8 --trigger-http
But while tested in browser, it is errored out Error: could not handle the request
Any suggestion is appreciated, thanks!
"use strict";
// [START functions_pubsub_setup]
const { PubSub } = require("#google-cloud/pubsub");
// Instantiates a client
const pubsub = new PubSub();
// [END functions_pubsub_setup]
const Buffer = require("safe-buffer").Buffer;
exports.helloGET = (req, res) => {
const topic = pubsub.topic("projects/myproject/topics/openit");
const message = {
data: {
message: "req.body.message"
}
};
// Publishes a message
res.send(
topic
.publish(message)
.then(() => res.status(200).send("Message published."))
.catch(err => {
err = `Catch block ... ${err}`;
console.error(err);
res.status(500).send(err);
return Promise.reject(err);
})
);
};
Below code will work. But it will take around 30 seconds or plus for the subscriber to receive the event - it is way too slow for my used case :S
"use strict";
const { PubSub } = require("#google-cloud/pubsub");
const pubsub = new PubSub();
const Buffer = require("safe-buffer").Buffer;
exports.helloGET = async (req, res) => {
var toPublish = `hello ${Date.now()}`;
publishMessage("_REPLACE_WITH_TOPIC_NAME_", toPublish);
res.send(`Published ${toPublish}`);
};
async function publishMessage(topicName, data) {
console.log("=> publishMessage, data = ", data);
const dataBuffer = Buffer.from(data);
const topic = pubsub.topic(topicName);
const publisher = topic.publisher();
publisher.publish(dataBuffer, { a: "XYZ" }, function() {
console.log("Published eventually ...");
});
}

Loopback: How to stop a console command?

I am running a loopback command through the command line.
the script is as follows:
const app = require('../../server/server');
let acquireToken = function() {
app.stop();
};
acquireToken();
However this does not seem to work as stop() is not a function. So I have added this in the server.js file:
'use strict';
let loopback = require('loopback');
let boot = require('loopback-boot');
let app = module.exports = loopback();
let winston = require('./winston');
app.start = function() {
// start the web server
return app.listen(function() {
app.emit('started');
let baseUrl = app.get('url').replace(/\/$/, '');
winston.info('Web server listening at: %s', baseUrl);
if (app.get('loopback-component-explorer')) {
let explorerPath = app.get('loopback-component-explorer').mountPath;
winston.info('Browse your REST API at %s%s', baseUrl, explorerPath);
}
});
};
let server;
app.stop = () => {
server.close();
}
// Bootstrap the application, configure models, datasources and middleware.
// Sub-apps like REST API are mounted via boot scripts.
boot(app, __dirname, function(err) {
if (err) throw err;
// start the server if `$ node server.js`
if (require.main === module)
server = app.start();
});
module.exports = app;
But require.main == module is preventing this from working as this gives me the following:
TypeError: Cannot read property 'close' of undefined
What is the recommended way of making this work?
Your question is a bit misleading because you're trying to stop an express object returned from a require call, so a more accurate question is
How do I start and stop a listening loopback app from another script?
In server.js change app.start = function() to app.start = function(cb) and add if (cb) {cb()}; to the end.
'use strict';
let loopback = require('loopback');
let boot = require('loopback-boot');
let app = module.exports = loopback();
let winston = require('./winston');
app.start = function(cb) {
// start the web server
return app.listen(function() {
app.emit('started');
let baseUrl = app.get('url').replace(/\/$/, '');
winston.info('Web server listening at: %s', baseUrl);
if (app.get('loopback-component-explorer')) {
let explorerPath = app.get('loopback-component-explorer').mountPath;
winston.info('Browse your REST API at %s%s', baseUrl, explorerPath);
}
if (cb) cb();
});
};
let server;
app.stop = () => {
server.close();
}
// Bootstrap the application, configure models, datasources and middleware.
// Sub-apps like REST API are mounted via boot scripts.
boot(app, __dirname, function(err) {
if (err) throw err;
// start the server if `$ node server.js`
if (require.main === module)
server = app.start();
});
module.exports = app;
And for your file which starts and stops loopback
// Setup loopback, but don't start the server
const app = require('./server');
// Define our callback function
let acquireToken = function() {
app.server.close();
console.log('closed');
};
// Hold onto the listener object so we can stop it later and start the server with the callback which executes after startup.
app.server = app.start(acquireToken);